diff --git a/BUILD.gn b/BUILD.gn index 8c4052f7..c9d40e8 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -481,7 +481,6 @@ "//breakpad:microdump_stackwalk($host_toolchain)", "//breakpad:minidump_dump($host_toolchain)", "//breakpad:minidump_stackwalk($host_toolchain)", - "//cloud_print:cloud_print_unittests", "//components/network_hints/browser", "//content/public/app:browser", "//content/public/app:child", @@ -548,8 +547,6 @@ "//chrome/installer/setup:setup_unittests", "//chrome_elf:chrome_elf_unittests", "//chrome_elf:dll_hash_main", - "//cloud_print:cloud_print_unittests", - "//cloud_print/service/win:cloud_print_service", "//components/wifi:wifi_test", "//net:quic_client", "//net:quic_server", @@ -603,15 +600,10 @@ "//mash/wm:tests", "//media/mojo/services:tests", "//mojo/services/network:apptests", - "//mojo/shell:apptests", - "//sql/mojo:apptests", + "//mojo/shell/tests:apptests", "//ui/views/mus:tests", ] } - - if (is_android) { - deps += [ "//mojo/shell/standalone:mojo_shell_standalone_apptests_apk" ] - } } group("gn_only") {
diff --git a/DEPS b/DEPS index f712a8e1..a9fe226 100644 --- a/DEPS +++ b/DEPS
@@ -39,11 +39,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'c5eddd7d8d67a6e931973a729c5868c155cb751f', + 'skia_revision': 'f5d4746ad73ef5eabc927d3d988bb9ee97c77921', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '89e3e864fa9e410e7ed185cad16922ecd124da62', + 'v8_revision': '1f168dc0b2346e0596b0e4939ca20bf316fc4fae', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -67,7 +67,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. - 'boringssl_revision': '894a47df2423f0d2b6be57e6d90f2bea88213382', + 'boringssl_revision': '6d49157929abdefb155daf2751dc03fd9eb4240c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling nss # and whatever else without interference from each other. @@ -87,7 +87,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling NaCl # and whatever else without interference from each other. - 'nacl_revision': '6b2da5cf95dee651e53baa6247484e79e45b31e6', + 'nacl_revision': '91f4487c5eca1165cead18e2a581f9b3652cc587', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling dEQP # and whatever else without interference from each other. @@ -185,7 +185,7 @@ Var('chromium_git') + '/external/selenium/py.git' + '@' + '5fd78261a75fe08d27ca4835fb6c5ce4b42275bd', 'src/third_party/libvpx_new/source/libvpx': - Var('chromium_git') + '/webm/libvpx.git' + '@' + 'f288c943c4d6c3fb03266dee821df797fb99bde0', + Var('chromium_git') + '/webm/libvpx.git' + '@' + '89cc68252846478fa7f2d570d96ff93776cefac6', 'src/third_party/ffmpeg': Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + 'e6e47f514216bbcdbfe796eb1f398c9afece93c8', @@ -215,7 +215,7 @@ Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067', 'src/third_party/webrtc': - Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'c422cbd570b0982db84538ee6d0843a65ff203d0', # commit position 11652 + Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '6dd0ae444aaa75cb9cd6726c3d74376f1aa804c6', # commit position 11686 'src/third_party/openmax_dl': Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' + Var('openmax_dl_revision'), @@ -275,7 +275,7 @@ 'src/third_party/catapult': Var('chromium_git') + '/external/github.com/catapult-project/catapult.git' + '@' + - '14d7b1b3fd8ca4dad28b37db4235674ef980a10b', + '14c9d0ed23d3b45a87811d615a446cae3ea07044', 'src/third_party/openh264/src': Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'b37cda248234162033e3e11b0335f3131cdfe488',
diff --git a/WATCHLISTS b/WATCHLISTS index e864a18..e48ecbfb 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -168,6 +168,10 @@ 'browser_resources': { 'filepath': 'chrome/browser/resources/', }, + 'browser_resources_md': { + 'filepath': 'chrome/browser/resources/md_'\ + '|chrome/browser/resources/settings/', + }, 'browsing_data': { 'filepath': 'chrome/browser/browsing_data/', }, @@ -287,6 +291,9 @@ 'filepath': 'net/cookies/|'\ 'chrome/browser/net/sqlite_persistent_cookie_store', }, + 'cr_elements': { + 'filepath': 'ui/webui/resources/cr_element', + }, 'custom_tabs': { 'filepath': 'chrome/android/java/src/org/chromium/chrome/browser/customtabs/|'\ 'chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/', @@ -472,8 +479,7 @@ '|content/public/common/manifest*', }, 'md_settings': { - 'filepath': 'ui/webui/resources/cr_element'\ - '|chrome/browser/resources/settings/'\ + 'filepath': 'chrome/browser/resources/settings/'\ '|chrome/browser/ui/webui/settings/', }, 'media': { @@ -1220,7 +1226,6 @@ 'dtseng+watch@chromium.org', 'je_julie.kim@chromium.org', 'nektar+watch@chromium.org', - 'plundblad+watch@chromium.org', 'yuzo+watch@chromium.org'], 'activity_log': ['felt@chromium.org'], 'android_infobars': ['dfalcantara+watch@chromium.org'], @@ -1275,6 +1280,7 @@ 'browser_compositor': ['vollick@chromium.org', 'piman+watch@chromium.org'], 'browser_resources': ['arv+watch@chromium.org'], + 'browser_resources_md': ['michaelpg+watch-md-ui@chromium.org'], 'browsing_data': ['markusheintz@chromium.org'], 'bubble': ['msw+watch@chromium.org', 'rouslan+bubble@chromium.org', @@ -1328,6 +1334,9 @@ 'peter@chromium.org'], 'content_worker': ['blink-worker-reviews@chromium.org', 'kinuko+watch@chromium.org'], + 'cr_elements': ['dbeam+watch-elements@chromium.org', + 'michaelpg+watch-elements@chromium.org', + 'stevenjb+watch-md-settings@chromium.org'], 'deep_memory_profiler': ['dmikurube@chromium.org'], 'device_bluetooth': ['scheib+watch@chromium.org', 'ortuno+watch@chromium.org'],
diff --git a/android_webview/browser/aw_permission_manager.cc b/android_webview/browser/aw_permission_manager.cc index c7fe04db2..b8ddbbd 100644 --- a/android_webview/browser/aw_permission_manager.cc +++ b/android_webview/browser/aw_permission_manager.cc
@@ -169,7 +169,6 @@ PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(PermissionStatus)>& callback) { int render_process_id = render_frame_host->GetProcess()->GetID(); int render_frame_id = render_frame_host->GetRoutingID(); @@ -262,7 +261,6 @@ const std::vector<PermissionType>& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<PermissionStatus>&)>& callback) { NOTIMPLEMENTED() << "RequestPermissions has not been implemented in WebView";
diff --git a/android_webview/browser/aw_permission_manager.h b/android_webview/browser/aw_permission_manager.h index 38a052f..0bb24f4 100644 --- a/android_webview/browser/aw_permission_manager.h +++ b/android_webview/browser/aw_permission_manager.h
@@ -26,13 +26,11 @@ content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(content::PermissionStatus)>& callback) override; int RequestPermissions( const std::vector<content::PermissionType>& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<content::PermissionStatus>&)>& callback) override; void CancelPermissionRequest(int request_id) override;
diff --git a/android_webview/browser/net/aw_request_interceptor.cc b/android_webview/browser/net/aw_request_interceptor.cc index 9aa6a2a4..62e0619 100644 --- a/android_webview/browser/net/aw_request_interceptor.cc +++ b/android_webview/browser/net/aw_request_interceptor.cc
@@ -116,25 +116,16 @@ scoped_ptr<AwContentsIoThreadClient> GetCorrespondingIoThreadClient( net::URLRequest* request) { + if (content::ResourceRequestInfo::OriginatedFromServiceWorker(request)) + return AwContentsIoThreadClient::GetServiceWorkerIoThreadClient(); + int render_process_id, render_frame_id; if (!content::ResourceRequestInfo::GetRenderFrameForRequest( request, &render_process_id, &render_frame_id)) { - // When there is no associated render frame, or the frame_id is - // invalid we assume the request come from a service worker. - // TODO: add a more explicit service worker check. - return AwContentsIoThreadClient::GetServiceWorkerIoThreadClient(); + return nullptr; } - scoped_ptr<AwContentsIoThreadClient> io_thread_client = - AwContentsIoThreadClient::FromID(render_process_id, render_frame_id); - - if (!io_thread_client) { - // This means the frame id is invalid/not set, this currently happens - // with e.g. fetch requests coming from service workers. - return AwContentsIoThreadClient::GetServiceWorkerIoThreadClient(); - } - - return io_thread_client; + return AwContentsIoThreadClient::FromID(render_process_id, render_frame_id); } } // namespace
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java index a9643c8..e8e6a38 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -29,6 +29,7 @@ import org.chromium.android_webview.test.util.ImagePageGenerator; import org.chromium.android_webview.test.util.VideoTestUtil; import org.chromium.android_webview.test.util.VideoTestWebServer; +import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.TestFileUtil; import org.chromium.base.test.util.UrlUtils; @@ -1603,8 +1604,12 @@ views.getContainer1(), views.getClient1(), new ImagePageGenerator(1, true))); } - @SmallTest - @Feature({"AndroidWebView", "Preferences"}) + /* + * Disabled due to document.defaultCharset removal. crbug.com/587484 + * @SmallTest + * @Feature({"AndroidWebView", "Preferences"}) + */ + @DisabledTest public void testDefaultTextEncodingWithTwoViews() throws Throwable { ViewPair views = createViews(); runPerViewSettingsTest(
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java index 159983b..fb5a052 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java
@@ -113,7 +113,7 @@ assertEquals(4, findAllAsyncOnUiThread("wood")); clearMatchesOnUiThread(); assertEquals(4, findAllAsyncOnUiThread("wood")); - assertEquals(2, findNextOnUiThread(true)); + assertEquals(1, findNextOnUiThread(true)); } @SmallTest
diff --git a/android_webview/tools/WebViewShell/test/webexposed/global-interface-listing-expected.txt b/android_webview/tools/WebViewShell/test/webexposed/global-interface-listing-expected.txt index d14f681..da2b84ac 100644 --- a/android_webview/tools/WebViewShell/test/webexposed/global-interface-listing-expected.txt +++ b/android_webview/tools/WebViewShell/test/webexposed/global-interface-listing-expected.txt
@@ -642,7 +642,6 @@ getter contentType getter cookie getter currentScript - getter defaultCharset getter defaultView getter designMode getter dir
diff --git a/ash/mus/sysui_application.cc b/ash/mus/sysui_application.cc index 917788b..d5e008c 100644 --- a/ash/mus/sysui_application.cc +++ b/ash/mus/sysui_application.cc
@@ -261,5 +261,9 @@ ash_init_->Initialize(shell); } +bool SysUIApplication::AcceptConnection(mojo::Connection* connection) { + return true; +} + } // namespace sysui } // namespace ash
diff --git a/ash/mus/sysui_application.h b/ash/mus/sysui_application.h index c899736..d7bebe5 100644 --- a/ash/mus/sysui_application.h +++ b/ash/mus/sysui_application.h
@@ -25,6 +25,7 @@ void Initialize(mojo::Shell* shell, const std::string& url, uint32_t id) override; + bool AcceptConnection(mojo::Connection* connection) override; mojo::TracingImpl tracing_; scoped_ptr<AshInit> ash_init_;
diff --git a/base/BUILD.gn b/base/BUILD.gn index 852d4d2d..2014163 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -1311,6 +1311,7 @@ "mac/scoped_nsobject.h", "mac/scoped_objc_class_swizzler.h", "mac/scoped_objc_class_swizzler.mm", + "memory/shared_memory_posix.cc", "message_loop/message_pump_mac.h", "message_loop/message_pump_mac.mm", "process/memory_stubs.cc", @@ -1642,6 +1643,7 @@ "memory/scoped_vector_unittest.cc", "memory/shared_memory_mac_unittest.cc", "memory/shared_memory_unittest.cc", + "memory/shared_memory_win_unittest.cc", "memory/singleton_unittest.cc", "memory/weak_ptr_unittest.cc", "message_loop/message_loop_task_runner_unittest.cc",
diff --git a/base/android/java/src/org/chromium/base/ResourceExtractor.java b/base/android/java/src/org/chromium/base/ResourceExtractor.java index c8691bb..458e266 100644 --- a/base/android/java/src/org/chromium/base/ResourceExtractor.java +++ b/base/android/java/src/org/chromium/base/ResourceExtractor.java
@@ -263,11 +263,6 @@ sResourcesToExtract = entries; } - // TODO(agrieve): Delete this method ones all usages of it are updated. - public static void setMandatoryPaksToExtract(String... paths) { - assert paths.length == 1 && "".equals(paths[0]); - } - private ResourceExtractor(Context context) { mContext = context.getApplicationContext(); }
diff --git a/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java b/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java index 5abc4e5..bff22c8 100644 --- a/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java +++ b/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java
@@ -9,6 +9,7 @@ import org.chromium.base.Log; import org.chromium.base.PathUtils; +import org.chromium.base.annotations.SuppressFBWarnings; import java.util.HashMap; import java.util.Locale; @@ -159,6 +160,7 @@ // Used internally to wait for shared RELROs. Returns once useSharedRelros() has been // called to supply a valid shared RELROs bundle. + @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE") private void waitForSharedRelrosLocked() { if (DEBUG) { Log.i(TAG, "waitForSharedRelros called"); @@ -172,17 +174,12 @@ // Wait until notified by useSharedRelros() that shared RELROs have arrived. long startTime = DEBUG ? SystemClock.uptimeMillis() : 0; - // Note: The additional synchronized block is present only to silence Findbugs. - // Without it, Findbugs reports a false positive: - // RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE: Redundant nullcheck of value known to be null - synchronized (mLock) { - while (mSharedRelros == null) { - try { - mLock.wait(); - } catch (InterruptedException e) { - // Restore the thread's interrupt status. - Thread.currentThread().interrupt(); - } + while (mSharedRelros == null) { + try { + mLock.wait(); + } catch (InterruptedException e) { + // Restore the thread's interrupt status. + Thread.currentThread().interrupt(); } }
diff --git a/base/base.gyp b/base/base.gyp index 38cb811..2b93cda 100644 --- a/base/base.gyp +++ b/base/base.gyp
@@ -467,8 +467,9 @@ 'memory/scoped_ptr_unittest.cc', 'memory/scoped_ptr_unittest.nc', 'memory/scoped_vector_unittest.cc', - 'memory/shared_memory_unittest.cc', 'memory/shared_memory_mac_unittest.cc', + 'memory/shared_memory_unittest.cc', + 'memory/shared_memory_win_unittest.cc', 'memory/singleton_unittest.cc', 'memory/weak_ptr_unittest.cc', 'memory/weak_ptr_unittest.nc', @@ -1551,6 +1552,7 @@ '../base/test/android/junit/src/org/chromium/base/test/util/DisableIfTest.java', ], 'test_type': 'junit', + 'wrapper_script_name': 'helper/<(_target_name)', }, 'includes': [ '../build/android/test_runner.gypi',
diff --git a/base/deferred_sequenced_task_runner.cc b/base/deferred_sequenced_task_runner.cc index dc118f3..f805b50 100644 --- a/base/deferred_sequenced_task_runner.cc +++ b/base/deferred_sequenced_task_runner.cc
@@ -13,6 +13,9 @@ : is_non_nestable(false) { } +DeferredSequencedTaskRunner::DeferredTask::DeferredTask( + const DeferredTask& other) = default; + DeferredSequencedTaskRunner::DeferredTask::~DeferredTask() { }
diff --git a/base/deferred_sequenced_task_runner.h b/base/deferred_sequenced_task_runner.h index de5c3b90..5a57e867 100644 --- a/base/deferred_sequenced_task_runner.h +++ b/base/deferred_sequenced_task_runner.h
@@ -46,6 +46,7 @@ private: struct DeferredTask { DeferredTask(); + DeferredTask(const DeferredTask& other); ~DeferredTask(); tracked_objects::Location posted_from;
diff --git a/base/memory/shared_memory_win.cc b/base/memory/shared_memory_win.cc index d7545f7f..9da7f63 100644 --- a/base/memory/shared_memory_win.cc +++ b/base/memory/shared_memory_win.cc
@@ -62,6 +62,41 @@ return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE; } +// Returns a HANDLE on success and |nullptr| on failure. +// This function is similar to CreateFileMapping, but removes the permissions +// WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE. +// +// A newly created file mapping has two sets of permissions. It has access +// control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and +// file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). ::DuplicateHandle() +// with the parameter DUPLICATE_SAME_ACCESS copies both sets of permissions. +// +// The Chrome sandbox prevents HANDLEs with the WRITE_DAC permission from being +// duplicated into unprivileged processes. But the only way to copy file +// permissions is with the parameter DUPLICATE_SAME_ACCESS. This means that +// there is no way for a privileged process to duplicate a file mapping into an +// unprivileged process while maintaining the previous file permissions. +// +// By removing all access control permissions of a file mapping immediately +// after creation, ::DuplicateHandle() effectively only copies the file +// permissions. +HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa, + size_t rounded_size, + LPCWSTR name) { + HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0, + static_cast<DWORD>(rounded_size), name); + if (!h) + return nullptr; + + HANDLE h2; + BOOL success = ::DuplicateHandle( + GetCurrentProcess(), h, GetCurrentProcess(), &h2, + FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY, FALSE, 0); + BOOL rv = ::CloseHandle(h); + DCHECK(rv); + return success ? h2 : nullptr; +} + } // namespace. namespace base { @@ -169,7 +204,7 @@ SECURITY_DESCRIPTOR sd; ACL dacl; - if (options.share_read_only && name_.empty()) { + if (name_.empty()) { // Add an empty DACL to enforce anonymous read-only sections. sa.lpSecurityDescriptor = &sd; if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) @@ -187,9 +222,8 @@ rand_values[0], rand_values[1], rand_values[2], rand_values[3]); } - mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, - PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size), - name_.empty() ? nullptr : name_.c_str()); + mapped_file_ = CreateFileMappingWithReducedPermissions( + &sa, rounded_size, name_.empty() ? nullptr : name_.c_str()); if (!mapped_file_) return false;
diff --git a/base/memory/shared_memory_win_unittest.cc b/base/memory/shared_memory_win_unittest.cc new file mode 100644 index 0000000..9dbefb5 --- /dev/null +++ b/base/memory/shared_memory_win_unittest.cc
@@ -0,0 +1,221 @@ +// 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 <windows.h> +#include <sddl.h> + +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/process/process.h" +#include "base/rand_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "base/test/multiprocess_test.h" +#include "base/test/test_timeouts.h" +#include "base/win/scoped_handle.h" +#include "base/win/win_util.h" +#include "testing/multiprocess_func_list.h" + +namespace base { +namespace { +const char* kHandleSwitchName = "shared_memory_win_test_switch"; + +// Creates a process token with a low integrity SID. +win::ScopedHandle CreateLowIntegritySID() { + HANDLE process_token_raw = nullptr; + BOOL success = ::OpenProcessToken(GetCurrentProcess(), + TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | + TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, + &process_token_raw); + if (!success) + return base::win::ScopedHandle(); + win::ScopedHandle process_token(process_token_raw); + + HANDLE lowered_process_token_raw = nullptr; + success = + ::DuplicateTokenEx(process_token.Get(), 0, NULL, SecurityImpersonation, + TokenPrimary, &lowered_process_token_raw); + if (!success) + return base::win::ScopedHandle(); + win::ScopedHandle lowered_process_token(lowered_process_token_raw); + + // Low integrity SID + WCHAR integrity_sid_string[20] = L"S-1-16-4096"; + PSID integrity_sid = nullptr; + success = ::ConvertStringSidToSid(integrity_sid_string, &integrity_sid); + if (!success) + return base::win::ScopedHandle(); + + TOKEN_MANDATORY_LABEL TIL = {}; + TIL.Label.Attributes = SE_GROUP_INTEGRITY; + TIL.Label.Sid = integrity_sid; + success = ::SetTokenInformation( + lowered_process_token.Get(), TokenIntegrityLevel, &TIL, + sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(integrity_sid)); + if (!success) + return base::win::ScopedHandle(); + return lowered_process_token; +} + +// Reads a HANDLE from the pipe as a raw int, least significant digit first. +win::ScopedHandle ReadHandleFromPipe(HANDLE pipe) { + // Read from parent pipe. + const size_t buf_size = 1000; + char buffer[buf_size]; + memset(buffer, 0, buf_size); + DWORD bytes_read; + BOOL success = ReadFile(pipe, buffer, buf_size, &bytes_read, NULL); + + if (!success || bytes_read == 0) { + LOG(ERROR) << "Failed to read handle from pipe."; + return win::ScopedHandle(); + } + + int handle_as_int = 0; + int power_of_ten = 1; + for (unsigned int i = 0; i < bytes_read; ++i) { + handle_as_int += buffer[i] * power_of_ten; + power_of_ten *= 10; + } + + return win::ScopedHandle(reinterpret_cast<HANDLE>(handle_as_int)); +} + +// Writes a HANDLE to a pipe as a raw int, least significant digit first. +void WriteHandleToPipe(HANDLE pipe, HANDLE handle) { + uint32_t handle_as_int = base::win::HandleToUint32(handle); + + scoped_ptr<char, base::FreeDeleter> buffer(static_cast<char*>(malloc(1000))); + size_t index = 0; + while (handle_as_int > 0) { + buffer.get()[index] = handle_as_int % 10; + handle_as_int /= 10; + ++index; + } + + ::ConnectNamedPipe(pipe, nullptr); + DWORD written; + ASSERT_TRUE(::WriteFile(pipe, buffer.get(), index, &written, NULL)); +} + +// Creates a communication pipe with the given name. +win::ScopedHandle CreateCommunicationPipe(const std::wstring& name) { + return win::ScopedHandle(CreateNamedPipe(name.c_str(), // pipe name + PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, + 1000, 1000, 0, NULL)); +} + +// Generates a random name for a communication pipe. +std::wstring CreateCommunicationPipeName() { + uint64_t rand_values[4]; + RandBytes(&rand_values, sizeof(rand_values)); + std::wstring child_pipe_name = StringPrintf( + L"\\\\.\\pipe\\SharedMemoryWinTest_%016llx%016llx%016llx%016llx", + rand_values[0], rand_values[1], rand_values[2], rand_values[3]); + return child_pipe_name; +} + +class SharedMemoryWinTest : public base::MultiProcessTest { + protected: + CommandLine MakeCmdLine(const std::string& procname) override { + CommandLine line = base::MultiProcessTest::MakeCmdLine(procname); + line.AppendSwitchASCII(kHandleSwitchName, communication_pipe_name_); + return line; + } + + std::string communication_pipe_name_; +}; + +MULTIPROCESS_TEST_MAIN(LowerPermissions) { + std::string handle_name = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kHandleSwitchName); + std::wstring handle_name16 = SysUTF8ToWide(handle_name); + win::ScopedHandle parent_pipe( + ::CreateFile(handle_name16.c_str(), // pipe name + GENERIC_READ, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL)); // no template file + if (parent_pipe.Get() == INVALID_HANDLE_VALUE) { + LOG(ERROR) << "Failed to open communication pipe."; + return 1; + } + + win::ScopedHandle received_handle = ReadHandleFromPipe(parent_pipe.Get()); + if (!received_handle.Get()) { + LOG(ERROR) << "Failed to read handle from pipe."; + return 1; + } + + // Attempting to add the WRITE_DAC permission should fail. + HANDLE duped_handle; + BOOL success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), + GetCurrentProcess(), &duped_handle, + FILE_MAP_READ | WRITE_DAC, FALSE, 0); + if (success) { + LOG(ERROR) << "Should not have been able to add WRITE_DAC permission."; + return 1; + } + + // Attempting to add the FILE_MAP_WRITE permission should fail. + success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), + GetCurrentProcess(), &duped_handle, + FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0); + if (success) { + LOG(ERROR) << "Should not have been able to add FILE_MAP_WRITE permission."; + return 1; + } + + // Attempting to duplicate the HANDLE with the same permissions should + // succeed. + success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), + GetCurrentProcess(), &duped_handle, FILE_MAP_READ, + FALSE, 0); + if (!success) { + LOG(ERROR) << "Failed to duplicate handle."; + return 4; + } + ::CloseHandle(duped_handle); + return 0; +} + +TEST_F(SharedMemoryWinTest, LowerPermissions) { + std::wstring communication_pipe_name = CreateCommunicationPipeName(); + communication_pipe_name_ = SysWideToUTF8(communication_pipe_name); + + win::ScopedHandle communication_pipe = + CreateCommunicationPipe(communication_pipe_name); + ASSERT_TRUE(communication_pipe.Get()); + + win::ScopedHandle lowered_process_token = CreateLowIntegritySID(); + ASSERT_TRUE(lowered_process_token.Get()); + + base::LaunchOptions options; + options.as_user = lowered_process_token.Get(); + base::Process process = SpawnChildWithOptions("LowerPermissions", options); + ASSERT_TRUE(process.IsValid()); + + SharedMemory memory; + memory.CreateAndMapAnonymous(1001); + + // Duplicate into child process, giving only FILE_MAP_READ permissions. + HANDLE raw_handle = nullptr; + ::DuplicateHandle(::GetCurrentProcess(), memory.handle().GetHandle(), + process.Handle(), &raw_handle, + FILE_MAP_READ | SECTION_QUERY, FALSE, 0); + ASSERT_TRUE(raw_handle); + + WriteHandleToPipe(communication_pipe.Get(), raw_handle); + + int exit_code; + EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(), + &exit_code)); + EXPECT_EQ(0, exit_code); +} + +} // namespace +} // namespace base
diff --git a/base/memory/weak_ptr.cc b/base/memory/weak_ptr.cc index d9ce86a..16d3dff 100644 --- a/base/memory/weak_ptr.cc +++ b/base/memory/weak_ptr.cc
@@ -34,6 +34,8 @@ WeakReference::WeakReference() { } +WeakReference::WeakReference(const WeakReference& other) = default; + WeakReference::WeakReference(const Flag* flag) : flag_(flag) { }
diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h index 33d1e47..ffe6f97 100644 --- a/base/memory/weak_ptr.h +++ b/base/memory/weak_ptr.h
@@ -107,6 +107,7 @@ }; WeakReference(); + WeakReference(const WeakReference& other); explicit WeakReference(const Flag* flag); ~WeakReference();
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc index 647fe55..238779d 100644 --- a/base/metrics/field_trial.cc +++ b/base/metrics/field_trial.cc
@@ -132,6 +132,8 @@ FieldTrial::State::State() : activated(false) {} +FieldTrial::State::State(const State& other) = default; + FieldTrial::State::~State() {} void FieldTrial::Disable() {
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h index 0dfa036..d087729 100644 --- a/base/metrics/field_trial.h +++ b/base/metrics/field_trial.h
@@ -119,6 +119,7 @@ bool activated; State(); + State(const State& other); ~State(); };
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index c33d2f0..ea4f816 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc
@@ -267,15 +267,18 @@ flags); } -HistogramBase* Histogram::PersistentGet(const std::string& name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - HistogramBase::AtomicCount* counts, - uint32_t counts_size, - HistogramSamples::Metadata* meta) { - return new Histogram(name, minimum, maximum, ranges, counts, counts_size, - meta); +HistogramBase* Histogram::PersistentGet( + const std::string& name, + Sample minimum, + Sample maximum, + const BucketRanges* ranges, + HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, + uint32_t counts_size, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) { + return new Histogram(name, minimum, maximum, ranges, counts, logged_counts, + counts_size, meta, logged_meta); } // Calculate what range of values are held in each bucket. @@ -320,7 +323,7 @@ // static const int Histogram::kCommonRaceBasedCountMismatch = 5; -int Histogram::FindCorruption(const HistogramSamples& samples) const { +uint32_t Histogram::FindCorruption(const HistogramSamples& samples) const { int inconsistencies = NO_INCONSISTENCIES; Sample previous_range = -1; // Bottom range is always 0. for (uint32_t index = 0; index < bucket_count(); ++index) { @@ -430,6 +433,21 @@ return SnapshotSampleVector(); } +scoped_ptr<HistogramSamples> Histogram::SnapshotDelta() { + scoped_ptr<HistogramSamples> snapshot = SnapshotSampleVector(); + if (!logged_samples_) { + // If nothing has been previously logged, save this one as + // |logged_samples_| and gather another snapshot to return. + logged_samples_.swap(snapshot); + return SnapshotSampleVector(); + } + + // Subtract what was previously logged and update that information. + snapshot->Subtract(*logged_samples_); + logged_samples_->Add(*snapshot); + return snapshot; +} + void Histogram::AddSamples(const HistogramSamples& samples) { samples_->Add(samples); } @@ -477,8 +495,10 @@ Sample maximum, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta) + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) : HistogramBase(name), bucket_ranges_(ranges), declared_min_(minimum), @@ -486,6 +506,8 @@ if (ranges) { samples_.reset(new SampleVector(HashMetricName(name), counts, counts_size, meta, ranges)); + logged_samples_.reset(new SampleVector(samples_->id(), logged_counts, + counts_size, logged_meta, ranges)); } } @@ -776,10 +798,12 @@ Sample maximum, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta) { + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) { return new LinearHistogram(name, minimum, maximum, ranges, counts, - counts_size, meta); + logged_counts, counts_size, meta, logged_meta); } HistogramBase* LinearHistogram::FactoryGetWithRangeDescription( @@ -813,15 +837,12 @@ Sample maximum, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta) - : Histogram(name, - minimum, - maximum, - ranges, - counts, - counts_size, - meta) {} + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) + : Histogram(name, minimum, maximum, ranges, counts, logged_counts, + counts_size, meta, logged_meta) {} double LinearHistogram::GetBucketSize(Count current, uint32_t i) const { DCHECK_GT(ranges(i + 1), ranges(i)); @@ -854,6 +875,8 @@ double linear_range = (min * (bucket_count - 1 - i) + max * (i - 1)) / (bucket_count - 2); ranges->set_range(i, static_cast<Sample>(linear_range + 0.5)); + // TODO(bcwhite): Remove once crbug/586622 is fixed. + base::debug::Alias(&linear_range); } ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX); ranges->ResetChecksum(); @@ -919,8 +942,11 @@ const std::string& name, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, - HistogramSamples::Metadata* meta) { - return new BooleanHistogram(name, ranges, counts, meta); + HistogramBase::AtomicCount* logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) { + return new BooleanHistogram(name, ranges, counts, logged_counts, meta, + logged_meta); } HistogramType BooleanHistogram::GetHistogramType() const { @@ -934,8 +960,11 @@ BooleanHistogram::BooleanHistogram(const std::string& name, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, - HistogramSamples::Metadata* meta) - : LinearHistogram(name, 1, 2, ranges, counts, 2, meta) {} + HistogramBase::AtomicCount* logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) + : LinearHistogram(name, 1, 2, ranges, counts, logged_counts, 2, meta, + logged_meta) {} HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) { std::string histogram_name; @@ -1019,9 +1048,12 @@ const std::string& name, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta) { - return new CustomHistogram(name, ranges, counts, counts_size, meta); + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) { + return new CustomHistogram(name, ranges, counts, logged_counts, counts_size, + meta, logged_meta); } HistogramType CustomHistogram::GetHistogramType() const { @@ -1053,15 +1085,19 @@ CustomHistogram::CustomHistogram(const std::string& name, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta) + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta) : Histogram(name, ranges->range(1), ranges->range(ranges->bucket_count() - 1), ranges, counts, + logged_counts, counts_size, - meta) {} + meta, + logged_meta) {} bool CustomHistogram::SerializeInfoImpl(Pickle* pickle) const { if (!Histogram::SerializeInfoImpl(pickle))
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h index 4e722aa2..ea59de64 100644 --- a/base/metrics/histogram.h +++ b/base/metrics/histogram.h
@@ -145,8 +145,10 @@ Sample maximum, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta); + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); static void InitializeBucketRanges(Sample minimum, Sample maximum, @@ -165,8 +167,9 @@ // consistent with the bucket ranges and checksums in our histogram. This can // produce a false-alarm if a race occurred in the reading of the data during // a SnapShot process, but should otherwise be false at all times (unless we - // have memory over-writes, or DRAM failures). - int FindCorruption(const HistogramSamples& samples) const override; + // have memory over-writes, or DRAM failures). Flag definitions are located + // under "enum Inconsistency" in base/metrics/histogram_base.h. + uint32_t FindCorruption(const HistogramSamples& samples) const override; //---------------------------------------------------------------------------- // Accessors for factory construction, serialization and testing. @@ -197,6 +200,7 @@ void Add(Sample value) override; void AddCount(Sample value, int count) override; scoped_ptr<HistogramSamples> SnapshotSamples() const override; + scoped_ptr<HistogramSamples> SnapshotDelta() override; void AddSamples(const HistogramSamples& samples) override; bool AddSamplesFromPickle(base::PickleIterator* iter) override; void WriteHTMLGraph(std::string* output) const override; @@ -228,8 +232,10 @@ Sample maximum, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta); + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); ~Histogram() override; @@ -303,6 +309,9 @@ // sample. scoped_ptr<SampleVector> samples_; + // Also keep a previous uploaded state for calculating deltas. + scoped_ptr<HistogramSamples> logged_samples_; + DISALLOW_COPY_AND_ASSIGN(Histogram); }; @@ -347,8 +356,10 @@ Sample maximum, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta); + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); struct DescriptionPair { Sample sample; @@ -388,8 +399,10 @@ Sample maximum, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta); + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); double GetBucketSize(Count current, uint32_t i) const override; @@ -431,7 +444,9 @@ static HistogramBase* PersistentGet(const std::string& name, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, - HistogramSamples::Metadata* meta); + HistogramBase::AtomicCount* logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); HistogramType GetHistogramType() const override; @@ -443,7 +458,9 @@ BooleanHistogram(const std::string& name, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, - HistogramSamples::Metadata* meta); + HistogramBase::AtomicCount* logged_counts, + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( base::PickleIterator* iter); @@ -476,8 +493,10 @@ static HistogramBase* PersistentGet(const std::string& name, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta); + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); // Overridden from Histogram: HistogramType GetHistogramType() const override; @@ -499,8 +518,10 @@ CustomHistogram(const std::string& name, const BucketRanges* ranges, HistogramBase::AtomicCount* counts, + HistogramBase::AtomicCount* logged_counts, uint32_t counts_size, - HistogramSamples::Metadata* meta); + HistogramSamples::Metadata* meta, + HistogramSamples::Metadata* logged_meta); // HistogramBase implementation: bool SerializeInfoImpl(base::Pickle* pickle) const override;
diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc index 412f143..3a40e68 100644 --- a/base/metrics/histogram_base.cc +++ b/base/metrics/histogram_base.cc
@@ -97,7 +97,7 @@ return SerializeInfoImpl(pickle); } -int HistogramBase::FindCorruption(const HistogramSamples& samples) const { +uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const { // Not supported by default. return NO_INCONSISTENCIES; }
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h index 40c2cae..6817629b 100644 --- a/base/metrics/histogram_base.h +++ b/base/metrics/histogram_base.h
@@ -94,14 +94,18 @@ }; // Histogram data inconsistency types. - enum Inconsistency { + enum Inconsistency : uint32_t { NO_INCONSISTENCIES = 0x0, RANGE_CHECKSUM_ERROR = 0x1, BUCKET_ORDER_ERROR = 0x2, COUNT_HIGH_ERROR = 0x4, COUNT_LOW_ERROR = 0x8, - NEVER_EXCEEDED_VALUE = 0x10 + NEVER_EXCEEDED_VALUE = 0x10, + + // This value is used only in HistogramSnapshotManager for marking + // internally when new inconsistencies are found. + NEW_INCONSISTENCY_FOUND = 0x8000000 }; explicit HistogramBase(const std::string& name); @@ -154,12 +158,17 @@ // Try to find out data corruption from histogram and the samples. // The returned value is a combination of Inconsistency enum. - virtual int FindCorruption(const HistogramSamples& samples) const; + virtual uint32_t FindCorruption(const HistogramSamples& samples) const; // Snapshot the current complete set of sample data. // Override with atomic/locked snapshot if needed. virtual scoped_ptr<HistogramSamples> SnapshotSamples() const = 0; + // Calculate the change (delta) in histogram counts since the previous call + // to this method. Each successive call will return only those counts + // changed since the last call. + virtual scoped_ptr<HistogramSamples> SnapshotDelta() = 0; + // The following methods provide graphical histogram displays. virtual void WriteHTMLGraph(std::string* output) const = 0; virtual void WriteAscii(std::string* output) const = 0;
diff --git a/base/metrics/histogram_persistence.cc b/base/metrics/histogram_persistence.cc index ae7d48b7..bd250ec8 100644 --- a/base/metrics/histogram_persistence.cc +++ b/base/metrics/histogram_persistence.cc
@@ -53,6 +53,9 @@ CREATE_HISTOGRAM_MAX }; +// Name of histogram for storing results of local operations. +const char kResultHistogram[] = "UMA.CreatePersistentHistogram.Result"; + // Type identifiers used when storing in persistent memory so they can be // identified during extraction; the first 4 bytes of the SHA1 of the name // is used as a unique integer. A "version number" is added to the base @@ -76,6 +79,7 @@ uint32_t ranges_checksum; PersistentMemoryAllocator::Reference counts_ref; HistogramSamples::Metadata samples_metadata; + HistogramSamples::Metadata logged_metadata; // Space for the histogram name will be added during the actual allocation // request. This must be the last field of the structure. A zero-size array @@ -111,6 +115,22 @@ return ranges.release(); } +// Calculate the number of bytes required to store all of a histogram's +// "counts". This will return zero (0) if |bucket_count| is not valid. +size_t CalculateRequiredCountsBytes(size_t bucket_count) { + // 2 because each "sample count" also requires a backup "logged count" + // used for calculating the delta during snapshot operations. + const unsigned kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount); + + // If the |bucket_count| is such that it would overflow the return type, + // perhaps as the result of a malicious actor, then return zero to + // indicate the problem to the caller. + if (bucket_count > std::numeric_limits<uint32_t>::max() / kBytesPerBucket) + return 0; + + return bucket_count * kBytesPerBucket; +} + } // namespace const Feature kPersistentHistogramsFeature{ @@ -142,8 +162,7 @@ } histogram_pointer = LinearHistogram::FactoryGet( - "UMA.CreatePersistentHistogram.Result", - 1, CREATE_HISTOGRAM_MAX, CREATE_HISTOGRAM_MAX + 1, + kResultHistogram, 1, CREATE_HISTOGRAM_MAX, CREATE_HISTOGRAM_MAX + 1, HistogramBase::kUmaTargetedHistogramFlag); base::subtle::Release_Store( &atomic_histogram_pointer, @@ -193,6 +212,15 @@ ref, kTypeIdHistogram); DCHECK(histogram_data); StatisticsRecorder::ForgetHistogramForTesting(histogram_data->name); + + // If a test breaks here then a memory region containing a histogram + // actively used by this code is being released back to the test. + // If that memory segment were to be deleted, future calls to create + // persistent histograms would crash. To avoid this, have the test call + // the method GetCreateHistogramResultHistogram() *before* setting the + // (temporary) memory allocator via SetPersistentMemoryAllocator() so + // that the histogram is instead allocated from the process heap. + DCHECK_NE(kResultHistogram, histogram_data->name); } } @@ -244,14 +272,21 @@ HistogramBase::AtomicCount* counts_data = allocator->GetAsObject<HistogramBase::AtomicCount>( histogram_data.counts_ref, kTypeIdCountsArray); - if (!counts_data || - allocator->GetAllocSize(histogram_data.counts_ref) < - histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) { + size_t counts_bytes = + CalculateRequiredCountsBytes(histogram_data.bucket_count); + if (!counts_data || !counts_bytes || + allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) { RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY); NOTREACHED(); return nullptr; } + // After the main "counts" array is a second array using for storing what + // was previously logged. This is used to calculate the "delta" during + // snapshot operations. + HistogramBase::AtomicCount* logged_data = + counts_data + histogram_data.bucket_count; + std::string name(histogram_data_ptr->name); HistogramBase* histogram = nullptr; switch (histogram_data.histogram_type) { @@ -262,8 +297,10 @@ histogram_data.maximum, ranges, counts_data, + logged_data, histogram_data.bucket_count, - &histogram_data_ptr->samples_metadata); + &histogram_data_ptr->samples_metadata, + &histogram_data_ptr->logged_metadata); DCHECK(histogram); break; case LINEAR_HISTOGRAM: @@ -273,8 +310,10 @@ histogram_data.maximum, ranges, counts_data, + logged_data, histogram_data.bucket_count, - &histogram_data_ptr->samples_metadata); + &histogram_data_ptr->samples_metadata, + &histogram_data_ptr->logged_metadata); DCHECK(histogram); break; case BOOLEAN_HISTOGRAM: @@ -282,7 +321,9 @@ name, ranges, counts_data, - &histogram_data_ptr->samples_metadata); + logged_data, + &histogram_data_ptr->samples_metadata, + &histogram_data_ptr->logged_metadata); DCHECK(histogram); break; case CUSTOM_HISTOGRAM: @@ -290,8 +331,10 @@ name, ranges, counts_data, + logged_data, histogram_data.bucket_count, - &histogram_data_ptr->samples_metadata); + &histogram_data_ptr->samples_metadata, + &histogram_data_ptr->logged_metadata); DCHECK(histogram); break; default: @@ -375,16 +418,15 @@ return nullptr; } + // If CalculateRequiredCountsBytes() returns zero then the bucket_count + // was not valid. size_t bucket_count = bucket_ranges->bucket_count(); - // An overflow such as this, perhaps as the result of a milicious actor, - // could lead to writing beyond the allocation boundary and into other - // memory. Just fail the allocation and let the caller deal with it. - if (bucket_count > std::numeric_limits<int32_t>::max() / - sizeof(HistogramBase::AtomicCount)) { + size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); + if (!counts_bytes) { NOTREACHED(); return nullptr; } - size_t counts_bytes = bucket_count * sizeof(HistogramBase::AtomicCount); + size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample); PersistentMemoryAllocator::Reference ranges_ref = allocator->Allocate(ranges_bytes, kTypeIdRangesArray); @@ -449,7 +491,7 @@ void ImportPersistentHistograms() { // The lock protects against concurrent access to the iterator and is created // in a thread-safe manner when needed. - static base::LazyInstance<base::Lock> lock = LAZY_INSTANCE_INITIALIZER; + static base::LazyInstance<base::Lock>::Leaky lock = LAZY_INSTANCE_INITIALIZER; if (g_allocator) { base::AutoLock auto_lock(lock.Get()); @@ -461,7 +503,7 @@ if (iter.is_clear()) g_allocator->CreateIterator(&iter); - for (;;) { + while (true) { HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter); if (!histogram) break;
diff --git a/base/metrics/histogram_samples.cc b/base/metrics/histogram_samples.cc index 4cd8e556b..246a020 100644 --- a/base/metrics/histogram_samples.cc +++ b/base/metrics/histogram_samples.cc
@@ -73,7 +73,11 @@ HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta) : meta_(meta) { DCHECK(meta_->id == 0 || meta_->id == id); - meta_->id = id; + + // It's possible that |meta| is contained in initialized, read-only memory + // so it's essential that no write be done in that case. + if (!meta_->id) + meta_->id = id; } HistogramSamples::~HistogramSamples() {}
diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc index 3d1c0d6..32dd4e67 100644 --- a/base/metrics/histogram_snapshot_manager.cc +++ b/base/metrics/histogram_snapshot_manager.cc
@@ -14,68 +14,138 @@ HistogramSnapshotManager::HistogramSnapshotManager( HistogramFlattener* histogram_flattener) - : histogram_flattener_(histogram_flattener) { + : preparing_deltas_(false), + histogram_flattener_(histogram_flattener) { DCHECK(histogram_flattener_); } HistogramSnapshotManager::~HistogramSnapshotManager() { - STLDeleteValues(&logged_samples_); } -void HistogramSnapshotManager::PrepareDelta(const HistogramBase& histogram) { +void HistogramSnapshotManager::StartDeltas() { + // Ensure that start/finish calls do not get nested. + DCHECK(!preparing_deltas_); + preparing_deltas_ = true; + + DCHECK(owned_histograms_.empty()); + +#ifdef DEBUG + CHECK(!iter->second.histogram); + CHECK(!iter->second.accumulated_samples); + CHECK(!(iter->second.inconsistencies & + HistogramBase::NEW_INCONSISTENCY_FOUND)); + } +#endif +} + +void HistogramSnapshotManager::PrepareDelta(HistogramBase* histogram) { + PrepareSamples(histogram, histogram->SnapshotDelta()); +} + +void HistogramSnapshotManager::PrepareDeltaTakingOwnership( + scoped_ptr<HistogramBase> histogram) { + PrepareSamples(histogram.get(), histogram->SnapshotDelta()); + owned_histograms_.push_back(std::move(histogram)); +} + +void HistogramSnapshotManager::PrepareAbsolute(const HistogramBase* histogram) { + PrepareSamples(histogram, histogram->SnapshotSamples()); +} + +void HistogramSnapshotManager::PrepareAbsoluteTakingOwnership( + scoped_ptr<const HistogramBase> histogram) { + PrepareSamples(histogram.get(), histogram->SnapshotSamples()); + owned_histograms_.push_back(std::move(histogram)); +} + +void HistogramSnapshotManager::FinishDeltas() { + DCHECK(preparing_deltas_); + + // Iterate over all known histograms to see what should be recorded. + for (auto& hash_and_info : known_histograms_) { + SampleInfo* sample_info = &hash_and_info.second; + + // First, record any histograms in which corruption was detected. + if (sample_info->inconsistencies & HistogramBase::NEW_INCONSISTENCY_FOUND) { + sample_info->inconsistencies &= ~HistogramBase::NEW_INCONSISTENCY_FOUND; + histogram_flattener_->UniqueInconsistencyDetected( + static_cast<HistogramBase::Inconsistency>( + sample_info->inconsistencies)); + } + + // Second, record actual accumulated deltas. + if (sample_info->accumulated_samples) { + // TODO(bcwhite): Investigate using redundant_count() below to avoid + // additional pass through all the samples to calculate real total. + if (sample_info->accumulated_samples->TotalCount() > 0) { + histogram_flattener_->RecordDelta(*sample_info->histogram, + *sample_info->accumulated_samples); + } + delete sample_info->accumulated_samples; + sample_info->accumulated_samples = nullptr; + } + + // The Histogram pointer must be cleared at this point because the owner + // is only required to keep it alive until FinishDeltas() completes. + sample_info->histogram = nullptr; + } + + owned_histograms_.clear(); + preparing_deltas_ = false; +} + +void HistogramSnapshotManager::PrepareSamples( + const HistogramBase* histogram, + scoped_ptr<HistogramSamples> samples) { DCHECK(histogram_flattener_); - // Get up-to-date snapshot of sample stats. - scoped_ptr<HistogramSamples> snapshot(histogram.SnapshotSamples()); + // Get information known about this histogram. + SampleInfo* sample_info = &known_histograms_[histogram->name_hash()]; + if (sample_info->histogram) { + DCHECK_EQ(sample_info->histogram->histogram_name(), + histogram->histogram_name()) << "hash collision"; + } else { + // First time this histogram has been seen; datafill. + sample_info->histogram = histogram; + } // Crash if we detect that our histograms have been overwritten. This may be // a fair distance from the memory smasher, but we hope to correlate these // crashes with other events, such as plugins, or usage patterns, etc. - int corruption = histogram.FindCorruption(*snapshot); + uint32_t corruption = histogram->FindCorruption(*samples); if (HistogramBase::BUCKET_ORDER_ERROR & corruption) { // The checksum should have caught this, so crash separately if it didn't. - CHECK_NE(0, HistogramBase::RANGE_CHECKSUM_ERROR & corruption); + CHECK_NE(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption); CHECK(false); // Crash for the bucket order corruption. } // Checksum corruption might not have caused order corruption. - CHECK_EQ(0, HistogramBase::RANGE_CHECKSUM_ERROR & corruption); + CHECK_EQ(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption); // Note, at this point corruption can only be COUNT_HIGH_ERROR or // COUNT_LOW_ERROR and they never arise together, so we don't need to extract // bits from corruption. - const uint64_t histogram_hash = histogram.name_hash(); if (corruption) { - DLOG(ERROR) << "Histogram: " << histogram.histogram_name() - << " has data corruption: " << corruption; + DLOG(ERROR) << "Histogram: \"" << histogram->histogram_name() + << "\" has data corruption: " << corruption; histogram_flattener_->InconsistencyDetected( static_cast<HistogramBase::Inconsistency>(corruption)); // Don't record corrupt data to metrics services. - int old_corruption = inconsistencies_[histogram_hash]; + const uint32_t old_corruption = sample_info->inconsistencies; if (old_corruption == (corruption | old_corruption)) return; // We've already seen this corruption for this histogram. - inconsistencies_[histogram_hash] |= corruption; - histogram_flattener_->UniqueInconsistencyDetected( - static_cast<HistogramBase::Inconsistency>(corruption)); + sample_info->inconsistencies |= + corruption | HistogramBase::NEW_INCONSISTENCY_FOUND; + // TODO(bcwhite): Can we clear the inconsistency for future collection? return; } - HistogramSamples* to_log; - auto it = logged_samples_.find(histogram_hash); - if (it == logged_samples_.end()) { - to_log = snapshot.release(); - - // This histogram has not been logged before, add a new entry. - logged_samples_[histogram_hash] = to_log; + if (!sample_info->accumulated_samples) { + // This histogram has not been seen before; add it as a new entry. + sample_info->accumulated_samples = samples.release(); } else { - HistogramSamples* already_logged = it->second; - InspectLoggedSamplesInconsistency(*snapshot, already_logged); - snapshot->Subtract(*already_logged); - already_logged->Add(*snapshot); - to_log = snapshot.get(); + // There are previous values from this histogram; add them together. + sample_info->accumulated_samples->Add(*samples); } - - if (to_log->TotalCount() > 0) - histogram_flattener_->RecordDelta(histogram, *to_log); } void HistogramSnapshotManager::InspectLoggedSamplesInconsistency(
diff --git a/base/metrics/histogram_snapshot_manager.h b/base/metrics/histogram_snapshot_manager.h index d1ba2a5..d44db194 100644 --- a/base/metrics/histogram_snapshot_manager.h +++ b/base/metrics/histogram_snapshot_manager.h
@@ -9,7 +9,9 @@ #include <map> #include <string> +#include <vector> +#include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/metrics/histogram_base.h" @@ -35,36 +37,86 @@ // |required_flags| is used to select histograms to be recorded. // Only histograms that have all the flags specified by the argument will be // chosen. If all histograms should be recorded, set it to - // |Histogram::kNoFlags|. + // |Histogram::kNoFlags|. Though any "forward" iterator will work, the + // histograms over which it iterates *must* remain valid until this method + // returns; the iterator cannot deallocate histograms once it iterates past + // them. template <class ForwardHistogramIterator> void PrepareDeltas(ForwardHistogramIterator begin, ForwardHistogramIterator end, HistogramBase::Flags flags_to_set, HistogramBase::Flags required_flags) { + StartDeltas(); for (ForwardHistogramIterator it = begin; it != end; ++it) { (*it)->SetFlags(flags_to_set); if (((*it)->flags() & required_flags) == required_flags) - PrepareDelta(**it); + PrepareDelta(*it); } + FinishDeltas(); } + // When the collection is not so simple as can be done using a single + // iterator, the steps can be performed separately. Call PerpareDelta() + // as many times as necessary with a single StartDeltas() before and + // a single FinishDeltas() after. All passed histograms must live + // until FinishDeltas() completes. PrepareAbsolute() works the same + // but assumes there were no previous logged values and no future deltas + // will be created (and thus can work on read-only histograms). + // Use Prepare*TakingOwnership() if it is desireable to have this class + // automatically delete the histogram once it is "finished". + void StartDeltas(); + void PrepareDelta(HistogramBase* histogram); + void PrepareDeltaTakingOwnership(scoped_ptr<HistogramBase> histogram); + void PrepareAbsolute(const HistogramBase* histogram); + void PrepareAbsoluteTakingOwnership( + scoped_ptr<const HistogramBase> histogram); + void FinishDeltas(); + private: - // Snapshot this histogram, and record the delta. - void PrepareDelta(const HistogramBase& histogram); + FRIEND_TEST_ALL_PREFIXES(HistogramSnapshotManagerTest, CheckMerge); + + // During a snapshot, samples are acquired and aggregated. This structure + // contains all the information collected for a given histogram. Once a + // snapshot operation is finished, it is generally emptied except for + // information that must persist from one report to the next, such as + // the "inconsistencies". + struct SampleInfo { + // A histogram associated with this sample; it may be one of many if + // several have been aggregated into the same "accumulated" sample set. + // Ownership of the histogram remains elsewhere and this pointer is + // cleared by FinishDeltas(). + const HistogramBase* histogram = nullptr; + + // The current snapshot-delta values being accumulated. + // TODO(bcwhite): Change this to a scoped_ptr once all build architectures + // support such as the value of a std::map. + HistogramSamples* accumulated_samples = nullptr; + + // The set of inconsistencies (flags) already seen for the histogram. + // See HistogramBase::Inconsistency for values. + uint32_t inconsistencies = 0; + }; + + // Capture and hold samples from a histogram. This does all the heavy + // lifting for PrepareDelta() and PrepareAbsolute(). + void PrepareSamples(const HistogramBase* histogram, + scoped_ptr<HistogramSamples> samples); // Try to detect and fix count inconsistency of logged samples. void InspectLoggedSamplesInconsistency( const HistogramSamples& new_snapshot, HistogramSamples* logged_samples); - // For histograms, track what we've already recorded (as a sample for - // each histogram) so that we can record only the delta with the next log. - // The information is indexed by the hash of the histogram name. - std::map<uint64_t, HistogramSamples*> logged_samples_; - - // Set of histograms found to be corrupt and their problems, indexed + // For histograms, track what has been previously seen, indexed // by the hash of the histogram name. - std::map<uint64_t, int> inconsistencies_; + std::map<uint64_t, SampleInfo> known_histograms_; + + // Collection of histograms of which ownership has been passed to this + // object. They will be deleted by FinishDeltas(). + std::vector<scoped_ptr<const HistogramBase>> owned_histograms_; + + // Indicates if deltas are currently being prepared. + bool preparing_deltas_; // |histogram_flattener_| handles the logistics of recording the histogram // deltas.
diff --git a/base/metrics/histogram_snapshot_manager_unittest.cc b/base/metrics/histogram_snapshot_manager_unittest.cc index 72db7d58..8ec03daa 100644 --- a/base/metrics/histogram_snapshot_manager_unittest.cc +++ b/base/metrics/histogram_snapshot_manager_unittest.cc
@@ -10,7 +10,9 @@ #include "base/macros.h" #include "base/metrics/histogram_delta_serialization.h" #include "base/metrics/histogram_macros.h" +#include "base/metrics/sample_vector.h" #include "base/metrics/statistics_recorder.h" +#include "base/stl_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -22,6 +24,11 @@ void RecordDelta(const HistogramBase& histogram, const HistogramSamples& snapshot) override { recorded_delta_histogram_names_.push_back(histogram.histogram_name()); + ASSERT_FALSE(ContainsKey(recorded_delta_histogram_sum_, + histogram.histogram_name())); + // Keep pointer to snapshot for testing. This really isn't ideal but the + // snapshot-manager keeps the snapshot alive until it's "forgotten". + recorded_delta_histogram_sum_[histogram.histogram_name()] = snapshot.sum(); } void InconsistencyDetected(HistogramBase::Inconsistency problem) override { @@ -37,12 +44,23 @@ ASSERT_TRUE(false); } + void Reset() { + recorded_delta_histogram_names_.clear(); + recorded_delta_histogram_sum_.clear(); + } + std::vector<std::string> GetRecordedDeltaHistogramNames() { return recorded_delta_histogram_names_; } + int64_t GetRecordedDeltaHistogramSum(const std::string& name) { + EXPECT_TRUE(ContainsKey(recorded_delta_histogram_sum_, name)); + return recorded_delta_histogram_sum_[name]; + } + private: std::vector<std::string> recorded_delta_histogram_names_; + std::map<std::string, int64_t> recorded_delta_histogram_sum_; DISALLOW_COPY_AND_ASSIGN(HistogramFlattenerDeltaRecorder); }; @@ -61,7 +79,7 @@ TEST_F(HistogramSnapshotManagerTest, PrepareDeltasNoFlagsFilter) { // kNoFlags filter should record all histograms. - UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2); + UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 4); UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); histogram_snapshot_manager_.PrepareDeltas( @@ -77,7 +95,7 @@ TEST_F(HistogramSnapshotManagerTest, PrepareDeltasUmaHistogramFlagFilter) { // Note that kUmaStabilityHistogramFlag includes kUmaTargetedHistogramFlag. - UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2); + UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 4); UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); histogram_snapshot_manager_.PrepareDeltas( @@ -93,7 +111,7 @@ TEST_F(HistogramSnapshotManagerTest, PrepareDeltasUmaStabilityHistogramFlagFilter) { - UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2); + UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 4); UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); histogram_snapshot_manager_.PrepareDeltas( @@ -106,4 +124,35 @@ EXPECT_EQ("UmaStabilityHistogram", histograms[0]); } +TEST_F(HistogramSnapshotManagerTest, CheckMerge) { + UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 4); + UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); + + base::HistogramBase* h1 = base::LinearHistogram::FactoryGet( + "UmaHistogram", 1, 4, 5, 0); + ASSERT_TRUE(h1); + base::HistogramBase* h2 = base::LinearHistogram::FactoryGet( + "UmaStabilityHistogram", 1, 2, 3, 0); + ASSERT_TRUE(h2); + + histogram_snapshot_manager_.StartDeltas(); + histogram_snapshot_manager_.PrepareDelta(h1); + histogram_snapshot_manager_.PrepareDelta(h1); // Delta will be zero. + histogram_snapshot_manager_.PrepareDelta(h2); + h1->Add(2); + h2->Add(1); + histogram_snapshot_manager_.PrepareDelta(h2); + histogram_snapshot_manager_.PrepareDelta(h1); + histogram_snapshot_manager_.FinishDeltas(); + { + const std::vector<std::string> histograms = + histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames(); + EXPECT_EQ(2U, histograms.size()); + EXPECT_EQ(3, histogram_flattener_delta_recorder_. + GetRecordedDeltaHistogramSum("UmaHistogram")); + EXPECT_EQ(2, histogram_flattener_delta_recorder_. + GetRecordedDeltaHistogramSum("UmaStabilityHistogram")); + } +} + } // namespace base
diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc index 2931897..beb6a71 100644 --- a/base/metrics/histogram_unittest.cc +++ b/base/metrics/histogram_unittest.cc
@@ -32,7 +32,7 @@ // heap. class HistogramTest : public testing::TestWithParam<bool> { protected: - const int32_t kAllocatorMemorySize = 4 << 20; // 4 MiB + const int32_t kAllocatorMemorySize = 8 << 20; // 8 MiB HistogramTest() : use_persistent_histogram_allocator_(GetParam()) {} @@ -235,6 +235,35 @@ EXPECT_EQ(2, samples->GetCount(10)); } +// Check that delta calculations work correct. +TEST_P(HistogramTest, DeltaTest) { + HistogramBase* histogram = + Histogram::FactoryGet("DeltaHistogram", 1, 64, 8, + HistogramBase::kNoFlags); + histogram->Add(1); + histogram->Add(10); + histogram->Add(50); + + scoped_ptr<HistogramSamples> samples = histogram->SnapshotDelta(); + EXPECT_EQ(3, samples->TotalCount()); + EXPECT_EQ(1, samples->GetCount(1)); + EXPECT_EQ(1, samples->GetCount(10)); + EXPECT_EQ(1, samples->GetCount(50)); + EXPECT_EQ(samples->TotalCount(), samples->redundant_count()); + + samples = histogram->SnapshotDelta(); + EXPECT_EQ(0, samples->TotalCount()); + + histogram->Add(10); + histogram->Add(10); + samples = histogram->SnapshotDelta(); + EXPECT_EQ(2, samples->TotalCount()); + EXPECT_EQ(2, samples->GetCount(10)); + + samples = histogram->SnapshotDelta(); + EXPECT_EQ(0, samples->TotalCount()); +} + TEST_P(HistogramTest, ExponentialRangesTest) { // Check that we got a nice exponential when there was enough room. BucketRanges ranges(9); @@ -547,7 +576,7 @@ bucket_ranges->set_range(2, bucket_ranges->range(1)); bucket_ranges->set_range(1, tmp); - EXPECT_EQ(0, histogram->FindCorruption(*snapshot)); + EXPECT_EQ(0U, histogram->FindCorruption(*snapshot)); // Show that two simple changes don't offset each other bucket_ranges->set_range(3, bucket_ranges->range(3) + 1);
diff --git a/base/metrics/metrics_hashes.cc b/base/metrics/metrics_hashes.cc index 73bce2e..5672b06d 100644 --- a/base/metrics/metrics_hashes.cc +++ b/base/metrics/metrics_hashes.cc
@@ -22,9 +22,9 @@ } // namespace -uint64_t HashMetricName(const std::string& name) { +uint64_t HashMetricName(base::StringPiece name) { base::MD5Digest digest; - base::MD5Sum(name.c_str(), name.size(), &digest); + base::MD5Sum(name.data(), name.size(), &digest); return DigestToUInt64(digest); }
diff --git a/base/metrics/metrics_hashes.h b/base/metrics/metrics_hashes.h index bd040173..d05c4ba 100644 --- a/base/metrics/metrics_hashes.h +++ b/base/metrics/metrics_hashes.h
@@ -6,15 +6,15 @@ #define BASE_METRICS_METRICS_HASHES_H_ #include <stdint.h> -#include <string> #include "base/base_export.h" +#include "base/strings/string_piece.h" namespace base { // Computes a uint64_t hash of a given string based on its MD5 hash. Suitable // for metric names. -BASE_EXPORT uint64_t HashMetricName(const std::string& name); +BASE_EXPORT uint64_t HashMetricName(base::StringPiece name); } // namespace metrics
diff --git a/base/metrics/sparse_histogram.cc b/base/metrics/sparse_histogram.cc index 5282aa6..5653456 100644 --- a/base/metrics/sparse_histogram.cc +++ b/base/metrics/sparse_histogram.cc
@@ -77,6 +77,17 @@ return std::move(snapshot); } +scoped_ptr<HistogramSamples> SparseHistogram::SnapshotDelta() { + scoped_ptr<SampleMap> snapshot(new SampleMap(name_hash())); + base::AutoLock auto_lock(lock_); + snapshot->Add(samples_); + + // Subtract what was previously logged and update that information. + snapshot->Subtract(logged_samples_); + logged_samples_.Add(*snapshot); + return std::move(snapshot); +} + void SparseHistogram::AddSamples(const HistogramSamples& samples) { base::AutoLock auto_lock(lock_); samples_.Add(samples);
diff --git a/base/metrics/sparse_histogram.h b/base/metrics/sparse_histogram.h index 04909c08..7f36313 100644 --- a/base/metrics/sparse_histogram.h +++ b/base/metrics/sparse_histogram.h
@@ -71,6 +71,7 @@ void AddSamples(const HistogramSamples& samples) override; bool AddSamplesFromPickle(base::PickleIterator* iter) override; scoped_ptr<HistogramSamples> SnapshotSamples() const override; + scoped_ptr<HistogramSamples> SnapshotDelta() override; void WriteHTMLGraph(std::string* output) const override; void WriteAscii(std::string* output) const override; @@ -107,6 +108,7 @@ mutable base::Lock lock_; SampleMap samples_; + SampleMap logged_samples_; DISALLOW_COPY_AND_ASSIGN(SparseHistogram); };
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc index ea3b257..d0fa2ad 100644 --- a/base/metrics/statistics_recorder.cc +++ b/base/metrics/statistics_recorder.cc
@@ -272,7 +272,7 @@ } // static -HistogramBase* StatisticsRecorder::FindHistogram(const std::string& name) { +HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) { if (lock_ == NULL) return NULL; base::AutoLock auto_lock(*lock_);
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h index 04ffa1d..36b2f30 100644 --- a/base/metrics/statistics_recorder.h +++ b/base/metrics/statistics_recorder.h
@@ -100,7 +100,7 @@ // Find a histogram by name. It matches the exact name. This method is thread // safe. It returns NULL if a matching histogram is not found. - static HistogramBase* FindHistogram(const std::string& name); + static HistogramBase* FindHistogram(base::StringPiece name); // Support for iterating over known histograms. static HistogramIterator begin(bool include_persistent);
diff --git a/base/pending_task.cc b/base/pending_task.cc index 3d789141..d21f7c7 100644 --- a/base/pending_task.cc +++ b/base/pending_task.cc
@@ -30,6 +30,8 @@ is_high_res(false) { } +PendingTask::PendingTask(const PendingTask& other) = default; + PendingTask::~PendingTask() { }
diff --git a/base/pending_task.h b/base/pending_task.h index fddfc86..fd0b883 100644 --- a/base/pending_task.h +++ b/base/pending_task.h
@@ -24,6 +24,7 @@ const Closure& task, TimeTicks delayed_run_time, bool nestable); + PendingTask(const PendingTask& other); ~PendingTask(); // Used to support sorting.
diff --git a/base/process/launch.cc b/base/process/launch.cc index f09317dc..3ca5155 100644 --- a/base/process/launch.cc +++ b/base/process/launch.cc
@@ -40,6 +40,8 @@ { } +LaunchOptions::LaunchOptions(const LaunchOptions& other) = default; + LaunchOptions::~LaunchOptions() { }
diff --git a/base/process/launch.h b/base/process/launch.h index 9a76e20..b1811d4 100644 --- a/base/process/launch.h +++ b/base/process/launch.h
@@ -59,6 +59,7 @@ #endif // defined(OS_POSIX) LaunchOptions(); + LaunchOptions(const LaunchOptions&); ~LaunchOptions(); // If true, wait for the process to complete.
diff --git a/base/process/process_iterator.cc b/base/process/process_iterator.cc index 94f53b6..d4024d9 100644 --- a/base/process/process_iterator.cc +++ b/base/process/process_iterator.cc
@@ -9,6 +9,7 @@ #if defined(OS_POSIX) ProcessEntry::ProcessEntry() : pid_(0), ppid_(0), gid_(0) {} +ProcessEntry::ProcessEntry(const ProcessEntry& other) = default; ProcessEntry::~ProcessEntry() {} #endif
diff --git a/base/process/process_iterator.h b/base/process/process_iterator.h index 26fe690..0d1f1a6 100644 --- a/base/process/process_iterator.h +++ b/base/process/process_iterator.h
@@ -41,6 +41,7 @@ #elif defined(OS_POSIX) struct BASE_EXPORT ProcessEntry { ProcessEntry(); + ProcessEntry(const ProcessEntry& other); ~ProcessEntry(); ProcessId pid() const { return pid_; }
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h index 8d8f7fc..0d4d04a 100644 --- a/base/process/process_metrics.h +++ b/base/process/process_metrics.h
@@ -261,6 +261,7 @@ // Linux/Android/Chrome OS. Shmem/slab/gem_objects/gem_size are Chrome OS only. struct BASE_EXPORT SystemMemoryInfoKB { SystemMemoryInfoKB(); + SystemMemoryInfoKB(const SystemMemoryInfoKB& other); // Serializes the platform specific fields to value. scoped_ptr<Value> ToValue() const; @@ -336,6 +337,7 @@ // Data from /proc/diskstats about system-wide disk I/O. struct BASE_EXPORT SystemDiskInfo { SystemDiskInfo(); + SystemDiskInfo(const SystemDiskInfo& other); // Serializes the platform specific fields to value. scoped_ptr<Value> ToValue() const;
diff --git a/base/process/process_metrics_ios.cc b/base/process/process_metrics_ios.cc index 9625c22..8f9806e 100644 --- a/base/process/process_metrics_ios.cc +++ b/base/process/process_metrics_ios.cc
@@ -30,6 +30,9 @@ free = 0; } +SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) = + default; + ProcessMetrics::ProcessMetrics(ProcessHandle process) {} ProcessMetrics::~ProcessMetrics() {}
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc index 7a731bb..c6aff3e8 100644 --- a/base/process/process_metrics_linux.cc +++ b/base/process/process_metrics_linux.cc
@@ -556,6 +556,9 @@ #endif } +SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) = + default; + scoped_ptr<Value> SystemMemoryInfoKB::ToValue() const { scoped_ptr<DictionaryValue> res(new DictionaryValue()); @@ -767,6 +770,8 @@ weighted_io_time = 0; } +SystemDiskInfo::SystemDiskInfo(const SystemDiskInfo& other) = default; + scoped_ptr<Value> SystemDiskInfo::ToValue() const { scoped_ptr<DictionaryValue> res(new DictionaryValue());
diff --git a/base/process/process_metrics_mac.cc b/base/process/process_metrics_mac.cc index a8957e28..b3e8717 100644 --- a/base/process/process_metrics_mac.cc +++ b/base/process/process_metrics_mac.cc
@@ -84,6 +84,9 @@ free = 0; } +SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) = + default; + // Getting a mach task from a pid for another process requires permissions in // general, so there doesn't really seem to be a way to do these (and spinning // up ps to fetch each stats seems dangerous to put in a base api for anyone to
diff --git a/base/process/process_metrics_win.cc b/base/process/process_metrics_win.cc index 290d918f..46e4b8d 100644 --- a/base/process/process_metrics_win.cc +++ b/base/process/process_metrics_win.cc
@@ -36,6 +36,9 @@ swap_free = 0; } +SystemMemoryInfoKB::SystemMemoryInfoKB(const SystemMemoryInfoKB& other) = + default; + ProcessMetrics::~ProcessMetrics() { } // static
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc index e89d0527..0749ae5 100644 --- a/base/profiler/stack_sampling_profiler.cc +++ b/base/profiler/stack_sampling_profiler.cc
@@ -107,6 +107,9 @@ StackSamplingProfiler::CallStackProfile::CallStackProfile() {} +StackSamplingProfiler::CallStackProfile::CallStackProfile( + const CallStackProfile& other) = default; + StackSamplingProfiler::CallStackProfile::~CallStackProfile() {} // StackSamplingProfiler::SamplingThread --------------------------------------
diff --git a/base/profiler/stack_sampling_profiler.h b/base/profiler/stack_sampling_profiler.h index ae252bb..e972d57 100644 --- a/base/profiler/stack_sampling_profiler.h +++ b/base/profiler/stack_sampling_profiler.h
@@ -113,6 +113,7 @@ // CallStackProfile represents a set of samples. struct BASE_EXPORT CallStackProfile { CallStackProfile(); + CallStackProfile(const CallStackProfile& other); ~CallStackProfile(); std::vector<Module> modules;
diff --git a/base/time/time_mac.cc b/base/time/time_mac.cc index 285e95a..f0c78044 100644 --- a/base/time/time_mac.cc +++ b/base/time/time_mac.cc
@@ -181,8 +181,6 @@ gregorian, &absolute_time, "yMdHmsS", exploded.year, exploded.month, exploded.day_of_month, exploded.hour, exploded.minute, exploded.second, exploded.millisecond); - // Milliseconds from |exploded| is added back to |absolute_time| here - // because CFCalendar parameters are integer values. CFAbsoluteTime seconds = absolute_time + kCFAbsoluteTimeIntervalSince1970; return Time(static_cast<int64_t>(seconds * kMicrosecondsPerSecond) + kWindowsEpochDeltaMicroseconds);
diff --git a/base/trace_event/heap_profiler_stack_frame_deduplicator.cc b/base/trace_event/heap_profiler_stack_frame_deduplicator.cc index cf3d1987..5e11e9d 100644 --- a/base/trace_event/heap_profiler_stack_frame_deduplicator.cc +++ b/base/trace_event/heap_profiler_stack_frame_deduplicator.cc
@@ -19,6 +19,7 @@ StackFrameDeduplicator::FrameNode::FrameNode(StackFrame frame, int parent_frame_index) : frame(frame), parent_frame_index(parent_frame_index) {} +StackFrameDeduplicator::FrameNode::FrameNode(const FrameNode& other) = default; StackFrameDeduplicator::FrameNode::~FrameNode() {} StackFrameDeduplicator::StackFrameDeduplicator() {}
diff --git a/base/trace_event/heap_profiler_stack_frame_deduplicator.h b/base/trace_event/heap_profiler_stack_frame_deduplicator.h index 60df1ba..e8c693a 100644 --- a/base/trace_event/heap_profiler_stack_frame_deduplicator.h +++ b/base/trace_event/heap_profiler_stack_frame_deduplicator.h
@@ -31,6 +31,7 @@ // A node in the call tree. struct FrameNode { FrameNode(StackFrame frame, int parent_frame_index); + FrameNode(const FrameNode& other); ~FrameNode(); StackFrame frame;
diff --git a/base/trace_event/process_memory_maps.cc b/base/trace_event/process_memory_maps.cc index 0eb140c..a121239 100644 --- a/base/trace_event/process_memory_maps.cc +++ b/base/trace_event/process_memory_maps.cc
@@ -29,6 +29,8 @@ byte_stats_proportional_resident(0) { } +ProcessMemoryMaps::VMRegion::VMRegion(const VMRegion& other) = default; + ProcessMemoryMaps::ProcessMemoryMaps() { }
diff --git a/base/trace_event/process_memory_maps.h b/base/trace_event/process_memory_maps.h index 2c8191b..6a73674 100644 --- a/base/trace_event/process_memory_maps.h +++ b/base/trace_event/process_memory_maps.h
@@ -28,6 +28,7 @@ static const uint32_t kProtectionFlagsMayshare; VMRegion(); + VMRegion(const VMRegion& other); uint64_t start_address; uint64_t size_in_bytes;
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index 229cb90..543e436b 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc
@@ -951,6 +951,9 @@ ProcessDataPhaseSnapshot::ProcessDataPhaseSnapshot() { } +ProcessDataPhaseSnapshot::ProcessDataPhaseSnapshot( + const ProcessDataPhaseSnapshot& other) = default; + ProcessDataPhaseSnapshot::~ProcessDataPhaseSnapshot() { }
diff --git a/base/tracked_objects.h b/base/tracked_objects.h index 518805a..4553c0a 100644 --- a/base/tracked_objects.h +++ b/base/tracked_objects.h
@@ -795,6 +795,7 @@ struct BASE_EXPORT ProcessDataPhaseSnapshot { public: ProcessDataPhaseSnapshot(); + ProcessDataPhaseSnapshot(const ProcessDataPhaseSnapshot& other); ~ProcessDataPhaseSnapshot(); std::vector<TaskSnapshot> tasks;
diff --git a/blimp/BUILD.gn b/blimp/BUILD.gn index f8ee944..64345a07 100644 --- a/blimp/BUILD.gn +++ b/blimp/BUILD.gn
@@ -50,6 +50,7 @@ test("blimp_unittests") { deps = [ "//blimp/client:app_unit_tests", + "//blimp/client:blimp_client_unit_tests", "//blimp/client:feature_unit_tests", "//blimp/common:unit_tests", "//blimp/net:unit_tests",
diff --git a/blimp/client/BUILD.gn b/blimp/client/BUILD.gn index d629ef8..410ba5b0a 100644 --- a/blimp/client/BUILD.gn +++ b/blimp/client/BUILD.gn
@@ -44,6 +44,27 @@ ] } +source_set("blimp_client_unit_tests") { + testonly = true + + sources = [] + + # TODO(dtrainor): Fix the test harness to allow this to run on Android. + # See crbug.com/588240. + if (is_linux) { + sources += [ "session/assignment_source_unittest.cc" ] + } + + deps = [ + ":blimp_client", + "//base", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +} + source_set("app_unit_tests") { testonly = true @@ -258,7 +279,15 @@ ] } + java_cpp_enum("blimp_java_enums_srcjar") { + sources = [ + "session/assignment_source.h", + ] + } + android_library("blimp_java") { + srcjar_deps = [ ":blimp_java_enums_srcjar" ] + deps = [ ":blimp_java_resources", "//base:base_java", @@ -342,6 +371,7 @@ ":blimp_java", ":blimp_java_resources", "//base:base_java", + "//net/android:net_java", google_play_services_resources, ] apk_name = "Blimp"
diff --git a/blimp/client/DEPS b/blimp/client/DEPS index 06e3f96..62e48705 100644 --- a/blimp/client/DEPS +++ b/blimp/client/DEPS
@@ -6,7 +6,7 @@ "-content", "+gpu", "+jni", - "+net/base", + "+net", "+skia", "+third_party/libwebp", "+third_party/skia",
diff --git a/blimp/client/app/android/blimp_client_session_android.cc b/blimp/client/app/android/blimp_client_session_android.cc index 97b8372c..2ab42c5f 100644 --- a/blimp/client/app/android/blimp_client_session_android.cc +++ b/blimp/client/app/android/blimp_client_session_android.cc
@@ -4,6 +4,8 @@ #include "blimp/client/app/android/blimp_client_session_android.h" +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" #include "base/thread_task_runner_handle.h" #include "blimp/client/feature/tab_control_feature.h" #include "blimp/client/session/assignment_source.h" @@ -16,10 +18,7 @@ } // namespace static jlong Init(JNIEnv* env, const JavaParamRef<jobject>& jobj) { - scoped_ptr<AssignmentSource> assignment_source = make_scoped_ptr( - new AssignmentSource(base::ThreadTaskRunnerHandle::Get())); - return reinterpret_cast<intptr_t>( - new BlimpClientSessionAndroid(env, jobj, std::move(assignment_source))); + return reinterpret_cast<intptr_t>(new BlimpClientSessionAndroid(env, jobj)); } // static @@ -37,9 +36,7 @@ BlimpClientSessionAndroid::BlimpClientSessionAndroid( JNIEnv* env, - const base::android::JavaParamRef<jobject>& jobj, - scoped_ptr<AssignmentSource> assignment_source) - : BlimpClientSession(std::move(assignment_source)) { + const base::android::JavaParamRef<jobject>& jobj) { java_obj_.Reset(env, jobj); // Create a single tab's WebContents. @@ -49,8 +46,15 @@ void BlimpClientSessionAndroid::Connect( JNIEnv* env, - const base::android::JavaParamRef<jobject>& jobj) { - BlimpClientSession::Connect(); + const base::android::JavaParamRef<jobject>& jobj, + const base::android::JavaParamRef<jstring>& jclient_auth_token) { + std::string client_auth_token; + if (jclient_auth_token.obj()) { + client_auth_token = + base::android::ConvertJavaStringToUTF8(env, jclient_auth_token); + } + + BlimpClientSession::Connect(client_auth_token); } BlimpClientSessionAndroid::~BlimpClientSessionAndroid() {} @@ -60,5 +64,15 @@ delete this; } +void BlimpClientSessionAndroid::OnAssignmentConnectionAttempted( + AssignmentSource::Result result) { + // Notify the front end of the assignment result. + JNIEnv* env = base::android::AttachCurrentThread(); + Java_BlimpClientSession_onAssignmentReceived(env, java_obj_.obj(), + static_cast<jint>(result)); + + BlimpClientSession::OnAssignmentConnectionAttempted(result); +} + } // namespace client } // namespace blimp
diff --git a/blimp/client/app/android/blimp_client_session_android.h b/blimp/client/app/android/blimp_client_session_android.h index 7ba6d9f6..226188d6 100644 --- a/blimp/client/app/android/blimp_client_session_android.h +++ b/blimp/client/app/android/blimp_client_session_android.h
@@ -20,14 +20,21 @@ static BlimpClientSessionAndroid* FromJavaObject(JNIEnv* env, jobject jobj); BlimpClientSessionAndroid(JNIEnv* env, - const base::android::JavaParamRef<jobject>& jobj, - scoped_ptr<AssignmentSource> assignment_source); + const base::android::JavaParamRef<jobject>& jobj); // Methods called from Java via JNI. - void Connect(JNIEnv* env, const base::android::JavaParamRef<jobject>& jobj); + // |jclient_auth_token| is an OAuth2 access token created by GoogleAuthUtil. + // See BlimpClientSession::Connect() for more information. + void Connect(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jobj, + const base::android::JavaParamRef<jstring>& jclient_auth_token); void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& jobj); + // BlimpClientSession overrides. + void OnAssignmentConnectionAttempted( + AssignmentSource::Result result) override; + private: ~BlimpClientSessionAndroid() override;
diff --git a/blimp/client/app/android/blimp_library_loader.cc b/blimp/client/app/android/blimp_library_loader.cc index e6a009f51..bdb8c730 100644 --- a/blimp/client/app/android/blimp_library_loader.cc +++ b/blimp/client/app/android/blimp_library_loader.cc
@@ -15,6 +15,7 @@ #include "blimp/client/app/android/blimp_jni_registrar.h" #include "blimp/client/app/blimp_startup.h" #include "jni/BlimpLibraryLoader_jni.h" +#include "net/android/net_jni_registrar.h" #include "ui/gl/gl_surface.h" namespace { @@ -33,6 +34,9 @@ if (!base::android::RegisterJni(env)) return false; + if (!net::android::RegisterJni(env)) + return false; + if (!blimp::client::RegisterBlimpJni(env)) return false;
diff --git a/blimp/client/app/android/java/src/org/chromium/blimp/BlimpApplication.java b/blimp/client/app/android/java/src/org/chromium/blimp/BlimpApplication.java index 0fb67b9c..17fd5fd 100644 --- a/blimp/client/app/android/java/src/org/chromium/blimp/BlimpApplication.java +++ b/blimp/client/app/android/java/src/org/chromium/blimp/BlimpApplication.java
@@ -22,7 +22,7 @@ @Override public void onCreate() { super.onCreate(); - ResourceExtractor.setMandatoryPaksToExtract(MANDATORY_PAK_FILES); + ResourceExtractor.setResourcesToExtract(new ResourceExtractor.ResourceEntry[0]); PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, this); initCommandLine(); }
diff --git a/blimp/client/app/android/java/src/org/chromium/blimp/BlimpLibraryLoader.java b/blimp/client/app/android/java/src/org/chromium/blimp/BlimpLibraryLoader.java index f4b05ee..3463887 100644 --- a/blimp/client/app/android/java/src/org/chromium/blimp/BlimpLibraryLoader.java +++ b/blimp/client/app/android/java/src/org/chromium/blimp/BlimpLibraryLoader.java
@@ -97,7 +97,7 @@ public void run() { // Only run nativeStartBlimp if we properly initialized native. boolean startResult = nativeStartBlimp(); - sLibraryLoadResult = new Boolean(startResult); + sLibraryLoadResult = Boolean.valueOf(startResult); // Notify any oustanding callers to #startAsync(). notifyCallbacksAndClear();
diff --git a/blimp/client/app/android/java/src/org/chromium/blimp/BlimpRendererActivity.java b/blimp/client/app/android/java/src/org/chromium/blimp/BlimpRendererActivity.java index 23d52e68..35f8e19 100644 --- a/blimp/client/app/android/java/src/org/chromium/blimp/BlimpRendererActivity.java +++ b/blimp/client/app/android/java/src/org/chromium/blimp/BlimpRendererActivity.java
@@ -22,12 +22,16 @@ * The {@link Activity} for rendering the main Blimp client. This loads the Blimp rendering stack * and displays it. */ -public class BlimpRendererActivity extends Activity implements BlimpLibraryLoader.Callback, - TokenSource.Callback { - +public class BlimpRendererActivity extends Activity + implements BlimpLibraryLoader.Callback, TokenSource.Callback, BlimpClientSession.Callback { private static final int ACCOUNT_CHOOSER_INTENT_REQUEST_CODE = 100; private static final String TAG = "Blimp"; + + /** Provides user authentication tokens that can be used to query for engine assignments. This + * can potentially query GoogleAuthUtil for an OAuth2 authentication token with userinfo.email + * privileges for a chosen Android account. */ private TokenSource mTokenSource; + private BlimpView mBlimpView; private Toolbar mToolbar; private BlimpClientSession mBlimpClientSession; @@ -37,6 +41,12 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // Build a TokenSource that will internally retry accessing the underlying TokenSourceImpl. + // This will exponentially backoff while it tries to get the access token. See + // {@link RetryingTokenSource} for more information. The underlying + // TokenSourceImpl will attempt to query GoogleAuthUtil, but might fail if there is no + // account selected, in which case it will ask this Activity to show an account chooser and + // notify it of the selection result. mTokenSource = new RetryingTokenSource(new TokenSourceImpl(this)); mTokenSource.setCallback(this); mTokenSource.getToken(); @@ -115,8 +125,7 @@ setContentView(R.layout.blimp_main); - mBlimpClientSession = new BlimpClientSession(); - mBlimpClientSession.connect(); + mBlimpClientSession = new BlimpClientSession(this); mBlimpView = (BlimpView) findViewById(R.id.renderer); mBlimpView.initializeRenderer(mBlimpClientSession); @@ -131,8 +140,7 @@ // TokenSource.Callback implementation. @Override public void onTokenReceived(String token) { - // TODO(dtrainor): Do something with the token and the assigner! - Toast.makeText(this, R.string.signin_get_token_succeeded, Toast.LENGTH_SHORT).show(); + if (mBlimpClientSession != null) mBlimpClientSession.connect(token); } @Override @@ -140,11 +148,16 @@ // Ignore isTransient here because we're relying on the auto-retry TokenSource. // TODO(dtrainor): Show a better error dialog/message. Toast.makeText(this, R.string.signin_get_token_failed, Toast.LENGTH_LONG).show(); - finish(); } @Override public void onNeedsAccountToBeSelected(Intent suggestedIntent) { startActivityForResult(suggestedIntent, ACCOUNT_CHOOSER_INTENT_REQUEST_CODE); } + + // BlimpClientSession.Callback implementation. + @Override + public void onAssignmentReceived(int result, int suggestedMessageResourceId) { + Toast.makeText(this, suggestedMessageResourceId, Toast.LENGTH_LONG).show(); + } }
diff --git a/blimp/client/app/android/java/src/org/chromium/blimp/session/BlimpClientSession.java b/blimp/client/app/android/java/src/org/chromium/blimp/session/BlimpClientSession.java index b3012d8..567c623 100644 --- a/blimp/client/app/android/java/src/org/chromium/blimp/session/BlimpClientSession.java +++ b/blimp/client/app/android/java/src/org/chromium/blimp/session/BlimpClientSession.java
@@ -6,6 +6,8 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.blimp.R; +import org.chromium.blimp.assignment.Result; /** * The Java representation of a native BlimpClientSession. This is primarily used to provide access @@ -14,17 +16,35 @@ */ @JNINamespace("blimp::client") public class BlimpClientSession { + /** + * A callback for when the session needs to notify the UI about the state of the Blimp session. + */ + public interface Callback { + /** + * Called when an engine assignment has been successful or failed. + * @param result The result code of the assignment. See + * assignment_source.h for details. Maps to a value in + * {@link Result}. + * @param suggestedMessageResourceId A suggested resource id for a string to display to the + * user if necessary. + */ + void onAssignmentReceived(int result, int suggestedMessageResourceId); + } + + private final Callback mCallback; private long mNativeBlimpClientSessionAndroidPtr; - public BlimpClientSession() { + public BlimpClientSession(Callback callback) { + mCallback = callback; mNativeBlimpClientSessionAndroidPtr = nativeInit(); } /** * Retrieves an assignment and uses it to connect to the engine. + * @param token A OAuth2 access token for the account requesting access. */ - public void connect() { - nativeConnect(mNativeBlimpClientSessionAndroidPtr); + public void connect(String token) { + nativeConnect(mNativeBlimpClientSessionAndroidPtr, token); } /** @@ -39,12 +59,56 @@ // Methods that are called by native via JNI. @CalledByNative + private void onAssignmentReceived(int result) { + if (mCallback == null) return; + + int resultMessageResourceId = R.string.assignment_failure_unknown; + switch (result) { + case Result.OK: + resultMessageResourceId = R.string.assignment_success; + break; + case Result.BAD_REQUEST: + resultMessageResourceId = R.string.assignment_failure_bad_request; + break; + case Result.BAD_RESPONSE: + resultMessageResourceId = R.string.assignment_failure_bad_response; + break; + case Result.INVALID_PROTOCOL_VERSION: + resultMessageResourceId = R.string.assignment_failure_bad_version; + break; + case Result.EXPIRED_ACCESS_TOKEN: + resultMessageResourceId = R.string.assignment_failure_expired_token; + break; + case Result.USER_INVALID: + resultMessageResourceId = R.string.assignment_failure_user_invalid; + break; + case Result.OUT_OF_VMS: + resultMessageResourceId = R.string.assignment_failure_out_of_vms; + break; + case Result.SERVER_ERROR: + resultMessageResourceId = R.string.assignment_failure_server_error; + break; + case Result.SERVER_INTERRUPTED: + resultMessageResourceId = R.string.assignment_failure_server_interrupted; + break; + case Result.NETWORK_FAILURE: + resultMessageResourceId = R.string.assignment_failure_network; + break; + case Result.UNKNOWN: + default: + resultMessageResourceId = R.string.assignment_failure_unknown; + break; + } + mCallback.onAssignmentReceived(result, resultMessageResourceId); + } + + @CalledByNative private long getNativePtr() { assert mNativeBlimpClientSessionAndroidPtr != 0; return mNativeBlimpClientSessionAndroidPtr; } private native long nativeInit(); - private native void nativeConnect(long nativeBlimpClientSessionAndroid); + private native void nativeConnect(long nativeBlimpClientSessionAndroid, String token); private native void nativeDestroy(long nativeBlimpClientSessionAndroid); }
diff --git a/blimp/client/app/android/java/strings/android_blimp_strings.grd b/blimp/client/app/android/java/strings/android_blimp_strings.grd index c245fe44..6b4a42c5 100644 --- a/blimp/client/app/android/java/strings/android_blimp_strings.grd +++ b/blimp/client/app/android/java/strings/android_blimp_strings.grd
@@ -103,6 +103,39 @@ <message name="IDS_SIGNIN_CHOOSER_DESCRIPTION" desc="Message shown with the Android signin chooser screen."> Please choose an account to use with Blimp. </message> + <message name="IDS_ASSIGNMENT_FAILURE_UNKNOWN" desc="Message for when getting an assignment has failed due to an unknown issue."> + Assignment failed. Unknown issue. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_BAD_REQUEST" desc="Message for when getting an assignment has failed due to a bad request."> + Assignment failed. 400 (BAD_REQUEST) error. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_BAD_RESPONSE" desc="Message for when getting an assignment has failed due to a bad response."> + Assignment failed. Invalid response. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_BAD_VERSION" desc="Message for when getting an assignment has failed due to an out of date client."> + Assignment failed. No matching engine version. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_EXPIRED_TOKEN" desc="Message for when getting an assignment has failed due to an expired user token."> + Assignment failed. Expired user access token. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_USER_INVALID" desc="Message for when getting an assignment has failed due to an invalid user."> + Assignment failed. Account does not have access. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_OUT_OF_VMS" desc="Message for when getting an assignment has failed due to a shortage of engines."> + Assignment failed. No engine available. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_SERVER_ERROR" desc="Message for when getting an assignment has failed due to a server error."> + Assignment failed. Server error. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_SERVER_INTERRUPTED" desc="Message for when getting an assignment has failed due to another request."> + Assignment failed. Second request sent before first request responded. + </message> + <message name="IDS_ASSIGNMENT_FAILURE_NETWORK" desc="Message for when getting an assignment has failed due to a general network issue."> + Assignment failed. Network request failed. + </message> + <message name="IDS_ASSIGNMENT_SUCCESS" desc="Message for when getting an assignment succeeds."> + Assignment succeeded. + </message> </messages> </release> </grit> \ No newline at end of file
diff --git a/blimp/client/app/blimp_client_switches.cc b/blimp/client/app/blimp_client_switches.cc index 445ebe9..cfcf2a00 100644 --- a/blimp/client/app/blimp_client_switches.cc +++ b/blimp/client/app/blimp_client_switches.cc
@@ -7,14 +7,11 @@ namespace blimp { namespace switches { -// Specifies the blimplet IP-address to connect to, e.g.: -// --blimplet-host="127.0.0.1". +// Specifies the blimplet scheme, IP-address and port to connect to, e.g.: +// --blimplet-host="tcp:127.0.0.1:25467". Valid schemes are "ssl", +// "tcp", and "quic". // TODO(nyquist): Add support for DNS-lookup. See http://crbug.com/576857. -const char kBlimpletHost[] = "blimplet-host"; - -// Specifies the blimplet port to connect to, e.g.: -// --blimplet-tcp-port=25467. -const char kBlimpletTCPPort[] = "blimplet-tcp-port"; +const char kBlimpletEndpoint[] = "blimplet-endpoint"; } // namespace switches } // namespace blimp
diff --git a/blimp/client/app/blimp_client_switches.h b/blimp/client/app/blimp_client_switches.h index 88167f1..19b91ddd 100644 --- a/blimp/client/app/blimp_client_switches.h +++ b/blimp/client/app/blimp_client_switches.h
@@ -10,8 +10,7 @@ namespace blimp { namespace switches { -extern const char kBlimpletHost[]; -extern const char kBlimpletTCPPort[]; +extern const char kBlimpletEndpoint[]; } // namespace switches } // namespace blimp
diff --git a/blimp/client/app/linux/blimp_client_session_linux.cc b/blimp/client/app/linux/blimp_client_session_linux.cc index d4491be8..84317ca 100644 --- a/blimp/client/app/linux/blimp_client_session_linux.cc +++ b/blimp/client/app/linux/blimp_client_session_linux.cc
@@ -59,10 +59,8 @@ } // namespace -BlimpClientSessionLinux::BlimpClientSessionLinux( - scoped_ptr<AssignmentSource> assignment_source) - : BlimpClientSession(std::move(assignment_source)), - event_source_(ui::PlatformEventSource::CreateDefault()), +BlimpClientSessionLinux::BlimpClientSessionLinux() + : event_source_(ui::PlatformEventSource::CreateDefault()), navigation_feature_delegate_(new FakeNavigationFeatureDelegate) { blimp_display_manager_.reset(new BlimpDisplayManager(gfx::Size(800, 600), this,
diff --git a/blimp/client/app/linux/blimp_client_session_linux.h b/blimp/client/app/linux/blimp_client_session_linux.h index 119feba..1a60b0d 100644 --- a/blimp/client/app/linux/blimp_client_session_linux.h +++ b/blimp/client/app/linux/blimp_client_session_linux.h
@@ -20,8 +20,7 @@ class BlimpClientSessionLinux : public BlimpClientSession, public BlimpDisplayManagerDelegate { public: - explicit BlimpClientSessionLinux( - scoped_ptr<AssignmentSource> assignment_source); + BlimpClientSessionLinux(); ~BlimpClientSessionLinux() override; // BlimpDisplayManagerDelegate implementation.
diff --git a/blimp/client/app/linux/blimp_main.cc b/blimp/client/app/linux/blimp_main.cc index 332a7930..3eedffe 100644 --- a/blimp/client/app/linux/blimp_main.cc +++ b/blimp/client/app/linux/blimp_main.cc
@@ -18,6 +18,7 @@ #include "blimp/client/session/assignment_source.h" namespace { +const char kDummyLoginToken[] = ""; const char kDefaultUrl[] = "https://www.google.com"; const int kDummyTabId = 0; } @@ -31,12 +32,9 @@ blimp::client::InitializeLogging(); blimp::client::InitializeMainMessageLoop(); - scoped_ptr<blimp::client::AssignmentSource> assignment_source = - make_scoped_ptr(new blimp::client::AssignmentSource( - base::ThreadTaskRunnerHandle::Get())); - blimp::client::BlimpClientSessionLinux session(std::move(assignment_source)); + blimp::client::BlimpClientSessionLinux session; session.GetTabControlFeature()->CreateTab(kDummyTabId); - session.Connect(); + session.Connect(kDummyLoginToken); // If there is a non-switch argument to the command line, load that url. base::CommandLine::StringVector args =
diff --git a/blimp/client/feature/compositor/blimp_input_handler_wrapper.cc b/blimp/client/feature/compositor/blimp_input_handler_wrapper.cc index 4716b58c..5e38f33a 100644 --- a/blimp/client/feature/compositor/blimp_input_handler_wrapper.cc +++ b/blimp/client/feature/compositor/blimp_input_handler_wrapper.cc
@@ -51,6 +51,7 @@ case ui::InputHandlerProxy::EventDisposition::DROP_EVENT: consumed = true; break; + case ui::InputHandlerProxy::EventDisposition::DID_HANDLE_NON_BLOCKING: case ui::InputHandlerProxy::EventDisposition::DID_NOT_HANDLE: consumed = false; break;
diff --git a/blimp/client/feature/compositor/blimp_layer_tree_settings.cc b/blimp/client/feature/compositor/blimp_layer_tree_settings.cc index dea0793..c78604c 100644 --- a/blimp/client/feature/compositor/blimp_layer_tree_settings.cc +++ b/blimp/client/feature/compositor/blimp_layer_tree_settings.cc
@@ -42,7 +42,6 @@ #endif settings->enable_elastic_overscroll = false; settings->image_decode_tasks_enabled = false; - settings->verify_property_trees = false; settings->single_thread_proxy_scheduler = false; settings->initial_debug_state.show_debug_borders = false; settings->initial_debug_state.show_fps_counter = false;
diff --git a/blimp/client/session/assignment_source.cc b/blimp/client/session/assignment_source.cc index a83c66f4..242d7839 100644 --- a/blimp/client/session/assignment_source.cc +++ b/blimp/client/session/assignment_source.cc
@@ -5,71 +5,308 @@ #include "blimp/client/session/assignment_source.h" #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/command_line.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/location.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" +#include "base/values.h" #include "blimp/client/app/blimp_client_switches.h" +#include "blimp/common/protocol_version.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/base/url_util.h" +#include "net/http/http_status_code.h" +#include "net/proxy/proxy_config_service.h" +#include "net/proxy/proxy_service.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_builder.h" +#include "net/url_request/url_request_context_getter.h" namespace blimp { +namespace client { + namespace { -// TODO(kmarshall): Take values from configuration data. -const char kDummyClientToken[] = "MyVoiceIsMyPassport"; -const std::string kDefaultBlimpletIPAddress = "127.0.0.1"; -const uint16_t kDefaultBlimpletTCPPort = 25467; +// Assignment request JSON keys. +const char kProtocolVersionKey[] = "protocol_version"; -net::IPAddress GetBlimpletIPAddress() { +// Assignment response JSON keys. +const char kClientTokenKey[] = "clientToken"; +const char kHostKey[] = "host"; +const char kPortKey[] = "port"; +const char kCertificateFingerprintKey[] = "certificateFingerprint"; +const char kCertificateKey[] = "certificate"; + +// URL scheme constants for custom assignments. See the '--blimplet-endpoint' +// documentation in blimp_client_switches.cc for details. +const char kCustomSSLScheme[] = "ssl"; +const char kCustomTCPScheme[] = "tcp"; +const char kCustomQUICScheme[] = "quic"; + +Assignment GetCustomBlimpletAssignment() { + GURL url(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kBlimpletEndpoint)); + std::string host; - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kBlimpletHost)) { - host = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kBlimpletHost); - } else { - host = kDefaultBlimpletIPAddress; + int port; + if (url.is_empty() || !url.is_valid() || !url.has_scheme() || + !net::ParseHostAndPort(url.path(), &host, &port)) { + return Assignment(); } + net::IPAddress ip_address; - if (!ip_address.AssignFromIPLiteral(host)) + if (!ip_address.AssignFromIPLiteral(host)) { CHECK(false) << "Invalid BlimpletAssignment host " << host; - return ip_address; + } + + if (!base::IsValueInRangeForNumericType<uint16_t>(port)) { + CHECK(false) << "Invalid BlimpletAssignment port " << port; + } + + Assignment::TransportProtocol protocol = + Assignment::TransportProtocol::UNKNOWN; + if (url.has_scheme()) { + if (url.SchemeIs(kCustomSSLScheme)) { + protocol = Assignment::TransportProtocol::SSL; + } else if (url.SchemeIs(kCustomTCPScheme)) { + protocol = Assignment::TransportProtocol::TCP; + } else if (url.SchemeIs(kCustomQUICScheme)) { + protocol = Assignment::TransportProtocol::QUIC; + } else { + CHECK(false) << "Invalid BlimpletAssignment scheme " << url.scheme(); + } + } + + Assignment assignment; + assignment.transport_protocol = protocol; + assignment.ip_endpoint = net::IPEndPoint(ip_address, port); + assignment.client_token = kDummyClientToken; + return assignment; } -uint16_t GetBlimpletTCPPort() { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kBlimpletTCPPort)) { - std::string port_str = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kBlimpletTCPPort); - uint port_64t; - if (!base::StringToUint(port_str, &port_64t) || - !base::IsValueInRangeForNumericType<uint16_t>(port_64t)) { - CHECK(false) << "Invalid BlimpletAssignment port " << port_str; - } - return base::checked_cast<uint16_t>(port_64t); - } else { - return kDefaultBlimpletTCPPort; - } +GURL GetBlimpAssignerURL() { + // TODO(dtrainor): Add a way to specify another assigner. + return GURL(kDefaultAssignerURL); } +class SimpleURLRequestContextGetter : public net::URLRequestContextGetter { + public: + SimpleURLRequestContextGetter( + const scoped_refptr<base::SingleThreadTaskRunner>& io_loop_task_runner) + : io_loop_task_runner_(io_loop_task_runner), + proxy_config_service_(net::ProxyService::CreateSystemProxyConfigService( + io_loop_task_runner_, + io_loop_task_runner_)) {} + + // net::URLRequestContextGetter implementation. + net::URLRequestContext* GetURLRequestContext() override { + if (!url_request_context_) { + net::URLRequestContextBuilder builder; + builder.set_proxy_config_service(std::move(proxy_config_service_)); + builder.DisableHttpCache(); + url_request_context_ = builder.Build(); + } + + return url_request_context_.get(); + } + + scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() + const override { + return io_loop_task_runner_; + } + + private: + ~SimpleURLRequestContextGetter() override {} + + scoped_refptr<base::SingleThreadTaskRunner> io_loop_task_runner_; + scoped_ptr<net::URLRequestContext> url_request_context_; + + // Temporary storage for the ProxyConfigService, which needs to be created on + // the main thread but cleared on the IO thread. This will be built in the + // constructor and cleared on the IO thread. Due to the usage of this class + // this is safe. + scoped_ptr<net::ProxyConfigService> proxy_config_service_; + + DISALLOW_COPY_AND_ASSIGN(SimpleURLRequestContextGetter); +}; + } // namespace -namespace client { +Assignment::Assignment() : transport_protocol(TransportProtocol::UNKNOWN) {} + +Assignment::~Assignment() {} + +bool Assignment::is_null() const { + return ip_endpoint.address().empty() || ip_endpoint.port() == 0 || + transport_protocol == TransportProtocol::UNKNOWN; +} AssignmentSource::AssignmentSource( - const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner) - : main_task_runner_(main_task_runner) {} + const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner, + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) + : main_task_runner_(main_task_runner), + url_request_context_(new SimpleURLRequestContextGetter(io_task_runner)) {} AssignmentSource::~AssignmentSource() {} -void AssignmentSource::GetAssignment(const AssignmentCallback& callback) { +void AssignmentSource::GetAssignment(const std::string& client_auth_token, + const AssignmentCallback& callback) { DCHECK(main_task_runner_->BelongsToCurrentThread()); + + // Cancel any outstanding callback. + if (!callback_.is_null()) { + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_SERVER_INTERRUPTED, Assignment()); + } + callback_ = AssignmentCallback(callback); + + Assignment assignment = GetCustomBlimpletAssignment(); + if (!assignment.is_null()) { + // Post the result so that the behavior of this function is consistent. + main_task_runner_->PostTask( + FROM_HERE, base::Bind(base::ResetAndReturn(&callback_), + AssignmentSource::Result::RESULT_OK, assignment)); + return; + } + + // Call out to the network for a real assignment. Build the network request + // to hit the assigner. + url_fetcher_ = net::URLFetcher::Create(GetBlimpAssignerURL(), + net::URLFetcher::POST, this); + url_fetcher_->SetRequestContext(url_request_context_.get()); + url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DO_NOT_SEND_COOKIES); + url_fetcher_->AddExtraRequestHeader("Authorization: Bearer " + + client_auth_token); + + // Write the JSON for the request data. + base::DictionaryValue dictionary; + dictionary.SetString(kProtocolVersionKey, blimp::kEngineVersion); + std::string json; + base::JSONWriter::Write(dictionary, &json); + url_fetcher_->SetUploadData("application/json", json); + + url_fetcher_->Start(); +} + +void AssignmentSource::OnURLFetchComplete(const net::URLFetcher* source) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + DCHECK(!callback_.is_null()); + DCHECK_EQ(url_fetcher_.get(), source); + + if (!source->GetStatus().is_success()) { + DVLOG(1) << "Assignment request failed due to network error: " + << net::ErrorToString(source->GetStatus().error()); + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_NETWORK_FAILURE, Assignment()); + return; + } + + switch (source->GetResponseCode()) { + case net::HTTP_OK: + ParseAssignerResponse(); + break; + case net::HTTP_BAD_REQUEST: + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_BAD_REQUEST, Assignment()); + break; + case net::HTTP_UNAUTHORIZED: + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_EXPIRED_ACCESS_TOKEN, + Assignment()); + break; + case net::HTTP_FORBIDDEN: + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_USER_INVALID, Assignment()); + break; + case 429: // Too Many Requests + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_OUT_OF_VMS, Assignment()); + break; + case net::HTTP_INTERNAL_SERVER_ERROR: + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_SERVER_ERROR, Assignment()); + break; + default: + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment()); + break; + } +} + +void AssignmentSource::ParseAssignerResponse() { + DCHECK(url_fetcher_.get()); + DCHECK(url_fetcher_->GetStatus().is_success()); + DCHECK_EQ(net::HTTP_OK, url_fetcher_->GetResponseCode()); + + // Grab the response from the assigner request. + std::string response; + if (!url_fetcher_->GetResponseAsString(&response)) { + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment()); + return; + } + + // Attempt to interpret the response as JSON and treat it as a dictionary. + scoped_ptr<base::Value> json = base::JSONReader::Read(response); + if (!json) { + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment()); + return; + } + + const base::DictionaryValue* dict; + if (!json->GetAsDictionary(&dict)) { + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment()); + return; + } + + // Validate that all the expected fields are present. + std::string client_token; + std::string host; + int port; + std::string cert_fingerprint; + std::string cert; + if (!(dict->GetString(kClientTokenKey, &client_token) && + dict->GetString(kHostKey, &host) && dict->GetInteger(kPortKey, &port) && + dict->GetString(kCertificateFingerprintKey, &cert_fingerprint) && + dict->GetString(kCertificateKey, &cert))) { + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment()); + return; + } + + net::IPAddress ip_address; + if (!ip_address.AssignFromIPLiteral(host)) { + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment()); + return; + } + + if (!base::IsValueInRangeForNumericType<uint16_t>(port)) { + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_BAD_RESPONSE, Assignment()); + return; + } + Assignment assignment; - assignment.ip_endpoint = - net::IPEndPoint(GetBlimpletIPAddress(), GetBlimpletTCPPort()); - assignment.client_token = kDummyClientToken; - main_task_runner_->PostTask(FROM_HERE, base::Bind(callback, assignment)); + // The assigner assumes SSL-only and all engines it assigns only communicate + // over SSL. + assignment.transport_protocol = Assignment::TransportProtocol::SSL; + assignment.ip_endpoint = net::IPEndPoint(ip_address, port); + assignment.client_token = client_token; + assignment.certificate = cert; + assignment.certificate_fingerprint = cert_fingerprint; + + base::ResetAndReturn(&callback_) + .Run(AssignmentSource::Result::RESULT_OK, assignment); } } // namespace client
diff --git a/blimp/client/session/assignment_source.h b/blimp/client/session/assignment_source.h index 9891d4a..dabe72c 100644 --- a/blimp/client/session/assignment_source.h +++ b/blimp/client/session/assignment_source.h
@@ -10,41 +10,105 @@ #include "base/callback.h" #include "blimp/client/blimp_client_export.h" #include "net/base/ip_endpoint.h" +#include "net/url_request/url_fetcher_delegate.h" namespace base { class SingleThreadTaskRunner; } +namespace net { +class URLFetcher; +class URLRequestContextGetter; +} + namespace blimp { namespace client { +// TODO(kmarshall): Take values from configuration data. +const char kDummyClientToken[] = "MyVoiceIsMyPassport"; + +// Potential assigner URLs. +const char kDefaultAssignerURL[] = + "https://blimp-pa.googleapis.com/v1/assignment"; + // An Assignment contains the configuration data needed for a client // to connect to the engine. struct BLIMP_CLIENT_EXPORT Assignment { + enum TransportProtocol { + UNKNOWN = 0, + SSL = 1, + TCP = 2, + QUIC = 3, + }; + + Assignment(); + ~Assignment(); + + TransportProtocol transport_protocol; net::IPEndPoint ip_endpoint; std::string client_token; + std::string certificate; + std::string certificate_fingerprint; + + // Returns true if the net::IPEndPoint has an unspecified IP, port, or + // transport protocol. + bool is_null() const; }; // AssignmentSource provides functionality to find out how a client should // connect to an engine. -class BLIMP_CLIENT_EXPORT AssignmentSource { +class BLIMP_CLIENT_EXPORT AssignmentSource : public net::URLFetcherDelegate { public: - typedef const base::Callback<void(const Assignment&)> AssignmentCallback; + // A Java counterpart will be generated for this enum. + // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.blimp.assignment + enum Result { + RESULT_UNKNOWN = 0, + RESULT_OK = 1, + RESULT_BAD_REQUEST = 2, + RESULT_BAD_RESPONSE = 3, + RESULT_INVALID_PROTOCOL_VERSION = 4, + RESULT_EXPIRED_ACCESS_TOKEN = 5, + RESULT_USER_INVALID = 6, + RESULT_OUT_OF_VMS = 7, + RESULT_SERVER_ERROR = 8, + RESULT_SERVER_INTERRUPTED = 9, + RESULT_NETWORK_FAILURE = 10 + }; + + typedef base::Callback<void(AssignmentSource::Result, const Assignment&)> + AssignmentCallback; // The |main_task_runner| should be the task runner for the UI thread because // this will in some cases be used to trigger user interaction on the UI // thread. AssignmentSource( - const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner); - virtual ~AssignmentSource(); + const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner, + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner); + ~AssignmentSource() override; // Retrieves a valid assignment for the client and posts the result to the - // given callback. - void GetAssignment(const AssignmentCallback& callback); + // given callback. |client_auth_token| is the OAuth2 access token to send to + // the assigner when requesting an assignment. If this is called before a + // previous call has completed, the old callback will be called with + // RESULT_SERVER_INTERRUPTED and no Assignment. + void GetAssignment(const std::string& client_auth_token, + const AssignmentCallback& callback); + + // net::URLFetcherDelegate implementation: + void OnURLFetchComplete(const net::URLFetcher* source) override; private: + void ParseAssignerResponse(); + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; + scoped_refptr<net::URLRequestContextGetter> url_request_context_; + scoped_ptr<net::URLFetcher> url_fetcher_; + + // This callback is set during a call to GetAssignment() and is cleared after + // the request has completed (whether it be a success or failure). + AssignmentCallback callback_; + DISALLOW_COPY_AND_ASSIGN(AssignmentSource); };
diff --git a/blimp/client/session/assignment_source_unittest.cc b/blimp/client/session/assignment_source_unittest.cc new file mode 100644 index 0000000..d893ae3 --- /dev/null +++ b/blimp/client/session/assignment_source_unittest.cc
@@ -0,0 +1,355 @@ +// 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/client/session/assignment_source.h" + +#include "base/command_line.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/test/test_simple_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/values.h" +#include "blimp/client/app/blimp_client_switches.h" +#include "blimp/common/protocol_version.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::InSequence; + +namespace blimp { +namespace client { +namespace { + +MATCHER_P(AssignmentEquals, assignment, "") { + return arg.transport_protocol == assignment.transport_protocol && + arg.ip_endpoint == assignment.ip_endpoint && + arg.client_token == assignment.client_token && + arg.certificate == assignment.certificate && + arg.certificate_fingerprint == assignment.certificate_fingerprint; +} + +net::IPEndPoint BuildIPEndPoint(const std::string& ip, int port) { + net::IPAddress ip_address; + EXPECT_TRUE(ip_address.AssignFromIPLiteral(ip)); + + return net::IPEndPoint(ip_address, port); +} + +Assignment BuildValidAssignment() { + Assignment assignment; + assignment.transport_protocol = Assignment::TransportProtocol::SSL; + assignment.ip_endpoint = BuildIPEndPoint("100.150.200.250", 500); + assignment.client_token = "SecretT0kenz"; + assignment.certificate_fingerprint = "WhaleWhaleWhale"; + assignment.certificate = "whaaaaaaaaaaaaale"; + return assignment; +} + +std::string BuildResponseFromAssignment(const Assignment& assignment) { + base::DictionaryValue dict; + dict.SetString("clientToken", assignment.client_token); + dict.SetString("host", assignment.ip_endpoint.address().ToString()); + dict.SetInteger("port", assignment.ip_endpoint.port()); + dict.SetString("certificateFingerprint", assignment.certificate_fingerprint); + dict.SetString("certificate", assignment.certificate); + + std::string json; + base::JSONWriter::Write(dict, &json); + return json; +} + +class AssignmentSourceTest : public testing::Test { + public: + AssignmentSourceTest() + : task_runner_(new base::TestSimpleTaskRunner), + task_runner_handle_(task_runner_), + source_(task_runner_, task_runner_) {} + + // This expects the AssignmentSource::GetAssignment to return a custom + // endpoint without having to hit the network. This will typically be used + // for testing that specifying an assignment via the command line works as + // expected. + void GetAlternateAssignment() { + source_.GetAssignment("", + base::Bind(&AssignmentSourceTest::AssignmentResponse, + base::Unretained(this))); + EXPECT_EQ(nullptr, factory_.GetFetcherByID(0)); + task_runner_->RunUntilIdle(); + } + + // See net/base/net_errors.h for possible status errors. + void GetNetworkAssignmentAndWaitForResponse( + net::HttpStatusCode response_code, + int status, + const std::string& response, + const std::string& client_auth_token, + const std::string& protocol_version) { + source_.GetAssignment(client_auth_token, + base::Bind(&AssignmentSourceTest::AssignmentResponse, + base::Unretained(this))); + + net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + + task_runner_->RunUntilIdle(); + + EXPECT_NE(nullptr, fetcher); + EXPECT_EQ(kDefaultAssignerURL, fetcher->GetOriginalURL().spec()); + + // Check that the request has a valid protocol_version. + scoped_ptr<base::Value> json = + base::JSONReader::Read(fetcher->upload_data()); + EXPECT_NE(nullptr, json.get()); + + const base::DictionaryValue* dict; + EXPECT_TRUE(json->GetAsDictionary(&dict)); + + std::string uploaded_protocol_version; + EXPECT_TRUE( + dict->GetString("protocol_version", &uploaded_protocol_version)); + EXPECT_EQ(protocol_version, uploaded_protocol_version); + + // Check that the request has a valid authentication header. + net::HttpRequestHeaders headers; + fetcher->GetExtraRequestHeaders(&headers); + + std::string authorization; + EXPECT_TRUE(headers.GetHeader("Authorization", &authorization)); + EXPECT_EQ("Bearer " + client_auth_token, authorization); + + // Send the fake response back. + fetcher->set_response_code(response_code); + fetcher->set_status(net::URLRequestStatus::FromError(status)); + fetcher->SetResponseString(response); + fetcher->delegate()->OnURLFetchComplete(fetcher); + + task_runner_->RunUntilIdle(); + } + + MOCK_METHOD2(AssignmentResponse, + void(AssignmentSource::Result, const Assignment&)); + + protected: + // Used to drive all AssignmentSource tasks. + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + base::ThreadTaskRunnerHandle task_runner_handle_; + + net::TestURLFetcherFactory factory_; + + AssignmentSource source_; +}; + +TEST_F(AssignmentSourceTest, TestTCPAlternateEndpointSuccess) { + Assignment assignment; + assignment.transport_protocol = Assignment::TransportProtocol::TCP; + assignment.ip_endpoint = BuildIPEndPoint("100.150.200.250", 500); + assignment.client_token = kDummyClientToken; + + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kBlimpletEndpoint, "tcp:100.150.200.250:500"); + + EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, + AssignmentEquals(assignment))) + .Times(1); + + GetAlternateAssignment(); +} + +TEST_F(AssignmentSourceTest, TestSSLAlternateEndpointSuccess) { + Assignment assignment; + assignment.transport_protocol = Assignment::TransportProtocol::SSL; + assignment.ip_endpoint = BuildIPEndPoint("100.150.200.250", 500); + assignment.client_token = kDummyClientToken; + + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kBlimpletEndpoint, "ssl:100.150.200.250:500"); + + EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, + AssignmentEquals(assignment))) + .Times(1); + + GetAlternateAssignment(); +} + +TEST_F(AssignmentSourceTest, TestQUICAlternateEndpointSuccess) { + Assignment assignment; + assignment.transport_protocol = Assignment::TransportProtocol::QUIC; + assignment.ip_endpoint = BuildIPEndPoint("100.150.200.250", 500); + assignment.client_token = kDummyClientToken; + + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kBlimpletEndpoint, "quic:100.150.200.250:500"); + + EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, + AssignmentEquals(assignment))) + .Times(1); + + GetAlternateAssignment(); +} + +TEST_F(AssignmentSourceTest, TestSuccess) { + Assignment assignment = BuildValidAssignment(); + + EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, + AssignmentEquals(assignment))) + .Times(1); + + GetNetworkAssignmentAndWaitForResponse( + net::HTTP_OK, net::Error::OK, BuildResponseFromAssignment(assignment), + "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestSecondRequestInterruptsFirst) { + InSequence sequence; + Assignment assignment = BuildValidAssignment(); + + source_.GetAssignment("", + base::Bind(&AssignmentSourceTest::AssignmentResponse, + base::Unretained(this))); + + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_SERVER_INTERRUPTED, + AssignmentEquals(Assignment()))) + .Times(1) + .RetiresOnSaturation(); + + EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, + AssignmentEquals(assignment))) + .Times(1) + .RetiresOnSaturation(); + + GetNetworkAssignmentAndWaitForResponse( + net::HTTP_OK, net::Error::OK, BuildResponseFromAssignment(assignment), + "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestValidAfterError) { + InSequence sequence; + Assignment assignment = BuildValidAssignment(); + + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_NETWORK_FAILURE, _)) + .Times(1) + .RetiresOnSaturation(); + + EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, + AssignmentEquals(assignment))) + .Times(1) + .RetiresOnSaturation(); + + GetNetworkAssignmentAndWaitForResponse(net::HTTP_OK, + net::Error::ERR_INSUFFICIENT_RESOURCES, + "", "UserAuthT0kenz", kEngineVersion); + + GetNetworkAssignmentAndWaitForResponse( + net::HTTP_OK, net::Error::OK, BuildResponseFromAssignment(assignment), + "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestNetworkFailure) { + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_NETWORK_FAILURE, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_OK, + net::Error::ERR_INSUFFICIENT_RESOURCES, + "", "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestBadRequest) { + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_BAD_REQUEST, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_BAD_REQUEST, net::Error::OK, + "", "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestUnauthorized) { + EXPECT_CALL(*this, + AssignmentResponse( + AssignmentSource::Result::RESULT_EXPIRED_ACCESS_TOKEN, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_UNAUTHORIZED, net::Error::OK, + "", "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestForbidden) { + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_USER_INVALID, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_FORBIDDEN, net::Error::OK, + "", "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestTooManyRequests) { + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_OUT_OF_VMS, _)); + GetNetworkAssignmentAndWaitForResponse(static_cast<net::HttpStatusCode>(429), + net::Error::OK, "", "UserAuthT0kenz", + kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestInternalServerError) { + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_SERVER_ERROR, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_INTERNAL_SERVER_ERROR, + net::Error::OK, "", "UserAuthT0kenz", + kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestUnexpectedNetCodeFallback) { + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_BAD_RESPONSE, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_NOT_IMPLEMENTED, + net::Error::OK, "", "UserAuthT0kenz", + kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestInvalidJsonResponse) { + Assignment assignment = BuildValidAssignment(); + + // Remove half the response. + std::string response = BuildResponseFromAssignment(assignment); + response = response.substr(response.size() / 2); + + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_BAD_RESPONSE, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_OK, net::Error::OK, response, + "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestMissingResponsePort) { + // Purposely do not add the 'port' field to the response. + base::DictionaryValue dict; + dict.SetString("clientToken", "SecretT0kenz"); + dict.SetString("host", "happywhales"); + dict.SetString("certificateFingerprint", "WhaleWhaleWhale"); + dict.SetString("certificate", "whaaaaaaaaaaaaale"); + + std::string response; + base::JSONWriter::Write(dict, &response); + + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_BAD_RESPONSE, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_OK, net::Error::OK, response, + "UserAuthT0kenz", kEngineVersion); +} + +TEST_F(AssignmentSourceTest, TestInvalidIPAddress) { + // Purposely add an invalid IP field to the response. + base::DictionaryValue dict; + dict.SetString("clientToken", "SecretT0kenz"); + dict.SetString("host", "happywhales"); + dict.SetInteger("port", 500); + dict.SetString("certificateFingerprint", "WhaleWhaleWhale"); + dict.SetString("certificate", "whaaaaaaaaaaaaale"); + + std::string response; + base::JSONWriter::Write(dict, &response); + + EXPECT_CALL(*this, AssignmentResponse( + AssignmentSource::Result::RESULT_BAD_RESPONSE, _)); + GetNetworkAssignmentAndWaitForResponse(net::HTTP_OK, net::Error::OK, response, + "UserAuthT0kenz", kEngineVersion); +} + +} // namespace +} // namespace client +} // namespace blimp
diff --git a/blimp/client/session/blimp_client_session.cc b/blimp/client/session/blimp_client_session.cc index e681a0a..29c0b32b 100644 --- a/blimp/client/session/blimp_client_session.cc +++ b/blimp/client/session/blimp_client_session.cc
@@ -10,6 +10,7 @@ #include "base/command_line.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/sequenced_task_runner_handle.h" #include "blimp/client/app/blimp_client_switches.h" #include "blimp/client/feature/navigation_feature.h" @@ -106,10 +107,8 @@ outgoing_message_processors_.push_back(std::move(outgoing_message_processor)); } -BlimpClientSession::BlimpClientSession( - scoped_ptr<AssignmentSource> assignment_source) - : assignment_source_(std::move(assignment_source)), - io_thread_("BlimpIOThread"), +BlimpClientSession::BlimpClientSession() + : io_thread_("BlimpIOThread"), tab_control_feature_(new TabControlFeature), navigation_feature_(new NavigationFeature), render_widget_feature_(new RenderWidgetFeature), @@ -119,6 +118,9 @@ options.message_loop_type = base::MessageLoop::TYPE_IO; io_thread_.StartWithOptions(options); + assignment_source_.reset(new AssignmentSource( + base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner())); + // Register features' message senders and receivers. tab_control_feature_->set_outgoing_message_processor( RegisterFeature(BlimpMessage::TAB_CONTROL, tab_control_feature_.get())); @@ -144,18 +146,30 @@ io_thread_.task_runner()->DeleteSoon(FROM_HERE, net_components_.release()); } -void BlimpClientSession::Connect() { - assignment_source_->GetAssignment(base::Bind( - &BlimpClientSession::ConnectWithAssignment, weak_factory_.GetWeakPtr())); +void BlimpClientSession::Connect(const std::string& client_auth_token) { + assignment_source_->GetAssignment( + client_auth_token, base::Bind(&BlimpClientSession::ConnectWithAssignment, + weak_factory_.GetWeakPtr())); } -void BlimpClientSession::ConnectWithAssignment(const Assignment& assignment) { +void BlimpClientSession::ConnectWithAssignment(AssignmentSource::Result result, + const Assignment& assignment) { + OnAssignmentConnectionAttempted(result); + + if (result != AssignmentSource::Result::RESULT_OK) { + VLOG(1) << "Assignment request failed: " << result; + return; + } + io_thread_.task_runner()->PostTask( FROM_HERE, base::Bind(&ClientNetworkComponents::ConnectWithAssignment, base::Unretained(net_components_.get()), assignment)); } +void BlimpClientSession::OnAssignmentConnectionAttempted( + AssignmentSource::Result result) {} + scoped_ptr<BlimpMessageProcessor> BlimpClientSession::RegisterFeature( BlimpMessage::Type type, BlimpMessageProcessor* incoming_processor) {
diff --git a/blimp/client/session/blimp_client_session.h b/blimp/client/session/blimp_client_session.h index afcfd53..b715453c 100644 --- a/blimp/client/session/blimp_client_session.h +++ b/blimp/client/session/blimp_client_session.h
@@ -42,19 +42,31 @@ // feature proxies must be interacted with on the UI thread. class BLIMP_CLIENT_EXPORT BlimpClientSession { public: - explicit BlimpClientSession(scoped_ptr<AssignmentSource> assignment_source); + BlimpClientSession(); // Uses the AssignmentSource to get an Assignment and then uses the assignment // configuration to connect to the Blimplet. - void Connect(); + // |client_auth_token| is the OAuth2 access token to use when querying + // for an assignment. This token needs the OAuth2 scope of userinfo.email and + // only needs to be an access token, not a refresh token. + void Connect(const std::string& client_auth_token); TabControlFeature* GetTabControlFeature() const; NavigationFeature* GetNavigationFeature() const; RenderWidgetFeature* GetRenderWidgetFeature() const; + // The AssignmentCallback for when an assignment is ready. This will trigger + // a connection to the engine. + virtual void ConnectWithAssignment(AssignmentSource::Result result, + const Assignment& assignment); + protected: virtual ~BlimpClientSession(); + // Notified every time the AssignmentSource returns the result of an attempted + // assignment request. + virtual void OnAssignmentConnectionAttempted(AssignmentSource::Result result); + private: // Registers a message processor which will receive all messages of the |type| // specified. Returns a BlimpMessageProcessor object for sending messages of @@ -63,19 +75,15 @@ BlimpMessage::Type type, BlimpMessageProcessor* incoming_processor); - // The AssignmentCallback for when an assignment is ready. This will trigger - // a connection to the engine. - void ConnectWithAssignment(const Assignment& assignment); - - // The AssignmentSource is used when the user of BlimpClientSession calls - // Connect() to get a valid assignment and later connect to the engine. - scoped_ptr<AssignmentSource> assignment_source_; - base::Thread io_thread_; scoped_ptr<TabControlFeature> tab_control_feature_; scoped_ptr<NavigationFeature> navigation_feature_; scoped_ptr<RenderWidgetFeature> render_widget_feature_; + // The AssignmentSource is used when the user of BlimpClientSession calls + // Connect() to get a valid assignment and later connect to the engine. + scoped_ptr<AssignmentSource> assignment_source_; + // Container struct for network components. // Must be deleted on the IO thread. scoped_ptr<ClientNetworkComponents> net_components_;
diff --git a/blimp/common/protocol_version.h b/blimp/common/protocol_version.h index 98a7e63..ea628a2 100644 --- a/blimp/common/protocol_version.h +++ b/blimp/common/protocol_version.h
@@ -11,6 +11,9 @@ const int BLIMP_COMMON_EXPORT kProtocolVersion = 0; +// TODO(dtrainor): Move this out or integrate this with kProtocolVersion? +const char BLIMP_COMMON_EXPORT kEngineVersion[] = "20160209094838"; + } // namespace blimp #endif // BLIMP_COMMON_PROTOCOL_VERSION_H_
diff --git a/blimp/docs/running.md b/blimp/docs/running.md index 9678a09..f4a1d96 100644 --- a/blimp/docs/running.md +++ b/blimp/docs/running.md
@@ -21,6 +21,11 @@ ./build/android/adb_blimp_command_line --enable-webgl ``` +To have the client connect to a custom engine use the `--blimplet-endpoint` +flag. This takes values in the form of scheme:ip:port. The possible valid +schemes are 'tcp', 'quic', and 'ssl'. An example valid endpoint would be +`--blimplet-endpoint=tcp:127.0.0.1:500`. + Run the Blimp APK with: ```bash @@ -46,3 +51,4 @@ ### Required flags * `--blimp-client-token-path=$PATH`: Path to a file containing a nonempty token string. If this is not present, the engine will fail to boot. +
diff --git a/blimp/engine/app/blimp_permission_manager.cc b/blimp/engine/app/blimp_permission_manager.cc index 11f1e23..cf4d9f5 100644 --- a/blimp/engine/app/blimp_permission_manager.cc +++ b/blimp/engine/app/blimp_permission_manager.cc
@@ -21,7 +21,6 @@ content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& origin, - bool user_gesture, const base::Callback<void(content::PermissionStatus)>& callback) { callback.Run(content::PermissionStatus::DENIED); return kNoPendingOperation; @@ -31,7 +30,6 @@ const std::vector<content::PermissionType>& permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(const std::vector<content::PermissionStatus>&)>& callback) { callback.Run(std::vector<content::PermissionStatus>(
diff --git a/blimp/engine/app/blimp_permission_manager.h b/blimp/engine/app/blimp_permission_manager.h index e7f356d..a5ae164e 100644 --- a/blimp/engine/app/blimp_permission_manager.h +++ b/blimp/engine/app/blimp_permission_manager.h
@@ -24,13 +24,11 @@ content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(content::PermissionStatus)>& callback) override; int RequestPermissions( const std::vector<content::PermissionType>& permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(const std::vector<content::PermissionStatus>&)>& callback) override; void CancelPermissionRequest(int request_id) override;
diff --git a/blimp/net/blimp_message_pump.cc b/blimp/net/blimp_message_pump.cc index c3d6380..046df8e2 100644 --- a/blimp/net/blimp_message_pump.cc +++ b/blimp/net/blimp_message_pump.cc
@@ -16,13 +16,7 @@ namespace blimp { BlimpMessagePump::BlimpMessagePump(PacketReader* reader) - : reader_(reader), - buffer_(new net::GrowableIOBuffer), - process_msg_callback_( - base::Bind(&BlimpMessagePump::OnProcessMessageComplete, - base::Unretained(this))), - read_callback_(base::Bind(&BlimpMessagePump::OnReadPacketComplete, - base::Unretained(this))) { + : reader_(reader), buffer_(new net::GrowableIOBuffer), weak_factory_(this) { DCHECK(reader_); buffer_->SetCapacity(kMaxPacketPayloadSizeBytes); } @@ -49,7 +43,9 @@ DCHECK(!read_inflight_); read_inflight_ = true; buffer_->set_offset(0); - reader_->ReadPacket(buffer_.get(), read_callback_.callback()); + reader_->ReadPacket(buffer_.get(), + base::Bind(&BlimpMessagePump::OnReadPacketComplete, + weak_factory_.GetWeakPtr())); } void BlimpMessagePump::OnReadPacketComplete(int result) { @@ -60,8 +56,10 @@ scoped_ptr<BlimpMessage> message(new BlimpMessage); if (message->ParseFromArray(buffer_->StartOfBuffer(), buffer_->offset())) { DVLOG(2) << "OnReadPacketComplete, result=" << *message; - processor_->ProcessMessage(std::move(message), - process_msg_callback_.callback()); + processor_->ProcessMessage( + std::move(message), + base::Bind(&BlimpMessagePump::OnProcessMessageComplete, + weak_factory_.GetWeakPtr())); } else { result = net::ERR_FAILED; }
diff --git a/blimp/net/blimp_message_pump.h b/blimp/net/blimp_message_pump.h index 5d8aaa7..07a80c1 100644 --- a/blimp/net/blimp_message_pump.h +++ b/blimp/net/blimp_message_pump.h
@@ -8,6 +8,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "blimp/net/blimp_net_export.h" #include "net/base/completion_callback.h" @@ -63,13 +64,8 @@ scoped_refptr<net::GrowableIOBuffer> buffer_; bool read_inflight_ = false; - // Cancelled in the event that the connection is destroyed (along with - // |this|) while a inflight callback is held by |processor_|. - net::CancelableCompletionCallback process_msg_callback_; - - // Cancelled to guard against |this| being called back from a completed read - // operation. - net::CancelableCompletionCallback read_callback_; + // Used to abandon ProcessMessage completion callbacks if |this| is deleted. + base::WeakPtrFactory<BlimpMessagePump> weak_factory_; DISALLOW_COPY_AND_ASSIGN(BlimpMessagePump); };
diff --git a/build/all.gyp b/build/all.gyp index 1984049..a487355 100644 --- a/build/all.gyp +++ b/build/all.gyp
@@ -224,7 +224,6 @@ 'dependencies': [ '../chrome/tools/crash_service/caps/caps.gyp:*', '../chrome_elf/chrome_elf.gyp:*', - '../cloud_print/cloud_print.gyp:*', '../courgette/courgette.gyp:*', '../rlz/rlz.gyp:*', '../sandbox/sandbox.gyp:*', @@ -339,7 +338,6 @@ 'dependencies': [ '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests', '../cc/cc_tests.gyp:cc_unittests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../content/content_shell_and_tests.gyp:content_browsertests', '../content/content_shell_and_tests.gyp:content_shell', '../content/content_shell_and_tests.gyp:content_unittests', @@ -923,7 +921,6 @@ '../chrome/chrome.gyp:interactive_ui_tests', '../chrome/chrome.gyp:sync_integration_tests', '../chrome/chrome.gyp:unit_tests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../components/components_tests.gyp:components_unittests', '../content/content_shell_and_tests.gyp:content_browsertests', '../content/content_shell_and_tests.gyp:content_unittests', @@ -964,7 +961,6 @@ '../chrome/chrome.gyp:performance_browser_tests', '../chrome/chrome.gyp:sync_integration_tests', '../chrome/chrome.gyp:unit_tests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../components/components_tests.gyp:components_unittests', '../content/content_shell_and_tests.gyp:content_browsertests', '../content/content_shell_and_tests.gyp:content_unittests', @@ -999,7 +995,6 @@ 'type': 'none', 'dependencies': [ '../base/base.gyp:base_unittests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../crypto/crypto.gyp:crypto_unittests', '../ipc/ipc.gyp:ipc_tests', '../jingle/jingle.gyp:jingle_unittests', @@ -1020,7 +1015,6 @@ 'dependencies': [ '../base/base.gyp:base_unittests', '../chrome/chrome.gyp:unit_tests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../components/components_tests.gyp:components_unittests', '../content/content_shell_and_tests.gyp:content_unittests', '../crypto/crypto.gyp:crypto_unittests', @@ -1067,7 +1061,6 @@ '../chrome/chrome.gyp:setup_unittests', '../chrome/chrome.gyp:sync_integration_tests', '../chrome/chrome.gyp:unit_tests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../components/components_tests.gyp:components_unittests', '../content/content_shell_and_tests.gyp:content_browsertests', '../content/content_shell_and_tests.gyp:content_unittests', @@ -1116,7 +1109,6 @@ 'type': 'none', 'dependencies': [ '../base/base.gyp:base_unittests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../components/components_tests.gyp:components_unittests', '../content/content_shell_and_tests.gyp:content_unittests', '../crypto/crypto.gyp:crypto_unittests', @@ -1158,7 +1150,6 @@ '../chrome/chrome.gyp:setup_unittests', '../chrome/chrome.gyp:unit_tests', '../chrome_elf/chrome_elf.gyp:chrome_elf_unittests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../components/components_tests.gyp:components_unittests', '../components/test_runner/test_runner.gyp:layout_test_helper', '../content/content_shell_and_tests.gyp:content_browsertests', @@ -1220,7 +1211,6 @@ '../chrome/chrome.gyp:gcapi_dll', '../chrome/chrome.gyp:pack_policy_templates', '../chrome/installer/mini_installer.gyp:mini_installer', - '../cloud_print/cloud_print.gyp:cloud_print', '../courgette/courgette.gyp:courgette', '../courgette/courgette.gyp:courgette64', '../remoting/remoting.gyp:remoting_webapp',
diff --git a/build/android/generate_emma_html.py b/build/android/generate_emma_html.py index 64d834f..9d1d7330 100755 --- a/build/android/generate_emma_html.py +++ b/build/android/generate_emma_html.py
@@ -58,6 +58,9 @@ coverage_files = _GetFilesWithExt(options.coverage_dir, 'ec') metadata_files = _GetFilesWithExt(options.metadata_dir, 'em') + # Filter out zero-length files. These are created by emma_instr.py when a + # target has no classes matching the coverage filter. + metadata_files = [f for f in metadata_files if os.path.getsize(f)] print 'Found coverage files: %s' % str(coverage_files) print 'Found metadata files: %s' % str(metadata_files) @@ -87,6 +90,10 @@ for f in coverage_files: os.remove(f) + # Command tends to exit with status 0 when it actually failed. + if not exit_code and not os.path.exists(options.output): + exit_code = 1 + return exit_code
diff --git a/build/android/incremental_install/create_install_script.py b/build/android/incremental_install/create_install_script.py index 4024e05..5be4fe4 100755 --- a/build/android/incremental_install/create_install_script.py +++ b/build/android/incremental_install/create_install_script.py
@@ -37,13 +37,15 @@ # Exported to allow test runner to be able to install incremental apks. def GetInstallParameters(): apk_path = {apk_path} - native_libs = {native_libs} dex_files = {dex_files} - splits = {splits} + dont_even_try = {dont_even_try} + native_libs = {native_libs} show_proguard_warning = {show_proguard_warning} + splits = {splits} return dict(apk_path=_ResolvePath(apk_path), dex_files=[_ResolvePath(p) for p in dex_files], + dont_even_try=dont_even_try, native_libs=[_ResolvePath(p) for p in native_libs], show_proguard_warning=show_proguard_warning, splits=[_ResolvePath(p) for p in splits]) @@ -64,6 +66,8 @@ for split in params['splits']: cmd_args.extend(('--split', split)) cmd_args.append(params['apk_path']) + if params['dont_even_try']: + cmd_args.extend(('--dont-even-try', params['dont_even_try'])) if params['show_proguard_warning']: cmd_args.append('--show-proguard-warning') return subprocess.call(cmd_args + sys.argv[1:]) @@ -108,6 +112,8 @@ action='store_true', default=False, help='Print a warning about proguard being disabled') + parser.add_argument('--dont-even-try', + help='Prints this message and exits.') options = parser.parse_args(args) options.dex_files += build_utils.ParseGypList(options.dex_file_list) @@ -134,6 +140,7 @@ 'output_directory': pformat(relativize(options.output_directory)), 'native_libs': pformat([relativize(p) for p in options.native_libs]), 'dex_files': pformat([relativize(p) for p in options.dex_files]), + 'dont_even_try': pformat(options.dont_even_try), 'show_proguard_warning': pformat(options.show_proguard_warning), 'splits': pformat([relativize(p) for p in options.splits]), }
diff --git a/build/android/incremental_install/installer.py b/build/android/incremental_install/installer.py index b5b1d05..2f60a9a 100755 --- a/build/android/incremental_install/installer.py +++ b/build/android/incremental_install/installer.py
@@ -262,6 +262,8 @@ action='store_true', default=False, help='Print a warning about proguard being disabled') + parser.add_argument('--dont-even-try', + help='Prints this message and exits.') parser.add_argument('-v', '--verbose', dest='verbose_count', @@ -278,6 +280,10 @@ devil_chromium.Initialize(output_directory=constants.GetOutDirectory()) + if args.dont_even_try: + logging.fatal(args.dont_even_try) + return 1 + if args.device: # Retries are annoying when commands fail for legitimate reasons. Might want # to enable them if this is ever used on bots though.
diff --git a/build/common.gypi b/build/common.gypi index fc82959..8afb0653 100644 --- a/build/common.gypi +++ b/build/common.gypi
@@ -474,12 +474,8 @@ 'asan%': 0, 'asan_blacklist%': '<(PRODUCT_DIR)/../../tools/memory/asan/blacklist.txt', # Enable coverage gathering instrumentation in sanitizer tools. This flag - # also controls coverage granularity (1 for function-level coverage, 2 - # for block-level coverage). - 'sanitizer_coverage%': 0, - # Deprecated, only works if |sanitizer_coverage| isn't set. - # TODO(glider): remove this flag. - 'asan_coverage%': 0, + # also controls coverage granularity. + 'sanitizer_coverage%': '', # Enable intra-object-overflow detection in ASan (experimental). 'asan_field_padding%': 0, @@ -491,12 +487,6 @@ # See https://github.com/google/syzygy/wiki/SyzyASanHowTo 'syzyasan%': 0, - # Enable crash reporting via Kasko. - 'kasko%': 0, - - # Enable hang reports in Kasko. Requires Kasko to be enabled. - 'kasko_hang_reports%': 0, - # Enable building with LSan (Clang's -fsanitize=leak option). # -fsanitize=leak only works with clang, but lsan=1 implies clang=1 # See https://sites.google.com/a/chromium.org/dev/developers/testing/leaksanitizer @@ -1060,8 +1050,19 @@ # http://crbug.com/574476 'fastbuild%': 2, }], + + # Enable crash reporting via Kasko. + ['OS=="win" and target_arch=="ia32" and branding=="Chrome"', { + # This needs to be enabled with kasko_hang_reports. + 'kasko%': 0, + }, { + 'kasko%': 0, + }], ], + # Enable hang reports in Kasko. Requires Kasko to be enabled. + 'kasko_hang_reports%': 0, + # Setting this to '0' will cause V8's startup snapshot to be # embedded in the binary instead of being a external files. 'v8_use_external_startup_data%': 1, @@ -1205,7 +1206,6 @@ 'mac_want_real_dsym%': '<(mac_want_real_dsym)', 'asan%': '<(asan)', 'asan_blacklist%': '<(asan_blacklist)', - 'asan_coverage%': '<(asan_coverage)', 'sanitizer_coverage%': '<(sanitizer_coverage)', 'asan_field_padding%': '<(asan_field_padding)', 'use_sanitizer_options%': '<(use_sanitizer_options)', @@ -2012,6 +2012,8 @@ }], ['syzyasan==1', { 'kasko%': 1, + # Disable hang reports for SyzyASAN builds. + 'kasko_hang_reports': 0, }], ['component=="shared_library" and "<(GENERATOR)"=="ninja"', { # Only enabled by default for ninja because it's buggy in VS. @@ -2404,14 +2406,12 @@ }], ], }, { # chromecast!=1 + # Build all platforms whose deps are in install-build-deps.sh. + # Only these platforms will be compile tested by buildbots. + 'ozone_platform_egltest%': 1, 'conditions': [ - ['OS=="chromeos"', { + ['chromeos==1', { 'ozone_platform_gbm%': 1, - 'ozone_platform_egltest%': 1, - }, { - # Build all platforms whose deps are in install-build-deps.sh. - # Only these platforms will be compile tested by buildbots. - 'ozone_platform_egltest%': 1, }], ], }], @@ -3260,7 +3260,7 @@ }, }, 'conditions': [ - ['OS=="win" and win_fastlink==1', { + ['OS=="win" and win_fastlink==1 and MSVS_VERSION != "2013"', { 'msvs_settings': { 'VCLinkerTool': { # /PROFILE is incompatible with /debug:fastlink @@ -3280,6 +3280,10 @@ 'AdditionalOptions': [ # Work around crbug.com/526851, bug in VS 2015 RTM compiler. '/Zc:sizedDealloc-', + # Disable thread-safe statics to avoid overhead and because + # they are disabled on other platforms. See crbug.com/587210 + # and -fno-threadsafe-statics. + '/Zc:threadSafeInit-', ], }, }, @@ -3687,6 +3691,8 @@ 'cflags_cc': [ '-fno-exceptions', '-fno-rtti', + # If this is removed then remove the corresponding /Zc:threadSafeInit- + # for Windows. '-fno-threadsafe-statics', # Make inline functions have hidden visiblity by default. # Surprisingly, not covered by -fvisibility=hidden. @@ -4466,19 +4472,7 @@ }], ], }], - ['asan_coverage!=0 and sanitizer_coverage==0', { - 'target_conditions': [ - ['_toolset=="target"', { - 'cflags': [ - '-fsanitize-coverage=<(asan_coverage)', - ], - 'defines': [ - 'SANITIZER_COVERAGE', - ], - }], - ], - }], - ['sanitizer_coverage!=0', { + ['sanitizer_coverage!=""', { 'target_conditions': [ ['_toolset=="target"', { 'cflags': [ @@ -4490,7 +4484,7 @@ }], ], }], - ['(asan_coverage>1 or sanitizer_coverage>1) and target_arch=="arm"', { + ['sanitizer_coverage!="" and target_arch=="arm"', { 'target_conditions': [ ['_toolset=="target"', { 'cflags': [ @@ -5132,19 +5126,7 @@ ], }, }], - ['asan_coverage!=0 and sanitizer_coverage==0', { - 'target_conditions': [ - ['_toolset=="target"', { - 'cflags': [ - '-fsanitize-coverage=<(asan_coverage)', - ], - 'defines': [ - 'SANITIZER_COVERAGE', - ], - }], - ], - }], - ['sanitizer_coverage!=0', { + ['sanitizer_coverage!=""', { 'target_conditions': [ ['_toolset=="target"', { 'cflags': [ @@ -5978,7 +5960,7 @@ }], ], }], - ['sanitizer_coverage!=0', { + ['sanitizer_coverage!=""', { # TODO(asan/win): Move this down into the general # win-target_defaults section once the 64-bit asan runtime # exists. See crbug.com/345874. @@ -5991,7 +5973,7 @@ ], }, 'conditions': [ - ['sanitizer_coverage!=0', { + ['sanitizer_coverage!=""', { # TODO(asan/win): Move this down into the general # win-target_defaults section once the 64-bit asan runtime # exists. See crbug.com/345874.
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn index a2b88f6..a868fbd 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn
@@ -466,6 +466,7 @@ "//build/config/win:nominmax", "//build/config/win:unicode", "//build/config/win:winver", + "//build/config/win:vs_code_analysis", ] } if (current_os == "winrt_81" || current_os == "winrt_81_phone" ||
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 952f78a..528fd0c 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -198,7 +198,7 @@ action(target_name) { script = "//build/android/gyp/create_java_binary_script.py" depfile = "$target_gen_dir/$_script_name.d" - java_script = "$root_build_dir/bin/helper/$_script_name" + java_script = "$root_build_dir/bin/$_script_name" inputs = [ _build_config, ]
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index a280be3b..49ccc1d 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -971,7 +971,7 @@ testonly = true bypass_platform_checks = true main_class = "org.chromium.testing.local.JunitTestMain" - wrapper_script_name = "$target_name" + wrapper_script_name = "helper/$target_name" deps += [ "//testing/android/junit:junit_test_support", @@ -1919,6 +1919,9 @@ if (_create_language_splits) { args += [ "--split=${_rebased_apk_path_no_ext}-language-*.apk" ] } + if (_load_library_from_apk) { + args += [ "--dont-even-try=Incremental builds do not work with load_library_from_apk. Try setting is_component_build=true in your GN args." ] + } } group(target_name) {
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index b1afcca5..3f23e8e 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -149,6 +149,8 @@ # -------------------------------- cflags += [ "-fno-strict-aliasing" ] # See http://crbug.com/32204 cflags_cc += [ + # If this is removed then remove the corresponding /Zc:threadSafeInit- for + # Windows. "-fno-threadsafe-statics", # Not exporting C++ inline functions can generally be applied anywhere @@ -1266,7 +1268,7 @@ } else { cflags = [ "/Zi" ] # Produce PDB file, no edit and continue. } - if (is_win_fastlink) { + if (is_win_fastlink && visual_studio_version != "2013") { # Tell VS 2015+ to create a PDB that references debug # information in .obj and .lib files instead of copying # it all. This flag is incompatible with /PROFILE @@ -1289,7 +1291,7 @@ if (is_win) { # Linker symbols for backtraces only. cflags = [] - if (is_win_fastlink) { + if (is_win_fastlink && visual_studio_version != "2013") { # Tell VS 2015+ to create a PDB that references debug # information in .obj and .lib files instead of copying # it all. This flag is incompatible with /PROFILE
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn index bdbf7ea..bf1e613 100644 --- a/build/config/win/BUILD.gn +++ b/build/config/win/BUILD.gn
@@ -8,6 +8,14 @@ assert(is_win) +declare_args() { + # Set this to true to enable static analysis through Visual Studio's + # /analyze. This dramatically slows compiles and reports thousands of + # warnings, so normally this is done on a build machine and only the new + # warnings are examined. + use_vs_code_analysis = false +} + # This is included by reference in the //build/config/compiler config that # is applied to all targets. It is here to separate out the logic that is # Windows-only. @@ -36,9 +44,16 @@ cflags_c = [ "/TC" ] cflags_cc = [ "/TP" ] - # Work around crbug.com/526851, bug in VS 2015 RTM compiler. if (visual_studio_version == "2015") { - cflags += [ "/Zc:sizedDealloc-" ] + cflags += [ + # Work around crbug.com/526851, bug in VS 2015 RTM compiler. + "/Zc:sizedDealloc-", + + # Disable thread-safe statics to avoid overhead and because + # they are disabled on other platforms. See crbug.com/587210 + # and -fno-threadsafe-statics. + "/Zc:threadSafeInit-", + ] } # Building with Clang on Windows is a work in progress and very @@ -80,6 +95,46 @@ } } +config("vs_code_analysis") { + if (use_vs_code_analysis) { + # When use_vs_code_analysis is specified add the /analyze switch to enable + # static analysis. Specifying /analyze:WX- says that /analyze warnings + # should not be treated as errors. + cflags = [ "/analyze:WX-" ] + + # Also, disable various noisy warnings that have low value. + cflags += [ + "/wd6011", # Dereferencing NULL pointer + + # C6285 is ~16% of raw warnings and has low value + "/wd6285", # non-zero constant || non-zero constant + "/wd6308", # realloc might return null pointer + + # Possible infinite loop: use of the constant + # EXCEPTION_CONTINUE_EXECUTION in the exception-filter + "/wd6312", + + "/wd6322", # Empty _except block + "/wd6330", # 'char' used instead of 'unsigned char' for istype() call + + # C6334 is ~80% of raw warnings and has low value + "/wd6334", # sizeof applied to an expression with an operator + "/wd6326", # Potential comparison of constant with constant + "/wd6340", # Sign mismatch in function parameter + "/wd28159", # Consider using 'GetTickCount64' + "/wd28196", # The precondition is not satisfied + "/wd28204", # Inconsistent SAL annotations + "/wd28251", # Inconsistent SAL annotations + "/wd28252", # Inconsistent SAL annotations + "/wd28253", # Inconsistent SAL annotations + "/wd28278", # Function appears with no prototype in scope + "/wd28285", # syntax error in SAL annotation (in algorithm) + "/wd28301", # Inconsistent SAL annotations + "/wd28182", # Dereferencing NULL pointer + ] + } +} + # This is included by reference in the //build/config/compiler:runtime_library # config that is applied to all targets. It is here to separate out the logic # that is Windows-only. Please see that target for advice on what should go in @@ -105,11 +160,14 @@ "PSAPI_VERSION=1", "WIN32", "_SECURE_ATL", - - # This is required for ATL to use XP-safe versions of its functions. - "_USING_V110_SDK71_", ] + if (!use_vs_code_analysis) { + # This is required for ATL to use XP-safe versions of its functions. + # However it is prohibited when using /analyze + defines += [ "_USING_V110_SDK71_" ] + } + if (is_component_build) { # Component mode: dynamic CRT. Since the library is shared, it requires # exceptions or will give errors about things not matching, so keep
diff --git a/build/gn_migration.gypi b/build/gn_migration.gypi index 7916d835..d9211dee 100644 --- a/build/gn_migration.gypi +++ b/build/gn_migration.gypi
@@ -269,7 +269,6 @@ '../chrome/chrome.gyp:chromedriver', '../chrome/chrome.gyp:chromedriver_tests', '../chrome/chrome.gyp:chromedriver_unittests', - '../cloud_print/cloud_print.gyp:cloud_print_unittests', '../content/content_shell_and_tests.gyp:content_shell', '../courgette/courgette.gyp:courgette', '../courgette/courgette.gyp:courgette_fuzz', @@ -502,11 +501,6 @@ '../chrome/installer/mini_installer.gyp:mini_installer', '../chrome_elf/chrome_elf.gyp:chrome_elf_unittests', '../chrome_elf/chrome_elf.gyp:dll_hash_main', - '../cloud_print/service/win/service.gyp:cloud_print_service', - '../cloud_print/service/win/service.gyp:cloud_print_service_config', - '../cloud_print/service/win/service.gyp:cloud_print_service_setup', - '../cloud_print/virtual_driver/win/install/virtual_driver_install.gyp:virtual_driver_setup', - '../cloud_print/virtual_driver/win/virtual_driver.gyp:gcp_portmon', '../components/components.gyp:wifi_test', '../net/net.gyp:quic_client', '../net/net.gyp:quic_server', @@ -593,6 +587,7 @@ '../ui/app_list/app_list.gyp:app_list_unittests_run', '../ui/compositor/compositor.gyp:compositor_unittests_run', '../ui/events/events_unittests.gyp:events_unittests_run', + '../ui/gfx/gfx_tests.gyp:gfx_unittests_run', '../ui/message_center/message_center.gyp:message_center_unittests_run', '../url/url.gyp:url_unittests_run', ], @@ -729,8 +724,6 @@ # we decide we need for something. Owner: scottmg. '../chrome/tools/crash_service/caps/caps.gyp:caps', - '../cloud_print/gcp20/prototype/gcp20_device.gyp:gcp20_device', - '../cloud_print/gcp20/prototype/gcp20_device.gyp:gcp20_device_unittests', '../components/test_runner/test_runner.gyp:layout_test_helper', '../content/content_shell_and_tests.gyp:content_shell_crash_service', '../gpu/gpu.gyp:angle_end2end_tests', @@ -756,8 +749,6 @@ '../crypto/crypto.gyp:crypto_nacl_win64', '../ipc/ipc.gyp:ipc_win64', '../sandbox/sandbox.gyp:sandbox_win64', - '../cloud_print/virtual_driver/win/virtual_driver64.gyp:gcp_portmon64', - '../cloud_print/virtual_driver/win/virtual_driver64.gyp:virtual_driver_lib64', ], }], ['OS=="win" and target_arch=="ia32" and configuration_policy==1', {
diff --git a/build/gypi_to_gn.py b/build/gypi_to_gn.py index 6de1a63..a5175a40 100644 --- a/build/gypi_to_gn.py +++ b/build/gypi_to_gn.py
@@ -90,18 +90,17 @@ file_data.update(file_data['variables']) del file_data['variables'] - # Strip any conditions. - if 'conditions' in file_data: - del file_data['conditions'] - if 'target_conditions' in file_data: - del file_data['target_conditions'] - - # Strip targets and includes in the toplevel, since some files define these - # and we can't slurp them in. - if 'targets' in file_data: - del file_data['targets'] - if 'includes' in file_data: - del file_data['includes'] + # Strip all elements that this script can't process. + elements_to_strip = [ + 'conditions', + 'target_conditions', + 'targets', + 'includes', + 'actions', + ] + for element in elements_to_strip: + if element in file_data: + del file_data[element] return file_data
diff --git a/build/host_jar.gypi b/build/host_jar.gypi index 9f39d7c..3b8d548 100644 --- a/build/host_jar.gypi +++ b/build/host_jar.gypi
@@ -60,6 +60,7 @@ ], 'enable_errorprone%': 0, 'errorprone_exe_path': '<(PRODUCT_DIR)/bin.java/chromium_errorprone', + 'wrapper_script_name%': '<(_target_name)', }, 'all_dependent_settings': { 'variables': { @@ -122,7 +123,7 @@ 'action_name': 'create_java_binary_script_<(_target_name)', 'message': 'Creating java binary script <(_target_name)', 'variables': { - 'output': '<(PRODUCT_DIR)/bin/helper/<(_target_name)', + 'output': '<(PRODUCT_DIR)/bin/<(wrapper_script_name)', }, 'inputs': [ '<(DEPTH)/build/android/gyp/create_java_binary_script.py',
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index 69c41c9..d922e79 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -249,8 +249,6 @@ "output/overlay_candidate_validator.h", "output/overlay_processor.cc", "output/overlay_processor.h", - "output/overlay_strategy_sandwich.cc", - "output/overlay_strategy_sandwich.h", "output/overlay_strategy_single_on_top.cc", "output/overlay_strategy_single_on_top.h", "output/overlay_strategy_underlay.cc",
diff --git a/cc/base/switches.cc b/cc/base/switches.cc index 16f96d9..4e02de1 100644 --- a/cc/base/switches.cc +++ b/cc/base/switches.cc
@@ -45,22 +45,12 @@ const char kStrictLayerPropertyChangeChecking[] = "strict-layer-property-change-checking"; -// Ensures that the draw properties computed via the property trees match those -// computed by CalcDrawProperties. -const char kEnablePropertyTreeVerification[] = - "enable-property-tree-verification"; - // Use a BeginFrame signal from browser to renderer to schedule rendering. const char kEnableBeginFrameScheduling[] = "enable-begin-frame-scheduling"; // Enables the GPU benchmarking extension const char kEnableGpuBenchmarking[] = "enable-gpu-benchmarking"; -// Disables the use of property trees rather than CalcDrawProps for computing -// draw properties. -const char kDisableCompositorPropertyTrees[] = - "disable-compositor-property-trees"; - // Renders a border around compositor layers to help debug and study // layer compositing. const char kShowCompositedLayerBorders[] = "show-composited-layer-borders";
diff --git a/cc/base/switches.h b/cc/base/switches.h index 3465eaf9..63d806c 100644 --- a/cc/base/switches.h +++ b/cc/base/switches.h
@@ -25,12 +25,10 @@ CC_EXPORT extern const char kTopControlsShowThreshold[]; CC_EXPORT extern const char kSlowDownRasterScaleFactor[]; CC_EXPORT extern const char kStrictLayerPropertyChangeChecking[]; -CC_EXPORT extern const char kEnablePropertyTreeVerification[]; // Switches for both the renderer and ui compositors. CC_EXPORT extern const char kEnableBeginFrameScheduling[]; CC_EXPORT extern const char kEnableGpuBenchmarking[]; -CC_EXPORT extern const char kDisableCompositorPropertyTrees[]; // Debug visualizations. CC_EXPORT extern const char kShowCompositedLayerBorders[];
diff --git a/cc/cc.gyp b/cc/cc.gyp index a3cbc5f..8eba025 100644 --- a/cc/cc.gyp +++ b/cc/cc.gyp
@@ -311,8 +311,6 @@ 'output/overlay_candidate_validator.h', 'output/overlay_processor.cc', 'output/overlay_processor.h', - 'output/overlay_strategy_sandwich.cc', - 'output/overlay_strategy_sandwich.h', 'output/overlay_strategy_single_on_top.cc', 'output/overlay_strategy_single_on_top.h', 'output/overlay_strategy_underlay.cc',
diff --git a/cc/layers/heads_up_display_layer.cc b/cc/layers/heads_up_display_layer.cc index 158186f..a34ca42 100644 --- a/cc/layers/heads_up_display_layer.cc +++ b/cc/layers/heads_up_display_layer.cc
@@ -19,7 +19,14 @@ } HeadsUpDisplayLayer::HeadsUpDisplayLayer(const LayerSettings& settings) - : Layer(settings) { + : Layer(settings), + typeface_(skia::AdoptRef( + SkTypeface::CreateFromName("times new roman", SkTypeface::kNormal))) { + if (!typeface_) { + typeface_ = skia::AdoptRef( + SkTypeface::CreateFromName("monospace", SkTypeface::kBold)); + } + DCHECK(typeface_.get()); SetIsDrawable(true); UpdateDrawsContent(HasDrawableContent()); } @@ -64,7 +71,16 @@ void HeadsUpDisplayLayer::SetTypeForProtoSerialization( proto::LayerNode* proto) const { - proto->set_type(proto::LayerType::HEADS_UP_DISPLAY_LAYER); + proto->set_type(proto::LayerNode::HEADS_UP_DISPLAY_LAYER); +} + +void HeadsUpDisplayLayer::PushPropertiesTo(LayerImpl* layer) { + Layer::PushPropertiesTo(layer); + TRACE_EVENT0("cc", "HeadsUpDisplayLayer::PushPropertiesTo"); + HeadsUpDisplayLayerImpl* layer_impl = + static_cast<HeadsUpDisplayLayerImpl*>(layer); + + layer_impl->SetHUDTypeface(typeface_); } } // namespace cc
diff --git a/cc/layers/heads_up_display_layer.h b/cc/layers/heads_up_display_layer.h index 554d33e..23b3407a 100644 --- a/cc/layers/heads_up_display_layer.h +++ b/cc/layers/heads_up_display_layer.h
@@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "cc/layers/layer.h" +#include "third_party/skia/include/core/SkTypeface.h" namespace cc { @@ -30,6 +31,9 @@ void SetTypeForProtoSerialization(proto::LayerNode* proto) const override; + // Layer overrides. + void PushPropertiesTo(LayerImpl* layer) override; + protected: explicit HeadsUpDisplayLayer(const LayerSettings& settings); bool HasDrawableContent() const override; @@ -37,6 +41,8 @@ private: ~HeadsUpDisplayLayer() override; + skia::RefPtr<SkTypeface> typeface_; + DISALLOW_COPY_AND_ASSIGN(HeadsUpDisplayLayer); };
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc index 035ebc9..55e13e7 100644 --- a/cc/layers/heads_up_display_layer_impl.cc +++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -32,7 +32,6 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size_conversions.h" -#include "ui/gfx/hud_font.h" namespace cc { @@ -74,15 +73,10 @@ HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id), - typeface_(gfx::GetHudTypeface()), internal_contents_scale_(1.f), fps_graph_(60.0, 80.0), paint_time_graph_(16.0, 48.0), fade_step_(0) { - if (!typeface_) { - typeface_ = skia::AdoptRef( - SkTypeface::CreateFromName("monospace", SkTypeface::kBold)); - } } HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl() {} @@ -224,6 +218,25 @@ return GetScaledEnclosingRectInTargetSpace(internal_contents_scale_); } +void HeadsUpDisplayLayerImpl::SetHUDTypeface( + const skia::RefPtr<SkTypeface>& typeface) { + if (typeface_ == typeface) + return; + + DCHECK(typeface_.get() == nullptr); + typeface_ = typeface; + NoteLayerPropertyChanged(); +} + +void HeadsUpDisplayLayerImpl::PushPropertiesTo(LayerImpl* layer) { + LayerImpl::PushPropertiesTo(layer); + + HeadsUpDisplayLayerImpl* layer_impl = + static_cast<HeadsUpDisplayLayerImpl*>(layer); + + layer_impl->SetHUDTypeface(typeface_); +} + void HeadsUpDisplayLayerImpl::UpdateHudContents() { const LayerTreeDebugState& debug_state = layer_tree_impl()->debug_state(); @@ -275,6 +288,7 @@ int HeadsUpDisplayLayerImpl::MeasureText(SkPaint* paint, const std::string& text, int size) const { + DCHECK(typeface_.get()); const bool anti_alias = paint->isAntiAlias(); paint->setAntiAlias(true); paint->setTextSize(size); @@ -291,6 +305,7 @@ int size, int x, int y) const { + DCHECK(typeface_.get()); const bool anti_alias = paint->isAntiAlias(); paint->setAntiAlias(true); @@ -643,6 +658,7 @@ SkColor fill_color, float stroke_width, const std::string& label_text) const { + DCHECK(typeface_.get()); gfx::Rect debug_layer_rect = gfx::ScaleToEnclosingRect(rect.rect, 1.0 / internal_contents_scale_, 1.0 / internal_contents_scale_);
diff --git a/cc/layers/heads_up_display_layer_impl.h b/cc/layers/heads_up_display_layer_impl.h index 07e4bed..dfe9b0d 100644 --- a/cc/layers/heads_up_display_layer_impl.h +++ b/cc/layers/heads_up_display_layer_impl.h
@@ -50,6 +50,11 @@ bool IsAnimatingHUDContents() const { return fade_step_ > 0; } + void SetHUDTypeface(const skia::RefPtr<SkTypeface>& typeface); + + // LayerImpl overrides. + void PushPropertiesTo(LayerImpl* layer) override; + private: class Graph { public:
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc index 24eed8d..8d58b31a0 100644 --- a/cc/layers/layer.cc +++ b/cc/layers/layer.cc
@@ -1384,7 +1384,7 @@ } void Layer::SetTypeForProtoSerialization(proto::LayerNode* proto) const { - proto->set_type(proto::LayerType::LAYER); + proto->set_type(proto::LayerNode::LAYER); } void Layer::ToLayerNodeProto(proto::LayerNode* proto) const { @@ -1420,12 +1420,12 @@ DCHECK(child_proto.has_type()); scoped_refptr<Layer> child = LayerProtoConverter::FindOrAllocateAndConstruct(child_proto, layer_map); - child->FromLayerNodeProto(child_proto, layer_map); - children_.push_back(child); // The child must now refer to this layer as its parent, and must also have - // the same LayerTreeHost. + // the same LayerTreeHost. This must be done before deserializing children. child->parent_ = this; child->layer_tree_host_ = layer_tree_host_; + child->FromLayerNodeProto(child_proto, layer_map); + children_.push_back(child); } // Remove now-unused children from the tree. @@ -1441,26 +1441,30 @@ } } - if (mask_layer_) - mask_layer_->RemoveFromParent(); + if (mask_layer_) { + mask_layer_->parent_ = nullptr; + mask_layer_->layer_tree_host_ = nullptr; + } if (proto.has_mask_layer()) { mask_layer_ = LayerProtoConverter::FindOrAllocateAndConstruct( proto.mask_layer(), layer_map); + mask_layer_->parent_ = this; + mask_layer_->layer_tree_host_ = layer_tree_host_; mask_layer_->FromLayerNodeProto(proto.mask_layer(), layer_map); - mask_layer_->SetParent(this); - // SetIsMask() is only ever called with true, so no need to reset flag. - mask_layer_->SetIsMask(true); } else { mask_layer_ = nullptr; } - if (replica_layer_) - replica_layer_->RemoveFromParent(); + if (replica_layer_) { + replica_layer_->parent_ = nullptr; + replica_layer_->layer_tree_host_ = nullptr; + } if (proto.has_replica_layer()) { replica_layer_ = LayerProtoConverter::FindOrAllocateAndConstruct( proto.replica_layer(), layer_map); + replica_layer_->parent_ = this; + replica_layer_->layer_tree_host_ = layer_tree_host_; replica_layer_->FromLayerNodeProto(proto.replica_layer(), layer_map); - replica_layer_->SetParent(this); } else { replica_layer_ = nullptr; }
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index a938515..fb9075fe 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc
@@ -1723,10 +1723,8 @@ } gfx::Transform LayerImpl::DrawTransform() const { - // Only drawn layers have up-to-date draw properties when property trees are - // enabled. - if (layer_tree_impl()->settings().use_property_trees && - !IsDrawnRenderSurfaceLayerListMember()) { + // Only drawn layers have up-to-date draw properties. + if (!IsDrawnRenderSurfaceLayerListMember()) { if (layer_tree_impl()->property_trees()->non_root_surfaces_enabled) { return DrawTransformFromPropertyTrees( this, layer_tree_impl()->property_trees()->transform_tree); @@ -1740,10 +1738,8 @@ } gfx::Transform LayerImpl::ScreenSpaceTransform() const { - // Only drawn layers have up-to-date draw properties when property trees are - // enabled. - if (layer_tree_impl()->settings().use_property_trees && - !IsDrawnRenderSurfaceLayerListMember()) { + // Only drawn layers have up-to-date draw properties. + if (!IsDrawnRenderSurfaceLayerListMember()) { return ScreenSpaceTransformFromPropertyTrees( this, layer_tree_impl()->property_trees()->transform_tree); } @@ -1777,13 +1773,9 @@ } bool LayerImpl::IsHidden() const { - if (layer_tree_impl()->settings().use_property_trees) { - EffectTree& effect_tree = layer_tree_impl_->property_trees()->effect_tree; - EffectNode* node = effect_tree.Node(effect_tree_index_); - return node->data.screen_space_opacity == 0.f; - } else { - return EffectiveOpacity() == 0.f || (parent() && parent()->IsHidden()); - } + EffectTree& effect_tree = layer_tree_impl_->property_trees()->effect_tree; + EffectNode* node = effect_tree.Node(effect_tree_index_); + return node->data.screen_space_opacity == 0.f; } float LayerImpl::GetIdealContentsScale() const {
diff --git a/cc/layers/layer_proto_converter.cc b/cc/layers/layer_proto_converter.cc index 399985c..19215c9 100644 --- a/cc/layers/layer_proto_converter.cc +++ b/cc/layers/layer_proto_converter.cc
@@ -111,13 +111,13 @@ // Fall through and build a base layer. This won't have any special layer // properties but still maintains the layer hierarchy if we run into a // layer type we don't support. - case proto::UNKNOWN: - case proto::LAYER: + case proto::LayerNode::UNKNOWN: + case proto::LayerNode::LAYER: return Layer::Create(LayerSettings()).get(); - case proto::PICTURE_LAYER: + case proto::LayerNode::PICTURE_LAYER: return PictureLayer::Create(LayerSettings(), EmptyContentLayerClient::GetInstance()); - case proto::HEADS_UP_DISPLAY_LAYER: + case proto::LayerNode::HEADS_UP_DISPLAY_LAYER: return HeadsUpDisplayLayer::Create(LayerSettings()); } // TODO(nyquist): Add the rest of the necessary LayerTypes. This function
diff --git a/cc/layers/layer_proto_converter_unittest.cc b/cc/layers/layer_proto_converter_unittest.cc index fbd13e1..e72ab584 100644 --- a/cc/layers/layer_proto_converter_unittest.cc +++ b/cc/layers/layer_proto_converter_unittest.cc
@@ -51,21 +51,21 @@ scoped_refptr<Layer> old_root = Layer::Create(LayerSettings()); proto::LayerNode root_node; root_node.set_id(old_root->id()); - root_node.set_type(proto::LayerType::LAYER); + root_node.set_type(proto::LayerNode::LAYER); proto::LayerNode* child_a_node = root_node.add_children(); child_a_node->set_id(442); - child_a_node->set_type(proto::LayerType::LAYER); + child_a_node->set_type(proto::LayerNode::LAYER); child_a_node->set_parent_id(old_root->id()); // root_node proto::LayerNode* child_b_node = root_node.add_children(); child_b_node->set_id(443); - child_b_node->set_type(proto::LayerType::LAYER); + child_b_node->set_type(proto::LayerNode::LAYER); child_b_node->set_parent_id(old_root->id()); // root_node proto::LayerNode* child_c_node = child_b_node->add_children(); child_c_node->set_id(444); - child_c_node->set_type(proto::LayerType::LAYER); + child_c_node->set_type(proto::LayerNode::LAYER); child_c_node->set_parent_id(child_b_node->id()); scoped_refptr<Layer> new_root = @@ -97,11 +97,11 @@ int new_root_id = 244; proto::LayerNode root_node; root_node.set_id(new_root_id); - root_node.set_type(proto::LayerType::LAYER); + root_node.set_type(proto::LayerNode::LAYER); proto::LayerNode* child_a_node = root_node.add_children(); child_a_node->set_id(442); - child_a_node->set_type(proto::LayerType::LAYER); + child_a_node->set_type(proto::LayerNode::LAYER); child_a_node->set_parent_id(new_root_id); // root_node scoped_refptr<Layer> new_root = @@ -127,21 +127,21 @@ */ proto::LayerNode root_node; root_node.set_id(441); - root_node.set_type(proto::LayerType::LAYER); + root_node.set_type(proto::LayerNode::LAYER); proto::LayerNode* child_a_node = root_node.add_children(); child_a_node->set_id(442); - child_a_node->set_type(proto::LayerType::LAYER); + child_a_node->set_type(proto::LayerNode::LAYER); child_a_node->set_parent_id(root_node.id()); proto::LayerNode* child_b_node = root_node.add_children(); child_b_node->set_id(443); - child_b_node->set_type(proto::LayerType::LAYER); + child_b_node->set_type(proto::LayerNode::LAYER); child_b_node->set_parent_id(root_node.id()); proto::LayerNode* child_c_node = child_b_node->add_children(); child_c_node->set_id(444); - child_c_node->set_type(proto::LayerType::LAYER); + child_c_node->set_type(proto::LayerNode::LAYER); child_c_node->set_parent_id(child_b_node->id()); scoped_refptr<Layer> old_root = Layer::Create(LayerSettings()); @@ -379,7 +379,7 @@ proto::LayerNode layer_hierarchy; LayerProtoConverter::SerializeLayerHierarchy(layer.get(), &layer_hierarchy); - EXPECT_EQ(proto::LayerType::PICTURE_LAYER, layer_hierarchy.type()); + EXPECT_EQ(proto::LayerNode::PICTURE_LAYER, layer_hierarchy.type()); } TEST_F(LayerProtoConverterTest, PictureLayerTypeDeserialization) { @@ -389,7 +389,7 @@ LayerSettings(), EmptyContentLayerClient::GetInstance()); proto::LayerNode root_node; root_node.set_id(old_root->id()); - root_node.set_type(proto::LayerType::PICTURE_LAYER); + root_node.set_type(proto::LayerNode::PICTURE_LAYER); scoped_refptr<Layer> new_root = LayerProtoConverter::DeserializeLayerHierarchy(old_root, root_node); @@ -401,7 +401,7 @@ // serialize to. proto::LayerNode layer_node; new_root->SetTypeForProtoSerialization(&layer_node); - EXPECT_EQ(proto::LayerType::PICTURE_LAYER, layer_node.type()); + EXPECT_EQ(proto::LayerNode::PICTURE_LAYER, layer_node.type()); } TEST_F(LayerProtoConverterTest, HudLayerTypeSerialization) { @@ -412,7 +412,7 @@ proto::LayerNode layer_hierarchy; LayerProtoConverter::SerializeLayerHierarchy(layer.get(), &layer_hierarchy); - EXPECT_EQ(proto::LayerType::HEADS_UP_DISPLAY_LAYER, layer_hierarchy.type()); + EXPECT_EQ(proto::LayerNode::HEADS_UP_DISPLAY_LAYER, layer_hierarchy.type()); } TEST_F(LayerProtoConverterTest, HudLayerTypeDeserialization) { @@ -421,7 +421,7 @@ scoped_refptr<Layer> old_root = HeadsUpDisplayLayer::Create(LayerSettings()); proto::LayerNode root_node; root_node.set_id(old_root->id()); - root_node.set_type(proto::LayerType::HEADS_UP_DISPLAY_LAYER); + root_node.set_type(proto::LayerNode::HEADS_UP_DISPLAY_LAYER); scoped_refptr<Layer> new_root = LayerProtoConverter::DeserializeLayerHierarchy(old_root, root_node); @@ -433,7 +433,7 @@ // serialize to. proto::LayerNode layer_node; new_root->SetTypeForProtoSerialization(&layer_node); - EXPECT_EQ(proto::LayerType::HEADS_UP_DISPLAY_LAYER, layer_node.type()); + EXPECT_EQ(proto::LayerNode::HEADS_UP_DISPLAY_LAYER, layer_node.type()); } } // namespace
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc index 3ecf5ac..ce71b08 100644 --- a/cc/layers/layer_unittest.cc +++ b/cc/layers/layer_unittest.cc
@@ -369,6 +369,69 @@ VerifyBaseLayerPropertiesSerializationAndDeserialization(layer.get()); } + void RunHierarchyDeserializationWithLayerTreeHostTest() { + /* Testing serialization and deserialization of a tree that looks like this: + root + \ + a + \ + b + \ + c + The root layer has a LayerTreeHost, and it should propagate to all the + children. + */ + scoped_refptr<Layer> layer_src_root = Layer::Create(LayerSettings()); + scoped_refptr<Layer> layer_src_a = Layer::Create(LayerSettings()); + scoped_refptr<Layer> layer_src_b = Layer::Create(LayerSettings()); + scoped_refptr<Layer> layer_src_c = Layer::Create(LayerSettings()); + layer_src_root->AddChild(layer_src_a); + layer_src_a->AddChild(layer_src_b); + layer_src_b->AddChild(layer_src_c); + + proto::LayerNode proto; + layer_src_root->ToLayerNodeProto(&proto); + + Layer::LayerIdMap empty_dest_layer_map; + scoped_refptr<Layer> layer_dest_root = Layer::Create(LayerSettings()); + + // Forcefully set the layer tree host for the root layer, which should cause + // it to propagate to all the children. + layer_dest_root->layer_tree_host_ = layer_tree_host_.get(); + + layer_dest_root->FromLayerNodeProto(proto, empty_dest_layer_map); + + EXPECT_EQ(layer_src_root->id(), layer_dest_root->id()); + EXPECT_EQ(nullptr, layer_dest_root->parent()); + ASSERT_EQ(1u, layer_dest_root->children().size()); + EXPECT_EQ(layer_tree_host_.get(), layer_dest_root->layer_tree_host_); + + scoped_refptr<Layer> layer_dest_a = layer_dest_root->children()[0]; + EXPECT_EQ(layer_src_a->id(), layer_dest_a->id()); + EXPECT_EQ(layer_src_root->id(), layer_dest_a->parent()->id()); + EXPECT_EQ(1u, layer_dest_a->children().size()); + EXPECT_EQ(layer_tree_host_.get(), layer_dest_a->layer_tree_host_); + + scoped_refptr<Layer> layer_dest_b = layer_dest_a->children()[0]; + EXPECT_EQ(layer_src_b->id(), layer_dest_b->id()); + EXPECT_EQ(layer_src_a->id(), layer_dest_b->parent()->id()); + ASSERT_EQ(1u, layer_dest_b->children().size()); + EXPECT_EQ(layer_tree_host_.get(), layer_dest_b->layer_tree_host_); + + scoped_refptr<Layer> layer_dest_c = layer_dest_b->children()[0]; + EXPECT_EQ(layer_src_c->id(), layer_dest_c->id()); + EXPECT_EQ(layer_src_b->id(), layer_dest_c->parent()->id()); + EXPECT_EQ(0u, layer_dest_c->children().size()); + EXPECT_EQ(layer_tree_host_.get(), layer_dest_c->layer_tree_host_); + + // The layers have not been added to the LayerTreeHost layer map, so the + // LTH pointers must be cleared manually. + layer_dest_root->layer_tree_host_ = nullptr; + layer_dest_a->layer_tree_host_ = nullptr; + layer_dest_b->layer_tree_host_ = nullptr; + layer_dest_c->layer_tree_host_ = nullptr; + } + void RunNonDestructiveDeserializationBaseCaseTest() { /* Testing serialization and deserialization of a tree that initially looks like this: @@ -2494,6 +2557,10 @@ EXPECT_EQ(nullptr, layer_dest_root->replica_layer()); } +TEST_F(LayerSerializationTest, HierarchyDeserializationWithLayerTreeHost) { + RunHierarchyDeserializationWithLayerTreeHostTest(); +} + TEST_F(LayerSerializationTest, NonDestructiveDeserializationBaseCase) { RunNonDestructiveDeserializationBaseCaseTest(); }
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc index 17c920f..afb62c0 100644 --- a/cc/layers/picture_layer.cc +++ b/cc/layers/picture_layer.cc
@@ -186,7 +186,7 @@ } void PictureLayer::SetTypeForProtoSerialization(proto::LayerNode* proto) const { - proto->set_type(proto::LayerType::PICTURE_LAYER); + proto->set_type(proto::LayerNode::PICTURE_LAYER); } void PictureLayer::LayerSpecificPropertiesToProto( @@ -213,6 +213,12 @@ const proto::LayerProperties& proto) { Layer::FromLayerSpecificPropertiesProto(proto); const proto::PictureLayerProperties& picture = proto.picture(); + // If this is a new layer, ensure it has a recording source. During layer + // hierarchy deserialization, ::SetLayerTreeHost(...) is not called, but + // instead the member is set directly, so it needs to be set here explicitly. + if (!recording_source_) + recording_source_.reset(new DisplayListRecordingSource); + recording_source_->FromProtobuf( picture.recording_source(), layer_tree_host()->image_serialization_processor());
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 1447442..0f73ef191 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -84,7 +84,6 @@ public: PictureLayerImplTestSettings() { layer_transforms_should_scale_layer_contents = true; - verify_property_trees = true; } };
diff --git a/cc/layers/picture_layer_unittest.cc b/cc/layers/picture_layer_unittest.cc index 7c35d554..64f071a0 100644 --- a/cc/layers/picture_layer_unittest.cc +++ b/cc/layers/picture_layer_unittest.cc
@@ -145,7 +145,6 @@ FakeLayerTreeHostClient host_client(FakeLayerTreeHostClient::DIRECT_3D); TestTaskGraphRunner task_graph_runner; LayerTreeSettings settings; - settings.verify_property_trees = true; settings.use_compositor_animation_timelines = true; scoped_ptr<FakeImageSerializationProcessor> fake_image_serialization_processor =
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc index 72f0fea..a9b641d6 100644 --- a/cc/layers/render_surface_impl.cc +++ b/cc/layers/render_surface_impl.cc
@@ -123,15 +123,6 @@ content_rect_ = content_rect; } -void RenderSurfaceImpl::SetContentRectFromPropertyTrees( - const gfx::Rect& content_rect) { - if (content_rect_from_property_trees_ == content_rect) - return; - - surface_property_changed_ = true; - content_rect_from_property_trees_ = content_rect; -} - void RenderSurfaceImpl::SetAccumulatedContentRect( const gfx::Rect& content_rect) { accumulated_content_rect_ = content_rect;
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h index 613bf897..410a640 100644 --- a/cc/layers/render_surface_impl.h +++ b/cc/layers/render_surface_impl.h
@@ -108,11 +108,6 @@ void SetContentRect(const gfx::Rect& content_rect); gfx::Rect content_rect() const { return content_rect_; } - void SetContentRectFromPropertyTrees(const gfx::Rect& content_rect); - gfx::Rect content_rect_from_property_trees() const { - return content_rect_from_property_trees_; - } - void SetAccumulatedContentRect(const gfx::Rect& content_rect); gfx::Rect accumulated_content_rect() const { return accumulated_content_rect_; @@ -159,7 +154,6 @@ // Uses this surface's space. gfx::Rect content_rect_; - gfx::Rect content_rect_from_property_trees_; // Is used to calculate the content rect from property trees. gfx::Rect accumulated_content_rect_; bool surface_property_changed_ : 1;
diff --git a/cc/output/overlay_strategy_sandwich.cc b/cc/output/overlay_strategy_sandwich.cc deleted file mode 100644 index ce590b9..0000000 --- a/cc/output/overlay_strategy_sandwich.cc +++ /dev/null
@@ -1,183 +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. - -#include "cc/output/overlay_strategy_sandwich.h" - -#include "cc/base/math_util.h" -#include "cc/base/region.h" -#include "cc/output/overlay_candidate_validator.h" -#include "cc/quads/draw_quad.h" -#include "cc/quads/solid_color_draw_quad.h" - -namespace cc { - -namespace { - -void ClipDisplayAndUVRects(gfx::Rect* display_rect, - gfx::RectF* uv_rect, - const gfx::Rect& clip_rect) { - gfx::Rect display_cropped_rect = - gfx::IntersectRects(*display_rect, clip_rect); - - gfx::RectF uv_cropped_rect = gfx::RectF(display_cropped_rect); - uv_cropped_rect -= gfx::Vector2dF(display_rect->x(), display_rect->y()); - uv_cropped_rect.Scale(uv_rect->width() / display_rect->width(), - uv_rect->height() / display_rect->height()); - uv_cropped_rect += gfx::Vector2dF(uv_rect->x(), uv_rect->y()); - - *display_rect = display_cropped_rect; - *uv_rect = uv_cropped_rect; -} - -} // namespace - -OverlayStrategySandwich::OverlayStrategySandwich( - OverlayCandidateValidator* capability_checker) - : capability_checker_(capability_checker) { - DCHECK(capability_checker); -} - -OverlayStrategySandwich::~OverlayStrategySandwich() {} - -bool OverlayStrategySandwich::Attempt(ResourceProvider* resource_provider, - RenderPassList* render_passes, - OverlayCandidateList* candidate_list) { - QuadList& quad_list = render_passes->back()->quad_list; - for (auto it = quad_list.begin(); it != quad_list.end();) { - OverlayCandidate candidate; - if (OverlayCandidate::FromDrawQuad(resource_provider, *it, &candidate)) { - it = TryOverlay(render_passes->back().get(), candidate_list, candidate, - it); - } else { - ++it; - } - } - - return candidate_list->size() > 1; -} - -QuadList::Iterator OverlayStrategySandwich::TryOverlay( - RenderPass* render_pass, - OverlayCandidateList* candidate_list, - const OverlayCandidate& candidate, - QuadList::Iterator candidate_iter_in_quad_list) { - QuadList& quad_list = render_pass->quad_list; - gfx::Rect pixel_bounds = render_pass->output_rect; - - const DrawQuad* candidate_quad = *candidate_iter_in_quad_list; - const gfx::Transform& candidate_transform = - candidate_quad->shared_quad_state->quad_to_target_transform; - gfx::Transform candidate_inverse_transform; - if (!candidate_transform.GetInverse(&candidate_inverse_transform)) - return ++candidate_iter_in_quad_list; - - // Compute the candidate's rect in display space (pixels on the screen). - gfx::Rect candidate_pixel_rect = candidate.quad_rect_in_target_space; - gfx::RectF candidate_uv_rect = candidate.uv_rect; - if (candidate.is_clipped && - !candidate.clip_rect.Contains(candidate_pixel_rect)) { - ClipDisplayAndUVRects(&candidate_pixel_rect, &candidate_uv_rect, - candidate.clip_rect); - } - - // Don't allow overlapping overlays for now. - for (const OverlayCandidate& other_candidate : *candidate_list) { - if (other_candidate.display_rect.Intersects(candidate.display_rect) && - other_candidate.plane_z_order == 1) { - return ++candidate_iter_in_quad_list; - } - } - - // Iterate through the quads in front of |candidate|, and compute the region - // of |candidate| that is covered. - Region pixel_covered_region; - for (auto overlap_iter = quad_list.cbegin(); - overlap_iter != candidate_iter_in_quad_list; ++overlap_iter) { - if (OverlayCandidate::IsInvisibleQuad(*overlap_iter)) - continue; - // Compute the quad's bounds in display space. - gfx::Rect pixel_covered_rect = MathUtil::MapEnclosingClippedRect( - overlap_iter->shared_quad_state->quad_to_target_transform, - overlap_iter->rect); - - // Include the intersection of that quad with the candidate's quad in the - // covered region. - pixel_covered_rect.Intersect(candidate_pixel_rect); - pixel_covered_region.Union(pixel_covered_rect); - } - - // Add the candidate's overlay. - DCHECK(candidate.resource_id); - OverlayCandidateList new_candidate_list = *candidate_list; - new_candidate_list.push_back(candidate); - OverlayCandidate& new_candidate = new_candidate_list.back(); - new_candidate.plane_z_order = 1; - new_candidate.display_rect = gfx::RectF(candidate_pixel_rect); - new_candidate.quad_rect_in_target_space = candidate_pixel_rect; - new_candidate.uv_rect = candidate_uv_rect; - - // Add an overlay of the primary surface for any part of the candidate's - // quad that was covered. - std::vector<gfx::Rect> pixel_covered_rects; - for (Region::Iterator it(pixel_covered_region); it.has_rect(); it.next()) - pixel_covered_rects.push_back(it.rect()); - for (const gfx::Rect& pixel_covered_rect : pixel_covered_rects) { - OverlayCandidate main_image_on_top; - main_image_on_top.display_rect = gfx::RectF(pixel_covered_rect); - main_image_on_top.uv_rect = gfx::RectF(pixel_covered_rect); - main_image_on_top.uv_rect.Scale(1.f / pixel_bounds.width(), - 1.f / pixel_bounds.height()); - main_image_on_top.plane_z_order = 2; - main_image_on_top.transform = gfx::OVERLAY_TRANSFORM_NONE; - main_image_on_top.use_output_surface_for_resource = true; - new_candidate_list.push_back(main_image_on_top); - } - - // Check for support. - capability_checker_->CheckOverlaySupport(&new_candidate_list); - for (const OverlayCandidate& candidate : new_candidate_list) { - if (!candidate.overlay_handled) - return ++candidate_iter_in_quad_list; - } - - // Remove the quad for the overlay quad. Replace it with a transparent quad - // if we're putting a new overlay on top. - if (pixel_covered_rects.empty()) { - candidate_iter_in_quad_list = - quad_list.EraseAndInvalidateAllPointers(candidate_iter_in_quad_list); - } else { - // Cache the information from the candidate quad that we'll need to - // construct the solid color quads. - const SharedQuadState* candidate_shared_quad_state = - candidate_quad->shared_quad_state; - const gfx::Rect candidate_rect = candidate_quad->rect; - - // Reserve space in the quad list for the transparent quads. - quad_list.ReplaceExistingElement<SolidColorDrawQuad>( - candidate_iter_in_quad_list); - candidate_iter_in_quad_list = - quad_list.InsertBeforeAndInvalidateAllPointers<SolidColorDrawQuad>( - candidate_iter_in_quad_list, pixel_covered_rects.size() - 1); - - // Cover the region with transparent quads. - for (const gfx::Rect& pixel_covered_rect : pixel_covered_rects) { - gfx::Rect quad_space_covered_rect = MathUtil::MapEnclosingClippedRect( - candidate_inverse_transform, pixel_covered_rect); - quad_space_covered_rect.Intersect(candidate_rect); - - SolidColorDrawQuad* transparent_quad = - static_cast<SolidColorDrawQuad*>(*candidate_iter_in_quad_list); - transparent_quad->SetAll(candidate_shared_quad_state, - quad_space_covered_rect, quad_space_covered_rect, - quad_space_covered_rect, false, - SK_ColorTRANSPARENT, true); - ++candidate_iter_in_quad_list; - } - } - - candidate_list->swap(new_candidate_list); - return candidate_iter_in_quad_list; -} - -} // namespace cc
diff --git a/cc/output/overlay_strategy_sandwich.h b/cc/output/overlay_strategy_sandwich.h deleted file mode 100644 index f46dd03..0000000 --- a/cc/output/overlay_strategy_sandwich.h +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_OUTPUT_OVERLAY_STRATEGY_SANDWICH_H_ -#define CC_OUTPUT_OVERLAY_STRATEGY_SANDWICH_H_ - -#include "base/macros.h" -#include "cc/output/overlay_processor.h" - -namespace cc { - -class OverlayCandidateValidator; - -// The sandwich strategy looks for a video quad without regard to quads above -// it. The video is "overlaid" on top of the scene, and then the non-empty -// parts of the scene are "overlaid" on top of the video. This is only valid -// for overlay contents that are fully opaque. -class CC_EXPORT OverlayStrategySandwich : public OverlayProcessor::Strategy { - public: - explicit OverlayStrategySandwich( - OverlayCandidateValidator* capability_checker); - ~OverlayStrategySandwich() override; - - bool Attempt(ResourceProvider* resource_provider, - RenderPassList* render_passes, - OverlayCandidateList* candidate_list) override; - - private: - QuadList::Iterator TryOverlay(RenderPass* render_pass, - OverlayCandidateList* candidate_list, - const OverlayCandidate& candidate, - QuadList::Iterator candidate_iterator); - - OverlayCandidateValidator* capability_checker_; // Weak. - - DISALLOW_COPY_AND_ASSIGN(OverlayStrategySandwich); -}; - -} // namespace cc - -#endif // CC_OUTPUT_OVERLAY_STRATEGY_SANDWICH_H_
diff --git a/cc/output/overlay_unittest.cc b/cc/output/overlay_unittest.cc index 9b3bf97..bc5cf561 100644 --- a/cc/output/overlay_unittest.cc +++ b/cc/output/overlay_unittest.cc
@@ -13,7 +13,6 @@ #include "cc/output/output_surface_client.h" #include "cc/output/overlay_candidate_validator.h" #include "cc/output/overlay_processor.h" -#include "cc/output/overlay_strategy_sandwich.h" #include "cc/output/overlay_strategy_single_on_top.h" #include "cc/output/overlay_strategy_underlay.h" #include "cc/quads/render_pass.h" @@ -117,18 +116,6 @@ } }; -class SandwichOverlayValidator : public OverlayCandidateValidator { - public: - void GetStrategies(OverlayProcessor::StrategyList* strategies) override { - strategies->push_back(make_scoped_ptr(new OverlayStrategySandwich(this))); - } - bool AllowCALayerOverlays() override { return false; } - void CheckOverlaySupport(OverlayCandidateList* surfaces) override { - for (OverlayCandidate& candidate : *surfaces) - candidate.overlay_handled = true; - } -}; - class DefaultOverlayProcessor : public OverlayProcessor { public: explicit DefaultOverlayProcessor(OutputSurface* surface); @@ -373,7 +360,6 @@ typedef OverlayTest<SingleOnTopOverlayValidator> SingleOverlayOnTopTest; typedef OverlayTest<UnderlayOverlayValidator> UnderlayTest; -typedef OverlayTest<SandwichOverlayValidator> SandwichTest; typedef OverlayTest<CALayerValidator> CALayerOverlayTest; TEST(OverlayTest, NoOverlaysByDefault) { @@ -403,325 +389,6 @@ EXPECT_GE(2U, overlay_processor->GetStrategyCount()); } -TEST_F(SandwichTest, SuccessfulSingleOverlay) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - TextureDrawQuad* original_quad = CreateFullscreenCandidateQuad( - resource_provider_.get(), pass->shared_quad_state_list.back(), - pass.get()); - unsigned original_resource_id = original_quad->resource_id(); - - // Add something behind it. - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - - // Check for potential candidates. - OverlayCandidateList candidate_list; - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &candidate_list, nullptr, - &damage_rect_); - - ASSERT_EQ(1U, pass_list.size()); - ASSERT_EQ(1U, candidate_list.size()); - - RenderPass* main_pass = pass_list.back().get(); - // Check that the quad is gone. - EXPECT_EQ(2U, main_pass->quad_list.size()); - const QuadList& quad_list = main_pass->quad_list; - for (QuadList::ConstBackToFrontIterator it = quad_list.BackToFrontBegin(); - it != quad_list.BackToFrontEnd(); ++it) { - EXPECT_NE(DrawQuad::TEXTURE_CONTENT, it->material); - } - - // Check that the right resource id got extracted. - EXPECT_EQ(original_resource_id, candidate_list.back().resource_id); -} - -TEST_F(SandwichTest, CroppedSingleOverlay) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - pass->shared_quad_state_list.back()->is_clipped = true; - pass->shared_quad_state_list.back()->clip_rect = gfx::Rect(0, 32, 64, 64); - - TextureDrawQuad* original_quad = CreateFullscreenCandidateQuad( - resource_provider_.get(), pass->shared_quad_state_list.back(), - pass.get()); - original_quad->uv_top_left = gfx::PointF(0, 0); - original_quad->uv_bottom_right = gfx::PointF(1, 1); - unsigned candidate_id = original_quad->resource_id(); - - // Add something behind it. - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - - // Check for potential candidates. - OverlayCandidateList candidate_list; - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &candidate_list, nullptr, - &damage_rect_); - - // Ensure that the display and uv rects have cropping applied to them. - ASSERT_EQ(1U, pass_list.size()); - ASSERT_EQ(1U, candidate_list.size()); - EXPECT_EQ(candidate_id, candidate_list[0].resource_id); - EXPECT_EQ(gfx::RectF(0.f, 32.f, 64.f, 64.f), candidate_list[0].display_rect); - EXPECT_EQ(gfx::RectF(0.f, 0.25f, 0.5f, 0.5f), candidate_list[0].uv_rect); -} - -TEST_F(SandwichTest, SuccessfulTwoOverlays) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - - // Add two non-overlapping candidates. - CreateCandidateQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - kOverlayTopLeftRect); - CreateCandidateQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - kOverlayBottomRightRect); - - // Add something behind it. - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - OverlayCandidateList candidate_list; - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &candidate_list, nullptr, - &damage_rect_); - - // Both candidates should become overlays. - EXPECT_EQ(1u, pass_list.size()); - EXPECT_EQ(2u, candidate_list.size()); - EXPECT_EQ(gfx::RectF(kOverlayTopLeftRect), candidate_list[0].display_rect); - EXPECT_EQ(gfx::RectF(kOverlayBottomRightRect), - candidate_list[1].display_rect); - - // The overlay quads should be gone. - const QuadList& quad_list = pass_list.back()->quad_list; - EXPECT_EQ(1u, quad_list.size()); - EXPECT_EQ(DrawQuad::SOLID_COLOR, quad_list.front()->material); -} - -TEST_F(SandwichTest, OverlappingOverlays) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - - // Add two overlapping candidates. - CreateCandidateQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - kOverlayTopLeftRect); - CreateCandidateQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - kOverlayRect); - - // Add something behind it. - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - OverlayCandidateList candidate_list; - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &candidate_list, nullptr, - &damage_rect_); - - // Only one of the candidates should become an overlay. - EXPECT_EQ(1u, pass_list.size()); - EXPECT_EQ(1u, candidate_list.size()); - EXPECT_EQ(gfx::RectF(kOverlayTopLeftRect), candidate_list[0].display_rect); - - // One of the overlay quads should be gone. - const QuadList& quad_list = pass_list.back()->quad_list; - EXPECT_EQ(2u, quad_list.size()); - EXPECT_EQ(DrawQuad::TEXTURE_CONTENT, quad_list.front()->material); - EXPECT_EQ(DrawQuad::SOLID_COLOR, quad_list.back()->material); -} - -TEST_F(SandwichTest, SuccessfulSandwichOverlay) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - - CreateOpaqueQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - gfx::Rect(16, 16, 32, 32)); - unsigned candidate_id = - CreateCandidateQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - gfx::Rect(32, 32, 32, 32)) - ->resource_id(); - CreateOpaqueQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - gfx::Rect(kDisplaySize)); - - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - - // Check for potential candidates. - OverlayCandidateList candidate_list; - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &candidate_list, nullptr, - &damage_rect_); - ASSERT_EQ(1U, pass_list.size()); - ASSERT_EQ(2U, candidate_list.size()); - - RenderPass* main_pass = pass_list.back().get(); - // Check that the quad is gone. - EXPECT_EQ(3U, main_pass->quad_list.size()); - const QuadList& quad_list = main_pass->quad_list; - for (QuadList::ConstBackToFrontIterator it = quad_list.BackToFrontBegin(); - it != quad_list.BackToFrontEnd(); ++it) { - EXPECT_NE(DrawQuad::TEXTURE_CONTENT, it->material); - } - - EXPECT_EQ(candidate_id, candidate_list[0].resource_id); - EXPECT_EQ(gfx::RectF(32.f, 32.f, 32.f, 32.f), candidate_list[0].display_rect); - EXPECT_TRUE(candidate_list[1].use_output_surface_for_resource); - EXPECT_EQ(gfx::RectF(32.f, 32.f, 16.f, 16.f), candidate_list[1].display_rect); - EXPECT_EQ(gfx::RectF(32.f / 256.f, 32.f / 256.f, 16.f / 256.f, 16.f / 256.f), - candidate_list[1].uv_rect); -} - -TEST_F(SandwichTest, MultiQuadOverlay) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - - // Put two non-intersecting quads on top. - const gfx::Rect rect1(gfx::Rect(0, 0, 32, 32)); - const gfx::Rect rect2(gfx::Rect(32, 32, 32, 32)); - Region covered_region; - covered_region.Union(rect1); - covered_region.Union(rect2); - CreateOpaqueQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), rect1); - CreateOpaqueQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), rect2); - - // Then a candidate that we'll turn into an overlay. - unsigned candidate_id = - CreateCandidateQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - gfx::Rect(0, 0, 64, 64)) - ->resource_id(); - - // Then some opaque background. - CreateOpaqueQuadAt(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get(), - gfx::Rect(kDisplaySize)); - - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - - // Run the overlay strategy on that input. - RenderPass* main_pass = pass_list.back().get(); - OverlayCandidateList candidate_list; - EXPECT_EQ(4U, main_pass->quad_list.size()); - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &candidate_list, nullptr, - &damage_rect_); - ASSERT_EQ(1U, pass_list.size()); - ASSERT_EQ(3U, candidate_list.size()); - - // Check that the candidate quad is gone and that we now have two transparent - // quads for the same region that was covered on the overlay. - EXPECT_EQ(5U, main_pass->quad_list.size()); - const QuadList& quad_list = main_pass->quad_list; - Region transparent_quad_region; - for (QuadList::ConstBackToFrontIterator it = quad_list.BackToFrontBegin(); - it != quad_list.BackToFrontEnd(); ++it) { - EXPECT_NE(DrawQuad::TEXTURE_CONTENT, it->material); - if (it->material == DrawQuad::SOLID_COLOR) { - const SolidColorDrawQuad* solid_color_quad = - SolidColorDrawQuad::MaterialCast(*it); - if (solid_color_quad->color == SK_ColorTRANSPARENT) - transparent_quad_region.Union(solid_color_quad->rect); - } - } - DCHECK(covered_region == transparent_quad_region); - - // Check that overlays cover the same region that the quads covered. - EXPECT_EQ(candidate_id, candidate_list[0].resource_id); - EXPECT_EQ(gfx::RectF(64.f, 64.f), candidate_list[0].display_rect); - EXPECT_TRUE(candidate_list[1].use_output_surface_for_resource); - EXPECT_TRUE(candidate_list[2].use_output_surface_for_resource); - Region overlay_region; - overlay_region.Union(gfx::ToEnclosingRect(candidate_list[1].display_rect)); - overlay_region.Union(gfx::ToEnclosingRect(candidate_list[2].display_rect)); - DCHECK(covered_region == overlay_region); -} - -TEST_F(SandwichTest, DamageRect) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - CreateFullscreenCandidateQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), - pass.get()); - - damage_rect_ = kOverlayRect; - - // Add something behind it. - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - - // Check for potential candidates. - OverlayCandidateList candidate_list; - - // Primary plane. - OverlayCandidate output_surface_plane; - output_surface_plane.display_rect = gfx::RectF(kOverlayRect); - output_surface_plane.quad_rect_in_target_space = kOverlayRect; - output_surface_plane.use_output_surface_for_resource = true; - output_surface_plane.overlay_handled = true; - candidate_list.push_back(output_surface_plane); - - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &candidate_list, nullptr, - &damage_rect_); - EXPECT_EQ(2u, candidate_list.size()); - EXPECT_TRUE(damage_rect_.IsEmpty()); -} - -TEST_F(SandwichTest, DamageRectNonEmpty) { - scoped_ptr<RenderPass> pass = CreateRenderPass(); - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - CreateFullscreenCandidateQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), - pass.get()); - CreateFullscreenOpaqueQuad(resource_provider_.get(), - pass->shared_quad_state_list.back(), pass.get()); - damage_rect_ = kOverlayRect; - - RenderPassList pass_list; - pass_list.push_back(std::move(pass)); - - // Check for potential candidates. - OverlayCandidateList candidate_list; - - // Primary plane. - OverlayCandidate output_surface_plane; - output_surface_plane.display_rect = gfx::RectF(kOverlayRect); - output_surface_plane.quad_rect_in_target_space = kOverlayRect; - output_surface_plane.use_output_surface_for_resource = true; - output_surface_plane.overlay_handled = true; - candidate_list.push_back(output_surface_plane); - - overlay_processor_->ProcessForOverlays(resource_provider_.get(), &pass_list, - &candidate_list, nullptr, - &damage_rect_); - EXPECT_EQ(3u, candidate_list.size()); - EXPECT_EQ(damage_rect_, kOverlayRect); -} - TEST_F(SingleOverlayOnTopTest, SuccessfulOverlay) { scoped_ptr<RenderPass> pass = CreateRenderPass(); TextureDrawQuad* original_quad =
diff --git a/cc/proto/layer.proto b/cc/proto/layer.proto index 3498c19..e644e55b 100644 --- a/cc/proto/layer.proto +++ b/cc/proto/layer.proto
@@ -20,23 +20,23 @@ option optimize_for = LITE_RUNTIME; -// Identifies the type of cc:Layer a LayerNode represents. It is used to -// facilitate reconstruction of a Layer of the correct type on the client. -enum LayerType { - UNKNOWN = 0; - LAYER = 1; - PICTURE_LAYER = 2; - HEADS_UP_DISPLAY_LAYER = 3; - - // TODO(nyquist): Add the rest of the necessary LayerTypes. -}; - // Hierarchical structure for serializing the Layer tree. message LayerNode { + // Identifies the type of cc:Layer a LayerNode represents. It is used to + // facilitate reconstruction of a Layer of the correct type on the client. + enum Type { + UNKNOWN = 0; + LAYER = 1; + PICTURE_LAYER = 2; + HEADS_UP_DISPLAY_LAYER = 3; + + // TODO(nyquist): Add the rest of the necessary LayerTypes. + }; + // required optional int32 id = 1; // required - optional LayerType type = 2; + optional Type type = 2; optional int32 parent_id = 3; // A List of all the children of the current LayerNode. repeated LayerNode children = 4;
diff --git a/cc/proto/layer_tree_settings.proto b/cc/proto/layer_tree_settings.proto index 844d75b1..d171625 100644 --- a/cc/proto/layer_tree_settings.proto +++ b/cc/proto/layer_tree_settings.proto
@@ -62,8 +62,6 @@ optional bool ignore_root_layer_flings = 39; optional uint32 scheduled_raster_task_limit = 40; optional bool use_occlusion_for_tile_prioritization = 41; - optional bool verify_property_trees = 42; - optional bool use_property_trees = 43; optional bool image_decode_tasks_enabled = 44; optional bool use_compositor_animation_timelines = 45; optional bool wait_for_beginframe_interval = 46;
diff --git a/cc/scheduler/compositor_timing_history.cc b/cc/scheduler/compositor_timing_history.cc index f5460622..ab1cd04 100644 --- a/cc/scheduler/compositor_timing_history.cc +++ b/cc/scheduler/compositor_timing_history.cc
@@ -50,6 +50,9 @@ base::TimeDelta estimate, bool affects_estimate) = 0; virtual void AddSwapToAckLatency(base::TimeDelta duration) = 0; + + // Synchronization measurements + virtual void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) = 0; }; namespace { @@ -217,6 +220,11 @@ UMA_HISTOGRAM_CUSTOM_TIMES_MICROS("Scheduling.Renderer.SwapToAckLatency", duration); } + + void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override { + UMA_HISTOGRAM_CUSTOM_TIMES_MICROS( + "Scheduling.Renderer.MainAndImplFrameTimeDelta", delta); + } }; class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter { @@ -298,6 +306,11 @@ UMA_HISTOGRAM_CUSTOM_TIMES_MICROS("Scheduling.Browser.SwapToAckLatency", duration); } + + void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override { + UMA_HISTOGRAM_CUSTOM_TIMES_MICROS( + "Scheduling.Browser.MainAndImplFrameTimeDelta", delta); + } }; class NullUMAReporter : public CompositorTimingHistory::UMAReporter { @@ -332,14 +345,18 @@ base::TimeDelta estimate, bool affects_estimate) override {} void AddSwapToAckLatency(base::TimeDelta duration) override {} + void AddMainAndImplFrameTimeDelta(base::TimeDelta delta) override {} }; } // namespace CompositorTimingHistory::CompositorTimingHistory( + bool using_synchronous_renderer_compositor, UMACategory uma_category, RenderingStatsInstrumentation* rendering_stats_instrumentation) - : enabled_(false), + : using_synchronous_renderer_compositor_( + using_synchronous_renderer_compositor), + enabled_(false), did_send_begin_main_frame_(false), begin_main_frame_needed_continuously_(false), begin_main_frame_committing_continuously_(false), @@ -478,6 +495,12 @@ return draw_duration_history_.Percentile(kDrawEstimationPercentile); } +void CompositorTimingHistory::DidCreateAndInitializeOutputSurface() { + // After we get a new output surface, we won't get a spurious + // swap ack from the old output surface. + swap_start_time_ = base::TimeTicks(); +} + void CompositorTimingHistory::WillBeginImplFrame( bool new_active_tree_is_likely) { // The check for whether a BeginMainFrame was sent anytime between two @@ -505,10 +528,15 @@ SetCompositorDrawingContinuously(false); } -void CompositorTimingHistory::WillBeginMainFrame(bool on_critical_path) { +void CompositorTimingHistory::WillBeginMainFrame( + bool on_critical_path, + base::TimeTicks main_frame_time) { DCHECK_EQ(base::TimeTicks(), begin_main_frame_sent_time_); + DCHECK_EQ(base::TimeTicks(), begin_main_frame_frame_time_); + begin_main_frame_on_critical_path_ = on_critical_path; begin_main_frame_sent_time_ = Now(); + begin_main_frame_frame_time_ = main_frame_time; did_send_begin_main_frame_ = true; SetBeginMainFrameNeededContinuously(true); @@ -524,11 +552,15 @@ void CompositorTimingHistory::BeginMainFrameAborted() { SetBeginMainFrameCommittingContinuously(false); DidBeginMainFrame(); + begin_main_frame_frame_time_ = base::TimeTicks(); } void CompositorTimingHistory::DidCommit() { + DCHECK_EQ(base::TimeTicks(), pending_tree_main_frame_time_); SetBeginMainFrameCommittingContinuously(true); DidBeginMainFrame(); + pending_tree_main_frame_time_ = begin_main_frame_frame_time_; + begin_main_frame_frame_time_ = base::TimeTicks(); } void CompositorTimingHistory::DidBeginMainFrame() { @@ -666,7 +698,13 @@ if (enabled_) activate_duration_history_.InsertSample(activate_duration); + // The synchronous compositor doesn't necessarily draw every new active tree. + if (!using_synchronous_renderer_compositor_) + DCHECK_EQ(base::TimeTicks(), active_tree_main_frame_time_); + active_tree_main_frame_time_ = pending_tree_main_frame_time_; + activate_start_time_ = base::TimeTicks(); + pending_tree_main_frame_time_ = base::TimeTicks(); } void CompositorTimingHistory::WillDraw() { @@ -674,7 +712,13 @@ draw_start_time_ = Now(); } -void CompositorTimingHistory::DidDraw(bool used_new_active_tree) { +void CompositorTimingHistory::DrawAborted() { + active_tree_main_frame_time_ = base::TimeTicks(); +} + +void CompositorTimingHistory::DidDraw(bool used_new_active_tree, + bool main_thread_missed_last_deadline, + base::TimeTicks impl_frame_time) { DCHECK_NE(base::TimeTicks(), draw_start_time_); base::TimeTicks draw_end_time = Now(); base::TimeDelta draw_duration = draw_end_time - draw_start_time_; @@ -699,13 +743,26 @@ } draw_end_time_prev_ = draw_end_time; - if (begin_main_frame_committing_continuously_ && used_new_active_tree) { - if (!new_active_tree_draw_end_time_prev_.is_null()) { - base::TimeDelta draw_interval = - draw_end_time - new_active_tree_draw_end_time_prev_; - uma_reporter_->AddCommitInterval(draw_interval); + if (used_new_active_tree) { + DCHECK_NE(base::TimeTicks(), active_tree_main_frame_time_); + base::TimeDelta main_and_impl_delta = + impl_frame_time - active_tree_main_frame_time_; + DCHECK_GE(main_and_impl_delta, base::TimeDelta()); + if (!using_synchronous_renderer_compositor_) { + DCHECK_EQ(main_thread_missed_last_deadline, + !main_and_impl_delta.is_zero()); } - new_active_tree_draw_end_time_prev_ = draw_end_time; + uma_reporter_->AddMainAndImplFrameTimeDelta(main_and_impl_delta); + active_tree_main_frame_time_ = base::TimeTicks(); + + if (begin_main_frame_committing_continuously_) { + if (!new_active_tree_draw_end_time_prev_.is_null()) { + base::TimeDelta draw_interval = + draw_end_time - new_active_tree_draw_end_time_prev_; + uma_reporter_->AddCommitInterval(draw_interval); + } + new_active_tree_draw_end_time_prev_ = draw_end_time; + } } draw_start_time_ = base::TimeTicks(); @@ -723,8 +780,4 @@ swap_start_time_ = base::TimeTicks(); } -void CompositorTimingHistory::DidSwapBuffersReset() { - swap_start_time_ = base::TimeTicks(); -} - } // namespace cc
diff --git a/cc/scheduler/compositor_timing_history.h b/cc/scheduler/compositor_timing_history.h index 11dfc00..a7c58dd 100644 --- a/cc/scheduler/compositor_timing_history.h +++ b/cc/scheduler/compositor_timing_history.h
@@ -29,6 +29,7 @@ class UMAReporter; CompositorTimingHistory( + bool using_synchronous_renderer_compositor, UMACategory uma_category, RenderingStatsInstrumentation* rendering_stats_instrumentation); virtual ~CompositorTimingHistory(); @@ -49,12 +50,16 @@ virtual base::TimeDelta ActivateDurationEstimate() const; virtual base::TimeDelta DrawDurationEstimate() const; + // State that affects when events should be expected/recorded/reported. void SetRecordingEnabled(bool enabled); + void DidCreateAndInitializeOutputSurface(); + // Events to be timed. void WillBeginImplFrame(bool new_active_tree_is_likely); void WillFinishImplFrame(bool needs_redraw); void BeginImplFrameNotExpectedSoon(); - void WillBeginMainFrame(bool on_critical_path); + void WillBeginMainFrame(bool on_critical_path, + base::TimeTicks main_frame_time); void BeginMainFrameStarted(base::TimeTicks main_thread_start_time); void BeginMainFrameAborted(); void DidCommit(); @@ -63,11 +68,13 @@ void ReadyToActivate(); void WillActivate(); void DidActivate(); + void DrawAborted(); void WillDraw(); - void DidDraw(bool used_new_active_tree); + void DidDraw(bool used_new_active_tree, + bool main_thread_missed_last_deadline, + base::TimeTicks impl_frame_time); void DidSwapBuffers(); void DidSwapBuffersComplete(); - void DidSwapBuffersReset(); protected: void DidBeginMainFrame(); @@ -79,6 +86,7 @@ static scoped_ptr<UMAReporter> CreateUMAReporter(UMACategory category); virtual base::TimeTicks Now() const; + bool using_synchronous_renderer_compositor_; bool enabled_; // Used to calculate frame rates of Main and Impl threads. @@ -101,11 +109,14 @@ RollingTimeDeltaHistory draw_duration_history_; bool begin_main_frame_on_critical_path_; + base::TimeTicks begin_main_frame_frame_time_; base::TimeTicks begin_main_frame_sent_time_; base::TimeTicks begin_main_frame_start_time_; base::TimeTicks begin_main_frame_end_time_; + base::TimeTicks pending_tree_main_frame_time_; base::TimeTicks prepare_tiles_start_time_; base::TimeTicks activate_start_time_; + base::TimeTicks active_tree_main_frame_time_; base::TimeTicks draw_start_time_; base::TimeTicks swap_start_time_;
diff --git a/cc/scheduler/compositor_timing_history_unittest.cc b/cc/scheduler/compositor_timing_history_unittest.cc index 900367f..f57d443 100644 --- a/cc/scheduler/compositor_timing_history_unittest.cc +++ b/cc/scheduler/compositor_timing_history_unittest.cc
@@ -17,7 +17,8 @@ public: TestCompositorTimingHistory(CompositorTimingHistoryTest* test, RenderingStatsInstrumentation* rendering_stats) - : CompositorTimingHistory(NULL_UMA, rendering_stats), test_(test) {} + : CompositorTimingHistory(false, NULL_UMA, rendering_stats), + test_(test) {} protected: base::TimeTicks Now() const override; @@ -68,7 +69,7 @@ base::TimeDelta activate_duration = base::TimeDelta::FromMilliseconds(4); base::TimeDelta draw_duration = base::TimeDelta::FromMilliseconds(5); - timing_history_.WillBeginMainFrame(true); + timing_history_.WillBeginMainFrame(true, Now()); AdvanceNowBy(begin_main_frame_queue_duration); timing_history_.BeginMainFrameStarted(Now()); AdvanceNowBy(begin_main_frame_start_to_commit_duration); @@ -87,7 +88,7 @@ AdvanceNowBy(one_second); timing_history_.WillDraw(); AdvanceNowBy(draw_duration); - timing_history_.DidDraw(true); + timing_history_.DidDraw(true, true, Now()); EXPECT_EQ(begin_main_frame_queue_duration, timing_history_.BeginMainFrameQueueDurationCriticalEstimate()); @@ -126,7 +127,7 @@ base::TimeDelta activate_duration = base::TimeDelta::FromMilliseconds(4); base::TimeDelta draw_duration = base::TimeDelta::FromMilliseconds(5); - timing_history_.WillBeginMainFrame(true); + timing_history_.WillBeginMainFrame(false, Now()); AdvanceNowBy(begin_main_frame_queue_duration); timing_history_.BeginMainFrameStarted(Now()); AdvanceNowBy(begin_main_frame_start_to_commit_duration); @@ -146,9 +147,9 @@ AdvanceNowBy(one_second); timing_history_.WillDraw(); AdvanceNowBy(draw_duration); - timing_history_.DidDraw(true); + timing_history_.DidDraw(false, false, Now()); - EXPECT_EQ(begin_main_frame_queue_duration, + EXPECT_EQ(base::TimeDelta(), timing_history_.BeginMainFrameQueueDurationCriticalEstimate()); EXPECT_EQ(begin_main_frame_queue_duration, timing_history_.BeginMainFrameQueueDurationNotCriticalEstimate()); @@ -179,13 +180,13 @@ base::TimeDelta begin_main_frame_start_to_commit_duration = base::TimeDelta::FromMilliseconds(1); - timing_history_.WillBeginMainFrame(true); + timing_history_.WillBeginMainFrame(true, Now()); AdvanceNowBy(begin_main_frame_queue_duration_critical); timing_history_.BeginMainFrameStarted(Now()); AdvanceNowBy(begin_main_frame_start_to_commit_duration); timing_history_.BeginMainFrameAborted(); - timing_history_.WillBeginMainFrame(false); + timing_history_.WillBeginMainFrame(false, Now()); AdvanceNowBy(begin_main_frame_queue_duration_not_critical); timing_history_.BeginMainFrameStarted(Now()); AdvanceNowBy(begin_main_frame_start_to_commit_duration); @@ -219,7 +220,7 @@ base::TimeDelta::FromMilliseconds(1); // A single critical frame that is slow. - timing_history_.WillBeginMainFrame(true); + timing_history_.WillBeginMainFrame(true, Now()); AdvanceNowBy(begin_main_frame_queue_duration_critical); timing_history_.BeginMainFrameStarted(Now()); AdvanceNowBy(begin_main_frame_start_to_commit_duration); @@ -228,7 +229,7 @@ // A bunch of faster non critical frames that are newer. for (int i = 0; i < 100; i++) { - timing_history_.WillBeginMainFrame(false); + timing_history_.WillBeginMainFrame(false, Now()); AdvanceNowBy(begin_main_frame_queue_duration_not_critical); timing_history_.BeginMainFrameStarted(Now()); AdvanceNowBy(begin_main_frame_start_to_commit_duration); @@ -265,7 +266,7 @@ base::TimeDelta::FromMilliseconds(1); // A single non critical frame that is fast. - timing_history_.WillBeginMainFrame(false); + timing_history_.WillBeginMainFrame(false, Now()); AdvanceNowBy(begin_main_frame_queue_duration_not_critical); timing_history_.BeginMainFrameStarted(Now()); AdvanceNowBy(begin_main_frame_start_to_commit_duration); @@ -273,7 +274,7 @@ // A bunch of slower critical frames that are newer. for (int i = 0; i < 100; i++) { - timing_history_.WillBeginMainFrame(true); + timing_history_.WillBeginMainFrame(true, Now()); AdvanceNowBy(begin_main_frame_queue_duration_critical); timing_history_.BeginMainFrameStarted(Now()); AdvanceNowBy(begin_main_frame_start_to_commit_duration);
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index 499fae7..61607de6 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc
@@ -249,8 +249,8 @@ TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); DCHECK(!observing_frame_source_); DCHECK(begin_impl_frame_deadline_task_.IsCancelled()); - compositor_timing_history_->DidSwapBuffersReset(); state_machine_.DidCreateAndInitializeOutputSurface(); + compositor_timing_history_->DidCreateAndInitializeOutputSurface(); UpdateCompositorTimingHistoryRecordingEnabled(); ProcessScheduledActions(); } @@ -663,21 +663,29 @@ void Scheduler::DrawAndSwapIfPossible() { bool drawing_with_new_active_tree = state_machine_.active_tree_needs_first_draw(); + bool main_thread_missed_last_deadline = + state_machine_.main_thread_missed_last_deadline(); compositor_timing_history_->WillDraw(); state_machine_.WillDraw(); DrawResult result = client_->ScheduledActionDrawAndSwapIfPossible(); state_machine_.DidDraw(result); - compositor_timing_history_->DidDraw(drawing_with_new_active_tree); + compositor_timing_history_->DidDraw( + drawing_with_new_active_tree, main_thread_missed_last_deadline, + begin_impl_frame_tracker_.DangerousMethodCurrentOrLast().frame_time); } void Scheduler::DrawAndSwapForced() { bool drawing_with_new_active_tree = state_machine_.active_tree_needs_first_draw(); + bool main_thread_missed_last_deadline = + state_machine_.main_thread_missed_last_deadline(); compositor_timing_history_->WillDraw(); state_machine_.WillDraw(); DrawResult result = client_->ScheduledActionDrawAndSwapForced(); state_machine_.DidDraw(result); - compositor_timing_history_->DidDraw(drawing_with_new_active_tree); + compositor_timing_history_->DidDraw( + drawing_with_new_active_tree, main_thread_missed_last_deadline, + begin_impl_frame_tracker_.DangerousMethodCurrentOrLast().frame_time); } void Scheduler::SetDeferCommits(bool defer_commits) { @@ -710,7 +718,8 @@ break; case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME: compositor_timing_history_->WillBeginMainFrame( - begin_main_frame_args_.on_critical_path); + begin_main_frame_args_.on_critical_path, + begin_main_frame_args_.frame_time); state_machine_.WillSendBeginMainFrame(); // TODO(brianderson): Pass begin_main_frame_args_ directly to client. client_->ScheduledActionSendBeginMainFrame(begin_main_frame_args_); @@ -748,6 +757,7 @@ // No action is actually performed, but this allows the state machine to // drain the pipeline without actually drawing. state_machine_.AbortDrawAndSwap(); + compositor_timing_history_->DrawAborted(); break; } case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index b168034..23715ae 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc
@@ -613,7 +613,6 @@ forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) { DCHECK(!has_pending_tree_); needs_redraw_ = true; - active_tree_needs_first_draw_ = true; } // This post-commit work is common to both completed and aborted commits. @@ -643,6 +642,13 @@ } void SchedulerStateMachine::WillDrawInternal() { + // If a new active tree is pending after the one we are about to draw, + // the main thread is in a high latency mode. + // main_thread_missed_last_deadline_ is here in addition to + // OnBeginImplFrameIdle for cases where the scheduler aborts draws outside + // of the deadline. + main_thread_missed_last_deadline_ = CommitPending() || has_pending_tree_; + // We need to reset needs_redraw_ before we draw since the // draw itself might request another draw. needs_redraw_ = false;
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index 63b25cd..8989e04 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc
@@ -263,7 +263,8 @@ } scoped_ptr<FakeCompositorTimingHistory> fake_compositor_timing_history = - FakeCompositorTimingHistory::Create(); + FakeCompositorTimingHistory::Create( + scheduler_settings_.using_synchronous_renderer_compositor); fake_compositor_timing_history_ = fake_compositor_timing_history.get(); scheduler_ = TestScheduler::Create( @@ -1483,7 +1484,7 @@ // If the BeginMainFrame aborts, it doesn't actually insert a frame into the // queue, which means there is no latency to recover. -TEST_F(SchedulerTest, MainFrameNotSkippedAfterLateAbort) { +TEST_F(SchedulerTest, MainFrameNotSkippedAfterLateBeginMainFrameAbort) { scheduler_settings_.use_external_begin_frame_source = true; SetUpScheduler(true); @@ -1518,6 +1519,56 @@ EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); } +// If the BeginMainFrame aborts, it doesn't actually insert a frame into the +// queue, which means there is no latency to recover. +TEST_F(SchedulerTest, MainFrameNotSkippedAfterCanDrawChanges) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + // Use fast estimates so we think we can recover latency if needed. + fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration); + + // Impl thread hits deadline before BeginMainFrame aborts. + scheduler_->SetNeedsBeginMainFrame(); + EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_ACTION("AddObserver(this)", client_, 0, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 3); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 3); + EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); + scheduler_->NotifyBeginMainFrameStarted(base::TimeTicks()); + EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); + + // Make us abort the upcoming draw. + client_->Reset(); + scheduler_->NotifyReadyToCommit(); + scheduler_->NotifyReadyToActivate(); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 2); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 1, 2); + EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); + scheduler_->SetCanDraw(false); + EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); + + // Make CanDraw true after activation. + client_->Reset(); + scheduler_->SetCanDraw(true); + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); + + // Make sure we don't skip the next BeginMainFrame. + client_->Reset(); + scheduler_->SetNeedsBeginMainFrame(); + EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_TRUE(client_->HasAction("WillBeginImplFrame")); + EXPECT_TRUE(client_->HasAction("ScheduledActionSendBeginMainFrame")); + EXPECT_FALSE(scheduler_->MainThreadMissedLastDeadline()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_TRUE(scheduler_->MainThreadMissedLastDeadline()); +} + void SchedulerTest::ImplFrameSkippedAfterLateSwapAck( bool swap_ack_before_deadline) { // To get into a high latency state, this test disables automatic swap acks.
diff --git a/cc/test/fake_layer_tree_host.cc b/cc/test/fake_layer_tree_host.cc index ca054a70..85acf7d 100644 --- a/cc/test/fake_layer_tree_host.cc +++ b/cc/test/fake_layer_tree_host.cc
@@ -32,7 +32,6 @@ FakeLayerTreeHostClient* client, TestTaskGraphRunner* task_graph_runner) { LayerTreeSettings settings; - settings.verify_property_trees = true; settings.use_compositor_animation_timelines = true; return Create(client, task_graph_runner, settings); }
diff --git a/cc/test/layer_tree_settings_for_testing.cc b/cc/test/layer_tree_settings_for_testing.cc index 952bb7045..dcc947d 100644 --- a/cc/test/layer_tree_settings_for_testing.cc +++ b/cc/test/layer_tree_settings_for_testing.cc
@@ -9,7 +9,6 @@ LayerTreeSettingsForTesting::LayerTreeSettingsForTesting() : LayerTreeSettings() { this->use_compositor_animation_timelines = true; - this->verify_property_trees = true; } LayerTreeSettingsForTesting::~LayerTreeSettingsForTesting() {}
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index fcc6fe8..8d94971 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc
@@ -529,7 +529,6 @@ started_(false), ended_(false), delegating_renderer_(false), - verify_property_trees_(true), timeout_seconds_(0), weak_factory_(this) { main_thread_weak_ptr_ = weak_factory_.GetWeakPtr(); @@ -917,7 +916,6 @@ // mocked out. settings_.renderer_settings.refresh_rate = 200.0; settings_.background_animation_rate = 200.0; - settings_.verify_property_trees = verify_property_trees_; InitializeSettings(&settings_); InitializeLayerSettings(&layer_settings_);
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index da70a06..931a15a 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h
@@ -99,11 +99,6 @@ void DoBeginTest(); void Timeout(); - bool verify_property_trees() const { return verify_property_trees_; } - void set_verify_property_trees(bool verify_property_trees) { - verify_property_trees_ = verify_property_trees; - } - const LayerSettings& layer_settings() { return layer_settings_; } protected: @@ -220,7 +215,6 @@ bool started_; bool ended_; bool delegating_renderer_; - bool verify_property_trees_; int timeout_seconds_;
diff --git a/cc/test/scheduler_test_common.cc b/cc/test/scheduler_test_common.cc index b6b1b2b9..ef760d2 100644 --- a/cc/test/scheduler_test_common.cc +++ b/cc/test/scheduler_test_common.cc
@@ -67,16 +67,20 @@ TestSyntheticBeginFrameSource::~TestSyntheticBeginFrameSource() { } -scoped_ptr<FakeCompositorTimingHistory> FakeCompositorTimingHistory::Create() { +scoped_ptr<FakeCompositorTimingHistory> FakeCompositorTimingHistory::Create( + bool using_synchronous_renderer_compositor) { scoped_ptr<RenderingStatsInstrumentation> rendering_stats_instrumentation = RenderingStatsInstrumentation::Create(); return make_scoped_ptr(new FakeCompositorTimingHistory( + using_synchronous_renderer_compositor, std::move(rendering_stats_instrumentation))); } FakeCompositorTimingHistory::FakeCompositorTimingHistory( + bool using_synchronous_renderer_compositor, scoped_ptr<RenderingStatsInstrumentation> rendering_stats_instrumentation) - : CompositorTimingHistory(CompositorTimingHistory::NULL_UMA, + : CompositorTimingHistory(using_synchronous_renderer_compositor, + CompositorTimingHistory::NULL_UMA, rendering_stats_instrumentation.get()), rendering_stats_instrumentation_owned_( std::move(rendering_stats_instrumentation)) {}
diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h index 3993c9f..d442aca 100644 --- a/cc/test/scheduler_test_common.h +++ b/cc/test/scheduler_test_common.h
@@ -164,7 +164,8 @@ class FakeCompositorTimingHistory : public CompositorTimingHistory { public: - static scoped_ptr<FakeCompositorTimingHistory> Create(); + static scoped_ptr<FakeCompositorTimingHistory> Create( + bool using_synchronous_renderer_compositor); ~FakeCompositorTimingHistory() override; void SetAllEstimatesTo(base::TimeDelta duration); @@ -190,7 +191,8 @@ base::TimeDelta DrawDurationEstimate() const override; protected: - FakeCompositorTimingHistory(scoped_ptr<RenderingStatsInstrumentation> + FakeCompositorTimingHistory(bool using_synchronous_renderer_compositor, + scoped_ptr<RenderingStatsInstrumentation> rendering_stats_instrumentation_owned); scoped_ptr<RenderingStatsInstrumentation>
diff --git a/cc/tiles/tile_manager_unittest.cc b/cc/tiles/tile_manager_unittest.cc index 1801389..355fd04 100644 --- a/cc/tiles/tile_manager_unittest.cc +++ b/cc/tiles/tile_manager_unittest.cc
@@ -43,7 +43,6 @@ public: LowResTilingsSettings() { create_low_res_tiling = true; - verify_property_trees = true; } };
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index 489a9e9a..c8baceb 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc
@@ -82,8 +82,6 @@ bool layers_always_allowed_lcd_text, bool can_render_to_separate_surface, bool can_adjust_raster_scales, - bool verify_property_trees, - bool use_property_trees, LayerImplList* render_surface_layer_list, int current_render_surface_layer_list_id, PropertyTrees* property_trees) @@ -103,8 +101,6 @@ layers_always_allowed_lcd_text(layers_always_allowed_lcd_text), can_render_to_separate_surface(can_render_to_separate_surface), can_adjust_raster_scales(can_adjust_raster_scales), - verify_property_trees(verify_property_trees), - use_property_trees(use_property_trees), render_surface_layer_list(render_surface_layer_list), current_render_surface_layer_list_id( current_render_surface_layer_list_id), @@ -131,8 +127,6 @@ false, true, false, - true, - true, render_surface_layer_list, current_render_surface_layer_list_id, GetPropertyTrees(root_layer)) { @@ -201,36 +195,6 @@ top_controls_delta = proto.top_controls_delta(); } -static gfx::Vector2dF GetEffectiveScrollDelta(LayerImpl* layer) { - // Layer's scroll offset can have an integer part and fractional part. - // Due to Blink's limitation, it only counter-scrolls the position-fixed - // layer using the integer part of Layer's scroll offset. - // CC scrolls the layer using the full scroll offset, so we have to - // add the ScrollCompensationAdjustment (fractional part of the scroll - // offset) to the effective scroll delta which is used to counter-scroll - // the position-fixed layer. - gfx::Vector2dF scroll_delta = - layer->ScrollDelta() + layer->ScrollCompensationAdjustment(); - // The scroll parent's scroll delta is the amount we've scrolled on the - // compositor thread since the commit for this layer tree's source frame. - // we last reported to the main thread. I.e., it's the discrepancy between - // a scroll parent's scroll delta and offset, so we must add it here. - if (layer->scroll_parent()) - scroll_delta += layer->scroll_parent()->ScrollDelta() + - layer->ScrollCompensationAdjustment(); - return scroll_delta; -} - -static gfx::ScrollOffset GetEffectiveCurrentScrollOffset(LayerImpl* layer) { - gfx::ScrollOffset offset = layer->CurrentScrollOffset(); - // The scroll parent's total scroll offset (scroll offset + scroll delta) - // can't be used because its scroll offset has already been applied to the - // scroll children's positions by the main thread layer positioning code. - if (layer->scroll_parent()) - offset += gfx::ScrollOffset(layer->scroll_parent()->ScrollDelta()); - return offset; -} - inline gfx::Rect CalculateVisibleRectWithCachedLayerRect( const gfx::Rect& target_surface_rect, const gfx::Rect& layer_bound_rect, @@ -330,51 +294,6 @@ gfx::RectF(rect_f.origin() + translation, rect_f.size())); } -// Attempts to update the clip rects for the given layer. If the layer has a -// clip_parent, it may not inherit its immediate ancestor's clip. -static void UpdateClipRectsForClipChild( - const LayerImpl* layer, - gfx::Rect* clip_rect_in_parent_target_space, - bool* subtree_should_be_clipped) { - // If the layer has no clip_parent, or the ancestor is the same as its actual - // parent, then we don't need special clip rects. Bail now and leave the out - // parameters untouched. - const LayerImpl* clip_parent = layer->scroll_parent(); - - if (!clip_parent) - clip_parent = layer->clip_parent(); - - if (!clip_parent || clip_parent == layer->parent()) - return; - - // The root layer is never a clip child. - DCHECK(layer->parent()); - - // Grab the cached values. - *clip_rect_in_parent_target_space = clip_parent->clip_rect(); - *subtree_should_be_clipped = clip_parent->is_clipped(); - - // We may have to project the clip rect into our parent's target space. Note, - // it must be our parent's target space, not ours. For one, we haven't - // computed our transforms, so we couldn't put it in our space yet even if we - // wanted to. But more importantly, this matches the expectations of - // CalculateDrawPropertiesInternal. If we, say, create a render surface, these - // clip rects will want to be in its target space, not ours. - if (clip_parent == layer->clip_parent()) { - *clip_rect_in_parent_target_space = TranslateRectToTargetSpace( - *clip_parent, *layer->parent(), *clip_rect_in_parent_target_space, - TRANSLATE_RECT_DIRECTION_TO_DESCENDANT); - } else { - // If we're being clipped by our scroll parent, we must translate through - // our common ancestor. This happens to be our parent, so it is sufficent to - // translate from our clip parent's space to the space of its ancestor (our - // parent). - *clip_rect_in_parent_target_space = TranslateRectToTargetSpace( - *layer->parent(), *clip_parent, *clip_rect_in_parent_target_space, - TRANSLATE_RECT_DIRECTION_TO_ANCESTOR); - } -} - // We collect an accumulated drawable content rect per render surface. // Typically, a layer will contribute to only one surface, the surface // associated with its render target. Clip children, however, may affect @@ -497,7 +416,6 @@ } static bool IsLayerBackFaceVisible(LayerImpl* layer, - bool use_property_trees, const TransformTree& transform_tree) { // The current W3C spec on CSS transforms says that backface visibility should // be determined differently depending on whether the layer is in a "3d @@ -505,13 +423,8 @@ // are in a 3d rendering context by checking if the parent preserves 3d. if (LayerIsInExisting3DRenderingContext(layer)) { - if (use_property_trees) { - return DrawTransformFromPropertyTrees(layer, transform_tree) - .IsBackFaceVisible(); - } else { - return layer->draw_properties() - .target_space_transform.IsBackFaceVisible(); - } + return DrawTransformFromPropertyTrees(layer, transform_tree) + .IsBackFaceVisible(); } // In this case, either the layer establishes a new 3d rendering context, or @@ -538,51 +451,8 @@ return layer->masks_to_bounds() || layer->mask_layer(); } -static gfx::Rect CalculateVisibleLayerRect( - LayerImpl* layer, - const gfx::Rect& clip_rect_of_target_surface_in_target_space, - const gfx::Rect& layer_rect_in_target_space) { - DCHECK(layer->render_target()); - - // Nothing is visible if the layer bounds are empty. - if (!layer->DrawsContent() || layer->bounds().IsEmpty() || - layer->drawable_content_rect().IsEmpty()) - return gfx::Rect(); - - // The layer is fully visible if it has copy requests. - if (layer->HasCopyRequest()) - return gfx::Rect(layer->bounds()); - - // Compute visible bounds in target surface space. - gfx::Rect visible_rect_in_target_surface_space = - layer->drawable_content_rect(); - - if (layer->render_target()->render_surface()->is_clipped()) { - // The |layer| L has a target T which owns a surface Ts. The surface Ts - // has a target TsT. - // - // In this case the target surface Ts does clip the layer L that contributes - // to it. So, we have to convert the clip rect of Ts from the target space - // of Ts (that is the space of TsT), to the current render target's space - // (that is the space of T). This conversion is done outside this function - // so that it can be cached instead of computing it redundantly for every - // layer. - visible_rect_in_target_surface_space.Intersect( - clip_rect_of_target_surface_in_target_space); - } - - if (visible_rect_in_target_surface_space.IsEmpty()) - return gfx::Rect(); - - return CalculateVisibleRectWithCachedLayerRect( - visible_rect_in_target_surface_space, gfx::Rect(layer->bounds()), - layer_rect_in_target_space, - layer->draw_properties().target_space_transform); -} - static bool LayerShouldBeSkipped(LayerImpl* layer, bool layer_is_drawn, - bool use_property_trees, const TransformTree& transform_tree) { // Layers can be skipped if any of these conditions are met. // - is not drawn due to it or one of its ancestors being hidden (or having @@ -617,8 +487,7 @@ // The layer should not be drawn if (1) it is not double-sided and (2) the // back of the layer is known to be facing the screen. if (!backface_test_layer->double_sided() && - IsLayerBackFaceVisible(backface_test_layer, use_property_trees, - transform_tree)) + IsLayerBackFaceVisible(backface_test_layer, transform_tree)) return true; return false; @@ -671,8 +540,6 @@ return !layer->EffectiveOpacity(); } -static inline void SavePaintPropertiesLayer(LayerImpl* layer) {} - // This function returns a translation matrix that can be applied on a vector // that's in the layer's target surface coordinate, while the position offset is // specified in some ancestor layer's coordinate. @@ -895,106 +762,6 @@ return next_scroll_compensation_matrix; } -static inline void UpdateLayerScaleDrawProperties( - LayerImpl* layer, - float maximum_animation_contents_scale, - float starting_animation_contents_scale) { - layer->draw_properties().maximum_animation_contents_scale = - maximum_animation_contents_scale; - layer->draw_properties().starting_animation_contents_scale = - starting_animation_contents_scale; -} - -static inline void CalculateAnimationContentsScale( - LayerImpl* layer, - bool ancestor_is_animating_scale, - float ancestor_maximum_animation_contents_scale, - float ancestor_starting_animation_contents_scale, - const gfx::Transform& ancestor_transform, - const gfx::Transform& combined_transform, - bool* combined_is_animating_scale, - float* combined_maximum_animation_contents_scale, - float* combined_starting_animation_contents_scale) { - if (ancestor_is_animating_scale && - ancestor_maximum_animation_contents_scale == 0.f) { - // We've already failed to compute a maximum animated scale at an - // ancestor, so we'll continue to fail. - *combined_maximum_animation_contents_scale = 0.f; - *combined_starting_animation_contents_scale = 0.f; - *combined_is_animating_scale = true; - return; - } - - if (!combined_transform.IsScaleOrTranslation()) { - // Computing maximum animated scale in the presence of - // non-scale/translation transforms isn't supported. - *combined_maximum_animation_contents_scale = 0.f; - *combined_starting_animation_contents_scale = 0.f; - *combined_is_animating_scale = true; - return; - } - - // We currently only support computing maximum scale for combinations of - // scales and translations. We treat all non-translations as potentially - // affecting scale. Animations that include non-translation/scale components - // will cause the computation of MaximumScale below to fail. - bool layer_is_animating_scale = !layer->HasOnlyTranslationTransforms(); - - if (!layer_is_animating_scale && !ancestor_is_animating_scale) { - *combined_maximum_animation_contents_scale = 0.f; - *combined_starting_animation_contents_scale = 0.f; - *combined_is_animating_scale = false; - return; - } - - // We don't attempt to accumulate animation scale from multiple nodes, - // because of the risk of significant overestimation. For example, one node - // may be increasing scale from 1 to 10 at the same time as a descendant is - // decreasing scale from 10 to 1. Naively combining these scales would produce - // a scale of 100. - if (layer_is_animating_scale && ancestor_is_animating_scale) { - *combined_maximum_animation_contents_scale = 0.f; - *combined_starting_animation_contents_scale = 0.f; - *combined_is_animating_scale = true; - return; - } - - // At this point, we know either the layer or an ancestor, but not both, - // is animating scale. - *combined_is_animating_scale = true; - if (!layer_is_animating_scale) { - gfx::Vector2dF layer_transform_scales = - MathUtil::ComputeTransform2dScaleComponents(layer->transform(), 0.f); - float max_layer_scale = - std::max(layer_transform_scales.x(), layer_transform_scales.y()); - *combined_maximum_animation_contents_scale = - ancestor_maximum_animation_contents_scale * max_layer_scale; - *combined_starting_animation_contents_scale = - ancestor_starting_animation_contents_scale * max_layer_scale; - return; - } - - float layer_maximum_animated_scale = 0.f; - float layer_start_animated_scale = 0.f; - if (!layer->MaximumTargetScale(&layer_maximum_animated_scale)) { - *combined_maximum_animation_contents_scale = 0.f; - return; - } - if (!layer->AnimationStartScale(&layer_start_animated_scale)) { - *combined_starting_animation_contents_scale = 0.f; - return; - } - - gfx::Vector2dF ancestor_transform_scales = - MathUtil::ComputeTransform2dScaleComponents(ancestor_transform, 0.f); - float max_scale_xy = - std::max(ancestor_transform_scales.x(), ancestor_transform_scales.y()); - *combined_maximum_animation_contents_scale = - layer_maximum_animated_scale * max_scale_xy; - *combined_starting_animation_contents_scale = - layer_start_animated_scale * max_scale_xy; -} - static inline void MarkLayerWithRenderSurfaceLayerListId( LayerImpl* layer, int current_render_surface_layer_list_id) { @@ -1321,26 +1088,6 @@ child->set_sorted_for_recursion(sorted_for_recursion); } -static bool SortChildrenForRecursion(std::vector<LayerImpl*>* out, - const LayerImpl& parent) { - out->reserve(parent.children().size()); - bool order_changed = false; - for (size_t i = 0; i < parent.children().size(); ++i) { - LayerImpl* current = - LayerTreeHostCommon::get_layer_as_raw_ptr(parent.children(), i); - - if (current->sorted_for_recursion()) { - order_changed = true; - continue; - } - - AddScrollParentChain(out, parent, current); - } - - DCHECK_EQ(parent.children().size(), out->size()); - return order_changed; -} - static bool CdpPerfTracingEnabled() { bool tracing_enabled; TRACE_EVENT_CATEGORY_GROUP_ENABLED("cdp.perf", &tracing_enabled); @@ -1371,775 +1118,6 @@ return 0.f; } -// Recursively walks the layer tree starting at the given node and computes all -// the necessary transformations, clip rects, render surfaces, etc. -static void CalculateDrawPropertiesInternal( - LayerImpl* layer, - const SubtreeGlobals& globals, - const DataForRecursion& data_from_ancestor, - std::vector<AccumulatedSurfaceState>* accumulated_surface_state) { - // This function computes the new matrix transformations recursively for this - // layer and all its descendants. It also computes the appropriate render - // surfaces. - // Some important points to remember: - // - // 0. Here, transforms are notated in Matrix x Vector order, and in words we - // describe what the transform does from left to right. - // - // 1. In our terminology, the "layer origin" refers to the top-left corner of - // a layer, and the positive Y-axis points downwards. This interpretation is - // valid because the orthographic projection applied at draw time flips the Y - // axis appropriately. - // - // 2. The anchor point, when given as a PointF object, is specified in "unit - // layer space", where the bounds of the layer map to [0, 1]. However, as a - // Transform object, the transform to the anchor point is specified in "layer - // space", where the bounds of the layer map to [bounds.width(), - // bounds.height()]. - // - // 3. Definition of various transforms used: - // M[parent] is the parent matrix, with respect to the nearest render - // surface, passed down recursively. - // - // M[root] is the full hierarchy, with respect to the root, passed down - // recursively. - // - // Tr[origin] is the translation matrix from the parent's origin to - // this layer's origin. - // - // Tr[origin2anchor] is the translation from the layer's origin to its - // anchor point - // - // Tr[origin2center] is the translation from the layer's origin to its - // center - // - // M[layer] is the layer's matrix (applied at the anchor point) - // - // S[layer2content] is the ratio of a layer's content_bounds() to its - // Bounds(). - // - // Some composite transforms can help in understanding the sequence of - // transforms: - // composite_layer_transform = Tr[origin2anchor] * M[layer] * - // Tr[origin2anchor].inverse() - // - // 4. When a layer (or render surface) is drawn, it is drawn into a "target - // render surface". Therefore the draw transform does not necessarily - // transform from screen space to local layer space. Instead, the draw - // transform is the transform between the "target render surface space" and - // local layer space. Note that render surfaces, except for the root, also - // draw themselves into a different target render surface, and so their draw - // transform and origin transforms are also described with respect to the - // target. - // - // Using these definitions, then: - // - // The draw transform for the layer is: - // M[draw] = M[parent] * Tr[origin] * composite_layer_transform * - // S[layer2content] = M[parent] * Tr[layer->position() + anchor] * - // M[layer] * Tr[anchor2origin] * S[layer2content] - // - // Interpreting the math left-to-right, this transforms from the - // layer's render surface to the origin of the layer in content space. - // - // The screen space transform is: - // M[screenspace] = M[root] * Tr[origin] * composite_layer_transform * - // S[layer2content] - // = M[root] * Tr[layer->position() + anchor] * M[layer] - // * Tr[anchor2origin] * S[layer2content] - // - // Interpreting the math left-to-right, this transforms from the root - // render surface's content space to the origin of the layer in content - // space. - // - // The transform hierarchy that is passed on to children (i.e. the child's - // parent_matrix) is: - // M[parent]_for_child = M[parent] * Tr[origin] * - // composite_layer_transform - // = M[parent] * Tr[layer->position() + anchor] * - // M[layer] * Tr[anchor2origin] - // - // and a similar matrix for the full hierarchy with respect to the - // root. - // - // Finally, note that the final matrix used by the shader for the layer is P * - // M[draw] * S . This final product is computed in drawTexturedQuad(), where: - // P is the projection matrix - // S is the scale adjustment (to scale up a canonical quad to the - // layer's size) - // - // When a render surface has a replica layer, that layer's transform is used - // to draw a second copy of the surface. gfx::Transforms named here are - // relative to the surface, unless they specify they are relative to the - // replica layer. - // - // We will denote a scale by device scale S[deviceScale] - // - // The render surface draw transform to its target surface origin is: - // M[surfaceDraw] = M[owningLayer->Draw] - // - // The render surface origin transform to its the root (screen space) origin - // is: - // M[surface2root] = M[owningLayer->screenspace] * - // S[deviceScale].inverse() - // - // The replica draw transform to its target surface origin is: - // M[replicaDraw] = S[deviceScale] * M[surfaceDraw] * - // Tr[replica->position() + replica->anchor()] * Tr[replica] * - // Tr[origin2anchor].inverse() * S[contents_scale].inverse() - // - // The replica draw transform to the root (screen space) origin is: - // M[replica2root] = M[surface2root] * Tr[replica->position()] * - // Tr[replica] * Tr[origin2anchor].inverse() - // - - // It makes no sense to have a non-unit page_scale_factor without specifying - // which layer roots the subtree the scale is applied to. - DCHECK(globals.page_scale_layer || (globals.page_scale_factor == 1.f)); - - CHECK(!layer->visited()); - bool visited = true; - layer->set_visited(visited); - - DataForRecursion data_for_children; - data_for_children.in_subtree_of_page_scale_layer = - data_from_ancestor.in_subtree_of_page_scale_layer; - data_for_children.subtree_can_use_lcd_text = - data_from_ancestor.subtree_can_use_lcd_text; - - // Layers that are marked as hidden will hide themselves and their subtree. - // Exception: Layers with copy requests, whether hidden or not, must be drawn - // anyway. In this case, we will inform their subtree they are visible to get - // the right results. - const bool layer_is_visible = - data_from_ancestor.subtree_is_visible_from_ancestor && - layer->EffectiveOpacity() != 0; - const bool layer_is_drawn = layer_is_visible || layer->HasCopyRequest(); - - // The root layer cannot skip CalcDrawProperties. - if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_drawn)) { - return; - } - - // We need to circumvent the normal recursive flow of information for clip - // children (they don't inherit their direct ancestor's clip information). - // This is unfortunate, and would be unnecessary if we were to formally - // separate the clipping hierarchy from the layer hierarchy. - bool ancestor_clips_subtree = data_from_ancestor.ancestor_clips_subtree; - gfx::Rect ancestor_clip_rect_in_target_space = - data_from_ancestor.clip_rect_in_target_space; - - // Update our clipping state. If we have a clip parent we will need to pull - // from the clip state cache rather than using the clip state passed from our - // immediate ancestor. - UpdateClipRectsForClipChild(layer, &ancestor_clip_rect_in_target_space, - &ancestor_clips_subtree); - - // As this function proceeds, these are the properties for the current - // layer that actually get computed. To avoid unnecessary copies - // (particularly for matrices), we do computations directly on these values - // when possible. - DrawProperties& layer_draw_properties = layer->draw_properties(); - - gfx::Rect clip_rect_in_target_space; - bool layer_or_ancestor_clips_descendants = false; - - // This value is cached on the stack so that we don't have to inverse-project - // the surface's clip rect redundantly for every layer. This value is the - // same as the target surface's clip rect, except that instead of being - // described in the target surface's target's space, it is described in the - // current render target's space. - gfx::Rect clip_rect_of_target_surface_in_target_space; - - float accumulated_draw_opacity = layer->opacity(); - if (layer->parent()) - accumulated_draw_opacity *= layer->parent()->draw_opacity(); - - bool animating_transform_to_screen = - layer->HasPotentiallyRunningTransformAnimation(); - if (layer->parent()) { - animating_transform_to_screen |= - layer->parent()->screen_space_transform_is_animating(); - } - gfx::Point3F transform_origin = layer->transform_origin(); - gfx::ScrollOffset scroll_offset = GetEffectiveCurrentScrollOffset(layer); - gfx::PointF position = - layer->position() - ScrollOffsetToVector2dF(scroll_offset); - gfx::Transform combined_transform = data_from_ancestor.parent_matrix; - if (!layer->transform().IsIdentity()) { - // LT = Tr[origin] * Tr[origin2transformOrigin] - combined_transform.Translate3d(position.x() + transform_origin.x(), - position.y() + transform_origin.y(), - transform_origin.z()); - // LT = Tr[origin] * Tr[origin2origin] * M[layer] - combined_transform.PreconcatTransform(layer->transform()); - // LT = Tr[origin] * Tr[origin2origin] * M[layer] * - // Tr[transformOrigin2origin] - combined_transform.Translate3d( - -transform_origin.x(), -transform_origin.y(), -transform_origin.z()); - } else { - combined_transform.Translate(position.x(), position.y()); - } - - gfx::Vector2dF effective_scroll_delta = GetEffectiveScrollDelta(layer); - if (!animating_transform_to_screen && layer->scrollable() && - combined_transform.IsScaleOrTranslation()) { - // Align the scrollable layer's position to screen space pixels to avoid - // blurriness. To avoid side-effects, do this only if the transform is - // simple. - gfx::Vector2dF previous_translation = combined_transform.To2dTranslation(); - combined_transform.RoundTranslationComponents(); - gfx::Vector2dF current_translation = combined_transform.To2dTranslation(); - - // This rounding changes the scroll delta, and so must be included - // in the scroll compensation matrix. The scaling converts from physical - // coordinates to the scroll delta's CSS coordinates (using the parent - // matrix instead of combined transform since scrolling is applied before - // the layer's transform). For example, if we have a total scale factor of - // 3.0, then 1 physical pixel is only 1/3 of a CSS pixel. - gfx::Vector2dF parent_scales = MathUtil::ComputeTransform2dScaleComponents( - data_from_ancestor.parent_matrix, 1.f); - effective_scroll_delta -= - gfx::ScaleVector2d(current_translation - previous_translation, - 1.f / parent_scales.x(), - 1.f / parent_scales.y()); - } - - // Apply adjustment from position constraints. - ApplyPositionAdjustment(layer, data_from_ancestor.fixed_container, - data_from_ancestor.scroll_compensation_matrix, &combined_transform); - - bool combined_is_animating_scale = false; - float combined_maximum_animation_contents_scale = 0.f; - float combined_starting_animation_contents_scale = 0.f; - if (globals.can_adjust_raster_scales) { - CalculateAnimationContentsScale( - layer, data_from_ancestor.ancestor_is_animating_scale, - data_from_ancestor.maximum_animation_contents_scale, - data_from_ancestor.starting_animation_contents_scale, - data_from_ancestor.parent_matrix, combined_transform, - &combined_is_animating_scale, - &combined_maximum_animation_contents_scale, - &combined_starting_animation_contents_scale); - } - data_for_children.ancestor_is_animating_scale = combined_is_animating_scale; - data_for_children.maximum_animation_contents_scale = - combined_maximum_animation_contents_scale; - data_for_children.starting_animation_contents_scale = - combined_starting_animation_contents_scale; - - // Compute the 2d scale components of the transform hierarchy up to the target - // surface. From there, we can decide on a contents scale for the layer. - float layer_scale_factors = globals.device_scale_factor; - if (data_from_ancestor.in_subtree_of_page_scale_layer) - layer_scale_factors *= globals.page_scale_factor; - gfx::Vector2dF combined_transform_scales = - MathUtil::ComputeTransform2dScaleComponents( - combined_transform, - layer_scale_factors); - - UpdateLayerScaleDrawProperties(layer, - combined_maximum_animation_contents_scale, - combined_starting_animation_contents_scale); - - LayerImpl* mask_layer = layer->mask_layer(); - if (mask_layer) { - UpdateLayerScaleDrawProperties(mask_layer, - combined_maximum_animation_contents_scale, - combined_starting_animation_contents_scale); - } - - LayerImpl* replica_mask_layer = - layer->replica_layer() ? layer->replica_layer()->mask_layer() : NULL; - if (replica_mask_layer) { - UpdateLayerScaleDrawProperties(replica_mask_layer, - combined_maximum_animation_contents_scale, - combined_starting_animation_contents_scale); - } - - if (layer == globals.page_scale_layer) { - combined_transform.Scale(globals.page_scale_factor, - globals.page_scale_factor); - data_for_children.in_subtree_of_page_scale_layer = true; - } - - // The draw_transform that gets computed below is effectively the layer's - // draw_transform, unless the layer itself creates a render_surface. In that - // case, the render_surface re-parents the transforms. - layer_draw_properties.target_space_transform = combined_transform; - - // The layer's screen_space_transform represents the transform between root - // layer's "screen space" and local content space. - layer_draw_properties.screen_space_transform = - data_from_ancestor.full_hierarchy_matrix; - layer_draw_properties.screen_space_transform.PreconcatTransform - (layer_draw_properties.target_space_transform); - - bool layer_can_use_lcd_text = true; - bool subtree_can_use_lcd_text = true; - if (!globals.layers_always_allowed_lcd_text) { - // To avoid color fringing, LCD text should only be used on opaque layers - // with just integral translation. - subtree_can_use_lcd_text = data_from_ancestor.subtree_can_use_lcd_text && - accumulated_draw_opacity == 1.f && - layer_draw_properties.target_space_transform - .IsIdentityOrIntegerTranslation(); - // Also disable LCD text locally for non-opaque content. - layer_can_use_lcd_text = subtree_can_use_lcd_text && - layer->contents_opaque(); - } - - // full_hierarchy_matrix is the matrix that transforms objects between screen - // space (except projection matrix) and the most recent RenderSurfaceImpl's - // space. next_hierarchy_matrix will only change if this layer uses a new - // RenderSurfaceImpl, otherwise remains the same. - data_for_children.full_hierarchy_matrix = - data_from_ancestor.full_hierarchy_matrix; - - bool render_to_separate_surface = - IsRootLayer(layer) || - (globals.can_render_to_separate_surface && layer->render_surface()); - - if (render_to_separate_surface) { - DCHECK(layer->render_surface()); - // Check back-face visibility before continuing with this surface and its - // subtree - RenderSurfaceImpl* render_surface = layer->render_surface(); - if (!layer->double_sided() && - IsSurfaceBackFaceVisible(layer, combined_transform)) { - gfx::Transform draw_transform = combined_transform; - draw_transform.Scale(1.0 / combined_transform_scales.x(), - 1.0 / combined_transform_scales.y()); - render_surface->SetDrawTransform(draw_transform); - return; - } - - if (IsRootLayer(layer)) { - // The root layer's render surface size is predetermined and so the root - // layer can't directly support non-identity transforms. It should just - // forward top-level transforms to the rest of the tree. - data_for_children.parent_matrix = combined_transform; - } else { - // The owning layer's draw transform has a scale from content to layer - // space which we do not want; so here we use the combined_transform - // instead of the draw_transform. However, we do need to add a different - // scale factor that accounts for the surface's pixel dimensions. - // Remove the combined_transform scale from the draw transform. - gfx::Transform draw_transform = combined_transform; - draw_transform.Scale(1.0 / combined_transform_scales.x(), - 1.0 / combined_transform_scales.y()); - render_surface->SetDrawTransform(draw_transform); - - // The owning layer's transform was re-parented by the surface, so the - // layer's new draw_transform only needs to scale the layer to surface - // space. - layer_draw_properties.target_space_transform.MakeIdentity(); - layer_draw_properties.target_space_transform.Scale( - combined_transform_scales.x(), combined_transform_scales.y()); - - // Inside the surface's subtree, we scale everything to the owning layer's - // scale. The sublayer matrix transforms layer rects into target surface - // content space. Conceptually, all layers in the subtree inherit the - // scale at the point of the render surface in the transform hierarchy, - // but we apply it explicitly to the owning layer and the remainder of the - // subtree independently. - DCHECK(data_for_children.parent_matrix.IsIdentity()); - data_for_children.parent_matrix.Scale(combined_transform_scales.x(), - combined_transform_scales.y()); - } - - // The opacity value is moved from the layer to its surface, so that the - // entire subtree properly inherits opacity. - render_surface->SetDrawOpacity(accumulated_draw_opacity); - layer_draw_properties.opacity = 1.f; - DCHECK_EQ(layer->draw_blend_mode(), SkXfermode::kSrcOver_Mode); - - layer_draw_properties.screen_space_transform_is_animating = - animating_transform_to_screen; - - // Update the aggregate hierarchy matrix to include the transform of the - // newly created RenderSurfaceImpl. - data_for_children.full_hierarchy_matrix.PreconcatTransform( - render_surface->draw_transform()); - - // A render surface inherently acts as a flattening point for the content of - // its descendants. - data_for_children.full_hierarchy_matrix.FlattenTo2d(); - - if (layer->mask_layer()) { - DrawProperties& mask_layer_draw_properties = - layer->mask_layer()->draw_properties(); - mask_layer_draw_properties.visible_layer_rect = - gfx::Rect(layer->bounds()); - // Temporarily copy the draw transform of the mask's owning layer into the - // mask layer draw properties. This won't actually get used for drawing - // (the render surface uses the mask texture directly), but will get used - // to get the correct contents scale. - // TODO(enne): do something similar for property trees. - mask_layer_draw_properties.target_space_transform = - layer_draw_properties.target_space_transform; - } - - if (layer->replica_layer() && layer->replica_layer()->mask_layer()) { - DrawProperties& replica_mask_draw_properties = - layer->replica_layer()->mask_layer()->draw_properties(); - replica_mask_draw_properties.visible_layer_rect = - gfx::Rect(layer->bounds()); - replica_mask_draw_properties.target_space_transform = - layer_draw_properties.target_space_transform; - } - - layer_or_ancestor_clips_descendants = false; - bool subtree_is_clipped_by_surface_bounds = false; - // It may be the layer or the surface doing the clipping of the subtree, - // but in either case, we'll be clipping to the projected clip rect of our - // ancestor. - gfx::Transform inverse_surface_draw_transform( - gfx::Transform::kSkipInitialization); - if (!render_surface->draw_transform().GetInverse( - &inverse_surface_draw_transform)) { - // TODO(shawnsingh): Either we need to handle uninvertible transforms - // here, or DCHECK that the transform is invertible. - } - - gfx::Rect surface_clip_rect_in_target_space = - data_from_ancestor.clip_rect_of_target_surface_in_target_space; - if (ancestor_clips_subtree) - surface_clip_rect_in_target_space.Intersect( - ancestor_clip_rect_in_target_space); - gfx::Rect projected_surface_rect = MathUtil::ProjectEnclosingClippedRect( - inverse_surface_draw_transform, surface_clip_rect_in_target_space); - clip_rect_of_target_surface_in_target_space = projected_surface_rect; - - if (ancestor_clips_subtree) { - if (layer_draw_properties.num_unclipped_descendants > 0u) { - // If we have unclipped descendants, we cannot count on the render - // surface's bounds clipping our subtree: the unclipped descendants - // could cause us to expand our bounds. In this case, we must rely on - // layer clipping for correctess. NB: since we can only encounter - // translations between a clip child and its clip parent, clipping is - // guaranteed to be exact in this case. - layer_or_ancestor_clips_descendants = true; - clip_rect_in_target_space = projected_surface_rect; - } else { - // The new render_surface here will correctly clip the entire subtree. - // So, we do not need to continue propagating the clipping state further - // down the tree. This way, we can avoid transforming clip rects from - // ancestor target surface space to current target surface space that - // could cause more w < 0 headaches. The render surface clip rect is - // expressed in the space where this surface draws, i.e. the same space - // as clip_rect_from_ancestor_in_ancestor_target_space. - render_surface->SetClipRect(ancestor_clip_rect_in_target_space); - subtree_is_clipped_by_surface_bounds = true; - } - } - - DCHECK(layer->render_surface()); - DCHECK(!layer->parent() || layer->parent()->render_target() == - accumulated_surface_state->back().render_target); - - accumulated_surface_state->push_back(AccumulatedSurfaceState(layer)); - - render_surface->SetIsClipped(subtree_is_clipped_by_surface_bounds); - if (!subtree_is_clipped_by_surface_bounds) { - render_surface->SetClipRect(gfx::Rect()); - } - - // If the new render surface is drawn translucent or with a non-integral - // translation then the subtree that gets drawn on this render surface - // cannot use LCD text. - data_for_children.subtree_can_use_lcd_text = subtree_can_use_lcd_text; - - } else { - DCHECK(layer->parent()); - - // Note: layer_draw_properties.target_space_transform is computed above, - // before this if-else statement. - layer_draw_properties.screen_space_transform_is_animating = - animating_transform_to_screen; - layer_draw_properties.opacity = accumulated_draw_opacity; - DCHECK_EQ(layer->draw_blend_mode(), layer->blend_mode()); - data_for_children.parent_matrix = combined_transform; - - // Layers without render_surfaces directly inherit the ancestor's clip - // status. - layer_or_ancestor_clips_descendants = ancestor_clips_subtree; - if (ancestor_clips_subtree) { - clip_rect_in_target_space = - ancestor_clip_rect_in_target_space; - } - - // The surface's cached clip rect value propagates regardless of what - // clipping goes on between layers here. - clip_rect_of_target_surface_in_target_space = - data_from_ancestor.clip_rect_of_target_surface_in_target_space; - } - - layer_draw_properties.can_use_lcd_text = layer_can_use_lcd_text; - - // The layer bounds() includes the layer's bounds_delta() which we want - // for the clip rect. - gfx::Rect rect_in_target_space = MathUtil::MapEnclosingClippedRect( - layer->draw_properties().target_space_transform, - gfx::Rect(layer->bounds())); - - if (LayerClipsSubtree(layer)) { - layer_or_ancestor_clips_descendants = true; - if (ancestor_clips_subtree && !render_to_separate_surface) { - // A layer without render surface shares the same target as its ancestor. - clip_rect_in_target_space = - ancestor_clip_rect_in_target_space; - clip_rect_in_target_space.Intersect(rect_in_target_space); - } else { - clip_rect_in_target_space = rect_in_target_space; - } - } - - // Tell the layer the rect that it's clipped by. In theory we could use a - // tighter clip rect here (drawable_content_rect), but that actually does not - // reduce how much would be drawn, and instead it would create unnecessary - // changes to scissor state affecting GPU performance. Our clip information - // is used in the recursion below, so we must set it beforehand. - layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants; - if (layer_or_ancestor_clips_descendants) { - layer_draw_properties.clip_rect = clip_rect_in_target_space; - } else { - // Initialize the clip rect to a safe value that will not clip the - // layer, just in case clipping is still accidentally used. - layer_draw_properties.clip_rect = rect_in_target_space; - } - - if (!layer->children().empty()) { - if (layer == globals.elastic_overscroll_application_layer) { - data_for_children.parent_matrix.Translate( - -globals.elastic_overscroll.x(), -globals.elastic_overscroll.y()); - } - - // Flatten to 2D if the layer doesn't preserve 3D. - if (layer->should_flatten_transform()) - data_for_children.parent_matrix.FlattenTo2d(); - - data_for_children.scroll_compensation_matrix = - ComputeScrollCompensationMatrixForChildren( - layer, - data_from_ancestor.parent_matrix, - data_from_ancestor.scroll_compensation_matrix, - effective_scroll_delta); - data_for_children.fixed_container = - layer->IsContainerForFixedPositionLayers() ? - layer : data_from_ancestor.fixed_container; - - data_for_children.clip_rect_in_target_space = clip_rect_in_target_space; - data_for_children.clip_rect_of_target_surface_in_target_space = - clip_rect_of_target_surface_in_target_space; - data_for_children.ancestor_clips_subtree = - layer_or_ancestor_clips_descendants; - data_for_children.subtree_is_visible_from_ancestor = layer_is_drawn; - } - - std::vector<LayerImpl*> sorted_children; - if (layer_draw_properties.has_child_with_a_scroll_parent) - SortChildrenForRecursion(&sorted_children, *layer); - - for (size_t i = 0; i < layer->children().size(); ++i) { - // If one of layer's children has a scroll parent, then we may have to - // visit the children out of order. The new order is stored in - // sorted_children. Otherwise, we'll grab the child directly from the - // layer's list of children. - - LayerImpl* child = - layer_draw_properties.has_child_with_a_scroll_parent - ? sorted_children[i] - : LayerTreeHostCommon::get_layer_as_raw_ptr(layer->children(), i); - - CalculateDrawPropertiesInternal(child, globals, data_for_children, - accumulated_surface_state); - - if (child->layer_or_descendant_is_drawn()) { - bool layer_or_descendant_is_drawn = true; - layer->set_layer_or_descendant_is_drawn(layer_or_descendant_is_drawn); - } - } - - // Compute the total drawable_content_rect for this subtree (the rect is in - // target surface space). - gfx::Rect local_drawable_content_rect_of_subtree = - accumulated_surface_state->back().drawable_content_rect; - if (render_to_separate_surface) { - DCHECK(accumulated_surface_state->back().render_target == layer); - accumulated_surface_state->pop_back(); - } - - // Compute the layer's drawable content rect (the rect is in target surface - // space). - layer_draw_properties.drawable_content_rect = rect_in_target_space; - if (layer_or_ancestor_clips_descendants) { - layer_draw_properties.drawable_content_rect.Intersect( - clip_rect_in_target_space); - } - if (layer->DrawsContent()) { - local_drawable_content_rect_of_subtree.Union( - layer_draw_properties.drawable_content_rect); - } - - // Compute the layer's visible content rect (the rect is in content space). - layer_draw_properties.visible_layer_rect = CalculateVisibleLayerRect( - layer, clip_rect_of_target_surface_in_target_space, rect_in_target_space); - - // Compute the remaining properties for the render surface, if the layer has - // one. - if (IsRootLayer(layer)) { - // The root layer's surface's content_rect is always the entire viewport. - DCHECK(render_to_separate_surface); - layer->render_surface()->SetContentRect( - ancestor_clip_rect_in_target_space); - } else if (render_to_separate_surface) { - RenderSurfaceImpl* render_surface = layer->render_surface(); - gfx::Rect clipped_content_rect = local_drawable_content_rect_of_subtree; - - // Don't clip if the layer is reflected as the reflection shouldn't be - // clipped. Also, don't clip if the layer has copy requests. - if (!layer->replica_layer() && !layer->HasCopyRequest()) { - // Note, it is correct to use data_from_ancestor.ancestor_clips_subtree - // here, because we are looking at this layer's render_surface, not the - // layer itself. - if (render_surface->is_clipped() && !clipped_content_rect.IsEmpty()) { - gfx::Rect surface_clip_rect = LayerTreeHostCommon::CalculateVisibleRect( - render_surface->clip_rect(), - clipped_content_rect, - render_surface->draw_transform()); - clipped_content_rect.Intersect(surface_clip_rect); - } - } - - // The RenderSurfaceImpl backing texture cannot exceed the maximum supported - // texture size. - clipped_content_rect.set_width( - std::min(clipped_content_rect.width(), globals.max_texture_size)); - clipped_content_rect.set_height( - std::min(clipped_content_rect.height(), globals.max_texture_size)); - - // Layers having a non-default blend mode will blend with the content - // inside its parent's render target. This render target should be - // either root_for_isolated_group, or the root of the layer tree. - // Otherwise, this layer will use an incomplete backdrop, limited to its - // render target and the blending result will be incorrect. - DCHECK(layer->uses_default_blend_mode() || IsRootLayer(layer) || - !layer->parent()->render_target() || - IsRootLayer(layer->parent()->render_target()) || - layer->parent()->render_target()->is_root_for_isolated_group()); - - render_surface->SetContentRect(clipped_content_rect); - - if (clipped_content_rect.IsEmpty()) { - return; - } - - // The owning layer's screen_space_transform has a scale from content to - // layer space which we need to undo and replace with a scale from the - // surface's subtree into layer space. - gfx::Transform screen_space_transform = - layer->draw_properties().screen_space_transform; - screen_space_transform.Scale(1.0 / combined_transform_scales.x(), - 1.0 / combined_transform_scales.y()); - render_surface->SetScreenSpaceTransform(screen_space_transform); - - if (layer->replica_layer()) { - gfx::Transform surface_origin_to_replica_origin_transform; - surface_origin_to_replica_origin_transform.Scale( - combined_transform_scales.x(), combined_transform_scales.y()); - surface_origin_to_replica_origin_transform.Translate( - layer->replica_layer()->position().x() + - layer->replica_layer()->transform_origin().x(), - layer->replica_layer()->position().y() + - layer->replica_layer()->transform_origin().y()); - surface_origin_to_replica_origin_transform.PreconcatTransform( - layer->replica_layer()->transform()); - surface_origin_to_replica_origin_transform.Translate( - -layer->replica_layer()->transform_origin().x(), - -layer->replica_layer()->transform_origin().y()); - surface_origin_to_replica_origin_transform.Scale( - 1.0 / combined_transform_scales.x(), - 1.0 / combined_transform_scales.y()); - - // Compute the replica's "originTransform" that maps from the replica's - // origin space to the target surface origin space. - gfx::Transform replica_origin_transform = - layer->render_surface()->draw_transform() * - surface_origin_to_replica_origin_transform; - render_surface->SetReplicaDrawTransform(replica_origin_transform); - - // Compute the replica's "screen_space_transform" that maps from the - // replica's origin space to the screen's origin space. - gfx::Transform replica_screen_space_transform = - layer->render_surface()->screen_space_transform() * - surface_origin_to_replica_origin_transform; - render_surface->SetReplicaScreenSpaceTransform( - replica_screen_space_transform); - } - } - - SavePaintPropertiesLayer(layer); - - UpdateAccumulatedSurfaceState(layer, local_drawable_content_rect_of_subtree, - accumulated_surface_state); -} // NOLINT(readability/fn_size) - -static void ProcessCalcDrawPropsInputs( - const LayerTreeHostCommon::CalcDrawPropsImplInputs& inputs, - SubtreeGlobals* globals, - DataForRecursion* data_for_recursion) { - DCHECK(inputs.root_layer); - DCHECK(IsRootLayer(inputs.root_layer)); - DCHECK(inputs.render_surface_layer_list); - - gfx::Transform identity_matrix; - - // The root layer's render_surface should receive the device viewport as the - // initial clip rect. - gfx::Rect device_viewport_rect(inputs.device_viewport_size); - - gfx::Vector2dF device_transform_scale_components = - MathUtil::ComputeTransform2dScaleComponents(inputs.device_transform, 1.f); - // Not handling the rare case of different x and y device scale. - float device_transform_scale = - std::max(device_transform_scale_components.x(), - device_transform_scale_components.y()); - - gfx::Transform scaled_device_transform = inputs.device_transform; - scaled_device_transform.Scale(inputs.device_scale_factor, - inputs.device_scale_factor); - - globals->max_texture_size = inputs.max_texture_size; - globals->device_scale_factor = - inputs.device_scale_factor * device_transform_scale; - globals->page_scale_factor = inputs.page_scale_factor; - globals->page_scale_layer = inputs.page_scale_layer; - globals->elastic_overscroll = inputs.elastic_overscroll; - globals->elastic_overscroll_application_layer = - inputs.elastic_overscroll_application_layer; - globals->can_render_to_separate_surface = - inputs.can_render_to_separate_surface; - globals->can_adjust_raster_scales = inputs.can_adjust_raster_scales; - globals->layers_always_allowed_lcd_text = - inputs.layers_always_allowed_lcd_text; - - data_for_recursion->parent_matrix = scaled_device_transform; - data_for_recursion->full_hierarchy_matrix = identity_matrix; - data_for_recursion->scroll_compensation_matrix = identity_matrix; - data_for_recursion->fixed_container = inputs.root_layer; - data_for_recursion->clip_rect_in_target_space = device_viewport_rect; - data_for_recursion->clip_rect_of_target_surface_in_target_space = - device_viewport_rect; - data_for_recursion->maximum_animation_contents_scale = 0.f; - data_for_recursion->starting_animation_contents_scale = 0.f; - data_for_recursion->ancestor_is_animating_scale = false; - data_for_recursion->ancestor_clips_subtree = true; - data_for_recursion->in_subtree_of_page_scale_layer = false; - data_for_recursion->subtree_can_use_lcd_text = inputs.can_use_lcd_text; - data_for_recursion->subtree_is_visible_from_ancestor = true; -} - // A layer jitters if its screen space transform is same on two successive // commits, but has changed in between the commits. CalculateFrameJitter // computes the jitter in the entire frame. @@ -2186,139 +1164,6 @@ return jitter; } -void VerifyPropertyTreeValuesForSurface(RenderSurfaceImpl* render_surface, - PropertyTrees* property_trees) { - RenderSurfaceDrawProperties draw_properties; - ComputeSurfaceDrawPropertiesUsingPropertyTrees(render_surface, property_trees, - &draw_properties); - // TODO(vollick): This tolerance should be lower: crbug.com/471786 - const int tolerance = 1; - - // content_rect has to be computed recursively, so is computed separately from - // other draw properties. - draw_properties.content_rect = - render_surface->content_rect_from_property_trees(); - - const bool render_surface_draw_transforms_match = - render_surface->draw_transform().ApproximatelyEqual( - draw_properties.draw_transform); - CHECK(render_surface_draw_transforms_match) - << "expected: " << render_surface->draw_transform().ToString() - << " actual: " << draw_properties.draw_transform.ToString(); - - const bool render_surface_screen_space_transform_match = - render_surface->screen_space_transform().ApproximatelyEqual( - draw_properties.screen_space_transform); - CHECK(render_surface_screen_space_transform_match) - << "expected: " << render_surface->screen_space_transform().ToString() - << " actual: " << draw_properties.screen_space_transform.ToString(); - - const bool render_surface_replica_draw_transforms_match = - render_surface->replica_draw_transform().ApproximatelyEqual( - draw_properties.replica_draw_transform); - CHECK(render_surface_replica_draw_transforms_match) - << "expected: " << render_surface->replica_draw_transform().ToString() - << " actual: " << draw_properties.replica_draw_transform.ToString(); - - const bool render_surface_replica_screen_space_transforms_match = - render_surface->replica_screen_space_transform().ApproximatelyEqual( - draw_properties.replica_screen_space_transform); - CHECK(render_surface_replica_screen_space_transforms_match) - << "expected: " - << render_surface->replica_screen_space_transform().ToString() - << " actual: " - << draw_properties.replica_screen_space_transform.ToString(); - - CHECK_EQ(render_surface->is_clipped(), draw_properties.is_clipped); - - const bool render_surface_clip_rects_match = - render_surface->clip_rect().ApproximatelyEqual(draw_properties.clip_rect, - tolerance); - CHECK(render_surface_clip_rects_match) - << "expected: " << render_surface->clip_rect().ToString() - << " actual: " << draw_properties.clip_rect.ToString(); - - CHECK_EQ(render_surface->draw_opacity(), draw_properties.draw_opacity); - - const bool render_surface_content_rects_match = - render_surface->content_rect().ApproximatelyEqual( - draw_properties.content_rect, tolerance); - CHECK(render_surface_content_rects_match) - << "expected: " << render_surface->content_rect().ToString() - << " actual: " << draw_properties.content_rect.ToString(); -} - -void VerifyPropertyTreeValuesForLayer(LayerImpl* current_layer, - PropertyTrees* property_trees, - bool layers_always_allowed_lcd_text, - bool can_use_lcd_text) { - DrawProperties draw_properties; - ComputeLayerDrawPropertiesUsingPropertyTrees( - current_layer, property_trees, layers_always_allowed_lcd_text, - can_use_lcd_text, &draw_properties); - // TODO(vollick): This tolerance should be lower: crbug.com/471786 - const int tolerance = 1; - - const bool visible_rects_match = - current_layer->visible_layer_rect().ApproximatelyEqual( - draw_properties.visible_layer_rect, tolerance); - CHECK(visible_rects_match) - << "expected: " << current_layer->visible_layer_rect().ToString() - << " actual: " << draw_properties.visible_layer_rect.ToString(); - - const bool draw_transforms_match = - current_layer->draw_properties() - .target_space_transform.ApproximatelyEqual( - draw_properties.target_space_transform); - CHECK(draw_transforms_match) - << "expected: " - << current_layer->draw_properties().target_space_transform.ToString() - << " actual: " << draw_properties.target_space_transform.ToString(); - - CHECK_EQ(current_layer->draw_opacity(), draw_properties.opacity); - CHECK_EQ(current_layer->can_use_lcd_text(), draw_properties.can_use_lcd_text); - CHECK_EQ(current_layer->is_clipped(), draw_properties.is_clipped); - CHECK_EQ(current_layer->screen_space_transform_is_animating(), - draw_properties.screen_space_transform_is_animating); - - const bool drawable_content_rects_match = - current_layer->drawable_content_rect().ApproximatelyEqual( - draw_properties.drawable_content_rect, tolerance); - CHECK(drawable_content_rects_match) - << "expected: " << current_layer->drawable_content_rect().ToString() - << " actual: " << draw_properties.drawable_content_rect.ToString(); - - const bool clip_rects_match = current_layer->clip_rect().ApproximatelyEqual( - draw_properties.clip_rect, tolerance); - CHECK(clip_rects_match) << "expected: " - << current_layer->clip_rect().ToString() - << " actual: " - << draw_properties.clip_rect.ToString(); - - CHECK_EQ(current_layer->draw_properties().maximum_animation_contents_scale, - draw_properties.maximum_animation_contents_scale); - CHECK_EQ(current_layer->draw_properties().starting_animation_contents_scale, - draw_properties.starting_animation_contents_scale); -} - -void VerifyPropertyTreeValues( - LayerTreeHostCommon::CalcDrawPropsImplInputs* inputs) { - LayerIterator it, end; - for (it = LayerIterator::Begin(inputs->render_surface_layer_list), - end = LayerIterator::End(inputs->render_surface_layer_list); - it != end; ++it) { - LayerImpl* current_layer = *it; - if (it.represents_target_render_surface()) - VerifyPropertyTreeValuesForSurface(current_layer->render_surface(), - inputs->property_trees); - if (!it.represents_itself() || !current_layer->DrawsContent()) - continue; - VerifyPropertyTreeValuesForLayer(current_layer, inputs->property_trees, - inputs->layers_always_allowed_lcd_text, - inputs->can_use_lcd_text); - } -} - enum PropertyTreeOption { BUILD_PROPERTY_TREES_IF_NEEDED, DONT_BUILD_PROPERTY_TREES @@ -2327,19 +1172,11 @@ void CalculateRenderTargetInternal(LayerImpl* layer, PropertyTrees* property_trees, bool subtree_visible_from_ancestor, - bool can_render_to_separate_surface, - bool use_property_trees) { + bool can_render_to_separate_surface) { bool layer_is_drawn; - if (use_property_trees) { - DCHECK_GE(layer->effect_tree_index(), 0); - layer_is_drawn = - property_trees->effect_tree.Node(layer->effect_tree_index()) - ->data.is_drawn; - } else { - layer_is_drawn = - (subtree_visible_from_ancestor && layer->EffectiveOpacity() != 0) || - layer->HasCopyRequest(); - } + DCHECK_GE(layer->effect_tree_index(), 0); + layer_is_drawn = property_trees->effect_tree.Node(layer->effect_tree_index()) + ->data.is_drawn; // The root layer cannot be skipped. if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_drawn)) { @@ -2372,8 +1209,7 @@ for (size_t i = 0; i < layer->children().size(); ++i) { CalculateRenderTargetInternal( LayerTreeHostCommon::get_layer_as_raw_ptr(layer->children(), i), - property_trees, layer_is_drawn, can_render_to_separate_surface, - use_property_trees); + property_trees, layer_is_drawn, can_render_to_separate_surface); } } @@ -2386,9 +1222,7 @@ bool subtree_visible_from_ancestor, const bool can_render_to_separate_surface, const int current_render_surface_layer_list_id, - const int max_texture_size, - const bool verify_property_trees, - const bool use_property_trees) { + const int max_texture_size) { // This calculates top level Render Surface Layer List, and Layer List for all // Render Surfaces. @@ -2405,16 +1239,9 @@ // |can_render_to_separate_surface| and |current_render_surface_layer_list_id| // are settings that should stay the same during recursion. bool layer_is_drawn = false; - if (use_property_trees) { - DCHECK_GE(layer->effect_tree_index(), 0); - layer_is_drawn = - property_trees->effect_tree.Node(layer->effect_tree_index()) - ->data.is_drawn; - } else { - layer_is_drawn = - (subtree_visible_from_ancestor && layer->EffectiveOpacity() != 0) || - layer->HasCopyRequest(); - } + DCHECK_GE(layer->effect_tree_index(), 0); + layer_is_drawn = property_trees->effect_tree.Node(layer->effect_tree_index()) + ->data.is_drawn; // The root layer cannot be skipped. if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_drawn)) { @@ -2430,25 +1257,22 @@ if (render_to_separate_surface) { DCHECK(layer->render_surface()); - - if (use_property_trees) { - RenderSurfaceDrawProperties draw_properties; - ComputeSurfaceDrawPropertiesUsingPropertyTrees( - layer->render_surface(), property_trees, &draw_properties); - // TODO(ajuma): Once property tree verification is removed, make the above - // call directly set the surface's properties, so that the copying below - // is no longer needed. - layer->render_surface()->SetIsClipped(draw_properties.is_clipped); - layer->render_surface()->SetDrawOpacity(draw_properties.draw_opacity); - layer->render_surface()->SetDrawTransform(draw_properties.draw_transform); - layer->render_surface()->SetScreenSpaceTransform( - draw_properties.screen_space_transform); - layer->render_surface()->SetReplicaDrawTransform( - draw_properties.replica_draw_transform); - layer->render_surface()->SetReplicaScreenSpaceTransform( - draw_properties.replica_screen_space_transform); - layer->render_surface()->SetClipRect(draw_properties.clip_rect); - } + RenderSurfaceDrawProperties draw_properties; + ComputeSurfaceDrawPropertiesUsingPropertyTrees( + layer->render_surface(), property_trees, &draw_properties); + // TODO(ajuma): Once property tree verification is removed, make the above + // call directly set the surface's properties, so that the copying below + // is no longer needed. + layer->render_surface()->SetIsClipped(draw_properties.is_clipped); + layer->render_surface()->SetDrawOpacity(draw_properties.draw_opacity); + layer->render_surface()->SetDrawTransform(draw_properties.draw_transform); + layer->render_surface()->SetScreenSpaceTransform( + draw_properties.screen_space_transform); + layer->render_surface()->SetReplicaDrawTransform( + draw_properties.replica_draw_transform); + layer->render_surface()->SetReplicaScreenSpaceTransform( + draw_properties.replica_screen_space_transform); + layer->render_surface()->SetClipRect(draw_properties.clip_rect); if (!layer->double_sided() && IsSurfaceBackFaceVisible(layer, @@ -2464,11 +1288,8 @@ layer->render_surface()->set_contributes_to_drawn_surface(false); } else { bool contributes_to_drawn_surface = - use_property_trees - ? property_trees->effect_tree.ContributesToDrawnSurface( - layer->effect_tree_index()) - : subtree_visible_from_ancestor && - layer->EffectiveOpacity() != 0.f; + property_trees->effect_tree.ContributesToDrawnSurface( + layer->effect_tree_index()); layer->render_surface()->set_contributes_to_drawn_surface( contributes_to_drawn_surface); } @@ -2496,19 +1317,17 @@ size_t descendants_size = descendants->size(); - bool layer_should_be_skipped = - LayerShouldBeSkipped(layer, layer_is_drawn, use_property_trees, - property_trees->transform_tree); + bool layer_should_be_skipped = LayerShouldBeSkipped( + layer, layer_is_drawn, property_trees->transform_tree); if (!layer_should_be_skipped) { MarkLayerWithRenderSurfaceLayerListId(layer, current_render_surface_layer_list_id); descendants->push_back(layer); } - bool compute_content_rects = verify_property_trees || use_property_trees; // Clear the old accumulated content rect of surface. - if (compute_content_rects && render_to_separate_surface) + if (render_to_separate_surface) layer->render_surface()->SetAccumulatedContentRect(gfx::Rect()); for (const auto& child_layer : layer->children()) { @@ -2516,7 +1335,7 @@ child_layer.get(), property_trees, render_surface_layer_list, descendants, nearest_occlusion_immune_ancestor, layer_is_drawn, can_render_to_separate_surface, current_render_surface_layer_list_id, - max_texture_size, verify_property_trees, use_property_trees); + max_texture_size); // If the child is its own render target, then it has a render surface. if (child_layer->render_target() == child_layer.get() && @@ -2545,73 +1364,63 @@ // The render surface's content rect is the union of drawable content rects // of the layers that draw into the surface. If the render surface is clipped, // it is also intersected with the render's surface clip rect. - if (compute_content_rects) { - if (!IsRootLayer(layer)) { - if (render_to_separate_surface) { - gfx::Rect surface_content_rect = - layer->render_surface()->accumulated_content_rect(); - // If the owning layer of a render surface draws content, the content - // rect of the render surface is expanded to include the drawable - // content rect of the layer. - if (layer->DrawsContent()) - surface_content_rect.Union(layer->drawable_content_rect()); + if (!IsRootLayer(layer)) { + if (render_to_separate_surface) { + gfx::Rect surface_content_rect = + layer->render_surface()->accumulated_content_rect(); + // If the owning layer of a render surface draws content, the content + // rect of the render surface is expanded to include the drawable + // content rect of the layer. + if (layer->DrawsContent()) + surface_content_rect.Union(layer->drawable_content_rect()); - if (!layer->replica_layer() && !layer->HasCopyRequest() && - layer->render_surface()->is_clipped()) { - // Here, we clip the render surface's content rect with its clip rect. - // As the clip rect of render surface is in the surface's target - // space, we first map the content rect into the target space, - // intersect it with clip rect and project back the result to the - // surface space. - if (!surface_content_rect.IsEmpty()) { - gfx::Rect surface_clip_rect = - LayerTreeHostCommon::CalculateVisibleRect( - layer->render_surface()->clip_rect(), surface_content_rect, - layer->render_surface()->draw_transform()); - surface_content_rect.Intersect(surface_clip_rect); - } - } - // The RenderSurfaceImpl backing texture cannot exceed the maximum - // supported texture size. - surface_content_rect.set_width( - std::min(surface_content_rect.width(), max_texture_size)); - surface_content_rect.set_height( - std::min(surface_content_rect.height(), max_texture_size)); - if (use_property_trees) - layer->render_surface()->SetContentRect(surface_content_rect); - if (verify_property_trees) { - layer->render_surface()->SetContentRectFromPropertyTrees( - surface_content_rect); + if (!layer->replica_layer() && !layer->HasCopyRequest() && + layer->render_surface()->is_clipped()) { + // Here, we clip the render surface's content rect with its clip rect. + // As the clip rect of render surface is in the surface's target + // space, we first map the content rect into the target space, + // intersect it with clip rect and project back the result to the + // surface space. + if (!surface_content_rect.IsEmpty()) { + gfx::Rect surface_clip_rect = + LayerTreeHostCommon::CalculateVisibleRect( + layer->render_surface()->clip_rect(), surface_content_rect, + layer->render_surface()->draw_transform()); + surface_content_rect.Intersect(surface_clip_rect); } } - const LayerImpl* parent_target = layer->parent()->render_target(); - if (!IsRootLayer(parent_target)) { - gfx::Rect surface_content_rect = - parent_target->render_surface()->accumulated_content_rect(); - if (render_to_separate_surface) { - // If the layer owns a surface, then the content rect is in the wrong - // space. Instead, we will use the surface's DrawableContentRect which - // is in target space as required. We also need to clip it with the - // target's clip if the target is clipped. - surface_content_rect.Union(gfx::ToEnclosedRect( - layer->render_surface()->DrawableContentRect())); - if (parent_target->is_clipped()) - surface_content_rect.Intersect(parent_target->clip_rect()); - } else if (layer->DrawsContent()) { - surface_content_rect.Union(layer->drawable_content_rect()); - } - parent_target->render_surface()->SetAccumulatedContentRect( - surface_content_rect); - } - } else { - // The root layer's surface content rect is always the entire viewport. - gfx::Rect viewport = - gfx::ToEnclosingRect(property_trees->clip_tree.ViewportClip()); - if (use_property_trees) - layer->render_surface()->SetContentRect(viewport); - if (verify_property_trees) - layer->render_surface()->SetContentRectFromPropertyTrees(viewport); + // The RenderSurfaceImpl backing texture cannot exceed the maximum + // supported texture size. + surface_content_rect.set_width( + std::min(surface_content_rect.width(), max_texture_size)); + surface_content_rect.set_height( + std::min(surface_content_rect.height(), max_texture_size)); + layer->render_surface()->SetContentRect(surface_content_rect); } + const LayerImpl* parent_target = layer->parent()->render_target(); + if (!IsRootLayer(parent_target)) { + gfx::Rect surface_content_rect = + parent_target->render_surface()->accumulated_content_rect(); + if (render_to_separate_surface) { + // If the layer owns a surface, then the content rect is in the wrong + // space. Instead, we will use the surface's DrawableContentRect which + // is in target space as required. We also need to clip it with the + // target's clip if the target is clipped. + surface_content_rect.Union(gfx::ToEnclosedRect( + layer->render_surface()->DrawableContentRect())); + if (parent_target->is_clipped()) + surface_content_rect.Intersect(parent_target->clip_rect()); + } else if (layer->DrawsContent()) { + surface_content_rect.Union(layer->drawable_content_rect()); + } + parent_target->render_surface()->SetAccumulatedContentRect( + surface_content_rect); + } + } else { + // The root layer's surface content rect is always the entire viewport. + gfx::Rect viewport = + gfx::ToEnclosingRect(property_trees->clip_tree.ViewportClip()); + layer->render_surface()->SetContentRect(viewport); } if (render_to_separate_surface && !IsRootLayer(layer) && @@ -2629,10 +1438,8 @@ void CalculateRenderTarget( LayerTreeHostCommon::CalcDrawPropsImplInputs* inputs) { - CalculateRenderTargetInternal( - inputs->root_layer, inputs->property_trees, true, - inputs->can_render_to_separate_surface, - inputs->verify_property_trees || inputs->use_property_trees); + CalculateRenderTargetInternal(inputs->root_layer, inputs->property_trees, + true, inputs->can_render_to_separate_surface); } void CalculateRenderSurfaceLayerList( @@ -2645,8 +1452,7 @@ inputs->root_layer, inputs->property_trees, inputs->render_surface_layer_list, nullptr, nullptr, subtree_visible_from_ancestor, inputs->can_render_to_separate_surface, - inputs->current_render_surface_layer_list_id, inputs->max_texture_size, - inputs->verify_property_trees, inputs->use_property_trees); + inputs->current_render_surface_layer_list_id, inputs->max_texture_size); } static void ComputeMaskLayerDrawProperties(const LayerImpl* layer, @@ -2663,7 +1469,7 @@ layer->draw_properties().starting_animation_contents_scale; } -void CalculateDrawPropertiesAndVerify( +void CalculateDrawPropertiesInternal( LayerTreeHostCommon::CalcDrawPropsImplInputs* inputs, PropertyTreeOption property_tree_option) { inputs->render_surface_layer_list->clear(); @@ -2672,77 +1478,72 @@ PreCalculateMetaInformationRecursiveData recursive_data; PreCalculateMetaInformationInternal(inputs->root_layer, &recursive_data); const bool should_measure_property_tree_performance = - inputs->verify_property_trees && - (property_tree_option == BUILD_PROPERTY_TREES_IF_NEEDED); + property_tree_option == BUILD_PROPERTY_TREES_IF_NEEDED; LayerImplList visible_layer_list; - if (inputs->verify_property_trees || inputs->use_property_trees) { - switch (property_tree_option) { - case BUILD_PROPERTY_TREES_IF_NEEDED: { - // The translation from layer to property trees is an intermediate - // state. We will eventually get these data passed directly to the - // compositor. - if (should_measure_property_tree_performance) { - TRACE_EVENT_BEGIN0( - TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), - "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees"); - } - - BuildPropertyTreesAndComputeVisibleRects( - inputs->root_layer, inputs->page_scale_layer, - inputs->inner_viewport_scroll_layer, - inputs->outer_viewport_scroll_layer, - inputs->elastic_overscroll_application_layer, - inputs->elastic_overscroll, inputs->page_scale_factor, - inputs->device_scale_factor, - gfx::Rect(inputs->device_viewport_size), inputs->device_transform, - inputs->can_render_to_separate_surface, inputs->property_trees, - &visible_layer_list); - - // Property trees are normally constructed on the main thread and - // passed to compositor thread. Source to parent updates on them are not - // allowed in the compositor thread. Some tests build them on the - // compositor thread, so we need to explicitly disallow source to parent - // updates when they are built on compositor thread. - inputs->property_trees->transform_tree - .set_source_to_parent_updates_allowed(false); - if (should_measure_property_tree_performance) { - TRACE_EVENT_END0( - TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), - "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees"); - } - - break; - } - case DONT_BUILD_PROPERTY_TREES: { - TRACE_EVENT0( + switch (property_tree_option) { + case BUILD_PROPERTY_TREES_IF_NEEDED: { + // The translation from layer to property trees is an intermediate + // state. We will eventually get these data passed directly to the + // compositor. + if (should_measure_property_tree_performance) { + TRACE_EVENT_BEGIN0( TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), - "LayerTreeHostCommon::ComputeJustVisibleRectsWithPropertyTrees"); - // Since page scale and elastic overscroll are SyncedProperties, changes - // on the active tree immediately affect the pending tree, so instead of - // trying to update property trees whenever these values change, we - // update property trees before using them. - UpdatePageScaleFactorInPropertyTrees( - inputs->property_trees, inputs->page_scale_layer, - inputs->page_scale_factor, inputs->device_scale_factor, - inputs->device_transform); - UpdateElasticOverscrollInPropertyTrees( - inputs->property_trees, - inputs->elastic_overscroll_application_layer, - inputs->elastic_overscroll); - // Similarly, the device viewport and device transform are shared - // by both trees. - inputs->property_trees->clip_tree.SetViewportClip( - gfx::RectF(gfx::SizeF(inputs->device_viewport_size))); - inputs->property_trees->transform_tree.SetDeviceTransform( - inputs->device_transform, inputs->root_layer->position()); - inputs->property_trees->transform_tree.SetDeviceTransformScaleFactor( - inputs->device_transform); - ComputeVisibleRectsUsingPropertyTrees( - inputs->root_layer, inputs->property_trees, - inputs->can_render_to_separate_surface, &visible_layer_list); - break; + "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees"); } + + BuildPropertyTreesAndComputeVisibleRects( + inputs->root_layer, inputs->page_scale_layer, + inputs->inner_viewport_scroll_layer, + inputs->outer_viewport_scroll_layer, + inputs->elastic_overscroll_application_layer, + inputs->elastic_overscroll, inputs->page_scale_factor, + inputs->device_scale_factor, gfx::Rect(inputs->device_viewport_size), + inputs->device_transform, inputs->can_render_to_separate_surface, + inputs->property_trees, &visible_layer_list); + + // Property trees are normally constructed on the main thread and + // passed to compositor thread. Source to parent updates on them are not + // allowed in the compositor thread. Some tests build them on the + // compositor thread, so we need to explicitly disallow source to parent + // updates when they are built on compositor thread. + inputs->property_trees->transform_tree + .set_source_to_parent_updates_allowed(false); + if (should_measure_property_tree_performance) { + TRACE_EVENT_END0( + TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), + "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees"); + } + + break; + } + case DONT_BUILD_PROPERTY_TREES: { + TRACE_EVENT0( + TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"), + "LayerTreeHostCommon::ComputeJustVisibleRectsWithPropertyTrees"); + // Since page scale and elastic overscroll are SyncedProperties, changes + // on the active tree immediately affect the pending tree, so instead of + // trying to update property trees whenever these values change, we + // update property trees before using them. + UpdatePageScaleFactorInPropertyTrees( + inputs->property_trees, inputs->page_scale_layer, + inputs->page_scale_factor, inputs->device_scale_factor, + inputs->device_transform); + UpdateElasticOverscrollInPropertyTrees( + inputs->property_trees, inputs->elastic_overscroll_application_layer, + inputs->elastic_overscroll); + // Similarly, the device viewport and device transform are shared + // by both trees. + inputs->property_trees->clip_tree.SetViewportClip( + gfx::RectF(gfx::SizeF(inputs->device_viewport_size))); + inputs->property_trees->transform_tree.SetDeviceTransform( + inputs->device_transform, inputs->root_layer->position()); + inputs->property_trees->transform_tree.SetDeviceTransformScaleFactor( + inputs->device_transform); + ComputeVisibleRectsUsingPropertyTrees( + inputs->root_layer, inputs->property_trees, + inputs->can_render_to_separate_surface, &visible_layer_list); + break; } } @@ -2755,27 +1556,18 @@ DCHECK(inputs->can_render_to_separate_surface == inputs->property_trees->non_root_surfaces_enabled); CalculateRenderTarget(inputs); - if (inputs->use_property_trees) { - for (LayerImpl* layer : visible_layer_list) { - ComputeLayerDrawPropertiesUsingPropertyTrees( - layer, inputs->property_trees, inputs->layers_always_allowed_lcd_text, - inputs->can_use_lcd_text, &layer->draw_properties()); - if (layer->mask_layer()) - ComputeMaskLayerDrawProperties(layer, layer->mask_layer()); - LayerImpl* replica_mask_layer = layer->replica_layer() - ? layer->replica_layer()->mask_layer() - : nullptr; - if (replica_mask_layer) - ComputeMaskLayerDrawProperties(layer, replica_mask_layer); - } - } else { - SubtreeGlobals globals; - DataForRecursion data_for_recursion; - ProcessCalcDrawPropsInputs(*inputs, &globals, &data_for_recursion); - CalculateDrawPropertiesInternal(inputs->root_layer, globals, - data_for_recursion, - &accumulated_surface_state); + for (LayerImpl* layer : visible_layer_list) { + ComputeLayerDrawPropertiesUsingPropertyTrees( + layer, inputs->property_trees, inputs->layers_always_allowed_lcd_text, + inputs->can_use_lcd_text, &layer->draw_properties()); + if (layer->mask_layer()) + ComputeMaskLayerDrawProperties(layer, layer->mask_layer()); + LayerImpl* replica_mask_layer = + layer->replica_layer() ? layer->replica_layer()->mask_layer() : nullptr; + if (replica_mask_layer) + ComputeMaskLayerDrawProperties(layer, replica_mask_layer); } + CalculateRenderSurfaceLayerList(inputs); if (should_measure_property_tree_performance) { @@ -2783,9 +1575,6 @@ "LayerTreeHostCommon::CalculateDrawProperties"); } - if (inputs->verify_property_trees) - VerifyPropertyTreeValues(inputs); - // A root layer render_surface should always exist after // CalculateDrawProperties. DCHECK(inputs->root_layer->render_surface()); @@ -2810,7 +1599,7 @@ void LayerTreeHostCommon::CalculateDrawProperties( CalcDrawPropsImplInputs* inputs) { - CalculateDrawPropertiesAndVerify(inputs, DONT_BUILD_PROPERTY_TREES); + CalculateDrawPropertiesInternal(inputs, DONT_BUILD_PROPERTY_TREES); if (CdpPerfTracingEnabled()) { LayerTreeImpl* layer_tree_impl = inputs->root_layer->layer_tree_impl(); @@ -2840,7 +1629,7 @@ void LayerTreeHostCommon::CalculateDrawProperties( CalcDrawPropsImplInputsForTesting* inputs) { - CalculateDrawPropertiesAndVerify(inputs, BUILD_PROPERTY_TREES_IF_NEEDED); + CalculateDrawPropertiesInternal(inputs, BUILD_PROPERTY_TREES_IF_NEEDED); } PropertyTrees* GetPropertyTrees(Layer* layer) {
diff --git a/cc/trees/layer_tree_host_common.h b/cc/trees/layer_tree_host_common.h index 4a09194..c2e7cdc 100644 --- a/cc/trees/layer_tree_host_common.h +++ b/cc/trees/layer_tree_host_common.h
@@ -81,8 +81,6 @@ bool layers_always_allowed_lcd_text, bool can_render_to_separate_surface, bool can_adjust_raster_scales, - bool verify_property_trees, - bool use_property_trees, LayerImplList* render_surface_layer_list, int current_render_surface_layer_list_id, PropertyTrees* property_trees); @@ -102,8 +100,6 @@ bool layers_always_allowed_lcd_text; bool can_render_to_separate_surface; bool can_adjust_raster_scales; - bool verify_property_trees; - bool use_property_trees; LayerImplList* render_surface_layer_list; int current_render_surface_layer_list_id; PropertyTrees* property_trees;
diff --git a/cc/trees/layer_tree_host_common_perftest.cc b/cc/trees/layer_tree_host_common_perftest.cc index 9989e84..b4717fa 100644 --- a/cc/trees/layer_tree_host_common_perftest.cc +++ b/cc/trees/layer_tree_host_common_perftest.cc
@@ -108,8 +108,6 @@ LayerTreeImpl* active_tree, LayerTreeHostImpl* host_impl) { LayerImplList update_list; - bool verify_property_trees = false; - bool use_property_trees = true; active_tree->IncrementRenderSurfaceListIdForTesting(); LayerTreeHostCommon::CalcDrawPropsImplInputs inputs( active_tree->root_layer(), active_tree->DrawViewportSize(), @@ -124,8 +122,7 @@ host_impl->settings().layers_always_allowed_lcd_text, can_render_to_separate_surface, host_impl->settings().layer_transforms_should_scale_layer_contents, - verify_property_trees, use_property_trees, &update_list, - active_tree->current_render_surface_list_id(), + &update_list, active_tree->current_render_surface_list_id(), active_tree->property_trees()); LayerTreeHostCommon::CalculateDrawProperties(&inputs); } @@ -229,12 +226,7 @@ TEST_F(BspTreePerfTest, LayerSorterRubik) { SetTestName("layer_sort_rubik"); ReadTestFile("layer_sort_rubik"); - // TODO(vollick): Remove verify_property_trees setting after - // crbug.com/444219 is fixed. - bool old_verify_property_trees = verify_property_trees(); - set_verify_property_trees(false); RunSortLayers(); - set_verify_property_trees(old_verify_property_trees); } TEST_F(BspTreePerfTest, BspTreeCubes) { @@ -248,12 +240,7 @@ SetTestName("bsp_tree_rubik"); SetNumberOfDuplicates(1); ReadTestFile("layer_sort_rubik"); - // TODO(vollick): Remove verify_property_trees setting after - // crbug.com/444219 is fixed. - bool old_verify_property_trees = verify_property_trees(); - set_verify_property_trees(false); RunSortLayers(); - set_verify_property_trees(old_verify_property_trees); } TEST_F(BspTreePerfTest, BspTreeCubes_2) {
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 756ad9ad..1463023 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -112,7 +112,6 @@ settings.renderer_settings.texture_id_allocation_chunk_size = 1; settings.gpu_rasterization_enabled = true; settings.use_compositor_animation_timelines = true; - settings.verify_property_trees = true; return settings; } @@ -2728,7 +2727,6 @@ settings.scrollbar_animator = animator; settings.scrollbar_fade_delay_ms = 20; settings.scrollbar_fade_duration_ms = 20; - settings.verify_property_trees = true; gfx::Size content_size(100, 100); CreateHostImpl(settings, CreateOutputSurface());
diff --git a/cc/trees/layer_tree_host_perftest.cc b/cc/trees/layer_tree_host_perftest.cc index fa546b2..278c7c51 100644 --- a/cc/trees/layer_tree_host_perftest.cc +++ b/cc/trees/layer_tree_host_perftest.cc
@@ -244,23 +244,13 @@ TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageSingleThread) { SetTestName("long_scrollable_page"); ReadTestFile("long_scrollable_page"); - // TODO(vollick): Remove verify_property_trees setting after - // crbug.com/444219 is fixed. - bool old_verify_property_trees = verify_property_trees(); - set_verify_property_trees(false); RunTest(CompositorMode::SINGLE_THREADED, false); - set_verify_property_trees(old_verify_property_trees); } TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageThreaded) { SetTestName("long_scrollable_page_threaded_impl_side"); ReadTestFile("long_scrollable_page"); - // TODO(vollick): Remove verify_property_trees setting after - // crbug.com/444219 is fixed. - bool old_verify_property_trees = verify_property_trees(); - set_verify_property_trees(false); RunTest(CompositorMode::THREADED, false); - set_verify_property_trees(old_verify_property_trees); } static void EmptyReleaseCallback(const gpu::SyncToken& sync_token,
diff --git a/cc/trees/layer_tree_host_unittest_serialization.cc b/cc/trees/layer_tree_host_unittest_serialization.cc index 50f96d7e..66d1242 100644 --- a/cc/trees/layer_tree_host_unittest_serialization.cc +++ b/cc/trees/layer_tree_host_unittest_serialization.cc
@@ -113,7 +113,7 @@ for (int i = 0; i < proto.root_layer().children_size(); ++i) { if (proto.root_layer().children(i).id() == layer_tree_host_src_->hud_layer_->id()) { - EXPECT_EQ(proto::HEADS_UP_DISPLAY_LAYER, + EXPECT_EQ(proto::LayerNode::HEADS_UP_DISPLAY_LAYER, proto.root_layer().children(i).type()); found_hud_layer_type = true; break;
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 50bc2725..a4d3e47 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc
@@ -753,7 +753,6 @@ settings().can_use_lcd_text, settings().layers_always_allowed_lcd_text, can_render_to_separate_surface, settings().layer_transforms_should_scale_layer_contents, - settings().verify_property_trees, settings().use_property_trees, &render_surface_layer_list_, render_surface_layer_list_id_, &property_trees_); LayerTreeHostCommon::CalculateDrawProperties(&inputs); @@ -1497,21 +1496,10 @@ gfx::ToRoundedPoint(hit_test_point_in_layer_space)); } -static const LayerImpl* GetNextClippingLayer(const LayerImpl* layer) { - if (layer->scroll_parent()) - return layer->scroll_parent(); - if (layer->clip_parent()) - return layer->clip_parent(); - return layer->parent(); -} - static const gfx::Transform SurfaceScreenSpaceTransform( const LayerImpl* layer, - const TransformTree& transform_tree, - const bool use_property_trees) { + const TransformTree& transform_tree) { DCHECK(layer->render_surface()); - if (!use_property_trees) - return layer->render_surface()->screen_space_transform(); return layer->IsDrawnRenderSurfaceLayerListMember() ? layer->render_surface()->screen_space_transform() : SurfaceScreenSpaceTransformFromPropertyTrees( @@ -1555,8 +1543,7 @@ if (clip_node_owner->render_surface() && !PointHitsRect( screen_space_point, - SurfaceScreenSpaceTransform(clip_node_owner, transform_tree, - true /*use_property_trees*/), + SurfaceScreenSpaceTransform(clip_node_owner, transform_tree), clip_node_owner->render_surface()->content_rect(), NULL)) { return true; } @@ -1568,42 +1555,18 @@ const gfx::PointF& screen_space_point, const LayerImpl* layer, const TransformTree& transform_tree, - const ClipTree& clip_tree, - const bool use_property_trees) { + const ClipTree& clip_tree) { // Walk up the layer tree and hit-test any render_surfaces and any layer // clip rects that are active. - if (use_property_trees) { - return PointIsClippedByAncestorClipNode(screen_space_point, layer, - clip_tree, transform_tree); - } - - for (; layer; layer = GetNextClippingLayer(layer)) { - if (layer->render_surface() && - !PointHitsRect(screen_space_point, - SurfaceScreenSpaceTransform(layer, transform_tree, - use_property_trees), - layer->render_surface()->content_rect(), NULL)) { - return true; - } - - if (LayerClipsSubtree(layer) && - !PointHitsRect(screen_space_point, layer->ScreenSpaceTransform(), - gfx::Rect(layer->bounds()), NULL)) { - return true; - } - } - - // If we have finished walking all ancestors without having already exited, - // then the point is not clipped by any ancestors. - return false; + return PointIsClippedByAncestorClipNode(screen_space_point, layer, clip_tree, + transform_tree); } static bool PointHitsLayer(const LayerImpl* layer, const gfx::PointF& screen_space_point, float* distance_to_intersection, const TransformTree& transform_tree, - const ClipTree& clip_tree, - const bool use_property_trees) { + const ClipTree& clip_tree) { gfx::Rect content_rect(layer->bounds()); if (!PointHitsRect(screen_space_point, layer->ScreenSpaceTransform(), content_rect, distance_to_intersection)) @@ -1613,8 +1576,7 @@ // up the parents to ensure that the layer was not clipped in such a way // that the hit point actually should not hit the layer. if (PointIsClippedBySurfaceOrClipRect(screen_space_point, layer, - transform_tree, clip_tree, - use_property_trees)) + transform_tree, clip_tree)) return false; // Skip the HUD layer. @@ -1641,14 +1603,13 @@ const Functor& func, const TransformTree& transform_tree, const ClipTree& clip_tree, - const bool use_property_trees, FindClosestMatchingLayerDataForRecursion* data_for_recursion) { size_t children_size = layer->children().size(); for (size_t i = 0; i < children_size; ++i) { size_t index = children_size - 1 - i; FindClosestMatchingLayer(screen_space_point, layer->children()[index].get(), func, transform_tree, clip_tree, - use_property_trees, data_for_recursion); + data_for_recursion); } if (!func(layer)) @@ -1658,10 +1619,10 @@ bool hit = false; if (layer->Is3dSorted()) hit = PointHitsLayer(layer, screen_space_point, &distance_to_intersection, - transform_tree, clip_tree, use_property_trees); + transform_tree, clip_tree); else hit = PointHitsLayer(layer, screen_space_point, nullptr, transform_tree, - clip_tree, use_property_trees); + clip_tree); if (!hit) return; @@ -1706,12 +1667,10 @@ LayerImpl* LayerTreeImpl::FindFirstScrollingLayerThatIsHitByPoint( const gfx::PointF& screen_space_point) { FindClosestMatchingLayerDataForRecursion data_for_recursion; - bool use_property_trees = - settings().use_property_trees || settings().verify_property_trees; - FindClosestMatchingLayer( - screen_space_point, root_layer(), FindScrollingLayerFunctor(), - property_trees_.transform_tree, property_trees_.clip_tree, - use_property_trees, &data_for_recursion); + FindClosestMatchingLayer(screen_space_point, root_layer(), + FindScrollingLayerFunctor(), + property_trees_.transform_tree, + property_trees_.clip_tree, &data_for_recursion); return data_for_recursion.closest_match; } @@ -1733,22 +1692,18 @@ bool update_lcd_text = false; if (!UpdateDrawProperties(update_lcd_text)) return NULL; - bool use_property_trees = - settings().use_property_trees || settings().verify_property_trees; FindClosestMatchingLayerDataForRecursion data_for_recursion; FindClosestMatchingLayer(screen_space_point, root_layer(), HitTestVisibleScrollableOrTouchableFunctor(), property_trees_.transform_tree, - property_trees_.clip_tree, use_property_trees, - &data_for_recursion); + property_trees_.clip_tree, &data_for_recursion); return data_for_recursion.closest_match; } static bool LayerHasTouchEventHandlersAt(const gfx::PointF& screen_space_point, LayerImpl* layer_impl, const TransformTree& transform_tree, - const ClipTree& clip_tree, - const bool use_property_trees) { + const ClipTree& clip_tree) { if (layer_impl->touch_event_handler_region().IsEmpty()) return false; @@ -1761,8 +1716,7 @@ // was not clipped in such a way that the hit point actually should not hit // the layer. if (PointIsClippedBySurfaceOrClipRect(screen_space_point, layer_impl, - transform_tree, clip_tree, - use_property_trees)) + transform_tree, clip_tree)) return false; return true; @@ -1771,13 +1725,11 @@ struct FindTouchEventLayerFunctor { bool operator()(LayerImpl* layer) const { return LayerHasTouchEventHandlersAt(screen_space_point, layer, - transform_tree, clip_tree, - use_property_trees); + transform_tree, clip_tree); } const gfx::PointF screen_space_point; const TransformTree& transform_tree; const ClipTree& clip_tree; - const bool use_property_trees; }; LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPointInTouchHandlerRegion( @@ -1787,15 +1739,13 @@ bool update_lcd_text = false; if (!UpdateDrawProperties(update_lcd_text)) return NULL; - bool use_property_trees = - settings().use_property_trees || settings().verify_property_trees; - FindTouchEventLayerFunctor func = { - screen_space_point, property_trees_.transform_tree, - property_trees_.clip_tree, use_property_trees}; + FindTouchEventLayerFunctor func = {screen_space_point, + property_trees_.transform_tree, + property_trees_.clip_tree}; FindClosestMatchingLayerDataForRecursion data_for_recursion; - FindClosestMatchingLayer( - screen_space_point, root_layer(), func, property_trees_.transform_tree, - property_trees_.clip_tree, use_property_trees, &data_for_recursion); + FindClosestMatchingLayer(screen_space_point, root_layer(), func, + property_trees_.transform_tree, + property_trees_.clip_tree, &data_for_recursion); return data_for_recursion.closest_match; } @@ -1808,8 +1758,7 @@ LayerImpl* layer, float device_scale_factor, const TransformTree& transform_tree, - const ClipTree& clip_tree, - const bool use_property_trees) { + const ClipTree& clip_tree) { ViewportSelectionBound viewport_bound; viewport_bound.type = layer_bound.type; @@ -1852,9 +1801,8 @@ MathUtil::MapPoint(screen_space_transform, visibility_point, &clipped); float intersect_distance = 0.f; - viewport_bound.visible = - PointHitsLayer(layer, visibility_point, &intersect_distance, - transform_tree, clip_tree, use_property_trees); + viewport_bound.visible = PointHitsLayer( + layer, visibility_point, &intersect_distance, transform_tree, clip_tree); return viewport_bound; } @@ -1862,13 +1810,11 @@ void LayerTreeImpl::GetViewportSelection(ViewportSelection* selection) { DCHECK(selection); - bool use_property_trees = - settings().use_property_trees || settings().verify_property_trees; selection->start = ComputeViewportSelectionBound( selection_.start, selection_.start.layer_id ? LayerById(selection_.start.layer_id) : NULL, device_scale_factor(), property_trees_.transform_tree, - property_trees_.clip_tree, use_property_trees); + property_trees_.clip_tree); selection->is_editable = selection_.is_editable; selection->is_empty_text_form_control = selection_.is_empty_text_form_control; if (selection->start.type == SELECTION_BOUND_CENTER || @@ -1879,7 +1825,7 @@ selection_.end, selection_.end.layer_id ? LayerById(selection_.end.layer_id) : NULL, device_scale_factor(), property_trees_.transform_tree, - property_trees_.clip_tree, use_property_trees); + property_trees_.clip_tree); } }
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc index bd8a810..9f4b473 100644 --- a/cc/trees/layer_tree_impl_unittest.cc +++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -26,7 +26,6 @@ LayerTreeImplTest() : output_surface_(FakeOutputSurface::Create3d()) { LayerTreeSettings settings; settings.layer_transforms_should_scale_layer_contents = true; - settings.verify_property_trees = true; host_impl_.reset(new FakeLayerTreeHostImpl(settings, &task_runner_provider_, &shared_bitmap_manager_, &task_graph_runner_)); @@ -110,7 +109,6 @@ TestTaskGraphRunner task_graph_runner; FakeImplTaskRunnerProvider task_runner_provider; LayerTreeSettings settings; - settings.verify_property_trees = true; scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d(); scoped_ptr<FakeLayerTreeHostImpl> host_impl; host_impl.reset(new FakeLayerTreeHostImpl(settings, &task_runner_provider,
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc index da6c031..73a9eb3b 100644 --- a/cc/trees/layer_tree_settings.cc +++ b/cc/trees/layer_tree_settings.cc
@@ -98,8 +98,6 @@ ignore_root_layer_flings(false), scheduled_raster_task_limit(32), use_occlusion_for_tile_prioritization(false), - verify_property_trees(false), - use_property_trees(true), image_decode_tasks_enabled(false), use_compositor_animation_timelines(true), wait_for_beginframe_interval(true), @@ -166,8 +164,6 @@ scheduled_raster_task_limit == other.scheduled_raster_task_limit && use_occlusion_for_tile_prioritization == other.use_occlusion_for_tile_prioritization && - verify_property_trees == other.verify_property_trees && - use_property_trees == other.use_property_trees && image_decode_tasks_enabled == other.image_decode_tasks_enabled && use_compositor_animation_timelines == other.use_compositor_animation_timelines && @@ -231,8 +227,6 @@ proto->set_scheduled_raster_task_limit(scheduled_raster_task_limit); proto->set_use_occlusion_for_tile_prioritization( use_occlusion_for_tile_prioritization); - proto->set_verify_property_trees(verify_property_trees); - proto->set_use_property_trees(use_property_trees); proto->set_image_decode_tasks_enabled(image_decode_tasks_enabled); proto->set_use_compositor_animation_timelines( use_compositor_animation_timelines); @@ -300,8 +294,6 @@ scheduled_raster_task_limit = proto.scheduled_raster_task_limit(); use_occlusion_for_tile_prioritization = proto.use_occlusion_for_tile_prioritization(); - verify_property_trees = proto.verify_property_trees(); - use_property_trees = proto.use_property_trees(); image_decode_tasks_enabled = proto.image_decode_tasks_enabled(); use_compositor_animation_timelines = proto.use_compositor_animation_timelines();
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h index ff00f5d..dfad4a50 100644 --- a/cc/trees/layer_tree_settings.h +++ b/cc/trees/layer_tree_settings.h
@@ -83,8 +83,6 @@ bool ignore_root_layer_flings; size_t scheduled_raster_task_limit; bool use_occlusion_for_tile_prioritization; - bool verify_property_trees; - bool use_property_trees; bool image_decode_tasks_enabled; bool use_compositor_animation_timelines; bool wait_for_beginframe_interval;
diff --git a/cc/trees/layer_tree_settings_unittest.cc b/cc/trees/layer_tree_settings_unittest.cc index 48e6122..1466b9f 100644 --- a/cc/trees/layer_tree_settings_unittest.cc +++ b/cc/trees/layer_tree_settings_unittest.cc
@@ -88,7 +88,6 @@ settings.scheduled_raster_task_limit * 3 + 1; settings.use_occlusion_for_tile_prioritization = !settings.use_occlusion_for_tile_prioritization; - settings.use_property_trees = !settings.use_property_trees; settings.use_compositor_animation_timelines = !settings.use_compositor_animation_timelines; settings.wait_for_beginframe_interval = @@ -149,7 +148,6 @@ settings.ignore_root_layer_flings = true; settings.scheduled_raster_task_limit = 41; settings.use_occlusion_for_tile_prioritization = true; - settings.use_property_trees = true; settings.use_compositor_animation_timelines = true; settings.wait_for_beginframe_interval = true; settings.max_staging_buffer_usage_in_bytes = 70;
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc index f9a1eab4..aefd84770 100644 --- a/cc/trees/proxy_impl.cc +++ b/cc/trees/proxy_impl.cc
@@ -74,8 +74,10 @@ layer_tree_host->settings().ToSchedulerSettings()); scoped_ptr<CompositorTimingHistory> compositor_timing_history( - new CompositorTimingHistory(CompositorTimingHistory::RENDERER_UMA, - rendering_stats_instrumentation_)); + new CompositorTimingHistory( + scheduler_settings.using_synchronous_renderer_compositor, + CompositorTimingHistory::RENDERER_UMA, + rendering_stats_instrumentation_)); scheduler_ = Scheduler::Create(this, scheduler_settings, layer_tree_host_id_, task_runner_provider_->ImplThreadTaskRunner(),
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 5898c7a..571c78d 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc
@@ -67,6 +67,7 @@ scoped_ptr<CompositorTimingHistory> compositor_timing_history( new CompositorTimingHistory( + scheduler_settings.using_synchronous_renderer_compositor, CompositorTimingHistory::BROWSER_UMA, layer_tree_host_->rendering_stats_instrumentation()));
diff --git a/chrome/VERSION b/chrome/VERSION index 2abcee7..19449368 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=50 MINOR=0 -BUILD=2655 +BUILD=2656 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 6cf137ec..2e88395b 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -137,6 +137,7 @@ "//components/web_restrictions:web_restrictions_java", "//content/public/android:content_java", "//media/base/android:media_java", + "//media/capture/video/android:capture_java", "//media/midi:midi_java", "//net/android:net_java", "//printing:printing_java", @@ -234,6 +235,7 @@ "//chrome/browser/android/shortcut_info.h", "//chrome/browser/android/tab_android.h", "//chrome/browser/browsing_data/browsing_data_counter_utils.h", + "//chrome/browser/browsing_data/browsing_data_remover.h", "//chrome/browser/profiles/profile_metrics.h", "//chrome/browser/ui/android/infobars/infobar_android.h", ]
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 6bab1bcc..2cce2a7 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -419,7 +419,7 @@ </receiver> <!-- Providers for chrome data. --> - <provider android:name="org.chromium.chrome.browser.ChromeBrowserProvider" + <provider android:name="org.chromium.chrome.browser.provider.ChromeBrowserProvider" android:authorities="{{ manifest_package }}.ChromeBrowserProvider;{{ manifest_package }}.browser;{{ manifest_package }}" android:exported="true"> <path-permission android:path="/bookmarks/search_suggest_query"
diff --git a/chrome/android/java/res/layout/preference_spinner.xml b/chrome/android/java/res/layout/preference_spinner.xml new file mode 100644 index 0000000..048ae53 --- /dev/null +++ b/chrome/android/java/res/layout/preference_spinner.xml
@@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<!-- Layout used by SpinnerPreference. --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:chrome="http://schemas.android.com/apk/res-auto" + style="@style/PreferenceLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:id="@+id/title" + android:layout_height="wrap_content" + android:layout_width="match_parent" /> + + <Spinner + android:id="@+id/spinner" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingStart="0dp" + android:paddingTop="6dp" + android:paddingBottom="4dp" /> + + <View style="@style/PreferenceSpinnerUnderlineView" /> + +</LinearLayout>
diff --git a/chrome/android/java/res/menu/custom_tabs_menu.xml b/chrome/android/java/res/menu/custom_tabs_menu.xml index b30b01f..f3b0b82 100644 --- a/chrome/android/java/res/menu/custom_tabs_menu.xml +++ b/chrome/android/java/res/menu/custom_tabs_menu.xml
@@ -11,6 +11,9 @@ <item android:id="@+id/forward_menu_id" android:title="@string/accessibility_menu_forward" android:icon="@drawable/btn_forward"/> + <item android:id="@+id/bookmark_this_page_id" + android:title="@string/accessibility_menu_bookmark" + android:icon="@drawable/btn_star"/> <item android:id="@+id/info_menu_id" android:title="@string/accessibility_menu_info" android:icon="@drawable/btn_info" />
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml index d4525802..5f2d61c4 100644 --- a/chrome/android/java/res/values/colors.xml +++ b/chrome/android/java/res/values/colors.xml
@@ -171,6 +171,7 @@ <!-- Physical Web diagnostics colors --> <color name="physical_web_diags_success_color">#00933b</color> <color name="physical_web_diags_failure_color">#f90101</color> + <color name="physical_web_diags_indeterminate_color">#f2b50f</color> <!-- Bottom bar colors --> <color name="bottom_bar_shadow_color">#1d000000</color>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index df56050c..470dbbe 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -318,4 +318,7 @@ <dimen name="account_chooser_dialog_item_margin">20dp</dimen> <dimen name="account_chooser_dialog_title_main_text_size">18sp</dimen> <dimen name="account_chooser_dialog_title_descriptive_text_size">16sp</dimen> + + <!-- Clear browsing data preferences dimensions --> + <dimen name="clear_browsing_data_checkbox_height">56dp</dimen> </resources>
diff --git a/chrome/android/java/res/xml/clear_browsing_data_preferences.xml b/chrome/android/java/res/xml/clear_browsing_data_preferences.xml index 450415a..6ea731f 100644 --- a/chrome/android/java/res/xml/clear_browsing_data_preferences.xml +++ b/chrome/android/java/res/xml/clear_browsing_data_preferences.xml
@@ -7,32 +7,37 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:chrome="http://schemas.android.com/apk/res-auto"> - <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference + <org.chromium.chrome.browser.preferences.SpinnerPreference + android:key="time_period_spinner" + android:persistent="false" + android:title="@string/clear_browsing_data_period_title" /> + + <org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference android:key="clear_history_checkbox" android:persistent="false" android:title="@string/clear_history_title" /> - <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference - android:key="clear_cache_checkbox" - android:persistent="false" - android:title="@string/clear_cache_title" /> - - <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference + <org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference android:key="clear_cookies_checkbox" android:persistent="false" android:title="@string/clear_cookies_and_site_data_title" /> - <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference + <org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference + android:key="clear_cache_checkbox" + android:persistent="false" + android:title="@string/clear_cache_title" /> + + <org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference android:key="clear_passwords_checkbox" android:persistent="false" android:title="@string/clear_passwords_title" /> - <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference + <org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference android:key="clear_form_data_checkbox" android:persistent="false" android:title="@string/clear_form_data_title" /> - <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference + <org.chromium.chrome.browser.preferences.ClearBrowsingDataCheckBoxPreference android:key="clear_bookmarks_checkbox" android:persistent="false" android:title="@string/clear_bookmarks_title" /> @@ -41,6 +46,8 @@ android:key="clear_button" android:title="@string/clear_data_delete" /> + <Preference android:layout="@layout/divider_preference" /> + <org.chromium.chrome.browser.preferences.TextMessagePreference android:key="summary" />
diff --git a/chrome/android/java/res/xml/contextual_search_preferences.xml b/chrome/android/java/res/xml/contextual_search_preferences.xml index d12541b..2478b1c 100644 --- a/chrome/android/java/res/xml/contextual_search_preferences.xml +++ b/chrome/android/java/res/xml/contextual_search_preferences.xml
@@ -3,12 +3,15 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> -<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:chrome="http://schemas.android.com/apk/res-auto"> <org.chromium.chrome.browser.preferences.ChromeSwitchPreference android:key="contextual_search_switch" android:summaryOn="@string/text_on" - android:summaryOff="@string/text_off" /> + android:summaryOff="@string/text_off" + chrome:drawDivider="true" /> <org.chromium.chrome.browser.preferences.TextMessagePreference android:title="@string/contextual_search_description" />
diff --git a/chrome/android/java/res/xml/do_not_track_preferences.xml b/chrome/android/java/res/xml/do_not_track_preferences.xml index 621d2f8..a12a993 100644 --- a/chrome/android/java/res/xml/do_not_track_preferences.xml +++ b/chrome/android/java/res/xml/do_not_track_preferences.xml
@@ -10,7 +10,8 @@ <org.chromium.chrome.browser.preferences.ChromeSwitchPreference android:key="do_not_track_switch" android:summaryOn="@string/text_on" - android:summaryOff="@string/text_off" /> + android:summaryOff="@string/text_off" + chrome:drawDivider="true" /> <org.chromium.chrome.browser.preferences.TextMessagePreference android:title="@string/do_not_track_description" />
diff --git a/chrome/android/java/res/xml/physical_web_preferences.xml b/chrome/android/java/res/xml/physical_web_preferences.xml index 8ad4878..dcdbe11 100644 --- a/chrome/android/java/res/xml/physical_web_preferences.xml +++ b/chrome/android/java/res/xml/physical_web_preferences.xml
@@ -10,7 +10,8 @@ <org.chromium.chrome.browser.preferences.ChromeSwitchPreference android:key="physical_web_switch" android:summaryOn="@string/text_on" - android:summaryOff="@string/text_off" /> + android:summaryOff="@string/text_off" + chrome:drawDivider="true" /> <org.chromium.chrome.browser.preferences.TextMessagePreference android:title="@string/physical_web_pref_description" />
diff --git a/chrome/android/java/res/xml/usage_and_crash_reports_preferences.xml b/chrome/android/java/res/xml/usage_and_crash_reports_preferences.xml index 8491344..0660da2 100644 --- a/chrome/android/java/res/xml/usage_and_crash_reports_preferences.xml +++ b/chrome/android/java/res/xml/usage_and_crash_reports_preferences.xml
@@ -10,7 +10,8 @@ <org.chromium.chrome.browser.preferences.ChromeSwitchPreference android:key="usage_and_crash_reports_switch" android:summaryOn="@string/text_on" - android:summaryOff="@string/text_off"/> + android:summaryOff="@string/text_off" + chrome:drawDivider="true"/> <org.chromium.chrome.browser.preferences.TextMessagePreference android:title="@string/usage_and_crash_reports_description"/>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java index c457178..a0e07a24 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java
@@ -67,7 +67,6 @@ // The type of link that is shown within the dialog. private enum LinkType { EXPLAIN_BLUETOOTH, - EXPLAIN_PARING, ADAPTER_OFF, ADAPTER_OFF_HELP, REQUEST_LOCATION_PERMISSION, @@ -100,19 +99,21 @@ OmniboxUrlEmphasizer.emphasizeUrl( origin, mContext.getResources(), profile, mSecurityLevel, false, true, true); // Construct a full string and replace the origin text with emphasized version. - String message = mContext.getString(R.string.bluetooth_dialog_title, mOrigin); - SpannableString title = SpanApplier.applySpans( - message, new SpanInfo("<link>", "</link>", - new NoUnderlineClickableSpan(LinkType.EXPLAIN_PARING, mContext))); + SpannableString title = + new SpannableString(mContext.getString(R.string.bluetooth_dialog_title, mOrigin)); int start = title.toString().indexOf(mOrigin); TextUtils.copySpansFrom(origin, 0, origin.length(), Object.class, title, start); - message = mContext.getString(R.string.bluetooth_not_found); + String message = mContext.getString(R.string.bluetooth_not_found); SpannableString noneFound = SpanApplier.applySpans( message, new SpanInfo("<link>", "</link>", new NoUnderlineClickableSpan(LinkType.RESTART_SEARCH, mContext))); - String searching = mContext.getString(R.string.bluetooth_searching); + SpannableString searching = SpanApplier.applySpans( + mContext.getString(R.string.bluetooth_searching), + new SpanInfo("<link>", "</link>", + new NoUnderlineClickableSpan(LinkType.EXPLAIN_BLUETOOTH, mContext))); + String positiveButton = mContext.getString(R.string.bluetooth_confirm_button); SpannableString statusActive = SpanApplier.applySpans( @@ -120,15 +121,21 @@ new SpanInfo("<link>", "</link>", new NoUnderlineClickableSpan(LinkType.EXPLAIN_BLUETOOTH, mContext))); - SpannableString statusIdle = SpanApplier.applySpans( - mContext.getString(R.string.bluetooth_not_seeing_it_idle), + SpannableString statusIdleNoneFound = SpanApplier.applySpans( + mContext.getString(R.string.bluetooth_not_seeing_it_idle_none_found), + new SpanInfo("<link>", "</link>", + new NoUnderlineClickableSpan(LinkType.EXPLAIN_BLUETOOTH, mContext))); + + SpannableString statusIdleSomeFound = SpanApplier.applySpans( + mContext.getString(R.string.bluetooth_not_seeing_it_idle_some_found), new SpanInfo("<link1>", "</link1>", new NoUnderlineClickableSpan(LinkType.EXPLAIN_BLUETOOTH, mContext)), new SpanInfo("<link2>", "</link2>", new NoUnderlineClickableSpan(LinkType.RESTART_SEARCH, mContext))); - ItemChooserDialog.ItemChooserLabels labels = new ItemChooserDialog.ItemChooserLabels( - title, searching, noneFound, statusActive, statusIdle, positiveButton); + ItemChooserDialog.ItemChooserLabels labels = + new ItemChooserDialog.ItemChooserLabels(title, searching, noneFound, statusActive, + statusIdleNoneFound, statusIdleSomeFound, positiveButton); mItemChooserDialog = new ItemChooserDialog(mContext, this, labels); } @@ -212,13 +219,12 @@ switch (mLinkType) { case EXPLAIN_BLUETOOTH: { + // No need to close the dialog here because + // ShowBluetoothOverviewLink will close it. + // TODO(ortuno): The BluetoothChooserDialog should dismiss + // itself when a new tab is opened or the current tab navigates. + // https://crbug.com/588127 nativeShowBluetoothOverviewLink(mNativeBluetoothChooserDialogPtr); - closeDialog(); - break; - } - case EXPLAIN_PARING: { - nativeShowBluetoothPairingLink(mNativeBluetoothChooserDialogPtr); - closeDialog(); break; } case ADAPTER_OFF: { @@ -339,7 +345,6 @@ private native void nativeRestartSearch(long nativeBluetoothChooserAndroid); // Help links. private native void nativeShowBluetoothOverviewLink(long nativeBluetoothChooserAndroid); - private native void nativeShowBluetoothPairingLink(long nativeBluetoothChooserAndroid); private native void nativeShowBluetoothAdapterOffLink(long nativeBluetoothChooserAndroid); private native void nativeShowNeedLocationPermissionLink(long nativeBluetoothChooserAndroid); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java index f6c1edb..ed00ff5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -185,7 +185,7 @@ private ContextReporter mContextReporter; protected GSAServiceClient mGSAServiceClient; - private boolean mPartnerBrowserRefreshNeeded = false; + private boolean mPartnerBrowserRefreshNeeded; protected IntentHandler mIntentHandler; @@ -240,6 +240,12 @@ @Override public void preInflationStartup() { super.preInflationStartup(); + + // Force a partner customizations refresh if it has yet to be initialized. This can happen + // if Chrome is killed and you refocus a previous activity from Android recents, which does + // not go through ChromeLauncherActivity that would have normally triggered this. + mPartnerBrowserRefreshNeeded = !PartnerBrowserCustomizations.isInitialized(); + ApplicationInitialization.enableFullscreenFlags( getResources(), this, getControlContainerHeightResource()); // TODO(twellington): Remove this work around when the underlying bug is fixed. @@ -369,9 +375,6 @@ // The app menu badge should be removed the first time the menu is opened. if (mToolbarManager.getToolbar().isShowingAppMenuUpdateBadge()) { mToolbarManager.getToolbar().removeAppMenuUpdateBadge(true); - - // TODO(twellington): Try invalidating the toolbar instead of requesting - // render. See crbug.com/585975. mCompositorViewHolder.requestRender(); } } @@ -872,6 +875,9 @@ protected void onAccessibilityModeChanged(boolean enabled) { InfoBarContainer.setIsAllowedToAutoHide(!enabled); if (mToolbarManager != null) mToolbarManager.onAccessibilityStatusChanged(enabled); + if (mContextualSearchManager != null) { + mContextualSearchManager.onAccessibilityModeChanged(enabled); + } } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java deleted file mode 100644 index d955cab4..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java +++ /dev/null
@@ -1,1438 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser; - -import android.annotation.SuppressLint; -import android.app.SearchManager; -import android.content.ContentProvider; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.UriMatcher; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.net.Uri; -import android.os.Binder; -import android.os.Build; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.UserHandle; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.text.TextUtils; -import android.util.Log; -import android.util.LongSparseArray; - -import org.chromium.base.ThreadUtils; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.CalledByNativeUnchecked; -import org.chromium.base.annotations.SuppressFBWarnings; -import org.chromium.base.library_loader.LibraryProcessType; -import org.chromium.base.library_loader.ProcessInitException; -import org.chromium.chrome.browser.bookmark.BookmarkColumns; -import org.chromium.chrome.browser.bookmark.SearchColumns; -import org.chromium.chrome.browser.database.SQLiteCursor; -import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; -import org.chromium.chrome.browser.init.ChromeBrowserInitializer; -import org.chromium.content.app.ContentApplication; -import org.chromium.content.browser.BrowserStartupController; -import org.chromium.sync.AndroidSyncSettings; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Vector; - -/** - * This class provides access to user data stored in Chrome, such as bookmarks, most visited pages, - * etc. It is used to support android.provider.Browser. - */ -public class ChromeBrowserProvider extends ContentProvider { - private static final String TAG = "ChromeBrowserProvider"; - - /** - * A projection of {@link #SEARCHES_URI} that contains {@link SearchColumns#ID}, - * {@link SearchColumns#SEARCH}, and {@link SearchColumns#DATE}. - */ - @VisibleForTesting - @SuppressFBWarnings("MS_PKGPROTECT") - public static final String[] SEARCHES_PROJECTION = new String[] { - // if you change column order you must also change indices below - SearchColumns.ID, // 0 - SearchColumns.SEARCH, // 1 - SearchColumns.DATE, // 2 - }; - - /* these indices dependent on SEARCHES_PROJECTION */ - @VisibleForTesting - public static final int SEARCHES_PROJECTION_SEARCH_INDEX = 1; - @VisibleForTesting - public static final int SEARCHES_PROJECTION_DATE_INDEX = 2; - - // The permission required for using the bookmark folders API. Android build system does - // not generate Manifest.java for java libraries, hence use the permission name string. When - // making changes to this permission, also update the permission in AndroidManifest.xml. - private static final String PERMISSION_READ_WRITE_BOOKMARKS = "READ_WRITE_BOOKMARK_FOLDERS"; - - // Defines the API methods that the Client can call by name. - static final String CLIENT_API_BOOKMARK_NODE_EXISTS = "BOOKMARK_NODE_EXISTS"; - static final String CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE = "CREATE_BOOKMARKS_FOLDER_ONCE"; - static final String CLIENT_API_GET_EDITABLE_BOOKMARK_FOLDER_HIERARCHY = - "GET_EDITABLE_BOOKMARK_FOLDER_HIERARCHY"; - static final String CLIENT_API_GET_BOOKMARK_NODE = "GET_BOOKMARK_NODE"; - static final String CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER = "GET_DEFAULT_BOOKMARK_FOLDER"; - static final String CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID = - "GET_MOBILE_BOOKMARKS_FOLDER_ID"; - static final String CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH = - "IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH"; - static final String CLIENT_API_DELETE_ALL_USER_BOOKMARKS = "DELETE_ALL_USER_BOOKMARKS"; - static final String CLIENT_API_RESULT_KEY = "result"; - - - // Defines Chrome's API authority, so it can be run and tested - // independently. - private static final String API_AUTHORITY_SUFFIX = ".browser"; - - private static final String BROWSER_CONTRACT_API_AUTHORITY = - "com.google.android.apps.chrome.browser-contract"; - - // These values are taken from android.provider.BrowserContract.java since - // that class is hidden from the SDK. - private static final String BROWSER_CONTRACT_AUTHORITY = "com.android.browser"; - private static final String BROWSER_CONTRACT_HISTORY_CONTENT_TYPE = - "vnd.android.cursor.dir/browser-history"; - private static final String BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE = - "vnd.android.cursor.item/browser-history"; - private static final String BROWSER_CONTRACT_BOOKMARK_CONTENT_TYPE = - "vnd.android.cursor.dir/bookmark"; - private static final String BROWSER_CONTRACT_BOOKMARK_CONTENT_ITEM_TYPE = - "vnd.android.cursor.item/bookmark"; - private static final String BROWSER_CONTRACT_SEARCH_CONTENT_TYPE = - "vnd.android.cursor.dir/searches"; - private static final String BROWSER_CONTRACT_SEARCH_CONTENT_ITEM_TYPE = - "vnd.android.cursor.item/searches"; - - // This Authority is for internal interface. It's concatenated with - // Context.getPackageName() so that we can install different channels - // SxS and have different authorities. - private static final String AUTHORITY_SUFFIX = ".ChromeBrowserProvider"; - private static final String BOOKMARKS_PATH = "bookmarks"; - private static final String SEARCHES_PATH = "searches"; - private static final String HISTORY_PATH = "history"; - private static final String COMBINED_PATH = "combined"; - private static final String BOOKMARK_FOLDER_PATH = "hierarchy"; - - public static final Uri BROWSER_CONTRACTS_BOOKMAKRS_API_URI = buildContentUri( - BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH); - - public static final Uri BROWSER_CONTRACTS_SEARCHES_API_URI = buildContentUri( - BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH); - - public static final Uri BROWSER_CONTRACTS_HISTORY_API_URI = buildContentUri( - BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH); - - public static final Uri BROWSER_CONTRACTS_COMBINED_API_URI = buildContentUri( - BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH); - - /** The parameter used to specify a bookmark parent ID in ContentValues. */ - public static final String BOOKMARK_PARENT_ID_PARAM = "parentId"; - - /** The parameter used to specify whether this is a bookmark folder. */ - public static final String BOOKMARK_IS_FOLDER_PARAM = "isFolder"; - - /** - * Invalid ID value for the Android ContentProvider API calls. - * The value 0 is intentional: if the ID represents a bookmark node then it's the root node - * and not accessible. Otherwise it represents a SQLite row id, so 0 is also invalid. - */ - public static final long INVALID_CONTENT_PROVIDER_ID = 0; - - // ID used to indicate an invalid id for bookmark nodes. - // Client API queries should use ChromeBrowserProviderClient.INVALID_BOOKMARK_ID. - static final long INVALID_BOOKMARK_ID = -1; - - private static final String LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY = "last_bookmark_folder_id"; - - private static final int URI_MATCH_BOOKMARKS = 0; - private static final int URI_MATCH_BOOKMARKS_ID = 1; - private static final int URL_MATCH_API_BOOKMARK = 2; - private static final int URL_MATCH_API_BOOKMARK_ID = 3; - private static final int URL_MATCH_API_SEARCHES = 4; - private static final int URL_MATCH_API_SEARCHES_ID = 5; - private static final int URL_MATCH_API_HISTORY_CONTENT = 6; - private static final int URL_MATCH_API_HISTORY_CONTENT_ID = 7; - private static final int URL_MATCH_API_BOOKMARK_CONTENT = 8; - private static final int URL_MATCH_API_BOOKMARK_CONTENT_ID = 9; - private static final int URL_MATCH_BOOKMARK_SUGGESTIONS_ID = 10; - private static final int URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID = 11; - - // TODO : Using Android.provider.Browser.HISTORY_PROJECTION once THUMBNAIL, - // TOUCH_ICON, and USER_ENTERED fields are supported. - private static final String[] BOOKMARK_DEFAULT_PROJECTION = new String[] { - BookmarkColumns.ID, BookmarkColumns.URL, BookmarkColumns.VISITS, - BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE, - BookmarkColumns.FAVICON, BookmarkColumns.CREATED - }; - - private static final String[] SUGGEST_PROJECTION = new String[] { - BookmarkColumns.ID, - BookmarkColumns.TITLE, - BookmarkColumns.URL, - BookmarkColumns.DATE, - BookmarkColumns.BOOKMARK - }; - - private final Object mInitializeUriMatcherLock = new Object(); - private final Object mLoadNativeLock = new Object(); - private UriMatcher mUriMatcher; - private long mLastModifiedBookmarkFolderId = INVALID_BOOKMARK_ID; - private long mNativeChromeBrowserProvider; - private BookmarkNode mMobileBookmarksFolder; - - private void ensureUriMatcherInitialized() { - synchronized (mInitializeUriMatcherLock) { - if (mUriMatcher != null) return; - - mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - // The internal URIs - String authority = getContext().getPackageName() + AUTHORITY_SUFFIX; - mUriMatcher.addURI(authority, BOOKMARKS_PATH, URI_MATCH_BOOKMARKS); - mUriMatcher.addURI(authority, BOOKMARKS_PATH + "/#", URI_MATCH_BOOKMARKS_ID); - // The internal authority for public APIs - String apiAuthority = getContext().getPackageName() + API_AUTHORITY_SUFFIX; - mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK); - mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH + "/#", URL_MATCH_API_BOOKMARK_ID); - mUriMatcher.addURI(apiAuthority, SEARCHES_PATH, URL_MATCH_API_SEARCHES); - mUriMatcher.addURI(apiAuthority, SEARCHES_PATH + "/#", URL_MATCH_API_SEARCHES_ID); - mUriMatcher.addURI(apiAuthority, HISTORY_PATH, URL_MATCH_API_HISTORY_CONTENT); - mUriMatcher.addURI(apiAuthority, HISTORY_PATH + "/#", URL_MATCH_API_HISTORY_CONTENT_ID); - mUriMatcher.addURI(apiAuthority, COMBINED_PATH, URL_MATCH_API_BOOKMARK); - mUriMatcher.addURI(apiAuthority, COMBINED_PATH + "/#", URL_MATCH_API_BOOKMARK_ID); - // The internal authority for BrowserContracts - mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH, - URL_MATCH_API_HISTORY_CONTENT); - mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH + "/#", - URL_MATCH_API_HISTORY_CONTENT_ID); - mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH, - URL_MATCH_API_BOOKMARK); - mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH + "/#", - URL_MATCH_API_BOOKMARK_ID); - mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH, - URL_MATCH_API_SEARCHES); - mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH + "/#", - URL_MATCH_API_SEARCHES_ID); - mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH, - URL_MATCH_API_BOOKMARK_CONTENT); - mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH + "/#", - URL_MATCH_API_BOOKMARK_CONTENT_ID); - // Added the Android Framework URIs, so the provider can easily switched - // by adding 'browser' and 'com.android.browser' in manifest. - // The Android's BrowserContract - mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH, - URL_MATCH_API_HISTORY_CONTENT); - mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH + "/#", - URL_MATCH_API_HISTORY_CONTENT_ID); - mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined", URL_MATCH_API_BOOKMARK); - mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined/#", URL_MATCH_API_BOOKMARK_ID); - mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH, URL_MATCH_API_SEARCHES); - mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH + "/#", - URL_MATCH_API_SEARCHES_ID); - mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH, - URL_MATCH_API_BOOKMARK_CONTENT); - mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH + "/#", - URL_MATCH_API_BOOKMARK_CONTENT_ID); - // For supporting android.provider.browser.BookmarkColumns and - // SearchColumns - mUriMatcher.addURI("browser", BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK); - mUriMatcher.addURI("browser", BOOKMARKS_PATH + "/#", URL_MATCH_API_BOOKMARK_ID); - mUriMatcher.addURI("browser", SEARCHES_PATH, URL_MATCH_API_SEARCHES); - mUriMatcher.addURI("browser", SEARCHES_PATH + "/#", URL_MATCH_API_SEARCHES_ID); - - mUriMatcher.addURI(apiAuthority, - BOOKMARKS_PATH + "/" + SearchManager.SUGGEST_URI_PATH_QUERY, - URL_MATCH_BOOKMARK_SUGGESTIONS_ID); - mUriMatcher.addURI(apiAuthority, - SearchManager.SUGGEST_URI_PATH_QUERY, - URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID); - } - } - - @Override - public boolean onCreate() { - ContentApplication.initCommandLine(getContext()); - - BrowserStartupController.get(getContext(), LibraryProcessType.PROCESS_BROWSER) - .addStartupCompletedObserver( - new BrowserStartupController.StartupCallback() { - @Override - public void onSuccess(boolean alreadyStarted) { - ensureNativeSideInitialized(); - } - - @Override - public void onFailure() { - } - }); - - // Pre-load shared preferences object, this happens on a separate thread - PreferenceManager.getDefaultSharedPreferences(getContext()); - return true; - } - - /** - * Lazily fetches the last modified bookmark folder id. - */ - private long getLastModifiedBookmarkFolderId() { - if (mLastModifiedBookmarkFolderId == INVALID_BOOKMARK_ID) { - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(getContext()); - mLastModifiedBookmarkFolderId = sharedPreferences.getLong( - LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, INVALID_BOOKMARK_ID); - } - return mLastModifiedBookmarkFolderId; - } - - private String buildSuggestWhere(String selection, int argc) { - StringBuilder sb = new StringBuilder(selection); - for (int i = 0; i < argc - 1; i++) { - sb.append(" OR "); - sb.append(selection); - } - return sb.toString(); - } - - private String getReadWritePermissionNameForBookmarkFolders() { - return getContext().getApplicationContext().getPackageName() + ".permission." - + PERMISSION_READ_WRITE_BOOKMARKS; - } - - private Cursor getBookmarkHistorySuggestions(String selection, String[] selectionArgs, - String sortOrder, boolean excludeHistory) { - boolean matchTitles = false; - Vector<String> args = new Vector<String>(); - String like = selectionArgs[0] + "%"; - if (selectionArgs[0].startsWith("http") || selectionArgs[0].startsWith("file")) { - args.add(like); - } else { - // Match against common URL prefixes. - args.add("http://" + like); - args.add("https://" + like); - args.add("http://www." + like); - args.add("https://www." + like); - args.add("file://" + like); - matchTitles = true; - } - - StringBuilder urlWhere = new StringBuilder("("); - urlWhere.append(buildSuggestWhere(selection, args.size())); - if (matchTitles) { - args.add(like); - urlWhere.append(" OR title LIKE ?"); - } - urlWhere.append(")"); - - if (excludeHistory) { - urlWhere.append(" AND bookmark=?"); - args.add("1"); - } - - selectionArgs = args.toArray(selectionArgs); - Cursor cursor = queryBookmarkFromAPI(SUGGEST_PROJECTION, urlWhere.toString(), - selectionArgs, sortOrder); - return new ChromeBrowserProviderSuggestionsCursor(cursor); - } - - /** - * @see android.content.ContentUris#parseId(Uri) - * @return The id from a content URI or -1 if the URI has no id or is malformed. - */ - private static long getContentUriId(Uri uri) { - try { - return ContentUris.parseId(uri); - } catch (UnsupportedOperationException e) { - return -1; - } catch (NumberFormatException e) { - return -1; - } - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - if (!canHandleContentProviderApiCall()) return null; - - // Starting with M, other apps are no longer allowed to access bookmarks. But returning null - // might break old apps, so return an empty Cursor instead. - if (!hasReadAccess()) return new MatrixCursor(BOOKMARK_DEFAULT_PROJECTION, 0); - - // Check for invalid id values if provided. - long bookmarkId = getContentUriId(uri); - if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return null; - - int match = mUriMatcher.match(uri); - Cursor cursor = null; - switch (match) { - case URL_MATCH_BOOKMARK_SUGGESTIONS_ID: - cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, true); - break; - case URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID: - cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, false); - break; - case URL_MATCH_API_BOOKMARK: - cursor = queryBookmarkFromAPI(projection, selection, selectionArgs, sortOrder); - break; - case URL_MATCH_API_BOOKMARK_ID: - cursor = queryBookmarkFromAPI(projection, buildWhereClause(bookmarkId, selection), - selectionArgs, sortOrder); - break; - case URL_MATCH_API_SEARCHES: - cursor = querySearchTermFromAPI(projection, selection, selectionArgs, sortOrder); - break; - case URL_MATCH_API_SEARCHES_ID: - cursor = querySearchTermFromAPI(projection, buildWhereClause(bookmarkId, selection), - selectionArgs, sortOrder); - break; - case URL_MATCH_API_HISTORY_CONTENT: - cursor = queryBookmarkFromAPI(projection, buildHistoryWhereClause(selection), - selectionArgs, sortOrder); - break; - case URL_MATCH_API_HISTORY_CONTENT_ID: - cursor = queryBookmarkFromAPI(projection, - buildHistoryWhereClause(bookmarkId, selection), selectionArgs, sortOrder); - break; - case URL_MATCH_API_BOOKMARK_CONTENT: - cursor = queryBookmarkFromAPI(projection, buildBookmarkWhereClause(selection), - selectionArgs, sortOrder); - break; - case URL_MATCH_API_BOOKMARK_CONTENT_ID: - cursor = queryBookmarkFromAPI(projection, - buildBookmarkWhereClause(bookmarkId, selection), selectionArgs, sortOrder); - break; - default: - throw new IllegalArgumentException(TAG + ": query - unknown URL uri = " + uri); - } - if (cursor == null) { - cursor = new MatrixCursor(new String[] { }); - } - cursor.setNotificationUri(getContext().getContentResolver(), uri); - return cursor; - } - - @Override - @SuppressFBWarnings("SF_SWITCH_FALLTHROUGH") - public Uri insert(Uri uri, ContentValues values) { - if (!canHandleContentProviderApiCall() || !hasWriteAccess()) return null; - - int match = mUriMatcher.match(uri); - Uri res = null; - long id; - switch (match) { - case URI_MATCH_BOOKMARKS: - id = addBookmark(values); - if (id == INVALID_BOOKMARK_ID) return null; - break; - case URL_MATCH_API_BOOKMARK_CONTENT: - values.put(BookmarkColumns.BOOKMARK, 1); - //$FALL-THROUGH$ - case URL_MATCH_API_BOOKMARK: - case URL_MATCH_API_HISTORY_CONTENT: - id = addBookmarkFromAPI(values); - if (id == INVALID_CONTENT_PROVIDER_ID) return null; - break; - case URL_MATCH_API_SEARCHES: - id = addSearchTermFromAPI(values); - if (id == INVALID_CONTENT_PROVIDER_ID) return null; - break; - default: - throw new IllegalArgumentException(TAG + ": insert - unknown URL " + uri); - } - - res = ContentUris.withAppendedId(uri, id); - notifyChange(res); - return res; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - if (!canHandleContentProviderApiCall() || !hasWriteAccess()) return 0; - - // Check for invalid id values if provided. - long bookmarkId = getContentUriId(uri); - if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0; - - int match = mUriMatcher.match(uri); - int result; - switch (match) { - case URI_MATCH_BOOKMARKS_ID : - result = nativeRemoveBookmark(mNativeChromeBrowserProvider, bookmarkId); - break; - case URL_MATCH_API_BOOKMARK_ID: - result = removeBookmarkFromAPI( - buildWhereClause(bookmarkId, selection), selectionArgs); - break; - case URL_MATCH_API_BOOKMARK: - result = removeBookmarkFromAPI(selection, selectionArgs); - break; - case URL_MATCH_API_SEARCHES_ID: - result = removeSearchFromAPI(buildWhereClause(bookmarkId, selection), - selectionArgs); - break; - case URL_MATCH_API_SEARCHES: - result = removeSearchFromAPI(selection, selectionArgs); - break; - case URL_MATCH_API_HISTORY_CONTENT: - result = removeHistoryFromAPI(selection, selectionArgs); - break; - case URL_MATCH_API_HISTORY_CONTENT_ID: - result = removeHistoryFromAPI(buildWhereClause(bookmarkId, selection), - selectionArgs); - break; - case URL_MATCH_API_BOOKMARK_CONTENT: - result = removeBookmarkFromAPI(buildBookmarkWhereClause(selection), selectionArgs); - break; - case URL_MATCH_API_BOOKMARK_CONTENT_ID: - result = removeBookmarkFromAPI(buildBookmarkWhereClause(bookmarkId, selection), - selectionArgs); - break; - default: - throw new IllegalArgumentException(TAG + ": delete - unknown URL " + uri); - } - if (result != 0) notifyChange(uri); - return result; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - if (!canHandleContentProviderApiCall() || !hasWriteAccess()) return 0; - - // Check for invalid id values if provided. - long bookmarkId = getContentUriId(uri); - if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0; - - int match = mUriMatcher.match(uri); - int result; - switch (match) { - case URI_MATCH_BOOKMARKS_ID: - String url = null; - if (values.containsKey(BookmarkColumns.URL)) { - url = values.getAsString(BookmarkColumns.URL); - } - String title = values.getAsString(BookmarkColumns.TITLE); - long parentId = INVALID_BOOKMARK_ID; - if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) { - parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM); - } - result = nativeUpdateBookmark(mNativeChromeBrowserProvider, bookmarkId, url, title, - parentId); - updateLastModifiedBookmarkFolder(parentId); - break; - case URL_MATCH_API_BOOKMARK_ID: - result = updateBookmarkFromAPI(values, buildWhereClause(bookmarkId, selection), - selectionArgs); - break; - case URL_MATCH_API_BOOKMARK: - result = updateBookmarkFromAPI(values, selection, selectionArgs); - break; - case URL_MATCH_API_SEARCHES_ID: - result = updateSearchTermFromAPI(values, buildWhereClause(bookmarkId, selection), - selectionArgs); - break; - case URL_MATCH_API_SEARCHES: - result = updateSearchTermFromAPI(values, selection, selectionArgs); - break; - case URL_MATCH_API_HISTORY_CONTENT: - result = updateBookmarkFromAPI(values, buildHistoryWhereClause(selection), - selectionArgs); - break; - case URL_MATCH_API_HISTORY_CONTENT_ID: - result = updateBookmarkFromAPI(values, - buildHistoryWhereClause(bookmarkId, selection), selectionArgs); - break; - case URL_MATCH_API_BOOKMARK_CONTENT: - result = updateBookmarkFromAPI(values, buildBookmarkWhereClause(selection), - selectionArgs); - break; - case URL_MATCH_API_BOOKMARK_CONTENT_ID: - result = updateBookmarkFromAPI(values, - buildBookmarkWhereClause(bookmarkId, selection), selectionArgs); - break; - default: - throw new IllegalArgumentException(TAG + ": update - unknown URL " + uri); - } - if (result != 0) notifyChange(uri); - return result; - } - - @Override - public String getType(Uri uri) { - ensureUriMatcherInitialized(); - int match = mUriMatcher.match(uri); - switch (match) { - case URI_MATCH_BOOKMARKS: - case URL_MATCH_API_BOOKMARK: - return BROWSER_CONTRACT_BOOKMARK_CONTENT_TYPE; - case URI_MATCH_BOOKMARKS_ID: - case URL_MATCH_API_BOOKMARK_ID: - return BROWSER_CONTRACT_BOOKMARK_CONTENT_ITEM_TYPE; - case URL_MATCH_API_SEARCHES: - return BROWSER_CONTRACT_SEARCH_CONTENT_TYPE; - case URL_MATCH_API_SEARCHES_ID: - return BROWSER_CONTRACT_SEARCH_CONTENT_ITEM_TYPE; - case URL_MATCH_API_HISTORY_CONTENT: - return BROWSER_CONTRACT_HISTORY_CONTENT_TYPE; - case URL_MATCH_API_HISTORY_CONTENT_ID: - return BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE; - default: - throw new IllegalArgumentException(TAG + ": getType - unknown URL " + uri); - } - } - - private long addBookmark(ContentValues values) { - String url = values.getAsString(BookmarkColumns.URL); - String title = values.getAsString(BookmarkColumns.TITLE); - boolean isFolder = false; - if (values.containsKey(BOOKMARK_IS_FOLDER_PARAM)) { - isFolder = values.getAsBoolean(BOOKMARK_IS_FOLDER_PARAM); - } - long parentId = INVALID_BOOKMARK_ID; - if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) { - parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM); - } - long id = nativeAddBookmark(mNativeChromeBrowserProvider, url, title, isFolder, parentId); - if (id == INVALID_BOOKMARK_ID) return id; - - if (isFolder) { - updateLastModifiedBookmarkFolder(id); - } else { - updateLastModifiedBookmarkFolder(parentId); - } - return id; - } - - private void updateLastModifiedBookmarkFolder(long id) { - if (getLastModifiedBookmarkFolderId() == id) return; - - mLastModifiedBookmarkFolderId = id; - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(getContext()); - sharedPreferences.edit() - .putLong(LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, mLastModifiedBookmarkFolderId) - .apply(); - } - - public static String getApiAuthority(Context context) { - return context.getPackageName() + API_AUTHORITY_SUFFIX; - } - - public static String getInternalAuthority(Context context) { - return context.getPackageName() + AUTHORITY_SUFFIX; - } - - public static Uri getBookmarksUri(Context context) { - return buildContentUri(getInternalAuthority(context), BOOKMARKS_PATH); - } - - public static Uri getBookmarkFolderUri(Context context) { - return buildContentUri(getInternalAuthority(context), BOOKMARK_FOLDER_PATH); - } - - public static Uri getBookmarksApiUri(Context context) { - return buildContentUri(getApiAuthority(context), BOOKMARKS_PATH); - } - - @VisibleForTesting - public static Uri getSearchesApiUri(Context context) { - return buildContentUri(getApiAuthority(context), SEARCHES_PATH); - } - - private boolean bookmarkNodeExists(long nodeId) { - if (nodeId < 0) return false; - return nativeBookmarkNodeExists(mNativeChromeBrowserProvider, nodeId); - } - - private long createBookmarksFolderOnce(String title, long parentId) { - return nativeCreateBookmarksFolderOnce(mNativeChromeBrowserProvider, title, parentId); - } - - private BookmarkNode getEditableBookmarkFolderHierarchy() { - return nativeGetEditableBookmarkFolders(mNativeChromeBrowserProvider); - } - - private BookmarkNode getBookmarkNode(long nodeId, boolean getParent, boolean getChildren, - boolean getFavicons, boolean getThumbnails) { - // Don't allow going up the hierarchy if sync is disabled and the requested node - // is the Mobile Bookmarks folder. - if (getParent && nodeId == getMobileBookmarksFolderId() - && !AndroidSyncSettings.isSyncEnabled(getContext())) { - getParent = false; - } - - BookmarkNode node = nativeGetBookmarkNode(mNativeChromeBrowserProvider, nodeId, getParent, - getChildren); - if (!getFavicons && !getThumbnails) return node; - - // Favicons and thumbnails need to be populated separately as they are provided - // asynchronously by Chromium services other than the bookmark model. - if (node.parent() != null) populateNodeImages(node.parent(), getFavicons, getThumbnails); - for (BookmarkNode child : node.children()) { - populateNodeImages(child, getFavicons, getThumbnails); - } - - return node; - } - - private BookmarkNode getDefaultBookmarkFolder() { - // Try to access the bookmark folder last modified by us. If it doesn't exist anymore - // then use the synced node (Mobile Bookmarks). - BookmarkNode lastModified = getBookmarkNode(getLastModifiedBookmarkFolderId(), false, false, - false, false); - if (lastModified == null || lastModified.isUrl()) { - lastModified = getMobileBookmarksFolder(); - mLastModifiedBookmarkFolderId = lastModified != null ? lastModified.id() : - INVALID_BOOKMARK_ID; - } - return lastModified; - } - - private void populateNodeImages(BookmarkNode node, boolean favicon, boolean thumbnail) { - if (node == null || node.type() != Type.URL) return; - - if (favicon) { - node.setFavicon(nativeGetFaviconOrTouchIcon(mNativeChromeBrowserProvider, node.url())); - } - - if (thumbnail) { - node.setThumbnail(nativeGetThumbnail(mNativeChromeBrowserProvider, node.url())); - } - } - - private BookmarkNode getMobileBookmarksFolder() { - if (mMobileBookmarksFolder == null) { - mMobileBookmarksFolder = nativeGetMobileBookmarksFolder(mNativeChromeBrowserProvider); - } - return mMobileBookmarksFolder; - } - - private long getMobileBookmarksFolderId() { - BookmarkNode mobileBookmarks = getMobileBookmarksFolder(); - return mobileBookmarks != null ? mobileBookmarks.id() : INVALID_BOOKMARK_ID; - } - - private boolean isBookmarkInMobileBookmarksBranch(long nodeId) { - if (nodeId <= 0) return false; - return nativeIsBookmarkInMobileBookmarksBranch(mNativeChromeBrowserProvider, nodeId); - } - - static String argKey(int i) { - return "arg" + i; - } - - @Override - public Bundle call(String method, String arg, Bundle extras) { - // TODO(shashishekhar): Refactor this code into a separate class. - // Caller must have the READ_WRITE_BOOKMARK_FOLDERS permission. - getContext().enforcePermission(getReadWritePermissionNameForBookmarkFolders(), - Binder.getCallingPid(), Binder.getCallingUid(), TAG); - if (!canHandleContentProviderApiCall()) return null; - if (method == null || extras == null) return null; - - Bundle result = new Bundle(); - if (CLIENT_API_BOOKMARK_NODE_EXISTS.equals(method)) { - result.putBoolean(CLIENT_API_RESULT_KEY, - bookmarkNodeExists(extras.getLong(argKey(0)))); - } else if (CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE.equals(method)) { - result.putLong(CLIENT_API_RESULT_KEY, - createBookmarksFolderOnce(extras.getString(argKey(0)), - extras.getLong(argKey(1)))); - } else if (CLIENT_API_GET_EDITABLE_BOOKMARK_FOLDER_HIERARCHY.equals(method)) { - result.putParcelable(CLIENT_API_RESULT_KEY, getEditableBookmarkFolderHierarchy()); - } else if (CLIENT_API_GET_BOOKMARK_NODE.equals(method)) { - result.putParcelable(CLIENT_API_RESULT_KEY, - getBookmarkNode(extras.getLong(argKey(0)), - extras.getBoolean(argKey(1)), - extras.getBoolean(argKey(2)), - extras.getBoolean(argKey(3)), - extras.getBoolean(argKey(4)))); - } else if (CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER.equals(method)) { - result.putParcelable(CLIENT_API_RESULT_KEY, getDefaultBookmarkFolder()); - } else if (method.equals(CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID)) { - result.putLong(CLIENT_API_RESULT_KEY, getMobileBookmarksFolderId()); - } else if (CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH.equals(method)) { - result.putBoolean(CLIENT_API_RESULT_KEY, - isBookmarkInMobileBookmarksBranch(extras.getLong(argKey(0)))); - } else if (CLIENT_API_DELETE_ALL_USER_BOOKMARKS.equals(method)) { - android.util.Log.i(TAG, "before nativeRemoveAllUserBookmarks"); - nativeRemoveAllUserBookmarks(mNativeChromeBrowserProvider); - android.util.Log.i(TAG, "after nativeRemoveAllUserBookmarks"); - } else { - Log.w(TAG, "Received invalid method " + method); - return null; - } - - return result; - } - - /** - * Checks whether Chrome is sufficiently initialized to handle a call to the - * ChromeBrowserProvider. - */ - private boolean canHandleContentProviderApiCall() { - if (isInUiThread()) return false; - ensureUriMatcherInitialized(); - if (mNativeChromeBrowserProvider != 0) return true; - synchronized (mLoadNativeLock) { - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - @SuppressFBWarnings("DM_EXIT") - public void run() { - if (mNativeChromeBrowserProvider != 0) return; - try { - ChromeBrowserInitializer.getInstance(getContext()) - .handleSynchronousStartup(); - } catch (ProcessInitException e) { - // Chrome browser runs in the background, so exit silently; but do exit, - // since otherwise the next attempt to use Chrome will find a broken JNI. - System.exit(-1); - } - ensureNativeSideInitialized(); - } - }); - } - return true; - } - - /** - * @return Whether the caller has read access to history and bookmarks information. - */ - private boolean hasReadAccess() { - return hasPermission("com.android.browser.permission.READ_HISTORY_BOOKMARKS"); - } - - /** - * @return Whether the caller has write access to history and bookmarks information. - */ - private boolean hasWriteAccess() { - return hasPermission("com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"); - } - - /** - * The type of a BookmarkNode. - */ - public enum Type { - URL, - FOLDER, - BOOKMARK_BAR, - OTHER_NODE, - MOBILE - } - - /** - * Simple Data Object representing the chrome bookmark node. - */ - public static class BookmarkNode implements Parcelable { - private final long mId; - private final String mName; - private final String mUrl; - private final Type mType; - private final BookmarkNode mParent; - private final List<BookmarkNode> mChildren = new ArrayList<BookmarkNode>(); - - // Favicon and thumbnail optionally set in a 2-step procedure. - private byte[] mFavicon; - private byte[] mThumbnail; - - /** Used to pass structured data back from the native code. */ - @VisibleForTesting - public BookmarkNode(long id, Type type, String name, String url, BookmarkNode parent) { - mId = id; - mName = name; - mUrl = url; - mType = type; - mParent = parent; - } - - /** - * @return The id of this bookmark entry. - */ - public long id() { - return mId; - } - - /** - * @return The name of this bookmark entry. - */ - public String name() { - return mName; - } - - /** - * @return The URL of this bookmark entry. - */ - public String url() { - return mUrl; - } - - /** - * @return The type of this bookmark entry. - */ - public Type type() { - return mType; - } - - /** - * @return The bookmark favicon, if any. - */ - @SuppressFBWarnings("EI_EXPOSE_REP") - public byte[] favicon() { - return mFavicon; - } - - /** - * @return The bookmark thumbnail, if any. - */ - @SuppressFBWarnings("EI_EXPOSE_REP") - public byte[] thumbnail() { - return mThumbnail; - } - - /** - * @return The parent folder of this bookmark entry. - */ - public BookmarkNode parent() { - return mParent; - } - - /** - * Adds a child to this node. - * - * <p> - * Used solely by the native code. - */ - @VisibleForTesting - @CalledByNativeUnchecked("BookmarkNode") - public void addChild(BookmarkNode child) { - mChildren.add(child); - } - - /** - * @return The child bookmark nodes of this node. - */ - public List<BookmarkNode> children() { - return mChildren; - } - - /** - * @return Whether this node represents a bookmarked URL or not. - */ - public boolean isUrl() { - return mUrl != null; - } - - /** - * @return true if the two individual nodes contain the same information. - * The existence of parent and children nodes is checked, but their contents are not. - */ - @VisibleForTesting - public boolean equalContents(BookmarkNode node) { - return node != null - && mId == node.mId - && !(mName == null ^ node.mName == null) - && (mName == null || mName.equals(node.mName)) - && !(mUrl == null ^ node.mUrl == null) - && (mUrl == null || mUrl.equals(node.mUrl)) - && mType == node.mType - && byteArrayEqual(mFavicon, node.mFavicon) - && byteArrayEqual(mThumbnail, node.mThumbnail) - && !(mParent == null ^ node.mParent == null) - && children().size() == node.children().size(); - } - - private static boolean byteArrayEqual(byte[] byte1, byte[] byte2) { - if (byte1 == null && byte2 != null) return byte2.length == 0; - if (byte2 == null && byte1 != null) return byte1.length == 0; - return Arrays.equals(byte1, byte2); - } - - @CalledByNative("BookmarkNode") - private static BookmarkNode create( - long id, int type, String name, String url, BookmarkNode parent) { - return new BookmarkNode(id, Type.values()[type], name, url, parent); - } - - @VisibleForTesting - @SuppressFBWarnings("EI_EXPOSE_REP2") - public void setFavicon(byte[] favicon) { - mFavicon = favicon; - } - - @VisibleForTesting - @SuppressFBWarnings("EI_EXPOSE_REP2") - public void setThumbnail(byte[] thumbnail) { - mThumbnail = thumbnail; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - // Write the current node id. - dest.writeLong(mId); - - // Serialize the full hierarchy from the root. - getHierarchyRoot().writeNodeContentsRecursive(dest); - } - - @VisibleForTesting - public BookmarkNode getHierarchyRoot() { - BookmarkNode root = this; - while (root.parent() != null) { - root = root.parent(); - } - return root; - } - - private void writeNodeContentsRecursive(Parcel dest) { - writeNodeContents(dest); - dest.writeInt(mChildren.size()); - for (BookmarkNode child : mChildren) { - child.writeNodeContentsRecursive(dest); - } - } - - private void writeNodeContents(Parcel dest) { - dest.writeLong(mId); - dest.writeString(mName); - dest.writeString(mUrl); - dest.writeInt(mType.ordinal()); - dest.writeByteArray(mFavicon); - dest.writeByteArray(mThumbnail); - dest.writeLong(mParent != null ? mParent.mId : INVALID_BOOKMARK_ID); - } - - public static final Creator<BookmarkNode> CREATOR = new Creator<BookmarkNode>() { - private LongSparseArray<BookmarkNode> mNodeMap; - - @Override - public BookmarkNode createFromParcel(Parcel source) { - mNodeMap = new LongSparseArray<>(); - long currentNodeId = source.readLong(); - readNodeContentsRecursive(source); - BookmarkNode node = getNode(currentNodeId); - mNodeMap.clear(); - return node; - } - - @Override - public BookmarkNode[] newArray(int size) { - return new BookmarkNode[size]; - } - - private BookmarkNode getNode(long id) { - if (id == INVALID_BOOKMARK_ID) return null; - Long nodeId = Long.valueOf(id); - if (mNodeMap.indexOfKey(nodeId) < 0) { - Log.e(TAG, "Invalid BookmarkNode hierarchy. Unknown id " + id); - return null; - } - return mNodeMap.get(nodeId); - } - - private BookmarkNode readNodeContents(Parcel source) { - long id = source.readLong(); - String name = source.readString(); - String url = source.readString(); - int type = source.readInt(); - byte[] favicon = source.createByteArray(); - byte[] thumbnail = source.createByteArray(); - long parentId = source.readLong(); - if (type < 0 || type >= Type.values().length) { - Log.w(TAG, "Invalid node type ordinal value."); - return null; - } - - BookmarkNode node = new BookmarkNode(id, Type.values()[type], name, url, - getNode(parentId)); - node.setFavicon(favicon); - node.setThumbnail(thumbnail); - return node; - } - - private BookmarkNode readNodeContentsRecursive(Parcel source) { - BookmarkNode node = readNodeContents(source); - if (node == null) return null; - - Long nodeId = Long.valueOf(node.id()); - if (mNodeMap.indexOfKey(nodeId) >= 0) { - Log.e(TAG, "Invalid BookmarkNode hierarchy. Duplicate id " + node.id()); - return null; - } - mNodeMap.put(nodeId, node); - - int numChildren = source.readInt(); - for (int i = 0; i < numChildren; ++i) { - node.addChild(readNodeContentsRecursive(source)); - } - - return node; - } - }; - } - - private long addBookmarkFromAPI(ContentValues values) { - BookmarkRow row = BookmarkRow.fromContentValues(values); - if (row.mUrl == null) { - throw new IllegalArgumentException("Must have a bookmark URL"); - } - return nativeAddBookmarkFromAPI(mNativeChromeBrowserProvider, - row.mUrl, row.mCreated, row.mIsBookmark, row.mDate, row.mFavicon, - row.mTitle, row.mVisits, row.mParentId); - } - - private Cursor queryBookmarkFromAPI(String[] projectionIn, String selection, - String[] selectionArgs, String sortOrder) { - String[] projection = null; - if (projectionIn == null || projectionIn.length == 0) { - projection = BOOKMARK_DEFAULT_PROJECTION; - } else { - projection = projectionIn; - } - - return nativeQueryBookmarkFromAPI(mNativeChromeBrowserProvider, projection, selection, - selectionArgs, sortOrder); - } - - private int updateBookmarkFromAPI(ContentValues values, String selection, - String[] selectionArgs) { - BookmarkRow row = BookmarkRow.fromContentValues(values); - return nativeUpdateBookmarkFromAPI(mNativeChromeBrowserProvider, - row.mUrl, row.mCreated, row.mIsBookmark, row.mDate, - row.mFavicon, row.mTitle, row.mVisits, row.mParentId, selection, selectionArgs); - } - - private int removeBookmarkFromAPI(String selection, String[] selectionArgs) { - return nativeRemoveBookmarkFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs); - } - - private int removeHistoryFromAPI(String selection, String[] selectionArgs) { - return nativeRemoveHistoryFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs); - } - - @CalledByNative - private void onBookmarkChanged() { - notifyChange(buildAPIContentUri(getContext(), BOOKMARKS_PATH)); - } - - @CalledByNative - private void onHistoryChanged() { - notifyChange(buildAPIContentUri(getContext(), HISTORY_PATH)); - } - - @CalledByNative - private void onSearchTermChanged() { - notifyChange(buildAPIContentUri(getContext(), SEARCHES_PATH)); - } - - private long addSearchTermFromAPI(ContentValues values) { - SearchRow row = SearchRow.fromContentValues(values); - if (row.mTerm == null) { - throw new IllegalArgumentException("Must have a search term"); - } - return nativeAddSearchTermFromAPI(mNativeChromeBrowserProvider, row.mTerm, row.mDate); - } - - private int updateSearchTermFromAPI(ContentValues values, String selection, - String[] selectionArgs) { - SearchRow row = SearchRow.fromContentValues(values); - return nativeUpdateSearchTermFromAPI(mNativeChromeBrowserProvider, - row.mTerm, row.mDate, selection, selectionArgs); - } - - private Cursor querySearchTermFromAPI(String[] projectionIn, String selection, - String[] selectionArgs, String sortOrder) { - String[] projection = null; - if (projectionIn == null || projectionIn.length == 0) { - projection = SEARCHES_PROJECTION; - } else { - projection = projectionIn; - } - return nativeQuerySearchTermFromAPI(mNativeChromeBrowserProvider, projection, selection, - selectionArgs, sortOrder); - } - - private int removeSearchFromAPI(String selection, String[] selectionArgs) { - return nativeRemoveSearchTermFromAPI(mNativeChromeBrowserProvider, - selection, selectionArgs); - } - - private static boolean isInUiThread() { - if (!ThreadUtils.runningOnUiThread()) return false; - - if (!"REL".equals(Build.VERSION.CODENAME)) { - throw new IllegalStateException("Shouldn't run in the UI thread"); - } - - Log.w(TAG, "ChromeBrowserProvider methods cannot be called from the UI thread."); - return true; - } - - private static Uri buildContentUri(String authority, String path) { - return Uri.parse("content://" + authority + "/" + path); - } - - private static Uri buildAPIContentUri(Context context, String path) { - return buildContentUri(context.getPackageName() + API_AUTHORITY_SUFFIX, path); - } - - private static String buildWhereClause(long id, String selection) { - StringBuilder sb = new StringBuilder(); - sb.append(BaseColumns._ID); - sb.append(" = "); - sb.append(id); - if (!TextUtils.isEmpty(selection)) { - sb.append(" AND ("); - sb.append(selection); - sb.append(")"); - } - return sb.toString(); - } - - private static String buildHistoryWhereClause(long id, String selection) { - return buildWhereClause(id, buildBookmarkWhereClause(selection, false)); - } - - private static String buildHistoryWhereClause(String selection) { - return buildBookmarkWhereClause(selection, false); - } - - /** - * @return a SQL where class which is inserted the bookmark condition. - */ - private static String buildBookmarkWhereClause(String selection, boolean isBookmark) { - StringBuilder sb = new StringBuilder(); - sb.append(BookmarkColumns.BOOKMARK); - sb.append(isBookmark ? " = 1 " : " = 0"); - if (!TextUtils.isEmpty(selection)) { - sb.append(" AND ("); - sb.append(selection); - sb.append(")"); - } - return sb.toString(); - } - - private static String buildBookmarkWhereClause(long id, String selection) { - return buildWhereClause(id, buildBookmarkWhereClause(selection, true)); - } - - private static String buildBookmarkWhereClause(String selection) { - return buildBookmarkWhereClause(selection, true); - } - - // Wrap the value of BookmarkColumn. - private static class BookmarkRow { - Boolean mIsBookmark; - Long mCreated; - String mUrl; - Long mDate; - byte[] mFavicon; - String mTitle; - Integer mVisits; - long mParentId; - - static BookmarkRow fromContentValues(ContentValues values) { - BookmarkRow row = new BookmarkRow(); - if (values.containsKey(BookmarkColumns.URL)) { - row.mUrl = values.getAsString(BookmarkColumns.URL); - } - if (values.containsKey(BookmarkColumns.BOOKMARK)) { - row.mIsBookmark = values.getAsInteger(BookmarkColumns.BOOKMARK) != 0; - } - if (values.containsKey(BookmarkColumns.CREATED)) { - row.mCreated = values.getAsLong(BookmarkColumns.CREATED); - } - if (values.containsKey(BookmarkColumns.DATE)) { - row.mDate = values.getAsLong(BookmarkColumns.DATE); - } - if (values.containsKey(BookmarkColumns.FAVICON)) { - row.mFavicon = values.getAsByteArray(BookmarkColumns.FAVICON); - // We need to know that the caller set the favicon column. - if (row.mFavicon == null) { - row.mFavicon = new byte[0]; - } - } - if (values.containsKey(BookmarkColumns.TITLE)) { - row.mTitle = values.getAsString(BookmarkColumns.TITLE); - } - if (values.containsKey(BookmarkColumns.VISITS)) { - row.mVisits = values.getAsInteger(BookmarkColumns.VISITS); - } - if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) { - row.mParentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM); - } - return row; - } - } - - // Wrap the value of SearchColumn. - private static class SearchRow { - String mTerm; - Long mDate; - - static SearchRow fromContentValues(ContentValues values) { - SearchRow row = new SearchRow(); - if (values.containsKey(SearchColumns.SEARCH)) { - row.mTerm = values.getAsString(SearchColumns.SEARCH); - } - if (values.containsKey(SearchColumns.DATE)) { - row.mDate = values.getAsLong(SearchColumns.DATE); - } - return row; - } - } - - /** - * Initialize native side if it hasn't been already initialized. - * This is called from BrowserStartupCallback during normal startup except when called - * through one of the public ContentProvider APIs. - */ - private void ensureNativeSideInitialized() { - ThreadUtils.assertOnUiThread(); - if (mNativeChromeBrowserProvider == 0) mNativeChromeBrowserProvider = nativeInit(); - } - - @Override - protected void finalize() throws Throwable { - try { - // Tests might try to destroy this in the wrong thread. - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - ensureNativeChromeDestroyedOnUIThread(); - } - }); - } finally { - super.finalize(); - } - } - - /** - * This method should only run on UI thread. - */ - private void ensureNativeChromeDestroyedOnUIThread() { - if (mNativeChromeBrowserProvider != 0) { - nativeDestroy(mNativeChromeBrowserProvider); - mNativeChromeBrowserProvider = 0; - } - } - - @SuppressLint("NewApi") - private void notifyChange(final Uri uri) { - // If the calling user is different than current one, we need to post a - // task to notify change, otherwise, a system level hidden permission - // INTERACT_ACROSS_USERS_FULL is needed. - // The related APIs were added in API 17, it should be safe to fallback to - // normal way for notifying change, because caller can't be other users in - // devices whose API level is less than API 17. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - UserHandle callingUserHandle = Binder.getCallingUserHandle(); - if (callingUserHandle != null - && !callingUserHandle.equals(android.os.Process.myUserHandle())) { - ThreadUtils.postOnUiThread(new Runnable() { - @Override - public void run() { - getContext().getContentResolver().notifyChange(uri, null); - } - }); - return; - } - } - getContext().getContentResolver().notifyChange(uri, null); - } - - private boolean hasPermission(String permission) { - boolean isSystemOrGoogleCaller = ExternalAuthUtils.getInstance().isCallerValid( - getContext(), ExternalAuthUtils.FLAG_SHOULD_BE_GOOGLE_SIGNED - | ExternalAuthUtils.FLAG_SHOULD_BE_SYSTEM); - if (isSystemOrGoogleCaller) return true; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - return getContext().checkCallingOrSelfPermission( - getReadWritePermissionNameForBookmarkFolders()) - == PackageManager.PERMISSION_GRANTED; - } else { - return getContext().checkCallingOrSelfPermission(permission) - == PackageManager.PERMISSION_GRANTED; - } - } - - private native long nativeInit(); - private native void nativeDestroy(long nativeChromeBrowserProvider); - - // Public API native methods. - private native long nativeAddBookmark(long nativeChromeBrowserProvider, - String url, String title, boolean isFolder, long parentId); - - private native int nativeRemoveBookmark(long nativeChromeBrowserProvider, long id); - - private native int nativeUpdateBookmark(long nativeChromeBrowserProvider, - long id, String url, String title, long parentId); - - private native long nativeAddBookmarkFromAPI(long nativeChromeBrowserProvider, - String url, Long created, Boolean isBookmark, Long date, byte[] favicon, - String title, Integer visits, long parentId); - - private native SQLiteCursor nativeQueryBookmarkFromAPI(long nativeChromeBrowserProvider, - String[] projection, String selection, String[] selectionArgs, String sortOrder); - - private native int nativeUpdateBookmarkFromAPI(long nativeChromeBrowserProvider, - String url, Long created, Boolean isBookmark, Long date, byte[] favicon, - String title, Integer visits, long parentId, String selection, String[] selectionArgs); - - private native int nativeRemoveBookmarkFromAPI(long nativeChromeBrowserProvider, - String selection, String[] selectionArgs); - - private native int nativeRemoveHistoryFromAPI(long nativeChromeBrowserProvider, - String selection, String[] selectionArgs); - - private native long nativeAddSearchTermFromAPI(long nativeChromeBrowserProvider, - String term, Long date); - - private native SQLiteCursor nativeQuerySearchTermFromAPI(long nativeChromeBrowserProvider, - String[] projection, String selection, String[] selectionArgs, String sortOrder); - - private native int nativeUpdateSearchTermFromAPI(long nativeChromeBrowserProvider, - String search, Long date, String selection, String[] selectionArgs); - - private native int nativeRemoveSearchTermFromAPI(long nativeChromeBrowserProvider, - String selection, String[] selectionArgs); - - // Client API native methods. - private native boolean nativeBookmarkNodeExists(long nativeChromeBrowserProvider, long id); - - private native long nativeCreateBookmarksFolderOnce(long nativeChromeBrowserProvider, - String title, long parentId); - - private native BookmarkNode nativeGetEditableBookmarkFolders(long nativeChromeBrowserProvider); - - private native void nativeRemoveAllUserBookmarks(long nativeChromeBrowserProvider); - - private native BookmarkNode nativeGetBookmarkNode(long nativeChromeBrowserProvider, - long id, boolean getParent, boolean getChildren); - - private native BookmarkNode nativeGetMobileBookmarksFolder(long nativeChromeBrowserProvider); - - private native boolean nativeIsBookmarkInMobileBookmarksBranch(long nativeChromeBrowserProvider, - long id); - - private native byte[] nativeGetFaviconOrTouchIcon(long nativeChromeBrowserProvider, String url); - - private native byte[] nativeGetThumbnail(long nativeChromeBrowserProvider, String url); -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProviderClient.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProviderClient.java deleted file mode 100644 index 6c38968..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProviderClient.java +++ /dev/null
@@ -1,145 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser; - -import android.content.Context; -import android.net.Uri; -import android.os.Bundle; -import android.os.Parcelable; -import android.util.Log; - -import org.chromium.chrome.browser.ChromeBrowserProvider.BookmarkNode; - -import java.io.Serializable; - -/** - * Exposes the custom API methods for ChromeBrowserProvider. - */ -public class ChromeBrowserProviderClient { - private static final String TAG = "ChromeBrowserProviderClient"; - - // Returned by some of the methods in this class. - public static final long INVALID_BOOKMARK_ID = ChromeBrowserProvider.INVALID_BOOKMARK_ID; - - // Flags used with getBookmarkNode. - /** Retrieve the node corresponding to the id provided in getBookmarkNode. */ - public static final int GET_NODE = 0x00000000; - - /** Retrieve the parent of the node requested in getBookmarkNode. */ - public static final int GET_PARENT = 0x00000001; - - /** Retrieve the immediate children of the node requested in getBookmarkNode. */ - public static final int GET_CHILDREN = 0x00000002; - - /** Retrieve the favicon or touch icon, if any, in all the nodes returned by getBookmarkNode. */ - public static final int GET_FAVICONS = 0x00000004; - - /** Retrieve the thumbnail, if any, in all the nodes returned by getBookmarkNode. */ - public static final int GET_THUMBNAILS = 0x00000008; - - /** - * Verifies if a bookmark node given by its ID exists in the bookmark model. - * - * @return True if the provided bookmark node exists in the bookmark model. - */ - public static boolean bookmarkNodeExists(Context context, long nodeId) { - Boolean result = chromeBrowserProviderCall(Boolean.class, - ChromeBrowserProvider.CLIENT_API_BOOKMARK_NODE_EXISTS, - context, argsToBundle(nodeId)); - return result != null ? result.booleanValue() : false; - } - - /** - * Removes all bookmarks and bookmark folders that the user can edit. - * Only the permanent bookmark folders remain after this operation, and any managed bookmarks. - */ - public static void removeAllUserBookmarks(Context context) { - chromeBrowserProviderCall(BookmarkNode.class, - ChromeBrowserProvider.CLIENT_API_DELETE_ALL_USER_BOOKMARKS, context, - argsToBundle()); - } - - /** - * Retrieves a bookmark node given its ID or null if no such node exists. - * The parent and immediate child nodes can be also retrieved by enabling the getParent - * and getChildren flags. No deeper child nodes can be retrieved with this method. - * - * @param nodeId The ID of the bookmark node to be retrieved. - * @param flags Combination of constants telling what information of the node is required. - * @return The bookmark node corresponding to the provided ID. - */ - public static BookmarkNode getBookmarkNode(Context context, long nodeId, int flags) { - return chromeBrowserProviderCall(BookmarkNode.class, - ChromeBrowserProvider.CLIENT_API_GET_BOOKMARK_NODE, context, - argsToBundle(nodeId, - (flags & GET_PARENT) != 0, - (flags & GET_CHILDREN) != 0, - (flags & GET_FAVICONS) != 0, - (flags & GET_THUMBNAILS) != 0)); - } - - /** - * Returns the ID of the Mobile Bookmarks folder. - * - * @return The ID of the Mobile Bookmarks folder or INVALID_BOOKMARK_ID in case of error. - */ - public static long getMobileBookmarksFolderId(Context context) { - Long id = chromeBrowserProviderCall(Long.class, - ChromeBrowserProvider.CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID, context, - argsToBundle()); - return id != null ? id.longValue() : INVALID_BOOKMARK_ID; - } - - /** - * Checks if a bookmark node is in the Mobile Bookmarks folder branch. - * - * @return True if the ID belongs to a node in the Mobile Bookmarks folder branch. - */ - public static boolean isBookmarkInMobileBookmarksBranch(Context context, long nodeId) { - Boolean result = chromeBrowserProviderCall(Boolean.class, - ChromeBrowserProvider.CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH, context, - argsToBundle(nodeId)); - return result != null ? result.booleanValue() : false; - } - - // --------------------- End of the client API --------------------- // - - private static Uri getPrivateProviderUri(Context context) { - // The Bookmarks Uri uses the private provider authority. - return ChromeBrowserProvider.getBookmarksUri(context); - } - - - private static Bundle argsToBundle(Object ... args) { - Bundle methodArgs = new Bundle(); - for (int i = 0; i < args.length; ++i) { - Class<? extends Object> argClass = args[i].getClass(); - if (Parcelable.class.isAssignableFrom(argClass)) { - methodArgs.putParcelable(ChromeBrowserProvider.argKey(i), (Parcelable) args[i]); - } else if (Serializable.class.isAssignableFrom(argClass)) { - methodArgs.putSerializable(ChromeBrowserProvider.argKey(i), (Serializable) args[i]); - } else { - Log.e(TAG, "Argument implements neither Parcelable nor Serializable."); - return null; - } - } - return methodArgs; - } - - private static <T> T chromeBrowserProviderCall(Class<T> returnType, String name, - Context context, Bundle args) { - android.util.Log.i(TAG, "before executing " + name + " call"); - Bundle result = context.getContentResolver().call(getPrivateProviderUri(context), - name, null, args); - android.util.Log.i(TAG, "after executing " + name + " call"); - if (result == null) return null; - if (Parcelable.class.isAssignableFrom(returnType)) { - return returnType.cast( - result.getParcelable(ChromeBrowserProvider.CLIENT_API_RESULT_KEY)); - } else { - return returnType.cast(result.get(ChromeBrowserProvider.CLIENT_API_RESULT_KEY)); - } - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProviderSuggestionsCursor.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProviderSuggestionsCursor.java deleted file mode 100644 index 4585779..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProviderSuggestionsCursor.java +++ /dev/null
@@ -1,130 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser; - -import android.app.SearchManager; -import android.content.Intent; -import android.database.AbstractCursor; -import android.database.Cursor; - -import org.chromium.chrome.R; -import org.chromium.chrome.browser.bookmark.BaseColumns; -import org.chromium.chrome.browser.bookmark.BookmarkColumns; - -/** - * For bookmarks/history suggestions, wrap the cursor returned in one that can feed - * the data back to global search in the format it wants. - */ -class ChromeBrowserProviderSuggestionsCursor extends AbstractCursor { - - private static final String[] COLS = new String [] { - BaseColumns.ID, - SearchManager.SUGGEST_COLUMN_INTENT_ACTION, - SearchManager.SUGGEST_COLUMN_INTENT_DATA, - SearchManager.SUGGEST_COLUMN_TEXT_1, - SearchManager.SUGGEST_COLUMN_TEXT_2, - SearchManager.SUGGEST_COLUMN_TEXT_2_URL, - SearchManager.SUGGEST_COLUMN_ICON_1, - SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT - }; - - private static final int COLUMN_ID = 0; - private static final int COLUMN_SUGGEST_INTENT_ACTION = 1; - private static final int COLUMN_SUGGEST_INTENT_DATA = 2; - private static final int COLUMN_SUGGEST_TEXT_1 = 3; - private static final int COLUMN_SUGGEST_TEXT_2 = 4; - private static final int COLUMN_SUGGEST_TEXT_2_URL = 5; - private static final int COLUMN_SUGGEST_ICON_1 = 6; - private static final int COLUMN_SUGGEST_LAST_ACCESS_HINT = 7; - - private final Cursor mCursor; - - public ChromeBrowserProviderSuggestionsCursor(Cursor c) { - mCursor = c; - } - - @Override - public String[] getColumnNames() { - return COLS; - } - - @Override - public int getCount() { - return mCursor.getCount(); - } - - @Override - public String getString(int column) { - switch (column) { - case COLUMN_ID: - return mCursor.getString(mCursor.getColumnIndex(BookmarkColumns.ID)); - case COLUMN_SUGGEST_INTENT_ACTION: - return Intent.ACTION_VIEW; - case COLUMN_SUGGEST_INTENT_DATA: - return mCursor.getString(mCursor.getColumnIndex(BookmarkColumns.URL)); - case COLUMN_SUGGEST_TEXT_1: - return mCursor.getString(mCursor.getColumnIndex(BookmarkColumns.TITLE)); - case COLUMN_SUGGEST_TEXT_2: - case COLUMN_SUGGEST_TEXT_2_URL: - return mCursor.getString(mCursor.getColumnIndex(BookmarkColumns.URL)); - case COLUMN_SUGGEST_ICON_1: - // This is the icon displayed to the left of the result in QSB. - return Integer.toString(R.mipmap.app_icon); - case COLUMN_SUGGEST_LAST_ACCESS_HINT: - // After clearing history, the Chrome bookmarks database will have a last access - // time of 0 for all bookmarks. In the Android provider, this will yield a negative - // last access time. A negative last access time will cause global search to discard - // the result, so fix it up before we return it. - long lastAccess = mCursor.getLong( - mCursor.getColumnIndex(BookmarkColumns.DATE)); - return lastAccess < 0 ? "0" : "" + lastAccess; - default: - throw new UnsupportedOperationException(); - } - } - - @Override - public boolean isNull(int c) { - return mCursor.isNull(c); - } - - @Override - public long getLong(int c) { - switch (c) { - case 7: - // See comments above in getString() re. negative last access times. - long lastAccess = mCursor.getLong( - mCursor.getColumnIndex(BookmarkColumns.DATE)); - return lastAccess < 0 ? 0 : lastAccess; - default: - throw new UnsupportedOperationException(); - } - } - - @Override - public short getShort(int c) { - throw new UnsupportedOperationException(); - } - - @Override - public double getDouble(int c) { - throw new UnsupportedOperationException(); - } - - @Override - public int getInt(int c) { - throw new UnsupportedOperationException(); - } - - @Override - public float getFloat(int c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean onMove(int oldPosition, int newPosition) { - return mCursor.moveToPosition(newPosition); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeServiceTabLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeServiceTabLauncher.java index 08ac455..0231b296 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeServiceTabLauncher.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeServiceTabLauncher.java
@@ -46,6 +46,6 @@ TabDelegate tabDelegate = new TabDelegate(incognito); tabDelegate.createNewTab( - asyncParams, TabLaunchType.FROM_MENU_OR_OVERVIEW, Tab.INVALID_TAB_ID); + asyncParams, TabLaunchType.FROM_CHROME_UI, Tab.INVALID_TAB_ID); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 2222614..f92ddbe 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -579,7 +579,7 @@ private void createInitialTab() { String url = HomepageManager.getHomepageUri(getApplicationContext()); if (TextUtils.isEmpty(url)) url = UrlConstants.NTP_URL; - getTabCreator(false).launchUrl(url, TabLaunchType.FROM_MENU_OR_OVERVIEW); + getTabCreator(false).launchUrl(url, TabLaunchType.FROM_CHROME_UI); } @Override @@ -609,8 +609,8 @@ // // TODO(dfalcantara): Use real tab reparenting here when possible. We should fall // back to using the TabState file or URL, in that order. - getTabCreator(false).createNewTab( - new LoadUrlParams(data.getDataString()), TabLaunchType.FROM_LINK, null); + getTabCreator(false).createNewTab(new LoadUrlParams( + data.getDataString()), TabLaunchType.FROM_CHROME_UI, null); } else if ((resultCode == CustomTabActivity.RESULT_BACK_PRESSED || resultCode == CustomTabActivity.RESULT_CLOSED) && data != null) { Log.d(TAG, "Herb: Sending app to the background"); @@ -725,7 +725,7 @@ // Used by the Account management screen to open a new incognito tab. // Account management screen collects its metrics separately. getTabCreator(true).launchUrl( - UrlConstants.NTP_URL, TabLaunchType.FROM_MENU_OR_OVERVIEW); + UrlConstants.NTP_URL, TabLaunchType.FROM_CHROME_UI); } else { getTabCreator(true).launchUrl( UrlConstants.NTP_URL, TabLaunchType.FROM_EXTERNAL_APP); @@ -970,8 +970,7 @@ final Tab currentTab = getActivityTab(); if (id == R.id.new_tab_menu_id) { Tab launchedTab = getTabCreator(false).launchUrl( - UrlConstants.NTP_URL, - fromMenu ? TabLaunchType.FROM_MENU_OR_OVERVIEW : TabLaunchType.FROM_KEYBOARD); + UrlConstants.NTP_URL, TabLaunchType.FROM_CHROME_UI); RecordUserAction.record("MobileMenuNewTab"); RecordUserAction.record("MobileNewTabOpened"); if (isTablet() && !fromMenu && !launchedTab.isHidden()) { @@ -984,9 +983,7 @@ RecordUserAction.record("MobileMenuNewIncognitoTab"); RecordUserAction.record("MobileNewTabOpened"); Tab launchedTab = getTabCreator(true).launchUrl( - UrlConstants.NTP_URL, - fromMenu ? TabLaunchType.FROM_MENU_OR_OVERVIEW - : TabLaunchType.FROM_KEYBOARD); + UrlConstants.NTP_URL, TabLaunchType.FROM_CHROME_UI); if (isTablet() && !fromMenu && !launchedTab.isHidden()) { getToolbarManager().setUrlBarFocus(true); } @@ -1101,7 +1098,7 @@ // If the current tab url is HELP_URL, then the back button should close the tab to // get back to the previous state. The reason for startsWith check is that the // actual redirected URL is a different system language based help url. - if (type == TabLaunchType.FROM_MENU_OR_OVERVIEW && helpUrl) { + if (type == TabLaunchType.FROM_CHROME_UI && helpUrl) { getCurrentTabModel().closeTab(currentTab); Log.i(TAG, "handleBackPressedWithoutBackStack() - help url"); return true; @@ -1137,7 +1134,7 @@ final boolean minimizeApp = !shouldCloseTab || currentTab.isCreatedForExternalApp(); if (minimizeApp) { - sendToBackground(currentTab); + sendToBackground(shouldCloseTab ? currentTab : null); } else if (shouldCloseTab) { getCurrentTabModel().closeTab(currentTab, true, false, false); }
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 9b54d37..89c697f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java
@@ -75,25 +75,31 @@ // The title at the top of the dialog. public final SpannableString mTitle; // The message to show while there are no results. - public final String mSearching; + public final SpannableString mSearching; // The message to show when no results were produced. public final SpannableString mNoneFound; // A status message to show above the button row after an item has // been added and discovery is still ongoing. public final SpannableString mStatusActive; + // A status message to show above the button row after discovery has + // stopped and no devices have been found. + public final SpannableString mStatusIdleNoneFound; // A status message to show above the button row after an item has // been added and discovery has stopped. - public final SpannableString mStatusIdle; + public final SpannableString mStatusIdleSomeFound; // The label for the positive button (e.g. Select/Pair). public final String mPositiveButton; - public ItemChooserLabels(SpannableString title, String searching, SpannableString noneFound, - SpannableString statusActive, SpannableString statusIdle, String positiveButton) { + public ItemChooserLabels(SpannableString title, SpannableString searching, + SpannableString noneFound, SpannableString statusActive, + SpannableString statusIdleNoneFound, SpannableString statusIdleSomeFound, + String positiveButton) { mTitle = title; mSearching = searching; mNoneFound = noneFound; mStatusActive = statusActive; - mStatusIdle = statusIdle; + mStatusIdleNoneFound = statusIdleNoneFound; + mStatusIdleSomeFound = statusIdleSomeFound; mPositiveButton = positiveButton; } } @@ -411,8 +417,9 @@ mListView.setVisibility(View.VISIBLE); break; case DISCOVERY_IDLE: - mStatus.setText(mLabels.mStatusIdle); boolean showEmptyMessage = mItemAdapter.isEmpty(); + mStatus.setText(showEmptyMessage + ? mLabels.mStatusIdleNoneFound : mLabels.mStatusIdleSomeFound); mEmptyMessage.setText(mLabels.mNoneFound); mEmptyMessage.setVisibility(showEmptyMessage ? View.VISIBLE : View.GONE); break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java index 1d501a97..1c3cc6b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
@@ -10,7 +10,6 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.ChromeBrowserProviderClient; import org.chromium.chrome.browser.ShortcutHelper; import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.bookmarks.BookmarkBridge; @@ -18,6 +17,7 @@ import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; import org.chromium.chrome.browser.preferences.ManagedPreferencesUtils; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.provider.ChromeBrowserProviderClient; import org.chromium.chrome.browser.share.ShareHelper; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.util.FeatureUtilities; @@ -99,16 +99,7 @@ loadingStateChanged(currentTab.isLoading()); MenuItem bookmarkMenuItem = menu.findItem(R.id.bookmark_this_page_id); - bookmarkMenuItem.setEnabled(mBookmarkBridge.isEditBookmarksEnabled()); - if (currentTab.getBookmarkId() != ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) { - bookmarkMenuItem.setIcon(R.drawable.btn_star_filled); - bookmarkMenuItem.setChecked(true); - bookmarkMenuItem.setTitleCondensed(mActivity.getString(R.string.edit_bookmark)); - } else { - bookmarkMenuItem.setIcon(R.drawable.btn_star); - bookmarkMenuItem.setChecked(false); - bookmarkMenuItem.setTitleCondensed(null); - } + updateBookmarkMenuItem(bookmarkMenuItem, currentTab); } menu.findItem(R.id.update_menu_id).setVisible( @@ -249,4 +240,23 @@ public void setBookmarkBridge(BookmarkBridge bookmarkBridge) { mBookmarkBridge = bookmarkBridge; } + + /** + * Updates the bookmark item's visibility. + * + * @param bookmarkMenuItem {@link MenuItem} for adding/editing the bookmark. + * @param currentTab Current tab being displayed. + */ + protected void updateBookmarkMenuItem(MenuItem bookmarkMenuItem, Tab currentTab) { + bookmarkMenuItem.setEnabled(mBookmarkBridge.isEditBookmarksEnabled()); + if (currentTab.getBookmarkId() != ChromeBrowserProviderClient.INVALID_BOOKMARK_ID) { + bookmarkMenuItem.setIcon(R.drawable.btn_star_filled); + bookmarkMenuItem.setChecked(true); + bookmarkMenuItem.setTitleCondensed(mActivity.getString(R.string.edit_bookmark)); + } else { + bookmarkMenuItem.setIcon(R.drawable.btn_star); + bookmarkMenuItem.setChecked(false); + bookmarkMenuItem.setTitleCondensed(null); + } + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmark/BaseColumns.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmark/BaseColumns.java deleted file mode 100644 index b749263..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmark/BaseColumns.java +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.bookmark; - -/** - * Copy of android.provider.BaseColumns. - */ -public interface BaseColumns { - /** - * The unique ID for a row. - * <P>Type: INTEGER (long)</P> - */ - public static final String ID = "_id"; - - /** - * The count of rows in a directory. - * <P>Type: INTEGER</P> - */ - public static final String COUNT = "_count"; -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmark/BookmarkColumns.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmark/BookmarkColumns.java deleted file mode 100644 index 194fda1..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmark/BookmarkColumns.java +++ /dev/null
@@ -1,53 +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. - -package org.chromium.chrome.browser.bookmark; - -/** - * Copy of android.provider.Browser.BookmarkColumns. - */ -public class BookmarkColumns implements BaseColumns { - /** - * The URL of the bookmark or history item. - * <p>Type: TEXT (URL)</p> - */ - public static final String URL = "url"; - - /** - * The number of time the item has been visited. - * <p>Type: NUMBER</p> - */ - public static final String VISITS = "visits"; - - /** - * The date the item was last visited, in milliseconds since the epoch. - * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p> - */ - public static final String DATE = "date"; - - /** - * Flag indicating that an item is a bookmark. A value of 1 indicates a bookmark, a value - * of 0 indicates a history item. - * <p>Type: INTEGER (boolean)</p> - */ - public static final String BOOKMARK = "bookmark"; - - /** - * The user visible title of the bookmark or history item. - * <p>Type: TEXT</p> - */ - public static final String TITLE = "title"; - - /** - * The date the item created, in milliseconds since the epoch. - * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p> - */ - public static final String CREATED = "created"; - - /** - * The favicon of the bookmark. Must decode via {@link BitmapFactory#decodeByteArray}. - * <p>Type: BLOB (image)</p> - */ - public static final String FAVICON = "favicon"; -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmark/SearchColumns.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmark/SearchColumns.java deleted file mode 100644 index 896a2e1..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmark/SearchColumns.java +++ /dev/null
@@ -1,21 +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. - -package org.chromium.chrome.browser.bookmark; - -/** - * Copy of android.provider.Browser.SearchColumns. - */ -public class SearchColumns implements BaseColumns { - /** - * The user entered search term. - */ - public static final String SEARCH = "search"; - - /** - * The date the search was performed, in milliseconds since the epoch. - * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p> - */ - public static final String DATE = "date"; -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java index 3112078..4da3d62 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
@@ -7,7 +7,6 @@ import android.content.Intent; import android.os.Bundle; import android.support.v7.widget.Toolbar; -import android.text.TextUtils; import android.text.format.Formatter; import android.view.Menu; import android.view.MenuItem; @@ -65,37 +64,11 @@ private BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() { @Override - public void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, BookmarkItem node, - boolean isDoingExtensiveChanges) { - if (mBookmarkId.equals(node.getId())) { - finish(); - } - } - - @Override - public void bookmarkNodeMoved(BookmarkItem oldParent, int oldIndex, BookmarkItem newParent, - int newIndex) { - BookmarkId movedBookmark = mModel.getChildAt(newParent.getId(), - newIndex); - if (movedBookmark.equals(mBookmarkId)) { - mFolderTextView.setText(newParent.getTitle()); - } - } - - @Override - public void bookmarkNodeChanged(BookmarkItem node) { - if (mBookmarkId.equals(node.getId()) || node.getId().equals( - mModel.getBookmarkById(mBookmarkId).getParentId())) { - updateViewContent(); - } - } - - @Override public void bookmarkModelChanged() { if (mModel.doesBookmarkExist(mBookmarkId)) { - updateViewContent(); + updateViewContent(true); } else { - Log.wtf(TAG, "The bookmark was deleted somehow during bookmarkModelChange!", + Log.wtf(TAG, "A partner bookmark might be removed while the user is editing it.", new Exception(TAG)); finish(); } @@ -155,22 +128,20 @@ setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); - updateViewContent(); + updateViewContent(false); } - private void updateViewContent() { + /** + * @param modelChanged Whether this view update is due to a model change in background. + */ + private void updateViewContent(boolean modelChanged) { BookmarkItem bookmarkItem = mModel.getBookmarkById(mBookmarkId); - - if (!TextUtils.equals(mTitleEditText.getTrimmedText(), bookmarkItem.getTitle())) { + // While the user is editing the bookmark, do not override user's input. + if (!modelChanged) { mTitleEditText.setText(bookmarkItem.getTitle()); - } - String folderTitle = mModel.getBookmarkTitle(bookmarkItem.getParentId()); - if (!TextUtils.equals(mFolderTextView.getText(), folderTitle)) { - mFolderTextView.setText(folderTitle); - } - if (!TextUtils.equals(mUrlEditText.getTrimmedText(), bookmarkItem.getUrl())) { mUrlEditText.setText(bookmarkItem.getUrl()); } + mFolderTextView.setText(mModel.getBookmarkTitle(bookmarkItem.getParentId())); mTitleEditText.setEnabled(bookmarkItem.isEditable()); mUrlEditText.setEnabled(bookmarkItem.isUrlEditable()); mFolderTextView.setEnabled(bookmarkItem.isMovable());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java index 7fc4a19..26e2553 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java
@@ -9,13 +9,13 @@ import org.chromium.base.ObserverList; import org.chromium.base.VisibleForTesting; import org.chromium.base.metrics.RecordHistogram; -import org.chromium.chrome.browser.ChromeBrowserProviderClient; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.OfflinePageModelObserver; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.SavePageCallback; import org.chromium.chrome.browser.offlinepages.OfflinePageItem; import org.chromium.chrome.browser.offlinepages.OfflinePageUtils; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.provider.ChromeBrowserProviderClient; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkType; import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java index dcc02ff8..e4b2679 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
@@ -19,7 +19,6 @@ import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.R; -import org.chromium.chrome.browser.ChromeBrowserProviderClient; import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.bookmarks.BookmarkModel.AddBookmarkCallback; @@ -31,6 +30,7 @@ import org.chromium.chrome.browser.offlinepages.OfflinePageOpenStorageSettingsDialog; import org.chromium.chrome.browser.offlinepages.OfflinePageStorageSpacePolicy; import org.chromium.chrome.browser.offlinepages.OfflinePageUtils; +import org.chromium.chrome.browser.provider.ChromeBrowserProviderClient; import org.chromium.chrome.browser.snackbar.Snackbar; import org.chromium.chrome.browser.snackbar.SnackbarManager; import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java index 0185797..10adf505 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkThumbnailWidgetService.java
@@ -27,10 +27,10 @@ import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeApplication; -import org.chromium.chrome.browser.ChromeBrowserProvider.BookmarkNode; -import org.chromium.chrome.browser.ChromeBrowserProviderClient; -import org.chromium.chrome.browser.bookmark.BookmarkColumns; import org.chromium.chrome.browser.init.ChromeBrowserInitializer; +import org.chromium.chrome.browser.provider.BookmarkColumns; +import org.chromium.chrome.browser.provider.ChromeBrowserProvider.BookmarkNode; +import org.chromium.chrome.browser.provider.ChromeBrowserProviderClient; import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.sync.AndroidSyncSettings;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetUpdateListener.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetUpdateListener.java index f75ae9a..f4c3875 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetUpdateListener.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetUpdateListener.java
@@ -9,7 +9,7 @@ import android.database.ContentObserver; import android.os.Handler; -import org.chromium.chrome.browser.ChromeBrowserProvider; +import org.chromium.chrome.browser.provider.ChromeBrowserProvider; import org.chromium.sync.AndroidSyncSettings; /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java index c648182..91902fa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -90,7 +90,7 @@ @Override public void willAddTab(Tab tab, TabLaunchType type) { // Open the new tab - if (type == TabLaunchType.FROM_INSTANT || type == TabLaunchType.FROM_RESTORE) return; + if (type == TabLaunchType.FROM_RESTORE) return; tabCreating(getTabModelSelector().getCurrentTabId(), tab.getUrl(), tab.isIncognito()); } @@ -98,14 +98,13 @@ @Override public void didAddTab(Tab tab, TabLaunchType launchType) { int tabId = tab.getId(); - if (launchType != TabLaunchType.FROM_INSTANT - && launchType != TabLaunchType.FROM_RESTORE) { + if (launchType != TabLaunchType.FROM_RESTORE) { boolean incognito = tab.isIncognito(); boolean willBeSelected = launchType != TabLaunchType.FROM_LONGPRESS_BACKGROUND || (!getTabModelSelector().isIncognitoSelected() && incognito); float lastTapX = LocalizationUtils.isLayoutRtl() ? mLastContentWidthDp : 0.f; float lastTapY = 0.f; - if (launchType != TabLaunchType.FROM_MENU_OR_OVERVIEW) { + if (launchType != TabLaunchType.FROM_CHROME_UI) { float heightDelta = mLastFullscreenViewportDp.height() - mLastVisibleViewportDp.height(); lastTapX = mPxToDp * mLastTapX;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java index 5b020ed..1db8081 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
@@ -490,11 +490,12 @@ // Remove any views in case we're getting another call to show before we hide (quickly // toggling the tab switcher button). mViewContainer.removeAllViews(); + int currentTabModel = mTabModelSelector.isIncognitoSelected() ? 1 : 0; for (int i = mStacks.length - 1; i >= 0; --i) { mStacks[i].reset(); if (mStacks[i].isDisplayable()) { - mStacks[i].show(); + mStacks[i].show(i == currentTabModel); } else { mStacks[i].cleanupTabs(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java index 02061c79..54acce2e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
@@ -240,6 +240,8 @@ private float mBorderTopPadding; private float mBorderLeftPadding; + private boolean mIsStackForCurrentTabModel; + private final AnimatorListenerAdapter mViewAnimatorListener = new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { @@ -444,8 +446,11 @@ /** * show is called to set up the initial variables, and must always be called before * displaying the stack. + * @param isStackForCurrentTabModel Whether this {@link Stack} is for the current tab model. */ - public void show() { + public void show(boolean isStackForCurrentTabModel) { + mIsStackForCurrentTabModel = isStackForCurrentTabModel; + mDiscardDirection = getDefaultDiscardDirection(); // Reinitialize the roll over counter for each tabswitcher session. @@ -1989,7 +1994,8 @@ layoutTab.setInsetBorderVertical(true); layoutTab.setShowToolbar(true); layoutTab.setToolbarAlpha(0.f); - layoutTab.setAnonymizeToolbar(mTabModel.index() != i); + layoutTab.setAnonymizeToolbar(!mIsStackForCurrentTabModel + || mTabModel.index() != i); if (mStackTabs[i] == null) { mStackTabs[i] = new StackTab(layoutTab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java index bbdae0a..4e18b0d5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -137,6 +137,11 @@ private TabRedirectHandler mTabRedirectHandler; /** + * Whether the Accessibility Mode is enabled. + */ + private boolean mIsAccessibilityModeEnabled; + + /** * The delegate that is responsible for promoting a {@link ContentViewCore} to a {@link Tab} * when necessary. */ @@ -786,6 +791,15 @@ Log.i(TAG, "ctxs setCaption: '" + caption + "', " + doesAnswer); } + /** + * Notifies that the Accessibility Mode state has changed. + * + * @param enabled Whether the Accessibility Mode is enabled. + */ + public void onAccessibilityModeChanged(boolean enabled) { + mIsAccessibilityModeEnabled = enabled; + } + // ============================================================================================ // ContextualSearchTranslateInterface // ============================================================================================ @@ -1157,16 +1171,22 @@ @Override public void handleScroll() { + if (mIsAccessibilityModeEnabled) return; + hideContextualSearch(StateChangeReason.BASE_PAGE_SCROLL); } @Override public void handleInvalidTap() { + if (mIsAccessibilityModeEnabled) return; + hideContextualSearch(StateChangeReason.BASE_PAGE_TAP); } @Override public void handleValidTap() { + if (mIsAccessibilityModeEnabled) return; + if (isTapSupported()) { // Here we are starting a new Contextual Search with a Tap gesture, therefore // we need to clear to properly reflect that a search just started and we don't @@ -1184,6 +1204,8 @@ @Override public void handleSelection(String selection, boolean selectionValid, SelectionType type, float x, float y) { + if (mIsAccessibilityModeEnabled) return; + if (!selection.isEmpty()) { StateChangeReason stateChangeReason = type == SelectionType.TAP ? StateChangeReason.TEXT_SELECT_TAP : StateChangeReason.TEXT_SELECT_LONG_PRESS; @@ -1199,6 +1221,8 @@ @Override public void handleSelectionDismissal() { + if (mIsAccessibilityModeEnabled) return; + if (mSearchPanel.isShowing() && !mIsPromotingToTab // If the selection is dismissed when the Panel is not peeking anymore, @@ -1214,6 +1238,8 @@ @Override public void handleSelectionModification( String selection, boolean selectionValid, float x, float y) { + if (mIsAccessibilityModeEnabled) return; + if (mSearchPanel.isShowing()) { if (selectionValid) { mSearchPanel.displaySearchTerm(selection);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java index 745e050..74b321c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -278,8 +278,6 @@ CustomTabsConnection.getInstance(getApplication()) .getClientPackageNameForSession(mSession), IntentHandler.getUrlFromIntent(getIntent())); - mainTab.setAppAssociatedWith(CustomTabsConnection.getInstance(getApplication()) - .getClientPackageNameForSession(mSession)); recordClientPackageName(); loadUrlInCurrentTab(new LoadUrlParams(IntentHandler.getUrlFromIntent(getIntent())), IntentHandler.getTimestampFromIntent(getIntent())); @@ -300,6 +298,7 @@ TabLaunchType.FROM_EXTERNAL_APP, null, null); CustomTabsConnection customTabsConnection = CustomTabsConnection.getInstance(getApplication()); + tab.setAppAssociatedWith(customTabsConnection.getClientPackageNameForSession(mSession)); WebContents webContents = customTabsConnection.takePrerenderedUrl(mSession, url, referrerUrl); if (webContents == null) { @@ -400,7 +399,8 @@ @Override protected AppMenuPropertiesDelegate createAppMenuPropertiesDelegate() { return new CustomTabAppMenuPropertiesDelegate(this, mIntentDataProvider.getMenuTitles(), - mIntentDataProvider.shouldShowShareMenuItem()); + mIntentDataProvider.shouldShowShareMenuItem(), + mIntentDataProvider.shouldShowBookmarkMenuItem()); } @Override @@ -575,10 +575,13 @@ public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) { // Disable creating new tabs, bookmark, history, print, help, focus_url, etc. if (id == R.id.focus_url_bar || id == R.id.all_bookmarks_menu_id - || id == R.id.bookmark_this_page_id || id == R.id.print_id || id == R.id.help_id + || id == R.id.print_id || id == R.id.help_id || id == R.id.recent_tabs_menu_id || id == R.id.new_incognito_tab_menu_id || id == R.id.new_tab_menu_id || id == R.id.open_history_menu_id) { return true; + } else if (id == R.id.bookmark_this_page_id + && !mIntentDataProvider.shouldShowBookmarkMenuItem()) { + return true; } else if (id == R.id.open_in_browser_id) { openCurrentUrlInBrowser(); RecordUserAction.record("CustomTabsMenuOpenInChrome");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java index cde678c..f4ff270 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
@@ -8,7 +8,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; -import android.os.StrictMode; +import android.os.AsyncTask; import android.view.Menu; import android.view.MenuItem; @@ -21,24 +21,43 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; /** * App menu properties delegate for {@link CustomTabActivity}. */ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegate { private static final String SAMPLE_URL = "https://www.google.com"; + + private final boolean mShowShare; + private final boolean mShowBookmark; + private final List<String> mMenuEntries; + private final Map<MenuItem, Integer> mItemToIndexMap = new HashMap<MenuItem, Integer>(); + private final AsyncTask<Void, Void, String> mDefaultBrowserFetcher; + private boolean mIsCustomEntryAdded; - private boolean mShowShare; - private List<String> mMenuEntries; - private Map<MenuItem, Integer> mItemToIndexMap = new HashMap<MenuItem, Integer>(); + /** * Creates an {@link CustomTabAppMenuPropertiesDelegate} instance. */ - public CustomTabAppMenuPropertiesDelegate(ChromeActivity activity, List<String> menuEntries, - boolean showShare) { + public CustomTabAppMenuPropertiesDelegate(final ChromeActivity activity, + List<String> menuEntries, boolean showShare, boolean showBookmark) { super(activity); mMenuEntries = menuEntries; mShowShare = showShare; + mShowBookmark = showBookmark; + mDefaultBrowserFetcher = new AsyncTask<Void, Void, String>() { + @Override + protected String doInBackground(Void... params) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(SAMPLE_URL)); + PackageManager pm = activity.getPackageManager(); + ResolveInfo info = pm.resolveActivity(intent, 0); + return info != null && info.match != 0 + ? activity.getString( + R.string.menu_open_in_product, info.loadLabel(pm).toString()) + : activity.getString(R.string.menu_open_in_product_default); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @Override @@ -56,25 +75,23 @@ shareItem.setVisible(mShowShare); shareItem.setEnabled(mShowShare); - MenuItem openInChromeItem = menu.findItem(R.id.open_in_browser_id); - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(SAMPLE_URL)); - PackageManager pm = mActivity.getPackageManager(); - ResolveInfo info = null; - - // Temporarily allowing disk access while fixing. TODO: http://crbug.com/581856 - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - StrictMode.allowThreadDiskWrites(); - try { - info = pm.resolveActivity(intent, 0); - } finally { - StrictMode.setThreadPolicy(oldPolicy); + if (mShowBookmark) { + MenuItem bookmarkItem = menu.findItem(R.id.bookmark_this_page_id); + updateBookmarkMenuItem(bookmarkItem, currentTab); + } else { + // Because we have custom logic for laying out the icon row, the bookmark icon must + // be explicitly removed instead of just made invisible. + menu.findItem(R.id.icon_row_menu_id).getSubMenu().removeItem( + R.id.bookmark_this_page_id); } - String menuItemTitle = info != null && info.match != 0 - ? mActivity.getString( - R.string.menu_open_in_product, info.loadLabel(pm).toString()) - : mActivity.getString(R.string.menu_open_in_product_default); - openInChromeItem.setTitle(menuItemTitle); + MenuItem openInChromeItem = menu.findItem(R.id.open_in_browser_id); + try { + openInChromeItem.setTitle(mDefaultBrowserFetcher.get()); + } catch (InterruptedException | ExecutionException e) { + openInChromeItem.setTitle( + mActivity.getString(R.string.menu_open_in_product_default)); + } // Add custom menu items. Make sure they are only added once. if (!mIsCustomEntryAdded) { @@ -114,4 +131,4 @@ } return null; } -} \ No newline at end of file +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabDelegateFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabDelegateFactory.java index 0d7eb6d4..0d4c76cd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabDelegateFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabDelegateFactory.java
@@ -9,6 +9,7 @@ import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.TransactionTooLargeException; +import android.text.TextUtils; import org.chromium.base.Log; import org.chromium.base.VisibleForTesting; @@ -59,7 +60,7 @@ try { // For a URL chrome can handle and there is no default set, handle it ourselves. if (!hasDefaultHandler) { - if (isPackageSpecializedHandler( + if (!TextUtils.isEmpty(mClientPackageName) && isPackageSpecializedHandler( mApplicationContext, mClientPackageName, intent)) { intent.setPackage(mClientPackageName); } else if (!isExternalProtocol) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java index 620dc1e6..79c6db48 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -26,6 +26,7 @@ import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.preferences.ChromePreferenceManager; import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.browser.widget.TintedDrawable; @@ -46,15 +47,26 @@ public static final String EXTRA_KEEP_ALIVE = "android.support.customtabs.extra.KEEP_ALIVE"; /** - * Extra used by Chrome to tell the CustomTabActivity to finish itself and open the current URL - * in the browser. Guarded explicitly for use only by PendingIntents with the Chrome package. + * Herb: Extra used by Chrome to tell the CustomTabActivity to finish itself and open the + * current URL in the browser. Guarded explicitly for use only by PendingIntents with the + * Chrome package. */ public static final String EXTRA_FINISH_AFTER_OPENING_IN_BROWSER = "org.chromium.chrome.browser.customtabs.FINISH_AFTER_OPENING_IN_BROWSER"; + /** + * Herb: Extra used by the main Chrome browser to tell the {@link CustomTabActivity} that it + * sits on top of the main browser Activity. + */ public static final String EXTRA_OPENED_BY_BROWSER = "org.chromium.chrome.browser.customtabs.OPENED_BY_BROWSER"; + /** + * Herb: Extra used by the main Chrome browser to enable the bookmark icon in the menu. + */ + public static final String EXTRA_SHOW_STAR_ICON = + "org.chromium.chrome.browser.customtabs.SHOW_STAR_ICON"; + private static final int MAX_CUSTOM_MENU_ITEMS = 5; private static final String ANIMATION_BUNDLE_PREFIX = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? "android:activity." : "android:"; @@ -85,13 +97,16 @@ /** Herb: Whether or not this CustomTabActivity was opened by the Browser directly. */ private boolean mIsOpenedByBrowser; + /** Herb: Whether or not the bookmark button should be shown. */ + private boolean mShowBookmarkItem; + /** * Constructs a {@link CustomTabIntentDataProvider}. */ public CustomTabIntentDataProvider(Intent intent, Context context) { if (intent == null) assert false; mSession = IntentUtils.safeGetBinderExtra(intent, CustomTabsIntent.EXTRA_SESSION); - parseHerbExtras(intent); + parseHerbExtras(intent, context); retrieveCustomButtons(intent, context); retrieveToolbarColor(intent, context); @@ -237,6 +252,13 @@ } /** + * @return Whether the bookmark item should be shown in the menu. + */ + public boolean shouldShowBookmarkMenuItem() { + return mShowBookmarkItem; + } + + /** * @return The params for the custom button that shows on the toolbar. If there is no applicable * buttons, returns null. */ @@ -397,13 +419,17 @@ * Parses out extras specifically added for Herb. * * @param intent Intent fired to open the CustomTabActivity. + * @param context Context for the package. */ - private void parseHerbExtras(Intent intent) { + private void parseHerbExtras(Intent intent, Context context) { if (TextUtils.isEmpty(ChromePreferenceManager.getHerbFlavor())) return; + if (!IntentHandler.isIntentChromeOrFirstParty(intent, context)) return; - mFinishAfterOpeningInBrowser = IntentUtils.safeGetBooleanExtra( + mFinishAfterOpeningInBrowser = IntentUtils.safeGetBooleanExtra( intent, EXTRA_FINISH_AFTER_OPENING_IN_BROWSER, false); mIsOpenedByBrowser = IntentUtils.safeGetBooleanExtra( intent, EXTRA_OPENED_BY_BROWSER, false); + mShowBookmarkItem = IntentUtils.safeGetBooleanExtra( + intent, EXTRA_SHOW_STAR_ICON, false); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java index 47c691d5a..aa47784 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java
@@ -326,7 +326,8 @@ if (TextUtils.isEmpty(flavor)) { return false; } else if (TextUtils.equals(flavor, ChromeSwitches.HERB_FLAVOR_ANISE) - || TextUtils.equals(flavor, ChromeSwitches.HERB_FLAVOR_BASIL)) { + || TextUtils.equals(flavor, ChromeSwitches.HERB_FLAVOR_BASIL) + || TextUtils.equals(flavor, ChromeSwitches.HERB_FLAVOR_DILL)) { // Only Intents without NEW_TASK and NEW_DOCUMENT will trigger a Custom Tab. boolean isSameTask = (getIntent().getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0; boolean isSameDocument = @@ -334,8 +335,7 @@ Log.d(TAG, "Herb Intent proprties -- SAME TASK: " + isSameTask + ", SAME DOCUMENT: " + isSameDocument); return isSameTask && isSameDocument; - } else if (TextUtils.equals(flavor, ChromeSwitches.HERB_FLAVOR_CHIVE) - || TextUtils.equals(flavor, ChromeSwitches.HERB_FLAVOR_DILL)) { + } else if (TextUtils.equals(flavor, ChromeSwitches.HERB_FLAVOR_CHIVE)) { // Send all View Intents to the main browser. return false; } else { @@ -367,7 +367,12 @@ context.getResources().getString(R.string.menu_open_in_chrome)); newIntent.putExtra(CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE, herbBundle); + newIntent.putExtra(CustomTabsIntent.EXTRA_DEFAULT_SHARE_MENU_ITEM, true); newIntent.putExtra(CustomTabIntentDataProvider.EXTRA_FINISH_AFTER_OPENING_IN_BROWSER, true); + newIntent.putExtra(CustomTabIntentDataProvider.EXTRA_SHOW_STAR_ICON, true); + + // Mark this as a trusted Chrome Intent. + IntentHandler.addTrustedIntentExtras(newIntent, context); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java index bcc1c44..91bd08b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentActivity.java
@@ -781,7 +781,7 @@ if (hasTabState) return TabLaunchType.FROM_RESTORE; if (isAffiliated) return TabLaunchType.FROM_LONGPRESS_BACKGROUND; if (!TextUtils.isEmpty(url) && url.equals(UrlConstants.NTP_URL)) { - return TabLaunchType.FROM_MENU_OR_OVERVIEW; + return TabLaunchType.FROM_CHROME_UI; } return TabLaunchType.FROM_EXTERNAL_APP; } @@ -792,7 +792,7 @@ new AsyncTabCreationParams(new LoadUrlParams(searchUrl, PageTransition.LINK)); asyncParams.setDocumentStartedBy(DocumentMetricIds.STARTED_BY_CONTEXTUAL_SEARCH); getTabCreator(false).createNewTab( - asyncParams, TabLaunchType.FROM_MENU_OR_OVERVIEW, getActivityTab().getId()); + asyncParams, TabLaunchType.FROM_CHROME_UI, getActivityTab().getId()); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelper.java index 95e3511f..549c5ab 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelper.java
@@ -15,6 +15,7 @@ import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus.ApplicationStateListener; import org.chromium.base.Log; +import org.chromium.base.ThreadUtils; /** * Helps managing connections when using {@link GoogleApiClient}. @@ -72,10 +73,10 @@ private int mResolutionAttempts = 0; private boolean mWasConnectedBefore = false; - private Handler mHandler; + private final Handler mHandler = new Handler(ThreadUtils.getUiThreadLooper()); private final GoogleApiClient mClient; private long mDisconnectionDelayMs = 0; - + private Runnable mPendingDisconnect; /** * Creates a helper and enrolls it in the various connection management features. @@ -93,14 +94,19 @@ * Opts in or out of lifecycle management. The client's connection will be closed and reopened * when Chrome goes in and out of background. * - * Enabling or disabling it while it is already enabled or disabled has no effect. + * It is safe to set it to the current state. Disabling lifecycle management also cancels + * pending disconnections. */ public void enableLifecycleManagement(final boolean enabled) { Log.d(TAG, "enableLifecycleManagement(%s)", enabled); LifecycleHook hook = LifecycleHook.getInstance(); - if (enabled) hook.registerClientHelper(GoogleApiClientHelper.this); - else hook.unregisterClientHelper(GoogleApiClientHelper.this); + if (enabled) { + hook.registerClientHelper(GoogleApiClientHelper.this); + } else { + cancelPendingDisconnection(); + hook.unregisterClientHelper(GoogleApiClientHelper.this); + } } /** @@ -140,23 +146,49 @@ setDisconnectionDelay(0); } + /** + * Tells the helper that we are going to use the connection. It should postpone disconnections + * and make sure the client is connected. + * This is useful if the client might be used when we are in the background. + */ + public void willUseConnection() { + // Cancel and reschedule the disconnection if we are in the background. We do it early to + // avoid race conditions between a disconnect on the UI thread and the connect below. + if (!ApplicationStatus.hasVisibleActivities()) scheduleDisconnection(); + + // The client might be disconnected if we were idle in the background for too long. + if (!mClient.isConnected() && !mClient.isConnecting()) { + Log.d(TAG, "Reconnecting the client."); + mClient.connect(); + } + } + void restoreConnectedState() { + // If we go back to the foreground before a delayed disconnect happens, cancel it. + cancelPendingDisconnection(); + if (mWasConnectedBefore) { mClient.connect(); } } - void disconnectWithDelay() { - new Handler().postDelayed(new Runnable() { + /** + * Schedule a disconnection of the client after the predefined delay. If there was a + * disconnection already planned, it will be rescheduled from now. + */ + void scheduleDisconnection() { + cancelPendingDisconnection(); + + mPendingDisconnect = new Runnable() { @Override public void run() { - // Double check that Chrome is still in the background - boolean skipDisconnect = ApplicationStatus.hasVisibleActivities(); - - Log.d(TAG, "Disconnect delay expired. skipDisconnect=%s", skipDisconnect); - if (!skipDisconnect) disconnect(); + Log.d(TAG, "Disconnect delay expired."); + mPendingDisconnect = null; + disconnect(); } - }, mDisconnectionDelayMs); + }; + + mHandler.postDelayed(mPendingDisconnect, mDisconnectionDelayMs); } private void disconnect() { @@ -168,6 +200,12 @@ mClient.disconnect(); } + private void cancelPendingDisconnection() { + if (mPendingDisconnect == null) return; + + mHandler.removeCallbacks(mPendingDisconnect); + mPendingDisconnect = null; + } @Override public void onConnectionFailed(ConnectionResult result) { @@ -182,7 +220,6 @@ mResolutionAttempts, ConnectedTask.RETRY_NUMBER_LIMIT, result.getErrorCode()); mResolutionAttempts += 1; - if (mHandler == null) mHandler = new Handler(); mHandler.postDelayed(new Runnable() { @Override public void run() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gcore/LifecycleHook.java b/chrome/android/java/src/org/chromium/chrome/browser/gcore/LifecycleHook.java index 777c31ff..dbd7fb2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gcore/LifecycleHook.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gcore/LifecycleHook.java
@@ -84,7 +84,7 @@ synchronized (mClientHelpers) { for (GoogleApiClientHelper clientHelper : mClientHelpers) { if (mIsApplicationVisible) clientHelper.restoreConnectedState(); - else clientHelper.disconnectWithDelay(); + else clientHelper.scheduleDisconnection(); } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/pageinfo/WebsiteSettingsPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/pageinfo/WebsiteSettingsPopup.java index 2a776e9..5dd60b0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/pageinfo/WebsiteSettingsPopup.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/pageinfo/WebsiteSettingsPopup.java
@@ -858,7 +858,4 @@ private static native long nativeInit(WebsiteSettingsPopup popup, WebContents webContents); private native void nativeDestroy(long nativeWebsiteSettingsPopupAndroid); - - private native void nativeOnPermissionSettingChanged(long nativeWebsiteSettingsPopupAndroid, - int type, int setting); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java b/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java index 01ccb13..73583b3a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizations.java
@@ -71,7 +71,7 @@ * to read provider is also considered initialization. */ @VisibleForTesting - static boolean isInitialized() { + public static boolean isInitialized() { return sIsInitialized; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebDiagnosticsPage.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebDiagnosticsPage.java index 48a807f..31dfdd90 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebDiagnosticsPage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PhysicalWebDiagnosticsPage.java
@@ -33,11 +33,16 @@ * Provides diagnostic information about the Physical Web feature. */ public class PhysicalWebDiagnosticsPage implements NativePage { + private static final int RESULT_FAILURE = 0; + private static final int RESULT_SUCCESS = 1; + private static final int RESULT_INDETERMINATE = 2; + private final Context mContext; private final int mBackgroundColor; private final int mThemeColor; private final String mSuccessColor; private final String mFailureColor; + private final String mIndeterminateColor; private final View mPageView; private final Button mLaunchButton; private final TextView mDiagnosticsText; @@ -52,6 +57,8 @@ R.color.physical_web_diags_success_color)); mFailureColor = colorToHexValue(ApiCompatibilityUtils.getColor(resources, R.color.physical_web_diags_failure_color)); + mIndeterminateColor = colorToHexValue(ApiCompatibilityUtils.getColor(resources, + R.color.physical_web_diags_indeterminate_color)); LayoutInflater inflater = LayoutInflater.from(mContext); mPageView = inflater.inflate(R.layout.physical_web_diagnostics, null); @@ -71,9 +78,28 @@ private void appendResult(StringBuilder sb, boolean success, String successMessage, String failureMessage) { - sb.append(String.format("<font color=\"%s\">%s</font><br/>", - success ? mSuccessColor : mFailureColor, - success ? successMessage : failureMessage)); + int successValue = (success ? RESULT_SUCCESS : RESULT_FAILURE); + appendResult(sb, successValue, successMessage, failureMessage, null); + } + + private void appendResult(StringBuilder sb, int successValue, String successMessage, + String failureMessage, String indeterminateMessage) { + String color; + String message; + if (successValue == RESULT_SUCCESS) { + color = mSuccessColor; + message = successMessage; + } else if (successValue == RESULT_FAILURE) { + color = mFailureColor; + message = failureMessage; + } else if (successValue == RESULT_INDETERMINATE) { + color = mIndeterminateColor; + message = indeterminateMessage; + } else { + return; + } + + sb.append(String.format("<font color=\"%s\">%s</font><br/>", color, message)); } private boolean appendSdkVersionReport(StringBuilder sb) { @@ -95,13 +121,24 @@ return isDataConnectionActive; } - private boolean appendBluetoothReport(StringBuilder sb) { - BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); - boolean isBluetoothEnabled = (bt != null && bt.isEnabled()); + private boolean isBluetoothPermissionGranted() { + return PermissionChecker.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH) + == PackageManager.PERMISSION_GRANTED; + } + + private int appendBluetoothReport(StringBuilder sb) { + // Chrome normally does not have the Bluetooth permission, which is required to check + // whether Bluetooth is enabled. Without the Bluetooth permission we must ask the user to + // perform a manual check. + int successValue = RESULT_INDETERMINATE; + if (isBluetoothPermissionGranted()) { + BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); + successValue = (bt != null && bt.isEnabled()) ? RESULT_SUCCESS : RESULT_FAILURE; + } sb.append("Bluetooth: "); - appendResult(sb, isBluetoothEnabled, "Enabled", "Disabled"); - return isBluetoothEnabled; + appendResult(sb, successValue, "Enabled", "Disabled", "Needs verification"); + return successValue; } private boolean appendLocationServicesReport(StringBuilder sb) { @@ -143,7 +180,7 @@ boolean isSdkVersionCorrect = appendSdkVersionReport(sb); boolean isDataConnectionActive = appendDataConnectivityReport(sb); - boolean isBluetoothEnabled = appendBluetoothReport(sb); + int bluetoothStatus = appendBluetoothReport(sb); boolean isLocationProviderAvailable = appendLocationServicesReport(sb); boolean isLocationPermissionGranted = true; if (isAndroidMOrLater) { @@ -151,17 +188,29 @@ } boolean isPreferenceEnabled = appendPhysicalWebPrivacyPreferenceReport(sb); - boolean allSucceeded = isSdkVersionCorrect && isDataConnectionActive && isBluetoothEnabled - && isLocationProviderAvailable && isLocationPermissionGranted - && isPreferenceEnabled; + int successValue = RESULT_SUCCESS; + if (!isSdkVersionCorrect || !isDataConnectionActive || (bluetoothStatus == RESULT_FAILURE) + || !isLocationProviderAvailable || !isLocationPermissionGranted + || !isPreferenceEnabled) { + successValue = RESULT_FAILURE; + } else if (bluetoothStatus == RESULT_INDETERMINATE) { + successValue = RESULT_INDETERMINATE; + } sb.append("<br/>"); - if (allSucceeded) { + if (successValue == RESULT_SUCCESS) { sb.append("All prerequisite checks "); } else { sb.append("One or more prerequisite checks "); } - appendResult(sb, allSucceeded, "SUCCEEDED", "FAILED"); + appendResult(sb, successValue, "SUCCEEDED", "FAILED", "need verification"); + + // Append instructions for how to verify Bluetooth is enabled when we are unable to check + // programmatically. + if (!isBluetoothPermissionGranted()) { + sb.append("<br/>To verify Bluetooth is enabled, check that the Bluetooth icon is " + + "shown in the status bar."); + } } private void appendUrlManagerReport(StringBuilder sb) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ClearBrowsingDataCheckBoxPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ClearBrowsingDataCheckBoxPreference.java new file mode 100644 index 0000000..05072296 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ClearBrowsingDataCheckBoxPreference.java
@@ -0,0 +1,51 @@ +// 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. + +package org.chromium.chrome.browser.preferences; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.chrome.R; + +/** + * A preference representing one browsing data type in ClearBrowsingDataPreferences. + */ +public class ClearBrowsingDataCheckBoxPreference extends ChromeBaseCheckBoxPreference { + + /** + * Constructor for inflating from XML. + */ + public ClearBrowsingDataCheckBoxPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public View onCreateView(ViewGroup parent) { + LinearLayout view = (LinearLayout) super.onCreateView(parent); + + // Checkboxes in the Clear Browsing Data dialog will show and hide the results of + // BrowsingDataCounter. It is important that they will not change height when doing so. + // We will therefore set a fixed height. + int height = getContext().getResources().getDimensionPixelSize( + R.dimen.clear_browsing_data_checkbox_height); + view.setMinimumHeight(height); + + // The title and summary are enclosed in a common RelativeLayout. We must remove + // its vertical padding for it to be correctly vertically centered in the fixed-height view. + View textLayout = (View) view.findViewById(android.R.id.title).getParent(); + ApiCompatibilityUtils.setPaddingRelative( + textLayout, + ApiCompatibilityUtils.getPaddingStart(textLayout), + 0, + ApiCompatibilityUtils.getPaddingEnd(textLayout), + 0); + + return view; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java index 034c90e1..fcacce2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java
@@ -659,6 +659,24 @@ } /** + * Gets the time period for which browsing data will be deleted. + * @return The currently selected browsing data deletion time period (from the shared enum + * {@link org.chromium.chrome.browser.TimePeriod}). + */ + public int getBrowsingDataDeletionTimePeriod() { + return nativeGetBrowsingDataDeletionTimePeriod(); + } + + /** + * Sets the time period for which browsing data will be deleted. + * @param timePeriod The selected browsing data deletion time period (from the shared enum + * {@link org.chromium.chrome.browser.TimePeriod}). + */ + public void setBrowsingDataDeletionTimePeriod(int timePeriod) { + nativeSetBrowsingDataDeletionTimePeriod(timePeriod); + } + + /** * Clear the specified types of browsing data asynchronously. * |listener| is an object to be notified when clearing completes. * It can be null, but many operations (e.g. navigation) are @@ -1000,6 +1018,8 @@ private native void nativeSetJavaScriptAllowed(String pattern, int setting); private native boolean nativeGetBrowsingDataDeletionPreference(int dataType); private native void nativeSetBrowsingDataDeletionPreference(int dataType, boolean value); + private native int nativeGetBrowsingDataDeletionTimePeriod(); + private native void nativeSetBrowsingDataDeletionTimePeriod(int timePeriod); private native void nativeClearBrowsingData(int[] dataTypes); private native boolean nativeCanDeleteBrowsingHistory(); private native void nativeSetAllowCookiesEnabled(boolean allow);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SpinnerPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SpinnerPreference.java new file mode 100644 index 0000000..ff8b6e09 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SpinnerPreference.java
@@ -0,0 +1,87 @@ +// 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. + +package org.chromium.chrome.browser.preferences; + +import android.content.Context; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; + +import org.chromium.chrome.R; + +/** + * A preference that takes value from a specified list of objects, presented as a dropdown. + */ +public class SpinnerPreference extends Preference { + private Spinner mSpinner; + private ArrayAdapter<Object> mAdapter; + private int mSelectedIndex; + + /** + * Constructor for inflating from XML. + */ + public SpinnerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.preference_spinner); + } + + /** + * Provides a list of arbitrary objects to be shown in the spinner. Visually, each option will + * be presented as its toString() text. + * @param options The options to be shown in the spinner. + * @param selectedIndex Index of the initially selected option. + */ + public void setOptions(Object[] options, int selectedIndex) { + mAdapter = new ArrayAdapter<Object>( + getContext(), android.R.layout.simple_spinner_item, options); + mAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mSelectedIndex = selectedIndex; + } + + /** + * @return The currently selected option. + */ + public Object getSelectedOption() { + if (mSpinner == null) return null; + return mSpinner.getSelectedItem(); + } + + @Override + public View onCreateView(ViewGroup parent) { + View view = super.onCreateView(parent); + + ((TextView) view.findViewById(R.id.title)).setText(getTitle()); + mSpinner = (Spinner) view.findViewById(R.id.spinner); + mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected( + AdapterView<?> parent, View view, int position, long id) { + mSelectedIndex = position; + if (getOnPreferenceChangeListener() != null) { + getOnPreferenceChangeListener().onPreferenceChange( + SpinnerPreference.this, getSelectedOption()); + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + // No callback. Only update listeners when an actual option is selected. + } + }); + + return view; + } + + @Override + protected void onBindView(View view) { + mSpinner.setAdapter(mAdapter); + mSpinner.setSelection(mSelectedIndex); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java index 957443b7..49fb94d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java
@@ -4,17 +4,20 @@ package org.chromium.chrome.browser.preferences.privacy; +import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceFragment; +import android.widget.ListView; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; import org.chromium.chrome.browser.BrowsingDataType; import org.chromium.chrome.browser.preferences.ButtonPreference; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.preferences.SpinnerPreference; import org.chromium.chrome.browser.preferences.privacy.BrowsingDataCounterBridge.BrowsingDataCounterCallback; import org.chromium.sync.signin.ChromeSigninController; @@ -27,7 +30,8 @@ */ public class ClearBrowsingDataPreferences extends PreferenceFragment implements PrefServiceBridge.OnClearBrowsingDataListener, - Preference.OnPreferenceClickListener { + Preference.OnPreferenceClickListener, + Preference.OnPreferenceChangeListener{ /** * Represents a single item in the dialog. */ @@ -84,13 +88,14 @@ } private static final String PREF_HISTORY = "clear_history_checkbox"; - private static final String PREF_CACHE = "clear_cache_checkbox"; private static final String PREF_COOKIES = "clear_cookies_checkbox"; + private static final String PREF_CACHE = "clear_cache_checkbox"; private static final String PREF_PASSWORDS = "clear_passwords_checkbox"; private static final String PREF_FORM_DATA = "clear_form_data_checkbox"; private static final String PREF_BOOKMARKS = "clear_bookmarks_checkbox"; private static final String PREF_SUMMARY = "summary"; + private static final String PREF_TIME_RANGE = "time_period_spinner"; /** The "Clear" button preference. Referenced in tests. */ public static final String PREF_CLEAR_BUTTON = "clear_button"; @@ -103,8 +108,8 @@ */ public enum DialogOption { CLEAR_HISTORY(BrowsingDataType.HISTORY, PREF_HISTORY), - CLEAR_CACHE(BrowsingDataType.CACHE, PREF_CACHE), CLEAR_COOKIES_AND_SITE_DATA(BrowsingDataType.COOKIES, PREF_COOKIES), + CLEAR_CACHE(BrowsingDataType.CACHE, PREF_CACHE), CLEAR_PASSWORDS(BrowsingDataType.PASSWORDS, PREF_PASSWORDS), CLEAR_FORM_DATA(BrowsingDataType.FORM_DATA, PREF_FORM_DATA), // Clear bookmarks is only used by ClearSyncData dialog. @@ -130,6 +135,38 @@ } } + /** + * An option to be shown in the time period spiner. + */ + private static class TimePeriodSpinnerOption { + private int mTimePeriod; + private String mTitle; + + /** + * Constructs this time period spinner option. + * @param timePeriod The time period represented as an int from the shared enum + * {@link org.chromium.chrome.browser.TimePeriod}. + * @param title The text that will be used to represent this item in the spinner. + */ + public TimePeriodSpinnerOption(int timePeriod, String title) { + mTimePeriod = timePeriod; + mTitle = title; + } + + /** + * @return The time period represented as an int from the shared enum + * {@link org.chromium.chrome.browser.TimePeriod} + */ + public int getTimePeriod() { + return mTimePeriod; + } + + @Override + public String toString() { + return mTitle; + } + } + private ProgressDialog mProgressDialog; private boolean mCanDeleteBrowsingHistory; private Item[] mItems; @@ -174,13 +211,35 @@ protected DialogOption[] getDialogOptions() { return new DialogOption[] { DialogOption.CLEAR_HISTORY, - DialogOption.CLEAR_CACHE, DialogOption.CLEAR_COOKIES_AND_SITE_DATA, + DialogOption.CLEAR_CACHE, DialogOption.CLEAR_PASSWORDS, DialogOption.CLEAR_FORM_DATA}; } /** + * Returns the Array of time periods. Options are displayed in the same order as they appear + * in the array. + */ + private TimePeriodSpinnerOption[] getTimePeriodSpinnerOptions() { + Activity activity = getActivity(); + + TimePeriodSpinnerOption[] options = new TimePeriodSpinnerOption[] { + new TimePeriodSpinnerOption(org.chromium.chrome.browser.TimePeriod.LAST_HOUR, + activity.getString(R.string.clear_browsing_data_period_hour)), + new TimePeriodSpinnerOption(org.chromium.chrome.browser.TimePeriod.LAST_DAY, + activity.getString(R.string.clear_browsing_data_period_day)), + new TimePeriodSpinnerOption(org.chromium.chrome.browser.TimePeriod.LAST_WEEK, + activity.getString(R.string.clear_browsing_data_period_week)), + new TimePeriodSpinnerOption(org.chromium.chrome.browser.TimePeriod.FOUR_WEEKS, + activity.getString(R.string.clear_browsing_data_period_four_weeks)), + new TimePeriodSpinnerOption(org.chromium.chrome.browser.TimePeriod.EVERYTHING, + activity.getString(R.string.clear_browsing_data_period_everything))}; + + return options; + } + + /** * Decides whether a given dialog option should be selected when the dialog is initialized. * @param option The option in question. * @return boolean Whether the given option should be preselected. @@ -210,6 +269,16 @@ return false; } + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + if (preference.getKey().equals(PREF_TIME_RANGE)) { + PrefServiceBridge.getInstance().setBrowsingDataDeletionTimePeriod( + ((TimePeriodSpinnerOption) value).getTimePeriod()); + return true; + } + return false; + } + /** * Disable the "Clear" button if none of the options are selected. Otherwise, enable it. */ @@ -254,6 +323,22 @@ getPreferenceScreen().removePreference(findPreference(option.getPreferenceKey())); } + // The time range selection spinner. + SpinnerPreference spinner = (SpinnerPreference) findPreference(PREF_TIME_RANGE); + spinner.setOnPreferenceChangeListener(this); + TimePeriodSpinnerOption[] spinnerOptions = getTimePeriodSpinnerOptions(); + int selectedTimePeriod = + PrefServiceBridge.getInstance().getBrowsingDataDeletionTimePeriod(); + int spinnerOptionIndex = -1; + for (int i = 0; i < spinnerOptions.length; ++i) { + if (spinnerOptions[i].getTimePeriod() == selectedTimePeriod) { + spinnerOptionIndex = i; + break; + } + } + assert spinnerOptionIndex != -1; + spinner.setOptions(spinnerOptions, spinnerOptionIndex); + // The "Clear" button. ButtonPreference clearButton = (ButtonPreference) findPreference(PREF_CLEAR_BUTTON); clearButton.setOnPreferenceClickListener(this); @@ -263,9 +348,9 @@ // the user is signed in. Preference summary = findPreference(PREF_SUMMARY); if (ChromeSigninController.get(getActivity()).isSignedIn()) { - summary.setTitle(R.string.clear_browsing_data_footnote); + summary.setSummary(R.string.clear_browsing_data_footnote_signed); } else { - summary.setTitle(R.string.clear_browsing_data_footnote_synced); + summary.setSummary(R.string.clear_browsing_data_footnote); } } @@ -274,6 +359,9 @@ super.onActivityCreated(savedInstanceState); // Now that the dialog's view has been created, update the button state. updateButtonState(); + + // Remove the dividers between checkboxes. + ((ListView) getView().findViewById(android.R.id.list)).setDivider(null); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/BaseColumns.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/BaseColumns.java new file mode 100644 index 0000000..3ee7ae97 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/BaseColumns.java
@@ -0,0 +1,22 @@ +// 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. + +package org.chromium.chrome.browser.provider; + +/** + * Copy of android.provider.BaseColumns. + */ +public interface BaseColumns { + /** + * The unique ID for a row. + * <P>Type: INTEGER (long)</P> + */ + public static final String ID = "_id"; + + /** + * The count of rows in a directory. + * <P>Type: INTEGER</P> + */ + public static final String COUNT = "_count"; +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/BookmarkColumns.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/BookmarkColumns.java new file mode 100644 index 0000000..bf9c7adf --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/BookmarkColumns.java
@@ -0,0 +1,54 @@ +// 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. + +package org.chromium.chrome.browser.provider; + +/** + * Copy of android.provider.Browser.BookmarkColumns. + */ +public class BookmarkColumns implements BaseColumns { + /** + * The URL of the bookmark or history item. + * <p>Type: TEXT (URL)</p> + */ + public static final String URL = "url"; + + /** + * The number of time the item has been visited. + * <p>Type: NUMBER</p> + */ + public static final String VISITS = "visits"; + + /** + * The date the item was last visited, in milliseconds since the epoch. + * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p> + */ + public static final String DATE = "date"; + + /** + * Flag indicating that an item is a bookmark. A value of 1 indicates a bookmark, a value + * of 0 indicates a history item. + * <p>Type: INTEGER (boolean)</p> + */ + public static final String BOOKMARK = "bookmark"; + + /** + * The user visible title of the bookmark or history item. + * <p>Type: TEXT</p> + */ + public static final String TITLE = "title"; + + /** + * The date the item created, in milliseconds since the epoch. + * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p> + */ + public static final String CREATED = "created"; + + /** + * The favicon of the bookmark. Must decode via + * {@link android.graphics.BitmapFactory#decodeByteArray}. + * <p>Type: BLOB (image)</p> + */ + public static final String FAVICON = "favicon"; +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java new file mode 100644 index 0000000..2c7b83e --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java
@@ -0,0 +1,1436 @@ +// Copyright 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. + +package org.chromium.chrome.browser.provider; + +import android.annotation.SuppressLint; +import android.app.SearchManager; +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.UriMatcher; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import android.preference.PreferenceManager; +import android.provider.BaseColumns; +import android.text.TextUtils; +import android.util.Log; +import android.util.LongSparseArray; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.VisibleForTesting; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.CalledByNativeUnchecked; +import org.chromium.base.annotations.SuppressFBWarnings; +import org.chromium.base.library_loader.LibraryProcessType; +import org.chromium.base.library_loader.ProcessInitException; +import org.chromium.chrome.browser.database.SQLiteCursor; +import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; +import org.chromium.chrome.browser.init.ChromeBrowserInitializer; +import org.chromium.content.app.ContentApplication; +import org.chromium.content.browser.BrowserStartupController; +import org.chromium.sync.AndroidSyncSettings; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Vector; + +/** + * This class provides access to user data stored in Chrome, such as bookmarks, most visited pages, + * etc. It is used to support android.provider.Browser. + */ +public class ChromeBrowserProvider extends ContentProvider { + private static final String TAG = "ChromeBrowserProvider"; + + /** + * A projection of {@link #SEARCHES_URI} that contains {@link SearchColumns#ID}, + * {@link SearchColumns#SEARCH}, and {@link SearchColumns#DATE}. + */ + @VisibleForTesting + @SuppressFBWarnings("MS_PKGPROTECT") + public static final String[] SEARCHES_PROJECTION = new String[] { + // if you change column order you must also change indices below + SearchColumns.ID, // 0 + SearchColumns.SEARCH, // 1 + SearchColumns.DATE, // 2 + }; + + /* these indices dependent on SEARCHES_PROJECTION */ + @VisibleForTesting + public static final int SEARCHES_PROJECTION_SEARCH_INDEX = 1; + @VisibleForTesting + public static final int SEARCHES_PROJECTION_DATE_INDEX = 2; + + // The permission required for using the bookmark folders API. Android build system does + // not generate Manifest.java for java libraries, hence use the permission name string. When + // making changes to this permission, also update the permission in AndroidManifest.xml. + private static final String PERMISSION_READ_WRITE_BOOKMARKS = "READ_WRITE_BOOKMARK_FOLDERS"; + + // Defines the API methods that the Client can call by name. + static final String CLIENT_API_BOOKMARK_NODE_EXISTS = "BOOKMARK_NODE_EXISTS"; + static final String CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE = "CREATE_BOOKMARKS_FOLDER_ONCE"; + static final String CLIENT_API_GET_EDITABLE_BOOKMARK_FOLDER_HIERARCHY = + "GET_EDITABLE_BOOKMARK_FOLDER_HIERARCHY"; + static final String CLIENT_API_GET_BOOKMARK_NODE = "GET_BOOKMARK_NODE"; + static final String CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER = "GET_DEFAULT_BOOKMARK_FOLDER"; + static final String CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID = + "GET_MOBILE_BOOKMARKS_FOLDER_ID"; + static final String CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH = + "IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH"; + static final String CLIENT_API_DELETE_ALL_USER_BOOKMARKS = "DELETE_ALL_USER_BOOKMARKS"; + static final String CLIENT_API_RESULT_KEY = "result"; + + + // Defines Chrome's API authority, so it can be run and tested + // independently. + private static final String API_AUTHORITY_SUFFIX = ".browser"; + + private static final String BROWSER_CONTRACT_API_AUTHORITY = + "com.google.android.apps.chrome.browser-contract"; + + // These values are taken from android.provider.BrowserContract.java since + // that class is hidden from the SDK. + private static final String BROWSER_CONTRACT_AUTHORITY = "com.android.browser"; + private static final String BROWSER_CONTRACT_HISTORY_CONTENT_TYPE = + "vnd.android.cursor.dir/browser-history"; + private static final String BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/browser-history"; + private static final String BROWSER_CONTRACT_BOOKMARK_CONTENT_TYPE = + "vnd.android.cursor.dir/bookmark"; + private static final String BROWSER_CONTRACT_BOOKMARK_CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/bookmark"; + private static final String BROWSER_CONTRACT_SEARCH_CONTENT_TYPE = + "vnd.android.cursor.dir/searches"; + private static final String BROWSER_CONTRACT_SEARCH_CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/searches"; + + // This Authority is for internal interface. It's concatenated with + // Context.getPackageName() so that we can install different channels + // SxS and have different authorities. + private static final String AUTHORITY_SUFFIX = ".ChromeBrowserProvider"; + private static final String BOOKMARKS_PATH = "bookmarks"; + private static final String SEARCHES_PATH = "searches"; + private static final String HISTORY_PATH = "history"; + private static final String COMBINED_PATH = "combined"; + private static final String BOOKMARK_FOLDER_PATH = "hierarchy"; + + public static final Uri BROWSER_CONTRACTS_BOOKMAKRS_API_URI = buildContentUri( + BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH); + + public static final Uri BROWSER_CONTRACTS_SEARCHES_API_URI = buildContentUri( + BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH); + + public static final Uri BROWSER_CONTRACTS_HISTORY_API_URI = buildContentUri( + BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH); + + public static final Uri BROWSER_CONTRACTS_COMBINED_API_URI = buildContentUri( + BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH); + + /** The parameter used to specify a bookmark parent ID in ContentValues. */ + public static final String BOOKMARK_PARENT_ID_PARAM = "parentId"; + + /** The parameter used to specify whether this is a bookmark folder. */ + public static final String BOOKMARK_IS_FOLDER_PARAM = "isFolder"; + + /** + * Invalid ID value for the Android ContentProvider API calls. + * The value 0 is intentional: if the ID represents a bookmark node then it's the root node + * and not accessible. Otherwise it represents a SQLite row id, so 0 is also invalid. + */ + public static final long INVALID_CONTENT_PROVIDER_ID = 0; + + // ID used to indicate an invalid id for bookmark nodes. + // Client API queries should use ChromeBrowserProviderClient.INVALID_BOOKMARK_ID. + static final long INVALID_BOOKMARK_ID = -1; + + private static final String LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY = "last_bookmark_folder_id"; + + private static final int URI_MATCH_BOOKMARKS = 0; + private static final int URI_MATCH_BOOKMARKS_ID = 1; + private static final int URL_MATCH_API_BOOKMARK = 2; + private static final int URL_MATCH_API_BOOKMARK_ID = 3; + private static final int URL_MATCH_API_SEARCHES = 4; + private static final int URL_MATCH_API_SEARCHES_ID = 5; + private static final int URL_MATCH_API_HISTORY_CONTENT = 6; + private static final int URL_MATCH_API_HISTORY_CONTENT_ID = 7; + private static final int URL_MATCH_API_BOOKMARK_CONTENT = 8; + private static final int URL_MATCH_API_BOOKMARK_CONTENT_ID = 9; + private static final int URL_MATCH_BOOKMARK_SUGGESTIONS_ID = 10; + private static final int URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID = 11; + + // TODO : Using Android.provider.Browser.HISTORY_PROJECTION once THUMBNAIL, + // TOUCH_ICON, and USER_ENTERED fields are supported. + private static final String[] BOOKMARK_DEFAULT_PROJECTION = new String[] { + BookmarkColumns.ID, BookmarkColumns.URL, BookmarkColumns.VISITS, + BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE, + BookmarkColumns.FAVICON, BookmarkColumns.CREATED + }; + + private static final String[] SUGGEST_PROJECTION = new String[] { + BookmarkColumns.ID, + BookmarkColumns.TITLE, + BookmarkColumns.URL, + BookmarkColumns.DATE, + BookmarkColumns.BOOKMARK + }; + + private final Object mInitializeUriMatcherLock = new Object(); + private final Object mLoadNativeLock = new Object(); + private UriMatcher mUriMatcher; + private long mLastModifiedBookmarkFolderId = INVALID_BOOKMARK_ID; + private long mNativeChromeBrowserProvider; + private BookmarkNode mMobileBookmarksFolder; + + private void ensureUriMatcherInitialized() { + synchronized (mInitializeUriMatcherLock) { + if (mUriMatcher != null) return; + + mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + // The internal URIs + String authority = getContext().getPackageName() + AUTHORITY_SUFFIX; + mUriMatcher.addURI(authority, BOOKMARKS_PATH, URI_MATCH_BOOKMARKS); + mUriMatcher.addURI(authority, BOOKMARKS_PATH + "/#", URI_MATCH_BOOKMARKS_ID); + // The internal authority for public APIs + String apiAuthority = getContext().getPackageName() + API_AUTHORITY_SUFFIX; + mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK); + mUriMatcher.addURI(apiAuthority, BOOKMARKS_PATH + "/#", URL_MATCH_API_BOOKMARK_ID); + mUriMatcher.addURI(apiAuthority, SEARCHES_PATH, URL_MATCH_API_SEARCHES); + mUriMatcher.addURI(apiAuthority, SEARCHES_PATH + "/#", URL_MATCH_API_SEARCHES_ID); + mUriMatcher.addURI(apiAuthority, HISTORY_PATH, URL_MATCH_API_HISTORY_CONTENT); + mUriMatcher.addURI(apiAuthority, HISTORY_PATH + "/#", URL_MATCH_API_HISTORY_CONTENT_ID); + mUriMatcher.addURI(apiAuthority, COMBINED_PATH, URL_MATCH_API_BOOKMARK); + mUriMatcher.addURI(apiAuthority, COMBINED_PATH + "/#", URL_MATCH_API_BOOKMARK_ID); + // The internal authority for BrowserContracts + mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH, + URL_MATCH_API_HISTORY_CONTENT); + mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, HISTORY_PATH + "/#", + URL_MATCH_API_HISTORY_CONTENT_ID); + mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH, + URL_MATCH_API_BOOKMARK); + mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, COMBINED_PATH + "/#", + URL_MATCH_API_BOOKMARK_ID); + mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH, + URL_MATCH_API_SEARCHES); + mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, SEARCHES_PATH + "/#", + URL_MATCH_API_SEARCHES_ID); + mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH, + URL_MATCH_API_BOOKMARK_CONTENT); + mUriMatcher.addURI(BROWSER_CONTRACT_API_AUTHORITY, BOOKMARKS_PATH + "/#", + URL_MATCH_API_BOOKMARK_CONTENT_ID); + // Added the Android Framework URIs, so the provider can easily switched + // by adding 'browser' and 'com.android.browser' in manifest. + // The Android's BrowserContract + mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH, + URL_MATCH_API_HISTORY_CONTENT); + mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, HISTORY_PATH + "/#", + URL_MATCH_API_HISTORY_CONTENT_ID); + mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined", URL_MATCH_API_BOOKMARK); + mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, "combined/#", URL_MATCH_API_BOOKMARK_ID); + mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH, URL_MATCH_API_SEARCHES); + mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, SEARCHES_PATH + "/#", + URL_MATCH_API_SEARCHES_ID); + mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH, + URL_MATCH_API_BOOKMARK_CONTENT); + mUriMatcher.addURI(BROWSER_CONTRACT_AUTHORITY, BOOKMARKS_PATH + "/#", + URL_MATCH_API_BOOKMARK_CONTENT_ID); + // For supporting android.provider.browser.BookmarkColumns and + // SearchColumns + mUriMatcher.addURI("browser", BOOKMARKS_PATH, URL_MATCH_API_BOOKMARK); + mUriMatcher.addURI("browser", BOOKMARKS_PATH + "/#", URL_MATCH_API_BOOKMARK_ID); + mUriMatcher.addURI("browser", SEARCHES_PATH, URL_MATCH_API_SEARCHES); + mUriMatcher.addURI("browser", SEARCHES_PATH + "/#", URL_MATCH_API_SEARCHES_ID); + + mUriMatcher.addURI(apiAuthority, + BOOKMARKS_PATH + "/" + SearchManager.SUGGEST_URI_PATH_QUERY, + URL_MATCH_BOOKMARK_SUGGESTIONS_ID); + mUriMatcher.addURI(apiAuthority, + SearchManager.SUGGEST_URI_PATH_QUERY, + URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID); + } + } + + @Override + public boolean onCreate() { + ContentApplication.initCommandLine(getContext()); + + BrowserStartupController.get(getContext(), LibraryProcessType.PROCESS_BROWSER) + .addStartupCompletedObserver( + new BrowserStartupController.StartupCallback() { + @Override + public void onSuccess(boolean alreadyStarted) { + ensureNativeSideInitialized(); + } + + @Override + public void onFailure() { + } + }); + + // Pre-load shared preferences object, this happens on a separate thread + PreferenceManager.getDefaultSharedPreferences(getContext()); + return true; + } + + /** + * Lazily fetches the last modified bookmark folder id. + */ + private long getLastModifiedBookmarkFolderId() { + if (mLastModifiedBookmarkFolderId == INVALID_BOOKMARK_ID) { + SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(getContext()); + mLastModifiedBookmarkFolderId = sharedPreferences.getLong( + LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, INVALID_BOOKMARK_ID); + } + return mLastModifiedBookmarkFolderId; + } + + private String buildSuggestWhere(String selection, int argc) { + StringBuilder sb = new StringBuilder(selection); + for (int i = 0; i < argc - 1; i++) { + sb.append(" OR "); + sb.append(selection); + } + return sb.toString(); + } + + private String getReadWritePermissionNameForBookmarkFolders() { + return getContext().getApplicationContext().getPackageName() + ".permission." + + PERMISSION_READ_WRITE_BOOKMARKS; + } + + private Cursor getBookmarkHistorySuggestions(String selection, String[] selectionArgs, + String sortOrder, boolean excludeHistory) { + boolean matchTitles = false; + Vector<String> args = new Vector<String>(); + String like = selectionArgs[0] + "%"; + if (selectionArgs[0].startsWith("http") || selectionArgs[0].startsWith("file")) { + args.add(like); + } else { + // Match against common URL prefixes. + args.add("http://" + like); + args.add("https://" + like); + args.add("http://www." + like); + args.add("https://www." + like); + args.add("file://" + like); + matchTitles = true; + } + + StringBuilder urlWhere = new StringBuilder("("); + urlWhere.append(buildSuggestWhere(selection, args.size())); + if (matchTitles) { + args.add(like); + urlWhere.append(" OR title LIKE ?"); + } + urlWhere.append(")"); + + if (excludeHistory) { + urlWhere.append(" AND bookmark=?"); + args.add("1"); + } + + selectionArgs = args.toArray(selectionArgs); + Cursor cursor = queryBookmarkFromAPI(SUGGEST_PROJECTION, urlWhere.toString(), + selectionArgs, sortOrder); + return new ChromeBrowserProviderSuggestionsCursor(cursor); + } + + /** + * @see android.content.ContentUris#parseId(Uri) + * @return The id from a content URI or -1 if the URI has no id or is malformed. + */ + private static long getContentUriId(Uri uri) { + try { + return ContentUris.parseId(uri); + } catch (UnsupportedOperationException e) { + return -1; + } catch (NumberFormatException e) { + return -1; + } + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + if (!canHandleContentProviderApiCall()) return null; + + // Starting with M, other apps are no longer allowed to access bookmarks. But returning null + // might break old apps, so return an empty Cursor instead. + if (!hasReadAccess()) return new MatrixCursor(BOOKMARK_DEFAULT_PROJECTION, 0); + + // Check for invalid id values if provided. + long bookmarkId = getContentUriId(uri); + if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return null; + + int match = mUriMatcher.match(uri); + Cursor cursor = null; + switch (match) { + case URL_MATCH_BOOKMARK_SUGGESTIONS_ID: + cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, true); + break; + case URL_MATCH_BOOKMARK_HISTORY_SUGGESTIONS_ID: + cursor = getBookmarkHistorySuggestions(selection, selectionArgs, sortOrder, false); + break; + case URL_MATCH_API_BOOKMARK: + cursor = queryBookmarkFromAPI(projection, selection, selectionArgs, sortOrder); + break; + case URL_MATCH_API_BOOKMARK_ID: + cursor = queryBookmarkFromAPI(projection, buildWhereClause(bookmarkId, selection), + selectionArgs, sortOrder); + break; + case URL_MATCH_API_SEARCHES: + cursor = querySearchTermFromAPI(projection, selection, selectionArgs, sortOrder); + break; + case URL_MATCH_API_SEARCHES_ID: + cursor = querySearchTermFromAPI(projection, buildWhereClause(bookmarkId, selection), + selectionArgs, sortOrder); + break; + case URL_MATCH_API_HISTORY_CONTENT: + cursor = queryBookmarkFromAPI(projection, buildHistoryWhereClause(selection), + selectionArgs, sortOrder); + break; + case URL_MATCH_API_HISTORY_CONTENT_ID: + cursor = queryBookmarkFromAPI(projection, + buildHistoryWhereClause(bookmarkId, selection), selectionArgs, sortOrder); + break; + case URL_MATCH_API_BOOKMARK_CONTENT: + cursor = queryBookmarkFromAPI(projection, buildBookmarkWhereClause(selection), + selectionArgs, sortOrder); + break; + case URL_MATCH_API_BOOKMARK_CONTENT_ID: + cursor = queryBookmarkFromAPI(projection, + buildBookmarkWhereClause(bookmarkId, selection), selectionArgs, sortOrder); + break; + default: + throw new IllegalArgumentException(TAG + ": query - unknown URL uri = " + uri); + } + if (cursor == null) { + cursor = new MatrixCursor(new String[] { }); + } + cursor.setNotificationUri(getContext().getContentResolver(), uri); + return cursor; + } + + @Override + @SuppressFBWarnings("SF_SWITCH_FALLTHROUGH") + public Uri insert(Uri uri, ContentValues values) { + if (!canHandleContentProviderApiCall() || !hasWriteAccess()) return null; + + int match = mUriMatcher.match(uri); + Uri res = null; + long id; + switch (match) { + case URI_MATCH_BOOKMARKS: + id = addBookmark(values); + if (id == INVALID_BOOKMARK_ID) return null; + break; + case URL_MATCH_API_BOOKMARK_CONTENT: + values.put(BookmarkColumns.BOOKMARK, 1); + //$FALL-THROUGH$ + case URL_MATCH_API_BOOKMARK: + case URL_MATCH_API_HISTORY_CONTENT: + id = addBookmarkFromAPI(values); + if (id == INVALID_CONTENT_PROVIDER_ID) return null; + break; + case URL_MATCH_API_SEARCHES: + id = addSearchTermFromAPI(values); + if (id == INVALID_CONTENT_PROVIDER_ID) return null; + break; + default: + throw new IllegalArgumentException(TAG + ": insert - unknown URL " + uri); + } + + res = ContentUris.withAppendedId(uri, id); + notifyChange(res); + return res; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + if (!canHandleContentProviderApiCall() || !hasWriteAccess()) return 0; + + // Check for invalid id values if provided. + long bookmarkId = getContentUriId(uri); + if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0; + + int match = mUriMatcher.match(uri); + int result; + switch (match) { + case URI_MATCH_BOOKMARKS_ID : + result = nativeRemoveBookmark(mNativeChromeBrowserProvider, bookmarkId); + break; + case URL_MATCH_API_BOOKMARK_ID: + result = removeBookmarkFromAPI( + buildWhereClause(bookmarkId, selection), selectionArgs); + break; + case URL_MATCH_API_BOOKMARK: + result = removeBookmarkFromAPI(selection, selectionArgs); + break; + case URL_MATCH_API_SEARCHES_ID: + result = removeSearchFromAPI(buildWhereClause(bookmarkId, selection), + selectionArgs); + break; + case URL_MATCH_API_SEARCHES: + result = removeSearchFromAPI(selection, selectionArgs); + break; + case URL_MATCH_API_HISTORY_CONTENT: + result = removeHistoryFromAPI(selection, selectionArgs); + break; + case URL_MATCH_API_HISTORY_CONTENT_ID: + result = removeHistoryFromAPI(buildWhereClause(bookmarkId, selection), + selectionArgs); + break; + case URL_MATCH_API_BOOKMARK_CONTENT: + result = removeBookmarkFromAPI(buildBookmarkWhereClause(selection), selectionArgs); + break; + case URL_MATCH_API_BOOKMARK_CONTENT_ID: + result = removeBookmarkFromAPI(buildBookmarkWhereClause(bookmarkId, selection), + selectionArgs); + break; + default: + throw new IllegalArgumentException(TAG + ": delete - unknown URL " + uri); + } + if (result != 0) notifyChange(uri); + return result; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + if (!canHandleContentProviderApiCall() || !hasWriteAccess()) return 0; + + // Check for invalid id values if provided. + long bookmarkId = getContentUriId(uri); + if (bookmarkId == INVALID_CONTENT_PROVIDER_ID) return 0; + + int match = mUriMatcher.match(uri); + int result; + switch (match) { + case URI_MATCH_BOOKMARKS_ID: + String url = null; + if (values.containsKey(BookmarkColumns.URL)) { + url = values.getAsString(BookmarkColumns.URL); + } + String title = values.getAsString(BookmarkColumns.TITLE); + long parentId = INVALID_BOOKMARK_ID; + if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) { + parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM); + } + result = nativeUpdateBookmark(mNativeChromeBrowserProvider, bookmarkId, url, title, + parentId); + updateLastModifiedBookmarkFolder(parentId); + break; + case URL_MATCH_API_BOOKMARK_ID: + result = updateBookmarkFromAPI(values, buildWhereClause(bookmarkId, selection), + selectionArgs); + break; + case URL_MATCH_API_BOOKMARK: + result = updateBookmarkFromAPI(values, selection, selectionArgs); + break; + case URL_MATCH_API_SEARCHES_ID: + result = updateSearchTermFromAPI(values, buildWhereClause(bookmarkId, selection), + selectionArgs); + break; + case URL_MATCH_API_SEARCHES: + result = updateSearchTermFromAPI(values, selection, selectionArgs); + break; + case URL_MATCH_API_HISTORY_CONTENT: + result = updateBookmarkFromAPI(values, buildHistoryWhereClause(selection), + selectionArgs); + break; + case URL_MATCH_API_HISTORY_CONTENT_ID: + result = updateBookmarkFromAPI(values, + buildHistoryWhereClause(bookmarkId, selection), selectionArgs); + break; + case URL_MATCH_API_BOOKMARK_CONTENT: + result = updateBookmarkFromAPI(values, buildBookmarkWhereClause(selection), + selectionArgs); + break; + case URL_MATCH_API_BOOKMARK_CONTENT_ID: + result = updateBookmarkFromAPI(values, + buildBookmarkWhereClause(bookmarkId, selection), selectionArgs); + break; + default: + throw new IllegalArgumentException(TAG + ": update - unknown URL " + uri); + } + if (result != 0) notifyChange(uri); + return result; + } + + @Override + public String getType(Uri uri) { + ensureUriMatcherInitialized(); + int match = mUriMatcher.match(uri); + switch (match) { + case URI_MATCH_BOOKMARKS: + case URL_MATCH_API_BOOKMARK: + return BROWSER_CONTRACT_BOOKMARK_CONTENT_TYPE; + case URI_MATCH_BOOKMARKS_ID: + case URL_MATCH_API_BOOKMARK_ID: + return BROWSER_CONTRACT_BOOKMARK_CONTENT_ITEM_TYPE; + case URL_MATCH_API_SEARCHES: + return BROWSER_CONTRACT_SEARCH_CONTENT_TYPE; + case URL_MATCH_API_SEARCHES_ID: + return BROWSER_CONTRACT_SEARCH_CONTENT_ITEM_TYPE; + case URL_MATCH_API_HISTORY_CONTENT: + return BROWSER_CONTRACT_HISTORY_CONTENT_TYPE; + case URL_MATCH_API_HISTORY_CONTENT_ID: + return BROWSER_CONTRACT_HISTORY_CONTENT_ITEM_TYPE; + default: + throw new IllegalArgumentException(TAG + ": getType - unknown URL " + uri); + } + } + + private long addBookmark(ContentValues values) { + String url = values.getAsString(BookmarkColumns.URL); + String title = values.getAsString(BookmarkColumns.TITLE); + boolean isFolder = false; + if (values.containsKey(BOOKMARK_IS_FOLDER_PARAM)) { + isFolder = values.getAsBoolean(BOOKMARK_IS_FOLDER_PARAM); + } + long parentId = INVALID_BOOKMARK_ID; + if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) { + parentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM); + } + long id = nativeAddBookmark(mNativeChromeBrowserProvider, url, title, isFolder, parentId); + if (id == INVALID_BOOKMARK_ID) return id; + + if (isFolder) { + updateLastModifiedBookmarkFolder(id); + } else { + updateLastModifiedBookmarkFolder(parentId); + } + return id; + } + + private void updateLastModifiedBookmarkFolder(long id) { + if (getLastModifiedBookmarkFolderId() == id) return; + + mLastModifiedBookmarkFolderId = id; + SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(getContext()); + sharedPreferences.edit() + .putLong(LAST_MODIFIED_BOOKMARK_FOLDER_ID_KEY, mLastModifiedBookmarkFolderId) + .apply(); + } + + public static String getApiAuthority(Context context) { + return context.getPackageName() + API_AUTHORITY_SUFFIX; + } + + public static String getInternalAuthority(Context context) { + return context.getPackageName() + AUTHORITY_SUFFIX; + } + + public static Uri getBookmarksUri(Context context) { + return buildContentUri(getInternalAuthority(context), BOOKMARKS_PATH); + } + + public static Uri getBookmarkFolderUri(Context context) { + return buildContentUri(getInternalAuthority(context), BOOKMARK_FOLDER_PATH); + } + + public static Uri getBookmarksApiUri(Context context) { + return buildContentUri(getApiAuthority(context), BOOKMARKS_PATH); + } + + @VisibleForTesting + public static Uri getSearchesApiUri(Context context) { + return buildContentUri(getApiAuthority(context), SEARCHES_PATH); + } + + private boolean bookmarkNodeExists(long nodeId) { + if (nodeId < 0) return false; + return nativeBookmarkNodeExists(mNativeChromeBrowserProvider, nodeId); + } + + private long createBookmarksFolderOnce(String title, long parentId) { + return nativeCreateBookmarksFolderOnce(mNativeChromeBrowserProvider, title, parentId); + } + + private BookmarkNode getEditableBookmarkFolderHierarchy() { + return nativeGetEditableBookmarkFolders(mNativeChromeBrowserProvider); + } + + private BookmarkNode getBookmarkNode(long nodeId, boolean getParent, boolean getChildren, + boolean getFavicons, boolean getThumbnails) { + // Don't allow going up the hierarchy if sync is disabled and the requested node + // is the Mobile Bookmarks folder. + if (getParent && nodeId == getMobileBookmarksFolderId() + && !AndroidSyncSettings.isSyncEnabled(getContext())) { + getParent = false; + } + + BookmarkNode node = nativeGetBookmarkNode(mNativeChromeBrowserProvider, nodeId, getParent, + getChildren); + if (!getFavicons && !getThumbnails) return node; + + // Favicons and thumbnails need to be populated separately as they are provided + // asynchronously by Chromium services other than the bookmark model. + if (node.parent() != null) populateNodeImages(node.parent(), getFavicons, getThumbnails); + for (BookmarkNode child : node.children()) { + populateNodeImages(child, getFavicons, getThumbnails); + } + + return node; + } + + private BookmarkNode getDefaultBookmarkFolder() { + // Try to access the bookmark folder last modified by us. If it doesn't exist anymore + // then use the synced node (Mobile Bookmarks). + BookmarkNode lastModified = getBookmarkNode(getLastModifiedBookmarkFolderId(), false, false, + false, false); + if (lastModified == null || lastModified.isUrl()) { + lastModified = getMobileBookmarksFolder(); + mLastModifiedBookmarkFolderId = lastModified != null ? lastModified.id() : + INVALID_BOOKMARK_ID; + } + return lastModified; + } + + private void populateNodeImages(BookmarkNode node, boolean favicon, boolean thumbnail) { + if (node == null || node.type() != Type.URL) return; + + if (favicon) { + node.setFavicon(nativeGetFaviconOrTouchIcon(mNativeChromeBrowserProvider, node.url())); + } + + if (thumbnail) { + node.setThumbnail(nativeGetThumbnail(mNativeChromeBrowserProvider, node.url())); + } + } + + private BookmarkNode getMobileBookmarksFolder() { + if (mMobileBookmarksFolder == null) { + mMobileBookmarksFolder = nativeGetMobileBookmarksFolder(mNativeChromeBrowserProvider); + } + return mMobileBookmarksFolder; + } + + private long getMobileBookmarksFolderId() { + BookmarkNode mobileBookmarks = getMobileBookmarksFolder(); + return mobileBookmarks != null ? mobileBookmarks.id() : INVALID_BOOKMARK_ID; + } + + private boolean isBookmarkInMobileBookmarksBranch(long nodeId) { + if (nodeId <= 0) return false; + return nativeIsBookmarkInMobileBookmarksBranch(mNativeChromeBrowserProvider, nodeId); + } + + static String argKey(int i) { + return "arg" + i; + } + + @Override + public Bundle call(String method, String arg, Bundle extras) { + // TODO(shashishekhar): Refactor this code into a separate class. + // Caller must have the READ_WRITE_BOOKMARK_FOLDERS permission. + getContext().enforcePermission(getReadWritePermissionNameForBookmarkFolders(), + Binder.getCallingPid(), Binder.getCallingUid(), TAG); + if (!canHandleContentProviderApiCall()) return null; + if (method == null || extras == null) return null; + + Bundle result = new Bundle(); + if (CLIENT_API_BOOKMARK_NODE_EXISTS.equals(method)) { + result.putBoolean(CLIENT_API_RESULT_KEY, + bookmarkNodeExists(extras.getLong(argKey(0)))); + } else if (CLIENT_API_CREATE_BOOKMARKS_FOLDER_ONCE.equals(method)) { + result.putLong(CLIENT_API_RESULT_KEY, + createBookmarksFolderOnce(extras.getString(argKey(0)), + extras.getLong(argKey(1)))); + } else if (CLIENT_API_GET_EDITABLE_BOOKMARK_FOLDER_HIERARCHY.equals(method)) { + result.putParcelable(CLIENT_API_RESULT_KEY, getEditableBookmarkFolderHierarchy()); + } else if (CLIENT_API_GET_BOOKMARK_NODE.equals(method)) { + result.putParcelable(CLIENT_API_RESULT_KEY, + getBookmarkNode(extras.getLong(argKey(0)), + extras.getBoolean(argKey(1)), + extras.getBoolean(argKey(2)), + extras.getBoolean(argKey(3)), + extras.getBoolean(argKey(4)))); + } else if (CLIENT_API_GET_DEFAULT_BOOKMARK_FOLDER.equals(method)) { + result.putParcelable(CLIENT_API_RESULT_KEY, getDefaultBookmarkFolder()); + } else if (method.equals(CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID)) { + result.putLong(CLIENT_API_RESULT_KEY, getMobileBookmarksFolderId()); + } else if (CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH.equals(method)) { + result.putBoolean(CLIENT_API_RESULT_KEY, + isBookmarkInMobileBookmarksBranch(extras.getLong(argKey(0)))); + } else if (CLIENT_API_DELETE_ALL_USER_BOOKMARKS.equals(method)) { + android.util.Log.i(TAG, "before nativeRemoveAllUserBookmarks"); + nativeRemoveAllUserBookmarks(mNativeChromeBrowserProvider); + android.util.Log.i(TAG, "after nativeRemoveAllUserBookmarks"); + } else { + Log.w(TAG, "Received invalid method " + method); + return null; + } + + return result; + } + + /** + * Checks whether Chrome is sufficiently initialized to handle a call to the + * ChromeBrowserProvider. + */ + private boolean canHandleContentProviderApiCall() { + if (isInUiThread()) return false; + ensureUriMatcherInitialized(); + if (mNativeChromeBrowserProvider != 0) return true; + synchronized (mLoadNativeLock) { + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + @SuppressFBWarnings("DM_EXIT") + public void run() { + if (mNativeChromeBrowserProvider != 0) return; + try { + ChromeBrowserInitializer.getInstance(getContext()) + .handleSynchronousStartup(); + } catch (ProcessInitException e) { + // Chrome browser runs in the background, so exit silently; but do exit, + // since otherwise the next attempt to use Chrome will find a broken JNI. + System.exit(-1); + } + ensureNativeSideInitialized(); + } + }); + } + return true; + } + + /** + * @return Whether the caller has read access to history and bookmarks information. + */ + private boolean hasReadAccess() { + return hasPermission("com.android.browser.permission.READ_HISTORY_BOOKMARKS"); + } + + /** + * @return Whether the caller has write access to history and bookmarks information. + */ + private boolean hasWriteAccess() { + return hasPermission("com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"); + } + + /** + * The type of a BookmarkNode. + */ + public enum Type { + URL, + FOLDER, + BOOKMARK_BAR, + OTHER_NODE, + MOBILE + } + + /** + * Simple Data Object representing the chrome bookmark node. + */ + public static class BookmarkNode implements Parcelable { + private final long mId; + private final String mName; + private final String mUrl; + private final Type mType; + private final BookmarkNode mParent; + private final List<BookmarkNode> mChildren = new ArrayList<BookmarkNode>(); + + // Favicon and thumbnail optionally set in a 2-step procedure. + private byte[] mFavicon; + private byte[] mThumbnail; + + /** Used to pass structured data back from the native code. */ + @VisibleForTesting + public BookmarkNode(long id, Type type, String name, String url, BookmarkNode parent) { + mId = id; + mName = name; + mUrl = url; + mType = type; + mParent = parent; + } + + /** + * @return The id of this bookmark entry. + */ + public long id() { + return mId; + } + + /** + * @return The name of this bookmark entry. + */ + public String name() { + return mName; + } + + /** + * @return The URL of this bookmark entry. + */ + public String url() { + return mUrl; + } + + /** + * @return The type of this bookmark entry. + */ + public Type type() { + return mType; + } + + /** + * @return The bookmark favicon, if any. + */ + @SuppressFBWarnings("EI_EXPOSE_REP") + public byte[] favicon() { + return mFavicon; + } + + /** + * @return The bookmark thumbnail, if any. + */ + @SuppressFBWarnings("EI_EXPOSE_REP") + public byte[] thumbnail() { + return mThumbnail; + } + + /** + * @return The parent folder of this bookmark entry. + */ + public BookmarkNode parent() { + return mParent; + } + + /** + * Adds a child to this node. + * + * <p> + * Used solely by the native code. + */ + @VisibleForTesting + @CalledByNativeUnchecked("BookmarkNode") + public void addChild(BookmarkNode child) { + mChildren.add(child); + } + + /** + * @return The child bookmark nodes of this node. + */ + public List<BookmarkNode> children() { + return mChildren; + } + + /** + * @return Whether this node represents a bookmarked URL or not. + */ + public boolean isUrl() { + return mUrl != null; + } + + /** + * @return true if the two individual nodes contain the same information. + * The existence of parent and children nodes is checked, but their contents are not. + */ + @VisibleForTesting + public boolean equalContents(BookmarkNode node) { + return node != null + && mId == node.mId + && !(mName == null ^ node.mName == null) + && (mName == null || mName.equals(node.mName)) + && !(mUrl == null ^ node.mUrl == null) + && (mUrl == null || mUrl.equals(node.mUrl)) + && mType == node.mType + && byteArrayEqual(mFavicon, node.mFavicon) + && byteArrayEqual(mThumbnail, node.mThumbnail) + && !(mParent == null ^ node.mParent == null) + && children().size() == node.children().size(); + } + + private static boolean byteArrayEqual(byte[] byte1, byte[] byte2) { + if (byte1 == null && byte2 != null) return byte2.length == 0; + if (byte2 == null && byte1 != null) return byte1.length == 0; + return Arrays.equals(byte1, byte2); + } + + @CalledByNative("BookmarkNode") + private static BookmarkNode create( + long id, int type, String name, String url, BookmarkNode parent) { + return new BookmarkNode(id, Type.values()[type], name, url, parent); + } + + @VisibleForTesting + @SuppressFBWarnings("EI_EXPOSE_REP2") + public void setFavicon(byte[] favicon) { + mFavicon = favicon; + } + + @VisibleForTesting + @SuppressFBWarnings("EI_EXPOSE_REP2") + public void setThumbnail(byte[] thumbnail) { + mThumbnail = thumbnail; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + // Write the current node id. + dest.writeLong(mId); + + // Serialize the full hierarchy from the root. + getHierarchyRoot().writeNodeContentsRecursive(dest); + } + + @VisibleForTesting + public BookmarkNode getHierarchyRoot() { + BookmarkNode root = this; + while (root.parent() != null) { + root = root.parent(); + } + return root; + } + + private void writeNodeContentsRecursive(Parcel dest) { + writeNodeContents(dest); + dest.writeInt(mChildren.size()); + for (BookmarkNode child : mChildren) { + child.writeNodeContentsRecursive(dest); + } + } + + private void writeNodeContents(Parcel dest) { + dest.writeLong(mId); + dest.writeString(mName); + dest.writeString(mUrl); + dest.writeInt(mType.ordinal()); + dest.writeByteArray(mFavicon); + dest.writeByteArray(mThumbnail); + dest.writeLong(mParent != null ? mParent.mId : INVALID_BOOKMARK_ID); + } + + public static final Creator<BookmarkNode> CREATOR = new Creator<BookmarkNode>() { + private LongSparseArray<BookmarkNode> mNodeMap; + + @Override + public BookmarkNode createFromParcel(Parcel source) { + mNodeMap = new LongSparseArray<>(); + long currentNodeId = source.readLong(); + readNodeContentsRecursive(source); + BookmarkNode node = getNode(currentNodeId); + mNodeMap.clear(); + return node; + } + + @Override + public BookmarkNode[] newArray(int size) { + return new BookmarkNode[size]; + } + + private BookmarkNode getNode(long id) { + if (id == INVALID_BOOKMARK_ID) return null; + Long nodeId = Long.valueOf(id); + if (mNodeMap.indexOfKey(nodeId) < 0) { + Log.e(TAG, "Invalid BookmarkNode hierarchy. Unknown id " + id); + return null; + } + return mNodeMap.get(nodeId); + } + + private BookmarkNode readNodeContents(Parcel source) { + long id = source.readLong(); + String name = source.readString(); + String url = source.readString(); + int type = source.readInt(); + byte[] favicon = source.createByteArray(); + byte[] thumbnail = source.createByteArray(); + long parentId = source.readLong(); + if (type < 0 || type >= Type.values().length) { + Log.w(TAG, "Invalid node type ordinal value."); + return null; + } + + BookmarkNode node = new BookmarkNode(id, Type.values()[type], name, url, + getNode(parentId)); + node.setFavicon(favicon); + node.setThumbnail(thumbnail); + return node; + } + + private BookmarkNode readNodeContentsRecursive(Parcel source) { + BookmarkNode node = readNodeContents(source); + if (node == null) return null; + + Long nodeId = Long.valueOf(node.id()); + if (mNodeMap.indexOfKey(nodeId) >= 0) { + Log.e(TAG, "Invalid BookmarkNode hierarchy. Duplicate id " + node.id()); + return null; + } + mNodeMap.put(nodeId, node); + + int numChildren = source.readInt(); + for (int i = 0; i < numChildren; ++i) { + node.addChild(readNodeContentsRecursive(source)); + } + + return node; + } + }; + } + + private long addBookmarkFromAPI(ContentValues values) { + BookmarkRow row = BookmarkRow.fromContentValues(values); + if (row.mUrl == null) { + throw new IllegalArgumentException("Must have a bookmark URL"); + } + return nativeAddBookmarkFromAPI(mNativeChromeBrowserProvider, + row.mUrl, row.mCreated, row.mIsBookmark, row.mDate, row.mFavicon, + row.mTitle, row.mVisits, row.mParentId); + } + + private Cursor queryBookmarkFromAPI(String[] projectionIn, String selection, + String[] selectionArgs, String sortOrder) { + String[] projection = null; + if (projectionIn == null || projectionIn.length == 0) { + projection = BOOKMARK_DEFAULT_PROJECTION; + } else { + projection = projectionIn; + } + + return nativeQueryBookmarkFromAPI(mNativeChromeBrowserProvider, projection, selection, + selectionArgs, sortOrder); + } + + private int updateBookmarkFromAPI(ContentValues values, String selection, + String[] selectionArgs) { + BookmarkRow row = BookmarkRow.fromContentValues(values); + return nativeUpdateBookmarkFromAPI(mNativeChromeBrowserProvider, + row.mUrl, row.mCreated, row.mIsBookmark, row.mDate, + row.mFavicon, row.mTitle, row.mVisits, row.mParentId, selection, selectionArgs); + } + + private int removeBookmarkFromAPI(String selection, String[] selectionArgs) { + return nativeRemoveBookmarkFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs); + } + + private int removeHistoryFromAPI(String selection, String[] selectionArgs) { + return nativeRemoveHistoryFromAPI(mNativeChromeBrowserProvider, selection, selectionArgs); + } + + @CalledByNative + private void onBookmarkChanged() { + notifyChange(buildAPIContentUri(getContext(), BOOKMARKS_PATH)); + } + + @CalledByNative + private void onHistoryChanged() { + notifyChange(buildAPIContentUri(getContext(), HISTORY_PATH)); + } + + @CalledByNative + private void onSearchTermChanged() { + notifyChange(buildAPIContentUri(getContext(), SEARCHES_PATH)); + } + + private long addSearchTermFromAPI(ContentValues values) { + SearchRow row = SearchRow.fromContentValues(values); + if (row.mTerm == null) { + throw new IllegalArgumentException("Must have a search term"); + } + return nativeAddSearchTermFromAPI(mNativeChromeBrowserProvider, row.mTerm, row.mDate); + } + + private int updateSearchTermFromAPI(ContentValues values, String selection, + String[] selectionArgs) { + SearchRow row = SearchRow.fromContentValues(values); + return nativeUpdateSearchTermFromAPI(mNativeChromeBrowserProvider, + row.mTerm, row.mDate, selection, selectionArgs); + } + + private Cursor querySearchTermFromAPI(String[] projectionIn, String selection, + String[] selectionArgs, String sortOrder) { + String[] projection = null; + if (projectionIn == null || projectionIn.length == 0) { + projection = SEARCHES_PROJECTION; + } else { + projection = projectionIn; + } + return nativeQuerySearchTermFromAPI(mNativeChromeBrowserProvider, projection, selection, + selectionArgs, sortOrder); + } + + private int removeSearchFromAPI(String selection, String[] selectionArgs) { + return nativeRemoveSearchTermFromAPI(mNativeChromeBrowserProvider, + selection, selectionArgs); + } + + private static boolean isInUiThread() { + if (!ThreadUtils.runningOnUiThread()) return false; + + if (!"REL".equals(Build.VERSION.CODENAME)) { + throw new IllegalStateException("Shouldn't run in the UI thread"); + } + + Log.w(TAG, "ChromeBrowserProvider methods cannot be called from the UI thread."); + return true; + } + + private static Uri buildContentUri(String authority, String path) { + return Uri.parse("content://" + authority + "/" + path); + } + + private static Uri buildAPIContentUri(Context context, String path) { + return buildContentUri(context.getPackageName() + API_AUTHORITY_SUFFIX, path); + } + + private static String buildWhereClause(long id, String selection) { + StringBuilder sb = new StringBuilder(); + sb.append(BaseColumns._ID); + sb.append(" = "); + sb.append(id); + if (!TextUtils.isEmpty(selection)) { + sb.append(" AND ("); + sb.append(selection); + sb.append(")"); + } + return sb.toString(); + } + + private static String buildHistoryWhereClause(long id, String selection) { + return buildWhereClause(id, buildBookmarkWhereClause(selection, false)); + } + + private static String buildHistoryWhereClause(String selection) { + return buildBookmarkWhereClause(selection, false); + } + + /** + * @return a SQL where class which is inserted the bookmark condition. + */ + private static String buildBookmarkWhereClause(String selection, boolean isBookmark) { + StringBuilder sb = new StringBuilder(); + sb.append(BookmarkColumns.BOOKMARK); + sb.append(isBookmark ? " = 1 " : " = 0"); + if (!TextUtils.isEmpty(selection)) { + sb.append(" AND ("); + sb.append(selection); + sb.append(")"); + } + return sb.toString(); + } + + private static String buildBookmarkWhereClause(long id, String selection) { + return buildWhereClause(id, buildBookmarkWhereClause(selection, true)); + } + + private static String buildBookmarkWhereClause(String selection) { + return buildBookmarkWhereClause(selection, true); + } + + // Wrap the value of BookmarkColumn. + private static class BookmarkRow { + Boolean mIsBookmark; + Long mCreated; + String mUrl; + Long mDate; + byte[] mFavicon; + String mTitle; + Integer mVisits; + long mParentId; + + static BookmarkRow fromContentValues(ContentValues values) { + BookmarkRow row = new BookmarkRow(); + if (values.containsKey(BookmarkColumns.URL)) { + row.mUrl = values.getAsString(BookmarkColumns.URL); + } + if (values.containsKey(BookmarkColumns.BOOKMARK)) { + row.mIsBookmark = values.getAsInteger(BookmarkColumns.BOOKMARK) != 0; + } + if (values.containsKey(BookmarkColumns.CREATED)) { + row.mCreated = values.getAsLong(BookmarkColumns.CREATED); + } + if (values.containsKey(BookmarkColumns.DATE)) { + row.mDate = values.getAsLong(BookmarkColumns.DATE); + } + if (values.containsKey(BookmarkColumns.FAVICON)) { + row.mFavicon = values.getAsByteArray(BookmarkColumns.FAVICON); + // We need to know that the caller set the favicon column. + if (row.mFavicon == null) { + row.mFavicon = new byte[0]; + } + } + if (values.containsKey(BookmarkColumns.TITLE)) { + row.mTitle = values.getAsString(BookmarkColumns.TITLE); + } + if (values.containsKey(BookmarkColumns.VISITS)) { + row.mVisits = values.getAsInteger(BookmarkColumns.VISITS); + } + if (values.containsKey(BOOKMARK_PARENT_ID_PARAM)) { + row.mParentId = values.getAsLong(BOOKMARK_PARENT_ID_PARAM); + } + return row; + } + } + + // Wrap the value of SearchColumn. + private static class SearchRow { + String mTerm; + Long mDate; + + static SearchRow fromContentValues(ContentValues values) { + SearchRow row = new SearchRow(); + if (values.containsKey(SearchColumns.SEARCH)) { + row.mTerm = values.getAsString(SearchColumns.SEARCH); + } + if (values.containsKey(SearchColumns.DATE)) { + row.mDate = values.getAsLong(SearchColumns.DATE); + } + return row; + } + } + + /** + * Initialize native side if it hasn't been already initialized. + * This is called from BrowserStartupCallback during normal startup except when called + * through one of the public ContentProvider APIs. + */ + private void ensureNativeSideInitialized() { + ThreadUtils.assertOnUiThread(); + if (mNativeChromeBrowserProvider == 0) mNativeChromeBrowserProvider = nativeInit(); + } + + @Override + protected void finalize() throws Throwable { + try { + // Tests might try to destroy this in the wrong thread. + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + ensureNativeChromeDestroyedOnUIThread(); + } + }); + } finally { + super.finalize(); + } + } + + /** + * This method should only run on UI thread. + */ + private void ensureNativeChromeDestroyedOnUIThread() { + if (mNativeChromeBrowserProvider != 0) { + nativeDestroy(mNativeChromeBrowserProvider); + mNativeChromeBrowserProvider = 0; + } + } + + @SuppressLint("NewApi") + private void notifyChange(final Uri uri) { + // If the calling user is different than current one, we need to post a + // task to notify change, otherwise, a system level hidden permission + // INTERACT_ACROSS_USERS_FULL is needed. + // The related APIs were added in API 17, it should be safe to fallback to + // normal way for notifying change, because caller can't be other users in + // devices whose API level is less than API 17. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + UserHandle callingUserHandle = Binder.getCallingUserHandle(); + if (callingUserHandle != null + && !callingUserHandle.equals(android.os.Process.myUserHandle())) { + ThreadUtils.postOnUiThread(new Runnable() { + @Override + public void run() { + getContext().getContentResolver().notifyChange(uri, null); + } + }); + return; + } + } + getContext().getContentResolver().notifyChange(uri, null); + } + + private boolean hasPermission(String permission) { + boolean isSystemOrGoogleCaller = ExternalAuthUtils.getInstance().isCallerValid( + getContext(), ExternalAuthUtils.FLAG_SHOULD_BE_GOOGLE_SIGNED + | ExternalAuthUtils.FLAG_SHOULD_BE_SYSTEM); + if (isSystemOrGoogleCaller) return true; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return getContext().checkCallingOrSelfPermission( + getReadWritePermissionNameForBookmarkFolders()) + == PackageManager.PERMISSION_GRANTED; + } else { + return getContext().checkCallingOrSelfPermission(permission) + == PackageManager.PERMISSION_GRANTED; + } + } + + private native long nativeInit(); + private native void nativeDestroy(long nativeChromeBrowserProvider); + + // Public API native methods. + private native long nativeAddBookmark(long nativeChromeBrowserProvider, + String url, String title, boolean isFolder, long parentId); + + private native int nativeRemoveBookmark(long nativeChromeBrowserProvider, long id); + + private native int nativeUpdateBookmark(long nativeChromeBrowserProvider, + long id, String url, String title, long parentId); + + private native long nativeAddBookmarkFromAPI(long nativeChromeBrowserProvider, + String url, Long created, Boolean isBookmark, Long date, byte[] favicon, + String title, Integer visits, long parentId); + + private native SQLiteCursor nativeQueryBookmarkFromAPI(long nativeChromeBrowserProvider, + String[] projection, String selection, String[] selectionArgs, String sortOrder); + + private native int nativeUpdateBookmarkFromAPI(long nativeChromeBrowserProvider, + String url, Long created, Boolean isBookmark, Long date, byte[] favicon, + String title, Integer visits, long parentId, String selection, String[] selectionArgs); + + private native int nativeRemoveBookmarkFromAPI(long nativeChromeBrowserProvider, + String selection, String[] selectionArgs); + + private native int nativeRemoveHistoryFromAPI(long nativeChromeBrowserProvider, + String selection, String[] selectionArgs); + + private native long nativeAddSearchTermFromAPI(long nativeChromeBrowserProvider, + String term, Long date); + + private native SQLiteCursor nativeQuerySearchTermFromAPI(long nativeChromeBrowserProvider, + String[] projection, String selection, String[] selectionArgs, String sortOrder); + + private native int nativeUpdateSearchTermFromAPI(long nativeChromeBrowserProvider, + String search, Long date, String selection, String[] selectionArgs); + + private native int nativeRemoveSearchTermFromAPI(long nativeChromeBrowserProvider, + String selection, String[] selectionArgs); + + // Client API native methods. + private native boolean nativeBookmarkNodeExists(long nativeChromeBrowserProvider, long id); + + private native long nativeCreateBookmarksFolderOnce(long nativeChromeBrowserProvider, + String title, long parentId); + + private native BookmarkNode nativeGetEditableBookmarkFolders(long nativeChromeBrowserProvider); + + private native void nativeRemoveAllUserBookmarks(long nativeChromeBrowserProvider); + + private native BookmarkNode nativeGetBookmarkNode(long nativeChromeBrowserProvider, + long id, boolean getParent, boolean getChildren); + + private native BookmarkNode nativeGetMobileBookmarksFolder(long nativeChromeBrowserProvider); + + private native boolean nativeIsBookmarkInMobileBookmarksBranch(long nativeChromeBrowserProvider, + long id); + + private native byte[] nativeGetFaviconOrTouchIcon(long nativeChromeBrowserProvider, String url); + + private native byte[] nativeGetThumbnail(long nativeChromeBrowserProvider, String url); +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProviderClient.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProviderClient.java new file mode 100644 index 0000000..93d1bcc --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProviderClient.java
@@ -0,0 +1,145 @@ +// Copyright 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. + +package org.chromium.chrome.browser.provider; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.Log; + +import org.chromium.chrome.browser.provider.ChromeBrowserProvider.BookmarkNode; + +import java.io.Serializable; + +/** + * Exposes the custom API methods for ChromeBrowserProvider. + */ +public class ChromeBrowserProviderClient { + private static final String TAG = "ChromeBrowserProviderClient"; + + // Returned by some of the methods in this class. + public static final long INVALID_BOOKMARK_ID = ChromeBrowserProvider.INVALID_BOOKMARK_ID; + + // Flags used with getBookmarkNode. + /** Retrieve the node corresponding to the id provided in getBookmarkNode. */ + public static final int GET_NODE = 0x00000000; + + /** Retrieve the parent of the node requested in getBookmarkNode. */ + public static final int GET_PARENT = 0x00000001; + + /** Retrieve the immediate children of the node requested in getBookmarkNode. */ + public static final int GET_CHILDREN = 0x00000002; + + /** Retrieve the favicon or touch icon, if any, in all the nodes returned by getBookmarkNode. */ + public static final int GET_FAVICONS = 0x00000004; + + /** Retrieve the thumbnail, if any, in all the nodes returned by getBookmarkNode. */ + public static final int GET_THUMBNAILS = 0x00000008; + + /** + * Verifies if a bookmark node given by its ID exists in the bookmark model. + * + * @return True if the provided bookmark node exists in the bookmark model. + */ + public static boolean bookmarkNodeExists(Context context, long nodeId) { + Boolean result = chromeBrowserProviderCall(Boolean.class, + ChromeBrowserProvider.CLIENT_API_BOOKMARK_NODE_EXISTS, + context, argsToBundle(nodeId)); + return result != null ? result.booleanValue() : false; + } + + /** + * Removes all bookmarks and bookmark folders that the user can edit. + * Only the permanent bookmark folders remain after this operation, and any managed bookmarks. + */ + public static void removeAllUserBookmarks(Context context) { + chromeBrowserProviderCall(BookmarkNode.class, + ChromeBrowserProvider.CLIENT_API_DELETE_ALL_USER_BOOKMARKS, context, + argsToBundle()); + } + + /** + * Retrieves a bookmark node given its ID or null if no such node exists. + * The parent and immediate child nodes can be also retrieved by enabling the getParent + * and getChildren flags. No deeper child nodes can be retrieved with this method. + * + * @param nodeId The ID of the bookmark node to be retrieved. + * @param flags Combination of constants telling what information of the node is required. + * @return The bookmark node corresponding to the provided ID. + */ + public static BookmarkNode getBookmarkNode(Context context, long nodeId, int flags) { + return chromeBrowserProviderCall(BookmarkNode.class, + ChromeBrowserProvider.CLIENT_API_GET_BOOKMARK_NODE, context, + argsToBundle(nodeId, + (flags & GET_PARENT) != 0, + (flags & GET_CHILDREN) != 0, + (flags & GET_FAVICONS) != 0, + (flags & GET_THUMBNAILS) != 0)); + } + + /** + * Returns the ID of the Mobile Bookmarks folder. + * + * @return The ID of the Mobile Bookmarks folder or INVALID_BOOKMARK_ID in case of error. + */ + public static long getMobileBookmarksFolderId(Context context) { + Long id = chromeBrowserProviderCall(Long.class, + ChromeBrowserProvider.CLIENT_API_GET_MOBILE_BOOKMARKS_FOLDER_ID, context, + argsToBundle()); + return id != null ? id.longValue() : INVALID_BOOKMARK_ID; + } + + /** + * Checks if a bookmark node is in the Mobile Bookmarks folder branch. + * + * @return True if the ID belongs to a node in the Mobile Bookmarks folder branch. + */ + public static boolean isBookmarkInMobileBookmarksBranch(Context context, long nodeId) { + Boolean result = chromeBrowserProviderCall(Boolean.class, + ChromeBrowserProvider.CLIENT_API_IS_BOOKMARK_IN_MOBILE_BOOKMARKS_BRANCH, context, + argsToBundle(nodeId)); + return result != null ? result.booleanValue() : false; + } + + // --------------------- End of the client API --------------------- // + + private static Uri getPrivateProviderUri(Context context) { + // The Bookmarks Uri uses the private provider authority. + return ChromeBrowserProvider.getBookmarksUri(context); + } + + + private static Bundle argsToBundle(Object ... args) { + Bundle methodArgs = new Bundle(); + for (int i = 0; i < args.length; ++i) { + Class<? extends Object> argClass = args[i].getClass(); + if (Parcelable.class.isAssignableFrom(argClass)) { + methodArgs.putParcelable(ChromeBrowserProvider.argKey(i), (Parcelable) args[i]); + } else if (Serializable.class.isAssignableFrom(argClass)) { + methodArgs.putSerializable(ChromeBrowserProvider.argKey(i), (Serializable) args[i]); + } else { + Log.e(TAG, "Argument implements neither Parcelable nor Serializable."); + return null; + } + } + return methodArgs; + } + + private static <T> T chromeBrowserProviderCall(Class<T> returnType, String name, + Context context, Bundle args) { + android.util.Log.i(TAG, "before executing " + name + " call"); + Bundle result = context.getContentResolver().call(getPrivateProviderUri(context), + name, null, args); + android.util.Log.i(TAG, "after executing " + name + " call"); + if (result == null) return null; + if (Parcelable.class.isAssignableFrom(returnType)) { + return returnType.cast( + result.getParcelable(ChromeBrowserProvider.CLIENT_API_RESULT_KEY)); + } else { + return returnType.cast(result.get(ChromeBrowserProvider.CLIENT_API_RESULT_KEY)); + } + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProviderSuggestionsCursor.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProviderSuggestionsCursor.java new file mode 100644 index 0000000..b26dafd --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProviderSuggestionsCursor.java
@@ -0,0 +1,128 @@ +// Copyright 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. + +package org.chromium.chrome.browser.provider; + +import android.app.SearchManager; +import android.content.Intent; +import android.database.AbstractCursor; +import android.database.Cursor; + +import org.chromium.chrome.R; + +/** + * For bookmarks/history suggestions, wrap the cursor returned in one that can feed + * the data back to global search in the format it wants. + */ +class ChromeBrowserProviderSuggestionsCursor extends AbstractCursor { + + private static final String[] COLS = new String [] { + BaseColumns.ID, + SearchManager.SUGGEST_COLUMN_INTENT_ACTION, + SearchManager.SUGGEST_COLUMN_INTENT_DATA, + SearchManager.SUGGEST_COLUMN_TEXT_1, + SearchManager.SUGGEST_COLUMN_TEXT_2, + SearchManager.SUGGEST_COLUMN_TEXT_2_URL, + SearchManager.SUGGEST_COLUMN_ICON_1, + SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT + }; + + private static final int COLUMN_ID = 0; + private static final int COLUMN_SUGGEST_INTENT_ACTION = 1; + private static final int COLUMN_SUGGEST_INTENT_DATA = 2; + private static final int COLUMN_SUGGEST_TEXT_1 = 3; + private static final int COLUMN_SUGGEST_TEXT_2 = 4; + private static final int COLUMN_SUGGEST_TEXT_2_URL = 5; + private static final int COLUMN_SUGGEST_ICON_1 = 6; + private static final int COLUMN_SUGGEST_LAST_ACCESS_HINT = 7; + + private final Cursor mCursor; + + public ChromeBrowserProviderSuggestionsCursor(Cursor c) { + mCursor = c; + } + + @Override + public String[] getColumnNames() { + return COLS; + } + + @Override + public int getCount() { + return mCursor.getCount(); + } + + @Override + public String getString(int column) { + switch (column) { + case COLUMN_ID: + return mCursor.getString(mCursor.getColumnIndex(BookmarkColumns.ID)); + case COLUMN_SUGGEST_INTENT_ACTION: + return Intent.ACTION_VIEW; + case COLUMN_SUGGEST_INTENT_DATA: + return mCursor.getString(mCursor.getColumnIndex(BookmarkColumns.URL)); + case COLUMN_SUGGEST_TEXT_1: + return mCursor.getString(mCursor.getColumnIndex(BookmarkColumns.TITLE)); + case COLUMN_SUGGEST_TEXT_2: + case COLUMN_SUGGEST_TEXT_2_URL: + return mCursor.getString(mCursor.getColumnIndex(BookmarkColumns.URL)); + case COLUMN_SUGGEST_ICON_1: + // This is the icon displayed to the left of the result in QSB. + return Integer.toString(R.mipmap.app_icon); + case COLUMN_SUGGEST_LAST_ACCESS_HINT: + // After clearing history, the Chrome bookmarks database will have a last access + // time of 0 for all bookmarks. In the Android provider, this will yield a negative + // last access time. A negative last access time will cause global search to discard + // the result, so fix it up before we return it. + long lastAccess = mCursor.getLong( + mCursor.getColumnIndex(BookmarkColumns.DATE)); + return lastAccess < 0 ? "0" : "" + lastAccess; + default: + throw new UnsupportedOperationException(); + } + } + + @Override + public boolean isNull(int c) { + return mCursor.isNull(c); + } + + @Override + public long getLong(int c) { + switch (c) { + case 7: + // See comments above in getString() re. negative last access times. + long lastAccess = mCursor.getLong( + mCursor.getColumnIndex(BookmarkColumns.DATE)); + return lastAccess < 0 ? 0 : lastAccess; + default: + throw new UnsupportedOperationException(); + } + } + + @Override + public short getShort(int c) { + throw new UnsupportedOperationException(); + } + + @Override + public double getDouble(int c) { + throw new UnsupportedOperationException(); + } + + @Override + public int getInt(int c) { + throw new UnsupportedOperationException(); + } + + @Override + public float getFloat(int c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean onMove(int oldPosition, int newPosition) { + return mCursor.moveToPosition(newPosition); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/SearchColumns.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/SearchColumns.java new file mode 100644 index 0000000..3dcf221 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/SearchColumns.java
@@ -0,0 +1,21 @@ +// 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. + +package org.chromium.chrome.browser.provider; + +/** + * Copy of android.provider.Browser.SearchColumns. + */ +public class SearchColumns implements BaseColumns { + /** + * The user entered search term. + */ + public static final String SEARCH = "search"; + + /** + * The date the search was performed, in milliseconds since the epoch. + * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p> + */ + public static final String DATE = "date"; +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/ClearSyncDataPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/ClearSyncDataPreferences.java index 77b5eb7..1e6c605 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/ClearSyncDataPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/ClearSyncDataPreferences.java
@@ -8,8 +8,8 @@ import android.content.Context; import android.os.AsyncTask; -import org.chromium.chrome.browser.ChromeBrowserProviderClient; import org.chromium.chrome.browser.preferences.privacy.ClearBrowsingDataPreferences; +import org.chromium.chrome.browser.provider.ChromeBrowserProviderClient; import org.chromium.chrome.browser.signin.SigninManager; import java.util.EnumSet;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java index 2b1fa79..da056d46 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
@@ -293,10 +293,9 @@ case FROM_LINK: case FROM_EXTERNAL_APP: return PageTransition.LINK | PageTransition.FROM_API; - case FROM_MENU_OR_OVERVIEW: + case FROM_CHROME_UI: case FROM_LONGPRESS_FOREGROUND: case FROM_LONGPRESS_BACKGROUND: - case FROM_KEYBOARD: return PageTransition.AUTO_TOPLEVEL; default: assert false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java index 7c6e0d7a..6257c64 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java
@@ -90,7 +90,7 @@ public final void launchNTP() { try { TraceEvent.begin("TabCreator.launchNTP"); - launchUrl(UrlConstants.NTP_URL, TabModel.TabLaunchType.FROM_MENU_OR_OVERVIEW); + launchUrl(UrlConstants.NTP_URL, TabModel.TabLaunchType.FROM_CHROME_UI); } finally { TraceEvent.end("TabCreator.launchNTP"); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java index 796e033..287af15 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModel.java
@@ -8,37 +8,63 @@ import org.chromium.chrome.browser.tab.Tab; /** - * TabModel organizes all the open tabs and allows you to create new ones. There are two TabModels - * in the app at this time: normal and incognito. More could be added to allow for windows or - * something. + * TabModel organizes all the open tabs and allows you to create new ones. Regular and Incognito + * tabs are kept in different TabModels. */ public interface TabModel extends TabList { /** * A list of the various ways tabs can be launched. */ public enum TabLaunchType { - FROM_LINK, // Opened from a link. - FROM_EXTERNAL_APP, // Opened by and external app. - FROM_MENU_OR_OVERVIEW, // Opened from the options menu or the tab stack overview. - FROM_RESTORE, // Opened after restoring state from storage. - // Opened from the long press menu. Like FROM_MENU but also sets up a parent/child - // relationship like FROM_LINK. FOREGROUND and BACKGROUND indicates whether the current tab - // should be automatically switched to the new tab or not. + /** + * Opened from a link. Sets up a relationship between the newly created tab and its parent. + */ + FROM_LINK, + + /** Opened by an external app. */ + FROM_EXTERNAL_APP, + + /** + * Catch-all for Tabs opened by Chrome UI not covered by more specific TabLaunchTypes. + * Examples include: + * - Tabs created by the options menu. + * - Tabs created via the New Tab button in the tab stack overview. + * - Tabs created via Push Notifications. + * - Tabs opened via a keyboard shortcut. + */ + FROM_CHROME_UI, + + /** Opened during the restoration process on startup. */ + FROM_RESTORE, + + /** + * Opened from the long press context menu. Will be brought to the foreground. + * Like FROM_CHROME_UI, but also sets up a parent/child relationship like FROM_LINK. + */ FROM_LONGPRESS_FOREGROUND, - FROM_LONGPRESS_BACKGROUND, - FROM_INSTANT, // Tab was created by instant. - FROM_KEYBOARD // Opened from a physical keyboard via shortcut. + + /** + * Opened from the long press context menu. Will not be brought to the foreground. + * Like FROM_CHROME_UI, but also sets up a parent/child relationship like FROM_LINK. + */ + FROM_LONGPRESS_BACKGROUND } /** - * A list of the various ways tabs can eb selected. + * A list of the various ways tabs can be selected. */ public enum TabSelectionType { - FROM_CLOSE, // Selection of adjacent tab when the active tab is closed in foreground. - FROM_EXIT, // Selection of adjacent tab when the active tab is closed upon app exit. - FROM_NEW, // Selection of newly created tab (e.g. for a url intent or NTP). - FROM_USER // User-originated switch to existing tab or selection of main tab on app - // startup. + /** Selection of adjacent tab when the active tab is closed in foreground. */ + FROM_CLOSE, + + /** Selection of adjacent tab when the active tab is closed upon app exit. */ + FROM_EXIT, + + /** Selection of newly created tab (e.g. for a URL intent or NTP). */ + FROM_NEW, + + /** User-originated switch to existing tab or selection of main tab on app startup. */ + FROM_USER } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java index e341ead..d6a0a51d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
@@ -123,7 +123,7 @@ @CalledByNative protected Tab createNewTabForDevTools(String url) { return getTabCreator(false).createNewTab(new LoadUrlParams(url), - TabModel.TabLaunchType.FROM_MENU_OR_OVERVIEW, null); + TabModel.TabLaunchType.FROM_CHROME_UI, null); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java index c095092..ff8ccad 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java
@@ -116,7 +116,7 @@ } else if (type == TabLaunchType.FROM_LONGPRESS_BACKGROUND || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND) { asyncParams.setDocumentStartedBy(DocumentMetricIds.STARTED_BY_CONTEXT_MENU); - } else if (type == TabLaunchType.FROM_MENU_OR_OVERVIEW) { + } else if (type == TabLaunchType.FROM_CHROME_UI) { asyncParams.setDocumentStartedBy(DocumentMetricIds.STARTED_BY_OPTIONS_MENU); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/Toolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/Toolbar.java index 9f985a8..9cf3508 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/Toolbar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/Toolbar.java
@@ -7,6 +7,8 @@ import android.graphics.Rect; import android.view.View; +import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; + /** * An interface for outside packages to interact with ToolbarLayout. Other than for testing purposes * this interface should be used rather than {@link ToolbarLayout} and extending classes. @@ -46,6 +48,23 @@ boolean isReadyForTextureCapture(); /** + * Sets whether or not the toolbar should force itself to draw for a texture capture regardless + * of other criteria used in isReadyForTextureCapture(). A texture capture will only be forced + * if the toolbar drawables tint is changing. + * + * @param forceTextureCapture Whether the toolbar should force itself to draw. + * @return True if a texture capture will be forced on the next draw. + */ + boolean setForceTextureCapture(boolean forceTextureCapture); + + /** + * Sets the {@link LayoutUpdateHost} for use in requesting an update when the toolbar texture + * needs to be recaptured. + * @param layoutUpdateHost The {@link LayoutUpdateHost} for requesting updates. + */ + void setLayoutUpdateHost(LayoutUpdateHost layoutUpdateHost); + + /** * Show the update badge on the app menu button. Will have no effect if there is no app menu * button for the current Activity. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java index aa9c2162..683b0e6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarControlContainer.java
@@ -185,6 +185,9 @@ @Override protected void onCaptureEnd() { mToolbar.setTextureCaptureMode(false); + // Forcing a texture capture should only be done for one draw. Turn off forced + // texture capture. + mToolbar.setForceTextureCapture(false); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java index ccbceac..f3d918b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarLayout.java
@@ -27,6 +27,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper; import org.chromium.chrome.browser.compositor.Invalidator; +import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; import org.chromium.chrome.browser.ntp.NewTabPage; import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; import org.chromium.chrome.browser.omnibox.LocationBar; @@ -439,6 +440,14 @@ return true; } + @Override + public boolean setForceTextureCapture(boolean forceTextureCapture) { + return false; + } + + @Override + public void setLayoutUpdateHost(LayoutUpdateHost layoutUpdateHost) { } + /** * @param attached Whether or not the web content is attached to the view heirarchy. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java index f637fff..7cde469 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -23,7 +23,6 @@ import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.ChromeBrowserProviderClient; import org.chromium.chrome.browser.TabLoadStatus; import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.WindowDelegate; @@ -48,6 +47,7 @@ import org.chromium.chrome.browser.partnercustomizations.HomepageManager; import org.chromium.chrome.browser.partnercustomizations.HomepageManager.HomepageStateListener; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.provider.ChromeBrowserProviderClient; import org.chromium.chrome.browser.search_engines.TemplateUrlService; import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrl; import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver; @@ -244,7 +244,6 @@ public void onTabModelSelected(TabModel newModel, TabModel oldModel) { refreshSelectedTab(); updateTabCount(); - mControlContainer.invalidateBitmap(); } @Override @@ -526,6 +525,10 @@ public void onTabSelectionHinted(int tabId) { mPreselectedTabId = tabId; refreshSelectedTab(); + + if (mToolbar.setForceTextureCapture(true)) { + mControlContainer.invalidateBitmap(); + } } @Override @@ -589,6 +592,7 @@ mToolbar.setOnNewTabClickHandler(newTabClickHandler); mToolbar.setBookmarkClickHandler(bookmarkClickHandler); mToolbar.setCustomTabCloseClickHandler(customTabsBackClickHandler); + mToolbar.setLayoutUpdateHost(layoutDriver); mToolbarModel.initializeWithNative();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java index 6db5840..2b06dc80 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -48,6 +48,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.compositor.Invalidator; +import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; import org.chromium.chrome.browser.ntp.NewTabPage; import org.chromium.chrome.browser.omnibox.LocationBar; import org.chromium.chrome.browser.omnibox.LocationBarPhone; @@ -122,6 +123,9 @@ // a bitmap to use as a texture representation of this view. @ViewDebug.ExportedProperty(category = "chrome") private boolean mTextureCaptureMode; + private boolean mForceTextureCapture; + private boolean mUseLightDrawablesForTextureCapture; + private boolean mLightDrawablesUsedForLastTextureCapture; @ViewDebug.ExportedProperty(category = "chrome") private boolean mAnimateNormalToolbar; @@ -194,6 +198,8 @@ private View.OnClickListener mReturnButtonListener; private boolean mIsHomeButtonEnabled; + private LayoutUpdateHost mLayoutUpdateHost; + /** * Used to specify the visual state of the toolbar. */ @@ -1005,7 +1011,7 @@ mMenuButton.getHeight() - mMenuButton.getPaddingBottom()); translateCanvasToView(mToolbarButtonsContainer, mMenuButton, canvas); mTabSwitcherAnimationMenuDrawable.setAlpha(rgbAlpha); - int color = mUseLightToolbarDrawables + int color = mUseLightDrawablesForTextureCapture ? mLightModeDefaultColor : mDarkModeDefaultColor; mTabSwitcherAnimationMenuDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN); @@ -1013,7 +1019,7 @@ } // Draw the menu badge if necessary. - Drawable badgeDrawable = mUseLightToolbarDrawables + Drawable badgeDrawable = mUseLightDrawablesForTextureCapture ? mTabSwitcherAnimationMenuBadgeLightDrawable : mTabSwitcherAnimationMenuBadgeDarkDrawable; if (mShowMenuBadge && badgeDrawable != null && mUrlExpansionPercent != 1f) { @@ -1026,6 +1032,8 @@ badgeDrawable.draw(canvas); } + mLightDrawablesUsedForLastTextureCapture = mUseLightDrawablesForTextureCapture; + canvas.restore(); } @@ -1222,11 +1230,33 @@ @Override public boolean isReadyForTextureCapture() { + if (mForceTextureCapture) { + return true; + } return !(mIsInTabSwitcherMode || mTabSwitcherModeAnimation != null || urlHasFocus() || mUrlFocusChangeInProgress); } @Override + public boolean setForceTextureCapture(boolean forceTextureCapture) { + if (forceTextureCapture) { + setUseLightDrawablesForTextureCapture(); + // Only force a texture capture if the tint for the toolbar drawables is changing. + mForceTextureCapture = mLightDrawablesUsedForLastTextureCapture + != mUseLightDrawablesForTextureCapture; + return mForceTextureCapture; + } + + mForceTextureCapture = forceTextureCapture; + return false; + } + + @Override + public void setLayoutUpdateHost(LayoutUpdateHost layoutUpdateHost) { + mLayoutUpdateHost = layoutUpdateHost; + } + + @Override protected void onNavigatedToDifferentPage() { super.onNavigatedToDifferentPage(); if (FeatureUtilities.isDocumentMode(getContext())) { @@ -1469,7 +1499,7 @@ } } - private void updateOverlayDrawables() { + private void updateOverlayDrawables(boolean isInTabSwitcherMode) { if (!isNativeLibraryReady()) return; VisualState overlayState = computeVisualState(false); @@ -1487,6 +1517,15 @@ mOverlayDrawablesVisualState)); setTabSwitcherAnimationMenuDrawable(); + setUseLightDrawablesForTextureCapture(); + + if (!isInTabSwitcherMode && !mTextureCaptureMode && mLayoutUpdateHost != null) { + // Request a layout update to trigger a texture capture if the tint color is changing + // and we're not already in texture capture mode. This is necessary if the tab switcher + // is entered immediately after a change to the tint color without any user interactions + // that would normally trigger a texture capture. + mLayoutUpdateHost.requestUpdate(); + } } @Override @@ -1943,7 +1982,7 @@ mVisualState = newVisualState; - updateOverlayDrawables(); + updateOverlayDrawables(isInTabSwitcherMode); updateShadowVisibility(isInTabSwitcherMode); if (!visualStateChanged) { if (mVisualState == VisualState.NEW_TAB_NORMAL) { @@ -2118,5 +2157,12 @@ return mReturnButtonListener != null && currentTab != null && currentTab.isAllowedToReturnToExternalApp(); } + + private void setUseLightDrawablesForTextureCapture() { + int currentPrimaryColor = getToolbarDataProvider().getPrimaryColor(); + mUseLightDrawablesForTextureCapture = isIncognito() + || (currentPrimaryColor != 0 + && ColorUtils.shoudUseLightForegroundOnBackground(currentPrimaryColor)); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java index 9e3dfe1f..842e9bad 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/FullScreenActivity.java
@@ -141,7 +141,7 @@ if (tab == null) { tab = new Tab(Tab.INVALID_TAB_ID, Tab.INVALID_TAB_ID, false, this, getWindowAndroid(), - TabLaunchType.FROM_MENU_OR_OVERVIEW, null, null); + TabLaunchType.FROM_CHROME_UI, null, null); } tab.initialize(null, getTabContentManager(), createTabDelegateFactory(), false, unfreeze);
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index 4610450..2f44aaa4 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -422,19 +422,19 @@ Clear browsing data </message> <message name="IDS_CLEAR_BROWSING_DATA_FOOTNOTE" desc="A summary string at the bottom of the Clear Browsing Data dialog, informing the user that not all data can be cleared through this dialog."> - Some settings that may reflect browsing habits will not be cleared. + Saved site settings will not be deleted and may reflect your browsing habits. </message> - <message name="IDS_CLEAR_BROWSING_DATA_FOOTNOTE_SYNCED" desc="A summary string at the bottom of the Clear Browsing Data dialog, informing the user that synced data types are deleted across devices, and that not all data can be cleared through this dialog."> - This clears synced data from all devices. Some settings that may reflect browsing habits will not be cleared. + <message name="IDS_CLEAR_BROWSING_DATA_FOOTNOTE_SIGNED" desc="A summary string at the bottom of the Clear Browsing Data dialog, informing the user that synced data types are deleted across devices, and that not all data can be cleared through this dialog."> + This clears synced data from all devices. You won’t be signed out of your Google account. Saved site settings will not be deleted and may reflect your browsing habits. </message> <message name="IDS_CLEAR_CACHE_TITLE" desc="Title for Clear Cache in Clear Browsing Data dialog"> - Cache + Cached images and files </message> <message name="IDS_CLEAR_HISTORY_TITLE" desc="Title for Clear History in Clear Browsing Data dialog"> Browsing history </message> <message name="IDS_CLEAR_COOKIES_AND_SITE_DATA_TITLE" desc="Title for Clear Cookies and site data in Clear Browsing Data dialog"> - Cookies, site data + Cookies and site data </message> <message name="IDS_CLEAR_COOKIES_NO_SIGN_OUT_SUMMARY" desc="Text at the bottom of the Clear Browsing Data dialog informing that clearing cookies wouldn't sign the user out of Google Accounts. Dialog also contains a link which opens the Account Management screen."> You won’t be signed out of your <ph name="BEGIN_LINK"><link></ph>Google Accounts<ph name="END_LINK"></link></ph> @@ -443,7 +443,7 @@ Saved passwords </message> <message name="IDS_CLEAR_FORM_DATA_TITLE" desc="Title for Clear Form Data in Clear Browsing Data preference"> - Autofill data + Autofill form data </message> <message name="IDS_CLEAR_BOOKMARKS_TITLE" desc="Title for Clear Bookmarks data in Clear Synced Data dialog"> Bookmarks @@ -452,7 +452,7 @@ Clearing browsing data </message> <message name="IDS_CLEAR_DATA_DELETE" desc="Button that allows the user to clear their browsing data. [CHAR-LIMIT=20]"> - Clear + Clear data </message> <message name="IDS_CLEAR_BROWSING_DATA_PROGRESS_MESSAGE" desc='Message on the progress dialog used when waiting for "clear browsing data" to complete.'> Please wait… @@ -460,6 +460,25 @@ <message name="IDS_CAN_NOT_CLEAR_BROWSING_HISTORY_TOAST" desc="Message on the toast explaining that child account users can not clear their browsing history."> Browsing history can’t be cleared with accounts for kids </message> + <message name="IDS_CLEAR_BROWSING_DATA_PERIOD_TITLE" desc="Label of the dropdown that selects the time period for which browsing data will be deleted. This sentence can be followed by any option, such as 'Clear data from the past day' or 'Clear data from the past hour'."> + Clear data from the + </message> + <message name="IDS_CLEAR_BROWSING_DATA_PERIOD_HOUR" desc="The option to delete browsing data from the past hour. This is the direct object of the sentence 'Clear data from the past hour'."> + past hour + </message> + <message name="IDS_CLEAR_BROWSING_DATA_PERIOD_DAY" desc="The option to delete browsing data from the past day. This is the direct object of the sentence 'Clear data from the past day'."> + past day + </message> + <message name="IDS_CLEAR_BROWSING_DATA_PERIOD_WEEK" desc="The option to delete browsing data from the past week. This is the direct object of the sentence 'Clear data from the past week'."> + past week + </message> + <message name="IDS_CLEAR_BROWSING_DATA_PERIOD_FOUR_WEEKS" desc="The option to delete browsing data from the last 4 weeks. This is the direct object of the sentence 'Clear data from the last 4 weeks'."> + last 4 weeks + </message> + <message name="IDS_CLEAR_BROWSING_DATA_PERIOD_EVERYTHING" desc="The option to delete browsing data from the beginning of time. This is the direct object of the sentence 'Clear data from the beginning of time'."> + beginning of time + </message> + <message name="IDS_USAGE_AND_CRASH_REPORTS_TITLE" desc="Title for 'Usage and crash reports' preference"> Usage and crash reports </message> @@ -1041,9 +1060,9 @@ <!-- Bluetooth Picker UI strings --> <message name="IDS_BLUETOOTH_DIALOG_TITLE" desc="The header message shown on top of the dialog that lets the user pick a Bluetooth device for the site to pair with. Shown above a list of Bluetooth devices discovered. "> - <ph name="SITE">%1$s<ex>https://www.google.com</ex></ph> wants to <ph name="BEGIN_LINK"><link></ph>pair with<ph name="END_LINK"></link></ph>:</message> + <ph name="SITE">%1$s<ex>https://www.google.com</ex></ph> wants to pair with:</message> <message name="IDS_BLUETOOTH_SEARCHING" desc="The message shown in the Bluetooth picker dialog while scanning for devices."> - Searching for devices… + Searching for devices… <ph name="BEGIN_LINK"><link></ph>get help<ph name="END_LINK"></link></ph> </message> <message name="IDS_BLUETOOTH_CONFIRM_BUTTON" desc="The button to confirm association of a website with a Bluetooth device. Use the same term as in 'Pair this Bluetooth headset with your phone.'"> Pair @@ -1051,7 +1070,10 @@ <message name="IDS_BLUETOOTH_NOT_SEEING_IT" desc="The message to show at the bottom of the dialog when Bluetooth scanning has uncovered some items but is still scanning for others."> Not seeing your device? <ph name="BEGIN_LINK"><link></ph>Get help<ph name="END_LINK"></link></ph>. </message> - <message name="IDS_BLUETOOTH_NOT_SEEING_IT_IDLE" desc="The message to show at the bottom of the dialog when Bluetooth scanning has uncovered some items but is no longer discovering devices. Allows the user to restart the scan, in case their device is not listed."> + <message name="IDS_BLUETOOTH_NOT_SEEING_IT_IDLE_NONE_FOUND" desc="The message to show at the bottom of the dialog when Bluetooth scanning has not uncovered any devices and it is no longer discovering devices."> + <ph name="BEGIN_LINK"><link></ph>Get help<ph name="END_LINK"></link></ph> + </message> + <message name="IDS_BLUETOOTH_NOT_SEEING_IT_IDLE_SOME_FOUND" desc="The message to show at the bottom of the dialog when Bluetooth scanning has uncovered some items but is no longer discovering devices. Allows the user to restart the scan, in case their device is not listed."> Not seeing your device? <ph name="BEGIN_LINK1"><link1></ph>Get help<ph name="END_LINK1"></link1></ph> or <ph name="BEGIN_LINK2"><link2></ph>search again<ph name="END_LINK2"></link2></ph>. </message> <message name="IDS_BLUETOOTH_NOT_FOUND" desc="The message to show when no items were found during Bluetooth device scanning.">
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java index 718c8ed..484464c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BindingManagerIntegrationTest.java
@@ -192,7 +192,7 @@ TabCreator tabCreator = getActivity().getCurrentTabCreator(); tabs[0] = tabCreator.createNewTab( new LoadUrlParams(mTestServer.getURL(FILE_PATH)), - TabLaunchType.FROM_KEYBOARD, null); + TabLaunchType.FROM_CHROME_UI, null); // Background tab. tabs[1] = tabCreator.createNewTab( new LoadUrlParams(mTestServer.getURL(FILE_PATH)), @@ -263,7 +263,7 @@ TabCreator tabCreator = getActivity().getCurrentTabCreator(); tabs[0] = tabCreator.createNewTab( new LoadUrlParams(mTestServer.getURL(FILE_PATH)), - TabLaunchType.FROM_KEYBOARD, null); + TabLaunchType.FROM_CHROME_UI, null); // Background tab. tabs[1] = tabCreator.createNewTab( new LoadUrlParams(mTestServer.getURL(FILE_PATH)), @@ -368,7 +368,7 @@ TabCreator tabCreator = getActivity().getCurrentTabCreator(); return tabCreator.createNewTab( new LoadUrlParams(mTestServer.getURL(FILE_PATH)), - TabLaunchType.FROM_KEYBOARD, null); + TabLaunchType.FROM_CHROME_UI, null); } }); ChromeTabUtils.waitForTabPageLoaded(tab, mTestServer.getURL(FILE_PATH)); @@ -442,7 +442,7 @@ TabCreator tabCreator = getActivity().getCurrentTabCreator(); return tabCreator.createNewTab( new LoadUrlParams(mTestServer.getURL(FILE_PATH)), - TabLaunchType.FROM_KEYBOARD, null); + TabLaunchType.FROM_CHROME_UI, null); }}); ChromeTabUtils.waitForTabPageLoaded(fgTab, mTestServer.getURL(FILE_PATH)); int initialNavigationPid = getRenderProcessId(fgTab); @@ -504,10 +504,10 @@ public void run() { // Foreground tab. tabs[0] = tabCreator.createNewTab( - new LoadUrlParams("about:blank"), TabLaunchType.FROM_KEYBOARD, null); + new LoadUrlParams("about:blank"), TabLaunchType.FROM_CHROME_UI, null); // Background tab. tabs[1] = tabCreator.createNewTab( - new LoadUrlParams("about:blank"), TabLaunchType.FROM_KEYBOARD, null); + new LoadUrlParams("about:blank"), TabLaunchType.FROM_CHROME_UI, null); } }); ChromeTabUtils.waitForTabPageLoaded(tabs[0], "about:blank"); @@ -520,7 +520,7 @@ public void run() { // Foreground tab. tabs[2] = tabCreator.createNewTab( - new LoadUrlParams("about:blank"), TabLaunchType.FROM_KEYBOARD, null); + new LoadUrlParams("about:blank"), TabLaunchType.FROM_CHROME_UI, null); } }); ChromeTabUtils.waitForTabPageLoaded(tabs[2], "about:blank");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeActivityTest.java index ca61e29..4524038 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeActivityTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeActivityTest.java
@@ -60,7 +60,7 @@ ChromeTabCreator tabCreator = getActivity().getCurrentTabCreator(); tabs[0] = tabCreator.createNewTab( new LoadUrlParams(mTestServer.getURL(FILE_PATH)), - TabLaunchType.FROM_KEYBOARD, null); + TabLaunchType.FROM_CHROME_UI, null); // Background tab. tabs[1] = tabCreator.createNewTab( new LoadUrlParams(mTestServer.getURL(FILE_PATH)),
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 d1f1df8..e4b0618 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java
@@ -59,13 +59,15 @@ private ItemChooserDialog createDialog() { SpannableString title = new SpannableString("title"); - String searching = new String("searching"); + SpannableString searching = new SpannableString("searching"); SpannableString noneFound = new SpannableString("noneFound"); SpannableString statusActive = new SpannableString("statusActive"); - SpannableString statusIdle = new SpannableString("statusIdle"); + SpannableString statusIdleNoneFound = new SpannableString("statusIdleNoneFound"); + SpannableString statusIdleSomeFound = new SpannableString("statusIdleSomeFound"); String positiveButton = new String("positiveButton"); - final ItemChooserDialog.ItemChooserLabels labels = new ItemChooserDialog.ItemChooserLabels( - title, searching, noneFound, statusActive, statusIdle, positiveButton); + final ItemChooserDialog.ItemChooserLabels labels = + new ItemChooserDialog.ItemChooserLabels(title, searching, noneFound, statusActive, + statusIdleNoneFound, statusIdleSomeFound, positiveButton); ItemChooserDialog dialog = ThreadUtils.runOnUiThreadBlockingNoException( new Callable<ItemChooserDialog>() { @Override @@ -147,7 +149,7 @@ // The chooser should show the status idle text. assertEquals(View.VISIBLE, items.getVisibility()); assertEquals(View.GONE, items.getEmptyView().getVisibility()); - assertEquals("statusIdle", statusView.getText().toString()); + assertEquals("statusIdleSomeFound", statusView.getText().toString()); assertFalse(button.isEnabled()); // Select the first item and verify it got selected. @@ -178,7 +180,7 @@ // drive home the point and a status message at the bottom. assertEquals(View.GONE, items.getVisibility()); assertEquals(View.VISIBLE, items.getEmptyView().getVisibility()); - assertEquals("statusIdle", statusView.getText().toString()); + assertEquals("statusIdleNoneFound", statusView.getText().toString()); assertFalse(button.isEnabled()); mChooserDialog.dismiss();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java index f5ab4de..ee492387 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
@@ -6,6 +6,7 @@ import android.content.Intent; import android.net.Uri; +import android.os.Bundle; import android.os.Environment; import android.provider.Browser; import android.test.FlakyTest; @@ -43,9 +44,9 @@ * Test the behavior of tabs when opening a URL from an external app. */ public class TabsOpenedFromExternalAppTest extends ChromeTabbedActivityTestBase { - private static final String EXTERNAL_APP_1_ID = "app1"; private static final String EXTERNAL_APP_2_ID = "app2"; + private static final String ANDROID_APP_REFERRER = "android-app://com.my.great.great.app"; static class ElementFocusedCriteria extends Criteria { private final Tab mTab; @@ -116,6 +117,40 @@ } } + private static class ReferrerCriteria extends Criteria { + private final Tab mTab; + private final String mExpectedReferrer; + private static final String GET_REFERRER_JS = + "(function() { return document.referrer; })();"; + + public ReferrerCriteria(Tab tab, String expectedReferrer) { + super("Referrer is not as expected."); + mTab = tab; + // Add quotes to match returned value from JS. + mExpectedReferrer = "\"" + expectedReferrer + "\""; + } + + @Override + public boolean isSatisfied() { + String referrer; + try { + String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult( + mTab.getWebContents(), GET_REFERRER_JS); + if (jsonText.equalsIgnoreCase("null")) jsonText = ""; + referrer = jsonText; + } catch (InterruptedException e) { + e.printStackTrace(); + Assert.fail("InterruptedException was thrown"); + return false; + } catch (TimeoutException e) { + e.printStackTrace(); + Assert.fail("TimeoutException was thrown"); + return false; + } + return TextUtils.equals(mExpectedReferrer, referrer); + } + } + private EmbeddedTestServer mTestServer; public TabsOpenedFromExternalAppTest() { @@ -146,8 +181,8 @@ * Returns when the URL has been navigated to. * @throws InterruptedException */ - private void launchUrlFromExternalApp(String url, String appId, boolean createNewTab) - throws InterruptedException { + private void launchUrlFromExternalApp(String url, String appId, boolean createNewTab, + Bundle extras) throws InterruptedException { final Intent intent = new Intent(Intent.ACTION_VIEW); if (appId != null) { intent.putExtra(Browser.EXTRA_APPLICATION_ID, appId); @@ -156,6 +191,7 @@ intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true); } intent.setData(Uri.parse(url)); + if (extras != null) intent.putExtras(extras); final Tab originalTab = getActivity().getActivityTab(); ThreadUtils.runOnUiThreadBlocking(new Runnable() { @@ -175,6 +211,45 @@ ChromeTabUtils.waitForTabPageLoaded(getActivity().getActivityTab(), url); } + private void launchUrlFromExternalApp(String url, String appId, boolean createNewTab) + throws InterruptedException { + launchUrlFromExternalApp(url, appId, createNewTab, null); + } + + /** + * Tests that URLs opened from external apps can set an android-app scheme referrer. + * @throws InterruptedException + */ + @LargeTest + @Feature({"Navigation"}) + public void testReferrer() throws InterruptedException { + String url = mTestServer.getURL("/chrome/test/data/android/about.html"); + startMainActivityFromLauncher(); + Bundle extras = new Bundle(); + extras.putParcelable(Intent.EXTRA_REFERRER, Uri.parse(ANDROID_APP_REFERRER)); + launchUrlFromExternalApp(url, EXTERNAL_APP_1_ID, true, extras); + CriteriaHelper.pollForCriteria( + new ReferrerCriteria(getActivity().getActivityTab(), ANDROID_APP_REFERRER), 2000, + 200); + } + + /** + * Tests that URLs opened from external apps can set an android-app scheme referrer. + * @throws InterruptedException + */ + @LargeTest + @Feature({"Navigation"}) + public void testCannotSetArbitraryReferrer() throws InterruptedException { + String url = mTestServer.getURL("/chrome/test/data/android/about.html"); + startMainActivityFromLauncher(); + String referrer = "foobar://totally.legit.referrer"; + Bundle extras = new Bundle(); + extras.putParcelable(Intent.EXTRA_REFERRER, Uri.parse(referrer)); + launchUrlFromExternalApp(url, EXTERNAL_APP_1_ID, true, extras); + CriteriaHelper.pollForCriteria( + new ReferrerCriteria(getActivity().getActivityTab(), ""), 2000, 200); + } + /** * Tests that URLs opened from the same external app don't create new tabs. * @throws InterruptedException
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderBookmarkNodeUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderBookmarkNodeUnitTest.java deleted file mode 100644 index adf5e97..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderBookmarkNodeUnitTest.java +++ /dev/null
@@ -1,155 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser.bookmark; - -import android.os.Parcel; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -import org.chromium.base.test.util.Feature; -import org.chromium.chrome.browser.ChromeBrowserProvider.BookmarkNode; -import org.chromium.chrome.browser.ChromeBrowserProvider.Type; - -import java.util.Random; - -/** - * Tests parceling of bookmark node hierarchies used by the provider client API. - */ -public class ProviderBookmarkNodeUnitTest extends AndroidTestCase { - Random mGenerator = new Random(); - byte[][] mImageBlobs = null; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - mImageBlobs = new byte[][] { - { 1, 2, 3 }, - { 4, 5, 6, 7 }, - { 8, 9, 10, 11, 12 }, - }; - - for (byte[] icon : mImageBlobs) { - assertNotNull(icon); - } - } - - private static BookmarkNode parcelNode(BookmarkNode node) { - Parcel output = Parcel.obtain(); - Parcel input = Parcel.obtain(); - node.writeToParcel(output, 0); - byte[] bytes = output.marshall(); - - input.unmarshall(bytes, 0, bytes.length); - input.setDataPosition(0); - - return BookmarkNode.CREATOR.createFromParcel(input); - } - - private byte[] getRandomImageBlob() { - return mImageBlobs[mGenerator.nextInt(mImageBlobs.length)]; - } - - private static BookmarkNode createMockHierarchy() { - // Mock hierarchy. - // + Bookmarks - // - Google - // - Google maps - // + Youtube - // + Empty folder - // + Some other folder - // - Surprised Vader - // - Rickroll'D - BookmarkNode root = new BookmarkNode(1, Type.FOLDER, "Bookmarks", null, null); - root.addChild(new BookmarkNode(2, Type.URL, "Google", "http://www.google.com/", root)); - root.addChild(new BookmarkNode(3, Type.URL, "GoogleMaps", "http://maps.google.com/", root)); - - BookmarkNode folder1 = new BookmarkNode(4, Type.FOLDER, "Youtube", null, root); - root.addChild(folder1); - folder1.addChild(new BookmarkNode(5, Type.FOLDER, "Empty folder", null, folder1)); - - BookmarkNode folder2 = new BookmarkNode(6, Type.FOLDER, "Some other folder", null, folder1); - folder1.addChild(folder2); - - folder1.addChild(new BookmarkNode(7, Type.URL, "RickRoll'D", - "http://www.youtube.com/watch?v=oHg5SJYRHA0", folder1)); - folder2.addChild(new BookmarkNode(8, Type.URL, "Surprised Vader", - "http://www.youtube.com/watch?v=9h1swNWgP8Q", folder2)); - return root; - } - - // Returns the same mock hierarchy as createMockHierarchy, but with random favicon and - // thumbnail information including null values. - private BookmarkNode createMockHierarchyWithImages() { - return addImagesRecursive(createMockHierarchy()); - } - - private BookmarkNode addImagesRecursive(BookmarkNode node) { - node.setFavicon(mGenerator.nextBoolean() ? getRandomImageBlob() : null); - node.setThumbnail(mGenerator.nextBoolean() ? getRandomImageBlob() : null); - - for (BookmarkNode child : node.children()) { - addImagesRecursive(child); - } - - return node; - } - - private static boolean isSameHierarchy(BookmarkNode h1, BookmarkNode h2) { - return isSameHierarchyDownwards(h1.getHierarchyRoot(), h2.getHierarchyRoot()); - } - - private static boolean isSameHierarchyDownwards(BookmarkNode n1, BookmarkNode n2) { - if (n1 == null && n2 == null) return true; - if (n1 == null || n2 == null) return false; - if (!n1.equalContents(n2)) return false; - for (int i = 0; i < n1.children().size(); ++i) { - if (!isSameHierarchyDownwards(n1.children().get(i), n2.children().get(i))) return false; - } - return true; - } - - // Tests parceling and comparing each of the nodes in the provided hierarchy. - private boolean internalTestNodeHierarchyParceling(BookmarkNode node) { - if (node == null) return false; - - BookmarkNode parceled = parcelNode(node); - if (!isSameHierarchy(node, parceled)) return false; - - for (BookmarkNode child : node.children()) { - if (!internalTestNodeHierarchyParceling(child)) return false; - } - - return true; - } - - @SmallTest - @Feature({"Android-ContentProvider"}) - public void testBookmarkNodeParceling() throws InterruptedException { - assertTrue(internalTestNodeHierarchyParceling(createMockHierarchy())); - } - - @SmallTest - @Feature({"Android-ContentProvider"}) - public void testBookmarkNodeParcelingWithImages() throws InterruptedException { - assertTrue(internalTestNodeHierarchyParceling(createMockHierarchyWithImages())); - } - - @SmallTest - @Feature({"Android-ContentProvider"}) - public void testSingleNodeParceling() throws InterruptedException { - BookmarkNode node = new BookmarkNode(1, Type.URL, "Google", "http://www.google.com/", null); - assertTrue(internalTestNodeHierarchyParceling(node)); - } - - @SmallTest - @Feature({"Android-ContentProvider"}) - public void testInvalidHierarchy() throws InterruptedException { - BookmarkNode root = new BookmarkNode(1, Type.FOLDER, "Bookmarks", null, null); - root.addChild(new BookmarkNode(2, Type.URL, "Google", "http://www.google.com/", root)); - root.addChild(new BookmarkNode(2, Type.URL, "GoogleMaps", "http://maps.google.com/", root)); - assertFalse(internalTestNodeHierarchyParceling(root)); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderBookmarksUriTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderBookmarksUriTest.java deleted file mode 100644 index b736d86d..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderBookmarksUriTest.java +++ /dev/null
@@ -1,368 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser.bookmark; - -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; -import android.test.suitebuilder.annotation.MediumTest; - -import org.chromium.base.test.util.Feature; -import org.chromium.chrome.browser.ChromeBrowserProvider; - -import java.util.Arrays; -import java.util.Date; - -/** - * Tests the use of the Bookmark URI as part of the Android provider public API. - */ -public class ProviderBookmarksUriTest extends ProviderTestBase { - private static final String TAG = "ProviderBookmarkUriTest"; - private static final byte[] FAVICON_DATA = { 1, 2, 3 }; - - private Uri mBookmarksUri; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mBookmarksUri = ChromeBrowserProvider.getBookmarksApiUri(getActivity()); - getContentResolver().delete(mBookmarksUri, null, null); - } - - @Override - protected void tearDown() throws Exception { - getContentResolver().delete(mBookmarksUri, null, null); - super.tearDown(); - } - - private Uri addBookmark(String url, String title, long lastVisitTime, long created, int visits, - byte[] icon, int isBookmark) { - ContentValues values = new ContentValues(); - values.put(BookmarkColumns.BOOKMARK, isBookmark); - values.put(BookmarkColumns.DATE, lastVisitTime); - values.put(BookmarkColumns.CREATED, created); - values.put(BookmarkColumns.FAVICON, icon); - values.put(BookmarkColumns.URL, url); - values.put(BookmarkColumns.VISITS, visits); - values.put(BookmarkColumns.TITLE, title); - return getContentResolver().insert(mBookmarksUri, values); - } - - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testAddBookmark() { - final long lastUpdateTime = System.currentTimeMillis(); - final long createdTime = lastUpdateTime - 1000 * 60 * 60; - final String url = "http://www.google.com/"; - final int visits = 2; - final String title = "Google"; - ContentValues values = new ContentValues(); - values.put(BookmarkColumns.BOOKMARK, 0); - values.put(BookmarkColumns.DATE, lastUpdateTime); - values.put(BookmarkColumns.CREATED, createdTime); - values.put(BookmarkColumns.FAVICON, FAVICON_DATA); - values.put(BookmarkColumns.URL, url); - values.put(BookmarkColumns.VISITS, visits); - values.put(BookmarkColumns.TITLE, title); - Uri uri = getContentResolver().insert(mBookmarksUri, values); - Cursor cursor = getContentResolver().query(uri, null, null, null, null); - assertEquals(1, cursor.getCount()); - assertTrue(cursor.moveToNext()); - int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK); - assertTrue(-1 != index); - assertEquals(0, cursor.getInt(index)); - index = cursor.getColumnIndex(BookmarkColumns.CREATED); - assertTrue(-1 != index); - assertEquals(createdTime, cursor.getLong(index)); - index = cursor.getColumnIndex(BookmarkColumns.DATE); - assertTrue(-1 != index); - assertEquals(lastUpdateTime, cursor.getLong(index)); - index = cursor.getColumnIndex(BookmarkColumns.FAVICON); - assertTrue(-1 != index); - assertTrue(byteArraysEqual(FAVICON_DATA, cursor.getBlob(index))); - index = cursor.getColumnIndex(BookmarkColumns.URL); - assertTrue(-1 != index); - assertEquals(url, cursor.getString(index)); - index = cursor.getColumnIndex(BookmarkColumns.VISITS); - assertTrue(-1 != index); - assertEquals(visits, cursor.getInt(index)); - } - - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testQueryBookmark() { - final long now = System.currentTimeMillis(); - final long lastUpdateTime[] = { now, now - 1000 * 60 }; - final long createdTime[] = { now - 1000 * 60 * 60, now - 1000 * 60 * 60 * 60 }; - final String url[] = { "http://www.google.com/", "http://mail.google.com/" }; - final int visits[] = { 2, 20 }; - final String title[] = { "Google", "Mail" }; - final int isBookmark[] = { 1, 0 }; - Uri[] uris = new Uri[2]; - byte[][] icons = { FAVICON_DATA, null }; - for (int i = 0; i < uris.length; i++) { - uris[i] = addBookmark(url[i], title[i], lastUpdateTime[i], createdTime[i], visits[i], - icons[i], isBookmark[i]); - assertNotNull(uris[i]); - } - - // Query the 1st row. - String[] selectionArgs = { url[0], String.valueOf(lastUpdateTime[0]), - String.valueOf(visits[0]), String.valueOf(isBookmark[0]) }; - Cursor cursor = getContentResolver().query(mBookmarksUri, null, - BookmarkColumns.URL + " = ? AND " + BookmarkColumns.DATE + " = ? AND " - + BookmarkColumns.VISITS + " = ? AND " + BookmarkColumns.BOOKMARK + " = ? AND " - + BookmarkColumns.FAVICON + " IS NOT NULL", - selectionArgs, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - assertTrue(cursor.moveToNext()); - int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK); - assertTrue(-1 != index); - assertEquals(isBookmark[0], cursor.getInt(index)); - index = cursor.getColumnIndex(BookmarkColumns.CREATED); - assertTrue(-1 != index); - assertEquals(createdTime[0], cursor.getLong(index)); - index = cursor.getColumnIndex(BookmarkColumns.DATE); - assertTrue(-1 != index); - assertEquals(lastUpdateTime[0], cursor.getLong(index)); - index = cursor.getColumnIndex(BookmarkColumns.FAVICON); - assertTrue(-1 != index); - assertTrue(byteArraysEqual(icons[0], cursor.getBlob(index))); - index = cursor.getColumnIndex(BookmarkColumns.URL); - assertTrue(-1 != index); - assertEquals(url[0], cursor.getString(index)); - index = cursor.getColumnIndex(BookmarkColumns.VISITS); - assertTrue(-1 != index); - assertEquals(visits[0], cursor.getInt(index)); - - // Query the 2nd row. - String[] selectionArgs2 = { url[1], String.valueOf(lastUpdateTime[1]), - String.valueOf(visits[1]), String.valueOf(isBookmark[1]) }; - cursor = getContentResolver().query(mBookmarksUri, null, - BookmarkColumns.URL + " = ? AND " + BookmarkColumns.DATE + " = ? AND " - + BookmarkColumns.VISITS + " = ? AND " + BookmarkColumns.BOOKMARK + " = ? AND " - + BookmarkColumns.FAVICON + " IS NULL", - selectionArgs2, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - assertTrue(cursor.moveToNext()); - index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK); - assertTrue(-1 != index); - assertEquals(isBookmark[1], cursor.getInt(index)); - index = cursor.getColumnIndex(BookmarkColumns.CREATED); - assertTrue(-1 != index); - assertEquals(createdTime[1], cursor.getLong(index)); - index = cursor.getColumnIndex(BookmarkColumns.DATE); - assertTrue(-1 != index); - assertEquals(lastUpdateTime[1], cursor.getLong(index)); - index = cursor.getColumnIndex(BookmarkColumns.FAVICON); - assertTrue(-1 != index); - assertTrue(byteArraysEqual(icons[1], cursor.getBlob(index))); - index = cursor.getColumnIndex(BookmarkColumns.URL); - assertTrue(-1 != index); - assertEquals(url[1], cursor.getString(index)); - index = cursor.getColumnIndex(BookmarkColumns.VISITS); - assertTrue(-1 != index); - assertEquals(visits[1], cursor.getInt(index)); - } - - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testUpdateBookmark() { - final long now = System.currentTimeMillis(); - final long lastUpdateTime[] = { now, now - 1000 * 60 }; - final long createdTime[] = { now - 1000 * 60 * 60, now - 1000 * 60 * 60 * 60 }; - final String url[] = { "http://www.google.com/", "http://mail.google.com/" }; - final int visits[] = { 2, 20 }; - final String title[] = { "Google", "Mail" }; - final int isBookmark[] = { 1, 0 }; - - byte[][] icons = { FAVICON_DATA, null }; - Uri uri = addBookmark(url[0], title[0], lastUpdateTime[0], createdTime[0], visits[0], - icons[0], isBookmark[0]); - assertNotNull(uri); - - ContentValues values = new ContentValues(); - values.put(BookmarkColumns.BOOKMARK, isBookmark[1]); - values.put(BookmarkColumns.DATE, lastUpdateTime[1]); - values.put(BookmarkColumns.URL, url[1]); - values.putNull(BookmarkColumns.FAVICON); - values.put(BookmarkColumns.TITLE, title[1]); - values.put(BookmarkColumns.VISITS, visits[1]); - String[] selectionArgs = { String.valueOf(lastUpdateTime[0]), - String.valueOf(isBookmark[0]) }; - getContentResolver().update(uri, values, BookmarkColumns.FAVICON + " IS NOT NULL AND " - + BookmarkColumns.DATE + "= ? AND " + BookmarkColumns.BOOKMARK + " = ?", - selectionArgs); - Cursor cursor = getContentResolver().query(uri, null, null, null, null); - assertEquals(1, cursor.getCount()); - assertTrue(cursor.moveToNext()); - int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK); - assertTrue(-1 != index); - assertEquals(isBookmark[1], cursor.getInt(index)); - index = cursor.getColumnIndex(BookmarkColumns.CREATED); - assertTrue(-1 != index); - assertEquals(createdTime[0], cursor.getLong(index)); - index = cursor.getColumnIndex(BookmarkColumns.DATE); - assertTrue(-1 != index); - assertEquals(lastUpdateTime[1], cursor.getLong(index)); - index = cursor.getColumnIndex(BookmarkColumns.FAVICON); - assertTrue(-1 != index); - assertTrue(byteArraysEqual(icons[1], cursor.getBlob(index))); - index = cursor.getColumnIndex(BookmarkColumns.URL); - assertTrue(-1 != index); - assertEquals(url[1], cursor.getString(index)); - index = cursor.getColumnIndex(BookmarkColumns.VISITS); - assertTrue(-1 != index); - assertEquals(visits[1], cursor.getInt(index)); - } - - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testDeleteBookmark() { - final long now = System.currentTimeMillis(); - final long lastUpdateTime[] = { now, now - 1000 * 60 }; - final long createdTime[] = { now - 1000 * 60 * 60, now - 1000 * 60 * 60 * 60 }; - final String url[] = { "http://www.google.com/", "http://mail.google.com/" }; - final int visits[] = { 2, 20 }; - final String title[] = { "Google", "Mail" }; - final int isBookmark[] = { 1, 0 }; - Uri[] uris = new Uri[2]; - byte[][] icons = { FAVICON_DATA, null }; - for (int i = 0; i < uris.length; i++) { - uris[i] = addBookmark(url[i], title[i], lastUpdateTime[i], createdTime[i], visits[i], - icons[i], isBookmark[i]); - assertNotNull(uris[i]); - } - - String[] selectionArgs = { String.valueOf(lastUpdateTime[0]), - String.valueOf(isBookmark[0]) }; - getContentResolver().delete(mBookmarksUri, BookmarkColumns.FAVICON + " IS NOT NULL AND " - + BookmarkColumns.DATE + "= ? AND " + BookmarkColumns.BOOKMARK + " = ?", - selectionArgs); - Cursor cursor = getContentResolver().query(uris[0], null, null, null, null); - assertNotNull(cursor); - assertEquals(0, cursor.getCount()); - cursor = getContentResolver().query(uris[1], null, null, null, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - String[] selectionArgs1 = { String.valueOf(lastUpdateTime[1]), - String.valueOf(isBookmark[1]) }; - getContentResolver().delete(mBookmarksUri, BookmarkColumns.FAVICON + " IS NULL AND " - + BookmarkColumns.DATE + "= ? AND " + BookmarkColumns.BOOKMARK + " = ?", - selectionArgs1); - cursor = getContentResolver().query(uris[1], null, null, null, null); - assertNotNull(cursor); - assertEquals(0, cursor.getCount()); - } - - /* - * Copied from CTS test with minor adaptations. - */ - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testBookmarksTable() { - final String[] bookmarksProjection = new String[] { - BookmarkColumns.ID, BookmarkColumns.URL, BookmarkColumns.VISITS, - BookmarkColumns.DATE, BookmarkColumns.CREATED, BookmarkColumns.BOOKMARK, - BookmarkColumns.TITLE, BookmarkColumns.FAVICON }; - final int idIndex = 0; - final int urlIndex = 1; - final int visitsIndex = 2; - final int dataIndex = 3; - final int createdIndex = 4; - final int bookmarkIndex = 5; - final int titleIndex = 6; - final int faviconIndex = 7; - - final String insertBookmarkTitle = "bookmark_insert"; - final String insertBookmarkUrl = "www.bookmark_insert.com"; - - final String updateBookmarkTitle = "bookmark_update"; - final String updateBookmarkUrl = "www.bookmark_update.com"; - - // Test: insert. - ContentValues value = new ContentValues(); - long createDate = new Date().getTime(); - value.put(BookmarkColumns.TITLE, insertBookmarkTitle); - value.put(BookmarkColumns.URL, insertBookmarkUrl); - value.put(BookmarkColumns.VISITS, 0); - value.put(BookmarkColumns.DATE, createDate); - value.put(BookmarkColumns.CREATED, createDate); - value.put(BookmarkColumns.BOOKMARK, 0); - - Uri insertUri = getContentResolver().insert(mBookmarksUri, value); - Cursor cursor = getContentResolver().query( - mBookmarksUri, - bookmarksProjection, - BookmarkColumns.TITLE + " = ?", - new String[] { insertBookmarkTitle }, - BookmarkColumns.DATE); - assertTrue(cursor.moveToNext()); - assertEquals(insertBookmarkTitle, cursor.getString(titleIndex)); - assertEquals(insertBookmarkUrl, cursor.getString(urlIndex)); - assertEquals(0, cursor.getInt(visitsIndex)); - assertEquals(createDate, cursor.getLong(dataIndex)); - assertEquals(createDate, cursor.getLong(createdIndex)); - assertEquals(0, cursor.getInt(bookmarkIndex)); - // TODO(michaelbai): according to the test this should be null instead of an empty byte[]. - // BUG 6288508 - // assertTrue(cursor.isNull(FAVICON_INDEX)); - int Id = cursor.getInt(idIndex); - cursor.close(); - - // Test: update. - value.clear(); - long updateDate = new Date().getTime(); - value.put(BookmarkColumns.TITLE, updateBookmarkTitle); - value.put(BookmarkColumns.URL, updateBookmarkUrl); - value.put(BookmarkColumns.VISITS, 1); - value.put(BookmarkColumns.DATE, updateDate); - - getContentResolver().update(mBookmarksUri, value, - BookmarkColumns.TITLE + " = ?", - new String[] { insertBookmarkTitle }); - cursor = getContentResolver().query( - mBookmarksUri, - bookmarksProjection, - BookmarkColumns.ID + " = " + Id, - null, null); - assertTrue(cursor.moveToNext()); - assertEquals(updateBookmarkTitle, cursor.getString(titleIndex)); - assertEquals(updateBookmarkUrl, cursor.getString(urlIndex)); - assertEquals(1, cursor.getInt(visitsIndex)); - assertEquals(updateDate, cursor.getLong(dataIndex)); - assertEquals(createDate, cursor.getLong(createdIndex)); - assertEquals(0, cursor.getInt(bookmarkIndex)); - // TODO(michaelbai): according to the test this should be null instead of an empty byte[]. - // BUG 6288508 - // assertTrue(cursor.isNull(FAVICON_INDEX)); - assertEquals(Id, cursor.getInt(idIndex)); - - // Test: delete. - getContentResolver().delete(insertUri, null, null); - cursor = getContentResolver().query( - mBookmarksUri, - bookmarksProjection, - BookmarkColumns.ID + " = " + Id, - null, null); - assertEquals(0, cursor.getCount()); - } - - /** - * Checks if two byte arrays are equal. Used to compare icons. - * @return True if equal, false otherwise. - */ - private static boolean byteArraysEqual(byte[] byte1, byte[] byte2) { - if (byte1 == null && byte2 != null) { - return byte2.length == 0; - } - if (byte2 == null && byte1 != null) { - return byte1.length == 0; - } - return Arrays.equals(byte1, byte2); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderSearchesUriTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderSearchesUriTest.java deleted file mode 100644 index e3e59c7..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderSearchesUriTest.java +++ /dev/null
@@ -1,178 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser.bookmark; - -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; -import android.test.suitebuilder.annotation.MediumTest; - -import org.chromium.base.test.util.Feature; -import org.chromium.chrome.browser.ChromeBrowserProvider; - -import java.util.Date; - -/** - * Tests the use of the Searches URI as part of the Android provider public API. - */ -public class ProviderSearchesUriTest extends ProviderTestBase { - - private Uri mSearchesUri; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mSearchesUri = ChromeBrowserProvider.getSearchesApiUri(getActivity()); - getContentResolver().delete(mSearchesUri, null, null); - } - - @Override - protected void tearDown() throws Exception { - getContentResolver().delete(mSearchesUri, null, null); - super.tearDown(); - } - - private Uri addSearchTerm(String searchTerm, long searchTime) { - ContentValues values = new ContentValues(); - values.put(SearchColumns.SEARCH, searchTerm); - values.put(SearchColumns.DATE, searchTime); - return getContentResolver().insert(mSearchesUri, values); - } - - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testAddSearchTerm() { - long searchTime = System.currentTimeMillis(); - String searchTerm = "chrome"; - Uri uri = addSearchTerm(searchTerm, searchTime); - assertNotNull(uri); - String[] selectionArgs = { searchTerm, String.valueOf(searchTime) }; - Cursor cursor = getContentResolver().query(uri, null, SearchColumns.SEARCH + "=? AND " - + SearchColumns.DATE + " = ? ", selectionArgs, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - assertTrue(cursor.moveToNext()); - int index = cursor.getColumnIndex(SearchColumns.SEARCH); - assertTrue(-1 != index); - assertEquals(searchTerm, cursor.getString(index)); - index = cursor.getColumnIndex(SearchColumns.DATE); - assertTrue(-1 != index); - assertEquals(searchTime, cursor.getLong(index)); - } - - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testUpdateSearchTerm() { - long[] searchTime = { System.currentTimeMillis(), System.currentTimeMillis() - 1000 }; - String[] searchTerm = { "chrome", "chromium" }; - Uri uri = addSearchTerm(searchTerm[0], searchTime[0]); - ContentValues values = new ContentValues(); - values.put(SearchColumns.SEARCH, searchTerm[1]); - values.put(SearchColumns.DATE, searchTime[1]); - getContentResolver().update(uri, values, null, null); - String[] selectionArgs = { searchTerm[0] }; - Cursor cursor = getContentResolver().query(mSearchesUri, null, SearchColumns.SEARCH + "=?", - selectionArgs, null); - assertNotNull(cursor); - assertEquals(0, cursor.getCount()); - String[] selectionArgs1 = { searchTerm[1] }; - cursor = getContentResolver().query(mSearchesUri, null, SearchColumns.SEARCH + "=?", - selectionArgs1, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - assertTrue(cursor.moveToNext()); - int index = cursor.getColumnIndex(SearchColumns.SEARCH); - assertTrue(-1 != index); - assertEquals(searchTerm[1], cursor.getString(index)); - index = cursor.getColumnIndex(SearchColumns.DATE); - assertTrue(-1 != index); - assertEquals(searchTime[1], cursor.getLong(index)); - } - - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testDeleteSearchTerm() { - long[] searchTime = { System.currentTimeMillis(), System.currentTimeMillis() - 1000 }; - String[] searchTerm = {"chrome", "chromium"}; - Uri uri[] = new Uri[2]; - for (int i = 0; i < uri.length; i++) { - uri[i] = addSearchTerm(searchTerm[i], searchTime[i]); - } - getContentResolver().delete(uri[0], null, null); - String[] selectionArgs = { searchTerm[0] }; - Cursor cursor = getContentResolver().query(mSearchesUri, null, SearchColumns.SEARCH + "=?", - selectionArgs, null); - assertNotNull(cursor); - assertEquals(0, cursor.getCount()); - String[] selectionArgs1 = { searchTerm[1] }; - cursor = getContentResolver().query(mSearchesUri, null, SearchColumns.SEARCH + "=?", - selectionArgs1, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - assertTrue(cursor.moveToNext()); - int index = cursor.getColumnIndex(SearchColumns.SEARCH); - assertTrue(-1 != index); - assertEquals(searchTerm[1], cursor.getString(index)); - index = cursor.getColumnIndex(SearchColumns.DATE); - assertTrue(-1 != index); - assertEquals(searchTime[1], cursor.getLong(index)); - getContentResolver().delete(uri[1], null, null); - cursor = getContentResolver().query(uri[1], null, null, null, null); - assertNotNull(cursor); - assertEquals(0, cursor.getCount()); - } - - // Copied from CTS test with minor adaptations. - @MediumTest - @Feature({"Android-ContentProvider"}) - public void testSearchesTable() { - final int idIndex = 0; - String insertSearch = "search_insert"; - String updateSearch = "search_update"; - - // Test: insert - ContentValues value = new ContentValues(); - long createDate = new Date().getTime(); - value.put(SearchColumns.SEARCH, insertSearch); - value.put(SearchColumns.DATE, createDate); - - Uri insertUri = getContentResolver().insert(mSearchesUri, value); - Cursor cursor = getContentResolver().query(mSearchesUri, - ChromeBrowserProvider.SEARCHES_PROJECTION, SearchColumns.SEARCH + " = ?", - new String[] { insertSearch }, null); - assertTrue(cursor.moveToNext()); - assertEquals(insertSearch, - cursor.getString(ChromeBrowserProvider.SEARCHES_PROJECTION_SEARCH_INDEX)); - assertEquals(createDate, - cursor.getLong(ChromeBrowserProvider.SEARCHES_PROJECTION_DATE_INDEX)); - int id = cursor.getInt(idIndex); - cursor.close(); - - // Test: update - value.clear(); - long updateDate = new Date().getTime(); - value.put(SearchColumns.SEARCH, updateSearch); - value.put(SearchColumns.DATE, updateDate); - - getContentResolver().update(mSearchesUri, value, - SearchColumns.ID + " = " + id, null); - cursor = getContentResolver().query(mSearchesUri, - ChromeBrowserProvider.SEARCHES_PROJECTION, - SearchColumns.ID + " = " + id, null, null); - assertTrue(cursor.moveToNext()); - assertEquals(updateSearch, - cursor.getString(ChromeBrowserProvider.SEARCHES_PROJECTION_SEARCH_INDEX)); - assertEquals(updateDate, - cursor.getLong(ChromeBrowserProvider.SEARCHES_PROJECTION_DATE_INDEX)); - assertEquals(id, cursor.getInt(idIndex)); - - // Test: delete - getContentResolver().delete(insertUri, null, null); - cursor = getContentResolver().query(mSearchesUri, - ChromeBrowserProvider.SEARCHES_PROJECTION, - SearchColumns.ID + " = " + id, null, null); - assertEquals(0, cursor.getCount()); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderTestBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderTestBase.java deleted file mode 100644 index fc27777..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmark/ProviderTestBase.java +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser.bookmark; - -import android.content.ContentProvider; -import android.content.ContentResolver; -import android.content.pm.ProviderInfo; -import android.test.IsolatedContext; -import android.test.mock.MockContentResolver; - -import org.chromium.base.ThreadUtils; -import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.ChromeBrowserProvider; -import org.chromium.chrome.test.ChromeActivityTestCaseBase; - -/** - * Base class for Chrome's ContentProvider tests. - * Sets up a local ChromeBrowserProvider associated to a mock resolver in an isolated context. - */ -public class ProviderTestBase extends ChromeActivityTestCaseBase<ChromeActivity> { - - private IsolatedContext mContext; - - public ProviderTestBase() { - super(ChromeActivity.class); - } - - @Override - public void startMainActivity() throws InterruptedException { - startMainActivityOnBlankPage(); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - - final ChromeActivity activity = getActivity(); - assertNotNull(activity); - - final ContentProvider provider = new ChromeBrowserProvider(); - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - ProviderInfo providerInfo = new ProviderInfo(); - providerInfo.authority = ChromeBrowserProvider.getApiAuthority(activity); - provider.attachInfo(activity, providerInfo); - } - }); - - MockContentResolver resolver = new MockContentResolver(); - resolver.addProvider(ChromeBrowserProvider.getApiAuthority(activity), provider); - resolver.addProvider(ChromeBrowserProvider.getInternalAuthority(activity), provider); - - mContext = new IsolatedContext(resolver, activity); - assertTrue(getContentResolver() instanceof MockContentResolver); - } - - protected ContentResolver getContentResolver() { - return mContext.getContentResolver(); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java index bd8892ee..cb65925 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -324,6 +324,7 @@ assertNotNull(menu.findItem(R.id.open_in_browser_id)); assertFalse(menu.findItem(R.id.share_menu_id).isVisible()); assertFalse(menu.findItem(R.id.share_menu_id).isEnabled()); + assertNull(menu.findItem(R.id.bookmark_this_page_id)); } /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarkNodeUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarkNodeUnitTest.java new file mode 100644 index 0000000..a42af0a --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarkNodeUnitTest.java
@@ -0,0 +1,155 @@ +// Copyright 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. + +package org.chromium.chrome.browser.provider; + +import android.os.Parcel; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.test.util.Feature; +import org.chromium.chrome.browser.provider.ChromeBrowserProvider.BookmarkNode; +import org.chromium.chrome.browser.provider.ChromeBrowserProvider.Type; + +import java.util.Random; + +/** + * Tests parceling of bookmark node hierarchies used by the provider client API. + */ +public class ProviderBookmarkNodeUnitTest extends AndroidTestCase { + Random mGenerator = new Random(); + byte[][] mImageBlobs = null; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mImageBlobs = new byte[][] { + { 1, 2, 3 }, + { 4, 5, 6, 7 }, + { 8, 9, 10, 11, 12 }, + }; + + for (byte[] icon : mImageBlobs) { + assertNotNull(icon); + } + } + + private static BookmarkNode parcelNode(BookmarkNode node) { + Parcel output = Parcel.obtain(); + Parcel input = Parcel.obtain(); + node.writeToParcel(output, 0); + byte[] bytes = output.marshall(); + + input.unmarshall(bytes, 0, bytes.length); + input.setDataPosition(0); + + return BookmarkNode.CREATOR.createFromParcel(input); + } + + private byte[] getRandomImageBlob() { + return mImageBlobs[mGenerator.nextInt(mImageBlobs.length)]; + } + + private static BookmarkNode createMockHierarchy() { + // Mock hierarchy. + // + Bookmarks + // - Google + // - Google maps + // + Youtube + // + Empty folder + // + Some other folder + // - Surprised Vader + // - Rickroll'D + BookmarkNode root = new BookmarkNode(1, Type.FOLDER, "Bookmarks", null, null); + root.addChild(new BookmarkNode(2, Type.URL, "Google", "http://www.google.com/", root)); + root.addChild(new BookmarkNode(3, Type.URL, "GoogleMaps", "http://maps.google.com/", root)); + + BookmarkNode folder1 = new BookmarkNode(4, Type.FOLDER, "Youtube", null, root); + root.addChild(folder1); + folder1.addChild(new BookmarkNode(5, Type.FOLDER, "Empty folder", null, folder1)); + + BookmarkNode folder2 = new BookmarkNode(6, Type.FOLDER, "Some other folder", null, folder1); + folder1.addChild(folder2); + + folder1.addChild(new BookmarkNode(7, Type.URL, "RickRoll'D", + "http://www.youtube.com/watch?v=oHg5SJYRHA0", folder1)); + folder2.addChild(new BookmarkNode(8, Type.URL, "Surprised Vader", + "http://www.youtube.com/watch?v=9h1swNWgP8Q", folder2)); + return root; + } + + // Returns the same mock hierarchy as createMockHierarchy, but with random favicon and + // thumbnail information including null values. + private BookmarkNode createMockHierarchyWithImages() { + return addImagesRecursive(createMockHierarchy()); + } + + private BookmarkNode addImagesRecursive(BookmarkNode node) { + node.setFavicon(mGenerator.nextBoolean() ? getRandomImageBlob() : null); + node.setThumbnail(mGenerator.nextBoolean() ? getRandomImageBlob() : null); + + for (BookmarkNode child : node.children()) { + addImagesRecursive(child); + } + + return node; + } + + private static boolean isSameHierarchy(BookmarkNode h1, BookmarkNode h2) { + return isSameHierarchyDownwards(h1.getHierarchyRoot(), h2.getHierarchyRoot()); + } + + private static boolean isSameHierarchyDownwards(BookmarkNode n1, BookmarkNode n2) { + if (n1 == null && n2 == null) return true; + if (n1 == null || n2 == null) return false; + if (!n1.equalContents(n2)) return false; + for (int i = 0; i < n1.children().size(); ++i) { + if (!isSameHierarchyDownwards(n1.children().get(i), n2.children().get(i))) return false; + } + return true; + } + + // Tests parceling and comparing each of the nodes in the provided hierarchy. + private boolean internalTestNodeHierarchyParceling(BookmarkNode node) { + if (node == null) return false; + + BookmarkNode parceled = parcelNode(node); + if (!isSameHierarchy(node, parceled)) return false; + + for (BookmarkNode child : node.children()) { + if (!internalTestNodeHierarchyParceling(child)) return false; + } + + return true; + } + + @SmallTest + @Feature({"Android-ContentProvider"}) + public void testBookmarkNodeParceling() throws InterruptedException { + assertTrue(internalTestNodeHierarchyParceling(createMockHierarchy())); + } + + @SmallTest + @Feature({"Android-ContentProvider"}) + public void testBookmarkNodeParcelingWithImages() throws InterruptedException { + assertTrue(internalTestNodeHierarchyParceling(createMockHierarchyWithImages())); + } + + @SmallTest + @Feature({"Android-ContentProvider"}) + public void testSingleNodeParceling() throws InterruptedException { + BookmarkNode node = new BookmarkNode(1, Type.URL, "Google", "http://www.google.com/", null); + assertTrue(internalTestNodeHierarchyParceling(node)); + } + + @SmallTest + @Feature({"Android-ContentProvider"}) + public void testInvalidHierarchy() throws InterruptedException { + BookmarkNode root = new BookmarkNode(1, Type.FOLDER, "Bookmarks", null, null); + root.addChild(new BookmarkNode(2, Type.URL, "Google", "http://www.google.com/", root)); + root.addChild(new BookmarkNode(2, Type.URL, "GoogleMaps", "http://maps.google.com/", root)); + assertFalse(internalTestNodeHierarchyParceling(root)); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java new file mode 100644 index 0000000..645f77f --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderBookmarksUriTest.java
@@ -0,0 +1,367 @@ +// Copyright 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. + +package org.chromium.chrome.browser.provider; + +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.test.suitebuilder.annotation.MediumTest; + +import org.chromium.base.test.util.Feature; + +import java.util.Arrays; +import java.util.Date; + +/** + * Tests the use of the Bookmark URI as part of the Android provider public API. + */ +public class ProviderBookmarksUriTest extends ProviderTestBase { + private static final String TAG = "ProviderBookmarkUriTest"; + private static final byte[] FAVICON_DATA = { 1, 2, 3 }; + + private Uri mBookmarksUri; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mBookmarksUri = ChromeBrowserProvider.getBookmarksApiUri(getActivity()); + getContentResolver().delete(mBookmarksUri, null, null); + } + + @Override + protected void tearDown() throws Exception { + getContentResolver().delete(mBookmarksUri, null, null); + super.tearDown(); + } + + private Uri addBookmark(String url, String title, long lastVisitTime, long created, int visits, + byte[] icon, int isBookmark) { + ContentValues values = new ContentValues(); + values.put(BookmarkColumns.BOOKMARK, isBookmark); + values.put(BookmarkColumns.DATE, lastVisitTime); + values.put(BookmarkColumns.CREATED, created); + values.put(BookmarkColumns.FAVICON, icon); + values.put(BookmarkColumns.URL, url); + values.put(BookmarkColumns.VISITS, visits); + values.put(BookmarkColumns.TITLE, title); + return getContentResolver().insert(mBookmarksUri, values); + } + + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testAddBookmark() { + final long lastUpdateTime = System.currentTimeMillis(); + final long createdTime = lastUpdateTime - 1000 * 60 * 60; + final String url = "http://www.google.com/"; + final int visits = 2; + final String title = "Google"; + ContentValues values = new ContentValues(); + values.put(BookmarkColumns.BOOKMARK, 0); + values.put(BookmarkColumns.DATE, lastUpdateTime); + values.put(BookmarkColumns.CREATED, createdTime); + values.put(BookmarkColumns.FAVICON, FAVICON_DATA); + values.put(BookmarkColumns.URL, url); + values.put(BookmarkColumns.VISITS, visits); + values.put(BookmarkColumns.TITLE, title); + Uri uri = getContentResolver().insert(mBookmarksUri, values); + Cursor cursor = getContentResolver().query(uri, null, null, null, null); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToNext()); + int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK); + assertTrue(-1 != index); + assertEquals(0, cursor.getInt(index)); + index = cursor.getColumnIndex(BookmarkColumns.CREATED); + assertTrue(-1 != index); + assertEquals(createdTime, cursor.getLong(index)); + index = cursor.getColumnIndex(BookmarkColumns.DATE); + assertTrue(-1 != index); + assertEquals(lastUpdateTime, cursor.getLong(index)); + index = cursor.getColumnIndex(BookmarkColumns.FAVICON); + assertTrue(-1 != index); + assertTrue(byteArraysEqual(FAVICON_DATA, cursor.getBlob(index))); + index = cursor.getColumnIndex(BookmarkColumns.URL); + assertTrue(-1 != index); + assertEquals(url, cursor.getString(index)); + index = cursor.getColumnIndex(BookmarkColumns.VISITS); + assertTrue(-1 != index); + assertEquals(visits, cursor.getInt(index)); + } + + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testQueryBookmark() { + final long now = System.currentTimeMillis(); + final long lastUpdateTime[] = { now, now - 1000 * 60 }; + final long createdTime[] = { now - 1000 * 60 * 60, now - 1000 * 60 * 60 * 60 }; + final String url[] = { "http://www.google.com/", "http://mail.google.com/" }; + final int visits[] = { 2, 20 }; + final String title[] = { "Google", "Mail" }; + final int isBookmark[] = { 1, 0 }; + Uri[] uris = new Uri[2]; + byte[][] icons = { FAVICON_DATA, null }; + for (int i = 0; i < uris.length; i++) { + uris[i] = addBookmark(url[i], title[i], lastUpdateTime[i], createdTime[i], visits[i], + icons[i], isBookmark[i]); + assertNotNull(uris[i]); + } + + // Query the 1st row. + String[] selectionArgs = { url[0], String.valueOf(lastUpdateTime[0]), + String.valueOf(visits[0]), String.valueOf(isBookmark[0]) }; + Cursor cursor = getContentResolver().query(mBookmarksUri, null, + BookmarkColumns.URL + " = ? AND " + BookmarkColumns.DATE + " = ? AND " + + BookmarkColumns.VISITS + " = ? AND " + BookmarkColumns.BOOKMARK + " = ? AND " + + BookmarkColumns.FAVICON + " IS NOT NULL", + selectionArgs, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToNext()); + int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK); + assertTrue(-1 != index); + assertEquals(isBookmark[0], cursor.getInt(index)); + index = cursor.getColumnIndex(BookmarkColumns.CREATED); + assertTrue(-1 != index); + assertEquals(createdTime[0], cursor.getLong(index)); + index = cursor.getColumnIndex(BookmarkColumns.DATE); + assertTrue(-1 != index); + assertEquals(lastUpdateTime[0], cursor.getLong(index)); + index = cursor.getColumnIndex(BookmarkColumns.FAVICON); + assertTrue(-1 != index); + assertTrue(byteArraysEqual(icons[0], cursor.getBlob(index))); + index = cursor.getColumnIndex(BookmarkColumns.URL); + assertTrue(-1 != index); + assertEquals(url[0], cursor.getString(index)); + index = cursor.getColumnIndex(BookmarkColumns.VISITS); + assertTrue(-1 != index); + assertEquals(visits[0], cursor.getInt(index)); + + // Query the 2nd row. + String[] selectionArgs2 = { url[1], String.valueOf(lastUpdateTime[1]), + String.valueOf(visits[1]), String.valueOf(isBookmark[1]) }; + cursor = getContentResolver().query(mBookmarksUri, null, + BookmarkColumns.URL + " = ? AND " + BookmarkColumns.DATE + " = ? AND " + + BookmarkColumns.VISITS + " = ? AND " + BookmarkColumns.BOOKMARK + " = ? AND " + + BookmarkColumns.FAVICON + " IS NULL", + selectionArgs2, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToNext()); + index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK); + assertTrue(-1 != index); + assertEquals(isBookmark[1], cursor.getInt(index)); + index = cursor.getColumnIndex(BookmarkColumns.CREATED); + assertTrue(-1 != index); + assertEquals(createdTime[1], cursor.getLong(index)); + index = cursor.getColumnIndex(BookmarkColumns.DATE); + assertTrue(-1 != index); + assertEquals(lastUpdateTime[1], cursor.getLong(index)); + index = cursor.getColumnIndex(BookmarkColumns.FAVICON); + assertTrue(-1 != index); + assertTrue(byteArraysEqual(icons[1], cursor.getBlob(index))); + index = cursor.getColumnIndex(BookmarkColumns.URL); + assertTrue(-1 != index); + assertEquals(url[1], cursor.getString(index)); + index = cursor.getColumnIndex(BookmarkColumns.VISITS); + assertTrue(-1 != index); + assertEquals(visits[1], cursor.getInt(index)); + } + + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testUpdateBookmark() { + final long now = System.currentTimeMillis(); + final long lastUpdateTime[] = { now, now - 1000 * 60 }; + final long createdTime[] = { now - 1000 * 60 * 60, now - 1000 * 60 * 60 * 60 }; + final String url[] = { "http://www.google.com/", "http://mail.google.com/" }; + final int visits[] = { 2, 20 }; + final String title[] = { "Google", "Mail" }; + final int isBookmark[] = { 1, 0 }; + + byte[][] icons = { FAVICON_DATA, null }; + Uri uri = addBookmark(url[0], title[0], lastUpdateTime[0], createdTime[0], visits[0], + icons[0], isBookmark[0]); + assertNotNull(uri); + + ContentValues values = new ContentValues(); + values.put(BookmarkColumns.BOOKMARK, isBookmark[1]); + values.put(BookmarkColumns.DATE, lastUpdateTime[1]); + values.put(BookmarkColumns.URL, url[1]); + values.putNull(BookmarkColumns.FAVICON); + values.put(BookmarkColumns.TITLE, title[1]); + values.put(BookmarkColumns.VISITS, visits[1]); + String[] selectionArgs = { String.valueOf(lastUpdateTime[0]), + String.valueOf(isBookmark[0]) }; + getContentResolver().update(uri, values, BookmarkColumns.FAVICON + " IS NOT NULL AND " + + BookmarkColumns.DATE + "= ? AND " + BookmarkColumns.BOOKMARK + " = ?", + selectionArgs); + Cursor cursor = getContentResolver().query(uri, null, null, null, null); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToNext()); + int index = cursor.getColumnIndex(BookmarkColumns.BOOKMARK); + assertTrue(-1 != index); + assertEquals(isBookmark[1], cursor.getInt(index)); + index = cursor.getColumnIndex(BookmarkColumns.CREATED); + assertTrue(-1 != index); + assertEquals(createdTime[0], cursor.getLong(index)); + index = cursor.getColumnIndex(BookmarkColumns.DATE); + assertTrue(-1 != index); + assertEquals(lastUpdateTime[1], cursor.getLong(index)); + index = cursor.getColumnIndex(BookmarkColumns.FAVICON); + assertTrue(-1 != index); + assertTrue(byteArraysEqual(icons[1], cursor.getBlob(index))); + index = cursor.getColumnIndex(BookmarkColumns.URL); + assertTrue(-1 != index); + assertEquals(url[1], cursor.getString(index)); + index = cursor.getColumnIndex(BookmarkColumns.VISITS); + assertTrue(-1 != index); + assertEquals(visits[1], cursor.getInt(index)); + } + + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testDeleteBookmark() { + final long now = System.currentTimeMillis(); + final long lastUpdateTime[] = { now, now - 1000 * 60 }; + final long createdTime[] = { now - 1000 * 60 * 60, now - 1000 * 60 * 60 * 60 }; + final String url[] = { "http://www.google.com/", "http://mail.google.com/" }; + final int visits[] = { 2, 20 }; + final String title[] = { "Google", "Mail" }; + final int isBookmark[] = { 1, 0 }; + Uri[] uris = new Uri[2]; + byte[][] icons = { FAVICON_DATA, null }; + for (int i = 0; i < uris.length; i++) { + uris[i] = addBookmark(url[i], title[i], lastUpdateTime[i], createdTime[i], visits[i], + icons[i], isBookmark[i]); + assertNotNull(uris[i]); + } + + String[] selectionArgs = { String.valueOf(lastUpdateTime[0]), + String.valueOf(isBookmark[0]) }; + getContentResolver().delete(mBookmarksUri, BookmarkColumns.FAVICON + " IS NOT NULL AND " + + BookmarkColumns.DATE + "= ? AND " + BookmarkColumns.BOOKMARK + " = ?", + selectionArgs); + Cursor cursor = getContentResolver().query(uris[0], null, null, null, null); + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + cursor = getContentResolver().query(uris[1], null, null, null, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + String[] selectionArgs1 = { String.valueOf(lastUpdateTime[1]), + String.valueOf(isBookmark[1]) }; + getContentResolver().delete(mBookmarksUri, BookmarkColumns.FAVICON + " IS NULL AND " + + BookmarkColumns.DATE + "= ? AND " + BookmarkColumns.BOOKMARK + " = ?", + selectionArgs1); + cursor = getContentResolver().query(uris[1], null, null, null, null); + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + } + + /* + * Copied from CTS test with minor adaptations. + */ + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testBookmarksTable() { + final String[] bookmarksProjection = new String[] { + BookmarkColumns.ID, BookmarkColumns.URL, BookmarkColumns.VISITS, + BookmarkColumns.DATE, BookmarkColumns.CREATED, BookmarkColumns.BOOKMARK, + BookmarkColumns.TITLE, BookmarkColumns.FAVICON }; + final int idIndex = 0; + final int urlIndex = 1; + final int visitsIndex = 2; + final int dataIndex = 3; + final int createdIndex = 4; + final int bookmarkIndex = 5; + final int titleIndex = 6; + final int faviconIndex = 7; + + final String insertBookmarkTitle = "bookmark_insert"; + final String insertBookmarkUrl = "www.bookmark_insert.com"; + + final String updateBookmarkTitle = "bookmark_update"; + final String updateBookmarkUrl = "www.bookmark_update.com"; + + // Test: insert. + ContentValues value = new ContentValues(); + long createDate = new Date().getTime(); + value.put(BookmarkColumns.TITLE, insertBookmarkTitle); + value.put(BookmarkColumns.URL, insertBookmarkUrl); + value.put(BookmarkColumns.VISITS, 0); + value.put(BookmarkColumns.DATE, createDate); + value.put(BookmarkColumns.CREATED, createDate); + value.put(BookmarkColumns.BOOKMARK, 0); + + Uri insertUri = getContentResolver().insert(mBookmarksUri, value); + Cursor cursor = getContentResolver().query( + mBookmarksUri, + bookmarksProjection, + BookmarkColumns.TITLE + " = ?", + new String[] { insertBookmarkTitle }, + BookmarkColumns.DATE); + assertTrue(cursor.moveToNext()); + assertEquals(insertBookmarkTitle, cursor.getString(titleIndex)); + assertEquals(insertBookmarkUrl, cursor.getString(urlIndex)); + assertEquals(0, cursor.getInt(visitsIndex)); + assertEquals(createDate, cursor.getLong(dataIndex)); + assertEquals(createDate, cursor.getLong(createdIndex)); + assertEquals(0, cursor.getInt(bookmarkIndex)); + // TODO(michaelbai): according to the test this should be null instead of an empty byte[]. + // BUG 6288508 + // assertTrue(cursor.isNull(FAVICON_INDEX)); + int Id = cursor.getInt(idIndex); + cursor.close(); + + // Test: update. + value.clear(); + long updateDate = new Date().getTime(); + value.put(BookmarkColumns.TITLE, updateBookmarkTitle); + value.put(BookmarkColumns.URL, updateBookmarkUrl); + value.put(BookmarkColumns.VISITS, 1); + value.put(BookmarkColumns.DATE, updateDate); + + getContentResolver().update(mBookmarksUri, value, + BookmarkColumns.TITLE + " = ?", + new String[] { insertBookmarkTitle }); + cursor = getContentResolver().query( + mBookmarksUri, + bookmarksProjection, + BookmarkColumns.ID + " = " + Id, + null, null); + assertTrue(cursor.moveToNext()); + assertEquals(updateBookmarkTitle, cursor.getString(titleIndex)); + assertEquals(updateBookmarkUrl, cursor.getString(urlIndex)); + assertEquals(1, cursor.getInt(visitsIndex)); + assertEquals(updateDate, cursor.getLong(dataIndex)); + assertEquals(createDate, cursor.getLong(createdIndex)); + assertEquals(0, cursor.getInt(bookmarkIndex)); + // TODO(michaelbai): according to the test this should be null instead of an empty byte[]. + // BUG 6288508 + // assertTrue(cursor.isNull(FAVICON_INDEX)); + assertEquals(Id, cursor.getInt(idIndex)); + + // Test: delete. + getContentResolver().delete(insertUri, null, null); + cursor = getContentResolver().query( + mBookmarksUri, + bookmarksProjection, + BookmarkColumns.ID + " = " + Id, + null, null); + assertEquals(0, cursor.getCount()); + } + + /** + * Checks if two byte arrays are equal. Used to compare icons. + * @return True if equal, false otherwise. + */ + private static boolean byteArraysEqual(byte[] byte1, byte[] byte2) { + if (byte1 == null && byte2 != null) { + return byte2.length == 0; + } + if (byte2 == null && byte1 != null) { + return byte1.length == 0; + } + return Arrays.equals(byte1, byte2); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java new file mode 100644 index 0000000..83446bd --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderSearchesUriTest.java
@@ -0,0 +1,177 @@ +// Copyright 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. + +package org.chromium.chrome.browser.provider; + +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.test.suitebuilder.annotation.MediumTest; + +import org.chromium.base.test.util.Feature; + +import java.util.Date; + +/** + * Tests the use of the Searches URI as part of the Android provider public API. + */ +public class ProviderSearchesUriTest extends ProviderTestBase { + + private Uri mSearchesUri; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mSearchesUri = ChromeBrowserProvider.getSearchesApiUri(getActivity()); + getContentResolver().delete(mSearchesUri, null, null); + } + + @Override + protected void tearDown() throws Exception { + getContentResolver().delete(mSearchesUri, null, null); + super.tearDown(); + } + + private Uri addSearchTerm(String searchTerm, long searchTime) { + ContentValues values = new ContentValues(); + values.put(SearchColumns.SEARCH, searchTerm); + values.put(SearchColumns.DATE, searchTime); + return getContentResolver().insert(mSearchesUri, values); + } + + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testAddSearchTerm() { + long searchTime = System.currentTimeMillis(); + String searchTerm = "chrome"; + Uri uri = addSearchTerm(searchTerm, searchTime); + assertNotNull(uri); + String[] selectionArgs = { searchTerm, String.valueOf(searchTime) }; + Cursor cursor = getContentResolver().query(uri, null, SearchColumns.SEARCH + "=? AND " + + SearchColumns.DATE + " = ? ", selectionArgs, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToNext()); + int index = cursor.getColumnIndex(SearchColumns.SEARCH); + assertTrue(-1 != index); + assertEquals(searchTerm, cursor.getString(index)); + index = cursor.getColumnIndex(SearchColumns.DATE); + assertTrue(-1 != index); + assertEquals(searchTime, cursor.getLong(index)); + } + + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testUpdateSearchTerm() { + long[] searchTime = { System.currentTimeMillis(), System.currentTimeMillis() - 1000 }; + String[] searchTerm = { "chrome", "chromium" }; + Uri uri = addSearchTerm(searchTerm[0], searchTime[0]); + ContentValues values = new ContentValues(); + values.put(SearchColumns.SEARCH, searchTerm[1]); + values.put(SearchColumns.DATE, searchTime[1]); + getContentResolver().update(uri, values, null, null); + String[] selectionArgs = { searchTerm[0] }; + Cursor cursor = getContentResolver().query(mSearchesUri, null, SearchColumns.SEARCH + "=?", + selectionArgs, null); + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + String[] selectionArgs1 = { searchTerm[1] }; + cursor = getContentResolver().query(mSearchesUri, null, SearchColumns.SEARCH + "=?", + selectionArgs1, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToNext()); + int index = cursor.getColumnIndex(SearchColumns.SEARCH); + assertTrue(-1 != index); + assertEquals(searchTerm[1], cursor.getString(index)); + index = cursor.getColumnIndex(SearchColumns.DATE); + assertTrue(-1 != index); + assertEquals(searchTime[1], cursor.getLong(index)); + } + + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testDeleteSearchTerm() { + long[] searchTime = { System.currentTimeMillis(), System.currentTimeMillis() - 1000 }; + String[] searchTerm = {"chrome", "chromium"}; + Uri uri[] = new Uri[2]; + for (int i = 0; i < uri.length; i++) { + uri[i] = addSearchTerm(searchTerm[i], searchTime[i]); + } + getContentResolver().delete(uri[0], null, null); + String[] selectionArgs = { searchTerm[0] }; + Cursor cursor = getContentResolver().query(mSearchesUri, null, SearchColumns.SEARCH + "=?", + selectionArgs, null); + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + String[] selectionArgs1 = { searchTerm[1] }; + cursor = getContentResolver().query(mSearchesUri, null, SearchColumns.SEARCH + "=?", + selectionArgs1, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToNext()); + int index = cursor.getColumnIndex(SearchColumns.SEARCH); + assertTrue(-1 != index); + assertEquals(searchTerm[1], cursor.getString(index)); + index = cursor.getColumnIndex(SearchColumns.DATE); + assertTrue(-1 != index); + assertEquals(searchTime[1], cursor.getLong(index)); + getContentResolver().delete(uri[1], null, null); + cursor = getContentResolver().query(uri[1], null, null, null, null); + assertNotNull(cursor); + assertEquals(0, cursor.getCount()); + } + + // Copied from CTS test with minor adaptations. + @MediumTest + @Feature({"Android-ContentProvider"}) + public void testSearchesTable() { + final int idIndex = 0; + String insertSearch = "search_insert"; + String updateSearch = "search_update"; + + // Test: insert + ContentValues value = new ContentValues(); + long createDate = new Date().getTime(); + value.put(SearchColumns.SEARCH, insertSearch); + value.put(SearchColumns.DATE, createDate); + + Uri insertUri = getContentResolver().insert(mSearchesUri, value); + Cursor cursor = getContentResolver().query(mSearchesUri, + ChromeBrowserProvider.SEARCHES_PROJECTION, SearchColumns.SEARCH + " = ?", + new String[] { insertSearch }, null); + assertTrue(cursor.moveToNext()); + assertEquals(insertSearch, + cursor.getString(ChromeBrowserProvider.SEARCHES_PROJECTION_SEARCH_INDEX)); + assertEquals(createDate, + cursor.getLong(ChromeBrowserProvider.SEARCHES_PROJECTION_DATE_INDEX)); + int id = cursor.getInt(idIndex); + cursor.close(); + + // Test: update + value.clear(); + long updateDate = new Date().getTime(); + value.put(SearchColumns.SEARCH, updateSearch); + value.put(SearchColumns.DATE, updateDate); + + getContentResolver().update(mSearchesUri, value, + SearchColumns.ID + " = " + id, null); + cursor = getContentResolver().query(mSearchesUri, + ChromeBrowserProvider.SEARCHES_PROJECTION, + SearchColumns.ID + " = " + id, null, null); + assertTrue(cursor.moveToNext()); + assertEquals(updateSearch, + cursor.getString(ChromeBrowserProvider.SEARCHES_PROJECTION_SEARCH_INDEX)); + assertEquals(updateDate, + cursor.getLong(ChromeBrowserProvider.SEARCHES_PROJECTION_DATE_INDEX)); + assertEquals(id, cursor.getInt(idIndex)); + + // Test: delete + getContentResolver().delete(insertUri, null, null); + cursor = getContentResolver().query(mSearchesUri, + ChromeBrowserProvider.SEARCHES_PROJECTION, + SearchColumns.ID + " = " + id, null, null); + assertEquals(0, cursor.getCount()); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderTestBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderTestBase.java new file mode 100644 index 0000000..cff5916 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/provider/ProviderTestBase.java
@@ -0,0 +1,62 @@ +// Copyright 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. + +package org.chromium.chrome.browser.provider; + +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.pm.ProviderInfo; +import android.test.IsolatedContext; +import android.test.mock.MockContentResolver; + +import org.chromium.base.ThreadUtils; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.test.ChromeActivityTestCaseBase; + +/** + * Base class for Chrome's ContentProvider tests. + * Sets up a local ChromeBrowserProvider associated to a mock resolver in an isolated context. + */ +public class ProviderTestBase extends ChromeActivityTestCaseBase<ChromeActivity> { + + private IsolatedContext mContext; + + public ProviderTestBase() { + super(ChromeActivity.class); + } + + @Override + public void startMainActivity() throws InterruptedException { + startMainActivityOnBlankPage(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + final ChromeActivity activity = getActivity(); + assertNotNull(activity); + + final ContentProvider provider = new ChromeBrowserProvider(); + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.authority = ChromeBrowserProvider.getApiAuthority(activity); + provider.attachInfo(activity, providerInfo); + } + }); + + MockContentResolver resolver = new MockContentResolver(); + resolver.addProvider(ChromeBrowserProvider.getApiAuthority(activity), provider); + resolver.addProvider(ChromeBrowserProvider.getInternalAuthority(activity), provider); + + mContext = new IsolatedContext(resolver, activity); + assertTrue(getContentResolver() instanceof MockContentResolver); + } + + protected ContentResolver getContentResolver() { + return mContext.getContentResolver(); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/OffTheRecordTabModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/OffTheRecordTabModelTest.java index 3943abc..3fcdc0f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/OffTheRecordTabModelTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/OffTheRecordTabModelTest.java
@@ -37,7 +37,7 @@ @Override public void run() { getActivity().getTabCreator(true).createNewTab(new LoadUrlParams("about:blank"), - TabLaunchType.FROM_MENU_OR_OVERVIEW, null); + TabLaunchType.FROM_CHROME_UI, null); } }); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java index 411d57b..f2e99c6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/UndoTabModelTest.java
@@ -67,7 +67,7 @@ @Override public void run() { tabCreator.createNewTab(new LoadUrlParams("about:blank"), - TabLaunchType.FROM_MENU_OR_OVERVIEW, null); + TabLaunchType.FROM_CHROME_UI, null); } }); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/video/VideoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/video/VideoTest.java index ed43cc2d..8393d50 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/video/VideoTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/video/VideoTest.java
@@ -7,6 +7,7 @@ import android.os.Environment; import android.test.suitebuilder.annotation.LargeTest; +import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.Feature; import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.tab.Tab; @@ -27,6 +28,7 @@ mSkipCheckHttpServer = true; } + @DisableIf.Build(sdk_is_less_than = 19, message = "crbug.com/582067") @Feature({"Media", "Media-Video", "Main"}) @LargeTest public void testLoadMediaUrl() throws InterruptedException, TimeoutException {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelperTest.java index fd4e8f5..cc8e7867 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelperTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/gcore/GoogleApiClientHelperTest.java
@@ -5,6 +5,7 @@ package org.chromium.chrome.browser.gcore; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,6 +36,8 @@ @Before public void setUp() { + ApplicationStatus.destroyForJUnitTests(); + LifecycleHook.destroyInstanceForJUnitTests(); mMockClient = mock(GoogleApiClient.class); } @@ -54,11 +57,11 @@ Robolectric.pauseMainLooper(); helper.onConnectionFailed(mockResult); - verify(mMockClient, times(0)).connect(); + verify(mMockClient, never()).connect(); Robolectric.unPauseMainLooper(); Robolectric.runUiThreadTasksIncludingDelayedTasks(); - verify(mMockClient, times(1)).connect(); + verify(mMockClient).connect(); } /** Tests that the connection handler gives up after a number of connection attempts. */ @@ -74,7 +77,7 @@ Robolectric.runUiThreadTasksIncludingDelayedTasks(); // Should not retry on unrecoverable errors - verify(mMockClient, times(0)).connect(); + verify(mMockClient, never()).connect(); // Connection attempts when(mockResult.getErrorCode()).thenReturn(ConnectionResult.SERVICE_UPDATING); @@ -140,12 +143,12 @@ ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED); // The helper should have been registered to handle connectivity issues. - verify(mMockClient, times(1)).registerConnectionCallbacks(helper); - verify(mMockClient, times(1)).registerConnectionFailedListener(helper); + verify(mMockClient).registerConnectionCallbacks(helper); + verify(mMockClient).registerConnectionFailedListener(helper); // Client was not connected. Coming in the foreground should not change that. ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED); - verify(mMockClient, times(0)).connect(); + verify(mMockClient, never()).connect(); // We now say we are connected when(mMockClient.isConnected()).thenReturn(true); @@ -153,21 +156,21 @@ // Should be disconnected when we go in the background ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED); Robolectric.runUiThreadTasksIncludingDelayedTasks(); - verify(mMockClient, times(1)).disconnect(); + verify(mMockClient).disconnect(); // Should be reconnected when we come in the foreground ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED); - verify(mMockClient, times(1)).connect(); + verify(mMockClient).connect(); helper.disable(); // The helper should have been unregistered from handling connectivity issues. - verify(mMockClient, times(1)).unregisterConnectionCallbacks(helper); - verify(mMockClient, times(1)).unregisterConnectionFailedListener(helper); + verify(mMockClient).unregisterConnectionCallbacks(helper); + verify(mMockClient).unregisterConnectionFailedListener(helper); // Should not be interacted with anymore when we stop managing it. ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED); - verify(mMockClient, times(1)).disconnect(); + verify(mMockClient).disconnect(); } @Test @@ -185,23 +188,102 @@ // Should not be disconnected when we go in the background ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED); Robolectric.runUiThreadTasks(); - verify(mMockClient, times(0)).disconnect(); + verify(mMockClient, never()).disconnect(); // Should be disconnected when we wait. Robolectric.runUiThreadTasksIncludingDelayedTasks(); - verify(mMockClient, times(1)).disconnect(); + verify(mMockClient).disconnect(); // Should be reconnected when we come in the foreground ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED); - verify(mMockClient, times(1)).connect(); + verify(mMockClient).connect(); // Should not disconnect when we became visible during the delay ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED); Robolectric.runUiThreadTasks(); - verify(mMockClient, times(1)).disconnect(); + verify(mMockClient).disconnect(); ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED); Robolectric.runUiThreadTasksIncludingDelayedTasks(); - verify(mMockClient, times(1)).disconnect(); + verify(mMockClient).disconnect(); } + @Test + @Feature({"GCore"}) + public void disconnectionCancellingTest() { + int disconnectionTimeout = 5000; + GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient); + Robolectric.runUiThreadTasksIncludingDelayedTasks(); + Activity mockActivity = mock(Activity.class); + ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED); + helper.setDisconnectionDelay(disconnectionTimeout); + + // We have a connected client + when(mMockClient.isConnected()).thenReturn(true); + + // We go in the background and come back before the end of the timeout. + ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED); + Robolectric.idleMainLooper(disconnectionTimeout - 42); + ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED); + Robolectric.runUiThreadTasksIncludingDelayedTasks(); + + // The client should not have been disconnected, which would drop requests otherwise. + verify(mMockClient, never()).disconnect(); + } + + @Test + @Feature({"GCore"}) + public void willUseConnectionBackgroundTest() { + int disconnectionTimeout = 5000; + int arbitraryNumberOfSeconds = 42; + GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient); + Robolectric.runUiThreadTasksIncludingDelayedTasks(); + Activity mockActivity = mock(Activity.class); + ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED); + helper.setDisconnectionDelay(disconnectionTimeout); + + // We have a connected client + when(mMockClient.isConnected()).thenReturn(true); + + // We go in the background and extend the delay + ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STOPPED); + Robolectric.idleMainLooper(disconnectionTimeout - arbitraryNumberOfSeconds); + helper.willUseConnection(); + + // The client should not have been disconnected. + Robolectric.idleMainLooper(disconnectionTimeout - arbitraryNumberOfSeconds); + verify(mMockClient, never()).disconnect(); + + // After the full timeout it should still disconnect though + Robolectric.idleMainLooper(arbitraryNumberOfSeconds); + verify(mMockClient).disconnect(); + + // The client is now disconnected then + when(mMockClient.isConnected()).thenReturn(false); + + // The call should reconnect a disconnected client + helper.willUseConnection(); + verify(mMockClient).connect(); + } + + @Test + @Feature({"GCore"}) + public void willUseConnectionForegroundTest() { + GoogleApiClientHelper helper = new GoogleApiClientHelper(mMockClient); + Robolectric.runUiThreadTasksIncludingDelayedTasks(); + Activity mockActivity = mock(Activity.class); + ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.CREATED); + helper.setDisconnectionDelay(5000); + + // We have a connected client + when(mMockClient.isConnected()).thenReturn(true); + + // We are in the foreground + ApplicationStatus.onStateChangeForTesting(mockActivity, ActivityState.STARTED); + Robolectric.runUiThreadTasksIncludingDelayedTasks(); + + // Disconnections should not be scheduled when in the foreground. + helper.willUseConnection(); + Robolectric.runUiThreadTasksIncludingDelayedTasks(); + verify(mMockClient, never()).disconnect(); + } }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 7a4013e..347d431a 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -351,9 +351,6 @@ <message name="IDS_WEBSITE_SETTINGS_SECURE_TRANSPORT" desc="Text that is displayed in the header of the Website Settings popup if the website uses secure transport."> Your connection to this site is private. </message> - <message name="IDS_WEBSITE_SETTINGS_NON_SECURE_TRANSPORT" desc="Text that is displayed in the header of the Website Settings popup if the website uses non-secure transport."> - Your connection to this site is not private. - </message> <message name="IDS_WEBSITE_DEPRECATED_SIGNATURE_ALGORITHM" desc="Text that is displayed in the header of the Website Settings popup if the website uses SHA-1 for certificate signatures, which are no longer considered secure."> This site uses a weak security configuration (SHA-1 signatures), so your connection may not be private. </message> @@ -449,9 +446,6 @@ <message name="IDS_DOWNLOAD_TITLE" desc="Title for the downloads tab."> Downloads </message> - <message name="IDS_DEFAULT_TAB_TITLE" desc="The default title in a tab."> - Untitled - </message> <message name="IDS_TAB_LOADING_TITLE" desc="The text to be displayed in the title of a tab before a title is discovered."> Loading... </message> @@ -989,20 +983,20 @@ <!-- Spelling submenu --> <if expr="is_macosx"> <message name="IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS" desc="In Title Case: The name of the Language Settings command in the content area context menu"> - &Language Settings... + &Language Settings </message> </if> <if expr="not is_macosx"> <message name="IDS_CONTENT_CONTEXT_SPELLCHECK_MENU" desc="The name of the menu item that shows the submenu for spellcheck options"> - &Spellcheck... + &Spellcheck </message> <message name="IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS" desc="The name of the menu item that opens spellcheck settings in a new tab"> - &Settings... + &Language settings </message> - <message name="IDS_CONTENT_CONTEXT_SPELLCHECK_MULTI_LINGUAL" desc="The name of the menu item that enables spellcheck in all languages"> - &All languages + <message name="IDS_CONTENT_CONTEXT_SPELLCHECK_MULTI_LINGUAL" desc="The name of the menu item that enables spellcheck in all of the languages that the user has selected in settings"> + &All your languages </message> - <message name="IDS_CONTENT_CONTEXT_CHECK_SPELLING_WHILE_TYPING" desc="The name of the Check Spelling while Typing field command in the spelling context menu"> + <message name="IDS_CONTENT_CONTEXT_CHECK_SPELLING_WHILE_TYPING" desc="The name of the menu item that toggles spellcheck on or off"> &Check the spelling of text fields </message> </if> @@ -1660,6 +1654,14 @@ desc="Download page message: Forbidden."> Access to this resource was forbidden by the server. </message> + <message name="IDS_DOWNLOAD_INTERRUPTED_STATUS_UNREACHABLE" + desc="An unexpected response was received which may indicate that the server is not reachable due to an intermediary."> + Server unreachable + </message> + <message name="IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_UNREACHABLE" + desc="Download page message: An unexpected response was received which may indicate that the server is not reachable due to an intermediary."> + The server may be unreachable. Try again later. + </message> <message name="IDS_DOWNLOAD_NOTIFICATION_LABEL_OPEN_WHEN_COMPLETE" desc="Download notifications: Label for 'Open when download is complete' action button on notification"> @@ -5748,7 +5750,7 @@ Web Bluetooth </message> <message name="IDS_FLAGS_WEB_BLUETOOTH_DESCRIPTION" desc="Description for the flag to enable Web Bluetooth."> - Enables Web Bluetooth which could allow websites to connect to and control Bluetooth devices around you. Including keyboards, cameras, microphones, etc. + Enables Web Bluetooth which could allow websites to connect to and control Bluetooth devices around you. </message> <message name="IDS_FLAGS_BLE_ADVERTISING_IN_EXTENSIONS_NAME" desc="Name for the flag for BLE Advertising"> BLE Advertising in Chrome Apps @@ -5999,6 +6001,12 @@ <message name="IDS_FLAG_ENABLE_AUDIO_FOR_DESKTOP_SHARE_DESCRIPTION" desc="Description for the flag to enable Audio Sharing."> With this flag on, desktop share picker window will let the user to choose whether to share audio. </message> + <message name="IDS_FLAG_ENABLE_TAB_FOR_DESKTOP_SHARE" desc="Title for the flag to enable tab for desktop share."> + Enable tab For Desktop Share. + </message> + <message name="IDS_FLAG_ENABLE_TAB_FOR_DESKTOP_SHARE_DESCRIPTION" desc="Description for the flag to enable tab for desktop share."> + Enables user to chose a tab for desktop share. + </message> <message name="IDS_TRACE_UPLOAD_URL_CHOICE_OTHER"> Other </message> @@ -9035,24 +9043,9 @@ </message> <!-- Sad Tab Strings --> - <message name="IDS_SAD_TAB_TITLE" desc="The title of the sad tab page that is shown when a tab crashes. This is intended to be a humorous exclamation of dismay." formatter_data="android_java"> - Aw, Snap! - </message> - <message name="IDS_SAD_TAB_MESSAGE" desc="The message displayed on the sad tab page." formatter_data="android_java"> - Something went wrong while displaying this webpage. - </message> - <message name="IDS_SAD_TAB_HELP_MESSAGE" desc="The help message displayed on the sad tab page, with IDS_SAD_TAB_HELP_LINK embedded as a link to help."> - If you're seeing this frequently, try these <ph name="HELP_LINK">$1<ex>suggestions</ex></ph>. - </message> - <message name="IDS_SAD_TAB_HELP_LINK" desc="The link text displayed on the sad tab page pointing the users to a help article."> - suggestions - </message> <message name="IDS_SAD_TAB_LEARN_MORE_LINK" desc="The link text displayed on the sad tab page pointing the users to a help article."> Learn more </message> - <message name="IDS_SAD_TAB_RELOAD_LABEL" desc="Button label in the sad tab page for reloading a page." formatter_data="android_java"> - Reload - </message> <message name="IDS_CRASHED_TAB_FEEDBACK_MESSAGE" desc="The text to auto-populate the feedback page with. The triple single quotes are needed to preserve the line break."> Tab crashed feedback. ''' @@ -9560,10 +9553,6 @@ desc="The label for a language that cannot be used for spell checking"> This language cannot be used for spell checking </message> - <message name="IDS_OPTIONS_SETTINGS_IS_USED_FOR_SPELL_CHECKING" - desc="The label for a language that is currently used for spell checking"> - This language is used for spell checking - </message> </if> <if expr="not chromeos"> <message name="IDS_OPTIONS_SETTINGS_LANGUAGES_RELAUNCH_BUTTON" @@ -10333,12 +10322,6 @@ ecdsa_sign </message> - <!-- New Tab --> - <message name="IDS_NEW_TAB_TITLE" - desc="Title of the new tab page, not to be confused with the action of opening a new tab."> - New Tab - </message> - <!-- Strings used for non-Android builds --> <if expr="not is_android"> <message name="IDS_APP_DEFAULT_PAGE_NAME" @@ -10349,22 +10332,6 @@ desc="Default name for stand alone web page to serve as an App launcher when the new tab doesn't show apps."> Apps </message> - <message name="IDS_NEW_TAB_OTR_HEADING" - desc="Heading used when a person opens an OTR window"> - You’ve gone incognito - </message> - <message name="IDS_NEW_TAB_OTR_DESCRIPTION" - desc="Used when a person opens an OTR window"> - Pages you view in incognito tabs won’t stick around in your browser’s history, cookie store, or search history after you’ve closed all of your incognito tabs. Any files you download or bookmarks you create will be kept. - </message> - <message name="IDS_NEW_TAB_OTR_LEARN_MORE_LINK" - desc="OTR window link text to learn more"> - Learn more - </message> - <message name="IDS_NEW_TAB_OTR_MESSAGE_WARNING" - desc="OTR window warning message. This follows the IDS_NEW_TAB_OTR_DESCRIPTION paragraph"> - However, you aren’t invisible. Going incognito doesn’t hide your browsing from your employer, your internet service provider, or the websites you visit. - </message> <message name="IDS_NEW_TAB_GUEST_SESSION_HEADING" desc="Heading used when a person enters Guest Session"> You’re browsing as a Guest @@ -10411,10 +10378,6 @@ desc="Notification text shown when a thumbnail has been removed from the most visited section."> Thumbnail removed. </message> - <message name="IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE" - desc="Action link text to undo removing a thumbnail from the most visited section."> - Undo - </message> <message name="IDS_NEW_TAB_REMOVE_THUMBNAIL_TOOLTIP" desc="Tooltip text for the button that removes/blacklists the thumbnail. Once removed the thumbnail will not show up on the new tab page again."> Don't show on this page @@ -11068,9 +11031,6 @@ </message> <!-- Advanced Section Titles --> - <message name="IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY"> - Privacy - </message> <message name="IDS_OPTIONS_ADVANCED_SECTION_TITLE_CONTENT"> Web content </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 117085e..90a1f72 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -132,6 +132,12 @@ <message name="IDS_SETTINGS_PASSWORDS_SAVED_HEADING" desc="The title for a list of username/site/password items. These items are already saved by the browser and can be deleted/edited."> Saved Passwords </message> + <message name="IDS_SETTINGS_PASSWORDS_EXCEPTIONS_HEADING" desc="The title for a list of sites where passwords will not be saved. These items are already saved by the browser and can only be deleted."> + Never Saved + </message> + <message name="IDS_SETTINGS_PASSWORDS_DELETE_EXCEPTION" desc="The alt text for a button that deletes a site for which passwords would not be saved."> + Delete this item + </message> <!-- Default Browser Page --> <if expr="not chromeos"> @@ -439,8 +445,8 @@ <message name="IDS_SETTINGS_SEARCH_EXPLANATION" desc="Explanation for the search engine dropdown setting."> Search engine used when searching from the omnibox </message> - <message name="IDS_SETTINGS_SEARCH_MANAGE_BUTTON_LABEL" desc="Label for the Manage Search Engines button."> - Manage Search Engines + <message name="IDS_SETTINGS_SEARCH_MANAGE_SEARCH_ENGINES" desc="Label for the Manage Search Engines button."> + Manage search engines </message> <message name="IDS_SETTINGS_SEARCH_OK_GOOGLE_LABEL" desc="Label for the checkbox which enables the OK Google hotword."> Enable "Ok Google" to start a voice search. @@ -456,26 +462,29 @@ <message name="IDS_SETTINGS_SEARCH_ENGINES" desc="Name of the settings page which manages search engines."> Search Engines </message> - <message name="IDS_SETTINGS_SEARCH_ENGINES_ADD_SEARCH_ENGINE_LABEL" desc="Label for section for adding a new search engine."> - Add a search engine + <message name="IDS_SETTINGS_SEARCH_ENGINES_ADD_SEARCH_ENGINE" desc="Title for a dialog that allows adding a new search engine."> + Add search engine </message> - <message name="IDS_SETTINGS_SEARCH_ENGINES_LABEL" desc="Label for regular Search Engines section"> - Search engines + <message name="IDS_SETTINGS_SEARCH_ENGINES_EDIT_SEARCH_ENGINE" desc="Title for a dialog that allows editing an existing search engine."> + Edit search engine </message> - <message name="IDS_SETTINGS_SEARCH_ENGINES_OTHER_ENGINES_LABEL" desc="Label for 'other' Search Engines section"> + <message name="IDS_SETTINGS_SEARCH_ENGINES_NOT_VALID" desc="Text indicating that the input to a given text field in the add/edit search engine dialog is not invalid."> + Not valid + </message> + <message name="IDS_SETTINGS_SEARCH_ENGINES_OTHER_ENGINES" desc="Label for 'other' Search Engines section"> Other search engines </message> - <message name="IDS_SETTINGS_SEARCH_ENGINES_SEARCH_ENGINE_LABEL" desc="Label for a table column that holds the name of a search engine"> + <message name="IDS_SETTINGS_SEARCH_ENGINES_SEARCH_ENGINE" desc="Label for a table column that holds the name of a search engine"> Search engine </message> - <message name="IDS_SETTINGS_SEARCH_ENGINES_KEYWORD_LABEL" desc="Label for Keyword column header for a search engine"> + <message name="IDS_SETTINGS_SEARCH_ENGINES_KEYWORD" desc="Label for Keyword column header for a search engine"> Keyword </message> - <message name="IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL_LABEL" desc="Label for Query URL column header for a search engine"> + <message name="IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL" desc="Label for Query URL column header for a search engine"> Query URL </message> - <message name="IDS_SETTINGS_SEARCH_ENGINES_ADD" desc="Label for Add button for adding a new search engine"> - Add + <message name="IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL_EXPLANATION" desc="Label for explaining the format of the URL that should be entered by the user in the add/edit search engine dialog."> + URL with %s in place of query </message> <message name="IDS_SETTINGS_SEARCH_ENGINES_MAKE_DEFAULT" desc="Text of the button that makes the selected engine the default search engine"> Make default @@ -621,6 +630,9 @@ </message> <if expr="chromeos"> + <message name="IDS_SETTINGS_PEOPLE_ENABLE_SCREENLOCK" desc="The text on the checkbox to enable screenlocker for current user."> + Require password to wake from sleep + </message> <message name="IDS_SETTINGS_CHANGE_PICTURE_DIALOG_TITLE" desc="Title of the subpage shown when user wants to change his/her picture."> Change picture </message> @@ -666,6 +678,9 @@ <message name="IDS_SETTINGS_CHANGE_PICTURE_PROFILE_LOADING_PHOTO" desc="The text on the loading stub for Google profile image."> Google Profile photo (loading) </message> + <message name="IDS_SETTINGS_CHANGE_PICTURE_OLD_PHOTO" desc="The text on the existing photo from camera or file."> + Existing photo from camera or file + </message> </if> <if expr="not chromeos"> @@ -859,4 +874,31 @@ <message name="IDS_SETTINGS_ADVANCED_FONT_SETTINGS" desc="Label for advanced font settings."> Advanced font settings </message> + + <!-- Device Page --> + <if expr="chromeos"> + <message name="IDS_SETTINGS_DEVICE_TITLE" desc="Name of the settings page which displays device and peripheral settings."> + Device + </message> + <!-- Touchpad --> + <message name="IDS_SETTINGS_TOUCHPAD_TITLE" desc="In Device Settings, the title of the Touchpad Settings section."> + Touchpad settings + </message> + <message name="IDS_SETTINGS_TOUCHPAD_TAP_TO_CLICK_ENABLED_LABEL" desc="In Device Settings, the text next to the checkbox that allows tapping the touchpad to work as a click."> + Enable tap-to-click + </message> + <message name="IDS_SETTINGS_SCROLL_LABEL" desc="In Device Settings, the title above the radio buttons to choose the scrolling behavior."> + Scrolling + </message> + <message name="IDS_SETTINGS_NATURAL_SCROLL_LABEL" desc="In Device Settings, the text next to the radio button that enables natural scrolling."> + Australian <ph name="BEGIN_LINK"><a href="$1" target="_blank"></ph>Learn more<ph name="END_LINK"></a></ph> + </message> + <message name="IDS_SETTINGS_TRADITIONAL_SCROLL_LABEL" desc="In Device Settings, the text next to the radio button that enables traditional scrolling."> + Traditional + </message> + <!-- Keyboard --> + <message name="IDS_SETTINGS_KEYBOARD_TITLE" desc="In Device Settings, the title for keyboard settings."> + Keyboard settings + </message> + </if> </grit-part>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 6ec6101..e86e4628 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -513,7 +513,6 @@ gypi_values.chrome_browser_policy_shared_with_ios_not_chromeos_sources, ".", "//chrome") - defines = [ "FRAME_AVATAR_BUTTON=1" ] } if (is_win || is_mac || is_desktop_linux) { sources += rebase_path(gypi_values.chrome_browser_policy_desktop_sources, @@ -964,10 +963,10 @@ deps += [ "//chrome/browser/media/router" ] } - if (enable_mojo_media == "browser") { - deps += [ "//media/mojo/services:application" ] - } else if (enable_mojo_media != "none") { - configs += [ "//media/mojo/services:enable_mojo_media_config" ] + if (mojo_media_host == "browser") { + deps += [ "//media/mojo/services:application_factory" ] + } else if (enable_mojo_media) { + configs += [ "//media/mojo/services:mojo_media_config" ] } if (enable_wayland_server) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index b06ac05..8113fcc 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -669,11 +669,12 @@ IDS_FLAGS_SHOW_AUTOFILL_TYPE_PREDICTIONS_NAME, IDS_FLAGS_SHOW_AUTOFILL_TYPE_PREDICTIONS_DESCRIPTION, kOsAll, SINGLE_VALUE_TYPE(autofill::switches::kShowAutofillTypePredictions)}, - {"disable-smooth-scrolling", // FLAGS:RECORD_UMA + {"smooth-scrolling", // FLAGS:RECORD_UMA IDS_FLAGS_SMOOTH_SCROLLING_NAME, IDS_FLAGS_SMOOTH_SCROLLING_DESCRIPTION, // Mac has a separate implementation with its own setting to disable. kOsLinux | kOsCrOS | kOsWin | kOsAndroid, - SINGLE_DISABLE_VALUE_TYPE(switches::kDisableSmoothScrolling)}, + ENABLE_DISABLE_VALUE_TYPE(switches::kEnableSmoothScrolling, + switches::kDisableSmoothScrolling)}, #if defined(USE_AURA) || defined(OS_LINUX) {"overlay-scrollbars", IDS_FLAGS_OVERLAY_SCROLLBARS_NAME, IDS_FLAGS_OVERLAY_SCROLLBARS_DESCRIPTION, @@ -807,11 +808,6 @@ proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery)}, #endif #if defined(USE_ASH) - {"disable-minimize-on-second-launcher-item-click", - IDS_FLAGS_MINIMIZE_ON_SECOND_LAUNCHER_ITEM_CLICK_NAME, - IDS_FLAGS_MINIMIZE_ON_SECOND_LAUNCHER_ITEM_CLICK_DESCRIPTION, kOsAll, - SINGLE_DISABLE_VALUE_TYPE( - switches::kDisableMinimizeOnSecondLauncherItemClick)}, {"show-touch-hud", IDS_FLAGS_SHOW_TOUCH_HUD_NAME, IDS_FLAGS_SHOW_TOUCH_HUD_DESCRIPTION, kOsAll, SINGLE_VALUE_TYPE(ash::switches::kAshTouchHud)}, @@ -1787,9 +1783,9 @@ IDS_FLAGS_ENABLE_WEBFONTS_INTERVENTION_DESCRIPTION, kOsAll, FEATURE_VALUE_TYPE(features::kWebFontsIntervention)}, {"enable-webfonts-intervention-trigger", - IDS_FLAGS_ENABLE_WEBFONTS_INTERVENTION_TRIGGER_NAME, - IDS_FLAGS_ENABLE_WEBFONTS_INTERVENTION_TRIGGER_DESCRIPTION, kOsAll, - SINGLE_VALUE_TYPE(switches::kEnableWebFontsInterventionTrigger)}, + IDS_FLAGS_ENABLE_WEBFONTS_INTERVENTION_TRIGGER_NAME, + IDS_FLAGS_ENABLE_WEBFONTS_INTERVENTION_TRIGGER_DESCRIPTION, kOsAll, + SINGLE_VALUE_TYPE(switches::kEnableWebFontsInterventionTrigger)}, {"enable-grouped-history", IDS_FLAGS_ENABLE_GROUPED_HISTORY_NAME, IDS_FLAGS_ENABLE_GROUPED_HISTORY_DESCRIPTION, kOsDesktop, SINGLE_VALUE_TYPE(switches::kHistoryEnableGroupByDomain)}, @@ -1816,7 +1812,11 @@ IDS_FLAG_ENABLE_AUDIO_FOR_DESKTOP_SHARE_DESCRIPTION, kOsAll, SINGLE_VALUE_TYPE(switches::kEnableAudioSupportForDesktopShare)}, - +#if defined(ENABLE_EXTENSIONS) + {"enable-tab-for-desktop-share", IDS_FLAG_ENABLE_TAB_FOR_DESKTOP_SHARE, + IDS_FLAG_ENABLE_TAB_FOR_DESKTOP_SHARE_DESCRIPTION, kOsAll, + SINGLE_VALUE_TYPE(extensions::switches::kEnableTabForDesktopShare)} +#endif // NOTE: Adding new command-line switches requires adding corresponding // entries to enum "LoginCustomFlags" in histograms.xml. See note in // histograms.xml and don't forget to run AboutFlagsHistogramTest unit test.
diff --git a/chrome/browser/android/preferences/pref_service_bridge.cc b/chrome/browser/android/preferences/pref_service_bridge.cc index 18dcba1..3546667 100644 --- a/chrome/browser/android/preferences/pref_service_bridge.cc +++ b/chrome/browser/android/preferences/pref_service_bridge.cc
@@ -540,6 +540,21 @@ GetOriginalProfile()->GetPrefs()->SetBoolean(pref, value); } +static jint GetBrowsingDataDeletionTimePeriod( + JNIEnv* env, + const JavaParamRef<jobject>& obj) { + return GetPrefService()->GetInteger(prefs::kDeleteTimePeriod); +} + +static void SetBrowsingDataDeletionTimePeriod( + JNIEnv* env, + const JavaParamRef<jobject>& obj, + jint time_period) { + DCHECK_GE(time_period, 0); + DCHECK_LE(time_period, BrowsingDataRemover::TIME_PERIOD_LAST); + GetPrefService()->SetInteger(prefs::kDeleteTimePeriod, time_period); +} + static void ClearBrowsingData(JNIEnv* env, const JavaParamRef<jobject>& obj, const JavaParamRef<jintArray>& data_types) {
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc index 7d5c9c4a8..54645635 100644 --- a/chrome/browser/apps/guest_view/web_view_browsertest.cc +++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -2033,7 +2033,10 @@ "web_view/chromevox_injection"); ASSERT_TRUE(guest_web_contents); - EXPECT_EQ("chrome vox test title", monitor.GetNextUtterance()); + for (;;) { + if (monitor.GetNextUtterance() == "chrome vox test title") + break; + } } #endif
diff --git a/chrome/browser/browsing_data/browsing_data_remover.h b/chrome/browser/browsing_data/browsing_data_remover.h index 0f5119e..7ff7412 100644 --- a/chrome/browser/browsing_data/browsing_data_remover.h +++ b/chrome/browser/browsing_data/browsing_data_remover.h
@@ -62,12 +62,20 @@ { public: // Time period ranges available when doing browsing data removals. + // TODO(msramek): As this is now reused on Android, we should move it + // to browsing_data_counter_utils.h (and rename appropriately), so that + // all fundamental types related to browsing data on all platforms are in + // one place. + // + // A Java counterpart will be generated for this enum. + // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser enum TimePeriod { LAST_HOUR = 0, LAST_DAY, LAST_WEEK, FOUR_WEEKS, - EVERYTHING + EVERYTHING, + TIME_PERIOD_LAST = EVERYTHING }; // Mask used for Remove.
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index f6efaedb..4af0317 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -297,7 +297,7 @@ #endif #if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) -#include "media/mojo/services/mojo_media_application.h" +#include "media/mojo/services/mojo_media_application_factory.h" #endif using base::FileDescriptor; @@ -695,6 +695,14 @@ } #endif // !defined(OS_ANDROID) && !defined(OS_IOS) +bool GetDataSaverEnabledPref(const PrefService* prefs) { + // Enable data saver only when data saver pref is enabled and not part of + // "Disabled" group of "SaveDataHeader" experiment. + return prefs->GetBoolean(prefs::kDataSaverEnabled) && + base::FieldTrialList::FindFullName("SaveDataHeader") + .compare("Disabled"); +} + } // namespace ChromeContentBrowserClient::ChromeContentBrowserClient() @@ -2379,8 +2387,7 @@ web_prefs->strict_powerful_feature_restrictions = true; } - web_prefs->data_saver_enabled = - prefs->GetBoolean(prefs::kDataSaverEnabled); + web_prefs->data_saver_enabled = GetDataSaverEnabledPref(prefs); for (size_t i = 0; i < extra_parts_.size(); ++i) extra_parts_[i]->OverrideWebkitPrefs(rvh, web_prefs); @@ -2729,8 +2736,8 @@ void ChromeContentBrowserClient::RegisterInProcessMojoApplications( StaticMojoApplicationMap* apps) { #if (ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) - apps->insert(std::make_pair( - GURL("mojo:media"), base::Bind(&media::MojoMediaApplication::CreateApp))); + apps->insert(std::make_pair(GURL("mojo:media"), + base::Bind(&media::CreateMojoMediaApplication))); #endif }
diff --git a/chrome/browser/chrome_webusb_browser_client.cc b/chrome/browser/chrome_webusb_browser_client.cc index 269cf15..a2a1e065 100644 --- a/chrome/browser/chrome_webusb_browser_client.cc +++ b/chrome/browser/chrome_webusb_browser_client.cc
@@ -60,9 +60,9 @@ } void OpenURL(const GURL& url) { - GetBrowser()->OpenURL( - content::OpenURLParams(url, content::Referrer(), NEW_FOREGROUND_TAB, - ui::PAGE_TRANSITION_AUTO_TOPLEVEL, true)); + GetBrowser()->OpenURL(content::OpenURLParams( + url, content::Referrer(), NEW_FOREGROUND_TAB, + ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false /* is_renderer_initialized */)); } // Delegate for webusb notification
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc index 85da09c..ed463867 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
@@ -130,11 +130,17 @@ const SkBitmap& install_icon) override { DCHECK(task_runner_->RunsTasksOnCurrentThread()); - success_ = true; - name_ = extension->name(); - icon_ = install_icon; - required_platform_version_ = - extensions::KioskModeInfo::Get(extension)->required_platform_version; + const extensions::KioskModeInfo* info = + extensions::KioskModeInfo::Get(extension); + if (info == nullptr) { + LOG(ERROR) << extension->id() << " is not a valid kiosk app."; + success_ = false; + } else { + success_ = true; + name_ = extension->name(); + icon_ = install_icon; + required_platform_version_ = info->required_platform_version; + } NotifyFinishedOnBlockingPool(); } void OnUnpackFailure(const extensions::CrxInstallError& error) override {
diff --git a/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.cc b/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.cc index 0342e2d2..9151fff9 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.cc +++ b/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.cc
@@ -12,6 +12,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" +#include "chrome/browser/chromeos/feedback_util.h" #include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h" #include "chrome/browser/profiles/profile.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" @@ -20,8 +21,6 @@ #include "extensions/browser/extension_system_provider.h" #include "extensions/browser/extensions_browser_client.h" -using feedback::FeedbackData; - namespace chromeos { class KioskDiagnosisRunner::Factory : public BrowserContextKeyedServiceFactory { @@ -83,44 +82,12 @@ } void KioskDiagnosisRunner::StartSystemLogCollection() { - extensions::FeedbackService* service = - extensions::FeedbackPrivateAPI::GetFactoryInstance() - ->Get(profile_) - ->GetService(); - DCHECK(service); - - service->GetSystemInformation( - base::Bind(&KioskDiagnosisRunner::SendSysLogFeedback, - weak_factory_.GetWeakPtr())); -} - -void KioskDiagnosisRunner::SendSysLogFeedback( - const extensions::SystemInformationList& sys_info) { - scoped_refptr<FeedbackData> feedback_data(new FeedbackData()); - - feedback_data->set_context(profile_); - feedback_data->set_description(base::StringPrintf( - "Autogenerated feedback:\nAppId: %s\n(uniquifier:%s)", - app_id_.c_str(), - base::Int64ToString(base::Time::Now().ToInternalValue()).c_str())); - - scoped_ptr<FeedbackData::SystemLogsMap> sys_logs( - new FeedbackData::SystemLogsMap); - for (extensions::SystemInformationList::const_iterator it = sys_info.begin(); - it != sys_info.end(); ++it) { - (*sys_logs.get())[it->get()->key] = it->get()->value; - } - feedback_data->SetAndCompressSystemInfo(std::move(sys_logs)); - - extensions::FeedbackService* service = - extensions::FeedbackPrivateAPI::GetFactoryInstance() - ->Get(profile_) - ->GetService(); - DCHECK(service); - service->SendFeedback(profile_, - feedback_data, - base::Bind(&KioskDiagnosisRunner::OnFeedbackSent, - weak_factory_.GetWeakPtr())); + const std::string description = base::StringPrintf( + "Autogenerated feedback:\nAppId: %s\n(uniquifier:%s)", app_id_.c_str(), + base::Int64ToString(base::Time::Now().ToInternalValue()).c_str()); + feedback_util::SendSysLogFeedback( + profile_, description, base::Bind(&KioskDiagnosisRunner::OnFeedbackSent, + weak_factory_.GetWeakPtr())); } void KioskDiagnosisRunner::OnFeedbackSent(bool) {
diff --git a/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.h b/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.h index 92de7aae1..5df2960 100644 --- a/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.h +++ b/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.h
@@ -9,7 +9,6 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/extensions/api/feedback_private/feedback_service.h" #include "components/keyed_service/core/keyed_service.h" class Profile; @@ -33,7 +32,6 @@ void Start(const std::string& app_id); void StartSystemLogCollection(); - void SendSysLogFeedback(const extensions::SystemInformationList& sys_info); void OnFeedbackSent(bool sent); Profile* profile_;
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index 1cb9f9af..c7b59cc0 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -190,7 +190,8 @@ bool IsRunningAsMusClient() { #if defined(MOJO_SHELL_CLIENT) - return !!content::MojoShellConnection::Get(); + return content::MojoShellConnection::Get() && + content::MojoShellConnection::Get()->UsingExternalShell(); #endif return false; }
diff --git a/chrome/browser/chromeos/extensions/wallpaper_api.cc b/chrome/browser/chromeos/extensions/wallpaper_api.cc index 6c401eb..1e3f078 100644 --- a/chrome/browser/chromeos/extensions/wallpaper_api.cc +++ b/chrome/browser/chromeos/extensions/wallpaper_api.cc
@@ -113,7 +113,7 @@ if (params_->details.data) { StartDecode(*params_->details.data); - } else { + } else if (params_->details.url) { GURL wallpaper_url(*params_->details.url); if (wallpaper_url.is_valid()) { g_wallpaper_fetcher.Get().FetchWallpaper( @@ -123,6 +123,9 @@ SetError("URL is invalid."); SendResponse(false); } + } else { + SetError("Either url or data field is required."); + SendResponse(false); } return true; }
diff --git a/chrome/browser/chromeos/feedback_util.cc b/chrome/browser/chromeos/feedback_util.cc new file mode 100644 index 0000000..18e7e5d --- /dev/null +++ b/chrome/browser/chromeos/feedback_util.cc
@@ -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. + +#include "chrome/browser/chromeos/feedback_util.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h" +#include "chrome/browser/extensions/api/feedback_private/feedback_service.h" +#include "chrome/browser/profiles/profile.h" + +using feedback::FeedbackData; + +namespace feedback_util { + +namespace { + +extensions::FeedbackService* GetFeedbackService(Profile* profile) { + return extensions::FeedbackPrivateAPI::GetFactoryInstance() + ->Get(profile) + ->GetService(); +} + +void OnGetSystemInformation(Profile* profile, + const std::string& description, + const SendSysLogFeedbackCallback& callback, + const extensions::SystemInformationList& sys_info) { + scoped_refptr<FeedbackData> feedback_data(new FeedbackData()); + + feedback_data->set_context(profile); + feedback_data->set_description(description); + + scoped_ptr<FeedbackData::SystemLogsMap> sys_logs( + new FeedbackData::SystemLogsMap); + for (extensions::SystemInformationList::const_iterator it = sys_info.begin(); + it != sys_info.end(); ++it) { + (*sys_logs.get())[it->get()->key] = it->get()->value; + } + feedback_data->SetAndCompressSystemInfo(std::move(sys_logs)); + + GetFeedbackService(profile)->SendFeedback(profile, feedback_data, callback); +} + +} // namespace + +void SendSysLogFeedback(Profile* profile, + const std::string& description, + const SendSysLogFeedbackCallback& callback) { + GetFeedbackService(profile)->GetSystemInformation( + base::Bind(&OnGetSystemInformation, profile, description, callback)); +} + +} // namespace feedback_util
diff --git a/chrome/browser/chromeos/feedback_util.h b/chrome/browser/chromeos/feedback_util.h new file mode 100644 index 0000000..7399061 --- /dev/null +++ b/chrome/browser/chromeos/feedback_util.h
@@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_FEEDBACK_UTIL_H_ +#define CHROME_BROWSER_CHROMEOS_FEEDBACK_UTIL_H_ + +#include <string> + +#include "base/callback_forward.h" + +class Profile; + +namespace feedback_util { + +// Sends a system log feedback from the given |profile| with the +// given |description|. |callback| will be invoked when the feedback is sent. +using SendSysLogFeedbackCallback = base::Callback<void(bool)>; +void SendSysLogFeedback(Profile* profile, + const std::string& description, + const SendSysLogFeedbackCallback& callback); + +} // namespace feedback_util + +#endif // CHROME_BROWSER_CHROMEOS_FEEDBACK_UTIL_H_
diff --git a/chrome/browser/chromeos/file_system_provider/notification_manager.cc b/chrome/browser/chromeos/file_system_provider/notification_manager.cc index 4288a17..a458caf 100644 --- a/chrome/browser/chromeos/file_system_provider/notification_manager.cc +++ b/chrome/browser/chromeos/file_system_provider/notification_manager.cc
@@ -7,7 +7,7 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/extensions/app_icon_loader_impl.h" +#include "chrome/browser/extensions/extension_app_icon_loader.h" #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/grit/generated_resources.h" #include "components/signin/core/account_id/account_id.h" @@ -56,8 +56,7 @@ : profile_(profile), file_system_info_(file_system_info), icon_loader_( - new extensions::AppIconLoaderImpl(profile, kIconSize, this)) { -} + new extensions::ExtensionAppIconLoader(profile, kIconSize, this)) {} NotificationManager::~NotificationManager() { if (callbacks_.size()) { @@ -100,8 +99,8 @@ OnNotificationResult(CONTINUE); } -void NotificationManager::SetAppImage(const std::string& id, - const gfx::ImageSkia& image) { +void NotificationManager::OnAppImageUpdated(const std::string& id, + const gfx::ImageSkia& image) { extension_icon_.reset(new gfx::Image(image)); g_browser_process->message_center()->UpdateNotification( file_system_info_.mount_path().value(), CreateNotification());
diff --git a/chrome/browser/chromeos/file_system_provider/notification_manager.h b/chrome/browser/chromeos/file_system_provider/notification_manager.h index 03d1458..eefb108 100644 --- a/chrome/browser/chromeos/file_system_provider/notification_manager.h +++ b/chrome/browser/chromeos/file_system_provider/notification_manager.h
@@ -13,7 +13,7 @@ #include "base/memory/scoped_ptr.h" #include "chrome/browser/chromeos/file_system_provider/notification_manager_interface.h" #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" -#include "chrome/browser/extensions/app_icon_loader.h" +#include "chrome/browser/ui/app_icon_loader.h" class Profile; @@ -33,7 +33,7 @@ // up to one notification. If more than one request is unresponsive, then // all of them will be aborted when clicking on the notification button. class NotificationManager : public NotificationManagerInterface, - public extensions::AppIconLoader::Delegate { + public AppIconLoaderDelegate { public: NotificationManager(Profile* profile, const ProvidedFileSystemInfo& file_system_info); @@ -51,8 +51,9 @@ // Invoked when the notification got closed either by user or by system. void OnClose(); - // extensions::AppIconLoader::Delegate overrides: - void SetAppImage(const std::string& id, const gfx::ImageSkia& image) override; + // AppIconLoaderDelegate overrides: + void OnAppImageUpdated(const std::string& id, + const gfx::ImageSkia& image) override; private: typedef std::map<int, NotificationCallback> CallbackMap; @@ -67,7 +68,7 @@ Profile* profile_; ProvidedFileSystemInfo file_system_info_; CallbackMap callbacks_; - scoped_ptr<extensions::AppIconLoader> icon_loader_; + scoped_ptr<AppIconLoader> icon_loader_; scoped_ptr<gfx::Image> extension_icon_; DISALLOW_COPY_AND_ASSIGN(NotificationManager);
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc index 8760f2b..2cb3458 100644 --- a/chrome/browser/chromeos/login/chrome_restart_request.cc +++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -191,12 +191,10 @@ // content/browser/renderer_host/render_process_host_impl.cc. cc::switches::kDisableCachedPictureRaster, cc::switches::kDisableCompositedAntialiasing, - cc::switches::kDisableCompositorPropertyTrees, cc::switches::kDisableMainFrameBeforeActivation, cc::switches::kDisableThreadedAnimation, cc::switches::kEnableBeginFrameScheduling, cc::switches::kEnableGpuBenchmarking, - cc::switches::kEnablePropertyTreeVerification, cc::switches::kEnableMainFrameBeforeActivation, cc::switches::kShowCompositedLayerBorders, cc::switches::kShowFPSCounter,
diff --git a/chrome/browser/chromeos/login/easy_unlock/bootstrap_browsertest.cc b/chrome/browser/chromeos/login/easy_unlock/bootstrap_browsertest.cc index bc5a9b1c..73d194d 100644 --- a/chrome/browser/chromeos/login/easy_unlock/bootstrap_browsertest.cc +++ b/chrome/browser/chromeos/login/easy_unlock/bootstrap_browsertest.cc
@@ -28,7 +28,7 @@ namespace { const char kFakeGaiaId[] = "123456"; -const char kFakeUser[] = "test_user@example.com"; +const char kFakeUser[] = "test_user@consumer.example.com"; const char kFakeSid[] = "fake-sid"; const char kFakeLsid[] = "fake-lsid"; const char kFakeRefreshToken[] = "fake-refresh-token";
diff --git a/chrome/browser/chromeos/login/saml/saml_browsertest.cc b/chrome/browser/chromeos/login/saml/saml_browsertest.cc index 0e96325..c98c8064 100644 --- a/chrome/browser/chromeos/login/saml/saml_browsertest.cc +++ b/chrome/browser/chromeos/login/saml/saml_browsertest.cc
@@ -317,6 +317,8 @@ void SetUpCommandLine(base::CommandLine* command_line) override { command_line->AppendSwitch(switches::kOobeSkipPostLogin); + command_line->AppendSwitch( + chromeos::switches::kAllowFailedPolicyFetchForTest); const GURL gaia_url = gaia_https_forwarder_.GetURLForSSLHost(""); const GURL saml_idp_url = saml_https_forwarder_.GetURLForSSLHost("SAML");
diff --git a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc index a08a650..44a3bd79 100644 --- a/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc +++ b/chrome/browser/chromeos/login/users/avatar/user_image_manager_browsertest.cc
@@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/command_line.h" #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -44,6 +45,7 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/testing_browser_process.h" #include "chromeos/chromeos_paths.h" +#include "chromeos/chromeos_switches.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_session_manager_client.h" @@ -117,6 +119,16 @@ ASSERT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir_)); } + void SetUpCommandLine(base::CommandLine* command_line) override { + LoginManagerTest::SetUpCommandLine(command_line); + // These tests create new users and then inject policy after the fact, + // to avoid having to set up a mock policy server. UserCloudPolicyManager + // will shut down the profile if there's an error loading the initial + // policy, so disable this behavior so we can inject policy directly. + command_line->AppendSwitch( + chromeos::switches::kAllowFailedPolicyFetchForTest); + } + void SetUpOnMainThread() override { LoginManagerTest::SetUpOnMainThread(); local_state_ = g_browser_process->local_state();
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc index 52e43cf..9e6c82e2 100644 --- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc +++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
@@ -184,6 +184,10 @@ command_line->AppendSwitch(switches::kLoginManager); command_line->AppendSwitch(switches::kForceLoginManagerInTests); + // Allow policy fetches to fail - these tests instead invoke InjectPolicy() + // to directly inject and modify policy dynamically. + command_line->AppendSwitch(switches::kAllowFailedPolicyFetchForTest); + LoginManagerTest::SetUpCommandLine(command_line); }
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index 9543c895..35392f7 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -304,7 +304,8 @@ // Use the saved screen preference from Local State. const std::string screen_pref = GetLocalState()->GetString(prefs::kOobeScreenPending); - if (is_out_of_box_ && !screen_pref.empty() && !IsHostPairingOobe() && + if (is_out_of_box_ && !screen_pref.empty() && !IsRemoraPairingOobe() && + !IsBootstrappingSlave() && (first_screen_name.empty() || first_screen_name == WizardController::kTestNoScreenName)) { first_screen_name_ = screen_pref; @@ -950,7 +951,7 @@ } else if (screen_name != kTestNoScreenName) { if (is_out_of_box_) { time_oobe_started_ = base::Time::Now(); - if (IsHostPairingOobe()) { + if (IsRemoraPairingOobe() || IsSlavePairingOobe()) { ShowHostPairingScreen(); } else if (CanShowHIDDetectionScreen()) { hid_screen_ = GetScreen(kHIDDetectionScreenName); @@ -1326,19 +1327,23 @@ return true; } -bool WizardController::IsHostPairingOobe() const { - return (IsRemoraRequisition() || IsBootstrappingSlave()) && +bool WizardController::IsRemoraPairingOobe() const { + return IsRemoraRequisition() && (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kHostPairingOobe) || shark_controller_detected_); } +bool WizardController::IsSlavePairingOobe() const { + return IsBootstrappingSlave() && shark_controller_detected_; +} + void WizardController::MaybeStartListeningForSharkConnection() { if (!IsRemoraRequisition() && !IsBootstrappingSlave()) return; // We shouldn't be here if we are running pairing OOBE already. - DCHECK(!IsHostPairingOobe()); + DCHECK(!IsRemoraPairingOobe() && !IsSlavePairingOobe()); if (!shark_connection_listener_) { shark_connection_listener_.reset(
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h index b9129fb..fd3a501b 100644 --- a/chrome/browser/chromeos/login/wizard_controller.h +++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -311,7 +311,10 @@ bool SetOnTimeZoneResolvedForTesting(const base::Closure& callback); // Returns true for pairing remora OOBE. - bool IsHostPairingOobe() const; + bool IsRemoraPairingOobe() const; + + // Returns true for pairing slave OOBE. + bool IsSlavePairingOobe() const; // Starts listening for an incoming shark controller connection, if we are // running remora OOBE.
diff --git a/chrome/browser/chromeos/policy/affiliation_test_helper.cc b/chrome/browser/chromeos/policy/affiliation_test_helper.cc index 9a309867..acdba14 100644 --- a/chrome/browser/chromeos/policy/affiliation_test_helper.cc +++ b/chrome/browser/chromeos/policy/affiliation_test_helper.cc
@@ -133,6 +133,11 @@ void AppendCommandLineSwitchesForLoginManager(base::CommandLine* command_line) { command_line->AppendSwitch(chromeos::switches::kLoginManager); command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests); + // LoginManager tests typically don't stand up a policy test server but + // instead inject policies directly through a SessionManagerClient. So allow + // policy fetches to fail - this is expected. + command_line->AppendSwitch( + chromeos::switches::kAllowFailedPolicyFetchForTest); } } // namespace affiliation_test_helper
diff --git a/chrome/browser/chromeos/policy/blocking_login_browsertest.cc b/chrome/browser/chromeos/policy/blocking_login_browsertest.cc index a57f3ac..651a9264 100644 --- a/chrome/browser/chromeos/policy/blocking_login_browsertest.cc +++ b/chrome/browser/chromeos/policy/blocking_login_browsertest.cc
@@ -81,6 +81,11 @@ const bool enroll_device; }; +// TODO(atwilson): This test is completely broken - it originally was built +// when we made an entirely different set of network calls on startup. As a +// result it generates random failures in startup network requests, then waits +// to see if the profile finishes loading which is not at all what it is +// intended to test. We need to fix this test or remove it (crbug.com/580537). class BlockingLoginTest : public OobeBaseTest, public content::NotificationObserver, @@ -95,6 +100,9 @@ command_line->AppendSwitchASCII( policy::switches::kDeviceManagementUrl, embedded_test_server()->GetURL("/device_management").spec()); + + command_line->AppendSwitch( + chromeos::switches::kAllowFailedPolicyFetchForTest); } void SetUpOnMainThread() override {
diff --git a/chrome/browser/chromeos/policy/login_policy_test_base.h b/chrome/browser/chromeos/policy/login_policy_test_base.h index 4c444414..76d836e 100644 --- a/chrome/browser/chromeos/policy/login_policy_test_base.h +++ b/chrome/browser/chromeos/policy/login_policy_test_base.h
@@ -34,6 +34,10 @@ virtual void GetMandatoryPoliciesValue(base::DictionaryValue* policy) const; virtual void GetRecommendedPoliciesValue(base::DictionaryValue* policy) const; + UserPolicyTestHelper* user_policy_helper() { + return user_policy_helper_.get(); + } + void SkipToLoginScreen(); void LogIn(const std::string& user_id, const std::string& password);
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc index 7e3610c..8fa1b39 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc +++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/command_line.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/metrics/sparse_histogram.h" @@ -24,6 +25,7 @@ #include "chrome/browser/chromeos/policy/wildcard_login_checker.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/common/chrome_content_client.h" +#include "chromeos/chromeos_switches.h" #include "components/policy/core/common/cloud/cloud_external_data_manager.h" #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h" #include "components/policy/core/common/cloud/device_management_service.h" @@ -31,6 +33,7 @@ #include "components/policy/core/common/policy_map.h" #include "components/policy/core/common/policy_pref_names.h" #include "components/policy/core/common/policy_types.h" +#include "components/user_manager/user.h" #include "components/user_manager/user_manager.h" #include "net/url_request/url_request_context_getter.h" #include "policy/policy_constants.h" @@ -99,7 +102,15 @@ wait_for_policy_fetch_(wait_for_policy_fetch), policy_fetch_timeout_(false, false) { time_init_started_ = base::Time::Now(); - if (wait_for_policy_fetch_ && !initial_policy_fetch_timeout.is_max()) { + + // Caller should pass a non-zero policy_fetch_timeout iff + // |wait_for_policy_fetch| is true. + DCHECK_NE(wait_for_policy_fetch_, initial_policy_fetch_timeout.is_zero()); + allow_failed_policy_fetches_ = + base::CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kAllowFailedPolicyFetchForTest) || + !initial_policy_fetch_timeout.is_max(); + if (wait_for_policy_fetch_ && allow_failed_policy_fetches_) { policy_fetch_timeout_.Start( FROM_HERE, initial_policy_fetch_timeout, @@ -260,7 +271,7 @@ } else { // If the client has switched to not registered, we bail out as this // indicates the cloud policy setup flow has been aborted. - CancelWaitForPolicyFetch(); + CancelWaitForPolicyFetch(true); } } } @@ -272,7 +283,18 @@ UMA_HISTOGRAM_SPARSE_SLOWLY(kUMAInitialFetchClientError, cloud_policy_client->status()); } - CancelWaitForPolicyFetch(); + switch (client()->status()) { + case DM_STATUS_SUCCESS: + case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED: + // If management is not supported for this user, then a registration + // error is to be expected. + CancelWaitForPolicyFetch(true); + break; + default: + // Unexpected error fetching policy. + CancelWaitForPolicyFetch(false); + break; + } } void UserCloudPolicyManagerChromeOS::OnComponentCloudPolicyUpdated() { @@ -354,9 +376,6 @@ policy_token, std::string(), std::string(), std::string()); } else { - // Failed to get a token, stop waiting and use an empty policy. - CancelWaitForPolicyFetch(); - UMA_HISTOGRAM_ENUMERATION(kUMAInitialFetchOAuth2Error, error.state(), GoogleServiceAuthError::NUM_STATES); @@ -366,6 +385,9 @@ UMA_HISTOGRAM_SPARSE_SLOWLY(kUMAInitialFetchOAuth2NetworkError, -error.network_error()); } + // Failed to get a token, stop waiting if policy is not required for this + // user. + CancelWaitForPolicyFetch(false); } token_fetcher_.reset(); @@ -378,23 +400,38 @@ now - time_client_registered_); UMA_HISTOGRAM_MEDIUM_TIMES(kUMAInitialFetchDelayTotal, now - time_init_started_); - CancelWaitForPolicyFetch(); + CancelWaitForPolicyFetch(success); } void UserCloudPolicyManagerChromeOS::OnBlockingFetchTimeout() { if (!wait_for_policy_fetch_) return; - LOG(WARNING) << "Timed out while waiting for the initial policy fetch. " - << "The first session will start without policy."; - CancelWaitForPolicyFetch(); + LOG(WARNING) << "Timed out while waiting for the policy fetch. " + << "The session will start with the cached policy."; + CancelWaitForPolicyFetch(false); } -void UserCloudPolicyManagerChromeOS::CancelWaitForPolicyFetch() { +void UserCloudPolicyManagerChromeOS::CancelWaitForPolicyFetch(bool success) { if (!wait_for_policy_fetch_) return; - wait_for_policy_fetch_ = false; policy_fetch_timeout_.Stop(); + + // If there was an error, and we don't want to allow profile initialization + // to go forward after a failed policy fetch, then just return (profile + // initialization will not complete). + // TODO(atwilson): Add code to retry policy fetching. + if (!success && !allow_failed_policy_fetches_) { + LOG(ERROR) << "Policy fetch failed for " + << user_manager::UserManager::Get()->GetActiveUser()->email() + << " - aborting profile initialization"; + // Need to exit the current user, because we've already started this user's + // session. + chrome::AttemptUserExit(); + return; + } + + wait_for_policy_fetch_ = false; CheckAndPublishPolicy(); // Now that |wait_for_policy_fetch_| is guaranteed to be false, the scheduler // can be started.
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h index a0eb497..c19588d 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h +++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h
@@ -132,8 +132,10 @@ // Cancels waiting for the policy fetch and flags the // ConfigurationPolicyProvider ready (assuming all other initialization tasks - // have completed). - void CancelWaitForPolicyFetch(); + // have completed). Pass |true| if policy fetch was successful (either + // because policy was successfully fetched, or if DMServer has notified us + // that the user is not managed). + void CancelWaitForPolicyFetch(bool success); void StartRefreshSchedulerIfReady(); @@ -153,6 +155,11 @@ // IsInitializationComplete(). bool wait_for_policy_fetch_; + // Whether we should allow policy fetches to fail, or wait forever until they + // succeed (typically we won't allow them to fail until we have loaded policy + // at least once). + bool allow_failed_policy_fetches_; + // A timer that puts a hard limit on the maximum time to wait for the initial // policy fetch. base::Timer policy_fetch_timeout_;
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc index 6ae59a61..f8d91e76 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc +++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc
@@ -7,14 +7,54 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/run_loop.h" #include "base/values.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/chromeos/login/ui/webui_login_display.h" #include "chrome/browser/chromeos/policy/login_policy_test_base.h" +#include "chrome/browser/chromeos/policy/user_policy_test_helper.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/host_desktop.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "components/user_manager/user.h" +#include "components/user_manager/user_manager.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" #include "policy/policy_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +// Helper class that counts the number of notifications of the specified +// type that have been received. +class CountNotificationObserver : public content::NotificationObserver { + public: + CountNotificationObserver(int notification_type_to_count, + const content::NotificationSource& source) + : notification_count_(0) { + registrar_.Add(this, notification_type_to_count, source); + } + + // NotificationObserver: + void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) override { + // Count the number of notifications seen. + notification_count_++; + } + + int notification_count() const { return notification_count_; } + + private: + int notification_count_; + content::NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(CountNotificationObserver); +}; + +} // namespace namespace policy { @@ -42,6 +82,12 @@ SkipToLoginScreen(); LogIn(kAccountId, kAccountPassword); + // User should be marked as having a valid OAuth token. + const user_manager::UserManager* const user_manager = + user_manager::UserManager::Get(); + EXPECT_EQ(user_manager::User::OAUTH2_TOKEN_STATUS_VALID, + user_manager->GetActiveUser()->oauth_token_status()); + // Check that the startup pages specified in policy were opened. BrowserList* browser_list = BrowserList::GetInstance(); EXPECT_EQ(1U, browser_list->size()); @@ -58,4 +104,24 @@ } } +IN_PROC_BROWSER_TEST_F(UserCloudPolicyManagerTest, ErrorLoadingPolicy) { + // Delete the policy file - this will cause a 500 error on policy requests. + user_policy_helper()->DeletePolicyFile(); + SkipToLoginScreen(); + CountNotificationObserver observer( + chrome::NOTIFICATION_SESSION_STARTED, + content::NotificationService::AllSources()); + GetLoginDisplay()->ShowSigninScreenForCreds(kAccountId, kAccountPassword); + base::RunLoop().Run(); + // Should not receive a SESSION_STARTED notification. + ASSERT_EQ(0, observer.notification_count()); + + // User should not be marked as having a valid OAuth token. That way, if we + // try to load the user in the future, we will attempt to load policy again. + const user_manager::UserManager* user_manager = + user_manager::UserManager::Get(); + EXPECT_NE(user_manager::User::OAUTH2_TOKEN_STATUS_VALID, + user_manager->GetActiveUser()->oauth_token_status()); +} + } // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc index 3fad89c..7695acf 100644 --- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc +++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc
@@ -149,16 +149,35 @@ g_browser_process->platform_part()->browser_policy_connector_chromeos(); const bool is_browser_restart = command_line->HasSwitch(chromeos::switches::kLoginUser); - const bool wait_for_initial_policy = !is_browser_restart; + const user_manager::UserManager* const user_manager = + user_manager::UserManager::Get(); - const base::TimeDelta initial_policy_fetch_timeout = - user_manager::UserManager::Get()->IsCurrentUserNew() - ? base::TimeDelta::Max() - : base::TimeDelta::FromSeconds(kInitialPolicyFetchTimeoutSeconds); + // We want to block for policy in a few situations: if the user is new, or + // if we are forcing an online signin. An online signin will be forced if + // there has been a credential error, or if the initial session creation + // was not completed (the oauth_token_status is not set to valid by + // OAuth2LoginManager until profile creation/session restore is complete). + const bool block_forever_for_policy = + !user_manager->IsLoggedInAsStub() && + (user_manager->IsCurrentUserNew() || + user_manager->GetActiveUser()->force_online_signin() || + user_manager->GetActiveUser()->oauth_token_status() != + user_manager::User::OAUTH2_TOKEN_STATUS_VALID); + + const bool wait_for_policy_fetch = + block_forever_for_policy || !is_browser_restart; + + base::TimeDelta initial_policy_fetch_timeout; + if (block_forever_for_policy) { + initial_policy_fetch_timeout = base::TimeDelta::Max(); + } else if (wait_for_policy_fetch) { + initial_policy_fetch_timeout = + base::TimeDelta::FromSeconds(kInitialPolicyFetchTimeoutSeconds); + } DeviceManagementService* device_management_service = connector->device_management_service(); - if (wait_for_initial_policy) + if (wait_for_policy_fetch) device_management_service->ScheduleInitialization(0); base::FilePath profile_dir = profile->GetPath(); @@ -201,7 +220,7 @@ scoped_ptr<UserCloudPolicyManagerChromeOS> manager( new UserCloudPolicyManagerChromeOS( std::move(store), std::move(external_data_manager), - component_policy_cache_dir, wait_for_initial_policy, + component_policy_cache_dir, wait_for_policy_fetch, initial_policy_fetch_timeout, base::ThreadTaskRunnerHandle::Get(), file_task_runner, io_task_runner));
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.cc b/chrome/browser/chromeos/policy/user_policy_test_helper.cc index b439621e..5f6d28f 100644 --- a/chrome/browser/chromeos/policy/user_policy_test_helper.cc +++ b/chrome/browser/chromeos/policy/user_policy_test_helper.cc
@@ -128,6 +128,10 @@ run_loop.Run(); } +void UserPolicyTestHelper::DeletePolicyFile() { + base::DeleteFile(PolicyFilePath(), false); +} + void UserPolicyTestHelper::WritePolicyFile( const base::DictionaryValue& mandatory, const base::DictionaryValue& recommended) {
diff --git a/chrome/browser/chromeos/policy/user_policy_test_helper.h b/chrome/browser/chromeos/policy/user_policy_test_helper.h index 941a067..a59fc910 100644 --- a/chrome/browser/chromeos/policy/user_policy_test_helper.h +++ b/chrome/browser/chromeos/policy/user_policy_test_helper.h
@@ -49,6 +49,8 @@ const base::DictionaryValue& recommended_policy, Profile* profile); + void DeletePolicyFile(); + private: void WritePolicyFile(const base::DictionaryValue& mandatory, const base::DictionaryValue& recommended);
diff --git a/chrome/browser/chromeos/profiles/profile_list_chromeos_unittest.cc b/chrome/browser/chromeos/profiles/profile_list_chromeos_unittest.cc index 41350f5c..d222bd6 100644 --- a/chrome/browser/chromeos/profiles/profile_list_chromeos_unittest.cc +++ b/chrome/browser/chromeos/profiles/profile_list_chromeos_unittest.cc
@@ -90,10 +90,8 @@ EXPECT_EQ(0, change_count()); // Reset the menu. - avatar_menu_.reset(new AvatarMenu( - manager()->profile_info_cache(), - mock_observer_.get(), - NULL)); + avatar_menu_.reset(new AvatarMenu(manager()->profile_attributes_storage(), + mock_observer_.get(), nullptr)); avatar_menu_->RebuildMenu(); EXPECT_EQ(0, change_count()); return avatar_menu_.get();
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc index 2803ecc0..39013af 100644 --- a/chrome/browser/devtools/devtools_sanity_browsertest.cc +++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -49,6 +49,8 @@ #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/worker_service.h" #include "content/public/browser/worker_service_observer.h" @@ -62,6 +64,7 @@ #include "extensions/common/value_builder.h" #include "net/dns/mock_host_resolver.h" #include "net/test/spawned_test_server/spawned_test_server.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/compositor/compositor_switches.h" #include "ui/gl/gl_switches.h" @@ -89,6 +92,7 @@ const char kNavigateBackTestPage[] = "files/devtools/navigate_back.html"; const char kWindowOpenTestPage[] = "files/devtools/window_open.html"; +const char kLatencyInfoTestPage[] = "files/devtools/latency_info.html"; const char kChunkedTestPage[] = "chunked"; const char kSlowTestPage[] = "chunked?waitBeforeHeaders=100&waitBetweenChunks=100&chunksNumber=2"; @@ -102,6 +106,25 @@ "files/workers/debug_shared_worker_initialization.js"; template <typename... T> +void DispatchOnTestSuiteSkipCheck(DevToolsWindow* window, + const char* method, + T... args) { + RenderViewHost* rvh = DevToolsWindowTesting::Get(window) + ->main_web_contents() + ->GetRenderViewHost(); + std::string result; + const char* args_array[] = {method, args...}; + std::ostringstream script; + script << "uiTests.dispatchOnTestSuite(["; + for (size_t i = 0; i < arraysize(args_array); ++i) + script << (i ? "," : "") << '\"' << args_array[i] << '\"'; + script << "])"; + ASSERT_TRUE( + content::ExecuteScriptAndExtractString(rvh, script.str(), &result)); + EXPECT_EQ("[OK]", result); +} + +template <typename... T> void DispatchOnTestSuite(DevToolsWindow* window, const char* method, T... args) { @@ -119,16 +142,7 @@ " '' + (window.uiTests && (typeof uiTests.dispatchOnTestSuite)));", &result)); ASSERT_EQ("function", result) << "DevTools front-end is broken."; - - const char* args_array[] = {method, args...}; - std::ostringstream script; - script << "uiTests.dispatchOnTestSuite(["; - for (size_t i = 0; i < arraysize(args_array); ++i) - script << (i ? "," : "") << '\"' << args_array[i] << '\"'; - script << "])"; - ASSERT_TRUE( - content::ExecuteScriptAndExtractString(rvh, script.str(), &result)); - EXPECT_EQ("[OK]", result); + DispatchOnTestSuiteSkipCheck(window, method, args...); } void RunTestFunction(DevToolsWindow* window, const char* test_name) { @@ -168,6 +182,21 @@ CloseDevToolsWindow(); } + template <typename... T> + void RunTestMethod(const char* method, T... args) { + DispatchOnTestSuiteSkipCheck(window_, method, args...); + } + + template <typename... T> + void DispatchAndWait(const char* method, T... args) { + DispatchOnTestSuiteSkipCheck(window_, "waitForAsync", method, args...); + } + + template <typename... T> + void DispatchInPageAndWait(const char* method, T... args) { + DispatchAndWait("invokePageFunctionAsync", method, args...); + } + void LoadTestPage(const std::string& test_page) { GURL url = spawned_test_server()->GetURL(test_page); ui_test_utils::NavigateToURL(browser(), url); @@ -1145,8 +1174,46 @@ #else #define MAYBE_TestScreenshotRecording TestScreenshotRecording #endif -// Tests raw headers text. IN_PROC_BROWSER_TEST_F(DevToolsPixelOutputTests, MAYBE_TestScreenshotRecording) { RunTest("testScreenshotRecording", std::string()); } + +// This test enables switches::kUseGpuInTests which causes false positives +// with MemorySanitizer. +#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) +#define MAYBE_TestLatencyInfoInstrumentation \ + DISABLED_TestLatencyInfoInstrumentation +#else +#define MAYBE_TestLatencyInfoInstrumentation TestLatencyInfoInstrumentation +#endif +IN_PROC_BROWSER_TEST_F(DevToolsPixelOutputTests, + MAYBE_TestLatencyInfoInstrumentation) { + WebContents* web_contents = GetInspectedTab(); + OpenDevToolsWindow(kLatencyInfoTestPage, false); + RunTestMethod("enableExperiment", "timelineLatencyInfo"); + DispatchAndWait("startTimeline"); + + for (int i = 0; i < 3; ++i) { + SimulateMouseEvent(web_contents, blink::WebInputEvent::MouseMove, + gfx::Point(30, 60)); + DispatchInPageAndWait("waitForEvent", "mousemove"); + } + + SimulateMouseClickAt(web_contents, 0, blink::WebPointerProperties::ButtonLeft, + gfx::Point(30, 60)); + DispatchInPageAndWait("waitForEvent", "click"); + + SimulateMouseWheelEvent(web_contents, gfx::Point(300, 100), + gfx::Vector2d(0, 120)); + DispatchInPageAndWait("waitForEvent", "wheel"); + + SimulateTapAt(web_contents, gfx::Point(30, 60)); + DispatchInPageAndWait("waitForEvent", "gesturetap"); + + DispatchAndWait("stopTimeline"); + RunTestMethod("checkInputEventsPresent", "MouseMove", "MouseDown", + "MouseWheel", "GestureTap"); + + CloseDevToolsWindow(); +}
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc index 5fdcbf45..d871837 100644 --- a/chrome/browser/download/download_browsertest.cc +++ b/chrome/browser/download/download_browsertest.cc
@@ -398,8 +398,12 @@ // Information passed in to |DownloadFileCheckErrors()|. struct DownloadInfo { - const char* url_name; // URL for the download. - DownloadMethod download_method; // Navigation or Direct. + const char* starting_url; // URL for initiating the download. + const char* expected_download_url; // Expected value of DI::GetURL(). Can + // be different if |starting_url| + // initiates a download from another + // URL. + DownloadMethod download_method; // Navigation or Direct. // Download interrupt reason (NONE is OK). content::DownloadInterruptReason reason; bool show_download_item; // True if the download item appears on the shelf. @@ -832,24 +836,33 @@ void DownloadFilesCheckErrorsLoopBody(const DownloadInfo& download_info, size_t i) { - SCOPED_TRACE( - ::testing::Message() - << " " << __FUNCTION__ << "()" - << " index = " << i << " url = '" << download_info.url_name << "'" - << " method = " << ((download_info.download_method == DOWNLOAD_DIRECT) - ? "DOWNLOAD_DIRECT" - : "DOWNLOAD_NAVIGATE") - << " show_item = " << download_info.show_download_item << " reason = " - << DownloadInterruptReasonToString(download_info.reason)); + SCOPED_TRACE(testing::Message() + << " " << __FUNCTION__ << "()" + << " index = " << i << " starting_url = '" + << download_info.starting_url << "'" + << " download_url = '" << download_info.expected_download_url + << "'" + << " method = " + << ((download_info.download_method == DOWNLOAD_DIRECT) + ? "DOWNLOAD_DIRECT" + : "DOWNLOAD_NAVIGATE") + << " show_item = " << download_info.show_download_item + << " reason = " + << DownloadInterruptReasonToString(download_info.reason)); std::vector<DownloadItem*> download_items; GetDownloads(browser(), &download_items); size_t downloads_expected = download_items.size(); - std::string server_path = "/downloads/"; - server_path += download_info.url_name; - GURL url = embedded_test_server()->GetURL(server_path); - ASSERT_TRUE(url.is_valid()); + // GURL("http://foo/bar").Resolve("baz") => "http://foo/bar/baz" + // GURL("http://foo/bar").Resolve("http://baz") => "http://baz" + // I.e. both starting_url and expected_download_url can either be relative + // to the base test server URL or be an absolute URL. + GURL base_url = embedded_test_server()->GetURL("/downloads/"); + GURL starting_url = base_url.Resolve(download_info.starting_url); + GURL download_url = base_url.Resolve(download_info.expected_download_url); + ASSERT_TRUE(starting_url.is_valid()); + ASSERT_TRUE(download_url.is_valid()); DownloadManager* download_manager = DownloadManagerForBrowser(browser()); WebContents* web_contents = @@ -873,7 +886,7 @@ creation_observer(new content::DownloadTestItemCreationObserver); scoped_ptr<DownloadUrlParameters> params( - DownloadUrlParameters::FromWebContents(web_contents, url)); + DownloadUrlParameters::FromWebContents(web_contents, starting_url)); params->set_callback(creation_observer->callback()); DownloadManagerForBrowser(browser())->DownloadUrl(std::move(params)); @@ -881,22 +894,12 @@ // won't be. creation_observer->WaitForDownloadItemCreation(); - ASSERT_EQ(download_info.show_download_item, - creation_observer->succeeded()); - if (download_info.show_download_item) { - EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, - creation_observer->interrupt_reason()); - EXPECT_NE(content::DownloadItem::kInvalidId, - creation_observer->download_id()); - } else { - EXPECT_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE, - creation_observer->interrupt_reason()); - EXPECT_EQ(content::DownloadItem::kInvalidId, - creation_observer->download_id()); - } + EXPECT_NE(content::DownloadItem::kInvalidId, + creation_observer->download_id()); } else { // Navigate to URL normally, wait until done. - ui_test_utils::NavigateToURL(browser(), url); + ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( + browser(), starting_url, 1); } if (download_info.show_download_item) { @@ -926,8 +929,8 @@ item = download_items[d]; } - ASSERT_EQ(url, item->GetOriginalUrl()); - ASSERT_EQ(download_info.reason, item->GetLastReason()); + EXPECT_EQ(download_url, item->GetURL()); + EXPECT_EQ(download_info.reason, item->GetLastReason()); if (item->GetState() == content::DownloadItem::COMPLETE) { // Clean up the file, in case it ended up in the My Documents folder. @@ -1006,7 +1009,7 @@ for (size_t i = 0; i < count; ++i) { // Set up the full URL, for download file tracking. std::string server_path = "/downloads/"; - server_path += info[i].download_info.url_name; + server_path += info[i].download_info.starting_url; GURL url = embedded_test_server()->GetURL(server_path); info[i].error_info.url = url.spec(); @@ -1258,12 +1261,12 @@ // Try to start the download via Javascript and wait for the corresponding // load stop event. content::TestNavigationObserver observer(web_contents); - bool download_assempted; + bool download_attempted; ASSERT_TRUE(content::ExecuteScriptAndExtractBool( browser()->tab_strip_model()->GetActiveWebContents(), "window.domAutomationController.send(startDownload());", - &download_assempted)); - ASSERT_TRUE(download_assempted); + &download_attempted)); + ASSERT_TRUE(download_attempted); observer.Wait(); // Check that we did not download the file. @@ -2291,249 +2294,156 @@ IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadErrorsServer) { DownloadInfo download_info[] = { - { // Normal navigated download. - "a_zip_file.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_NONE, - true, - false - }, - { // Normal direct download. - "a_zip_file.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_NONE, - true, - false - }, - { // Direct download with 404 error. - "there_IS_no_spoon.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, - true, - false - }, - { // Navigated download with 404 error. - "there_IS_no_spoon.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, - false, - false - }, - { // Direct download with 400 error. - "zip_file_not_found.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, - true, - false - }, - { // Navigated download with 400 error. - "zip_file_not_found.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, - false, - false - } - }; + {// Normal navigated download. + "a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_NONE, true, false}, + {// Normal direct download. + "a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_NONE, true, false}, + {// Direct download with 404 error. + "there_IS_no_spoon.zip", "there_IS_no_spoon.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, true, false}, + {// Navigated download with 404 error. + "there_IS_no_spoon.zip", "there_IS_no_spoon.zip", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, false, false}, + {// Direct download with 400 error. + "zip_file_not_found.zip", "zip_file_not_found.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, true, false}, + {// Navigated download with 400 error. + "zip_file_not_found.zip", "", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, false, false}, + {// Simulates clicking on <a href="http://..." download="">. The name does + // not resolve. But since this is an explicit download, the download + // should appear on the shelf and the error should be indicated. + "download-anchor-attrib-name-not-resolved.html", + "http://doesnotexist/shouldnotberesolved", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, true, false}, + {// Simulates clicking on <a href="http://..." download=""> where the URL + // leads to a 404 response. This is different from the previous test case + // in that the ResourceLoader issues a OnResponseStarted() callback since + // the headers are successfully received. + "download-anchor-attrib-404.html", "there_IS_no_spoon.zip", + DOWNLOAD_NAVIGATE, content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + true, false}, + {// Similar to the above, but the resulting response contains a status + // code of 400. + "download-anchor-attrib-400.html", "zip_file_not_found.zip", + DOWNLOAD_NAVIGATE, content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, + true, false}, + {// Direct download of a URL where the hostname doesn't resolve. + "http://doesnotexist/shouldnotdownloadsuccessfully", + "http://doesnotexist/shouldnotdownloadsuccessfully", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, true, false}}; DownloadFilesCheckErrors(arraysize(download_info), download_info); } IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadErrorsFile) { FileErrorInjectInfo error_info[] = { - { // Navigated download with injected "Disk full" error in Initialize(). - { "a_zip_file.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - } - }, - { // Direct download with injected "Disk full" error in Initialize(). - { "a_zip_file.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - } - }, - { // Navigated download with injected "Disk full" error in Write(). - { "a_zip_file.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_WRITE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - } - }, - { // Direct download with injected "Disk full" error in Write(). - { "a_zip_file.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_WRITE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - } - }, - { // Navigated download with injected "Failed" error in Initialize(). - { "a_zip_file.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - } - }, - { // Direct download with injected "Failed" error in Initialize(). - { "a_zip_file.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - } - }, - { // Navigated download with injected "Failed" error in Write(). - { "a_zip_file.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_WRITE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - } - }, - { // Direct download with injected "Failed" error in Write(). - { "a_zip_file.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_WRITE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - } - }, - { // Navigated download with injected "Name too long" error in + {// Navigated download with injected "Disk full" error in Initialize(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, + }}, + {// Direct download with injected "Disk full" error in Initialize(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, + }}, + {// Navigated download with injected "Disk full" error in Write(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_WRITE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, + }}, + {// Direct download with injected "Disk full" error in Write(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_WRITE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, + }}, + {// Navigated download with injected "Failed" error in Initialize(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + }}, + {// Direct download with injected "Failed" error in Initialize(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + }}, + {// Navigated download with injected "Failed" error in Write(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_WRITE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + }}, + {// Direct download with injected "Failed" error in Write(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_WRITE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + }}, + {// Navigated download with injected "Name too long" error in // Initialize(). - { "a_zip_file.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, - } - }, - { // Direct download with injected "Name too long" error in Initialize(). - { "a_zip_file.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, - } - }, - { // Navigated download with injected "Name too long" error in Write(). - { "a_zip_file.zip", - DOWNLOAD_NAVIGATE, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_WRITE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - } - }, - { // Direct download with injected "Name too long" error in Write(). - { "a_zip_file.zip", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_WRITE, - 0, - content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - } - }, - { // Direct download with injected "Disk full" error in 2nd Write(). - { "06bESSE21Evolution.ppt", - DOWNLOAD_DIRECT, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - 1 - }, - { - "", - content::TestFileErrorInjector::FILE_OPERATION_WRITE, - 1, - content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - } - } - }; + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, + }}, + {// Direct download with injected "Name too long" error in Initialize(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, + }}, + {// Navigated download with injected "Name too long" error in Write(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_WRITE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + }}, + {// Direct download with injected "Name too long" error in Write(). + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_WRITE, 0, + content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + }}, + {// Direct download with injected "Disk full" error in 2nd Write(). + {"06bESSE21Evolution.ppt", "06bESSE21Evolution.ppt", DOWNLOAD_DIRECT, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, + { + "", content::TestFileErrorInjector::FILE_OPERATION_WRITE, 1, + content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, + }}}; DownloadInsertFilesErrorCheckErrors(arraysize(error_info), error_info); } IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadErrorReadonlyFolder) { DownloadInfo download_info[] = { - { - "a_zip_file.zip", - DOWNLOAD_DIRECT, - // This passes because we switch to the My Documents folder. - content::DOWNLOAD_INTERRUPT_REASON_NONE, - true, - true - }, - { - "a_zip_file.zip", - DOWNLOAD_NAVIGATE, - // This passes because we switch to the My Documents folder. - content::DOWNLOAD_INTERRUPT_REASON_NONE, - true, - true - } - }; + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, + // This passes because we switch to the My Documents folder. + content::DOWNLOAD_INTERRUPT_REASON_NONE, true, true}, + {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, + // This passes because we switch to the My Documents folder. + content::DOWNLOAD_INTERRUPT_REASON_NONE, true, true}}; DownloadFilesToReadonlyFolder(arraysize(download_info), download_info); }
diff --git a/chrome/browser/download/download_item_model.cc b/chrome/browser/download/download_item_model.cc index e82f24e..46694aec 100644 --- a/chrome/browser/download/download_item_model.cc +++ b/chrome/browser/download/download_item_model.cc
@@ -170,6 +170,10 @@ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN: string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_FORBIDDEN; break; + case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE: + string_id = IDS_DOWNLOAD_INTERRUPTED_STATUS_UNREACHABLE; + break; + case content::DOWNLOAD_INTERRUPT_REASON_NONE: NOTREACHED(); // fallthrough @@ -250,6 +254,9 @@ case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN: string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_FORBIDDEN; break; + case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE: + string_id = IDS_DOWNLOAD_INTERRUPTED_DESCRIPTION_UNREACHABLE; + break; case content::DOWNLOAD_INTERRUPT_REASON_NONE: NOTREACHED(); // fallthrough
diff --git a/chrome/browser/download/download_item_model_unittest.cc b/chrome/browser/download/download_item_model_unittest.cc index 322e722..188e8e0 100644 --- a/chrome/browser/download/download_item_model_unittest.cc +++ b/chrome/browser/download/download_item_model_unittest.cc
@@ -124,56 +124,52 @@ // Expected status string. This will include the progress as well. const char* expected_status; } kTestCases[] = { - { content::DOWNLOAD_INTERRUPT_REASON_NONE, - "1/2 B" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - "Failed - Download error" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, - "Failed - Insufficient permissions" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - "Failed - Disk full" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, - "Failed - Path too long" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE, - "Failed - File too large" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED, - "Failed - Virus detected" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, - "Failed - Blocked" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED, - "Failed - Virus scan failed" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT, - "Failed - File truncated" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, - "Failed - System busy" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, - "Failed - Network error" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT, - "Failed - Network timeout" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, - "Failed - Network disconnected" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN, - "Failed - Server unavailable" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, - "Failed - Network error" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, - "Failed - Server problem" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE, - "Failed - Download error" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, - "Failed - No file" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED, - "Failed - Needs authorization" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM, - "Failed - Bad certificate" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN, - "Failed - Forbidden" }, - { content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED, - "Canceled" }, - { content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN, - "Failed - Shutdown" }, - { content::DOWNLOAD_INTERRUPT_REASON_CRASH, - "Failed - Crash" }, + {content::DOWNLOAD_INTERRUPT_REASON_NONE, "1/2 B"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + "Failed - Download error"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, + "Failed - Insufficient permissions"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, "Failed - Disk full"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, + "Failed - Path too long"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE, + "Failed - File too large"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED, + "Failed - Virus detected"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, "Failed - Blocked"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED, + "Failed - Virus scan failed"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT, + "Failed - File truncated"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, + "Failed - System busy"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + "Failed - Network error"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT, + "Failed - Network timeout"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, + "Failed - Network disconnected"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN, + "Failed - Server unavailable"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, + "Failed - Network error"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, + "Failed - Server problem"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE, + "Failed - Download error"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + "Failed - No file"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED, + "Failed - Needs authorization"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM, + "Failed - Bad certificate"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN, + "Failed - Forbidden"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE, + "Failed - Server unreachable"}, + {content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED, "Canceled"}, + {content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN, "Failed - Shutdown"}, + {content::DOWNLOAD_INTERRUPT_REASON_CRASH, "Failed - Crash"}, }; static_assert(kInterruptReasonCount == arraysize(kTestCases), "interrupt reason mismatch"); @@ -200,56 +196,52 @@ // interrupt reason. The returned string contains a newline. const char* expected_tooltip; } kTestCases[] = { - { content::DOWNLOAD_INTERRUPT_REASON_NONE, - "foo.bar" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, - "foo.bar\nDownload error" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, - "foo.bar\nInsufficient permissions" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, - "foo.bar\nDisk full" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, - "foo.bar\nPath too long" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE, - "foo.bar\nFile too large" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED, - "foo.bar\nVirus detected" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, - "foo.bar\nBlocked" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED, - "foo.bar\nVirus scan failed" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT, - "foo.bar\nFile truncated" }, - { content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, - "foo.bar\nSystem busy" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, - "foo.bar\nNetwork error" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT, - "foo.bar\nNetwork timeout" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, - "foo.bar\nNetwork disconnected" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN, - "foo.bar\nServer unavailable" }, - { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, - "foo.bar\nNetwork error" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, - "foo.bar\nServer problem" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE, - "foo.bar\nDownload error" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, - "foo.bar\nNo file" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED, - "foo.bar\nNeeds authorization" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM, - "foo.bar\nBad certificate" }, - { content::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN, - "foo.bar\nForbidden" }, - { content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED, - "foo.bar" }, - { content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN, - "foo.bar\nShutdown" }, - { content::DOWNLOAD_INTERRUPT_REASON_CRASH, - "foo.bar\nCrash" }, + {content::DOWNLOAD_INTERRUPT_REASON_NONE, "foo.bar"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, + "foo.bar\nDownload error"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, + "foo.bar\nInsufficient permissions"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, "foo.bar\nDisk full"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, + "foo.bar\nPath too long"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE, + "foo.bar\nFile too large"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED, + "foo.bar\nVirus detected"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, "foo.bar\nBlocked"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED, + "foo.bar\nVirus scan failed"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT, + "foo.bar\nFile truncated"}, + {content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, + "foo.bar\nSystem busy"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + "foo.bar\nNetwork error"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT, + "foo.bar\nNetwork timeout"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, + "foo.bar\nNetwork disconnected"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN, + "foo.bar\nServer unavailable"}, + {content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, + "foo.bar\nNetwork error"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, + "foo.bar\nServer problem"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE, + "foo.bar\nDownload error"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + "foo.bar\nNo file"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED, + "foo.bar\nNeeds authorization"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM, + "foo.bar\nBad certificate"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN, + "foo.bar\nForbidden"}, + {content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE, + "foo.bar\nServer unreachable"}, + {content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED, "foo.bar"}, + {content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN, "foo.bar\nShutdown"}, + {content::DOWNLOAD_INTERRUPT_REASON_CRASH, "foo.bar\nCrash"}, }; static_assert(kInterruptReasonCount == arraysize(kTestCases), "interrupt reason mismatch");
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc index 4b757d5..aeaa328 100644 --- a/chrome/browser/download/download_target_determiner.cc +++ b/chrome/browser/download/download_target_determiner.cc
@@ -240,15 +240,6 @@ DCHECK(virtual_path_.IsAbsolute()); DVLOG(20) << "Generated virtual path: " << virtual_path_.AsUTF8Unsafe(); - // If the download is DOA, don't bother going any further. This would be the - // case for a download that failed to initialize (e.g. the initial temporary - // file couldn't be created because both the downloads directory and the - // temporary directory are unwriteable). - // - // A virtual path is determined for DOA downloads for display purposes. This - // is why this check is performed here instead of at the start. - if (download_->GetState() != DownloadItem::IN_PROGRESS) - return COMPLETE; return CONTINUE; } @@ -259,7 +250,8 @@ next_state_ = STATE_RESERVE_VIRTUAL_PATH; - if (!should_notify_extensions_) + if (!should_notify_extensions_ || + download_->GetState() != DownloadItem::IN_PROGRESS) return CONTINUE; delegate_->NotifyExtensions(download_, virtual_path_, @@ -307,6 +299,8 @@ DCHECK(!virtual_path_.empty()); next_state_ = STATE_PROMPT_USER_FOR_DOWNLOAD_PATH; + if (download_->GetState() != DownloadItem::IN_PROGRESS) + return CONTINUE; delegate_->ReserveVirtualPath( download_, virtual_path_, create_target_directory_, conflict_action_, @@ -343,7 +337,9 @@ next_state_ = STATE_DETERMINE_LOCAL_PATH; - if (should_prompt_) { + // Avoid prompting for a download if it isn't in-progress. The user will be + // prompted once the download is resumed and headers are available. + if (should_prompt_ && download_->GetState() == DownloadItem::IN_PROGRESS) { delegate_->PromptUserForDownloadPath( download_, virtual_path_, @@ -581,6 +577,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!virtual_path_.empty()); next_state_ = STATE_CHECK_VISITED_REFERRER_BEFORE; + delegate_->CheckDownloadUrl( download_, virtual_path_,
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc index dea27dc..26f67397b 100644 --- a/chrome/browser/download/download_target_determiner_unittest.cc +++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -878,17 +878,17 @@ download_util::NOT_DANGEROUS, "http://example.com/foo.txt", "text/plain", FILE_PATH_LITERAL(""), - FILE_PATH_LITERAL(""), DownloadItem::TARGET_DISPOSITION_OVERWRITE, + FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE, - EXPECT_LOCAL_PATH}, + EXPECT_CRDOWNLOAD}, {SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, download_util::NOT_DANGEROUS, "http://example.com/foo.txt", "text/plain", FILE_PATH_LITERAL(""), - FILE_PATH_LITERAL(""), DownloadItem::TARGET_DISPOSITION_PROMPT, + FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT, - EXPECT_LOCAL_PATH}}; + EXPECT_CRDOWNLOAD}}; for (size_t i = 0; i < arraysize(kInactiveTestCases); ++i) { SCOPED_TRACE(testing::Message() << "Running test case " << i); @@ -897,10 +897,11 @@ CreateActiveDownloadItem(i, test_case)); EXPECT_CALL(*item.get(), GetState()) .WillRepeatedly(Return(content::DownloadItem::CANCELLED)); - // Even though one is a SAVE_AS download, no prompt will be displayed to - // the user because the download is inactive. - EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, _, _)) - .Times(0); + + EXPECT_CALL(*delegate(), PromptUserForDownloadPath(_, _, _)).Times(0); + EXPECT_CALL(*delegate(), NotifyExtensions(_, _, _)).Times(0); + EXPECT_CALL(*delegate(), ReserveVirtualPath(_, _, _, _, _)).Times(0); + EXPECT_CALL(*delegate(), DetermineLocalPath(_, _, _)).Times(1); RunTestCase(test_case, base::FilePath(), item.get()); } }
diff --git a/chrome/browser/extensions/api/automation/automation_apitest.cc b/chrome/browser/extensions/api/automation/automation_apitest.cc index 379ec41e..7bed325 100644 --- a/chrome/browser/extensions/api/automation/automation_apitest.cc +++ b/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -159,6 +159,31 @@ << message_; } +#if defined(OS_CHROMEOS) +IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopInitialFocus) { + ASSERT_TRUE( + RunExtensionSubtest("automation/tests/desktop", "initial_focus.html")) + << message_; +} + +IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopFocusWeb) { + ASSERT_TRUE( + RunExtensionSubtest("automation/tests/desktop", "focus_web.html")) + << message_; +} + +IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopFocusViews) { + AutomationManagerAura::GetInstance()->Enable(browser()->profile()); + // Trigger the shelf subtree to be computed. + ash::Shell::GetInstance()->accelerator_controller()->PerformActionIfEnabled( + ash::FOCUS_SHELF); + + ASSERT_TRUE( + RunExtensionSubtest("automation/tests/desktop", "focus_views.html")) + << message_; +} +#endif + IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopNotRequested) { ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "desktop_not_requested.html")) << message_;
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc index 7bc5da4..4e0903d 100644 --- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc +++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
@@ -11,10 +11,12 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/media/combined_desktop_media_list.h" #include "chrome/browser/media/desktop_media_list_ash.h" #include "chrome/browser/media/desktop_streams_registry.h" #include "chrome/browser/media/media_capture_devices_dispatcher.h" #include "chrome/browser/media/native_desktop_media_list.h" +#include "chrome/browser/media/tab_desktop_media_list.h" #include "chrome/browser/ui/ash/ash_util.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" @@ -31,7 +33,6 @@ const char kInvalidSourceNameError[] = "Invalid source type specified."; const char kEmptySourcesListError[] = "At least one source type must be specified."; -const char kTabCaptureNotSupportedError[] = "Tab capture is not supported yet."; DesktopCaptureChooseDesktopMediaFunctionBase::PickerFactory* g_picker_factory = NULL; @@ -78,7 +79,7 @@ bool show_screens = false; bool show_windows = false; - + bool show_tabs = false; bool request_audio = false; for (auto source_type : sources) { @@ -96,8 +97,11 @@ break; case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB: - error_ = kTabCaptureNotSupportedError; - return false; + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + extensions::switches::kEnableTabForDesktopShare)) { + show_tabs = true; + } + break; case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_AUDIO: bool has_flag = base::CommandLine::ForCurrentProcess()->HasSwitch( @@ -107,7 +111,7 @@ } } - if (!show_screens && !show_windows) { + if (!show_screens && !show_windows && !show_tabs) { error_ = kEmptySourcesListError; return false; } @@ -120,24 +124,39 @@ show_screens, show_windows); picker_ = g_picker_factory->CreatePicker(); } else { + std::vector<scoped_ptr<DesktopMediaList>> media_lists; + // Create a screens/windows list and push it into media_lists. + if (show_screens || show_windows) { #if defined(USE_ASH) - if (chrome::IsNativeWindowInAsh(parent_window)) { - media_list.reset(new DesktopMediaListAsh( - (show_screens ? DesktopMediaListAsh::SCREENS : 0) | - (show_windows ? DesktopMediaListAsh::WINDOWS : 0))); - } + if (chrome::IsNativeWindowInAsh(parent_window)) { + media_lists.push_back(make_scoped_ptr(new DesktopMediaListAsh( + (show_screens ? DesktopMediaListAsh::SCREENS : 0) | + (show_windows ? DesktopMediaListAsh::WINDOWS : 0)))); + } #endif - if (!media_list) { - webrtc::DesktopCaptureOptions options = - webrtc::DesktopCaptureOptions::CreateDefault(); - options.set_disable_effects(false); - scoped_ptr<webrtc::ScreenCapturer> screen_capturer( - show_screens ? webrtc::ScreenCapturer::Create(options) : NULL); - scoped_ptr<webrtc::WindowCapturer> window_capturer( - show_windows ? webrtc::WindowCapturer::Create(options) : NULL); + if (!media_list) { + webrtc::DesktopCaptureOptions options = + webrtc::DesktopCaptureOptions::CreateDefault(); + options.set_disable_effects(false); + scoped_ptr<webrtc::ScreenCapturer> screen_capturer( + show_screens ? webrtc::ScreenCapturer::Create(options) : NULL); + scoped_ptr<webrtc::WindowCapturer> window_capturer( + show_windows ? webrtc::WindowCapturer::Create(options) : NULL); - media_list.reset(new NativeDesktopMediaList(std::move(screen_capturer), - std::move(window_capturer))); + media_lists.push_back(make_scoped_ptr(new NativeDesktopMediaList( + std::move(screen_capturer), std::move(window_capturer)))); + } + } + + if (show_tabs) + media_lists.push_back(make_scoped_ptr(new TabDesktopMediaList())); + + DCHECK(!media_lists.empty()); + + if (media_lists.size() == 1) { + media_list = std::move(media_lists[0]); + } else { + media_list.reset(new CombinedDesktopMediaList(media_lists)); } // DesktopMediaPicker is implemented only for Windows, OSX and @@ -149,6 +168,7 @@ return false; #endif } + DesktopMediaPicker::DoneCallback callback = base::Bind( &DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults, this);
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc index e3d02ab..0266ef8 100644 --- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc +++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -31,6 +31,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/common/extensions/api/downloads.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" @@ -1766,15 +1767,42 @@ << kInvalidURLs[index]; } - EXPECT_STREQ("NETWORK_INVALID_REQUEST", RunFunctionAndReturnError( + int result_id = -1; + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), - "[{\"url\": \"javascript:document.write(\\\"hello\\\");\"}]").c_str()); - EXPECT_STREQ("NETWORK_INVALID_REQUEST", RunFunctionAndReturnError( + "[{\"url\": \"javascript:document.write(\\\"hello\\\");\"}]")); + ASSERT_TRUE(result.get()); + ASSERT_TRUE(result->GetAsInteger(&result_id)); + DownloadItem* item = GetCurrentManager()->GetDownload(result_id); + ASSERT_TRUE(item); + ASSERT_TRUE(WaitForInterruption( + item, content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, + "[{\"state\": \"in_progress\"," + " \"url\": \"javascript:document.write(\\\"hello\\\");\"}]")); + + result.reset( + RunFunctionAndReturnResult(new DownloadsDownloadFunction(), + "[{\"url\": \"javascript:return false;\"}]")); + ASSERT_TRUE(result.get()); + ASSERT_TRUE(result->GetAsInteger(&result_id)); + item = GetCurrentManager()->GetDownload(result_id); + ASSERT_TRUE(item); + ASSERT_TRUE(WaitForInterruption( + item, content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, + "[{\"state\": \"in_progress\"," + " \"url\": \"javascript:return false;\"}]")); + + result.reset(RunFunctionAndReturnResult( new DownloadsDownloadFunction(), - "[{\"url\": \"javascript:return false;\"}]").c_str()); - EXPECT_STREQ("NETWORK_FAILED", RunFunctionAndReturnError( - new DownloadsDownloadFunction(), - "[{\"url\": \"ftp://example.com/example.txt\"}]").c_str()); + "[{\"url\": \"ftp://example.com/example.txt\"}]")); + ASSERT_TRUE(result.get()); + ASSERT_TRUE(result->GetAsInteger(&result_id)); + item = GetCurrentManager()->GetDownload(result_id); + ASSERT_TRUE(item); + ASSERT_TRUE(WaitForInterruption( + item, content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, + "[{\"state\": \"in_progress\"," + " \"url\": \"ftp://example.com/example.txt\"}]")); } // TODO(benjhayden): Set up a test ftp server, add ftp://localhost* to @@ -2033,7 +2061,6 @@ content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED, base::StringPrintf("[{\"danger\": \"safe\"," " \"incognito\": false," - " \"mime\": \"text/html\"," " \"paused\": false," " \"url\": \"%s\"}]", download_url.c_str())));
diff --git a/chrome/browser/extensions/api/file_system/request_file_system_notification.cc b/chrome/browser/extensions/api/file_system/request_file_system_notification.cc index b1cddde..e77e316 100644 --- a/chrome/browser/extensions/api/file_system/request_file_system_notification.cc +++ b/chrome/browser/extensions/api/file_system/request_file_system_notification.cc
@@ -11,7 +11,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/file_manager/volume_manager.h" -#include "chrome/browser/extensions/app_icon_loader_impl.h" +#include "chrome/browser/extensions/extension_app_icon_loader.h" #include "chrome/grit/generated_resources.h" #include "extensions/common/extension.h" #include "ui/base/l10n/l10n_util.h" @@ -88,8 +88,8 @@ request_file_system_notification->Show(std::move(notification)); } -void RequestFileSystemNotification::SetAppImage(const std::string& id, - const gfx::ImageSkia& image) { +void RequestFileSystemNotification::OnAppImageUpdated( + const std::string& id, const gfx::ImageSkia& image) { extension_icon_.reset(new gfx::Image(image)); // If there is a pending notification, then show it now. @@ -104,7 +104,7 @@ Profile* profile, const extensions::Extension& extension) : icon_loader_( - new extensions::AppIconLoaderImpl(profile, kIconSize, this)) { + new extensions::ExtensionAppIconLoader(profile, kIconSize, this)) { icon_loader_->FetchImage(extension.id()); }
diff --git a/chrome/browser/extensions/api/file_system/request_file_system_notification.h b/chrome/browser/extensions/api/file_system/request_file_system_notification.h index ab707f8..d98d7ee1 100644 --- a/chrome/browser/extensions/api/file_system/request_file_system_notification.h +++ b/chrome/browser/extensions/api/file_system/request_file_system_notification.h
@@ -11,7 +11,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/extensions/app_icon_loader.h" +#include "chrome/browser/ui/app_icon_loader.h" #include "ui/message_center/notification_delegate.h" class Profile; @@ -36,7 +36,7 @@ // Shows notifications for the chrome.fileSystem.requestFileSystem() API. class RequestFileSystemNotification : public message_center::NotificationDelegate, - public extensions::AppIconLoader::Delegate { + public AppIconLoaderDelegate { public: // Shows a notification about automatically granted access to a file system. static void ShowAutoGrantedNotification( @@ -53,10 +53,11 @@ // Shows the notification. Can be called only once. void Show(scoped_ptr<message_center::Notification> notification); - // extensions::AppIconLoader::Delegate overrides: - void SetAppImage(const std::string& id, const gfx::ImageSkia& image) override; + // AppIconLoaderDelegate overrides: + void OnAppImageUpdated(const std::string& id, + const gfx::ImageSkia& image) override; - scoped_ptr<extensions::AppIconLoader> icon_loader_; + scoped_ptr<AppIconLoader> icon_loader_; scoped_ptr<gfx::Image> extension_icon_; scoped_ptr<message_center::Notification> pending_notification_;
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc index 8baca8b..f32ef90 100644 --- a/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -220,6 +220,14 @@ settings_private::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)["settings.internet.wake_on_wifi_darkconnect"] = settings_private::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)["settings.enable_screen_lock"] = + settings_private::PrefType::PREF_TYPE_BOOLEAN; + + // Input settings. + (*s_whitelist)["settings.touchpad.enable_tap_to_click"] = + settings_private::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)["settings.touchpad.natural_scroll"] = + settings_private::PrefType::PREF_TYPE_BOOLEAN; #else (*s_whitelist)["intl.accept_languages"] = settings_private::PrefType::PREF_TYPE_STRING;
diff --git a/chrome/browser/extensions/app_icon_loader.h b/chrome/browser/extensions/app_icon_loader.h deleted file mode 100644 index 950f0e6..0000000 --- a/chrome/browser/extensions/app_icon_loader.h +++ /dev/null
@@ -1,52 +0,0 @@ -// 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 CHROME_BROWSER_EXTENSIONS_APP_ICON_LOADER_H_ -#define CHROME_BROWSER_EXTENSIONS_APP_ICON_LOADER_H_ - -#include <string> - -#include "base/macros.h" - -namespace gfx { -class ImageSkia; -} - -namespace extensions { - -// Interface used to load app icons. This is in its own class so that it can -// be mocked. -class AppIconLoader { - public: - class Delegate { - public: - virtual ~Delegate() {} - - // Called when the image for an app is loaded. - virtual void SetAppImage(const std::string& id, - const gfx::ImageSkia& image) = 0; - }; - - AppIconLoader() {} - virtual ~AppIconLoader() {} - - // Fetches the image for the specified id. When done (which may be - // synchronous), this should invoke SetAppImage() on the delegate. - virtual void FetchImage(const std::string& id) = 0; - - // Clears the image for the specified id. - virtual void ClearImage(const std::string& id) = 0; - - // Updates the image for the specified id. This is called to re-create - // the app icon with latest app state (enabled or disabled/terminiated). - // SetAppImage() is called when done. - virtual void UpdateImage(const std::string& id) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(AppIconLoader); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_APP_ICON_LOADER_H_
diff --git a/chrome/browser/extensions/app_icon_loader_impl.cc b/chrome/browser/extensions/app_icon_loader_impl.cc deleted file mode 100644 index e661fa5e..0000000 --- a/chrome/browser/extensions/app_icon_loader_impl.cc +++ /dev/null
@@ -1,116 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/extensions/app_icon_loader_impl.h" - -#include "base/stl_util.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_util.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/common/extensions/extension_constants.h" -#include "extensions/browser/extension_system.h" -#include "extensions/common/extension.h" -#include "extensions/common/manifest_handlers/icons_handler.h" -#include "ui/gfx/color_utils.h" -#include "ui/gfx/image/image_skia_operations.h" - -namespace { - -const extensions::Extension* GetExtensionByID(Profile* profile, - const std::string& id) { - ExtensionService* service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - if (!service) - return NULL; - return service->GetInstalledExtension(id); -} - -} // namespace - -namespace extensions { - -AppIconLoaderImpl::AppIconLoaderImpl( - Profile* profile, - int icon_size, - AppIconLoader::Delegate* delegate) - : profile_(profile), - delegate_(delegate), - icon_size_(icon_size) { - -} - -AppIconLoaderImpl::~AppIconLoaderImpl() { - STLDeleteContainerPairFirstPointers(map_.begin(), map_.end()); -} - -void AppIconLoaderImpl::FetchImage(const std::string& id) { - for (ImageToExtensionIDMap::const_iterator i = map_.begin(); - i != map_.end(); ++i) { - if (i->second == id) - return; // Already loading the image. - } - - const extensions::Extension* extension = GetExtensionByID(profile_, id); - if (!extension) - return; - - extensions::IconImage* image = new extensions::IconImage( - profile_, - extension, - extensions::IconsInfo::GetIcons(extension), - icon_size_, - extensions::util::GetDefaultAppIcon(), - this); - // |map_| takes ownership of |image|. - map_[image] = id; - - // Triggers image loading now instead of depending on paint message. This - // makes the temp blank image be shown for shorter time and improves user - // experience. See http://crbug.com/146114. - image->image_skia().EnsureRepsForSupportedScales(); -} - -void AppIconLoaderImpl::ClearImage(const std::string& id) { - for (ImageToExtensionIDMap::iterator i = map_.begin(); - i != map_.end(); ++i) { - if (i->second == id) { - delete i->first; - map_.erase(i); - break; - } - } -} - -void AppIconLoaderImpl::UpdateImage(const std::string& id) { - for (ImageToExtensionIDMap::iterator i = map_.begin(); - i != map_.end(); ++i) { - if (i->second == id) { - BuildImage(i->second, i->first->image_skia()); - break; - } - } -} - -void AppIconLoaderImpl::OnExtensionIconImageChanged( - extensions::IconImage* image) { - ImageToExtensionIDMap::iterator i = map_.find(image); - if (i == map_.end()) - return; // The image has been removed, do nothing. - - BuildImage(i->second, i->first->image_skia()); -} - -void AppIconLoaderImpl::BuildImage(const std::string& id, - const gfx::ImageSkia& icon) { - gfx::ImageSkia image = icon; - - if (!util::IsAppLaunchable(id, profile_)) { - const color_utils::HSL shift = {-1, 0, 0.6}; - image = gfx::ImageSkiaOperations::CreateHSLShiftedImage(image, shift); - } - - delegate_->SetAppImage(id, image); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/app_icon_loader_impl.h b/chrome/browser/extensions/app_icon_loader_impl.h deleted file mode 100644 index 555c480..0000000 --- a/chrome/browser/extensions/app_icon_loader_impl.h +++ /dev/null
@@ -1,58 +0,0 @@ -// 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 CHROME_BROWSER_EXTENSIONS_APP_ICON_LOADER_IMPL_H_ -#define CHROME_BROWSER_EXTENSIONS_APP_ICON_LOADER_IMPL_H_ - -#include <map> -#include <string> - -#include "base/macros.h" -#include "chrome/browser/extensions/app_icon_loader.h" -#include "extensions/browser/extension_icon_image.h" - -class Profile; - -namespace extensions { -class Extension; - -// Default implementation of ash::AppIconLoader that interacts with the -// ExtensionService and ImageLoader to load images. -class AppIconLoaderImpl : public AppIconLoader, - public extensions::IconImage::Observer { - public: - AppIconLoaderImpl(Profile* profile, int icon_size, - AppIconLoader::Delegate* delegate); - ~AppIconLoaderImpl() override; - - // AppIconLoader overrides: - void FetchImage(const std::string& id) override; - void ClearImage(const std::string& id) override; - void UpdateImage(const std::string& id) override; - - // extensions::IconImage::Observer overrides: - void OnExtensionIconImageChanged(extensions::IconImage* image) override; - - private: - typedef std::map<extensions::IconImage*, std::string> ImageToExtensionIDMap; - - // Builds image for given |id| and |icon|. - void BuildImage(const std::string& id, const gfx::ImageSkia& icon); - - Profile* profile_; - - // The delegate object which receives the icon images. No ownership. - AppIconLoader::Delegate* delegate_; - - // Maps from IconImage pointer to the extension id. - ImageToExtensionIDMap map_; - - const int icon_size_; - - DISALLOW_COPY_AND_ASSIGN(AppIconLoaderImpl); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_APP_ICON_LOADER_IMPL_H_
diff --git a/chrome/browser/extensions/background_xhr_browsertest.cc b/chrome/browser/extensions/background_xhr_browsertest.cc index 276352c..8f6deaed 100644 --- a/chrome/browser/extensions/background_xhr_browsertest.cc +++ b/chrome/browser/extensions/background_xhr_browsertest.cc
@@ -66,7 +66,8 @@ // Launch HTTPS server. net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; - ssl_config.require_client_cert = true; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); https_server.ServeFilesFromSourceDirectory("content/test/data"); ASSERT_TRUE(https_server.Start());
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc index 5dc5f2b0..ccae49f 100644 --- a/chrome/browser/extensions/bookmark_app_helper.cc +++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -696,30 +696,29 @@ return; } +#if !defined(USE_ASH) // Pin the app to the relevant launcher depending on the OS. Profile* current_profile = profile_->GetOriginalProfile(); +#endif // !defined(USE_ASH) // On Mac, shortcuts are automatically created for hosted apps when they are // installed, so there is no need to create them again. #if !defined(OS_MACOSX) - chrome::HostDesktopType desktop = browser->host_desktop_type(); - if (desktop != chrome::HOST_DESKTOP_TYPE_ASH) { - web_app::ShortcutLocations creation_locations; +#if !defined(USE_ASH) + web_app::ShortcutLocations creation_locations; #if defined(OS_LINUX) || defined(OS_WIN) - creation_locations.on_desktop = true; + creation_locations.on_desktop = true; #else - creation_locations.on_desktop = false; + creation_locations.on_desktop = false; #endif - creation_locations.applications_menu_location = - web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS; - creation_locations.in_quick_launch_bar = false; - web_app::CreateShortcuts(web_app::SHORTCUT_CREATION_BY_USER, - creation_locations, current_profile, extension); -#if defined(USE_ASH) - } else { - ChromeLauncherController::instance()->PinAppWithID(extension->id()); -#endif - } + creation_locations.applications_menu_location = + web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS; + creation_locations.in_quick_launch_bar = false; + web_app::CreateShortcuts(web_app::SHORTCUT_CREATION_BY_USER, + creation_locations, current_profile, extension); +#else + ChromeLauncherController::instance()->PinAppWithID(extension->id()); +#endif // !defined(USE_ASH) #endif // !defined(OS_MACOSX) #if defined(OS_MACOSX)
diff --git a/chrome/browser/extensions/extension_app_icon_loader.cc b/chrome/browser/extensions/extension_app_icon_loader.cc new file mode 100644 index 0000000..992568e --- /dev/null +++ b/chrome/browser/extensions/extension_app_icon_loader.cc
@@ -0,0 +1,107 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_app_icon_loader.h" + +#include "base/stl_util.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/extension_constants.h" +#include "extensions/browser/extension_system.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handlers/icons_handler.h" +#include "ui/gfx/color_utils.h" +#include "ui/gfx/image/image_skia_operations.h" + +namespace { + +const extensions::Extension* GetExtensionByID(Profile* profile, + const std::string& id) { + ExtensionService* service = + extensions::ExtensionSystem::Get(profile)->extension_service(); + if (!service) + return nullptr; + return service->GetInstalledExtension(id); +} + +} // namespace + +namespace extensions { + +ExtensionAppIconLoader::ExtensionAppIconLoader( + Profile* profile, + int icon_size, + AppIconLoaderDelegate* delegate) + : AppIconLoader(profile, icon_size, delegate) { +} + +ExtensionAppIconLoader::~ExtensionAppIconLoader() { +} + +bool ExtensionAppIconLoader::CanLoadImageForApp(const std::string& id) { + if (map_.find(id) != map_.end()) + return true; + return GetExtensionByID(profile(), id) != nullptr; +} + +void ExtensionAppIconLoader::FetchImage(const std::string& id) { + if (map_.find(id) != map_.end()) + return; // Already loading the image. + + const extensions::Extension* extension = GetExtensionByID(profile(), id); + if (!extension) + return; + + scoped_ptr<extensions::IconImage> image(new extensions::IconImage( + profile(), + extension, + extensions::IconsInfo::GetIcons(extension), + icon_size(), + extensions::util::GetDefaultAppIcon(), + this)); + + // Triggers image loading now instead of depending on paint message. This + // makes the temp blank image be shown for shorter time and improves user + // experience. See http://crbug.com/146114. + image->image_skia().EnsureRepsForSupportedScales(); + map_[id] = std::move(image); +} + +void ExtensionAppIconLoader::ClearImage(const std::string& id) { + map_.erase(id); +} + +void ExtensionAppIconLoader::UpdateImage(const std::string& id) { + ExtensionIDToImageMap::iterator it = map_.find(id); + if (it == map_.end()) + return; + + BuildImage(it->first, it->second->image_skia()); +} + +void ExtensionAppIconLoader::OnExtensionIconImageChanged( + extensions::IconImage* image) { + for (const auto& pair : map_) { + if (pair.second.get() == image) { + BuildImage(pair.first, pair.second->image_skia()); + return; + } + } + // The image has been removed, do nothing. +} + +void ExtensionAppIconLoader::BuildImage(const std::string& id, + const gfx::ImageSkia& icon) { + gfx::ImageSkia image = icon; + + if (!util::IsAppLaunchable(id, profile())) { + const color_utils::HSL shift = {-1, 0, 0.6}; + image = gfx::ImageSkiaOperations::CreateHSLShiftedImage(image, shift); + } + + delegate()->OnAppImageUpdated(id, image); +} + +} // namespace extensions
diff --git a/chrome/browser/extensions/extension_app_icon_loader.h b/chrome/browser/extensions/extension_app_icon_loader.h new file mode 100644 index 0000000..6ce5fb0 --- /dev/null +++ b/chrome/browser/extensions/extension_app_icon_loader.h
@@ -0,0 +1,54 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_APP_ICON_LOADER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_APP_ICON_LOADER_H_ + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/app_icon_loader.h" +#include "extensions/browser/extension_icon_image.h" + +class Profile; + +namespace extensions { +class Extension; + +// Implementation of AppIconLoader that interacts with the ExtensionService and +// ImageLoader to load images. +class ExtensionAppIconLoader : public AppIconLoader, + public extensions::IconImage::Observer { + public: + ExtensionAppIconLoader(Profile* profile, int icon_size, + AppIconLoaderDelegate* delegate); + ~ExtensionAppIconLoader() override; + + // AppIconLoader overrides: + bool CanLoadImageForApp(const std::string& id) override; + void FetchImage(const std::string& id) override; + void ClearImage(const std::string& id) override; + void UpdateImage(const std::string& id) override; + + // extensions::IconImage::Observer overrides: + void OnExtensionIconImageChanged(extensions::IconImage* image) override; + + private: + using ExtensionIDToImageMap = + std::map<std::string, scoped_ptr<extensions::IconImage>>; + + // Builds image for given |id| and |icon|. + void BuildImage(const std::string& id, const gfx::ImageSkia& icon); + + // Maps from extension id to IconImage pointer. + ExtensionIDToImageMap map_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionAppIconLoader); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_APP_ICON_LOADER_H_
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc index 454b4c03..c62ad3a8 100644 --- a/chrome/browser/extensions/extension_tab_util.cc +++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -655,7 +655,10 @@ chrome::NavigateParams params( chrome::GetSingletonTabNavigateParams(browser, url_to_navigate)); - params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE; + // We need to respect path differences because we don't want opening the + // options page to close a page that might be open to extension content. + // Note: default ref behavior is IGNORE_REF, which is correct. + params.path_behavior = chrome::NavigateParams::RESPECT; params.url = url_to_navigate; chrome::ShowSingletonTabOverwritingNTP(browser, params); return true;
diff --git a/chrome/browser/extensions/extension_tab_util_browsertest.cc b/chrome/browser/extensions/extension_tab_util_browsertest.cc new file mode 100644 index 0000000..9995c3d --- /dev/null +++ b/chrome/browser/extensions/extension_tab_util_browsertest.cc
@@ -0,0 +1,89 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/test/browser_test_utils.h" +#include "extensions/common/manifest_handlers/options_page_info.h" + +namespace extensions { + +using ExtensionTabUtilBrowserTest = ExtensionBrowserTest; + +IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest, OpenExtensionsOptionsPage) { + // Load an extension with an options page. + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("options_page")); + ASSERT_TRUE(extension); + ASSERT_TRUE(OptionsPageInfo::HasOptionsPage(extension)); + + // Start at the new tab page, and then open the extension options page. + ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab")); + EXPECT_EQ(1, browser()->tab_strip_model()->count()); + GURL options_url = OptionsPageInfo::GetOptionsPage(extension); + EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(extension, browser())); + + // Opening the options page should take the new tab and use it, so we should + // have only one tab, and it should be open to the options page. + EXPECT_EQ(1, browser()->tab_strip_model()->count()); + EXPECT_TRUE(content::WaitForLoadStop( + browser()->tab_strip_model()->GetActiveWebContents())); + EXPECT_EQ(options_url, + browser()->tab_strip_model()-> + GetActiveWebContents()->GetLastCommittedURL()); + + // Calling OpenOptionsPage again shouldn't result in any new tabs, since we + // re-use the existing options page. + EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(extension, browser())); + EXPECT_EQ(1, browser()->tab_strip_model()->count()); + EXPECT_TRUE(content::WaitForLoadStop( + browser()->tab_strip_model()->GetActiveWebContents())); + EXPECT_EQ(options_url, + browser()->tab_strip_model()-> + GetActiveWebContents()->GetLastCommittedURL()); + + // Navigate to google.com (something non-newtab, non-options). Calling + // OpenOptionsPage() should create a new tab and navigate it to the options + // page. So we should have two total tabs, with the active tab pointing to + // options. + ui_test_utils::NavigateToURL(browser(), GURL("http://www.google.com/")); + EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(extension, browser())); + EXPECT_EQ(2, browser()->tab_strip_model()->count()); + EXPECT_TRUE(content::WaitForLoadStop( + browser()->tab_strip_model()->GetActiveWebContents())); + EXPECT_EQ(options_url, + browser()->tab_strip_model()-> + GetActiveWebContents()->GetLastCommittedURL()); + + // Navigate the tab to a different extension URL, and call OpenOptionsPage(). + // We should not reuse the current tab since it's opened to a page that isn't + // the options page, and we don't want to arbitrarily close extension content. + // Regression test for crbug.com/587581. + ui_test_utils::NavigateToURL( + browser(), extension->GetResourceURL("other.html")); + EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPage(extension, browser())); + EXPECT_EQ(3, browser()->tab_strip_model()->count()); + EXPECT_TRUE(content::WaitForLoadStop( + browser()->tab_strip_model()->GetActiveWebContents())); + EXPECT_EQ(options_url, + browser()->tab_strip_model()-> + GetActiveWebContents()->GetLastCommittedURL()); + + // If the user navigates to the options page e.g. by typing in the url, it + // should not override the currently-open tab. + ui_test_utils::NavigateToURLWithDisposition( + browser(), + options_url, + NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + EXPECT_EQ(4, browser()->tab_strip_model()->count()); + EXPECT_EQ(options_url, + browser()->tab_strip_model()-> + GetActiveWebContents()->GetLastCommittedURL()); +} + +} // namespace extensions
diff --git a/chrome/browser/extensions/webstore_installer.cc b/chrome/browser/extensions/webstore_installer.cc index a97ec769..fa910f3 100644 --- a/chrome/browser/extensions/webstore_installer.cc +++ b/chrome/browser/extensions/webstore_installer.cc
@@ -459,8 +459,9 @@ const std::string& extension_id, DownloadItem* item, content::DownloadInterruptReason interrupt_reason) { - if (!item) { - DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); + if (!item || interrupt_reason != content::DOWNLOAD_INTERRUPT_REASON_NONE) { + if (item) + item->Remove(); ReportFailure(content::DownloadInterruptReasonToString(interrupt_reason), FAILURE_REASON_OTHER); return;
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc index ea551c7..133a9e4d 100644 --- a/chrome/browser/memory_details.cc +++ b/chrome/browser/memory_details.cc
@@ -18,6 +18,7 @@ #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" #include "components/nacl/common/nacl_process_type.h" +#include "components/strings/grit/components_strings.h" #include "content/public/browser/browser_child_process_host_iterator.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_data.h"
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc index 5d2a3fb..2fbb86f 100644 --- a/chrome/browser/metrics/chrome_metrics_service_client.cc +++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -14,6 +14,7 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/metrics/histogram.h" +#include "base/path_service.h" #include "base/rand_util.h" #include "base/strings/string16.h" #include "base/threading/platform_thread.h" @@ -30,8 +31,10 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/crash_keys.h" #include "chrome/common/features.h" +#include "chrome/installer/util/util_constants.h" #include "components/metrics/call_stack_profile_metrics_provider.h" #include "components/metrics/drive_metrics_provider.h" +#include "components/metrics/file_metrics_provider.h" #include "components/metrics/gpu/gpu_metrics_provider.h" #include "components/metrics/metrics_pref_names.h" #include "components/metrics/metrics_service.h" @@ -128,6 +131,33 @@ #endif } +void RegisterInstallerFileMetricsPreferences(PrefRegistrySimple* registry) { +#if defined(OS_WIN) + metrics::FileMetricsProvider::RegisterPrefs( + registry, installer::kSetupHistogramAllocatorName); +#endif +} + +void RegisterInstallerFileMetricsProvider( + metrics::MetricsService* metrics_service) { +#if defined(OS_WIN) + scoped_ptr<metrics::FileMetricsProvider> file_metrics( + new metrics::FileMetricsProvider( + content::BrowserThread::GetBlockingPool() + ->GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN), + g_browser_process->local_state())); + base::FilePath program_dir; + base::PathService::Get(base::DIR_EXE, &program_dir); + file_metrics->RegisterFile( + program_dir.AppendASCII(installer::kSetupHistogramAllocatorName) + .AddExtension(L".pma"), + metrics::FileMetricsProvider::FILE_HISTOGRAMS_ATOMIC, + installer::kSetupHistogramAllocatorName); + metrics_service->RegisterMetricsProvider(std::move(file_metrics)); +#endif +} + } // namespace @@ -177,6 +207,8 @@ metrics::MetricsService::RegisterPrefs(registry); metrics::StabilityMetricsHelper::RegisterPrefs(registry); + RegisterInstallerFileMetricsPreferences(registry); + #if BUILDFLAG(ANDROID_JAVA_UI) AndroidMetricsProvider::RegisterPrefs(registry); #endif // BUILDFLAG(ANDROID_JAVA_UI) @@ -335,6 +367,8 @@ scoped_ptr<metrics::MetricsProvider>( new metrics::ScreenInfoMetricsProvider)); + RegisterInstallerFileMetricsProvider(metrics_service_.get()); + drive_metrics_provider_ = new metrics::DriveMetricsProvider( content::BrowserThread::GetMessageLoopProxyForThread( content::BrowserThread::FILE), @@ -523,7 +557,7 @@ } #endif // !ENABLE_PRINT_PREVIEW - // Set up the callback to task to call after we receive histograms from all + // Set up the callback task to call after we receive histograms from all // child processes. |timeout| specifies how long to wait before absolutely // calling us back on the task. content::FetchHistogramsAsynchronously(base::MessageLoop::current(), callback,
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.cc b/chrome/browser/metrics/chromeos_metrics_provider.cc index d099d57c..fae5560 100644 --- a/chrome/browser/metrics/chromeos_metrics_provider.cc +++ b/chrome/browser/metrics/chromeos_metrics_provider.cc
@@ -139,6 +139,10 @@ return connector->IsEnterpriseManaged() ? MANAGED : NON_MANAGED; } +void ChromeOSMetricsProvider::Init() { + perf_provider_.Init(); +} + void ChromeOSMetricsProvider::OnDidCreateMetricsLog() { registered_user_count_at_log_initialization_ = false; if (user_manager::UserManager::IsInitialized()) {
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.h b/chrome/browser/metrics/chromeos_metrics_provider.h index 6b1c900..680c17a 100644 --- a/chrome/browser/metrics/chromeos_metrics_provider.h +++ b/chrome/browser/metrics/chromeos_metrics_provider.h
@@ -52,6 +52,7 @@ void InitTaskGetHardwareClass(const base::Closure& callback); // metrics::MetricsProvider: + void Init() override; void OnDidCreateMetricsLog() override; void ProvideSystemProfileMetrics( metrics::SystemProfileProto* system_profile_proto) override;
diff --git a/chrome/browser/metrics/perf/perf_provider_chromeos.cc b/chrome/browser/metrics/perf/perf_provider_chromeos.cc index a655d10..1d782ac 100644 --- a/chrome/browser/metrics/perf/perf_provider_chromeos.cc +++ b/chrome/browser/metrics/perf/perf_provider_chromeos.cc
@@ -222,6 +222,13 @@ login_observer_(this), next_profiling_interval_start_(base::TimeTicks::Now()), weak_factory_(this) { +} + +PerfProvider::~PerfProvider() { + chromeos::LoginState::Get()->RemoveObserver(&login_observer_); +} + +void PerfProvider::Init() { CHECK(command_selector_.SetOdds( internal::GetDefaultCommandsForCpu(GetCPUIdentity()))); std::map<std::string, std::string> params; @@ -249,10 +256,6 @@ login_observer_.LoggedInStateChanged(); } -PerfProvider::~PerfProvider() { - chromeos::LoginState::Get()->RemoveObserver(&login_observer_); -} - namespace internal { std::string FindBestCpuSpecifierFromParams(
diff --git a/chrome/browser/metrics/perf/perf_provider_chromeos.h b/chrome/browser/metrics/perf/perf_provider_chromeos.h index 315e16c..6f2c7e7 100644 --- a/chrome/browser/metrics/perf/perf_provider_chromeos.h +++ b/chrome/browser/metrics/perf/perf_provider_chromeos.h
@@ -34,6 +34,8 @@ PerfProvider(); ~PerfProvider() override; + void Init(); + // Stores collected perf data protobufs in |sampled_profiles|. Clears all the // stored profile data. Returns true if it wrote to |sampled_profiles|. bool GetSampledProfiles(std::vector<SampledProfile>* sampled_profiles);
diff --git a/chrome/browser/metrics/perf/perf_provider_chromeos_unittest.cc b/chrome/browser/metrics/perf/perf_provider_chromeos_unittest.cc index f79e7eb2..3edf1aa 100644 --- a/chrome/browser/metrics/perf/perf_provider_chromeos_unittest.cc +++ b/chrome/browser/metrics/perf/perf_provider_chromeos_unittest.cc
@@ -158,6 +158,7 @@ chromeos::DBusThreadManager::Initialize(); perf_provider_.reset(new TestPerfProvider); + perf_provider_->Init(); // PerfProvider requires the user to be logged in. chromeos::LoginState::Get()->SetLoggedInState( @@ -733,6 +734,14 @@ DISALLOW_COPY_AND_ASSIGN(PerfProviderCollectionParamsTest); }; +TEST_F(PerfProviderCollectionParamsTest, Commands_InitializedAfterVariations) { + TestPerfProvider perf_provider; + EXPECT_TRUE(perf_provider.command_selector().odds().empty()); + // Init would be called after VariationsService is initialized. + perf_provider.Init(); + EXPECT_FALSE(perf_provider.command_selector().odds().empty()); +} + TEST_F(PerfProviderCollectionParamsTest, Commands_EmptyExperiment) { std::vector<RandomSelector::WeightAndValue> default_cmds = internal::GetDefaultCommandsForCpu(GetCPUIdentity()); @@ -743,6 +752,8 @@ "ChromeOSWideProfilingCollection", "group_name")); TestPerfProvider perf_provider; + EXPECT_TRUE(perf_provider.command_selector().odds().empty()); + perf_provider.Init(); EXPECT_EQ(default_cmds, perf_provider.command_selector().odds()); } @@ -767,6 +778,8 @@ "ChromeOSWideProfilingCollection", "group_name")); TestPerfProvider perf_provider; + EXPECT_TRUE(perf_provider.command_selector().odds().empty()); + perf_provider.Init(); EXPECT_EQ(default_cmds, perf_provider.command_selector().odds()); } @@ -791,6 +804,8 @@ "ChromeOSWideProfilingCollection", "group_name")); TestPerfProvider perf_provider; + EXPECT_TRUE(perf_provider.command_selector().odds().empty()); + perf_provider.Init(); std::vector<WeightAndValue> expected_cmds; expected_cmds.push_back(WeightAndValue(50.0, "perf record foo")); @@ -814,8 +829,22 @@ "ChromeOSWideProfilingCollection", "group_name")); TestPerfProvider perf_provider; - const auto& parsed_params = perf_provider.collection_params(); + + // Not initialized yet: + EXPECT_NE(base::TimeDelta::FromSeconds(15), + parsed_params.collection_duration()); + EXPECT_NE(base::TimeDelta::FromHours(1), + parsed_params.periodic_interval()); + EXPECT_NE(1, parsed_params.resume_from_suspend().sampling_factor()); + EXPECT_NE(base::TimeDelta::FromSeconds(10), + parsed_params.resume_from_suspend().max_collection_delay()); + EXPECT_NE(2, parsed_params.restore_session().sampling_factor()); + EXPECT_NE(base::TimeDelta::FromSeconds(20), + parsed_params.restore_session().max_collection_delay()); + + perf_provider.Init(); + EXPECT_EQ(base::TimeDelta::FromSeconds(15), parsed_params.collection_duration()); EXPECT_EQ(base::TimeDelta::FromHours(1), @@ -841,6 +870,7 @@ TestPerfProvider perf_provider; chromeos::PowerManagerClient::Observer& pm_observer = perf_provider; + perf_provider.Init(); // Cancel the background collection. perf_provider.Deactivate();
diff --git a/chrome/browser/notifications/message_center_settings_controller.cc b/chrome/browser/notifications/message_center_settings_controller.cc index 5761d055..6459b29 100644 --- a/chrome/browser/notifications/message_center_settings_controller.cc +++ b/chrome/browser/notifications/message_center_settings_controller.cc
@@ -17,7 +17,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" -#include "chrome/browser/extensions/app_icon_loader_impl.h" +#include "chrome/browser/extensions/extension_app_icon_loader.h" #include "chrome/browser/favicon/favicon_service_factory.h" #include "chrome/browser/notifications/desktop_notification_profile_util.h" #include "chrome/browser/notifications/notifier_state_tracker.h" @@ -215,7 +215,7 @@ // back to the default icon. The fetched icon will be resized in the settings // dialog. See chrome/browser/extensions/extension_icon_image.cc and // crbug.com/222931 - app_icon_loader_.reset(new extensions::AppIconLoaderImpl( + app_icon_loader_.reset(new extensions::ExtensionAppIconLoader( profile, extension_misc::EXTENSION_ICON_SMALL, this)); for (extensions::ExtensionSet::const_iterator iter = extension_set.begin(); iter != extension_set.end(); @@ -428,8 +428,8 @@ } #endif -void MessageCenterSettingsController::SetAppImage(const std::string& id, - const gfx::ImageSkia& image) { +void MessageCenterSettingsController::OnAppImageUpdated( + const std::string& id, const gfx::ImageSkia& image) { FOR_EACH_OBSERVER(message_center::NotifierSettingsObserver, observers_, UpdateIconImage(NotifierId(NotifierId::APPLICATION, id),
diff --git a/chrome/browser/notifications/message_center_settings_controller.h b/chrome/browser/notifications/message_center_settings_controller.h index a15b215a..ae2f4f1 100644 --- a/chrome/browser/notifications/message_center_settings_controller.h +++ b/chrome/browser/notifications/message_center_settings_controller.h
@@ -15,8 +15,8 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "build/build_config.h" -#include "chrome/browser/extensions/app_icon_loader.h" #include "chrome/browser/profiles/profile_attributes_storage.h" +#include "chrome/browser/ui/app_icon_loader.h" #include "components/content_settings/core/common/content_settings.h" #include "components/favicon_base/favicon_types.h" #include "content/public/browser/notification_details.h" @@ -52,7 +52,7 @@ #if defined(OS_CHROMEOS) public user_manager::UserManager::UserSessionStateObserver, #endif - public extensions::AppIconLoader::Delegate { + public AppIconLoaderDelegate { public: explicit MessageCenterSettingsController( ProfileAttributesStorage& profile_attributes_storage); @@ -84,8 +84,9 @@ void ActiveUserChanged(const user_manager::User* active_user) override; #endif - // Overridden from extensions::AppIconLoader::Delegate. - void SetAppImage(const std::string& id, const gfx::ImageSkia& image) override; + // Overridden from AppIconLoaderDelegate. + void OnAppImageUpdated(const std::string& id, + const gfx::ImageSkia& image) override; private: // Overridden from content::NotificationObserver. @@ -122,7 +123,7 @@ // The task tracker for loading favicons. scoped_ptr<base::CancelableTaskTracker> favicon_tracker_; - scoped_ptr<extensions::AppIconLoader> app_icon_loader_; + scoped_ptr<AppIconLoader> app_icon_loader_; std::map<base::string16, ContentSettingsPattern> patterns_;
diff --git a/chrome/browser/notifications/notification_permission_context_unittest.cc b/chrome/browser/notifications/notification_permission_context_unittest.cc index 4fef775..d39d1af 100644 --- a/chrome/browser/notifications/notification_permission_context_unittest.cc +++ b/chrome/browser/notifications/notification_permission_context_unittest.cc
@@ -230,7 +230,7 @@ // failing, PermissionManager::OnPermissionsRequestResponseStatus will crash. int request_id = permission_manager->RequestPermission( content::PermissionType::NOTIFICATIONS, web_contents()->GetMainFrame(), - url.GetOrigin(), true /* user_gesture */, base::Bind(&DoNothing2)); + url.GetOrigin(), base::Bind(&DoNothing2)); permission_manager->CancelPermissionRequest(request_id);
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc index 67ca1a1..f72ac4f9 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -326,6 +326,14 @@ void ChromePasswordManagerClient::NotifySuccessfulLoginWithExistingPassword( const autofill::PasswordForm& form) { + if (form.skip_zero_click && + credential_manager_dispatcher_.IsZeroClickAllowed() && + GetPasswordStore()) { + autofill::PasswordForm update(form); + update.skip_zero_click = false; + GetPasswordStore()->UpdateLogin(update); + } + if (!form_blocked_on_first_run_) return;
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc index f598ab7a..292351d377 100644 --- a/chrome/browser/password_manager/password_manager_browsertest.cc +++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -2905,4 +2905,45 @@ "mypassword"); } + +IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, + SkipZeroClickToggledAfterSuccessfulSubmission) { + ASSERT_TRUE(ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()); + // Save credentials with 'skip_zero_click' + scoped_refptr<password_manager::TestPasswordStore> password_store = + static_cast<password_manager::TestPasswordStore*>( + PasswordStoreFactory::GetForProfile( + browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS) + .get()); + autofill::PasswordForm signin_form; + signin_form.signon_realm = embedded_test_server()->base_url().spec(); + signin_form.password_value = base::ASCIIToUTF16("password"); + signin_form.username_value = base::ASCIIToUTF16("user"); + signin_form.skip_zero_click = true; + password_store->AddLogin(signin_form); + + NavigateToFile("/password/password_form.html"); + NavigationObserver observer(WebContents()); + scoped_ptr<PromptObserver> prompt_observer( + PromptObserver::Create(WebContents())); + std::string fill_and_submit_change_password = + "document.getElementById('username_field').value = 'user';" + "document.getElementById('password_field').value = 'password';" + "document.getElementById('input_submit_button').click()"; + ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), + fill_and_submit_change_password)); + observer.Wait(); + EXPECT_FALSE(prompt_observer->IsShowingPrompt()); + + // Verify that the form's 'skip_zero_click' is updated. + auto& passwords_map = password_store->stored_passwords(); + ASSERT_EQ(1u, passwords_map.size()); + auto& passwords_vector = passwords_map.begin()->second; + ASSERT_EQ(1u, passwords_vector.size()); + const autofill::PasswordForm& form = passwords_vector[0]; + EXPECT_EQ(base::ASCIIToUTF16("user"), form.username_value); + EXPECT_EQ(base::ASCIIToUTF16("password"), form.password_value); + EXPECT_FALSE(form.skip_zero_click); +} + } // namespace password_manager
diff --git a/chrome/browser/permissions/permission_manager.cc b/chrome/browser/permissions/permission_manager.cc index da0aba4..ccc98f4 100644 --- a/chrome/browser/permissions/permission_manager.cc +++ b/chrome/browser/permissions/permission_manager.cc
@@ -208,13 +208,11 @@ PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(PermissionStatus)>& callback) { return RequestPermissions( std::vector<PermissionType>(1, permission), render_frame_host, requesting_origin, - user_gesture, base::Bind(&PermissionRequestResponseCallbackWrapper, callback)); } @@ -222,7 +220,6 @@ const std::vector<PermissionType>& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<PermissionStatus>&)>& callback) { if (permissions.empty()) {
diff --git a/chrome/browser/permissions/permission_manager.h b/chrome/browser/permissions/permission_manager.h index 176799eb..79438b9 100644 --- a/chrome/browser/permissions/permission_manager.h +++ b/chrome/browser/permissions/permission_manager.h
@@ -33,13 +33,11 @@ content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(content::PermissionStatus)>& callback) override; int RequestPermissions( const std::vector<content::PermissionType>& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<content::PermissionStatus>&)>& callback) override; void CancelPermissionRequest(int request_id) override;
diff --git a/chrome/browser/platform_util_aura.cc b/chrome/browser/platform_util_aura.cc index db3a3438..16c3e2b 100644 --- a/chrome/browser/platform_util_aura.cc +++ b/chrome/browser/platform_util_aura.cc
@@ -6,10 +6,7 @@ #include "base/logging.h" #include "ui/aura/window.h" - -#if defined(USE_ASH) -#include "ash/wm/window_util.h" -#endif +#include "ui/wm/core/window_util.h" namespace platform_util { @@ -27,20 +24,11 @@ } bool IsWindowActive(gfx::NativeWindow window) { -#if defined(USE_ASH) - return ash::wm::IsActiveWindow(window); -#else - NOTIMPLEMENTED(); - return false; -#endif + return wm::IsActiveWindow(window); } void ActivateWindow(gfx::NativeWindow window) { -#if defined(USE_ASH) - ash::wm::ActivateWindow(window); -#else - NOTIMPLEMENTED(); -#endif + wm::ActivateWindow(window); } bool IsVisible(gfx::NativeView view) {
diff --git a/chrome/browser/plugins/plugin_observer.cc b/chrome/browser/plugins/plugin_observer.cc index e518aed..59fae76 100644 --- a/chrome/browser/plugins/plugin_observer.cc +++ b/chrome/browser/plugins/plugin_observer.cc
@@ -266,38 +266,6 @@ #endif } -void PluginObserver::RenderFrameCreated( - content::RenderFrameHost* render_frame_host) { -#if defined(OS_WIN) - // If the window belongs to the Ash desktop, before we navigate we need - // to tell the renderview that NPAPI plugins are not supported so it does - // not try to instantiate them. The final decision is actually done in - // the IO thread by PluginInfoMessageFilter of this proces,s but it's more - // complex to manage a map of Ash views in PluginInfoMessageFilter than - // just telling the renderer via IPC. - - // TODO(shrikant): Implement solution which will help associate - // render_view_host/webcontents/view/window instance with host desktop. - // Refer to issue http://crbug.com/317940. - // When non-active tabs are restored they are not added in view/window parent - // hierarchy (chrome::CreateRestoredTab/CreateParams). Normally we traverse - // parent hierarchy to identify containing desktop (like in function - // chrome::GetHostDesktopTypeForNativeView). - // Possible issue with chrome::GetActiveDesktop, is that it's global - // state, which remembers last active desktop, which may break in scenarios - // where we have instances on both Ash and Native desktop. - - // We will do both tests. Both have some factor of unreliability. - aura::Window* window = web_contents()->GetNativeView(); - if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH || - chrome::GetHostDesktopTypeForNativeView(window) == - chrome::HOST_DESKTOP_TYPE_ASH) { - int routing_id = render_frame_host->GetRoutingID(); - render_frame_host->Send(new ChromeViewMsg_NPAPINotSupported(routing_id)); - } -#endif -} - void PluginObserver::PluginCrashed(const base::FilePath& plugin_path, base::ProcessId plugin_pid) { DCHECK(!plugin_path.value().empty());
diff --git a/chrome/browser/plugins/plugin_observer.h b/chrome/browser/plugins/plugin_observer.h index ae88b2b..dfe8ad0 100644 --- a/chrome/browser/plugins/plugin_observer.h +++ b/chrome/browser/plugins/plugin_observer.h
@@ -38,7 +38,6 @@ ~PluginObserver() override; // content::WebContentsObserver implementation. - void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; void PluginCrashed(const base::FilePath& plugin_path, base::ProcessId plugin_pid) override; bool OnMessageReceived(const IPC::Message& message,
diff --git a/chrome/browser/policy/policy_helpers.cc b/chrome/browser/policy/policy_helpers.cc index 81e0150..da43eec 100644 --- a/chrome/browser/policy/policy_helpers.cc +++ b/chrome/browser/policy/policy_helpers.cc
@@ -21,17 +21,26 @@ bool OverrideBlacklistForURL(const GURL& url, bool* block, int* reason) { #if defined(OS_CHROMEOS) - // On ChromeOS browsing is only allowed once OOBE has completed. Therefore all - // requests are blocked until this condition is met. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( + // Don't block if OOBE has completed. + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( chromeos::switches::kOobeGuestSession)) { - if (!url.SchemeIs("chrome") && !url.SchemeIs("chrome-extension")) { - *reason = net::ERR_BLOCKED_ENROLLMENT_CHECK_PENDING; - *block = true; - return true; - } + return false; } - return false; + + // Don't block internal pages and extensions. + if (url.SchemeIs("chrome") || url.SchemeIs("chrome-extension")) { + return false; + } + + // Don't block Google's support web site. + if (url.SchemeIs(url::kHttpsScheme) && url.DomainIs("support.google.com")) { + return false; + } + + // Block the rest. + *reason = net::ERR_BLOCKED_ENROLLMENT_CHECK_PENDING; + *block = true; + return true; #elif defined(OS_IOS) return false; #else
diff --git a/chrome/browser/policy/test/policy_testserver.py b/chrome/browser/policy/test/policy_testserver.py index 480124c..c7f933d 100644 --- a/chrome/browser/policy/test/policy_testserver.py +++ b/chrome/browser/policy/test/policy_testserver.py
@@ -376,6 +376,8 @@ return (403, 'No authorization') policy = self.server.GetPolicies() + if ('managed_users' not in policy): + return (500, 'error in config - no managed users') username = self.server.ResolveUser(auth) if ('*' not in policy['managed_users'] and username not in policy['managed_users']): @@ -990,7 +992,7 @@ pass def GetPolicies(self): - """Returns the policies to be used, reloaded form the backend file every + """Returns the policies to be used, reloaded from the backend file every time this is called. """ policy = {}
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc index 9b74c7f..5e6cb29 100644 --- a/chrome/browser/prerender/prerender_browsertest.cc +++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -2906,7 +2906,8 @@ base::Bind(&CreateCertStore)); net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; - ssl_config.require_client_cert = true; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); https_server.ServeFilesFromSourceDirectory("chrome/test/data"); ASSERT_TRUE(https_server.Start()); @@ -2924,7 +2925,8 @@ base::Bind(&CreateCertStore)); net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; - ssl_config.require_client_cert = true; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); https_server.ServeFilesFromSourceDirectory("chrome/test/data"); ASSERT_TRUE(https_server.Start()); @@ -2950,7 +2952,8 @@ base::Bind(&CreateCertStore)); net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; - ssl_config.require_client_cert = true; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); https_server.ServeFilesFromSourceDirectory("chrome/test/data"); ASSERT_TRUE(https_server.Start());
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc index 8b15173..83917c3 100644 --- a/chrome/browser/printing/print_view_manager_base.cc +++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -231,7 +231,7 @@ IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBase, message) IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintPage, OnDidPrintPage) IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError, - OnShowInvalidPrinterSettingsError); + OnShowInvalidPrinterSettingsError) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled || PrintManager::OnMessageReceived(message); @@ -521,13 +521,12 @@ int cookie = cookie_; cookie_ = 0; - printing::PrintJobManager* print_job_manager = - g_browser_process->print_job_manager(); + PrintJobManager* print_job_manager = g_browser_process->print_job_manager(); // May be NULL in tests. if (!print_job_manager) return; - scoped_refptr<printing::PrinterQuery> printer_query; + scoped_refptr<PrinterQuery> printer_query; printer_query = queue_->PopPrinterQuery(cookie); if (!printer_query.get()) return;
diff --git a/chrome/browser/profiles/avatar_menu.cc b/chrome/browser/profiles/avatar_menu.cc index 44ddd78..ca3c1fa 100644 --- a/chrome/browser/profiles/avatar_menu.cc +++ b/chrome/browser/profiles/avatar_menu.cc
@@ -40,15 +40,17 @@ using content::BrowserThread; -AvatarMenu::AvatarMenu(ProfileInfoInterface* profile_cache, +AvatarMenu::AvatarMenu(ProfileAttributesStorage* profile_storage, AvatarMenuObserver* observer, Browser* browser) - : profile_list_(ProfileList::Create(profile_cache)), + : profile_list_(ProfileList::Create( + &g_browser_process->profile_manager()->GetProfileInfoCache())), menu_actions_(AvatarMenuActions::Create()), #if defined(ENABLE_SUPERVISED_USERS) supervised_user_observer_(this), #endif - profile_info_(profile_cache), + profile_info_( + &g_browser_process->profile_manager()->GetProfileInfoCache()), observer_(observer), browser_(browser) { DCHECK(profile_info_); @@ -156,8 +158,8 @@ const AvatarMenu::Item& AvatarMenu::GetItemAt(size_t index) const { return profile_list_->GetItemAt(index); } -size_t AvatarMenu::GetActiveProfileIndex() { +size_t AvatarMenu::GetActiveProfileIndex() { // During singleton profile deletion, this function can be called with no // profiles in the model - crbug.com/102278 . if (profile_list_->GetNumberOfItems() == 0)
diff --git a/chrome/browser/profiles/avatar_menu.h b/chrome/browser/profiles/avatar_menu.h index 6b420a0..f4d3bc3 100644 --- a/chrome/browser/profiles/avatar_menu.h +++ b/chrome/browser/profiles/avatar_menu.h
@@ -29,6 +29,7 @@ class AvatarMenuObserver; class Browser; class Profile; +class ProfileAttributesStorage; class ProfileInfoInterface; class ProfileList; class SupervisedUserService; @@ -90,7 +91,7 @@ // Constructor. |observer| can be NULL. |browser| can be NULL and a new one // will be created if an action requires it. - AvatarMenu(ProfileInfoInterface* profile_cache, + AvatarMenu(ProfileAttributesStorage* profile_storage, AvatarMenuObserver* observer, Browser* browser); ~AvatarMenu() override;
diff --git a/chrome/browser/profiles/profile_attributes_storage.h b/chrome/browser/profiles/profile_attributes_storage.h index 0036813..cb55bf0 100644 --- a/chrome/browser/profiles/profile_attributes_storage.h +++ b/chrome/browser/profiles/profile_attributes_storage.h
@@ -58,6 +58,14 @@ // Returns the count of known profiles. virtual size_t GetNumberOfProfiles() const = 0; + // Returns a unique name that can be assigned to a newly created profile. + virtual base::string16 ChooseNameForNewProfile(size_t icon_index) const = 0; + + // Returns an avatar icon index that can be assigned to a newly created + // profile. Note that the icon may not be unique since there are a limited + // set of default icons. + virtual size_t ChooseAvatarIconIndexForNewProfile() const = 0; + virtual void AddObserver(ProfileAttributesStorage::Observer* observer) = 0; virtual void RemoveObserver(ProfileAttributesStorage::Observer* observer) = 0;
diff --git a/chrome/browser/profiles/profile_info_cache.h b/chrome/browser/profiles/profile_info_cache.h index c54edc5e..17c996f 100644 --- a/chrome/browser/profiles/profile_info_cache.h +++ b/chrome/browser/profiles/profile_info_cache.h
@@ -151,12 +151,12 @@ bool IsDefaultProfileName(const base::string16& name) const; // Returns unique name that can be assigned to a newly created profile. - base::string16 ChooseNameForNewProfile(size_t icon_index) const; + base::string16 ChooseNameForNewProfile(size_t icon_index) const override; // Returns an avatar icon index that can be assigned to a newly created // profile. Note that the icon may not be unique since there are a limited // set of default icons. - size_t ChooseAvatarIconIndexForNewProfile() const; + size_t ChooseAvatarIconIndexForNewProfile() const override; // Statistics void SetStatsBrowsingHistoryOfProfileAtIndex(size_t index, int value);
diff --git a/chrome/browser/profiles/profile_list_desktop_browsertest.cc b/chrome/browser/profiles/profile_list_desktop_browsertest.cc index 7f79ebc..89458355 100644 --- a/chrome/browser/profiles/profile_list_desktop_browsertest.cc +++ b/chrome/browser/profiles/profile_list_desktop_browsertest.cc
@@ -37,8 +37,8 @@ public: ProfileListDesktopBrowserTest() {} - scoped_ptr<AvatarMenu> CreateAvatarMenu(ProfileInfoCache* cache) { - return scoped_ptr<AvatarMenu>(new AvatarMenu(cache, NULL, browser())); + scoped_ptr<AvatarMenu> CreateAvatarMenu(ProfileAttributesStorage* storage) { + return scoped_ptr<AvatarMenu>(new AvatarMenu(storage, NULL, browser())); } private: @@ -64,9 +64,11 @@ ProfileManager* profile_manager = g_browser_process->profile_manager(); Profile* current_profile = browser()->profile(); ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); + ProfileAttributesStorage& storage = + profile_manager->GetProfileAttributesStorage(); size_t index = cache.GetIndexOfProfileWithPath(current_profile->GetPath()); - scoped_ptr<AvatarMenu> menu = CreateAvatarMenu(&cache); + scoped_ptr<AvatarMenu> menu = CreateAvatarMenu(&storage); menu->RebuildMenu(); BrowserList* browser_list = BrowserList::GetInstance(); @@ -107,6 +109,8 @@ ProfileManager* profile_manager = g_browser_process->profile_manager(); Profile* current_profile = browser()->profile(); ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); + ProfileAttributesStorage& storage = + profile_manager->GetProfileAttributesStorage(); base::FilePath path_profile1 = current_profile->GetPath(); base::FilePath user_dir = cache.GetUserDataDir(); @@ -123,7 +127,7 @@ content::RunMessageLoop(); ASSERT_EQ(cache.GetNumberOfProfiles(), 2U); - scoped_ptr<AvatarMenu> menu = CreateAvatarMenu(&cache); + scoped_ptr<AvatarMenu> menu = CreateAvatarMenu(&storage); menu->RebuildMenu(); BrowserList* browser_list = BrowserList::GetInstance(); EXPECT_EQ(1U, browser_list->size());
diff --git a/chrome/browser/profiles/profile_list_desktop_unittest.cc b/chrome/browser/profiles/profile_list_desktop_unittest.cc index 041473f..640f891a 100644 --- a/chrome/browser/profiles/profile_list_desktop_unittest.cc +++ b/chrome/browser/profiles/profile_list_desktop_unittest.cc
@@ -63,9 +63,9 @@ mock_observer_.reset(new MockObserver()); EXPECT_EQ(0, change_count()); - // Reset the model. + // Reset the menu. avatar_menu_.reset(new AvatarMenu( - manager()->profile_info_cache(), + manager()->profile_attributes_storage(), mock_observer_.get(), NULL)); avatar_menu_->RebuildMenu();
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc index b8725fc6..1457b87 100644 --- a/chrome/browser/push_messaging/push_messaging_service_impl.cc +++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -378,7 +378,7 @@ // Push does not allow permission requests from iframes. profile_->GetPermissionManager()->RequestPermission( content::PermissionType::PUSH_MESSAGING, web_contents->GetMainFrame(), - requesting_origin, true /* user_gesture */, + requesting_origin, base::Bind(&PushMessagingServiceImpl::DidRequestPermission, weak_factory_.GetWeakPtr(), app_identifier, sender_id, callback)); @@ -401,10 +401,10 @@ return; } - GURL embedding_origin = requesting_origin; blink::WebPushPermissionStatus permission_status = - PushMessagingServiceImpl::GetPermissionStatus( - requesting_origin, embedding_origin, user_visible); + PushMessagingServiceImpl::GetPermissionStatus(requesting_origin, + user_visible); + if (permission_status != blink::WebPushPermissionStatusGranted) { SubscribeEndWithError(register_callback, content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED); @@ -420,15 +420,16 @@ } blink::WebPushPermissionStatus PushMessagingServiceImpl::GetPermissionStatus( - const GURL& requesting_origin, - const GURL& embedding_origin, + const GURL& origin, bool user_visible) { if (!user_visible) return blink::WebPushPermissionStatusDenied; + // Because the Push API is tied to Service Workers, many usages of the API + // won't have an embedding origin at all. Only consider the requesting + // |origin| when checking whether permission to use the API has been granted. return ToPushPermission(profile_->GetPermissionManager()->GetPermissionStatus( - content::PermissionType::PUSH_MESSAGING, requesting_origin, - embedding_origin)); + content::PermissionType::PUSH_MESSAGING, origin, origin)); } bool PushMessagingServiceImpl::SupportNonVisibleMessages() { @@ -764,7 +765,7 @@ // Assumes user_visible always since this is just meant to check // if the permission was previously granted and not revoked. bool PushMessagingServiceImpl::IsPermissionSet(const GURL& origin) { - return GetPermissionStatus(origin, origin, true /* user_visible */) == + return GetPermissionStatus(origin, true /* user_visible */) == blink::WebPushPermissionStatusGranted; }
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h index 449154b9..cae261f 100644 --- a/chrome/browser/push_messaging/push_messaging_service_impl.h +++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -96,8 +96,7 @@ const std::string& sender_id, const content::PushMessagingService::UnregisterCallback&) override; blink::WebPushPermissionStatus GetPermissionStatus( - const GURL& requesting_origin, - const GURL& embedding_origin, + const GURL& origin, bool user_visible) override; bool SupportNonVisibleMessages() override;
diff --git a/chrome/browser/push_messaging/push_messaging_service_unittest.cc b/chrome/browser/push_messaging/push_messaging_service_unittest.cc index 31a6d9e..61fdd47 100644 --- a/chrome/browser/push_messaging/push_messaging_service_unittest.cc +++ b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
@@ -151,7 +151,7 @@ // (1) Make sure that |kExampleOrigin| has access to use Push Messaging. ASSERT_EQ(blink::WebPushPermissionStatusGranted, - push_service->GetPermissionStatus(origin, origin, true)); + push_service->GetPermissionStatus(origin, true)); std::string subscription_id; std::vector<uint8_t> p256dh, auth;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index e69f93ca..d492bf52 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -729,8 +729,8 @@ if (content_type_->SupportsGroup( ContextMenuContentType::ITEM_GROUP_EDITABLE)) { menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); - AppendPlatformEditableItems(); AppendLanguageSettings(); + AppendPlatformEditableItems(); } if (content_type_->SupportsGroup(
diff --git a/chrome/browser/renderer_context_menu/spelling_options_submenu_observer.cc b/chrome/browser/renderer_context_menu/spelling_options_submenu_observer.cc index 330910c..e570a59 100644 --- a/chrome/browser/renderer_context_menu/spelling_options_submenu_observer.cc +++ b/chrome/browser/renderer_context_menu/spelling_options_submenu_observer.cc
@@ -66,9 +66,9 @@ // Add an item that opens the 'Settings - Languages' page. This item is // handled in RenderViewContextMenu. - submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR); submenu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS, IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS); + submenu_model_.AddSeparator(ui::NORMAL_SEPARATOR); if (num_selected_dictionaries_ > 0) { // Add a 'Check spelling while typing' item in the sub menu.
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js index 78c4c03..1f1fbfb 100644 --- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js +++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js
@@ -483,6 +483,7 @@ var background = new cvox.ChromeVoxBackground(); background.init(); window['speak'] = goog.bind(background.tts.speak, background.tts); + global.backgroundTts = background.backgroundTts_; // Export the prefs object for access by the options page. window['prefs'] = background.prefs;
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json index b7bfeef..d75314fd 100644 --- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json +++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
@@ -283,8 +283,7 @@ "sequence": { "cvoxModifier": true, "keys": { - "keyCode": [32], - "shitKey": [true] + "keyCode": [77] } } }, @@ -415,6 +414,15 @@ } }, { + "command": "cyclePunctuationEcho", + "sequence": { + "cvoxModifier": true, + "keys": { + "keyCode": [65, 80] + } + } + }, + { "command": "showKbExplorerPage", "sequence": { "cvoxModifier": true, @@ -424,6 +432,15 @@ } }, { + "command": "cycleTypingEcho", + "sequence": { + "cvoxModifier": true, + "keys": { + "keyCode": [65, 84] + } + } + }, + { "command": "showOptionsPage", "sequence": { "cvoxModifier": true, @@ -431,6 +448,15 @@ "keyCode": [79, 79] } } + }, + { + "command": "toggleEarcons", + "sequence": { + "cvoxModifier": true, + "keys": { + "keyCode": [65, 69] + } + } } ] }
diff --git a/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js b/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js index 2e98775..26cb50e 100644 --- a/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js +++ b/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js
@@ -298,6 +298,9 @@ /** @param {function(!chrome.automation.AutomationNode):void} callback */ chrome.automation.getDesktop = function(callback) {}; +/** @param {function(!chrome.automation.AutomationNode):void} callback */ +chrome.automation.getFocus = function(callback) {}; + /** * @param {string} filter * @param {function(chrome.automation.TreeChange) : void}
diff --git a/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs index c5f0fa3..bd66571f 100644 --- a/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs +++ b/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs
@@ -197,12 +197,12 @@ var extractor = new cvox.ContentEditableExtractor(); extractor.update(textbox); - assertEquals('One\ntwo\nthree', extractor.getText()); + assertEquals('One two three', extractor.getText()); assertEquals(0, extractor.getLineStart(0)); - assertEquals(4, extractor.getLineEnd(0)); - assertEquals(4, extractor.getLineStart(1)); - assertEquals(8, extractor.getLineEnd(1)); - assertEquals(8, extractor.getLineStart(2)); + assertEquals(3, extractor.getLineEnd(0)); + assertEquals(3, extractor.getLineStart(1)); + assertEquals(7, extractor.getLineEnd(1)); + assertEquals(7, extractor.getLineStart(2)); assertEquals(13, extractor.getLineEnd(2)); // Test all possible cursor positions.
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js index 16e8cbb5..f671a2a4 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
@@ -208,6 +208,22 @@ (new PanelCommand(PanelCommandType.DISABLE_MENUS)).send(); } + // If switching to Classic from any automation-API-based mode, + // clear the focus ring. + if (mode === ChromeVoxMode.CLASSIC && mode != this.mode_) { + if (cvox.ChromeVox.isChromeOS) + chrome.accessibilityPrivate.setFocusRing([]); + } + + // If switching away from Classic to any automation-API-based mode, + // update the range based on what's focused. + if (this.mode_ === ChromeVoxMode.CLASSIC && mode != this.mode_) { + chrome.automation.getFocus((function(focus) { + if (focus) + this.setCurrentRange(cursors.Range.fromNode(focus)); + }).bind(this)); + } + this.mode_ = mode; }, @@ -499,6 +515,11 @@ // Leaving unlocalized as 'next' isn't an official name. cvox.ChromeVox.tts.speak(isClassic ? 'classic' : 'next', cvox.QueueMode.FLUSH, {doNotInterrupt: true}); + + // If the new mode is Classic, return now so we don't announce + // anything more. + if (newMode == ChromeVoxMode.CLASSIC) + return false; break; case 'toggleStickyMode': cvox.ChromeVoxBackground.setPref('sticky', @@ -524,26 +545,62 @@ break; case 'decreaseTtsRate': this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.RATE, false); - break; + return false; case 'increaseTtsRate': this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.RATE, true); - break; + return false; case 'decreaseTtsPitch': this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.PITCH, false); - break; + return false; case 'increaseTtsPitch': this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.PITCH, true); - break; + return false; case 'decreaseTtsVolume': this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.VOLUME, false); - break; + return false; case 'increaseTtsVolume': this.increaseOrDecreaseSpeechProperty_(cvox.AbstractTts.VOLUME, true); - break; + return false; case 'stopSpeech': cvox.ChromeVox.tts.stop(); global.isReadingContinuously = false; return false; + case 'toggleEarcons': + cvox.AbstractEarcons.enabled = !cvox.AbstractEarcons.enabled; + var announce = cvox.AbstractEarcons.enabled ? + Msgs.getMsg('earcons_on') : + Msgs.getMsg('earcons_off'); + cvox.ChromeVox.tts.speak( + announce, cvox.QueueMode.FLUSH, + cvox.AbstractTts.PERSONALITY_ANNOTATION); + return false; + case 'cycleTypingEcho': + cvox.ChromeVox.typingEcho = + cvox.TypingEcho.cycle(cvox.ChromeVox.typingEcho); + var announce = ''; + switch (cvox.ChromeVox.typingEcho) { + case cvox.TypingEcho.CHARACTER: + announce = Msgs.getMsg('character_echo'); + break; + case cvox.TypingEcho.WORD: + announce = Msgs.getMsg('word_echo'); + break; + case cvox.TypingEcho.CHARACTER_AND_WORD: + announce = Msgs.getMsg('character_and_word_echo'); + break; + case cvox.TypingEcho.NONE: + announce = Msgs.getMsg('none_echo'); + break; + } + cvox.ChromeVox.tts.speak( + announce, cvox.QueueMode.FLUSH, + cvox.AbstractTts.PERSONALITY_ANNOTATION); + return false; + case 'cyclePunctuationEcho': + cvox.ChromeVox.tts.speak(Msgs.getMsg( + global.backgroundTts.cyclePunctuationEcho()), + cvox.QueueMode.FLUSH); + return false; default: return true; }
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/base_automation_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/base_automation_handler.js index 5d2f56a..0081ae3 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/base_automation_handler.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/base_automation_handler.js
@@ -33,6 +33,7 @@ focus: this.onFocus, hover: this.onEventDefault, loadComplete: this.onLoadComplete, + menuListItemSelected: this.onEventDefault, menuStart: this.onEventDefault, menuEnd: this.onEventDefault, selection: this.onEventDefault,
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js index 8b34336..a278b9a 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js
@@ -49,6 +49,11 @@ type: chrome.automation.EventType.loadComplete}); } } + + chrome.automation.getFocus((function(focus) { + if (focus) + this.onFocus({target: focus, type: 'focus'}); + }).bind(this)); }; DesktopAutomationHandler.prototype = { @@ -140,8 +145,7 @@ if (node.role == RoleType.embeddedObject || node.role == RoleType.client) return; - if (this.isEditable_(node)) - this.createTextEditHandlerIfNeeded_(evt.target); + this.createTextEditHandlerIfNeeded_(evt.target); // Since we queue output mostly for live regions support and there isn't a // reliable way to know if this focus event resulted from a user's explicit @@ -180,13 +184,13 @@ /** @override */ onTextChanged: function(evt) { - if (this.isEditable_(evt.target)) + if (evt.target.state.editable) this.onEditableChanged_(evt); }, /** @override */ onTextSelectionChanged: function(evt) { - if (this.isEditable_(evt.target)) + if (evt.target.state.editable) this.onEditableChanged_(evt); }, @@ -223,7 +227,7 @@ */ onValueChanged: function(evt) { // Delegate to the edit text handler if this is an editable. - if (this.isEditable_(evt.target)) { + if (evt.target.state.editable) { this.onEditableChanged_(evt); return; } @@ -267,18 +271,6 @@ this.textEditHandler_.node !== node) { this.textEditHandler_ = editing.TextEditHandler.createForNode(node); } - }, - - /** - * Returns true if |node| is editable. - * @param {AutomationNode} node - * @return {boolean} - * @private - */ - isEditable_: function(node) { - // Remove the check for role after m47 whereafter the editable state can be - // used to know when to create an editable text handler. - return node.role == RoleType.textField || node.state.editable; } };
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js index de97d53..4d988b8 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing.js
@@ -78,7 +78,9 @@ evt.type !== EventType.valueChanged && evt.type !== EventType.focus) return; - if (!evt.target.state.focused || !evt.target.state.editable) + if (!evt.target.state.focused || + !evt.target.state.editable || + evt.target != this.node_) return; this.editableText_.onUpdate(); @@ -199,9 +201,18 @@ * @return {editing.TextEditHandler} */ editing.TextEditHandler.createForNode = function(node) { - if (!node.state.richlyEditable) - return new TextFieldTextEditHandler(node); - // TODO(plundblad): Support contenteditable. + var rootFocusedEditable = null; + var testNode = node; + + do { + if (testNode.state.focused && testNode.state.editable) + rootFocusedEditable = testNode; + testNode = testNode.parent; + } while (testNode); + + if (rootFocusedEditable) + return new TextFieldTextEditHandler(rootFocusedEditable); + return null; };
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js index 5f89c31..c6115d8 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -821,7 +821,7 @@ go: function() { // Speech. var queueMode = this.queueMode_; - if (Output.flushNextSpeechUtterance_) { + if (Output.flushNextSpeechUtterance_ && this.speechBuffer_.length > 0) { queueMode = cvox.QueueMode.FLUSH; Output.flushNextSpeechUtterance_ = false; }
diff --git a/chrome/browser/resources/options/chromeos/display_layout.js b/chrome/browser/resources/options/chromeos/display_layout.js new file mode 100644 index 0000000..ea6bf76d --- /dev/null +++ b/chrome/browser/resources/options/chromeos/display_layout.js
@@ -0,0 +1,263 @@ +// 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.exportPath('options'); + +/** + * Enumeration of display layout. These values must match the C++ values in + * ash::DisplayController. + * @enum {number} + */ +options.DisplayLayoutType = { + TOP: 0, + RIGHT: 1, + BOTTOM: 2, + LEFT: 3 +}; + +/** + * @typedef {{ + * left: number, + * top: number, + * width: number, + * height: number + * }} + */ +options.DisplayBounds; + +/** + * @typedef {{ + * x: number, + * y: number + * }} + */ +options.DisplayPosition; + +cr.define('options', function() { + 'use strict'; + + /** + * Snaps the region [point, width] to [basePoint, baseWidth] if + * the [point, width] is close enough to the base's edge. + * @param {number} point The starting point of the region. + * @param {number} width The width of the region. + * @param {number} basePoint The starting point of the base region. + * @param {number} baseWidth The width of the base region. + * @return {number} The moved point. Returns the point itself if it doesn't + * need to snap to the edge. + * @private + */ + function snapToEdge(point, width, basePoint, baseWidth) { + // If the edge of the region is smaller than this, it will snap to the + // base's edge. + /** @const */ var SNAP_DISTANCE_PX = 16; + + var startDiff = Math.abs(point - basePoint); + var endDiff = Math.abs(point + width - (basePoint + baseWidth)); + // Prefer the closer one if both edges are close enough. + if (startDiff < SNAP_DISTANCE_PX && startDiff < endDiff) + return basePoint; + else if (endDiff < SNAP_DISTANCE_PX) + return basePoint + baseWidth - width; + + return point; + } + + /** + * @constructor + * @param {string} id + * @param {string} name + * @param {!options.DisplayBounds} bounds + * @param {!options.DisplayLayoutType} layoutType + * @param {string} parentId + * @return {!options.DisplayLayout} + */ + function DisplayLayout(id, name, bounds, layoutType, parentId) { + this.bounds = bounds; + this.id = id; + this.layoutType = layoutType; + this.name = name; + this.originalDivOffsets = {x: 0, y: 0}; + this.parentId = parentId; + } + + // Class describing a display layout. + DisplayLayout.prototype = { + /** @type {?options.DisplayBounds} */ + bounds: null, + + /** @type {?HTMLElement} */ + div: null, + + /** @type {string} */ + id: '', + + /** @type {options.DisplayLayoutType} */ + layoutType: options.DisplayLayoutType.TOP, + + /** @type {string} */ + name: '', + + /** @type {number} */ + offset: 0, + + /** @type {?options.DisplayPosition} */ + originalDivOffsets: null, + + /** @type {string} */ + parentId: '', + + /** + * Calculates the div layout for displayLayout. + * @param {?options.DisplayPosition} offset + * @param {number} scale + * @param {?options.DisplayLayout} parentLayout + */ + layoutDivFromBounds: function(offset, scale, parentLayout) { + assert(offset); + var div = this.div; + var bounds = this.bounds; + + div.style.width = Math.floor(bounds.width * scale) + 'px'; + div.style.height = Math.floor(bounds.height * scale) + 'px'; + + if (!parentLayout) { + div.style.left = Math.trunc(bounds.left * scale) + offset.x + 'px'; + div.style.top = Math.trunc(bounds.top * scale) + offset.y + 'px'; + return; + } + + var parentDiv = parentLayout.div; + switch (this.layoutType) { + case options.DisplayLayoutType.TOP: + div.style.left = Math.trunc(bounds.left * scale) + offset.x + 'px'; + div.style.top = parentDiv.offsetTop - div.offsetHeight + 'px'; + break; + case options.DisplayLayoutType.RIGHT: + div.style.left = parentDiv.offsetLeft + parentDiv.offsetWidth + 'px'; + div.style.top = Math.trunc(bounds.top * scale) + offset.y + 'px'; + break; + case options.DisplayLayoutType.BOTTOM: + div.style.left = Math.trunc(bounds.left * scale) + offset.x + 'px'; + div.style.top = parentDiv.offsetTop + parentDiv.offsetHeight + 'px'; + break; + case options.DisplayLayoutType.LEFT: + div.style.left = parentDiv.offsetLeft - div.offsetWidth + 'px'; + div.style.top = Math.trunc(bounds.top * scale) + offset.y + 'px'; + break; + } + }, + + /** + * Calculates the offset for displayLayout relative to its parent. + * @param {number} scale + * @param {options.DisplayLayout} parent + */ + calculateOffset: function(scale, parent) { + // Offset is calculated from top or left edge. + if (!parent) { + this.offset = 0; + return; + } + assert(this.parentId == parent.id); + var offset; + if (this.layoutType == options.DisplayLayoutType.LEFT || + this.layoutType == options.DisplayLayoutType.RIGHT) { + offset = this.div.offsetTop - parent.div.offsetTop; + } else { + offset = this.div.offsetLeft - parent.div.offsetLeft; + } + this.offset = Math.trunc(offset / scale); + }, + + /** + * Update the div location to the position closest to |newPosition| along + * the edge of |parentDiv| specified by |layoutType|. + * @param {options.DisplayPosition} newPosition + * @param {?HTMLElement} parentDiv + * @param {!options.DisplayLayoutType} layoutType + * @private + */ + setDivPosition(newPosition, parentDiv, layoutType) { + var div = this.div; + switch (layoutType) { + case options.DisplayLayoutType.RIGHT: + div.style.left = parentDiv.offsetLeft + parentDiv.offsetWidth + 'px'; + div.style.top = newPosition.y + 'px'; + break; + case options.DisplayLayoutType.LEFT: + div.style.left = parentDiv.offsetLeft - div.offsetWidth + 'px'; + div.style.top = newPosition.y + 'px'; + break; + case options.DisplayLayoutType.TOP: + div.style.top = parentDiv.offsetTop - div.offsetHeight + 'px'; + div.style.left = newPosition.x + 'px'; + break; + case options.DisplayLayoutType.BOTTOM: + div.style.top = parentDiv.offsetTop + parentDiv.offsetHeight + 'px'; + div.style.left = newPosition.x + 'px'; + break; + } + }, + + /** + * Ensures that there is a minimum overlap when displays meet at a corner. + * @param {?HTMLElement} parentDiv + * @param {options.DisplayLayoutType} layoutType + * @private + */ + adjustCorners: function(parentDiv, layoutType) { + // The number of pixels to share the edges between displays. + /** @const */ var MIN_OFFSET_OVERLAP = 5; + + var div = this.div; + if (layoutType == options.DisplayLayoutType.LEFT || + layoutType == options.DisplayLayoutType.RIGHT) { + var top = Math.max( + div.offsetTop, + parentDiv.offsetTop - div.offsetHeight + MIN_OFFSET_OVERLAP); + top = Math.min( + top, + parentDiv.offsetTop + parentDiv.offsetHeight - MIN_OFFSET_OVERLAP); + div.style.top = top + 'px'; + } else { + var left = Math.max( + div.offsetLeft, + parentDiv.offsetLeft - div.offsetWidth + MIN_OFFSET_OVERLAP); + left = Math.min( + left, + parentDiv.offsetLeft + parentDiv.offsetWidth - MIN_OFFSET_OVERLAP); + div.style.left = left + 'px'; + } + }, + + /** + * Snaps a horizontal value, see SnapToEdge. + * @param {number} x + * @param {?HTMLElement} parentDiv + * @return {number} + * @private + */ + snapToX: function(x, parentDiv) { + return snapToEdge( + x, this.div.offsetWidth, parentDiv.offsetLeft, parentDiv.offsetWidth); + }, + + /** + * Snaps a vertical value, see SnapToEdge. + * @param {number} y + * @param {?HTMLElement} parentDiv + * @return {number} + * @private + */ + snapToY: function(y, parentDiv) { + return snapToEdge( + y, this.div.offsetHeight, parentDiv.offsetTop, + parentDiv.offsetHeight); + }, + }; + + // Export + return {DisplayLayout: DisplayLayout}; +});
diff --git a/chrome/browser/resources/options/chromeos/display_layout_manager.js b/chrome/browser/resources/options/chromeos/display_layout_manager.js index f264393..54ba6a1 100644 --- a/chrome/browser/resources/options/chromeos/display_layout_manager.js +++ b/chrome/browser/resources/options/chromeos/display_layout_manager.js
@@ -4,50 +4,6 @@ cr.exportPath('options'); -/** - * Enumeration of display layout. These values must match the C++ values in - * ash::DisplayController. - * @enum {number} - */ -options.DisplayLayoutType = { - TOP: 0, - RIGHT: 1, - BOTTOM: 2, - LEFT: 3 -}; - -/** - * @typedef {{ - * left: number, - * top: number, - * width: number, - * height: number - * }} - */ -options.DisplayBounds; - -/** - * @typedef {{ - * x: number, - * y: number - * }} - */ -options.DisplayPosition; - -/** - * @typedef {{ - * bounds: !options.DisplayBounds, - * div: ?HTMLElement, - * id: string, - * layoutType: options.DisplayLayoutType, - * name: string, - * offset: number, - * originalPosition: !options.DisplayPosition, - * parentId: string - * }} - */ -options.DisplayLayout; - cr.define('options', function() { 'use strict'; @@ -57,7 +13,7 @@ * @param {!options.DisplayPosition} point The point to check the position. * @return {options.DisplayLayoutType} */ - function getPositionToRectangle(rect, point) { + function getLayoutTypeForPosition(rect, point) { // Separates the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of // the rect, and decides which area the display should reside. var diagonalSlope = rect.height / rect.width; @@ -78,30 +34,22 @@ } /** - * Snaps the region [point, width] to [basePoint, baseWidth] if - * the [point, width] is close enough to the base's edge. - * @param {number} point The starting point of the region. - * @param {number} width The width of the region. - * @param {number} basePoint The starting point of the base region. - * @param {number} baseWidth The width of the base region. - * @return {number} The moved point. Returns the point itself if it doesn't - * need to snap to the edge. - * @private + * @param {options.DisplayLayoutType} layoutType + * @return {!options.DisplayLayoutType} */ - function snapToEdge(point, width, basePoint, baseWidth) { - // If the edge of the region is smaller than this, it will snap to the - // base's edge. - /** @const */ var SNAP_DISTANCE_PX = 16; - - var startDiff = Math.abs(point - basePoint); - var endDiff = Math.abs(point + width - (basePoint + baseWidth)); - // Prefer the closer one if both edges are close enough. - if (startDiff < SNAP_DISTANCE_PX && startDiff < endDiff) - return basePoint; - else if (endDiff < SNAP_DISTANCE_PX) - return basePoint + baseWidth - width; - - return point; + function invertLayoutType(layoutType) { + switch (layoutType) { + case options.DisplayLayoutType.RIGHT: + return options.DisplayLayoutType.LEFT; + case options.DisplayLayoutType.LEFT: + return options.DisplayLayoutType.RIGHT; + case options.DisplayLayoutType.TOP: + return options.DisplayLayoutType.BOTTOM; + case options.DisplayLayoutType.BOTTOM: + return options.DisplayLayoutType.TOP; + } + assertNotReached(); + return layoutType; } /** @@ -236,12 +184,8 @@ var baseLayout = this.getBaseLayout_(displayLayout); var baseDiv = baseLayout.div; - newPosition.x = snapToEdge( - newPosition.x, div.offsetWidth, baseDiv.offsetLeft, - baseDiv.offsetWidth); - newPosition.y = snapToEdge( - newPosition.y, div.offsetHeight, baseDiv.offsetTop, - baseDiv.offsetHeight); + newPosition.x = displayLayout.snapToX(newPosition.x, baseDiv); + newPosition.y = displayLayout.snapToY(newPosition.y, baseDiv); /** @type {!options.DisplayPosition} */ var newCenter = { x: newPosition.x + div.offsetWidth / 2, @@ -259,86 +203,40 @@ // a single parent with one child. var isPrimary = displayLayout.parentId == ''; - // layoutType is always stored in the child layout. - var layoutType = - isPrimary ? baseLayout.layoutType : displayLayout.layoutType; - - switch (getPositionToRectangle(baseBounds, newCenter)) { - case options.DisplayLayoutType.RIGHT: - layoutType = isPrimary ? options.DisplayLayoutType.LEFT : - options.DisplayLayoutType.RIGHT; - break; - case options.DisplayLayoutType.LEFT: - layoutType = isPrimary ? options.DisplayLayoutType.RIGHT : - options.DisplayLayoutType.LEFT; - break; - case options.DisplayLayoutType.TOP: - layoutType = isPrimary ? options.DisplayLayoutType.BOTTOM : - options.DisplayLayoutType.TOP; - break; - case options.DisplayLayoutType.BOTTOM: - layoutType = isPrimary ? options.DisplayLayoutType.TOP : - options.DisplayLayoutType.BOTTOM; - break; - } + var layoutType = getLayoutTypeForPosition(baseBounds, newCenter); + if (isPrimary) + layoutType = invertLayoutType(layoutType); if (layoutType == options.DisplayLayoutType.LEFT || layoutType == options.DisplayLayoutType.RIGHT) { - if (newPosition.y > baseDiv.offsetTop + baseDiv.offsetHeight) + if (newPosition.y > baseDiv.offsetTop + baseDiv.offsetHeight) { layoutType = isPrimary ? options.DisplayLayoutType.TOP : options.DisplayLayoutType.BOTTOM; - else if (newPosition.y + div.offsetHeight < baseDiv.offsetTop) + } else if (newPosition.y + div.offsetHeight < baseDiv.offsetTop) { layoutType = isPrimary ? options.DisplayLayoutType.BOTTOM : options.DisplayLayoutType.TOP; + } } else { - if (newPosition.x > baseDiv.offsetLeft + baseDiv.offsetWidth) + if (newPosition.x > baseDiv.offsetLeft + baseDiv.offsetWidth) { layoutType = isPrimary ? options.DisplayLayoutType.LEFT : options.DisplayLayoutType.RIGHT; - else if (newPosition.x + div.offsetWidth < baseDiv.offsetLeft) + } else if (newPosition.x + div.offsetWidth < baseDiv.offsetLeft) { layoutType = isPrimary ? options.DisplayLayoutType.RIGHT : options.DisplayLayoutType.LEFT; + } } + // layout is always relative to primary and stored in the child layout. var layoutToBase; if (!isPrimary) { displayLayout.layoutType = layoutType; layoutToBase = layoutType; } else { baseLayout.layoutType = layoutType; - switch (layoutType) { - case options.DisplayLayoutType.RIGHT: - layoutToBase = options.DisplayLayoutType.LEFT; - break; - case options.DisplayLayoutType.LEFT: - layoutToBase = options.DisplayLayoutType.RIGHT; - break; - case options.DisplayLayoutType.TOP: - layoutToBase = options.DisplayLayoutType.BOTTOM; - break; - case options.DisplayLayoutType.BOTTOM: - layoutToBase = options.DisplayLayoutType.TOP; - break; - } + layoutToBase = invertLayoutType(layoutType); } - switch (layoutToBase) { - case options.DisplayLayoutType.RIGHT: - div.style.left = baseDiv.offsetLeft + baseDiv.offsetWidth + 'px'; - div.style.top = newPosition.y + 'px'; - break; - case options.DisplayLayoutType.LEFT: - div.style.left = baseDiv.offsetLeft - div.offsetWidth + 'px'; - div.style.top = newPosition.y + 'px'; - break; - case options.DisplayLayoutType.TOP: - div.style.top = baseDiv.offsetTop - div.offsetHeight + 'px'; - div.style.left = newPosition.x + 'px'; - break; - case options.DisplayLayoutType.BOTTOM: - div.style.top = baseDiv.offsetTop + baseDiv.offsetHeight + 'px'; - div.style.left = newPosition.x + 'px'; - break; - } + displayLayout.setDivPosition(newPosition, baseDiv, layoutToBase); }, /** @@ -348,42 +246,27 @@ * @return {boolean} True if the final position differs from the original. */ finalizePosition: function(id) { - // Make sure the dragging location is connected. var displayLayout = this.displayLayoutMap_[id]; var div = displayLayout.div; var baseLayout = this.getBaseLayout_(displayLayout); - var baseDiv = baseLayout.div; var isPrimary = displayLayout.parentId == ''; var layoutType = isPrimary ? baseLayout.layoutType : displayLayout.layoutType; - // The number of pixels to share the edges between displays. - /** @const */ var MIN_OFFSET_OVERLAP = 5; - - if (layoutType == options.DisplayLayoutType.LEFT || - layoutType == options.DisplayLayoutType.RIGHT) { - var top = Math.max( - div.offsetTop, - baseDiv.offsetTop - div.offsetHeight + MIN_OFFSET_OVERLAP); - top = Math.min( - top, baseDiv.offsetTop + baseDiv.offsetHeight - MIN_OFFSET_OVERLAP); - div.style.top = top + 'px'; - } else { - var left = Math.max( - div.offsetLeft, - baseDiv.offsetLeft - div.offsetWidth + MIN_OFFSET_OVERLAP); - left = Math.min( - left, - baseDiv.offsetLeft + baseDiv.offsetWidth - MIN_OFFSET_OVERLAP); - div.style.left = left + 'px'; - } + // Make sure the dragging location is connected. + displayLayout.adjustCorners(baseLayout.div, layoutType); // Calculate the offset of the child display. - this.calculateOffset_(isPrimary ? baseLayout : displayLayout); + if (isPrimary) { + baseLayout.calculateOffset(this.visualScale_, displayLayout); + } else { + var parent = this.displayLayoutMap_[displayLayout.parentId]; + displayLayout.calculateOffset(this.visualScale_, parent); + } - return displayLayout.originalPosition.x != div.offsetLeft || - displayLayout.originalPosition.y != div.offsetTop; + return displayLayout.originalDivOffsets.x != div.offsetLeft || + displayLayout.originalDivOffsets.y != div.offsetTop; }, /** @@ -442,16 +325,17 @@ createDisplayLayoutDiv_: function(id, displayAreaDiv) { var displayLayout = this.displayLayoutMap_[id]; var parentId = displayLayout.parentId; - var offset = this.displayAreaOffset_; + var parentLayout = null; if (parentId) { // Ensure the parent div is created first. - var parentLayout = this.displayLayoutMap_[parentId]; + parentLayout = this.displayLayoutMap_[parentId]; if (!parentLayout.div) this.createDisplayLayoutDiv_(parentId, displayAreaDiv); } var div = /** @type {!HTMLElement} */ (document.createElement('div')); div.className = 'displays-display'; + displayLayout.div = div; // div needs to be added to the DOM tree first, otherwise offsetHeight for // nameContainer below cannot be computed. @@ -461,74 +345,18 @@ nameContainer.textContent = displayLayout.name; div.appendChild(nameContainer); - var bounds = displayLayout.bounds; - div.style.width = Math.floor(bounds.width * this.visualScale_) + 'px'; - var newHeight = Math.floor(bounds.height * this.visualScale_); - div.style.height = newHeight + 'px'; + var newHeight = + Math.floor(displayLayout.bounds.height * this.visualScale_); nameContainer.style.marginTop = (newHeight - nameContainer.offsetHeight) / 2 + 'px'; - if (displayLayout.parentId == '') { - div.style.left = - Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; - div.style.top = - Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; - } else { - // Don't trust the child display's x or y, because it may cause a - // 1px gap due to rounding, which will create a fake update on end - // dragging. See crbug.com/386401 - var parentDiv = this.displayLayoutMap_[displayLayout.parentId].div; - switch (displayLayout.layoutType) { - case options.DisplayLayoutType.TOP: - div.style.left = - Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; - div.style.top = parentDiv.offsetTop - div.offsetHeight + 'px'; - break; - case options.DisplayLayoutType.RIGHT: - div.style.left = - parentDiv.offsetLeft + parentDiv.offsetWidth + 'px'; - div.style.top = - Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; - break; - case options.DisplayLayoutType.BOTTOM: - div.style.left = - Math.floor(bounds.left * this.visualScale_) + offset.x + 'px'; - div.style.top = parentDiv.offsetTop + parentDiv.offsetHeight + 'px'; - break; - case options.DisplayLayoutType.LEFT: - div.style.left = parentDiv.offsetLeft - div.offsetWidth + 'px'; - div.style.top = - Math.floor(bounds.top * this.visualScale_) + offset.y + 'px'; - break; - } - } + displayLayout.layoutDivFromBounds( + this.displayAreaOffset_, this.visualScale_, parentLayout); - displayLayout.div = div; - displayLayout.originalPosition.x = div.offsetLeft; - displayLayout.originalPosition.y = div.offsetTop; + displayLayout.originalDivOffsets.x = div.offsetLeft; + displayLayout.originalDivOffsets.y = div.offsetTop; - this.calculateOffset_(displayLayout); - }, - - /** - * Calculates the offset for display |id| relative to its parent. - * @param {options.DisplayLayout} displayLayout - */ - calculateOffset_: function(displayLayout) { - // Offset is calculated from top or left edge. - var parent = this.displayLayoutMap_[displayLayout.parentId]; - if (!parent) { - displayLayout.offset = 0; - return; - } - var offset; - if (displayLayout.layoutType == options.DisplayLayoutType.LEFT || - displayLayout.layoutType == options.DisplayLayoutType.RIGHT) { - offset = displayLayout.div.offsetTop - parent.div.offsetTop; - } else { - offset = displayLayout.div.offsetLeft - parent.div.offsetLeft; - } - displayLayout.offset = Math.floor(offset / this.visualScale_); + displayLayout.calculateOffset(this.visualScale_, parentLayout); }, /**
diff --git a/chrome/browser/resources/options/chromeos/display_options.js b/chrome/browser/resources/options/chromeos/display_options.js index 600451d8..778b21c 100644 --- a/chrome/browser/resources/options/chromeos/display_options.js +++ b/chrome/browser/resources/options/chromeos/display_options.js
@@ -61,33 +61,6 @@ /** @const */ var VISUAL_SCALE = 1 / 10; /** - * Snaps the region [point, width] to [basePoint, baseWidth] if - * the [point, width] is close enough to the base's edge. - * @param {number} point The starting point of the region. - * @param {number} width The width of the region. - * @param {number} basePoint The starting point of the base region. - * @param {number} baseWidth The width of the base region. - * @return {number} The moved point. Returns the point itself if it doesn't - * need to snap to the edge. - * @private - */ - function snapToEdge(point, width, basePoint, baseWidth) { - // If the edge of the region is smaller than this, it will snap to the - // base's edge. - /** @const */ var SNAP_DISTANCE_PX = 16; - - var startDiff = Math.abs(point - basePoint); - var endDiff = Math.abs(point + width - (basePoint + baseWidth)); - // Prefer the closer one if both edges are close enough. - if (startDiff < SNAP_DISTANCE_PX && startDiff < endDiff) - return basePoint; - else if (endDiff < SNAP_DISTANCE_PX) - return basePoint + baseWidth - width; - - return point; - } - - /** * Encapsulated handling of the 'Display' page. * @constructor * @extends {cr.ui.pageManager.Page} @@ -660,27 +633,6 @@ }, /** - * Creates a DisplayLayout object representing the display. - * @param {!options.DisplayInfo} display - * @param {!options.DisplayLayoutType} layoutType - * @param {string} parentId - * @return {!options.DisplayLayout} - * @private - */ - createDisplayLayout_: function(display, layoutType, parentId) { - return { - bounds: display.bounds, - div: null, - id: display.id, - layoutType: layoutType, - name: display.name, - offset: 0, - originalPosition: {x: 0, y: 0}, - parentId: parentId - }; - }, - - /** * Layouts the display rectangles according to the current layout_. * @param {options.DisplayLayoutType} layoutType * @private @@ -702,7 +654,8 @@ for (var i = 0; i < this.displays_.length; i++) { var display = this.displays_[i]; var parentId = display.isPrimary ? '' : primaryDisplayId; - var layout = this.createDisplayLayout_(display, layoutType, parentId); + var layout = new options.DisplayLayout( + display.id, display.name, display.bounds, layoutType, parentId); this.displayLayoutManager_.addDisplayLayout(layout); }
diff --git a/chrome/browser/resources/options/options_bundle.js b/chrome/browser/resources/options/options_bundle.js index 8f9531b..7e14154 100644 --- a/chrome/browser/resources/options/options_bundle.js +++ b/chrome/browser/resources/options/options_bundle.js
@@ -32,6 +32,7 @@ <include src="chromeos/accounts_user_list.js"> <include src="chromeos/accounts_user_name_edit.js"> <include src="chromeos/consumer_management_overlay.js"> +<include src="chromeos/display_layout.js"> <include src="chromeos/display_layout_manager.js"> <include src="chromeos/display_options.js"> <include src="chromeos/display_overscan.js">
diff --git a/chrome/browser/resources/plugin_metadata/plugins_linux.json b/chrome/browser/resources/plugin_metadata/plugins_linux.json index 80f8b1869..1bb4dac 100644 --- a/chrome/browser/resources/plugin_metadata/plugins_linux.json +++ b/chrome/browser/resources/plugin_metadata/plugins_linux.json
@@ -1,5 +1,5 @@ { - "x-version": 10, + "x-version": 11, "google-talk": { "mime_types": [ ], @@ -80,9 +80,9 @@ ], "versions": [ { - "version": "20.0.0.267", + "version": "20.0.0.306", "status": "up_to_date", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb16-01.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb16-04.html" } ], "lang": "en-US",
diff --git a/chrome/browser/resources/plugin_metadata/plugins_mac.json b/chrome/browser/resources/plugin_metadata/plugins_mac.json index 2979a50..ad658f7 100644 --- a/chrome/browser/resources/plugin_metadata/plugins_mac.json +++ b/chrome/browser/resources/plugin_metadata/plugins_mac.json
@@ -1,5 +1,5 @@ { - "x-version": 16, + "x-version": 17, "google-talk": { "mime_types": [ ], @@ -115,9 +115,9 @@ ], "versions": [ { - "version": "20.0.0.267", + "version": "20.0.0.306", "status": "requires_authorization", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb16-01.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb16-04.html" } ], "lang": "en-US",
diff --git a/chrome/browser/resources/plugin_metadata/plugins_win.json b/chrome/browser/resources/plugin_metadata/plugins_win.json index 9ba115b..68ee094 100644 --- a/chrome/browser/resources/plugin_metadata/plugins_win.json +++ b/chrome/browser/resources/plugin_metadata/plugins_win.json
@@ -1,5 +1,5 @@ { - "x-version": 25, + "x-version": 26, "google-talk": { "mime_types": [ ], @@ -137,9 +137,9 @@ ], "versions": [ { - "version": "20.0.0.267", + "version": "20.0.0.306", "status": "requires_authorization", - "reference": "https://helpx.adobe.com/security/products/flash-player/apsb16-01.html" + "reference": "https://helpx.adobe.com/security/products/flash-player/apsb16-04.html" } ], "lang": "en-US",
diff --git a/chrome/browser/resources/print_preview/print_preview_utils.js b/chrome/browser/resources/print_preview/print_preview_utils.js index 49a367b..0cacb70 100644 --- a/chrome/browser/resources/print_preview/print_preview_utils.js +++ b/chrome/browser/resources/print_preview/print_preview_utils.js
@@ -94,7 +94,7 @@ * Example: "1-4dsf, 11" is invalid regardless of |totalPageCount|. * @param {string} pageRangeText The text to be checked. * @param {number=} opt_totalPageCount The total number of pages. - * @return {Array<{from: number, to: number}>} An array of page range objects. + * @return {?Array<{from: number, to: number}>} An array of page range objects. */ function pageRangeTextToPageRanges(pageRangeText, opt_totalPageCount) { if (pageRangeText == '') { @@ -118,7 +118,7 @@ return null; var from = match[1] ? parseInt(match[1], 10) : 1; var to = match[2] ? parseInt(match[2], 10) : totalPageCount; - if (from > to || from > totalPageCount) + if (from > to || to > totalPageCount) return null; pageRanges.push({'from': from, 'to': to}); } else {
diff --git a/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs b/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs index de50bcb..dd17e6e 100644 --- a/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs +++ b/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
@@ -77,7 +77,7 @@ assertRangesEqual([[10, 100]], pageRangeTextToPageRanges("10-", 100)); assertRangesEqual([[10, 100000]], - pageRangeTextToPageRanges("10-100000", 100)); + pageRangeTextToPageRanges("10-100000", 100000)); assertRangesEqual([[1, 100]], pageRangeTextToPageRanges("-", 100)); assertRangesEqual([1, 2], @@ -89,11 +89,12 @@ }); TEST_F('PrintPreviewUtilsUnitTest', 'InvalidPageRanges', function() { - assertTrue(pageRangeTextToPageRanges("1,100000", 100) == null); - assertTrue(pageRangeTextToPageRanges("1,2,0,56", 100) == null); - assertTrue(pageRangeTextToPageRanges("-1,1,2,,56", 100) == null); - assertTrue(pageRangeTextToPageRanges("1,2,56-40", 100) == null); - assertTrue(pageRangeTextToPageRanges("101-110", 100) == null); + assertTrue(pageRangeTextToPageRanges("10-100000", 100) === null); + assertTrue(pageRangeTextToPageRanges("1,100000", 100) === null); + assertTrue(pageRangeTextToPageRanges("1,2,0,56", 100) === null); + assertTrue(pageRangeTextToPageRanges("-1,1,2,,56", 100) === null); + assertTrue(pageRangeTextToPageRanges("1,2,56-40", 100) === null); + assertTrue(pageRangeTextToPageRanges("101-110", 100) === null); }); TEST_F('PrintPreviewUtilsUnitTest', 'PageRangeTextToPageList', function() {
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html index e597f677..678a8d24 100644 --- a/chrome/browser/resources/settings/a11y_page/a11y_page.html +++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -1,10 +1,11 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-a11y-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <template> + <style include="settings-shared"></style> <div class="settings-card"> <div class="settings-box row first"> <span i18n-content="a11yExplanation"></span>
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html index e1d7d44b..638364a 100644 --- a/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html +++ b/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
@@ -1,13 +1,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-slider/paper-slider.html"> <link rel="import" href="chrome://md-settings/controls/settings_dropdown_menu.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-appearance-fonts-page"> <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> - <link rel="import" type="css" href="chrome://md-settings/appearance_page/appearance_shared.css"> <template> + <style include="settings-shared"></style> <div>[[selectedStandardFont_]]</div> <div class="settings-box first"> <div i18n-content="fontSize"></div>
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chrome/browser/resources/settings/appearance_page/appearance_page.html index 9c54f296..204156f 100644 --- a/chrome/browser/resources/settings/appearance_page/appearance_page.html +++ b/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -10,17 +10,17 @@ <link rel="import" href="chrome://md-settings/controls/settings_radio_group.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="appearance_fonts_page.html"> <dom-module id="settings-appearance-page"> <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> - <link rel="import" type="css" href="chrome://md-settings/appearance_page/appearance_shared.css"> <link rel="import" type="css" href="chrome://resources/css/widgets.css"> <link rel="import" href="chrome://resources/html/action_link.html"> <template> + <style include="settings-shared"></style> <settings-animated-pages id="pages" current-route="{{currentRoute}}" section="appearance"> <neon-animatable id="main">
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html index 319532c4..54971fa 100644 --- a/chrome/browser/resources/settings/basic_page/basic_page.html +++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -9,6 +9,7 @@ <link rel="import" href="chrome://md-settings/reset_page/reset_profile_banner.html"> <if expr="chromeos"> +<link rel="import" href="chrome://md-settings/device_page/device_page.html"> <link rel="import" href="chrome://md-settings/internet_page/internet_page.html"> </if> @@ -47,6 +48,16 @@ </settings-appearance-page> </settings-section> </template> +<if expr="chromeos"> + <template is="dom-if" if="[[showPage(pageVisibility.device)]]" restamp> + <settings-section page-title="[[i18n('devicePageTitle')]]" + current-route="[[currentRoute]]" section="device"> + <settings-device-page prefs="{{prefs}}" + current-route="{{currentRoute}}"> + </settings-device-page> + </settings-section> + </template> +</if> <template is="dom-if" if="[[showPage(pageVisibility.onStartup)]]" restamp> <settings-section page-title="[[i18n('onStartup')]]" current-route="[[currentRoute]]" section="onStartup">
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_add_device_dialog.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_add_device_dialog.html index 962795221a..9552283 100644 --- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_add_device_dialog.html +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_add_device_dialog.html
@@ -3,12 +3,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-selector/iron-selector.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-bluetooth-add-device-dialog"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="bluetooth_page.css"> <link rel="import" type="css" href="bluetooth_dialog.css"> <template> + <style include="settings-shared"></style> <div id="dialogOuterDiv" class="layout vertical flex"> <div id="dialogHeaderDiv" class="settings-box layout horizontal"> <span id="dialogTitle" class="flex"
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html index df84932..f308a00 100644 --- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
@@ -9,14 +9,15 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="bluetooth_device_list_item.html"> <link rel="import" href="bluetooth_add_device_dialog.html"> <link rel="import" href="bluetooth_pair_device_dialog.html"> <dom-module id="settings-bluetooth-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="bluetooth_page.css"> <template> + <style include="settings-shared"></style> <settings-animated-pages id="pages" current-route="{{currentRoute}}" section="bluetooth"> <neon-animatable id="main">
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_pair_device_dialog.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_pair_device_dialog.html index c167a4b..5d8264b 100644 --- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_pair_device_dialog.html +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_pair_device_dialog.html
@@ -3,12 +3,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-bluetooth-pair-device-dialog"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="bluetooth_page.css"> <link rel="import" type="css" href="bluetooth_dialog.css"> <template> + <style include="settings-shared"></style> <div id="dialogOuterDiv" class="layout vertical flex"> <div id="dialogHeaderDiv" class="settings-box layout horizontal center"> <span id="dialogTitle" class="flex"
diff --git a/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.html b/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.html index b83580f..bdaa740 100644 --- a/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.html +++ b/chrome/browser/resources/settings/clear_browsing_data_page/clear_browsing_data_page.html
@@ -5,12 +5,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-dropdown-menu/paper-dropdown-menu.html"> <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> <link rel="import" href="chrome://md-settings/controls/settings_dropdown_menu.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-clear-browsing-data-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="clear_browsing_data_page.css"> <template> + <style include="settings-shared"></style> <div class="settings-box block"> <span i18n-content="clearFollowingItemsFrom"></span> <settings-dropdown-menu id="clearFrom"
diff --git a/chrome/browser/resources/settings/compiled_resources2.gyp b/chrome/browser/resources/settings/compiled_resources2.gyp index d2009aa2..97f826a 100644 --- a/chrome/browser/resources/settings/compiled_resources2.gyp +++ b/chrome/browser/resources/settings/compiled_resources2.gyp
@@ -7,7 +7,9 @@ 'target_name': 'settings_resources', 'type': 'none', 'dependencies': [ + 'device_page/compiled_resources2.gyp:*', 'reset_page/compiled_resources2.gyp:*', + 'settings_page/compiled_resources2.gyp:*', ], }, {
diff --git a/chrome/browser/resources/settings/controls/settings_dropdown_menu.html b/chrome/browser/resources/settings/controls/settings_dropdown_menu.html index 4e85ec1..4ab4e6db 100644 --- a/chrome/browser/resources/settings/controls/settings_dropdown_menu.html +++ b/chrome/browser/resources/settings/controls/settings_dropdown_menu.html
@@ -4,11 +4,11 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-menu/paper-menu.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://md-settings/prefs/pref_util.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-dropdown-menu"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <template> + <style include="settings-shared"></style> <paper-dropdown-menu id="dropdownMenu" label="[[menuLabel_]]" on-iron-select="onSelect_" no-label-float$="[[noLabelFloat]]" disabled="[[shouldDisableMenu_(disabled, menuOptions.*)]]">
diff --git a/chrome/browser/resources/settings/date_time_page/date_time_page.html b/chrome/browser/resources/settings/date_time_page/date_time_page.html index 359e649..c0ddb9c 100644 --- a/chrome/browser/resources/settings/date_time_page/date_time_page.html +++ b/chrome/browser/resources/settings/date_time_page/date_time_page.html
@@ -1,10 +1,11 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-date-time-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <template> + <style include="settings-shared"></style> <div class="settings-card"> <div class="settings-box first block"> <div i18n-content="timeZone"></div>
diff --git a/chrome/browser/resources/settings/default_browser_page/default_browser_page.html b/chrome/browser/resources/settings/default_browser_page/default_browser_page.html index cb6a350..5cf158c 100644 --- a/chrome/browser/resources/settings/default_browser_page/default_browser_page.html +++ b/chrome/browser/resources/settings/default_browser_page/default_browser_page.html
@@ -2,12 +2,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-default-browser-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="default_browser_page.css"> <template> + <style include="settings-shared"></style> <div class="settings-card"> <div class="settings-box first">[[message_]]</div> <div class="settings-box">
diff --git a/chrome/browser/resources/settings/device_page/compiled_resources2.gyp b/chrome/browser/resources/settings/device_page/compiled_resources2.gyp new file mode 100644 index 0000000..9e55d9d7 --- /dev/null +++ b/chrome/browser/resources/settings/device_page/compiled_resources2.gyp
@@ -0,0 +1,19 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'device_page', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '../settings_page/compiled_resources2.gyp:settings_animated_pages', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'touchpad', + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +}
diff --git a/chrome/browser/resources/settings/device_page/device_page.html b/chrome/browser/resources/settings/device_page/device_page.html new file mode 100644 index 0000000..e487036 --- /dev/null +++ b/chrome/browser/resources/settings/device_page/device_page.html
@@ -0,0 +1,35 @@ +<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/hardware-icons.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/image-icons.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html"> +<link rel="import" href="chrome://md-settings/device_page/touchpad.html"> +<link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> +<link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> + +<dom-module id="settings-device-page"> + <template> + <style include="settings-shared"></style> + <settings-animated-pages id="pages" section="device" + current-route="{{currentRoute}}"> + <neon-animatable id="main"> + <div class="settings-box first" on-tap="onTouchpadTap_"> + <iron-icon icon="hardware:keyboard"></iron-icon> + <div class="middle">[[i18n('touchpadTitle')]]</div> + </div> + <div class="settings-box"> + <iron-icon icon="image:brightness-1"></iron-icon> + <div class="middle">[[i18n('keyboardTitle')]]</div> + </div> + </neon-animatable> + <neon-animatable id="touchpad"> + <settings-subheader page-title="[[i18n('touchpadTitle')]]"> + </settings-subheader> + <settings-touchpad prefs="{{prefs}}"></settings-touchpad> + </neon-animatable> + </settings-animated-pages> + </template> + <script src="device_page.js"></script> +</dom-module>
diff --git a/chrome/browser/resources/settings/device_page/device_page.js b/chrome/browser/resources/settings/device_page/device_page.js new file mode 100644 index 0000000..3028f42 --- /dev/null +++ b/chrome/browser/resources/settings/device_page/device_page.js
@@ -0,0 +1,40 @@ +// 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. + +/** + * @fileoverview 'settings-device-page' is the settings page for device and + * peripheral settings. + * + * @group Chrome Settings Elements + * @element settings-device-page + */ +Polymer({ + is: 'settings-device-page', + + behaviors: [ + I18nBehavior, + ], + + properties: { + /** The current active route. */ + currentRoute: { + type: Object, + notify: true, + }, + + /** Preferences state. */ + prefs: { + type: Object, + notify: true, + }, + }, + + /** + * Handler for tapping the Touchpad settings menu item. + * @private + */ + onTouchpadTap_: function() { + this.$.pages.setSubpageChain(['touchpad']); + }, +});
diff --git a/chrome/browser/resources/settings/device_page/touchpad.html b/chrome/browser/resources/settings/device_page/touchpad.html new file mode 100644 index 0000000..d6a8d0b --- /dev/null +++ b/chrome/browser/resources/settings/device_page/touchpad.html
@@ -0,0 +1,33 @@ +<link rel="import" href="chrome://resources/polymer/v1_0/iron-selector/iron-selector.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> +<link rel="import" href="chrome://md-settings/controls/settings_radio_group.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> + +<dom-module id="settings-touchpad"> + <template> + <style include="settings-shared"></style> + <!-- TODO(michaelpg): Add touchpad speed slider. --> + <div class="settings-box block first"> + <settings-checkbox pref="{{prefs.settings.touchpad.enable_tap_to_click}}" + i18n-values="label:touchpadTapToClickEnabledLabel"> + </settings-checkbox> + <settings-checkbox pref="{{prefs.settings.touchpad.enable_tap_dragging}}" + i18n-values="label:tapDraggingLabel"> + </settings-checkbox> + </div> + <div class="settings-box" i18n-content="scrollLabel"></div> + <div class="list-frame"> + <settings-radio-group pref="{{prefs.settings.touchpad.natural_scroll}}"> + <paper-radio-button name="false" i18n-content="traditionalScrollLabel"> + </paper-radio-button> + <!-- TODO(michaelpg): Make link inside label clickable. + https://github.com/PolymerElements/paper-radio-button/issues/86 + --> + <paper-radio-button name="true" + i18n-values=".innerHTML:naturalScrollLabel"></paper-radio-button> + </settings-radio-group> + </div> + </template> + <script src="touchpad.js"></script> +</dom-module>
diff --git a/chrome/browser/resources/settings/device_page/touchpad.js b/chrome/browser/resources/settings/device_page/touchpad.js new file mode 100644 index 0000000..0b0ae6f --- /dev/null +++ b/chrome/browser/resources/settings/device_page/touchpad.js
@@ -0,0 +1,22 @@ +// 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. + +/** + * @fileoverview + * 'settings-touchpad' is the settings subpage with touchpad settings. + * + * @group Chrome Settings Elements + * @element settings-change-picture + */ +Polymer({ + is: 'settings-touchpad', + + properties: { + /** Preferences state. */ + prefs: { + type: Object, + notify: true, + }, + }, +});
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.html b/chrome/browser/resources/settings/downloads_page/downloads_page.html index 7887133..f8c50666 100644 --- a/chrome/browser/resources/settings/downloads_page/downloads_page.html +++ b/chrome/browser/resources/settings/downloads_page/downloads_page.html
@@ -3,12 +3,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> <link rel="import" href="chrome://md-settings/controls/settings_input.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-downloads-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="downloads_page.css"> <template> + <style include="settings-shared"></style> <div class="settings-card"> <div class="settings-box first"> <div class="start">
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.html b/chrome/browser/resources/settings/internet_page/internet_detail_page.html index 0cf3f8fc..81ba7d59 100644 --- a/chrome/browser/resources/settings/internet_page/internet_detail_page.html +++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
@@ -10,6 +10,7 @@ <link rel="import" href="chrome://resources/cr_elements/network/cr_onc_types.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html"> <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_indicator.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="network_apnlist.html"> <link rel="import" href="network_ip_config.html"> <link rel="import" href="network_nameservers.html"> @@ -18,9 +19,9 @@ <link rel="import" href="network_siminfo.html"> <dom-module id="settings-internet-detail-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="internet_detail_page.css"> <template> + <style include="settings-shared"></style> <div class="layout vertical"> <!-- Title section: Icon + name + connection state. --> <div id="titleDiv" class="layout horizontal center">
diff --git a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html index 6dd5f1e..494e8c9c 100644 --- a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html +++ b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
@@ -3,11 +3,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/cr_elements/network/cr_network_list.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-internet-known-networks-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="internet_known_networks_page.css"> <template> + <style include="settings-shared"></style> <div class="layout vertical"> <div id="outerDiv" class="layout vertical"> <div id="headerDiv" class="layout horizontal">
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.html b/chrome/browser/resources/settings/internet_page/internet_page.html index ccaa873..a544bbd 100644 --- a/chrome/browser/resources/settings/internet_page/internet_page.html +++ b/chrome/browser/resources/settings/internet_page/internet_page.html
@@ -3,14 +3,14 @@ <link rel="import" href="chrome://resources/cr_elements/network/cr_onc_types.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="internet_detail_page.html"> <link rel="import" href="internet_known_networks_page.html"> <link rel="import" href="network_summary.html"> <dom-module id="settings-internet-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <template> + <style include="settings-shared"></style> <settings-animated-pages id="pages" current-route="{{currentRoute}}" section="internet"> <neon-animatable id="main">
diff --git a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html index cf2f19a0..6bacd42 100644 --- a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html +++ b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
@@ -5,11 +5,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-edit-dictionary-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="edit_dictionary_page.css"> <template> + <style include="settings-shared"></style> <div class="settings-box block"> <div id="addWordRow"> <iron-a11y-keys id="keys" keys="enter esc"
diff --git a/chrome/browser/resources/settings/languages_page/language_detail_page.html b/chrome/browser/resources/settings/languages_page/language_detail_page.html index 28bc6d6..4ab9d16 100644 --- a/chrome/browser/resources/settings/languages_page/language_detail_page.html +++ b/chrome/browser/resources/settings/languages_page/language_detail_page.html
@@ -4,6 +4,7 @@ <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="languages.html"> <if expr="chromeos"> @@ -11,9 +12,9 @@ </if> <dom-module id="settings-language-detail-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="language_detail_page.css"> <template> + <style include="settings-shared"></style> <settings-languages languages="{{languages}}"></settings-languages> <if expr="chromeos or is_win"> <div id="languageSettings">
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.html b/chrome/browser/resources/settings/languages_page/languages_page.html index bc59c35..0f5f7ae2 100644 --- a/chrome/browser/resources/settings/languages_page/languages_page.html +++ b/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -10,6 +10,7 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="language_detail_page.html"> <link rel="import" href="languages.html"> <link rel="import" href="manage_languages_page.html"> @@ -19,9 +20,8 @@ </if> <dom-module id="settings-languages-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <template> + <style include="settings-shared"></style> <settings-languages languages="{{languages}}"></settings-languages> <settings-animated-pages id="pages" current-route="{{currentRoute}}" section="languages">
diff --git a/chrome/browser/resources/settings/languages_page/manage_languages_page.html b/chrome/browser/resources/settings/languages_page/manage_languages_page.html index c1142a5..44d2474f 100644 --- a/chrome/browser/resources/settings/languages_page/manage_languages_page.html +++ b/chrome/browser/resources/settings/languages_page/manage_languages_page.html
@@ -6,13 +6,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="languages.html"> <dom-module id="settings-manage-languages-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="manage_languages_page.css"> <template> + <style include="settings-shared"></style> <settings-languages languages="{{languages}}"></settings-languages> <div class="settings-box block content"> <h2 i18n-content="enabledLanguages"></h2>
diff --git a/chrome/browser/resources/settings/on_startup_page/on_startup_page.html b/chrome/browser/resources/settings/on_startup_page/on_startup_page.html index 2b55751..4681097 100644 --- a/chrome/browser/resources/settings/on_startup_page/on_startup_page.html +++ b/chrome/browser/resources/settings/on_startup_page/on_startup_page.html
@@ -6,12 +6,12 @@ <link rel="import" href="chrome://md-settings/on_startup_page/startup_urls_page.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-on-startup-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="on_startup_shared.css"> <template> + <style include="settings-shared"></style> <div class="settings-card"> <div class="settings-box block first"> <settings-radio-group id="onStartupRadioGroup"
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html index 5d967c4..47d2419a 100644 --- a/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html +++ b/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
@@ -2,12 +2,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-dialog/paper-dialog.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-icon-item.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-startup-urls-page"> <link rel="import" type="css" href="chrome://md-settings/settings_dialog.css"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="on_startup_shared.css"> <template> + <style include="settings-shared"></style> <div class="list-frame vertical-list"> <template is="dom-repeat" items="[[startupPages_]]"> <div class="list-item"> @@ -46,7 +47,7 @@ <paper-button class="cancel-button" on-tap="onCancelTap_" id="cancel" i18n-content="cancel"></paper-button> <paper-button class="action-button" id="addUrlButton" - on-tap="onOkTap_" i18n-content="addLabel"> + on-tap="onOkTap_" i18n-content="add"> </paper-button> </div> </div>
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources.gyp b/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources.gyp index 997c1b2..b2a4d4c 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources.gyp +++ b/chrome/browser/resources/settings/passwords_and_forms_page/compiled_resources.gyp
@@ -9,6 +9,8 @@ 'depends': [ 'compiled_resources.gyp:passwords_section', '../../../../../third_party/closure_compiler/externs/compiled_resources.gyp:passwords_private', + '../../../../../ui/webui/resources/js/compiled_resources.gyp:assert', + '../../../../../ui/webui/resources/js/compiled_resources.gyp:cr', ], }, 'includes': ['../../../../../third_party/closure_compiler/compile_js.gypi'],
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html index e694b87..852f4b1 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html +++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html
@@ -1,14 +1,17 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item-body.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> <link rel="import" href="chrome://md-settings/passwords_and_forms_page/passwords_section.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-passwords-and-forms-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <template> + <style include="settings-shared"></style> <div class="settings-card"> <div class="settings-box first two-line"> <div class="start"> @@ -34,8 +37,9 @@ </cr-expand-button> </div> <iron-collapse id="collapse" opened="{{passwordsOpened}}"> - <passwords-section saved-passwords="{{savedPasswords}}"> - </passwords-section> + <passwords-section saved-passwords="{{savedPasswords}}" + password-exceptions="{{passwordExceptions}}"> + </passwords-section> </iron-collapse> </div> </template>
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.js b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.js index 07b6ac1e..20bfecc3 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.js +++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.js
@@ -9,6 +9,67 @@ * @group Chrome Settings Elements * @element settings-passwords-and-forms-page */ + +/** + * Interface for all callbacks to the password API. + * @interface + */ +function PasswordManager() {} + +/** @typedef {chrome.passwordsPrivate.PasswordUiEntry} */ +PasswordManager.PasswordUiEntry; + +PasswordManager.prototype = { + /** + * Register a callback for when the list of passwords is updated. + * Calling this function should trigger an update. + * @param {function(!Array<!PasswordManager.PasswordUiEntry>):void} callback + */ + onSavedPasswordListChangedCallback: assertNotReached, + + /** + * Register a callback for when the list of exceptions is updated. + * Calling this function should trigger an update. + * @param {function(!Array<!string>):void} callback + */ + onExceptionListChangedCallback: assertNotReached, + + /** + * Should remove the password exception and notify that the list has changed. + * @param {!string} exception The exception that should be removed from the + * list. No-op if |exception| is not in the list. + */ + removePasswordException: assertNotReached, +}; + +/** + * Implementation that accesses the private API. + * @implements {PasswordManager} + * @constructor + */ +function PasswordManagerImpl() {} +cr.addSingletonGetter(PasswordManagerImpl); + +PasswordManagerImpl.prototype = { + __proto__: PasswordManager, + + /** @override */ + onSavedPasswordListChangedCallback: function(callback) { + chrome.passwordsPrivate.onSavedPasswordsListChanged.addListener(callback); + }, + + /** @override */ + onExceptionListChangedCallback: function(callback) { + chrome.passwordsPrivate.onPasswordExceptionsListChanged.addListener( + callback); + }, + + /** @override */ + removePasswordException: function(exception) { + chrome.passwordsPrivate.removePasswordException(exception); + }, +}; + (function() { 'use strict'; @@ -16,9 +77,7 @@ is: 'settings-passwords-and-forms-page', properties: { - /** - * Preferences state. - */ + /** Preferences state. */ prefs: { type: Object, notify: true, @@ -26,8 +85,7 @@ /** * An array of passwords to display. - * Lazy loaded when the password section is expanded. - * @type {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} + * @type {!Array<!PasswordManager.PasswordUiEntry>} */ savedPasswords: { type: Array, @@ -35,7 +93,17 @@ }, /** + * An array of sites to display. + * @type {!Array<!string>} + */ + passwordExceptions: { + type: Array, + value: function() { return []; }, + }, + + /** * Whether the password section section is opened or not. + * @type {boolean} */ passwordsOpened: { type: Boolean, @@ -43,13 +111,29 @@ }, }, + listeners: { + 'remove-password-exception': 'removePasswordException_' + }, + /** @override */ ready: function() { - // Triggers a callback after the listener is added. - chrome.passwordsPrivate.onSavedPasswordsListChanged.addListener( - function(passwordList) { - this.savedPasswords = passwordList; - }.bind(this)); + this.passwordManager_ = PasswordManagerImpl.getInstance(); + + this.passwordManager_.onSavedPasswordListChangedCallback(function(list) { + this.savedPasswords = list; + }.bind(this)); + this.passwordManager_.onExceptionListChangedCallback(function(list) { + this.passwordExceptions = list; + }.bind(this)); + }, + + /** + * Listens for the remove-password-exception event, and calls the private API. + * @param {!Event} event + * @private + */ + removePasswordException_: function(event) { + this.passwordManager_.removePasswordException(event.detail); }, }); })();
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.css b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.css index b076bab..05405be 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.css +++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.css
@@ -9,6 +9,10 @@ padding-top: 16px; } +paper-icon-button { + color: var(--paper-grey-500); +} + paper-input { --paper-input-container-disabled: { opacity: 1; @@ -18,3 +22,7 @@ }; width: 150px; } + +.list-frame { + margin-top: 3%; +}
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html index 0bc12207..5094d9c 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html +++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html
@@ -1,14 +1,14 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item-body.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="passwords-section"> <link rel="import" type="css" href="chrome://md-settings/site_settings_page/site_settings_page.css"> <link rel="import" type="css" href="chrome://md-settings/passwords_and_forms_page/passwords_section.css"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <template> + <style include="settings-shared"></style> <div class="list-frame"> <div class="secondary" i18n-content="savedPasswordsHeading"></div> <iron-list id="passwordList" class="vertical-list list-section" @@ -28,6 +28,20 @@ </template> </iron-list> </div> + <div class="list-frame"> + <div class="secondary" i18n-content="passwordExceptionsHeading"></div> + <iron-list id="passwordExceptionsList" class="vertical-list list-section" + items="{{passwordExceptions}}"> + <template> + <div class="list-item two-line"> + <div id="exception" class="start">[[item]]</div> + <paper-icon-button id="removeButton" icon="close" + on-tap="onRemovePasswordException_" tabindex$="[[tabIndex]]" + i18n-values="alt:deletePasswordException"></paper-icon-button> + </div> + </template> + </iron-list> + </div> </template> <script src="chrome://md-settings/passwords_and_forms_page/passwords_section.js"></script> </dom-module>
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js index 4eb338d8..7f7dfd4 100644 --- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js +++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
@@ -25,6 +25,24 @@ type: Array, value: function() { return []; }, }, + + /** + * An array of sites to display. + * @type {!Array<!string>} + */ + passwordExceptions: { + type: Array, + value: function() { return []; }, + }, + }, + + /** + * Fires an event that should delete the passwordException. + * @param {!{model: !{item: !string}}} e The polymer event. + * @private + */ + onRemovePasswordException_: function(e) { + this.fire('remove-password-exception', e.model.item); }, /** @@ -33,6 +51,6 @@ * @return {string} password * @private */ - getEmptyPassword_(length) { return ' '.repeat(length); }, + getEmptyPassword_: function(length) { return ' '.repeat(length); }, }); })();
diff --git a/chrome/browser/resources/settings/people_page/camera.html b/chrome/browser/resources/settings/people_page/camera.html index 3d46d00..b4aa8d0 100644 --- a/chrome/browser/resources/settings/people_page/camera.html +++ b/chrome/browser/resources/settings/people_page/camera.html
@@ -1,13 +1,15 @@ <link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/image-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html"> <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-camera"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="camera.css"> <template> + <style include="settings-shared"></style> <div hidden="[[!cameraActive]]"> <div id="perspectiveBox"> <div id="userImageStreamCrop"> @@ -16,11 +18,11 @@ </div> </div> <div id="cameraControls"> - <paper-icon-button id="flipPhoto" tabindex="1" icon="image:flip" + <paper-icon-button id="flipPhoto" tabindex="2" icon="image:flip" i18n-values="title:flipPhoto" on-tap="onTapFlipPhoto_" disabled="[[!cameraOnline_]]"> </paper-icon-button> - <paper-icon-button id="takePhoto" tabindex="2" icon="image:camera-alt" + <paper-icon-button id="takePhoto" tabindex="1" icon="image:camera-alt" i18n-values="title:takePhoto" on-tap="onTapTakePhoto_" disabled="[[!cameraOnline_]]"> </paper-icon-button>
diff --git a/chrome/browser/resources/settings/people_page/camera.js b/chrome/browser/resources/settings/people_page/camera.js index 97c8cf4..b403884 100644 --- a/chrome/browser/resources/settings/people_page/camera.js +++ b/chrome/browser/resources/settings/people_page/camera.js
@@ -134,7 +134,9 @@ this.isFlipped_ = !this.isFlipped_; this.$.userImageStreamCrop.classList.toggle('flip-x', this.isFlipped_); - // TODO(tommycli): Add announce of accessible message for photo flipping. + var flipMessageId = this.isFlipped_ ? + 'photoFlippedAccessibleText' : 'photoFlippedBackAccessibleText'; + announceAccessibleMessage(loadTimeData.getString(flipMessageId)); }, /** @@ -157,6 +159,9 @@ var photoDataUrl = this.isFlipped_ ? this.flipFrame_(canvas) : canvas.toDataURL('image/png'); this.fire('phototaken', {photoDataUrl: photoDataUrl}); + + announceAccessibleMessage( + loadTimeData.getString('photoCaptureAccessibleText')); }, /**
diff --git a/chrome/browser/resources/settings/people_page/change_picture.html b/chrome/browser/resources/settings/people_page/change_picture.html index 2554f158..be7bd44 100644 --- a/chrome/browser/resources/settings/people_page/change_picture.html +++ b/chrome/browser/resources/settings/people_page/change_picture.html
@@ -1,4 +1,5 @@ <link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/image-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-selector/iron-selector.html"> @@ -6,25 +7,24 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://md-settings/people_page/camera.html"> <link rel="import" href="chrome://md-settings/people_page/change_picture_private_api.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-change-picture"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="change_picture.css"> <template> + <style include="settings-shared"></style> <div id="container" class="settings-box"> <div id="availableIcons" class="start"> <iron-selector id="selector" on-iron-activate="onImageActivate_" selected-item="{{selectedItem_}}"> <iron-icon id="cameraImage" data-type="camera" icon="image:camera-alt" - alt="[[i18n('takePhoto')]]" hidden="[[!cameraPresent_]]"> + i18n-values="alt:takePhoto" hidden="[[!cameraPresent_]]"> </iron-icon> <iron-icon data-type="file" icon="folder" - alt="[[i18n('chooseFile')]]"> + i18n-values="alt:chooseFile"> </iron-icon> <img id="profileImage" data-type="profile" - src="[[profileImageUrl_]]" - alt="[[i18n('profilePhotoLoading')]]"> + src="[[profileImageUrl_]]" i18n-values="alt:profilePhotoLoading"> <img id="oldImage" data-type="old" src="[[oldImageUrl_]]" hidden="[[!oldImageUrl_]]"> <template is="dom-repeat" items="[[defaultImages_]]"> @@ -45,9 +45,9 @@ </div> <div id="previewPane"> <img i18n-values="alt:previewAltText" src="[[selectedItem_.src]]" - hidden="[[isCameraActive_(cameraPresent_, selectedItem_)]]"> + hidden="[[isPreviewImageHidden_(selectedItem_)]]"> <div id="discardControlBar" hidden="[[isDiscardHidden_(selectedItem_)]]"> - <paper-icon-button id="discardOldImage" tabindex="2" icon="delete" + <paper-icon-button id="discardOldImage" tabindex="0" icon="delete" i18n-values="title:discardPhoto" on-tap="onTapDiscardOldImage_"> </paper-icon-button> </div>
diff --git a/chrome/browser/resources/settings/people_page/change_picture.js b/chrome/browser/resources/settings/people_page/change_picture.js index 384a02c..0f6c7b7 100644 --- a/chrome/browser/resources/settings/people_page/change_picture.js +++ b/chrome/browser/resources/settings/people_page/change_picture.js
@@ -160,6 +160,7 @@ */ receiveProfileImage: function(imageUrl, selected) { this.profileImageUrl_ = imageUrl; + this.$.profileImage.alt = this.i18n('profilePhoto'); if (!selected) return; @@ -235,8 +236,6 @@ */ onPhotoTaken_: function(event) { settings.ChangePicturePrivateApi.photoTaken(event.detail.photoDataUrl); - - // TODO(tommycli): Add announce of accessible message for photo capture. }, /** @@ -260,6 +259,9 @@ assert(this.defaultImages_.length > 0); settings.ChangePicturePrivateApi.selectDefaultImage( this.defaultImages_[0].url); + + announceAccessibleMessage( + loadTimeData.getString('photoDiscardAccessibleText')); }, /** @@ -272,6 +274,19 @@ /** * @param {settings.ChangePictureImageElement} selectedItem + * @return {boolean} True if the preview image should be hidden. + * @private + */ + isPreviewImageHidden_: function(selectedItem) { + if (selectedItem == undefined) + return true; + + var type = selectedItem.dataset.type; + return type != 'default' && type != 'profile' && type != 'old'; + }, + + /** + * @param {settings.ChangePictureImageElement} selectedItem * @return {boolean} True if the camera is selected in the image grid. * @private */
diff --git a/chrome/browser/resources/settings/people_page/compiled_resources.gyp b/chrome/browser/resources/settings/people_page/compiled_resources.gyp index 68075e0c..9f076c0 100644 --- a/chrome/browser/resources/settings/people_page/compiled_resources.gyp +++ b/chrome/browser/resources/settings/people_page/compiled_resources.gyp
@@ -8,6 +8,7 @@ 'variables': { 'depends': [ '../../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data', + '../../../../../ui/webui/resources/js/compiled_resources.gyp:util', '../../../../../ui/webui/resources/js/i18n_behavior.js', ], }, @@ -20,6 +21,7 @@ '../../../../../ui/webui/resources/js/compiled_resources.gyp:assert', '../../../../../ui/webui/resources/js/compiled_resources.gyp:cr', '../../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data', + '../../../../../ui/webui/resources/js/compiled_resources.gyp:util', '../../../../../ui/webui/resources/js/i18n_behavior.js', 'change_picture_private_api.js', ],
diff --git a/chrome/browser/resources/settings/people_page/manage_profile.html b/chrome/browser/resources/settings/people_page/manage_profile.html index 8228368..026632a 100644 --- a/chrome/browser/resources/settings/people_page/manage_profile.html +++ b/chrome/browser/resources/settings/people_page/manage_profile.html
@@ -2,12 +2,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> <link rel="import" href="chrome://md-settings/people_page/sync_private_api.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-manage-profile"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="manage_profile.css"> <template> + <style include="settings-shared"></style> <div class="settings-box"> <paper-input value="{{profileName}}" pattern=".*\S.*" auto-validate on-blur="onProfileNameChanged_">
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html index f81982bd..547b8a6 100644 --- a/chrome/browser/resources/settings/people_page/people_page.html +++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -4,10 +4,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-dialog/paper-dialog.html"> +<link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> <link rel="import" href="chrome://md-settings/people_page/sync_page.html"> <link rel="import" href="chrome://md-settings/people_page/sync_private_api.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <if expr="chromeos"> <link rel="import" href="chrome://md-settings/people_page/change_picture.html"> @@ -18,9 +20,9 @@ </if> <dom-module id="settings-people-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="people_page.css"> <template> + <style include="settings-shared"></style> <settings-animated-pages id="pages" current-route="{{currentRoute}}" section="people"> <neon-animatable id="main"> @@ -78,6 +80,14 @@ </div> </template> +<if expr="chromeos"> + <div class="settings-box"> + <settings-checkbox pref="{{prefs.settings.enable_screen_lock}}" + i18n-values="label:enableScreenlock"> + </settings-checkbox> + </div> +</if> + <div class="settings-box"> <paper-button class="link-button" i18n-content="manageOtherPeople" on-tap="onManageOtherPeople_">
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html index aef16784..113b5f5 100644 --- a/chrome/browser/resources/settings/people_page/sync_page.html +++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -8,13 +8,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-group/paper-radio-group.html"> <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> <link rel="import" href="chrome://md-settings/people_page/sync_private_api.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <dom-module id="settings-sync-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="sync_page.css"> <template> + <style include="settings-shared"></style> <iron-pages id="pages" selected="loading" attr-for-selected="id"> <div id="loading" i18n-content="syncLoading"></div> <div id="timeout" i18n-content="syncTimeout"></div>
diff --git a/chrome/browser/resources/settings/people_page/user_list.html b/chrome/browser/resources/settings/people_page/user_list.html index 579424d..48163ece 100644 --- a/chrome/browser/resources/settings/people_page/user_list.html +++ b/chrome/browser/resources/settings/people_page/user_list.html
@@ -1,12 +1,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-user-list"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="user_list.css"> <template> + <style include="settings-shared"></style> <div class="user-list soft-border"> <template is="dom-repeat" items="[[users]]"> <div class="user layout horizontal justified">
diff --git a/chrome/browser/resources/settings/people_page/users_page.html b/chrome/browser/resources/settings/people_page/users_page.html index 1a421521..95b8dfa 100644 --- a/chrome/browser/resources/settings/people_page/users_page.html +++ b/chrome/browser/resources/settings/people_page/users_page.html
@@ -5,12 +5,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_section.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="user_list.html"> <dom-module id="settings-users-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="users_page.css"> <template> + <style include="settings-shared"></style> <div class="settings-box" i18n-content="usersModifiedByOwnerLabel" hidden$="{{computeHideOwnerLabel_(isOwner, isWhitelistManaged)}}"> </div> @@ -52,7 +53,7 @@ disabled="[[editingUsersDisabled]]"> </paper-input> <div class="add-user-button layout horizontal end-justified"> - <paper-button i18n-content="addLabel" on-tap="addUser_" + <paper-button i18n-content="add" on-tap="addUser_" disabled="[[editingUsersDisabled]]" raised> </paper-button> </div>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html index e264f7f2..41dcf32 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_page.html +++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -9,15 +9,15 @@ <link rel="import" href="chrome://md-settings/controls/settings_checkbox.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="chrome://md-settings/site_settings/all_sites.html"> <link rel="import" href="chrome://md-settings/site_settings/constants.html"> <link rel="import" href="chrome://md-settings/site_settings_page/site_settings_page.html"> <dom-module id="settings-privacy-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="privacy_page.css"> <template> + <style include="settings-shared"></style> <settings-animated-pages id="pages" current-route="{{currentRoute}}" section="privacy"> <neon-animatable id="main">
diff --git a/chrome/browser/resources/settings/reset_page/reset_page.html b/chrome/browser/resources/settings/reset_page/reset_page.html index 85d82144..ba136156 100644 --- a/chrome/browser/resources/settings/reset_page/reset_page.html +++ b/chrome/browser/resources/settings/reset_page/reset_page.html
@@ -1,13 +1,14 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://md-settings/reset_page/reset_profile_dialog.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <if expr="chromeos"> <link rel="import" href="chrome://md-settings/reset_page/powerwash_dialog.html"> </if> <dom-module id="settings-reset-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <template> + <style include="settings-shared"></style> <div class="settings-card"> <div class="settings-box first two-line" id="resetProfile" on-tap="onShowResetProfileDialog_">
diff --git a/chrome/browser/resources/settings/search_engines_page/add_search_engine_dialog.html b/chrome/browser/resources/settings/search_engines_page/add_search_engine_dialog.html deleted file mode 100644 index 553f29c5c..0000000 --- a/chrome/browser/resources/settings/search_engines_page/add_search_engine_dialog.html +++ /dev/null
@@ -1,44 +0,0 @@ -<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-dialog/paper-dialog.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> - -<dom-module id="settings-add-search-engine-dialog"> - <link rel="import" type="css" href="chrome://md-settings/settings_dialog.css"> - <template> - <paper-dialog modal id="dialog"> - <div id="dialog-content"> - <div class="top-row"> - <span class="title">Add search engine</span> - <paper-icon-button icon="clear" on-tap="onCancelTap_"> - </paper-icon-button> - </div> - <div class="body"> - <div class="explanation"> - <paper-input always-float-label id="searchEngine" - i18n-values="label:searchEnginesSearchEngineLabel" - value="{{searchEngine}}"> - </paper-input> - <paper-input always-float-label id="keyword" - i18n-values="label:searchEnginesKeywordLabel" - value="{{keyword}}"> - </paper-input> - <paper-input always-float-label id="queryUrl" - i18n-values="label:searchEnginesQueryURLLabel" - value="{{queryUrl}}"> - </paper-input> - </div> - <div class="button-container"> - <paper-button class="cancel-button" id="cancel" - on-tap="onCancelTap_" i18n-content="cancel"></paper-button> - <paper-button class="action-button" id="add" - on-tap="onAddTap_" i18n-content="searchEnginesAdd"> - </paper-button> - </div> - </div> - </div> - </paper-dialog> - </template> - <script src="add_search_engine_dialog.js"></script> -</dom-module>
diff --git a/chrome/browser/resources/settings/search_engines_page/add_search_engine_dialog.js b/chrome/browser/resources/settings/search_engines_page/add_search_engine_dialog.js deleted file mode 100644 index ca161cf1..0000000 --- a/chrome/browser/resources/settings/search_engines_page/add_search_engine_dialog.js +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview 'settings-add-search-engine-dialog' is a component for adding a - * new search engine. - * - * @group Chrome Settings Elements - * @element settings-add-search-engine-dialog - */ -Polymer({ - is: 'settings-add-search-engine-dialog', - - open: function() { - this.$.dialog.open(); - }, - - /** @private */ - onCancelTap_: function() { - this.$.dialog.close(); - }, - - /** @private */ - onAddTap_: function() { - if (this.$.searchEngine.isInvalid || - this.$.keyword.isInvalid || - this.$.queryUrl.isInvalid) { - // TODO(dpapad): Handle validation properly. - this.$.dialog.close(); - return; - } - - chrome.searchEnginesPrivate.addOtherSearchEngine( - this.$.searchEngine.value, this.$.keyword.value, - this.$.queryUrl.value); - this.$.dialog.close(); - }, -});
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.html b/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.html new file mode 100644 index 0000000..05e8bec7 --- /dev/null +++ b/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.html
@@ -0,0 +1,49 @@ +<link rel="import" href="chrome://resources/html/polymer_config.html"> +<link rel="import" href="chrome://md-settings/i18n_setup.html"> +<link rel="import" href="chrome://md-settings/search_engines_page/search_engines_browser_proxy.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-dialog/paper-dialog.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> + +<dom-module id="settings-search-engine-dialog"> + <link rel="import" type="css" href="chrome://md-settings/settings_dialog.css"> + <template> + <paper-dialog modal id="dialog"> + <div id="dialog-content"> + <div class="top-row"> + <span class="title">[[dialogTitle_]]</span> + <paper-icon-button icon="clear" on-tap="cancel_" id="close"> + </paper-icon-button> + </div> + <div class="body"> + <div class="explanation"> + <paper-input always-float-label id="searchEngine" + i18n-values="label:searchEnginesSearchEngine;error-message:searchEnginesNotValid" + value="{{searchEngine_}}" on-focus="validate_" + on-input="validate_"> + </paper-input> + <paper-input always-float-label id="keyword" + i18n-values="label:searchEnginesKeyword;error-message:searchEnginesNotValid" + value="{{keyword_}}" on-focus="validate_" on-input="validate_"> + </paper-input> + <paper-input always-float-label id="queryUrl" + i18n-values="label:searchEnginesQueryURLExplanation;error-message:searchEnginesNotValid" + value="{{queryUrl_}}" on-focus="validate_" on-input="validate_"> + </paper-input> + </div> + <div class="button-container"> + <paper-button class="cancel-button" on-tap="cancel_" + i18n-content="cancel" id="cancel"></paper-button> + <paper-button id="actionButton" class="action-button" + on-tap="onActionButtonTap_"> + [[actionButtonText_]] + </paper-button> + </div> + </div> + </div> + </paper-dialog> + </template> + <script src="search_engine_dialog.js"></script> +</dom-module>
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.js b/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.js new file mode 100644 index 0000000..10e4e29 --- /dev/null +++ b/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.js
@@ -0,0 +1,117 @@ +// 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. + +/** + * @fileoverview 'settings-search-engine-dialog' is a component for adding + * or editing a search engine entry. + * + * @group Chrome Settings Elements + * @element settings-search-engine-dialog + */ +Polymer({ + is: 'settings-search-engine-dialog', + + properties: { + /** + * The search engine to be edited. If not populated a new search engine + * should be added. + * @type {?SearchEngine} + */ + model: Object, + + /** @private {string} */ + searchEngine_: String, + + /** @private {string} */ + keyword_: String, + + /** @private {string} */ + queryUrl_: String, + + /** @private {string} */ + dialogTitle_: String, + + /** @private {string} */ + actionButtonText_: String, + }, + + /** @private {!settings.SearchEnginesBrowserProxy} */ + browserProxy_: null, + + /** + * The |modelIndex| to use when a new search engine is added. Must match with + * kNewSearchEngineIndex constant specified at + * chrome/browser/ui/webui/settings/search_engines_handler.cc + * @const {number} + */ + DEFAULT_MODEL_INDEX: -1, + + /** @override */ + created: function() { + this.browserProxy_ = settings.SearchEnginesBrowserProxyImpl.getInstance(); + }, + + /** @override */ + ready: function() { + if (this.model) { + this.dialogTitle_ = + loadTimeData.getString('searchEnginesEditSearchEngine'); + this.actionButtonText_ = loadTimeData.getString('save'); + + // If editing an existing search engine, pre-populate the input fields. + this.searchEngine_ = this.model.displayName; + this.keyword_ = this.model.keyword; + this.queryUrl_ = this.model.url; + } else { + this.dialogTitle_ = + loadTimeData.getString('searchEnginesAddSearchEngine'); + this.actionButtonText_ = loadTimeData.getString('add'); + } + }, + + /** @override */ + attached: function() { + this.updateActionButtonState_(); + this.browserProxy_.searchEngineEditStarted( + this.model ? this.model.modelIndex : this.DEFAULT_MODEL_INDEX); + this.$.dialog.open(); + }, + + /** @private */ + cancel_: function() { + this.browserProxy_.searchEngineEditCancelled(); + this.$.dialog.close(); + }, + + /** @private */ + onActionButtonTap_: function() { + this.browserProxy_.searchEngineEditCompleted( + this.searchEngine_, this.keyword_, this.queryUrl_); + this.$.dialog.close(); + }, + + /** + * @param {!Event} event + * @private + */ + validate_: function(event) { + var inputElement = Polymer.dom(event).localTarget; + + this.browserProxy_.validateSearchEngineInput( + inputElement.id, inputElement.value).then(function(isValid) { + inputElement.invalid = !isValid; + this.updateActionButtonState_(); + }.bind(this)); + }, + + /** @private */ + updateActionButtonState_: function() { + var allValid = [ + this.$.searchEngine, this.$.keyword, this.$.queryUrl + ].every(function(inputElement) { + return !inputElement.invalid && inputElement.value.length != 0; + }); + this.$.actionButton.disabled = !allValid; + }, +});
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.css b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.css index 1dbf5c6..0a643fe 100644 --- a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.css +++ b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.css
@@ -4,7 +4,7 @@ .name-column, .keyword-column { - flex: 2; + flex: 3; } .name-column { @@ -12,7 +12,7 @@ } .url-column { - flex: 5; + flex: 3.5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -20,11 +20,13 @@ .icon-container { flex: 1; + margin: auto; text-align: center; } .name { flex: 3; + margin: auto; } #container {
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html index 905aa33..509da503 100644 --- a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html +++ b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
@@ -1,21 +1,28 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-dropdown/iron-dropdown.html"> +<link rel="import" href="chrome://md-settings/search_engines_page/search_engines_browser_proxy.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item.html"> +<link rel="import" href="chrome://md-settings/search_engines_page/search_engine_dialog.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-search-engine-entry"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="chrome://md-settings/search_engines_page/search_engine_entry.css"> <template> + <style include="settings-shared"></style> + <template is="dom-if" if="[[showEditSearchEngineDialog_]]" restamp> + <settings-search-engine-dialog model="[[engine]]"> + </settings-search-engine-dialog> + </template> <div id="container" class="list-item"> <div class="name-column"> <span class="icon-container"> - <iron-icon icon="image:brightness-1" item-icon></iron-icon> + <iron-icon src="[[engine.iconURL]]"></iron-icon> </span> - <div class="name">{{engine.name}}</div> + <div class="name">[[engine.displayName]]</div> </div> - <div class="keyword-column">{{engine.keyword}}</div> - <div class="url-column">{{engine.url}}</div> + <div class="keyword-column">[[engine.keyword]]</div> + <div class="url-column">[[engine.url]]</div> <div class="icon-container"> <paper-icon-button icon="more-vert" toggles active="{{editMenuOpened}}"> </paper-icon-button> @@ -23,11 +30,13 @@ vertical-align="top"> <div class="dropdown-content"> <paper-item on-tap="onMakeDefaultTap_" - i18n-content="searchEnginesMakeDefault"></paper-item> + i18n-content="searchEnginesMakeDefault" + hidden="[[engine.default]]" id="makeDefault"></paper-item> <paper-item on-tap="onEditTap_" - i18n-content="searchEnginesEdit"></paper-item> + i18n-content="searchEnginesEdit" id="edit"></paper-item> <paper-item on-tap="onDeleteTap_" - i18n-content="searchEnginesRemoveFromList"></paper-item> + i18n-content="searchEnginesRemoveFromList" + hidden="[[engine.default]]" id="delete"></paper-item> <div> </iron-dropdown> </div>
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.js b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.js index f25513d5..9e840ab8 100644 --- a/chrome/browser/resources/settings/search_engines_page/search_engine_entry.js +++ b/chrome/browser/resources/settings/search_engines_page/search_engine_entry.js
@@ -14,23 +14,48 @@ properties: { /** @type {!SearchEngine} */ - engine: Object + engine: Object, + + /** @private {boolean} */ + showEditSearchEngineDialog_: Boolean, + }, + + /** @private {!settings.SearchEnginesBrowserProxy} */ + browserProxy_: null, + + created: function() { + this.browserProxy_ = settings.SearchEnginesBrowserProxyImpl.getInstance(); }, /** @private */ onDeleteTap_: function() { - chrome.searchEnginesPrivate.removeSearchEngine(this.engine.guid); + this.browserProxy_.removeSearchEngine(this.engine.modelIndex); }, /** @private */ onEditTap_: function() { - // TODO(dpapad): Implement edit mode. + this.closePopupMenu_(); + + this.showEditSearchEngineDialog_ = true; + this.async(function() { + var dialog = this.$$('settings-search-engine-dialog'); + // Register listener to detect when the dialog is closed. Flip the boolean + // once closed to force a restamp next time it is shown such that the + // previous dialog's contents are cleared. + dialog.addEventListener('iron-overlay-closed', function() { + this.showEditSearchEngineDialog_ = false; + }.bind(this)); + }.bind(this)); }, /** @private */ onMakeDefaultTap_: function() { - chrome.searchEnginesPrivate.setSelectedSearchEngine(this.engine.guid); - var popupMenu = this.$$('iron-dropdown'); - popupMenu.close(); + this.closePopupMenu_(); + this.browserProxy_.setDefaultSearchEngine(this.engine.modelIndex); + }, + + /** @private */ + closePopupMenu_: function() { + this.$$('iron-dropdown').close(); }, });
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js b/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js index 6e7bc928..dbb705c 100644 --- a/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js +++ b/chrome/browser/resources/settings/search_engines_page/search_engines_browser_proxy.js
@@ -35,56 +35,84 @@ var SearchEnginesInfo; cr.define('settings', function() { - /** @constructor */ - function SearchEnginesBrowserProxy() {}; - - // The singleton instance_ is replaced with a test version of this wrapper - // during testing. - cr.addSingletonGetter(SearchEnginesBrowserProxy); + /** @interface */ + function SearchEnginesBrowserProxy() {} SearchEnginesBrowserProxy.prototype = { /** @param {number} modelIndex */ - setDefaultSearchEngine: function(modelIndex) { - chrome.send('setDefaultSearchEngine', [modelIndex]); - }, + setDefaultSearchEngine: function(modelIndex) {}, /** @param {number} modelIndex */ - removeSearchEngine: function(modelIndex) { - chrome.send('removeSearchEngine', [modelIndex]); - }, + removeSearchEngine: function(modelIndex) {}, /** @param {number} modelIndex */ - searchEngineEditStarted: function(modelIndex) { - chrome.send('searchEngineEditStarted', [modelIndex]); - }, + searchEngineEditStarted: function(modelIndex) {}, - searchEngineEditCancelled: function() { - chrome.send('searchEngineEditCancelled'); - }, + searchEngineEditCancelled: function() {}, /** * @param {string} searchEngine * @param {string} keyword * @param {string} queryUrl */ - searchEngineEditCompleted: function(searchEngine, keyword, queryUrl) { - chrome.send('searchEngineEditCompleted', [ - searchEngine, keyword, queryUrl, - ]); - }, + searchEngineEditCompleted: function(searchEngine, keyword, queryUrl) {}, /** * @return {!Promise<!SearchEnginesInfo>} */ - getSearchEnginesList: function() { - return cr.sendWithPromise('getSearchEnginesList'); - }, + getSearchEnginesList: function() {}, /** * @param {string} fieldName * @param {string} fieldValue * @return {!Promise<boolean>} */ + validateSearchEngineInput: function(fieldName, fieldValue) {}, + }; + + /** + * @constructor + * @implements {settings.SearchEnginesBrowserProxy} + */ + function SearchEnginesBrowserProxyImpl() {} + // The singleton instance_ is replaced with a test version of this wrapper + // during testing. + cr.addSingletonGetter(SearchEnginesBrowserProxyImpl); + + SearchEnginesBrowserProxyImpl.prototype = { + /** @override */ + setDefaultSearchEngine: function(modelIndex) { + chrome.send('setDefaultSearchEngine', [modelIndex]); + }, + + /** @override */ + removeSearchEngine: function(modelIndex) { + chrome.send('removeSearchEngine', [modelIndex]); + }, + + /** @override */ + searchEngineEditStarted: function(modelIndex) { + chrome.send('searchEngineEditStarted', [modelIndex]); + }, + + /** @override */ + searchEngineEditCancelled: function() { + chrome.send('searchEngineEditCancelled'); + }, + + /** @override */ + searchEngineEditCompleted: function(searchEngine, keyword, queryUrl) { + chrome.send('searchEngineEditCompleted', [ + searchEngine, keyword, queryUrl, + ]); + }, + + /** @override */ + getSearchEnginesList: function() { + return cr.sendWithPromise('getSearchEnginesList'); + }, + + /** @override */ validateSearchEngineInput: function(fieldName, fieldValue) { return cr.sendWithPromise( 'validateSearchEngineInput', fieldName, fieldValue); @@ -92,6 +120,6 @@ }; return { - SearchEnginesBrowserProxy: SearchEnginesBrowserProxy, + SearchEnginesBrowserProxyImpl: SearchEnginesBrowserProxyImpl, }; });
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_list.css b/chrome/browser/resources/settings/search_engines_page/search_engines_list.css index 0156514..7be65e6 100644 --- a/chrome/browser/resources/settings/search_engines_page/search_engines_list.css +++ b/chrome/browser/resources/settings/search_engines_page/search_engines_list.css
@@ -9,9 +9,9 @@ .headers .name, .headers .keyword { - flex: 1; + flex: 3; } .headers .url { - flex: 3; + flex: 4.5; }
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_list.html b/chrome/browser/resources/settings/search_engines_page/search_engines_list.html index 111c1dc..ef46116 100644 --- a/chrome/browser/resources/settings/search_engines_page/search_engines_list.html +++ b/chrome/browser/resources/settings/search_engines_page/search_engines_list.html
@@ -6,9 +6,9 @@ <link rel="import" type="css" href="search_engines_list.css"> <template> <div class="headers"> - <div class="name" i18n-content="searchEnginesSearchEngineLabel"></div> - <div class="keyword" i18n-content="searchEnginesKeywordLabel"></div> - <div class="url" i18n-content="searchEnginesQueryURLLabel"></div> + <div class="name" i18n-content="searchEnginesSearchEngine"></div> + <div class="keyword" i18n-content="searchEnginesKeyword"></div> + <div class="url" i18n-content="searchEnginesQueryURL"></div> </div> <div> <template is="dom-repeat" items="[[engines]]">
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html index 375d41c5..6a0d45c2 100644 --- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.html +++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
@@ -1,15 +1,17 @@ +<link rel="import" href="chrome://resources/html/polymer_config.html"> <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> -<link rel="import" href="chrome://md-settings/search_engines_page/add_search_engine_dialog.html"> +<link rel="import" href="chrome://md-settings/search_engines_page/search_engines_browser_proxy.html"> +<link rel="import" href="chrome://md-settings/search_engines_page/search_engine_dialog.html"> <link rel="import" href="chrome://md-settings/search_engines_page/search_engines_list.html"> -<link rel="import" href="chrome://md-settings/reset_page/reset_profile_dialog.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-search-engines-page"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <template> - <style> + <style include="settings-shared"> .other-label-container { display: flex; } @@ -24,20 +26,20 @@ </div> <div class="settings-box"> <paper-button class="link-button" - i18n-content="searchEnginesAddSearchEngineLabel" - on-tap="onAddSearchEngineTap_"></paper-button> + i18n-content="searchEnginesAddSearchEngine" + on-tap="onAddSearchEngineTap_" id="addSearchEngine"></paper-button> </div> <template is="dom-if" if="[[showAddSearchEngineDialog_]]" restamp> - <settings-add-search-engine-dialog></settings-add-search-engine-dialog> + <settings-search-engine-dialog></settings-search-engine-dialog> </template> <div class="settings-box block"> <div class="other-label-container"> - <span i18n-content="searchEnginesOtherLabel"></span> - <cr-expand-button expanded="{{otherSearchEnginesExpanded}}"> + <span i18n-content="searchEnginesOther"></span> + <cr-expand-button expanded="{{otherSearchEnginesExpanded_}}"> </cr-expand-button> </div> - <iron-collapse id="collapse" opened=[[otherSearchEnginesExpanded]]> + <iron-collapse id="collapse" opened=[[otherSearchEnginesExpanded_]]> <settings-search-engines-list engines="[[otherEngines]]"> </settings-search-engines-list> </iron-collapse>
diff --git a/chrome/browser/resources/settings/search_engines_page/search_engines_page.js b/chrome/browser/resources/settings/search_engines_page/search_engines_page.js index be4cee5..2f58998 100644 --- a/chrome/browser/resources/settings/search_engines_page/search_engines_page.js +++ b/chrome/browser/resources/settings/search_engines_page/search_engines_page.js
@@ -6,14 +6,6 @@ * @fileoverview 'settings-search-engines-page' is the settings page * containing search engines settings. * - * Example: - * - * <core-animated-pages> - * <settings-search-engines-page prefs="{{prefs}}"> - * </settings-search-engines-page> - * ... other pages ... - * </core-animated-pages> - * * @group Chrome Settings Elements * @element settings-search-engines-page */ @@ -33,47 +25,62 @@ value: function() { return []; } }, - /** @type {boolean} */ - showAddSearchEngineDialog_: { + /** @private {boolean} */ + showAddSearchEngineDialog_: Boolean, + + /** @private {boolean} */ + otherSearchEnginesExpanded_: { type: Boolean, - value: false, + value: true, }, }, + /** + * Holds WebUI listeners that need to be removed when this element is + * destroyed. + * TODO(dpapad): Move listener tracking logic to a Polymer behavior class, + * such that it can be re-used. + * @private {!Array<!WebUIListener>} + */ + webUIListeners_: [], + /** @override */ ready: function() { - chrome.searchEnginesPrivate.onSearchEnginesChanged.addListener( - this.enginesChanged_.bind(this)); - this.enginesChanged_(); + settings.SearchEnginesBrowserProxyImpl.getInstance(). + getSearchEnginesList().then(this.enginesChanged_.bind(this)); + this.webUIListeners_.push(cr.addWebUIListener( + 'search-engines-changed', this.enginesChanged_.bind(this))); }, - /** @private */ - enginesChanged_: function() { - chrome.searchEnginesPrivate.getSearchEngines(function(engines) { - this.defaultEngines = engines.filter(function(engine) { - return engine.type == - chrome.searchEnginesPrivate.SearchEngineType.DEFAULT; - }, this); + /** @override */ + detached: function() { + this.webUIListeners_.forEach(function(listener) { + cr.removeWebUIListener(listener); + }); + }, - this.otherEngines = engines.filter(function(engine) { - return engine.type == - chrome.searchEnginesPrivate.SearchEngineType.OTHER; - }, this); - }.bind(this)); + /** + * @param {!SearchEnginesInfo} searchEnginesInfo + * @private + */ + enginesChanged_: function(searchEnginesInfo) { + this.defaultEngines = searchEnginesInfo['defaults']; + this.otherEngines = searchEnginesInfo['others']; + // TODO(dpapad): Use searchEnginesInfo['extensions'] once UI mocks are + // provided. }, /** @private */ onAddSearchEngineTap_: function() { this.showAddSearchEngineDialog_ = true; this.async(function() { - var dialog = this.$$('settings-add-search-engine-dialog'); + var dialog = this.$$('settings-search-engine-dialog'); // Register listener to detect when the dialog is closed. Flip the boolean // once closed to force a restamp next time it is shown such that the // previous dialog's contents are cleared. dialog.addEventListener('iron-overlay-closed', function() { this.showAddSearchEngineDialog_ = false; }.bind(this)); - dialog.open(); }.bind(this)); }, });
diff --git a/chrome/browser/resources/settings/search_page/search_page.html b/chrome/browser/resources/settings/search_page/search_page.html index dc75bde..395f80b 100644 --- a/chrome/browser/resources/settings/search_page/search_page.html +++ b/chrome/browser/resources/settings/search_page/search_page.html
@@ -5,12 +5,12 @@ <link rel="import" href="chrome://md-settings/search_engines_page/search_engines_page.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-search-page"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="search_page.css"> <template> + <style include="settings-shared"></style> <settings-animated-pages id="pages" current-route="{{currentRoute}}" section="search"> <neon-animatable id="main"> @@ -25,13 +25,12 @@ </div> <div class="settings-box"> <paper-button class="link-button" - i18n-content="searchManageButtonLabel" - on-tap="onSearchEnginesTap_"> + i18n-content="searchEnginesManage" on-tap="onSearchEnginesTap_"> </paper-button> </div> </neon-animatable> <neon-animatable id="search-engines"> - <settings-subheader i18n-values="page-title:searchEnginesLabel"> + <settings-subheader i18n-values="page-title:searchEnginesManage"> </settings-subheader> <settings-search-engines-page></settings-search-engines-page> </neon-animatable>
diff --git a/chrome/browser/resources/settings/settings_dialog.css b/chrome/browser/resources/settings/settings_dialog.css index 8d2eaba..4d72f16 100644 --- a/chrome/browser/resources/settings/settings_dialog.css +++ b/chrome/browser/resources/settings/settings_dialog.css
@@ -45,9 +45,12 @@ paper-dialog .action-button { -webkit-margin-start: 10px; + font-weight: 500; +} + +paper-dialog .action-button:not([disabled]) { background-color: rgb(66, 133, 244); color: white; - font-weight: 500; } paper-dialog .cancel-button {
diff --git a/chrome/browser/resources/settings/settings_page/compiled_resources2.gyp b/chrome/browser/resources/settings/settings_page/compiled_resources2.gyp new file mode 100644 index 0000000..42cd2ac0 --- /dev/null +++ b/chrome/browser/resources/settings/settings_page/compiled_resources2.gyp
@@ -0,0 +1,15 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'settings_animated_pages', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +}
diff --git a/chrome/browser/resources/settings/settings_page/settings_animated_pages.html b/chrome/browser/resources/settings/settings_page/settings_animated_pages.html index 6965ca1d..4844215 100644 --- a/chrome/browser/resources/settings/settings_page/settings_animated_pages.html +++ b/chrome/browser/resources/settings/settings_page/settings_animated_pages.html
@@ -9,11 +9,12 @@ <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animated-pages.html"> <link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animation-runner-behavior.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <dom-module id="settings-animated-pages"> - <link rel="import" type="css" href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="settings_animated_pages.css"> <template> + <style include="settings-shared"></style> <div class="settings-card"> <neon-animated-pages id="animatedPages" attr-for-selected="id"> <content select="*"></content>
diff --git a/chrome/browser/resources/settings/settings_page/settings_router.js b/chrome/browser/resources/settings/settings_page/settings_router.js index 563c7d4..b6b8002 100644 --- a/chrome/browser/resources/settings/settings_page/settings_router.js +++ b/chrome/browser/resources/settings/settings_page/settings_router.js
@@ -388,6 +388,15 @@ subpageTitles: ['editDictionaryPageTitle'], }, </if> +<if expr="chromeos"> + { + url: '/pointer-overlay', + page: 'basic', + section: 'device', + subpage: ['touchpad'], + subpageTitles: ['touchpadTitle'], + }, +</if> ], /**
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd index 11a655d2..ed795e8 100644 --- a/chrome/browser/resources/settings/settings_resources.grd +++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -136,8 +136,8 @@ <structure name="IDR_SETTINGS_CR_SETTINGS_PAGE_CSS" file="settings_page.css" type="chrome_html" /> - <structure name="IDR_SETTINGS_CR_SETTINGS_SHARED_CSS" - file="settings_shared.css" + <structure name="IDR_SETTINGS_SETTINGS_SHARED_CSS_HTML" + file="settings_shared_css.html" type="chrome_html" /> <structure name="IDR_SETTINGS_BREADCRUMB_CSS" file="settings_ui/breadcrumb.css" @@ -220,6 +220,20 @@ file="default_browser_page/default_browser_page.js" type="chrome_html" /> </if> + <if expr="chromeos"> + <structure name="IDR_SETTINGS_DEVICE_PAGE_HTML" + file="device_page/device_page.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_DEVICE_PAGE_JS" + file="device_page/device_page.js" + type="chrome_html" /> + <structure name="IDR_SETTINGS_DEVICE_TOUCHPAD_HTML" + file="device_page/touchpad.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_DEVICE_TOUCHPAD_JS" + file="device_page/touchpad.js" + type="chrome_html" /> + </if> <structure name="IDR_SETTINGS_DIRECTION_DELEGATE_HTML" file="direction_delegate.html" type="chrome_html" /> @@ -459,6 +473,12 @@ <structure name="IDR_SETTINGS_SITE_SETTINGS_PAGE_JS" file="site_settings_page/site_settings_page.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_SITE_SETTINGS_PREFS_BROWSER_PROXY_HTML" + file="site_settings/site_settings_prefs_browser_proxy.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_SITE_SETTINGS_PREFS_BROWSER_PROXY_JS" + file="site_settings/site_settings_prefs_browser_proxy.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_SITE_DETAILS_CSS" file="site_settings/site_details.css" type="chrome_html" /> @@ -477,11 +497,11 @@ <structure name="IDR_SETTINGS_SITE_DETAILS_PERMISSION_JS" file="site_settings/site_details_permission.js" type="chrome_html" /> - <structure name="IDR_SETTINGS_SEARCH_ENGINES_PAGE_ADD_SEARCH_ENGINE_DIALOG_JS" - file="search_engines_page/add_search_engine_dialog.js" + <structure name="IDR_SETTINGS_SEARCH_ENGINES_PAGE_SEARCH_ENGINE_DIALOG_JS" + file="search_engines_page/search_engine_dialog.js" type="chrome_html" /> - <structure name="IDR_SETTINGS_SEARCH_ENGINES_PAGE_ADD_SEARCH_ENGINE_DIALOG_HTML" - file="search_engines_page/add_search_engine_dialog.html" + <structure name="IDR_SETTINGS_SEARCH_ENGINES_PAGE_SEARCH_ENGINE_DIALOG_HTML" + file="search_engines_page/search_engine_dialog.html" type="chrome_html" flattenhtml="true" allowexternalscript="true" />
diff --git a/chrome/browser/resources/settings/settings_shared.css b/chrome/browser/resources/settings/settings_shared.css deleted file mode 100644 index 4be12d8c..0000000 --- a/chrome/browser/resources/settings/settings_shared.css +++ /dev/null
@@ -1,230 +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. */ - -/** - * @fileoverview - * Common styles for Material Design settings. - */ - -h2 { - color: var(--paper-grey-500); - font-size: 100%; - font-weight: normal; - margin-bottom: 20px; - margin-top: 30px; -} - -iron-icon { - color: #969696; -} - -iron-icon[icon=done] { - color: rgb(0, 128, 0); -} - -paper-button { - margin: 0; - min-width: auto; - padding: 0; -} - -paper-button[toggles][active] { - background-color: LightGray; -} - -span ~ a { - -webkit-margin-start: 4px; -} - -paper-button.primary-button { - --paper-button: { - color: rgb(66, 133, 244); - text-align: start; - text-transform: none; - font-weight: 500; - }; -} - -paper-button.secondary-button { - --paper-button: { - color: #5a5a5a; - text-decoration: none; - font-weight: 500; - }; -} - -paper-button.tertiary-button { - --paper-button: { - color: rgb(51, 103, 214); - text-decoration: none; - font-weight: 400; - }; -} - -paper-radio-button { - --paper-radio-button-label-spacing: 18px; - --paper-radio-button-checked-color: var(--google-blue-500); - --paper-radio-button-unchecked-color: #969696; - -webkit-margin-start: 2px; -} - -.text-elide { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.button-row { - display: flex; - margin-top: 25px; -} - -.button-strip { - text-align: end; -} - -.list-frame { - @apply(--layout-center); - display: block; - -webkit-padding-end: 20px; - -webkit-padding-start: 48px; - padding-bottom: 0; - padding-top: 0; -} - -.list-frame select { - -webkit-margin-start: 4px; -} - -.list-frame .secondary { - color: #969696; -} - -.list-item { - @apply(--layout-center); - display: flex; - min-height: 40px; - padding: 0; -} - -/* This button has no ink ripple */ -.list-item.list-button { - @apply(--layout-center); - color: rgb(66, 133, 244); - font-weight: 500; - text-decoration: none; -} - -.list-item iron-icon { - @apply(--layout-center); - height: 20px; - width: 20px; -} - -.list-item > .middle { - flex: 1; - margin: 8px 12px; - overflow: hidden; - white-space: nowrap; -} - -.list-item > .start { - flex: 1; - overflow: hidden; - white-space: nowrap; -} - -.list-item > paper-icon-item { - padding: 0; -} - -/* This button has no ink ripple. */ -.list-item.list-button { - @apply(--layout-center); - color: rgb(66, 133, 244); - font-weight: 500; - padding: 0; - text-decoration: none; - text-transform: none; -} - -.link-button { - color: rgb(61, 130, 243); - padding: 6px 0; - text-transform: none; -} - -.settings-card { - padding: 8px 0; -} - -.settings-box { - @apply(--layout-center); - border-top: 1px solid var(--paper-grey-300); - display: flex; - min-height: 40px; - padding: 0 20px; -} - -.settings-box.first { - border-top: none; -} - -.settings-box.block { - display: block; -} - -.settings-box.two-line { - min-height: 52px; -} - -.settings-box iron-icon { - @apply(--layout-center); - height: 20px; - width: 20px; -} - -.settings-box .secondary { - color: #969696; - font-weight: 400; -} - -.settings-box .middle { - -webkit-margin-start: 16px; - align-items: center; - flex: auto; -} - -.settings-box .start { - align-items: center; - flex: auto; -} - -/* The secondary-action wraps a clickable sub-area of a .settings-box. - * An example is the |sign out| button on the People settings. - * Here is an example with and without a secondary action box: - * - * +-------------------------------------------------------+ - * | Main action area .settings-box | .secondary-action | - * +-------------------------------------------------------+ - * | Another setting-box without a secondary-action | - * +-------------------------------------------------------+ - */ -.settings-box .secondary-action { - -webkit-border-start: 1px solid var(--paper-grey-300); - -webkit-padding-start: 20px; -} - -.settings-box paper-item iron-icon { - /* Same padding as paper-icon-button. */ - padding: 8px; -} - -.vertical-list > div:first-of-type { - border-top: none; -} - -.vertical-list > div { - border-top: 1px solid var(--paper-grey-300); -}
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html new file mode 100644 index 0000000..b2c7411 --- /dev/null +++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -0,0 +1,236 @@ +/* Copyright 2016 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +/** + * @fileoverview + * Common styles for Material Design settings. + */ + +<dom-module id="settings-shared"> + <template> + <style> + h2 { + color: var(--paper-grey-500); + font-size: 100%; + font-weight: normal; + margin-bottom: 20px; + margin-top: 30px; + } + + iron-icon { + color: #969696; + } + + iron-icon[icon=done] { + color: rgb(0, 128, 0); + } + + paper-button { + margin: 0; + min-width: auto; + padding: 0; + } + + paper-button[toggles][active] { + background-color: LightGray; + } + + span ~ a { + -webkit-margin-start: 4px; + } + + paper-button.primary-button { + --paper-button: { + color: rgb(66, 133, 244); + text-align: start; + text-transform: none; + font-weight: 500; + }; + } + + paper-button.secondary-button { + --paper-button: { + color: #5a5a5a; + text-decoration: none; + font-weight: 500; + }; + } + + paper-button.tertiary-button { + --paper-button: { + color: rgb(51, 103, 214); + text-decoration: none; + font-weight: 400; + }; + } + + paper-radio-button { + --paper-radio-button-checked-color: var(--google-blue-500); + --paper-radio-button-label-spacing: 18px; + --paper-radio-button-unchecked-color: #969696; + -webkit-margin-start: 2px; + } + + .text-elide { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .button-row { + display: flex; + margin-top: 25px; + } + + .button-strip { + text-align: end; + } + + .list-frame { + @apply(--layout-center); + display: block; + -webkit-padding-end: 20px; + -webkit-padding-start: 48px; + padding-bottom: 0; + padding-top: 0; + } + + .list-frame select { + -webkit-margin-start: 4px; + } + + .list-frame .secondary { + color: #969696; + } + + .list-item { + @apply(--layout-center); + display: flex; + min-height: 40px; + padding: 0; + } + + /* This button has no ink ripple */ + .list-item.list-button { + @apply(--layout-center); + color: rgb(66, 133, 244); + font-weight: 500; + text-decoration: none; + } + + .list-item iron-icon { + @apply(--layout-center); + height: 20px; + width: 20px; + } + + .list-item > .middle { + flex: 1; + margin: 8px 12px; + overflow: hidden; + white-space: nowrap; + } + + .list-item > .start { + flex: 1; + overflow: hidden; + white-space: nowrap; + } + + .list-item > paper-icon-item { + padding: 0; + } + + /* This button has no ink ripple. */ + .list-item.list-button { + @apply(--layout-center); + color: rgb(66, 133, 244); + font-weight: 500; + padding: 0; + text-decoration: none; + text-transform: none; + } + + .link-button { + color: rgb(61, 130, 243); + padding: 6px 0; + text-transform: none; + } + + .settings-card { + padding: 8px 0; + } + + .settings-box { + @apply(--layout-center); + border-top: 1px solid var(--paper-grey-300); + display: flex; + min-height: 40px; + padding: 0 20px; + } + + .settings-box.first { + border-top: none; + } + + .settings-box.block { + display: block; + } + + .settings-box.two-line { + min-height: 52px; + } + + .settings-box iron-icon { + @apply(--layout-center); + height: 20px; + width: 20px; + } + + .settings-box .secondary { + color: #969696; + font-weight: 400; + } + + .settings-box .middle { + -webkit-margin-start: 16px; + align-items: center; + flex: auto; + } + + .settings-box .start { + align-items: center; + flex: auto; + } + + /* The secondary-action wraps a clickable sub-area of a .settings-box. + * An example is the |sign out| button on the People settings. + * Here is an example with and without a secondary action box: + * + * +-------------------------------------------------------+ + * | Main action area .settings-box | .secondary-action | + * +-------------------------------------------------------+ + * | Another setting-box without a secondary-action | + * +-------------------------------------------------------+ + */ + .settings-box .secondary-action { + -webkit-border-start: 1px solid var(--paper-grey-300); + -webkit-padding-start: 20px; + } + + .settings-box paper-item iron-icon { + /* Same padding as paper-icon-button. */ + padding: 8px; + } + + .vertical-list > div:first-of-type { + border-top: none; + } + + .vertical-list > div { + border-top: 1px solid var(--paper-grey-300); + } + </style> + </template> +</dom-module>
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.html b/chrome/browser/resources/settings/site_settings/all_sites.html index e91669f..242a6f8 100644 --- a/chrome/browser/resources/settings/site_settings/all_sites.html +++ b/chrome/browser/resources/settings/site_settings/all_sites.html
@@ -1,13 +1,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="chrome://md-settings/site_settings/constants.html"> <link rel="import" href="chrome://md-settings/site_settings/site_list.html"> <link rel="import" href="chrome://md-settings/site_settings/site_settings_behavior.html"> <dom-module id="all-sites"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="all_sites.css"> <template> + <style include="settings-shared"></style> <settings-site-list id="siteList" all-sites category="-1" prefs="{{prefs}}" current-route="{{currentRoute}}" selected-origin="{{selectedOrigin}}"></settings-site-list>
diff --git a/chrome/browser/resources/settings/site_settings/compiled_resources.gyp b/chrome/browser/resources/settings/site_settings/compiled_resources.gyp index 34ee414..647c2d7 100644 --- a/chrome/browser/resources/settings/site_settings/compiled_resources.gyp +++ b/chrome/browser/resources/settings/site_settings/compiled_resources.gyp
@@ -102,6 +102,7 @@ '../settings_page/settings_animated_pages.js', 'constants.js', 'site_settings_behavior.js', + 'site_settings_prefs_browser_proxy.js', ], 'externs': [ '../../../../../third_party/closure_compiler/externs/settings_private.js',
diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b/chrome/browser/resources/settings/site_settings/site_details.html index 3512297..bfd94d86 100644 --- a/chrome/browser/resources/settings/site_settings/site_details.html +++ b/chrome/browser/resources/settings/site_settings/site_details.html
@@ -2,14 +2,14 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="chrome://md-settings/site_settings/site_details_permission.html"> <link rel="import" href="chrome://md-settings/site_settings/website_usage_private_api.html"> <dom-module id="site-details"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="site_details.css"> <template> + <style include="settings-shared"></style> <div class="settings-box block"> <div class="origin">[[origin]]</div> <h2 i18n-content="siteSettingsUsage" id="usage"
diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.html b/chrome/browser/resources/settings/site_settings/site_details_permission.html index 0c65a99..131c906 100644 --- a/chrome/browser/resources/settings/site_settings/site_details_permission.html +++ b/chrome/browser/resources/settings/site_settings/site_details_permission.html
@@ -8,13 +8,13 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-menu/paper-menu.html"> <link rel="import" href="chrome://md-settings/prefs/prefs_behavior.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="chrome://md-settings/site_settings/site_settings_behavior.html"> <dom-module id="site-details-permission"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="site_details_permission.css"> <template> + <style include="settings-shared"></style> <div id="details" class="horizontal layout top" hidden> <div class="left-column"> <iron-icon icon="[[computeIconForContentCategory(category)]]"
diff --git a/chrome/browser/resources/settings/site_settings/site_list.html b/chrome/browser/resources/settings/site_settings/site_list.html index 82943a9..047a79f 100644 --- a/chrome/browser/resources/settings/site_settings/site_list.html +++ b/chrome/browser/resources/settings/site_settings/site_list.html
@@ -9,13 +9,14 @@ <link rel="import" href="chrome://resources/polymer/v1_0/paper-menu-button/paper-menu-button.html"> <link rel="import" href="chrome://md-settings/prefs/prefs_behavior.html"> <link rel="import" href="chrome://md-settings/prefs/prefs_types.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="chrome://md-settings/site_settings/constants.html"> <link rel="import" href="chrome://md-settings/site_settings/site_settings_behavior.html"> <dom-module id="settings-site-list"> - <link rel="import" type="css" href="../settings_shared.css"> <link rel="import" type="css" href="site_list.css"> <template> + <style include="settings-shared"></style> <paper-submenu id="category" hidden on-paper-submenu-open="onToggle_" on-paper-submenu-close="onToggle_"> <paper-item class="menu-trigger" hidden$="[[allSites]]">
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_behavior.js b/chrome/browser/resources/settings/site_settings/site_settings_behavior.js index 3c9c2f4e..7ffd765ae 100644 --- a/chrome/browser/resources/settings/site_settings/site_settings_behavior.js +++ b/chrome/browser/resources/settings/site_settings/site_settings_behavior.js
@@ -17,22 +17,6 @@ }, /** - * Returns whether the category default is set to enabled or not. - * @param {number} category The category to check. - * @return {boolean} True if the category default is set to enabled. - * @protected - */ - isCategoryAllowed: function(category) { - var pref = this.getPref(this.computeCategoryPrefName(category)); - - // FullScreen is Allow vs. Ask. - if (category == settings.ContentSettingsTypes.FULLSCREEN) - return pref.value != settings.PermissionValues.ASK; - - return pref.value != settings.PermissionValues.BLOCK; - }, - - /** * Re-sets the category permission for a given origin. * @param {string} origin The origin to change the permission for. * @param {number} category The category permission to change.
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_category.html b/chrome/browser/resources/settings/site_settings/site_settings_category.html index dca5ac35..0598180 100644 --- a/chrome/browser/resources/settings/site_settings/site_settings_category.html +++ b/chrome/browser/resources/settings/site_settings/site_settings_category.html
@@ -9,16 +9,17 @@ <link rel="import" href="chrome://md-settings/prefs/prefs_types.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_animated_pages.html"> <link rel="import" href="chrome://md-settings/settings_page/settings_subheader.html"> +<link rel="import" href="chrome://md-settings/settings_shared_css.html"> <link rel="import" href="chrome://md-settings/site_settings/constants.html"> +<link rel="import" href="chrome://md-settings/site_settings/site_details.html"> <link rel="import" href="chrome://md-settings/site_settings/site_list.html"> <link rel="import" href="chrome://md-settings/site_settings/site_settings_behavior.html"> -<link rel="import" href="chrome://md-settings/site_settings/site_details.html"> +<link rel="import" href="chrome://md-settings/site_settings/site_settings_prefs_browser_proxy.html"> <dom-module id="site-settings-category"> - <link rel="import" type="css" - href="chrome://md-settings/settings_shared.css"> <link rel="import" type="css" href="site_settings_category.css"> <template> + <style include="settings-shared"></style> <paper-icon-item> <iron-icon icon="[[computeIconForContentCategory(category)]]" item-icon></iron-icon>
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_category.js b/chrome/browser/resources/settings/site_settings/site_settings_category.js index 7d1a33f..45d8b14 100644 --- a/chrome/browser/resources/settings/site_settings/site_settings_category.js +++ b/chrome/browser/resources/settings/site_settings/site_settings_category.js
@@ -2,6 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Define a global boolean for notifications (only enabled in the test class). +cr.define('settings_test', function() { + var siteSettingsCategoryOptions = + settings_test.siteSettingsCategoryOptions || { + /** + * True if property changes should fire events for testing purposes. + * @type {boolean} + */ + notifyPropertyChangesForTest: false, + }; + return {siteSettingsCategoryOptions: siteSettingsCategoryOptions}; +}); + /** * @fileoverview * 'site-settings-category' is the polymer element for showing a certain @@ -43,7 +56,11 @@ * example, the Location category can be set to Block/Ask so false, in that * case, represents Block and true represents Ask. */ - categoryEnabled: Boolean, + categoryEnabled: { + type: Boolean, + notify: settings_test.siteSettingsCategoryOptions. + notifyPropertyChangesForTest, + }, /** * The origin that was selected by the user in the dropdown list. @@ -77,32 +94,36 @@ * @private */ onToggleChange_: function(event) { + var prefsProxy = settings.SiteSettingsPrefsBrowserProxy.getInstance(); switch (this.category) { case settings.ContentSettingsTypes.COOKIES: case settings.ContentSettingsTypes.JAVASCRIPT: case settings.ContentSettingsTypes.POPUPS: // "Allowed" vs "Blocked". - this.setPrefValue(this.computeCategoryPrefName(this.category), - this.categoryEnabled ? - settings.PermissionValues.ALLOW : - settings.PermissionValues.BLOCK); + prefsProxy.setDefaultValueForContentType( + this.category, + this.categoryEnabled ? + settings.PermissionValues.ALLOW : + settings.PermissionValues.BLOCK); break; case settings.ContentSettingsTypes.NOTIFICATIONS: case settings.ContentSettingsTypes.GEOLOCATION: case settings.ContentSettingsTypes.CAMERA: case settings.ContentSettingsTypes.MIC: // "Ask" vs "Blocked". - this.setPrefValue(this.computeCategoryPrefName(this.category), - this.categoryEnabled ? - settings.PermissionValues.ASK : - settings.PermissionValues.BLOCK); + prefsProxy.setDefaultValueForContentType( + this.category, + this.categoryEnabled ? + settings.PermissionValues.ASK : + settings.PermissionValues.BLOCK); break; case settings.ContentSettingsTypes.FULLSCREEN: // "Allowed" vs. "Ask first". - this.setPrefValue(this.computeCategoryPrefName(this.category), - this.categoryEnabled ? - settings.PermissionValues.ALLOW : - settings.PermissionValues.ASK); + prefsProxy.setDefaultValueForContentType( + this.category, + this.categoryEnabled ? + settings.PermissionValues.ALLOW : + settings.PermissionValues.ASK); break; default: assertNotReached(); @@ -114,6 +135,10 @@ * @private */ onCategoryChanged_: function() { - this.categoryEnabled = this.isCategoryAllowed(this.category); + var prefsProxy = settings.SiteSettingsPrefsBrowserProxy.getInstance(); + prefsProxy.getDefaultValueForContentType( + this.category).then(function(enabled) { + this.categoryEnabled = enabled; + }.bind(this)); }, });
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.html b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.html new file mode 100644 index 0000000..8a9ed62 --- /dev/null +++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.html
@@ -0,0 +1 @@ +<script src="chrome://md-settings/site_settings/site_settings_prefs_browser_proxy.js"></script> \ No newline at end of file
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js new file mode 100644 index 0000000..2a8b281 --- /dev/null +++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -0,0 +1,41 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview A helper object used from the "Site Settings" section to + * interact with the content settings prefs. + */ + +cr.define('settings', function() { + /** @constructor */ + function SiteSettingsPrefsBrowserProxy() {}; + + // The singleton instance_ is replaced with a test version of this wrapper + // during testing. + cr.addSingletonGetter(SiteSettingsPrefsBrowserProxy); + + SiteSettingsPrefsBrowserProxy.prototype = { + /** + * Sets the default value for a site settings category. + * @param {number} contentType The category to change. + * @param {number} defaultValue The value to set as default. + */ + setDefaultValueForContentType: function(contentType, defaultValue) { + chrome.send('setDefaultValueForContentType', [contentType, defaultValue]); + }, + + /** + * Gets the default value for a site settings category. + * @param {number} contentType The category to change. + * @return {Promise} + */ + getDefaultValueForContentType: function(contentType) { + return cr.sendWithPromise('getDefaultValueForContentType', contentType); + }, + }; + + return { + SiteSettingsPrefsBrowserProxy: SiteSettingsPrefsBrowserProxy, + }; +});
diff --git a/chrome/browser/resources/settings/site_settings_page/compiled_resources.gyp b/chrome/browser/resources/settings/site_settings_page/compiled_resources.gyp index 033958e5..953d45c4 100644 --- a/chrome/browser/resources/settings/site_settings_page/compiled_resources.gyp +++ b/chrome/browser/resources/settings/site_settings_page/compiled_resources.gyp
@@ -15,6 +15,7 @@ '../settings_page/settings_animated_pages.js', '../site_settings/constants.js', '../site_settings/site_settings_behavior.js', + '../site_settings/site_settings_prefs_browser_proxy.js', ], 'externs': [ '../../../../../third_party/closure_compiler/externs/settings_private.js',
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html index b838332..6ef8712 100644 --- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html +++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -5,6 +5,7 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/social-icons.html"> <link rel="import" href="chrome://md-settings/site_settings/constants.html"> <link rel="import" href="chrome://md-settings/site_settings/site_settings_behavior.html"> +<link rel="import" href="chrome://md-settings/site_settings/site_settings_prefs_browser_proxy.html"> <dom-module id="settings-site-settings-page"> <link rel="import" type="css" href="site_settings_page.css">
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js index 270f9bcd..da2dbb0a 100644 --- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js +++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -51,36 +51,54 @@ ready: function() { CrSettingsPrefs.initialized.then(function() { - this.addCategory(-1); // Every category (All Sites). - this.addCategory(settings.ContentSettingsTypes.COOKIES); - this.addCategory(settings.ContentSettingsTypes.GEOLOCATION); - this.addCategory(settings.ContentSettingsTypes.CAMERA); - this.addCategory(settings.ContentSettingsTypes.MIC); - this.addCategory(settings.ContentSettingsTypes.JAVASCRIPT); - this.addCategory(settings.ContentSettingsTypes.POPUPS); - this.addCategory(settings.ContentSettingsTypes.FULLSCREEN); - this.addCategory(settings.ContentSettingsTypes.NOTIFICATIONS); - this.addCategory(settings.ContentSettingsTypes.IMAGES); + this.addAllSitesCategory_(); + this.addCategory_(settings.ContentSettingsTypes.COOKIES); + this.addCategory_(settings.ContentSettingsTypes.GEOLOCATION); + this.addCategory_(settings.ContentSettingsTypes.CAMERA); + this.addCategory_(settings.ContentSettingsTypes.MIC); + this.addCategory_(settings.ContentSettingsTypes.JAVASCRIPT); + this.addCategory_(settings.ContentSettingsTypes.POPUPS); + this.addCategory_(settings.ContentSettingsTypes.FULLSCREEN); + this.addCategory_(settings.ContentSettingsTypes.NOTIFICATIONS); + this.addCategory_(settings.ContentSettingsTypes.IMAGES); }.bind(this)); }, /** + * Adds the All Sites category to the page. + * @private + */ + addAllSitesCategory_: function() { + var description = loadTimeData.getString('siteSettingsCategoryAllSites'); + this.addCategoryImpl_(-1, 'list', description, ''); + }, + + /** * Adds a single category to the page. * @param {number} category The category to add. + * @private */ - addCategory: function(category) { - var icon, title, categoryDescription; - if (category === -1) { - icon = 'list'; - title = loadTimeData.getString('siteSettingsCategoryAllSites'); - categoryDescription = ''; - } else { - icon = this.computeIconForContentCategory(category); - title = this.computeTitleForContentCategory(category); - categoryDescription = this.computeCategoryDesc( - category, this.isCategoryAllowed(category), false); - } + addCategory_: function(category) { + var icon = this.computeIconForContentCategory(category); + var title = this.computeTitleForContentCategory(category); + var prefsProxy = settings.SiteSettingsPrefsBrowserProxy.getInstance(); + prefsProxy.getDefaultValueForContentType( + category).then(function(enabled) { + var description = this.computeCategoryDesc(category, enabled, false); + this.addCategoryImpl_(category, icon, title, description); + }.bind(this)); + }, + /** + * Constructs the actual HTML elements for the category. + * @param {number} category The category to add. + * @param {string} icon The icon to show with it. + * @param {string} title The title to show for the category. + * @param {string} defaultValue The default value (permission) for the + * category. + * @private + */ + addCategoryImpl_: function(category, icon, title, defaultValue) { var root = this.$.list; var paperIcon = document.createElement('paper-icon-item'); paperIcon.addEventListener('tap', this.onTapCategory.bind(this, category)); @@ -95,7 +113,7 @@ var setting = document.createElement('div'); setting.setAttribute('class', 'option-value'); - setting.appendChild(document.createTextNode(categoryDescription)); + setting.appendChild(document.createTextNode(defaultValue)); paperIcon.appendChild(ironIcon); paperIcon.appendChild(description);
diff --git a/chrome/browser/safe_browsing/local_database_manager.cc b/chrome/browser/safe_browsing/local_database_manager.cc index 9763d6eb..38395ff 100644 --- a/chrome/browser/safe_browsing/local_database_manager.cc +++ b/chrome/browser/safe_browsing/local_database_manager.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/safe_browsing/local_database_manager.h" #include <algorithm> +#include <limits> #include "base/bind.h" #include "base/bind_helpers.h" @@ -72,6 +73,34 @@ threat_type); } +// Returns threat level of the list. Lists with lower threat levels are more +// severe than lists with higher threat levels. Zero is the severest threat +// level possible. +int GetThreatSeverity(ListType threat) { + switch (threat) { + case MALWARE: // Falls through. + case PHISH: // Falls through. + case BINURL: // Falls through. + case CSDWHITELIST: // Falls through. + case DOWNLOADWHITELIST: // Falls through. + case INCLUSIONWHITELIST: // Falls through. + case MODULEWHITELIST: // Falls through. + case EXTENSIONBLACKLIST: // Falls through. + case IPBLACKLIST: + return 0; + case UNWANTEDURL: + // UNWANTEDURL is considered less severe than other threats. + return 1; + case RESOURCEBLACKLIST: + // RESOURCEBLACKLIST is even less severe than UNWANTEDURL. + return 2; + case INVALID: + return std::numeric_limits<int>::max(); + } + NOTREACHED(); + return -1; +} + // Return the severest list id from the results in |full_hashes| which matches // |hash|, or INVALID if none match. ListType GetHashSeverestThreatListType( @@ -79,35 +108,20 @@ const std::vector<SBFullHashResult>& full_hashes, size_t* index) { ListType pending_threat = INVALID; + int pending_threat_severity = GetThreatSeverity(INVALID); for (size_t i = 0; i < full_hashes.size(); ++i) { if (SBFullHashEqual(hash, full_hashes[i].hash)) { const ListType threat = static_cast<ListType>(full_hashes[i].list_id); - switch (threat) { - case INVALID: - // |full_hashes| should never contain INVALID as a |list_id|. - NOTREACHED(); - break; - case MALWARE: // Falls through. - case PHISH: // Falls through. - case BINURL: // Falls through. - case CSDWHITELIST: // Falls through. - case DOWNLOADWHITELIST: // Falls through. - case INCLUSIONWHITELIST: // Falls through. - case MODULEWHITELIST: // Falls through. - case EXTENSIONBLACKLIST: // Falls through. - case IPBLACKLIST: - if (index) - *index = i; - return threat; - case UNWANTEDURL: - // UNWANTEDURL is considered less severe than other threats, keep - // looking. - pending_threat = threat; - if (index) - *index = i; - break; + int threat_severity = GetThreatSeverity(threat); + if (threat_severity < pending_threat_severity) { + pending_threat = threat; + pending_threat_severity = threat_severity; + if (index) + *index = i; } + if (pending_threat_severity == 0) + return pending_threat; } } return pending_threat; @@ -127,29 +141,17 @@ GeneratePatternsToCheck(url, &patterns); ListType pending_threat = INVALID; + int pending_threat_severity = GetThreatSeverity(INVALID); for (size_t i = 0; i < patterns.size(); ++i) { ListType threat = GetHashSeverestThreatListType( SBFullHashForString(patterns[i]), full_hashes, index); - switch (threat) { - case INVALID: - // Ignore patterns with no matching threat. - break; - case MALWARE: // Falls through. - case PHISH: // Falls through. - case BINURL: // Falls through. - case CSDWHITELIST: // Falls through. - case DOWNLOADWHITELIST: // Falls through. - case INCLUSIONWHITELIST: // Falls through. - case MODULEWHITELIST: // Falls through. - case EXTENSIONBLACKLIST: // Falls through. - case IPBLACKLIST: - return threat; - case UNWANTEDURL: - // UNWANTEDURL is considered less severe than other threats, keep - // looking. - pending_threat = threat; - break; + int threat_severity = GetThreatSeverity(threat); + if (threat_severity < pending_threat_severity) { + pending_threat = threat; + pending_threat_severity = threat_severity; } + if (pending_threat_severity == 0) + return pending_threat; } return pending_threat; } @@ -166,6 +168,8 @@ return SB_THREAT_TYPE_BINARY_MALWARE_URL; case EXTENSIONBLACKLIST: return SB_THREAT_TYPE_EXTENSION; + case RESOURCEBLACKLIST: + return SB_THREAT_TYPE_BLACKLISTED_RESOURCE; default: DVLOG(1) << "Unknown safe browsing list id " << list_type; return SB_THREAT_TYPE_SAFE; @@ -200,6 +204,7 @@ : urls(urls), url_results(urls.size(), SB_THREAT_TYPE_SAFE), url_metadata(urls.size()), + url_hit_hash(urls.size()), full_hashes(full_hashes), full_hash_results(full_hashes.size(), SB_THREAT_TYPE_SAFE), client(client), @@ -235,6 +240,11 @@ client->OnCheckDownloadUrlResult( urls, *std::max_element(url_results.begin(), url_results.end())); break; + case RESOURCEBLACKLIST: + DCHECK_EQ(1u, urls.size()); + client->OnCheckResourceUrlResult(urls[0], url_results[0], + url_hit_hash[0]); + break; default: NOTREACHED(); } @@ -389,6 +399,36 @@ return false; } +bool LocalSafeBrowsingDatabaseManager::CheckResourceUrl( + const GURL& url, Client* client) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!enabled_ || !CanCheckUrl(url)) + return true; + + std::vector<SBThreatType> expected_threats = + {SB_THREAT_TYPE_BLACKLISTED_RESOURCE}; + + if (!MakeDatabaseAvailable()) { + QueuedCheck queued_check(RESOURCEBLACKLIST, client, url, + expected_threats, base::TimeTicks::Now()); + queued_checks_.push_back(queued_check); + return false; + } + + SafeBrowsingCheck* check = + new SafeBrowsingCheck({url}, std::vector<SBFullHash>(), client, + RESOURCEBLACKLIST, expected_threats); + + std::vector<SBPrefix> prefixes; + SafeBrowsingDatabase::GetDownloadUrlPrefixes(check->urls, &prefixes); + StartSafeBrowsingCheck( + check, + base::Bind(&LocalSafeBrowsingDatabaseManager::CheckResourceUrlOnSBThread, + this, prefixes)); + return false; +} + bool LocalSafeBrowsingDatabaseManager::MatchMalwareIP( const std::string& ip_address) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -1110,6 +1150,9 @@ if (threat != SB_THREAT_TYPE_SAFE) { check->url_results[i] = threat; check->url_metadata[i] = expected_full_hashes[threat_index].metadata; + const SBFullHash& hash = expected_full_hashes[threat_index].hash; + check->url_hit_hash[i] = std::string(hash.full_hash, + arraysize(hash.full_hash)); is_threat = true; } } @@ -1167,6 +1210,18 @@ return prefix_hits; } +std::vector<SBPrefix> +LocalSafeBrowsingDatabaseManager::CheckResourceUrlOnSBThread( + const std::vector<SBPrefix>& prefixes) { + DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); + + std::vector<SBPrefix> prefix_hits; + const bool result = + database_->ContainsResourceUrlPrefixes(prefixes, &prefix_hits); + DCHECK_EQ(result, !prefix_hits.empty()); + return prefix_hits; +} + void LocalSafeBrowsingDatabaseManager::TimeoutCallback( SafeBrowsingCheck* check) { DCHECK_CURRENTLY_ON(BrowserThread::IO);
diff --git a/chrome/browser/safe_browsing/local_database_manager.h b/chrome/browser/safe_browsing/local_database_manager.h index ef543e9..c1635034 100644 --- a/chrome/browser/safe_browsing/local_database_manager.h +++ b/chrome/browser/safe_browsing/local_database_manager.h
@@ -66,9 +66,13 @@ // Either |urls| or |full_hashes| is used to lookup database. |*_results| // are parallel vectors containing the results. They are initialized to // contain SB_THREAT_TYPE_SAFE. + // |url_hit_hash| and |url_metadata| are parallel vectors containing full + // hash and metadata of a database record provided the result. They are + // initialized to be empty strings. std::vector<GURL> urls; std::vector<SBThreatType> url_results; std::vector<std::string> url_metadata; + std::vector<std::string> url_hit_hash; std::vector<SBFullHash> full_hashes; std::vector<SBThreatType> full_hash_results; @@ -115,6 +119,7 @@ Client* client) override; bool CheckExtensionIDs(const std::set<std::string>& extension_ids, Client* client) override; + bool CheckResourceUrl(const GURL& url, Client* client) override; bool MatchCsdWhitelistUrl(const GURL& url) override; bool MatchMalwareIP(const std::string& ip_address) override; bool MatchDownloadWhitelistUrl(const GURL& url) override; @@ -286,6 +291,10 @@ std::vector<SBPrefix> CheckExtensionIDsOnSBThread( const std::vector<SBPrefix>& prefixes); + // Checks all resource URL hashes on |safe_browsing_task_runner_|. + std::vector<SBPrefix> CheckResourceUrlOnSBThread( + const std::vector<SBPrefix>& prefixes); + // Helper function that calls safe browsing client and cleans up |checks_|. void SafeBrowsingCheckDone(SafeBrowsingCheck* check);
diff --git a/chrome/browser/safe_browsing/local_database_manager_unittest.cc b/chrome/browser/safe_browsing/local_database_manager_unittest.cc index 5ea449d..96f3236 100644 --- a/chrome/browser/safe_browsing/local_database_manager_unittest.cc +++ b/chrome/browser/safe_browsing/local_database_manager_unittest.cc
@@ -149,6 +149,9 @@ const GURL kUnwantedUrl("http://www.unwanted.com/page.html"); const GURL kUnwantedAndMalwareUrl( "http://www.unwantedandmalware.com/page.html"); + const GURL kBlacklistedResourceUrl("http://www.blacklisted.com/script.js"); + const GURL kUnwantedResourceUrl("http://www.unwantedresource.com/script.js"); + const GURL kMalwareResourceUrl("http://www.malwareresource.com/script.js"); const GURL kSafeUrl("http://www.safe.com/page.html"); const SBFullHash kMalwareHostHash = SBFullHashForString("malware.com/"); @@ -156,6 +159,12 @@ const SBFullHash kUnwantedHostHash = SBFullHashForString("unwanted.com/"); const SBFullHash kUnwantedAndMalwareHostHash = SBFullHashForString("unwantedandmalware.com/"); + const SBFullHash kBlacklistedResourceHostHash = + SBFullHashForString("blacklisted.com/"); + const SBFullHash kUnwantedResourceHostHash = + SBFullHashForString("unwantedresource.com/"); + const SBFullHash kMalwareResourceHostHash = + SBFullHashForString("malwareresource.com/"); const SBFullHash kSafeHostHash = SBFullHashForString("www.safe.com/"); { @@ -180,6 +189,13 @@ } { + SBFullHashResult full_hash; + full_hash.hash = kBlacklistedResourceHostHash; + full_hash.list_id = static_cast<int>(RESOURCEBLACKLIST); + full_hashes.push_back(full_hash); + } + + { // Add both MALWARE and UNWANTEDURL list IDs for // kUnwantedAndMalwareHostHash. SBFullHashResult full_hash_malware; @@ -193,6 +209,26 @@ full_hashes.push_back(full_hash_unwanted); } + { + SBFullHashResult full_hash_unwanted = + {kUnwantedResourceHostHash, static_cast<int>(UNWANTEDURL)}; + full_hashes.push_back(full_hash_unwanted); + + SBFullHashResult full_hash_resource = + {kUnwantedResourceHostHash, static_cast<int>(RESOURCEBLACKLIST)}; + full_hashes.push_back(full_hash_resource); + } + + { + SBFullHashResult full_hash_malware = + {kMalwareResourceHostHash, static_cast<int>(MALWARE)}; + full_hashes.push_back(full_hash_malware); + + SBFullHashResult full_hash_resource = + {kMalwareResourceHostHash, static_cast<int>(RESOURCEBLACKLIST)}; + full_hashes.push_back(full_hash_resource); + } + EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, LocalSafeBrowsingDatabaseManager::GetHashSeverestThreatType( kMalwareHostHash, full_hashes)); @@ -209,6 +245,18 @@ LocalSafeBrowsingDatabaseManager::GetHashSeverestThreatType( kUnwantedAndMalwareHostHash, full_hashes)); + EXPECT_EQ(SB_THREAT_TYPE_BLACKLISTED_RESOURCE, + LocalSafeBrowsingDatabaseManager::GetHashSeverestThreatType( + kBlacklistedResourceHostHash, full_hashes)); + + EXPECT_EQ(SB_THREAT_TYPE_URL_UNWANTED, + LocalSafeBrowsingDatabaseManager::GetHashSeverestThreatType( + kUnwantedResourceHostHash, full_hashes)); + + EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, + LocalSafeBrowsingDatabaseManager::GetHashSeverestThreatType( + kMalwareResourceHostHash, full_hashes)); + EXPECT_EQ(SB_THREAT_TYPE_SAFE, LocalSafeBrowsingDatabaseManager::GetHashSeverestThreatType( kSafeHostHash, full_hashes)); @@ -230,10 +278,25 @@ kUnwantedUrl, full_hashes, &index)); EXPECT_EQ(2U, index); + EXPECT_EQ(SB_THREAT_TYPE_BLACKLISTED_RESOURCE, + LocalSafeBrowsingDatabaseManager::GetUrlSeverestThreatType( + kBlacklistedResourceUrl, full_hashes, &index)); + EXPECT_EQ(3U, index); + EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, LocalSafeBrowsingDatabaseManager::GetUrlSeverestThreatType( kUnwantedAndMalwareUrl, full_hashes, &index)); - EXPECT_EQ(3U, index); + EXPECT_EQ(4U, index); + + EXPECT_EQ(SB_THREAT_TYPE_URL_UNWANTED, + LocalSafeBrowsingDatabaseManager::GetUrlSeverestThreatType( + kUnwantedResourceUrl, full_hashes, &index)); + EXPECT_EQ(6U, index); + + EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, + LocalSafeBrowsingDatabaseManager::GetUrlSeverestThreatType( + kMalwareResourceUrl, full_hashes, &index)); + EXPECT_EQ(8U, index); index = kArbitraryValue; EXPECT_EQ(SB_THREAT_TYPE_SAFE,
diff --git a/chrome/browser/safe_browsing/safe_browsing_database.cc b/chrome/browser/safe_browsing/safe_browsing_database.cc index 26f1eea..cd78d76 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database.cc
@@ -72,6 +72,9 @@ FILE_PATH_LITERAL(" UwS List"); const base::FilePath::CharType kModuleWhitelistDBFile[] = FILE_PATH_LITERAL(" Module Whitelist"); +// Filename suffix for the resource blacklist store. +const base::FilePath::CharType kResourceBlacklistDBFile[] = + FILE_PATH_LITERAL(" Resource Blacklist"); // Filename suffix for browse store. // TODO(shess): "Safe Browsing Bloom Prefix Set" is full of win. @@ -293,7 +296,8 @@ CreateStore(enable_extension_blacklist, db_task_runner), CreateStore(enable_ip_blacklist, db_task_runner), CreateStore(enable_unwanted_software_list, db_task_runner), - CreateStore(enable_module_whitelist, db_task_runner)); + CreateStore(enable_module_whitelist, db_task_runner), + CreateStore(true, db_task_runner)); // resource_blacklist_store } SafeBrowsingDatabaseFactoryImpl() {} @@ -404,6 +408,12 @@ } // static +base::FilePath SafeBrowsingDatabase::ResourceBlacklistDBFilename( + const base::FilePath& db_filename) { + return base::FilePath(db_filename.value() + kResourceBlacklistDBFile); +} + +// static void SafeBrowsingDatabase::GetDownloadUrlPrefixes( const std::vector<GURL>& urls, std::vector<SBPrefix>* prefixes) { @@ -437,6 +447,8 @@ return unwanted_software_store_.get(); } else if (list_id == MODULEWHITELIST) { return module_whitelist_store_.get(); + } else if (list_id == RESOURCEBLACKLIST) { + return resource_blacklist_store_.get(); } return NULL; } @@ -624,7 +636,8 @@ SafeBrowsingStore* extension_blacklist_store, SafeBrowsingStore* ip_blacklist_store, SafeBrowsingStore* unwanted_software_store, - SafeBrowsingStore* module_whitelist_store) + SafeBrowsingStore* module_whitelist_store, + SafeBrowsingStore* resource_blacklist_store) : db_task_runner_(db_task_runner), state_manager_(db_task_runner_), db_state_manager_(db_task_runner_), @@ -637,6 +650,7 @@ ip_blacklist_store_(ip_blacklist_store), unwanted_software_store_(unwanted_software_store), module_whitelist_store_(module_whitelist_store), + resource_blacklist_store_(resource_blacklist_store), reset_factory_(this) { DCHECK(browse_store_.get()); } @@ -801,6 +815,13 @@ state_manager_.BeginWriteTransaction()->WhitelistEverything( SBWhitelistId::MODULE); // Just to be safe. } + + if (resource_blacklist_store_.get()) { + resource_blacklist_store_->Init( + ResourceBlacklistDBFilename(db_state_manager_.filename_base()), + base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase, + base::Unretained(this))); + } } bool SafeBrowsingDatabaseNew::ResetDatabase() { @@ -983,6 +1004,18 @@ return false; } +bool SafeBrowsingDatabaseNew::ContainsResourceUrlPrefixes( + const std::vector<SBPrefix>& prefixes, + std::vector<SBPrefix>* prefix_hits) { + DCHECK(db_task_runner_->RunsTasksOnCurrentThread()); + + if (!resource_blacklist_store_) + return false; + + return MatchAddPrefixes(resource_blacklist_store_.get(), + RESOURCEBLACKLIST % 2, prefixes, prefix_hits); +} + bool SafeBrowsingDatabaseNew::ContainsDownloadWhitelistedString( const std::string& str) { std::vector<SBFullHash> hashes; @@ -1227,6 +1260,12 @@ return false; } + if (resource_blacklist_store_ && !resource_blacklist_store_->BeginUpdate()) { + RecordFailure(FAILURE_RESOURCE_BLACKLIST_UPDATE_BEGIN); + HandleCorruptDatabase(); + return false; + } + // Cached fullhash results must be cleared on every database update (whether // successful or not). state_manager_.BeginWriteTransaction()->clear_prefix_gethash_cache(); @@ -1259,6 +1298,9 @@ UpdateChunkRangesForList(module_whitelist_store_.get(), kModuleWhitelist, lists); + UpdateChunkRangesForList(resource_blacklist_store_.get(), kResourceBlacklist, + lists); + db_state_manager_.reset_corruption_detected(); db_state_manager_.reset_change_detected(); return true; @@ -1310,6 +1352,11 @@ if (module_whitelist_store_ && !module_whitelist_store_->CheckValidity()) { DLOG(ERROR) << "Module digest whitelist database corrupt."; } + + if (resource_blacklist_store_ && + !resource_blacklist_store_->CheckValidity()) { + DLOG(ERROR) << "Resources blacklist url list database corrupt."; + } } if (db_state_manager_.corruption_detected()) @@ -1339,6 +1386,8 @@ unwanted_software_store_->CancelUpdate(); if (module_whitelist_store_) module_whitelist_store_->CancelUpdate(); + if (resource_blacklist_store_) + resource_blacklist_store_->CancelUpdate(); return; } @@ -1386,6 +1435,13 @@ ModuleWhitelistDBFilename(db_state_manager_.filename_base()), module_whitelist_store_.get(), SBWhitelistId::MODULE); } + + if (resource_blacklist_store_) { + UpdateHashPrefixStore( + ResourceBlacklistDBFilename(db_state_manager_.filename_base()), + resource_blacklist_store_.get(), + FAILURE_RESOURCE_BLACKLIST_UPDATE_FINISH); + } } void SafeBrowsingDatabaseNew::UpdateWhitelistStore( @@ -1669,7 +1725,12 @@ if (!r10) RecordFailure(FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_DELETE); - return r1 && r2 && r3 && r4 && r5 && r6 && r7 && r8 && r9 && r10; + const bool r11 = base::DeleteFile( + ResourceBlacklistDBFilename(db_state_manager_.filename_base()), false); + if (!r11) + RecordFailure(FAILURE_RESOURCE_BLACKLIST_DELETE); + + return r1 && r2 && r3 && r4 && r5 && r6 && r7 && r8 && r9 && r10 && r11; } void SafeBrowsingDatabaseNew::WritePrefixSet(const base::FilePath& db_filename, @@ -1845,6 +1906,9 @@ else if (base::EndsWith(filename, kModuleWhitelistDBFile, base::CompareCase::SENSITIVE)) histogram_name.append(".ModuleWhitelist"); + else if (base::EndsWith(filename, kResourceBlacklistDBFile, + base::CompareCase::SENSITIVE)) + histogram_name.append(".ResourceBlacklist"); else NOTREACHED(); // Add support for new lists above.
diff --git a/chrome/browser/safe_browsing/safe_browsing_database.h b/chrome/browser/safe_browsing/safe_browsing_database.h index 719241f..eb7d0d3 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.h +++ b/chrome/browser/safe_browsing/safe_browsing_database.h
@@ -175,6 +175,14 @@ // This function is safe to call from any thread. virtual bool ContainsMalwareIP(const std::string& ip_address) = 0; + // Populates |prefix_hits| with any prefixes in |prefixes| that have matches + // in the database. Returns true iff there were any matches. + // + // This function can ONLY by accessed from the creation thread. + virtual bool ContainsResourceUrlPrefixes( + const std::vector<SBPrefix>& prefixes, + std::vector<SBPrefix>* prefix_hits) = 0; + // A database transaction should look like: // // std::vector<SBListChunkRanges> lists; @@ -270,6 +278,9 @@ static base::FilePath ModuleWhitelistDBFilename( const base::FilePath& db_filename); + static base::FilePath ResourceBlacklistDBFilename( + const base::FilePath& db_filename); + // Get the prefixes matching the download |urls|. static void GetDownloadUrlPrefixes(const std::vector<GURL>& urls, std::vector<SBPrefix>* prefixes); @@ -313,6 +324,9 @@ FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_READ = 32, FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_WRITE = 33, FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_DELETE = 34, + FAILURE_RESOURCE_BLACKLIST_UPDATE_BEGIN = 35, + FAILURE_RESOURCE_BLACKLIST_UPDATE_FINISH = 36, + FAILURE_RESOURCE_BLACKLIST_DELETE = 37, // Memory space for histograms is determined by the max. ALWAYS // ADD NEW VALUES BEFORE THIS ONE. @@ -343,7 +357,8 @@ SafeBrowsingStore* extension_blacklist_store, SafeBrowsingStore* ip_blacklist_store, SafeBrowsingStore* unwanted_software_store, - SafeBrowsingStore* module_whitelist_store); + SafeBrowsingStore* module_whitelist_store, + SafeBrowsingStore* resource_blacklist_store); ~SafeBrowsingDatabaseNew() override; @@ -374,6 +389,9 @@ bool ContainsExtensionPrefixes(const std::vector<SBPrefix>& prefixes, std::vector<SBPrefix>* prefix_hits) override; bool ContainsMalwareIP(const std::string& ip_address) override; + bool ContainsResourceUrlPrefixes(const std::vector<SBPrefix>& prefixes, + std::vector<SBPrefix>* prefix_hits) override; + bool UpdateStarted(std::vector<SBListChunkRanges>* lists) override; void InsertChunks( const std::string& list_name, @@ -696,6 +714,8 @@ // identical to browsing lists). // - |module_whitelist_store_|: For module whitelist. This list only // contains 256 bit hashes. + // - |resource_blacklist_store_|: For script resource list (format identical + // to browsing lists). // // The stores themselves will be modified throughout the existence of this // database, but shouldn't ever be swapped out (hence the const scoped_ptr -- @@ -711,6 +731,7 @@ const scoped_ptr<SafeBrowsingStore> ip_blacklist_store_; const scoped_ptr<SafeBrowsingStore> unwanted_software_store_; const scoped_ptr<SafeBrowsingStore> module_whitelist_store_; + const scoped_ptr<SafeBrowsingStore> resource_blacklist_store_; // Used to schedule resetting the database because of corruption. This factory // and the WeakPtrs it issues should only be used on the database's main
diff --git a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc index b405ad10..6d27a1f 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc
@@ -22,6 +22,7 @@ #include "chrome/browser/safe_browsing/safe_browsing_store_file.h" #include "crypto/sha2.h" #include "net/base/ip_address_number.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" #include "url/gurl.h" @@ -282,11 +283,13 @@ new SafeBrowsingStoreFile(task_runner_); SafeBrowsingStoreFile* module_whitelist_store = new SafeBrowsingStoreFile(task_runner_); + SafeBrowsingStoreFile* resource_blacklist_store = + new SafeBrowsingStoreFile(task_runner_); database_.reset(new SafeBrowsingDatabaseNew( task_runner_, browse_store, download_store, csd_whitelist_store, download_whitelist_store, inclusion_whitelist_store, extension_blacklist_store, ip_blacklist_store, unwanted_software_store, - module_whitelist_store)); + module_whitelist_store, resource_blacklist_store)); database_->Init(database_filename_); } @@ -297,6 +300,16 @@ return database_->ContainsDownloadUrlPrefixes(prefixes, prefix_hits); } + bool ContainsResourceUrl(const GURL& url, + std::vector<SBPrefix>* prefix_hits) { + std::vector<SBFullHash> full_hashes; + UrlToFullHashes(url, false, &full_hashes); + std::vector<SBPrefix> prefixes(full_hashes.size()); + for (size_t i = 0; i < full_hashes.size(); ++i) + prefixes[i] = full_hashes[i].prefix; + return database_->ContainsResourceUrlPrefixes(prefixes, prefix_hits); + } + void GetListsInfo(std::vector<SBListChunkRanges>* lists) { lists->clear(); ASSERT_TRUE(database_->UpdateStarted(lists)); @@ -446,10 +459,14 @@ chunks.push_back(AddChunkPrefixValue(12, "chrome.dll")); database_->InsertChunks(kModuleWhitelist, chunks); + chunks.clear(); + chunks.push_back(AddChunkPrefixValue(13, "foo.com/script.js")); + database_->InsertChunks(kResourceBlacklist, chunks); + database_->UpdateFinished(true); GetListsInfo(&lists); - ASSERT_EQ(10U, lists.size()); + ASSERT_EQ(11U, lists.size()); EXPECT_EQ(kMalwareList, lists[0].name); EXPECT_EQ("1", lists[0].adds); EXPECT_TRUE(lists[0].subs.empty()); @@ -480,6 +497,9 @@ EXPECT_EQ(kModuleWhitelist, lists[9].name); EXPECT_EQ("12", lists[9].adds); EXPECT_TRUE(lists[9].subs.empty()); + EXPECT_EQ(kResourceBlacklist, lists[10].name); + EXPECT_EQ("13", lists[10].adds); + EXPECT_TRUE(lists[10].subs.empty()); database_.reset(); } @@ -1162,7 +1182,8 @@ base::MessageLoop loop; SafeBrowsingStoreFile* store = new SafeBrowsingStoreFile(task_runner_); database_.reset(new SafeBrowsingDatabaseNew( - task_runner_, store, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)); + task_runner_, store, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL)); database_->Init(database_filename_); // This will cause an empty database to be created. @@ -1303,6 +1324,45 @@ database_.reset(); } +TEST_F(SafeBrowsingDatabaseTest, ContainsResourceUrlPrefixes) { + const char* kBadUrl1 = "bad1.com/"; + const char* kBadUrl2 = "bad2.com/script.js"; + const SBPrefix kBadPrefix1 = SBPrefixForString(kBadUrl1); + const SBPrefix kBadPrefix2 = SBPrefixForString(kBadUrl2); + + // Populate database + std::vector<scoped_ptr<SBChunkData>> chunks; + chunks.push_back(AddChunkPrefix2Value(1, kBadUrl1, kBadUrl2)); + + std::vector<SBListChunkRanges> lists; + ASSERT_TRUE(database_->UpdateStarted(&lists)); + database_->InsertChunks(kResourceBlacklist, chunks); + database_->UpdateFinished(true); + + struct { + std::string url; + bool found_in_db; + std::vector<SBPrefix> prefix_hits; + } test_cases[] = { + {std::string("http://") + kBadUrl1, true, {kBadPrefix1}}, + {std::string("https://") + kBadUrl2, true, {kBadPrefix2}}, + {std::string("ftp://") + kBadUrl1, true, {kBadPrefix1}}, + {std::string("http://") + kBadUrl1 + "a/b/?arg=value", true, {kBadPrefix1}}, + {std::string("http://") + kBadUrl1 + "script.js", true, {kBadPrefix1}}, + {std::string("http://www.domain.") + kBadUrl2, true, {kBadPrefix2}}, + {"http://www.good.org/script.js", false, std::vector<SBPrefix>()}, + }; + + std::vector<SBPrefix> prefix_hits; + for (const auto& test_case : test_cases) { + EXPECT_EQ(test_case.found_in_db, + ContainsResourceUrl(GURL(test_case.url), &prefix_hits)); + EXPECT_THAT(prefix_hits, testing::ElementsAreArray(test_case.prefix_hits)); + } + + database_.reset(); +} + // Checks that the whitelists are handled properly. TEST_F(SafeBrowsingDatabaseTest, Whitelists) { struct TestCase { @@ -1343,7 +1403,7 @@ // If the whitelist is disabled everything should match the whitelist. database_.reset(new SafeBrowsingDatabaseNew( task_runner_, new SafeBrowsingStoreFile(task_runner_), NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL)); + NULL, NULL, NULL, NULL, NULL, NULL)); database_->Init(database_filename_); for (const auto& test_case : kTestCases) { SCOPED_TRACE(std::string("Tested list at fault => ") +
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc index 73f66552..8807d149 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -263,6 +263,13 @@ bool ContainsMalwareIP(const std::string& ip_address) override { return true; } + bool ContainsResourceUrlPrefixes( + const std::vector<SBPrefix>& prefixes, + std::vector<SBPrefix>* prefix_hits) override { + prefix_hits->clear(); + return ContainsUrlPrefixes(RESOURCEBLACKLIST, RESOURCEBLACKLIST, + prefixes, prefix_hits); + } bool UpdateStarted(std::vector<SBListChunkRanges>* lists) override { ADD_FAILURE() << "Not implemented."; return false; @@ -1141,6 +1148,8 @@ SBThreatType GetThreatType() const { return threat_type_; } + std::string GetThreatHash() const { return threat_hash_; } + void CheckDownloadUrl(const std::vector<GURL>& url_chain) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, @@ -1155,6 +1164,13 @@ content::RunMessageLoop(); // Will stop in OnCheckBrowseUrlResult. } + void CheckResourceUrl(const GURL& url) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&TestSBClient::CheckResourceUrlOnIOThread, this, url)); + content::RunMessageLoop(); // Will stop in OnCheckResourceUrlResult. + } + private: friend class base::RefCountedThreadSafe<TestSBClient>; ~TestSBClient() override {} @@ -1182,6 +1198,16 @@ } } + void CheckResourceUrlOnIOThread(const GURL& url) { + bool synchronous_safe_signal = + safe_browsing_service_->database_manager()->CheckResourceUrl(url, this); + if (synchronous_safe_signal) { + threat_type_ = SB_THREAT_TYPE_SAFE; + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TestSBClient::CheckDone, this)); + } + } + // Called when the result of checking a download URL is known. void OnCheckDownloadUrlResult(const std::vector<GURL>& /* url_chain */, SBThreatType threat_type) override { @@ -1199,9 +1225,20 @@ base::Bind(&TestSBClient::CheckDone, this)); } + // Called when the result of checking a resource URL is known. + void OnCheckResourceUrlResult(const GURL& /* url */, + SBThreatType threat_type, + const std::string& threat_hash) override { + threat_type_ = threat_type; + threat_hash_ = threat_hash; + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&TestSBClient::CheckDone, this)); + } + void CheckDone() { base::MessageLoopForUI::current()->QuitWhenIdle(); } SBThreatType threat_type_; + std::string threat_hash_; SafeBrowsingService* safe_browsing_service_; DISALLOW_COPY_AND_ASSIGN(TestSBClient); @@ -1337,6 +1374,47 @@ EXPECT_EQ(SB_THREAT_TYPE_BINARY_MALWARE_URL, client->GetThreatType()); } +IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest, CheckResourceUrl) { + const char* kBlacklistResource = "/blacklisted/script.js"; + GURL blacklist_resource = embedded_test_server()->GetURL(kBlacklistResource); + std::string blacklist_resource_hash; + const char* kMaliciousResource = "/malware/script.js"; + GURL malware_resource = embedded_test_server()->GetURL(kMaliciousResource); + std::string malware_resource_hash; + + { + SBFullHashResult full_hash; + GenUrlFullhashResult(blacklist_resource, RESOURCEBLACKLIST, &full_hash); + SetupResponseForUrl(blacklist_resource, full_hash); + blacklist_resource_hash = std::string(full_hash.hash.full_hash, + full_hash.hash.full_hash + 32); + } + { + SBFullHashResult full_hash; + GenUrlFullhashResult(malware_resource, MALWARE, &full_hash); + SetupResponseForUrl(malware_resource, full_hash); + full_hash.list_id = RESOURCEBLACKLIST; + SetupResponseForUrl(malware_resource, full_hash); + malware_resource_hash = std::string(full_hash.hash.full_hash, + full_hash.hash.full_hash + 32); + } + + scoped_refptr<TestSBClient> client(new TestSBClient); + client->CheckResourceUrl(blacklist_resource); + EXPECT_EQ(SB_THREAT_TYPE_BLACKLISTED_RESOURCE, client->GetThreatType()); + EXPECT_EQ(blacklist_resource_hash, client->GetThreatHash()); + + // Since we're checking a resource url, we should receive result that it's + // a blacklisted resource, not a malware. + client = new TestSBClient; + client->CheckResourceUrl(malware_resource); + EXPECT_EQ(SB_THREAT_TYPE_BLACKLISTED_RESOURCE, client->GetThreatType()); + EXPECT_EQ(malware_resource_hash, client->GetThreatHash()); + + client->CheckResourceUrl(embedded_test_server()->GetURL(kEmptyPage)); + EXPECT_EQ(SB_THREAT_TYPE_SAFE, client->GetThreatType()); +} + #if defined(OS_WIN) // http://crbug.com/396409 #define MAYBE_CheckDownloadUrlTimedOut DISABLED_CheckDownloadUrlTimedOut
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc index bf6441b..7b73134 100644 --- a/chrome/browser/search/local_ntp_source.cc +++ b/chrome/browser/search/local_ntp_source.cc
@@ -30,6 +30,7 @@ #include "chrome/grit/generated_resources.h" #include "components/search_engines/template_url_prepopulate_data.h" #include "components/search_engines/template_url_service.h" +#include "components/strings/grit/components_strings.h" #include "grit/browser_resources.h" #include "grit/theme_resources.h" #include "net/url_request/url_request.h"
diff --git a/chrome/browser/signin/oauth2_token_service_delegate_android.cc b/chrome/browser/signin/oauth2_token_service_delegate_android.cc index fac9e00..5397c93a 100644 --- a/chrome/browser/signin/oauth2_token_service_delegate_android.cc +++ b/chrome/browser/signin/oauth2_token_service_delegate_android.cc
@@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/macros.h" +#include "base/metrics/histogram_macros.h" #include "chrome/browser/profiles/profile_android.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/sync/profile_sync_service_android.h" @@ -478,13 +479,21 @@ DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked id=" << account_id; std::string account_name = MapAccountIdToAccountName(account_id); - // TODO(knn): Convert to DCHECK after https://crbug.com/535211 - CHECK(!account_name.empty()); - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> j_account_name = - ConvertUTF8ToJavaString(env, account_name); - Java_OAuth2TokenService_notifyRefreshTokenRevoked(env, java_ref_.obj(), - j_account_name.obj()); + if (!account_name.empty()) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> j_account_name = + ConvertUTF8ToJavaString(env, account_name); + Java_OAuth2TokenService_notifyRefreshTokenRevoked(env, java_ref_.obj(), + j_account_name.obj()); + } else { + // Current prognosis is that we have an unmigrated account which is due for + // deletion. Record a histogram to debug this. + UMA_HISTOGRAM_ENUMERATION("OAuth2Login.AccountRevoked.MigrationState", + account_tracker_service_->GetMigrationState(), + AccountTrackerService::NUM_MIGRATION_STATES); + bool is_email_id = account_id.find('@') != std::string::npos; + UMA_HISTOGRAM_BOOLEAN("OAuth2Login.AccountRevoked.IsEmailId", is_email_id); + } OAuth2TokenServiceDelegate::FireRefreshTokenRevoked(account_id); }
diff --git a/chrome/browser/spellchecker/feedback_sender.cc b/chrome/browser/spellchecker/feedback_sender.cc index 27be020..b52c89d8 100644 --- a/chrome/browser/spellchecker/feedback_sender.cc +++ b/chrome/browser/spellchecker/feedback_sender.cc
@@ -50,6 +50,9 @@ #include "chrome/common/spellcheck_messages.h" #include "components/data_use_measurement/core/data_use_user_data.h" #include "content/public/browser/render_process_host.h" +#include "crypto/random.h" +#include "crypto/secure_hash.h" +#include "crypto/sha2.h" #include "google_apis/google_api_keys.h" #include "net/base/load_flags.h" #include "net/url_request/url_fetcher.h" @@ -77,6 +80,19 @@ suggestion_index)); } +uint64_t BuildAnonymousHash(const FeedbackSender::RandSalt& r, + const base::string16& s) { + scoped_ptr<crypto::SecureHash> hash( + crypto::SecureHash::Create(crypto::SecureHash::SHA256)); + + hash->Update(s.data(), s.size() * sizeof(s[0])); + hash->Update(&r, sizeof(r)); + + uint64_t result; + hash->Finish(&result, sizeof(result)); + return result; +} + // Returns a pending feedback data structure for the spellcheck |result| and // |text|. Misspelling BuildFeedback(const SpellCheckResult& result, @@ -96,13 +112,28 @@ // Builds suggestion info from |suggestions|. scoped_ptr<base::ListValue> BuildSuggestionInfo( const std::vector<Misspelling>& misspellings, - bool is_first_feedback_batch) { + bool is_first_feedback_batch, + const FeedbackSender::RandSalt& salt) { scoped_ptr<base::ListValue> list(new base::ListValue); for (const auto& raw_misspelling : misspellings) { scoped_ptr<base::DictionaryValue> misspelling( SerializeMisspelling(raw_misspelling)); misspelling->SetBoolean("isFirstInSession", is_first_feedback_batch); misspelling->SetBoolean("isAutoCorrection", false); + // hash(R) fields come from red_underline_extensions.proto + // fixed64 user_misspelling_id = ... + misspelling->SetString( + "userMisspellingId", + base::Uint64ToString(BuildAnonymousHash( + salt, raw_misspelling.context.substr(raw_misspelling.location, + raw_misspelling.length)))); + // repeated fixed64 user_suggestion_id = ... + scoped_ptr<base::ListValue> suggestion_list(new base::ListValue()); + for (const auto& suggestion : raw_misspelling.suggestions) { + suggestion_list->AppendString( + base::Uint64ToString(BuildAnonymousHash(salt, suggestion))); + } + misspelling->Set("userSuggestionId", suggestion_list.release()); list->Append(misspelling.release()); } return list; @@ -342,6 +373,10 @@ timer_.Stop(); } +void FeedbackSender::RandBytes(void* p, size_t len) { + crypto::RandBytes(p, len); +} + void FeedbackSender::OnURLFetchComplete(const net::URLFetcher* source) { for (ScopedVector<net::URLFetcher>::iterator sender_it = senders_.begin(); sender_it != senders_.end(); @@ -394,10 +429,14 @@ void FeedbackSender::SendFeedback(const std::vector<Misspelling>& feedback_data, bool is_first_feedback_batch) { + if (base::Time::Now() - last_salt_update_ > base::TimeDelta::FromHours(24)) { + RandBytes(&salt_, sizeof(salt_)); + last_salt_update_ = base::Time::Now(); + } scoped_ptr<base::Value> feedback_value(BuildFeedbackValue( - BuildParams(BuildSuggestionInfo(feedback_data, is_first_feedback_batch), - language_, - country_), + BuildParams( + BuildSuggestionInfo(feedback_data, is_first_feedback_batch, salt_), + language_, country_), api_version_)); std::string feedback; base::JSONWriter::Write(*feedback_value, &feedback);
diff --git a/chrome/browser/spellchecker/feedback_sender.h b/chrome/browser/spellchecker/feedback_sender.h index c98d5ba..42d6aac 100644 --- a/chrome/browser/spellchecker/feedback_sender.h +++ b/chrome/browser/spellchecker/feedback_sender.h
@@ -16,6 +16,8 @@ #include <stddef.h> #include <stdint.h> +#include <array> +#include <climits> #include <map> #include <set> #include <vector> @@ -115,9 +117,15 @@ // it was being collected. void StopFeedbackCollection(); + // 256 bits + typedef std::array<char, 256 / CHAR_BIT> RandSalt; + private: friend class FeedbackSenderTest; + // Allow unit tests to override RNG. + virtual void RandBytes(void* p, size_t len); + // net::URLFetcherDelegate implementation. Takes ownership of |source|. void OnURLFetchComplete(const net::URLFetcher* source) override; @@ -160,6 +168,12 @@ // When the session started. base::Time session_start_; + // The last time that we updated |salt_|. + base::Time last_salt_update_; + + // A random number updated once a day. + RandSalt salt_; + // The URL where the feedback data should be sent. GURL feedback_service_url_;
diff --git a/chrome/browser/spellchecker/feedback_sender_unittest.cc b/chrome/browser/spellchecker/feedback_sender_unittest.cc index b3a54c6..8ff6b664 100644 --- a/chrome/browser/spellchecker/feedback_sender_unittest.cc +++ b/chrome/browser/spellchecker/feedback_sender_unittest.cc
@@ -59,13 +59,46 @@ return number_of_occurrences; } +class MockFeedbackSender : public spellcheck::FeedbackSender { + public: + MockFeedbackSender(net::URLRequestContextGetter* request_context, + const std::string& language, + const std::string& country) + : FeedbackSender(request_context, language, country), random_(0) {} + + void RandBytes(void* p, size_t len) override { + memset(p, 0, len); + if (len >= sizeof(random_)) + *(unsigned*)p = ++random_; + } + + private: + // For returning a different value from each call to RandUint64(). + unsigned random_; +}; + +std::string GetMisspellingId(const std::string& raw_data) { + scoped_ptr<base::Value> parsed_data( + base::JSONReader::Read(raw_data).release()); + EXPECT_TRUE(parsed_data.get()); + base::DictionaryValue* actual_data; + EXPECT_TRUE(parsed_data->GetAsDictionary(&actual_data)); + base::ListValue* suggestions = NULL; + EXPECT_TRUE(actual_data->GetList("params.suggestionInfo", &suggestions)); + base::DictionaryValue* suggestion = NULL; + EXPECT_TRUE(suggestions->GetDictionary(0, &suggestion)); + std::string value; + EXPECT_TRUE(suggestion->GetString("userMisspellingId", &value)); + return value; +} + } // namespace // A test fixture to help keep tests simple. class FeedbackSenderTest : public testing::Test { public: FeedbackSenderTest() : ui_thread_(content::BrowserThread::UI, &loop_) { - feedback_.reset(new FeedbackSender(nullptr, kLanguage, kCountry)); + feedback_.reset(new MockFeedbackSender(NULL, kLanguage, kCountry)); feedback_->StartFeedbackCollection(); } @@ -79,7 +112,7 @@ // TODO(rouslan): Remove the command-line switch. http://crbug.com/247726 base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableSpellingFeedbackFieldTrial); - feedback_.reset(new FeedbackSender(nullptr, kLanguage, kCountry)); + feedback_.reset(new MockFeedbackSender(NULL, kLanguage, kCountry)); feedback_->StartFeedbackCollection(); } @@ -92,7 +125,7 @@ field_trial_ = base::FieldTrialList::CreateFieldTrial( kFeedbackFieldTrialName, kFeedbackFieldTrialEnabledGroupName); field_trial_->group(); - feedback_.reset(new FeedbackSender(nullptr, kLanguage, kCountry)); + feedback_.reset(new MockFeedbackSender(NULL, kLanguage, kCountry)); feedback_->StartFeedbackCollection(); } @@ -141,7 +174,11 @@ return fetcher ? fetcher->upload_data() : std::string(); } - scoped_ptr<spellcheck::FeedbackSender> feedback_; + void AdjustUpdateTime(base::TimeDelta offset) { + feedback_->last_salt_update_ += offset; + } + + scoped_ptr<MockFeedbackSender> feedback_; private: base::MessageLoop loop_; @@ -179,6 +216,48 @@ EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\"")); } +TEST_F(FeedbackSenderTest, IdenticalFeedback) { + std::vector<uint32_t> hashes; + hashes.push_back(AddPendingFeedback()); + hashes.push_back(AddPendingFeedback()); + feedback_->OnReceiveDocumentMarkers(kRendererProcessId, hashes); + std::string actual_data = GetUploadData(); + scoped_ptr<base::DictionaryValue> actual(static_cast<base::DictionaryValue*>( + base::JSONReader::Read(GetUploadData()).release())); + base::ListValue* suggestions = NULL; + ASSERT_TRUE(actual->GetList("params.suggestionInfo", &suggestions)); + base::DictionaryValue* suggestion0 = NULL; + ASSERT_TRUE(suggestions->GetDictionary(0, &suggestion0)); + base::DictionaryValue* suggestion1 = NULL; + ASSERT_TRUE(suggestions->GetDictionary(0, &suggestion1)); + std::string value0, value1; + ASSERT_TRUE(suggestion0->GetString("userMisspellingId", &value0)); + ASSERT_TRUE(suggestion1->GetString("userMisspellingId", &value1)); + EXPECT_EQ(value0, value1); + base::ListValue* suggestion_ids = NULL; + ASSERT_TRUE(suggestion0->GetList("userSuggestionId", &suggestion_ids)); + ASSERT_TRUE(suggestion_ids->GetString(0, &value0)); + ASSERT_TRUE(suggestion1->GetList("userSuggestionId", &suggestion_ids)); + ASSERT_TRUE(suggestion_ids->GetString(0, &value1)); + EXPECT_EQ(value0, value1); +} + +TEST_F(FeedbackSenderTest, NonidenticalFeedback) { + std::vector<uint32_t> hashes; + hashes.push_back(AddPendingFeedback()); + feedback_->OnReceiveDocumentMarkers(kRendererProcessId, hashes); + std::string raw_data0 = GetUploadData(); + hashes.clear(); + hashes.push_back(AddPendingFeedback()); + AdjustUpdateTime(-base::TimeDelta::FromHours(25)); + feedback_->OnReceiveDocumentMarkers(kRendererProcessId, hashes); + std::string raw_data1 = GetUploadData(); + + std::string value0(GetMisspellingId(raw_data0)); + std::string value1(GetMisspellingId(raw_data1)); + EXPECT_NE(value0, value1); +} + // Send NO_ACTION feedback message if the marker has been removed from the // document. TEST_F(FeedbackSenderTest, NoActionFeedback) { @@ -446,7 +525,9 @@ "\"suggestionId\":\"42\"," "\"suggestions\":[\"Hello\"]," "\"timestamp\":\"9001\"," - "\"userActions\":[{\"actionType\":\"NO_ACTION\"}]}]}}"; + "\"userActions\":[{\"actionType\":\"NO_ACTION\"}]," + "\"userMisspellingId\":\"14573599553589145012\"," + "\"userSuggestionId\":[\"14761077877524043800\"]}]}}"; scoped_ptr<base::Value> expected = base::JSONReader::Read(expected_data); EXPECT_TRUE(expected->Equals(actual.get())) << "Expected data: " << expected_data
diff --git a/chrome/browser/sync/abstract_profile_sync_service_test.cc b/chrome/browser/sync/abstract_profile_sync_service_test.cc deleted file mode 100644 index 3089b52b..0000000 --- a/chrome/browser/sync/abstract_profile_sync_service_test.cc +++ /dev/null
@@ -1,92 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/sync/abstract_profile_sync_service_test.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/location.h" -#include "base/run_loop.h" -#include "chrome/browser/sync/test_profile_sync_service.h" -#include "content/public/test/test_utils.h" -#include "sync/internal_api/public/test/test_user_share.h" -#include "sync/internal_api/public/write_transaction.h" -#include "sync/protocol/sync.pb.h" -#include "sync/util/cryptographer.h" - -using syncer::ModelType; -using syncer::UserShare; - -/* static */ -syncer::ImmutableChangeRecordList -ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList( - int64_t node_id, - syncer::ChangeRecord::Action action) { - syncer::ChangeRecord record; - record.action = action; - record.id = node_id; - syncer::ChangeRecordList records(1, record); - return syncer::ImmutableChangeRecordList(&records); -} - -/* static */ -syncer::ImmutableChangeRecordList -ProfileSyncServiceTestHelper::MakeSingletonDeletionChangeRecordList( - int64_t node_id, - const sync_pb::EntitySpecifics& specifics) { - syncer::ChangeRecord record; - record.action = syncer::ChangeRecord::ACTION_DELETE; - record.id = node_id; - record.specifics = specifics; - syncer::ChangeRecordList records(1, record); - return syncer::ImmutableChangeRecordList(&records); -} - -AbstractProfileSyncServiceTest::AbstractProfileSyncServiceTest() - // Purposefully do not use a real FILE thread, see crbug/550013. - : thread_bundle_(content::TestBrowserThreadBundle::REAL_DB_THREAD | - content::TestBrowserThreadBundle::REAL_IO_THREAD), - sync_service_(NULL) {} - -AbstractProfileSyncServiceTest::~AbstractProfileSyncServiceTest() {} - -void AbstractProfileSyncServiceTest::SetUp() { -} - -void AbstractProfileSyncServiceTest::TearDown() { - // Pump messages posted by the sync core thread (which may end up - // posting on the IO thread). - base::RunLoop().RunUntilIdle(); - content::RunAllPendingInMessageLoop(content::BrowserThread::IO); - base::RunLoop().RunUntilIdle(); -} - -bool AbstractProfileSyncServiceTest::CreateRoot(ModelType model_type) { - return syncer::TestUserShare::CreateRoot(model_type, - sync_service_->GetUserShare()); -} - -CreateRootHelper::CreateRootHelper(AbstractProfileSyncServiceTest* test, - ModelType model_type) - : callback_(base::Bind(&CreateRootHelper::CreateRootCallback, - base::Unretained(this))), - test_(test), - model_type_(model_type), - success_(false) { -} - -CreateRootHelper::~CreateRootHelper() { -} - -const base::Closure& CreateRootHelper::callback() const { - return callback_; -} - -bool CreateRootHelper::success() { - return success_; -} - -void CreateRootHelper::CreateRootCallback() { - success_ = test_->CreateRoot(model_type_); -}
diff --git a/chrome/browser/sync/abstract_profile_sync_service_test.h b/chrome/browser/sync/abstract_profile_sync_service_test.h deleted file mode 100644 index d6131b7..0000000 --- a/chrome/browser/sync/abstract_profile_sync_service_test.h +++ /dev/null
@@ -1,76 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_ -#define CHROME_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_ - -#include <stdint.h> - -#include <string> - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "components/sync_driver/sync_api_component_factory_mock.h" -#include "content/public/test/test_browser_thread_bundle.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/internal_api/public/change_record.h" -#include "testing/gtest/include/gtest/gtest.h" - -class ProfileSyncService; -class TestProfileSyncService; - -namespace syncer { -struct UserShare; -} // namespace syncer - -class ProfileSyncServiceTestHelper { - public: - static syncer::ImmutableChangeRecordList MakeSingletonChangeRecordList( - int64_t node_id, - syncer::ChangeRecord::Action action); - - // Deletions must provide an EntitySpecifics for the deleted data. - static syncer::ImmutableChangeRecordList - MakeSingletonDeletionChangeRecordList( - int64_t node_id, - const sync_pb::EntitySpecifics& specifics); -}; - -class AbstractProfileSyncServiceTest : public testing::Test { - public: - AbstractProfileSyncServiceTest(); - ~AbstractProfileSyncServiceTest() override; - - void SetUp() override; - - void TearDown() override; - - bool CreateRoot(syncer::ModelType model_type); - - protected: - content::TestBrowserThreadBundle thread_bundle_; - TestProfileSyncService* sync_service_; -}; - -class CreateRootHelper { - public: - CreateRootHelper(AbstractProfileSyncServiceTest* test, - syncer::ModelType model_type); - virtual ~CreateRootHelper(); - - const base::Closure& callback() const; - bool success(); - - private: - void CreateRootCallback(); - - base::Closure callback_; - AbstractProfileSyncServiceTest* test_; - syncer::ModelType model_type_; - bool success_; -}; - -#endif // CHROME_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_
diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc deleted file mode 100644 index ccd554d..0000000 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ /dev/null
@@ -1,1319 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <set> -#include <string> -#include <utility> -#include <vector> - -#include "testing/gtest/include/gtest/gtest.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/run_loop.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/waitable_event.h" -#include "base/time/time.h" -#include "chrome/browser/autofill/personal_data_manager_factory.h" -#include "chrome/browser/signin/account_tracker_service_factory.h" -#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h" -#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" -#include "chrome/browser/signin/signin_manager_factory.h" -#include "chrome/browser/sync/abstract_profile_sync_service_test.h" -#include "chrome/browser/sync/profile_sync_service_factory.h" -#include "chrome/browser/sync/profile_sync_test_util.h" -#include "chrome/browser/sync/test_profile_sync_service.h" -#include "chrome/browser/web_data_service_factory.h" -#include "chrome/test/base/testing_browser_process.h" -#include "chrome/test/base/testing_profile.h" -#include "chrome/test/base/testing_profile_manager.h" -#include "components/autofill/core/browser/autofill_test_utils.h" -#include "components/autofill/core/browser/country_names.h" -#include "components/autofill/core/browser/personal_data_manager.h" -#include "components/autofill/core/browser/webdata/autocomplete_syncable_service.h" -#include "components/autofill/core/browser/webdata/autofill_change.h" -#include "components/autofill/core/browser/webdata/autofill_data_type_controller.h" -#include "components/autofill/core/browser/webdata/autofill_entry.h" -#include "components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h" -#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h" -#include "components/autofill/core/browser/webdata/autofill_table.h" -#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" -#include "components/browser_sync/browser/profile_sync_service.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/fake_profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_manager.h" -#include "components/sync_driver/data_type_controller.h" -#include "components/sync_driver/fake_sync_client.h" -#include "components/sync_driver/sync_api_component_factory_mock.h" -#include "components/syncable_prefs/pref_service_syncable.h" -#include "components/webdata/common/web_database.h" -#include "components/webdata_services/web_data_service_test_util.h" -#include "content/public/test/test_browser_thread.h" -#include "google_apis/gaia/gaia_constants.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/internal_api/public/data_type_debug_info_listener.h" -#include "sync/internal_api/public/read_node.h" -#include "sync/internal_api/public/read_transaction.h" -#include "sync/internal_api/public/write_node.h" -#include "sync/internal_api/public/write_transaction.h" -#include "sync/protocol/autofill_specifics.pb.h" -#include "sync/syncable/mutable_entry.h" -#include "sync/syncable/syncable_write_transaction.h" -#include "sync/test/engine/test_id_factory.h" -#include "testing/gmock/include/gmock/gmock.h" - -using autofill::AutocompleteSyncableService; -using autofill::AutofillChange; -using autofill::AutofillChangeList; -using autofill::AutofillEntry; -using autofill::ServerFieldType; -using autofill::AutofillKey; -using autofill::AutofillProfile; -using autofill::AutofillProfileChange; -using autofill::AutofillProfileSyncableService; -using autofill::AutofillTable; -using autofill::AutofillWebDataService; -using autofill::PersonalDataManager; -using base::Time; -using base::TimeDelta; -using base::WaitableEvent; -using browser_sync::AutofillDataTypeController; -using browser_sync::AutofillProfileDataTypeController; -using content::BrowserThread; -using syncer::AUTOFILL; -using syncer::AUTOFILL_PROFILE; -using syncer::BaseNode; -using syncer::syncable::BASE_VERSION; -using syncer::syncable::CREATE; -using syncer::syncable::GET_TYPE_ROOT; -using syncer::syncable::MutableEntry; -using syncer::syncable::SERVER_SPECIFICS; -using syncer::syncable::SPECIFICS; -using syncer::syncable::UNITTEST; -using syncer::syncable::WriterTag; -using syncer::syncable::WriteTransaction; -using sync_driver::DataTypeController; -using testing::_; -using testing::DoAll; -using testing::ElementsAre; -using testing::Not; -using testing::SetArgumentPointee; -using testing::Return; - -namespace { - -const char kTestProfileName[] = "test-profile"; - -void RunAndSignal(const base::Closure& cb, WaitableEvent* event) { - cb.Run(); - event->Signal(); -} - -} // namespace - -class AutofillTableMock : public AutofillTable { - public: - AutofillTableMock() {} - MOCK_METHOD2(RemoveFormElement, - bool(const base::string16& name, - const base::string16& value)); // NOLINT - MOCK_METHOD1(GetAllAutofillEntries, - bool(std::vector<AutofillEntry>* entries)); // NOLINT - MOCK_METHOD4(GetAutofillTimestamps, - bool(const base::string16& name, // NOLINT - const base::string16& value, - base::Time* date_created, - base::Time* date_last_used)); - MOCK_METHOD1(UpdateAutofillEntries, - bool(const std::vector<AutofillEntry>&)); // NOLINT - MOCK_METHOD1(GetAutofillProfiles, - bool(std::vector<AutofillProfile*>*)); // NOLINT - MOCK_METHOD1(UpdateAutofillProfile, - bool(const AutofillProfile&)); // NOLINT - MOCK_METHOD1(AddAutofillProfile, - bool(const AutofillProfile&)); // NOLINT - MOCK_METHOD1(RemoveAutofillProfile, - bool(const std::string&)); // NOLINT -}; - -MATCHER_P(MatchProfiles, profile, "") { - return (profile.Compare(arg) == 0); -} - -class TestSyncClient : public sync_driver::FakeSyncClient { - public: - TestSyncClient(PersonalDataManager* pdm, - const scoped_refptr<AutofillWebDataService>& web_data_service) - : pdm_(pdm), - sync_service_(nullptr), - web_data_service_(web_data_service) {} - ~TestSyncClient() override {} - - // FakeSyncClient overrides. - autofill::PersonalDataManager* GetPersonalDataManager() override { - return pdm_; - } - sync_driver::SyncService* GetSyncService() override { - DCHECK(sync_service_); - return sync_service_; - } - base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType( - syncer::ModelType type) override { - DCHECK(type == AUTOFILL || type == AUTOFILL_PROFILE); - if (type == AUTOFILL) { - return AutocompleteSyncableService::FromWebDataService( - web_data_service_.get())->AsWeakPtr(); - } else { - return AutofillProfileSyncableService::FromWebDataService( - web_data_service_.get())->AsWeakPtr(); - } - } - - void SetSyncService(sync_driver::SyncService* sync_service) { - sync_service_ = sync_service; - } - - private: - PersonalDataManager* pdm_; - sync_driver::SyncService* sync_service_; - scoped_refptr<AutofillWebDataService> web_data_service_; -}; - -class WebDatabaseFake : public WebDatabase { - public: - explicit WebDatabaseFake(AutofillTable* autofill_table) { - AddTable(autofill_table); - } -}; - -class MockAutofillBackend : public autofill::AutofillWebDataBackend { - public: - MockAutofillBackend( - WebDatabase* web_database, - const base::Closure& on_changed) - : web_database_(web_database), - on_changed_(on_changed) { - } - - ~MockAutofillBackend() override {} - WebDatabase* GetDatabase() override { return web_database_; } - void AddObserver( - autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} - void RemoveObserver( - autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} - void RemoveExpiredFormElements() override {} - void NotifyOfMultipleAutofillChanges() override { - DCHECK_CURRENTLY_ON(BrowserThread::DB); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, on_changed_); - } - - private: - WebDatabase* web_database_; - base::Closure on_changed_; -}; - -class ProfileSyncServiceAutofillTest; - -template<class AutofillProfile> -syncer::ModelType GetModelType() { - return syncer::UNSPECIFIED; -} - -template<> -syncer::ModelType GetModelType<AutofillEntry>() { - return syncer::AUTOFILL; -} - -template<> -syncer::ModelType GetModelType<AutofillProfile>() { - return syncer::AUTOFILL_PROFILE; -} - -class TokenWebDataServiceFake : public TokenWebData { - public: - TokenWebDataServiceFake() - : TokenWebData( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)) { - } - - bool IsDatabaseLoaded() override { return true; } - - AutofillWebDataService::Handle GetAllTokens( - WebDataServiceConsumer* consumer) override { - // TODO(tim): It would be nice if WebDataService was injected on - // construction of ProfileOAuth2TokenService rather than fetched by - // Initialize so that this isn't necessary (we could pass a NULL service). - // We currently do return it via EXPECT_CALLs, but without depending on - // order-of-initialization (which seems way more fragile) we can't tell - // which component is asking at what time, and some components in these - // Autofill tests require a WebDataService. - return 0; - } - - private: - ~TokenWebDataServiceFake() override {} - - DISALLOW_COPY_AND_ASSIGN(TokenWebDataServiceFake); -}; - -class WebDataServiceFake : public AutofillWebDataService { - public: - WebDataServiceFake() - : AutofillWebDataService( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)), - web_database_(NULL), - autocomplete_syncable_service_(NULL), - autofill_profile_syncable_service_(NULL), - syncable_service_created_or_destroyed_(false, false) { - } - - void SetDatabase(WebDatabase* web_database) { - web_database_ = web_database; - } - - void StartSyncableService() { - // The |autofill_profile_syncable_service_| must be constructed on the DB - // thread. - const base::Closure& on_changed_callback = base::Bind( - &WebDataServiceFake::NotifyAutofillMultipleChangedOnUIThread, - AsWeakPtr()); - - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - base::Bind(&WebDataServiceFake::CreateSyncableService, - base::Unretained(this), - on_changed_callback)); - syncable_service_created_or_destroyed_.Wait(); - } - - void ShutdownSyncableService() { - // The |autofill_profile_syncable_service_| must be destructed on the DB - // thread. - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - base::Bind(&WebDataServiceFake::DestroySyncableService, - base::Unretained(this))); - syncable_service_created_or_destroyed_.Wait(); - } - - bool IsDatabaseLoaded() override { return true; } - - WebDatabase* GetDatabase() override { return web_database_; } - - void OnAutofillEntriesChanged(const AutofillChangeList& changes) { - WaitableEvent event(true, false); - - base::Closure notify_cb = - base::Bind(&AutocompleteSyncableService::AutofillEntriesChanged, - base::Unretained(autocomplete_syncable_service_), - changes); - BrowserThread::PostTask( - BrowserThread::DB, - FROM_HERE, - base::Bind(&RunAndSignal, notify_cb, &event)); - event.Wait(); - } - - void OnAutofillProfileChanged(const AutofillProfileChange& changes) { - WaitableEvent event(true, false); - - base::Closure notify_cb = - base::Bind(&AutocompleteSyncableService::AutofillProfileChanged, - base::Unretained(autofill_profile_syncable_service_), - changes); - BrowserThread::PostTask( - BrowserThread::DB, - FROM_HERE, - base::Bind(&RunAndSignal, notify_cb, &event)); - event.Wait(); - } - - private: - ~WebDataServiceFake() override {} - - void CreateSyncableService(const base::Closure& on_changed_callback) { - ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); - // These services are deleted in DestroySyncableService(). - backend_.reset(new MockAutofillBackend( - GetDatabase(), on_changed_callback)); - AutocompleteSyncableService::CreateForWebDataServiceAndBackend( - this, backend_.get()); - AutofillProfileSyncableService::CreateForWebDataServiceAndBackend( - this, backend_.get(), "en-US"); - - autocomplete_syncable_service_ = - AutocompleteSyncableService::FromWebDataService(this); - autofill_profile_syncable_service_ = - AutofillProfileSyncableService::FromWebDataService(this); - - syncable_service_created_or_destroyed_.Signal(); - } - - void DestroySyncableService() { - ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); - autocomplete_syncable_service_ = NULL; - autofill_profile_syncable_service_ = NULL; - backend_.reset(); - syncable_service_created_or_destroyed_.Signal(); - } - - WebDatabase* web_database_; - AutocompleteSyncableService* autocomplete_syncable_service_; - AutofillProfileSyncableService* autofill_profile_syncable_service_; - scoped_ptr<autofill::AutofillWebDataBackend> backend_; - - WaitableEvent syncable_service_created_or_destroyed_; - - DISALLOW_COPY_AND_ASSIGN(WebDataServiceFake); -}; - -scoped_ptr<KeyedService> BuildMockWebDataServiceWrapper( - content::BrowserContext* profile) { - return make_scoped_ptr(new MockWebDataServiceWrapper( - new WebDataServiceFake(), new TokenWebDataServiceFake())); -} - -ACTION_P(MakeAutocompleteSyncComponents, wds) { - EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); - if (!BrowserThread::CurrentlyOn(BrowserThread::DB)) - return base::WeakPtr<syncer::SyncableService>(); - return AutocompleteSyncableService::FromWebDataService(wds)->AsWeakPtr(); -} - -ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) { - return new sync_driver::DataTypeManagerImpl( - debug_listener, - arg1, - arg2, - arg3, - arg4); -} - -class MockPersonalDataManager : public PersonalDataManager { - public: - MockPersonalDataManager() : PersonalDataManager("en-US") {} - MOCK_CONST_METHOD0(IsDataLoaded, bool()); - MOCK_METHOD0(LoadProfiles, void()); - MOCK_METHOD0(LoadCreditCards, void()); - MOCK_METHOD0(Refresh, void()); - - static scoped_ptr<KeyedService> Build(content::BrowserContext* profile) { - return make_scoped_ptr(new MockPersonalDataManager()); - } -}; - -template <class T> class AddAutofillHelper; - -class ProfileSyncServiceAutofillTest - : public AbstractProfileSyncServiceTest, - public syncer::DataTypeDebugInfoListener { - public: - // DataTypeDebugInfoListener implementation. - void OnDataTypeConfigureComplete(const std::vector< - syncer::DataTypeConfigurationStats>& configuration_stats) override { - ASSERT_EQ(1u, configuration_stats.size()); - association_stats_ = configuration_stats[0].association_stats; - } - - protected: - ProfileSyncServiceAutofillTest() - : profile_manager_(TestingBrowserProcess::GetGlobal()), - debug_ptr_factory_(this) { - autofill::CountryNames::SetLocaleString("en-US"); - } - - ~ProfileSyncServiceAutofillTest() override {} - - void SetUp() override { - AbstractProfileSyncServiceTest::SetUp(); - ASSERT_TRUE(profile_manager_.SetUp()); - TestingProfile::TestingFactories testing_factories; - testing_factories.push_back(std::make_pair( - ProfileOAuth2TokenServiceFactory::GetInstance(), - BuildAutoIssuingFakeProfileOAuth2TokenService)); - profile_ = profile_manager_.CreateTestingProfile( - kTestProfileName, - scoped_ptr<syncable_prefs::PrefServiceSyncable>(), - base::UTF8ToUTF16(kTestProfileName), - 0, - std::string(), - testing_factories); - web_database_.reset(new WebDatabaseFake(&autofill_table_)); - MockWebDataServiceWrapper* wrapper = - static_cast<MockWebDataServiceWrapper*>( - WebDataServiceFactory::GetInstance()->SetTestingFactoryAndUse( - profile_, BuildMockWebDataServiceWrapper)); - web_data_service_ = - static_cast<WebDataServiceFake*>(wrapper->GetAutofillWebData().get()); - web_data_service_->SetDatabase(web_database_.get()); - - personal_data_manager_ = static_cast<MockPersonalDataManager*>( - autofill::PersonalDataManagerFactory::GetInstance() - ->SetTestingFactoryAndUse(profile_, - MockPersonalDataManager::Build)); - - EXPECT_CALL(*personal_data_manager_, LoadProfiles()).Times(1); - EXPECT_CALL(*personal_data_manager_, LoadCreditCards()).Times(1); - - personal_data_manager_->Init( - WebDataServiceFactory::GetAutofillWebDataForProfile( - profile_, ServiceAccessType::EXPLICIT_ACCESS), - profile_->GetPrefs(), - AccountTrackerServiceFactory::GetForProfile(profile_), - SigninManagerFactory::GetForProfile(profile_), - profile_->IsOffTheRecord()); - - web_data_service_->StartSyncableService(); - - sync_client_.reset(new TestSyncClient(personal_data_manager_, - web_data_service_)); - - // When UpdateAutofillEntries() is called with an empty list, the return - // value should be |true|, rather than the default of |false|. - std::vector<AutofillEntry> empty; - EXPECT_CALL(autofill_table_, UpdateAutofillEntries(empty)) - .WillRepeatedly(Return(true)); - } - - void TearDown() override { - // Note: The tear down order is important. - ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(profile_, NULL); - web_data_service_->ShutdownOnUIThread(); - web_data_service_->ShutdownSyncableService(); - web_data_service_ = NULL; - // To prevent a leak, fully release TestURLRequestContext to ensure its - // destruction on the IO message loop. - profile_ = NULL; - profile_manager_.DeleteTestingProfile(kTestProfileName); - AbstractProfileSyncServiceTest::TearDown(); - } - - int GetSyncCount(syncer::ModelType type) { - syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - syncer::ReadNode node(&trans); - if (node.InitTypeRoot(type) != syncer::BaseNode::INIT_OK) - return 0; - return node.GetTotalNodeCount() - 1; - } - - void StartSyncService(const base::Closure& callback, - bool will_fail_association, - syncer::ModelType type) { - SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); - signin->SetAuthenticatedAccountInfo("12345", "test_user@gmail.com"); - sync_service_ = TestProfileSyncService::BuildAutoStartAsyncInit(profile_, - callback); - sync_client_->SetSyncService(sync_service_); - - SyncApiComponentFactoryMock* components = - sync_service_->GetSyncApiComponentFactoryMock(); - - EXPECT_CALL(*components, CreateDataTypeManager(_, _, _, _, _)). - WillOnce(ReturnNewDataTypeManagerWithDebugListener( - syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr()))); - - EXPECT_CALL(*personal_data_manager_, IsDataLoaded()). - WillRepeatedly(Return(true)); - - // We need tokens to get the tests going - ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) - ->UpdateCredentials(signin->GetAuthenticatedAccountId(), - "oauth2_login_token"); - - sync_service_->RegisterDataTypeController(CreateDataTypeController(type)); - sync_service_->Initialize(); - base::RunLoop().Run(); - - // It's possible this test triggered an unrecoverable error, in which case - // we can't get the sync count. - if (sync_service_->IsSyncActive()) { - EXPECT_EQ(GetSyncCount(type), - association_stats_.num_sync_items_after_association); - } - EXPECT_EQ(association_stats_.num_sync_items_after_association, - association_stats_.num_sync_items_before_association + - association_stats_.num_sync_items_added - - association_stats_.num_sync_items_deleted); - } - - bool AddAutofillSyncNode(const AutofillEntry& entry) { - syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - syncer::WriteNode node(&trans); - std::string tag = AutocompleteSyncableService::KeyToTag( - base::UTF16ToUTF8(entry.key().name()), - base::UTF16ToUTF8(entry.key().value())); - syncer::WriteNode::InitUniqueByCreationResult result = - node.InitUniqueByCreation(syncer::AUTOFILL, tag); - if (result != syncer::WriteNode::INIT_SUCCESS) - return false; - - sync_pb::EntitySpecifics specifics; - AutocompleteSyncableService::WriteAutofillEntry(entry, &specifics); - node.SetEntitySpecifics(specifics); - return true; - } - - bool AddAutofillSyncNode(const AutofillProfile& profile) { - syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - syncer::WriteNode node(&trans); - std::string tag = profile.guid(); - syncer::WriteNode::InitUniqueByCreationResult result = - node.InitUniqueByCreation(syncer::AUTOFILL_PROFILE, tag); - if (result != syncer::WriteNode::INIT_SUCCESS) - return false; - - sync_pb::EntitySpecifics specifics; - AutofillProfileSyncableService::WriteAutofillProfile(profile, &specifics); - node.SetEntitySpecifics(specifics); - return true; - } - - bool GetAutofillEntriesFromSyncDB(std::vector<AutofillEntry>* entries, - std::vector<AutofillProfile>* profiles) { - syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - syncer::ReadNode autofill_root(&trans); - if (autofill_root.InitTypeRoot(syncer::AUTOFILL) != BaseNode::INIT_OK) { - return false; - } - - int64_t child_id = autofill_root.GetFirstChildId(); - while (child_id != syncer::kInvalidId) { - syncer::ReadNode child_node(&trans); - if (child_node.InitByIdLookup(child_id) != BaseNode::INIT_OK) - return false; - - const sync_pb::AutofillSpecifics& autofill( - child_node.GetEntitySpecifics().autofill()); - if (autofill.has_value()) { - AutofillKey key(base::UTF8ToUTF16(autofill.name()), - base::UTF8ToUTF16(autofill.value())); - std::vector<base::Time> timestamps; - int timestamps_count = autofill.usage_timestamp_size(); - for (int i = 0; i < timestamps_count; ++i) { - timestamps.push_back(Time::FromInternalValue( - autofill.usage_timestamp(i))); - } - entries->push_back( - AutofillEntry(key, timestamps.front(), timestamps.back())); - } else if (autofill.has_profile()) { - AutofillProfile p; - p.set_guid(autofill.profile().guid()); - AutofillProfileSyncableService::OverwriteProfileWithServerData( - autofill.profile(), &p); - profiles->push_back(p); - } - child_id = child_node.GetSuccessorId(); - } - return true; - } - - bool GetAutofillProfilesFromSyncDBUnderProfileNode( - std::vector<AutofillProfile>* profiles) { - syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - syncer::ReadNode autofill_root(&trans); - if (autofill_root.InitTypeRoot(AUTOFILL_PROFILE) != BaseNode::INIT_OK) { - return false; - } - - int64_t child_id = autofill_root.GetFirstChildId(); - while (child_id != syncer::kInvalidId) { - syncer::ReadNode child_node(&trans); - if (child_node.InitByIdLookup(child_id) != BaseNode::INIT_OK) - return false; - - const sync_pb::AutofillProfileSpecifics& autofill( - child_node.GetEntitySpecifics().autofill_profile()); - AutofillProfile p; - p.set_guid(autofill.guid()); - AutofillProfileSyncableService::OverwriteProfileWithServerData(autofill, - &p); - profiles->push_back(p); - child_id = child_node.GetSuccessorId(); - } - return true; - } - - void SetIdleChangeProcessorExpectations() { - EXPECT_CALL(autofill_table_, RemoveFormElement(_, _)).Times(0); - EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _, _)).Times(0); - - // Only permit UpdateAutofillEntries() to be called with an empty list. - std::vector<AutofillEntry> empty; - EXPECT_CALL(autofill_table_, UpdateAutofillEntries(Not(empty))).Times(0); - } - - static AutofillEntry MakeAutofillEntry(const char* name, - const char* value, - int time_shift0, - int time_shift1) { - // Time deep in the past would cause Autocomplete sync to discard the - // entries. - static Time base_time = Time::Now().LocalMidnight(); - - base::Time date_created = base_time + TimeDelta::FromSeconds(time_shift0); - base::Time date_last_used = date_created; - if (time_shift1 >= 0) - date_last_used = base_time + TimeDelta::FromSeconds(time_shift1); - return AutofillEntry( - AutofillKey(base::ASCIIToUTF16(name), base::ASCIIToUTF16(value)), - date_created, date_last_used); - } - - static AutofillEntry MakeAutofillEntry(const char* name, - const char* value, - int time_shift) { - return MakeAutofillEntry(name, value, time_shift, -1); - } - - DataTypeController* CreateDataTypeController(syncer::ModelType type) { - DCHECK(type == AUTOFILL || type == AUTOFILL_PROFILE); - if (type == AUTOFILL) { - return new AutofillDataTypeController( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB), - base::Bind(&base::DoNothing), sync_client_.get(), web_data_service_); - } else { - return new AutofillProfileDataTypeController( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB), - base::Bind(&base::DoNothing), sync_client_.get(), web_data_service_); - } - } - - friend class AddAutofillHelper<AutofillEntry>; - friend class AddAutofillHelper<AutofillProfile>; - friend class FakeServerUpdater; - - TestingProfileManager profile_manager_; - TestingProfile* profile_; - AutofillTableMock autofill_table_; - scoped_ptr<WebDatabaseFake> web_database_; - scoped_refptr<WebDataServiceFake> web_data_service_; - MockPersonalDataManager* personal_data_manager_; - syncer::DataTypeAssociationStats association_stats_; - base::WeakPtrFactory<DataTypeDebugInfoListener> debug_ptr_factory_; - scoped_ptr<TestSyncClient> sync_client_; -}; - -template <class T> -class AddAutofillHelper { - public: - AddAutofillHelper(ProfileSyncServiceAutofillTest* test, - const std::vector<T>& entries) - : callback_(base::Bind(&AddAutofillHelper::AddAutofillCallback, - base::Unretained(this), test, entries)), - success_(false) { - } - - const base::Closure& callback() const { return callback_; } - bool success() { return success_; } - - private: - void AddAutofillCallback(ProfileSyncServiceAutofillTest* test, - const std::vector<T>& entries) { - if (!test->CreateRoot(GetModelType<T>())) - return; - - for (size_t i = 0; i < entries.size(); ++i) { - if (!test->AddAutofillSyncNode(entries[i])) - return; - } - success_ = true; - } - - base::Closure callback_; - bool success_; -}; - -// Overload write transaction to use custom NotifyTransactionComplete -class WriteTransactionTest: public WriteTransaction { - public: - WriteTransactionTest(const tracked_objects::Location& from_here, - WriterTag writer, - syncer::syncable::Directory* directory, - scoped_ptr<WaitableEvent>* wait_for_syncapi) - : WriteTransaction(from_here, writer, directory), - wait_for_syncapi_(wait_for_syncapi) { } - - void NotifyTransactionComplete(syncer::ModelTypeSet types) override { - // This is where we differ. Force a thread change here, giving another - // thread a chance to create a WriteTransaction - (*wait_for_syncapi_)->Wait(); - - WriteTransaction::NotifyTransactionComplete(types); - } - - private: - scoped_ptr<WaitableEvent>* wait_for_syncapi_; -}; - -// Our fake server updater. Needs the RefCountedThreadSafe inheritance so we can -// post tasks with it. -class FakeServerUpdater : public base::RefCountedThreadSafe<FakeServerUpdater> { - public: - FakeServerUpdater(TestProfileSyncService* service, - scoped_ptr<WaitableEvent>* wait_for_start, - scoped_ptr<WaitableEvent>* wait_for_syncapi) - : entry_(ProfileSyncServiceAutofillTest::MakeAutofillEntry("0", "0", 0)), - service_(service), - wait_for_start_(wait_for_start), - wait_for_syncapi_(wait_for_syncapi), - is_finished_(false, false) { } - - void Update() { - // This gets called in a modelsafeworker thread. - ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); - - syncer::UserShare* user_share = service_->GetUserShare(); - syncer::syncable::Directory* directory = user_share->directory.get(); - - // Create autofill protobuf. - std::string tag = AutocompleteSyncableService::KeyToTag( - base::UTF16ToUTF8(entry_.key().name()), - base::UTF16ToUTF8(entry_.key().value())); - sync_pb::AutofillSpecifics new_autofill; - new_autofill.set_name(base::UTF16ToUTF8(entry_.key().name())); - new_autofill.set_value(base::UTF16ToUTF8(entry_.key().value())); - new_autofill.add_usage_timestamp(entry_.date_created().ToInternalValue()); - if (entry_.date_created() != entry_.date_last_used()) { - new_autofill.add_usage_timestamp( - entry_.date_last_used().ToInternalValue()); - } - - sync_pb::EntitySpecifics entity_specifics; - entity_specifics.mutable_autofill()->CopyFrom(new_autofill); - - { - // Tell main thread we've started - (*wait_for_start_)->Signal(); - - // Create write transaction. - WriteTransactionTest trans(FROM_HERE, UNITTEST, directory, - wait_for_syncapi_); - - // Create actual entry based on autofill protobuf information. - // Simulates effects of UpdateLocalDataFromServerData - MutableEntry parent(&trans, GET_TYPE_ROOT, syncer::AUTOFILL); - MutableEntry item(&trans, CREATE, syncer::AUTOFILL, parent.GetId(), tag); - ASSERT_TRUE(item.good()); - item.PutSpecifics(entity_specifics); - item.PutServerSpecifics(entity_specifics); - item.PutBaseVersion(1); - syncer::syncable::Id server_item_id = - service_->id_factory()->NewServerId(); - item.PutId(server_item_id); - syncer::syncable::Id new_predecessor; - ASSERT_TRUE(item.PutPredecessor(new_predecessor)); - } - DVLOG(1) << "FakeServerUpdater finishing."; - is_finished_.Signal(); - } - - void CreateNewEntry(const AutofillEntry& entry) { - entry_ = entry; - ASSERT_FALSE(BrowserThread::CurrentlyOn(BrowserThread::DB)); - if (!BrowserThread::PostTask( - BrowserThread::DB, FROM_HERE, - base::Bind(&FakeServerUpdater::Update, this))) { - NOTREACHED() << "Failed to post task to the db thread."; - return; - } - } - - void WaitForUpdateCompletion() { - is_finished_.Wait(); - } - - private: - friend class base::RefCountedThreadSafe<FakeServerUpdater>; - ~FakeServerUpdater() { } - - AutofillEntry entry_; - TestProfileSyncService* service_; - scoped_ptr<WaitableEvent>* wait_for_start_; - scoped_ptr<WaitableEvent>* wait_for_syncapi_; - WaitableEvent is_finished_; - syncer::syncable::Id parent_id_; -}; - -// TODO(skrul): Test abort startup. -// TODO(skrul): Test processing of cloud changes. -// TODO(tim): Add autofill data type controller test, and a case to cover -// waiting for the PersonalDataManager. -TEST_F(ProfileSyncServiceAutofillTest, FailModelAssociation) { - // Don't create the root autofill node so startup fails. - StartSyncService(base::Closure(), true, syncer::AUTOFILL); - EXPECT_TRUE(sync_service_->HasUnrecoverableError()); -} - -TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) { - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true)); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::AUTOFILL); - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL); - EXPECT_TRUE(create_root.success()); - std::vector<AutofillEntry> sync_entries; - std::vector<AutofillProfile> sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); - EXPECT_EQ(0U, sync_entries.size()); - EXPECT_EQ(0U, sync_profiles.size()); -} - -TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) { - std::vector<AutofillEntry> entries; - entries.push_back(MakeAutofillEntry("foo", "bar", 1)); - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). - WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::AUTOFILL); - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL); - ASSERT_TRUE(create_root.success()); - std::vector<AutofillEntry> sync_entries; - std::vector<AutofillProfile> sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); - ASSERT_EQ(1U, entries.size()); - EXPECT_TRUE(entries[0] == sync_entries[0]); - EXPECT_EQ(0U, sync_profiles.size()); -} - -TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) { - std::vector<AutofillProfile*> profiles; - std::vector<AutofillProfile> expected_profiles; - // Owned by GetAutofillProfiles caller. - AutofillProfile* profile0 = new AutofillProfile; - autofill::test::SetProfileInfoWithGuid(profile0, - "54B3F9AA-335E-4F71-A27D-719C41564230", "Billing", - "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", - "91601", "US", "12345678910"); - profiles.push_back(profile0); - expected_profiles.push_back(*profile0); - EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). - WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true))); - EXPECT_CALL(*personal_data_manager_, Refresh()); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::AUTOFILL_PROFILE); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL_PROFILE); - ASSERT_TRUE(create_root.success()); - std::vector<AutofillProfile> sync_profiles; - ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(&sync_profiles)); - EXPECT_EQ(1U, sync_profiles.size()); - EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[0])); -} - -TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) { - // There is buggy autofill code that allows duplicate name/value - // pairs to exist in the database with separate pair_ids. - std::vector<AutofillEntry> entries; - entries.push_back(MakeAutofillEntry("foo", "bar", 1)); - entries.push_back(MakeAutofillEntry("dup", "", 2)); - entries.push_back(MakeAutofillEntry("dup", "", 3)); - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). - WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::AUTOFILL); - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL); - ASSERT_TRUE(create_root.success()); - std::vector<AutofillEntry> sync_entries; - std::vector<AutofillProfile> sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); - EXPECT_EQ(2U, sync_entries.size()); -} - -TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) { - AutofillEntry native_entry(MakeAutofillEntry("native", "entry", 1)); - AutofillEntry sync_entry(MakeAutofillEntry("sync", "entry", 2)); - - std::vector<AutofillEntry> native_entries; - native_entries.push_back(native_entry); - - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); - - std::vector<AutofillEntry> sync_entries; - sync_entries.push_back(sync_entry); - - AddAutofillHelper<AutofillEntry> add_autofill(this, sync_entries); - - EXPECT_CALL(autofill_table_, UpdateAutofillEntries(ElementsAre(sync_entry))). - WillOnce(Return(true)); - - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL); - ASSERT_TRUE(add_autofill.success()); - - std::set<AutofillEntry> expected_entries; - expected_entries.insert(native_entry); - expected_entries.insert(sync_entry); - - std::vector<AutofillEntry> new_sync_entries; - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, - &new_sync_profiles)); - std::set<AutofillEntry> new_sync_entries_set(new_sync_entries.begin(), - new_sync_entries.end()); - - EXPECT_TRUE(expected_entries == new_sync_entries_set); -} - -TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) { - AutofillEntry native_entry(MakeAutofillEntry("merge", "entry", 1)); - AutofillEntry sync_entry(MakeAutofillEntry("merge", "entry", 2)); - AutofillEntry merged_entry(MakeAutofillEntry("merge", "entry", 1, 2)); - - std::vector<AutofillEntry> native_entries; - native_entries.push_back(native_entry); - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); - - std::vector<AutofillEntry> sync_entries; - sync_entries.push_back(sync_entry); - AddAutofillHelper<AutofillEntry> add_autofill(this, sync_entries); - - EXPECT_CALL(autofill_table_, - UpdateAutofillEntries(ElementsAre(merged_entry))).WillOnce(Return(true)); - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL); - ASSERT_TRUE(add_autofill.success()); - - std::vector<AutofillEntry> new_sync_entries; - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, - &new_sync_profiles)); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(merged_entry == new_sync_entries[0]); -} - -TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) { - AutofillProfile sync_profile; - autofill::test::SetProfileInfoWithGuid(&sync_profile, - "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", - "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", - "91601", "US", "12345678910"); - - AutofillProfile* native_profile = new AutofillProfile; - autofill::test::SetProfileInfoWithGuid(native_profile, - "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", "Alicia", "Saenz", - "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", - "32801", "US", "19482937549"); - - std::vector<AutofillProfile*> native_profiles; - native_profiles.push_back(native_profile); - EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); - - std::vector<AutofillProfile> sync_profiles; - sync_profiles.push_back(sync_profile); - AddAutofillHelper<AutofillProfile> add_autofill(this, sync_profiles); - - EXPECT_CALL(autofill_table_, - UpdateAutofillProfile(MatchProfiles(sync_profile))). - WillOnce(Return(true)); - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL_PROFILE); - ASSERT_TRUE(add_autofill.success()); - - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( - &new_sync_profiles)); - ASSERT_EQ(1U, new_sync_profiles.size()); - EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0])); -} - -TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfileCombine) { - AutofillProfile sync_profile; - autofill::test::SetProfileInfoWithGuid(&sync_profile, - "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", - "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", - "91601", "US", "12345678910"); - - AutofillProfile* native_profile = new AutofillProfile; - // Same address, but different names, phones and e-mails. - autofill::test::SetProfileInfoWithGuid(native_profile, - "23355099-1170-4B71-8ED4-144470CC9EBF", "Billing", "Alicia", "Saenz", - "joewayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", - "91601", "US", "19482937549"); - - AutofillProfile expected_profile(sync_profile); - expected_profile.OverwriteWith(*native_profile, "en-US"); - - std::vector<AutofillProfile*> native_profiles; - native_profiles.push_back(native_profile); - EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); - EXPECT_CALL(autofill_table_, - AddAutofillProfile(MatchProfiles(expected_profile))). - WillOnce(Return(true)); - EXPECT_CALL(autofill_table_, - RemoveAutofillProfile("23355099-1170-4B71-8ED4-144470CC9EBF")). - WillOnce(Return(true)); - std::vector<AutofillProfile> sync_profiles; - sync_profiles.push_back(sync_profile); - AddAutofillHelper<AutofillProfile> add_autofill(this, sync_profiles); - - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL_PROFILE); - ASSERT_TRUE(add_autofill.success()); - - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( - &new_sync_profiles)); - ASSERT_EQ(1U, new_sync_profiles.size()); - // Check that key fields are the same. - EXPECT_TRUE(new_sync_profiles[0].IsSubsetOf(sync_profile, "en-US")); -} - -TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) { - AutofillProfile sync_profile; - - autofill::test::SetProfileInfoWithGuid(&sync_profile, - "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", - "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", - "91601", "US", "12345678910"); - - std::string native_guid = "EDC609ED-7EEE-4F27-B00C-423242A9C44B"; - AutofillProfile* native_profile = new AutofillProfile; - autofill::test::SetProfileInfoWithGuid(native_profile, - native_guid.c_str(), "Billing", - "Mitchell", "Morrison", - "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", - "91601", "US", "12345678910"); - - std::vector<AutofillProfile*> native_profiles; - native_profiles.push_back(native_profile); - EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); - - std::vector<AutofillProfile> sync_profiles; - sync_profiles.push_back(sync_profile); - AddAutofillHelper<AutofillProfile> add_autofill(this, sync_profiles); - - EXPECT_CALL(autofill_table_, AddAutofillProfile(_)). - WillOnce(Return(true)); - EXPECT_CALL(autofill_table_, RemoveAutofillProfile(native_guid)). - WillOnce(Return(true)); - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL_PROFILE); - ASSERT_TRUE(add_autofill.success()); - - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( - &new_sync_profiles)); - ASSERT_EQ(1U, new_sync_profiles.size()); - EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0])); - EXPECT_EQ(sync_profile.guid(), new_sync_profiles[0].guid()); -} - -TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) { - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true)); - EXPECT_CALL(*personal_data_manager_, Refresh()); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::AUTOFILL); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL); - ASSERT_TRUE(create_root.success()); - - AutofillEntry added_entry(MakeAutofillEntry("added", "entry", 1)); - - EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _, _)). - WillOnce(DoAll(SetArgumentPointee<2>(added_entry.date_created()), - SetArgumentPointee<3>(added_entry.date_last_used()), - Return(true))); - - AutofillChangeList changes; - changes.push_back(AutofillChange(AutofillChange::ADD, added_entry.key())); - - web_data_service_->OnAutofillEntriesChanged(changes); - - std::vector<AutofillEntry> new_sync_entries; - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, - &new_sync_profiles)); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(added_entry == new_sync_entries[0]); -} - -TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) { - EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); - EXPECT_CALL(*personal_data_manager_, Refresh()); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::AUTOFILL_PROFILE); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL_PROFILE); - ASSERT_TRUE(create_root.success()); - - AutofillProfile added_profile; - autofill::test::SetProfileInfoWithGuid(&added_profile, - "D6ADA912-D374-4C0A-917D-F5C8EBE43011", "Josephine", "Alicia", "Saenz", - "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", - "32801", "US", "19482937549"); - - AutofillProfileChange change( - AutofillProfileChange::ADD, added_profile.guid(), &added_profile); - web_data_service_->OnAutofillProfileChanged(change); - - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( - &new_sync_profiles)); - ASSERT_EQ(1U, new_sync_profiles.size()); - EXPECT_EQ(0, added_profile.Compare(new_sync_profiles[0])); -} - -TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) { - AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1)); - std::vector<AutofillEntry> original_entries; - original_entries.push_back(original_entry); - - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). - WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); - EXPECT_CALL(*personal_data_manager_, Refresh()); - CreateRootHelper create_root(this, syncer::AUTOFILL); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL); - ASSERT_TRUE(create_root.success()); - - AutofillEntry updated_entry(MakeAutofillEntry("my", "entry", 1, 2)); - - EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _, _)). - WillOnce(DoAll(SetArgumentPointee<2>(updated_entry.date_created()), - SetArgumentPointee<3>(updated_entry.date_last_used()), - Return(true))); - - AutofillChangeList changes; - changes.push_back(AutofillChange(AutofillChange::UPDATE, - updated_entry.key())); - web_data_service_->OnAutofillEntriesChanged(changes); - - std::vector<AutofillEntry> new_sync_entries; - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, - &new_sync_profiles)); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(updated_entry == new_sync_entries[0]); -} - - -TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) { - AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1)); - std::vector<AutofillEntry> original_entries; - original_entries.push_back(original_entry); - - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). - WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); - EXPECT_CALL(*personal_data_manager_, Refresh()); - CreateRootHelper create_root(this, syncer::AUTOFILL); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL); - ASSERT_TRUE(create_root.success()); - - AutofillChangeList changes; - changes.push_back(AutofillChange(AutofillChange::REMOVE, - original_entry.key())); - web_data_service_->OnAutofillEntriesChanged(changes); - - std::vector<AutofillEntry> new_sync_entries; - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, - &new_sync_profiles)); - ASSERT_EQ(0U, new_sync_entries.size()); -} - -TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) { - AutofillProfile sync_profile; - autofill::test::SetProfileInfoWithGuid(&sync_profile, - "3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz", - "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", - "32801", "US", "19482937549"); - AutofillProfile* native_profile = new AutofillProfile; - autofill::test::SetProfileInfoWithGuid(native_profile, - "3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz", - "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", - "32801", "US", "19482937549"); - - std::vector<AutofillProfile*> native_profiles; - native_profiles.push_back(native_profile); - EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); - - std::vector<AutofillProfile> sync_profiles; - sync_profiles.push_back(sync_profile); - AddAutofillHelper<AutofillProfile> add_autofill(this, sync_profiles); - EXPECT_CALL(*personal_data_manager_, Refresh()); - StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL_PROFILE); - ASSERT_TRUE(add_autofill.success()); - - AutofillProfileChange change( - AutofillProfileChange::REMOVE, sync_profile.guid(), NULL); - web_data_service_->OnAutofillProfileChanged(change); - - std::vector<AutofillProfile> new_sync_profiles; - ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( - &new_sync_profiles)); - ASSERT_EQ(0U, new_sync_profiles.size()); -} - -TEST_F(ProfileSyncServiceAutofillTest, ServerChangeRace) { - // Once for MergeDataAndStartSyncing() and twice for ProcessSyncChanges(), via - // LoadAutofillData(). - EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). - Times(3).WillRepeatedly(Return(true)); - // On the other hand Autofill and Autocomplete are separated now, so - // GetAutofillProfiles() should not be called. - EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).Times(0); - EXPECT_CALL(autofill_table_, UpdateAutofillEntries(_)). - WillRepeatedly(Return(true)); - EXPECT_CALL(*personal_data_manager_, Refresh()).Times(3); - CreateRootHelper create_root(this, syncer::AUTOFILL); - StartSyncService(create_root.callback(), false, syncer::AUTOFILL); - ASSERT_TRUE(create_root.success()); - - // (true, false) means we have to reset after |Signal|, init to unsignaled. - scoped_ptr<WaitableEvent> wait_for_start(new WaitableEvent(true, false)); - scoped_ptr<WaitableEvent> wait_for_syncapi(new WaitableEvent(true, false)); - scoped_refptr<FakeServerUpdater> updater(new FakeServerUpdater( - sync_service_, &wait_for_start, &wait_for_syncapi)); - - // This server side update will stall waiting for CommitWaiter. - updater->CreateNewEntry(MakeAutofillEntry("server", "entry", 1)); - wait_for_start->Wait(); - - AutofillEntry syncapi_entry(MakeAutofillEntry("syncapi", "entry", 2)); - ASSERT_TRUE(AddAutofillSyncNode(syncapi_entry)); - DVLOG(1) << "Syncapi update finished."; - - // If we reach here, it means syncapi succeeded and we didn't deadlock. Yay! - // Signal FakeServerUpdater that it can complete. - wait_for_syncapi->Signal(); - - // Make another entry to ensure nothing broke afterwards and wait for finish - // to clean up. - updater->WaitForUpdateCompletion(); - updater->CreateNewEntry(MakeAutofillEntry("server2", "entry2", 3)); - updater->WaitForUpdateCompletion(); - - // Let callbacks posted on UI thread execute. - base::RunLoop().RunUntilIdle(); - - std::vector<AutofillEntry> sync_entries; - std::vector<AutofillProfile> sync_profiles; - ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); - EXPECT_EQ(3U, sync_entries.size()); - EXPECT_EQ(0U, sync_profiles.size()); - for (size_t i = 0; i < sync_entries.size(); i++) { - DVLOG(1) << "Entry " << i << ": " << sync_entries[i].key().name() - << ", " << sync_entries[i].key().value(); - } -}
diff --git a/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc b/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc index 24235385..1ed8d91 100644 --- a/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc
@@ -12,13 +12,11 @@ #include <map> #include <queue> #include <stack> -#include <vector> -#include "base/command_line.h" -#include "base/files/file_path.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/run_loop.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -27,11 +25,9 @@ #include "base/time/time.h" #include "build/build_config.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" -#include "chrome/browser/bookmarks/chrome_bookmark_client.h" #include "chrome/browser/bookmarks/managed_bookmark_service_factory.h" #include "chrome/browser/favicon/favicon_service_factory.h" #include "chrome/browser/history/history_service_factory.h" -#include "chrome/common/chrome_switches.h" #include "chrome/test/base/testing_profile.h" #include "components/bookmarks/browser/base_bookmark_model_observer.h" #include "components/bookmarks/browser/bookmark_model.h" @@ -65,8 +61,6 @@ using bookmarks::BookmarkNode; using syncer::BaseNode; using testing::_; -using testing::InvokeWithoutArgs; -using testing::Mock; using testing::Return; using testing::StrictMock; @@ -98,7 +92,7 @@ public: explicit TestSyncClient(Profile* profile) : profile_(profile) {} - bookmarks::BookmarkModel* GetBookmarkModel() override { + BookmarkModel* GetBookmarkModel() override { return BookmarkModelFactory::GetForProfile(profile_); } @@ -332,6 +326,7 @@ // The change list we construct. syncer::ChangeRecordList changes_; + DISALLOW_COPY_AND_ASSIGN(FakeServerChange); }; class ExtensiveChangesBookmarkModelObserver @@ -385,18 +380,14 @@ local_merge_result_(syncer::BOOKMARKS), syncer_merge_result_(syncer::BOOKMARKS) {} - virtual ~ProfileSyncServiceBookmarkTest() { + ~ProfileSyncServiceBookmarkTest() override { StopSync(); UnloadBookmarkModel(); } - virtual void SetUp() { - test_user_share_.SetUp(); - } + void SetUp() override { test_user_share_.SetUp(); } - virtual void TearDown() { - test_user_share_.TearDown(); - } + void TearDown() override { test_user_share_.TearDown(); } bool CanSyncNode(const BookmarkNode* node) { return model_->client()->CanSyncNode(node); @@ -474,7 +465,7 @@ int GetSyncBookmarkCount() { syncer::ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); syncer::ReadNode node(&trans); - if (node.InitTypeRoot(syncer::BOOKMARKS) != syncer::BaseNode::INIT_OK) + if (node.InitTypeRoot(syncer::BOOKMARKS) != BaseNode::INIT_OK) return 0; return node.GetTotalNodeCount(); } @@ -568,7 +559,7 @@ if (error.IsSet()) return false; - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); // Verify the merge results were calculated properly. EXPECT_EQ(local_count_before, @@ -597,8 +588,7 @@ ASSERT_TRUE(AssociateModels()); // Set up change processor. - change_processor_.reset(new BookmarkChangeProcessor( - &sync_client_, model_associator_.get(), &mock_error_handler_)); + ResetChangeProcessor(); change_processor_->Start(test_user_share_.user_share()); } @@ -610,7 +600,7 @@ } model_associator_.reset(); - base::MessageLoop::current()->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); // TODO(akalin): Actually close the database and flush it to disk // (and make StartSync reload from disk). This would require @@ -624,7 +614,7 @@ } bool InitSyncNodeFromChromeNode(const BookmarkNode* bnode, - syncer::BaseNode* sync_node) { + BaseNode* sync_node) { return model_associator_->InitSyncNodeFromChromeId(bnode->id(), sync_node); } @@ -803,10 +793,38 @@ model_->bookmark_bar_node()->id()); } + BookmarkModel* model() { return model_; } + + syncer::TestUserShare* test_user_share() { return &test_user_share_; } + + BookmarkChangeProcessor* change_processor() { + return change_processor_.get(); + } + + void delete_change_processor() { change_processor_.reset(); } + + void ResetChangeProcessor() { + change_processor_ = make_scoped_ptr(new BookmarkChangeProcessor( + &sync_client_, model_associator_.get(), &mock_error_handler_)); + } + + sync_driver::DataTypeErrorHandlerMock* mock_error_handler() { + return &mock_error_handler_; + } + + void delete_model_associator() { model_associator_.reset(); } + + BookmarkModelAssociator* model_associator() { + return model_associator_.get(); + } + + bookmarks::ManagedBookmarkService* GetManagedBookmarkService() { + return ManagedBookmarkServiceFactory::GetForProfile(&profile_); + } + private: content::TestBrowserThreadBundle thread_bundle_; - protected: TestingProfile profile_; TestSyncClient sync_client_; BookmarkModel* model_; @@ -815,9 +833,10 @@ StrictMock<sync_driver::DataTypeErrorHandlerMock> mock_error_handler_; scoped_ptr<BookmarkModelAssociator> model_associator_; - private: syncer::SyncMergeResult local_merge_result_; syncer::SyncMergeResult syncer_merge_result_; + + DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceBookmarkTest); }; TEST_F(ProfileSyncServiceBookmarkTest, InitialState) { @@ -847,7 +866,7 @@ CreatePermanentBookmarkNodes(); { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); for (int i = 0; i < kNumFolders; ++i) { int64_t folder_id = AddFolderToShare(&trans, base::StringPrintf("folder%05d", i)); @@ -870,13 +889,13 @@ TEST_F(ProfileSyncServiceBookmarkTest, InitialModelAssociateWithBookmarkModelNodes) { LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE); - const BookmarkNode* folder = - model_->AddFolder(model_->other_node(), 0, base::ASCIIToUTF16("foobar")); - model_->AddFolder(folder, 0, base::ASCIIToUTF16("nested")); - model_->AddURL(folder, 0, base::ASCIIToUTF16("Internets #1 Pies Site"), - GURL("http://www.easypie.com/")); - model_->AddURL(folder, 1, base::ASCIIToUTF16("Airplanes"), - GURL("http://www.easyjet.com/")); + const BookmarkNode* folder = model()->AddFolder(model()->other_node(), 0, + base::ASCIIToUTF16("foobar")); + model()->AddFolder(folder, 0, base::ASCIIToUTF16("nested")); + model()->AddURL(folder, 0, base::ASCIIToUTF16("Internets #1 Pies Site"), + GURL("http://www.easypie.com/")); + model()->AddURL(folder, 1, base::ASCIIToUTF16("Airplanes"), + GURL("http://www.easyjet.com/")); StartSync(); ExpectModelMatch(); @@ -886,18 +905,18 @@ // that matches one of native bookmarks. TEST_F(ProfileSyncServiceBookmarkTest, InitialModelAssociateWithDeleteJournal) { LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE); - const BookmarkNode* folder = model_->AddFolder(model_->bookmark_bar_node(), 0, - base::ASCIIToUTF16("foobar")); + const BookmarkNode* folder = model()->AddFolder( + model()->bookmark_bar_node(), 0, base::ASCIIToUTF16("foobar")); const BookmarkNode* bookmark = - model_->AddURL(folder, 0, base::ASCIIToUTF16("Airplanes"), - GURL("http://www.easyjet.com/")); + model()->AddURL(folder, 0, base::ASCIIToUTF16("Airplanes"), + GURL("http://www.easyjet.com/")); CreatePermanentBookmarkNodes(); // Create entries matching the folder and the bookmark above. int64_t folder_id, bookmark_id; { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); folder_id = AddFolderToShare(&trans, "foobar"); bookmark_id = AddBookmarkToShare(&trans, folder_id, "Airplanes", "http://www.easyjet.com/"); @@ -906,7 +925,7 @@ // Associate the bookmark sync node with the native model one and make // it look like it was deleted by a server update. { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); syncer::WriteNode node(&trans); EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(bookmark_id)); @@ -958,8 +977,8 @@ const BookmarkNode* parent_folder = nullptr; for (int i = 0; i < kNumFolders; i++) { - const BookmarkNode* folder = model_->AddFolder( - model_->bookmark_bar_node(), i, base::ASCIIToUTF16("folder")); + const BookmarkNode* folder = model()->AddFolder( + model()->bookmark_bar_node(), i, base::ASCIIToUTF16("folder")); folder_ids[i] = folder->id(); if (i == kFolderToIncludeBookmarks) { parent_folder = folder; @@ -968,13 +987,13 @@ for (int i = 0; i < kNumBookmarks; i++) { const BookmarkNode* bookmark = - model_->AddURL(parent_folder, i, base::ASCIIToUTF16("bookmark"), - GURL("http://www.google.com/")); + model()->AddURL(parent_folder, i, base::ASCIIToUTF16("bookmark"), + GURL("http://www.google.com/")); bookmark_ids[i] = bookmark->id(); } // Number of nodes in bookmark bar before association. - int total_node_count = model_->bookmark_bar_node()->GetTotalNodeCount(); + int total_node_count = model()->bookmark_bar_node()->GetTotalNodeCount(); CreatePermanentBookmarkNodes(); @@ -982,7 +1001,7 @@ { // Create sync folders matching native folders above. int64_t parent_id = 0; - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); // Create in reverse order because AddFolderToShare passes NULL for // |predecessor| argument. for (int i = kNumFolders - 1; i >= 0; i--) { @@ -1023,7 +1042,7 @@ // Make one bookmark deleted. { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); syncer::WriteNode node(&trans); EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(sync_bookmark_id_to_delete)); @@ -1039,10 +1058,10 @@ // Only one native node should have been deleted and no nodes cloned due to // matching folder names. - EXPECT_EQ(kNumFolders, model_->bookmark_bar_node()->child_count()); + EXPECT_EQ(kNumFolders, model()->bookmark_bar_node()->child_count()); EXPECT_EQ(kNumBookmarks - 1, parent_folder->child_count()); EXPECT_EQ(total_node_count - 1, - model_->bookmark_bar_node()->GetTotalNodeCount()); + model()->bookmark_bar_node()->GetTotalNodeCount()); // Verify that the right bookmark got deleted and no bookmarks reordered. for (int i = 0; i < parent_folder->child_count(); i++) { @@ -1054,23 +1073,23 @@ // Verifies that the bookmark association skips sync nodes with invalid URLs. TEST_F(ProfileSyncServiceBookmarkTest, InitialModelAssociateWithInvalidUrl) { - EXPECT_CALL(mock_error_handler_, CreateAndUploadError(_, _, _)) + EXPECT_CALL(*mock_error_handler(), CreateAndUploadError(_, _, _)) .WillOnce(Return(syncer::SyncError())); LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE); // On the local side create a folder and two nodes. - const BookmarkNode* folder = model_->AddFolder(model_->bookmark_bar_node(), 0, - base::ASCIIToUTF16("folder")); - model_->AddURL(folder, 0, base::ASCIIToUTF16("node1"), - GURL("http://www.node1.com/")); - model_->AddURL(folder, 1, base::ASCIIToUTF16("node2"), - GURL("http://www.node2.com/")); + const BookmarkNode* folder = model()->AddFolder( + model()->bookmark_bar_node(), 0, base::ASCIIToUTF16("folder")); + model()->AddURL(folder, 0, base::ASCIIToUTF16("node1"), + GURL("http://www.node1.com/")); + model()->AddURL(folder, 1, base::ASCIIToUTF16("node2"), + GURL("http://www.node2.com/")); // On the sync side create a matching folder, one matching node, one // unmatching node, and one node with an invalid URL. CreatePermanentBookmarkNodes(); { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); int64_t folder_id = AddFolderToShare(&trans, "folder"); // Please note that each AddBookmarkToShare inserts the node at the front // so the actual order of children in the directory will be opposite. @@ -1102,56 +1121,56 @@ StartSync(); // Test addition. - const BookmarkNode* folder = - model_->AddFolder(model_->other_node(), 0, base::ASCIIToUTF16("foobar")); + const BookmarkNode* folder = model()->AddFolder(model()->other_node(), 0, + base::ASCIIToUTF16("foobar")); ExpectSyncerNodeMatching(folder); ExpectModelMatch(); const BookmarkNode* folder2 = - model_->AddFolder(folder, 0, base::ASCIIToUTF16("nested")); + model()->AddFolder(folder, 0, base::ASCIIToUTF16("nested")); ExpectSyncerNodeMatching(folder2); ExpectModelMatch(); - const BookmarkNode* url1 = model_->AddURL( - folder, 0, base::ASCIIToUTF16("Internets #1 Pies Site"), - GURL("http://www.easypie.com/")); + const BookmarkNode* url1 = + model()->AddURL(folder, 0, base::ASCIIToUTF16("Internets #1 Pies Site"), + GURL("http://www.easypie.com/")); ExpectSyncerNodeMatching(url1); ExpectModelMatch(); - const BookmarkNode* url2 = model_->AddURL( - folder, 1, base::ASCIIToUTF16("Airplanes"), - GURL("http://www.easyjet.com/")); + const BookmarkNode* url2 = + model()->AddURL(folder, 1, base::ASCIIToUTF16("Airplanes"), + GURL("http://www.easyjet.com/")); ExpectSyncerNodeMatching(url2); ExpectModelMatch(); // Test addition. const BookmarkNode* mobile_folder = - model_->AddFolder(model_->mobile_node(), 0, base::ASCIIToUTF16("pie")); + model()->AddFolder(model()->mobile_node(), 0, base::ASCIIToUTF16("pie")); ExpectSyncerNodeMatching(mobile_folder); ExpectModelMatch(); // Test modification. - model_->SetTitle(url2, base::ASCIIToUTF16("EasyJet")); + model()->SetTitle(url2, base::ASCIIToUTF16("EasyJet")); ExpectModelMatch(); - model_->Move(url1, folder2, 0); + model()->Move(url1, folder2, 0); ExpectModelMatch(); - model_->Move(folder2, model_->bookmark_bar_node(), 0); + model()->Move(folder2, model()->bookmark_bar_node(), 0); ExpectModelMatch(); - model_->SetTitle(folder2, base::ASCIIToUTF16("Not Nested")); + model()->SetTitle(folder2, base::ASCIIToUTF16("Not Nested")); ExpectModelMatch(); - model_->Move(folder, folder2, 0); + model()->Move(folder, folder2, 0); ExpectModelMatch(); - model_->SetTitle(folder, base::ASCIIToUTF16("who's nested now?")); + model()->SetTitle(folder, base::ASCIIToUTF16("who's nested now?")); ExpectModelMatch(); - model_->Copy(url2, model_->bookmark_bar_node(), 0); + model()->Copy(url2, model()->bookmark_bar_node(), 0); ExpectModelMatch(); - model_->SetTitle(mobile_folder, base::ASCIIToUTF16("strawberry")); + model()->SetTitle(mobile_folder, base::ASCIIToUTF16("strawberry")); ExpectModelMatch(); // Test deletion. // Delete a single item. - model_->Remove(url2); + model()->Remove(url2); ExpectModelMatch(); // Delete an item with several children. - model_->Remove(folder2); + model()->Remove(folder2); ExpectModelMatch(); - model_->Remove(model_->mobile_node()->GetChild(0)); + model()->Remove(model()->mobile_node()->GetChild(0)); ExpectModelMatch(); } @@ -1159,7 +1178,7 @@ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE); StartSync(); - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); FakeServerChange adds(&trans); int64_t f1 = adds.AddFolder("Server Folder B", bookmark_bar_id(), 0); @@ -1187,7 +1206,7 @@ for (it = adds.changes().begin(); it != adds.changes().end(); ++it) ExpectBrowserNodeUnknown(it->id); - adds.ApplyPendingChanges(change_processor_.get()); + adds.ApplyPendingChanges(change_processor()); // Make sure the bookmark model received all of the nodes in |adds|. for (it = adds.changes().begin(); it != adds.changes().end(); ++it) @@ -1224,7 +1243,7 @@ ExpectBrowserNodeTitle(u6, u6_old_title); // Apply the changes. - mods.ApplyPendingChanges(change_processor_.get()); + mods.ApplyPendingChanges(change_processor()); // Check for successful application. for (it = mods.changes().begin(); it != mods.changes().end(); ++it) @@ -1240,7 +1259,7 @@ ExpectBrowserNodeKnown(u2); ExpectBrowserNodeKnown(u3); - dels.ApplyPendingChanges(change_processor_.get()); + dels.ApplyPendingChanges(change_processor()); ExpectBrowserNodeUnknown(u2); ExpectBrowserNodeUnknown(u3); @@ -1256,7 +1275,7 @@ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE); StartSync(); - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); // Stress the immediate children of other_node because that's where // ApplyModelChanges puts a temporary foster parent node. @@ -1276,7 +1295,7 @@ for (it = adds.changes().begin(); it != adds.changes().end(); ++it) ExpectBrowserNodeUnknown(it->id); - adds.ApplyPendingChanges(change_processor_.get()); + adds.ApplyPendingChanges(change_processor()); // Make sure the bookmark model received all of the nodes in |adds|. for (it = adds.changes().begin(); it != adds.changes().end(); ++it) @@ -1294,7 +1313,7 @@ ops.ModifyPosition(u5, f6, 0); ops.Delete(f1); - ops.ApplyPendingChanges(change_processor_.get()); + ops.ApplyPendingChanges(change_processor()); ExpectModelMatch(&trans); } @@ -1305,16 +1324,16 @@ StartSync(); { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); FakeServerChange adds(&trans); std::string url("http://dev.chromium.org"); EXPECT_NE(GURL(url).spec(), url); adds.AddURL("u1", url, other_bookmarks_id(), 0); - adds.ApplyPendingChanges(change_processor_.get()); + adds.ApplyPendingChanges(change_processor()); - EXPECT_EQ(1, model_->other_node()->child_count()); + EXPECT_EQ(1, model()->other_node()->child_count()); ExpectModelMatch(&trans); } @@ -1324,7 +1343,7 @@ StartSync(); // There should still be just the one bookmark. - EXPECT_EQ(1, model_->other_node()->child_count()); + EXPECT_EQ(1, model()->other_node()->child_count()); ExpectModelMatch(); } @@ -1336,19 +1355,19 @@ int child_count = 0; { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); FakeServerChange adds(&trans); std::string url("x"); EXPECT_FALSE(GURL(url).is_valid()); adds.AddURL("u1", url, other_bookmarks_id(), 0); - adds.ApplyPendingChanges(change_processor_.get()); + adds.ApplyPendingChanges(change_processor()); // We're lenient about what should happen -- the model could wind up with // the node or without it; but things should be consistent, and we // shouldn't crash. - child_count = model_->other_node()->child_count(); + child_count = model()->other_node()->child_count(); EXPECT_TRUE(child_count == 0 || child_count == 1); ExpectModelMatch(&trans); } @@ -1359,7 +1378,7 @@ StartSync(); // Things ought not to have changed. - EXPECT_EQ(model_->other_node()->child_count(), child_count); + EXPECT_EQ(model()->other_node()->child_count(), child_count); ExpectModelMatch(); } @@ -1396,21 +1415,22 @@ // Create both folders and bookmarks using each name. GURL url("http://www.doublemint.com"); for (size_t i = 0; i < arraysize(names); ++i) { - model_->AddFolder(model_->other_node(), 0, base::ASCIIToUTF16(names[i])); - model_->AddURL(model_->other_node(), 0, base::ASCIIToUTF16(names[i]), url); + model()->AddFolder(model()->other_node(), 0, base::ASCIIToUTF16(names[i])); + model()->AddURL(model()->other_node(), 0, base::ASCIIToUTF16(names[i]), + url); } // Verify that the browser model matches the sync model. - EXPECT_EQ(static_cast<size_t>(model_->other_node()->child_count()), - 2*arraysize(names)); + EXPECT_EQ(static_cast<size_t>(model()->other_node()->child_count()), + 2 * arraysize(names)); ExpectModelMatch(); // Restart and re-associate. Verify things still match. StopSync(); LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE); StartSync(); - EXPECT_EQ(static_cast<size_t>(model_->other_node()->child_count()), - 2*arraysize(names)); + EXPECT_EQ(static_cast<size_t>(model()->other_node()->child_count()), + 2 * arraysize(names)); ExpectModelMatch(); } @@ -1425,8 +1445,8 @@ static const int kTimesToInsert = 256; // Create two book-end nodes to insert between. - model_->AddFolder(model_->other_node(), 0, base::ASCIIToUTF16("Alpha")); - model_->AddFolder(model_->other_node(), 1, base::ASCIIToUTF16("Omega")); + model()->AddFolder(model()->other_node(), 0, base::ASCIIToUTF16("Alpha")); + model()->AddFolder(model()->other_node(), 1, base::ASCIIToUTF16("Omega")); int count = 2; // Test insertion in first half of range by repeatedly inserting in second @@ -1434,7 +1454,7 @@ for (int i = 0; i < kTimesToInsert; ++i) { base::string16 title = base::ASCIIToUTF16("Pre-insertion ") + base::IntToString16(i); - model_->AddFolder(model_->other_node(), 1, title); + model()->AddFolder(model()->other_node(), 1, title); count++; } @@ -1443,34 +1463,33 @@ for (int i = 0; i < kTimesToInsert; ++i) { base::string16 title = base::ASCIIToUTF16("Post-insertion ") + base::IntToString16(i); - model_->AddFolder(model_->other_node(), count - 1, title); + model()->AddFolder(model()->other_node(), count - 1, title); count++; } // Verify that the browser model matches the sync model. - EXPECT_EQ(model_->other_node()->child_count(), count); + EXPECT_EQ(model()->other_node()->child_count(), count); ExpectModelMatch(); } // Introduce a consistency violation into the model, and see that it // puts itself into a lame, error state. TEST_F(ProfileSyncServiceBookmarkTest, UnrecoverableErrorSuspendsService) { - EXPECT_CALL(mock_error_handler_, - OnSingleDataTypeUnrecoverableError(_)); + EXPECT_CALL(*mock_error_handler(), OnSingleDataTypeUnrecoverableError(_)); LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE); StartSync(); // Add a node which will be the target of the consistency violation. const BookmarkNode* node = - model_->AddFolder(model_->other_node(), 0, base::ASCIIToUTF16("node")); + model()->AddFolder(model()->other_node(), 0, base::ASCIIToUTF16("node")); ExpectSyncerNodeMatching(node); // Now destroy the syncer node as if we were the ProfileSyncService without // updating the ProfileSyncService state. This should introduce // inconsistency between the two models. { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); syncer::WriteNode sync_node(&trans); ASSERT_TRUE(InitSyncNodeFromChromeNode(node, &sync_node)); sync_node.Tombstone(); @@ -1481,7 +1500,7 @@ // Add a child to the inconsistent node. This should cause detection of the // problem and the syncer should stop processing changes. - model_->AddFolder(node, 0, base::ASCIIToUTF16("nested")); + model()->AddFolder(node, 0, base::ASCIIToUTF16("nested")); } // See what happens if we run model association when there are two exact URL @@ -1491,18 +1510,18 @@ LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE); StartSync(); - model_->AddURL(model_->other_node(), 0, base::ASCIIToUTF16("Dup"), - GURL("http://dup.com/")); - model_->AddURL(model_->other_node(), 0, base::ASCIIToUTF16("Dup"), - GURL("http://dup.com/")); + model()->AddURL(model()->other_node(), 0, base::ASCIIToUTF16("Dup"), + GURL("http://dup.com/")); + model()->AddURL(model()->other_node(), 0, base::ASCIIToUTF16("Dup"), + GURL("http://dup.com/")); - EXPECT_EQ(2, model_->other_node()->child_count()); + EXPECT_EQ(2, model()->other_node()->child_count()); // Restart the sync service to trigger model association. StopSync(); StartSync(); - EXPECT_EQ(2, model_->other_node()->child_count()); + EXPECT_EQ(2, model()->other_node()->child_count()); ExpectModelMatch(); } @@ -1522,26 +1541,26 @@ StartSync(); int fixed_sync_bk_count = GetSyncBookmarkCount(); { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); FakeServerChange adds(&trans); u0 = adds.AddURL("URL 0", "http://plus.google.com/", bookmark_bar_id(), 0); f1 = adds.AddFolder("Folder 1", bookmark_bar_id(), u0); u1 = adds.AddURL("URL 1", "http://www.google.com/", f1, 0); f2 = adds.AddFolder("Folder 2", f1, u1); u2 = adds.AddURL("URL 2", "http://mail.google.com/", f2, 0); - adds.ApplyPendingChanges(change_processor_.get()); + adds.ApplyPendingChanges(change_processor()); } StopSync(); // Reload bookmark model and disable model saving to make sync changes not // persisted. LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE); - EXPECT_EQ(6, model_->bookmark_bar_node()->GetTotalNodeCount()); + EXPECT_EQ(6, model()->bookmark_bar_node()->GetTotalNodeCount()); EXPECT_EQ(fixed_sync_bk_count + 5, GetSyncBookmarkCount()); StartSync(); { // Remove all folders/bookmarks except u3 added above. - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); MakeServerUpdate(&trans, f1); MakeServerUpdate(&trans, u1); MakeServerUpdate(&trans, f2); @@ -1551,44 +1570,44 @@ dels.Delete(f2); dels.Delete(u1); dels.Delete(f1); - dels.ApplyPendingChanges(change_processor_.get()); + dels.ApplyPendingChanges(change_processor()); } StopSync(); // Bookmark bar itself and u0 remain. - EXPECT_EQ(2, model_->bookmark_bar_node()->GetTotalNodeCount()); + EXPECT_EQ(2, model()->bookmark_bar_node()->GetTotalNodeCount()); // Reload bookmarks including ones deleted in sync model from storage. LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE); - EXPECT_EQ(6, model_->bookmark_bar_node()->GetTotalNodeCount()); + EXPECT_EQ(6, model()->bookmark_bar_node()->GetTotalNodeCount()); // Add a bookmark under f1 when sync is off so that f1 will not be // deleted even when f1 matches delete journal because it's not empty. - model_->AddURL(model_->bookmark_bar_node()->GetChild(1), - 0, base::UTF8ToUTF16("local"), GURL("http://www.youtube.com")); + model()->AddURL(model()->bookmark_bar_node()->GetChild(1), 0, + base::UTF8ToUTF16("local"), GURL("http://www.youtube.com")); // Sync model has fixed bookmarks nodes and u3. EXPECT_EQ(fixed_sync_bk_count + 1, GetSyncBookmarkCount()); StartSync(); // Expect 4 bookmarks after model association because u2, f2, u1 are removed // by delete journal, f1 is not removed by delete journal because it's // not empty due to www.youtube.com added above. - EXPECT_EQ(4, model_->bookmark_bar_node()->GetTotalNodeCount()); + EXPECT_EQ(4, model()->bookmark_bar_node()->GetTotalNodeCount()); EXPECT_EQ(base::UTF8ToUTF16("URL 0"), - model_->bookmark_bar_node()->GetChild(0)->GetTitle()); + model()->bookmark_bar_node()->GetChild(0)->GetTitle()); EXPECT_EQ(base::UTF8ToUTF16("Folder 1"), - model_->bookmark_bar_node()->GetChild(1)->GetTitle()); + model()->bookmark_bar_node()->GetChild(1)->GetTitle()); EXPECT_EQ(base::UTF8ToUTF16("local"), - model_->bookmark_bar_node()->GetChild(1)->GetChild(0)->GetTitle()); + model()->bookmark_bar_node()->GetChild(1)->GetChild(0)->GetTitle()); StopSync(); // Verify purging of delete journals. // Delete journals for u2, f2, u1 remains because they are used in last // association. - EXPECT_EQ(3u, test_user_share_.GetDeleteJournalSize()); + EXPECT_EQ(3u, test_user_share()->GetDeleteJournalSize()); StartSync(); StopSync(); // Reload again and all delete journals should be gone because none is used // in last association. - ASSERT_TRUE(test_user_share_.Reload()); - EXPECT_EQ(0u, test_user_share_.GetDeleteJournalSize()); + ASSERT_TRUE(test_user_share()->Reload()); + EXPECT_EQ(0u, test_user_share()->GetDeleteJournalSize()); } struct TestData { @@ -1767,14 +1786,11 @@ if (item.url) { const base::Time add_time = start_time_ + base::TimeDelta::FromMinutes(*running_count); - model_->AddURLWithCreationTimeAndMetaInfo(node, - i, - base::UTF8ToUTF16(item.title), - GURL(item.url), - add_time, - NULL); + model()->AddURLWithCreationTimeAndMetaInfo( + node, i, base::UTF8ToUTF16(item.title), GURL(item.url), add_time, + NULL); } else { - model_->AddFolder(node, i, base::UTF8ToUTF16(item.title)); + model()->AddFolder(node, i, base::UTF8ToUTF16(item.title)); } (*running_count)++; } @@ -1815,7 +1831,7 @@ // TODO(munjal): We should implement some way of generating random data and can // use the same seed to generate the same sequence. void ProfileSyncServiceBookmarkTestWithData::WriteTestDataToBookmarkModel() { - const BookmarkNode* bookmarks_bar_node = model_->bookmark_bar_node(); + const BookmarkNode* bookmarks_bar_node = model()->bookmark_bar_node(); int count = 0; PopulateFromTestData(bookmarks_bar_node, kBookmarkBarChildren, @@ -1828,7 +1844,7 @@ const BookmarkNode* f2_node = bookmarks_bar_node->GetChild(3); PopulateFromTestData(f2_node, kF2Children, arraysize(kF2Children), &count); - const BookmarkNode* other_bookmarks_node = model_->other_node(); + const BookmarkNode* other_bookmarks_node = model()->other_node(); PopulateFromTestData(other_bookmarks_node, kOtherBookmarkChildren, arraysize(kOtherBookmarkChildren), @@ -1846,7 +1862,7 @@ PopulateFromTestData(dup_node, kDup2Children, arraysize(kDup2Children), &count); - const BookmarkNode* mobile_bookmarks_node = model_->mobile_node(); + const BookmarkNode* mobile_bookmarks_node = model()->mobile_node(); PopulateFromTestData(mobile_bookmarks_node, kMobileBookmarkChildren, arraysize(kMobileBookmarkChildren), @@ -1863,7 +1879,7 @@ void ProfileSyncServiceBookmarkTestWithData:: ExpectBookmarkModelMatchesTestData() { - const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node(); + const BookmarkNode* bookmark_bar_node = model()->bookmark_bar_node(); int count = 0; CompareWithTestData(bookmark_bar_node, kBookmarkBarChildren, @@ -1876,7 +1892,7 @@ const BookmarkNode* f2_node = bookmark_bar_node->GetChild(3); CompareWithTestData(f2_node, kF2Children, arraysize(kF2Children), &count); - const BookmarkNode* other_bookmarks_node = model_->other_node(); + const BookmarkNode* other_bookmarks_node = model()->other_node(); CompareWithTestData(other_bookmarks_node, kOtherBookmarkChildren, arraysize(kOtherBookmarkChildren), @@ -1894,7 +1910,7 @@ CompareWithTestData(dup_node, kDup2Children, arraysize(kDup2Children), &count); - const BookmarkNode* mobile_bookmarks_node = model_->mobile_node(); + const BookmarkNode* mobile_bookmarks_node = model()->mobile_node(); CompareWithTestData(mobile_bookmarks_node, kMobileBookmarkChildren, arraysize(kMobileBookmarkChildren), @@ -1969,9 +1985,9 @@ // Blow away the bookmark model -- it should be empty afterwards. UnloadBookmarkModel(); LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE); - EXPECT_EQ(model_->bookmark_bar_node()->child_count(), 0); - EXPECT_EQ(model_->other_node()->child_count(), 0); - EXPECT_EQ(model_->mobile_node()->child_count(), 0); + EXPECT_EQ(model()->bookmark_bar_node()->child_count(), 0); + EXPECT_EQ(model()->other_node()->child_count(), 0); + EXPECT_EQ(model()->mobile_node()->child_count(), 0); // Now restart the sync service. Starting it should populate the bookmark // model -- test for consistency. @@ -2003,10 +2019,10 @@ // sure we get the order of the server after merge. LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE); ExpectBookmarkModelMatchesTestData(); - const BookmarkNode* bookmark_bar = model_->bookmark_bar_node(); + const BookmarkNode* bookmark_bar = model()->bookmark_bar_node(); ASSERT_TRUE(bookmark_bar); ASSERT_GT(bookmark_bar->child_count(), 1); - model_->Move(bookmark_bar->GetChild(0), bookmark_bar, 1); + model()->Move(bookmark_bar->GetChild(0), bookmark_bar, 1); StartSync(); ExpectModelMatch(); ExpectBookmarkModelMatchesTestData(); @@ -2020,29 +2036,29 @@ ExpectBookmarkModelMatchesTestData(); // Remove some nodes and reorder some nodes. - const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node(); + const BookmarkNode* bookmark_bar_node = model()->bookmark_bar_node(); int remove_index = 2; ASSERT_GT(bookmark_bar_node->child_count(), remove_index); const BookmarkNode* child_node = bookmark_bar_node->GetChild(remove_index); ASSERT_TRUE(child_node); ASSERT_TRUE(child_node->is_url()); - model_->Remove(bookmark_bar_node->GetChild(remove_index)); + model()->Remove(bookmark_bar_node->GetChild(remove_index)); ASSERT_GT(bookmark_bar_node->child_count(), remove_index); child_node = bookmark_bar_node->GetChild(remove_index); ASSERT_TRUE(child_node); ASSERT_TRUE(child_node->is_folder()); - model_->Remove(bookmark_bar_node->GetChild(remove_index)); + model()->Remove(bookmark_bar_node->GetChild(remove_index)); - const BookmarkNode* other_node = model_->other_node(); + const BookmarkNode* other_node = model()->other_node(); ASSERT_GE(other_node->child_count(), 1); const BookmarkNode* f3_node = other_node->GetChild(0); ASSERT_TRUE(f3_node); ASSERT_TRUE(f3_node->is_folder()); remove_index = 2; ASSERT_GT(f3_node->child_count(), remove_index); - model_->Remove(f3_node->GetChild(remove_index)); + model()->Remove(f3_node->GetChild(remove_index)); ASSERT_GT(f3_node->child_count(), remove_index); - model_->Remove(f3_node->GetChild(remove_index)); + model()->Remove(f3_node->GetChild(remove_index)); StartSync(); ExpectModelMatch(); @@ -2053,36 +2069,36 @@ ExpectBookmarkModelMatchesTestData(); // Remove some nodes and reorder some nodes. - bookmark_bar_node = model_->bookmark_bar_node(); + bookmark_bar_node = model()->bookmark_bar_node(); remove_index = 0; ASSERT_GT(bookmark_bar_node->child_count(), remove_index); child_node = bookmark_bar_node->GetChild(remove_index); ASSERT_TRUE(child_node); ASSERT_TRUE(child_node->is_url()); - model_->Remove(bookmark_bar_node->GetChild(remove_index)); + model()->Remove(bookmark_bar_node->GetChild(remove_index)); ASSERT_GT(bookmark_bar_node->child_count(), remove_index); child_node = bookmark_bar_node->GetChild(remove_index); ASSERT_TRUE(child_node); ASSERT_TRUE(child_node->is_folder()); - model_->Remove(bookmark_bar_node->GetChild(remove_index)); + model()->Remove(bookmark_bar_node->GetChild(remove_index)); ASSERT_GE(bookmark_bar_node->child_count(), 2); - model_->Move(bookmark_bar_node->GetChild(0), bookmark_bar_node, 1); + model()->Move(bookmark_bar_node->GetChild(0), bookmark_bar_node, 1); - other_node = model_->other_node(); + other_node = model()->other_node(); ASSERT_GE(other_node->child_count(), 1); f3_node = other_node->GetChild(0); ASSERT_TRUE(f3_node); ASSERT_TRUE(f3_node->is_folder()); remove_index = 0; ASSERT_GT(f3_node->child_count(), remove_index); - model_->Remove(f3_node->GetChild(remove_index)); + model()->Remove(f3_node->GetChild(remove_index)); ASSERT_GT(f3_node->child_count(), remove_index); - model_->Remove(f3_node->GetChild(remove_index)); + model()->Remove(f3_node->GetChild(remove_index)); ASSERT_GE(other_node->child_count(), 4); - model_->Move(other_node->GetChild(0), other_node, 1); - model_->Move(other_node->GetChild(2), other_node, 3); + model()->Move(other_node->GetChild(0), other_node, 1); + model()->Move(other_node->GetChild(2), other_node, 3); StartSync(); ExpectModelMatch(); @@ -2117,9 +2133,9 @@ // Change the bookmark model before restarting sync service to simulate // the situation where bookmark model is different from sync model and // make sure model associator correctly rebuilds associations. - const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node(); - model_->AddURL(bookmark_bar_node, 0, base::ASCIIToUTF16("xtra"), - GURL("http://www.xtra.com")); + const BookmarkNode* bookmark_bar_node = model()->bookmark_bar_node(); + model()->AddURL(bookmark_bar_node, 0, base::ASCIIToUTF16("xtra"), + GURL("http://www.xtra.com")); // Now restart sync. This time it will try to use the persistent // associations and realize that they are invalid and hence will rebuild // associations. @@ -2133,14 +2149,14 @@ // Write test data to bookmark model and verify that the models match. WriteTestDataToBookmarkModel(); - const BookmarkNode* folder_added = model_->other_node()->GetChild(0); + const BookmarkNode* folder_added = model()->other_node()->GetChild(0); ASSERT_TRUE(folder_added); ASSERT_TRUE(folder_added->is_folder()); ExpectModelMatch(); // Sort the other-bookmarks children and expect that the models match. - model_->SortChildren(folder_added); + model()->SortChildren(folder_added); ExpectModelMatch(); } @@ -2165,7 +2181,7 @@ // Second attempt succeeds due to the previous error resetting the native // transaction version. - model_associator_.reset(); + delete_model_associator(); EXPECT_TRUE(CreatePermanentBookmarkNodes()); EXPECT_TRUE(AssociateModels()); @@ -2182,7 +2198,7 @@ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE); ExtensiveChangesBookmarkModelObserver observer; - model_->AddObserver(&observer); + model()->AddObserver(&observer); StartSync(); @@ -2190,7 +2206,7 @@ EXPECT_EQ(0, observer.get_completed_count_at_started()); EXPECT_EQ(1, observer.get_completed()); - model_->RemoveObserver(&observer); + model()->RemoveObserver(&observer); } // Verify that the creation_time_us changes are applied in the local model at @@ -2205,13 +2221,13 @@ // Modify the date_added field of a bookmark so it doesn't match with // the sync data. - const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node(); + const BookmarkNode* bookmark_bar_node = model()->bookmark_bar_node(); int modified_index = 2; ASSERT_GT(bookmark_bar_node->child_count(), modified_index); const BookmarkNode* child_node = bookmark_bar_node->GetChild(modified_index); ASSERT_TRUE(child_node); EXPECT_TRUE(child_node->is_url()); - model_->SetDateAdded(child_node, base::Time::FromInternalValue(10)); + model()->SetDateAdded(child_node, base::Time::FromInternalValue(10)); StartSync(); StopSync(); @@ -2219,9 +2235,9 @@ // Verify that transaction versions are in sync between the native model // and Sync. { - syncer::ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::ReadTransaction trans(FROM_HERE, test_user_share()->user_share()); int64_t sync_version = trans.GetModelVersion(syncer::BOOKMARKS); - int64_t native_version = model_->root_node()->sync_transaction_version(); + int64_t native_version = model()->root_node()->sync_transaction_version(); EXPECT_EQ(native_version, sync_version); } @@ -2232,8 +2248,8 @@ // Reset transaction version on the native model to trigger updating data // for all bookmark nodes. - model_->SetNodeSyncTransactionVersion( - model_->root_node(), syncer::syncable::kInvalidTransactionVersion); + model()->SetNodeSyncTransactionVersion( + model()->root_node(), syncer::syncable::kInvalidTransactionVersion); StartSync(); @@ -2243,18 +2259,18 @@ // Now trigger a change while syncing. We add a new bookmark, sync it, then // updates it's creation time. - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); FakeServerChange adds(&trans); const std::string kTitle = "Some site"; const std::string kUrl = "http://www.whatwhat.yeah/"; const int kCreationTime = 30; int64_t id = adds.AddURL(kTitle, kUrl, bookmark_bar_id(), 0); - adds.ApplyPendingChanges(change_processor_.get()); + adds.ApplyPendingChanges(change_processor()); FakeServerChange updates(&trans); updates.ModifyCreationTime(id, kCreationTime); - updates.ApplyPendingChanges(change_processor_.get()); + updates.ApplyPendingChanges(change_processor()); - const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(0); + const BookmarkNode* node = model()->bookmark_bar_node()->GetChild(0); ASSERT_TRUE(node); EXPECT_TRUE(node->is_url()); EXPECT_EQ(base::UTF8ToUTF16(kTitle), node->GetTitle()); @@ -2277,8 +2293,8 @@ StopSync(); // Reset transaction version on the native mode to "unset". - model_->SetNodeSyncTransactionVersion( - model_->root_node(), syncer::syncable::kInvalidTransactionVersion); + model()->SetNodeSyncTransactionVersion( + model()->root_node(), syncer::syncable::kInvalidTransactionVersion); // Restart sync. StartSync(); @@ -2287,9 +2303,9 @@ // Verify that the native transaction version has been updated and is now // in sync with the sync version. { - syncer::ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::ReadTransaction trans(FROM_HERE, test_user_share()->user_share()); int64_t sync_version = trans.GetModelVersion(syncer::BOOKMARKS); - int64_t native_version = model_->root_node()->sync_transaction_version(); + int64_t native_version = model()->root_node()->sync_transaction_version(); EXPECT_EQ(native_version, sync_version); } } @@ -2302,7 +2318,7 @@ StartSync(); // Create bookmark nodes containing meta info. - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); FakeServerChange adds(&trans); BookmarkNode::MetaInfoMap folder_meta_info; folder_meta_info["folder"] = "foldervalue"; @@ -2313,11 +2329,11 @@ node_meta_info["other"] = "othervalue"; int64_t id = adds.AddURLWithMetaInfo("node title", "http://www.foo.com", &node_meta_info, folder_id, 0); - adds.ApplyPendingChanges(change_processor_.get()); + adds.ApplyPendingChanges(change_processor()); // Verify that the nodes are created with the correct meta info. - ASSERT_LT(0, model_->bookmark_bar_node()->child_count()); - const BookmarkNode* folder_node = model_->bookmark_bar_node()->GetChild(0); + ASSERT_LT(0, model()->bookmark_bar_node()->child_count()); + const BookmarkNode* folder_node = model()->bookmark_bar_node()->GetChild(0); ASSERT_TRUE(folder_node->GetMetaInfoMap()); EXPECT_EQ(folder_meta_info, *folder_node->GetMetaInfoMap()); ASSERT_LT(0, folder_node->child_count()); @@ -2333,7 +2349,7 @@ node_meta_info.erase("other"); node_meta_info["newkey"] = "newkeyvalue"; updates.ModifyMetaInfo(id, node_meta_info); - updates.ApplyPendingChanges(change_processor_.get()); + updates.ApplyPendingChanges(change_processor()); // Confirm that the updated values are reflected in the bookmark nodes. EXPECT_FALSE(folder_node->GetMetaInfoMap()); @@ -2349,25 +2365,24 @@ StartSync(); ExpectBookmarkModelMatchesTestData(); - const BookmarkNode* folder_node = - model_->AddFolder(model_->bookmark_bar_node(), 0, - base::ASCIIToUTF16("folder title")); - const BookmarkNode* node = model_->AddURL(folder_node, 0, - base::ASCIIToUTF16("node title"), - GURL("http://www.foo.com")); + const BookmarkNode* folder_node = model()->AddFolder( + model()->bookmark_bar_node(), 0, base::ASCIIToUTF16("folder title")); + const BookmarkNode* node = + model()->AddURL(folder_node, 0, base::ASCIIToUTF16("node title"), + GURL("http://www.foo.com")); ExpectModelMatch(); // Add some meta info and verify sync model matches the changes. - model_->SetNodeMetaInfo(folder_node, "folder", "foldervalue"); - model_->SetNodeMetaInfo(node, "node", "nodevalue"); - model_->SetNodeMetaInfo(node, "other", "othervalue"); + model()->SetNodeMetaInfo(folder_node, "folder", "foldervalue"); + model()->SetNodeMetaInfo(node, "node", "nodevalue"); + model()->SetNodeMetaInfo(node, "other", "othervalue"); ExpectModelMatch(); // Change/delete existing meta info and verify. - model_->DeleteNodeMetaInfo(folder_node, "folder"); - model_->SetNodeMetaInfo(node, "node", "changednodevalue"); - model_->DeleteNodeMetaInfo(node, "other"); - model_->SetNodeMetaInfo(node, "newkey", "newkeyvalue"); + model()->DeleteNodeMetaInfo(folder_node, "folder"); + model()->SetNodeMetaInfo(node, "node", "changednodevalue"); + model()->DeleteNodeMetaInfo(node, "other"); + model()->SetNodeMetaInfo(node, "newkey", "newkeyvalue"); ExpectModelMatch(); } @@ -2385,7 +2400,7 @@ // Create bookmark folder node containing meta info. { - syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::WriteTransaction trans(FROM_HERE, test_user_share()->user_share()); FakeServerChange adds(&trans); int64_t folder_id = adds.AddFolder("folder title", bookmark_bar_id(), 0); @@ -2399,9 +2414,9 @@ &node_meta_info, folder_id, 0); // Verify that the node propagates to the bookmark model - adds.ApplyPendingChanges(change_processor_.get()); + adds.ApplyPendingChanges(change_processor()); - bookmark = model_->bookmark_bar_node()->GetChild(0)->GetChild(0); + bookmark = model()->bookmark_bar_node()->GetChild(0)->GetChild(0); EXPECT_EQ(node_meta_info, *bookmark->GetMetaInfoMap()); syncer::ReadNode sync_node(&trans); @@ -2410,11 +2425,11 @@ } // Force change processor to update the sync node. - change_processor_->BookmarkMetaInfoChanged(model_, bookmark); + change_processor()->BookmarkMetaInfoChanged(model(), bookmark); // Read bookmark specifics again and verify that there is no change. { - syncer::ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::ReadTransaction trans(FROM_HERE, test_user_share()->user_share()); syncer::ReadNode sync_node(&trans); EXPECT_EQ(BaseNode::INIT_OK, sync_node.InitByIdLookup(sync_id)); std::string new_specifics = @@ -2448,15 +2463,15 @@ void ProfileSyncServiceBookmarkTestWithData::ExpectTransactionVersionMatch( const BookmarkNode* node, const BookmarkNodeVersionMap& version_expected) { - syncer::ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::ReadTransaction trans(FROM_HERE, test_user_share()->user_share()); BookmarkNodeVersionMap bnodes_versions; GetTransactionVersions(node, &bnodes_versions); for (BookmarkNodeVersionMap::const_iterator it = bnodes_versions.begin(); it != bnodes_versions.end(); ++it) { syncer::ReadNode sync_node(&trans); - ASSERT_TRUE(model_associator_->InitSyncNodeFromChromeId(it->first, - &sync_node)); + ASSERT_TRUE( + model_associator()->InitSyncNodeFromChromeId(it->first, &sync_node)); EXPECT_EQ(sync_node.GetTransactionVersion(), it->second); BookmarkNodeVersionMap::const_iterator expected_ver_it = version_expected.find(it->first); @@ -2479,47 +2494,47 @@ // transaction version of root node) are equal after // WriteTestDataToBookmarkModel() created bookmarks. { - syncer::ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::ReadTransaction trans(FROM_HERE, test_user_share()->user_share()); EXPECT_GT(trans.GetModelVersion(syncer::BOOKMARKS), 0); - GetTransactionVersions(model_->root_node(), &initial_versions); + GetTransactionVersions(model()->root_node(), &initial_versions); EXPECT_EQ(trans.GetModelVersion(syncer::BOOKMARKS), - initial_versions[model_->root_node()->id()]); + initial_versions[model()->root_node()->id()]); } - ExpectTransactionVersionMatch(model_->bookmark_bar_node(), + ExpectTransactionVersionMatch(model()->bookmark_bar_node(), BookmarkNodeVersionMap()); - ExpectTransactionVersionMatch(model_->other_node(), + ExpectTransactionVersionMatch(model()->other_node(), BookmarkNodeVersionMap()); - ExpectTransactionVersionMatch(model_->mobile_node(), + ExpectTransactionVersionMatch(model()->mobile_node(), BookmarkNodeVersionMap()); // Verify model version is incremented and bookmark node versions remain // the same. - const BookmarkNode* bookmark_bar = model_->bookmark_bar_node(); - model_->Remove(bookmark_bar->GetChild(0)); + const BookmarkNode* bookmark_bar = model()->bookmark_bar_node(); + model()->Remove(bookmark_bar->GetChild(0)); base::MessageLoop::current()->RunUntilIdle(); BookmarkNodeVersionMap new_versions; - GetTransactionVersions(model_->root_node(), &new_versions); - EXPECT_EQ(initial_versions[model_->root_node()->id()] + 1, - new_versions[model_->root_node()->id()]); - ExpectTransactionVersionMatch(model_->bookmark_bar_node(), initial_versions); - ExpectTransactionVersionMatch(model_->other_node(), initial_versions); - ExpectTransactionVersionMatch(model_->mobile_node(), initial_versions); + GetTransactionVersions(model()->root_node(), &new_versions); + EXPECT_EQ(initial_versions[model()->root_node()->id()] + 1, + new_versions[model()->root_node()->id()]); + ExpectTransactionVersionMatch(model()->bookmark_bar_node(), initial_versions); + ExpectTransactionVersionMatch(model()->other_node(), initial_versions); + ExpectTransactionVersionMatch(model()->mobile_node(), initial_versions); // Verify model version and version of changed bookmark are incremented and // versions of others remain same. const BookmarkNode* changed_bookmark = - model_->bookmark_bar_node()->GetChild(0); - model_->SetTitle(changed_bookmark, base::ASCIIToUTF16("test")); + model()->bookmark_bar_node()->GetChild(0); + model()->SetTitle(changed_bookmark, base::ASCIIToUTF16("test")); base::MessageLoop::current()->RunUntilIdle(); - GetTransactionVersions(model_->root_node(), &new_versions); - EXPECT_EQ(initial_versions[model_->root_node()->id()] + 2, - new_versions[model_->root_node()->id()]); + GetTransactionVersions(model()->root_node(), &new_versions); + EXPECT_EQ(initial_versions[model()->root_node()->id()] + 2, + new_versions[model()->root_node()->id()]); EXPECT_LT(initial_versions[changed_bookmark->id()], new_versions[changed_bookmark->id()]); initial_versions.erase(changed_bookmark->id()); - ExpectTransactionVersionMatch(model_->bookmark_bar_node(), initial_versions); - ExpectTransactionVersionMatch(model_->other_node(), initial_versions); - ExpectTransactionVersionMatch(model_->mobile_node(), initial_versions); + ExpectTransactionVersionMatch(model()->bookmark_bar_node(), initial_versions); + ExpectTransactionVersionMatch(model()->other_node(), initial_versions); + ExpectTransactionVersionMatch(model()->mobile_node(), initial_versions); } // Test that sync persistence errors are detected and trigger a failed @@ -2536,23 +2551,24 @@ // transaction version of root node) are equal after // WriteTestDataToBookmarkModel() created bookmarks. { - syncer::ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); + syncer::ReadTransaction trans(FROM_HERE, test_user_share()->user_share()); EXPECT_GT(trans.GetModelVersion(syncer::BOOKMARKS), 0); - GetTransactionVersions(model_->root_node(), &initial_versions); + GetTransactionVersions(model()->root_node(), &initial_versions); EXPECT_EQ(trans.GetModelVersion(syncer::BOOKMARKS), - initial_versions[model_->root_node()->id()]); + initial_versions[model()->root_node()->id()]); } - ExpectTransactionVersionMatch(model_->bookmark_bar_node(), + ExpectTransactionVersionMatch(model()->bookmark_bar_node(), BookmarkNodeVersionMap()); - ExpectTransactionVersionMatch(model_->other_node(), + ExpectTransactionVersionMatch(model()->other_node(), BookmarkNodeVersionMap()); - ExpectTransactionVersionMatch(model_->mobile_node(), + ExpectTransactionVersionMatch(model()->mobile_node(), BookmarkNodeVersionMap()); // Now shut down sync and artificially increment the native model's version. StopSync(); - int64_t root_version = initial_versions[model_->root_node()->id()]; - model_->SetNodeSyncTransactionVersion(model_->root_node(), root_version + 1); + int64_t root_version = initial_versions[model()->root_node()->id()]; + model()->SetNodeSyncTransactionVersion(model()->root_node(), + root_version + 1); // Upon association, bookmarks should fail to associate. EXPECT_FALSE(AssociateModels()); @@ -2572,22 +2588,21 @@ // Now destroy the change processor then add a bookmark, to simulate // missing the Update call. - change_processor_.reset(); - const BookmarkNode* node = model_->AddURL(model_->bookmark_bar_node(), - 0, - base::ASCIIToUTF16("title"), - GURL("http://www.url.com")); + delete_change_processor(); + const BookmarkNode* node = + model()->AddURL(model()->bookmark_bar_node(), 0, + base::ASCIIToUTF16("title"), GURL("http://www.url.com")); // Recreate the change processor then update that bookmark. Sync should // receive the update call and gracefully treat that as if it were an add. - change_processor_.reset(new BookmarkChangeProcessor( - &sync_client_, model_associator_.get(), &mock_error_handler_)); - change_processor_->Start(test_user_share_.user_share()); - model_->SetTitle(node, base::ASCIIToUTF16("title2")); + ResetChangeProcessor(); + change_processor()->Start(test_user_share()->user_share()); + model()->SetTitle(node, base::ASCIIToUTF16("title2")); ExpectModelMatch(); // Then simulate the add call arriving late. - change_processor_->BookmarkNodeAdded(model_, model_->bookmark_bar_node(), 0); + change_processor()->BookmarkNodeAdded(model(), model()->bookmark_bar_node(), + 0); ExpectModelMatch(); } @@ -2601,18 +2616,18 @@ // Create a bookmark under managed_node() permanent folder. bookmarks::ManagedBookmarkService* managed_bookmark_service = - ManagedBookmarkServiceFactory::GetForProfile(&profile_); + GetManagedBookmarkService(); const BookmarkNode* folder = managed_bookmark_service->managed_node(); - const BookmarkNode* node = model_->AddURL( + const BookmarkNode* node = model()->AddURL( folder, 0, base::ASCIIToUTF16("node"), GURL("http://www.node.com/")); // Verify that these changes are ignored by Sync. EXPECT_EQ(sync_bookmark_count, GetSyncBookmarkCount()); - int64_t sync_id = model_associator_->GetSyncIdFromChromeId(node->id()); + int64_t sync_id = model_associator()->GetSyncIdFromChromeId(node->id()); EXPECT_EQ(syncer::kInvalidId, sync_id); // Verify that Sync ignores deleting this node. - model_->Remove(node); + model()->Remove(node); EXPECT_EQ(sync_bookmark_count, GetSyncBookmarkCount()); }
diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc deleted file mode 100644 index fdcf6b1..0000000 --- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc +++ /dev/null
@@ -1,1119 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <string> -#include <utility> -#include <vector> - -#include "testing/gtest/include/gtest/gtest.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/location.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "base/thread_task_runner_handle.h" -#include "base/threading/thread.h" -#include "base/time/time.h" -#include "chrome/browser/history/history_service_factory.h" -#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h" -#include "chrome/browser/signin/account_tracker_service_factory.h" -#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h" -#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" -#include "chrome/browser/signin/signin_manager_factory.h" -#include "chrome/browser/sync/abstract_profile_sync_service_test.h" -#include "chrome/browser/sync/chrome_sync_client.h" -#include "chrome/browser/sync/profile_sync_service_factory.h" -#include "chrome/browser/sync/profile_sync_test_util.h" -#include "chrome/browser/sync/test_profile_sync_service.h" -#include "chrome/common/pref_names.h" -#include "chrome/test/base/testing_browser_process.h" -#include "chrome/test/base/testing_profile.h" -#include "chrome/test/base/testing_profile_manager.h" -#include "components/browser_sync/browser/profile_sync_service.h" -#include "components/history/core/browser/history_backend.h" -#include "components/history/core/browser/history_backend_client.h" -#include "components/history/core/browser/history_backend_notifier.h" -#include "components/history/core/browser/history_db_task.h" -#include "components/history/core/browser/history_service.h" -#include "components/history/core/browser/history_types.h" -#include "components/history/core/browser/typed_url_data_type_controller.h" -#include "components/invalidation/impl/fake_invalidation_service.h" -#include "components/invalidation/impl/profile_invalidation_provider.h" -#include "components/invalidation/public/invalidation_service.h" -#include "components/keyed_service/core/refcounted_keyed_service.h" -#include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/fake_profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_manager.h" -#include "components/sync_driver/data_type_error_handler_mock.h" -#include "components/sync_driver/sync_api_component_factory_mock.h" -#include "components/syncable_prefs/pref_service_syncable.h" -#include "content/public/browser/notification_service.h" -#include "google_apis/gaia/gaia_constants.h" -#include "sync/internal_api/public/read_node.h" -#include "sync/internal_api/public/read_transaction.h" -#include "sync/internal_api/public/write_node.h" -#include "sync/internal_api/public/write_transaction.h" -#include "sync/protocol/typed_url_specifics.pb.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "url/gurl.h" - -using base::Thread; -using base::Time; -using browser_sync::TypedUrlDataTypeController; -using history::HistoryBackend; -using history::HistoryBackendNotifier; -using history::TypedUrlSyncableService; -using history::URLID; -using history::URLRow; -using syncer::syncable::WriteTransaction; -using testing::DoAll; -using testing::Return; -using testing::SetArgumentPointee; -using testing::_; - -namespace content { -class BrowserContext; -} - -namespace { - -const char kTestProfileName[] = "test-profile"; - -// Visits with this timestamp are treated as expired. -static const int EXPIRED_VISIT = -1; - -class HistoryBackendMock : public HistoryBackend { - public: - HistoryBackendMock() - : HistoryBackend(nullptr, nullptr, base::ThreadTaskRunnerHandle::Get()) {} - bool IsExpiredVisitTime(const base::Time& time) override { - return time.ToInternalValue() == EXPIRED_VISIT; - } - MOCK_METHOD1(GetAllTypedURLs, bool(history::URLRows* entries)); - MOCK_METHOD3(GetMostRecentVisitsForURL, bool(history::URLID id, - int max_visits, - history::VisitVector* visits)); - MOCK_METHOD2(UpdateURL, bool(history::URLID id, const history::URLRow& url)); - MOCK_METHOD3(AddVisits, bool(const GURL& url, - const std::vector<history::VisitInfo>& visits, - history::VisitSource visit_source)); - MOCK_METHOD1(RemoveVisits, bool(const history::VisitVector& visits)); - MOCK_METHOD2(GetURL, bool(const GURL& url_id, history::URLRow* url_row)); - MOCK_METHOD2(SetPageTitle, void(const GURL& url, - const base::string16& title)); - MOCK_METHOD1(DeleteURL, void(const GURL& url)); - - private: - friend class ProfileSyncServiceTypedUrlTest; - - virtual ~HistoryBackendMock() {} -}; - -class HistoryServiceMock : public history::HistoryService { - public: - HistoryServiceMock() : history::HistoryService(), backend_(nullptr) {} - - base::CancelableTaskTracker::TaskId ScheduleDBTask( - scoped_ptr<history::HistoryDBTask> task, - base::CancelableTaskTracker* tracker) override { - history::HistoryDBTask* task_raw = task.get(); - task_runner_->PostTaskAndReply( - FROM_HERE, - base::Bind(&HistoryServiceMock::RunTaskOnDBThread, - base::Unretained(this), task_raw), - base::Bind(&base::DeletePointer<history::HistoryDBTask>, - task.release())); - return base::CancelableTaskTracker::kBadTaskId; // unused - } - - MOCK_METHOD0(Shutdown, void()); - - MOCK_CONST_METHOD0(GetTypedUrlSyncableService, TypedUrlSyncableService*()); - - void ShutdownBaseService() { history::HistoryService::Shutdown(); } - - void set_task_runner( - scoped_refptr<base::SingleThreadTaskRunner> task_runner) { - DCHECK(task_runner.get()); - task_runner_ = task_runner; - } - - void set_backend(scoped_refptr<history::HistoryBackend> backend) { - backend_ = backend; - } - - private: - ~HistoryServiceMock() override {} - - void RunTaskOnDBThread(history::HistoryDBTask* task) { - EXPECT_TRUE(task->RunOnDBThread(backend_.get(), NULL)); - } - - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - scoped_refptr<history::HistoryBackend> backend_; -}; - -scoped_ptr<KeyedService> BuildFakeProfileInvalidationProvider( - content::BrowserContext* context) { - return make_scoped_ptr(new invalidation::ProfileInvalidationProvider( - scoped_ptr<invalidation::InvalidationService>( - new invalidation::FakeInvalidationService))); -} - -scoped_ptr<KeyedService> BuildHistoryService(content::BrowserContext* profile) { - return scoped_ptr<KeyedService>(new HistoryServiceMock); -} - -class TestTypedUrlSyncableService : public TypedUrlSyncableService { - // TODO(gangwu): remove TestProfileSyncService or even remove whole test - // suite, and make sure typed_url_syncable_service_unittest.cc and the various - // typed url integration tests. - public: - explicit TestTypedUrlSyncableService(history::HistoryBackend* history_backend) - : TypedUrlSyncableService(history_backend) {} - - static void WriteToSyncNode(const history::URLRow& url, - const history::VisitVector& visits, - syncer::WriteNode* node) { - sync_pb::TypedUrlSpecifics typed_url; - WriteToTypedUrlSpecifics(url, visits, &typed_url); - node->SetTypedUrlSpecifics(typed_url); - } - - protected: - // Don't clear error stats - that way we can verify their values in our - // tests. - void ClearErrorStats() override {} -}; - -ACTION_P2(ShutdownHistoryService, thread, service) { - service->ShutdownBaseService(); - delete thread; -} - -ACTION_P2(ReturnTypedUrlSyncableService, hb, syncable_service) { - syncable_service->reset(new TestTypedUrlSyncableService(hb)); - return syncable_service->get(); -} - -class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { - public: - void AddTypedUrlSyncNode(const history::URLRow& url, - const history::VisitVector& visits) { - syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - - syncer::WriteNode node(&trans); - std::string tag = url.url().spec(); - syncer::WriteNode::InitUniqueByCreationResult result = - node.InitUniqueByCreation(syncer::TYPED_URLS, tag); - ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result); - TestTypedUrlSyncableService::WriteToSyncNode(url, visits, &node); - } - - protected: - ProfileSyncServiceTypedUrlTest() - : profile_manager_(TestingBrowserProcess::GetGlobal()) { - history_thread_.reset(new Thread("history")); - } - - void SetUp() override { - AbstractProfileSyncServiceTest::SetUp(); - ASSERT_TRUE(profile_manager_.SetUp()); - TestingProfile::TestingFactories testing_factories; - testing_factories.push_back(std::make_pair( - ProfileOAuth2TokenServiceFactory::GetInstance(), - BuildAutoIssuingFakeProfileOAuth2TokenService)); - profile_ = profile_manager_.CreateTestingProfile( - kTestProfileName, - scoped_ptr<syncable_prefs::PrefServiceSyncable>(), - base::UTF8ToUTF16(kTestProfileName), - 0, - std::string(), - testing_factories); - invalidation::ProfileInvalidationProviderFactory::GetInstance()-> - SetTestingFactory(profile_, BuildFakeProfileInvalidationProvider); - history_thread_->Start(); - history_backend_ = new HistoryBackendMock(); - history_service_ = static_cast<HistoryServiceMock*>( - HistoryServiceFactory::GetInstance()->SetTestingFactoryAndUse( - profile_, BuildHistoryService)); - history_service_->set_task_runner(history_thread_->task_runner()); - history_service_->set_backend(history_backend_); - } - - void TearDown() override { - EXPECT_CALL((*history_service_), Shutdown()) - .WillOnce(ShutdownHistoryService(history_thread_.release(), - history_service_)); - profile_ = NULL; - profile_manager_.DeleteTestingProfile(kTestProfileName); - AbstractProfileSyncServiceTest::TearDown(); - } - - TypedUrlSyncableService* StartSyncService(const base::Closure& callback) { - if (!sync_service_) { - std::string account_id = - AccountTrackerServiceFactory::GetForProfile(profile_) - ->SeedAccountInfo("gaia_id", "test"); - SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); - signin->SetAuthenticatedAccountInfo("gaia_id", "test"); - sync_service_ = TestProfileSyncService::BuildAutoStartAsyncInit(profile_, - callback); - data_type_controller = new TypedUrlDataTypeController( - base::ThreadTaskRunnerHandle::Get(), base::Bind(&base::DoNothing), - sync_service_->GetSyncClient(), prefs::kSavingBrowserHistoryDisabled); - SyncApiComponentFactoryMock* components = - sync_service_->GetSyncApiComponentFactoryMock(); - - EXPECT_CALL(*components, CreateDataTypeManager(_, _, _, _, _)). - WillOnce(ReturnNewDataTypeManager()); - - EXPECT_CALL(*history_service_, GetTypedUrlSyncableService()) - .WillOnce(ReturnTypedUrlSyncableService(history_backend_.get(), - &syncable_service_)); - - ProfileOAuth2TokenService* oauth2_token_service = - ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); - oauth2_token_service->UpdateCredentials(account_id, "oauth2_login_token"); - - sync_service_->RegisterDataTypeController(data_type_controller); - - sync_service_->Initialize(); - base::MessageLoop::current()->Run(); - } - return syncable_service_.get(); - } - - void GetTypedUrlsFromSyncDB(history::URLRows* urls) { - urls->clear(); - syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); - syncer::ReadNode typed_url_root(&trans); - if (typed_url_root.InitTypeRoot(syncer::TYPED_URLS) != - syncer::BaseNode::INIT_OK) - return; - - int64_t child_id = typed_url_root.GetFirstChildId(); - while (child_id != syncer::kInvalidId) { - syncer::ReadNode child_node(&trans); - if (child_node.InitByIdLookup(child_id) != syncer::BaseNode::INIT_OK) - return; - - const sync_pb::TypedUrlSpecifics& typed_url( - child_node.GetTypedUrlSpecifics()); - history::URLRow new_url(GURL(typed_url.url())); - - new_url.set_title(base::UTF8ToUTF16(typed_url.title())); - DCHECK(typed_url.visits_size()); - DCHECK_EQ(typed_url.visits_size(), typed_url.visit_transitions_size()); - new_url.set_last_visit(base::Time::FromInternalValue( - typed_url.visits(typed_url.visits_size() - 1))); - new_url.set_hidden(typed_url.hidden()); - - urls->push_back(new_url); - child_id = child_node.GetSuccessorId(); - } - } - - void SetIdleChangeProcessorExpectations() { - EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)).Times(0); - EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)).Times(0); - EXPECT_CALL((*history_backend_.get()), GetURL(_, _)).Times(0); - EXPECT_CALL((*history_backend_.get()), DeleteURL(_)).Times(0); - } - - void SendNotification(const base::Closure& task) { - history_thread_->task_runner()->PostTaskAndReply( - FROM_HERE, - task, - base::Bind(&base::MessageLoop::QuitNow, - base::Unretained(base::MessageLoop::current()))); - base::MessageLoop::current()->Run(); - } - - void SendNotificationURLVisited(ui::PageTransition transition, - const history::URLRow& row) { - base::Time visit_time; - history::RedirectList redirects; - SendNotification( - base::Bind(&HistoryBackendNotifier::NotifyURLVisited, - base::Unretained(history_backend_.get()), - transition, - row, - redirects, - visit_time)); - } - - void SendNotificationURLsModified(const history::URLRows& rows) { - SendNotification(base::Bind(&HistoryBackendNotifier::NotifyURLsModified, - base::Unretained(history_backend_.get()), - rows)); - } - - void SendNotificationURLsDeleted(bool all_history, - bool expired, - const history::URLRows& deleted_rows, - const std::set<GURL>& favicon_urls) { - SendNotification(base::Bind(&HistoryBackendNotifier::NotifyURLsDeleted, - base::Unretained(history_backend_.get()), - all_history, expired, deleted_rows, - favicon_urls)); - } - - static bool URLsEqual(const history::URLRow& lhs, - const history::URLRow& rhs) { - // Only verify the fields we explicitly sync (i.e. don't verify typed_count - // or visit_count because we rely on the history DB to manage those values - // and they are left unchanged by HistoryBackendMock). - return (lhs.url().spec().compare(rhs.url().spec()) == 0) && - (lhs.title().compare(rhs.title()) == 0) && - (lhs.last_visit() == rhs.last_visit()) && - (lhs.hidden() == rhs.hidden()); - } - - static history::URLRow MakeTypedUrlEntry(const char* url, - const char* title, - int typed_count, - int64_t last_visit, - bool hidden, - history::VisitVector* visits) { - // Give each URL a unique ID, to mimic the behavior of the real database. - static int unique_url_id = 0; - GURL gurl(url); - URLRow history_url(gurl, ++unique_url_id); - history_url.set_title(base::UTF8ToUTF16(title)); - history_url.set_typed_count(typed_count); - history_url.set_last_visit( - base::Time::FromInternalValue(last_visit)); - history_url.set_hidden(hidden); - visits->push_back(history::VisitRow( - history_url.id(), history_url.last_visit(), 0, - ui::PAGE_TRANSITION_TYPED, 0)); - history_url.set_visit_count(visits->size()); - return history_url; - } - - scoped_ptr<Thread> history_thread_; - TestingProfileManager profile_manager_; - TestingProfile* profile_; - scoped_refptr<HistoryBackendMock> history_backend_; - HistoryServiceMock* history_service_; - sync_driver::DataTypeErrorHandlerMock error_handler_; - TypedUrlDataTypeController* data_type_controller; - scoped_ptr<TestTypedUrlSyncableService> syncable_service_; -}; - -void AddTypedUrlEntries(ProfileSyncServiceTypedUrlTest* test, - const history::URLRows& entries) { - test->CreateRoot(syncer::TYPED_URLS); - for (size_t i = 0; i < entries.size(); ++i) { - history::VisitVector visits; - visits.push_back(history::VisitRow( - entries[i].id(), entries[i].last_visit(), 0, - ui::PageTransitionFromInt(0), 0)); - test->AddTypedUrlSyncNode(entries[i], visits); - } -} - -} // namespace - -TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeEmptySync) { - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(Return(true)); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - TypedUrlSyncableService* syncable_service = - StartSyncService(create_root.callback()); - history::URLRows sync_entries; - GetTypedUrlsFromSyncDB(&sync_entries); - EXPECT_EQ(0U, sync_entries.size()); - ASSERT_EQ(0, syncable_service->GetErrorPercentage()); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) { - history::URLRows entries; - history::VisitVector visits; - entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar", - 2, 15, false, &visits)); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true))); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - TypedUrlSyncableService* syncable_service = - StartSyncService(create_root.callback()); - history::URLRows sync_entries; - GetTypedUrlsFromSyncDB(&sync_entries); - ASSERT_EQ(1U, sync_entries.size()); - EXPECT_TRUE(URLsEqual(entries[0], sync_entries[0])); - ASSERT_EQ(0, syncable_service->GetErrorPercentage()); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeErrorReadingVisits) { - history::URLRows entries; - history::VisitVector visits; - history::URLRow native_entry1(MakeTypedUrlEntry("http://foo.com", "bar", - 2, 15, false, &visits)); - history::URLRow native_entry2(MakeTypedUrlEntry("http://foo2.com", "bar", - 3, 15, false, &visits)); - entries.push_back(native_entry1); - entries.push_back(native_entry2); - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); - // Return an error from GetMostRecentVisitsForURL() for the second URL. - EXPECT_CALL((*history_backend_.get()), - GetMostRecentVisitsForURL(native_entry1.id(), _, _)). - WillRepeatedly(Return(true)); - EXPECT_CALL((*history_backend_.get()), - GetMostRecentVisitsForURL(native_entry2.id(), _, _)). - WillRepeatedly(Return(false)); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - history::URLRows sync_entries; - GetTypedUrlsFromSyncDB(&sync_entries); - ASSERT_EQ(1U, sync_entries.size()); - EXPECT_TRUE(URLsEqual(native_entry1, sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithBlankEmptySync) { - std::vector<history::URLRow> entries; - history::VisitVector visits; - // Add an empty URL. - entries.push_back(MakeTypedUrlEntry("", "bar", - 2, 15, false, &visits)); - entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar", - 2, 15, false, &visits)); - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true))); - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - std::vector<history::URLRow> sync_entries; - GetTypedUrlsFromSyncDB(&sync_entries); - // The empty URL should be ignored. - ASSERT_EQ(1U, sync_entries.size()); - EXPECT_TRUE(URLsEqual(entries[1], sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) { - history::VisitVector native_visits; - history::VisitVector sync_visits; - history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", - 2, 15, false, &native_visits)); - history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", - 3, 16, false, &sync_visits)); - - history::URLRows native_entries; - native_entries.push_back(native_entry); - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true))); - EXPECT_CALL((*history_backend_.get()), - AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(true)); - - history::URLRows sync_entries; - sync_entries.push_back(sync_entry); - - EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). - WillRepeatedly(Return(true)); - StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); - - std::map<std::string, history::URLRow> expected; - expected[native_entry.url().spec()] = native_entry; - expected[sync_entry.url().spec()] = sync_entry; - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - - EXPECT_TRUE(new_sync_entries.size() == expected.size()); - for (history::URLRows::iterator entry = new_sync_entries.begin(); - entry != new_sync_entries.end(); ++entry) { - EXPECT_TRUE(URLsEqual(expected[entry->url().spec()], *entry)); - } -} - -TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeExpiredSync) { - history::VisitVector sync_visits; - history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", - 3, EXPIRED_VISIT, false, - &sync_visits)); - history::URLRows sync_entries; - sync_entries.push_back(sync_entry); - - // Since all our URLs are expired, no backend calls to add new URLs will be - // made. - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(Return(true)); - SetIdleChangeProcessorExpectations(); - - StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) { - history::VisitVector native_visits; - history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", - 2, 15, false, &native_visits)); - history::VisitVector sync_visits; - history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "name", - 1, 17, false, &sync_visits)); - history::VisitVector merged_visits; - merged_visits.push_back(history::VisitRow( - sync_entry.id(), base::Time::FromInternalValue(15), 0, - ui::PageTransitionFromInt(0), 0)); - - history::URLRow merged_entry(MakeTypedUrlEntry("http://native.com", "name", - 2, 17, false, &merged_visits)); - - history::URLRows native_entries; - native_entries.push_back(native_entry); - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true))); - EXPECT_CALL((*history_backend_.get()), - AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true)); - - history::URLRows sync_entries; - sync_entries.push_back(sync_entry); - - EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). - WillRepeatedly(Return(true)); - EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)). - WillRepeatedly(Return()); - StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(merged_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithErrorHasSyncMerge) { - history::VisitVector native_visits; - history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "native", - 2, 15, false, &native_visits)); - history::VisitVector sync_visits; - history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "sync", - 1, 17, false, &sync_visits)); - - history::URLRows native_entries; - native_entries.push_back(native_entry); - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); - // Return an error getting the visits for the native URL. - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(Return(false)); - EXPECT_CALL((*history_backend_.get()), GetURL(_, _)). - WillRepeatedly(DoAll(SetArgumentPointee<1>(native_entry), Return(true))); - EXPECT_CALL((*history_backend_.get()), - AddVisits(_, _, history::SOURCE_SYNCED)). WillRepeatedly(Return(true)); - - history::URLRows sync_entries; - sync_entries.push_back(sync_entry); - - EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). - WillRepeatedly(Return(true)); - EXPECT_CALL((*history_backend_.get()), SetPageTitle(_, _)). - WillRepeatedly(Return()); - StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(sync_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) { - history::VisitVector added_visits; - history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", - 2, 15, false, &added_visits)); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(Return(true)); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); - - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::URLRows changed_urls; - changed_urls.push_back(added_entry); - SendNotificationURLsModified(changed_urls); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddWithBlank) { - history::VisitVector added_visits; - history::URLRow empty_entry(MakeTypedUrlEntry("", "entry", - 2, 15, false, &added_visits)); - history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", - 2, 15, false, &added_visits)); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(Return(true)); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); - - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::URLRows changed_urls; - changed_urls.push_back(empty_entry); - changed_urls.push_back(added_entry); - SendNotificationURLsModified(changed_urls); - - std::vector<history::URLRow> new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) { - history::VisitVector original_visits; - history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", - 2, 15, false, - &original_visits)); - history::URLRows original_entries; - original_entries.push_back(original_entry); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillOnce(DoAll(SetArgumentPointee<2>(original_visits), - Return(true))); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::VisitVector updated_visits; - history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", - 7, 17, false, - &updated_visits)); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillOnce(DoAll(SetArgumentPointee<2>(updated_visits), - Return(true))); - - history::URLRows changed_urls; - changed_urls.push_back(updated_entry); - SendNotificationURLsModified(changed_urls); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddFromVisit) { - history::VisitVector added_visits; - history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", - 2, 15, false, &added_visits)); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(Return(true)); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); - - SetIdleChangeProcessorExpectations(); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - SendNotificationURLVisited(ui::PAGE_TRANSITION_TYPED, added_entry); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdateFromVisit) { - history::VisitVector original_visits; - history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", - 2, 15, false, - &original_visits)); - history::URLRows original_entries; - original_entries.push_back(original_entry); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillOnce(DoAll(SetArgumentPointee<2>(original_visits), - Return(true))); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::VisitVector updated_visits; - history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", - 7, 17, false, - &updated_visits)); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillOnce(DoAll(SetArgumentPointee<2>(updated_visits), - Return(true))); - - SendNotificationURLVisited(ui::PAGE_TRANSITION_TYPED, updated_entry); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserIgnoreChangeUpdateFromVisit) { - history::VisitVector original_visits; - history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", - 2, 15, false, - &original_visits)); - history::URLRows original_entries; - original_entries.push_back(original_entry); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits), - Return(true))); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); - - history::VisitVector updated_visits; - history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", - 7, 15, false, - &updated_visits)); - - // Should ignore this change because it's not TYPED. - SendNotificationURLVisited(ui::PAGE_TRANSITION_RELOAD, updated_entry); - GetTypedUrlsFromSyncDB(&new_sync_entries); - - // Should be no changes to the sync DB from this notification. - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); - - // Now, try updating it with a large number of visits not divisible by 10 - // (should ignore this visit). - history::URLRow twelve_visits(MakeTypedUrlEntry("http://mine.com", "entry", - 12, 15, false, - &updated_visits)); - SendNotificationURLVisited(ui::PAGE_TRANSITION_TYPED, twelve_visits); - GetTypedUrlsFromSyncDB(&new_sync_entries); - - // Should be no changes to the sync DB from this notification. - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); - - // Now, try updating it with a large number of visits that is divisible by 10 - // (should *not* be ignored). - history::URLRow twenty_visits(MakeTypedUrlEntry("http://mine.com", "entry", - 20, 15, false, - &updated_visits)); - SendNotificationURLVisited(ui::PAGE_TRANSITION_TYPED, twenty_visits); - GetTypedUrlsFromSyncDB(&new_sync_entries); - - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(twenty_visits, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) { - history::VisitVector original_visits1; - history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", - 2, 15, false, - &original_visits1)); - history::VisitVector original_visits2; - history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", - "entry2", - 3, 15, false, - &original_visits2)); - history::URLRows original_entries; - original_entries.push_back(original_entry1); - original_entries.push_back(original_entry2); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1), - Return(true))); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::URLRows rows; - rows.push_back(history::URLRow(GURL("http://mine.com"))); - SendNotificationURLsDeleted(false, false, rows, std::set<GURL>()); - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(original_entry2, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveExpired) { - history::VisitVector original_visits1; - history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", - 2, 15, false, - &original_visits1)); - history::VisitVector original_visits2; - history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", - "entry2", - 3, 15, false, - &original_visits2)); - history::URLRows original_entries; - original_entries.push_back(original_entry1); - original_entries.push_back(original_entry2); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1), - Return(true))); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - // Setting expired=true should cause the sync code to ignore this deletion. - history::URLRows rows; - rows.push_back(history::URLRow(GURL("http://mine.com"))); - SendNotificationURLsDeleted(false, true, rows, std::set<GURL>()); - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - // Both URLs should still be there. - ASSERT_EQ(2U, new_sync_entries.size()); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) { - history::VisitVector original_visits1; - history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", - 2, 15, false, - &original_visits1)); - history::VisitVector original_visits2; - history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", - "entry2", - 3, 15, false, - &original_visits2)); - history::URLRows original_entries; - original_entries.push_back(original_entry1); - original_entries.push_back(original_entry2); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits1), - Return(true))); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(2U, new_sync_entries.size()); - - SendNotificationURLsDeleted(true, false, history::URLRows(), - std::set<GURL>()); - - GetTypedUrlsFromSyncDB(&new_sync_entries); - ASSERT_EQ(0U, new_sync_entries.size()); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, FailWriteToHistoryBackend) { - history::VisitVector native_visits; - history::VisitVector sync_visits; - history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", - 2, 15, false, &native_visits)); - history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", - 3, 16, false, &sync_visits)); - - history::URLRows native_entries; - native_entries.push_back(native_entry); - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); - EXPECT_CALL((*history_backend_.get()), GetURL(_, _)) - .WillOnce(DoAll(SetArgumentPointee<1>(native_entry), Return(false))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(native_visits), Return(true))); - EXPECT_CALL((*history_backend_.get()), - AddVisits(_, _, history::SOURCE_SYNCED)).WillRepeatedly(Return(false)); - - history::URLRows sync_entries; - sync_entries.push_back(sync_entry); - - EXPECT_CALL((*history_backend_.get()), UpdateURL(_, _)). - WillRepeatedly(Return(false)); - TypedUrlSyncableService* syncable_service = - StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); - // Errors writing to the DB should be recorded, but should not cause an - // unrecoverable error. - ASSERT_FALSE( - sync_service_->data_type_status_table().GetFailedTypes().Has( - syncer::TYPED_URLS)); - // Some calls should have succeeded, so the error percentage should be - // somewhere > 0 and < 100. - ASSERT_NE(0, syncable_service->GetErrorPercentage()); - ASSERT_NE(100, syncable_service->GetErrorPercentage()); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, FailToGetTypedURLs) { - history::VisitVector native_visits; - history::VisitVector sync_visits; - history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", - 2, 15, false, &native_visits)); - history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", - 3, 16, false, &sync_visits)); - - history::URLRows native_entries; - native_entries.push_back(native_entry); - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(false))); - - history::URLRows sync_entries; - sync_entries.push_back(sync_entry); - - StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); - // Errors getting typed URLs will cause an unrecoverable error (since we can - // do *nothing* in that case). - ASSERT_TRUE( - sync_service_->data_type_status_table().GetFailedTypes().Has( - syncer::TYPED_URLS)); - ASSERT_EQ( - 1u, sync_service_->data_type_status_table().GetFailedTypes().Size()); - // Can't check GetErrorPercentage(), because generating an unrecoverable - // error will free the model associator. -} - -TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalFileURL) { - history::VisitVector original_visits; - // Create http and file url. - history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com", - "yey", 12, 15, false, - &original_visits)); - history::URLRow file_entry(MakeTypedUrlEntry("file:///kitty.jpg", - "kitteh", 12, 15, false, - &original_visits)); - - history::URLRows original_entries; - original_entries.push_back(url_entry); - original_entries.push_back(file_entry); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillRepeatedly(DoAll(SetArgumentPointee<0>(original_entries), - Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits), - Return(true))); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::VisitVector updated_visits; - // Create updates for the previous urls + a new file one. - history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com", - "yey", 20, 15, false, - &updated_visits)); - history::URLRow updated_file_entry(MakeTypedUrlEntry("file:///cat.jpg", - "cat", 20, 15, false, - &updated_visits)); - history::URLRow new_file_entry(MakeTypedUrlEntry("file:///dog.jpg", - "dog", 20, 15, false, - &updated_visits)); - - history::URLRows changed_urls; - changed_urls.push_back(updated_url_entry); - changed_urls.push_back(updated_file_entry); - changed_urls.push_back(new_file_entry); - SendNotificationURLsModified(changed_urls); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - - // We should ignore the local file urls (existing and updated), - // and only be left with the updated http url. - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalhostURL) { - history::VisitVector original_visits; - // Create http and localhost url. - history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com", - "yey", 12, 15, false, - &original_visits)); - history::URLRow localhost_entry(MakeTypedUrlEntry("http://localhost", - "localhost", 12, 15, false, - &original_visits)); - - history::URLRows original_entries; - original_entries.push_back(url_entry); - original_entries.push_back(localhost_entry); - - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillRepeatedly(DoAll(SetArgumentPointee<0>(original_entries), - Return(true))); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(DoAll(SetArgumentPointee<2>(original_visits), - Return(true))); - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::VisitVector updated_visits; - // Update the previous entries and add a new localhost. - history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com", - "yey", 20, 15, false, - &updated_visits)); - history::URLRow updated_localhost_entry(MakeTypedUrlEntry( - "http://localhost:80", - "localhost", 20, 15, false, - &original_visits)); - history::URLRow localhost_ip_entry(MakeTypedUrlEntry("http://127.0.0.1", - "localhost", 12, 15, false, - &original_visits)); - - history::URLRows changed_urls; - changed_urls.push_back(updated_url_entry); - changed_urls.push_back(updated_localhost_entry); - changed_urls.push_back(localhost_ip_entry); - SendNotificationURLsModified(changed_urls); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - - // We should ignore the localhost urls and left only with http url. - ASSERT_EQ(1U, new_sync_entries.size()); - EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0])); -} - -TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreModificationWithoutValidVisit) { - EXPECT_CALL((*history_backend_.get()), GetAllTypedURLs(_)). - WillRepeatedly(Return(true)); - EXPECT_CALL((*history_backend_.get()), GetMostRecentVisitsForURL(_, _, _)). - WillRepeatedly(Return(true)); - - CreateRootHelper create_root(this, syncer::TYPED_URLS); - StartSyncService(create_root.callback()); - - history::VisitVector updated_visits; - history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com", - "yey", 20, 0, false, - &updated_visits)); - - history::URLRows changed_urls; - changed_urls.push_back(updated_url_entry); - SendNotificationURLsModified(changed_urls); - - history::URLRows new_sync_entries; - GetTypedUrlsFromSyncDB(&new_sync_entries); - - // The change should be ignored. - ASSERT_EQ(0U, new_sync_entries.size()); -}
diff --git a/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc index a5cda22..efb7788a 100644 --- a/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
@@ -3,13 +3,18 @@ // found in the LICENSE file. #include "base/macros.h" +#include "chrome/browser/sync/test/integration/await_match_status_change_checker.h" #include "chrome/browser/sync/test/integration/extensions_helper.h" #include "chrome/browser/sync/test/integration/sync_integration_test_util.h" #include "chrome/browser/sync/test/integration/sync_test.h" #include "components/browser_sync/browser/profile_sync_service.h" +#include "sync/test/fake_server/tombstone_entity.h" using extensions_helper::AllProfilesHaveSameExtensionsAsVerifier; +using extensions_helper::DisableExtension; +using extensions_helper::GetInstalledExtensions; using extensions_helper::InstallExtension; +using extensions_helper::InstallExtensionForAllProfiles; using sync_integration_test_util::AwaitCommitActivityCompletion; class SingleClientExtensionsSyncTest : public SyncTest { @@ -56,3 +61,48 @@ ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier()); } + +// Helper function for waiting to see the extension count in a profile +// become a specific number. +static bool ExtensionCountCheck(Profile* profile, size_t expected_count) { + return GetInstalledExtensions(profile).size() == expected_count; +} + +// Tests the case of an uninstall from the server conflicting with a local +// modification, which we expect to be resolved in favor of the uninstall. +IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, UninstallWinsConflicts) { + ASSERT_TRUE(SetupClients()); + + // Start with an extension installed, and setup sync. + InstallExtensionForAllProfiles(0); + ASSERT_TRUE(SetupSync()); + EXPECT_TRUE(AllProfilesHaveSameExtensionsAsVerifier()); + + // Simulate a delete at the server. + std::vector<sync_pb::SyncEntity> server_extensions = + GetFakeServer()->GetSyncEntitiesByModelType(syncer::EXTENSIONS); + ASSERT_EQ(1ul, server_extensions.size()); + std::string entity_id = server_extensions[0].id_string(); + scoped_ptr<fake_server::FakeServerEntity> tombstone( + fake_server::TombstoneEntity::Create(entity_id)); + GetFakeServer()->InjectEntity(std::move(tombstone)); + + // Modify the extension in the local profile to cause a conflict. + DisableExtension(GetProfile(0), 0); + EXPECT_EQ(1u, GetInstalledExtensions(GetProfile(0)).size()); + + // Trigger sync, and expect the extension to remain uninstalled at the server + // and get uninstalled locally. + const syncer::ModelTypeSet kExtensionsType(syncer::EXTENSIONS); + TriggerSyncForModelTypes(0, kExtensionsType); + server_extensions = + GetFakeServer()->GetSyncEntitiesByModelType(syncer::EXTENSIONS); + EXPECT_EQ(0ul, server_extensions.size()); + + AwaitMatchStatusChangeChecker checker( + base::Bind(&ExtensionCountCheck, GetProfile(0), 0u), + "Waiting for profile to have no extensions"); + checker.Wait(); + EXPECT_TRUE(!checker.TimedOut()); + EXPECT_TRUE(GetInstalledExtensions(GetProfile(0)).empty()); +}
diff --git a/chrome/browser/sync/test/test_http_bridge_factory.cc b/chrome/browser/sync/test/test_http_bridge_factory.cc deleted file mode 100644 index f9ee282..0000000 --- a/chrome/browser/sync/test/test_http_bridge_factory.cc +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/sync/test/test_http_bridge_factory.h" - -namespace browser_sync { - -bool TestHttpBridge::MakeSynchronousPost(int* error_code, - int* response_code) { - return false; -} - -int TestHttpBridge::GetResponseContentLength() const { - return 0; -} - -const char* TestHttpBridge::GetResponseContent() const { - return 0; -} - -const std::string TestHttpBridge::GetResponseHeaderValue( - const std::string &) const { - return std::string(); -} - -void TestHttpBridge::Abort() { -} - -TestHttpBridgeFactory::TestHttpBridgeFactory() {} - -TestHttpBridgeFactory::~TestHttpBridgeFactory() {} - -void TestHttpBridgeFactory::Init( - const std::string& user_agent, - const syncer::BindToTrackerCallback& bind_to_tracker_callback) {} - -syncer::HttpPostProviderInterface* TestHttpBridgeFactory::Create() { - return new TestHttpBridge(); -} - -void TestHttpBridgeFactory::Destroy(syncer::HttpPostProviderInterface* http) { - delete static_cast<TestHttpBridge*>(http); -} - -} // namespace browser_sync
diff --git a/chrome/browser/sync/test/test_http_bridge_factory.h b/chrome/browser/sync/test/test_http_bridge_factory.h deleted file mode 100644 index 4ee18555..0000000 --- a/chrome/browser/sync/test/test_http_bridge_factory.h +++ /dev/null
@@ -1,52 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_SYNC_TEST_TEST_HTTP_BRIDGE_FACTORY_H_ -#define CHROME_BROWSER_SYNC_TEST_TEST_HTTP_BRIDGE_FACTORY_H_ - -#include "base/compiler_specific.h" -#include "sync/internal_api/public/http_post_provider_factory.h" -#include "sync/internal_api/public/http_post_provider_interface.h" - -namespace browser_sync { - -class TestHttpBridge : public syncer::HttpPostProviderInterface { - public: - // Begin syncer::HttpPostProviderInterface implementation: - void SetExtraRequestHeaders(const char* headers) override {} - - void SetURL(const char* url, int port) override {} - - void SetPostPayload(const char* content_type, - int content_length, - const char* content) override {} - - bool MakeSynchronousPost(int* error_code, int* response_code) override; - - int GetResponseContentLength() const override; - - const char* GetResponseContent() const override; - - const std::string GetResponseHeaderValue(const std::string&) const override; - - void Abort() override; - // End syncer::HttpPostProviderInterface implementation. -}; - -class TestHttpBridgeFactory : public syncer::HttpPostProviderFactory { - public: - TestHttpBridgeFactory(); - ~TestHttpBridgeFactory() override; - - // syncer::HttpPostProviderFactory: - void Init( - const std::string& user_agent, - const syncer::BindToTrackerCallback& bind_to_tracker_callback) override; - syncer::HttpPostProviderInterface* Create() override; - void Destroy(syncer::HttpPostProviderInterface* http) override; -}; - -} // namespace browser_sync - -#endif // CHROME_BROWSER_SYNC_TEST_TEST_HTTP_BRIDGE_FACTORY_H_
diff --git a/chrome/browser/sync/test_profile_sync_service.cc b/chrome/browser/sync/test_profile_sync_service.cc deleted file mode 100644 index 6c3b9d4..0000000 --- a/chrome/browser/sync/test_profile_sync_service.cc +++ /dev/null
@@ -1,228 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/sync/test_profile_sync_service.h" - -#include <utility> - -#include "base/location.h" -#include "base/memory/scoped_ptr.h" -#include "base/single_thread_task_runner.h" -#include "base/thread_task_runner_handle.h" -#include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" -#include "chrome/browser/signin/signin_manager_factory.h" -#include "chrome/browser/sync/chrome_sync_client.h" -#include "chrome/browser/sync/profile_sync_service_factory.h" -#include "chrome/browser/sync/test/test_http_bridge_factory.h" -#include "chrome/common/channel_info.h" -#include "components/browser_sync/browser/profile_sync_test_util.h" -#include "components/invalidation/impl/profile_invalidation_provider.h" -#include "components/signin/core/browser/signin_manager.h" -#include "components/sync_driver/glue/sync_backend_host.h" -#include "components/sync_driver/glue/sync_backend_host_core.h" -#include "components/sync_driver/signin_manager_wrapper.h" -#include "components/sync_driver/sync_api_component_factory_mock.h" -#include "content/public/browser/browser_thread.h" -#include "google_apis/gaia/gaia_constants.h" -#include "sync/internal_api/public/test/sync_manager_factory_for_profile_sync_test.h" -#include "sync/internal_api/public/test/test_internal_components_factory.h" -#include "sync/internal_api/public/user_share.h" -#include "sync/protocol/encryption.pb.h" -#include "testing/gmock/include/gmock/gmock.h" - -using content::BrowserThread; -using syncer::InternalComponentsFactory; -using syncer::TestInternalComponentsFactory; -using syncer::UserShare; - -namespace { - -ProfileSyncService::InitParams GetInitParams( - Profile* profile, - SigninManagerBase* signin, - ProfileOAuth2TokenService* oauth2_token_service, - browser_sync::ProfileSyncServiceStartBehavior behavior) { - ProfileSyncService::InitParams init_params; - - init_params.signin_wrapper = - make_scoped_ptr(new SigninManagerWrapper(signin)); - init_params.oauth2_token_service = oauth2_token_service; - init_params.start_behavior = behavior; - init_params.sync_client = - make_scoped_ptr(new browser_sync::ChromeSyncClient(profile)); - init_params.network_time_update_callback = - base::Bind(&browser_sync::EmptyNetworkTimeUpdate); - init_params.base_directory = profile->GetPath(); - init_params.url_request_context = profile->GetRequestContext(); - init_params.debug_identifier = profile->GetDebugName(); - init_params.channel = chrome::GetChannel(); - init_params.db_thread = content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::DB); - init_params.file_thread = - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::FILE); - init_params.blocking_pool = content::BrowserThread::GetBlockingPool(); - - return init_params; -} - -} // namespace - -namespace browser_sync { - -SyncBackendHostForProfileSyncTest::SyncBackendHostForProfileSyncTest( - Profile* profile, - sync_driver::SyncClient* sync_client, - const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread, - invalidation::InvalidationService* invalidator, - const base::WeakPtr<sync_driver::SyncPrefs>& sync_prefs, - base::Closure callback) - : browser_sync::SyncBackendHostImpl( - profile->GetDebugName(), - sync_client, - ui_thread, - invalidator, - sync_prefs, - profile->GetPath().Append(base::FilePath(FILE_PATH_LITERAL("test")))), - callback_(callback) {} - -SyncBackendHostForProfileSyncTest::~SyncBackendHostForProfileSyncTest() {} - -void SyncBackendHostForProfileSyncTest::InitCore( - scoped_ptr<DoInitializeOptions> options) { - options->http_bridge_factory = - scoped_ptr<syncer::HttpPostProviderFactory>( - new browser_sync::TestHttpBridgeFactory()); - options->sync_manager_factory.reset( - new syncer::SyncManagerFactoryForProfileSyncTest(callback_)); - options->credentials.email = "testuser@gmail.com"; - options->credentials.sync_token = "token"; - options->credentials.scope_set.insert(GaiaConstants::kChromeSyncOAuth2Scope); - options->restored_key_for_bootstrapping = ""; - - // It'd be nice if we avoided creating the InternalComponentsFactory in the - // first place, but SyncBackendHost will have created one by now so we must - // free it. Grab the switches to pass on first. - InternalComponentsFactory::Switches factory_switches = - options->internal_components_factory->GetSwitches(); - options->internal_components_factory.reset( - new TestInternalComponentsFactory( - factory_switches, InternalComponentsFactory::STORAGE_IN_MEMORY, - NULL)); - - SyncBackendHostImpl::InitCore(std::move(options)); -} - -void SyncBackendHostForProfileSyncTest::RequestConfigureSyncer( - syncer::ConfigureReason reason, - syncer::ModelTypeSet to_download, - syncer::ModelTypeSet to_purge, - syncer::ModelTypeSet to_journal, - syncer::ModelTypeSet to_unapply, - syncer::ModelTypeSet to_ignore, - const syncer::ModelSafeRoutingInfo& routing_info, - const base::Callback<void(syncer::ModelTypeSet, - syncer::ModelTypeSet)>& ready_task, - const base::Closure& retry_callback) { - syncer::ModelTypeSet failed_configuration_types; - - // The first parameter there should be the set of enabled types. That's not - // something we have access to from this strange test harness. We'll just - // send back the list of newly configured types instead and hope it doesn't - // break anything. - // Posted to avoid re-entrancy issues. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&SyncBackendHostForProfileSyncTest:: - FinishConfigureDataTypesOnFrontendLoop, - base::Unretained(this), - syncer::Difference(to_download, failed_configuration_types), - syncer::Difference(to_download, failed_configuration_types), - failed_configuration_types, ready_task)); -} - -} // namespace browser_sync - -syncer::TestIdFactory* TestProfileSyncService::id_factory() { - return &id_factory_; -} - -syncer::WeakHandle<syncer::JsEventHandler> -TestProfileSyncService::GetJsEventHandler() { - return syncer::WeakHandle<syncer::JsEventHandler>(); -} - -TestProfileSyncService::TestProfileSyncService( - Profile* profile, - SigninManagerBase* signin, - ProfileOAuth2TokenService* oauth2_token_service, - browser_sync::ProfileSyncServiceStartBehavior behavior) - : ProfileSyncService( - GetInitParams(profile, signin, oauth2_token_service, behavior)) { - static_cast<browser_sync::ChromeSyncClient*>(GetSyncClient()) - ->SetSyncApiComponentFactoryForTesting( - make_scoped_ptr(new SyncApiComponentFactoryMock)); - SetFirstSetupComplete(); -} - -TestProfileSyncService::~TestProfileSyncService() { -} - -// static -scoped_ptr<KeyedService> TestProfileSyncService::TestFactoryFunction( - content::BrowserContext* context) { - Profile* profile = static_cast<Profile*>(context); - SigninManagerBase* signin = - SigninManagerFactory::GetForProfile(profile); - ProfileOAuth2TokenService* oauth2_token_service = - ProfileOAuth2TokenServiceFactory::GetForProfile(profile); - return make_scoped_ptr(new TestProfileSyncService( - profile, signin, oauth2_token_service, browser_sync::AUTO_START)); -} - -// static -TestProfileSyncService* TestProfileSyncService::BuildAutoStartAsyncInit( - Profile* profile, base::Closure callback) { - TestProfileSyncService* sync_service = static_cast<TestProfileSyncService*>( - ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse( - profile, &TestProfileSyncService::TestFactoryFunction)); - SyncApiComponentFactoryMock* components = - sync_service->GetSyncApiComponentFactoryMock(); - // TODO(tim): Convert to a fake instead of mock. - EXPECT_CALL(*components, CreateSyncBackendHost(testing::_, testing::_, - testing::_, testing::_)) - .WillOnce( - testing::Return(new browser_sync::SyncBackendHostForProfileSyncTest( - profile, sync_service->GetSyncClient(), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), - invalidation::ProfileInvalidationProviderFactory::GetForProfile( - profile) - ->GetInvalidationService(), - sync_service->sync_prefs_.AsWeakPtr(), callback))); - return sync_service; -} - -SyncApiComponentFactoryMock* -TestProfileSyncService::GetSyncApiComponentFactoryMock() { - // We always create a mock factory, see Build* routines. - return static_cast<SyncApiComponentFactoryMock*>( - GetSyncClient()->GetSyncApiComponentFactory()); -} - -void TestProfileSyncService::OnConfigureDone( - const sync_driver::DataTypeManager::ConfigureResult& result) { - ProfileSyncService::OnConfigureDone(result); - base::MessageLoop::current()->QuitWhenIdle(); -} - -UserShare* TestProfileSyncService::GetUserShare() const { - return backend_->GetUserShare(); -} - -bool TestProfileSyncService::NeedBackup() const { - return false; -}
diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h deleted file mode 100644 index 8eafbd6d..0000000 --- a/chrome/browser/sync/test_profile_sync_service.h +++ /dev/null
@@ -1,111 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_SYNC_TEST_PROFILE_SYNC_SERVICE_H_ -#define CHROME_BROWSER_SYNC_TEST_PROFILE_SYNC_SERVICE_H_ - -#include <string> - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/memory/weak_ptr.h" -#include "components/browser_sync/browser/profile_sync_service.h" -#include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/sync_driver/data_type_manager_impl.h" -#include "components/sync_driver/glue/sync_backend_host_impl.h" -#include "components/sync_driver/sync_client.h" -#include "components/sync_driver/sync_prefs.h" -#include "content/public/browser/browser_context.h" -#include "sync/test/engine/test_id_factory.h" -#include "testing/gmock/include/gmock/gmock.h" - -class Profile; -class ProfileOAuth2TokenService; -class SyncApiComponentFactoryMock; - -ACTION(ReturnNewDataTypeManager) { - return new sync_driver::DataTypeManagerImpl(arg0, arg1, arg2, arg3, arg4); -} - -namespace browser_sync { - -class SyncBackendHostForProfileSyncTest : public SyncBackendHostImpl { - public: - SyncBackendHostForProfileSyncTest( - Profile* profile, - sync_driver::SyncClient* sync_client, - const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread, - invalidation::InvalidationService* invalidator, - const base::WeakPtr<sync_driver::SyncPrefs>& sync_prefs, - base::Closure callback); - ~SyncBackendHostForProfileSyncTest() override; - - void RequestConfigureSyncer( - syncer::ConfigureReason reason, - syncer::ModelTypeSet to_download, - syncer::ModelTypeSet to_purge, - syncer::ModelTypeSet to_journal, - syncer::ModelTypeSet to_unapply, - syncer::ModelTypeSet to_ignore, - const syncer::ModelSafeRoutingInfo& routing_info, - const base::Callback<void(syncer::ModelTypeSet, syncer::ModelTypeSet)>& - ready_task, - const base::Closure& retry_callback) override; - - protected: - void InitCore(scoped_ptr<DoInitializeOptions> options) override; - - private: - // Invoked at the start of HandleSyncManagerInitializationOnFrontendLoop. - // Allows extra initialization work to be performed before the backend comes - // up. - base::Closure callback_; -}; - -} // namespace browser_sync - -class TestProfileSyncService : public ProfileSyncService { - public: - // TODO(tim): Add ability to inject TokenService alongside SigninManager. - // TODO(rogerta): what does above comment mean? - TestProfileSyncService( - Profile* profile, - SigninManagerBase* signin, - ProfileOAuth2TokenService* oauth2_token_service, - browser_sync::ProfileSyncServiceStartBehavior behavior); - - ~TestProfileSyncService() override; - - void OnConfigureDone( - const sync_driver::DataTypeManager::ConfigureResult& result) override; - - // We implement our own version to avoid some DCHECKs. - syncer::UserShare* GetUserShare() const override; - - static TestProfileSyncService* BuildAutoStartAsyncInit( - Profile* profile, base::Closure callback); - - SyncApiComponentFactoryMock* GetSyncApiComponentFactoryMock(); - - syncer::TestIdFactory* id_factory(); - - // Raise visibility to ease testing. - using ProfileSyncService::NotifyObservers; - - protected: - static scoped_ptr<KeyedService> TestFactoryFunction( - content::BrowserContext* profile); - - // Return NULL handle to use in backend initialization to avoid receiving - // js messages on UI loop when it's being destroyed, which are not deleted - // and cause memory leak in test. - syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler() override; - - bool NeedBackup() const override; - - private: - syncer::TestIdFactory id_factory_; -}; - -#endif // CHROME_BROWSER_SYNC_TEST_PROFILE_SYNC_SERVICE_H_
diff --git a/chrome/browser/task_management/providers/arc/arc_process_task_provider.cc b/chrome/browser/task_management/providers/arc/arc_process_task_provider.cc index 53a52d5..37e9d195 100644 --- a/chrome/browser/task_management/providers/arc/arc_process_task_provider.cc +++ b/chrome/browser/task_management/providers/arc/arc_process_task_provider.cc
@@ -162,6 +162,8 @@ } base::ProcessId pid = iter->second; scoped_ptr<ArcProcessTask>& task = nspid_to_task_[process.pid]; + if (task.get()) + NotifyObserverTaskRemoved(task.get()); task.reset(new ArcProcessTask(pid, process.pid, process.process_name)); NotifyObserverTaskAdded(task.get()); }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 49f67af..3e4feba4 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -216,8 +216,6 @@ "//components/arc", "//ui/base/ime", ] - } else { - defines += [ "FRAME_AVATAR_BUTTON=1" ] } if (use_cups) { configs += [ "//printing:cups" ]
diff --git a/chrome/browser/ui/android/bluetooth_chooser_android.cc b/chrome/browser/ui/android/bluetooth_chooser_android.cc index bbc794a2..1b5567df 100644 --- a/chrome/browser/ui/android/bluetooth_chooser_android.cc +++ b/chrome/browser/ui/android/bluetooth_chooser_android.cc
@@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ssl/chrome_security_state_model_client.h" #include "chrome/browser/ui/android/view_android_helper.h" +#include "chrome/common/url_constants.h" #include "content/public/browser/android/content_view_core.h" #include "content/public/browser/render_frame_host.h" #include "jni/BluetoothChooserDialog_jni.h" @@ -23,17 +24,18 @@ BluetoothChooserAndroid::BluetoothChooserAndroid( content::RenderFrameHost* frame, const EventHandler& event_handler) - : event_handler_(event_handler) { + : web_contents_(content::WebContents::FromRenderFrameHost(frame)), + event_handler_(event_handler) { const url::Origin origin = frame->GetLastCommittedOrigin(); DCHECK(!origin.unique()); - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(frame); + base::android::ScopedJavaLocalRef<jobject> window_android = - content::ContentViewCore::FromWebContents( - web_contents)->GetWindowAndroid()->GetJavaObject(); + content::ContentViewCore::FromWebContents(web_contents_) + ->GetWindowAndroid() + ->GetJavaObject(); ChromeSecurityStateModelClient* security_model_client = - ChromeSecurityStateModelClient::FromWebContents(web_contents); + ChromeSecurityStateModelClient::FromWebContents(web_contents_); DCHECK(security_model_client); // Create (and show) the BluetoothChooser dialog. @@ -133,24 +135,21 @@ void BluetoothChooserAndroid::ShowBluetoothOverviewLink( JNIEnv* env, const JavaParamRef<jobject>& obj) { + OpenURL(chrome::kChooserBluetoothOverviewURL); event_handler_.Run(Event::SHOW_OVERVIEW_HELP, ""); } -void BluetoothChooserAndroid::ShowBluetoothPairingLink( - JNIEnv* env, - const JavaParamRef<jobject>& obj) { - event_handler_.Run(Event::SHOW_PAIRING_HELP, ""); -} - void BluetoothChooserAndroid::ShowBluetoothAdapterOffLink( JNIEnv* env, const JavaParamRef<jobject>& obj) { + OpenURL(chrome::kChooserBluetoothOverviewURL); event_handler_.Run(Event::SHOW_ADAPTER_OFF_HELP, ""); } void BluetoothChooserAndroid::ShowNeedLocationPermissionLink( JNIEnv* env, const JavaParamRef<jobject>& obj) { + OpenURL(chrome::kChooserBluetoothOverviewURL); event_handler_.Run(Event::SHOW_NEED_LOCATION_HELP, ""); } @@ -158,3 +157,9 @@ bool BluetoothChooserAndroid::Register(JNIEnv* env) { return RegisterNativesImpl(env); } + +void BluetoothChooserAndroid::OpenURL(const char* url) { + web_contents_->OpenURL(content::OpenURLParams( + GURL(url), content::Referrer(), NEW_FOREGROUND_TAB, + ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false /* is_renderer_initiated */)); +}
diff --git a/chrome/browser/ui/android/bluetooth_chooser_android.h b/chrome/browser/ui/android/bluetooth_chooser_android.h index d090c9e..fc77e636c 100644 --- a/chrome/browser/ui/android/bluetooth_chooser_android.h +++ b/chrome/browser/ui/android/bluetooth_chooser_android.h
@@ -17,6 +17,7 @@ // options. class BluetoothChooserAndroid : public content::BluetoothChooser { public: + // Both frame and event_handler must outlive the BluetoothChooserAndroid. BluetoothChooserAndroid(content::RenderFrameHost* frame, const EventHandler& event_handler); ~BluetoothChooserAndroid() override; @@ -42,9 +43,6 @@ void ShowBluetoothOverviewLink( JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); - void ShowBluetoothPairingLink( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj); void ShowBluetoothAdapterOffLink( JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); @@ -55,8 +53,10 @@ static bool Register(JNIEnv* env); private: + void OpenURL(const char* url); base::android::ScopedJavaGlobalRef<jobject> java_dialog_; + content::WebContents* web_contents_; BluetoothChooser::EventHandler event_handler_; };
diff --git a/chrome/browser/ui/android/website_settings_popup_android.cc b/chrome/browser/ui/android/website_settings_popup_android.cc index 4ca14049..45b0321 100644 --- a/chrome/browser/ui/android/website_settings_popup_android.cc +++ b/chrome/browser/ui/android/website_settings_popup_android.cc
@@ -70,17 +70,6 @@ delete this; } -void WebsiteSettingsPopupAndroid::OnPermissionSettingChanged( - JNIEnv* env, - const JavaParamRef<jobject>& obj, - jint type, - jint setting) { - ContentSettingsType content_setting_type = - static_cast<ContentSettingsType>(type); - ContentSetting content_setting = static_cast<ContentSetting>(setting); - presenter_->OnSitePermissionChanged(content_setting_type, content_setting); -} - void WebsiteSettingsPopupAndroid::SetIdentityInfo( const IdentityInfo& identity_info) { JNIEnv* env = base::android::AttachCurrentThread();
diff --git a/chrome/browser/ui/android/website_settings_popup_android.h b/chrome/browser/ui/android/website_settings_popup_android.h index c11f6fc..2bf6701 100644 --- a/chrome/browser/ui/android/website_settings_popup_android.h +++ b/chrome/browser/ui/android/website_settings_popup_android.h
@@ -35,11 +35,6 @@ content::WebContents* web_contents); ~WebsiteSettingsPopupAndroid() override; void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); - void OnPermissionSettingChanged( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jint type, - jint setting); // WebsiteSettingsUI implementations. void SetCookieInfo(const CookieInfoList& cookie_info_list) override;
diff --git a/chrome/browser/ui/app_icon_loader.cc b/chrome/browser/ui/app_icon_loader.cc new file mode 100644 index 0000000..ff657ac --- /dev/null +++ b/chrome/browser/ui/app_icon_loader.cc
@@ -0,0 +1,14 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/app_icon_loader.h" + +AppIconLoader::AppIconLoader() {} + +AppIconLoader::AppIconLoader(Profile* profile, + int icon_size, + AppIconLoaderDelegate* delegate) + : profile_(profile), icon_size_(icon_size), delegate_(delegate) {} + +AppIconLoader::~AppIconLoader() {}
diff --git a/chrome/browser/ui/app_icon_loader.h b/chrome/browser/ui/app_icon_loader.h new file mode 100644 index 0000000..65a2ded --- /dev/null +++ b/chrome/browser/ui/app_icon_loader.h
@@ -0,0 +1,60 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_APP_ICON_LOADER_H_ +#define CHROME_BROWSER_UI_APP_ICON_LOADER_H_ + +#include <string> + +#include "base/macros.h" +#include "chrome/browser/ui/app_icon_loader_delegate.h" + +class Profile; + +namespace gfx { +class ImageSkia; +} + +// Base class that loads and updates Chrome app's icons. +class AppIconLoader { + public: + virtual ~AppIconLoader(); + + // Returns true is this AppIconLoader is able to load an image for the + // requested app. + virtual bool CanLoadImageForApp(const std::string& app_id) = 0; + + // Fetches the image for the specified id. When done (which may be + // synchronous), this should invoke SetAppImage() on the delegate. + virtual void FetchImage(const std::string& app_id) = 0; + + // Clears the image for the specified id. + virtual void ClearImage(const std::string& app_id) = 0; + + // Updates the image for the specified id. This is called to re-create + // the app icon with latest app state (enabled or disabled/terminiated). + // SetAppImage() is called when done. + virtual void UpdateImage(const std::string& app_id) = 0; + + protected: + AppIconLoader(); + AppIconLoader(Profile* profile, + int icon_size, + AppIconLoaderDelegate* delegate); + + Profile* profile() { return profile_; } + int icon_size() const { return icon_size_; } + AppIconLoaderDelegate* delegate() { return delegate_; } + + private: + Profile* const profile_ = nullptr; + const int icon_size_ = 0; + + // The delegate object which receives the icon images. No ownership. + AppIconLoaderDelegate* const delegate_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(AppIconLoader); +}; + +#endif // CHROME_BROWSER_UI_APP_ICON_LOADER_H_
diff --git a/chrome/browser/ui/app_icon_loader_delegate.h b/chrome/browser/ui/app_icon_loader_delegate.h new file mode 100644 index 0000000..e8f4bdb --- /dev/null +++ b/chrome/browser/ui/app_icon_loader_delegate.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 CHROME_BROWSER_UI_APP_ICON_LOADER_DELEGATE_H_ +#define CHROME_BROWSER_UI_APP_ICON_LOADER_DELEGATE_H_ + +#include <string> + +namespace gfx { +class ImageSkia; +} + +class AppIconLoaderDelegate { + public: + // Called when the image for an app is loaded. + virtual void OnAppImageUpdated(const std::string& app_id, + const gfx::ImageSkia& image) = 0; + + protected: + virtual ~AppIconLoaderDelegate() = default; +}; + +#endif // CHROME_BROWSER_UI_APP_ICON_LOADER_DELEGATE_H_
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.cc b/chrome/browser/ui/app_list/arc/arc_app_icon.cc index 97f3158..94e9d3f 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_icon.cc +++ b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
@@ -271,7 +271,7 @@ image_ = gfx::Image(image_skia_); - observer_->OnIconUpdated(); + observer_->OnIconUpdated(this); } void ArcAppIcon::DiscardDecodeRequest(DecodeRequest* request) {
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.h b/chrome/browser/ui/app_list/arc/arc_app_icon.h index e3f09b82..bd89381 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_icon.h +++ b/chrome/browser/ui/app_list/arc/arc_app_icon.h
@@ -31,7 +31,7 @@ public: // Invoked when a new image rep for an additional scale factor // is loaded and added to |image|. - virtual void OnIconUpdated() = 0; + virtual void OnIconUpdated(ArcAppIcon* icon) = 0; protected: virtual ~Observer() {} @@ -43,6 +43,7 @@ Observer* observer); ~ArcAppIcon(); + const std::string& app_id() const { return app_id_; } gfx::Image image() const { return image_; } const gfx::ImageSkia& image_skia() const { return image_skia_; }
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc new file mode 100644 index 0000000..29619649 --- /dev/null +++ b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
@@ -0,0 +1,83 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h" + +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" +#include "chrome/browser/ui/app_list/chrome_app_list_item.h" +#include "ui/app_list/app_list_constants.h" + +ArcAppIconLoader::ArcAppIconLoader(Profile* profile, + int icon_size, + AppIconLoaderDelegate* delegate) + : AppIconLoader(profile, icon_size, delegate) { + arc_prefs_ = ArcAppListPrefs::Get(profile); + arc_prefs_->AddObserver(this); +} + +ArcAppIconLoader::~ArcAppIconLoader() { + arc_prefs_->RemoveObserver(this); +} + +bool ArcAppIconLoader::CanLoadImageForApp(const std::string& app_id) { + if (icon_map_.find(app_id) != icon_map_.end()) + return true; + return ArcAppListPrefs::Get(profile())->IsRegistered(app_id); +} + +void ArcAppIconLoader::FetchImage(const std::string& app_id) { + if (icon_map_.find(app_id) != icon_map_.end()) + return; // Already loading the image. + + // Note, ARC icon is available only for 48x48 dips. In case |icon_size_| + // differs from this size, re-scale is required. + scoped_ptr<ArcAppIcon> icon(new ArcAppIcon(profile(), + app_id, + app_list::kGridIconDimension, + this)); + icon->image_skia().EnsureRepsForSupportedScales(); + icon_map_[app_id] = std::move(icon); +} + +void ArcAppIconLoader::ClearImage(const std::string& app_id) { + icon_map_.erase(app_id); +} + +void ArcAppIconLoader::UpdateImage(const std::string& app_id) { + AppIDToIconMap::iterator it = icon_map_.find(app_id); + if (it == icon_map_.end()) + return; + + scoped_ptr<ArcAppListPrefs::AppInfo> app_info = + ArcAppListPrefs::Get(profile())->GetApp(app_id); + if (!app_info || !app_info->ready) { + delegate()->OnAppImageUpdated(app_id, + ChromeAppListItem::CreateDisabledIcon(it->second->image_skia())); + } else { + delegate()->OnAppImageUpdated(app_id, it->second->image_skia()); + } + +} + +void ArcAppIconLoader::OnIconUpdated(ArcAppIcon* icon) { + UpdateImage(icon->app_id()); +} + +void ArcAppIconLoader::OnAppReadyChanged(const std::string& app_id, + bool ready) { + AppIDToIconMap::const_iterator it = icon_map_.find(app_id); + if (it == icon_map_.end()) + return; + + UpdateImage(app_id); +} + +void ArcAppIconLoader::OnAppIconUpdated(const std::string& app_id, + ui::ScaleFactor scale_factor) { + AppIDToIconMap::const_iterator it = icon_map_.find(app_id); + if (it == icon_map_.end()) + return; + it->second->LoadForScaleFactor(scale_factor); +}
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.h b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.h new file mode 100644 index 0000000..a56bb4de --- /dev/null +++ b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.h
@@ -0,0 +1,53 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_LOADER_H_ +#define CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_LOADER_H_ + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/app_icon_loader.h" +#include "chrome/browser/ui/app_list/arc/arc_app_icon.h" +#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" + +class Profile; + +class ArcAppIconLoader : public AppIconLoader, + public ArcAppListPrefs::Observer, + public ArcAppIcon::Observer { + public: + ArcAppIconLoader(Profile* profile, + int icon_size, + AppIconLoaderDelegate* delegate); + ~ArcAppIconLoader() override; + + // Overrides AppIconLoader: + bool CanLoadImageForApp(const std::string& app_id) override; + void FetchImage(const std::string& id) override; + void ClearImage(const std::string& id) override; + void UpdateImage(const std::string& id) override; + + // Overrides ArcAppListPrefs::Observer: + void OnAppReadyChanged(const std::string& id, bool ready) override; + void OnAppIconUpdated(const std::string& id, + ui::ScaleFactor scale_factor) override; + + // Overrides ArcAppIcon::Observer: + void OnIconUpdated(ArcAppIcon* icon) override; + + private: + using AppIDToIconMap = std::map<std::string, scoped_ptr<ArcAppIcon>>; + + // Unowned pointer. + ArcAppListPrefs* arc_prefs_; + + AppIDToIconMap icon_map_; + + DISALLOW_COPY_AND_ASSIGN(ArcAppIconLoader); +}; + +#endif // CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_LOADER_H_
diff --git a/chrome/browser/ui/app_list/arc/arc_app_item.cc b/chrome/browser/ui/app_list/arc/arc_app_item.cc index 8874bd1..b1f79d2 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_item.cc +++ b/chrome/browser/ui/app_list/arc/arc_app_item.cc
@@ -114,6 +114,6 @@ set_position(pos); } -void ArcAppItem::OnIconUpdated() { +void ArcAppItem::OnIconUpdated(ArcAppIcon* icon) { UpdateIcon(); }
diff --git a/chrome/browser/ui/app_list/arc/arc_app_item.h b/chrome/browser/ui/app_list/arc/arc_app_item.h index 228978d..246648b 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_item.h +++ b/chrome/browser/ui/app_list/arc/arc_app_item.h
@@ -39,7 +39,7 @@ void SetReady(bool ready); // ArcAppIcon::Observer - void OnIconUpdated() override; + void OnIconUpdated(ArcAppIcon* icon) override; private: // Updates the app item's icon, if necessary making it gray.
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc index 46650862..16f9077 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc +++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -115,22 +115,16 @@ binding_(this), weak_ptr_factory_(this) { base_path_ = base_path.AppendASCII(prefs::kArcApps); - if (!bridge_service_) { - NOTREACHED(); + if (!bridge_service_) return; - } bridge_service_->AddObserver(this); OnStateChanged(bridge_service_->state()); } ArcAppListPrefs::~ArcAppListPrefs() { - if (!bridge_service_) { - NOTREACHED(); - return; - } - - bridge_service_->RemoveObserver(this); + if (bridge_service_) + bridge_service_->RemoveObserver(this); } base::FilePath ArcAppListPrefs::GetAppPath(const std::string& app_id) const { @@ -255,12 +249,13 @@ } void ArcAppListPrefs::DisableAllApps() { - for (auto& app_id : ready_apps_) + std::set<std::string> old_ready_apps; + old_ready_apps.swap(ready_apps_); + for (auto& app_id : old_ready_apps) { FOR_EACH_OBSERVER(Observer, observer_list_, OnAppReadyChanged(app_id, false)); - - ready_apps_.clear(); + } } void ArcAppListPrefs::OnStateChanged(arc::ArcBridgeService::State state) {
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h index ef110d4..66341fc 100644 --- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h +++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
@@ -60,17 +60,17 @@ public: // Notifies an observer that new app is registered. virtual void OnAppRegistered(const std::string& app_id, - const AppInfo& app_info) = 0; + const AppInfo& app_info) {} // Notifies an observer that app ready state has been changed. - virtual void OnAppReadyChanged(const std::string& id, bool ready) = 0; + virtual void OnAppReadyChanged(const std::string& id, bool ready) {} // Notifies an observer that app was removed. - virtual void OnAppRemoved(const std::string& id) = 0; + virtual void OnAppRemoved(const std::string& id) {} // Notifies an observer that app icon has been installed or updated. virtual void OnAppIconUpdated(const std::string& id, - ui::ScaleFactor scale_factor) = 0; + ui::ScaleFactor scale_factor) {} // Notifies an observer that the name of an app has changed. virtual void OnAppNameUpdated(const std::string& id, - const std::string& name) = 0; + const std::string& name) {} protected: virtual ~Observer() {}
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.h b/chrome/browser/ui/app_list/chrome_app_list_item.h index 1845d34..2091bb8 100644 --- a/chrome/browser/ui/app_list/chrome_app_list_item.h +++ b/chrome/browser/ui/app_list/chrome_app_list_item.h
@@ -30,6 +30,8 @@ static void OverrideAppListControllerDelegateForTesting( AppListControllerDelegate* controller); + static gfx::ImageSkia CreateDisabledIcon(const gfx::ImageSkia& icon); + protected: ChromeAppListItem(Profile* profile, const std::string& app_id); ~ChromeAppListItem() override; @@ -44,8 +46,6 @@ void UpdateFromSync( const app_list::AppListSyncableService::SyncItem* sync_item); - static gfx::ImageSkia CreateDisabledIcon(const gfx::ImageSkia& icon); - private: Profile* profile_;
diff --git a/chrome/browser/ui/ash/ash_util.cc b/chrome/browser/ui/ash/ash_util.cc index 3571624d..8ec99ce 100644 --- a/chrome/browser/ui/ash/ash_util.cc +++ b/chrome/browser/ui/ash/ash_util.cc
@@ -19,7 +19,8 @@ bool ShouldOpenAshOnStartup() { #if defined(OS_CHROMEOS) && defined(MOJO_SHELL_CLIENT) - return !content::MojoShellConnection::Get(); + return !content::MojoShellConnection::Get() || + !content::MojoShellConnection::Get()->UsingExternalShell(); #elif defined(OS_CHROMEOS) return true; #else
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc index 5a1b913..daac60d 100644 --- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc +++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
@@ -29,6 +29,7 @@ #include "chrome/common/extensions/extension_constants.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" +#include "components/strings/grit/components_strings.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" #include "grit/ash_resources.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc index dd9c48e..b76d703 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -31,7 +31,7 @@ #include "chrome/browser/app_mode/app_mode_utils.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/defaults.h" -#include "chrome/browser/extensions/app_icon_loader_impl.h" +#include "chrome/browser/extensions/extension_app_icon_loader.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/launch_util.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" @@ -73,6 +73,7 @@ #include "components/favicon/content/content_favicon_driver.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/signin/core/account_id/account_id.h" +#include "components/strings/grit/components_strings.h" #include "components/syncable_prefs/pref_service_syncable.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" @@ -99,6 +100,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/browser_process.h" +#include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h" #include "chrome/browser/ui/ash/chrome_shell_delegate.h" #include "chrome/browser/ui/ash/launcher/multi_profile_app_window_launcher_controller.h" #include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h" @@ -855,8 +857,8 @@ return controller->app_id(); } -void ChromeLauncherController::SetAppImage(const std::string& id, - const gfx::ImageSkia& image) { +void ChromeLauncherController::OnAppImageUpdated(const std::string& id, + const gfx::ImageSkia& image) { // TODO: need to get this working for shortcuts. for (IDToItemControllerMap::const_iterator i = id_to_item_controller_map_.begin(); @@ -1157,20 +1159,13 @@ } if (window->IsActive() && allow_minimize) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableMinimizeOnSecondLauncherItemClick)) { - AnimateWindow(window->GetNativeWindow(), - wm::WINDOW_ANIMATION_TYPE_BOUNCE); - } else { - window->Minimize(); - return ash::ShelfItemDelegate::kExistingWindowMinimized; - } - } else { - window->Show(); - window->Activate(); - return ash::ShelfItemDelegate::kExistingWindowActivated; + window->Minimize(); + return ash::ShelfItemDelegate::kNoAction; } - return ash::ShelfItemDelegate::kNoAction; + + window->Show(); + window->Activate(); + return ash::ShelfItemDelegate::kExistingWindowActivated; } void ChromeLauncherController::OnShelfCreated(ash::Shelf* shelf) { @@ -1262,10 +1257,14 @@ void ChromeLauncherController::OnExtensionLoaded( content::BrowserContext* browser_context, const Extension* extension) { - if (IsAppPinned(extension->id())) { + const std::string& app_id = extension->id(); + if (IsAppPinned(app_id)) { // Clear and re-fetch to ensure icon is up-to-date. - app_icon_loader_->ClearImage(extension->id()); - app_icon_loader_->FetchImage(extension->id()); + AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); + if (app_icon_loader) { + app_icon_loader->ClearImage(app_id); + app_icon_loader->FetchImage(app_id); + } } UpdateAppLaunchersFromPref(); @@ -1275,24 +1274,27 @@ content::BrowserContext* browser_context, const Extension* extension, UnloadedExtensionInfo::Reason reason) { - const std::string& id = extension->id(); + const std::string& app_id = extension->id(); const Profile* profile = Profile::FromBrowserContext(browser_context); // Since we might have windowed apps of this type which might have // outstanding locks which needs to be removed. - if (GetShelfIDForAppID(id) && + if (GetShelfIDForAppID(app_id) && reason == UnloadedExtensionInfo::REASON_UNINSTALL) { - CloseWindowedAppsFromRemovedExtension(id, profile); + CloseWindowedAppsFromRemovedExtension(app_id, profile); } - if (IsAppPinned(id)) { + if (IsAppPinned(app_id)) { + AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); if (reason == UnloadedExtensionInfo::REASON_UNINSTALL) { if (profile == profile_) { - DoUnpinAppWithID(id); + DoUnpinAppWithID(app_id); } - app_icon_loader_->ClearImage(id); + if (app_icon_loader) + app_icon_loader->ClearImage(app_id); } else { - app_icon_loader_->UpdateImage(id); + if (app_icon_loader) + app_icon_loader->UpdateImage(app_id); } } } @@ -1465,9 +1467,11 @@ app_tab_helper_.reset(helper); } -void ChromeLauncherController::SetAppIconLoaderForTest( - extensions::AppIconLoader* loader) { - app_icon_loader_.reset(loader); +void ChromeLauncherController::SetAppIconLoadersForTest( + std::vector<scoped_ptr<AppIconLoader>>& loaders) { + app_icon_loaders_.clear(); + for (auto& loader : loaders) + app_icon_loaders_.push_back(std::move(loader)); } const std::string& ChromeLauncherController::GetAppIdFromShelfIdForTest( @@ -1593,7 +1597,10 @@ IDToItemControllerMap::iterator iter = id_to_item_controller_map_.find(id); CHECK(iter != id_to_item_controller_map_.end()); CHECK(iter->second); - app_icon_loader_->ClearImage(iter->second->app_id()); + const std::string& app_id = iter->second->app_id(); + AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); + if (app_icon_loader) + app_icon_loader->ClearImage(app_id); id_to_item_controller_map_.erase(iter); int index = model_->ItemIndexByID(id); // A "browser proxy" is not known to the model and this removal does @@ -1927,8 +1934,11 @@ model_->AddAt(index, item); - app_icon_loader_->FetchImage(app_id); - app_icon_loader_->UpdateImage(app_id); + AppIconLoader* app_icon_loader = GetAppIconLoaderForApp(app_id); + if (app_icon_loader) { + app_icon_loader->FetchImage(app_id); + app_icon_loader->UpdateImage(app_id); + } SetShelfItemDelegate(id, controller); @@ -2186,8 +2196,16 @@ // Since icon size changes are possible, the icon could be requested to be // reloaded. However - having it not multi profile aware would cause problems // if the icon cache gets deleted upon user switch. - app_icon_loader_.reset(new extensions::AppIconLoaderImpl( + scoped_ptr<AppIconLoader> extension_app_icon_loader( + new extensions::ExtensionAppIconLoader( + profile_, extension_misc::EXTENSION_ICON_SMALL, this)); + app_icon_loaders_.push_back(std::move(extension_app_icon_loader)); + +#if defined(OS_CHROMEOS) + scoped_ptr<AppIconLoader> arc_app_icon_loader(new ArcAppIconLoader( profile_, extension_misc::EXTENSION_ICON_SMALL, this)); + app_icon_loaders_.push_back(std::move(arc_app_icon_loader)); +#endif pref_change_registrar_.Init(profile_->GetPrefs()); pref_change_registrar_.Add( @@ -2231,3 +2249,13 @@ pref_change_registrar_.RemoveAll(); } + +AppIconLoader* ChromeLauncherController::GetAppIconLoaderForApp( + const std::string& app_id) { + for (const auto& app_icon_loader : app_icon_loaders_) { + if (app_icon_loader->CanLoadImageForApp(app_id)) + return app_icon_loader.get(); + } + + return nullptr; +}
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h index 29dab76e..98d99e6 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -25,7 +25,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "build/build_config.h" -#include "chrome/browser/extensions/app_icon_loader.h" +#include "chrome/browser/ui/app_icon_loader.h" #include "chrome/browser/ui/ash/app_sync_ui_state_observer.h" #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h" #include "chrome/browser/ui/ash/launcher/chrome_launcher_types.h" @@ -90,7 +90,7 @@ public ash::ShellObserver, public ash::WindowTreeHostManager::Observer, public extensions::ExtensionRegistryObserver, - public extensions::AppIconLoader::Delegate, + public AppIconLoaderDelegate, public syncable_prefs::PrefServiceSyncableObserver, public AppSyncUIStateObserver, public ExtensionEnableFlowDelegate, @@ -330,9 +330,9 @@ void ExtensionEnableFlowFinished() override; void ExtensionEnableFlowAborted(bool user_initiated) override; - // extensions::AppIconLoader: - void SetAppImage(const std::string& app_id, - const gfx::ImageSkia& image) override; + // AppIconLoaderDelegate: + void OnAppImageUpdated(const std::string& app_id, + const gfx::ImageSkia& image) override; // ash::ShelfLayoutManagerObserver: void OnAutoHideBehaviorChanged( @@ -416,7 +416,8 @@ // Sets the AppTabHelper/AppIconLoader, taking ownership of the helper class. // These are intended for testing. void SetAppTabHelperForTest(AppTabHelper* helper); - void SetAppIconLoaderForTest(extensions::AppIconLoader* loader); + void SetAppIconLoadersForTest( + std::vector<scoped_ptr<AppIconLoader>>& loaders); const std::string& GetAppIdFromShelfIdForTest(ash::ShelfID id); // Sets the ash::ShelfItemDelegateManager only for unittests and doesn't @@ -551,6 +552,8 @@ // Forget the current profile to allow attaching to a new one. void ReleaseProfile(); + AppIconLoader* GetAppIconLoaderForApp(const std::string& app_id); + static ChromeLauncherController* instance_; ash::ShelfModel* model_; @@ -573,8 +576,8 @@ // Used to get app info for tabs. scoped_ptr<AppTabHelper> app_tab_helper_; - // Used to load the image for an app item. - scoped_ptr<extensions::AppIconLoader> app_icon_loader_; + // Used to load the image for an extension app item. + std::vector<scoped_ptr<AppIconLoader>> app_icon_loaders_; PrefChangeRegistrar pref_change_registrar_;
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc index 56283f9..824f2dc 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -377,26 +377,6 @@ DISALLOW_COPY_AND_ASSIGN(ShelfAppBrowserTestNoDefaultBrowser); }; -// Since the default for minimizing on click might change, I added both classes -// to either get the minimize on click or not. -class ShelfAppBrowserNoMinimizeOnClick : public LauncherPlatformAppBrowserTest { - protected: - ShelfAppBrowserNoMinimizeOnClick() {} - ~ShelfAppBrowserNoMinimizeOnClick() override {} - - void SetUpCommandLine(base::CommandLine* command_line) override { - LauncherPlatformAppBrowserTest::SetUpCommandLine(command_line); - command_line->AppendSwitch( - switches::kDisableMinimizeOnSecondLauncherItemClick); - } - - private: - - DISALLOW_COPY_AND_ASSIGN(ShelfAppBrowserNoMinimizeOnClick); -}; - -typedef LauncherPlatformAppBrowserTest ShelfAppBrowserMinimizeOnClick; - // Test that we can launch a platform app and get a running item. IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, LaunchUnpinned) { int item_count = shelf_model()->item_count(); @@ -690,45 +670,8 @@ EXPECT_EQ(item_count, shelf_model()->item_count()); } -// Confirm that Click behavior for app windows is correnct. -IN_PROC_BROWSER_TEST_F(ShelfAppBrowserNoMinimizeOnClick, AppClickBehavior) { - // Launch a platform app and create a window for it. - const Extension* extension1 = LoadAndLaunchPlatformApp("launch", "Launched"); - AppWindow* window1 = CreateAppWindow(extension1); - EXPECT_TRUE(window1->GetNativeWindow()->IsVisible()); - EXPECT_TRUE(window1->GetBaseWindow()->IsActive()); - // Confirm that a controller item was created and is the correct state. - const ash::ShelfItem& item1 = GetLastLauncherItem(); - LauncherItemController* item1_controller = GetItemController(item1.id); - EXPECT_EQ(ash::TYPE_PLATFORM_APP, item1.type); - EXPECT_EQ(ash::STATUS_ACTIVE, item1.status); - EXPECT_EQ(LauncherItemController::TYPE_APP, item1_controller->type()); - // Clicking the item should have no effect. - TestEvent click_event(ui::ET_MOUSE_PRESSED); - item1_controller->ItemSelected(click_event); - EXPECT_TRUE(window1->GetNativeWindow()->IsVisible()); - EXPECT_TRUE(window1->GetBaseWindow()->IsActive()); - // Minimize the window and confirm that the controller item is updated. - window1->GetBaseWindow()->Minimize(); - EXPECT_FALSE(window1->GetNativeWindow()->IsVisible()); - EXPECT_FALSE(window1->GetBaseWindow()->IsActive()); - EXPECT_EQ(ash::STATUS_RUNNING, item1.status); - // Clicking the item should activate the window. - item1_controller->ItemSelected(click_event); - EXPECT_TRUE(window1->GetNativeWindow()->IsVisible()); - EXPECT_TRUE(window1->GetBaseWindow()->IsActive()); - EXPECT_EQ(ash::STATUS_ACTIVE, item1.status); - // Maximizing a window should preserve state after minimize + click. - window1->GetBaseWindow()->Maximize(); - window1->GetBaseWindow()->Minimize(); - item1_controller->ItemSelected(click_event); - EXPECT_TRUE(window1->GetNativeWindow()->IsVisible()); - EXPECT_TRUE(window1->GetBaseWindow()->IsActive()); - EXPECT_TRUE(window1->GetBaseWindow()->IsMaximized()); -} - // Confirm the minimizing click behavior for apps. -IN_PROC_BROWSER_TEST_F(ShelfAppBrowserMinimizeOnClick, +IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, PackagedAppClickBehaviorInMinimizeMode) { // Launch one platform app and create a window for it. const Extension* extension1 = LoadAndLaunchPlatformApp("launch", "Launched");
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc index 2d3e3a9..4fe95ec 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -6,6 +6,7 @@ #include <stddef.h> #include <algorithm> +#include <set> #include <string> #include <utility> #include <vector> @@ -151,24 +152,31 @@ }; // Test implementation of AppIconLoader. -class TestAppIconLoaderImpl : public extensions::AppIconLoader { +class TestAppIconLoaderImpl : public AppIconLoader { public: - TestAppIconLoaderImpl() : fetch_count_(0) { - } + TestAppIconLoaderImpl() = default; + ~TestAppIconLoaderImpl() override = default; - ~TestAppIconLoaderImpl() override {} + void AddSupportedApp(const std::string& id) { supported_apps_.insert(id); } // AppIconLoader implementation: + bool CanLoadImageForApp(const std::string& id) override { + return supported_apps_.find(id) != supported_apps_.end(); + } + void FetchImage(const std::string& id) override { ++fetch_count_; } - void ClearImage(const std::string& id) override {} + void ClearImage(const std::string& id) override { ++clear_count_; } void UpdateImage(const std::string& id) override {} int fetch_count() const { return fetch_count_; } + int clear_count() const { return clear_count_; } private: - int fetch_count_; + int fetch_count_ = 0; + int clear_count_ = 0; + std::set<std::string> supported_apps_; DISALLOW_COPY_AND_ASSIGN(TestAppIconLoaderImpl); }; @@ -466,8 +474,18 @@ browser()->window()->Show(); } - void SetAppIconLoader(extensions::AppIconLoader* loader) { - launcher_controller_->SetAppIconLoaderForTest(loader); + void SetAppIconLoader(scoped_ptr<AppIconLoader> loader) { + std::vector<scoped_ptr<AppIconLoader>> loaders; + loaders.push_back(std::move(loader)); + launcher_controller_->SetAppIconLoadersForTest(loaders); + } + + void SetAppIconLoaders(scoped_ptr<AppIconLoader> loader1, + scoped_ptr<AppIconLoader> loader2) { + std::vector<scoped_ptr<AppIconLoader>> loaders; + loaders.push_back(std::move(loader1)); + loaders.push_back(std::move(loader2)); + launcher_controller_->SetAppIconLoadersForTest(loaders); } void SetAppTabHelper(ChromeLauncherController::AppTabHelper* helper) { @@ -2533,11 +2551,13 @@ // App list and Browser shortcut ShelfItems are added. EXPECT_EQ(2, model_observer_->added()); + const std::string app_id = extension1_->id(); + // app_icon_loader is owned by ChromeLauncherController. TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl(); - SetAppIconLoader(app_icon_loader); + app_icon_loader->AddSupportedApp(app_id); + SetAppIconLoader(scoped_ptr<AppIconLoader>(app_icon_loader)); // Test adding an app panel - std::string app_id = extension1_->id(); AppWindowLauncherItemController* app_panel_controller = new AppWindowLauncherItemController( LauncherItemController::TYPE_APP_PANEL, @@ -2558,7 +2578,7 @@ // Setting the app image image should not change the panel if it set its icon app_panel_controller->set_image_set_by_controller(true); gfx::ImageSkia image; - launcher_controller_->SetAppImage(app_id, image); + launcher_controller_->OnAppImageUpdated(app_id, image); EXPECT_EQ(0, model_observer_->changed()); model_observer_->clear_counts(); @@ -2718,8 +2738,10 @@ app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); SetAppTabHelper(app_tab_helper); + // app_icon_loader is owned by ChromeLauncherController. TestAppIconLoaderImpl* app_icon_loader = new TestAppIconLoaderImpl; - SetAppIconLoader(app_icon_loader); + app_icon_loader->AddSupportedApp("1"); + SetAppIconLoader(scoped_ptr<AppIconLoader>(app_icon_loader)); EXPECT_EQ(0, app_icon_loader->fetch_count()); launcher_controller_->PinAppWithID("1"); @@ -2748,8 +2770,10 @@ app_tab_helper = new TestAppTabHelperImpl; app_tab_helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1"); SetAppTabHelper(app_tab_helper); + // app_icon_loader is owned by ChromeLauncherController. app_icon_loader = new TestAppIconLoaderImpl; - SetAppIconLoader(app_icon_loader); + app_icon_loader->AddSupportedApp("1"); + SetAppIconLoader(scoped_ptr<AppIconLoader>(app_icon_loader)); if (!ash::Shell::HasInstance()) { item_delegate_manager_ = new ash::ShelfItemDelegateManager(model_.get()); SetShelfItemDelegateManager(item_delegate_manager_); @@ -2765,3 +2789,72 @@ launcher_controller_->UnpinAppWithID("1"); ASSERT_EQ(initial_size, model_->items().size()); } + +TEST_F(ChromeLauncherControllerTest, MultipleAppIconLoaders) { + InitLauncherControllerWithBrowser(); + + const std::string app_id1 = extension1_->id(); + const std::string app_id2 = extension2_->id(); + const std::string app_id3 = extension3_->id(); + // app_icon_loader1 and app_icon_loader2 are owned by + // ChromeLauncherController. + TestAppIconLoaderImpl* app_icon_loader1 = new TestAppIconLoaderImpl(); + TestAppIconLoaderImpl* app_icon_loader2 = new TestAppIconLoaderImpl(); + app_icon_loader1->AddSupportedApp(app_id1); + app_icon_loader2->AddSupportedApp(app_id2); + SetAppIconLoaders(scoped_ptr<AppIconLoader>(app_icon_loader1), + scoped_ptr<AppIconLoader>(app_icon_loader2)); + + AppWindowLauncherItemController* app_panel_controller3 = + new AppWindowLauncherItemController( + LauncherItemController::TYPE_APP_PANEL, "id", app_id3, + launcher_controller_.get()); + const ash::ShelfID shelfId3 = launcher_controller_->CreateAppLauncherItem( + app_panel_controller3, app_id3, ash::STATUS_RUNNING); + EXPECT_EQ(0, app_icon_loader1->fetch_count()); + EXPECT_EQ(0, app_icon_loader1->clear_count()); + EXPECT_EQ(0, app_icon_loader2->fetch_count()); + EXPECT_EQ(0, app_icon_loader2->clear_count()); + + AppWindowLauncherItemController* app_panel_controller2 = + new AppWindowLauncherItemController( + LauncherItemController::TYPE_APP_PANEL, "id", app_id2, + launcher_controller_.get()); + const ash::ShelfID shelfId2 = launcher_controller_->CreateAppLauncherItem( + app_panel_controller2, app_id2, ash::STATUS_RUNNING); + EXPECT_EQ(0, app_icon_loader1->fetch_count()); + EXPECT_EQ(0, app_icon_loader1->clear_count()); + EXPECT_EQ(1, app_icon_loader2->fetch_count()); + EXPECT_EQ(0, app_icon_loader2->clear_count()); + + // Test adding an app panel + AppWindowLauncherItemController* app_panel_controller1 = + new AppWindowLauncherItemController( + LauncherItemController::TYPE_APP_PANEL, "id", app_id1, + launcher_controller_.get()); + + const ash::ShelfID shelfId1 = launcher_controller_->CreateAppLauncherItem( + app_panel_controller1, app_id1, ash::STATUS_RUNNING); + EXPECT_EQ(1, app_icon_loader1->fetch_count()); + EXPECT_EQ(0, app_icon_loader1->clear_count()); + EXPECT_EQ(1, app_icon_loader2->fetch_count()); + EXPECT_EQ(0, app_icon_loader2->clear_count()); + + launcher_controller_->CloseLauncherItem(shelfId1); + EXPECT_EQ(1, app_icon_loader1->fetch_count()); + EXPECT_EQ(1, app_icon_loader1->clear_count()); + EXPECT_EQ(1, app_icon_loader2->fetch_count()); + EXPECT_EQ(0, app_icon_loader2->clear_count()); + + launcher_controller_->CloseLauncherItem(shelfId2); + EXPECT_EQ(1, app_icon_loader1->fetch_count()); + EXPECT_EQ(1, app_icon_loader1->clear_count()); + EXPECT_EQ(1, app_icon_loader2->fetch_count()); + EXPECT_EQ(1, app_icon_loader2->clear_count()); + + launcher_controller_->CloseLauncherItem(shelfId3); + EXPECT_EQ(1, app_icon_loader1->fetch_count()); + EXPECT_EQ(1, app_icon_loader1->clear_count()); + EXPECT_EQ(1, app_icon_loader2->fetch_count()); + EXPECT_EQ(1, app_icon_loader2->clear_count()); +}
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_util.cc b/chrome/browser/ui/ash/multi_user/multi_user_util.cc index f9b4655..c273936 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_util.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_util.cc
@@ -15,6 +15,7 @@ #include "google_apis/gaia/gaia_auth_util.h" #if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h" #include "components/user_manager/user_manager.h" #endif @@ -36,7 +37,7 @@ Profile* GetProfileFromAccountId(const AccountId& account_id) { // Unit tests can end up here without a |g_browser_process|. if (!g_browser_process || !g_browser_process->profile_manager()) - return NULL; + return nullptr; std::vector<Profile*> profiles = g_browser_process->profile_manager()->GetLoadedProfiles(); @@ -46,7 +47,21 @@ if (GetAccountIdFromProfile(*profile_iterator) == account_id) return *profile_iterator; } - return NULL; + +#if defined(OS_CHROMEOS) + // If in a session the refresh token is revoked, GetAccountIdFromProfile() + // returns an empty account id which will cause the profile not being fetched + // properly. In this case we fall back to use GetProfileByUser() function. + // Note: If the refresh token is revoked because the user changes his GAIA + // password, we will force log out the user within 120 seconds. See crbug.com/ + // 587318 for more detail. + const user_manager::User* user = + user_manager::UserManager::Get()->FindUser(account_id); + if (user) + return chromeos::ProfileHelper::Get()->GetProfileByUser(user); +#endif + + return nullptr; } Profile* GetProfileFromWindow(aura::Window* window) {
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc index fb915b2..bf12d330 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
@@ -24,7 +24,6 @@ #include "chrome/browser/chromeos/login/session/user_session_manager.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h" #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" #include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h" @@ -32,8 +31,6 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window.h" -#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h" -#include "components/keyed_service/core/keyed_service_shutdown_notifier.h" #include "content/public/browser/notification_service.h" #include "extensions/browser/app_window/app_window.h" #include "extensions/browser/app_window/app_window_registry.h" @@ -147,29 +144,6 @@ ash::MultiProfileUMA::RecordTeleportWindowType(window_type); } -// This is used to monitor profile destruction. -class MultiUserWindowManagerCrOSShutdownNotifierFactory - : public BrowserContextKeyedServiceShutdownNotifierFactory { - public: - static MultiUserWindowManagerCrOSShutdownNotifierFactory* GetInstance() { - return base::Singleton< - MultiUserWindowManagerCrOSShutdownNotifierFactory>::get(); - } - - private: - friend struct base::DefaultSingletonTraits< - MultiUserWindowManagerCrOSShutdownNotifierFactory>; - - MultiUserWindowManagerCrOSShutdownNotifierFactory() - : BrowserContextKeyedServiceShutdownNotifierFactory( - "MultiUserWindowManagerChromeOS") { - DependsOn(SigninManagerFactory::GetInstance()); - } - ~MultiUserWindowManagerCrOSShutdownNotifierFactory() override {} - - DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerCrOSShutdownNotifierFactory); -}; - } // namespace namespace chrome { @@ -218,11 +192,7 @@ // window observer will take care of that. class AppObserver : public extensions::AppWindowRegistry::Observer { public: - explicit AppObserver( - const std::string& user_id, - KeyedServiceShutdownNotifier::Subscription* shutdown_notification) - : user_id_(user_id), - profile_shutdown_notification_(shutdown_notification) {} + explicit AppObserver(const std::string& user_id) : user_id_(user_id) {} ~AppObserver() override {} // AppWindowRegistry::Observer overrides: @@ -236,12 +206,6 @@ private: std::string user_id_; - // This notification is triggered when the profile gets destroyed (during - // shutdown process). - // The callback (stored in notification) destroyes AppObserver object. - scoped_ptr<KeyedServiceShutdownNotifier::Subscription> - profile_shutdown_notification_; - DISALLOW_COPY_AND_ASSIGN(AppObserver); }; @@ -268,21 +232,28 @@ window = window_to_entry_.begin(); } - // Remove all app observers. - AccountIdToAppWindowObserver::iterator app_observer_iterator = - account_id_to_app_observer_.begin(); - while (app_observer_iterator != account_id_to_app_observer_.end()) { - Profile* profile = - multi_user_util::GetProfileFromAccountId(app_observer_iterator->first); - CHECK(profile) << "profile not found for:" - << app_observer_iterator->first.GetUserEmail(); - RemoveUser(app_observer_iterator->first, profile); - app_observer_iterator = account_id_to_app_observer_.begin(); - } - if (ash::Shell::HasInstance()) ash::Shell::GetInstance()->session_state_delegate()-> RemoveSessionStateObserver(this); + + // Remove all app observers. + ProfileManager* profile_manager = g_browser_process->profile_manager(); + // might be nullptr in unit tests. + if (!profile_manager) + return; + + std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles(); + for (auto it = profiles.begin(); it != profiles.end(); ++it) { + const AccountId account_id = multi_user_util::GetAccountIdFromProfile(*it); + AccountIdToAppWindowObserver::iterator app_observer_iterator = + account_id_to_app_observer_.find(account_id); + if (app_observer_iterator != account_id_to_app_observer_.end()) { + extensions::AppWindowRegistry::Get(*it)->RemoveObserver( + app_observer_iterator->second); + delete app_observer_iterator->second; + account_id_to_app_observer_.erase(app_observer_iterator); + } + } } void MultiUserWindowManagerChromeOS::Init() { @@ -411,15 +382,8 @@ account_id_to_app_observer_.end()) return; - scoped_ptr<KeyedServiceShutdownNotifier::Subscription> notification = - MultiUserWindowManagerCrOSShutdownNotifierFactory::GetInstance() - ->Get(profile) - ->Subscribe(base::Bind(&MultiUserWindowManagerChromeOS::RemoveUser, - base::Unretained(this), account_id, - base::Unretained(profile))); - account_id_to_app_observer_[account_id] = - new AppObserver(account_id.GetUserEmail(), notification.release()); + new AppObserver(account_id.GetUserEmail()); extensions::AppWindowRegistry::Get(profile) ->AddObserver(account_id_to_app_observer_[account_id]); @@ -790,20 +754,4 @@ (animation_speed_ == ANIMATION_SPEED_FAST ? 10 : 0); } -void MultiUserWindowManagerChromeOS::RemoveUser(const AccountId& account_id, - Profile* profile) { - AccountIdToAppWindowObserver::iterator app_observer_iterator = - account_id_to_app_observer_.find(account_id); - DCHECK(app_observer_iterator != account_id_to_app_observer_.end()) - << "User id '" << account_id.GetUserEmail() << "', profile=" << profile - << " was not found."; - if (app_observer_iterator == account_id_to_app_observer_.end()) - return; - - extensions::AppWindowRegistry::Get(profile) - ->RemoveObserver(app_observer_iterator->second); - delete app_observer_iterator->second; - account_id_to_app_observer_.erase(app_observer_iterator); -} - } // namespace chrome
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h index c623f6d..e1a729a 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
@@ -23,7 +23,6 @@ class Browser; class MultiUserNotificationBlockerChromeOS; class MultiUserNotificationBlockerChromeOSTest; -class Profile; namespace content { class BrowserContext; @@ -224,12 +223,6 @@ // from the passed |default_time_in_ms|. int GetAdjustedAnimationTimeInMS(int default_time_in_ms) const; - // This is called when KeyedService (for |account_id| and |profile|) is - // destroyed, or when MultiUserWindowManagerChromeOS is destroyed. - // This happens on shutdown, before profile prefs are stored to - // disk. - void RemoveUser(const AccountId& account_id, Profile* profile); - // A lookup to see to which user the given window belongs to, where and if it // should get shown. WindowToEntryMap window_to_entry_;
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc index 21f264a..830ee5b 100644 --- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc +++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -146,6 +146,13 @@ LOG(ERROR) << "Unable to serialize one accessibility event."; return; } + + // Make sure the focused node is serialized. + views::AXAuraObjWrapper* focus = + views::AXAuraObjCache::GetInstance()->GetFocus(); + if (focus) + current_tree_serializer_->SerializeChanges(focus, ¶ms.update); + params.tree_id = 0; params.id = aura_obj->GetID(); params.event_type = event_type;
diff --git a/chrome/browser/ui/aura/accessibility/ax_tree_source_aura.cc b/chrome/browser/ui/aura/accessibility/ax_tree_source_aura.cc index 5723ec55..544bbf2e 100644 --- a/chrome/browser/ui/aura/accessibility/ax_tree_source_aura.cc +++ b/chrome/browser/ui/aura/accessibility/ax_tree_source_aura.cc
@@ -63,6 +63,9 @@ tree_data.tree_id = 0; tree_data.loaded = true; tree_data.loading_progress = 1.0; + AXAuraObjWrapper* focus = AXAuraObjCache::GetInstance()->GetFocus(); + if (focus) + tree_data.focus_id = focus->GetID(); return tree_data; }
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index b5f04ec..59da215 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -1628,6 +1628,8 @@ const gfx::Point& location, bool motion, bool exited) { + exclusive_access_manager_->OnUserInput(); + // Mouse motion events update the status bubble, if it exists. if (!GetStatusBubble() || (!motion && !exited)) return;
diff --git a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h index 5cefe8f..3dcb1b0 100644 --- a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h +++ b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.h
@@ -31,7 +31,6 @@ target:(id)target action:(SEL)action; - (NSTextField*)addTitleLabel:(NSString*)title toView:(NSView*)view; -- (NSTextField*)addLabel:(NSString*)title toView:(NSView*)view; - (void)bubbleWillDisappear; // Returns the default button for the bubble.
diff --git a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm index a30a30d..096d5293 100644 --- a/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm +++ b/chrome/browser/ui/cocoa/passwords/base_passwords_content_view_controller.mm
@@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/mac/scoped_nsobject.h" #import "chrome/browser/ui/cocoa/passwords/passwords_bubble_utils.h" +#include "ui/base/resource/resource_bundle.h" @implementation BasePasswordsContentViewController @synthesize delegate = delegate_; @@ -38,19 +39,15 @@ [label setDrawsBackground:NO]; [label setBezeled:NO]; [label setStringValue:title]; + [label setFont:ResourceBundle::GetSharedInstance() + .GetFontList(ResourceBundle::MediumFont) + .GetPrimaryFont() + .GetNativeFont()]; [label sizeToFit]; [view addSubview:label.get()]; return label.autorelease(); } -- (NSTextField*)addLabel:(NSString*)title toView:(NSView*)view { - NSTextField* label = [self addTitleLabel:title toView:view]; - NSFont* font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; - [label setFont:font]; - [[label cell] setWraps:YES]; - return label; -} - - (void)bubbleWillDisappear { }
diff --git a/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.mm b/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.mm index 5bab804..56eb4c6a 100644 --- a/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.mm +++ b/chrome/browser/ui/cocoa/passwords/pending_password_view_controller.mm
@@ -109,15 +109,17 @@ base::scoped_nsobject<NSTextField> warm_welcome; if ([self shouldShowGoogleSmartLockWelcome]) { - NSString* label_text = - l10n_util::GetNSString(IDS_PASSWORD_MANAGER_SMART_LOCK_WELCOME); - warm_welcome.reset([[self addLabel:label_text - toView:view] retain]); + base::string16 label_text = + l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SMART_LOCK_WELCOME); + warm_welcome.reset([[NSTextField alloc] initWithFrame:NSZeroRect]); + InitLabel(warm_welcome.get(), label_text); + [[warm_welcome cell] setWraps:YES]; [warm_welcome setFrameSize:NSMakeSize(kDesiredBubbleWidth - 2*kFramePadding, MAXFLOAT)]; [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:warm_welcome]; - NSColor* color = skia::SkColorToCalibratedNSColor(kWarmWelcomeColor); + NSColor* color = skia::SkColorToSRGBNSColor(kWarmWelcomeColor); [warm_welcome setTextColor:color]; + [view addSubview:warm_welcome.get()]; } NSArray* buttons = [self createButtonsAndAddThemToView:view];
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.mm b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.mm index 1d57ef710..dab060a6 100644 --- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.mm +++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.mm
@@ -11,6 +11,7 @@ #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" #include "components/grit/components_scaled_resources.h" +#include "components/strings/grit/components_strings.h" #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" #import "ui/base/cocoa/controls/blue_label_button.h" #import "ui/base/cocoa/controls/hyperlink_text_view.h"
diff --git a/chrome/browser/ui/exclusive_access/exclusive_access_bubble.cc b/chrome/browser/ui/exclusive_access/exclusive_access_bubble.cc index b3d1684..69a0ad0 100644 --- a/chrome/browser/ui/exclusive_access/exclusive_access_bubble.cc +++ b/chrome/browser/ui/exclusive_access/exclusive_access_bubble.cc
@@ -74,9 +74,9 @@ hide_timeout_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kInitialDelayMs), this, &ExclusiveAccessBubble::CheckMousePosition); + + last_mouse_pos_ = GetCursorScreenPoint(); } - gfx::Point cursor_pos = GetCursorScreenPoint(); - last_mouse_pos_ = cursor_pos; mouse_position_checker_.Start( FROM_HERE, base::TimeDelta::FromMilliseconds(1000 / kPositionCheckHz), this, &ExclusiveAccessBubble::CheckMousePosition); @@ -116,11 +116,12 @@ // With the "simplified" flag, we ignore all this and just show and hide based // on timers (not mouse position). - gfx::Point cursor_pos = GetCursorScreenPoint(); + gfx::Point cursor_pos; + if (!ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled()) { + cursor_pos = GetCursorScreenPoint(); - // Check to see whether the mouse is idle. - if (cursor_pos != last_mouse_pos_) { - if (!ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled()) { + // Check to see whether the mouse is idle. + if (cursor_pos != last_mouse_pos_) { // OnUserInput() will reset the idle timer in simplified mode. In classic // mode, we need to do this here. idle_timeout_.Stop(); // If the timer isn't running, this is a no-op. @@ -128,10 +129,8 @@ base::TimeDelta::FromMilliseconds(kIdleTimeMs), this, &ExclusiveAccessBubble::CheckMousePosition); } - - OnUserInput(); + last_mouse_pos_ = cursor_pos; } - last_mouse_pos_ = cursor_pos; if (ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled() || !IsWindowActive() || !WindowContainsPoint(cursor_pos) ||
diff --git a/chrome/browser/ui/exclusive_access/exclusive_access_bubble.h b/chrome/browser/ui/exclusive_access/exclusive_access_bubble.h index f5bc4db..be715c5 100644 --- a/chrome/browser/ui/exclusive_access/exclusive_access_bubble.h +++ b/chrome/browser/ui/exclusive_access/exclusive_access_bubble.h
@@ -136,7 +136,8 @@ base::RepeatingTimer mouse_position_checker_; // The most recently seen mouse position, in screen coordinates. Used to see - // if the mouse has moved since our last check. + // if the mouse has moved since our last check. Only used in non-simplified + // fullscreen mode. gfx::Point last_mouse_pos_; DISALLOW_COPY_AND_ASSIGN(ExclusiveAccessBubble);
diff --git a/chrome/browser/ui/exclusive_access/exclusive_access_manager.cc b/chrome/browser/ui/exclusive_access/exclusive_access_manager.cc index 8537e5b..52251a5 100644 --- a/chrome/browser/ui/exclusive_access/exclusive_access_manager.cc +++ b/chrome/browser/ui/exclusive_access/exclusive_access_manager.cc
@@ -121,7 +121,7 @@ bool ExclusiveAccessManager::HandleUserKeyPress( const content::NativeWebKeyboardEvent& event) { if (event.windowsKeyCode != ui::VKEY_ESCAPE) { - exclusive_access_context_->OnExclusiveAccessUserInput(); + OnUserInput(); return false; } @@ -131,6 +131,10 @@ return handled; } +void ExclusiveAccessManager::OnUserInput() { + exclusive_access_context_->OnExclusiveAccessUserInput(); +} + void ExclusiveAccessManager::OnAcceptExclusiveAccessPermission() { bool updateBubble = mouse_lock_controller_.OnAcceptExclusiveAccessPermission();
diff --git a/chrome/browser/ui/exclusive_access/exclusive_access_manager.h b/chrome/browser/ui/exclusive_access/exclusive_access_manager.h index 0a91afe6..712a1c1 100644 --- a/chrome/browser/ui/exclusive_access/exclusive_access_manager.h +++ b/chrome/browser/ui/exclusive_access/exclusive_access_manager.h
@@ -66,6 +66,9 @@ // Called by Browser::PreHandleKeyboardEvent. bool HandleUserKeyPress(const content::NativeWebKeyboardEvent& event); + // Called by Browser::ContentsMouseEvent. + void OnUserInput(); + // Called by platform ExclusiveAccessExitBubble. void OnAcceptExclusiveAccessPermission(); void OnDenyExclusiveAccessPermission();
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc index fd4b921..d1baeac 100644 --- a/chrome/browser/ui/search/search_tab_helper.cc +++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -38,6 +38,7 @@ #include "components/omnibox/browser/omnibox_view.h" #include "components/search/search.h" #include "components/signin/core/browser/signin_manager.h" +#include "components/strings/grit/components_strings.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/ui/search/search_tab_helper_unittest.cc b/chrome/browser/ui/search/search_tab_helper_unittest.cc index 8b01e87..98e05ff 100644 --- a/chrome/browser/ui/search/search_tab_helper_unittest.cc +++ b/chrome/browser/ui/search/search_tab_helper_unittest.cc
@@ -36,6 +36,7 @@ #include "components/browser_sync/browser/profile_sync_service.h" #include "components/omnibox/common/omnibox_focus_state.h" #include "components/search_engines/template_url_service.h" +#include "components/strings/grit/components_strings.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/sync/one_click_signin_sync_observer_unittest.cc b/chrome/browser/ui/sync/one_click_signin_sync_observer_unittest.cc index b5edd31..5a5c80b 100644 --- a/chrome/browser/ui/sync/one_click_signin_sync_observer_unittest.cc +++ b/chrome/browser/ui/sync/one_click_signin_sync_observer_unittest.cc
@@ -4,6 +4,9 @@ #include "chrome/browser/ui/sync/one_click_signin_sync_observer.h" +#include <string> +#include <utility> + #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" @@ -12,9 +15,10 @@ #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/signin/signin_promo.h" #include "chrome/browser/sync/profile_sync_service_factory.h" -#include "chrome/browser/sync/test_profile_sync_service.h" +#include "chrome/browser/sync/profile_sync_test_util.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_profile.h" +#include "components/browser_sync/browser/test_profile_sync_service.h" #include "components/signin/core/browser/signin_manager.h" #include "components/sync_driver/startup_controller.h" #include "content/public/browser/render_frame_host.h" @@ -50,8 +54,9 @@ // Helper routine to be used in conjunction with // BrowserContextKeyedServiceFactory::SetTestingFactory(). static scoped_ptr<KeyedService> Build(content::BrowserContext* profile) { - return make_scoped_ptr( - new OneClickTestProfileSyncService(static_cast<Profile*>(profile))); + return make_scoped_ptr(new OneClickTestProfileSyncService( + CreateProfileSyncServiceParamsForTest( + Profile::FromBrowserContext(profile)))); } bool IsFirstSetupInProgress() const override { @@ -69,17 +74,16 @@ } private: - explicit OneClickTestProfileSyncService(Profile* profile) - : TestProfileSyncService( - profile, - SigninManagerFactory::GetForProfile(profile), - ProfileOAuth2TokenServiceFactory::GetForProfile(profile), - browser_sync::MANUAL_START), + explicit OneClickTestProfileSyncService( + ProfileSyncService::InitParams init_params) + : TestProfileSyncService(std::move(init_params)), first_setup_in_progress_(false), sync_active_(false) {} bool first_setup_in_progress_; bool sync_active_; + + DISALLOW_COPY_AND_ASSIGN(OneClickTestProfileSyncService); }; class TestOneClickSigninSyncObserver : public OneClickSigninSyncObserver { @@ -158,6 +162,8 @@ TestOneClickSigninSyncObserver* sync_observer_; bool sync_observer_destroyed_; + + DISALLOW_COPY_AND_ASSIGN(OneClickSigninSyncObserverTest); }; // Verify that if no Sync service is present, e.g. because Sync is disabled, the
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc index 67febf5..df57665 100644 --- a/chrome/browser/ui/tab_contents/core_tab_helper.cc +++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc
@@ -24,6 +24,7 @@ #include "components/guest_view/browser/guest_view_manager.h" #include "components/search_engines/template_url.h" #include "components/search_engines/template_url_service.h" +#include "components/strings/grit/components_strings.h" #include "components/web_cache/browser/web_cache_manager.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h"
diff --git a/chrome/browser/ui/views/BUILD.gn b/chrome/browser/ui/views/BUILD.gn index a4bda8b..8831767 100644 --- a/chrome/browser/ui/views/BUILD.gn +++ b/chrome/browser/ui/views/BUILD.gn
@@ -38,8 +38,4 @@ if (use_ash) { deps += [ "//ash" ] } - - if (!is_chromeos) { - defines += [ "FRAME_AVATAR_BUTTON=1" ] - } }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc index f02b54c..3954b1c 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -1425,11 +1425,20 @@ node = model_->bookmark_bar_node()->GetChild(button_index); } - RecordBookmarkFolderOpen(GetBookmarkLaunchLocation()); - bookmark_menu_ = new BookmarkMenuController( - browser_, page_navigator_, GetWidget(), node, start_index, false); - bookmark_menu_->set_observer(this); - bookmark_menu_->RunMenuAt(this); + // Clicking the middle mouse button opens all bookmarks in the folder in new + // tabs. + if (event && (event->flags() & ui::EF_MIDDLE_MOUSE_BUTTON) != 0) { + WindowOpenDisposition disposition_from_event_flags = + ui::DispositionFromEventFlags(event->flags()); + chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node, + disposition_from_event_flags, browser_->profile()); + } else { + RecordBookmarkFolderOpen(GetBookmarkLaunchLocation()); + bookmark_menu_ = new BookmarkMenuController( + browser_, page_navigator_, GetWidget(), node, start_index, false); + bookmark_menu_->set_observer(this); + bookmark_menu_->RunMenuAt(this); + } } void BookmarkBarView::ButtonPressed(views::Button* sender, @@ -1453,16 +1462,14 @@ const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(index); DCHECK(page_navigator_); - if (node->is_url()) { - RecordAppLaunch(browser_->profile(), node->url()); - OpenURLParams params( - node->url(), Referrer(), disposition_from_event_flags, - ui::PAGE_TRANSITION_AUTO_BOOKMARK, false); - page_navigator_->OpenURL(params); - } else { - chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node, - disposition_from_event_flags, browser_->profile()); - } + // Only URL nodes have regular buttons on the bookmarks bar; folder clicks + // are directed to OnMenuButtonClicked(). + DCHECK(node->is_url()); + RecordAppLaunch(browser_->profile(), node->url()); + OpenURLParams params( + node->url(), Referrer(), disposition_from_event_flags, + ui::PAGE_TRANSITION_AUTO_BOOKMARK, false); + page_navigator_->OpenURL(params); RecordBookmarkLaunch(node, GetBookmarkLaunchLocation()); }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc index 893f9770..21e15cde 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -210,12 +210,21 @@ // PageNavigator implementation that records the URL. class TestingPageNavigator : public PageNavigator { public: + TestingPageNavigator() {} + ~TestingPageNavigator() override {} + WebContents* OpenURL(const OpenURLParams& params) override { - url_ = params.url; + urls_.push_back(params.url); return NULL; } - GURL url_; + const std::vector<GURL>& urls() const { return urls_; } + GURL last_url() const { return urls_.empty() ? GURL() : urls_.back(); } + + private: + std::vector<GURL> urls_; + + DISALLOW_COPY_AND_ASSIGN(TestingPageNavigator); }; // TODO(erg): Fix bookmark DND tests on linux_aura. crbug.com/163931 @@ -237,6 +246,7 @@ // f1a // F11 // f11a +// f1b // * // a // b @@ -370,6 +380,7 @@ model_->AddURL(f1, 0, ASCIIToUTF16("f1a"), GURL(test_base + "f1a")); const BookmarkNode* f11 = model_->AddFolder(f1, 1, ASCIIToUTF16("F11")); model_->AddURL(f11, 0, ASCIIToUTF16("f11a"), GURL(test_base + "f11a")); + model_->AddURL(f1, 2, ASCIIToUTF16("f1b"), GURL(test_base + "f1b")); if (big_menu) { for (int i = 1; i <= 100; ++i) { model_->AddURL(f1, i + 1, ASCIIToUTF16("f") + base::IntToString16(i), @@ -438,8 +449,8 @@ void Step3() { // We should have navigated to URL f1a. - ASSERT_TRUE(navigator_.url_ == - model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url()); + ASSERT_EQ(navigator_.last_url(), + model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url()); // Make sure button is no longer pushed. views::LabelButton* button = GetBookmarkButton(0); @@ -576,7 +587,7 @@ ASSERT_TRUE(child_menu->GetSubmenu()->IsShowing()); // Nothing should have been selected. - EXPECT_EQ(GURL(), navigator_.url_); + EXPECT_EQ(GURL(), navigator_.last_url()); // Hide menu. menu->GetMenuController()->CancelAll(); @@ -667,7 +678,7 @@ } void Step4() { - EXPECT_EQ(navigator_.url_, model_->other_node()->GetChild(0)->url()); + EXPECT_EQ(navigator_.last_url(), model_->other_node()->GetChild(0)->url()); Done(); } @@ -777,8 +788,8 @@ } void Step3() { - ASSERT_TRUE(navigator_.url_ == - model_->bookmark_bar_node()->GetChild(6)->url()); + ASSERT_EQ(navigator_.last_url(), + model_->bookmark_bar_node()->GetChild(6)->url()); Done(); } @@ -1148,7 +1159,7 @@ ASSERT_TRUE(submenu->IsSelected()); ASSERT_TRUE(!submenu->GetSubmenu() || !submenu->GetSubmenu()->IsShowing()); - // Send a down arrow to wrap back to f1a + // Send a down arrow to go down to f1b. ASSERT_TRUE(ui_controls::SendKeyPressNotifyWhenDone( window_->GetNativeWindow(), ui::VKEY_DOWN, false, false, false, false, CreateEventTask(this, &BookmarkBarViewTest10::Step7))); @@ -1159,18 +1170,30 @@ views::MenuItemView* menu = bb_view_->GetMenu(); ASSERT_TRUE(menu != NULL); ASSERT_TRUE(menu->GetSubmenu()->IsShowing()); + ASSERT_TRUE(menu->GetSubmenu()->GetMenuItemAt(2)->IsSelected()); + + // Send a down arrow to wrap back to f1a. + ASSERT_TRUE(ui_controls::SendKeyPressNotifyWhenDone( + window_->GetNativeWindow(), ui::VKEY_DOWN, false, false, false, false, + CreateEventTask(this, &BookmarkBarViewTest10::Step8))); + } + + void Step8() { + // Make sure menu is showing and item is selected. + views::MenuItemView* menu = bb_view_->GetMenu(); + ASSERT_TRUE(menu != NULL); + ASSERT_TRUE(menu->GetSubmenu()->IsShowing()); ASSERT_TRUE(menu->GetSubmenu()->GetMenuItemAt(0)->IsSelected()); // Send enter, which should select the item. ASSERT_TRUE(ui_controls::SendKeyPressNotifyWhenDone( window_->GetNativeWindow(), ui::VKEY_RETURN, false, false, false, false, - CreateEventTask(this, &BookmarkBarViewTest10::Step8))); + CreateEventTask(this, &BookmarkBarViewTest10::Step9))); } - void Step8() { - ASSERT_TRUE( - model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url() == - navigator_.url_); + void Step9() { + ASSERT_EQ(navigator_.last_url(), + model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url()); Done(); } }; @@ -2147,7 +2170,7 @@ } void Step5() { - EXPECT_EQ(navigator_.url_, model_->other_node()->GetChild(0)->url()); + EXPECT_EQ(navigator_.last_url(), model_->other_node()->GetChild(0)->url()); Done(); } @@ -2333,3 +2356,25 @@ VIEW_TEST(BookmarkBarViewTest26, CancelModeClosesMenu); #endif + +class BookmarkBarViewTest27 : public BookmarkBarViewEventTestBase { + protected: + void DoTestOnMessageLoop() override { + views::LabelButton* button = GetBookmarkButton(0); + ui_test_utils::MoveMouseToCenterAndPress( + button, ui_controls::MIDDLE, ui_controls::DOWN | ui_controls::UP, + CreateEventTask(this, &BookmarkBarViewTest27::Step2)); + } + + private: + void Step2() { + ASSERT_EQ(2u, navigator_.urls().size()); + EXPECT_EQ(navigator_.urls()[0], + model_->bookmark_bar_node()->GetChild(0)->GetChild(0)->url()); + EXPECT_EQ(navigator_.urls()[1], + model_->bookmark_bar_node()->GetChild(0)->GetChild(2)->url()); + Done(); + } +}; + +VIEW_TEST(BookmarkBarViewTest27, MiddleClickOnFolderOpensAllBookmarks);
diff --git a/chrome/browser/ui/views/certificate_viewer_win.cc b/chrome/browser/ui/views/certificate_viewer_win.cc index 205e41c..fbbd3a2 100644 --- a/chrome/browser/ui/views/certificate_viewer_win.cc +++ b/chrome/browser/ui/views/certificate_viewer_win.cc
@@ -16,7 +16,6 @@ #include "base/message_loop/message_loop.h" #include "base/task_runner.h" #include "base/threading/thread.h" -#include "chrome/browser/ui/host_desktop.h" #include "net/cert/x509_certificate.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" @@ -88,13 +87,8 @@ void ShowCertificateViewer(content::WebContents* web_contents, gfx::NativeWindow parent, net::X509Certificate* cert) { - if (chrome::GetHostDesktopTypeForNativeWindow(parent) != - chrome::HOST_DESKTOP_TYPE_ASH) { - CertificateViewerDialog* dialog = new CertificateViewerDialog; - dialog->Show( - parent->GetHost()->GetAcceleratedWidget(), cert, - base::Bind(&base::DeletePointer<CertificateViewerDialog>, dialog)); - } else { - NOTIMPLEMENTED(); - } + CertificateViewerDialog* dialog = new CertificateViewerDialog; + dialog->Show( + parent->GetHost()->GetAcceleratedWidget(), cert, + base::Bind(&base::DeletePointer<CertificateViewerDialog>, dialog)); }
diff --git a/chrome/browser/ui/views/chrome_views_delegate.cc b/chrome/browser/ui/views/chrome_views_delegate.cc index 28d4aa2..35b2fae 100644 --- a/chrome/browser/ui/views/chrome_views_delegate.cc +++ b/chrome/browser/ui/views/chrome_views_delegate.cc
@@ -44,7 +44,6 @@ #endif #if defined(USE_AURA) && !defined(OS_CHROMEOS) -#include "chrome/browser/ui/host_desktop.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/native_widget_aura.h" #endif @@ -232,13 +231,9 @@ // On Ash environment, a window won't span across displays. Adjust // the bounds to fit the work area. gfx::NativeView window = widget->GetNativeView(); - if (chrome::GetHostDesktopTypeForNativeView(window) == - chrome::HOST_DESKTOP_TYPE_ASH) { - gfx::Display display = - gfx::Screen::GetScreen()->GetDisplayMatching(*bounds); - bounds->AdjustToFit(display.work_area()); - ash::wm::GetWindowState(window)->set_minimum_visibility(true); - } + gfx::Display display = gfx::Screen::GetScreen()->GetDisplayMatching(*bounds); + bounds->AdjustToFit(display.work_area()); + ash::wm::GetWindowState(window)->set_minimum_visibility(true); #endif return true; } @@ -353,10 +348,7 @@ // the side effects are not as bad as the poor window manager interactions. On // Windows however these WM interactions are not an issue, so we open windows // requested as top_level as actual top level windows on the desktop. - use_non_toplevel_window = use_non_toplevel_window && - (params->child || - chrome::GetHostDesktopTypeForNativeView(params->parent) != - chrome::HOST_DESKTOP_TYPE_NATIVE); + use_non_toplevel_window = use_non_toplevel_window && params->child; if (!ui::win::IsAeroGlassEnabled()) { // If we don't have composition (either because Glass is not enabled or @@ -434,14 +426,7 @@ } params->native_widget = native_widget; } else { - // TODO(erg): Once we've threaded context to everywhere that needs it, we - // should remove this check here. - gfx::NativeView to_check = - params->context ? params->context : params->parent; - if (chrome::GetHostDesktopTypeForNativeView(to_check) == - chrome::HOST_DESKTOP_TYPE_NATIVE) { - params->native_widget = new views::DesktopNativeWidgetAura(delegate); - } + params->native_widget = new views::DesktopNativeWidgetAura(delegate); } #endif }
diff --git a/chrome/browser/ui/views/color_chooser_win.cc b/chrome/browser/ui/views/color_chooser_win.cc index ef84986..06381ef 100644 --- a/chrome/browser/ui/views/color_chooser_win.cc +++ b/chrome/browser/ui/views/color_chooser_win.cc
@@ -6,8 +6,6 @@ #include "chrome/browser/platform_util.h" #include "chrome/browser/ui/browser_dialogs.h" -#include "chrome/browser/ui/host_desktop.h" -#include "chrome/browser/ui/views/color_chooser_aura.h" #include "chrome/browser/ui/views/color_chooser_dialog.h" #include "content/public/browser/color_chooser.h" #include "content/public/browser/render_view_host.h" @@ -103,10 +101,6 @@ content::ColorChooser* ShowColorChooser(content::WebContents* web_contents, SkColor initial_color) { - gfx::NativeView native_view = web_contents->GetNativeView(); - if (GetHostDesktopTypeForNativeView(native_view) == HOST_DESKTOP_TYPE_ASH) - return ColorChooserAura::Open(web_contents, initial_color); - return ColorChooserWin::Open(web_contents, initial_color); }
diff --git a/chrome/browser/ui/views/desktop_media_picker_views.cc b/chrome/browser/ui/views/desktop_media_picker_views.cc index 0200536..a87cd56e 100644 --- a/chrome/browser/ui/views/desktop_media_picker_views.cc +++ b/chrome/browser/ui/views/desktop_media_picker_views.cc
@@ -523,7 +523,8 @@ audio_share_checkbox_->enabled() && audio_share_checkbox_->checked(); - if (parent_) + // TYPE_WEB_CONTENTS/tab is not fully supported yet. + if (parent_ && source.type != DesktopMediaID::TYPE_WEB_CONTENTS) parent_->NotifyDialogResult(source); // Return true to close the window.
diff --git a/chrome/browser/ui/views/download/download_shelf_view.cc b/chrome/browser/ui/views/download/download_shelf_view.cc index 89ed79a..38a179e 100644 --- a/chrome/browser/ui/views/download/download_shelf_view.cc +++ b/chrome/browser/ui/views/download/download_shelf_view.cc
@@ -134,6 +134,10 @@ parent_(parent), mouse_watcher_(new views::MouseWatcherViewHost(this, gfx::Insets()), this) { + // Start out hidden: the shelf might be created but never shown in some + // cases, like when installing a theme. See DownloadShelf::AddDownload(). + SetVisible(false); + mouse_watcher_.set_notify_on_exit_time( base::TimeDelta::FromMilliseconds(kNotifyOnExitTimeMS)); set_id(VIEW_ID_DOWNLOAD_SHELF);
diff --git a/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.cc b/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.cc index c04443c..1f058a8a 100644 --- a/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.cc +++ b/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.cc
@@ -261,15 +261,11 @@ } int BookmarkAppBubbleView::TitleStringId() { - int string_id = IDS_ADD_TO_DESKTOP_BUBBLE_TITLE; #if defined(USE_ASH) - if (chrome::GetHostDesktopTypeForNativeWindow( - anchor_widget()->GetNativeWindow()) == - chrome::HOST_DESKTOP_TYPE_ASH) { - string_id = IDS_ADD_TO_SHELF_BUBBLE_TITLE; - } + return IDS_ADD_TO_SHELF_BUBBLE_TITLE; +#else + return IDS_ADD_TO_DESKTOP_BUBBLE_TITLE; #endif - return string_id; } base::string16 BookmarkAppBubbleView::GetTrimmedTitle() {
diff --git a/chrome/browser/ui/views/frame/avatar_button_manager.cc b/chrome/browser/ui/views/frame/avatar_button_manager.cc index 49b8272..c5ebffc 100644 --- a/chrome/browser/ui/views/frame/avatar_button_manager.cc +++ b/chrome/browser/ui/views/frame/avatar_button_manager.cc
@@ -15,13 +15,14 @@ void AvatarButtonManager::Update(AvatarButtonStyle style) { BrowserView* browser_view = frame_view_->browser_view(); BrowserFrame* frame = frame_view_->frame(); + Profile* profile = browser_view->browser()->profile(); // This should never be called in incognito mode. DCHECK(browser_view->IsRegularOrGuestSession()); if (browser_view->ShouldShowAvatar()) { if (!view_) { - view_ = new NewAvatarButton(this, style, browser_view->browser()); + view_ = new NewAvatarButton(this, style, profile); view_->set_id(VIEW_ID_NEW_AVATAR_BUTTON); frame_view_->AddChildView(view_); frame->GetRootView()->Layout(); @@ -33,6 +34,16 @@ } } +void AvatarButtonManager::ButtonPreferredSizeChanged() { + // Perform a re-layout if the avatar button has changed, since that can affect + // the size of the tabs. + if (!view_ || !frame_view_->browser_view()->initialized()) + return; // Ignore the update during view creation. + + frame_view_->InvalidateLayout(); + frame_view_->frame()->GetRootView()->Layout(); +} + void AvatarButtonManager::ButtonPressed(views::Button* sender, const ui::Event& event) { DCHECK_EQ(view_, sender);
diff --git a/chrome/browser/ui/views/frame/avatar_button_manager.h b/chrome/browser/ui/views/frame/avatar_button_manager.h index 76f7156..ca3aa5b 100644 --- a/chrome/browser/ui/views/frame/avatar_button_manager.h +++ b/chrome/browser/ui/views/frame/avatar_button_manager.h
@@ -5,14 +5,14 @@ #ifndef CHROME_BROWSER_UI_VIEWS_FRAME_AVATAR_BUTTON_MANAGER_H_ #define CHROME_BROWSER_UI_VIEWS_FRAME_AVATAR_BUTTON_MANAGER_H_ +#include "chrome/browser/ui/views/profiles/avatar_button_delegate.h" #include "chrome/browser/ui/views/profiles/avatar_button_style.h" -#include "ui/views/controls/button/button.h" class BrowserNonClientFrameView; // Manages an avatar button displayed in a browser frame. The button displays // the name of the active or guest profile, and may be null. -class AvatarButtonManager : public views::ButtonListener { +class AvatarButtonManager : public AvatarButtonDelegate { public: explicit AvatarButtonManager(BrowserNonClientFrameView* frame_view); @@ -24,6 +24,9 @@ views::View* view() const { return view_; } private: + // AvatarButtonDelegate: + void ButtonPreferredSizeChanged() override; + // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc index 2d40993..3c554e0 100644 --- a/chrome/browser/ui/views/frame/browser_frame.cc +++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -261,8 +261,6 @@ return browser_frame_view_->avatar_button(); } -#if defined(FRAME_AVATAR_BUTTON) views::View* BrowserFrame::GetNewAvatarMenuButton() { - return browser_frame_view_->new_avatar_button(); + return browser_frame_view_->GetProfileSwitcherView(); } -#endif
diff --git a/chrome/browser/ui/views/frame/browser_frame.h b/chrome/browser/ui/views/frame/browser_frame.h index b62cd215..4717eac6 100644 --- a/chrome/browser/ui/views/frame/browser_frame.h +++ b/chrome/browser/ui/views/frame/browser_frame.h
@@ -112,10 +112,7 @@ ui::MenuSourceType source_type) override; AvatarMenuButton* GetAvatarMenuButton(); - -#if defined(FRAME_AVATAR_BUTTON) views::View* GetNewAvatarMenuButton(); -#endif // Returns the menu model. BrowserFrame owns the returned model. // Note that in multi user mode this will upon each call create a new model.
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc index c44d5894..d9ca861c 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -9,7 +9,6 @@ #include "chrome/browser/profiles/avatar_menu.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/themes/theme_properties.h" @@ -31,25 +30,19 @@ BrowserNonClientFrameView::BrowserNonClientFrameView(BrowserFrame* frame, BrowserView* browser_view) : frame_(frame), - browser_view_(browser_view), -#if defined(FRAME_AVATAR_BUTTON) - profile_switcher_(this), -#endif - avatar_button_(nullptr) { + browser_view_(browser_view) { // The profile manager may by null in tests. if (g_browser_process->profile_manager()) { - ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - cache.AddObserver(this); + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().AddObserver(this); } } BrowserNonClientFrameView::~BrowserNonClientFrameView() { // The profile manager may by null in tests. if (g_browser_process->profile_manager()) { - ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - cache.RemoveObserver(this); + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().RemoveObserver(this); } } @@ -62,6 +55,10 @@ return nullptr; } +views::View* BrowserNonClientFrameView::GetProfileSwitcherView() const { + return nullptr; +} + void BrowserNonClientFrameView::VisibilityChanged(views::View* starting_from, bool is_visible) { if (!is_visible) @@ -89,17 +86,6 @@ #endif } -void BrowserNonClientFrameView::ChildPreferredSizeChanged(View* child) { -#if defined(FRAME_AVATAR_BUTTON) - // Only perform a re-layout if the avatar button has changed, since that - // can affect the size of the tabs. - if (child == new_avatar_button()) { - InvalidateLayout(); - frame_->GetRootView()->Layout(); - } -#endif -} - bool BrowserNonClientFrameView::ShouldPaintAsThemed() const { return browser_view_->IsBrowserTypeNormal(); } @@ -157,15 +143,6 @@ } #endif // !defined(OS_CHROMEOS) -void BrowserNonClientFrameView::UpdateAvatar() { -#if !defined(OS_CHROMEOS) - if (browser_view()->IsRegularOrGuestSession()) - UpdateNewAvatarButtonImpl(); - else -#endif - UpdateOldAvatarButton(); -} - void BrowserNonClientFrameView::UpdateOldAvatarButton() { if (browser_view_->ShouldShowAvatar()) { if (!avatar_button_) { @@ -204,13 +181,6 @@ avatar_button_->SetAvatarIcon(avatar, is_rectangle); } -#if defined(FRAME_AVATAR_BUTTON) -void BrowserNonClientFrameView::UpdateNewAvatarButton( - const AvatarButtonStyle style) { - profile_switcher_.Update(style); -} -#endif - void BrowserNonClientFrameView::OnProfileAdded( const base::FilePath& profile_path) { UpdateTaskbarDecoration(); @@ -259,9 +229,9 @@ // In tests, make sure that the browser process and profile manager are // valid before using. if (g_browser_process && g_browser_process->profile_manager()) { - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - show_decoration = show_decoration && cache.GetNumberOfProfiles() > 1; + const ProfileAttributesStorage& storage = + g_browser_process->profile_manager()->GetProfileAttributesStorage(); + show_decoration = show_decoration && storage.GetNumberOfProfiles() > 1; } chrome::DrawTaskbarDecoration(frame_->GetNativeWindow(), show_decoration
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h index ade7448..9bbdf7b6 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -5,13 +5,9 @@ #ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_H_ -#include "chrome/browser/profiles/profile_info_cache_observer.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "ui/views/window/non_client_view.h" -#if defined(FRAME_AVATAR_BUTTON) -#include "chrome/browser/ui/views/frame/avatar_button_manager.h" -#endif - class AvatarMenuButton; class BrowserFrame; class BrowserView; @@ -19,7 +15,7 @@ // A specialization of the NonClientFrameView object that provides additional // Browser-specific methods. class BrowserNonClientFrameView : public views::NonClientFrameView, - public ProfileInfoCacheObserver { + public ProfileAttributesStorage::Observer { public: BrowserNonClientFrameView(BrowserFrame* frame, BrowserView* browser_view); ~BrowserNonClientFrameView() override; @@ -28,12 +24,6 @@ BrowserFrame* frame() const { return frame_; } AvatarMenuButton* avatar_button() const { return avatar_button_; } -#if defined(FRAME_AVATAR_BUTTON) - views::View* new_avatar_button() const { - return profile_switcher_.view(); - } -#endif - // Called when BrowserView creates all it's child views. virtual void OnBrowserViewInitViewsComplete(); @@ -62,9 +52,11 @@ // Returns the location icon, if this frame has any. virtual views::View* GetLocationIconView() const; + // Returns the profile switcher button, if this frame has any. + virtual views::View* GetProfileSwitcherView() const; + // Overriden from views::View. void VisibilityChanged(views::View* starting_from, bool is_visible) override; - void ChildPreferredSizeChanged(View* child) override; protected: // Whether the frame should be painted with theming. @@ -79,27 +71,15 @@ gfx::ImageSkia* GetFrameOverlayImage() const; #endif - // Updates the avatar button using the old or new UI based on the BrowserView - // type, and the presence of the --enable-new-avatar-menu flag. Calls either - // UpdateOldAvatarButton() or UpdateNewAvatarButtonImpl() accordingly. - void UpdateAvatar(); + // Update the profile switcher button if one should exist. Otherwise, update + // the incognito avatar, or profile avatar for teleported frames in ChromeOS. + virtual void UpdateAvatar() = 0; // Updates the title and icon of the old avatar button. void UpdateOldAvatarButton(); - // Updates the avatar button displayed in the caption area by calling - // UpdateNewAvatarButton() with an implementation specific |listener| - // and button |style|. - virtual void UpdateNewAvatarButtonImpl() = 0; - -#if defined(FRAME_AVATAR_BUTTON) - // Updates the title of the avatar button displayed in the caption area. - // The button uses |style| to match the browser window style. - void UpdateNewAvatarButton(const AvatarButtonStyle style); -#endif - private: - // Overriden from ProfileInfoCacheObserver. + // Overriden from ProfileAttributesStorage::Observer. void OnProfileAdded(const base::FilePath& profile_path) override; void OnProfileWasRemoved(const base::FilePath& profile_path, const base::string16& profile_name) override; @@ -114,15 +94,9 @@ // The BrowserView hosted within this View. BrowserView* browser_view_; -#if defined(FRAME_AVATAR_BUTTON) - // Wrapper around the in-frame avatar switcher. - // TODO(tapted): Move this component down into the subclasses that need it. - AvatarButtonManager profile_switcher_; -#endif - // Menu button that displays the incognito icon. May be null for some frame // styles. TODO(anthonyvd): simplify/rename. - AvatarMenuButton* avatar_button_; + AvatarMenuButton* avatar_button_ = nullptr; }; namespace chrome {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc index 345cad7..a41a8939e 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -383,8 +383,8 @@ // BrowserNonClientFrameViewAsh, protected: // BrowserNonClientFrameView: -void BrowserNonClientFrameViewAsh::UpdateNewAvatarButtonImpl() { - NOTREACHED(); +void BrowserNonClientFrameViewAsh::UpdateAvatar() { + UpdateOldAvatarButton(); } ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h index d1e80066..fed74a9 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -73,7 +73,7 @@ protected: // BrowserNonClientFrameView: - void UpdateNewAvatarButtonImpl() override; + void UpdateAvatar() override; private: FRIEND_TEST_ALL_PREFIXES(BrowserNonClientFrameViewAshTest, WindowHeader);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h index dd230c2..561240cf 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
@@ -39,7 +39,7 @@ void OnPaint(gfx::Canvas* canvas) override; // BrowserNonClientFrameView: - void UpdateNewAvatarButtonImpl() override; + void UpdateAvatar() override; private: void PaintThemedFrame(gfx::Canvas* canvas);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm index d1bdb11c..2ec2907e 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
@@ -122,7 +122,7 @@ } // BrowserNonClientFrameView: -void BrowserNonClientFrameViewMac::UpdateNewAvatarButtonImpl() { +void BrowserNonClientFrameViewMac::UpdateAvatar() { NOTIMPLEMENTED(); }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc index 3c031d6..da75bb99 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
@@ -7,7 +7,6 @@ #include <algorithm> #include "base/profiler/scoped_tracker.h" -#include "build/build_config.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/profiles/profiles_state.h" @@ -49,6 +48,10 @@ #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" +#if !defined(OS_CHROMEOS) +#define FRAME_AVATAR_BUTTON +#endif + namespace { #if defined(FRAME_AVATAR_BUTTON) @@ -102,7 +105,11 @@ BrowserView* browser_view) : BrowserNonClientFrameView(frame, browser_view), window_icon_(nullptr), - tab_strip_(nullptr) {} +#if defined(FRAME_AVATAR_BUTTON) + profile_switcher_(this), +#endif + tab_strip_(nullptr) { +} BrowserNonClientFrameViewMus::~BrowserNonClientFrameViewMus() { if (tab_strip_) { @@ -185,6 +192,14 @@ return nullptr; } +views::View* BrowserNonClientFrameViewMus::GetProfileSwitcherView() const { +#if defined(FRAME_AVATAR_BUTTON) + return profile_switcher_.view(); +#else + return nullptr; +#endif +} + /////////////////////////////////////////////////////////////////////////////// // views::NonClientFrameView: @@ -208,8 +223,8 @@ int hit_test = HTCLIENT; #if defined(FRAME_AVATAR_BUTTON) - if (hit_test == HTCAPTION && new_avatar_button() && - ConvertedHitTest(this, new_avatar_button(), point)) { + if (hit_test == HTCAPTION && profile_switcher_.view() && + ConvertedHitTest(this, profile_switcher_.view(), point)) { return HTCLIENT; } #endif @@ -267,11 +282,11 @@ void BrowserNonClientFrameViewMus::Layout() { if (avatar_button()) - LayoutAvatar(); + LayoutIncognitoButton(); #if defined(FRAME_AVATAR_BUTTON) - if (new_avatar_button()) - LayoutNewStyleAvatar(); + if (profile_switcher_.view()) + LayoutProfileSwitcher(); #endif BrowserNonClientFrameView::Layout(); @@ -304,24 +319,6 @@ return gfx::Size(min_width, min_client_view_size.height()); } -void BrowserNonClientFrameViewMus::ChildPreferredSizeChanged( - views::View* child) { - // FrameCaptionButtonContainerView animates the visibility changes in - // UpdateSizeButtonVisibility(false). Due to this a new size is not available - // until the completion of the animation. Layout in response to the preferred - // size changes. - if (!browser_view()->initialized()) - return; - bool needs_layout = false; -#if defined(FRAME_AVATAR_BUTTON) - needs_layout = needs_layout || child == new_avatar_button(); -#endif - if (needs_layout) { - InvalidateLayout(); - frame()->GetRootView()->Layout(); - } -} - /////////////////////////////////////////////////////////////////////////////// // TabIconViewModel: @@ -343,10 +340,13 @@ // BrowserNonClientFrameViewMus, protected: // BrowserNonClientFrameView: -void BrowserNonClientFrameViewMus::UpdateNewAvatarButtonImpl() { +void BrowserNonClientFrameViewMus::UpdateAvatar() { #if defined(FRAME_AVATAR_BUTTON) - UpdateNewAvatarButton(AvatarButtonStyle::NATIVE); + if (browser_view()->IsRegularOrGuestSession()) + profile_switcher_.Update(AvatarButtonStyle::NATIVE); + else #endif + UpdateOldAvatarButton(); } /////////////////////////////////////////////////////////////////////////////// @@ -423,9 +423,9 @@ int right_inset = kTabstripRightSpacing + frame_right_insets; #if defined(FRAME_AVATAR_BUTTON) - if (new_avatar_button()) { + if (profile_switcher_.view()) { right_inset += kNewAvatarButtonOffset + - new_avatar_button()->GetPreferredSize().width(); + profile_switcher_.view()->GetPreferredSize().width(); } #endif @@ -456,7 +456,7 @@ Browser::FEATURE_WEBAPPFRAME); } -void BrowserNonClientFrameViewMus::LayoutAvatar() { +void BrowserNonClientFrameViewMus::LayoutIncognitoButton() { DCHECK(avatar_button()); #if !defined(OS_CHROMEOS) // ChromeOS shows avatar on V1 app. @@ -485,11 +485,14 @@ avatar_button()->SetVisible(avatar_visible); } +void BrowserNonClientFrameViewMus::LayoutProfileSwitcher() { #if defined(FRAME_AVATAR_BUTTON) -void BrowserNonClientFrameViewMus::LayoutNewStyleAvatar() { - NOTIMPLEMENTED(); -} + gfx::Size button_size = profile_switcher_.view()->GetPreferredSize(); + int button_x = width() - GetTabStripRightInset() + kNewAvatarButtonOffset; + profile_switcher_.view()->SetBounds(button_x, 0, button_size.width(), + button_size.height()); #endif +} bool BrowserNonClientFrameViewMus::ShouldPaint() const { if (!frame()->IsFullscreen())
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h index 90c367e..0931470 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
@@ -5,12 +5,17 @@ #ifndef CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_MUS_H_ #define CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_NON_CLIENT_FRAME_VIEW_MUS_H_ +#include "build/build_config.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h" #include "chrome/browser/ui/views/tab_icon_view_model.h" #include "chrome/browser/ui/views/tabs/tab_strip_observer.h" +#if !defined(OS_CHROMEOS) +#include "chrome/browser/ui/views/frame/avatar_button_manager.h" +#endif + class TabIconView; class WebAppLeftHeaderView; @@ -42,6 +47,7 @@ void UpdateThrobber(bool running) override; void UpdateToolbar() override; views::View* GetLocationIconView() const override; + views::View* GetProfileSwitcherView() const override; // views::NonClientFrameView: gfx::Rect GetBoundsForClientView() const override; @@ -60,7 +66,6 @@ const char* GetClassName() const override; void GetAccessibleState(ui::AXViewState* state) override; gfx::Size GetMinimumSize() const override; - void ChildPreferredSizeChanged(views::View* child) override; // TabIconViewModel: bool ShouldTabIconViewAnimate() const override; @@ -68,7 +73,7 @@ protected: // BrowserNonClientFrameView: - void UpdateNewAvatarButtonImpl() override; + void UpdateAvatar() override; private: mus::Window* mus_window(); @@ -106,11 +111,11 @@ // accoutrements. bool UseWebAppHeaderStyle() const; - // Layout the avatar button. - void LayoutAvatar(); -#if defined(FRAME_AVATAR_BUTTON) - void LayoutNewStyleAvatar(); -#endif + // Layout the incognito button. + void LayoutIncognitoButton(); + + // Layout the profile switcher (if there is one). + void LayoutProfileSwitcher(); // Returns true if there is anything to paint. Some fullscreen windows do not // need their frames painted. @@ -131,6 +136,11 @@ // For popups, the window icon. TabIconView* window_icon_; +#if !defined(OS_CHROMEOS) + // Wrapper around the in-frame avatar switcher. + AvatarButtonManager profile_switcher_; +#endif + TabStrip* tab_strip_; DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewMus);
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 8ab7ca9..8f1096b 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -32,8 +32,9 @@ #include "chrome/browser/native_window_notification_source.h" #include "chrome/browser/profiles/avatar_menu.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/profiles/profiles_state.h" @@ -260,8 +261,7 @@ void PaintBackgroundAttachedMode(gfx::Canvas* canvas, const ui::ThemeProvider* theme_provider, const gfx::Rect& bounds, - const gfx::Point& background_origin, - chrome::HostDesktopType host_desktop_type) { + const gfx::Point& background_origin) { canvas->FillRect(bounds, theme_provider->GetColor(ThemeProperties::COLOR_TOOLBAR)); @@ -279,8 +279,8 @@ bounds.height()); } - if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH && - !ui::MaterialDesignController::IsModeMaterial()) { +#if defined(USE_ASH) + if (!ui::MaterialDesignController::IsModeMaterial()) { // The pre-material design version of Ash provides additional lightening // at the edges of the toolbar. gfx::ImageSkia* toolbar_left = @@ -298,22 +298,19 @@ toolbar_right->width(), bounds.height()); } +#endif // USE_ASH } void PaintAttachedBookmarkBar(gfx::Canvas* canvas, BookmarkBarView* view, BrowserView* browser_view, - chrome::HostDesktopType host_desktop_type, int toolbar_overlap) { // Paint background for attached state, this is fade in/out. gfx::Point background_image_offset = browser_view->OffsetPointForToolbarBackgroundImage( gfx::Point(view->GetMirroredX(), view->y())); - PaintBackgroundAttachedMode(canvas, - view->GetThemeProvider(), - view->GetLocalBounds(), - background_image_offset, - host_desktop_type); + PaintBackgroundAttachedMode(canvas, view->GetThemeProvider(), + view->GetLocalBounds(), background_image_offset); if (view->height() >= toolbar_overlap) { // Draw the separator below the Bookmarks Bar; this is fading in/out. if (ui::MaterialDesignController::IsModeMaterial()) { @@ -432,10 +429,7 @@ SkAlpha detached_alpha = static_cast<SkAlpha>( bookmark_bar_view_->size_animation().CurrentValueBetween(0xff, 0)); if (detached_alpha != 0xff) { - PaintAttachedBookmarkBar(canvas, - bookmark_bar_view_, - browser_view_, - browser_->host_desktop_type(), + PaintAttachedBookmarkBar(canvas, bookmark_bar_view_, browser_view_, toolbar_overlap); } @@ -641,10 +635,9 @@ // Tests may not have a profile manager. if (!g_browser_process->profile_manager()) return false; - ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - if (cache.GetIndexOfProfileWithPath(browser_->profile()->GetPath()) == - std::string::npos) { + ProfileAttributesEntry* entry; + if (!g_browser_process->profile_manager()->GetProfileAttributesStorage(). + GetProfileAttributesWithPath(browser_->profile()->GetPath(), &entry)) { return false; } @@ -2584,7 +2577,7 @@ AvatarBubbleMode mode, const signin::ManageAccountsParams& manage_accounts_params, signin_metrics::AccessPoint access_point) { -#if defined(FRAME_AVATAR_BUTTON) +#if !defined(OS_CHROMEOS) // Do not show avatar bubble if there is no avatar menu button. if (!frame_->GetNewAvatarMenuButton()) return;
diff --git a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc index 54c5d3c..486e290 100644 --- a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc +++ b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
@@ -19,7 +19,8 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/profiles/profile_info_cache.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_shortcut_manager_win.h" #include "chrome/browser/profiles/profiles_state.h" @@ -203,7 +204,6 @@ // Two profile case. Both profile names should be shown. ProfileManager* profile_manager = g_browser_process->profile_manager(); - ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); base::FilePath path_profile2 = profile_manager->GenerateNextProfileDirectoryPath(); @@ -225,11 +225,13 @@ // The second profile's name should be part of the relaunch name. Browser* profile2_browser = CreateBrowser(profile_manager->GetProfileByPath(path_profile2)); - size_t profile2_index = cache.GetIndexOfProfileWithPath(path_profile2); + ProfileAttributesEntry* entry; + ASSERT_TRUE(profile_manager->GetProfileAttributesStorage(). + GetProfileAttributesWithPath(path_profile2, &entry)); WaitAndValidateBrowserWindowProperties( base::Bind(&ValidateBrowserWindowProperties, profile2_browser, - cache.GetNameOfProfileAtIndex(profile2_index))); + entry->GetName())); } // http://crbug.com/396344
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc index 9f4f93e..3c7248da 100644 --- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -84,6 +84,7 @@ GlassBrowserFrameView::GlassBrowserFrameView(BrowserFrame* frame, BrowserView* browser_view) : BrowserNonClientFrameView(frame, browser_view), + profile_switcher_(this), throbber_running_(false), throbber_frame_(0) { if (browser_view->ShouldShowWindowIcon()) @@ -116,9 +117,9 @@ // The new avatar button is optionally displayed to the left of the // minimize button. - if (new_avatar_button()) { + if (profile_switcher_.view()) { const int old_end_x = end_x; - end_x -= new_avatar_button()->width() + kNewAvatarButtonOffset; + end_x -= profile_switcher_.view()->width() + kNewAvatarButtonOffset; // In non-maximized mode, allow the new tab button to slide completely // under the avatar button. @@ -175,6 +176,10 @@ return min_size; } +views::View* GlassBrowserFrameView::GetProfileSwitcherView() const { + return profile_switcher_.view(); +} + /////////////////////////////////////////////////////////////////////////////// // GlassBrowserFrameView, views::NonClientFrameView implementation: @@ -214,9 +219,9 @@ // See if the point is within the incognito icon or the new avatar menu. if ((avatar_button() && avatar_button()->GetMirroredBounds().Contains(point)) || - (new_avatar_button() && - new_avatar_button()->GetMirroredBounds().Contains(point))) - return HTCLIENT; + (profile_switcher_.view() && + profile_switcher_.view()->GetMirroredBounds().Contains(point))) + return HTCLIENT; int frame_component = frame()->client_view()->NonClientHitTest(point); @@ -270,8 +275,11 @@ // GlassBrowserFrameView, protected: // BrowserNonClientFrameView: -void GlassBrowserFrameView::UpdateNewAvatarButtonImpl() { - UpdateNewAvatarButton(AvatarButtonStyle::NATIVE); +void GlassBrowserFrameView::UpdateAvatar() { + if (browser_view()->IsRegularOrGuestSession()) + profile_switcher_.Update(AvatarButtonStyle::NATIVE); + else + UpdateOldAvatarButton(); } /////////////////////////////////////////////////////////////////////////////// @@ -283,8 +291,9 @@ CHECK_EQ(target, this); bool hit_incognito_icon = avatar_button() && avatar_button()->GetMirroredBounds().Intersects(rect); - bool hit_new_avatar_button = new_avatar_button() && - new_avatar_button()->GetMirroredBounds().Intersects(rect); + bool hit_new_avatar_button = + profile_switcher_.view() && + profile_switcher_.view()->GetMirroredBounds().Intersects(rect); return hit_incognito_icon || hit_new_avatar_button || !frame()->client_view()->bounds().Intersects(rect); } @@ -486,10 +495,10 @@ void GlassBrowserFrameView::LayoutNewStyleAvatar() { DCHECK(browser_view()->IsRegularOrGuestSession()); - if (!new_avatar_button()) + if (!profile_switcher_.view()) return; - gfx::Size label_size = new_avatar_button()->GetPreferredSize(); + gfx::Size label_size = profile_switcher_.view()->GetPreferredSize(); int button_x = frame()->GetMinimizeButtonOffset() - kNewAvatarButtonOffset - label_size.width(); @@ -510,10 +519,8 @@ // pixel in height, then we place it at the correct position in restored mode, // or one pixel above the top of the screen in maximized mode. int button_y = frame()->IsMaximized() ? (FrameTopBorderHeight(false) - 1) : 1; - new_avatar_button()->SetBounds( - button_x, - button_y, - label_size.width(), + profile_switcher_.view()->SetBounds( + button_x, button_y, label_size.width(), gfx::win::GetSystemMetricsInDIP(SM_CYMENUSIZE) + 1); } @@ -533,8 +540,9 @@ // In RTL, the icon needs to start after the caption buttons. if (base::i18n::IsRTL()) { x = width() - frame()->GetMinimizeButtonOffset() + - (new_avatar_button() ? - (new_avatar_button()->width() + kNewAvatarButtonOffset) : 0); + (profile_switcher_.view() + ? (profile_switcher_.view()->width() + kNewAvatarButtonOffset) + : 0); } else if (!md && !avatar_button() && IsToolbarVisible() && (base::win::GetVersion() < base::win::VERSION_WIN10)) { // In non-MD before Win 10, the toolbar has a rounded corner that we don't
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h index e9280b63..f14ba9d 100644 --- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h +++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
@@ -8,6 +8,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/win/scoped_gdi_object.h" +#include "chrome/browser/ui/views/frame/avatar_button_manager.h" #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h" #include "ui/views/window/non_client_view.h" @@ -25,6 +26,7 @@ int GetThemeBackgroundXInset() const override; void UpdateThrobber(bool running) override; gfx::Size GetMinimumSize() const override; + views::View* GetProfileSwitcherView() const override; // views::NonClientFrameView: gfx::Rect GetBoundsForClientView() const override; @@ -43,7 +45,7 @@ void Layout() override; // BrowserNonClientFrameView: - void UpdateNewAvatarButtonImpl() override; + void UpdateAvatar() override; private: // views::NonClientFrameView: @@ -115,6 +117,9 @@ // The big icon created from the bitmap image of the window icon. base::win::ScopedHICON big_window_icon_; + // Wrapper around the in-frame avatar switcher. + AvatarButtonManager profile_switcher_; + // Whether or not the window throbber is currently animating. bool throbber_running_;
diff --git a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc index 8e5e0cc..6b9f3be 100644 --- a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc +++ b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc
@@ -19,7 +19,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/history/top_sites_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/sessions/tab_restore_service_factory.h" @@ -440,7 +439,7 @@ ProfileManager* profile_manager = g_browser_process->profile_manager(); DCHECK(profile_manager); avatar_menu_.reset(new AvatarMenu( - &profile_manager->GetProfileInfoCache(), this, nullptr)); + &profile_manager->GetProfileAttributesStorage(), this, nullptr)); avatar_menu_->RebuildMenu(); BrowserList::AddObserver(this);
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc index a14dc84..4dae2ff2 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -85,6 +85,7 @@ close_button_(nullptr), window_icon_(nullptr), window_title_(nullptr), + profile_switcher_(this), frame_background_(new views::FrameBackground()) { SetLayoutManager(layout_); @@ -172,6 +173,10 @@ return layout_->GetMinimumSize(width()); } +views::View* OpaqueBrowserFrameView::GetProfileSwitcherView() const { + return profile_switcher_.view(); +} + /////////////////////////////////////////////////////////////////////////////// // OpaqueBrowserFrameView, views::NonClientFrameView implementation: @@ -190,12 +195,10 @@ avatar_button()->GetMirroredBounds().Contains(point)) { return true; } -#if defined(FRAME_AVATAR_BUTTON) - if (new_avatar_button() && - new_avatar_button()->GetMirroredBounds().Contains(point)) { + if (profile_switcher_.view() && + profile_switcher_.view()->GetMirroredBounds().Contains(point)) { return true; } -#endif return false; } @@ -462,10 +465,11 @@ platform_observer_->IsUsingSystemTheme(); } -void OpaqueBrowserFrameView::UpdateNewAvatarButtonImpl() { -#if defined(FRAME_AVATAR_BUTTON) - UpdateNewAvatarButton(AvatarButtonStyle::THEMED); -#endif +void OpaqueBrowserFrameView::UpdateAvatar() { + if (browser_view()->IsRegularOrGuestSession()) + profile_switcher_.Update(AvatarButtonStyle::THEMED); + else + UpdateOldAvatarButton(); } ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h index 80577a1..e70e8b4 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -8,6 +8,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/ui/view_ids.h" +#include "chrome/browser/ui/views/frame/avatar_button_manager.h" #include "chrome/browser/ui/views/frame/browser_frame.h" #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h" #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h" @@ -43,6 +44,7 @@ int GetThemeBackgroundXInset() const override; void UpdateThrobber(bool running) override; gfx::Size GetMinimumSize() const override; + views::View* GetProfileSwitcherView() const override; // views::NonClientFrameView: gfx::Rect GetBoundsForClientView() const override; @@ -100,7 +102,7 @@ // BrowserNonClientFrameView: bool ShouldPaintAsThemed() const override; - void UpdateNewAvatarButtonImpl() override; + void UpdateAvatar() override; private: // views::NonClientFrameView: @@ -166,6 +168,9 @@ TabIconView* window_icon_; views::Label* window_title_; + // Wrapper around the in-frame avatar switcher. + AvatarButtonManager profile_switcher_; + // Background painter for the window frame. scoped_ptr<views::FrameBackground> frame_background_;
diff --git a/chrome/browser/ui/views/frame/taskbar_decorator_win.cc b/chrome/browser/ui/views/frame/taskbar_decorator_win.cc index d6328fad..713cee0 100644 --- a/chrome/browser/ui/views/frame/taskbar_decorator_win.cc +++ b/chrome/browser/ui/views/frame/taskbar_decorator_win.cc
@@ -12,7 +12,6 @@ #include "base/win/scoped_gdi_object.h" #include "base/win/windows_version.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/ui/host_desktop.h" #include "content/public/browser/browser_thread.h" #include "skia/ext/image_operations.h" #include "skia/ext/platform_canvas.h" @@ -69,10 +68,7 @@ } // namespace void DrawTaskbarDecoration(gfx::NativeWindow window, const gfx::Image* image) { - // HOST_DESKTOP_TYPE_ASH doesn't use the taskbar. - if (base::win::GetVersion() < base::win::VERSION_WIN7 || - chrome::GetHostDesktopTypeForNativeWindow(window) != - chrome::HOST_DESKTOP_TYPE_NATIVE) + if (base::win::GetVersion() < base::win::VERSION_WIN7) return; HWND hwnd = views::HWNDForNativeWindow(window);
diff --git a/chrome/browser/ui/views/profiles/avatar_button_delegate.h b/chrome/browser/ui/views/profiles/avatar_button_delegate.h new file mode 100644 index 0000000..a5f0bb60 --- /dev/null +++ b/chrome/browser/ui/views/profiles/avatar_button_delegate.h
@@ -0,0 +1,17 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_PROFILES_AVATAR_BUTTON_DELEGATE_H_ +#define CHROME_BROWSER_UI_VIEWS_PROFILES_AVATAR_BUTTON_DELEGATE_H_ + +#include "ui/views/controls/button/button.h" + +// Delegate allowing NewAvatarButton to communicate back to its manager. +class AvatarButtonDelegate : public views::ButtonListener { + public: + // Called when the preferred size changed, e.g., due to a profile name change. + virtual void ButtonPreferredSizeChanged() = 0; +}; + +#endif // CHROME_BROWSER_UI_VIEWS_PROFILES_AVATAR_BUTTON_DELEGATE_H_
diff --git a/chrome/browser/ui/views/profiles/avatar_menu_button.cc b/chrome/browser/ui/views/profiles/avatar_menu_button.cc index 66d7a81..c5d3f08b 100644 --- a/chrome/browser/ui/views/profiles/avatar_menu_button.cc +++ b/chrome/browser/ui/views/profiles/avatar_menu_button.cc
@@ -12,8 +12,9 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/profiles/avatar_menu.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_metrics.h" #include "chrome/browser/ui/browser.h" @@ -117,16 +118,15 @@ &is_badge_rectangle); #endif } else if (should_show_avatar_menu) { - ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - size_t index = cache.GetIndexOfProfileWithPath(profile->GetPath()); - if (index == std::string::npos) + ProfileAttributesEntry* entry; + if (!g_browser_process->profile_manager()->GetProfileAttributesStorage(). + GetProfileAttributesWithPath(profile->GetPath(), &entry)) return false; #if defined(OS_CHROMEOS) AvatarMenu::GetImageForMenuButton(profile->GetPath(), avatar, is_rectangle); #else - *avatar = cache.GetAvatarIconOfProfileAtIndex(index); + *avatar = entry->GetAvatarIcon(); // TODO(noms): Once the code for the old avatar menu button is removed, // this function will only be called for badging the taskbar icon. The // function can be renamed to something like GetAvatarImageForBadging()
diff --git a/chrome/browser/ui/views/profiles/new_avatar_button.cc b/chrome/browser/ui/views/profiles/new_avatar_button.cc index 5595166..beef30e 100644 --- a/chrome/browser/ui/views/profiles/new_avatar_button.cc +++ b/chrome/browser/ui/views/profiles/new_avatar_button.cc
@@ -9,9 +9,10 @@ #include "base/win/windows_version.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profiles_state.h" -#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/views/profiles/avatar_button_delegate.h" #include "chrome/browser/ui/views/profiles/profile_chooser_view.h" #include "grit/theme_resources.h" #include "ui/base/resource/resource_bundle.h" @@ -49,11 +50,12 @@ } // namespace -NewAvatarButton::NewAvatarButton(views::ButtonListener* listener, +NewAvatarButton::NewAvatarButton(AvatarButtonDelegate* delegate, AvatarButtonStyle button_style, - Browser* browser) - : LabelButton(listener, base::string16()), - browser_(browser), + Profile* profile) + : LabelButton(delegate, base::string16()), + delegate_(delegate), + profile_(profile), has_auth_error_(false), suppress_mouse_released_action_(false) { set_triggerable_event_flags( @@ -100,12 +102,12 @@ *rb->GetImageNamed(IDR_AVATAR_NATIVE_BUTTON_AVATAR).ToImageSkia(); } - g_browser_process->profile_manager()->GetProfileInfoCache().AddObserver(this); + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().AddObserver(this); // Subscribe to authentication error changes so that the avatar button can // update itself. Note that guest mode profiles won't have a token service. - SigninErrorController* error = - profiles::GetSigninErrorController(browser_->profile()); + SigninErrorController* error = profiles::GetSigninErrorController(profile_); if (error) { error->AddObserver(this); OnErrorChanged(); // This calls Update(). @@ -117,9 +119,9 @@ NewAvatarButton::~NewAvatarButton() { g_browser_process->profile_manager()-> - GetProfileInfoCache().RemoveObserver(this); + GetProfileAttributesStorage().RemoveObserver(this); SigninErrorController* error = - profiles::GetSigninErrorController(browser_->profile()); + profiles::GetSigninErrorController(profile_); if (error) error->RemoveObserver(this); } @@ -157,45 +159,47 @@ const base::string16& profile_name) { // If deleting the active profile, don't bother updating the avatar // button, as the browser window is being closed anyway. - if (browser_->profile()->GetPath() != profile_path) + if (profile_->GetPath() != profile_path) Update(); } void NewAvatarButton::OnProfileNameChanged( const base::FilePath& profile_path, const base::string16& old_profile_name) { - if (browser_->profile()->GetPath() == profile_path) + if (profile_->GetPath() == profile_path) Update(); } void NewAvatarButton::OnProfileSupervisedUserIdChanged( const base::FilePath& profile_path) { - if (browser_->profile()->GetPath() == profile_path) + if (profile_->GetPath() == profile_path) Update(); } void NewAvatarButton::OnErrorChanged() { // If there is an error, show an warning icon. const SigninErrorController* error = - profiles::GetSigninErrorController(browser_->profile()); + profiles::GetSigninErrorController(profile_); has_auth_error_ = error && error->HasError(); Update(); } void NewAvatarButton::Update() { - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); + ProfileAttributesStorage& storage = + g_browser_process->profile_manager()->GetProfileAttributesStorage(); // If we have a single local profile, then use the generic avatar // button instead of the profile name. Never use the generic button if // the active profile is Guest. - bool use_generic_button = (!browser_->profile()->IsGuestSession() && - cache.GetNumberOfProfiles() == 1 && - !cache.ProfileIsAuthenticatedAtIndex(0)); + const bool use_generic_button = + !profile_->IsGuestSession() && + storage.GetNumberOfProfiles() == 1 && + storage.GetAllProfilesAttributes().front()->IsAuthenticated(); - SetText(use_generic_button ? base::string16() : - profiles::GetAvatarButtonTextForProfile(browser_->profile())); + SetText(use_generic_button + ? base::string16() + : profiles::GetAvatarButtonTextForProfile(profile_)); // If the button has no text, clear the text shadows to make sure the // image is centered correctly. @@ -224,4 +228,5 @@ SetImageLabelSpacing(use_generic_button ? 0 : kDefaultImageTextSpacing); PreferredSizeChanged(); + delegate_->ButtonPreferredSizeChanged(); }
diff --git a/chrome/browser/ui/views/profiles/new_avatar_button.h b/chrome/browser/ui/views/profiles/new_avatar_button.h index 2a06e3f1..0811d6d99 100644 --- a/chrome/browser/ui/views/profiles/new_avatar_button.h +++ b/chrome/browser/ui/views/profiles/new_avatar_button.h
@@ -6,21 +6,22 @@ #define CHROME_BROWSER_UI_VIEWS_PROFILES_NEW_AVATAR_BUTTON_H_ #include "base/macros.h" -#include "chrome/browser/profiles/profile_info_cache_observer.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/ui/views/profiles/avatar_button_style.h" #include "components/signin/core/browser/signin_error_controller.h" #include "ui/views/controls/button/label_button.h" -class Browser; +class AvatarButtonDelegate; +class Profile; // Avatar button that displays the active profile's name in the caption area. class NewAvatarButton : public views::LabelButton, - public ProfileInfoCacheObserver, + public ProfileAttributesStorage::Observer, public SigninErrorController::Observer { public: - NewAvatarButton(views::ButtonListener* listener, + NewAvatarButton(AvatarButtonDelegate* delegate, AvatarButtonStyle button_style, - Browser* browser); + Profile* profile); ~NewAvatarButton() override; // Views::LabelButton @@ -33,7 +34,7 @@ private: friend class ProfileChooserViewExtensionsTest; - // ProfileInfoCacheObserver: + // ProfileAttributesStorage::Observer: void OnProfileAdded(const base::FilePath& profile_path) override; void OnProfileWasRemoved(const base::FilePath& profile_path, const base::string16& profile_name) override; @@ -49,7 +50,8 @@ // have to update the icon/text of the button. void Update(); - Browser* browser_; + AvatarButtonDelegate* delegate_; + Profile* profile_; // Whether the signed in profile has an authentication error. Used to display // an error icon next to the button text.
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc index 8149ce93..95234c0 100644 --- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc +++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_metrics.h" #include "chrome/browser/profiles/profile_window.h" @@ -671,7 +670,7 @@ ResetView(); avatar_menu_.reset(new AvatarMenu( - &g_browser_process->profile_manager()->GetProfileInfoCache(), + &g_browser_process->profile_manager()->GetProfileAttributesStorage(), this, browser_)); avatar_menu_->RebuildMenu();
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc index 51223c7..3f9d3ccdd 100644 --- a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc +++ b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
@@ -14,6 +14,8 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_metrics.h" #include "chrome/browser/profiles/profiles_state.h" @@ -68,17 +70,26 @@ // Set up the profiles to enable Lock. Takes as parameter a profile that will be // signed in, and also creates a supervised user (necessary for lock). void SetupProfilesForLock(Profile* signed_in) { - const char* signed_in_email = "me@google.com"; - Profile* supervised = CreateTestingProfile("supervised"); - ProfileInfoCache* cache = &g_browser_process->profile_manager()-> - GetProfileInfoCache(); - cache->SetAuthInfoOfProfileAtIndex(cache->GetIndexOfProfileWithPath( - signed_in->GetPath()), "12345", base::UTF8ToUTF16(signed_in_email)); - signed_in->GetPrefs()-> - SetString(prefs::kGoogleServicesHostedDomain, "google.com"); - cache->SetSupervisedUserIdOfProfileAtIndex(cache->GetIndexOfProfileWithPath( - supervised->GetPath()), signed_in_email); + const char signed_in_email[] = "me@google.com"; + // Set up the |signed_in| profile. + ProfileAttributesStorage* storage = + &g_browser_process->profile_manager()->GetProfileAttributesStorage(); + ProfileAttributesEntry* entry_signed_in; + ASSERT_TRUE(storage->GetProfileAttributesWithPath(signed_in->GetPath(), + &entry_signed_in)); + entry_signed_in->SetAuthInfo("12345", base::UTF8ToUTF16(signed_in_email)); + signed_in->GetPrefs()->SetString(prefs::kGoogleServicesHostedDomain, + "google.com"); + + // Create the |supervised| profile, which is supervised by |signed_in|. + ProfileAttributesEntry* entry_supervised; + Profile* supervised = CreateTestingProfile("supervised"); + ASSERT_TRUE(storage->GetProfileAttributesWithPath(supervised->GetPath(), + &entry_supervised)); + entry_supervised->SetSupervisedUserId(signed_in_email); + + // |signed_in| should now be lockable. EXPECT_TRUE(profiles::IsLockAvailable(signed_in)); } @@ -115,7 +126,7 @@ switches::EnableNewProfileManagementForTesting(command_line); } - void OpenProfileChooserView(Browser* browser){ + void OpenProfileChooserView(Browser* browser) { BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); views::View* button = browser_view->frame()->GetNewAvatarMenuButton(); if (!button)
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc index e8a114dd..7b3c647a 100644 --- a/chrome/browser/ui/views/sad_tab_view.cc +++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -13,6 +13,7 @@ #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" #include "components/feedback/feedback_util.h" +#include "components/strings/grit/components_strings.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/web_contents.h" #include "grit/components_strings.h"
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc index 17d816d5..fefb49f 100644 --- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -61,16 +61,15 @@ return TabRendererData::NETWORK_STATE_LOADING; } -bool DetermineTabStripLayoutStacked( - PrefService* prefs, - chrome::HostDesktopType host_desktop_type, - bool* adjust_layout) { +bool DetermineTabStripLayoutStacked(PrefService* prefs, bool* adjust_layout) { *adjust_layout = false; // For ash, always allow entering stacked mode. - if (host_desktop_type != chrome::HOST_DESKTOP_TYPE_ASH) - return false; +#if defined(USE_ASH) *adjust_layout = true; return prefs->GetBoolean(prefs::kTabStripStackedLayout); +#else + return false; +#endif } // Get the MIME type of the file pointed to by the url, based on the file's @@ -389,10 +388,8 @@ void BrowserTabStripController::StackedLayoutMaybeChanged() { bool adjust_layout = false; - bool stacked_layout = - DetermineTabStripLayoutStacked(g_browser_process->local_state(), - browser_->host_desktop_type(), - &adjust_layout); + bool stacked_layout = DetermineTabStripLayoutStacked( + g_browser_process->local_state(), &adjust_layout); if (!adjust_layout || stacked_layout == tabstrip_->stacked_layout()) return; @@ -562,10 +559,8 @@ void BrowserTabStripController::UpdateStackedLayout() { bool adjust_layout = false; - bool stacked_layout = - DetermineTabStripLayoutStacked(g_browser_process->local_state(), - browser_->host_desktop_type(), - &adjust_layout); + bool stacked_layout = DetermineTabStripLayoutStacked( + g_browser_process->local_state(), &adjust_layout); tabstrip_->set_adjust_layout(adjust_layout); tabstrip_->SetStackedLayout(stacked_layout); }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc index 2b8bb77..eb5dd06 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -198,7 +198,6 @@ : event_source_(EVENT_SOURCE_MOUSE), source_tabstrip_(NULL), attached_tabstrip_(NULL), - host_desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE), can_release_capture_(true), offset_to_width_ratio_(0), old_focused_view_id_( @@ -262,8 +261,6 @@ source_tabstrip_ = source_tabstrip; was_source_maximized_ = source_tabstrip->GetWidget()->IsMaximized(); was_source_fullscreen_ = source_tabstrip->GetWidget()->IsFullscreen(); - host_desktop_type_ = chrome::GetHostDesktopTypeForNativeView( - source_tabstrip->GetWidget()->GetNativeView()); // Do not release capture when transferring capture between widgets on: // - Desktop Linux // Mouse capture is not synchronous on desktop Linux. Chrome makes @@ -271,11 +268,8 @@ // synchronous on desktop Linux, so use that. // - Ash // Releasing capture on Ash cancels gestures so avoid it. -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(USE_ASH) can_release_capture_ = false; -#else - can_release_capture_ = - (host_desktop_type_ != chrome::HOST_DESKTOP_TYPE_ASH); #endif start_point_in_screen_ = gfx::Point(source_tab_offset, mouse_offset.y()); views::View::ConvertPointToScreen(source_tab, &start_point_in_screen_); @@ -604,10 +598,10 @@ // ReleaseCapture() is going to result in calling back to us (because it // results in a move). That'll cause all sorts of problems. Reset the // observer so we don't get notified and process the event. - if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) { - move_loop_widget_->RemoveObserver(this); - move_loop_widget_ = NULL; - } +#if defined(USE_ASH) + move_loop_widget_->RemoveObserver(this); + move_loop_widget_ = nullptr; +#endif // USE_ASH views::Widget* browser_widget = GetAttachedBrowserWidget(); // Need to release the drag controller before starting the move loop as it's // going to trigger capture lost, which cancels drag. @@ -1541,8 +1535,7 @@ void TabDragController::MaximizeAttachedWindow() { GetAttachedBrowserWidget()->Maximize(); #if defined(USE_ASH) - if (was_source_fullscreen_ && - host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) { + if (was_source_fullscreen_) { // In fullscreen mode it is only possible to get here if the source // was in "immersive fullscreen" mode, so toggle it back on. ash::accelerators::ToggleFullscreen(); @@ -1575,36 +1568,32 @@ return; #if defined(USE_ASH) - if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH) { - // TODO(varkha): The code below ensures that the phantom drag widget - // is shown on top of browser windows. The code should be moved to ash/ - // and the phantom should be able to assert its top-most state on its own. - // One strategy would be for DragWindowController to - // be able to observe stacking changes to the phantom drag widget's - // siblings in order to keep it on top. One way is to implement a - // notification that is sent to a window parent's observers when a - // stacking order is changed among the children of that same parent. - // Note that OnWindowStackingChanged is sent only to the child that is the - // argument of one of the Window::StackChildX calls and not to all its - // siblings affected by the stacking change. - aura::Window* browser_window = widget_window->GetNativeView(); - // Find a topmost non-popup window and stack the recipient browser above - // it in order to avoid stacking the browser window on top of the phantom - // drag widget created by DragWindowController in a second display. - for (aura::Window::Windows::const_reverse_iterator it = - browser_window->parent()->children().rbegin(); - it != browser_window->parent()->children().rend(); ++it) { - // If the iteration reached the recipient browser window then it is - // already topmost and it is safe to return with no stacking change. - if (*it == browser_window) - return; - if ((*it)->type() != ui::wm::WINDOW_TYPE_POPUP) { - widget_window->StackAbove(*it); - break; - } + // TODO(varkha): The code below ensures that the phantom drag widget + // is shown on top of browser windows. The code should be moved to ash/ + // and the phantom should be able to assert its top-most state on its own. + // One strategy would be for DragWindowController to + // be able to observe stacking changes to the phantom drag widget's + // siblings in order to keep it on top. One way is to implement a + // notification that is sent to a window parent's observers when a + // stacking order is changed among the children of that same parent. + // Note that OnWindowStackingChanged is sent only to the child that is the + // argument of one of the Window::StackChildX calls and not to all its + // siblings affected by the stacking change. + aura::Window* browser_window = widget_window->GetNativeView(); + // Find a topmost non-popup window and stack the recipient browser above + // it in order to avoid stacking the browser window on top of the phantom + // drag widget created by DragWindowController in a second display. + for (aura::Window::Windows::const_reverse_iterator it = + browser_window->parent()->children().rbegin(); + it != browser_window->parent()->children().rend(); ++it) { + // If the iteration reached the recipient browser window then it is + // already topmost and it is safe to return with no stacking change. + if (*it == browser_window) + return; + if ((*it)->type() != ui::wm::WINDOW_TYPE_POPUP) { + widget_window->StackAbove(*it); + break; } - } else { - widget_window->StackAtTop(); } #else widget_window->StackAtTop(); @@ -1762,8 +1751,7 @@ gfx::Point TabDragController::GetCursorScreenPoint() { #if defined(USE_ASH) - if (host_desktop_type_ == chrome::HOST_DESKTOP_TYPE_ASH && - event_source_ == EVENT_SOURCE_TOUCH && + if (event_source_ == EVENT_SOURCE_TOUCH && aura::Env::GetInstance()->is_touch_down()) { views::Widget* widget = GetAttachedBrowserWidget(); DCHECK(widget);
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h index 637a651..7e47fa59 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller.h +++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -462,11 +462,6 @@ // dragged Tab is detached. TabStrip* attached_tabstrip_; - // The desktop type that this drag is associated with. Cached, because other - // UI elements are NULLd at various points during the lifetime of this - // object. - chrome::HostDesktopType host_desktop_type_; - // Whether capture can be released during the drag. When false, capture should // not be released when transferring capture between widgets and when starting // the move loop.
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index 8fed116c..32edbdc9 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1093,18 +1093,18 @@ } SkAlpha TabStrip::GetInactiveAlpha(bool for_new_tab_button) const { - static const SkAlpha kInactiveTabAlphaAsh = 230; - static const SkAlpha kInactiveTabAlphaGlass = 200; static const SkAlpha kInactiveTabAlphaOpaque = 255; static const double kMultiSelectionMultiplier = 0.6; - const chrome::HostDesktopType host_desktop_type = - chrome::GetHostDesktopTypeForNativeView(GetWidget()->GetNativeView()); SkAlpha base_alpha = kInactiveTabAlphaOpaque; - if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH) - base_alpha = kInactiveTabAlphaAsh; - else if (GetWidget()->ShouldWindowContentsBeTransparent()) +#if defined(USE_ASH) + static const SkAlpha kInactiveTabAlphaAsh = 230; + base_alpha = kInactiveTabAlphaAsh; +#else + static const SkAlpha kInactiveTabAlphaGlass = 200; + if (GetWidget()->ShouldWindowContentsBeTransparent()) base_alpha = kInactiveTabAlphaGlass; +#endif // USE_ASH return (for_new_tab_button || (GetSelectionModel().size() <= 1)) ? base_alpha : static_cast<SkAlpha>(kMultiSelectionMultiplier * base_alpha); }
diff --git a/chrome/browser/ui/website_settings/website_settings_ui.cc b/chrome/browser/ui/website_settings/website_settings_ui.cc index 373792e4..ff950c3 100644 --- a/chrome/browser/ui/website_settings/website_settings_ui.cc +++ b/chrome/browser/ui/website_settings/website_settings_ui.cc
@@ -8,6 +8,7 @@ #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" #include "components/content_settings/core/browser/plugins_field_trial.h" +#include "components/strings/grit/components_strings.h" #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h"
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc b/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc index 1dc0fff6..47662f283 100644 --- a/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc
@@ -21,7 +21,9 @@ #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/web_ui.h" +#include "extensions/grit/extensions_browser_resources.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/base/webui/web_ui_util.h" namespace chromeos { @@ -104,10 +106,15 @@ app_info->SetString("id", app_data.app_id); app_info->SetString("label", app_data.name); - // TODO(xiyuan): Replace data url with a URLDataSource. - std::string icon_url("chrome://theme/IDR_APP_DEFAULT_ICON"); - if (!app_data.icon.isNull()) + std::string icon_url; + if (app_data.icon.isNull()) { + icon_url = + webui::GetBitmapDataUrl(*ResourceBundle::GetSharedInstance() + .GetImageNamed(IDR_APP_DEFAULT_ICON) + .ToSkBitmap()); + } else { icon_url = webui::GetBitmapDataUrl(*app_data.icon.bitmap()); + } app_info->SetString("iconUrl", icon_url); apps_list.Append(app_info.release()); @@ -155,6 +162,10 @@ SendKioskApps(); } +void KioskAppMenuHandler::OnKioskAppDataLoadFailure(const std::string& app_id) { + SendKioskApps(); +} + void KioskAppMenuHandler::UpdateState(NetworkError::ErrorReason reason) { if (network_state_informer_->state() == NetworkStateInformer::ONLINE) KioskAppManager::Get()->RetryFailedAppDataFetch();
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h b/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h index f3d5c7e..2e90a80 100644 --- a/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h
@@ -51,6 +51,7 @@ // KioskAppManagerObserver overrides: void OnKioskAppsSettingsChanged() override; void OnKioskAppDataChanged(const std::string& app_id) override; + void OnKioskAppDataLoadFailure(const std::string& app_id) override; // NetworkStateInformer::NetworkStateInformerObserver overrides: void UpdateState(NetworkError::ErrorReason reason) override;
diff --git a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc index 7c3bd564..2601cc4 100644 --- a/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc +++ b/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc
@@ -28,8 +28,10 @@ #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" #include "extensions/common/extension_urls.h" +#include "extensions/grit/extensions_browser_resources.h" #include "grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/base/webui/web_ui_util.h" #include "url/gurl.h" @@ -40,11 +42,14 @@ // Populates app info dictionary with |app_data|. void PopulateAppDict(const KioskAppManager::App& app_data, base::DictionaryValue* app_dict) { - std::string icon_url("chrome://theme/IDR_APP_DEFAULT_ICON"); - - // TODO(xiyuan): Replace data url with a URLDataSource. - if (!app_data.icon.isNull()) + std::string icon_url; + if (app_data.icon.isNull()) { + icon_url = webui::GetBitmapDataUrl(*ResourceBundle::GetSharedInstance() + .GetImageNamed(IDR_APP_DEFAULT_ICON) + .ToSkBitmap()); + } else { icon_url = webui::GetBitmapDataUrl(*app_data.icon.bitmap()); + } // The items which are to be written into app_dict are also described in // chrome/browser/resources/extensions/chromeos/kiosk_app_list.js in @typedef
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc index 62890457..a223179 100644 --- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc +++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
@@ -226,19 +226,19 @@ const char kResponseRegisterStart[] = "{" " \"action\": \"start\"," - " \"user\": \"user@host.com\"" + " \"user\": \"user@consumer.example.com\"" "}"; const char kResponseRegisterClaimTokenNoConfirm[] = "{" " \"action\": \"getClaimToken\"," - " \"user\": \"user@host.com\"," + " \"user\": \"user@consumer.example.com\"," " \"error\": \"pending_user_action\"," " \"timeout\": 1" "}"; const char kResponseRegisterClaimTokenConfirm[] = "{" " \"action\": \"getClaimToken\"," - " \"user\": \"user@host.com\"," + " \"user\": \"user@consumer.example.com\"," " \"token\": \"MySampleToken\"," " \"claim_url\": \"http://someurl.com/\"" "}"; @@ -247,7 +247,7 @@ const char kResponseRegisterComplete[] = "{" " \"action\": \"complete\"," - " \"user\": \"user@host.com\"," + " \"user\": \"user@consumer.example.com\"," " \"device_id\": \"my_id\"" "}"; @@ -264,20 +264,22 @@ const char kURLInfo[] = "http://1.2.3.4:8888/privet/info"; const char kURLRegisterStart[] = - "http://1.2.3.4:8888/privet/register?action=start&user=user%40host.com"; + "http://1.2.3.4:8888/privet/register?action=start&" + "user=user%40consumer.example.com"; const char kURLRegisterClaimToken[] = "http://1.2.3.4:8888/privet/register?action=getClaimToken&" - "user=user%40host.com"; + "user=user%40consumer.example.com"; const char kURLCloudPrintConfirm[] = "https://www.google.com/cloudprint/confirm?token=MySampleToken"; const char kURLRegisterComplete[] = - "http://1.2.3.4:8888/privet/register?action=complete&user=user%40host.com"; + "http://1.2.3.4:8888/privet/register?action=complete&" + "user=user%40consumer.example.com"; const char kSampleGaiaId[] = "12345"; -const char kSampleUser[] = "user@host.com"; +const char kSampleUser[] = "user@consumer.example.com"; class TestMessageLoopCondition { public:
diff --git a/chrome/browser/ui/webui/ntp/new_tab_ui.cc b/chrome/browser/ui/webui/ntp/new_tab_ui.cc index a07ae5a2..9fc2d3bd 100644 --- a/chrome/browser/ui/webui/ntp/new_tab_ui.cc +++ b/chrome/browser/ui/webui/ntp/new_tab_ui.cc
@@ -27,6 +27,7 @@ #include "components/bookmarks/common/bookmark_pref_names.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" +#include "components/strings/grit/components_strings.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h"
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc index 5c3f4f99..3baf8ef 100644 --- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc +++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -45,6 +45,7 @@ #include "components/google/core/browser/google_util.h" #include "components/prefs/pref_service.h" #include "components/signin/core/browser/signin_manager.h" +#include "components/strings/grit/components_strings.h" #include "components/web_resource/notification_promo.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc index b904f742..04d0fae 100644 --- a/chrome/browser/ui/webui/options/browser_options_handler.cc +++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -37,8 +37,8 @@ #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_metrics.h" #include "chrome/browser/profiles/profile_shortcut_manager.h" @@ -837,7 +837,7 @@ void BrowserOptionsHandler::Uninitialize() { registrar_.RemoveAll(); g_browser_process->profile_manager()-> - GetProfileInfoCache().RemoveObserver(this); + GetProfileAttributesStorage().RemoveObserver(this); #if defined(OS_WIN) ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))->RemoveObserver(this); #endif @@ -888,7 +888,8 @@ g_browser_process->policy_service()->AddObserver( policy::POLICY_DOMAIN_CHROME, this); - g_browser_process->profile_manager()->GetProfileInfoCache().AddObserver(this); + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().AddObserver(this); ProfileSyncService* sync_service( ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile)); @@ -1294,37 +1295,33 @@ } scoped_ptr<base::ListValue> BrowserOptionsHandler::GetProfilesInfoList() { - ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); + std::vector<ProfileAttributesEntry*> entries = + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().GetAllProfilesAttributesSortedByName(); scoped_ptr<base::ListValue> profile_info_list(new base::ListValue); base::FilePath current_profile_path = web_ui()->GetWebContents()->GetBrowserContext()->GetPath(); - for (size_t i = 0, e = cache.GetNumberOfProfiles(); i < e; ++i) { + for (const ProfileAttributesEntry* entry : entries) { // The items in |profile_value| are also described in // chrome/browser/resources/options/browser_options.js in a @typedef for // Profile. Please update it whenever you add or remove any keys here. - base::DictionaryValue* profile_value = new base::DictionaryValue(); - profile_value->SetString("name", cache.GetNameOfProfileAtIndex(i)); - base::FilePath profile_path = cache.GetPathOfProfileAtIndex(i); + profile_value->SetString("name", entry->GetName()); + base::FilePath profile_path = entry->GetPath(); profile_value->Set("filePath", base::CreateFilePathValue(profile_path)); profile_value->SetBoolean("isCurrentProfile", profile_path == current_profile_path); - profile_value->SetBoolean("isSupervised", - cache.ProfileIsSupervisedAtIndex(i)); - profile_value->SetBoolean("isChild", cache.ProfileIsChildAtIndex(i)); + profile_value->SetBoolean("isSupervised", entry->IsSupervised()); + profile_value->SetBoolean("isChild", entry->IsChild()); - bool is_gaia_picture = - cache.IsUsingGAIAPictureOfProfileAtIndex(i) && - cache.GetGAIAPictureOfProfileAtIndex(i); - if (is_gaia_picture) { - gfx::Image icon = profiles::GetAvatarIconForWebUI( - cache.GetAvatarIconOfProfileAtIndex(i), true); + if (entry->IsUsingGAIAPicture() && entry->GetGAIAPicture()) { + gfx::Image icon = profiles::GetAvatarIconForWebUI(entry->GetAvatarIcon(), + true); profile_value->SetString("iconURL", - webui::GetBitmapDataUrl(icon.AsBitmap())); + webui::GetBitmapDataUrl(icon.AsBitmap())); } else { - size_t icon_index = cache.GetAvatarIconIndexOfProfileAtIndex(i); + size_t icon_index = entry->GetAvatarIconIndex(); profile_value->SetString("iconURL", profiles::GetDefaultAvatarIconUrl(icon_index)); }
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.h b/chrome/browser/ui/webui/options/browser_options_handler.h index f3eeaad..2cbedb3 100644 --- a/chrome/browser/ui/webui/options/browser_options_handler.h +++ b/chrome/browser/ui/webui/options/browser_options_handler.h
@@ -15,7 +15,7 @@ #include "base/scoped_observer.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/profiles/profile_info_cache_observer.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/shell_integration.h" #include "chrome/browser/ui/host_desktop.h" #include "chrome/browser/ui/webui/options/options_ui.h" @@ -56,7 +56,7 @@ // Chrome browser options page UI handler. class BrowserOptionsHandler : public OptionsPageUIHandler, - public ProfileInfoCacheObserver, + public ProfileAttributesStorage::Observer, public sync_driver::SyncServiceObserver, public SigninManagerBase::Observer, public ui::SelectFileDialog::Listener, @@ -116,13 +116,13 @@ const content::NotificationSource& source, const content::NotificationDetails& details) override; - // ProfileInfoCacheObserver implementation. - void OnProfileAdded(const base::FilePath& profile_path) override; - void OnProfileWasRemoved(const base::FilePath& profile_path, - const base::string16& profile_name) override; - void OnProfileNameChanged(const base::FilePath& profile_path, - const base::string16& old_profile_name) override; - void OnProfileAvatarChanged(const base::FilePath& profile_path) override; + // ProfileAttributesStorage::Observer implementation. + void OnProfileAdded(const base::FilePath& profile_path) override; + void OnProfileWasRemoved(const base::FilePath& profile_path, + const base::string16& profile_name) override; + void OnProfileNameChanged(const base::FilePath& profile_path, + const base::string16& old_profile_name) override; + void OnProfileAvatarChanged(const base::FilePath& profile_path) override; #if defined(ENABLE_PRINT_PREVIEW) && !defined(OS_CHROMEOS) void OnCloudPrintPrefsChanged();
diff --git a/chrome/browser/ui/webui/options/certificate_manager_handler.cc b/chrome/browser/ui/webui/options/certificate_manager_handler.cc index cba6c94d..5dbd645 100644 --- a/chrome/browser/ui/webui/options/certificate_manager_handler.cc +++ b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
@@ -37,6 +37,8 @@ #include "net/base/crypto_module.h" #include "net/base/net_errors.h" #include "net/cert/x509_certificate.h" +#include "net/der/input.h" +#include "net/der/parser.h" #include "ui/base/l10n/l10n_util.h" #if defined(OS_CHROMEOS) @@ -145,9 +147,42 @@ } #endif -bool IsFileExtensionPkcs12(const base::FilePath& path) { - return path.MatchesExtension(FILE_PATH_LITERAL(".p12")) || - path.MatchesExtension(FILE_PATH_LITERAL(".pfx")); +// Determine if |data| could be a PFX Protocol Data Unit. +// This only does the minimum parsing necessary to distinguish a PFX file from a +// DER encoded Certificate. +// +// From RFC 7292 section 4: +// PFX ::= SEQUENCE { +// version INTEGER {v3(3)}(v3,...), +// authSafe ContentInfo, +// macData MacData OPTIONAL +// } +// From RFC 5280 section 4.1: +// Certificate ::= SEQUENCE { +// tbsCertificate TBSCertificate, +// signatureAlgorithm AlgorithmIdentifier, +// signatureValue BIT STRING } +// +// Certificate must be DER encoded, while PFX may be BER encoded. +// Therefore PFX can be distingushed by checking if the file starts with an +// indefinite SEQUENCE, or a definite SEQUENCE { INTEGER, ... }. +bool CouldBePFX(const std::string& data) { + if (data.size() < 4) + return false; + + // Indefinite length SEQUENCE. + if (data[0] == 0x30 && static_cast<uint8_t>(data[1]) == 0x80) + return true; + + // If the SEQUENCE is definite length, it can be parsed through the version + // tag using DER parser, since INTEGER must be definite length, even in BER. + net::der::Parser parser((net::der::Input(&data))); + net::der::Parser sequence_parser; + if (!parser.ReadSequence(&sequence_parser)) + return false; + if (!sequence_parser.SkipTag(net::der::kInteger)) + return false; + return true; } } // namespace @@ -737,33 +772,9 @@ void CertificateManagerHandler::ImportPersonalFileSelected( const base::FilePath& path) { - file_path_ = path; - if (IsFileExtensionPkcs12(file_path_)) { - web_ui()->CallJavascriptFunction( - "CertificateManager.importPersonalAskPassword"); - return; - } - - // Non .p12/.pfx files are treated as unencrypted certificates. - password_.clear(); file_access_provider_->StartRead( - file_path_, - base::Bind(&CertificateManagerHandler::ImportPersonalFileRead, - base::Unretained(this)), - &tracker_); -} - -void CertificateManagerHandler::ImportPersonalPasswordSelected( - const base::ListValue* args) { - if (!args->GetString(0, &password_)) { - web_ui()->CallJavascriptFunction("CertificateRestoreOverlay.dismiss"); - ImportExportCleanup(); - return; - } - file_access_provider_->StartRead( - file_path_, - base::Bind(&CertificateManagerHandler::ImportPersonalFileRead, - base::Unretained(this)), + path, base::Bind(&CertificateManagerHandler::ImportPersonalFileRead, + base::Unretained(this)), &tracker_); } @@ -782,22 +793,9 @@ file_data_ = *data; - if (IsFileExtensionPkcs12(file_path_)) { - if (use_hardware_backed_) { - module_ = certificate_manager_model_->cert_db()->GetPrivateModule(); - } else { - module_ = certificate_manager_model_->cert_db()->GetPublicModule(); - } - - net::CryptoModuleList modules; - modules.push_back(module_); - chrome::UnlockSlotsIfNecessary( - modules, - chrome::kCryptoModulePasswordCertImport, - net::HostPortPair(), // unused. - GetParentWindow(), - base::Bind(&CertificateManagerHandler::ImportPersonalSlotUnlocked, - base::Unretained(this))); + if (CouldBePFX(file_data_)) { + web_ui()->CallJavascriptFunction( + "CertificateManager.importPersonalAskPassword"); return; } @@ -827,6 +825,31 @@ l10n_util::GetStringUTF8(string_id)); } +void CertificateManagerHandler::ImportPersonalPasswordSelected( + const base::ListValue* args) { + if (!args->GetString(0, &password_)) { + web_ui()->CallJavascriptFunction("CertificateRestoreOverlay.dismiss"); + ImportExportCleanup(); + return; + } + + if (use_hardware_backed_) { + module_ = certificate_manager_model_->cert_db()->GetPrivateModule(); + } else { + module_ = certificate_manager_model_->cert_db()->GetPublicModule(); + } + + net::CryptoModuleList modules; + modules.push_back(module_); + chrome::UnlockSlotsIfNecessary( + modules, + chrome::kCryptoModulePasswordCertImport, + net::HostPortPair(), // unused. + GetParentWindow(), + base::Bind(&CertificateManagerHandler::ImportPersonalSlotUnlocked, + base::Unretained(this))); +} + void CertificateManagerHandler::ImportPersonalSlotUnlocked() { // Determine if the private key should be unextractable after the import. // We do this by checking the value of |use_hardware_backed_| which is set @@ -876,6 +899,7 @@ use_hardware_backed_ = false; selected_cert_list_.clear(); module_ = NULL; + tracker_.TryCancelAll(); // There may be pending file dialogs, we need to tell them that we've gone // away so they don't try and call back to us. @@ -897,11 +921,9 @@ void CertificateManagerHandler::ImportServerFileSelected( const base::FilePath& path) { - file_path_ = path; file_access_provider_->StartRead( - file_path_, - base::Bind(&CertificateManagerHandler::ImportServerFileRead, - base::Unretained(this)), + path, base::Bind(&CertificateManagerHandler::ImportServerFileRead, + base::Unretained(this)), &tracker_); } @@ -957,11 +979,9 @@ void CertificateManagerHandler::ImportCAFileSelected( const base::FilePath& path) { - file_path_ = path; file_access_provider_->StartRead( - file_path_, - base::Bind(&CertificateManagerHandler::ImportCAFileRead, - base::Unretained(this)), + path, base::Bind(&CertificateManagerHandler::ImportCAFileRead, + base::Unretained(this)), &tracker_); }
diff --git a/chrome/browser/ui/webui/options/certificate_manager_handler.h b/chrome/browser/ui/webui/options/certificate_manager_handler.h index bf60244..a5e9bc7 100644 --- a/chrome/browser/ui/webui/options/certificate_manager_handler.h +++ b/chrome/browser/ui/webui/options/certificate_manager_handler.h
@@ -81,14 +81,15 @@ void ExportPersonalFileWritten(const int* write_errno, const int* bytes_written); - // Import from PKCS #12 file. The sequence goes like: + // Import from PKCS #12 or cert file. The sequence goes like: // 1. user click on import button -> StartImportPersonal -> launches file // selector - // 2. user selects file -> ImportPersonalFileSelected -> launches password - // dialog - // 3. user enters password -> ImportPersonalPasswordSelected -> starts async + // 2. user selects file -> ImportPersonalFileSelected -> starts async // read operation - // 4. read operation completes -> ImportPersonalFileRead -> unlock slot + // 3. read operation completes -> ImportPersonalFileRead -> + // If file is PFX -> launches password dialog, goto step 4 + // Else -> import as certificate, goto step 6 + // 4. user enters password -> ImportPersonalPasswordSelected -> unlock slot // 5. slot unlocked -> ImportPersonalSlotUnlocked attempts to // import with previously entered password // 6a. if import succeeds -> ImportExportCleanup @@ -96,8 +97,8 @@ // TODO(mattm): allow retrying with different password void StartImportPersonal(const base::ListValue* args); void ImportPersonalFileSelected(const base::FilePath& path); - void ImportPersonalPasswordSelected(const base::ListValue* args); void ImportPersonalFileRead(const int* read_errno, const std::string* data); + void ImportPersonalPasswordSelected(const base::ListValue* args); void ImportPersonalSlotUnlocked(); // Import Server certificates from file. Sequence goes like:
diff --git a/chrome/browser/ui/webui/options/create_profile_handler.cc b/chrome/browser/ui/webui/options/create_profile_handler.cc index 7f87381..6036836a 100644 --- a/chrome/browser/ui/webui/options/create_profile_handler.cc +++ b/chrome/browser/ui/webui/options/create_profile_handler.cc
@@ -6,6 +6,8 @@ #include <stddef.h> +#include <vector> + #include "base/bind.h" #include "base/files/file_path.h" #include "base/metrics/histogram.h" @@ -13,6 +15,8 @@ #include "base/value_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_metrics.h" @@ -460,11 +464,11 @@ return false; // Check if this supervised user already exists on this machine. - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) { - if (existing_supervised_user_id == - cache.GetSupervisedUserIdOfProfileAtIndex(i)) + std::vector<ProfileAttributesEntry*> entries = + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().GetAllProfilesAttributes(); + for (const ProfileAttributesEntry* entry : entries) { + if (existing_supervised_user_id == entry->GetSupervisedUserId()) return false; } return true;
diff --git a/chrome/browser/ui/webui/options/create_profile_handler.h b/chrome/browser/ui/webui/options/create_profile_handler.h index dafcd91b..867f456 100644 --- a/chrome/browser/ui/webui/options/create_profile_handler.h +++ b/chrome/browser/ui/webui/options/create_profile_handler.h
@@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CREATE_PROFILE_HANDLER_H_ #define CHROME_BROWSER_UI_WEBUI_OPTIONS_CREATE_PROFILE_HANDLER_H_ +#include <string> + #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h"
diff --git a/chrome/browser/ui/webui/options/language_options_handler_common.cc b/chrome/browser/ui/webui/options/language_options_handler_common.cc index dfd89a1..dc570ca 100644 --- a/chrome/browser/ui/webui/options/language_options_handler_common.cc +++ b/chrome/browser/ui/webui/options/language_options_handler_common.cc
@@ -80,8 +80,6 @@ IDS_OPTIONS_SETTINGS_USE_THIS_FOR_SPELL_CHECKING }, { "cannotBeUsedForSpellChecking", IDS_OPTIONS_SETTINGS_CANNOT_BE_USED_FOR_SPELL_CHECKING }, - { "isUsedForSpellChecking", - IDS_OPTIONS_SETTINGS_IS_USED_FOR_SPELL_CHECKING }, { "enableSpellCheck", IDS_OPTIONS_ENABLE_SPELLCHECK }, { "downloadingDictionary", IDS_OPTIONS_DICTIONARY_DOWNLOADING }, { "downloadFailed", IDS_OPTIONS_DICTIONARY_DOWNLOAD_FAILED },
diff --git a/chrome/browser/ui/webui/options/manage_profile_handler.cc b/chrome/browser/ui/webui/options/manage_profile_handler.cc index 74e04d65..b678f1c8 100644 --- a/chrome/browser/ui/webui/options/manage_profile_handler.cc +++ b/chrome/browser/ui/webui/options/manage_profile_handler.cc
@@ -6,6 +6,8 @@ #include <stddef.h> +#include <vector> + #include "base/bind.h" #include "base/bind_helpers.h" #include "base/macros.h" @@ -19,8 +21,8 @@ #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/profiles/gaia_info_update_service.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_metrics.h" #include "chrome/browser/profiles/profile_shortcut_manager.h" @@ -144,7 +146,8 @@ } void ManageProfileHandler::InitializeHandler() { - g_browser_process->profile_manager()->GetProfileInfoCache().AddObserver(this); + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().AddObserver(this); Profile* profile = Profile::FromWebUI(web_ui()); pref_change_registrar_.Init(profile->GetPrefs()); @@ -207,7 +210,7 @@ void ManageProfileHandler::Uninitialize() { g_browser_process->profile_manager()-> - GetProfileInfoCache().RemoveObserver(this); + GetProfileAttributesStorage().RemoveObserver(this); } void ManageProfileHandler::OnProfileAdded(const base::FilePath& profile_path) { @@ -285,12 +288,12 @@ void ManageProfileHandler::RequestNewProfileDefaults( const base::ListValue* args) { - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - const size_t icon_index = cache.ChooseAvatarIconIndexForNewProfile(); + const ProfileAttributesStorage& storage = + g_browser_process->profile_manager()->GetProfileAttributesStorage(); + const size_t icon_index = storage.ChooseAvatarIconIndexForNewProfile(); base::DictionaryValue profile_info; - profile_info.SetString("name", cache.ChooseNameForNewProfile(icon_index)); + profile_info.SetString("name", storage.ChooseNameForNewProfile(icon_index)); profile_info.SetString("iconURL", profiles::GetDefaultAvatarIconUrl(icon_index)); @@ -303,23 +306,23 @@ base::ListValue image_url_list; base::ListValue default_name_list; - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); + ProfileAttributesStorage& storage = + g_browser_process->profile_manager()->GetProfileAttributesStorage(); // In manage mode, first add the GAIA picture if it is available. No GAIA // picture in create mode. if (mode.GetString() == kManageProfileIdentifier) { Profile* profile = Profile::FromWebUI(web_ui()); - size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath()); - if (profile_index != std::string::npos) { - const gfx::Image* icon = - cache.GetGAIAPictureOfProfileAtIndex(profile_index); - if (icon) { - gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true); - gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap()); - image_url_list.AppendString(gaia_picture_url_); - default_name_list.AppendString(std::string()); - } + ProfileAttributesEntry* entry = nullptr; + bool success = storage.GetProfileAttributesWithPath(profile->GetPath(), + &entry); + DCHECK(success); + const gfx::Image* icon = entry->GetGAIAPicture(); + if (icon) { + gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true); + gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap()); + image_url_list.AppendString(gaia_picture_url_); + default_name_list.AppendString(std::string()); } } @@ -327,7 +330,7 @@ for (size_t i = 0; i < profiles::GetDefaultAvatarIconCount(); i++) { std::string url = profiles::GetDefaultAvatarIconUrl(i); image_url_list.AppendString(url); - default_name_list.AppendString(cache.ChooseNameForNewProfile(i)); + default_name_list.AppendString(storage.ChooseNameForNewProfile(i)); } web_ui()->CallJavascriptFunction( @@ -336,13 +339,12 @@ } void ManageProfileHandler::SendExistingProfileNames() { - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); + std::vector<ProfileAttributesEntry*> entries = + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().GetAllProfilesAttributes(); base::DictionaryValue profile_name_dict; - for (size_t i = 0, e = cache.GetNumberOfProfiles(); i < e; ++i) { - profile_name_dict.SetBoolean( - base::UTF16ToUTF8(cache.GetNameOfProfileAtIndex(i)), true); - } + for (const ProfileAttributesEntry* entry : entries) + profile_name_dict.SetBoolean(base::UTF16ToUTF8(entry->GetName()), true); web_ui()->CallJavascriptFunction( "ManageProfileOverlay.receiveExistingProfileNames", profile_name_dict); @@ -454,12 +456,11 @@ // If the selection is the GAIA picture then also show the profile name in the // text field. This will display either the GAIA given name, if available, // or the first name. - ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); - if (profile_index == std::string::npos) + ProfileAttributesEntry* entry = nullptr; + if (!g_browser_process->profile_manager()->GetProfileAttributesStorage(). + GetProfileAttributesWithPath(profile_file_path, &entry)) return; - base::string16 gaia_name = cache.GetNameOfProfileAtIndex(profile_index); + base::string16 gaia_name = entry->GetName(); if (gaia_name.empty()) return; @@ -478,23 +479,21 @@ if (!GetProfilePathFromArgs(args, &profile_file_path)) return; - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - size_t profile_index = cache.GetIndexOfProfileWithPath(profile_file_path); - if (profile_index == std::string::npos) + ProfileAttributesStorage& storage = + g_browser_process->profile_manager()->GetProfileAttributesStorage(); + ProfileAttributesEntry* entry; + if (!storage.GetProfileAttributesWithPath(profile_file_path, &entry)) return; // Don't show the add/remove desktop shortcut button in the single user case. - if (cache.GetNumberOfProfiles() <= 1) + if (storage.GetNumberOfProfiles() <= 1u) return; - const base::FilePath profile_path = - cache.GetPathOfProfileAtIndex(profile_index); ProfileShortcutManager* shortcut_manager = g_browser_process->profile_manager()->profile_shortcut_manager(); shortcut_manager->HasProfileShortcuts( - profile_path, base::Bind(&ManageProfileHandler::OnHasProfileShortcuts, - weak_factory_.GetWeakPtr())); + entry->GetPath(), base::Bind(&ManageProfileHandler::OnHasProfileShortcuts, + weak_factory_.GetWeakPtr())); } void ManageProfileHandler::RequestCreateProfileUpdate(
diff --git a/chrome/browser/ui/webui/options/manage_profile_handler.h b/chrome/browser/ui/webui/options/manage_profile_handler.h index 698368d..4407c92b 100644 --- a/chrome/browser/ui/webui/options/manage_profile_handler.h +++ b/chrome/browser/ui/webui/options/manage_profile_handler.h
@@ -9,7 +9,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/profiles/profile_info_cache_observer.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/ui/webui/options/options_ui.h" #include "components/prefs/pref_change_registrar.h" #include "components/sync_driver/sync_service_observer.h" @@ -22,7 +22,7 @@ // Chrome personal stuff profiles manage overlay UI handler. class ManageProfileHandler : public OptionsPageUIHandler, - public ProfileInfoCacheObserver, + public ProfileAttributesStorage::Observer, public sync_driver::SyncServiceObserver { public: ManageProfileHandler(); @@ -37,7 +37,7 @@ // WebUIMessageHandler: void RegisterMessages() override; - // ProfileInfoCacheObserver: + // ProfileAttributesStorage::Observer: void OnProfileAdded(const base::FilePath& profile_path) override; void OnProfileWasRemoved(const base::FilePath& profile_path, const base::string16& profile_name) override;
diff --git a/chrome/browser/ui/webui/options/options_ui_browsertest.cc b/chrome/browser/ui/webui/options/options_ui_browsertest.cc index e5d7a97..c03aaa8 100644 --- a/chrome/browser/ui/webui/options/options_ui_browsertest.cc +++ b/chrome/browser/ui/webui/options/options_ui_browsertest.cc
@@ -39,6 +39,7 @@ #include "base/run_loop.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser_commands.h" #include "content/public/test/test_navigation_observer.h" @@ -225,12 +226,12 @@ EXPECT_TRUE(result); base::FilePath profile_dir = browser()->profile()->GetPath(); - ProfileInfoCache& profile_info_cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); + ProfileAttributesStorage& storage = + g_browser_process->profile_manager()->GetProfileAttributesStorage(); + ProfileAttributesEntry* entry; EXPECT_TRUE(DirectoryExists(profile_dir)); - EXPECT_TRUE(profile_info_cache.GetIndexOfProfileWithPath(profile_dir) != - std::string::npos); + EXPECT_TRUE(storage.GetProfileAttributesWithPath(profile_dir, &entry)); // TODO(kaliamoorthi): Get the macos problem fixed and remove this code. // Deleting the Profile also destroys all browser windows of that Profile. @@ -244,8 +245,7 @@ browser()->tab_strip_model()->GetActiveWebContents(), "$('disconnect-managed-profile-ok').click();")); - EXPECT_TRUE(profile_info_cache.GetIndexOfProfileWithPath(profile_dir) == - std::string::npos); + EXPECT_TRUE(storage.GetProfileAttributesWithPath(profile_dir, &entry)); wait_for_browser_closed.Wait(); }
diff --git a/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.cc b/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.cc index 2b169e0..cbc7922 100644 --- a/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.cc +++ b/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.cc
@@ -6,6 +6,8 @@ #include <stddef.h> +#include <string> + #include "base/bind.h" #include "base/files/file_path.h" #include "base/macros.h" @@ -15,8 +17,8 @@ #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/profiles/profile_info_cache.h" -#include "chrome/browser/profiles/profile_info_cache_observer.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_window.h" #include "chrome/browser/signin/signin_manager_factory.h" @@ -35,20 +37,20 @@ // ProfileUpdateObserver------------------------------------------------------ class SupervisedUserCreateConfirmHandler::ProfileUpdateObserver - : public ProfileInfoCacheObserver { + : public ProfileAttributesStorage::Observer { public: - ProfileUpdateObserver(ProfileInfoCache* profile_info_cache, + ProfileUpdateObserver(ProfileAttributesStorage* profile_attributes_storage, SupervisedUserCreateConfirmHandler* handler) - : profile_info_cache_(profile_info_cache), + : profile_attributes_storage_(profile_attributes_storage), create_confirm_handler_(handler), scoped_observer_(this) { - DCHECK(profile_info_cache_); + DCHECK(profile_attributes_storage_); DCHECK(create_confirm_handler_); - scoped_observer_.Add(profile_info_cache_); + scoped_observer_.Add(profile_attributes_storage_); } private: - // ProfileInfoCacheObserver implementation: + // ProfileAttributesStorage::Observer implementation: // Forward possibly relevant changes to the dialog, which will check the // affected profile and update or close as needed. void OnProfileWasRemoved(const base::FilePath& profile_path, @@ -62,12 +64,11 @@ void OnProfileNameChanged(const base::FilePath& profile_path, const base::string16& old_profile_name) override { - size_t profile_index = - profile_info_cache_->GetIndexOfProfileWithPath(profile_path); - if (profile_index == std::string::npos) + ProfileAttributesEntry* entry; + if (!profile_attributes_storage_-> + GetProfileAttributesWithPath(profile_path, &entry)) return; - base::string16 new_profile_name = - profile_info_cache_->GetNameOfProfileAtIndex(profile_index); + base::string16 new_profile_name = entry->GetName(); scoped_ptr<base::StringValue> profile_path_value( base::CreateFilePathValue(profile_path)); create_confirm_handler_->web_ui()->CallJavascriptFunction( @@ -77,14 +78,15 @@ } // Weak. - ProfileInfoCache* profile_info_cache_; + ProfileAttributesStorage* profile_attributes_storage_; // Weak; owns us. SupervisedUserCreateConfirmHandler* create_confirm_handler_; // Manages any sources we're observing, ensuring that they're all removed // on destruction. - ScopedObserver<ProfileInfoCache, ProfileUpdateObserver> scoped_observer_; + ScopedObserver<ProfileAttributesStorage, ProfileUpdateObserver> + scoped_observer_; DISALLOW_COPY_AND_ASSIGN(ProfileUpdateObserver); }; @@ -93,9 +95,10 @@ // SupervisedUserCreateConfirmHandler----------------------------------------- SupervisedUserCreateConfirmHandler::SupervisedUserCreateConfirmHandler() { - profile_info_cache_observer_.reset( + profile_update_observer_.reset( new SupervisedUserCreateConfirmHandler::ProfileUpdateObserver( - &g_browser_process->profile_manager()->GetProfileInfoCache(), this)); + &g_browser_process->profile_manager()->GetProfileAttributesStorage(), + this)); } SupervisedUserCreateConfirmHandler::~SupervisedUserCreateConfirmHandler() {
diff --git a/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h b/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h index 980728c2..a2593fd 100644 --- a/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h +++ b/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h
@@ -28,8 +28,8 @@ void RegisterMessages() override; private: - // An observer for any changes to Profiles in the ProfileInfoCache so that - // this dialog can be updated or closed. + // An observer for any changes to Profiles in the ProfileAttributesStorage so + // that this dialog can be updated or closed. class ProfileUpdateObserver; // Callback for the "switchToProfile" message. @@ -37,9 +37,9 @@ // |args| is of the form [ {string} profileFilePath ] void SwitchToProfile(const base::ListValue* args); - // Observes the ProfileInfoCache and gets notified when a profile has been - // modified, so that the dialog can be updated or closed. - scoped_ptr<ProfileUpdateObserver> profile_info_cache_observer_; + // Observes the ProfileAttributesStorage and gets notified when a profile has + // been modified, so that the dialog can be updated or closed. + scoped_ptr<ProfileUpdateObserver> profile_update_observer_; DISALLOW_COPY_AND_ASSIGN(SupervisedUserCreateConfirmHandler); };
diff --git a/chrome/browser/ui/webui/options/supervised_user_import_handler.cc b/chrome/browser/ui/webui/options/supervised_user_import_handler.cc index 89a3360e..4c81acc 100644 --- a/chrome/browser/ui/webui/options/supervised_user_import_handler.cc +++ b/chrome/browser/ui/webui/options/supervised_user_import_handler.cc
@@ -7,14 +7,15 @@ #include <stddef.h> #include <set> +#include <vector> #include "base/bind.h" #include "base/macros.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_attributes_entry.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" -#include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/signin/signin_error_controller_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" @@ -44,12 +45,11 @@ } bool ProfileIsLegacySupervised(const base::FilePath& profile_path) { - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); - size_t index = cache.GetIndexOfProfileWithPath(profile_path); - if (index == std::string::npos) - return false; - return cache.ProfileIsLegacySupervisedAtIndex(index); + ProfileAttributesEntry* entry; + + return g_browser_process->profile_manager()->GetProfileAttributesStorage(). + GetProfileAttributesWithPath(profile_path, &entry) && + entry->IsLegacySupervised(); } } // namespace @@ -101,7 +101,7 @@ Profile* profile = Profile::FromWebUI(web_ui()); if (!profile->IsSupervised()) { profile_observer_.Add( - &g_browser_process->profile_manager()->GetProfileInfoCache()); + &g_browser_process->profile_manager()->GetProfileAttributesStorage()); SupervisedUserSyncService* sync_service = SupervisedUserSyncServiceFactory::GetForProfile(profile); if (sync_service) { @@ -194,17 +194,17 @@ void SupervisedUserImportHandler::SendExistingSupervisedUsers( const base::DictionaryValue* dict) { DCHECK(dict); - const ProfileInfoCache& cache = - g_browser_process->profile_manager()->GetProfileInfoCache(); + std::vector<ProfileAttributesEntry*> entries = + g_browser_process->profile_manager()-> + GetProfileAttributesStorage().GetAllProfilesAttributes(); // Collect the ids of local supervised user profiles. std::set<std::string> supervised_user_ids; - for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) { + for (const ProfileAttributesEntry* entry : entries) { // Filter out omitted profiles. These are currently being imported, and // shouldn't show up as "already on this device" just yet. - if (cache.ProfileIsLegacySupervisedAtIndex(i) && - !cache.IsOmittedProfileAtIndex(i)) { - supervised_user_ids.insert(cache.GetSupervisedUserIdOfProfileAtIndex(i)); + if (entry->IsLegacySupervised() && !entry->IsOmitted()) { + supervised_user_ids.insert(entry->GetSupervisedUserId()); } }
diff --git a/chrome/browser/ui/webui/options/supervised_user_import_handler.h b/chrome/browser/ui/webui/options/supervised_user_import_handler.h index 0a5ff83..ee576bd 100644 --- a/chrome/browser/ui/webui/options/supervised_user_import_handler.h +++ b/chrome/browser/ui/webui/options/supervised_user_import_handler.h
@@ -12,7 +12,7 @@ #include "base/memory/weak_ptr.h" #include "base/scoped_observer.h" #include "base/strings/string16.h" -#include "chrome/browser/profiles/profile_info_cache_observer.h" +#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_observer.h" #include "chrome/browser/ui/webui/options/options_ui.h" #include "components/signin/core/browser/signin_error_controller.h" @@ -22,14 +22,14 @@ class ListValue; } -class ProfileInfoCache; +class ProfileAttributesStorage; class SupervisedUserSyncService; namespace options { // Handler for the 'import existing supervised user' dialog. class SupervisedUserImportHandler : public OptionsPageUIHandler, - public ProfileInfoCacheObserver, + public ProfileAttributesStorage::Observer, public SupervisedUserSyncServiceObserver, public SigninErrorController::Observer { public: @@ -47,7 +47,7 @@ // WebUIMessageHandler implementation. void RegisterMessages() override; - // ProfileInfoCacheObserver implementation. + // ProfileAttributesStorage::Observer implementation. void OnProfileAdded(const base::FilePath& profile_path) override; void OnProfileWillBeRemoved(const base::FilePath& profile_path) override; void OnProfileWasRemoved(const base::FilePath& profile_path, @@ -99,7 +99,7 @@ scoped_ptr<CallbackList::Subscription> subscription_; - ScopedObserver<ProfileInfoCache, SupervisedUserImportHandler> + ScopedObserver<ProfileAttributesStorage, SupervisedUserImportHandler> profile_observer_; ScopedObserver<SigninErrorController, SupervisedUserImportHandler> signin_error_observer_;
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc index 852cad3..e05e9f7 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -39,12 +39,14 @@ const char kLocalizedStringsFile[] = "strings.js"; void AddCommonStrings(content::WebUIDataSource* html_source) { - html_source->AddLocalizedString("basicPageTitle", IDS_SETTINGS_BASIC); - html_source->AddLocalizedString("advancedPageTitle", IDS_SETTINGS_ADVANCED); - html_source->AddLocalizedString("addLabel", IDS_ADD); - html_source->AddLocalizedString("learnMore", IDS_LEARN_MORE); + html_source->AddLocalizedString("add", IDS_ADD); html_source->AddLocalizedString("cancel", IDS_CANCEL); + html_source->AddLocalizedString("learnMore", IDS_LEARN_MORE); html_source->AddLocalizedString("ok", IDS_OK); + html_source->AddLocalizedString("save", IDS_SAVE); + + html_source->AddLocalizedString("advancedPageTitle", IDS_SETTINGS_ADVANCED); + html_source->AddLocalizedString("basicPageTitle", IDS_SETTINGS_BASIC); html_source->AddLocalizedString("settings", IDS_SETTINGS_SETTINGS); } @@ -283,6 +285,29 @@ } #endif +#if defined(OS_CHROMEOS) +void AddDeviceStrings(content::WebUIDataSource* html_source) { + html_source->AddLocalizedString( + "devicePageTitle", IDS_SETTINGS_DEVICE_TITLE); + html_source->AddLocalizedString( + "touchpadTitle", IDS_SETTINGS_TOUCHPAD_TITLE); + html_source->AddLocalizedString( + "touchpadTapToClickEnabledLabel", + IDS_SETTINGS_TOUCHPAD_TAP_TO_CLICK_ENABLED_LABEL); + html_source->AddLocalizedString( + "scrollLabel", IDS_SETTINGS_SCROLL_LABEL); + html_source->AddString( + "naturalScrollLabel", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_NATURAL_SCROLL_LABEL, + base::ASCIIToUTF16(chrome::kNaturalScrollHelpURL))); + html_source->AddLocalizedString( + "traditionalScrollLabel", IDS_SETTINGS_TRADITIONAL_SCROLL_LABEL); + html_source->AddLocalizedString( + "keyboardTitle", IDS_SETTINGS_KEYBOARD_TITLE); +} +#endif + void AddDownloadsStrings(content::WebUIDataSource* html_source) { html_source->AddLocalizedString( "downloadsPageTitle", IDS_SETTINGS_DOWNLOADS); @@ -487,6 +512,10 @@ IDS_SETTINGS_PASSWORDS_DETAIL); html_source->AddLocalizedString("savedPasswordsHeading", IDS_SETTINGS_PASSWORDS_SAVED_HEADING); + html_source->AddLocalizedString("passwordExceptionsHeading", + IDS_SETTINGS_PASSWORDS_EXCEPTIONS_HEADING); + html_source->AddLocalizedString("deletePasswordException", + IDS_SETTINGS_PASSWORDS_DELETE_EXCEPTION); } void AddPeopleStrings(content::WebUIDataSource* html_source) { @@ -494,6 +523,8 @@ html_source->AddLocalizedString("manageOtherPeople", IDS_SETTINGS_PEOPLE_MANAGE_OTHER_PEOPLE); #if defined(OS_CHROMEOS) + html_source->AddLocalizedString("enableScreenlock", + IDS_SETTINGS_PEOPLE_ENABLE_SCREENLOCK); html_source->AddLocalizedString("changePictureTitle", IDS_SETTINGS_CHANGE_PICTURE_DIALOG_TITLE); html_source->AddLocalizedString("changePicturePageDescription", @@ -508,6 +539,8 @@ IDS_SETTINGS_CHANGE_PICTURE_CHOOSE_FILE); html_source->AddLocalizedString("profilePhoto", IDS_SETTINGS_CHANGE_PICTURE_PROFILE_PHOTO); + html_source->AddLocalizedString("oldPhoto", + IDS_SETTINGS_CHANGE_PICTURE_OLD_PHOTO); html_source->AddLocalizedString( "profilePhotoLoading", IDS_SETTINGS_CHANGE_PICTURE_PROFILE_LOADING_PHOTO); html_source->AddLocalizedString("previewAltText", @@ -647,8 +680,9 @@ IDS_SETTINGS_SEARCH); html_source->AddLocalizedString("searchExplanation", IDS_SETTINGS_SEARCH_EXPLANATION); - html_source->AddLocalizedString("searchManageButtonLabel", - IDS_SETTINGS_SEARCH_MANAGE_BUTTON_LABEL); + html_source->AddLocalizedString( + "searchEnginesManage", + IDS_SETTINGS_SEARCH_MANAGE_SEARCH_ENGINES); html_source->AddLocalizedString("searchOkGoogleLabel", IDS_SETTINGS_SEARCH_OK_GOOGLE_LABEL); html_source->AddLocalizedString( @@ -663,22 +697,29 @@ html_source->AddLocalizedString("searchEnginesPageTitle", IDS_SETTINGS_SEARCH_ENGINES); html_source->AddLocalizedString( - "searchEnginesAddSearchEngineLabel", - IDS_SETTINGS_SEARCH_ENGINES_ADD_SEARCH_ENGINE_LABEL); - html_source->AddLocalizedString("searchEnginesLabel", - IDS_SETTINGS_SEARCH_ENGINES_LABEL); + "searchEnginesAddSearchEngine", + IDS_SETTINGS_SEARCH_ENGINES_ADD_SEARCH_ENGINE); html_source->AddLocalizedString( - "searchEnginesOtherLabel", - IDS_SETTINGS_SEARCH_ENGINES_OTHER_ENGINES_LABEL); + "searchEnginesEditSearchEngine", + IDS_SETTINGS_SEARCH_ENGINES_EDIT_SEARCH_ENGINE); html_source->AddLocalizedString( - "searchEnginesSearchEngineLabel", - IDS_SETTINGS_SEARCH_ENGINES_SEARCH_ENGINE_LABEL); - html_source->AddLocalizedString("searchEnginesKeywordLabel", - IDS_SETTINGS_SEARCH_ENGINES_KEYWORD_LABEL); - html_source->AddLocalizedString("searchEnginesQueryURLLabel", - IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL_LABEL); + "searchEnginesNotValid", + IDS_SETTINGS_SEARCH_ENGINES_NOT_VALID); + html_source->AddLocalizedString("searchEngines", + IDS_SETTINGS_SEARCH_ENGINES); html_source->AddLocalizedString( - "searchEnginesAdd", IDS_SETTINGS_SEARCH_ENGINES_ADD); + "searchEnginesOther", + IDS_SETTINGS_SEARCH_ENGINES_OTHER_ENGINES); + html_source->AddLocalizedString( + "searchEnginesSearchEngine", + IDS_SETTINGS_SEARCH_ENGINES_SEARCH_ENGINE); + html_source->AddLocalizedString("searchEnginesKeyword", + IDS_SETTINGS_SEARCH_ENGINES_KEYWORD); + html_source->AddLocalizedString("searchEnginesQueryURL", + IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL); + html_source->AddLocalizedString( + "searchEnginesQueryURLExplanation", + IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL_EXPLANATION); html_source->AddLocalizedString( "searchEnginesMakeDefault", IDS_SETTINGS_SEARCH_ENGINES_MAKE_DEFAULT); html_source->AddLocalizedString( @@ -885,6 +926,9 @@ AddDefaultBrowserStrings(html_source); #endif AddDateTimeStrings(html_source); +#if defined(OS_CHROMEOS) + AddDeviceStrings(html_source); +#endif AddDownloadsStrings(html_source); #if defined(OS_CHROMEOS) AddInternetStrings(html_source);
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc index 4144fba..1d03e27e 100644 --- a/chrome/browser/ui/webui/settings/site_settings_handler.cc +++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -7,7 +7,9 @@ #include "base/bind.h" #include "base/values.h" #include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/content_settings_types.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_ui.h" @@ -33,6 +35,14 @@ "clearUsage", base::Bind(&SiteSettingsHandler::HandleClearUsage, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "setDefaultValueForContentType", + base::Bind(&SiteSettingsHandler::HandleSetDefaultValueForContentType, + base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "getDefaultValueForContentType", + base::Bind(&SiteSettingsHandler::HandleGetDefaultValueForContentType, + base::Unretained(this))); } void SiteSettingsHandler::OnGetUsageInfo( @@ -101,4 +111,41 @@ } } +void SiteSettingsHandler::HandleSetDefaultValueForContentType( + const base::ListValue* args) { + CHECK_EQ(2U, args->GetSize()); + int content_type; + CHECK(args->GetInteger(0, &content_type)); + int default_setting; + CHECK(args->GetInteger(1, &default_setting)); + + HostContentSettingsMap* map = + HostContentSettingsMapFactory::GetForProfile(profile_); + map->SetDefaultContentSetting(static_cast<ContentSettingsType>(content_type), + static_cast<ContentSetting>(default_setting)); +} + +void SiteSettingsHandler::HandleGetDefaultValueForContentType( + const base::ListValue* args) { + CHECK_EQ(2U, args->GetSize()); + const base::Value* callback_id; + CHECK(args->Get(0, &callback_id)); + int type; + CHECK(args->GetInteger(1, &type)); + + ContentSettingsType content_type = static_cast<ContentSettingsType>(type); + HostContentSettingsMap* map = + HostContentSettingsMapFactory::GetForProfile(profile_); + ContentSetting setting = map->GetDefaultContentSetting(content_type, nullptr); + + // FullScreen is Allow vs. Ask. + bool enabled; + if (content_type == CONTENT_SETTINGS_TYPE_FULLSCREEN) + enabled = setting != CONTENT_SETTING_ASK; + else + enabled = setting != CONTENT_SETTING_BLOCK; + + CallJavascriptCallback(*callback_id, base::FundamentalValue(enabled)); +} + } // namespace settings
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h index eeedc3e..92f3fd2 100644 --- a/chrome/browser/ui/webui/settings/site_settings_handler.h +++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -38,6 +38,10 @@ // Deletes the storage being used for a given host. void HandleClearUsage(const base::ListValue* args); + // Gets and sets the default value for a particular content settings type. + void HandleSetDefaultValueForContentType(const base::ListValue* args); + void HandleGetDefaultValueForContentType(const base::ListValue* args); + Profile* profile_; // The host for which to fetch usage.
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 720fba11..28d62a90 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp
@@ -532,6 +532,7 @@ 'type': 'none', 'dependencies': [ 'activity_type_ids_java', + 'browsing_data_time_period_java', 'browsing_data_type_java', 'chrome_locale_paks', 'chrome_resources.gyp:chrome_strings',
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 1077526..68c7de9 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi
@@ -1824,7 +1824,6 @@ 'android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java', 'android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java', 'android/java/src/org/chromium/chrome/browser/ChromeApplication.java', - 'android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java', 'android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java', 'android/java/src/org/chromium/chrome/browser/ChromeHttpAuthHandler.java', 'android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java', @@ -1904,6 +1903,7 @@ 'android/java/src/org/chromium/chrome/browser/profiles/ProfileDownloader.java', 'android/java/src/org/chromium/chrome/browser/preferences/privacy/BrowsingDataCounterBridge.java', 'android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java', + 'android/java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java', 'android/java/src/org/chromium/chrome/browser/push_messaging/PushMessagingServiceObserver.java', 'android/java/src/org/chromium/chrome/browser/rappor/RapporServiceBridge.java', 'android/java/src/org/chromium/chrome/browser/rlz/RevenueStats.java', @@ -4063,6 +4063,15 @@ }, { # GN: //chrome/android:chrome_android_java_enums_srcjar + 'target_name': 'browsing_data_time_period_java', + 'type': 'none', + 'variables': { + 'source_file': 'browser/browsing_data/browsing_data_remover.h', + }, + 'includes': [ '../build/android/java_cpp_enum.gypi' ], + }, + { + # GN: //chrome/android:chrome_android_java_enums_srcjar 'target_name': 'connectivity_check_result_java', 'type': 'none', 'variables': {
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index cf48392..2b9e273 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi
@@ -188,6 +188,8 @@ 'browser/chromeos/external_metrics.h', 'browser/chromeos/external_protocol_dialog.cc', 'browser/chromeos/external_protocol_dialog.h', + 'browser/chromeos/feedback_util.cc', + 'browser/chromeos/feedback_util.h', 'browser/chromeos/file_manager/app_id.h', 'browser/chromeos/file_manager/file_browser_handlers.cc', 'browser/chromeos/file_manager/file_browser_handlers.h',
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 8d608f0a..f5924411 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi
@@ -561,9 +561,6 @@ 'browser/extensions/api/webstore_private/webstore_private_api.h', 'browser/extensions/app_data_migrator.cc', 'browser/extensions/app_data_migrator.h', - 'browser/extensions/app_icon_loader.h', - 'browser/extensions/app_icon_loader_impl.cc', - 'browser/extensions/app_icon_loader_impl.h', 'browser/extensions/blacklist.cc', 'browser/extensions/blacklist.h', 'browser/extensions/blacklist_factory.cc', @@ -649,6 +646,8 @@ 'browser/extensions/extension_action_manager.h', 'browser/extensions/extension_action_storage_manager.cc', 'browser/extensions/extension_action_storage_manager.h', + 'browser/extensions/extension_app_icon_loader.cc', + 'browser/extensions/extension_app_icon_loader.h', 'browser/extensions/extension_assets_manager.cc', 'browser/extensions/extension_assets_manager.h', 'browser/extensions/extension_assets_manager_chromeos.cc', @@ -892,6 +891,8 @@ 'browser/extensions/window_controller_list_observer.h', 'browser/extensions/zipfile_installer.cc', 'browser/extensions/zipfile_installer.h', + 'browser/ui/app_icon_loader.cc', + 'browser/ui/app_icon_loader.h', 'browser/ui/toolbar/toolbar_actions_model.cc', 'browser/ui/toolbar/toolbar_actions_model.h', 'browser/ui/toolbar/toolbar_actions_model_factory.cc',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi index 94d354a..17f2785 100644 --- a/chrome/chrome_browser_ui.gypi +++ b/chrome/chrome_browser_ui.gypi
@@ -704,6 +704,8 @@ 'chrome_browser_ui_chromeos_arc_sources': [ 'browser/ui/app_list/arc/arc_app_icon.cc', 'browser/ui/app_list/arc/arc_app_icon.h', + 'browser/ui/app_list/arc/arc_app_icon_loader.cc', + 'browser/ui/app_list/arc/arc_app_icon_loader.h', 'browser/ui/app_list/arc/arc_app_item.cc', 'browser/ui/app_list/arc/arc_app_item.h', 'browser/ui/app_list/arc/arc_app_list_prefs.cc', @@ -1312,6 +1314,7 @@ 'browser/ui/sync/one_click_signin_sync_starter.h', 'browser/ui/views/frame/avatar_button_manager.cc', 'browser/ui/views/frame/avatar_button_manager.h', + 'browser/ui/views/profiles/avatar_button_delegate.h', 'browser/ui/views/profiles/avatar_button_style.h', 'browser/ui/views/profiles/new_avatar_button.cc', 'browser/ui/views/profiles/new_avatar_button.h', @@ -3073,9 +3076,6 @@ ], }], ['OS=="win" or OS=="mac" or desktop_linux==1', { - # A temporary define to make it easier to remove CrOS dependencies on - # avatar button code. TODO(estade): remove. - 'defines': [ 'FRAME_AVATAR_BUTTON=1', ], 'sources': [ '<@(chrome_browser_ui_desktop_sources)' ], }], ['use_aura==1', {
diff --git a/chrome/chrome_installer.gypi b/chrome/chrome_installer.gypi index a16521f..dc4cb53 100644 --- a/chrome/chrome_installer.gypi +++ b/chrome/chrome_installer.gypi
@@ -239,6 +239,8 @@ 'installer/setup/installer_crash_reporter_client.h', 'installer/setup/installer_crash_reporting.cc', 'installer/setup/installer_crash_reporting.h', + 'installer/setup/installer_metrics.cc', + 'installer/setup/installer_metrics.h', 'installer/setup/setup_constants.cc', 'installer/setup/setup_constants.h', 'installer/setup/setup_util.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index d001d9a..d09e44b4b 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi
@@ -257,6 +257,7 @@ 'browser/extensions/extension_storage_apitest.cc', 'browser/extensions/extension_storage_monitor_browsertest.cc', 'browser/extensions/extension_tabs_apitest.cc', + 'browser/extensions/extension_tab_util_browsertest.cc', 'browser/extensions/extension_url_rewrite_browsertest.cc', 'browser/extensions/extension_view_host_factory_browsertest.cc', 'browser/extensions/extension_websocket_apitest.cc', @@ -2568,9 +2569,6 @@ 'sources': [ '<@(chrome_browser_tests_views_non_mac_sources)' ], }], ['toolkit_views==1 and OS!="mac" and chromeos == 0', { - # A temporary define to make it easier to remove CrOS dependencies on - # avatar button code. TODO(estade): remove. - 'defines': [ 'FRAME_AVATAR_BUTTON=1', ], 'sources': [ '<@(chrome_browser_tests_views_non_cros_or_mac_sources)' ], }], ['OS=="ios"', { @@ -3137,6 +3135,7 @@ 'android/junit/', ], 'test_type': 'junit', + 'wrapper_script_name': 'helper/<(_target_name)', }, 'includes': [ '../build/android/test_runner.gypi',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index e71db96..581e5f1 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi
@@ -230,17 +230,9 @@ 'browser/status_icons/status_icon_unittest.cc', 'browser/status_icons/status_tray_unittest.cc', 'browser/storage/durable_storage_permission_context_unittest.cc', - 'browser/sync/abstract_profile_sync_service_test.cc', - 'browser/sync/abstract_profile_sync_service_test.h', - 'browser/sync/profile_sync_service_autofill_unittest.cc', 'browser/sync/profile_sync_service_bookmark_unittest.cc', 'browser/sync/profile_sync_service_factory_unittest.cc', - 'browser/sync/profile_sync_service_typed_url_unittest.cc', 'browser/sync/sync_startup_tracker_unittest.cc', - 'browser/sync/test/test_http_bridge_factory.cc', - 'browser/sync/test/test_http_bridge_factory.h', - 'browser/sync/test_profile_sync_service.cc', - 'browser/sync/test_profile_sync_service.h', 'browser/task_profiler/task_profiler_data_serializer_unittest.cc', 'browser/thumbnails/content_analysis_unittest.cc', 'browser/thumbnails/content_based_thumbnailing_algorithm_unittest.cc',
diff --git a/chrome/chrome_watcher/OWNERS b/chrome/chrome_watcher/OWNERS index 147afd9..3224b9e 100644 --- a/chrome/chrome_watcher/OWNERS +++ b/chrome/chrome_watcher/OWNERS
@@ -1 +1,2 @@ siggi@chromium.org +pmonette@chromium.org
diff --git a/chrome/chrome_watcher/chrome_watcher_main.cc b/chrome/chrome_watcher/chrome_watcher_main.cc index 14765fb..2bf2151b 100644 --- a/chrome/chrome_watcher/chrome_watcher_main.cc +++ b/chrome/chrome_watcher/chrome_watcher_main.cc
@@ -251,16 +251,43 @@ key_buffers.push_back(nullptr); value_buffers.push_back(nullptr); - // Synthesize an exception for the main thread. + // Synthesize an exception for the main thread. Populate the record with the + // current context of the thread to get the stack trace bucketed on the crash + // backend. CONTEXT thread_context = {}; EXCEPTION_RECORD exception_record = {}; exception_record.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED; EXCEPTION_POINTERS exception_pointers = {&exception_record, &thread_context}; + base::win::ScopedHandle main_thread(::OpenThread( + THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, + FALSE, main_thread_id)); + + bool have_context = false; + if (main_thread.IsValid()) { + DWORD suspend_count = ::SuspendThread(main_thread.Get()); + const DWORD kSuspendFailed = static_cast<DWORD>(-1); + if (suspend_count != kSuspendFailed) { + // Best effort capture of the context. + thread_context.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_SEGMENTS | + CONTEXT_INTEGER | CONTEXT_CONTROL; + if (::GetThreadContext(main_thread.Get(), &thread_context) == TRUE) + have_context = true; + + ::ResumeThread(main_thread.Get()); + } + } + // TODO(erikwright): Make the dump-type channel-dependent. - kasko::api::SendReportForProcess( - process.Handle(), main_thread_id, &exception_pointers, - kasko::api::LARGER_DUMP_TYPE, key_buffers.data(), value_buffers.data()); + if (have_context) { + kasko::api::SendReportForProcess( + process.Handle(), main_thread_id, &exception_pointers, + kasko::api::LARGER_DUMP_TYPE, key_buffers.data(), value_buffers.data()); + } else { + kasko::api::SendReportForProcess(process.Handle(), 0, nullptr, + kasko::api::LARGER_DUMP_TYPE, + key_buffers.data(), value_buffers.data()); + } } void LoggedDeregisterEventSource(HANDLE event_source_handle) {
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc index 58564f2..779c8a5 100644 --- a/chrome/common/chrome_content_client.cc +++ b/chrome/common/chrome_content_client.cc
@@ -539,10 +539,16 @@ void ChromeContentClient::AddAdditionalSchemes( std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* savable_schemes) { for (int i = 0; i < kNumChromeStandardURLSchemes; i++) standard_schemes->push_back(kChromeStandardURLSchemes[i]); +#if defined(OS_ANDROID) + referrer_schemes->push_back( + {chrome::kAndroidAppScheme, url::SCHEME_WITHOUT_PORT}); +#endif + savable_schemes->push_back(extensions::kExtensionScheme); savable_schemes->push_back(extensions::kExtensionResourceScheme); savable_schemes->push_back(chrome::kChromeSearchScheme);
diff --git a/chrome/common/chrome_content_client.h b/chrome/common/chrome_content_client.h index 33515eab..21a8bbda 100644 --- a/chrome/common/chrome_content_client.h +++ b/chrome/common/chrome_content_client.h
@@ -60,6 +60,7 @@ void AddPepperPlugins( std::vector<content::PepperPluginInfo>* plugins) override; void AddAdditionalSchemes(std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* saveable_shemes) override; bool CanSendWhileSwappedOut(const IPC::Message* message) override; std::string GetProduct() const override;
diff --git a/chrome/common/chrome_content_client_ios.mm b/chrome/common/chrome_content_client_ios.mm index bfca613..fb603f7e 100644 --- a/chrome/common/chrome_content_client_ios.mm +++ b/chrome/common/chrome_content_client_ios.mm
@@ -32,6 +32,7 @@ void ChromeContentClient::AddAdditionalSchemes( std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* saveable_shemes) { // No additional schemes for iOS. }
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 9fe485e..259b0a9 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc
@@ -277,11 +277,6 @@ // Disables the Material Design version of chrome://downloads. const char kDisableMaterialDesignDownloads[] = "disable-md-downloads"; -// Disable the behavior that the second click on a launcher item (the click when -// the item is already active) minimizes the item. -const char kDisableMinimizeOnSecondLauncherItemClick[] = - "disable-minimize-on-second-launcher-item-click"; - // Disables the new bookmark app system. const char kDisableNewBookmarkApps[] = "disable-new-bookmark-apps";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index a0cef19..9aa3b6d48 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h
@@ -81,7 +81,6 @@ extern const char kDisableHttp2[]; extern const char kDisableJavaScriptHarmonyShipping[]; extern const char kDisableMaterialDesignDownloads[]; -extern const char kDisableMinimizeOnSecondLauncherItemClick[]; extern const char kDisableNewBookmarkApps[]; extern const char kDisableNTPPopularSites[]; extern const char kDisableOfflineAutoReload[];
diff --git a/chrome/common/extensions/api/automation.idl b/chrome/common/extensions/api/automation.idl index 96b1d37..48102a4 100644 --- a/chrome/common/extensions/api/automation.idl +++ b/chrome/common/extensions/api/automation.idl
@@ -605,6 +605,9 @@ // Called when the <code>AutomationNode</code> for the page is available. callback RootCallback = void(AutomationNode rootNode); + // Called with the <code>AutomationNode</code> that currently has focus. + callback FocusCallback = void(AutomationNode focusedNode); + interface Functions { // Get the automation tree for the tab with the given tabId, or the current // tab if no tabID is given, enabling automation if necessary. Returns a @@ -617,6 +620,10 @@ // screen views. Note this API is currently only supported on Chrome OS. [nocompile] static void getDesktop(RootCallback callback); + // Get the automation node that currently has focus, globally. Will return + // null if none of the nodes in any loaded trees have focus. + [nocompile] static void getFocus(FocusCallback callback); + // Add a tree change observer. Tree change observers are static/global, they // listen to changes across all trees. Pass a filter to determine what // specific tree changes to listen to, and note that listnening to all
diff --git a/chrome/common/extensions/api/downloads.idl b/chrome/common/extensions/api/downloads.idl index 95802fc5..ab03d3b 100644 --- a/chrome/common/extensions/api/downloads.idl +++ b/chrome/common/extensions/api/downloads.idl
@@ -59,6 +59,7 @@ SERVER_UNAUTHORIZED, SERVER_CERT_PROBLEM, SERVER_FORBIDDEN, + SERVER_UNREACHABLE, USER_CANCELED, USER_SHUTDOWN, CRASH};
diff --git a/chrome/common/extensions/chrome_extension_messages.h b/chrome/common/extensions/chrome_extension_messages.h index af0ed70..49357df 100644 --- a/chrome/common/extensions/chrome_extension_messages.h +++ b/chrome/common/extensions/chrome_extension_messages.h
@@ -80,6 +80,7 @@ IPC_STRUCT_TRAITS_MEMBER(doctype) IPC_STRUCT_TRAITS_MEMBER(loaded) IPC_STRUCT_TRAITS_MEMBER(loading_progress) + IPC_STRUCT_TRAITS_MEMBER(focus_id) IPC_STRUCT_TRAITS_MEMBER(sel_anchor_object_id) IPC_STRUCT_TRAITS_MEMBER(sel_anchor_offset) IPC_STRUCT_TRAITS_MEMBER(sel_focus_object_id)
diff --git a/chrome/common/extensions/docs/examples/api/desktopCapture/app.js b/chrome/common/extensions/docs/examples/api/desktopCapture/app.js index 7d5ba99..a6d8e47 100644 --- a/chrome/common/extensions/docs/examples/api/desktopCapture/app.js +++ b/chrome/common/extensions/docs/examples/api/desktopCapture/app.js
@@ -4,7 +4,7 @@ 'use strict'; -const DESKTOP_MEDIA = ['screen', 'window', 'audio']; +const DESKTOP_MEDIA = ['screen', 'window', 'tab', 'audio']; var pending_request_id = null; var pc1 = null; @@ -79,7 +79,7 @@ pc1.addStream(stream); - pc1.createOffer(onCreateOfferSuccess); + pc1.createOffer(onCreateOfferSuccess, function() {}); } function onCreateOfferSuccess(desc) {
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc index ed60a71d..3f9676b 100644 --- a/chrome/common/url_constants.cc +++ b/chrome/common/url_constants.cc
@@ -18,6 +18,10 @@ const char kCrosScheme[] = "cros"; #endif +#if defined(OS_ANDROID) +const char kAndroidAppScheme[] = "android-app"; +#endif + // Add Chrome UI URLs as necessary, in alphabetical order. // Be sure to add the corresponding kChromeUI*Host constant below. // This is a WebUI page that lists other WebUI pages. @@ -762,4 +766,7 @@ "https://support.google.com/chrome/answer/95346"; #endif +const char kChooserBluetoothOverviewURL[] = + "https://support.google.com/chrome?p=bluetooth"; + } // namespace chrome
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h index 2e25824a..d57d6eaf 100644 --- a/chrome/common/url_constants.h +++ b/chrome/common/url_constants.h
@@ -537,6 +537,10 @@ extern const char kCrosScheme[]; #endif +#if defined(OS_ANDROID) +extern const char kAndroidAppScheme[]; +#endif + // "Learn more" URL for the Cloud Print section under Options. extern const char kCloudPrintLearnMoreURL[]; @@ -570,6 +574,10 @@ extern const char kWindowsXPVistaDeprecationURL[]; #endif +// The URL for the Bluetooth Overview help center article in the Web Bluetooth +// Chooser. +extern const char kChooserBluetoothOverviewURL[]; + } // namespace chrome #endif // CHROME_COMMON_URL_CONSTANTS_H_
diff --git a/chrome/installer/setup/BUILD.gn b/chrome/installer/setup/BUILD.gn index c54637b..48c103d3 100644 --- a/chrome/installer/setup/BUILD.gn +++ b/chrome/installer/setup/BUILD.gn
@@ -41,6 +41,8 @@ "installer_crash_reporter_client.h", "installer_crash_reporting.cc", "installer_crash_reporting.h", + "installer_metrics.cc", + "installer_metrics.h", "setup_constants.cc", "setup_constants.h", "setup_util.cc",
diff --git a/chrome/installer/setup/installer_metrics.cc b/chrome/installer/setup/installer_metrics.cc new file mode 100644 index 0000000..8e5f0443 --- /dev/null +++ b/chrome/installer/setup/installer_metrics.cc
@@ -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. + +#include "chrome/installer/setup/installer_metrics.h" + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/histogram_persistence.h" +#include "base/metrics/persistent_memory_allocator.h" +#include "chrome/installer/util/util_constants.h" + +namespace installer { + +void BeginPersistentHistogramStorage() { + base::SetPersistentHistogramMemoryAllocator( + new base::LocalPersistentMemoryAllocator( + 1 << 20, 0, // 1 MiB + installer::kSetupHistogramAllocatorName)); + base::GetPersistentHistogramMemoryAllocator()->CreateTrackingHistograms( + installer::kSetupHistogramAllocatorName); +} + +void EndPersistentHistogramStorage(const base::FilePath& target_path) { + // For atomicity, first write to a temporary file and then rename it. + // The ImportantFileWriter would be good for this except it supports only + // std::string for its data. + base::PersistentMemoryAllocator* allocator = + base::GetPersistentHistogramMemoryAllocator(); + allocator->UpdateTrackingHistograms(); + + base::FilePath file_path = target_path + .AppendASCII(allocator->Name()) + .AddExtension(L".pma"); + base::FilePath tmp_file_path; + base::DeleteFile(file_path, false); + + if (base::CreateTemporaryFileInDir(file_path.DirName(), &tmp_file_path)) { + // Allocator doesn't support more than 1GB so can never overflow. + int used = static_cast<int>(allocator->used()); + if (base::WriteFile(tmp_file_path, + static_cast<const char*>(allocator->data()), + used) == used) { + if (base::ReplaceFile(tmp_file_path, file_path, nullptr)) { + VLOG(1) << "Persistent histograms saved in file: " + << file_path.value(); + } + } + base::DeleteFile(tmp_file_path, false); + } +} + +} // namespace installer
diff --git a/chrome/installer/setup/installer_metrics.h b/chrome/installer/setup/installer_metrics.h new file mode 100644 index 0000000..d843640 --- /dev/null +++ b/chrome/installer/setup/installer_metrics.h
@@ -0,0 +1,29 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_INSTALLER_SETUP_INSTALLER_METRICS_H_ +#define CHROME_INSTALLER_SETUP_INSTALLER_METRICS_H_ + +namespace base { +class FilePath; +} // namespace base + +namespace installer { + +// Creates a persistent space for any histograms that can be reported later +// by the browser process. It should be called as early as possible in the +// process lifetime and never called again. +void BeginPersistentHistogramStorage(); + +// Saves all the persistent histograms to a single file on disk for reading +// by the browser so it can be reported. It is generally called once during +// process exit. Multiple calls to this are possible if "snapshots" are +// desired; they are cumulative in what is saved, not just what has changed +// since the previous call. +void EndPersistentHistogramStorage(const base::FilePath& target_dir); + +} // namespace installer + +#endif // CHROME_INSTALLER_SETUP_INSTALLER_METRICS_H_ +
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index 6f6f9d6d..06f69ca 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc
@@ -45,6 +45,7 @@ #include "chrome/installer/setup/install.h" #include "chrome/installer/setup/install_worker.h" #include "chrome/installer/setup/installer_crash_reporting.h" +#include "chrome/installer/setup/installer_metrics.h" #include "chrome/installer/setup/setup_constants.h" #include "chrome/installer/setup/setup_util.h" #include "chrome/installer/setup/uninstall.h" @@ -69,6 +70,7 @@ #include "chrome/installer/util/self_cleaning_temp_dir.h" #include "chrome/installer/util/shell_util.h" #include "chrome/installer/util/user_experiment.h" +#include "chrome/installer/util/util_constants.h" #include "components/crash/content/app/crash_switches.h" #include "components/crash/content/app/run_as_crashpad_handler_win.h" #include "content/public/common/content_switches.h" @@ -1624,6 +1626,9 @@ if (!installer::IsProcessorSupported()) return installer::CPU_NOT_SUPPORTED; + // Persist histograms so they can be uploaded later. + installer::BeginPersistentHistogramStorage(); + // The exit manager is in charge of calling the dtors of singletons. base::AtExitManager exit_manager; base::CommandLine::Init(0, NULL); @@ -1782,6 +1787,7 @@ return_code = InstallUtil::GetInstallReturnCode(install_status); } + installer::EndPersistentHistogramStorage(installer_state.target_path()); VLOG(1) << "Installation complete, returning: " << return_code; return return_code;
diff --git a/chrome/installer/util/google_update_constants.h b/chrome/installer/util/google_update_constants.h index f55268bd..05d97b2e 100644 --- a/chrome/installer/util/google_update_constants.h +++ b/chrome/installer/util/google_update_constants.h
@@ -76,7 +76,7 @@ extern const wchar_t kRegVersionField[]; extern const wchar_t kRegWebAccessibleField[]; -// last time that chrome ran in the Time internal format. +// Last time that chrome ran in the Time internal format. extern const wchar_t kRegLastRunTimeField[]; // The name of the value where Google Update reads the list of experiments for
diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc index 33af853..6446d1a1 100644 --- a/chrome/installer/util/util_constants.cc +++ b/chrome/installer/util/util_constants.cc
@@ -255,4 +255,6 @@ const char kCourgette[] = "courgette"; const char kBsdiff[] = "bsdiff"; +const char kSetupHistogramAllocatorName[] = "SetupHistogramAllocator"; + } // namespace installer
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h index c7312399..b83be73 100644 --- a/chrome/installer/util/util_constants.h +++ b/chrome/installer/util/util_constants.h
@@ -253,6 +253,10 @@ extern const char kCourgette[]; extern const char kBsdiff[]; +// Name of the allocator (and associated file) for storing histograms to be +// reported by Chrome during its next upload. +extern const char kSetupHistogramAllocatorName[]; + } // namespace installer #endif // CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H_
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 7d00445..dc9f4e7 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -79,6 +79,7 @@ #include "components/web_cache/renderer/web_cache_render_process_observer.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" #include "content/public/renderer/plugin_instance_throttler.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" @@ -393,8 +394,13 @@ WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(dom_distiller_scheme); #if defined(OS_CHROMEOS) - WebString external_file_scheme(ASCIIToUTF16(content::kExternalFileScheme)); - WebSecurityPolicy::registerURLSchemeAsLocal(external_file_scheme); + WebSecurityPolicy::registerURLSchemeAsLocal( + WebString::fromUTF8(content::kExternalFileScheme)); +#endif + +#if defined(OS_ANDROID) + WebSecurityPolicy::registerURLSchemeAsAllowedForReferrer( + WebString::fromUTF8(chrome::kAndroidAppScheme)); #endif #if defined(ENABLE_IPC_FUZZER)
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc index 3a05d18..826859ed 100644 --- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc +++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
@@ -416,6 +416,8 @@ ROUTE_FUNCTION(AddTreeChangeObserver); ROUTE_FUNCTION(RemoveTreeChangeObserver); ROUTE_FUNCTION(GetChildIDAtIndex); + ROUTE_FUNCTION(GetFocus); + ROUTE_FUNCTION(GetState); #undef ROUTE_FUNCTION // Bindings that take a Tree ID and return a property of the tree. @@ -491,22 +493,6 @@ result.Set(v8::Integer::New(isolate, node->index_in_parent())); }); RouteNodeIDFunction( - "GetState", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - TreeCache* cache, ui::AXNode* node) { - v8::Local<v8::Object> state(v8::Object::New(isolate)); - uint32_t state_pos = 0, state_shifter = node->data().state; - while (state_shifter) { - if (state_shifter & 1) { - std::string key = ToString(static_cast<ui::AXState>(state_pos)); - state->Set(CreateV8String(isolate, key), - v8::Boolean::New(isolate, true)); - } - state_shifter = state_shifter >> 1; - state_pos++; - } - result.Set(state); - }); - RouteNodeIDFunction( "GetRole", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, TreeCache* cache, ui::AXNode* node) { std::string role_name = ui::ToString(node->data().role); @@ -777,6 +763,112 @@ UpdateOverallTreeChangeObserverFilter(); } +bool AutomationInternalCustomBindings::GetFocusInternal(TreeCache* cache, + TreeCache** out_cache, + ui::AXNode** out_node) { + int focus_id = cache->tree.data().focus_id; + ui::AXNode* focus = cache->tree.GetFromId(focus_id); + if (!focus) + return false; + + while (focus->data().HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { + // Try to keep following focus recursively, by letting |tree_id| be the + // new subtree to search in, while keeping |focus_tree_id| set to the tree + // where we know we found a focused node. + int child_tree_id = + focus->data().GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID); + + TreeCache* child_cache = GetTreeCacheFromTreeID(child_tree_id); + if (!child_cache) + break; + + int child_focus_id = child_cache->tree.data().focus_id; + ui::AXNode* child_focus = child_cache->tree.GetFromId(child_focus_id); + if (!child_focus) + break; + + focus = child_focus; + cache = child_cache; + } + + *out_cache = cache; + *out_node = focus; + return true; +} + +void AutomationInternalCustomBindings::GetFocus( + const v8::FunctionCallbackInfo<v8::Value>& args) { + if (args.Length() != 1 || !args[0]->IsNumber()) { + ThrowInvalidArgumentsException(this); + return; + } + + int tree_id = args[0]->Int32Value(); + TreeCache* cache = GetTreeCacheFromTreeID(tree_id); + if (!cache) + return; + + TreeCache* focused_tree_cache = nullptr; + ui::AXNode* focused_node = nullptr; + if (!GetFocusInternal(cache, &focused_tree_cache, &focused_node)) + return; + + v8::Isolate* isolate = GetIsolate(); + v8::Local<v8::Object> result(v8::Object::New(isolate)); + result->Set(CreateV8String(isolate, "treeId"), + v8::Integer::New(isolate, focused_tree_cache->tree_id)); + result->Set(CreateV8String(isolate, "nodeId"), + v8::Integer::New(isolate, focused_node->id())); + args.GetReturnValue().Set(result); +} + +void AutomationInternalCustomBindings::GetState( + const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::Isolate* isolate = GetIsolate(); + if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsNumber()) + ThrowInvalidArgumentsException(this); + + int tree_id = args[0]->Int32Value(); + int node_id = args[1]->Int32Value(); + + TreeCache* cache = GetTreeCacheFromTreeID(tree_id); + if (!cache) + return; + + ui::AXNode* node = cache->tree.GetFromId(node_id); + if (!node) + return; + + v8::Local<v8::Object> state(v8::Object::New(isolate)); + uint32_t state_pos = 0, state_shifter = node->data().state; + while (state_shifter) { + if (state_shifter & 1) { + std::string key = ToString(static_cast<ui::AXState>(state_pos)); + state->Set(CreateV8String(isolate, key), v8::Boolean::New(isolate, true)); + } + state_shifter = state_shifter >> 1; + state_pos++; + } + + TreeCache* top_cache = GetTreeCacheFromTreeID(0); + if (!top_cache) + top_cache = cache; + TreeCache* focused_cache = nullptr; + ui::AXNode* focused_node = nullptr; + if (GetFocusInternal(top_cache, &focused_cache, &focused_node)) { + if (focused_cache == cache && focused_node == node) { + state->Set(CreateV8String(isolate, "focused"), + v8::Boolean::New(isolate, true)); + } + } + if (cache->tree.data().focus_id == node->id()) { + state->Set(CreateV8String(isolate, "focused"), + v8::Boolean::New(isolate, true)); + } + + args.GetReturnValue().Set(state); +} + void AutomationInternalCustomBindings::UpdateOverallTreeChangeObserverFilter() { tree_change_observer_overall_filter_ = api::automation::TREE_CHANGE_OBSERVER_FILTER_NOTREECHANGES;
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.h b/chrome/renderer/extensions/automation_internal_custom_bindings.h index cad7959..6764354 100644 --- a/chrome/renderer/extensions/automation_internal_custom_bindings.h +++ b/chrome/renderer/extensions/automation_internal_custom_bindings.h
@@ -85,6 +85,14 @@ void RemoveTreeChangeObserver( const v8::FunctionCallbackInfo<v8::Value>& args); + void GetFocus(const v8::FunctionCallbackInfo<v8::Value>& args); + + // Given an initial TreeCache, return the TreeCache and node of the focused + // node within this tree or a focused descendant tree. + bool GetFocusInternal(TreeCache* top_cache, + TreeCache** out_cache, + ui::AXNode** out_node); + void RouteTreeIDFunction(const std::string& name, void (*callback)(v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, @@ -115,53 +123,13 @@ // Access the cached accessibility trees and properties of their nodes. // - // Args: int ax_tree_id, int node_id, Returns: int parent_node_id. - void GetParentID(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: int ax_tree_id, int node_id, Returns: int child_count. - void GetChildCount(const v8::FunctionCallbackInfo<v8::Value>& args); - // Args: int ax_tree_id, int node_id, Returns: int child_id. void GetChildIDAtIndex(const v8::FunctionCallbackInfo<v8::Value>& args); - // Args: int ax_tree_id, int node_id, Returns: int index_in_parent. - void GetIndexInParent(const v8::FunctionCallbackInfo<v8::Value>& args); - // Args: int ax_tree_id, int node_id // Returns: JS object with a string key for each state flag that's set. void GetState(const v8::FunctionCallbackInfo<v8::Value>& args); - // Args: int ax_tree_id, int node_id, Returns: string role_name - void GetRole(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: int ax_tree_id, int node_id - // Returns: JS object with {left, top, width, height} - void GetLocation(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: int ax_tree_id, int node_id, string attribute_name - // Returns: string attribute_value. - void GetStringAttribute(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: int ax_tree_id, int node_id, string attribute_name - // Returns: bool attribute_value. - void GetBoolAttribute(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: int ax_tree_id, int node_id, string attribute_name - // Returns: int attribute_value. - void GetIntAttribute(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: int ax_tree_id, int node_id, string attribute_name - // Returns: float attribute_value. - void GetFloatAttribute(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: int ax_tree_id, int node_id, string attribute_name - // Returns: JS array of int attribute_values. - void GetIntListAttribute(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: int ax_tree_id, int node_id, string attribute_name - // Returns: string attribute_value. - void GetHtmlAttribute(const v8::FunctionCallbackInfo<v8::Value>& args); - // // Helper functions. //
diff --git a/chrome/renderer/resources/extensions/automation_custom_bindings.js b/chrome/renderer/resources/extensions/automation_custom_bindings.js index c09243ae..82a1e6c8 100644 --- a/chrome/renderer/resources/extensions/automation_custom_bindings.js +++ b/chrome/renderer/resources/extensions/automation_custom_bindings.js
@@ -25,6 +25,7 @@ var AddTreeChangeObserver = nativeAutomationInternal.AddTreeChangeObserver; var RemoveTreeChangeObserver = nativeAutomationInternal.RemoveTreeChangeObserver; +var GetFocus = nativeAutomationInternal.GetFocus; var schema = GetSchemaAdditions(); /** @@ -66,6 +67,27 @@ */ automationUtil.nextTreeChangeObserverId = 1; +/** + * @type {AutomationNode} The current focused node. This is only updated + * when calling automationUtil.updateFocusedNode. + */ +automationUtil.focusedNode = null; + +/** + * Update automationUtil.focusedNode to be the node that currently has focus. + */ +automationUtil.updateFocusedNode = function() { + automationUtil.focusedNode = null; + var focusedNodeInfo = GetFocus(DESKTOP_TREE_ID); + if (!focusedNodeInfo) + return; + var tree = AutomationRootNode.getOrCreate(focusedNodeInfo.treeId); + if (tree) { + automationUtil.focusedNode = + privates(tree).impl.get(focusedNodeInfo.nodeId); + } +}; + automation.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; @@ -118,6 +140,11 @@ } }); + apiFunctions.setHandleRequest('getFocus', function(callback) { + automationUtil.updateFocusedNode(); + callback(automationUtil.focusedNode); + }); + function removeTreeChangeObserver(observer) { for (var id in automationUtil.treeChangeObserverMap) { if (automationUtil.treeChangeObserverMap[id] == observer) { @@ -231,14 +258,34 @@ } }); -// Listen to the automationInternal.onAccessibilityEvent event, which is -// essentially a proxy for the AccessibilityHostMsg_Events IPC from the -// renderer. -automationInternal.onAccessibilityEvent.addListener(function(data) { - var id = data.treeID; +/** + * Dispatch accessibility events fired on individual nodes to its + * corresponding AutomationNode. Handle focus events specially + * (see below). + */ +automationInternal.onAccessibilityEvent.addListener(function(eventParams) { + var id = eventParams.treeID; var targetTree = AutomationRootNode.getOrCreate(id); - if (!privates(targetTree).impl.onAccessibilityEvent(data)) + // When we get a focus event, ignore the actual event target, and instead + // check what node has focus globally. If that represents a focus change, + // fire a focus event on the correct target. + if (eventParams.eventType == schema.EventType.focus) { + var previousFocusedNode = automationUtil.focusedNode; + automationUtil.updateFocusedNode(); + if (automationUtil.focusedNode && + automationUtil.focusedNode == previousFocusedNode) { + return; + } + + if (automationUtil.focusedNode) { + targetTree = automationUtil.focusedNode.root; + eventParams.treeID = privates(targetTree).impl.treeID; + eventParams.targetID = privates(automationUtil.focusedNode).impl.id; + } + } + + if (!privates(targetTree).impl.onAccessibilityEvent(eventParams)) return; // If we're not waiting on a callback to getTree(), we can early out here.
diff --git a/chrome/renderer/resources/neterror.css b/chrome/renderer/resources/neterror.css deleted file mode 100644 index b4e8186b..0000000 --- a/chrome/renderer/resources/neterror.css +++ /dev/null
@@ -1,469 +0,0 @@ -/* Copyright 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. */ - -/* Don't use the main frame div when the error is in a subframe. */ -html[subframe] #main-frame-error { - display: none; -} - -/* Don't use the subframe error div when the error is in a main frame. */ -html:not([subframe]) #sub-frame-error { - display: none; -} - -#diagnose-button { - -webkit-margin-start: 0; - float: none; - margin-bottom: 10px; - margin-top: 20px; -} - -h1 { - margin-top: 0; - word-wrap: break-word; -} - -h1 span { - font-weight: 500; -} - -h2 { - color: #666; - font-size: 1.2em; - font-weight: normal; - margin: 10px 0; -} - -a { - color: rgb(17, 85, 204); - text-decoration: none; -} - -.icon { - -webkit-user-select: none; - display: inline-block; -} - -.icon-generic { - /** - * Can't access chrome://theme/IDR_ERROR_NETWORK_GENERIC from an untrusted - * renderer process, so embed the resource manually. - */ - content: -webkit-image-set( - url(default_100_percent/common/error_network_generic.png) 1x, - url(default_200_percent/common/error_network_generic.png) 2x); -} - -.icon-offline { - content: -webkit-image-set( - url(default_100_percent/offline/100-error-offline.png) 1x, - url(default_200_percent/offline/200-error-offline.png) 2x); - position: relative; -} - -.icon-disabled { - content: -webkit-image-set( - url(default_100_percent/offline/100-disabled.png) 1x, - url(default_200_percent/offline/200-disabled.png) 2x); - width: 112px; -} - -.error-code { - display: block; - font-size: .8em; -} - -#content-top { - margin: 20px; -} - -#help-box-inner { - background-color: #f9f9f9; - border-top: 1px solid #EEE; - color: #444; - padding: 20px; - text-align: start; -} - -.hidden { - display: none; -} - -#suggestion { - margin-top: 15px; -} - -#short-suggestion { - margin-top: 5px; -} - -#sub-frame-error-details { - color: #8F8F8F; -<if expr="not is_android and not is_ios"> - /* Not done on mobile for performance reasons. */ - text-shadow: 0 1px 0 rgba(255,255,255,0.3); -</if> -} - -[jscontent=failedUrl] { - overflow-wrap: break-word; -} - -#search-container { - /* Prevents a space between controls. */ - display: flex; - margin-top: 20px; -} - -#search-box { - border: 1px solid #cdcdcd; - flex-grow: 1; - font-size: 1em; - height: 26px; - margin-right: 0; - padding: 1px 9px; -} - -#search-box:focus { - border: 1px solid rgb(93, 154, 255); - outline: none; -} - -#search-button { - border: none; - border-bottom-left-radius: 0; - border-top-left-radius: 0; - box-shadow: none; - display: flex; - height: 30px; - margin: 0; - padding: 0; - width: 60px; -} - -#search-image { - content: - -webkit-image-set( - url(../../app/theme/default_100_percent/common/omnibox_search_button_loupe.png) 1x, - url(../../app/theme/default_200_percent/common/omnibox_search_button_loupe.png) 2x); - margin: auto; -} - -.secondary-button { - -webkit-margin-end: 16px; - background: #d9d9d9; - color: #696969; -} - -.snackbar { - background: #323232; - border-radius: 2px; - bottom: 24px; - box-sizing: border-box; - color: #fff; - font-size: .87em; - left: 24px; - max-width: 568px; - min-width: 288px; - opacity: 0; - padding: 16px 24px 12px; - position: fixed; - transform: translateY(90px); - will-change: opacity, transform; - z-index: 999; -} - -.snackbar-show { - -webkit-animation: - show-snackbar .25s cubic-bezier(0.0, 0.0, 0.2, 1) forwards, - hide-snackbar .25s cubic-bezier(0.4, 0.0, 1, 1) forwards 5s; -} - -@-webkit-keyframes show-snackbar { - 100% { - opacity: 1; - transform: translateY(0); - } -} - -@-webkit-keyframes hide-snackbar { - 0% { - opacity: 1; - transform: translateY(0); - } - 100% { - opacity: 0; - transform: translateY(90px); - } -} - -.suggestions { - margin-top: 18px; -} - -.suggestion-header { - font-weight: bold; - margin-bottom: 4px; -} - -.suggestion-body { - color: #777; -} - -/* Increase line height at higher resolutions. */ -@media (min-width: 641px) and (min-height: 641px) { - #help-box-inner { - line-height: 18px; - } -} - -/* Decrease padding at low sizes. */ -@media (max-width: 640px), (max-height: 640px) { - h1 { - margin: 0 0 15px; - } - #content-top { - margin: 15px; - } - #help-box-inner { - padding: 20px; - } - .suggestions { - margin-top: 10px; - } - .suggestion-header { - margin-bottom: 0; - } -} - -/* Don't allow overflow when in a subframe. */ -html[subframe] body { - overflow: hidden; -} - -#sub-frame-error { - -webkit-align-items: center; - background-color: #DDD; - display: -webkit-flex; - -webkit-flex-flow: column; - height: 100%; - -webkit-justify-content: center; - left: 0; - position: absolute; - top: 0; - transition: background-color .2s ease-in-out; - width: 100%; -} - -#sub-frame-error:hover { - background-color: #EEE; -} - -#sub-frame-error .icon-generic { - margin: 0 0 16px; -} - -#sub-frame-error-details { - margin: 0 10px; - text-align: center; - visibility: hidden; -} - -/* Show details only when hovering. */ -#sub-frame-error:hover #sub-frame-error-details { - visibility: visible; -} - -/* If the iframe is too small, always hide the error code. */ -/* TODO(mmenke): See if overflow: no-display works better, once supported. */ -@media (max-width: 200px), (max-height: 95px) { - #sub-frame-error-details { - display: none; - } -} - -/* Adjust icon for small embedded frames in apps. */ -@media (max-height: 100px) { - #sub-frame-error .icon-generic { - height: auto; - margin: 0; - padding-top: 0; - width: 25px; - } -} - -/* details-button is special; it's a <button> element that looks like a link. */ -#details-button { - box-shadow: none; - min-width: 0; -} - -/* Styles for platform dependent separation of controls and details button. */ -.suggested-left > #control-buttons, -.suggested-left #stale-load-button, -.suggested-right > #details-button { - float: left; -} - -.suggested-right > #control-buttons, -.suggested-right #stale-load-button, -.suggested-left > #details-button { - float: right; -} - -.suggested-left .secondary-button { - -webkit-margin-end: 0px; - -webkit-margin-start: 16px; -} - -#details-button.singular { - float: none; -} - -#buttons::after { - clear: both; - content: ''; - display: block; - width: 100%; -} - -/* Offline page */ -.offline { - transition: -webkit-filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1), - background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1); - will-change: -webkit-filter, background-color; -} - -.offline.inverted { - -webkit-filter: invert(100%); - background-color: #000; -} - -.offline .interstitial-wrapper { - color: #2b2b2b; - font-size: 1em; - line-height: 1.55; - margin: 0 auto; - max-width: 600px; - padding-top: 100px; - width: 100%; -} - -.offline .runner-container { - height: 150px; - max-width: 600px; - overflow: hidden; - position: absolute; - top: 35px; - width: 44px; -} - -.offline .runner-canvas { - height: 150px; - max-width: 600px; - opacity: 1; - overflow: hidden; - position: absolute; - top: 0; - z-index: 2; -} - -.offline .controller { - background: rgba(247,247,247, .1); - height: 100vh; - left: 0; - position: absolute; - top: 0; - width: 100vw; - z-index: 1; -} - -#offline-resources { - display: none; -} - -@media (max-width: 420px) { - .suggested-left > #control-buttons, - .suggested-right > #control-buttons { - float: none; - } - - .snackbar { - left: 0; - bottom: 0; - width: 100%; - border-radius: 0; - } -} - -@media (max-height: 350px) { - h1 { - margin: 0 0 15px; - } - - .icon-offline { - margin: 0 0 10px; - } - - .interstitial-wrapper { - margin-top: 5%; - } - - .nav-wrapper { - margin-top: 30px; - } -} - -@media (min-width: 600px) and (max-width: 736px) and (orientation: landscape) { - .offline .interstitial-wrapper { - margin-left: 0; - margin-right: 0; - } -} - -@media (min-width: 420px) and (max-width: 736px) and - (min-height: 240px) and (max-height: 420px) and - (orientation:landscape) { - .interstitial-wrapper { - margin-bottom: 100px; - } -} - -@media (min-height: 240px) and (orientation: landscape) { - .offline .interstitial-wrapper { - margin-bottom: 90px; - } - - .icon-offline { - margin-bottom: 20px; - } -} - -@media (max-height: 320px) and (orientation: landscape) { - .icon-offline { - margin-bottom: 0; - } - - .offline .runner-container { - top: 10px; - } -} - -@media (max-width: 240px) { - button { - padding-left: 12px; - padding-right: 12px; - } - - .interstitial-wrapper { - overflow: inherit; - padding: 0 8px; - } -} - -@media (max-width: 120px) { - button { - width: auto; - } -}
diff --git a/chrome/test/data/devtools/latency_info.html b/chrome/test/data/devtools/latency_info.html new file mode 100644 index 0000000..e3290d82 --- /dev/null +++ b/chrome/test/data/devtools/latency_info.html
@@ -0,0 +1,105 @@ +<html> +<head> +<style> +#test-hover { + position: absolute; + left: 20px; + top: 40px; + width: 100px; + height: 30px; + background-color: red; +} + +#test-hover:hover { + background-color: blue; +} + +#test-button { + position: absolute; + left: 20px; + top: 100px; + width: 100px; + height: 30px; +} + +#scrollable { + position: absolute; + left: 200px; + top: 30px; + width: 200px; + height: 200px; + overflow: scroll; +} + +#scrolled { + width: 800px; + height: 800px; +} +</style> +</head> +<body> +<div id="test-hover">Hover me</div> +<button id="test-button">Click me</button> +<div id="scrollable"><div id="scrolled"></div></div> +<script> +var pendingListeners = {}; +var pendingEvents = {}; +var inflightEvents = null; +function onEvent(event) +{ + if (inflightEvents) { + inflightEvents.push(event); + return; + } + inflightEvents = [event]; + if (event.type === "mousemove") + document.body.style.backgroundColor = "#" + Math.floor(Math.random() * (1 << 24)).toString(16); + requestAnimationFrame(onEventAfterFrame); +} + +function onEventAfterFrame() +{ + for (event of inflightEvents) { + var type = event.type; + var listener = pendingListeners[type]; + if (!listener) { + pendingEvents[type] = (pendingEvents[type] || 0) + 1; + continue; + } + delete pendingListeners[type]; + listener(); + } + inflightEvents = null; +} + +function waitForEvent(eventType, callback) +{ + if (pendingEvents[eventType]) { + pendingEvents[eventType]--; + callback(); + return; + } + pendingListeners[eventType] = callback; +} + +var eventTypes = [ + "mousemove", + "mousedown", + "mouseup", + "wheel", + "gesturetap", + "click", + "keydown", + "keyup", + "touchstart", + "touchend", + "touchcancel", + "touchmove" +]; + +for (var e of eventTypes) { + window.addEventListener(e, onEvent, true); +} +</script> +</body> +</html>
diff --git a/chrome/test/data/dom_distiller/simple_article.html b/chrome/test/data/dom_distiller/simple_article.html index ae8d3410..34c5340 100644 --- a/chrome/test/data/dom_distiller/simple_article.html +++ b/chrome/test/data/dom_distiller/simple_article.html
@@ -13,6 +13,18 @@ <p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. <p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. </div> </body> </html>
diff --git a/chrome/test/data/dom_distiller/simple_article_iframe.html b/chrome/test/data/dom_distiller/simple_article_iframe.html index c534456..8e6cc4a 100644 --- a/chrome/test/data/dom_distiller/simple_article_iframe.html +++ b/chrome/test/data/dom_distiller/simple_article_iframe.html
@@ -14,6 +14,18 @@ <p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. <p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. + +<p>Lorem ipsum dolor sit amet, at alia aliquip vel. Quas inani labore an vel. Sed an nemore minimum accusata. Sint inermis tacimates est ex, ad movet iracundia mei, delicata iracundia laboramus ei eos. Illud principes complectitur te nec, ius alienum insolens ea, cu quo oratio omnesque. </div> </body> </html>
diff --git a/chrome/test/data/downloads/download-anchor-attrib-400.html b/chrome/test/data/downloads/download-anchor-attrib-400.html new file mode 100644 index 0000000..6d3096c --- /dev/null +++ b/chrome/test/data/downloads/download-anchor-attrib-400.html
@@ -0,0 +1,13 @@ +<html> +<head><title><a download> that results in a status code of 400</title></head> +<body> +<a id='download' href="zip_file_not_found.zip" download='foo'>Download</a> +<script> +window.setTimeout(function() { + var evt = document.createEvent("MouseEvent"); + evt.initMouseEvent('click', true, true); + document.getElementById('download').dispatchEvent(evt); +}, 0); +</script> +</body> +</html>
diff --git a/chrome/test/data/downloads/download-anchor-attrib-404.html b/chrome/test/data/downloads/download-anchor-attrib-404.html new file mode 100644 index 0000000..665b2e3 --- /dev/null +++ b/chrome/test/data/downloads/download-anchor-attrib-404.html
@@ -0,0 +1,13 @@ +<html> +<head><title><a download> that results in a status code of 404</title></head> +<body> +<a id='download' href="there_IS_no_spoon.zip" download='foo'>Download</a> +<script> +window.setTimeout(function() { + var evt = document.createEvent("MouseEvent"); + evt.initMouseEvent('click', true, true); + document.getElementById('download').dispatchEvent(evt); +}, 0); +</script> +</body> +</html>
diff --git a/chrome/test/data/downloads/download-anchor-attrib-name-not-resolved.html b/chrome/test/data/downloads/download-anchor-attrib-name-not-resolved.html new file mode 100644 index 0000000..c489962 --- /dev/null +++ b/chrome/test/data/downloads/download-anchor-attrib-name-not-resolved.html
@@ -0,0 +1,13 @@ +<html> +<head><title><a download> that refers to a nonexistent hostname</title></head> +<body> +<a id='download' href="http://doesnotexist/shouldnotberesolved" download='foo'>Download</a> +<script> +window.setTimeout(function() { + var evt = document.createEvent("MouseEvent"); + evt.initMouseEvent('click', true, true); + document.getElementById('download').dispatchEvent(evt); +}, 0); +</script> +</body> +</html>
diff --git a/chrome/test/data/extensions/api_test/automation/sites/desktop.html b/chrome/test/data/extensions/api_test/automation/sites/desktop.html new file mode 100644 index 0000000..cfaf7eb --- /dev/null +++ b/chrome/test/data/extensions/api_test/automation/sites/desktop.html
@@ -0,0 +1 @@ +<input title="abc" autofocus>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/actions.js b/chrome/test/data/extensions/api_test/automation/tests/desktop/actions.js index 70a30c6..8b2f8df 100644 --- a/chrome/test/data/extensions/api_test/automation/tests/desktop/actions.js +++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/actions.js
@@ -15,18 +15,6 @@ firstTextField.doDefault(); }, - function testFocus() { - var firstFocusableNode = findAutomationNode(rootNode, - function(node) { - return node.role == 'button' && node.state.focusable; - }); - assertTrue(!!firstFocusableNode); - listenOnce(firstFocusableNode, EventType.focus, function(e) { - chrome.test.succeed(); - }, true); - firstFocusableNode.focus(); - }, - function testDoDefaultViews() { listenOnce(rootNode, 'focus', function(node) { chrome.test.succeed();
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_views.html b/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_views.html new file mode 100644 index 0000000..2c0f44fe --- /dev/null +++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_views.html
@@ -0,0 +1,7 @@ +<!-- + * Copyright 2016 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<script src="common.js"></script> +<script src="focus_views.js"></script>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_views.js b/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_views.js new file mode 100644 index 0000000..c6855d0 --- /dev/null +++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_views.js
@@ -0,0 +1,20 @@ +// 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 allTests = [ + function testFocusLocationBar() { + var firstFocusableNode = findAutomationNode(rootNode, + function(node) { + return node.role == 'textField' && node.state.focusable; + }); + + assertTrue(!!firstFocusableNode); + listenOnce(firstFocusableNode, EventType.focus, function(e) { + chrome.test.succeed(); + }, true); + firstFocusableNode.focus(); + } +]; + +setUpAndRunTests(allTests);
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_web.html b/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_web.html new file mode 100644 index 0000000..8d8c013 --- /dev/null +++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_web.html
@@ -0,0 +1,7 @@ +<!-- + * Copyright 2016 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<script src="common.js"></script> +<script src="focus_web.js"></script>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_web.js b/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_web.js new file mode 100644 index 0000000..211d0e9 --- /dev/null +++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/focus_web.js
@@ -0,0 +1,54 @@ +// 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 allTests = [ + function testFocusAcrossWindows() { + // Create two windows with focusable text fields in them. + // Let window2 have focus. (Enforce this by focusing the WebView + // that's the parent of the second window's document, below.) + var html1 = '<input title="Input1">'; + var html2 = '<input title="Input2">'; + var url1 = 'data:text/html,<!doctype html>' + encodeURI(html1); + var url2 = 'data:text/html,<!doctype html>' + encodeURI(html2); + chrome.windows.create({url: url1, focused: false}); + chrome.windows.create({url: url2, focused: true}); + + // Wait for the accessibility trees to load and get the accessibility + // objects for both text fields. + // + // Try to focus the first text field (in the unfocused window) and then + // the second text field (in the focused window) after a short delay. + // + // If we get a focus event on the first text field, the test fails, + // because that window is in the background. If we get a focus event on + // the second text field, the test succeeds. + var input1, input2; + chrome.automation.getDesktop(function(rootNode) { + rootNode.addEventListener('loadComplete', function(event) { + if (event.target.url == url1) { + input1 = event.target.find({role: 'textField'}); + } + if (event.target.url == url2) { + // Focus the WebView that's the parent of the second document. + event.target.parent.focus(); + input2 = event.target.find({role: 'textField'}); + } + if (input1 && input2) { + input1.addEventListener('focus', function(event) { + chrome.test.fail(); + }, false); + input2.addEventListener('focus', function(event) { + chrome.test.succeed(); + }, false); + input1.focus(); + setTimeout(function() { + input2.focus(); + }, 100); + } + }, false); + }); + }, +]; + +chrome.test.runTests(allTests);
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.html b/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.html new file mode 100644 index 0000000..75db122 --- /dev/null +++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.html
@@ -0,0 +1,7 @@ +<!-- + * Copyright 2016 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<script src="common.js"></script> +<script src="initial_focus.js"></script>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.js b/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.js new file mode 100644 index 0000000..cd467b2 --- /dev/null +++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/initial_focus.js
@@ -0,0 +1,25 @@ +// 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 allTests = [ + function testInitialFocus() { + var url = 'data:text/html,<!doctype html>' + + encodeURI('<input autofocus title=abc>'); + chrome.automation.getDesktop(function(rootNode) { + chrome.tabs.create({url: url}); + + rootNode.addEventListener('loadComplete', function(event) { + if (event.target.url == url) { + chrome.automation.getFocus(function(focus) { + assertEq('textField', focus.role); + assertEq('abc', focus.name); + chrome.test.succeed(); + }); + } + }, false); + }); + }, +]; + +chrome.test.runTests(allTests);
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/sanity_check.js b/chrome/test/data/extensions/api_test/automation/tests/tabs/sanity_check.js index 553c86d..b4d743d 100644 --- a/chrome/test/data/extensions/api_test/automation/tests/tabs/sanity_check.js +++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/sanity_check.js
@@ -19,7 +19,7 @@ var state = RemoveUntestedStates(rootNode.state); assertEq( - {enabled: true, focusable: true, readOnly: true}, + {enabled: true, focusable: true, readOnly: true, focused: true}, state); var children = rootNode.children;
diff --git a/chrome/test/data/extensions/api_test/tab_capture/end_to_end.js b/chrome/test/data/extensions/api_test/tab_capture/end_to_end.js index 47c23772..ba449bdf 100644 --- a/chrome/test/data/extensions/api_test/tab_capture/end_to_end.js +++ b/chrome/test/data/extensions/api_test/tab_capture/end_to_end.js
@@ -197,7 +197,9 @@ receiver.createAnswer(function (receiver_description) { receiver.setLocalDescription(receiver_description); sender.setRemoteDescription(receiver_description); + }, function (error) { }); + }, function (error) { }); } else { chrome.test.fail("Unknown transport method: " + transportMethod);
diff --git a/chrome/test/data/extensions/options_page/manifest.json b/chrome/test/data/extensions/options_page/manifest.json new file mode 100644 index 0000000..27d7bd7 --- /dev/null +++ b/chrome/test/data/extensions/options_page/manifest.json
@@ -0,0 +1,7 @@ +{ + "name": "Extension With Options Page", + "description": "An extension with an options page and an extra page", + "options_ui": { "page": "options.html", "open_in_tab": true }, + "version": "0.1.1", + "manifest_version": 2 +}
diff --git a/chrome/test/data/extensions/options_page/options.html b/chrome/test/data/extensions/options_page/options.html new file mode 100644 index 0000000..e25f1b9 --- /dev/null +++ b/chrome/test/data/extensions/options_page/options.html
@@ -0,0 +1 @@ +An options page
diff --git a/chrome/test/data/extensions/options_page/other.html b/chrome/test/data/extensions/options_page/other.html new file mode 100644 index 0000000..65032ff --- /dev/null +++ b/chrome/test/data/extensions/options_page/other.html
@@ -0,0 +1 @@ +Another page
diff --git a/chrome/test/data/webrtc/webrtc-simulcast.html b/chrome/test/data/webrtc/webrtc-simulcast.html index 486681a8..b61e4b3 100644 --- a/chrome/test/data/webrtc/webrtc-simulcast.html +++ b/chrome/test/data/webrtc/webrtc-simulcast.html
@@ -191,7 +191,7 @@ } function afterSetServerRemoteDescription() { - pcServer.createAnswer(onServerAnswer); + pcServer.createAnswer(onServerAnswer, function(error) {}); } function onServerAnswer(desc) {
diff --git a/chrome/test/data/webui/settings/basic_page_browsertest.js b/chrome/test/data/webui/settings/basic_page_browsertest.js index f36bb92..89e2695b 100644 --- a/chrome/test/data/webui/settings/basic_page_browsertest.js +++ b/chrome/test/data/webui/settings/basic_page_browsertest.js
@@ -46,6 +46,7 @@ expectTrue(!!self.getSection(page, 'defaultBrowser')); } else { expectTrue(!!self.getSection(page, 'internet')); + expectTrue(!!self.getSection(page, 'device')); } }); });
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index eb3423f..a82d8a72 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -98,3 +98,30 @@ settings_rtl_tests.registerDrawerPanelTests(); mocha.run(); }); + +/** + * Test fixture for chrome/browser/resources/settings/search_engines_page/. + * @constructor + * @extends {CrSettingsBrowserTest} +*/ +function CrSettingsSearchEnginesTest() {} + +CrSettingsSearchEnginesTest.prototype = { + __proto__: CrSettingsBrowserTest.prototype, + + /** @override */ + browsePreload: + 'chrome://md-settings/search_engines_page/search_engines_page.html', + + /** @override */ + extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + 'search_engines_page_test.js', + ]), +}; + +TEST_F('CrSettingsSearchEnginesTest', 'SearchEngines', function() { + settings_search_engines_page.registerDialogTests(); + settings_search_engines_page.registerEntryTests(); + settings_search_engines_page.registerPageTests(); + mocha.run(); +});
diff --git a/chrome/test/data/webui/settings/device_page_browsertest_chromeos.js b/chrome/test/data/webui/settings/device_page_browsertest_chromeos.js new file mode 100644 index 0000000..fdaf69b --- /dev/null +++ b/chrome/test/data/webui/settings/device_page_browsertest_chromeos.js
@@ -0,0 +1,44 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @fileoverview Suite of tests for the Settings Device page. */ + +GEN_INCLUDE(['settings_page_browsertest.js']); + +/** + * @constructor + * @extends {SettingsPageBrowserTest} +*/ +function SettingsDevicePageBrowserTest() {} + +SettingsDevicePageBrowserTest.prototype = { + __proto__: SettingsPageBrowserTest.prototype +}; + +// Times out on debug builders and may time out on memory bots because +// the Settings page can take several seconds to load in a Release build +// and several times that in a Debug build. See https://crbug.com/558434. +GEN('#if defined(MEMORY_SANITIZER) || !defined(NDEBUG)'); +GEN('#define MAYBE_DevicePage DISABLED_DevicePage'); +GEN('#else'); +GEN('#define MAYBE_DevicePage DevicePage'); +GEN('#endif'); + +TEST_F('SettingsDevicePageBrowserTest', 'MAYBE_DevicePage', function() { + // Assign |self| to |this| instead of binding since 'this' in suite() + // and test() will be a Mocha 'Suite' or 'Test' instance. + var self = this; + + // Register mocha tests. + suite('SettingsDevicePage', function() { + test('device section', function() { + var page = self.getPage('basic'); + var deviceSection = self.getSection(page, 'device'); + expectTrue(!!deviceSection); + }); + }); + + // Run all registered tests. + mocha.run(); +});
diff --git a/chrome/test/data/webui/settings/search_engines_page_test.js b/chrome/test/data/webui/settings/search_engines_page_test.js new file mode 100644 index 0000000..48042bcc --- /dev/null +++ b/chrome/test/data/webui/settings/search_engines_page_test.js
@@ -0,0 +1,361 @@ +// 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('settings_search_engines_page', function() { + /** + * TODO(dpapad): Similar class exists in webui_resource_async_browsertest.js. + * Move somewhere else and re-use. + * @constructor + */ + var PromiseResolver = function() { + this.promise = new Promise(function(resolve, reject) { + this.resolve = resolve; + this.reject = reject; + }.bind(this)); + }; + + /** + * A test version of SearchEnginesBrowserProxy. Provides helper methods + * for allowing tests to know when a method was called, as well as + * specifying mock responses. + * + * @constructor + * @implements {settings.SearchEnginesBrowserProxy} + */ + var TestSearchEnginesBrowserProxy = function() { + /** @private {!Map<string, !PromiseResolver>} */ + this.resolverMap_ = new Map(); + var wrapperMethods = [ + 'getSearchEnginesList', + 'removeSearchEngine', + 'searchEngineEditCancelled', + 'searchEngineEditCompleted', + 'searchEngineEditStarted', + 'setDefaultSearchEngine', + 'validateSearchEngineInput', + ]; + wrapperMethods.forEach(this.resetResolver, this); + + /** @private {!SearchEnginesInfo} */ + this.searchEnginesInfo_ = {defaults: [], others: [], extensions: []}; + }; + + TestSearchEnginesBrowserProxy.prototype = { + /** + * @param {string} methodName + * @return {!Promise} A promise that is resolved when the given method + * is called. + */ + whenCalled: function(methodName) { + return this.resolverMap_.get(methodName).promise; + }, + + /** + * Resets the PromiseResolver associated with the given method. + * @param {string} methodName + */ + resetResolver: function(methodName) { + this.resolverMap_.set(methodName, new PromiseResolver()); + }, + + /** @override */ + setDefaultSearchEngine: function(modelIndex) { + this.resolverMap_.get('setDefaultSearchEngine').resolve(modelIndex); + }, + + /** @override */ + removeSearchEngine: function(modelIndex) { + this.resolverMap_.get('removeSearchEngine').resolve(modelIndex); + }, + + /** @override */ + searchEngineEditStarted: function(modelIndex) { + this.resolverMap_.get('searchEngineEditStarted').resolve(modelIndex); + }, + + /** @override */ + searchEngineEditCancelled: function() { + this.resolverMap_.get('searchEngineEditCancelled').resolve(); + }, + + /** @override */ + searchEngineEditCompleted: function(searchEngine, keyword, queryUrl) { + this.resolverMap_.get('searchEngineEditCompleted').resolve(); + }, + + /** + * Sets the response to be returned by |getSearchEnginesList|. + * @param {!SearchEnginesInfo} + */ + setSearchEnginesInfo: function(searchEnginesInfo) { + this.searchEnginesInfo_ = searchEnginesInfo; + }, + + /** @override */ + getSearchEnginesList: function() { + this.resolverMap_.get('getSearchEnginesList').resolve(); + return Promise.resolve(this.searchEnginesInfo_); + }, + + /** @override */ + validateSearchEngineInput: function(fieldName, fieldValue) { + this.resolverMap_.get('validateSearchEngineInput').resolve(); + return Promise.resolve(true); + }, + }; + + /** @return {!SearchEngine} */ + var createSampleSearchEngine = function() { + return { + canBeDefault: false, + canBeEdited: true, + canBeRemoved: false, + default: false, + displayName: "Google", + iconURL: "http://www.google.com/favicon.ico", + isOmniboxExtension: false, + keyword: "google.com", + modelIndex: 0, + name: "Google", + url: "https://search.foo.com/search?p=%s", + urlLocked: false, + }; + }; + + function registerDialogTests() { + suite('AddSearchEngineDialogTests', function() { + /** @type {?SettingsAddSearchEngineDialog} */ + var dialog = null; + var browserProxy = null; + + setup(function() { + browserProxy = new TestSearchEnginesBrowserProxy(); + settings.SearchEnginesBrowserProxyImpl.instance_ = browserProxy; + PolymerTest.clearBody(); + dialog = document.createElement('settings-search-engine-dialog'); + document.body.appendChild(dialog); + }); + + teardown(function() { dialog.remove(); }); + + // Tests that the dialog calls 'searchEngineEditStarted' and + // 'searchEngineEditCancelled' when closed from the 'x' button. + test('DialogOpenAndClose', function() { + return browserProxy.whenCalled('searchEngineEditStarted').then( + function() { + MockInteractions.tap(dialog.$.close); + return browserProxy.whenCalled('searchEngineEditCancelled'); + }); + }); + + // Tests that the dialog calls 'searchEngineEditStarted' and + // 'searchEngineEditCancelled' when closed from the 'cancel' button. + test('DialogOpenAndCancel', function() { + return browserProxy.whenCalled('searchEngineEditStarted').then( + function() { + MockInteractions.tap(dialog.$.cancel); + return browserProxy.whenCalled('searchEngineEditCancelled'); + }); + }); + + // Tests the dialog to add a new search engine. Specifically + // - paper-input elements are empty initially. + // - action button initially disabled. + // - validation is triggered on 'focus'. 'change' is not teted because + // MockInteractions does not currently provide a way to trigger such + // events. + // - action button is enabled when all fields are valid. + // - action button triggers appropriate browser signal when tapped. + test('DialogAddSearchEngine', function() { + /** + * Focuses the given paper-input element and checks that validation is + * triggered. + * @param {string} inputId + * @return {!Promise} + */ + var focusAndValidate = function(inputId) { + browserProxy.resetResolver('validateSearchEngineInput'); + MockInteractions.focus(dialog.$[inputId]); + return browserProxy.whenCalled('validateSearchEngineInput'); + }; + + assertEquals('', dialog.$.searchEngine.value); + assertEquals('', dialog.$.keyword.value); + assertEquals('', dialog.$.queryUrl.value); + var actionButton = dialog.$.actionButton; + assertTrue(actionButton.disabled); + + return focusAndValidate('searchEngine').then(function() { + return focusAndValidate('keyword'); + }).then(function() { + return focusAndValidate('queryUrl'); + }).then(function() { + // Manually set the text to a non-empty string for all fields. + dialog.$.searchEngine.value = 'foo'; + dialog.$.keyword.value = 'bar'; + dialog.$.queryUrl.value = 'baz'; + + // MockInteractions does not provide a way to trigger a 'change' event + // yet. Triggering the 'focus' event instead, to update the state of + // the action button. + return focusAndValidate('searchEngine'); + }).then(function() { + // Assert that the action button has been enabled now that all input + // is valid and non-empty. + assertFalse(actionButton.disabled); + MockInteractions.tap(actionButton); + return browserProxy.whenCalled('searchEngineEditCompleted'); + }); + }); + + }); + } + + function registerEntryTests() { + suite('SearchEngineEntryTests', function() { + /** @type {?SettingsSearchEngineEntryElement} */ + var entry = null; + + var browserProxy = null; + + setup(function() { + browserProxy = new TestSearchEnginesBrowserProxy(); + settings.SearchEnginesBrowserProxyImpl.instance_ = browserProxy; + PolymerTest.clearBody(); + entry = document.createElement('settings-search-engine-entry'); + entry.set('engine', createSampleSearchEngine()); + document.body.appendChild(entry); + }); + + teardown(function() { entry.remove(); }); + + test('RemoveSearchEngine', function() { + var deleteButton = entry.$.delete; + assertTrue(!!deleteButton); + MockInteractions.tap(deleteButton); + return browserProxy.whenCalled('removeSearchEngine').then( + function(modelIndex) { + assertEquals(entry.engine.modelIndex, modelIndex); + }); + }); + + test('MakeDefault', function() { + var makeDefaultButton = entry.$.makeDefault; + assertTrue(!!makeDefaultButton); + MockInteractions.tap(makeDefaultButton); + return browserProxy.whenCalled('setDefaultSearchEngine').then( + function(modelIndex) { + assertEquals(entry.engine.modelIndex, modelIndex); + }); + }); + + // Test that the "make default" and "remove" buttons are hidden for + // the default search engine. + test('DefaultSearchEngineHiddenButtons', function() { + assertFalse(entry.$.makeDefault.hidden); + assertFalse(entry.$.edit.hidden); + assertFalse(entry.$.delete.hidden); + + // Mark the engine as the "default" one. + var engine = createSampleSearchEngine(); + engine.default = true; + entry.set('engine', engine); + Polymer.dom.flush(); + + assertTrue(entry.$.makeDefault.hidden); + assertFalse(entry.$.edit.hidden); + assertTrue(entry.$.delete.hidden); + }); + + // Test that clicking the "edit" button brings up a dialog. + test('Edit', function() { + var engine = entry.engine; + var editButton = entry.$.edit; + assertTrue(!!editButton); + MockInteractions.tap(editButton); + return browserProxy.whenCalled('searchEngineEditStarted').then( + function(modelIndex) { + assertEquals(engine.modelIndex, modelIndex); + var dialog = entry.$$('settings-search-engine-dialog'); + assertTrue(!!dialog); + + // Check that the paper-input fields are pre-populated. + assertEquals(engine.displayName, dialog.$.searchEngine.value); + assertEquals(engine.keyword, dialog.$.keyword.value); + assertEquals(engine.url, dialog.$.queryUrl.value); + + assertFalse(dialog.$.actionButton.disabled); + }); + }); + }); + } + + function registerPageTests() { + suite('SearchEnginePageTests', function() { + /** @type {?SettingsSearchEnginesPageElement} */ + var page = null; + + var browserProxy = null; + + /** @type {!SearchEnginesInfo} */ + var searchEnginesInfo = { + defaults: [createSampleSearchEngine()], + others: [], + extensions: [], + }; + + setup(function() { + browserProxy = new TestSearchEnginesBrowserProxy(); + browserProxy.setSearchEnginesInfo(searchEnginesInfo); + settings.SearchEnginesBrowserProxyImpl.instance_ = browserProxy; + PolymerTest.clearBody(); + page = document.createElement('settings-search-engines-page'); + document.body.appendChild(page); + }); + + teardown(function() { page.remove(); }); + + // Tests that the page is querying and displaying search engine info on + // startup. + test('Initialization', function() { + return browserProxy.whenCalled('getSearchEnginesList').then(function() { + var searchEnginesLists = Polymer.dom(page.shadowRoot). + querySelectorAll('settings-search-engines-list'); + assertEquals(2, searchEnginesLists.length); + + Polymer.dom.flush(); + var defaultsList = searchEnginesLists[0]; + var defaultsEntries = Polymer.dom(defaultsList.shadowRoot). + querySelectorAll('settings-search-engine-entry'); + assertEquals( + searchEnginesInfo.defaults.length, defaultsEntries.length); + + var othersList = searchEnginesLists[1]; + var othersEntries = Polymer.dom(othersList.shadowRoot). + querySelectorAll('settings-search-engine-entry'); + assertEquals( + searchEnginesInfo.others.length, othersEntries.length); + }); + }); + + // Tests that the add search engine dialog opens when the corresponding + // button is tapped. + test('AddSearchEngineDialog', function() { + assertFalse(!!page.$$('settings-search-engine-dialog')); + var addSearchEngineButton = page.$.addSearchEngine; + assertTrue(!!addSearchEngineButton); + + MockInteractions.tap(addSearchEngineButton); + Polymer.dom.flush(); + assertTrue(!!page.$$('settings-search-engine-dialog')); + }); + }); + } + + return { + registerDialogTests: registerDialogTests, + registerEntryTests: registerEntryTests, + registerPageTests: registerPageTests, + }; +});
diff --git a/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js b/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js index 67b36ba0..9266215 100644 --- a/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js +++ b/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js
@@ -51,29 +51,72 @@ }, /** - * Helper method that validates a node matches the data for an index. + * Helper method that validates a that elements in the password list match + * the expected data. * @param {!Array<!Element>} nodes The nodes that will be checked. * @param {!Array<!Object>} passwordList The expected data. - * @param {number} index The index that should match the node and data. * @private */ - validate_: function(nodes, passwordList, index) { - var node = nodes[index]; - var passwordInfo = passwordList[index]; - assertTrue(!!node, 'Failed to get nodes[' + index + ']'); - assertTrue(!!passwordInfo, 'Failed to get passwordList[' + index + ']'); - assertEquals( - passwordInfo.loginPair.originUrl, - node.querySelector('#originUrl').textContent, - 'originUrl mismatch in nodes[' + index + ']'); - assertEquals( - passwordInfo.loginPair.username, - node.querySelector('#username').textContent, - 'username mismatch in nodes[' + index + ']'); - assertEquals( - passwordInfo.numCharactersInPassword, - node.querySelector('#password').value.length, - 'password size mismatch in nodes[' + index + ']'); + validatePasswordList: function(nodes, passwordList) { + assertEquals(passwordList.length, nodes.length); + for (var index = 0; index < passwordList.length; ++index) { + var node = nodes[index]; + var passwordInfo = passwordList[index]; + assertEquals(passwordInfo.loginPair.originUrl, + node.querySelector('#originUrl').textContent); + assertEquals(passwordInfo.loginPair.username, + node.querySelector('#username').textContent); + assertEquals(passwordInfo.numCharactersInPassword, + node.querySelector('#password').value.length); + } + }, + + /** + * Helper method that validates a that elements in the exception list match + * the expected data. + * @param {!Array<!Element>} nodes The nodes that will be checked. + * @param {!Array<!Object>} exceptionList The expected data. + * @private + */ + validateExceptionList_: function(nodes, exceptionList) { + assertEquals(exceptionList.length, nodes.length); + for (var index = 0; index < exceptionList.length; ++index) { + var node = nodes[index]; + var exception = exceptionList[index]; + assertEquals(exception, node.querySelector('#exception').textContent); + } + }, + + /** + * Skips over the template and returns all visible children of an iron-list. + * @param {!Element} ironList + * @return {!Array<!Element>} + * @private + */ + getIronListChildren_: function(ironList) { + return Polymer.dom(ironList).children.filter(function(child, i) { + return i != 0 && !child.hidden; + }); + }, + + /** + * Helper method used to create a password section for the given lists. + * @param {!Array<!chrome.passwordsPrivate.PasswordUiEntry>} passwordList + * @param {!Array<!string>} exceptionList + * @return {!Object} + * @private + */ + createPasswordsSection_: function(passwordList, exceptionList) { + // Create a passwords-section to use for testing. + var passwordsSection = document.createElement('passwords-section'); + passwordsSection.savedPasswords = passwordList; + passwordsSection.passwordExceptions = exceptionList; + document.body.appendChild(passwordsSection); + + // Allow polymer binding to finish. + Polymer.dom.flush(); + + return passwordsSection; }, }; @@ -84,9 +127,8 @@ var self = this; suite('PasswordsSection', function() { - test('doWork', function(done) { - assertEquals(self.browsePreload, document.location.href, - 'Unexpected URL loaded'); + test('verifySavedPasswordLength', function() { + assertEquals(self.browsePreload, document.location.href) var passwordList = [ self.createPasswordItem_('anotherwebsite.com', 'luigi', 1), @@ -94,28 +136,107 @@ self.createPasswordItem_('website.com', 'mario', 70) ]; - // Create a passwords-section to use for testing. - var passwordsSection = document.createElement('passwords-section'); - passwordsSection.savedPasswords = passwordList; - document.body.appendChild(passwordsSection); + var passwordSection = self.createPasswordsSection_(passwordList, []); - // TODO(hcarmona): use an event listener rather than a setTimeout(0). - window.setTimeout(function() { - // Assert that the data is passed into the iron list. If this fails, - // then other expectations will also fail. - assertEquals(passwordList, passwordsSection.$.passwordList.items, - 'Failed to pass list of passwords to iron-list'); + // Assert that the data is passed into the iron list. If this fails, + // then other expectations will also fail. + assertEquals(passwordList, passwordSection.$.passwordList.items); - var list = Polymer.dom(passwordsSection.$.passwordList); - assertTrue(!!list, 'Failed to get the password list'); - // Skip the first child because it's the template. - var listChildren = list.children.slice(1); + self.validatePasswordList( + self.getIronListChildren_(passwordSection.$.passwordList), + passwordList); + }); - self.validate_(listChildren, passwordList, 0); - self.validate_(listChildren, passwordList, 1); - self.validate_(listChildren, passwordList, 2); - done(); - }, 0); + test('verifyPasswordExceptions', function() { + var exceptionList = [ + 'docs.google.com', + 'mail.com', + 'google.com', + 'inbox.google.com', + 'maps.google.com', + 'plus.google.com', + ]; + + var passwordsSection = self.createPasswordsSection_([], exceptionList); + + // Assert that the data is passed into the iron list. If this fails, + // then other expectations will also fail. + assertEquals(exceptionList, + passwordsSection.$.passwordExceptionsList.items); + + self.validateExceptionList_( + self.getIronListChildren_(passwordsSection.$.passwordExceptionsList), + exceptionList); + }); + + // Test verifies that removing an exception will update the elements. + test('verifyPasswordExceptionRemove', function() { + var exceptionList = [ + 'docs.google.com', + 'mail.com', + 'google.com', + 'inbox.google.com', + 'maps.google.com', + 'plus.google.com', + ]; + + var passwordsSection = self.createPasswordsSection_([], exceptionList); + + self.validateExceptionList_( + self.getIronListChildren_(passwordsSection.$.passwordExceptionsList), + exceptionList); + + // Simulate 'mail.com' being removed from the list. + passwordsSection.splice('passwordExceptions', 1, 1); + assertEquals(-1, passwordsSection.passwordExceptions.indexOf('mail.com')); + assertEquals(-1, exceptionList.indexOf('mail.com')); + Polymer.dom.flush(); + + self.validateExceptionList_( + self.getIronListChildren_(passwordsSection.$.passwordExceptionsList), + exceptionList); + }); + + // Test verifies that pressing the 'remove' button will trigger a remove + // event. Does not actually remove any exceptions. + test('verifyPasswordRemoveButton', function(done) { + var exceptionList = [ + 'docs.google.com', + 'mail.com', + 'google.com', + 'inbox.google.com', + 'maps.google.com', + 'plus.google.com', + ]; + + var passwordsSection = self.createPasswordsSection_([], exceptionList); + + var exceptions = + self.getIronListChildren_(passwordsSection.$.passwordExceptionsList); + + // The index of the button currently being checked. + var index = 0; + + var clickRemoveButton = function() { + exceptions[index].querySelector('#removeButton').click(); + }; + + // Listen for the remove event. If this event isn't received, the test + // will time out and fail. + passwordsSection.addEventListener('remove-password-exception', + function(event) { + // Verify that the event matches the expected value. + assertTrue(index < exceptionList.length); + assertEquals(exceptionList[index], event.detail); + + if (++index < exceptionList.length) + clickRemoveButton(); // Click 'remove' on all passwords, one by one. + else + done(); + }); + + // Start removing. + clickRemoveButton(); }); });
diff --git a/chrome/test/data/webui/settings/settings_subpage_browsertest.js b/chrome/test/data/webui/settings/settings_subpage_browsertest.js index 4582e604..13ce62b6 100644 --- a/chrome/test/data/webui/settings/settings_subpage_browsertest.js +++ b/chrome/test/data/webui/settings/settings_subpage_browsertest.js
@@ -98,8 +98,8 @@ includePage: function(id) { if (cr.isChromeOS) return id != 'people' && id != 'defaultBrowser'; - return id != 'internet' && id != 'users' && id != 'dateTime' && - id != 'bluetooth' && id != 'a11y'; + return id != 'internet' && id != 'users' && id != 'device' && + id != 'dateTime' && id != 'bluetooth' && id != 'a11y'; }, }; @@ -111,7 +111,8 @@ 'appearance', 'onStartup', 'search', - 'defaultBrowser' + 'defaultBrowser', + 'device' ]; SettingsSubPageBrowserTest.call(this, 'basic', subPages);
diff --git a/chrome/test/data/webui/settings/site_settings_category_tests.js b/chrome/test/data/webui/settings/site_settings_category_tests.js index c82f668..ad74790 100644 --- a/chrome/test/data/webui/settings/site_settings_category_tests.js +++ b/chrome/test/data/webui/settings/site_settings_category_tests.js
@@ -40,6 +40,17 @@ // Import necessary html before running suite. suiteSetup(function() { + cr.define('settings_test', function() { + var siteSettingsCategoryOptions = { + /** + * True if property changes should fire events for testing purposes. + * @type {boolean} + */ + notifyPropertyChangesForTest: true, + }; + return {siteSettingsCategoryOptions: siteSettingsCategoryOptions}; + }); + return PolymerTest.importHtml( 'chrome://md-settings/site_settings/site_settings_category.html'); }); @@ -51,18 +62,50 @@ document.body.appendChild(testElement); }); - test('categoryEnabled correctly represents prefs', function() { + /** + * Returns a promise that resolves once the selected item is updated. + * @param {function()} action is executed after the listener is set up. + * @return {!Promise} a Promise fulfilled when the selected item changes. + */ + function runAndResolveWhenCategoryEnabledChanged(action) { + return new Promise(function(resolve, reject) { + var handler = function() { + testElement.removeEventListener( + 'category-enabled-changed', handler); + resolve(); + }; + testElement.addEventListener('category-enabled-changed', handler); + action(); + }); + } + + test('categoryEnabled correctly represents prefs (enabled)', function() { testElement.category = settings.ContentSettingsTypes.GEOLOCATION; - testElement.prefs = prefsLocationEnabled; - assertTrue(testElement.categoryEnabled); - MockInteractions.tap(testElement.$.toggle); - assertFalse(testElement.categoryEnabled); + return runAndResolveWhenCategoryEnabledChanged(function() { + testElement.prefs = prefsLocationEnabled; + }).then(function() { + assertTrue(testElement.categoryEnabled); + MockInteractions.tap(testElement.$.toggle); + assertFalse(testElement.categoryEnabled); + }); + }); - testElement.prefs = prefsLocationDisabled; - assertFalse(testElement.categoryEnabled); - MockInteractions.tap(testElement.$.toggle); - assertTrue(testElement.categoryEnabled); + test('categoryEnabled correctly represents prefs (disabled)', function() { + testElement.category = settings.ContentSettingsTypes.GEOLOCATION; + + // In order for the 'change' event to trigger, the value monitored needs + // to actually change (the event is not sent otherwise). Therefore, + // ensure the initial state of enabledness is opposite of what we expect + // it to end at. + testElement.categoryEnabled = true; + return runAndResolveWhenCategoryEnabledChanged(function() { + testElement.prefs = prefsLocationDisabled; + }).then(function() { + assertFalse(testElement.categoryEnabled); + MockInteractions.tap(testElement.$.toggle); + assertTrue(testElement.categoryEnabled); + }); }); test('basic category tests', function() {
diff --git a/chrome/test/data/webui/splitter_test.html b/chrome/test/data/webui/splitter_test.html new file mode 100644 index 0000000..9d4a567 --- /dev/null +++ b/chrome/test/data/webui/splitter_test.html
@@ -0,0 +1,22 @@ +<!doctype html> +<html> +<body> +<div></div> +<div id="splitter"></div> +<div></div> +<script> +function testSplitter_IgnoresRightMouse() { + var splitter = document.getElementById('splitter'); + cr.ui.decorate(splitter, cr.ui.Splitter); + + var downRight = new MouseEvent('mousedown', {button: 1, cancelable: true}); + assertTrue(splitter.dispatchEvent(downRight)); + assertFalse(downRight.defaultPrevented); + + var downLeft = new MouseEvent('mousedown', {button: 0, cancelable: true}); + assertFalse(splitter.dispatchEvent(downLeft)); + assertTrue(downLeft.defaultPrevented); +} +</script> +</body> +</html>
diff --git a/chrome/test/data/webui/webui_resource_browsertest.cc b/chrome/test/data/webui/webui_resource_browsertest.cc index 3f376b4..28b0a55e 100644 --- a/chrome/test/data/webui/webui_resource_browsertest.cc +++ b/chrome/test/data/webui/webui_resource_browsertest.cc
@@ -218,3 +218,10 @@ AddLibrary(IDR_WEBUI_JS_CR_UI_MENU); LoadFile(base::FilePath(FILE_PATH_LITERAL("menu_button_test.html"))); } + +IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTest, SplitterTest) { + AddLibrary(IDR_WEBUI_JS_CR); + AddLibrary(IDR_WEBUI_JS_CR_UI); + AddLibrary(IDR_WEBUI_JS_CR_UI_SPLITTER); + LoadFile(base::FilePath(FILE_PATH_LITERAL("splitter_test.html"))); +}
diff --git a/chromecast/android/BUILD.gn b/chromecast/android/BUILD.gn index 7c59b7d4..1c0974c 100644 --- a/chromecast/android/BUILD.gn +++ b/chromecast/android/BUILD.gn
@@ -7,20 +7,27 @@ # These targets shall only be referenced on Android builds. assert(is_android) +# This source_set should only contain headers for internal code. These must be +# built separately from public stub implementations of this code to keep the +# dependency tree clean. +source_set("platform_jni_loader") { + sources = [ + "platform_jni_loader.h", + ] +} + # GYP target: chromecast.gyp:libcast_shell_android shared_library("libcast_shell_android") { sources = [ + "//chromecast/app/android/cast_jni_loader.cc", "cast_jni_registrar.cc", "cast_jni_registrar.h", "cast_metrics_helper_android.cc", "cast_metrics_helper_android.h", - "platform_jni_loader.h", - - # TODO(slan): Move this into this folder. - "//chromecast/app/android/cast_jni_loader.cc", ] deps = [ + ":platform_jni_loader", "//base", "//chromecast:cast_shell_common", "//chromecast/app",
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc index 12a47548..5f6596a2 100644 --- a/chromecast/browser/cast_content_browser_client.cc +++ b/chromecast/browser/cast_content_browser_client.cc
@@ -52,7 +52,7 @@ #include "ui/gl/gl_switches.h" #if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) -#include "chromecast/media/mojo/cast_mojo_media_client.h" +#include "chromecast/browser/media/cast_mojo_media_client.h" // nogncheck because of conditional dependency. #include "media/mojo/services/mojo_media_application.h" // nogncheck #endif // ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS @@ -67,6 +67,19 @@ namespace chromecast { namespace shell { +namespace { +#if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) +static scoped_ptr<mojo::ShellClient> CreateCastMojoMediaApplication( + CastContentBrowserClient* browser_client) { + scoped_ptr<::media::MojoMediaClient> mojo_media_client( + new media::CastMojoMediaClient( + browser_client->CreateCmaMediaPipelineClient())); + return scoped_ptr<mojo::ShellClient>( + new ::media::MojoMediaApplication(std::move(mojo_media_client))); +} +#endif // ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS +} // namespace + CastContentBrowserClient::CastContentBrowserClient() : url_request_context_factory_(new URLRequestContextFactory()) { } @@ -360,8 +373,7 @@ #if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) apps->insert(std::make_pair( GURL("mojo:media"), - base::Bind(::media::MojoMediaApplication::CreateAppWithClient, - base::Bind(&media::CastMojoMediaClient::Create)))); + base::Bind(&CreateCastMojoMediaApplication, base::Unretained(this)))); #endif }
diff --git a/chromecast/browser/cast_permission_manager.cc b/chromecast/browser/cast_permission_manager.cc index 0595f35..dba56142 100644 --- a/chromecast/browser/cast_permission_manager.cc +++ b/chromecast/browser/cast_permission_manager.cc
@@ -22,7 +22,6 @@ content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& origin, - bool user_gesture, const base::Callback<void(content::PermissionStatus)>& callback) { LOG(INFO) << __FUNCTION__ << ": " << static_cast<int>(permission); callback.Run(content::PermissionStatus::GRANTED); @@ -33,7 +32,6 @@ const std::vector<content::PermissionType>& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<content::PermissionStatus>&)>& callback) { callback.Run(std::vector<content::PermissionStatus>(
diff --git a/chromecast/browser/cast_permission_manager.h b/chromecast/browser/cast_permission_manager.h index ae17880..432fef2 100644 --- a/chromecast/browser/cast_permission_manager.h +++ b/chromecast/browser/cast_permission_manager.h
@@ -22,13 +22,11 @@ content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(content::PermissionStatus)>& callback) override; int RequestPermissions( const std::vector<content::PermissionType>& permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<content::PermissionStatus>&)>& callback) override; void CancelPermissionRequest(int request_id) override;
diff --git a/chromecast/browser/media/BUILD.gn b/chromecast/browser/media/BUILD.gn index 5a177d5..a2d65193 100644 --- a/chromecast/browser/media/BUILD.gn +++ b/chromecast/browser/media/BUILD.gn
@@ -3,6 +3,7 @@ # found in the LICENSE file. import("//chromecast/chromecast.gni") +import("//media/media_options.gni") source_set("media") { sources = [ @@ -14,10 +15,25 @@ "media_pipeline_host.h", ] + if (mojo_media_host == "browser") { + sources += [ + "cast_mojo_media_client.cc", + "cast_mojo_media_client.h", + "cast_renderer.cc", + "cast_renderer.h", + ] + } + if (use_playready) { public_configs = [ "//chromecast:playready_config" ] } + if (mojo_media_host == "browser") { + public_deps = [ + "//media/mojo/services:application", + ] + } + deps = [ "//base", "//chromecast/base",
diff --git a/chromecast/browser/media/cast_mojo_media_client.cc b/chromecast/browser/media/cast_mojo_media_client.cc new file mode 100644 index 0000000..980492e --- /dev/null +++ b/chromecast/browser/media/cast_mojo_media_client.cc
@@ -0,0 +1,52 @@ +// 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 "chromecast/browser/media/cast_mojo_media_client.h" + +#include "chromecast/browser/media/cast_renderer.h" +#include "chromecast/browser/media/cma_media_pipeline_client.h" + +namespace { +class CastRendererFactory : public media::RendererFactory { + public: + CastRendererFactory( + scoped_refptr<chromecast::media::CmaMediaPipelineClient> pipeline_client, + const scoped_refptr<media::MediaLog>& media_log) + : pipeline_client_(pipeline_client), media_log_(media_log) {} + ~CastRendererFactory() final {} + + scoped_ptr<media::Renderer> CreateRenderer( + const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, + const scoped_refptr<base::TaskRunner>& worker_task_runner, + media::AudioRendererSink* audio_renderer_sink, + media::VideoRendererSink* video_renderer_sink, + const media::RequestSurfaceCB& request_surface_cb) final { + DCHECK(!audio_renderer_sink && !video_renderer_sink); + return make_scoped_ptr(new chromecast::media::CastRenderer( + pipeline_client_, media_task_runner)); + } + + private: + scoped_refptr<chromecast::media::CmaMediaPipelineClient> pipeline_client_; + scoped_refptr<media::MediaLog> media_log_; + DISALLOW_COPY_AND_ASSIGN(CastRendererFactory); +}; +} // namespace + +namespace chromecast { +namespace media { + +CastMojoMediaClient::CastMojoMediaClient( + scoped_refptr<CmaMediaPipelineClient> pipeline_client) + : pipeline_client_(pipeline_client) {} + +CastMojoMediaClient::~CastMojoMediaClient() {} + +scoped_ptr<::media::RendererFactory> CastMojoMediaClient::CreateRendererFactory( + const scoped_refptr<::media::MediaLog>& media_log) { + return make_scoped_ptr(new CastRendererFactory(pipeline_client_, media_log)); +} + +} // namespace media +} // namespace chromecast
diff --git a/chromecast/browser/media/cast_mojo_media_client.h b/chromecast/browser/media/cast_mojo_media_client.h new file mode 100644 index 0000000..504d713 --- /dev/null +++ b/chromecast/browser/media/cast_mojo_media_client.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 CHROMECAST_BROWSER_MEDIA_CAST_MOJO_MEDIA_CLIENT_H_ +#define CHROMECAST_BROWSER_MEDIA_CAST_MOJO_MEDIA_CLIENT_H_ + +#include "media/mojo/services/mojo_media_client.h" + +namespace chromecast { +namespace media { + +class CmaMediaPipelineClient; + +class CastMojoMediaClient : public ::media::MojoMediaClient { + public: + CastMojoMediaClient(scoped_refptr<CmaMediaPipelineClient> pipeline_client); + ~CastMojoMediaClient() override; + + // MojoMediaClient overrides. + scoped_ptr<::media::RendererFactory> CreateRendererFactory( + const scoped_refptr<::media::MediaLog>& media_log) override; + + private: + scoped_refptr<CmaMediaPipelineClient> pipeline_client_; + DISALLOW_COPY_AND_ASSIGN(CastMojoMediaClient); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_BROWSER_MEDIA_CAST_MOJO_MEDIA_CLIENT_H_
diff --git a/chromecast/browser/media/cast_renderer.cc b/chromecast/browser/media/cast_renderer.cc new file mode 100644 index 0000000..6278486d --- /dev/null +++ b/chromecast/browser/media/cast_renderer.cc
@@ -0,0 +1,152 @@ +// 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 "chromecast/browser/media/cast_renderer.h" + +#include "base/bind.h" +#include "base/single_thread_task_runner.h" +#include "chromecast/base/task_runner_impl.h" +#include "chromecast/browser/media/cma_media_pipeline_client.h" +#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" +#include "chromecast/media/cma/base/cma_logging.h" +#include "chromecast/media/cma/base/demuxer_stream_adapter.h" +#include "chromecast/media/cma/pipeline/av_pipeline_client.h" +#include "chromecast/media/cma/pipeline/media_pipeline_impl.h" +#include "chromecast/public/media/media_pipeline_backend.h" +#include "chromecast/public/media/media_pipeline_device_params.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/demuxer_stream.h" +#include "media/base/media_log.h" + +namespace chromecast { +namespace media { + +namespace { +// Maximum difference between audio frame PTS and video frame PTS +// for frames read from the DemuxerStream. +const base::TimeDelta kMaxDeltaFetcher(base::TimeDelta::FromMilliseconds(2000)); +} // namespace + +CastRenderer::CastRenderer( + const scoped_refptr<CmaMediaPipelineClient> pipeline_client, + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) + : pipeline_client_(pipeline_client), + task_runner_(task_runner), + media_task_runner_factory_( + new BalancedMediaTaskRunnerFactory(kMaxDeltaFetcher)) { + CMALOG(kLogControl) << __FUNCTION__ << ": " << this; +} + +CastRenderer::~CastRenderer() { + CMALOG(kLogControl) << __FUNCTION__ << ": " << this; + DCHECK(task_runner_->BelongsToCurrentThread()); +} + +void CastRenderer::Initialize( + ::media::DemuxerStreamProvider* demuxer_stream_provider, + const ::media::PipelineStatusCB& init_cb, + const ::media::StatisticsCB& statistics_cb, + const ::media::BufferingStateCB& buffering_state_cb, + const base::Closure& ended_cb, + const ::media::PipelineStatusCB& error_cb, + const base::Closure& waiting_for_decryption_key_cb) { + CMALOG(kLogControl) << __FUNCTION__ << ": " << this; + DCHECK(task_runner_->BelongsToCurrentThread()); + + // Create pipeline backend. + backend_task_runner_.reset(new TaskRunnerImpl()); + // TODO(erickung): crbug.com/443956. Need to provide right LoadType. + LoadType load_type = kLoadTypeMediaSource; + MediaPipelineDeviceParams::MediaSyncType sync_type = + (load_type == kLoadTypeMediaStream) + ? MediaPipelineDeviceParams::kModeIgnorePts + : MediaPipelineDeviceParams::kModeSyncPts; + MediaPipelineDeviceParams params(sync_type, backend_task_runner_.get()); + scoped_ptr<MediaPipelineBackend> backend( + pipeline_client_->CreateMediaPipelineBackend(params)); + + // Create pipeline. + MediaPipelineClient pipeline_client; + pipeline_client.error_cb = error_cb; + pipeline_client.buffering_state_cb = buffering_state_cb; + pipeline_client.pipeline_backend_created_cb = base::Bind( + &CmaMediaPipelineClient::OnMediaPipelineBackendCreated, pipeline_client_); + pipeline_client.pipeline_backend_destroyed_cb = + base::Bind(&CmaMediaPipelineClient::OnMediaPipelineBackendDestroyed, + pipeline_client_); + pipeline_.reset(new MediaPipelineImpl); + pipeline_->SetClient(pipeline_client); + pipeline_->Initialize(load_type, std::move(backend)); + + // Initialize audio. + ::media::DemuxerStream* audio_stream = + demuxer_stream_provider->GetStream(::media::DemuxerStream::AUDIO); + if (audio_stream) { + AvPipelineClient audio_client; + audio_client.wait_for_key_cb = waiting_for_decryption_key_cb; + audio_client.eos_cb = ended_cb; + audio_client.playback_error_cb = error_cb; + audio_client.statistics_cb = statistics_cb; + scoped_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter( + task_runner_, media_task_runner_factory_, audio_stream)); + ::media::PipelineStatus status = + pipeline_->InitializeAudio(audio_stream->audio_decoder_config(), + audio_client, std::move(frame_provider)); + if (status != ::media::PIPELINE_OK) { + init_cb.Run(status); + return; + } + } + // Initialize video. + ::media::DemuxerStream* video_stream = + demuxer_stream_provider->GetStream(::media::DemuxerStream::VIDEO); + if (video_stream) { + NOTIMPLEMENTED(); + } + init_cb.Run(::media::PIPELINE_OK); +} + +void CastRenderer::SetCdm(::media::CdmContext* cdm_context, + const ::media::CdmAttachedCB& cdm_attached_cb) { + DCHECK(task_runner_->BelongsToCurrentThread()); + NOTIMPLEMENTED(); +} + +void CastRenderer::Flush(const base::Closure& flush_cb) { + DCHECK(task_runner_->BelongsToCurrentThread()); + pipeline_->Flush(flush_cb); +} + +void CastRenderer::StartPlayingFrom(base::TimeDelta time) { + DCHECK(task_runner_->BelongsToCurrentThread()); + pipeline_->StartPlayingFrom(time); +} + +void CastRenderer::SetPlaybackRate(double playback_rate) { + DCHECK(task_runner_->BelongsToCurrentThread()); + pipeline_->SetPlaybackRate(playback_rate); +} + +void CastRenderer::SetVolume(float volume) { + DCHECK(task_runner_->BelongsToCurrentThread()); + pipeline_->SetVolume(volume); +} + +base::TimeDelta CastRenderer::GetMediaTime() { + DCHECK(task_runner_->BelongsToCurrentThread()); + return pipeline_->GetMediaTime(); +} + +bool CastRenderer::HasAudio() { + DCHECK(task_runner_->BelongsToCurrentThread()); + return pipeline_->HasAudio(); +} + +bool CastRenderer::HasVideo() { + DCHECK(task_runner_->BelongsToCurrentThread()); + return pipeline_->HasVideo(); +} + +} // namespace media +} // namespace chromecast
diff --git a/chromecast/browser/media/cast_renderer.h b/chromecast/browser/media/cast_renderer.h new file mode 100644 index 0000000..1e83951 --- /dev/null +++ b/chromecast/browser/media/cast_renderer.h
@@ -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 "media/base/renderer.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace media { +class MediaLog; +} // namespace media + +namespace chromecast { +class TaskRunnerImpl; + +namespace media { +class BalancedMediaTaskRunnerFactory; +class CmaMediaPipelineClient; +class MediaPipelineImpl; + +class CastRenderer : public ::media::Renderer { + public: + CastRenderer(const scoped_refptr<CmaMediaPipelineClient> pipeline_client, + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); + ~CastRenderer() final; + + // ::media::Renderer implementation. + void Initialize(::media::DemuxerStreamProvider* demuxer_stream_provider, + const ::media::PipelineStatusCB& init_cb, + const ::media::StatisticsCB& statistics_cb, + const ::media::BufferingStateCB& buffering_state_cb, + const base::Closure& ended_cb, + const ::media::PipelineStatusCB& error_cb, + const base::Closure& waiting_for_decryption_key_cb) final; + void SetCdm(::media::CdmContext* cdm_context, + const ::media::CdmAttachedCB& cdm_attached_cb) final; + void Flush(const base::Closure& flush_cb) final; + void StartPlayingFrom(base::TimeDelta time) final; + void SetPlaybackRate(double playback_rate) final; + void SetVolume(float volume) final; + base::TimeDelta GetMediaTime() final; + bool HasAudio() final; + bool HasVideo() final; + + private: + scoped_refptr<CmaMediaPipelineClient> pipeline_client_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + scoped_refptr<BalancedMediaTaskRunnerFactory> media_task_runner_factory_; + scoped_ptr<TaskRunnerImpl> backend_task_runner_; + scoped_ptr<MediaPipelineImpl> pipeline_; + DISALLOW_COPY_AND_ASSIGN(CastRenderer); +}; + +} // namespace media +} // namespace chromecast
diff --git a/chromecast/common/cast_content_client.cc b/chromecast/common/cast_content_client.cc index 2332ba66..302be4e 100644 --- a/chromecast/common/cast_content_client.cc +++ b/chromecast/common/cast_content_client.cc
@@ -81,6 +81,7 @@ void CastContentClient::AddAdditionalSchemes( std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* savable_schemes) { standard_schemes->push_back(kChromeResourceSchemeWithType); }
diff --git a/chromecast/common/cast_content_client.h b/chromecast/common/cast_content_client.h index f77a610614..073debc 100644 --- a/chromecast/common/cast_content_client.h +++ b/chromecast/common/cast_content_client.h
@@ -18,6 +18,7 @@ // content::ContentClient implementation: void AddAdditionalSchemes(std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* saveable_shemes) override; std::string GetUserAgent() const override; base::string16 GetLocalizedString(int message_id) const override;
diff --git a/chromecast/media/BUILD.gn b/chromecast/media/BUILD.gn index 57093df5..02e14e8 100644 --- a/chromecast/media/BUILD.gn +++ b/chromecast/media/BUILD.gn
@@ -3,7 +3,6 @@ # found in the LICENSE file. import("//chromecast/chromecast.gni") -import("//media/media_options.gni") import("//testing/test.gni") group("media") { @@ -13,10 +12,6 @@ "//chromecast/media/cdm", "//chromecast/media/cma", ] - - if (enable_mojo_media == "browser") { - public_deps += [ "//chromecast/media/mojo" ] - } } test("cast_media_unittests") {
diff --git a/chromecast/media/cma/pipeline/media_pipeline_impl.cc b/chromecast/media/cma/pipeline/media_pipeline_impl.cc index da35477a..2ae9c51 100644 --- a/chromecast/media/cma/pipeline/media_pipeline_impl.cc +++ b/chromecast/media/cma/pipeline/media_pipeline_impl.cc
@@ -61,12 +61,10 @@ MediaPipelineImpl::MediaPipelineImpl() : cdm_(nullptr), + backend_state_(BACKEND_STATE_UNINITIALIZED), + playback_rate_(1.0f), audio_decoder_(nullptr), video_decoder_(nullptr), - backend_initialized_(false), - paused_(false), - target_playback_rate_(1.0f), - backend_started_(false), pending_time_update_task_(false), statistics_rolling_counter_(0), weak_factory_(this) { @@ -119,7 +117,6 @@ void MediaPipelineImpl::SetClient(const MediaPipelineClient& client) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!client.error_cb.is_null()); - DCHECK(!client.time_update_cb.is_null()); DCHECK(!client.buffering_state_cb.is_null()); DCHECK(!client.pipeline_backend_created_cb.is_null()); DCHECK(!client.pipeline_backend_destroyed_cb.is_null()); @@ -185,16 +182,14 @@ DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(audio_pipeline_ || video_pipeline_); DCHECK(!pending_flush_task_); - // When starting, we always enter the "playing" state (not paused). - paused_ = false; - // Lazy initialise - if (!backend_initialized_) { - backend_initialized_ = media_pipeline_backend_->Initialize(); - if (!backend_initialized_) { + // Lazy initialize. + if (backend_state_ == BACKEND_STATE_UNINITIALIZED) { + if (!media_pipeline_backend_->Initialize()) { OnError(::media::PIPELINE_ERROR_ABORT); return; } + backend_state_ = BACKEND_STATE_INITIALIZED; } // Start the backend. @@ -202,9 +197,9 @@ OnError(::media::PIPELINE_ERROR_ABORT); return; } + backend_state_ = BACKEND_STATE_PLAYING; // Enable time updates. - backend_started_ = true; statistics_rolling_counter_ = 0; if (!pending_time_update_task_) { pending_time_update_task_ = true; @@ -236,10 +231,11 @@ void MediaPipelineImpl::Flush(const base::Closure& flush_cb) { CMALOG(kLogControl) << __FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK((backend_state_ == BACKEND_STATE_PLAYING) || + (backend_state_ == BACKEND_STATE_PAUSED)); DCHECK(audio_pipeline_ || video_pipeline_); DCHECK(!pending_flush_task_); - backend_started_ = false; buffering_controller_->Reset(); // 1. Stop both the audio and video pipeline so that they stop feeding @@ -252,6 +248,7 @@ // 2. Stop the backend, so that the backend won't push their pending buffer, // which may be invalidated later, to hardware. (b/25342604) CHECK(media_pipeline_backend_->Stop()); + backend_state_ = BACKEND_STATE_INITIALIZED; // 3. Flush both the audio and video pipeline. This will flush the frame // provider and invalidate all the unreleased buffers. @@ -278,7 +275,6 @@ // audio/video pipelines. This will ensure A/V Flush won't happen in // stopped state. pending_flush_task_.reset(); - backend_started_ = false; // Stop both the audio and video pipeline. if (audio_pipeline_) @@ -291,26 +287,28 @@ video_pipeline_ = nullptr; audio_decoder_.reset(); media_pipeline_backend_.reset(); + backend_state_ = BACKEND_STATE_UNINITIALIZED; } void MediaPipelineImpl::SetPlaybackRate(double rate) { CMALOG(kLogControl) << __FUNCTION__ << " rate=" << rate; DCHECK(thread_checker_.CalledOnValidThread()); - target_playback_rate_ = rate; - if (!backend_started_) - return; + DCHECK((backend_state_ == BACKEND_STATE_PLAYING) || + (backend_state_ == BACKEND_STATE_PAUSED)); + + playback_rate_ = rate; if (buffering_controller_ && buffering_controller_->IsBuffering()) return; if (rate != 0.0f) { media_pipeline_backend_->SetPlaybackRate(rate); - if (paused_) { - paused_ = false; + if (backend_state_ == BACKEND_STATE_PAUSED) { media_pipeline_backend_->Resume(); + backend_state_ = BACKEND_STATE_PLAYING; } - } else if (!paused_) { - paused_ = true; + } else if (backend_state_ == BACKEND_STATE_PLAYING) { media_pipeline_backend_->Pause(); + backend_state_ = BACKEND_STATE_PAUSED; } } @@ -321,6 +319,11 @@ audio_pipeline_->SetVolume(volume); } +base::TimeDelta MediaPipelineImpl::GetMediaTime() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return last_media_time_; +} + bool MediaPipelineImpl::HasAudio() const { DCHECK(thread_checker_.CalledOnValidThread()); return audio_pipeline_ != nullptr; @@ -355,33 +358,37 @@ void MediaPipelineImpl::OnBufferingNotification(bool is_buffering) { CMALOG(kLogControl) << __FUNCTION__ << " is_buffering=" << is_buffering; DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK((backend_state_ == BACKEND_STATE_PLAYING) || + (backend_state_ == BACKEND_STATE_PAUSED)); DCHECK(buffering_controller_); + DCHECK_EQ(is_buffering, buffering_controller_->IsBuffering()); if (!client_.buffering_state_cb.is_null()) { - ::media::BufferingState buffering_state = - is_buffering ? ::media::BUFFERING_HAVE_NOTHING - : ::media::BUFFERING_HAVE_ENOUGH; - client_.buffering_state_cb.Run(buffering_state); + if (is_buffering) { + // TODO(alokp): WebMediaPlayerImpl currently only handles HAVE_ENOUGH. + // See WebMediaPlayerImpl::OnPipelineBufferingStateChanged, + // http://crbug.com/144683. + LOG(WARNING) << "Ignoring buffering notification."; + } else { + client_.buffering_state_cb.Run(::media::BUFFERING_HAVE_ENOUGH); + } } - if (!is_buffering) { - DCHECK(!buffering_controller_->IsBuffering()); + if (is_buffering && (backend_state_ == BACKEND_STATE_PLAYING)) { + media_pipeline_backend_->Pause(); + backend_state_ = BACKEND_STATE_PAUSED; + } else if (!is_buffering && (backend_state_ == BACKEND_STATE_PAUSED)) { // Once we finish buffering, we need to honour the desired playback rate // (rather than just resuming). This way, if playback was paused while // buffering, it will remain paused rather than incorrectly resuming. - SetPlaybackRate(target_playback_rate_); - return; - } - // Do not consume data in a rebuffering phase. - if (!paused_) { - paused_ = true; - media_pipeline_backend_->Pause(); + SetPlaybackRate(playback_rate_); } } void MediaPipelineImpl::UpdateMediaTime() { pending_time_update_task_ = false; - if (!backend_started_) + if ((backend_state_ != BACKEND_STATE_PLAYING) && + (backend_state_ != BACKEND_STATE_PAUSED)) return; if (statistics_rolling_counter_ == 0) {
diff --git a/chromecast/media/cma/pipeline/media_pipeline_impl.h b/chromecast/media/cma/pipeline/media_pipeline_impl.h index 1231cc3..6773643 100644 --- a/chromecast/media/cma/pipeline/media_pipeline_impl.h +++ b/chromecast/media/cma/pipeline/media_pipeline_impl.h
@@ -59,12 +59,19 @@ void Stop(); void SetPlaybackRate(double playback_rate); void SetVolume(float volume); + base::TimeDelta GetMediaTime() const; bool HasAudio() const; bool HasVideo() const; void SetCdm(BrowserCdmCast* cdm); private: + enum BackendState { + BACKEND_STATE_UNINITIALIZED, + BACKEND_STATE_INITIALIZED, + BACKEND_STATE_PLAYING, + BACKEND_STATE_PAUSED + }; struct FlushTask; void OnFlushDone(bool is_audio_stream); @@ -81,22 +88,20 @@ BrowserCdmCast* cdm_; // Interface with the underlying hardware media pipeline. + BackendState backend_state_; + // Playback rate set by the upper layer. + // Cached here because CMA pipeline backend does not support rate == 0, + // which is emulated by pausing the backend. + float playback_rate_; scoped_ptr<MediaPipelineBackend> media_pipeline_backend_; scoped_ptr<AudioDecoderSoftwareWrapper> audio_decoder_; MediaPipelineBackend::VideoDecoder* video_decoder_; - bool backend_initialized_; scoped_ptr<AudioPipelineImpl> audio_pipeline_; scoped_ptr<VideoPipelineImpl> video_pipeline_; scoped_ptr<FlushTask> pending_flush_task_; - // Whether or not the backend is currently paused. - bool paused_; - // Playback rate set by the upper layer. - float target_playback_rate_; - // The media time is retrieved at regular intervals. - bool backend_started_; // Whether or not the backend is playing/paused. bool pending_time_update_task_; base::TimeDelta last_media_time_;
diff --git a/chromecast/media/mojo/BUILD.gn b/chromecast/media/mojo/BUILD.gn deleted file mode 100644 index f8e8ec14..0000000 --- a/chromecast/media/mojo/BUILD.gn +++ /dev/null
@@ -1,19 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//media/media_options.gni") - -# We support mojo media service hosted in the browser process only. -assert(enable_mojo_media == "browser") - -source_set("mojo") { - sources = [ - "cast_mojo_media_client.cc", - "cast_mojo_media_client.h", - ] - - public_deps = [ - "//media/mojo/services:application", - ] -}
diff --git a/chromecast/media/mojo/cast_mojo_media_client.cc b/chromecast/media/mojo/cast_mojo_media_client.cc deleted file mode 100644 index 3a16021..0000000 --- a/chromecast/media/mojo/cast_mojo_media_client.cc +++ /dev/null
@@ -1,27 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chromecast/media/mojo/cast_mojo_media_client.h" - -namespace chromecast { -namespace media { - -CastMojoMediaClient::CastMojoMediaClient() {} - -CastMojoMediaClient::~CastMojoMediaClient() {} - -// static -scoped_ptr<::media::MojoMediaClient> CastMojoMediaClient::Create() { - return scoped_ptr<::media::MojoMediaClient>(new CastMojoMediaClient()); -} - -void CastMojoMediaClient::Initialize() {} - -scoped_ptr<::media::RendererFactory> CastMojoMediaClient::CreateRendererFactory( - const scoped_refptr<::media::MediaLog>& /* media_log */) { - return scoped_ptr<::media::RendererFactory>(); -} - -} // namespace media -} // namespace chromecast
diff --git a/chromecast/media/mojo/cast_mojo_media_client.h b/chromecast/media/mojo/cast_mojo_media_client.h deleted file mode 100644 index 1f7fcde0..0000000 --- a/chromecast/media/mojo/cast_mojo_media_client.h +++ /dev/null
@@ -1,32 +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 CHROMECAST_MEDIA_MOJO_CAST_MOJO_MEDIA_CLIENT_H_ -#define CHROMECAST_MEDIA_MOJO_CAST_MOJO_MEDIA_CLIENT_H_ - -#include "media/mojo/services/mojo_media_client.h" - -namespace chromecast { -namespace media { - -class CastMojoMediaClient : public ::media::MojoMediaClient { - public: - CastMojoMediaClient(); - ~CastMojoMediaClient() override; - - static scoped_ptr<::media::MojoMediaClient> Create(); - - // MojoMediaClient overrides. - void Initialize() override; - scoped_ptr<::media::RendererFactory> CreateRendererFactory( - const scoped_refptr<::media::MediaLog>& media_log) override; - - private: - DISALLOW_COPY_AND_ASSIGN(CastMojoMediaClient); -}; - -} // namespace media -} // namespace chromecast - -#endif // CHROMECAST_MEDIA_MOJO_CAST_MOJO_MEDIA_CLIENT_H_
diff --git a/chromecast/renderer/media/cma_renderer.cc b/chromecast/renderer/media/cma_renderer.cc index 088ebb8d..41ef271 100644 --- a/chromecast/renderer/media/cma_renderer.cc +++ b/chromecast/renderer/media/cma_renderer.cc
@@ -431,17 +431,6 @@ ::media::BufferingState buffering_state) { CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_ << ", buffering=" << buffering_state; - // TODO(gunsch): WebMediaPlayerImpl currently only handles HAVE_ENOUGH while - // playing. See OnPipelineBufferingStateChanged, http://crbug.com/144683. - if (state_ != kPlaying) { - LOG(WARNING) << "Ignoring buffering notification in state: " << state_; - return; - } - if (buffering_state != ::media::BUFFERING_HAVE_ENOUGH) { - LOG(WARNING) << "Ignoring buffering notification during playing: " - << buffering_state; - return; - } buffering_state_cb_.Run(buffering_state); }
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index cbd61100..851dc39 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -7934.0.0 \ No newline at end of file +7943.0.0 \ No newline at end of file
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc index 7958d31c..b148ac7 100644 --- a/chromeos/chromeos_switches.cc +++ b/chromeos/chromeos_switches.cc
@@ -12,6 +12,11 @@ namespace chromeos { namespace switches { +// If this flag is passed, failed policy fetches will not cause profile +// initialization to fail. This is useful for tests because it means that +// tests don't have to mock out the policy infrastructure. +const char kAllowFailedPolicyFetchForTest[] = + "allow-failed-policy-fetch-for-test"; // Allows remote attestation (RA) in dev mode for testing purpose. Usually RA // is disabled in dev mode because it will always fail. However, there are cases
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h index b4782ec..87e7a4c 100644 --- a/chromeos/chromeos_switches.h +++ b/chromeos/chromeos_switches.h
@@ -21,6 +21,7 @@ // see chromeos::LoginUtil::GetOffTheRecordCommandLine().) // Please keep alphabetized. +CHROMEOS_EXPORT extern const char kAllowFailedPolicyFetchForTest[]; CHROMEOS_EXPORT extern const char kAllowRAInDevMode[]; CHROMEOS_EXPORT extern const char kAppOemManifestFile[]; CHROMEOS_EXPORT extern const char kArtifactsDir[];
diff --git a/cloud_print/BRANDING b/cloud_print/BRANDING deleted file mode 100644 index 0ab41da..0000000 --- a/cloud_print/BRANDING +++ /dev/null
@@ -1,3 +0,0 @@ -PRODUCT_FULLNAME=Google Cloud Print -PRODUCT_SHORTNAME=GCP -PRODUCT_DESCRIPTION=Google Cloud Print (GCP) enables any app on any device to print to any cloud-connected printer.
diff --git a/cloud_print/BUILD.gn b/cloud_print/BUILD.gn deleted file mode 100644 index af1eca8f..0000000 --- a/cloud_print/BUILD.gn +++ /dev/null
@@ -1,46 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//testing/test.gni") - -group("cloud_print") { - if (is_win) { - public_deps = [ - "//cloud_print/service/win:cloud_print_service", - "//cloud_print/service/win:cloud_print_service_config", - "//cloud_print/service/win:cloud_print_service_setup", - "//cloud_print/virtual_driver/win/install:virtual_driver_setup", - "//cloud_print/virtual_driver/win/port_monitor", - ] - - # When compiling 32-bit, also reference the 64-bit driver for installing on - # 64-bit systems. - if (target_cpu == "x86" && current_cpu == "x86") { - public_deps += [ "//cloud_print/virtual_driver/win/port_monitor(//build/toolchain/win:x64)" ] - } - } -} - -test("cloud_print_unittests") { - sources = [ - "service/service_state_unittest.cc", - ] - - deps = [ - "//base", - "//base/test:run_all_unittests", - "//cloud_print/service:lib", - "//testing/gmock", - "//testing/gtest", - ] - - if (is_win) { - sources += [ - "service/win/service_ipc_unittest.cc", - "virtual_driver/win/port_monitor/port_monitor_unittest.cc", - ] - deps += [ "//cloud_print/virtual_driver/win/port_monitor:lib" ] - libs = [ "secur32.lib" ] - } -}
diff --git a/cloud_print/DEPS b/cloud_print/DEPS deleted file mode 100644 index bc512807..0000000 --- a/cloud_print/DEPS +++ /dev/null
@@ -1,13 +0,0 @@ -include_rules = [ - "+chrome/common", - "+chrome/installer/launcher_support", - "+components/browser_sync/common", - "+components/cloud_devices/common", - "+content/public/common", - "+google_apis", - "+grit", - "+jingle/notifier", - "+net", - "+printing", - "+url", -]
diff --git a/cloud_print/OWNERS b/cloud_print/OWNERS deleted file mode 100644 index 7a3a506..0000000 --- a/cloud_print/OWNERS +++ /dev/null
@@ -1,4 +0,0 @@ -scottbyer@chromium.org -gene@chromium.org -vitalybuka@chromium.org -
diff --git a/cloud_print/cloud_print.gyp b/cloud_print/cloud_print.gyp deleted file mode 100644 index ca463c7f..0000000 --- a/cloud_print/cloud_print.gyp +++ /dev/null
@@ -1,67 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - # GN version: //cloud_print - 'target_name': 'cloud_print', - 'type': 'none', - 'dependencies': [ - 'gcp20/prototype/gcp20_device.gyp:*', - 'service/service.gyp:*', - ], - 'conditions': [ - ['OS=="win"', { - 'dependencies': [ - 'service/win/service.gyp:*', - 'virtual_driver/win/install/virtual_driver_install.gyp:*', - 'virtual_driver/win/virtual_driver.gyp:*', - ], - }], - ['OS=="win" and target_arch=="ia32"', { - 'dependencies': [ - 'virtual_driver/win/virtual_driver64.gyp:*', - ], - }], - ], - }, - { - # GN version: //cloud_print:cloud_print_unittests - 'target_name': 'cloud_print_unittests', - 'type': 'executable', - 'sources': [ - 'service/service_state_unittest.cc', - ], - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base', - '<(DEPTH)/base/base.gyp:run_all_unittests', - '<(DEPTH)/base/base.gyp:test_support_base', - '<(DEPTH)/testing/gmock.gyp:gmock', - '<(DEPTH)/testing/gtest.gyp:gtest', - 'service/service.gyp:cloud_print_service_lib', - ], - 'conditions': [ - ['OS=="win"', { - 'sources': [ - 'service/win/service_ipc_unittest.cc', - 'virtual_driver/win/port_monitor/port_monitor_unittest.cc', - ], - 'dependencies': [ - 'virtual_driver/win/virtual_driver.gyp:gcp_portmon_lib', - ], - }], - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'AdditionalDependencies': [ - 'secur32.lib', - ], - }, - }, - }, - ], -}
diff --git a/cloud_print/cloud_print_resources.gyp b/cloud_print/cloud_print_resources.gyp deleted file mode 100644 index aba2e1e..0000000 --- a/cloud_print/cloud_print_resources.gyp +++ /dev/null
@@ -1,92 +0,0 @@ -# Copyright 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. -{ - 'variables': { - 'chromium_code': 1, - - 'variables': { - 'version_py_path': '<(DEPTH)/build/util/version.py', - 'version_path': 'VERSION', - }, - 'version_py_path': '<(version_py_path) -f', - 'version_path': '<(version_path)', - }, - 'includes': [ - '../build/util/version.gypi', - ], - 'targets': [ - { - # GN: Thes targets for this are split out depending on when the specific - # .ver file is used. For example, see: - # //cloud_print/sevice/win:exe_version - 'target_name': 'cloud_print_version_resources', - 'type': 'none', - 'variables': { - 'output_dir': 'cloud_print', - 'branding_path': '<(DEPTH)/chrome/app/theme/<(branding_path_component)/BRANDING', - 'template_input_path': '../chrome/app/chrome_version.rc.version', - 'extra_variable_files_arguments': [ '-f', 'BRANDING' ], - 'extra_variable_files': [ 'BRANDING' ], # NOTE: matches that above - }, - 'direct_dependent_settings': { - 'include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)/<(output_dir)', - ], - }, - 'sources': [ - 'service/win/cloud_print_service_exe.ver', - 'service/win/cloud_print_service_config_exe.ver', - 'service/win/cloud_print_service_setup_exe.ver', - 'virtual_driver/win/gcp_portmon64_dll.ver', - 'virtual_driver/win/gcp_portmon_dll.ver', - 'virtual_driver/win/install/virtual_driver_setup_exe.ver', - ], - 'includes': [ - '../chrome/version_resource_rules.gypi', - ], - }, - { - 'target_name': 'cloud_print_version_header', - 'type': 'none', - 'hard_dependency': 1, - 'actions': [ - { - 'action_name': 'version_header', - 'variables': { - 'branding_path': '<(DEPTH)/chrome/app/theme/<(branding_path_component)/BRANDING', - 'output_dir': 'cloud_print', - 'lastchange_path': - '<(DEPTH)/build/util/LASTCHANGE', - }, - 'direct_dependent_settings': { - 'include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)/<(output_dir)', - ], - }, - 'inputs': [ - '<(version_path)', - '<(branding_path)', - '<(lastchange_path)', - '<(DEPTH)/chrome/common/chrome_version.h.in', - 'BRANDING', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/<(output_dir)/version.h', - ], - 'action': [ - 'python', - '<(version_py_path)', - '-f', '<(version_path)', - '-f', '<(branding_path)', - '-f', '<(lastchange_path)', - '-f', 'BRANDING', - '<(DEPTH)/chrome/common/chrome_version.h.in', - '<@(_outputs)', - ], - 'message': 'Generating version header file: <@(_outputs)', - }, - ], - }, - ], -}
diff --git a/cloud_print/common/BUILD.gn b/cloud_print/common/BUILD.gn deleted file mode 100644 index ca0b546d..0000000 --- a/cloud_print/common/BUILD.gn +++ /dev/null
@@ -1,26 +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. - -source_set("common") { - sources = [ - "win/cloud_print_utils.cc", - "win/cloud_print_utils.h", - ] - - deps = [ - "//base", - ] -} - -source_set("install_utils") { - sources = [ - "win/install_utils.cc", - "win/install_utils.h", - ] - - deps = [ - ":common", - "//base", - ] -}
diff --git a/cloud_print/common/common.gyp b/cloud_print/common/common.gyp deleted file mode 100644 index 13351fda..0000000 --- a/cloud_print/common/common.gyp +++ /dev/null
@@ -1,22 +0,0 @@ -# Copyright 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. -{ - 'variables': { - 'chromium_code': 1, - }, - 'targets': [ - { - # GN version: //cloud_print/common:install_utils - 'target_name': 'cloud_print_install_lib', - 'type': 'static_library', - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base', - ], - 'sources': [ - '<(DEPTH)/cloud_print/common/win/install_utils.cc', - '<(DEPTH)/cloud_print/common/win/install_utils.h', - ], - }, - ], -}
diff --git a/cloud_print/common/win/cloud_print_utils.cc b/cloud_print/common/win/cloud_print_utils.cc deleted file mode 100644 index ee30964..0000000 --- a/cloud_print/common/win/cloud_print_utils.cc +++ /dev/null
@@ -1,61 +0,0 @@ -// Copyright 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. - -#include "cloud_print/common/win/cloud_print_utils.h" - -#include <windows.h> - -#include "base/win/registry.h" - -namespace cloud_print { - -namespace { - -// Google Update related constants. -const wchar_t kClientStateKey[] = L"SOFTWARE\\Google\\Update\\ClientState\\"; -const wchar_t* kUsageKey = L"dr"; - -} // namespace - -HRESULT GetLastHResult() { - DWORD error_code = GetLastError(); - return error_code ? HRESULT_FROM_WIN32(error_code) : E_FAIL; -} - -base::string16 LoadLocalString(DWORD id) { - static wchar_t dummy = L'\0'; - HMODULE module = NULL; - ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, &dummy, &module); - LPCWSTR buffer = NULL; - // If the last parameter is 0, LoadString assume that 3rd parameter type is - // LPCWSTR* and assign pointer to read-only memory with resource. - int count = ::LoadString(module, id, reinterpret_cast<LPWSTR>(&buffer), 0); - if (!buffer) - return base::string16(); - return base::string16(buffer, buffer + count); -} - -base::string16 GetErrorMessage(HRESULT hr) { - LPWSTR buffer = NULL; - ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_ALLOCATE_BUFFER, - 0, hr, 0, reinterpret_cast<LPWSTR>(&buffer), 0, NULL); - base::string16 result(buffer); - ::LocalFree(buffer); - return result; -} - -void SetGoogleUpdateUsage(const base::string16& product_id) { - // Set appropriate key to 1 to let Omaha record usage. - base::win::RegKey key; - if (key.Create(HKEY_CURRENT_USER, - (kClientStateKey + product_id).c_str(), - KEY_SET_VALUE) != ERROR_SUCCESS || - key.WriteValue(kUsageKey, L"1") != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to set usage key"; - } -} - -} // namespace cloud_print
diff --git a/cloud_print/common/win/cloud_print_utils.h b/cloud_print/common/win/cloud_print_utils.h deleted file mode 100644 index bc9895c6..0000000 --- a/cloud_print/common/win/cloud_print_utils.h +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 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 CLOUD_PRINT_COMMON_CLOUD_PRINT_UTILS_H_ -#define CLOUD_PRINT_COMMON_CLOUD_PRINT_UTILS_H_ - -#include <wtypes.h> - -#include "base/strings/string16.h" - -namespace cloud_print { - -// Similar to the Windows API call GetLastError but returns an HRESULT. -HRESULT GetLastHResult(); - -// Convert an HRESULT to a localized string. -base::string16 GetErrorMessage(HRESULT hr); - -// Retrieves a string from the string table of the module that contains the -// calling code. -base::string16 LoadLocalString(DWORD id); - -// Sets registry value to notify Google Update that product was used. -void SetGoogleUpdateUsage(const base::string16& product_id); - -} // namespace cloud_print - -#endif // CLOUD_PRINT_COMMON_CLOUD_PRINT_UTILS_H_ -
diff --git a/cloud_print/common/win/install_utils.cc b/cloud_print/common/win/install_utils.cc deleted file mode 100644 index b18d5fd..0000000 --- a/cloud_print/common/win/install_utils.cc +++ /dev/null
@@ -1,218 +0,0 @@ -// Copyright 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. - -#include "cloud_print/common/win/install_utils.h" - -#include <windows.h> - -#include "base/command_line.h" -#include "base/file_version_info_win.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/path_service.h" -#include "base/process/launch.h" -#include "base/win/registry.h" -#include "cloud_print/common/win/cloud_print_utils.h" - -namespace cloud_print { - -namespace { - -// Google Update related constants. -const wchar_t kClientsKey[] = L"SOFTWARE\\Google\\Update\\Clients\\"; -const wchar_t kClientStateKey[] = L"SOFTWARE\\Google\\Update\\ClientState\\"; -const wchar_t kVersionKey[] = L"pv"; -const wchar_t kNameKey[] = L"name"; - -enum InstallerResult { - INSTALLER_RESULT_FAILED_CUSTOM_ERROR = 1, - INSTALLER_RESULT_FAILED_SYSTEM_ERROR = 3, -}; - -const wchar_t kRegValueInstallerResult[] = L"InstallerResult"; -const wchar_t kRegValueInstallerResultUIString[] = L"InstallerResultUIString"; -const wchar_t kRegValueInstallerError[] = L"InstallerError"; - -// Uninstall related constants. -const wchar_t kUninstallKey[] = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"; -const wchar_t kInstallLocation[] = L"InstallLocation"; -const wchar_t kUninstallString[] = L"UninstallString"; -const wchar_t kDisplayVersion[] = L"DisplayVersion"; -const wchar_t kDisplayIcon[] = L"DisplayIcon"; -const wchar_t kDisplayName[] = L"DisplayName"; -const wchar_t kPublisher[] = L"Publisher"; -const wchar_t kNoModify[] = L"NoModify"; -const wchar_t kNoRepair[] = L"NoRepair"; - -} // namespace - - -void SetGoogleUpdateKeys(const base::string16& product_id, - const base::string16& product_name) { - base::win::RegKey key; - if (key.Create(HKEY_LOCAL_MACHINE, - (cloud_print::kClientsKey + product_id).c_str(), - KEY_SET_VALUE) != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to open key"; - } - - // Get the version from the resource file. - base::string16 version_string; - scoped_ptr<FileVersionInfo> version_info( - CREATE_FILE_VERSION_INFO_FOR_CURRENT_MODULE()); - - if (version_info.get()) { - FileVersionInfoWin* version_info_win = - static_cast<FileVersionInfoWin*>(version_info.get()); - version_string = version_info_win->product_version(); - } else { - LOG(ERROR) << "Unable to get version string"; - // Use a random version string so that Google Update has something to go by. - version_string = L"0.0.0.99"; - } - - if (key.WriteValue(kVersionKey, version_string.c_str()) != ERROR_SUCCESS || - key.WriteValue(kNameKey, product_name.c_str()) != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to set registry keys"; - } -} - -void SetGoogleUpdateError(const base::string16& product_id, - const base::string16& message) { - LOG(ERROR) << message; - base::win::RegKey key; - if (key.Create(HKEY_LOCAL_MACHINE, - (cloud_print::kClientStateKey + product_id).c_str(), - KEY_SET_VALUE) != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to open key"; - } - - if (key.WriteValue(kRegValueInstallerResult, - INSTALLER_RESULT_FAILED_CUSTOM_ERROR) != ERROR_SUCCESS || - key.WriteValue(kRegValueInstallerResultUIString, - message.c_str()) != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to set registry keys"; - } -} - -void SetGoogleUpdateError(const base::string16& product_id, HRESULT hr) { - LOG(ERROR) << cloud_print::GetErrorMessage(hr); - base::win::RegKey key; - if (key.Create(HKEY_LOCAL_MACHINE, - (cloud_print::kClientStateKey + product_id).c_str(), - KEY_SET_VALUE) != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to open key"; - } - - if (key.WriteValue(kRegValueInstallerResult, - INSTALLER_RESULT_FAILED_SYSTEM_ERROR) != ERROR_SUCCESS || - key.WriteValue(kRegValueInstallerError, hr) != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to set registry keys"; - } -} - -void DeleteGoogleUpdateKeys(const base::string16& product_id) { - base::win::RegKey key; - if (key.Open(HKEY_LOCAL_MACHINE, - (cloud_print::kClientsKey + product_id).c_str(), - DELETE) != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to open key to delete"; - return; - } - if (key.DeleteKey(L"") != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to delete key"; - } -} - -void CreateUninstallKey(const base::string16& uninstall_id, - const base::string16& product_name, - const std::string& uninstall_switch) { - // Now write the Windows Uninstall entries - // Minimal error checking here since the install can continue - // if this fails. - base::win::RegKey key; - if (key.Create(HKEY_LOCAL_MACHINE, - (cloud_print::kUninstallKey + uninstall_id).c_str(), - KEY_SET_VALUE) != ERROR_SUCCESS) { - LOG(ERROR) << "Unable to open key"; - return; - } - - base::FilePath unstall_binary; - CHECK(PathService::Get(base::FILE_EXE, &unstall_binary)); - - base::CommandLine uninstall_command(unstall_binary); - uninstall_command.AppendSwitch(uninstall_switch); - key.WriteValue(kUninstallString, - uninstall_command.GetCommandLineString().c_str()); - key.WriteValue(kInstallLocation, - unstall_binary.DirName().value().c_str()); - - // Get the version resource. - scoped_ptr<FileVersionInfo> version_info( - CREATE_FILE_VERSION_INFO_FOR_CURRENT_MODULE()); - - if (version_info.get()) { - FileVersionInfoWin* version_info_win = - static_cast<FileVersionInfoWin*>(version_info.get()); - key.WriteValue(kDisplayVersion, - version_info_win->file_version().c_str()); - key.WriteValue(kPublisher, version_info_win->company_name().c_str()); - } else { - LOG(ERROR) << "Unable to get version string"; - } - key.WriteValue(kDisplayName, product_name.c_str()); - key.WriteValue(kDisplayIcon, unstall_binary.value().c_str()); - key.WriteValue(kNoModify, 1); - key.WriteValue(kNoRepair, 1); -} - -void DeleteUninstallKey(const base::string16& uninstall_id) { - ::RegDeleteKey(HKEY_LOCAL_MACHINE, - (cloud_print::kUninstallKey + uninstall_id).c_str()); -} - -base::FilePath GetInstallLocation(const base::string16& uninstall_id) { - base::win::RegKey key; - if (key.Open(HKEY_LOCAL_MACHINE, - (cloud_print::kUninstallKey + uninstall_id).c_str(), - KEY_QUERY_VALUE) != ERROR_SUCCESS) { - // Not installed. - return base::FilePath(); - } - base::string16 install_path_value; - key.ReadValue(kInstallLocation, &install_path_value); - return base::FilePath(install_path_value); -} - -void DeleteProgramDir(const std::string& delete_switch) { - base::FilePath installer_source; - if (!PathService::Get(base::FILE_EXE, &installer_source)) - return; - // Deletes only subdirs of program files. - if (!IsProgramsFilesParent(installer_source)) - return; - base::FilePath temp_path; - if (!base::CreateTemporaryFile(&temp_path)) - return; - base::CopyFile(installer_source, temp_path); - base::DeleteFileAfterReboot(temp_path); - base::CommandLine command_line(temp_path); - command_line.AppendSwitchPath(delete_switch, installer_source.DirName()); - base::LaunchOptions options; - if (!base::LaunchProcess(command_line, options).IsValid()) { - LOG(ERROR) << "Unable to launch child uninstall."; - } -} - -bool IsProgramsFilesParent(const base::FilePath& path) { - base::FilePath program_files; - if (!PathService::Get(base::DIR_PROGRAM_FILESX86, &program_files)) - return false; - return program_files.IsParent(path); -} - -} // namespace cloud_print -
diff --git a/cloud_print/common/win/install_utils.h b/cloud_print/common/win/install_utils.h deleted file mode 100644 index 5fa7951..0000000 --- a/cloud_print/common/win/install_utils.h +++ /dev/null
@@ -1,50 +0,0 @@ -// Copyright 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 CLOUD_PRINT_COMMON_INSTALL_UTILS_H_ -#define CLOUD_PRINT_COMMON_INSTALL_UTILS_H_ - -#include <wtypes.h> -#include <string> - -#include "base/files/file_path.h" -#include "base/strings/string16.h" - -namespace cloud_print { - -// Sets Google Update registry keys after install or update. -void SetGoogleUpdateKeys(const base::string16& product_id, - const base::string16& product_name); - -// Sets custom error message to show by Google Update installer -void SetGoogleUpdateError(const base::string16& product_id, - const base::string16& message); - -// Sets custom system error code to show by Google Update installer -void SetGoogleUpdateError(const base::string16& product_id, HRESULT hr); - -// Deletes Google Update reg keys on product uninstall. -void DeleteGoogleUpdateKeys(const base::string16& product_id); - -// Creates control panel uninstall item. -void CreateUninstallKey(const base::string16& uninstall_id, - const base::string16& product_name, - const std::string& uninstall_switch); - -// Deletes control panel uninstall item. -void DeleteUninstallKey(const base::string16& uninstall_id); - -// Returns install location retrieved from control panel uninstall key. -base::FilePath GetInstallLocation(const base::string16& uninstall_id); - -// Returns install location retrieved from control panel uninstall key. -void DeleteProgramDir(const std::string& delete_switch); - -// Returns true if path is part of program files. -bool IsProgramsFilesParent(const base::FilePath& path); - -} // namespace cloud_print - -#endif // CLOUD_PRINT_COMMON_INSTALL_UTILS_H_ -
diff --git a/cloud_print/gcp20/prototype/cloud_print_request.cc b/cloud_print/gcp20/prototype/cloud_print_request.cc deleted file mode 100644 index 1b87aa8..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_request.cc +++ /dev/null
@@ -1,120 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/cloud_print_request.h" - -#include <stdint.h> -#include <utility> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/stringprintf.h" -#include "base/time/time.h" -#include "net/base/load_flags.h" -#include "net/http/http_status_code.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" - -using net::URLFetcher; -using base::MessageLoop; - -namespace { - -const uint32_t kDefaultTimeout = 20; // in seconds - -} // namespace - -CloudPrintRequest::CloudPrintRequest(const GURL& url, - URLFetcher::RequestType method, - Delegate* delegate) - : fetcher_(URLFetcher::Create(url, method, this)), - delegate_(delegate) { - int load_flags = fetcher_->GetLoadFlags(); - load_flags |= net::LOAD_DO_NOT_SEND_COOKIES; - load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES; - fetcher_->SetLoadFlags(load_flags); - - fetcher_->AddExtraRequestHeader("X-CloudPrint-Proxy: \"\""); -} - -CloudPrintRequest::~CloudPrintRequest() { -} - -scoped_ptr<CloudPrintRequest> CloudPrintRequest::CreateGet( - const GURL& url, - Delegate* delegate) { - return scoped_ptr<CloudPrintRequest>( - new CloudPrintRequest(url, URLFetcher::GET, delegate)); -} - -scoped_ptr<CloudPrintRequest> CloudPrintRequest::CreatePost( - const GURL& url, - const std::string& content, - const std::string& mimetype, - Delegate* delegate) { - scoped_ptr<CloudPrintRequest> request( - new CloudPrintRequest(url, URLFetcher::POST, delegate)); - request->fetcher_->SetUploadData(mimetype, content); - return request; -} - -void CloudPrintRequest::Run( - const std::string& access_token, - scoped_refptr<net::URLRequestContextGetter> context_getter) { - if (!access_token.empty()) - fetcher_->AddExtraRequestHeader(base::StringPrintf( - "Authorization: Bearer \"%s\"", access_token.c_str())); - - fetcher_->SetRequestContext(context_getter.get()); - fetcher_->Start(); - - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&CloudPrintRequest::OnRequestTimeout, AsWeakPtr()), - base::TimeDelta::FromSeconds(kDefaultTimeout)); -} - -void CloudPrintRequest::AddHeader(const std::string& header) { - fetcher_->AddExtraRequestHeader(header); -} - -void CloudPrintRequest::OnRequestTimeout() { - if (!fetcher_) - return; - fetcher_.reset(); - LOG(WARNING) << "Request timeout reached."; - - DCHECK(delegate_); - delegate_->OnFetchTimeoutReached(); // After this object can be deleted. - // Do *NOT* access members after this - // call. -} - -void CloudPrintRequest::OnURLFetchComplete(const URLFetcher* source) { - DCHECK(source == fetcher_.get()); - std::string response; - source->GetResponseAsString(&response); - - int http_code = fetcher_->GetResponseCode(); - fetcher_.reset(); - VLOG(3) << response; - - DCHECK(delegate_); - if (http_code == net::HTTP_OK) { - delegate_->OnFetchComplete(response); // After this object can be deleted. - // Do *NOT* access members after - // this call. - - } else { - // TODO(maksymb): Add Privet |server_http_code| and |server_api| support in - // case of server errors. - NOTIMPLEMENTED() << "HTTP code: " << http_code; - delegate_->OnFetchError("dummy", -1, http_code); // After this object can - // be deleted. - // Do *NOT* access members - // after this call. - } -} -
diff --git a/cloud_print/gcp20/prototype/cloud_print_request.h b/cloud_print/gcp20/prototype/cloud_print_request.h deleted file mode 100644 index 749f5ae..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_request.h +++ /dev/null
@@ -1,79 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_ - -#include <string> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_fetcher_delegate.h" - -// Request to CloudPrint with timeout control. -// Delegate should delete this object once it is deleting. -class CloudPrintRequest : public net::URLFetcherDelegate, - public base::SupportsWeakPtr<CloudPrintRequest> { - public: - class Delegate { - public: - Delegate() {} - virtual ~Delegate() {} - - // Invoked when |fetcher_| finished fetching successfully. - // Use for erasing instance of CloudPrintRequest class. - virtual void OnFetchComplete(const std::string& response) = 0; - - // Invoked when |fetcher_| finished fetching successfully. - // Use for erasing instance of CloudPrintRequest class. - virtual void OnFetchError(const std::string& server_api, - int server_code, - int server_http_code) = 0; - - // Invoked when timeout is reached. - // Use for erasing instance of CloudPrintRequest class. - virtual void OnFetchTimeoutReached() = 0; - }; - - ~CloudPrintRequest() override; - - // Creates GET request. - static scoped_ptr<CloudPrintRequest> CreateGet(const GURL& url, - Delegate* delegate); - - // Creates POST request. - static scoped_ptr<CloudPrintRequest> CreatePost(const GURL& url, - const std::string& content, - const std::string& mimetype, - Delegate* delegate); - - // Starts request. Once fetch was completed, parser will be called. - void Run(const std::string& access_token, - scoped_refptr<net::URLRequestContextGetter> context_getter); - - // Add header to request. - void AddHeader(const std::string& header); - - private: - CloudPrintRequest(const GURL& url, - net::URLFetcher::RequestType method, - Delegate* delegate); - - // net::URLFetcherDelegate methods: - void OnURLFetchComplete(const net::URLFetcher* source) override; - - // Method for handling timeout. - void OnRequestTimeout(); - - scoped_ptr<net::URLFetcher> fetcher_; - - Delegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(CloudPrintRequest); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_REQUEST_H_ -
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.cc b/cloud_print/gcp20/prototype/cloud_print_requester.cc deleted file mode 100644 index ccae344..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_requester.cc +++ /dev/null
@@ -1,467 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/cloud_print_requester.h" - -#include <stdint.h> -#include <limits> -#include <utility> - -#include "base/bind.h" -#include "base/json/json_writer.h" -#include "base/md5.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/stringprintf.h" -#include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h" -#include "google_apis/google_api_keys.h" -#include "net/base/escape.h" -#include "net/base/mime_util.h" -#include "net/base/url_util.h" -#include "net/http/http_status_code.h" -#include "net/proxy/proxy_config_service_fixed.h" -#include "net/url_request/url_request_context.h" -#include "url/gurl.h" - -const char kCloudPrintUrl[] = "https://www.google.com/cloudprint"; - -namespace { - -const char kProxyIdValue[] = "proxy"; -const char kPrinterNameValue[] = "printer"; -const char kPrinterCapsValue[] = "capabilities"; -const char kPrinterCapsHashValue[] = "capsHash"; -const char kPrinterUserValue[] = "user"; -const char kPrinterGcpVersion[] = "gcp_version"; -const char kPrinterLocalSettings[] = "local_settings"; -const char kPrinterFirmware[] = "firmware"; -const char kPrinterManufacturer[] = "manufacturer"; -const char kPrinterModel[] = "model"; -const char kPrinterSetupUrl[] = "setup_url"; -const char kPrinterSupportUrl[] = "support_url"; -const char kPrinterUpdateUrl[] = "update_url"; - -const char kFirmwareValue[] = "2.0"; -const char kManufacturerValue[] = "Google"; -const char kModelValue[] = "GCPPrototype"; - -// TODO(maksymb): Replace GCP Version with "2.0" once GCP Server will support it -const char kGcpVersion[] = "1.5"; - -const int kGaiaMaxRetries = 3; - -GURL CreateRegisterUrl() { - return GURL(std::string(kCloudPrintUrl) + "/register"); -} - -GURL CreateFetchUrl(const std::string& device_id) { - GURL url(std::string(kCloudPrintUrl) + "/fetch"); - url = net::AppendQueryParameter(url, "printerid", device_id); - return url; -} - -GURL CreateControlUrl(const std::string& job_id, const std::string& status) { - GURL url(std::string(kCloudPrintUrl) + "/control"); - url = net::AppendQueryParameter(url, "jobid", job_id); - url = net::AppendQueryParameter(url, "status", status); - return url; -} - -GURL CreatePrinterUrl(const std::string& device_id) { - GURL url(std::string(kCloudPrintUrl) + "/printer"); - url = net::AppendQueryParameter(url, "printerid", device_id); - return url; -} - -GURL CreateUpdateUrl(const std::string& device_id) { - GURL url(std::string(kCloudPrintUrl) + "/update"); - url = net::AppendQueryParameter(url, "printerid", device_id); - return url; -} - -std::string LocalSettingsToJson(const LocalSettings& settings) { - base::DictionaryValue dictionary; - scoped_ptr<base::DictionaryValue> current(new base::DictionaryValue); - - // TODO(maksymb): Formalize text as constants. - current->SetBoolean("local_discovery", settings.local_discovery); - current->SetBoolean("access_token_enabled", settings.access_token_enabled); - current->SetBoolean("printer/local_printing_enabled", - settings.local_printing_enabled); - current->SetInteger("xmpp_timeout_value", settings.xmpp_timeout_value); - dictionary.Set("current", std::move(current)); - - std::string local_settings; - base::JSONWriter::Write(dictionary, &local_settings); - return local_settings; -} - -} // namespace - -using cloud_print_response_parser::Job; - -CloudPrintRequester::CloudPrintRequester( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - Delegate* delegate) - : context_getter_(new CloudPrintURLRequestContextGetter(task_runner)), - delegate_(delegate) { - oauth_client_info_.client_id = - google_apis::GetOAuth2ClientID(google_apis::CLIENT_CLOUD_PRINT); - oauth_client_info_.client_secret = - google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_CLOUD_PRINT); - oauth_client_info_.redirect_uri = "oob"; -} - -CloudPrintRequester::~CloudPrintRequester() { -} - -bool CloudPrintRequester::IsBusy() const { - return request_ || gaia_; -} - -void CloudPrintRequester::StartRegistration(const std::string& proxy_id, - const std::string& device_name, - const std::string& user, - const LocalSettings& settings, - const std::string& cdd) { - std::string mime_boundary = net::GenerateMimeMultipartBoundary(); - - std::string data; - std::string data_mimetype; - data_mimetype = "multipart/form-data; boundary=" + mime_boundary; - - net::AddMultipartValueForUpload(kProxyIdValue, proxy_id, mime_boundary, - std::string(), &data); - net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary, - std::string(), &data); - net::AddMultipartValueForUpload("use_cdd", "true", mime_boundary, - std::string(), &data); - net::AddMultipartValueForUpload(kPrinterNameValue, device_name, mime_boundary, - std::string(), &data); - net::AddMultipartValueForUpload(kPrinterCapsValue, cdd, mime_boundary, - "application/json", &data); - net::AddMultipartValueForUpload(kPrinterCapsHashValue, base::MD5String(cdd), - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterUserValue, user, - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterGcpVersion, kGcpVersion, - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterLocalSettings, - LocalSettingsToJson(settings), - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterFirmware, - kFirmwareValue, - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterManufacturer, - kManufacturerValue, - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterModel, - kModelValue, - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterSetupUrl, - kCloudPrintUrl, - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterSupportUrl, - kCloudPrintUrl, - mime_boundary, std::string(), &data); - net::AddMultipartValueForUpload(kPrinterUpdateUrl, - kCloudPrintUrl, - mime_boundary, std::string(), &data); - net::AddMultipartFinalDelimiterForUpload(mime_boundary, &data); - - request_ = CreatePost( - CreateRegisterUrl(), - data, - data_mimetype, - base::Bind(&CloudPrintRequester::ParseRegisterStart, AsWeakPtr())); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - -void CloudPrintRequester::CompleteRegistration() { - request_ = CreateGet( - GURL(polling_url_ + oauth_client_info_.client_id), - base::Bind(&CloudPrintRequester::ParseRegisterComplete, AsWeakPtr())); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - -void CloudPrintRequester::FetchPrintJobs(const std::string& device_id) { - VLOG(3) << "Function: " << __FUNCTION__; - if (IsBusy()) - return; - - DCHECK(!delegate_->GetAccessToken().empty()); - - VLOG(3) << "Function: " << __FUNCTION__ << - ": request created"; - request_ = CreateGet( - CreateFetchUrl(device_id), - base::Bind(&CloudPrintRequester::ParseFetch, AsWeakPtr())); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - -void CloudPrintRequester::UpdateAccesstoken(const std::string& refresh_token) { - VLOG(3) << "Function: " << __FUNCTION__; - DCHECK(!IsBusy()); - gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get())); - gaia_->RefreshToken(oauth_client_info_, refresh_token, - std::vector<std::string>(), kGaiaMaxRetries, this); -} - -void CloudPrintRequester::RequestPrintJob(const Job& job) { - VLOG(3) << "Function: " << __FUNCTION__; - current_print_job_.reset(new Job(job)); - request_ = CreateGet( - CreateControlUrl(current_print_job_->job_id, "IN_PROGRESS"), - base::Bind(&CloudPrintRequester::ParsePrintJobInProgress, AsWeakPtr())); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - -void CloudPrintRequester::SendPrintJobDone(const std::string& job_id) { - VLOG(3) << "Function: " << __FUNCTION__; - request_ = CreateGet( - CreateControlUrl(job_id, "DONE"), - base::Bind(&CloudPrintRequester::ParsePrintJobDone, AsWeakPtr())); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - -void CloudPrintRequester::RequestLocalSettings(const std::string& device_id) { - VLOG(3) << "Function: " << __FUNCTION__; - request_ = CreateGet( - CreatePrinterUrl(device_id), - base::Bind(&CloudPrintRequester::ParseLocalSettings, AsWeakPtr())); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - -void CloudPrintRequester::SendLocalSettings( - const std::string& device_id, - const LocalSettings& settings) { - VLOG(3) << "Function: " << __FUNCTION__; - - std::string data_mimetype = "application/x-www-form-urlencoded"; - std::string data = base::StringPrintf( - "%s=%s", - kPrinterLocalSettings, - net::EscapeUrlEncodedData(LocalSettingsToJson(settings), false).c_str()); - - request_ = CreatePost( - CreateUpdateUrl(device_id), - data, data_mimetype, - base::Bind(&CloudPrintRequester::ParseLocalSettingUpdated, AsWeakPtr())); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - - -void CloudPrintRequester::OnFetchComplete(const std::string& response) { - VLOG(3) << "Function: " << __FUNCTION__; - ParserCallback callback = parser_callback_; - EraseRequest(); - callback.Run(response); -} - -void CloudPrintRequester::OnFetchError(const std::string& server_api, - int server_code, - int server_http_code) { - VLOG(3) << "Function: " << __FUNCTION__; - EraseRequest(); - current_print_job_.reset(); - - if (server_http_code == net::HTTP_FORBIDDEN) { - delegate_->OnAuthError(); - } else { - delegate_->OnServerError("Fetch error"); - } - - // TODO(maksymb): Add Privet |server_http_code| and |server_api| support in - // case of server errors. -} - -void CloudPrintRequester::OnFetchTimeoutReached() { - VLOG(3) << "Function: " << __FUNCTION__; - EraseRequest(); - current_print_job_.reset(); - delegate_->OnNetworkError(); -} - -void CloudPrintRequester::OnGetTokensResponse(const std::string& refresh_token, - const std::string& access_token, - int expires_in_seconds) { - VLOG(3) << "Function: " << __FUNCTION__; - gaia_.reset(); - delegate_->OnRegistrationFinished(refresh_token, - access_token, expires_in_seconds); -} - -void CloudPrintRequester::OnRefreshTokenResponse( - const std::string& access_token, - int expires_in_seconds) { - VLOG(3) << "Function: " << __FUNCTION__; - gaia_.reset(); - delegate_->OnAccesstokenReceviced(access_token, expires_in_seconds); -} - -void CloudPrintRequester::OnOAuthError() { - VLOG(3) << "Function: " << __FUNCTION__; - gaia_.reset(); - delegate_->OnAuthError(); -} - -void CloudPrintRequester::OnNetworkError(int response_code) { - VLOG(3) << "Function: " << __FUNCTION__; - gaia_.reset(); - - if (response_code == net::HTTP_FORBIDDEN) { - // TODO(maksymb): delegate_->OnPrinterDeleted(); - } else { - delegate_->OnNetworkError(); - } -} - -scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreateGet( - const GURL& url, - const ParserCallback& parser_callback) { - DCHECK(!IsBusy()); - DCHECK(parser_callback_.is_null()); - parser_callback_ = parser_callback; - return CloudPrintRequest::CreateGet(url, this); -} - -scoped_ptr<CloudPrintRequest> CloudPrintRequester::CreatePost( - const GURL& url, - const std::string& content, - const std::string& mimetype, - const ParserCallback& parser_callback) { - DCHECK(!IsBusy()); - DCHECK(parser_callback_.is_null()); - parser_callback_ = parser_callback; - return CloudPrintRequest::CreatePost(url, content, mimetype, this); -} - -void CloudPrintRequester::EraseRequest() { - DCHECK(request_); - DCHECK(!parser_callback_.is_null()); - request_.reset(); - parser_callback_.Reset(); -} - -void CloudPrintRequester::ParseRegisterStart(const std::string& response) { - std::string error_description; - std::string polling_url; - std::string registration_token; - std::string complete_invite_url; - std::string device_id; - - bool success = cloud_print_response_parser::ParseRegisterStartResponse( - response, - &error_description, - &polling_url, - ®istration_token, - &complete_invite_url, - &device_id); - - if (success) { - polling_url_ = polling_url; - delegate_->OnRegistrationStartResponseParsed(registration_token, - complete_invite_url, - device_id); - } else { - delegate_->OnRegistrationError(error_description); - } -} - -void CloudPrintRequester::ParseRegisterComplete(const std::string& response) { - std::string error_description; - std::string authorization_code; - - std::string xmpp_jid; - bool success = cloud_print_response_parser::ParseRegisterCompleteResponse( - response, - &error_description, - &authorization_code, - &xmpp_jid); - - if (success) { - delegate_->OnXmppJidReceived(xmpp_jid); - - gaia_.reset(new gaia::GaiaOAuthClient(context_getter_.get())); - gaia_->GetTokensFromAuthCode(oauth_client_info_, authorization_code, - kGaiaMaxRetries, this); - } else { - delegate_->OnRegistrationError(error_description); - } -} - -void CloudPrintRequester::ParseFetch(const std::string& response) { - VLOG(3) << "Function: " << __FUNCTION__; - - std::string error_description; - std::vector<Job> list; - bool success = cloud_print_response_parser::ParseFetchResponse( - response, - &error_description, - &list); - - if (success) { - delegate_->OnPrintJobsAvailable(list); - } else { - delegate_->OnServerError(error_description); - } -} - -void CloudPrintRequester::ParseGetPrintJobTicket(const std::string& response) { - VLOG(3) << "Function: " << __FUNCTION__; - current_print_job_->ticket = response; - - DCHECK(current_print_job_); - request_ = CreateGet( - GURL(current_print_job_->file_url), - base::Bind(&CloudPrintRequester::ParseGetPrintJobData, AsWeakPtr())); - request_->AddHeader("Accept: \"application/pdf\""); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - -void CloudPrintRequester::ParseGetPrintJobData(const std::string& response) { - VLOG(3) << "Function: " << __FUNCTION__; - current_print_job_->file = response; - DCHECK(current_print_job_); - delegate_->OnPrintJobDownloaded(*current_print_job_); -} - -void CloudPrintRequester::ParsePrintJobDone(const std::string& response) { - VLOG(3) << "Function: " << __FUNCTION__; - current_print_job_.reset(); - delegate_->OnPrintJobDone(); -} - -void CloudPrintRequester::ParsePrintJobInProgress(const std::string& response) { - VLOG(3) << "Function: " << __FUNCTION__; - DCHECK(current_print_job_); - request_ = CreateGet( - GURL(current_print_job_->ticket_url), - base::Bind(&CloudPrintRequester::ParseGetPrintJobTicket, AsWeakPtr())); - request_->Run(delegate_->GetAccessToken(), context_getter_); -} - -void CloudPrintRequester::ParseLocalSettings(const std::string& response) { - VLOG(3) << "Function: " << __FUNCTION__; - - std::string error_description; - LocalSettings settings; - LocalSettings::State state; - - bool success = cloud_print_response_parser::ParseLocalSettingsResponse( - response, - &error_description, - &state, - &settings); - - if (success) { - delegate_->OnLocalSettingsReceived(state, settings); - } else { - delegate_->OnServerError(error_description); - } -} - -void CloudPrintRequester::ParseLocalSettingUpdated( - const std::string& response) { - delegate_->OnLocalSettingsUpdated(); -}
diff --git a/cloud_print/gcp20/prototype/cloud_print_requester.h b/cloud_print/gcp20/prototype/cloud_print_requester.h deleted file mode 100644 index 6c543cc..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_requester.h +++ /dev/null
@@ -1,221 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_ - -#include <string> -#include <vector> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/values.h" -#include "cloud_print/gcp20/prototype/cloud_print_request.h" -#include "cloud_print/gcp20/prototype/cloud_print_response_parser.h" -#include "cloud_print/gcp20/prototype/local_settings.h" -#include "google_apis/gaia/gaia_oauth_client.h" - -class CloudPrintURLRequestContextGetter; -class GURL; -class URLRequestContextGetter; - -extern const char kCloudPrintUrl[]; - -// Class for requesting CloudPrint server and parsing responses. -class CloudPrintRequester : public base::SupportsWeakPtr<CloudPrintRequester>, - public gaia::GaiaOAuthClient::Delegate, - public CloudPrintRequest::Delegate { - public: - class Delegate { - public: - Delegate() {} - virtual ~Delegate() {} - - // Invoked when server respond for registration-start query and response is - // successfully parsed. - virtual void OnRegistrationStartResponseParsed( - const std::string& registration_token, - const std::string& complete_invite_url, - const std::string& device_id) = 0; - - // Invoked when server responded for registration-getAuthCode query and - // response is successfully parsed. - virtual void OnRegistrationFinished( - const std::string& refresh_token, - const std::string& access_token, - int access_token_expires_in_seconds) = 0; - - // Invoked when XMPP JID was received and it has to be saved. - virtual void OnXmppJidReceived(const std::string& xmpp_jid) = 0; - - // Invoked when access_token was received after UpdateAccesstoken() call. - virtual void OnAccesstokenReceviced(const std::string& access_token, - int expires_in_seconds) = 0; - - // Invoked when server respond with |"success" = false| or we cannot parse - // response. - virtual void OnRegistrationError(const std::string& description) = 0; - - // Invoked when network connection cannot be established. - virtual void OnNetworkError() = 0; - - // Invoked when server error is received or cannot parse json response. - virtual void OnServerError(const std::string& description) = 0; - - // Invoked when authorization failed. - virtual void OnAuthError() = 0; - - // Invoked when access_token is needed. - virtual std::string GetAccessToken() = 0; - - // Invoked when fetch response was received. - virtual void OnPrintJobsAvailable( - const std::vector<cloud_print_response_parser::Job>& jobs) = 0; - - // Invoked when printjob is finally downloaded and available for printing. - virtual void OnPrintJobDownloaded( - const cloud_print_response_parser::Job& job) = 0; - - // Invoked when printjob is marked as done on CloudPrint server. - virtual void OnPrintJobDone() = 0; - - // Invoked when local settings response was received. - virtual void OnLocalSettingsReceived( - LocalSettings::State state, - const LocalSettings& settings) = 0; - - // Invoked when CURRENT local settings was updated on server. - virtual void OnLocalSettingsUpdated() = 0; - }; - - // Creates and initializes object. - CloudPrintRequester(scoped_refptr<base::SingleThreadTaskRunner> task_runner, - Delegate* delegate); - - // Destroys the object. - ~CloudPrintRequester() override; - - // Returns |true| if either |gaia| or |request| is awaiting for response. - bool IsBusy() const; - - // Creates query to server for starting registration. - void StartRegistration(const std::string& proxy_id, - const std::string& device_name, - const std::string& user, - const LocalSettings& settings, - const std::string& cdd); - - // Creates request for completing registration and receiving refresh token. - void CompleteRegistration(); - - // Creates request for fetching printjobs. - void FetchPrintJobs(const std::string& device_id); - - // Creates request for updating accesstoken. - // TODO(maksymb): Handle expiration of accesstoken. - void UpdateAccesstoken(const std::string& refresh_token); - - // Creates chain of requests for requesting printjob. - void RequestPrintJob(const cloud_print_response_parser::Job& job); - - // Reports server that printjob has been printed. - void SendPrintJobDone(const std::string& job_id); - - // Requests /printer API to receive local settings. - void RequestLocalSettings(const std::string& device_id); - - // Updates local settings on server. - void SendLocalSettings(const std::string& device_id, - const LocalSettings& settings); - - private: - typedef base::Callback<void(const std::string&)> ParserCallback; - - // CloudPrintRequester::Delegate methods: - void OnFetchComplete(const std::string& response) override; - void OnFetchError(const std::string& server_api, - int server_code, - int server_http_code) override; - void OnFetchTimeoutReached() override; - - // gaia::GaiaOAuthClient::Delegate methods: - void OnGetTokensResponse(const std::string& refresh_token, - const std::string& access_token, - int expires_in_seconds) override; - void OnRefreshTokenResponse(const std::string& access_token, - int expires_in_seconds) override; - void OnOAuthError() override; - void OnNetworkError(int response_code) override; - - // Creates GET request. - scoped_ptr<CloudPrintRequest> CreateGet(const GURL& url, - const ParserCallback& callback); - - // Creates POST request. - scoped_ptr<CloudPrintRequest> CreatePost(const GURL& url, - const std::string& content, - const std::string& mimetype, - const ParserCallback& callback); - - // Deletes all info about current request. - void EraseRequest(); - - // Parses register-start server response. - void ParseRegisterStart(const std::string& response); - - // Parses register-complete server response. Initializes gaia (OAuth client) - // and receives refresh token. - void ParseRegisterComplete(const std::string& response); - - // Parses fetch printjobs server response. - void ParseFetch(const std::string& response); - - // Invoked after receiving printjob ticket. - void ParseGetPrintJobTicket(const std::string& response); - - // Invoked after receiving printjob file. - void ParseGetPrintJobData(const std::string& response); - - // Invoked after marking printjob as DONE. - void ParsePrintJobDone(const std::string& response); - - // Invoked after marking printjob as IN_PROGRESS. - void ParsePrintJobInProgress(const std::string& response); - - // Invoked after receiving local_settings. - void ParseLocalSettings(const std::string& response); - - // Invoked after updating current local_settings. - void ParseLocalSettingUpdated(const std::string& response); - - // |request| contains |NULL| if no server response is awaiting. Otherwise wait - // until callback will be called will be called and close connection. - scoped_ptr<CloudPrintRequest> request_; - - // Contains information about current printjob. Information is filled by - // CloudPrint server responses. - scoped_ptr<cloud_print_response_parser::Job> current_print_job_; - - // CloudPrint context getter. - scoped_refptr<net::URLRequestContextGetter> context_getter_; - - // URL for completing registration and receiving OAuth account. - std::string polling_url_; - - // OAuth client information (client_id, client_secret, etc). - gaia::OAuthClientInfo oauth_client_info_; - - // OAuth client. - scoped_ptr<gaia::GaiaOAuthClient> gaia_; - - ParserCallback parser_callback_; - - Delegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(CloudPrintRequester); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_REQUESTER_H_ -
diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc b/cloud_print/gcp20/prototype/cloud_print_response_parser.cc deleted file mode 100644 index acd8599..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_response_parser.cc +++ /dev/null
@@ -1,289 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/cloud_print_response_parser.h" - -#include <stddef.h> -#include <stdint.h> - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "base/values.h" - -namespace cloud_print_response_parser { - -Job::Job() { -} - -Job::~Job() { -} - -// Parses json base::Value to base::DictionaryValue. -// If |success| value in JSON dictionary is |false| then |message| will -// contain |message| from the JSON dictionary. -// Returns |true| if no parsing error occurred. -bool GetJsonDictinaryAndCheckSuccess(base::Value* json, - std::string* error_description, - bool* json_success, - std::string* message, - base::DictionaryValue** dictionary) { - base::DictionaryValue* response_dictionary = NULL; - if (!json || !json->GetAsDictionary(&response_dictionary)) { - *error_description = "No JSON dictionary response received."; - return false; - } - - bool response_json_success = false; - if (!response_dictionary->GetBoolean("success", &response_json_success)) { - *error_description = "Cannot parse success state."; - return false; - } - - if (!response_json_success) { - std::string response_message; - if (!response_dictionary->GetString("message", &response_message)) { - *error_description = "Cannot parse message from response."; - return false; - } - *message = response_message; - } - - *json_success = response_json_success; - *dictionary = response_dictionary; - return true; -} - -bool ParseRegisterStartResponse(const std::string& response, - std::string* error_description, - std::string* polling_url, - std::string* registration_token, - std::string* complete_invite_url, - std::string* device_id) { - scoped_ptr<base::Value> json(base::JSONReader::Read(response)); - base::DictionaryValue* response_dictionary = NULL; - bool json_success; - std::string message; - if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description, - &json_success, &message, - &response_dictionary)) { - return false; - } - if (!json_success) { - *error_description = message; - return false; - } - - std::string response_registration_token; - if (!response_dictionary->GetString("registration_token", - &response_registration_token)) { - *error_description = "No registration_token specified."; - return false; - } - - std::string response_complete_invite_url; - if (!response_dictionary->GetString("complete_invite_url", - &response_complete_invite_url)) { - *error_description = "No complete_invite_url specified."; - return false; - } - - std::string response_polling_url; - if (!response_dictionary->GetString("polling_url", &response_polling_url)) { - *error_description = "No polling_url specified."; - return false; - } - - base::ListValue* list = NULL; - if (!response_dictionary->GetList("printers", &list)) { - *error_description = "No printers list specified."; - return false; - } - - base::DictionaryValue* printer = NULL; - if (!list->GetDictionary(0, &printer)) { - *error_description = "Printers list is empty or printer is not dictionary."; - return false; - } - - std::string id; - if (!printer->GetString("id", &id)) { - *error_description = "No id specified."; - return false; - } - - if (id.empty()) { - *error_description = "id is empty."; - return false; - } - - *polling_url = response_polling_url; - *registration_token = response_registration_token; - *complete_invite_url = response_complete_invite_url; - *device_id = id; - return true; -} - -bool ParseRegisterCompleteResponse(const std::string& response, - std::string* error_description, - std::string* authorization_code, - std::string* xmpp_jid) { - scoped_ptr<base::Value> json(base::JSONReader::Read(response)); - base::DictionaryValue* response_dictionary = NULL; - bool json_success; - std::string message; - if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description, - &json_success, &message, - &response_dictionary)) { - return false; - } - if (!json_success) { - *error_description = message; - return false; - } - - std::string response_authorization_code; - if (!response_dictionary->GetString("authorization_code", - &response_authorization_code)) { - *error_description = "Cannot parse authorization_code."; - return false; - } - - std::string response_xmpp_jid; - if (!response_dictionary->GetString("xmpp_jid", &response_xmpp_jid)) { - *error_description = "Cannot parse xmpp jid."; - return false; - } - - *authorization_code = response_authorization_code; - *xmpp_jid = response_xmpp_jid; - return true; -} - -bool ParseFetchResponse(const std::string& response, - std::string* error_description, - std::vector<Job>* list) { - scoped_ptr<base::Value> json(base::JSONReader::Read(response)); - base::DictionaryValue* response_dictionary = NULL; - bool json_success; - std::string message; - if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description, - &json_success, &message, - &response_dictionary)) { - return false; - } - - if (!json_success) { // Let's suppose we have no jobs to proceed. - list->clear(); - return true; - } - - base::ListValue* jobs = NULL; - if (!response_dictionary->GetList("jobs", &jobs)) { - *error_description = "Cannot parse jobs list."; - return false; - } - - std::vector<Job> job_list(jobs->GetSize()); - std::string create_time_str; - for (size_t idx = 0; idx < job_list.size(); ++idx) { - base::DictionaryValue* job = NULL; - jobs->GetDictionary(idx, &job); - if (!job->GetString("id", &job_list[idx].job_id) || - !job->GetString("createTime", &create_time_str) || - !job->GetString("fileUrl", &job_list[idx].file_url) || - !job->GetString("ticketUrl", &job_list[idx].ticket_url) || - !job->GetString("title", &job_list[idx].title)) { - *error_description = "Cannot parse job info."; - return false; - } - int64_t create_time_ms = 0; - if (!base::StringToInt64(create_time_str, &create_time_ms)) { - *error_description = "Cannot convert time."; - return false; - } - job_list[idx].create_time = - base::Time::UnixEpoch() + - base::TimeDelta::FromMilliseconds(create_time_ms); - } - - *list = job_list; - return true; -} - -bool ParseLocalSettingsResponse(const std::string& response, - std::string* error_description, - LocalSettings::State* state, - LocalSettings* settings) { - scoped_ptr<base::Value> json(base::JSONReader::Read(response)); - base::DictionaryValue* response_dictionary = NULL; - bool json_success; - std::string message; - if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description, - &json_success, &message, - &response_dictionary)) { - return false; - } - - if (!json_success) { // Let's suppose our printer was deleted. - *state = LocalSettings::PRINTER_DELETED; - return true; - } - - base::ListValue* list = NULL; - if (!response_dictionary->GetList("printers", &list)) { - *error_description = "No printers list specified."; - return false; - } - - base::DictionaryValue* printer = NULL; - if (!list->GetDictionary(0, &printer)) { - *error_description = "Printers list is empty or printer is not dictionary."; - return false; - } - - base::DictionaryValue* local_settings_dict = NULL; - if (!printer->GetDictionary("local_settings", &local_settings_dict)) { - *error_description = "No local_settings found."; - return false; - } - - base::DictionaryValue* current = NULL; - if (!local_settings_dict->GetDictionary("current", ¤t)) { - *error_description = "No *current* local settings found."; - return false; - } - - LocalSettings::State settings_state; - base::DictionaryValue* pending = NULL; - base::DictionaryValue* settings_to_parse = NULL; - if (local_settings_dict->GetDictionary("pending", &pending)) { - settings_to_parse = pending; - settings_state = LocalSettings::PENDING; - } else { - settings_to_parse = current; - settings_state = LocalSettings::CURRENT; - } - - LocalSettings local_settings; - if (!settings_to_parse->GetBoolean("local_discovery", - &local_settings.local_discovery) || - !settings_to_parse->GetBoolean("access_token_enabled", - &local_settings.access_token_enabled) || - !settings_to_parse->GetBoolean("printer/local_printing_enabled", - &local_settings.local_printing_enabled) || - !settings_to_parse->GetInteger("xmpp_timeout_value", - &local_settings.xmpp_timeout_value)) { - *error_description = "Cannot parse local_settings info."; - return false; - } - - *state = settings_state; - *settings = local_settings; - return true; -} - -} // namespace cloud_print_response_parser -
diff --git a/cloud_print/gcp20/prototype/cloud_print_response_parser.h b/cloud_print/gcp20/prototype/cloud_print_response_parser.h deleted file mode 100644 index ff8daec..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_response_parser.h +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_ - -#include <string> -#include <vector> - -#include "base/callback.h" -#include "base/time/time.h" -#include "cloud_print/gcp20/prototype/local_settings.h" - -namespace base { - -class DictionaryValue; - -} // namespace base - -namespace cloud_print_response_parser { - -struct Job { - Job(); - ~Job(); - - std::string job_id; - base::Time create_time; - std::string file_url; - std::string ticket_url; - std::string title; - - // Downloaded data: - std::string file; - std::string ticket; -}; - -// Parses CloudPrint register start response to out parameters. -// Returns |true| on success. -bool ParseRegisterStartResponse(const std::string& response, - std::string* error_description, - std::string* polling_url, - std::string* registration_token, - std::string* complete_invite_url, - std::string* device_id); - -// Parses CloudPrint register complete response to out parameters. -// Returns |true| on success. -bool ParseRegisterCompleteResponse(const std::string& response, - std::string* error_description, - std::string* authorization_code, - std::string* xmpp_jid); - -// Parses CloudPrint fetch response to out parameters. -// Returns |true| on success. -bool ParseFetchResponse(const std::string& response, - std::string* error_description, - std::vector<Job>* list); - -// Parses CloudPrint printer response to get Local Settings. -// Returns |true| on success. -bool ParseLocalSettingsResponse(const std::string& response, - std::string* error_description, - LocalSettings::State* state, - LocalSettings* settings); - -} // namespace cloud_print_response_parser - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_RESPONSE_PARSER_H_ -
diff --git a/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.cc b/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.cc deleted file mode 100644 index 7840f407..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.cc +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h" - -#include <utility> - -#include "net/proxy/proxy_config_service_fixed.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_builder.h" - -CloudPrintURLRequestContextGetter::CloudPrintURLRequestContextGetter( - scoped_refptr<base::SingleThreadTaskRunner> task_runner) { - DCHECK(task_runner.get()); - network_task_runner_ = task_runner; -} - -CloudPrintURLRequestContextGetter::~CloudPrintURLRequestContextGetter() { -} - -net::URLRequestContext* -CloudPrintURLRequestContextGetter::GetURLRequestContext() { - if (!context_) { - net::URLRequestContextBuilder builder; -#if defined(OS_LINUX) || defined(OS_ANDROID) - builder.set_proxy_config_service( - make_scoped_ptr(new net::ProxyConfigServiceFixed(net::ProxyConfig()))); -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - context_ = builder.Build(); - } - return context_.get(); -} - -scoped_refptr<base::SingleThreadTaskRunner> -CloudPrintURLRequestContextGetter::GetNetworkTaskRunner() const { - return network_task_runner_; -} -
diff --git a/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h b/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h deleted file mode 100644 index 44de4e7a..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_URL_REQUEST_CONTEXT_GETTER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_URL_REQUEST_CONTEXT_GETTER_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "net/url_request/url_request_context_getter.h" - -// Used to return a dummy context, which lives on the message loop -// given in the constructor. -class CloudPrintURLRequestContextGetter : public net::URLRequestContextGetter { - public: - // |task_runner| must not be NULL. - explicit CloudPrintURLRequestContextGetter( - scoped_refptr<base::SingleThreadTaskRunner> task_runner); - - // URLRequestContextGetter implementation. - net::URLRequestContext* GetURLRequestContext() override; - - scoped_refptr<base::SingleThreadTaskRunner> - GetNetworkTaskRunner() const override; - - private: - ~CloudPrintURLRequestContextGetter() override; - - scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; - scoped_ptr<net::URLRequestContext> context_; - - DISALLOW_COPY_AND_ASSIGN(CloudPrintURLRequestContextGetter); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_URL_REQUEST_CONTEXT_GETTER_H_ -
diff --git a/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.cc b/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.cc deleted file mode 100644 index 4a02cae..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.cc +++ /dev/null
@@ -1,188 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h" - -#include <stddef.h> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/rand_util.h" -#include "base/single_thread_task_runner.h" -#include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h" -#include "jingle/notifier/base/notifier_options.h" -#include "jingle/notifier/listener/push_client.h" - -namespace { - -const int kUrgentPingInterval = 60; // in seconds -const int kPingTimeout = 30; // in seconds -const double kRandomDelayPercentage = 0.2; - -const char kCloudPrintPushNotificationsSource[] = "cloudprint.google.com"; - -// Splits message into two parts (e.g. '<device_id>/delete' will be splitted -// into '<device_id>' and '/delete'). -void TokenizeXmppMessage(const std::string& message, std::string* device_id, - std::string* path) { - std::string::size_type pos = message.find('/'); - if (pos != std::string::npos) { - *device_id = message.substr(0, pos); - *path = message.substr(pos); - } else { - *device_id = message; - path->clear(); - } -} - -} // namespace - -CloudPrintXmppListener::CloudPrintXmppListener( - const std::string& robot_email, - int standard_ping_interval, - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - Delegate* delegate) - : robot_email_(robot_email), - standard_ping_interval_(standard_ping_interval), - ping_timeouts_posted_(0), - ping_responses_pending_(0), - ping_scheduled_(false), - context_getter_(new CloudPrintURLRequestContextGetter(task_runner)), - delegate_(delegate) { -} - -CloudPrintXmppListener::~CloudPrintXmppListener() { - if (push_client_) { - push_client_->RemoveObserver(this); - push_client_.reset(); - } -} - -void CloudPrintXmppListener::Connect(const std::string& access_token) { - access_token_ = access_token; - ping_responses_pending_ = 0; - ping_scheduled_ = false; - - notifier::NotifierOptions options; - options.request_context_getter = context_getter_; - options.auth_mechanism = "X-OAUTH2"; - options.try_ssltcp_first = true; - push_client_ = notifier::PushClient::CreateDefault(options); - - notifier::Subscription subscription; - subscription.channel = kCloudPrintPushNotificationsSource; - subscription.from = kCloudPrintPushNotificationsSource; - - notifier::SubscriptionList list; - list.push_back(subscription); - - push_client_->UpdateSubscriptions(list); - push_client_->AddObserver(this); - push_client_->UpdateCredentials(robot_email_, access_token_); -} - -void CloudPrintXmppListener::set_ping_interval(int interval) { - standard_ping_interval_ = interval; -} - -void CloudPrintXmppListener::OnNotificationsEnabled() { - delegate_->OnXmppConnected(); - SchedulePing(); -} - -void CloudPrintXmppListener::OnNotificationsDisabled( - notifier::NotificationsDisabledReason reason) { - switch (reason) { - case notifier::NOTIFICATION_CREDENTIALS_REJECTED: - delegate_->OnXmppAuthError(); - break; - case notifier::TRANSIENT_NOTIFICATION_ERROR: - Disconnect(); - break; - default: - NOTREACHED() << "XMPP failed with unexpected reason code: " << reason; - } -} - -void CloudPrintXmppListener::OnIncomingNotification( - const notifier::Notification& notification) { - std::string device_id; - std::string path; - TokenizeXmppMessage(notification.data, &device_id, &path); - - if (path.empty() || path == "/") { - delegate_->OnXmppNewPrintJob(device_id); - } else if (path == "/update_settings") { - delegate_->OnXmppNewLocalSettings(device_id); - } else if (path == "/delete") { - delegate_->OnXmppDeleteNotification(device_id); - } else { - LOG(ERROR) << "Cannot parse XMPP notification: " << notification.data; - } -} - -void CloudPrintXmppListener::OnPingResponse() { - ping_responses_pending_ = 0; - SchedulePing(); -} - -void CloudPrintXmppListener::Disconnect() { - push_client_.reset(); - delegate_->OnXmppNetworkError(); -} - -void CloudPrintXmppListener::SchedulePing() { - if (ping_scheduled_) - return; - - DCHECK_LE(kPingTimeout, kUrgentPingInterval); - int delay = (ping_responses_pending_ > 0) - ? kUrgentPingInterval - kPingTimeout - : standard_ping_interval_; - - delay += base::RandInt(0, delay*kRandomDelayPercentage); - - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&CloudPrintXmppListener::SendPing, AsWeakPtr()), - base::TimeDelta::FromSeconds(delay)); - - ping_scheduled_ = true; -} - -void CloudPrintXmppListener::SendPing() { - ping_scheduled_ = false; - - DCHECK(push_client_); - push_client_->SendPing(); - ++ping_responses_pending_; - - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&CloudPrintXmppListener::OnPingTimeoutReached, AsWeakPtr()), - base::TimeDelta::FromSeconds(kPingTimeout)); - ++ping_timeouts_posted_; -} - -void CloudPrintXmppListener::OnPingTimeoutReached() { - DCHECK_GT(ping_timeouts_posted_, 0); - --ping_timeouts_posted_; - if (ping_timeouts_posted_ > 0) - return; // Fake (old) timeout. - - switch (ping_responses_pending_) { - case 0: - break; - case 1: - SchedulePing(); - break; - case 2: - Disconnect(); - break; - default: - NOTREACHED(); - } -} -
diff --git a/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h b/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h deleted file mode 100644 index ff5e9c7f..0000000 --- a/cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h +++ /dev/null
@@ -1,126 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_XMPP_LISTENER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_XMPP_LISTENER_H_ - -#include <string> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "jingle/notifier/listener/push_client_observer.h" - -namespace base { - -class SingleThreadTaskRunner; - -} // namespace base - -namespace net { - -class URLRequestContextGetter; - -} // namespace net - -namespace notifier { - -class PushClient; - -} // namespace notifier - -class CloudPrintXmppListener - : public base::SupportsWeakPtr<CloudPrintXmppListener>, - public notifier::PushClientObserver { - public: - class Delegate { - public: - virtual ~Delegate() {} - - // Invoked when XMPP connection was established. - virtual void OnXmppConnected() = 0; - - // Invoked when server rejected our credentials. - virtual void OnXmppAuthError() = 0; - - // Invoked when server is unavailable. - virtual void OnXmppNetworkError() = 0; - - // Invoked when new printjob was received. - virtual void OnXmppNewPrintJob(const std::string& device_id) = 0; - - // Invoked when local settings was updated. - virtual void OnXmppNewLocalSettings(const std::string& device_id) = 0; - - // Invoked when printer was deleted from the server. - virtual void OnXmppDeleteNotification(const std::string& device_id) = 0; - }; - - CloudPrintXmppListener( - const std::string& robot_email, - int standard_ping_interval, - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - Delegate* delegate); - - ~CloudPrintXmppListener() override; - - // Connects to the server. - void Connect(const std::string& access_token); - - // Update ping interval when new local_settings was received. - void set_ping_interval(int interval); - - private: - // notifier::PushClientObserver methods: - void OnNotificationsEnabled() override; - void OnNotificationsDisabled( - notifier::NotificationsDisabledReason reason) override; - void OnIncomingNotification( - const notifier::Notification& notification) override; - void OnPingResponse() override; - - // Stops listening and sending pings. - void Disconnect(); - - // Schedules ping (unless it was already scheduled). - void SchedulePing(); - - // Sends ping. - void SendPing(); - - // Checks if ping was received. - void OnPingTimeoutReached(); - - // Credentials: - std::string robot_email_; - std::string access_token_; - - // Internal listener. - scoped_ptr<notifier::PushClient> push_client_; - - // Interval between pings in regular workflow. - int standard_ping_interval_; - - // Number of timeouts posted to MessageLoop. Is used for controlling "fake" - // timeout calls. - int ping_timeouts_posted_; - - // Number of responses awaiting from XMPP server. Is used for controlling - // number of failed pings. - int ping_responses_pending_; - - // Is used for preventing multiple pings at the moment. - bool ping_scheduled_; - - scoped_refptr<net::URLRequestContextGetter> context_getter_; - - Delegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(CloudPrintXmppListener); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CLOUD_PRINT_XMPP_LISTENER_H_ -
diff --git a/cloud_print/gcp20/prototype/command_line_reader.cc b/cloud_print/gcp20/prototype/command_line_reader.cc deleted file mode 100644 index 777582207..0000000 --- a/cloud_print/gcp20/prototype/command_line_reader.cc +++ /dev/null
@@ -1,93 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/command_line_reader.h" - -#include <stdint.h> - -#include <limits> - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "cloud_print/gcp20/prototype/gcp20_switches.h" - -namespace command_line_reader { - -uint16_t ReadHttpPort(uint16_t default_value) { - uint32_t http_port = 0; - - std::string http_port_string = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kHttpPort); - - if (!base::StringToUint(http_port_string, &http_port)) - http_port = default_value; - - if (http_port > std::numeric_limits<uint16_t>::max()) { - LOG(ERROR) << "HTTP Port is too large"; - http_port = default_value; - } - - VLOG(1) << "HTTP port for responses: " << http_port; - return static_cast<uint16_t>(http_port); -} - -uint32_t ReadTtl(uint32_t default_value) { - uint32_t ttl = 0; - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - - if (!base::StringToUint( - command_line->GetSwitchValueASCII(switches::kTtl), - &ttl)) { - ttl = default_value; - } - - VLOG(1) << "TTL for announcements: " << ttl; - return ttl; -} - -std::string ReadServiceNamePrefix(const std::string& default_value) { - std::string service_name = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kServiceName); - - return service_name.empty() ? default_value : service_name; -} - -std::string ReadDomainName(const std::string& default_value) { - std::string domain_name = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kDomainName); - - if (domain_name.empty()) - return default_value; - - std::string suffix = ".local"; - if (domain_name == suffix) { - LOG(ERROR) << "Domain name cannot be only \"" << suffix << "\""; - return default_value; - } - - if (domain_name.size() < suffix.size() || - domain_name.substr(domain_name.size() - suffix.size()) != suffix) { - LOG(ERROR) << "Domain name should end with \"" << suffix << "\""; - return default_value; - } - - return domain_name; -} - -std::string ReadStatePath(const std::string& default_value) { - std::string filename = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kStatePath); - - if (filename.empty()) - return default_value; - return filename; -} - -} // namespace command_line_reader -
diff --git a/cloud_print/gcp20/prototype/command_line_reader.h b/cloud_print/gcp20/prototype/command_line_reader.h deleted file mode 100644 index 018b79eb..0000000 --- a/cloud_print/gcp20/prototype/command_line_reader.h +++ /dev/null
@@ -1,27 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_COMMAND_LINE_READER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_COMMAND_LINE_READER_H_ - -#include <stdint.h> - -#include <string> - -namespace command_line_reader { - -uint16_t ReadHttpPort(uint16_t default_value); - -uint32_t ReadTtl(uint32_t default_value); - -std::string ReadServiceNamePrefix(const std::string& default_value); - -std::string ReadDomainName(const std::string& default_value); - -std::string ReadStatePath(const std::string& default_value); - -} // namespace command_line_reader - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_COMMAND_LINE_READER_H_ -
diff --git a/cloud_print/gcp20/prototype/conio_posix.cc b/cloud_print/gcp20/prototype/conio_posix.cc deleted file mode 100644 index 0a955c1e..0000000 --- a/cloud_print/gcp20/prototype/conio_posix.cc +++ /dev/null
@@ -1,49 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/conio_posix.h" - -#include <stdio.h> -#include <sys/time.h> -#include <sys/types.h> -#include <termios.h> -#include <unistd.h> - -void SetTemporaryTermiosSettings(bool temporary) { - static termios oldt, newt; - - if (temporary) { - tcgetattr(STDIN_FILENO, &oldt); - newt = oldt; - newt.c_lflag &= ~ICANON; // Disable buffered IO. - tcsetattr(STDIN_FILENO, TCSANOW, &newt); - } else { - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // Restore default settings. - } -} - -int _kbhit() { - SetTemporaryTermiosSettings(true); - - timeval tv; - fd_set rdfs; - - tv.tv_sec = 0; - tv.tv_usec = 0; - - FD_ZERO(&rdfs); - FD_SET(STDIN_FILENO, &rdfs); - select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv); - SetTemporaryTermiosSettings(false); - - return FD_ISSET(STDIN_FILENO, &rdfs); -} - -int _getche() { - SetTemporaryTermiosSettings(true); - int c = getchar(); - SetTemporaryTermiosSettings(false); - return c; -} -
diff --git a/cloud_print/gcp20/prototype/conio_posix.h b/cloud_print/gcp20/prototype/conio_posix.h deleted file mode 100644 index f8bebea..0000000 --- a/cloud_print/gcp20/prototype/conio_posix.h +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_CONIO_POSIX_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_CONIO_POSIX_H_ - -// Method for disabling buffered IO. -// |true| - disable, |false| - restore previous settings. -void SetTemporaryTermiosSettings(bool temporary); - -// Analog from conio.h -int _kbhit(); - -// Analog from conio.h -int _getche(); - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_CONIO_POSIX_H_ -
diff --git a/cloud_print/gcp20/prototype/dns_packet_parser.cc b/cloud_print/gcp20/prototype/dns_packet_parser.cc deleted file mode 100644 index ef45e20..0000000 --- a/cloud_print/gcp20/prototype/dns_packet_parser.cc +++ /dev/null
@@ -1,41 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/dns_packet_parser.h" - -#include <stddef.h> - -#include "base/big_endian.h" -#include "base/logging.h" - -DnsPacketParser::DnsPacketParser(const char* packet, size_t length) - : packet_(packet), - length_(length), - record_parser_(packet, length, sizeof(header_)) { - base::BigEndianReader reader(packet, length); - is_header_read_ = reader.ReadU16(&header_.id) && - reader.ReadU16(&header_.flags) && - reader.ReadU16(&header_.qdcount) && - reader.ReadU16(&header_.ancount) && - reader.ReadU16(&header_.nscount) && - reader.ReadU16(&header_.arcount); -} - -bool DnsPacketParser::ReadRecord(DnsQueryRecord* out) { - DCHECK(packet_); - DnsQueryRecord result; - size_t consumed = ReadName(&result.qname); - if (!consumed) - return false; - base::BigEndianReader reader(packet_ + GetOffset() + consumed, - length_ - (GetOffset() + consumed)); - if (reader.ReadU16(&result.qtype) && reader.ReadU16(&result.qclass) && - record_parser_.SkipQuestion()) { // instead of |cur_ = reader.ptr();| - *out = result; - return true; - } - - return false; -} -
diff --git a/cloud_print/gcp20/prototype/dns_packet_parser.h b/cloud_print/gcp20/prototype/dns_packet_parser.h deleted file mode 100644 index 38305c2..0000000 --- a/cloud_print/gcp20/prototype/dns_packet_parser.h +++ /dev/null
@@ -1,82 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_DNS_PACKET_PARSER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_DNS_PACKET_PARSER_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <string> - -#include "base/macros.h" -#include "net/dns/dns_protocol.h" -#include "net/dns/dns_response.h" - -// Parsed response record. -struct DnsQueryRecord { - DnsQueryRecord() : qtype(0), qclass(0) {} - ~DnsQueryRecord() {} - - std::string qname; // in dotted form - uint16_t qtype; - uint16_t qclass; -}; - -// Iterator to walk over records of the DNS response packet. Encapsulates -// |DnsRecordParser| object for using its functionality. -class DnsPacketParser { - public: - // Constructs an iterator to process the |packet| of given |length|. - DnsPacketParser(const char* packet, size_t length); - - // Destroys the object. - ~DnsPacketParser() {} - - bool IsValid() const { return record_parser_.IsValid() && is_header_read_; } - - // Returns |true| if no more bytes remain in the packet. - bool AtEnd() const { return record_parser_.AtEnd(); } - - // Returns header of DNS packet. - const net::dns_protocol::Header& header() const { return header_; } - - // Parses the next query record into |record|. Returns true if succeeded. - bool ReadRecord(DnsQueryRecord* record); - - // Parses the next resource record into |record|. Returns true if succeeded. - bool ReadRecord(net::DnsResourceRecord* record) { - return record_parser_.ReadRecord(record); - } - - private: - // Returns current offset into the packet. - size_t GetOffset() const { return record_parser_.GetOffset(); } - - // Parses a (possibly compressed) DNS name from the packet starting at - // |pos|. Stores output (even partial) in |out| unless |out| is NULL. |out| - // is stored in the dotted form, e.g., "example.com". Returns number of bytes - // consumed or 0 on failure. - // This is exposed to allow parsing compressed names within RRDATA for TYPEs - // such as NS, CNAME, PTR, MX, SOA. - // See RFC 1035 section 4.1.4. - unsigned ReadName(std::string* out) const { - return record_parser_.ReadName(packet_ + GetOffset(), out); - } - - const char* packet_; - size_t length_; - - // Contents parsed header_; - net::dns_protocol::Header header_; - bool is_header_read_; - - // Encapsulated parser. - net::DnsRecordParser record_parser_; - - DISALLOW_COPY_AND_ASSIGN(DnsPacketParser); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_DNS_PACKET_PARSER_H_ -
diff --git a/cloud_print/gcp20/prototype/dns_response_builder.cc b/cloud_print/gcp20/prototype/dns_response_builder.cc deleted file mode 100644 index 5fe77c6..0000000 --- a/cloud_print/gcp20/prototype/dns_response_builder.cc +++ /dev/null
@@ -1,191 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/dns_response_builder.h" - -#include <stddef.h> -#include <stdint.h> - -#include "base/big_endian.h" -#include "base/logging.h" -#include "net/base/io_buffer.h" -#include "net/base/ip_endpoint.h" -#include "net/dns/dns_response.h" -#include "net/dns/dns_util.h" - -namespace { - -uint16_t klass = net::dns_protocol::kClassIN; - -} // namespace - -DnsResponseRecord::DnsResponseRecord() : type(0), klass(0), ttl(0) { -} - -DnsResponseRecord::~DnsResponseRecord() { -} - -DnsResponseBuilder::DnsResponseBuilder(uint16_t id) { - header_.id = id; - // TODO(maksymb): Check do we need AA flag enabled. - header_.flags = net::dns_protocol::kFlagResponse | - net::dns_protocol::kFlagAA; - header_.qdcount = 0; - header_.ancount = 0; - header_.nscount = 0; - header_.arcount = 0; -} - -DnsResponseBuilder::~DnsResponseBuilder() { -} - -void DnsResponseBuilder::AppendPtr(const std::string& service_type, - uint32_t ttl, - const std::string& service_name, - bool answer) { - std::string rdata; - bool success = net::DNSDomainFromDot(service_name, &rdata); - DCHECK(success); - - AddResponse(service_type, net::dns_protocol::kTypePTR, ttl, rdata, answer); -} - -void DnsResponseBuilder::AppendSrv(const std::string& service_name, - uint32_t ttl, - uint16_t priority, - uint16_t weight, - uint16_t http_port, - const std::string& service_domain_name, - bool answer) { - std::string domain_name; - bool success = net::DNSDomainFromDot(service_domain_name, &domain_name); - DCHECK(success); - - std::vector<uint8_t> rdata(2 + 2 + 2 + domain_name.size()); - - base::BigEndianWriter writer(reinterpret_cast<char*>(rdata.data()), - rdata.size()); - success = writer.WriteU16(priority) && - writer.WriteU16(weight) && - writer.WriteU16(http_port) && - writer.WriteBytes(domain_name.data(), domain_name.size()); - DCHECK(success); - DCHECK_EQ(writer.remaining(), 0); // For warranty of correct size allocation. - - AddResponse(service_name, net::dns_protocol::kTypeSRV, ttl, - std::string(rdata.begin(), rdata.end()), answer); -} - -void DnsResponseBuilder::AppendA(const std::string& service_domain_name, - uint32_t ttl, - net::IPAddressNumber http_ipv4, - bool answer) { - // TODO(maksymb): IP to send must depends on interface from where query was - // received. - if (http_ipv4.empty()) { - return; - } - - AddResponse(service_domain_name, net::dns_protocol::kTypeA, ttl, - std::string(http_ipv4.begin(), http_ipv4.end()), answer); -} - -void DnsResponseBuilder::AppendAAAA(const std::string& service_domain_name, - uint32_t ttl, - net::IPAddressNumber http_ipv6, - bool answer) { - // TODO(maksymb): IP to send must depends on interface from where query was - // received. - if (http_ipv6.empty()) { - return; - } - - AddResponse(service_domain_name, net::dns_protocol::kTypeAAAA, ttl, - std::string(http_ipv6.begin(), http_ipv6.end()), answer); -} - -void DnsResponseBuilder::AppendTxt(const std::string& service_name, - uint32_t ttl, - const std::vector<std::string>& metadata, - bool answer) { - std::string rdata; - for (std::vector<std::string>::const_iterator str = metadata.begin(); - str != metadata.end(); ++str) { - int len = static_cast<int>(str->size()); - DCHECK_LT(len, 256); - rdata += static_cast<char>(len); // Set length byte. - rdata += *str; - } - - AddResponse(service_name, net::dns_protocol::kTypeTXT, ttl, rdata, answer); -} - -scoped_refptr<net::IOBufferWithSize> DnsResponseBuilder::Build() { - size_t size = sizeof(header_); - for (std::vector<DnsResponseRecord>::const_iterator iter = responses_.begin(); - iter != responses_.end(); ++iter) { - size += iter->name.size() + 2 + // Two dots: first and last. - sizeof(iter->type) + sizeof(iter->klass) + sizeof(iter->ttl) + - 2 + // sizeof(RDLENGTH) - iter->rdata.size(); - } - - if (responses_.empty()) - return NULL; // No answer. - - DCHECK_EQ(static_cast<size_t>(header_.ancount + header_.arcount), - responses_.size()); - scoped_refptr<net::IOBufferWithSize> message( - new net::IOBufferWithSize(static_cast<int>(size))); - base::BigEndianWriter writer(message->data(), message->size()); - bool success = writer.WriteU16(header_.id) && - writer.WriteU16(header_.flags) && - writer.WriteU16(header_.qdcount) && - writer.WriteU16(header_.ancount) && - writer.WriteU16(header_.nscount) && - writer.WriteU16(header_.arcount); - DCHECK(success); - - std::string name_in_dns_format; - for (std::vector<DnsResponseRecord>::const_iterator iter = responses_.begin(); - iter != responses_.end(); ++iter) { - success = net::DNSDomainFromDot(iter->name, &name_in_dns_format); - DCHECK(success); - DCHECK_EQ(name_in_dns_format.size(), iter->name.size() + 2); - - success = writer.WriteBytes(name_in_dns_format.data(), - name_in_dns_format.size()) && - writer.WriteU16(iter->type) && writer.WriteU16(iter->klass) && - writer.WriteU32(iter->ttl) && - writer.WriteU16(static_cast<uint16_t>(iter->rdata.size())) && - writer.WriteBytes(iter->rdata.data(), iter->rdata.size()); - DCHECK(success); - } - - DCHECK_EQ(writer.remaining(), 0); // For warranty of correct size allocation. - - return message; -} - -void DnsResponseBuilder::AddResponse(const std::string& name, - uint16_t type, - uint32_t ttl, - const std::string& rdata, - bool answer) { - DnsResponseRecord response; - response.name = name; - response.klass = klass; - response.ttl = ttl; - response.type = type; - response.rdata = rdata; - - if (answer) { - responses_.insert(responses_.begin() + header_.ancount, response); - ++header_.ancount; - } else { - responses_.push_back(response); - ++header_.arcount; - } -} -
diff --git a/cloud_print/gcp20/prototype/dns_response_builder.h b/cloud_print/gcp20/prototype/dns_response_builder.h deleted file mode 100644 index 9f4bae557..0000000 --- a/cloud_print/gcp20/prototype/dns_response_builder.h +++ /dev/null
@@ -1,93 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_DNS_RESPONSE_BUILDER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_DNS_RESPONSE_BUILDER_H_ - -#include <stdint.h> - -#include <string> -#include <vector> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "net/base/io_buffer.h" -#include "net/base/ip_address_number.h" -#include "net/dns/dns_protocol.h" - -namespace net { -class IOBufferWithSize; -} - -// Record for storing response data. -struct DnsResponseRecord { - DnsResponseRecord(); - ~DnsResponseRecord(); - - std::string name; // in dotted form - uint16_t type; - uint16_t klass; - uint32_t ttl; - std::string rdata; -}; - -// Class for building service-specified responses. -class DnsResponseBuilder { - public: - // Initializes builder. - explicit DnsResponseBuilder(uint16_t id); - - // Destroys the object. - ~DnsResponseBuilder(); - - // Methods for appending different types of responses to packet. - void AppendPtr(const std::string& service_type, - uint32_t ttl, - const std::string& service_name, - bool answer); - - void AppendSrv(const std::string& service_name, - uint32_t ttl, - uint16_t priority, - uint16_t weight, - uint16_t http_port, - const std::string& service_domain_name, - bool answer); - - void AppendA(const std::string& service_domain_name, - uint32_t ttl, - net::IPAddressNumber http_ipv4, - bool answer); - - void AppendAAAA(const std::string& service_domain_name, - uint32_t ttl, - net::IPAddressNumber http_ipv6, - bool answer); - - void AppendTxt(const std::string& service_name, - uint32_t ttl, - const std::vector<std::string>& metadata, - bool answer); - - // Serializes packet to byte sequence. - scoped_refptr<net::IOBufferWithSize> Build(); - - private: - // Appends response to packet. - void AddResponse(const std::string& name, - uint16_t type, - uint32_t ttl, - const std::string& rdata, - bool answer); - - std::vector<DnsResponseRecord> responses_; - - // Header of response package. - net::dns_protocol::Header header_; - - DISALLOW_COPY_AND_ASSIGN(DnsResponseBuilder); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_DNS_RESPONSE_BUILDER_H_ -
diff --git a/cloud_print/gcp20/prototype/dns_sd_server.cc b/cloud_print/gcp20/prototype/dns_sd_server.cc deleted file mode 100644 index 1389778..0000000 --- a/cloud_print/gcp20/prototype/dns_sd_server.cc +++ /dev/null
@@ -1,325 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/dns_sd_server.h" - -#include <stdint.h> -#include <string.h> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/stringprintf.h" -#include "cloud_print/gcp20/prototype/dns_packet_parser.h" -#include "cloud_print/gcp20/prototype/dns_response_builder.h" -#include "cloud_print/gcp20/prototype/gcp20_switches.h" -#include "net/base/net_errors.h" -#include "net/base/net_util.h" -#include "net/dns/dns_protocol.h" - -namespace { - -const char kDefaultIpAddressMulticast[] = "224.0.0.251"; -const uint16_t kDefaultPortMulticast = 5353; - -const double kTimeToNextAnnouncement = 0.8; // relatively to TTL -const int kDnsBufSize = 65537; - -const uint16_t kSrvPriority = 0; -const uint16_t kSrvWeight = 0; - -void DoNothingAfterSendToSocket(int /*val*/) { - NOTREACHED(); - // TODO(maksymb): Delete this function once empty callback for SendTo() method - // will be allowed. -} - -} // namespace - -DnsSdServer::DnsSdServer() - : recv_buf_(new net::IOBufferWithSize(kDnsBufSize)), - full_ttl_(0) { -} - -DnsSdServer::~DnsSdServer() { - Shutdown(); -} - -bool DnsSdServer::Start(const ServiceParameters& serv_params, - uint32_t full_ttl, - const std::vector<std::string>& metadata) { - if (IsOnline()) - return true; - - if (!CreateSocket()) - return false; - - // Initializing server with parameters from arguments. - serv_params_ = serv_params; - full_ttl_ = full_ttl; - metadata_ = metadata; - - VLOG(0) << "DNS server started"; - LOG(WARNING) << "DNS server does not support probing"; - - SendAnnouncement(full_ttl_); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&DnsSdServer::OnDatagramReceived, AsWeakPtr())); - - return true; -} - -void DnsSdServer::Update() { - if (!IsOnline()) - return; - - SendAnnouncement(full_ttl_); -} - -void DnsSdServer::Shutdown() { - if (!IsOnline()) - return; - - SendAnnouncement(0); // TTL is 0 - socket_->Close(); - socket_.reset(NULL); - VLOG(0) << "DNS server stopped"; -} - -void DnsSdServer::UpdateMetadata(const std::vector<std::string>& metadata) { - if (!IsOnline()) - return; - - metadata_ = metadata; - - // TODO(maksymb): If less than 20% of full TTL left before next announcement - // then send it now. - - uint32_t current_ttl = GetCurrentTLL(); - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kNoAnnouncement)) { - DnsResponseBuilder builder(current_ttl); - - builder.AppendTxt(serv_params_.service_name_, current_ttl, metadata_, true); - scoped_refptr<net::IOBufferWithSize> buffer(builder.Build()); - - DCHECK(buffer.get() != NULL); - - socket_->SendTo(buffer.get(), buffer.get()->size(), multicast_address_, - base::Bind(&DoNothingAfterSendToSocket)); - } -} - -bool DnsSdServer::CreateSocket() { - net::IPAddressNumber local_ip_any; - bool success = net::ParseIPLiteralToNumber("0.0.0.0", &local_ip_any); - DCHECK(success); - - net::IPAddressNumber multicast_dns_ip_address; - success = net::ParseIPLiteralToNumber(kDefaultIpAddressMulticast, - &multicast_dns_ip_address); - DCHECK(success); - - - socket_.reset(new net::UDPServerSocket(NULL, net::NetLog::Source())); - - net::IPEndPoint local_address = net::IPEndPoint(local_ip_any, - kDefaultPortMulticast); - multicast_address_ = net::IPEndPoint(multicast_dns_ip_address, - kDefaultPortMulticast); - - socket_->AllowAddressReuse(); - - int status = socket_->Listen(local_address); - if (status < 0) - return false; - - socket_->SetMulticastLoopbackMode(false); - status = socket_->JoinGroup(multicast_dns_ip_address); - - if (status < 0) - return false; - return true; -} - -void DnsSdServer::ProcessMessage(int len, net::IOBufferWithSize* buf) { - VLOG(1) << "Received new message with length: " << len; - - // Parse the message. - DnsPacketParser parser(buf->data(), len); - - if (!parser.IsValid()) // Was unable to parse header. - return; - - // TODO(maksymb): Handle truncated messages. - if (parser.header().flags & net::dns_protocol::kFlagResponse) // Not a query. - return; - - DnsResponseBuilder builder(parser.header().id); - - uint32_t current_ttl = GetCurrentTLL(); - - DnsQueryRecord query; - // TODO(maksymb): Check known answers. - for (int query_idx = 0; query_idx < parser.header().qdcount; ++query_idx) { - bool success = parser.ReadRecord(&query); - if (success) { - ProccessQuery(current_ttl, query, &builder); - } else { // if (success) - VLOG(0) << "Broken package"; - break; - } - } - - scoped_refptr<net::IOBufferWithSize> buffer(builder.Build()); - if (buffer.get() == NULL) - return; // No answers. - - VLOG(1) << "Current TTL for respond: " << current_ttl; - - bool unicast_respond = base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUnicastRespond); - socket_->SendTo(buffer.get(), buffer.get()->size(), - unicast_respond ? recv_address_ : multicast_address_, - base::Bind(&DoNothingAfterSendToSocket)); - VLOG(1) << "Responded to " - << (unicast_respond ? recv_address_ : multicast_address_).ToString(); -} - -void DnsSdServer::ProccessQuery(uint32_t current_ttl, - const DnsQueryRecord& query, - DnsResponseBuilder* builder) const { - std::string log; - bool responded = false; - switch (query.qtype) { - // TODO(maksymb): Add IPv6 support. - case net::dns_protocol::kTypePTR: - log = "Processing PTR query"; - if (query.qname == serv_params_.service_type_ || - query.qname == serv_params_.secondary_service_type_) { - builder->AppendPtr(query.qname, current_ttl, - serv_params_.service_name_, true); - - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kExtendedResponce)) { - builder->AppendSrv(serv_params_.service_name_, current_ttl, - kSrvPriority, kSrvWeight, serv_params_.http_port_, - serv_params_.service_domain_name_, false); - builder->AppendA(serv_params_.service_domain_name_, current_ttl, - serv_params_.http_ipv4_, false); - builder->AppendAAAA(serv_params_.service_domain_name_, current_ttl, - serv_params_.http_ipv6_, false); - builder->AppendTxt(serv_params_.service_name_, current_ttl, metadata_, - false); - } - - responded = true; - } - - break; - case net::dns_protocol::kTypeSRV: - log = "Processing SRV query"; - if (query.qname == serv_params_.service_name_) { - builder->AppendSrv(serv_params_.service_name_, current_ttl, - kSrvPriority, kSrvWeight, serv_params_.http_port_, - serv_params_.service_domain_name_, true); - responded = true; - } - break; - case net::dns_protocol::kTypeA: - log = "Processing A query"; - if (query.qname == serv_params_.service_domain_name_) { - builder->AppendA(serv_params_.service_domain_name_, current_ttl, - serv_params_.http_ipv4_, true); - responded = true; - } - break; - case net::dns_protocol::kTypeAAAA: - log = "Processing AAAA query"; - if (query.qname == serv_params_.service_domain_name_) { - builder->AppendAAAA(serv_params_.service_domain_name_, current_ttl, - serv_params_.http_ipv6_, true); - responded = true; - } - break; - case net::dns_protocol::kTypeTXT: - log = "Processing TXT query"; - if (query.qname == serv_params_.service_name_) { - builder->AppendTxt(serv_params_.service_name_, current_ttl, metadata_, - true); - responded = true; - } - break; - default: - base::SStringPrintf(&log, "Unknown query type (%d)", query.qtype); - } - log += responded ? ": responded" : ": ignored"; - VLOG(1) << log; -} - -void DnsSdServer::DoLoop(int rv) { - // TODO(maksymb): Check what happened if buffer will be overflowed - do { - if (rv > 0) - ProcessMessage(rv, recv_buf_.get()); - rv = socket_->RecvFrom(recv_buf_.get(), recv_buf_->size(), &recv_address_, - base::Bind(&DnsSdServer::DoLoop, AsWeakPtr())); - } while (rv > 0); - - // TODO(maksymb): Add handler for errors - DCHECK(rv == net::ERR_IO_PENDING); -} - -void DnsSdServer::OnDatagramReceived() { - DoLoop(0); -} - -void DnsSdServer::SendAnnouncement(uint32_t ttl) { - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kNoAnnouncement)) { - DnsResponseBuilder builder(ttl); - - builder.AppendPtr(serv_params_.service_type_, ttl, - serv_params_.service_name_, true); - builder.AppendPtr(serv_params_.secondary_service_type_, ttl, - serv_params_.service_name_, true); - builder.AppendSrv(serv_params_.service_name_, ttl, kSrvPriority, - kSrvWeight, serv_params_.http_port_, - serv_params_.service_domain_name_, true); - builder.AppendA(serv_params_.service_domain_name_, ttl, - serv_params_.http_ipv4_, true); - builder.AppendAAAA(serv_params_.service_domain_name_, ttl, - serv_params_.http_ipv6_, true); - builder.AppendTxt(serv_params_.service_name_, ttl, metadata_, true); - - scoped_refptr<net::IOBufferWithSize> buffer(builder.Build()); - - DCHECK(buffer.get() != NULL); - - socket_->SendTo(buffer.get(), buffer.get()->size(), multicast_address_, - base::Bind(&DoNothingAfterSendToSocket)); - - VLOG(1) << "Announcement was sent with TTL: " << ttl; - } - - time_until_live_ = base::Time::Now() + - base::TimeDelta::FromSeconds(full_ttl_); - - // Schedule next announcement. - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, base::Bind(&DnsSdServer::Update, AsWeakPtr()), - base::TimeDelta::FromSeconds( - static_cast<int64_t>(kTimeToNextAnnouncement * full_ttl_))); -} - -uint32_t DnsSdServer::GetCurrentTLL() const { - uint32_t current_ttl = (time_until_live_ - base::Time::Now()).InSeconds(); - if (time_until_live_ < base::Time::Now() || current_ttl == 0) { - // This should not be reachable. But still we don't need to fail. - current_ttl = 1; // Service is still alive. - LOG(ERROR) << "|current_ttl| was equal to zero."; - } - return current_ttl; -}
diff --git a/cloud_print/gcp20/prototype/dns_sd_server.h b/cloud_print/gcp20/prototype/dns_sd_server.h deleted file mode 100644 index c154c2c2..0000000 --- a/cloud_print/gcp20/prototype/dns_sd_server.h +++ /dev/null
@@ -1,110 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_DNS_SD_SERVER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_DNS_SD_SERVER_H_ - -#include <stdint.h> - -#include <string> -#include <vector> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "cloud_print/gcp20/prototype/service_parameters.h" -#include "net/base/ip_endpoint.h" -#include "net/udp/udp_server_socket.h" - -namespace net { - -class IOBufferWithSize; - -} // namespace net - -struct DnsQueryRecord; -class DnsResponseBuilder; - -// Class for sending multicast announcements, receiving queries and answering on -// them. -// TODO(maksymb): Implement probing. -class DnsSdServer : public base::SupportsWeakPtr<DnsSdServer> { - public: - // Constructor does not start server. - DnsSdServer(); - - // Stops the server and destroys the object. - ~DnsSdServer(); - - // Starts the server. Returns |true| if server works. Also sends - // announcement. - bool Start(const ServiceParameters& serv_params, - uint32_t full_ttl, - const std::vector<std::string>& metadata) WARN_UNUSED_RESULT; - - // Sends announcement if server works. - void Update(); - - // Stops server with announcement. - void Shutdown(); - - // Returns |true| if server works. - bool IsOnline() { return !!socket_; } - - // Updates data for TXT respond. - void UpdateMetadata(const std::vector<std::string>& metadata); - - private: - // Binds a socket to multicast address. Returns |true| on success. - bool CreateSocket(); - - // Processes single query. - void ProccessQuery(uint32_t current_ttl, - const DnsQueryRecord& query, - DnsResponseBuilder* builder) const; - - // Processes DNS message. - void ProcessMessage(int len, net::IOBufferWithSize* buf); - - // CompletionCallback for receiving data from DNS. - void DoLoop(int rv); - - // Function to start listening to socket (delegate to DoLoop function). - void OnDatagramReceived(); - - // Sends announcement. - void SendAnnouncement(uint32_t ttl); - - // Calculates and returns current TTL (with accordance to last send - // announcement time. - uint32_t GetCurrentTLL() const; - - // Stores socket to multicast address. - scoped_ptr<net::UDPServerSocket> socket_; - - // Stores multicast address end point. - net::IPEndPoint multicast_address_; - - // Stores time until last announcement is live. - base::Time time_until_live_; - - // Stores service parameters (like service-name and service-type etc.) - ServiceParameters serv_params_; - - // Stores the buffer for receiving messages. - scoped_refptr<net::IOBufferWithSize> recv_buf_; - - // Stores address from where last message was sent. - net::IPEndPoint recv_address_; - - // Stores information for TXT respond. - std::vector<std::string> metadata_; - - // TTL for announcements - uint32_t full_ttl_; - - DISALLOW_COPY_AND_ASSIGN(DnsSdServer); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_DNS_SD_SERVER_H_
diff --git a/cloud_print/gcp20/prototype/gcp20_device.cc b/cloud_print/gcp20/prototype/gcp20_device.cc deleted file mode 100644 index d174107..0000000 --- a/cloud_print/gcp20/prototype/gcp20_device.cc +++ /dev/null
@@ -1,78 +0,0 @@ -// Copyright 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. - -#include <signal.h> - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "cloud_print/gcp20/prototype/gcp20_switches.h" -#include "cloud_print/gcp20/prototype/printer.h" - -namespace { - -void StartPrinter(Printer* printer) { - bool success = printer->Start(); - DCHECK(success); -} - -base::RunLoop* g_runner = NULL; -Printer* g_printer = NULL; -base::MessageLoop* g_message_loop; - -void StopLoop() { - // Always do after printer.Stop() to make sure XMPP will - // be disabled fully before |Quit| will be called - // (XMPP disables itself via MessageLoop call). - g_message_loop->PostTask(FROM_HERE, g_runner->QuitClosure()); - g_message_loop = NULL; - g_runner = NULL; -} - -void OnAbort(int val) { - if (g_printer) { - g_message_loop->PostTask( - FROM_HERE, - base::Bind(&Printer::Stop, base::Unretained(g_printer))); - g_message_loop->PostTask(FROM_HERE, base::Bind(&StopLoop)); - g_printer = NULL; - } -} - -} // namespace - -int main(int argc, char* argv[]) { - base::AtExitManager at_exit; - Printer printer; - base::CommandLine::Init(argc, argv); - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - - logging::LoggingSettings settings; - settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; - logging::InitLogging(settings); - - if (command_line->HasSwitch(switches::kHelp) || - command_line->HasSwitch(switches::kHelpShort)) { - switches::PrintUsage(); - return 0; - } - - signal(SIGINT, OnAbort); // Handle Ctrl+C signal. - - base::MessageLoopForIO loop; - g_message_loop = &loop; - g_message_loop->PostTask(FROM_HERE, base::Bind(&StartPrinter, &printer)); - base::RunLoop runner; - g_printer = &printer; - g_runner = &runner; - runner.Run(); - - return 0; -} -
diff --git a/cloud_print/gcp20/prototype/gcp20_device.gyp b/cloud_print/gcp20/prototype/gcp20_device.gyp deleted file mode 100644 index 9198052..0000000 --- a/cloud_print/gcp20/prototype/gcp20_device.gyp +++ /dev/null
@@ -1,117 +0,0 @@ -# Copyright 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. - -{ - 'target_defaults': { - 'variables': { - 'chromium_code': 1, - 'enable_wexit_time_destructors': 1, - }, - 'include_dirs': [ - '<(DEPTH)', - # To allow including "version.h" - '<(SHARED_INTERMEDIATE_DIR)', - ], - }, - 'targets': [ - { - 'target_name': 'gcp20_device_lib', - 'type': 'static_library', - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base', - '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_header', - '<(DEPTH)/google_apis/google_apis.gyp:google_apis', - '<(DEPTH)/jingle/jingle.gyp:notifier', - '<(DEPTH)/net/net.gyp:http_server', - '<(DEPTH)/net/net.gyp:net', - '<(DEPTH)/url/url.gyp:url_lib', - ], - 'sources': [ - 'cloud_print_response_parser.cc', - 'cloud_print_response_parser.h', - 'cloud_print_request.cc', - 'cloud_print_request.h', - 'cloud_print_requester.cc', - 'cloud_print_requester.h', - 'cloud_print_url_request_context_getter.cc', - 'cloud_print_url_request_context_getter.h', - 'cloud_print_xmpp_listener.cc', - 'cloud_print_xmpp_listener.h', - 'conio_posix.cc', - 'conio_posix.h', - 'command_line_reader.cc', - 'command_line_reader.h', - 'dns_packet_parser.cc', - 'dns_packet_parser.h', - 'dns_response_builder.cc', - 'dns_response_builder.h', - 'dns_sd_server.cc', - 'dns_sd_server.h', - 'gcp20_switches.cc', - 'gcp20_switches.h', - 'local_settings.h', - 'local_print_job.cc', - 'local_print_job.h', - 'print_job_handler.cc', - 'print_job_handler.h', - 'printer_state.cc', - 'printer_state.h', - 'printer.cc', - 'printer.h', - 'privet_http_server.cc', - 'privet_http_server.h', - 'service_parameters.cc', - 'service_parameters.h', - 'special_io.h', - 'x_privet_token.cc', - 'x_privet_token.h', - ], - }, - { - 'target_name': 'gcp20_device', - 'type': 'executable', - 'dependencies': [ - 'gcp20_device_lib', - ], - 'sources': [ - 'gcp20_device.cc', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '1', # Set /SUBSYSTEM:CONSOLE - 'AdditionalDependencies': [ -# TODO(maksymb): Check which of whis libs is needed. - 'secur32.lib', - 'httpapi.lib', - 'Ws2_32.lib', - ], - }, - }, - }, - { - 'target_name': 'gcp20_device_unittests', - 'type': 'executable', - 'sources': [ - 'printer_unittest.cc', - 'x_privet_token_unittest.cc', - ], - 'dependencies': [ - 'gcp20_device_lib', - '<(DEPTH)/base/base.gyp:run_all_unittests', - '<(DEPTH)/base/base.gyp:test_support_base', - '<(DEPTH)/testing/gmock.gyp:gmock', - '<(DEPTH)/testing/gtest.gyp:gtest', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '1', # Set /SUBSYSTEM:CONSOLE - 'AdditionalDependencies': [ - 'secur32.lib', - ], - }, - }, - }, - ], -}
diff --git a/cloud_print/gcp20/prototype/gcp20_switches.cc b/cloud_print/gcp20/prototype/gcp20_switches.cc deleted file mode 100644 index 85bb8dd..0000000 --- a/cloud_print/gcp20/prototype/gcp20_switches.cc +++ /dev/null
@@ -1,69 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/gcp20/prototype/gcp20_switches.h" - -#include <stddef.h> - -#include "base/files/file_path.h" -#include "base/path_service.h" - -namespace switches { - -const char kDisableConfirmation[] = "disable-confirmation"; -const char kDisableIpv4[] = "disable-ipv4"; -const char kDisableIpv6[] = "disable-ipv6"; -const char kDisableMethodCheck[] = "disable-method-check"; -const char kDisableXTocken[] = "disable-x-token"; -const char kDomainName[] = "domain-name"; -const char kExtendedResponce[] = "extended-response"; -const char kHelpShort[] = "h"; -const char kHelp[] = "help"; -const char kHttpPort[] = "http-port"; -const char kNoAnnouncement[] = "no-announcement"; -const char kServiceName[] = "service-name"; -const char kSimulatePrintingErrors[] = "simulate-printing-errors"; -const char kStatePath[] = "state-path"; -const char kTtl[] = "ttl"; -const char kUnicastRespond[] = "unicast-respond"; - -const struct { - const char* const name; - const char* const description; - const char* const arg; -} kHelpStrings[] = { - {kDisableConfirmation, "disables confirmation of registration", NULL}, - {kDisableIpv4, "disables IPv4 support", NULL}, - {kDisableIpv6, "disables IPv6 support", NULL}, - {kDisableMethodCheck, "disables HTTP method checking (POST, GET)", NULL}, - {kDisableXTocken, "disables checking of X-Privet-Token HTTP header", NULL}, - {kNoAnnouncement, "disables DNS announcements", NULL}, - {kExtendedResponce, "responds to PTR with additional records", NULL}, - {kSimulatePrintingErrors, "simulates some errors for local printing", NULL}, - {kUnicastRespond, - "DNS responses will be sent in unicast instead of multicast", NULL}, - {kDomainName, "sets, should ends with '.local'", "DOMAIN"}, - {kHttpPort, "sets port for HTTP server", "PORT"}, - {kServiceName, "sets DNS service name", "SERVICE"}, - {kStatePath, "sets path to file with registration state", "PATH"}, - {kTtl, "sets TTL for DNS announcements", "TTL"}, -}; - -void PrintUsage() { - base::FilePath exe; - PathService::Get(base::FILE_EXE, &exe); - printf("usage: %s [OPTION]...\n\n", exe.BaseName().MaybeAsASCII().c_str()); - for (size_t i = 0; i < arraysize(kHelpStrings); ++i) { - std::string name = kHelpStrings[i].name; - if (kHelpStrings[i].arg) { - name += '='; - name += kHelpStrings[i].arg; - } - name.resize(27, ' '); - printf(" --%s%s\n", name.c_str(), kHelpStrings[i].description); - } - printf("\n WARNING: mDNS probing is not implemented\n"); -} - -} // namespace switches
diff --git a/cloud_print/gcp20/prototype/gcp20_switches.h b/cloud_print/gcp20/prototype/gcp20_switches.h deleted file mode 100644 index dd112f4..0000000 --- a/cloud_print/gcp20/prototype/gcp20_switches.h +++ /dev/null
@@ -1,31 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_GCP20_SWITCHES_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_GCP20_SWITCHES_H_ - -namespace switches { - -extern const char kDisableConfirmation[]; -extern const char kDisableIpv4[]; -extern const char kDisableIpv6[]; -extern const char kDisableMethodCheck[]; -extern const char kDisableXTocken[]; -extern const char kDomainName[]; -extern const char kExtendedResponce[]; -extern const char kHelpShort[]; -extern const char kHelp[]; -extern const char kHttpPort[]; -extern const char kNoAnnouncement[]; -extern const char kServiceName[]; -extern const char kSimulatePrintingErrors[]; -extern const char kStatePath[]; -extern const char kTtl[]; -extern const char kUnicastRespond[]; - -void PrintUsage(); - -} // namespace switches - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_GCP20_SWITCHES_H_
diff --git a/cloud_print/gcp20/prototype/local_print_job.cc b/cloud_print/gcp20/prototype/local_print_job.cc deleted file mode 100644 index cafdecf..0000000 --- a/cloud_print/gcp20/prototype/local_print_job.cc +++ /dev/null
@@ -1,18 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/local_print_job.h" - -LocalPrintJob::LocalPrintJob() : offline(false) { -} - -LocalPrintJob::~LocalPrintJob() { -} - -LocalPrintJob::Info::Info() : state(STATE_DRAFT), expires_in(-1) { -} - -LocalPrintJob::Info::~Info() { -} -
diff --git a/cloud_print/gcp20/prototype/local_print_job.h b/cloud_print/gcp20/prototype/local_print_job.h deleted file mode 100644 index 4959aff..0000000 --- a/cloud_print/gcp20/prototype/local_print_job.h +++ /dev/null
@@ -1,54 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_LOCAL_PRINT_JOB_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_LOCAL_PRINT_JOB_H_ - -#include <string> - -struct LocalPrintJob { - enum CreateResult { - CREATE_SUCCESS, - CREATE_INVALID_TICKET, - CREATE_PRINTER_BUSY, - CREATE_PRINTER_ERROR, - }; - - enum SaveResult { - SAVE_SUCCESS, - SAVE_INVALID_PRINT_JOB, - SAVE_INVALID_DOCUMENT_TYPE, - SAVE_INVALID_DOCUMENT, - SAVE_DOCUMENT_TOO_LARGE, - SAVE_PRINTER_BUSY, - SAVE_PRINTER_ERROR, - }; - - enum State { - STATE_DRAFT, - STATE_ABORTED, - STATE_DONE, - }; - - struct Info { - Info(); - ~Info(); - - State state; - int expires_in; - }; - - LocalPrintJob(); - ~LocalPrintJob(); - - std::string user_name; - std::string client_name; - std::string job_name; - std::string content; - std::string content_type; - bool offline; -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_LOCAL_PRINT_JOB_H_ -
diff --git a/cloud_print/gcp20/prototype/local_settings.h b/cloud_print/gcp20/prototype/local_settings.h deleted file mode 100644 index b2ec58f..0000000 --- a/cloud_print/gcp20/prototype/local_settings.h +++ /dev/null
@@ -1,31 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_LOCAL_SETTINGS_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_LOCAL_SETTINGS_H_ - -// Contains local settings. -struct LocalSettings { - enum State { - CURRENT, - PENDING, - PRINTER_DELETED, - }; - - LocalSettings() - : local_discovery(true), - access_token_enabled(false), - local_printing_enabled(false), - xmpp_timeout_value(300) { - } - ~LocalSettings() {} - - bool local_discovery; - bool access_token_enabled; - bool local_printing_enabled; - int xmpp_timeout_value; -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_LOCAL_SETTINGS_H_ -
diff --git a/cloud_print/gcp20/prototype/print_job_handler.cc b/cloud_print/gcp20/prototype/print_job_handler.cc deleted file mode 100644 index 7a4d4cdf..0000000 --- a/cloud_print/gcp20/prototype/print_job_handler.cc +++ /dev/null
@@ -1,312 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/print_job_handler.h" - -#include <stddef.h> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/format_macros.h" -#include "base/guid.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/rand_util.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/time/time.h" -#include "cloud_print/gcp20/prototype/gcp20_switches.h" - -namespace { - -const int kDraftExpirationSec = 10; -const int kLocalPrintJobExpirationSec = 20; -const int kErrorTimeoutSec = 30; - -// Errors simulation constants: -const double kPaperJamProbability = 1.0; -const int kTooManyDraftsTimeout = 10; -const size_t kMaxDrafts = 5; - -const base::FilePath::CharType kJobsPath[] = FILE_PATH_LITERAL("printjobs"); - -bool ValidateTicket(const std::string& ticket) { - return true; -} - -std::string GenerateId() { - return base::ToLowerASCII(base::GenerateGUID()); -} - -} // namespace - -struct PrintJobHandler::LocalPrintJobExtended { - LocalPrintJobExtended(const LocalPrintJob& job, const std::string& ticket) - : data(job), - ticket(ticket), - state(LocalPrintJob::STATE_DRAFT) {} - LocalPrintJobExtended() : state(LocalPrintJob::STATE_DRAFT) {} - ~LocalPrintJobExtended() {} - - LocalPrintJob data; - std::string ticket; - LocalPrintJob::State state; - base::Time expiration; -}; - -struct PrintJobHandler::LocalPrintJobDraft { - LocalPrintJobDraft() {} - LocalPrintJobDraft(const std::string& ticket, const base::Time& expiration) - : ticket(ticket), - expiration(expiration) {} - ~LocalPrintJobDraft() {} - - std::string ticket; - base::Time expiration; -}; - -using base::StringPrintf; - -PrintJobHandler::PrintJobHandler() { -} - -PrintJobHandler::~PrintJobHandler() { -} - -LocalPrintJob::CreateResult PrintJobHandler::CreatePrintJob( - const std::string& ticket, - std::string* job_id_out, - // TODO(maksymb): Use base::TimeDelta for timeout values - int* expires_in_out, - // TODO(maksymb): Use base::TimeDelta for timeout values - int* error_timeout_out, - std::string* error_description) { - if (!ValidateTicket(ticket)) - return LocalPrintJob::CREATE_INVALID_TICKET; - - // Let's simulate at least some errors just for testing. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kSimulatePrintingErrors)) { - if (base::RandDouble() <= kPaperJamProbability) { - *error_description = "Paper jam, try again"; - return LocalPrintJob::CREATE_PRINTER_ERROR; - } - - if (drafts.size() > kMaxDrafts) { // Another simulation of error: business - *error_timeout_out = kTooManyDraftsTimeout; - return LocalPrintJob::CREATE_PRINTER_BUSY; - } - } - - std::string id = GenerateId(); - drafts[id] = LocalPrintJobDraft( - ticket, - base::Time::Now() + base::TimeDelta::FromSeconds(kDraftExpirationSec)); - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&PrintJobHandler::ForgetDraft, AsWeakPtr(), id), - base::TimeDelta::FromSeconds(kDraftExpirationSec)); - - *job_id_out = id; - *expires_in_out = kDraftExpirationSec; - return LocalPrintJob::CREATE_SUCCESS; -} - -LocalPrintJob::SaveResult PrintJobHandler::SaveLocalPrintJob( - const LocalPrintJob& job, - std::string* job_id_out, - int* expires_in_out, - std::string* error_description_out, - int* timeout_out) { - std::string id; - int expires_in; - - switch (CreatePrintJob(std::string(), &id, &expires_in, - timeout_out, error_description_out)) { - case LocalPrintJob::CREATE_INVALID_TICKET: - NOTREACHED(); - return LocalPrintJob::SAVE_SUCCESS; - - case LocalPrintJob::CREATE_PRINTER_BUSY: - return LocalPrintJob::SAVE_PRINTER_BUSY; - - case LocalPrintJob::CREATE_PRINTER_ERROR: - return LocalPrintJob::SAVE_PRINTER_ERROR; - - case LocalPrintJob::CREATE_SUCCESS: - *job_id_out = id; - return CompleteLocalPrintJob(job, id, expires_in_out, - error_description_out, timeout_out); - - default: - NOTREACHED(); - return LocalPrintJob::SAVE_SUCCESS; - } -} - -LocalPrintJob::SaveResult PrintJobHandler::CompleteLocalPrintJob( - const LocalPrintJob& job, - const std::string& job_id, - int* expires_in_out, - std::string* error_description_out, - int* timeout_out) { - if (!drafts.count(job_id)) { - *timeout_out = kErrorTimeoutSec; - return LocalPrintJob::SAVE_INVALID_PRINT_JOB; - } - - std::string file_extension; - // TODO(maksymb): Gather together this type checking with Printer - // supported types in kCdd. - if (job.content_type == "application/pdf") { - file_extension = "pdf"; - } else if (job.content_type == "image/pwg-raster") { - file_extension = "pwg"; - } else if (job.content_type == "image/jpeg") { - file_extension = "jpg"; - } else { - error_description_out->clear(); - return LocalPrintJob::SAVE_INVALID_DOCUMENT_TYPE; - } - CompleteDraft(job_id, job); - std::map<std::string, LocalPrintJobExtended>::iterator current_job = - jobs.find(job_id); - - if (!SavePrintJob(current_job->second.data.content, - current_job->second.ticket, - base::Time::Now(), - job_id, - current_job->second.data.job_name, file_extension)) { - SetJobState(job_id, LocalPrintJob::STATE_ABORTED); - *error_description_out = "IO error"; - return LocalPrintJob::SAVE_PRINTER_ERROR; - } - - SetJobState(job_id, LocalPrintJob::STATE_DONE); - *expires_in_out = static_cast<int>(GetJobExpiration(job_id).InSeconds()); - return LocalPrintJob::SAVE_SUCCESS; -} - -bool PrintJobHandler::GetJobState(const std::string& id, - LocalPrintJob::Info* info_out) { - using base::Time; - - std::map<std::string, LocalPrintJobDraft>::iterator draft = drafts.find(id); - if (draft != drafts.end()) { - info_out->state = LocalPrintJob::STATE_DRAFT; - info_out->expires_in = - static_cast<int>((draft->second.expiration - Time::Now()).InSeconds()); - return true; - } - - std::map<std::string, LocalPrintJobExtended>::iterator job = jobs.find(id); - if (job != jobs.end()) { - info_out->state = job->second.state; - info_out->expires_in = static_cast<int>(GetJobExpiration(id).InSeconds()); - return true; - } - return false; -} - -bool PrintJobHandler::SavePrintJob(const std::string& content, - const std::string& ticket, - const base::Time& create_time, - const std::string& id, - const std::string& title, - const std::string& file_extension) { - VLOG(1) << "Printing printjob: \"" + title + "\""; - base::FilePath directory(kJobsPath); - - if (!base::DirectoryExists(directory) && - !base::CreateDirectory(directory)) { - return false; - } - - base::Time::Exploded create_time_exploded; - create_time.UTCExplode(&create_time_exploded); - base::FilePath::StringType job_prefix = - StringPrintf(FILE_PATH_LITERAL("%.4d%.2d%.2d-%.2d%.2d%.2d_"), - create_time_exploded.year, - create_time_exploded.month, - create_time_exploded.day_of_month, - create_time_exploded.hour, - create_time_exploded.minute, - create_time_exploded.second); - if (!base::CreateTemporaryDirInDir(directory, job_prefix, &directory)) { - LOG(WARNING) << "Cannot create directory for " << job_prefix; - return false; - } - - int written = base::WriteFile(directory.AppendASCII("ticket.xml"), - ticket.data(), - static_cast<int>(ticket.size())); - if (static_cast<size_t>(written) != ticket.size()) { - LOG(WARNING) << "Cannot save ticket."; - return false; - } - - written = base::WriteFile( - directory.AppendASCII("data." + file_extension), - content.data(), static_cast<int>(content.size())); - if (static_cast<size_t>(written) != content.size()) { - LOG(WARNING) << "Cannot save data."; - return false; - } - - VLOG(0) << "Job saved at " << directory.value(); - return true; -} - -void PrintJobHandler::SetJobState(const std::string& id, - LocalPrintJob::State state) { - DCHECK(!drafts.count(id)) << "Draft should be completed at first"; - - std::map<std::string, LocalPrintJobExtended>::iterator job = jobs.find(id); - DCHECK(job != jobs.end()); - job->second.state = state; - switch (state) { - case LocalPrintJob::STATE_DRAFT: - NOTREACHED(); - break; - case LocalPrintJob::STATE_ABORTED: - case LocalPrintJob::STATE_DONE: - job->second.expiration = - base::Time::Now() + - base::TimeDelta::FromSeconds(kLocalPrintJobExpirationSec); - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&PrintJobHandler::ForgetLocalJob, AsWeakPtr(), id), - base::TimeDelta::FromSeconds(kLocalPrintJobExpirationSec)); - break; - default: - NOTREACHED(); - } -} - -void PrintJobHandler::CompleteDraft(const std::string& id, - const LocalPrintJob& job) { - std::map<std::string, LocalPrintJobDraft>::iterator draft = drafts.find(id); - if (draft != drafts.end()) { - jobs[id] = LocalPrintJobExtended(job, draft->second.ticket); - drafts.erase(draft); - } -} - -// TODO(maksymb): Use base::Time for expiration -base::TimeDelta PrintJobHandler::GetJobExpiration(const std::string& id) const { - DCHECK(jobs.count(id)); - base::Time expiration = jobs.at(id).expiration; - if (expiration.is_null()) - return base::TimeDelta::FromSeconds(kLocalPrintJobExpirationSec); - return expiration - base::Time::Now(); -} - -void PrintJobHandler::ForgetDraft(const std::string& id) { - drafts.erase(id); -} - -void PrintJobHandler::ForgetLocalJob(const std::string& id) { - jobs.erase(id); -}
diff --git a/cloud_print/gcp20/prototype/print_job_handler.h b/cloud_print/gcp20/prototype/print_job_handler.h deleted file mode 100644 index a0a2abfc..0000000 --- a/cloud_print/gcp20/prototype/print_job_handler.h +++ /dev/null
@@ -1,97 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_ - -#include <map> -#include <string> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "cloud_print/gcp20/prototype/local_print_job.h" - -namespace base { - -class DictionaryValue; -class Time; - -} // namespace base - -class PrintJobHandler : public base::SupportsWeakPtr<PrintJobHandler> { - public: - PrintJobHandler(); - ~PrintJobHandler(); - - // Creates printer job draft - LocalPrintJob::CreateResult CreatePrintJob( - const std::string& ticket, - std::string* job_id_out, - int* expires_in_out, - int* error_timeout_out, - std::string* error_description_out); - - // Creates printer job with empty ticket and "prints" it - LocalPrintJob::SaveResult SaveLocalPrintJob( - const LocalPrintJob& job, - std::string* job_id_out, - int* expires_in_out, - std::string* error_description_out, - int* timeout_out); - - // Completes printer job from draft - LocalPrintJob::SaveResult CompleteLocalPrintJob( - const LocalPrintJob& job, - const std::string& job_id, - int* expires_in_out, - std::string* error_description_out, - int* timeout_out); - - // Gives info about job - bool GetJobState(const std::string& id, LocalPrintJob::Info* info_out); - - // Saving print job directly to drive - bool SavePrintJob(const std::string& content, - const std::string& ticket, - const base::Time& create_time, - const std::string& id, - const std::string& title, - const std::string& file_extension); - - private: - // Contains ticket info and job info together - struct LocalPrintJobExtended; - - // Contains job ticket - struct LocalPrintJobDraft; - - // Contains all unexpired drafts - std::map<std::string, LocalPrintJobDraft> drafts; // id -> draft - - // Contains all unexpired jobs - std::map<std::string, LocalPrintJobExtended> jobs; // id -> printjob - - // Changes job state and creates timeouts to delete old jobs from memory - void SetJobState(const std::string& id, LocalPrintJob::State); - - // Moves draft to jobs - void CompleteDraft(const std::string& id, const LocalPrintJob& job); - - // Calculates expiration for job - // TODO(maksymb): Use base::Time for expiration - base::TimeDelta GetJobExpiration(const std::string& id) const; - - // Erases draft from memory - void ForgetDraft(const std::string& id); - - // Erases job from memory - void ForgetLocalJob(const std::string& id); - - DISALLOW_COPY_AND_ASSIGN(PrintJobHandler); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_PRINT_JOB_HANDLER_H_ -
diff --git a/cloud_print/gcp20/prototype/printer.cc b/cloud_print/gcp20/prototype/printer.cc deleted file mode 100644 index 4b91f165..0000000 --- a/cloud_print/gcp20/prototype/printer.cc +++ /dev/null
@@ -1,967 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/printer.h" - -#include <limits.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <algorithm> -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/format_macros.h" -#include "base/guid.h" -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/rand_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "cloud_print/gcp20/prototype/command_line_reader.h" -#include "cloud_print/gcp20/prototype/gcp20_switches.h" -#include "cloud_print/gcp20/prototype/local_settings.h" -#include "cloud_print/gcp20/prototype/service_parameters.h" -#include "cloud_print/gcp20/prototype/special_io.h" -#include "cloud_print/version.h" -#include "net/base/network_interfaces.h" -#include "net/base/url_util.h" - -const char kPrinterStatePathDefault[] = "printer_state.json"; - -namespace { - -const uint16_t kHttpPortDefault = 10101; -const uint32_t kTtlDefault = 60 * 60; // in seconds - -const char kServiceType[] = "_privet._tcp.local"; -const char kSecondaryServiceType[] = "_printer._sub._privet._tcp.local"; -const char kServiceNamePrefixDefault[] = "gcp20_device_"; -const char kServiceDomainNameFormatDefault[] = "my-privet-device%d.local"; - -const char kPrinterName[] = "Google GCP2.0 Prototype"; -const char kPrinterDescription[] = "Printer emulator"; - -const char kUserConfirmationTitle[] = "Confirm registration: type 'y' if you " - "agree and any other to discard\n"; -const int kUserConfirmationTimeout = 30; // in seconds -const int kRegistrationTimeout = 60; // in seconds -const int kReconnectTimeout = 5; // in seconds - -const double kTimeToNextAccessTokenUpdate = 0.8; // relatively to living time. - -const char kCdd[] = -"{" -" 'version': '1.0'," -" 'printer': {" -" 'supported_content_type': [" -" {" -" 'content_type': 'application/pdf'" -" }," -" {" -" 'content_type': 'image/pwg-raster'" -" }," -" {" -" 'content_type': 'image/jpeg'" -" }" -" ]," -" 'color': {" -" 'option': [" -" {" -" 'is_default': true," -" 'type': 'STANDARD_COLOR'" -" }," -" {" -" 'type': 'STANDARD_MONOCHROME'" -" }" -" ]" -" }," -" 'media_size': {" -" 'option': [ {" -" 'height_microns': 297000," -" 'name': 'ISO_A4'," -" 'width_microns': 210000" -" }, {" -" 'custom_display_name': 'Letter'," -" 'height_microns': 279400," -" 'is_default': true," -" 'name': 'NA_LETTER'," -" 'width_microns': 215900" -" } ]" -" }," -" 'page_orientation': {" -" 'option': [ {" -" 'is_default': true," -" 'type': 'PORTRAIT'" -" }, {" -" 'type': 'LANDSCAPE'" -" } ]" -" }," -" 'reverse_order': {" -" 'default': false" -" }" -" }" -"}"; - -// Returns local IP address number of first interface found (except loopback). -// Return value is empty if no interface found. Possible interfaces names are -// "eth0", "wlan0" etc. If interface name is empty, function will return IP -// address of first interface found. -net::IPAddressNumber GetLocalIp(const std::string& interface_name, - bool return_ipv6_number) { - net::NetworkInterfaceList interfaces; - bool success = net::GetNetworkList( - &interfaces, net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES); - DCHECK(success); - - size_t expected_address_size = return_ipv6_number ? net::kIPv6AddressSize - : net::kIPv4AddressSize; - - for (net::NetworkInterfaceList::iterator iter = interfaces.begin(); - iter != interfaces.end(); ++iter) { - if (iter->address.size() == expected_address_size && - (interface_name.empty() || interface_name == iter->name)) { - return iter->address; - } - } - - return net::IPAddressNumber(); -} - -std::string GetDescription() { - std::string result = kPrinterDescription; - net::IPAddressNumber ip = GetLocalIp("", false); - if (!ip.empty()) - result += " at " + net::IPAddressToString(ip); - return result; -} - -scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() { - return base::MessageLoop::current()->task_runner(); -} - -} // namespace - -using cloud_print_response_parser::Job; - -Printer::Printer() - : connection_state_(OFFLINE), - http_server_(this), - on_idle_posted_(false), - pending_local_settings_check_(false), - pending_print_jobs_check_(false), - pending_deletion_(false) { -} - -Printer::~Printer() { - Stop(); -} - -bool Printer::Start() { - if (IsRunning()) - return true; - - LoadFromFile(); - - if (state_.local_settings.local_discovery && !StartLocalDiscoveryServers()) - return false; - - print_job_handler_.reset(new PrintJobHandler); - xtoken_ = XPrivetToken(); - starttime_ = base::Time::Now(); - - TryConnect(); - return true; -} - -bool Printer::IsRunning() const { - return print_job_handler_; -} - -void Printer::Stop() { - if (!IsRunning()) - return; - dns_server_.Shutdown(); - http_server_.Shutdown(); - requester_.reset(); - print_job_handler_.reset(); - xmpp_listener_.reset(); -} - -std::string Printer::GetRawCdd() { - std::string json_str; - base::JSONWriter::WriteWithOptions(GetCapabilities(), - base::JSONWriter::OPTIONS_PRETTY_PRINT, - &json_str); - return json_str; -} - -void Printer::OnAuthError() { - LOG(ERROR) << "Auth error occurred"; - state_.access_token_update = base::Time(); - FallOffline(true); -} - -std::string Printer::GetAccessToken() { - return state_.access_token; -} - -PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationStart( - const std::string& user) { - CheckRegistrationExpiration(); - - PrinterState::ConfirmationState conf_state = state_.confirmation_state; - if (state_.registration_state == PrinterState::REGISTRATION_ERROR || - conf_state == PrinterState::CONFIRMATION_TIMEOUT || - conf_state == PrinterState::CONFIRMATION_DISCARDED) { - state_ = PrinterState(); - } - - PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); - if (status != PrivetHttpServer::REG_ERROR_OK) - return status; - - if (state_.registration_state != PrinterState::UNREGISTERED) - return PrivetHttpServer::REG_ERROR_INVALID_ACTION; - - UpdateRegistrationExpiration(); - - state_ = PrinterState(); - state_.user = user; - state_.registration_state = PrinterState::REGISTRATION_STARTED; - - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - "disable-confirmation")) { - state_.confirmation_state = PrinterState::CONFIRMATION_CONFIRMED; - VLOG(0) << "Registration confirmed by default."; - } else { - LOG(WARNING) << kUserConfirmationTitle; - base::Time valid_until = base::Time::Now() + - base::TimeDelta::FromSeconds(kUserConfirmationTimeout); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until)); - } - - requester_->StartRegistration(GenerateProxyId(), kPrinterName, user, - state_.local_settings, GetRawCdd()); - - return PrivetHttpServer::REG_ERROR_OK; -} - -PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationGetClaimToken( - const std::string& user, - std::string* token, - std::string* claim_url) { - PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); - if (status != PrivetHttpServer::REG_ERROR_OK) - return status; - - // Check if |action=start| was called, but |action=complete| wasn't. - if (state_.registration_state != PrinterState::REGISTRATION_STARTED && - state_.registration_state != - PrinterState::REGISTRATION_CLAIM_TOKEN_READY) { - return PrivetHttpServer::REG_ERROR_INVALID_ACTION; - } - - // If |action=getClaimToken| is valid in this state (was checked above) then - // check confirmation status. - if (state_.confirmation_state != PrinterState::CONFIRMATION_CONFIRMED) - return ConfirmationToRegistrationError(state_.confirmation_state); - - UpdateRegistrationExpiration(); - - // If reply wasn't received yet, reply with |pending_user_action| error. - if (state_.registration_state == PrinterState::REGISTRATION_STARTED) - return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION; - - DCHECK_EQ(state_.confirmation_state, PrinterState::CONFIRMATION_CONFIRMED); - DCHECK_EQ(state_.registration_state, - PrinterState::REGISTRATION_CLAIM_TOKEN_READY); - - *token = state_.registration_token; - *claim_url = state_.complete_invite_url; - return PrivetHttpServer::REG_ERROR_OK; -} - -PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationComplete( - const std::string& user, - std::string* device_id) { - PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); - if (status != PrivetHttpServer::REG_ERROR_OK) - return status; - - if (state_.registration_state != PrinterState::REGISTRATION_CLAIM_TOKEN_READY) - return PrivetHttpServer::REG_ERROR_INVALID_ACTION; - - UpdateRegistrationExpiration(); - - if (state_.confirmation_state != PrinterState::CONFIRMATION_CONFIRMED) - return ConfirmationToRegistrationError(state_.confirmation_state); - - state_.registration_state = PrinterState::REGISTRATION_COMPLETING; - requester_->CompleteRegistration(); - *device_id = state_.device_id; - - return PrivetHttpServer::REG_ERROR_OK; -} - -PrivetHttpServer::RegistrationErrorStatus Printer::RegistrationCancel( - const std::string& user) { - PrivetHttpServer::RegistrationErrorStatus status = CheckCommonRegErrors(user); - if (status != PrivetHttpServer::REG_ERROR_OK && - status != PrivetHttpServer::REG_ERROR_SERVER_ERROR) { - return status; - } - - if (state_.registration_state == PrinterState::UNREGISTERED) - return PrivetHttpServer::REG_ERROR_INVALID_ACTION; - - InvalidateRegistrationExpiration(); - - state_ = PrinterState(); - - requester_.reset(new CloudPrintRequester(GetTaskRunner(), this)); - - return PrivetHttpServer::REG_ERROR_OK; -} - -void Printer::GetRegistrationServerError(std::string* description) { - DCHECK_EQ(state_.registration_state, PrinterState::REGISTRATION_ERROR) - << "Method shouldn't be called when not needed."; - - *description = state_.error_description; -} - -void Printer::CreateInfo(PrivetHttpServer::DeviceInfo* info) { - CheckRegistrationExpiration(); - - // TODO(maksymb): Replace "text" with constants. - *info = PrivetHttpServer::DeviceInfo(); - info->version = "1.0"; - info->name = kPrinterName; - info->description = GetDescription(); - info->url = kCloudPrintUrl; - info->id = state_.device_id; - info->device_state = "idle"; - info->connection_state = ConnectionStateToString(connection_state_); - info->manufacturer = COMPANY_FULLNAME_STRING; - info->model = "Prototype r" + std::string(LASTCHANGE_STRING); - info->serial_number = "20CB5FF2-B28C-4EFA-8DCD-516CFF0455A2"; - info->firmware = CHROME_VERSION_STRING; - info->uptime = static_cast<int>((base::Time::Now() - starttime_).InSeconds()); - - info->x_privet_token = xtoken_.GenerateXToken(); - - // TODO(maksymb): Create enum for available APIs and replace - // this API text names with constants from enum. API text names should be only - // known in PrivetHttpServer. - if (!IsRegistered()) { - info->api.push_back("/privet/register"); - } else { - info->api.push_back("/privet/capabilities"); - if (IsLocalPrintingAllowed()) { - info->api.push_back("/privet/printer/createjob"); - info->api.push_back("/privet/printer/submitdoc"); - info->api.push_back("/privet/printer/jobstate"); - } - } - - info->type.push_back("printer"); -} - -bool Printer::IsRegistered() const { - return state_.registration_state == PrinterState::REGISTERED; -} - -bool Printer::IsLocalPrintingAllowed() const { - return state_.local_settings.local_printing_enabled; -} - -bool Printer::CheckXPrivetTokenHeader(const std::string& token) const { - return xtoken_.CheckValidXToken(token); -} - -const base::DictionaryValue& Printer::GetCapabilities() { - if (!state_.cdd.get()) { - std::string cdd_string; - base::ReplaceChars(kCdd, "'", "\"", &cdd_string); - scoped_ptr<base::Value> json_val(base::JSONReader::Read(cdd_string)); - base::DictionaryValue* json = NULL; - CHECK(json_val->GetAsDictionary(&json)); - state_.cdd.reset(json->DeepCopy()); - } - return *state_.cdd; -} - -LocalPrintJob::CreateResult Printer::CreateJob(const std::string& ticket, - std::string* job_id, - int* expires_in, - int* error_timeout, - std::string* error_description) { - return print_job_handler_->CreatePrintJob(ticket, job_id, expires_in, - error_timeout, error_description); -} - -LocalPrintJob::SaveResult Printer::SubmitDoc(const LocalPrintJob& job, - std::string* job_id, - int* expires_in, - std::string* error_description, - int* timeout) { - return print_job_handler_->SaveLocalPrintJob(job, job_id, expires_in, - error_description, timeout); -} - -LocalPrintJob::SaveResult Printer::SubmitDocWithId( - const LocalPrintJob& job, - const std::string& job_id, - int* expires_in, - std::string* error_description, - int* timeout) { - return print_job_handler_->CompleteLocalPrintJob(job, job_id, expires_in, - error_description, timeout); -} - -bool Printer::GetJobState(const std::string& id, LocalPrintJob::Info* info) { - return print_job_handler_->GetJobState(id, info); -} - -void Printer::OnRegistrationStartResponseParsed( - const std::string& registration_token, - const std::string& complete_invite_url, - const std::string& device_id) { - state_.registration_state = PrinterState::REGISTRATION_CLAIM_TOKEN_READY; - state_.device_id = device_id; - state_.registration_token = registration_token; - state_.complete_invite_url = complete_invite_url; -} - -void Printer::OnRegistrationFinished(const std::string& refresh_token, - const std::string& access_token, - int access_token_expires_in_seconds) { - InvalidateRegistrationExpiration(); - - state_.registration_state = PrinterState::REGISTERED; - state_.refresh_token = refresh_token; - RememberAccessToken(access_token, access_token_expires_in_seconds); - TryConnect(); -} - -void Printer::OnAccesstokenReceviced(const std::string& access_token, - int expires_in_seconds) { - VLOG(3) << "Function: " << __FUNCTION__; - RememberAccessToken(access_token, expires_in_seconds); - switch (connection_state_) { - case ONLINE: - PostOnIdle(); - break; - - case CONNECTING: - TryConnect(); - break; - - default: - NOTREACHED(); - } -} - -void Printer::OnXmppJidReceived(const std::string& xmpp_jid) { - state_.xmpp_jid = xmpp_jid; -} - -void Printer::OnRegistrationError(const std::string& description) { - LOG(ERROR) << "server_error: " << description; - - SetRegistrationError(description); -} - -void Printer::OnNetworkError() { - VLOG(3) << "Function: " << __FUNCTION__; - FallOffline(false); -} - -void Printer::OnServerError(const std::string& description) { - VLOG(3) << "Function: " << __FUNCTION__; - LOG(ERROR) << "Server error: " << description; - FallOffline(false); -} - -void Printer::OnPrintJobsAvailable(const std::vector<Job>& jobs) { - VLOG(3) << "Function: " << __FUNCTION__; - - VLOG(0) << "Available printjobs: " << jobs.size(); - if (jobs.empty()) { - pending_print_jobs_check_ = false; - PostOnIdle(); - return; - } - - VLOG(0) << "Downloading printjob."; - requester_->RequestPrintJob(jobs[0]); - return; -} - -void Printer::OnPrintJobDownloaded(const Job& job) { - VLOG(3) << "Function: " << __FUNCTION__; - print_job_handler_->SavePrintJob(job.file, job.ticket, job.create_time, - job.job_id, job.title, "pdf"); - requester_->SendPrintJobDone(job.job_id); -} - -void Printer::OnPrintJobDone() { - VLOG(3) << "Function: " << __FUNCTION__; - PostOnIdle(); -} - -void Printer::OnLocalSettingsReceived(LocalSettings::State state, - const LocalSettings& settings) { - pending_local_settings_check_ = false; - switch (state) { - case LocalSettings::CURRENT: - VLOG(0) << "No new local settings"; - PostOnIdle(); - break; - case LocalSettings::PENDING: - VLOG(0) << "New local settings were received"; - ApplyLocalSettings(settings); - break; - case LocalSettings::PRINTER_DELETED: - LOG(WARNING) << "Printer was deleted on server"; - pending_deletion_ = true; - PostOnIdle(); - break; - - default: - NOTREACHED(); - } -} - -void Printer::OnLocalSettingsUpdated() { - PostOnIdle(); -} - -void Printer::OnXmppConnected() { - pending_local_settings_check_ = true; - pending_print_jobs_check_ = true; - ChangeState(ONLINE); - PostOnIdle(); -} - -void Printer::OnXmppAuthError() { - OnAuthError(); -} - -void Printer::OnXmppNetworkError() { - FallOffline(false); -} - -void Printer::OnXmppNewPrintJob(const std::string& device_id) { - DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id"; - pending_print_jobs_check_ = true; -} - -void Printer::OnXmppNewLocalSettings(const std::string& device_id) { - DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id"; - pending_local_settings_check_ = true; -} - -void Printer::OnXmppDeleteNotification(const std::string& device_id) { - DCHECK_EQ(state_.device_id, device_id) << "Data should contain printer_id"; - pending_deletion_ = true; -} - -void Printer::TryConnect() { - VLOG(3) << "Function: " << __FUNCTION__; - - ChangeState(CONNECTING); - if (!requester_) - requester_.reset(new CloudPrintRequester(GetTaskRunner(), this)); - - if (IsRegistered()) { - if (state_.access_token_update < base::Time::Now()) { - requester_->UpdateAccesstoken(state_.refresh_token); - } else { - ConnectXmpp(); - } - } else { - // TODO(maksymb): Ping google.com to check connection state. - ChangeState(ONLINE); - } -} - -void Printer::ConnectXmpp() { - xmpp_listener_.reset( - new CloudPrintXmppListener(state_.xmpp_jid, - state_.local_settings.xmpp_timeout_value, - GetTaskRunner(), this)); - xmpp_listener_->Connect(state_.access_token); -} - -void Printer::OnIdle() { - DCHECK(IsRegistered()); - DCHECK(on_idle_posted_) << "Instant call is not allowed"; - on_idle_posted_ = false; - - if (connection_state_ != ONLINE) - return; - - if (pending_deletion_) { - OnPrinterDeleted(); - return; - } - - if (state_.access_token_update < base::Time::Now()) { - requester_->UpdateAccesstoken(state_.refresh_token); - return; - } - - // TODO(maksymb): Check if privet-accesstoken was requested. - - if (pending_local_settings_check_) { - GetLocalSettings(); - return; - } - - if (pending_print_jobs_check_) { - FetchPrintJobs(); - return; - } - - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&Printer::PostOnIdle, AsWeakPtr()), - base::TimeDelta::FromMilliseconds(1000)); -} - -void Printer::FetchPrintJobs() { - VLOG(3) << "Function: " << __FUNCTION__; - DCHECK(IsRegistered()); - requester_->FetchPrintJobs(state_.device_id); -} - -void Printer::GetLocalSettings() { - VLOG(3) << "Function: " << __FUNCTION__; - DCHECK(IsRegistered()); - requester_->RequestLocalSettings(state_.device_id); -} - -void Printer::ApplyLocalSettings(const LocalSettings& settings) { - state_.local_settings = settings; - SaveToFile(); - - if (state_.local_settings.local_discovery) { - bool success = StartLocalDiscoveryServers(); - if (!success) - LOG(ERROR) << "Local discovery servers cannot be started"; - // TODO(maksymb): If start failed try to start them again after some timeout - } else { - dns_server_.Shutdown(); - http_server_.Shutdown(); - } - xmpp_listener_->set_ping_interval(state_.local_settings.xmpp_timeout_value); - - requester_->SendLocalSettings(state_.device_id, state_.local_settings); -} - -void Printer::OnPrinterDeleted() { - pending_deletion_ = false; - - state_ = PrinterState(); - - SaveToFile(); - Stop(); - Start(); -} - -void Printer::RememberAccessToken(const std::string& access_token, - int expires_in_seconds) { - using base::Time; - using base::TimeDelta; - state_.access_token = access_token; - int64_t time_to_update = - static_cast<int64_t>(expires_in_seconds * kTimeToNextAccessTokenUpdate); - state_.access_token_update = - Time::Now() + TimeDelta::FromSeconds(time_to_update); - VLOG(0) << "Current access_token: " << access_token; - SaveToFile(); -} - -void Printer::SetRegistrationError(const std::string& description) { - DCHECK(!IsRegistered()); - state_.registration_state = PrinterState::REGISTRATION_ERROR; - state_.error_description = description; -} - -PrivetHttpServer::RegistrationErrorStatus Printer::CheckCommonRegErrors( - const std::string& user) { - CheckRegistrationExpiration(); - DCHECK(!IsRegistered()); - if (connection_state_ != ONLINE) - return PrivetHttpServer::REG_ERROR_OFFLINE; - - if (state_.registration_state != PrinterState::UNREGISTERED && - user != state_.user) { - return PrivetHttpServer::REG_ERROR_DEVICE_BUSY; - } - - if (state_.registration_state == PrinterState::REGISTRATION_ERROR) - return PrivetHttpServer::REG_ERROR_SERVER_ERROR; - - DCHECK_EQ(connection_state_, ONLINE); - - return PrivetHttpServer::REG_ERROR_OK; -} - -void Printer::WaitUserConfirmation(base::Time valid_until) { - // TODO(maksymb): Move to separate class. - - if (base::Time::Now() > valid_until) { - state_.confirmation_state = PrinterState::CONFIRMATION_TIMEOUT; - VLOG(0) << "Confirmation timeout reached."; - return; - } - - if (_kbhit()) { - int c = _getche(); - if (c == 'y' || c == 'Y') { - state_.confirmation_state = PrinterState::CONFIRMATION_CONFIRMED; - VLOG(0) << "Registration confirmed by user."; - } else { - state_.confirmation_state = PrinterState::CONFIRMATION_DISCARDED; - VLOG(0) << "Registration discarded by user."; - } - return; - } - - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&Printer::WaitUserConfirmation, AsWeakPtr(), valid_until), - base::TimeDelta::FromMilliseconds(100)); -} - -std::string Printer::GenerateProxyId() const { - return "{" + base::GenerateGUID() +"}"; -} - -std::vector<std::string> Printer::CreateTxt() const { - std::vector<std::string> txt; - txt.push_back("txtvers=1"); - txt.push_back("ty=" + std::string(kPrinterName)); - txt.push_back("note=" + std::string(GetDescription())); - txt.push_back("url=" + std::string(kCloudPrintUrl)); - txt.push_back("type=printer"); - txt.push_back("id=" + state_.device_id); - txt.push_back("cs=" + ConnectionStateToString(connection_state_)); - - return txt; -} - -void Printer::SaveToFile() { - GetCapabilities(); // Make sure capabilities created. - base::FilePath file_path; - file_path = file_path.AppendASCII( - command_line_reader::ReadStatePath(kPrinterStatePathDefault)); - - if (printer_state::SaveToFile(file_path, state_)) { - VLOG(0) << "Printer state written to file"; - } else { - VLOG(0) << "Cannot write printer state to file"; - } -} - -bool Printer::LoadFromFile() { - state_ = PrinterState(); - - base::FilePath file_path; - file_path = file_path.AppendASCII( - command_line_reader::ReadStatePath(kPrinterStatePathDefault)); - - if (!base::PathExists(file_path)) { - VLOG(0) << "Printer state file not found"; - return false; - } - - if (printer_state::LoadFromFile(file_path, &state_)) { - VLOG(0) << "Printer state loaded from file"; - SaveToFile(); - } else { - VLOG(0) << "Reading/parsing printer state from file failed"; - } - - return true; -} - -void Printer::PostOnIdle() { - VLOG(3) << "Function: " << __FUNCTION__; - DCHECK(!on_idle_posted_) << "Only one instance can be posted."; - on_idle_posted_ = true; - - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&Printer::OnIdle, AsWeakPtr())); -} - -void Printer::CheckRegistrationExpiration() { - if (!registration_expiration_.is_null() && - registration_expiration_ < base::Time::Now()) { - state_ = PrinterState(); - InvalidateRegistrationExpiration(); - } -} - -void Printer::UpdateRegistrationExpiration() { - registration_expiration_ = - base::Time::Now() + base::TimeDelta::FromSeconds(kRegistrationTimeout); -} - -void Printer::InvalidateRegistrationExpiration() { - registration_expiration_ = base::Time(); -} - -bool Printer::StartLocalDiscoveryServers() { - if (!StartHttpServer()) - return false; - if (!StartDnsServer()) { - http_server_.Shutdown(); - return false; - } - return true; -} - -bool Printer::StartDnsServer() { - DCHECK(state_.local_settings.local_discovery); - - net::IPAddressNumber ipv4; - net::IPAddressNumber ipv6; - - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableIpv4)) { - ipv4 = GetLocalIp("", false); - } - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableIpv6)) { - ipv6 = GetLocalIp("", true); - } - - // TODO(maksymb): Add switch for command line to control interface name. - if (ipv4.empty() && ipv6.empty()) { - LOG(ERROR) << "No local IP found. Cannot start printer."; - return false; - } - - uint16_t port = command_line_reader::ReadHttpPort(kHttpPortDefault); - - VLOG_IF(0, !ipv4.empty()) - << "Local IPv4 address: " << net::IPAddressToStringWithPort(ipv4, port); - VLOG_IF(0, !ipv6.empty()) - << "Local IPv6 address: " << net::IPAddressToStringWithPort(ipv6, port); - - std::string service_name_prefix = kServiceNamePrefixDefault; - if (!ipv4.empty()) - service_name_prefix += net::IPAddressToString(ipv4); - service_name_prefix = - command_line_reader::ReadServiceNamePrefix(service_name_prefix); - std::replace( - service_name_prefix.begin(), service_name_prefix.end(), '.', '_'); - - std::string service_domain_name = - command_line_reader::ReadDomainName( - base::StringPrintf(kServiceDomainNameFormatDefault, - base::RandInt(0, INT_MAX))); - - ServiceParameters params(kServiceType, - kSecondaryServiceType, - service_name_prefix, - service_domain_name, - ipv4, - ipv6, - port); - - return dns_server_.Start(params, - command_line_reader::ReadTtl(kTtlDefault), - CreateTxt()); -} - -bool Printer::StartHttpServer() { - DCHECK(state_.local_settings.local_discovery); - using command_line_reader::ReadHttpPort; - return http_server_.Start(ReadHttpPort(kHttpPortDefault)); -} - -PrivetHttpServer::RegistrationErrorStatus -Printer::ConfirmationToRegistrationError( - PrinterState::ConfirmationState state) { - switch (state) { - case PrinterState::CONFIRMATION_PENDING: - return PrivetHttpServer::REG_ERROR_PENDING_USER_ACTION; - case PrinterState::CONFIRMATION_DISCARDED: - return PrivetHttpServer::REG_ERROR_USER_CANCEL; - case PrinterState::CONFIRMATION_CONFIRMED: - NOTREACHED(); - return PrivetHttpServer::REG_ERROR_OK; - case PrinterState::CONFIRMATION_TIMEOUT: - return PrivetHttpServer::REG_ERROR_CONFIRMATION_TIMEOUT; - default: - NOTREACHED(); - return PrivetHttpServer::REG_ERROR_OK; - } -} - -std::string Printer::ConnectionStateToString(ConnectionState state) const { - switch (state) { - case OFFLINE: - return "offline"; - case ONLINE: - return "online"; - case CONNECTING: - return "connecting"; - case NOT_CONFIGURED: - return "not-configured"; - - default: - NOTREACHED(); - return ""; - } -} - -void Printer::FallOffline(bool instant_reconnect) { - bool changed = ChangeState(OFFLINE); - DCHECK(changed) << "Falling offline from offline is now allowed"; - - if (!IsRegistered()) - SetRegistrationError("Cannot access server during registration process"); - - if (instant_reconnect) { - TryConnect(); - } else { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&Printer::TryConnect, AsWeakPtr()), - base::TimeDelta::FromSeconds(kReconnectTimeout)); - } -} - -bool Printer::ChangeState(ConnectionState new_state) { - if (connection_state_ == new_state) - return false; - - connection_state_ = new_state; - VLOG(0) << base::StringPrintf( - "Printer is now %s (%s)", - ConnectionStateToString(connection_state_).c_str(), - IsRegistered() ? "registered" : "unregistered"); - - dns_server_.UpdateMetadata(CreateTxt()); - - if (connection_state_ == OFFLINE) { - requester_.reset(); - xmpp_listener_.reset(); - } - - return true; -}
diff --git a/cloud_print/gcp20/prototype/printer.h b/cloud_print/gcp20/prototype/printer.h deleted file mode 100644 index bfcb16e..0000000 --- a/cloud_print/gcp20/prototype/printer.h +++ /dev/null
@@ -1,258 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_PRINTER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_PRINTER_H_ - -#include <string> -#include <vector> - -#include "base/files/file_path.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "cloud_print/gcp20/prototype/cloud_print_requester.h" -#include "cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h" -#include "cloud_print/gcp20/prototype/dns_sd_server.h" -#include "cloud_print/gcp20/prototype/print_job_handler.h" -#include "cloud_print/gcp20/prototype/printer_state.h" -#include "cloud_print/gcp20/prototype/privet_http_server.h" -#include "cloud_print/gcp20/prototype/x_privet_token.h" - -extern const base::FilePath::CharType kPrinterStatePath[]; - -// This class maintains work of DNS-SD server, HTTP server and others. -class Printer : public base::SupportsWeakPtr<Printer>, - public PrivetHttpServer::Delegate, - public CloudPrintRequester::Delegate, - public CloudPrintXmppListener::Delegate { - public: - // Constructs uninitialized object. - Printer(); - - // Destroys the object. - ~Printer() override; - - // Starts all servers. - bool Start(); - - // Returns true if printer was started. - bool IsRunning() const; - - // Stops all servers. - void Stop(); - - private: - FRIEND_TEST_ALL_PREFIXES(Printer, ValidateCapabilities); - - enum ConnectionState { - NOT_CONFIGURED, - OFFLINE, - ONLINE, - CONNECTING - }; - - std::string GetRawCdd(); - - // PrivetHttpServer::Delegate methods: - PrivetHttpServer::RegistrationErrorStatus RegistrationStart( - const std::string& user) override; - PrivetHttpServer::RegistrationErrorStatus RegistrationGetClaimToken( - const std::string& user, - std::string* token, - std::string* claim_url) override; - PrivetHttpServer::RegistrationErrorStatus RegistrationComplete( - const std::string& user, - std::string* device_id) override; - PrivetHttpServer::RegistrationErrorStatus RegistrationCancel( - const std::string& user) override; - void GetRegistrationServerError(std::string* description) override; - void CreateInfo(PrivetHttpServer::DeviceInfo* info) override; - bool IsRegistered() const override; - bool IsLocalPrintingAllowed() const override; - bool CheckXPrivetTokenHeader(const std::string& token) const override; - const base::DictionaryValue& GetCapabilities() override; - LocalPrintJob::CreateResult CreateJob( - const std::string& ticket, - std::string* job_id, - int* expires_in, - int* error_timeout, - std::string* error_description) override; - LocalPrintJob::SaveResult SubmitDoc( - const LocalPrintJob& job, - std::string* job_id, - int* expires_in, - std::string* error_description, - int* timeout) override; - LocalPrintJob::SaveResult SubmitDocWithId( - const LocalPrintJob& job, - const std::string& job_id, - int* expires_in, - std::string* error_description, - int* timeout) override; - bool GetJobState(const std::string& id, LocalPrintJob::Info* info) override; - - // CloudRequester::Delegate methods: - void OnRegistrationStartResponseParsed( - const std::string& registration_token, - const std::string& complete_invite_url, - const std::string& device_id) override; - void OnRegistrationFinished( - const std::string& refresh_token, - const std::string& access_token, - int access_token_expires_in_seconds) override; - void OnXmppJidReceived(const std::string& xmpp_jid) override; - void OnAccesstokenReceviced(const std::string& access_token, - int expires_in_seconds) override; - void OnRegistrationError(const std::string& description) override; - void OnNetworkError() override; - void OnServerError(const std::string& description) override; - void OnAuthError() override; - std::string GetAccessToken() override; - void OnPrintJobsAvailable( - const std::vector<cloud_print_response_parser::Job>& jobs) override; - void OnPrintJobDownloaded( - const cloud_print_response_parser::Job& job) override; - void OnPrintJobDone() override; - void OnLocalSettingsReceived( - LocalSettings::State state, - const LocalSettings& settings) override; - void OnLocalSettingsUpdated() override; - - // CloudPrintXmppListener::Delegate methods: - void OnXmppConnected() override; - void OnXmppAuthError() override; - void OnXmppNetworkError() override; - void OnXmppNewPrintJob(const std::string& device_id) override; - void OnXmppNewLocalSettings(const std::string& device_id) override; - void OnXmppDeleteNotification(const std::string& device_id) override; - - // Method for trying to reconnecting to server on start or after network fail. - void TryConnect(); - - // Connects XMPP. - void ConnectXmpp(); - - // Method to handle pending events. - // Do *NOT* call this method instantly. Only with |PostOnIdle|. - void OnIdle(); - - // Ask Cloud Print server for printjobs. - void FetchPrintJobs(); - - // Ask Cloud Print server for new local sendings. - void GetLocalSettings(); - - // Applies new local settings to printer. - void ApplyLocalSettings(const LocalSettings& settings); - - // Used for erasing all printer info. - void OnPrinterDeleted(); - - // Saves |access_token| and calculates time for next update. - void RememberAccessToken(const std::string& access_token, - int expires_in_seconds); - - // Sets registration state to error and adds description. - void SetRegistrationError(const std::string& description); - - // Checks if register call is called correctly (|user| is correct, - // error is no set etc). Returns |false| if error status is put into |status|. - // Otherwise no error was occurred. - PrivetHttpServer::RegistrationErrorStatus CheckCommonRegErrors( - const std::string& user); - - // Checks if confirmation was received. - void WaitUserConfirmation(base::Time valid_until); - - // Generates ProxyId for this device. - std::string GenerateProxyId() const; - - // Creates data for DNS TXT respond. - std::vector<std::string> CreateTxt() const; - - // Saving and loading registration info from file. - void SaveToFile(); - bool LoadFromFile(); - - // Adds |OnIdle| method to the MessageLoop. - void PostOnIdle(); - - // Registration timeout. - void CheckRegistrationExpiration(); - - // Delays expiration after user action. - void UpdateRegistrationExpiration(); - - // Deletes registration expiration at all. - void InvalidateRegistrationExpiration(); - - // Methods to start HTTP and DNS-SD servers. Return |true| if servers - // were started. If failed neither HTTP nor DNS-SD server will be running. - bool StartLocalDiscoveryServers(); - - // Methods to start HTTP and DNS-SD servers. Return |true| if servers - // were started. - bool StartDnsServer(); - bool StartHttpServer(); - - // Converts errors. - PrivetHttpServer::RegistrationErrorStatus ConfirmationToRegistrationError( - PrinterState::ConfirmationState state); - - std::string ConnectionStateToString(ConnectionState state) const; - - // Changes state to OFFLINE and posts TryReconnect. - // For registration reconnect is instant every time. - void FallOffline(bool instant_reconnect); - - // Changes state and update info in DNS server. Returns |true| if state - // was changed (otherwise state was the same). - bool ChangeState(ConnectionState new_state); - - // Contains printers workflow info. - PrinterState state_; - - // Connection state of device. - ConnectionState connection_state_; - - // Contains DNS-SD server. - DnsSdServer dns_server_; - - // Contains Privet HTTP server. - PrivetHttpServer http_server_; - - // Contains CloudPrint client. - scoped_ptr<CloudPrintRequester> requester_; - - // XMPP Listener. - scoped_ptr<CloudPrintXmppListener> xmpp_listener_; - - XPrivetToken xtoken_; - - scoped_ptr<PrintJobHandler> print_job_handler_; - - // Uses for calculating uptime. - base::Time starttime_; - - // Uses to validate registration timeout. - base::Time registration_expiration_; - - // Used for preventing two and more OnIdle posted in message loop. - bool on_idle_posted_; - - // Contains |true| if Printer has to check pending local settings. - bool pending_local_settings_check_; - - // Contains |true| if Printer has to check available printjobs. - bool pending_print_jobs_check_; - - // Contains |true| if Printer has to be deleted. - bool pending_deletion_; - - DISALLOW_COPY_AND_ASSIGN(Printer); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_PRINTER_H_ -
diff --git a/cloud_print/gcp20/prototype/printer_state.cc b/cloud_print/gcp20/prototype/printer_state.cc deleted file mode 100644 index 0d8fead..0000000 --- a/cloud_print/gcp20/prototype/printer_state.cc +++ /dev/null
@@ -1,177 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/printer_state.h" - -#include "base/files/file_util.h" -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/logging.h" -#include "base/numerics/safe_conversions.h" -#include "base/values.h" - -namespace { - -const char kRegistered[] = "registered"; -const char kUser[] = "user"; -const char kDeviceId[] = "device_id"; -const char kRefreshToken[] = "refresh_token"; -const char kXmppJid[] = "xmpp_jid"; -const char kAccessToken[] = "access_token"; -const char kAccessTokenUpdate[] = "access_token_update"; - -const char kLocalSettings[] = "local_settings"; -const char kCdd[] = "cdd"; -const char kLocalSettingsLocalDiscovery[] = "local_discovery"; -const char kLocalSettingsAccessTokenEnabled[] = "access_token_enabled"; -const char kLocalSettingsLocalPrintingEnabled[] = - "printer/local_printing_enabled"; -const char kLocalSettingsXmppTimeoutValue[] = "xmpp_timeout_value"; - -} // namespace - -PrinterState::PrinterState() - : registration_state(UNREGISTERED), - confirmation_state(CONFIRMATION_PENDING) { -} - -PrinterState::~PrinterState() { -} - -namespace printer_state { - -bool SaveToFile(const base::FilePath& path, const PrinterState& state) { - base::DictionaryValue json; - if (state.registration_state == PrinterState::REGISTERED) { - json.SetBoolean(kRegistered, true); - json.SetString(kUser, state.user); - json.SetString(kDeviceId, state.device_id); - json.SetString(kRefreshToken, state.refresh_token); - json.SetString(kXmppJid, state.xmpp_jid); - json.SetString(kAccessToken, state.access_token); - json.SetInteger(kAccessTokenUpdate, - static_cast<int>(state.access_token_update.ToTimeT())); - - scoped_ptr<base::DictionaryValue> local_settings(new base::DictionaryValue); - local_settings->SetBoolean(kLocalSettingsLocalDiscovery, - state.local_settings.local_discovery); - local_settings->SetBoolean(kLocalSettingsAccessTokenEnabled, - state.local_settings.access_token_enabled); - local_settings->SetBoolean(kLocalSettingsLocalPrintingEnabled, - state.local_settings.local_printing_enabled); - local_settings->SetInteger(kLocalSettingsXmppTimeoutValue, - state.local_settings.xmpp_timeout_value); - json.Set(kLocalSettings, local_settings.release()); - } else { - json.SetBoolean(kRegistered, false); - } - - if (state.cdd.get()) - json.Set(kCdd, state.cdd->DeepCopy()); - - std::string json_str; - base::JSONWriter::WriteWithOptions(json, - base::JSONWriter::OPTIONS_PRETTY_PRINT, - &json_str); - int size = base::checked_cast<int>(json_str.size()); - return (base::WriteFile(path, json_str.data(), size) == size); -} - -bool LoadFromFile(const base::FilePath& path, PrinterState* state) { - std::string json_str; - if (!base::ReadFileToString(path, &json_str)) { - LOG(ERROR) << "Cannot open file."; - return false; - } - - scoped_ptr<base::Value> json_val(base::JSONReader::Read(json_str)); - base::DictionaryValue* json = NULL; - if (!json_val || !json_val->GetAsDictionary(&json)) { - LOG(ERROR) << "Cannot read JSON dictionary from file."; - return false; - } - - bool registered = false; - if (!json->GetBoolean(kRegistered, ®istered)) { - LOG(ERROR) << "Cannot parse |registered| state."; - return false; - } - - if (!registered) - return true; - - std::string user; - if (!json->GetString(kUser, &user)) { - LOG(ERROR) << "Cannot parse |user|."; - return false; - } - - std::string device_id; - if (!json->GetString(kDeviceId, &device_id)) { - LOG(ERROR) << "Cannot parse |device_id|."; - return false; - } - - std::string refresh_token; - if (!json->GetString(kRefreshToken, &refresh_token)) { - LOG(ERROR) << "Cannot parse |refresh_token|."; - return false; - } - - std::string xmpp_jid; - if (!json->GetString(kXmppJid, &xmpp_jid)) { - LOG(ERROR) << "Cannot parse |xmpp_jid|."; - return false; - } - - std::string access_token; - if (!json->GetString(kAccessToken, &access_token)) { - LOG(ERROR) << "Cannot parse |access_token|."; - return false; - } - - int access_token_update; - if (!json->GetInteger(kAccessTokenUpdate, &access_token_update)) { - LOG(ERROR) << "Cannot parse |access_token_update|."; - return false; - } - - LocalSettings local_settings; - base::DictionaryValue* settings_dict; - if (!json->GetDictionary(kLocalSettings, &settings_dict)) { - LOG(WARNING) << "Cannot read |local_settings|. Reset to default."; - } else { - if (!settings_dict->GetBoolean(kLocalSettingsLocalDiscovery, - &local_settings.local_discovery) || - !settings_dict->GetBoolean(kLocalSettingsAccessTokenEnabled, - &local_settings.access_token_enabled) || - !settings_dict->GetBoolean(kLocalSettingsLocalPrintingEnabled, - &local_settings.local_printing_enabled) || - !settings_dict->GetInteger(kLocalSettingsXmppTimeoutValue, - &local_settings.xmpp_timeout_value)) { - LOG(WARNING) << "Cannot parse |local_settings|. Reset to default."; - local_settings = LocalSettings(); - } - } - - base::DictionaryValue* cdd_dict = NULL; - if (!json->GetDictionary(kCdd, &cdd_dict)) - LOG(WARNING) << "Cannot read |cdd|. Reset to default."; - - *state = PrinterState(); - state->registration_state = PrinterState::REGISTERED; - state->user = user; - state->device_id = device_id; - state->refresh_token = refresh_token; - state->xmpp_jid = xmpp_jid; - state->access_token = access_token; - state->access_token_update = base::Time::FromTimeT(access_token_update); - state->local_settings = local_settings; - if (cdd_dict) - state->cdd.reset(cdd_dict->DeepCopy()); - return true; -} - -} // namespace printer_state -
diff --git a/cloud_print/gcp20/prototype/printer_state.h b/cloud_print/gcp20/prototype/printer_state.h deleted file mode 100644 index 50638dc..0000000 --- a/cloud_print/gcp20/prototype/printer_state.h +++ /dev/null
@@ -1,75 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_PRINTER_STATE_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_PRINTER_STATE_H_ - -#include <string> - -#include "base/memory/linked_ptr.h" -#include "base/time/time.h" -#include "cloud_print/gcp20/prototype/local_settings.h" - -namespace base { -class DictionaryValue; -class FilePath; -} // namespace base - -struct PrinterState { - enum RegistrationState { - UNREGISTERED, - REGISTRATION_STARTED, // |action=start| was called, - // request to CloudPrint was sent. - REGISTRATION_CLAIM_TOKEN_READY, // The same as previous, but request - // reply is already received. - REGISTRATION_COMPLETING, // |action=complete| was called, - // |complete| request was sent. - REGISTRATION_ERROR, // Is set when server error was occurred. - REGISTERED, - }; - - enum ConfirmationState { - CONFIRMATION_PENDING, - CONFIRMATION_CONFIRMED, - CONFIRMATION_DISCARDED, - CONFIRMATION_TIMEOUT, - }; - - PrinterState(); - ~PrinterState(); - - // Registration process info - std::string user; - std::string registration_token; - std::string complete_invite_url; - RegistrationState registration_state; - ConfirmationState confirmation_state; - - // Printer workflow info - std::string refresh_token; - std::string device_id; - std::string xmpp_jid; - LocalSettings local_settings; - linked_ptr<base::DictionaryValue> cdd; - - // Last valid |access_token|. - std::string access_token; - base::Time access_token_update; - - // Contains error if |REGISTRATION_ERROR| is set. - std::string error_description; -}; - -namespace printer_state { - -// -bool SaveToFile(const base::FilePath& path, const PrinterState& state); - -// -bool LoadFromFile(const base::FilePath& path, PrinterState* state); - -} // namespace printer_state - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_PRINTER_STATE_H_ -
diff --git a/cloud_print/gcp20/prototype/printer_unittest.cc b/cloud_print/gcp20/prototype/printer_unittest.cc deleted file mode 100644 index 1f189f6..0000000 --- a/cloud_print/gcp20/prototype/printer_unittest.cc +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/printer.h" - -#include "base/json/json_reader.h" -#include "base/memory/scoped_ptr.h" -#include "base/values.h" -#include "testing/gtest/include/gtest/gtest.h" - -TEST(Printer, ValidateCapabilities) { - int error_code; - std::string error_msg; - Printer printer; - scoped_ptr<base::Value> value( - base::JSONReader::ReadAndReturnError(printer.GetRawCdd(), 0, - &error_code, &error_msg)); - ASSERT_TRUE(value) << error_msg; - - base::DictionaryValue* dictionary_value = NULL; - EXPECT_TRUE(value->GetAsDictionary(&dictionary_value)) << "Not a dictionary"; -} -
diff --git a/cloud_print/gcp20/prototype/privet_http_server.cc b/cloud_print/gcp20/prototype/privet_http_server.cc deleted file mode 100644 index 50c1829a..0000000 --- a/cloud_print/gcp20/prototype/privet_http_server.cc +++ /dev/null
@@ -1,515 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/privet_http_server.h" - -#include <stddef.h> -#include <stdint.h> -#include <utility> - -#include "base/command_line.h" -#include "base/json/json_writer.h" -#include "base/strings/stringprintf.h" -#include "cloud_print/gcp20/prototype/gcp20_switches.h" -#include "net/base/ip_endpoint.h" -#include "net/base/net_errors.h" -#include "net/base/url_util.h" -#include "net/socket/tcp_server_socket.h" -#include "url/gurl.h" - -namespace { - -const int kDeviceBusyTimeout = 30; // in seconds -const int kPendingUserActionTimeout = 5; // in seconds - -const char kPrivetInfo[] = "/privet/info"; -const char kPrivetRegister[] = "/privet/register"; -const char kPrivetCapabilities[] = "/privet/capabilities"; -const char kPrivetPrinterCreateJob[] = "/privet/printer/createjob"; -const char kPrivetPrinterSubmitDoc[] = "/privet/printer/submitdoc"; -const char kPrivetPrinterJobState[] = "/privet/printer/jobstate"; - -// {"error":|error_type|} -scoped_ptr<base::DictionaryValue> CreateError(const std::string& error_type) { - scoped_ptr<base::DictionaryValue> error(new base::DictionaryValue); - error->SetString("error", error_type); - - return error; -} - -// {"error":|error_type|, "description":|description|} -scoped_ptr<base::DictionaryValue> CreateErrorWithDescription( - const std::string& error_type, - const std::string& description) { - scoped_ptr<base::DictionaryValue> error(CreateError(error_type)); - error->SetString("description", description); - return error; -} - -// {"error":|error_type|, "timeout":|timeout|} -scoped_ptr<base::DictionaryValue> CreateErrorWithTimeout( - const std::string& error_type, - int timeout) { - scoped_ptr<base::DictionaryValue> error(CreateError(error_type)); - error->SetInteger("timeout", timeout); - return error; -} - -// Converts state to string. -std::string LocalPrintJobStateToString(LocalPrintJob::State state) { - switch (state) { - case LocalPrintJob::STATE_DRAFT: - return "draft"; - break; - case LocalPrintJob::STATE_ABORTED: - return "done"; - break; - case LocalPrintJob::STATE_DONE: - return "done"; - break; - default: - NOTREACHED(); - return std::string(); - } -} - - -// Returns |true| if |request| should be GET method. -bool IsGetMethod(const std::string& request) { - return request == kPrivetInfo || - request == kPrivetCapabilities || - request == kPrivetPrinterJobState; -} - -// Returns |true| if |request| should be POST method. -bool IsPostMethod(const std::string& request) { - return request == kPrivetRegister || - request == kPrivetPrinterCreateJob || - request == kPrivetPrinterSubmitDoc; -} - -} // namespace - -PrivetHttpServer::DeviceInfo::DeviceInfo() : uptime(0) { -} - -PrivetHttpServer::DeviceInfo::~DeviceInfo() { -} - -PrivetHttpServer::PrivetHttpServer(Delegate* delegate) - : port_(0), - delegate_(delegate) { -} - -PrivetHttpServer::~PrivetHttpServer() { - Shutdown(); -} - -bool PrivetHttpServer::Start(uint16_t port) { - if (server_) - return true; - - scoped_ptr<net::ServerSocket> server_socket( - new net::TCPServerSocket(NULL, net::NetLog::Source())); - std::string listen_address = "::"; - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableIpv6)) - listen_address = "0.0.0.0"; - server_socket->ListenWithAddressAndPort(listen_address, port, 1); - server_.reset(new net::HttpServer(std::move(server_socket), this)); - - net::IPEndPoint address; - if (server_->GetLocalAddress(&address) != net::OK) { - NOTREACHED() << "Cannot start HTTP server"; - return false; - } - - VLOG(1) << "Address of HTTP server: " << address.ToString(); - return true; -} - -void PrivetHttpServer::Shutdown() { - if (!server_) - return; - - server_.reset(NULL); -} - -void PrivetHttpServer::OnHttpRequest(int connection_id, - const net::HttpServerRequestInfo& info) { - VLOG(1) << "Processing HTTP request: " << info.path; - GURL url("http://host" + info.path); - - if (!ValidateRequestMethod(connection_id, url.path(), info.method)) - return; - - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableXTocken)) { - net::HttpServerRequestInfo::HeadersMap::const_iterator iter = - info.headers.find("x-privet-token"); - if (iter == info.headers.end()) { - server_->Send(connection_id, net::HTTP_BAD_REQUEST, - "Missing X-Privet-Token header.\n" - "TODO: Message should be in header, not in the body!", - "text/plain"); - return; - } - - if (url.path() != kPrivetInfo && - !delegate_->CheckXPrivetTokenHeader(iter->second)) { - server_->Send(connection_id, net::HTTP_OK, - "{\"error\":\"invalid_x_privet_token\"}", - "application/json"); - return; - } - } - - std::string response; - net::HttpStatusCode status_code = ProcessHttpRequest(url, info, &response); - - server_->Send(connection_id, status_code, response, "application/json"); -} - -void PrivetHttpServer::OnWebSocketRequest( - int connection_id, - const net::HttpServerRequestInfo& info) { -} - -void PrivetHttpServer::OnWebSocketMessage(int connection_id, - const std::string& data) { -} - -void PrivetHttpServer::OnClose(int connection_id) { -} - -void PrivetHttpServer::ReportInvalidMethod(int connection_id) { - server_->Send(connection_id, net::HTTP_BAD_REQUEST, "Invalid method", - "text/plain"); -} - -bool PrivetHttpServer::ValidateRequestMethod(int connection_id, - const std::string& request, - const std::string& method) { - DCHECK(!IsGetMethod(request) || !IsPostMethod(request)); - - if (!IsGetMethod(request) && !IsPostMethod(request)) { - server_->Send404(connection_id); - return false; - } - - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - "disable-method-check")) - return true; - - if ((IsGetMethod(request) && method != "GET") || - (IsPostMethod(request) && method != "POST")) { - ReportInvalidMethod(connection_id); - return false; - } - - return true; -} - -net::HttpStatusCode PrivetHttpServer::ProcessHttpRequest( - const GURL& url, - const net::HttpServerRequestInfo& info, - std::string* response) { - net::HttpStatusCode status_code = net::HTTP_OK; - scoped_ptr<base::DictionaryValue> json_response; - - if (url.path() == kPrivetInfo) { - json_response = ProcessInfo(&status_code); - } else if (url.path() == kPrivetRegister) { - json_response = ProcessRegister(url, &status_code); - } else if (url.path() == kPrivetCapabilities) { - json_response = ProcessCapabilities(&status_code); - } else if (url.path() == kPrivetPrinterCreateJob) { - json_response = ProcessCreateJob(url, info.data, &status_code); - } else if (url.path() == kPrivetPrinterSubmitDoc) { - json_response = ProcessSubmitDoc(url, info, &status_code); - } else if (url.path() == kPrivetPrinterJobState) { - json_response = ProcessJobState(url, &status_code); - } else { - NOTREACHED(); - } - - if (!json_response) { - response->clear(); - return status_code; - } - - base::JSONWriter::WriteWithOptions(*json_response, - base::JSONWriter::OPTIONS_PRETTY_PRINT, - response); - return status_code; -} - -// Privet API methods: - -scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessInfo( - net::HttpStatusCode* status_code) const { - - DeviceInfo info; - delegate_->CreateInfo(&info); - - scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue); - response->SetString("version", info.version); - response->SetString("name", info.name); - response->SetString("description", info.description); - response->SetString("url", info.url); - response->SetString("id", info.id); - response->SetString("device_state", info.device_state); - response->SetString("connection_state", info.connection_state); - response->SetString("manufacturer", info.manufacturer); - response->SetString("model", info.model); - response->SetString("serial_number", info.serial_number); - response->SetString("firmware", info.firmware); - response->SetInteger("uptime", info.uptime); - response->SetString("x-privet-token", info.x_privet_token); - - base::ListValue api; - for (size_t i = 0; i < info.api.size(); ++i) - api.AppendString(info.api[i]); - response->Set("api", api.DeepCopy()); - - base::ListValue type; - for (size_t i = 0; i < info.type.size(); ++i) - type.AppendString(info.type[i]); - response->Set("type", type.DeepCopy()); - - *status_code = net::HTTP_OK; - return response; -} - -scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessCapabilities( - net::HttpStatusCode* status_code) const { - if (!delegate_->IsRegistered()) { - *status_code = net::HTTP_NOT_FOUND; - return scoped_ptr<base::DictionaryValue>(); - } - return make_scoped_ptr(delegate_->GetCapabilities().DeepCopy()); -} - -scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessCreateJob( - const GURL& url, - const std::string& body, - net::HttpStatusCode* status_code) const { - if (!delegate_->IsRegistered() || !delegate_->IsLocalPrintingAllowed()) { - *status_code = net::HTTP_NOT_FOUND; - return scoped_ptr<base::DictionaryValue>(); - } - - std::string job_id; - // TODO(maksymb): Use base::Time for expiration - int expires_in = 0; - // TODO(maksymb): Use base::TimeDelta for timeout values - int error_timeout = -1; - std::string error_description; - - LocalPrintJob::CreateResult result = - delegate_->CreateJob(body, &job_id, &expires_in, - &error_timeout, &error_description); - - scoped_ptr<base::DictionaryValue> response; - *status_code = net::HTTP_OK; - switch (result) { - case LocalPrintJob::CREATE_SUCCESS: - response.reset(new base::DictionaryValue); - response->SetString("job_id", job_id); - response->SetInteger("expires_in", expires_in); - return response; - - case LocalPrintJob::CREATE_INVALID_TICKET: - return CreateError("invalid_ticket"); - case LocalPrintJob::CREATE_PRINTER_BUSY: - return CreateErrorWithTimeout("printer_busy", error_timeout); - case LocalPrintJob::CREATE_PRINTER_ERROR: - return CreateErrorWithDescription("printer_error", error_description); - } - return scoped_ptr<base::DictionaryValue>(); -} - -scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessSubmitDoc( - const GURL& url, - const net::HttpServerRequestInfo& info, - net::HttpStatusCode* status_code) const { - if (!delegate_->IsRegistered() || !delegate_->IsLocalPrintingAllowed()) { - *status_code = net::HTTP_NOT_FOUND; - return scoped_ptr<base::DictionaryValue>(); - } - - using net::GetValueForKeyInQuery; - - // Parse query - LocalPrintJob job; - std::string offline; - std::string job_id; - bool job_name_present = GetValueForKeyInQuery(url, "job_name", &job.job_name); - bool job_id_present = GetValueForKeyInQuery(url, "job_id", &job_id); - GetValueForKeyInQuery(url, "client_name", &job.client_name); - GetValueForKeyInQuery(url, "user_name", &job.user_name); - GetValueForKeyInQuery(url, "offline", &offline); - job.offline = (offline == "1"); - job.content_type = info.GetHeaderValue("content-type"); - job.content = info.data; - - // Call delegate - // TODO(maksymb): Use base::Time for expiration - int expires_in = 0; - std::string error_description; - int timeout; - LocalPrintJob::SaveResult status = job_id_present - ? delegate_->SubmitDocWithId(job, job_id, &expires_in, - &error_description, &timeout) - : delegate_->SubmitDoc(job, &job_id, &expires_in, - &error_description, &timeout); - - // Create response - *status_code = net::HTTP_OK; - scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue); - switch (status) { - case LocalPrintJob::SAVE_SUCCESS: - response->SetString("job_id", job_id); - response->SetInteger("expires_in", expires_in); - response->SetString("job_type", job.content_type); - response->SetString( - "job_size", - base::StringPrintf("%u", static_cast<uint32_t>(job.content.size()))); - if (job_name_present) - response->SetString("job_name", job.job_name); - return response; - - case LocalPrintJob::SAVE_INVALID_PRINT_JOB: - return CreateErrorWithTimeout("invalid_print_job", timeout); - case LocalPrintJob::SAVE_INVALID_DOCUMENT_TYPE: - return CreateError("invalid_document_type"); - case LocalPrintJob::SAVE_INVALID_DOCUMENT: - return CreateError("invalid_document"); - case LocalPrintJob::SAVE_DOCUMENT_TOO_LARGE: - return CreateError("document_too_large"); - case LocalPrintJob::SAVE_PRINTER_BUSY: - return CreateErrorWithTimeout("printer_busy", -2); - case LocalPrintJob::SAVE_PRINTER_ERROR: - return CreateErrorWithDescription("printer_error", error_description); - default: - NOTREACHED(); - return scoped_ptr<base::DictionaryValue>(); - } -} - -scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessJobState( - const GURL& url, - net::HttpStatusCode* status_code) const { - if (!delegate_->IsRegistered() || !delegate_->IsLocalPrintingAllowed()) { - *status_code = net::HTTP_NOT_FOUND; - return scoped_ptr<base::DictionaryValue>(); - } - - std::string job_id; - net::GetValueForKeyInQuery(url, "job_id", &job_id); - LocalPrintJob::Info info; - if (!delegate_->GetJobState(job_id, &info)) - return CreateError("invalid_print_job"); - - scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue); - response->SetString("job_id", job_id); - response->SetString("state", LocalPrintJobStateToString(info.state)); - response->SetInteger("expires_in", info.expires_in); - return response; -} - -scoped_ptr<base::DictionaryValue> PrivetHttpServer::ProcessRegister( - const GURL& url, - net::HttpStatusCode* status_code) const { - if (delegate_->IsRegistered()) { - *status_code = net::HTTP_NOT_FOUND; - return scoped_ptr<base::DictionaryValue>(); - } - - std::string action; - std::string user; - bool params_present = - net::GetValueForKeyInQuery(url, "action", &action) && - net::GetValueForKeyInQuery(url, "user", &user) && - !user.empty(); - - RegistrationErrorStatus status = REG_ERROR_INVALID_PARAMS; - scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue); - - if (params_present) { - response->SetString("action", action); - response->SetString("user", user); - - if (action == "start") - status = delegate_->RegistrationStart(user); - - if (action == "getClaimToken") { - std::string token; - std::string claim_url; - status = delegate_->RegistrationGetClaimToken(user, &token, &claim_url); - response->SetString("token", token); - response->SetString("claim_url", claim_url); - } - - if (action == "complete") { - std::string device_id; - status = delegate_->RegistrationComplete(user, &device_id); - response->SetString("device_id", device_id); - } - - if (action == "cancel") - status = delegate_->RegistrationCancel(user); - } - - if (status != REG_ERROR_OK) - response.reset(); - - ProcessRegistrationStatus(status, &response); - *status_code = net::HTTP_OK; - return response; -} - -void PrivetHttpServer::ProcessRegistrationStatus( - RegistrationErrorStatus status, - scoped_ptr<base::DictionaryValue>* current_response) const { - switch (status) { - case REG_ERROR_OK: - DCHECK(*current_response) << "Response shouldn't be empty."; - break; - - case REG_ERROR_INVALID_PARAMS: - *current_response = CreateError("invalid_params"); - break; - case REG_ERROR_DEVICE_BUSY: - *current_response = CreateErrorWithTimeout("device_busy", - kDeviceBusyTimeout); - break; - case REG_ERROR_PENDING_USER_ACTION: - *current_response = CreateErrorWithTimeout("pending_user_action", - kPendingUserActionTimeout); - break; - case REG_ERROR_USER_CANCEL: - *current_response = CreateError("user_cancel"); - break; - case REG_ERROR_CONFIRMATION_TIMEOUT: - *current_response = CreateError("confirmation_timeout"); - break; - case REG_ERROR_INVALID_ACTION: - *current_response = CreateError("invalid_action"); - break; - case REG_ERROR_OFFLINE: - *current_response = CreateError("offline"); - break; - - case REG_ERROR_SERVER_ERROR: { - std::string description; - delegate_->GetRegistrationServerError(&description); - *current_response = CreateErrorWithDescription("server_error", - description); - break; - } - - default: - NOTREACHED(); - }; -}
diff --git a/cloud_print/gcp20/prototype/privet_http_server.h b/cloud_print/gcp20/prototype/privet_http_server.h deleted file mode 100644 index f5c815a0..0000000 --- a/cloud_print/gcp20/prototype/privet_http_server.h +++ /dev/null
@@ -1,216 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_ - -#include <stdint.h> - -#include <string> -#include <vector> - -#include "base/macros.h" -#include "base/values.h" -#include "cloud_print/gcp20/prototype/local_print_job.h" -#include "net/server/http_server.h" -#include "net/server/http_server_request_info.h" - -class GURL; - -// HTTP server for receiving . -class PrivetHttpServer: public net::HttpServer::Delegate { - public: - // TODO(maksymb): Move this enum to some namespace instead of this class. - enum RegistrationErrorStatus { - REG_ERROR_OK, - - REG_ERROR_INVALID_PARAMS, - REG_ERROR_DEVICE_BUSY, - REG_ERROR_PENDING_USER_ACTION, - REG_ERROR_USER_CANCEL, - REG_ERROR_CONFIRMATION_TIMEOUT, - REG_ERROR_INVALID_ACTION, - REG_ERROR_OFFLINE, - REG_ERROR_SERVER_ERROR - }; - - // TODO(maksymb): Move this struct to some namespace instead of this class. - struct DeviceInfo { - DeviceInfo(); - ~DeviceInfo(); - - std::string version; - std::string name; - std::string description; - std::string url; - std::string id; - std::string device_state; - std::string connection_state; - std::string manufacturer; - std::string model; - std::string serial_number; - std::string firmware; - int uptime; - std::string x_privet_token; - - std::vector<std::string> api; - std::vector<std::string> type; - }; - - class Delegate { - public: - virtual ~Delegate() {} - - // Invoked when registration is starting. - virtual RegistrationErrorStatus RegistrationStart( - const std::string& user) = 0; - - // Invoked when claimtoken is needed. - virtual RegistrationErrorStatus RegistrationGetClaimToken( - const std::string& user, - std::string* token, - std::string* claim_url) = 0; - - // Invoked when registration is going to be completed. - virtual RegistrationErrorStatus RegistrationComplete( - const std::string& user, - std::string* device_id) = 0; - - // Invoked when client asked for cancelling the registration. - virtual RegistrationErrorStatus RegistrationCancel( - const std::string& user) = 0; - - // Invoked for receiving server error details. - virtual void GetRegistrationServerError(std::string* description) = 0; - - // Invoked when /privet/info is called. - virtual void CreateInfo(DeviceInfo* info) = 0; - - // Invoked for checking wether /privet/register should be exposed. - virtual bool IsRegistered() const = 0; - - // Invoked for checking wether /privet/printer/* should be exposed. - virtual bool IsLocalPrintingAllowed() const = 0; - - // Invoked when XPrivetToken has to be checked. - virtual bool CheckXPrivetTokenHeader(const std::string& token) const = 0; - - // Invoked for getting capabilities. - virtual const base::DictionaryValue& GetCapabilities() = 0; - - // Invoked for creating a job. - virtual LocalPrintJob::CreateResult CreateJob( - const std::string& ticket, - std::string* job_id, - int* expires_in, - // TODO(maksymb): Use base::TimeDelta for timeouts - int* error_timeout, - std::string* error_description) = 0; - - // Invoked for simple local printing. - virtual LocalPrintJob::SaveResult SubmitDoc( - const LocalPrintJob& job, - std::string* job_id, - int* expires_in, - std::string* error_description, - int* timeout) = 0; - - // Invoked for advanced local printing. - virtual LocalPrintJob::SaveResult SubmitDocWithId( - const LocalPrintJob& job, - const std::string& job_id, - int* expires_in, - std::string* error_description, - int* timeout) = 0; - - // Invoked for getting job status. - virtual bool GetJobState(const std::string& job_id, - LocalPrintJob::Info* info) = 0; - }; - - // Constructor doesn't start server. - explicit PrivetHttpServer(Delegate* delegate); - - // Destroys the object. - ~PrivetHttpServer() override; - - // Starts HTTP server: start listening port |port| for HTTP requests. - bool Start(uint16_t port); - - // Stops HTTP server. - void Shutdown(); - - private: - // net::HttpServer::Delegate methods: - void OnConnect(int connection_id) override {} - void OnHttpRequest( - int connection_id, - const net::HttpServerRequestInfo& info) override; - void OnWebSocketRequest( - int connection_id, - const net::HttpServerRequestInfo& info) override; - void OnWebSocketMessage(int connection_id, const std::string& data) override; - void OnClose(int connection_id) override; - - // Sends error as response. Invoked when request method is invalid. - void ReportInvalidMethod(int connection_id); - - // Returns |true| if |request| should be done with correct |method|. - // Otherwise sends |Invalid method| error. - // Also checks support of |request| by this server. - bool ValidateRequestMethod(int connection_id, - const std::string& request, - const std::string& method); - - // Processes http request after all preparations (XPrivetHeader check, - // data handling etc.) - net::HttpStatusCode ProcessHttpRequest( - const GURL& url, - const net::HttpServerRequestInfo& info, - std::string* response); - - // Pivet API methods. Return reference to NULL if output should be empty. - scoped_ptr<base::DictionaryValue> ProcessInfo( - net::HttpStatusCode* status_code) const; - - scoped_ptr<base::DictionaryValue> ProcessCapabilities( - net::HttpStatusCode* status_code) const; - - scoped_ptr<base::DictionaryValue> ProcessCreateJob( - const GURL& url, - const std::string& body, - net::HttpStatusCode* status_code) const; - - scoped_ptr<base::DictionaryValue> ProcessSubmitDoc( - const GURL& url, - const net::HttpServerRequestInfo& info, - net::HttpStatusCode* status_code) const; - - scoped_ptr<base::DictionaryValue> ProcessJobState( - const GURL& url, - net::HttpStatusCode* status_code) const; - - scoped_ptr<base::DictionaryValue> ProcessRegister( - const GURL& url, - net::HttpStatusCode* status_code) const; - - // Processes current status and depending on it replaces (or not) - // |current_response| with error or empty response. - void ProcessRegistrationStatus( - RegistrationErrorStatus status, - scoped_ptr<base::DictionaryValue>* current_response) const; - - // Port for listening. - - uint16_t port_; - - // Contains encapsulated object for listening for requests. - scoped_ptr<net::HttpServer> server_; - - Delegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(PrivetHttpServer); -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_PRIVET_HTTP_SERVER_H_
diff --git a/cloud_print/gcp20/prototype/service_parameters.cc b/cloud_print/gcp20/prototype/service_parameters.cc deleted file mode 100644 index ac663f28..0000000 --- a/cloud_print/gcp20/prototype/service_parameters.cc +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 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. - -#include <stdint.h> - -#include "cloud_print/gcp20/prototype/service_parameters.h" - -ServiceParameters::ServiceParameters() : http_port_(0) { -} - -ServiceParameters::~ServiceParameters() { -} - -ServiceParameters::ServiceParameters(const std::string& service_type, - const std::string& secondary_service_type, - const std::string& service_name_prefix, - const std::string& service_domain_name, - const net::IPAddressNumber& http_ipv4, - const net::IPAddressNumber& http_ipv6, - uint16_t http_port) - : service_type_(service_type), - secondary_service_type_(secondary_service_type), - service_name_(service_name_prefix + "." + service_type), - service_domain_name_(service_domain_name), - http_ipv4_(http_ipv4), - http_ipv6_(http_ipv6), - http_port_(http_port) {}
diff --git a/cloud_print/gcp20/prototype/service_parameters.h b/cloud_print/gcp20/prototype/service_parameters.h deleted file mode 100644 index b1d1d8f..0000000 --- a/cloud_print/gcp20/prototype/service_parameters.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_SERVICE_PARAMETERS_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_SERVICE_PARAMETERS_H_ - -#include <stdint.h> - -#include <string> - -#include "net/base/ip_address_number.h" - -// Stores information about service. -struct ServiceParameters { - ServiceParameters(); - - ~ServiceParameters(); - - ServiceParameters(const std::string& service_type, - const std::string& secondary_service_type, - const std::string& service_name_prefix, - const std::string& service_domain_name, - const net::IPAddressNumber& http_ipv4, - const net::IPAddressNumber& http_ipv6, - uint16_t http_port); - - std::string service_type_; - std::string secondary_service_type_; - std::string service_name_; - std::string service_domain_name_; - net::IPAddressNumber http_ipv4_; - net::IPAddressNumber http_ipv6_; - uint16_t http_port_; -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_SERVICE_PARAMETERS_H_
diff --git a/cloud_print/gcp20/prototype/special_io.h b/cloud_print/gcp20/prototype/special_io.h deleted file mode 100644 index 3028feca..0000000 --- a/cloud_print/gcp20/prototype/special_io.h +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_SPECIAL_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_SPECIAL_H_ - -#if defined(OS_WIN) -#include <conio.h> -#elif defined(OS_POSIX) -#include "cloud_print/gcp20/prototype/conio_posix.h" -#endif - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_SPECIAL_H_ -
diff --git a/cloud_print/gcp20/prototype/x_privet_token.cc b/cloud_print/gcp20/prototype/x_privet_token.cc deleted file mode 100644 index d14d9a69..0000000 --- a/cloud_print/gcp20/prototype/x_privet_token.cc +++ /dev/null
@@ -1,78 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/x_privet_token.h" - -#include <stddef.h> -#include <stdint.h> - -#include "base/base64.h" -#include "base/format_macros.h" -#include "base/guid.h" -#include "base/logging.h" -#include "base/sha1.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" - -namespace { - -const char kXPrivetTokenDelimeter = ':'; -const uint64_t kTimeExpiration = 24 * 60 * 60; // in seconds -const uint64_t kTimeSecretRefresh = 24 * 60 * 60; // in seconds - -} // namespace - -using base::Time; -using base::TimeDelta; - -XPrivetToken::XPrivetToken() { - UpdateSecret(); -} - -XPrivetToken::XPrivetToken(const std::string& secret, - const base::Time& gen_time) - : secret_(secret), - last_gen_time_(gen_time) { -} - -std::string XPrivetToken::GenerateXToken() { - if (Time::Now() > last_gen_time_ + TimeDelta::FromSeconds(kTimeSecretRefresh)) - UpdateSecret(); - - return GenerateXTokenWithTime(static_cast<uint64_t>(Time::Now().ToTimeT())); -} - -bool XPrivetToken::CheckValidXToken(const std::string& token) const { - size_t delimeter_pos = token.find(kXPrivetTokenDelimeter); - if (delimeter_pos == std::string::npos) - return false; - - std::string issue_time_str = token.substr(delimeter_pos + 1); - uint64_t issue_time; - if (!base::StringToUint64(issue_time_str, &issue_time)) - return false; - - if (GenerateXTokenWithTime(issue_time) != token) - return false; - - return Time::FromTimeT(issue_time) - last_gen_time_ < - TimeDelta::FromSeconds(kTimeExpiration); -} - -std::string XPrivetToken::GenerateXTokenWithTime(uint64_t issue_time) const { - std::string result; - std::string issue_time_str = base::StringPrintf("%" PRIu64, issue_time); - std::string hash = base::SHA1HashString(secret_ + - kXPrivetTokenDelimeter + - issue_time_str); - base::Base64Encode(hash, &result); - return result + kXPrivetTokenDelimeter + issue_time_str; -} - -void XPrivetToken::UpdateSecret() { - secret_ = base::GenerateGUID(); - last_gen_time_ = base::Time::Now(); - VLOG(1) << "New Secret is Generated."; -} -
diff --git a/cloud_print/gcp20/prototype/x_privet_token.h b/cloud_print/gcp20/prototype/x_privet_token.h deleted file mode 100644 index af0341f9..0000000 --- a/cloud_print/gcp20/prototype/x_privet_token.h +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 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 CLOUD_PRINT_GCP20_PROTOTYPE_X_PRIVET_TOKEN_H_ -#define CLOUD_PRINT_GCP20_PROTOTYPE_X_PRIVET_TOKEN_H_ - -#include <stdint.h> - -#include <string> - -#include "base/gtest_prod_util.h" -#include "base/time/time.h" - -// Class for generating and checking X-Privet-Token. -class XPrivetToken { - public: - // Initializes the object. - XPrivetToken(); - - // Destroys the object. - ~XPrivetToken() {} - - // Generates X-Privet-Token for /privet/info request. Updates secret - // if expired. - std::string GenerateXToken(); - - // Checks - bool CheckValidXToken(const std::string& token) const; - - private: - FRIEND_TEST_ALL_PREFIXES(XPrivetTokenTest, Generation); - FRIEND_TEST_ALL_PREFIXES(XPrivetTokenTest, CheckingValid); - FRIEND_TEST_ALL_PREFIXES(XPrivetTokenTest, CheckingInvalid); - - // For testing purposes. - XPrivetToken(const std::string& secret, const base::Time& gen_time); - - // Generates X-Privet-Token for with certain time of issue. - std::string GenerateXTokenWithTime(uint64_t issue_time) const; - - // Creates new XPrivetToken secret. - void UpdateSecret(); - - // X-Privet-Token secret. - std::string secret_; - - // Time of last secret generation. - base::Time last_gen_time_; -}; - -#endif // CLOUD_PRINT_GCP20_PROTOTYPE_X_PRIVET_TOKEN_H_ -
diff --git a/cloud_print/gcp20/prototype/x_privet_token_unittest.cc b/cloud_print/gcp20/prototype/x_privet_token_unittest.cc deleted file mode 100644 index 0cf17c6b..0000000 --- a/cloud_print/gcp20/prototype/x_privet_token_unittest.cc +++ /dev/null
@@ -1,124 +0,0 @@ -// Copyright 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. - -#include "cloud_print/gcp20/prototype/x_privet_token.h" - -#include <stdint.h> -#include <stdio.h> - -#include "base/base64.h" -#include "base/format_macros.h" -#include "base/logging.h" -#include "base/sha1.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -TEST(XPrivetTokenTest, Generation) { - std::string secret = "E3E92296-E290-4E77-B678-6AEF256C30C8"; - uint64_t gen_time = 1372444784; - uint64_t issue_time = gen_time; - - XPrivetToken xtoken(secret, base::Time::FromTimeT(gen_time)); - - std::string issue_time_str = base::StringPrintf("%" PRIu64, issue_time); - std::string sha1_val = base::SHA1HashString(secret + ":" + issue_time_str); - - ASSERT_STRCASEEQ("2216828f9eefc3931c1b9a110dcca3dbec23571d", - base::HexEncode(sha1_val.data(), sha1_val.size()).c_str()); - - std::string base64_val; - base::Base64Encode(sha1_val, &base64_val); - std::string token = base64_val + ":" + issue_time_str; - - ASSERT_EQ(token, xtoken.GenerateXTokenWithTime(issue_time)); - - EXPECT_NE(xtoken.GenerateXTokenWithTime(issue_time), - xtoken.GenerateXTokenWithTime(issue_time + 1)); -} - -TEST(XPrivetTokenTest, CheckingValid) { - base::Time gen_time = base::Time::FromTimeT(1372444784); - XPrivetToken xtoken("9CEEA1AD-BC24-4D5A-8AB4-A6CE3E0CC4CD", gen_time); - - std::string token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT()); - EXPECT_TRUE(xtoken.CheckValidXToken(token)); - - token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 1); - EXPECT_TRUE(xtoken.CheckValidXToken(token)); - - token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 55); - EXPECT_TRUE(xtoken.CheckValidXToken(token)); - - token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 60*60 - 5); - EXPECT_TRUE(xtoken.CheckValidXToken(token)); - - token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 60*60 + 10); - EXPECT_TRUE(xtoken.CheckValidXToken(token)); - - token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 24*60*60 - 1); - EXPECT_TRUE(xtoken.CheckValidXToken(token)); -} - -TEST(XPrivetTokenTest, CheckingInvalid) { - base::Time gen_time = base::Time::FromTimeT(1372444784); - XPrivetToken xtoken("9CEEA1AD-BC24-4D5A-8AB4-A6CE3E0CC4CD", gen_time); - - // Meaningless tokens: - - std::string token = "CEEA1AD9CEEA1AD9CEEA1AD9CEEA1AD"; - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - base::Base64Encode("CEEA1AD9CEEA1AD9CEEA1AD9CEEA1AD", &token); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - base::Base64Encode("CEEA1AD9CEEA:1AD9CEEA1AD9CEEA1AD", &token); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - base::Base64Encode("CEEA1AD9CEEA:1AD9CEEA1AD9:CEEA1AD", &token); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - base::Base64Encode("CEEA1AD9CEEA:123456", &token); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - base::Base64Encode("CEEA1AD9CEEA:", &token); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - base::Base64Encode("CEEA1AD9CEEA:1372444784", &token); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - EXPECT_FALSE(xtoken.CheckValidXToken("")); - - // Expired tokens: - - token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 24*60*60 + 1); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - token = xtoken.GenerateXTokenWithTime(gen_time.ToTimeT() + 7*24*60*60 - 1023); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - // Tokens with different secret: - - XPrivetToken another("6F02AC4E-6F37-4078-AF42-5EE5D8180284", gen_time); - - token = another.GenerateXTokenWithTime(gen_time.ToTimeT() - 24*60*60 - 1); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - token = another.GenerateXTokenWithTime(gen_time.ToTimeT() - 24*60*60 + 1); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - token = another.GenerateXTokenWithTime(gen_time.ToTimeT()); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - token = another.GenerateXTokenWithTime(gen_time.ToTimeT() + 1); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - token = another.GenerateXTokenWithTime(gen_time.ToTimeT() + 24*60*60 - 1); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); - - token = another.GenerateXTokenWithTime(gen_time.ToTimeT() + 24*60*60 + 1); - EXPECT_FALSE(xtoken.CheckValidXToken(token)); -} -
diff --git a/cloud_print/service/BUILD.gn b/cloud_print/service/BUILD.gn deleted file mode 100644 index c7b7316..0000000 --- a/cloud_print/service/BUILD.gn +++ /dev/null
@@ -1,99 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/config/features.gni") -import("//tools/grit/grit_rule.gni") - -config("internal_config") { - defines = [ - "SECURITY_WIN32", - "STRICT", - "_ATL_APARTMENT_THREADED", - "_ATL_CSTRING_EXPLICIT_CONSTRUCTORS", - "_ATL_NO_COM_SUPPORT", - "_ATL_NO_AUTOMATIC_NAMESPACE", - "_ATL_NO_EXCEPTIONS", - ] -} - -if (is_win && is_clang) { - # service_controller.h uses DECLARE_REGISTRY_APPID_RESOURCEID, which - # in msvs2013 returns string literals via a non-const pointer. So - # disable this warning for now. - # TODO(thakis): Remove this once we're on 2014, - # https://connect.microsoft.com/VisualStudio/feedback/details/806376/atl-hindrances-to-adopting-new-strictstrings-conformance-option-in-vs2013 - config("service_warning_config") { - cflags = [ "-Wno-writable-strings" ] - } -} - -source_set("lib") { - sources = [ - "service_constants.cc", - "service_constants.h", - "service_state.cc", - "service_state.h", - "service_switches.cc", - "service_switches.h", - "win/chrome_launcher.cc", - "win/chrome_launcher.h", - "win/local_security_policy.cc", - "win/local_security_policy.h", - "win/service_controller.cc", - "win/service_controller.h", - "win/service_listener.cc", - "win/service_listener.h", - "win/service_utils.cc", - "win/service_utils.h", - "win/setup_listener.cc", - "win/setup_listener.h", - ] - - configs += [ - ":internal_config", - "//build/config/compiler:wexit_time_destructors", - ] - - deps = [ - ":resources", - "//base", - "//base:base_static", - "//base/third_party/dynamic_annotations", - "//chrome/common:constants", - "//cloud_print/common", - "//components/browser_sync/common", - "//components/cloud_devices/common", - "//content/public/common:static_switches", - "//google_apis", - "//ipc", - "//net", - "//url", - ] - - if (enable_basic_printing || enable_print_preview) { - deps += [ "//printing" ] - } - - if (is_win) { - deps += [ - "//chrome/common:constants", - "//chrome/installer/launcher_support", - ] - } - - if (is_clang) { - cflags = [ "-Wno-parentheses" ] - if (is_win) { - public_configs = [ ":service_warning_config" ] - } - } -} - -grit("resources") { - source = "win/service_resources.grd" - outputs = [ - "resources.h", - "service_resources_en.rc", - ] -}
diff --git a/cloud_print/service/service.gyp b/cloud_print/service/service.gyp deleted file mode 100644 index b1a83c9..0000000 --- a/cloud_print/service/service.gyp +++ /dev/null
@@ -1,120 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -{ - 'target_defaults': { - 'variables': { - 'chromium_code': 1, - 'enable_wexit_time_destructors': 1, - }, - 'include_dirs': [ - '<(DEPTH)', - # To allow including "version.h" - '<(SHARED_INTERMEDIATE_DIR)', - ], - 'defines' : [ - 'COMPILE_CONTENT_STATICALLY', - 'SECURITY_WIN32', - 'STRICT', - '_ATL_APARTMENT_THREADED', - '_ATL_CSTRING_EXPLICIT_CONSTRUCTORS', - '_ATL_NO_COM_SUPPORT', - '_ATL_NO_AUTOMATIC_NAMESPACE', - '_ATL_NO_EXCEPTIONS', - ], - }, - 'targets': [ - { - # GN version: //cloud_print/service:resources - 'target_name': 'service_resources', - 'type': 'none', - 'variables': { - 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/cloud_print/service', - }, - 'actions': [ - { - 'action_name': 'service_resources', - 'variables': { - 'grit_grd_file': 'win/service_resources.grd', - }, - 'includes': [ '../../build/grit_action.gypi' ], - }, - ], - 'includes': [ '../../build/grit_target.gypi' ], - }, - { - # GN version: //cloud_print/service - 'target_name': 'cloud_print_service_lib', - 'type': 'static_library', - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base', - '<(DEPTH)/base/base.gyp:base_static', - '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - '<(DEPTH)/components/components.gyp:browser_sync_common', - '<(DEPTH)/components/components.gyp:cloud_devices_common', - '<(DEPTH)/google_apis/google_apis.gyp:google_apis', - '<(DEPTH)/ipc/ipc.gyp:ipc', - '<(DEPTH)/net/net.gyp:net', - '<(DEPTH)/url/url.gyp:url_lib', - 'service_resources', - ], - 'conditions': [ - ['OS=="win"', { - 'dependencies': [ - '<(DEPTH)/chrome/chrome.gyp:launcher_support', - '<(DEPTH)/chrome/common_constants.gyp:common_constants', - '<(DEPTH)/chrome/common_constants.gyp:version_header', - ], - }], - ['OS=="win" and clang==1', { - # service_controller.h uses DECLARE_REGISTRY_APPID_RESOURCEID, which - # in msvs2013 returns string literals via a non-const pointer. So - # disable this warning for now. - # TODO(thakis): Remove this once we're on 2014, - # https://connect.microsoft.com/VisualStudio/feedback/details/806376/atl-hindrances-to-adopting-new-strictstrings-conformance-option-in-vs2013 - 'msvs_settings': { - 'VCCLCompilerTool': { - 'AdditionalOptions': ['-Wno-writable-strings'], - }, - }, - 'direct_dependent_settings': { - 'msvs_settings': { - 'VCCLCompilerTool': { - 'AdditionalOptions': ['-Wno-writable-strings'], - }, - }, - }, - }], - ['enable_basic_printing==1 or enable_print_preview==1', { - 'dependencies': [ - '<(DEPTH)/printing/printing.gyp:printing', - ], - }], - ], - 'sources': [ - '<(DEPTH)/content/public/common/content_switches.h', - '<(DEPTH)/content/public/common/content_switches.cc', - '<(DEPTH)/cloud_print/common/win/cloud_print_utils.cc', - '<(DEPTH)/cloud_print/common/win/cloud_print_utils.h', - 'service_constants.cc', - 'service_constants.h', - 'service_state.cc', - 'service_state.h', - 'service_switches.cc', - 'service_switches.h', - 'win/chrome_launcher.cc', - 'win/chrome_launcher.h', - 'win/local_security_policy.cc', - 'win/local_security_policy.h', - 'win/service_controller.cc', - 'win/service_controller.h', - 'win/service_listener.cc', - 'win/service_listener.h', - 'win/service_utils.cc', - 'win/service_utils.h', - 'win/setup_listener.cc', - 'win/setup_listener.h', - ], - }, - ], -}
diff --git a/cloud_print/service/service_constants.cc b/cloud_print/service/service_constants.cc deleted file mode 100644 index 74b51f8e..0000000 --- a/cloud_print/service/service_constants.cc +++ /dev/null
@@ -1,10 +0,0 @@ -// Copyright 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. - -#include "cloud_print/service/service_constants.h" - -const wchar_t kGoogleUpdateId[] = L"{86206CC6-CD3C-427D-9A34-77B34819CDD3}"; - -const wchar_t kSubDirectory[] = L"Google\\Cloud Print Service"; -
diff --git a/cloud_print/service/service_constants.h b/cloud_print/service/service_constants.h deleted file mode 100644 index 75dfec2..0000000 --- a/cloud_print/service/service_constants.h +++ /dev/null
@@ -1,13 +0,0 @@ -// Copyright 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 CLOUD_PRINT_SERVICE_SERVICE_CONSTANTS_H_ -#define CLOUD_PRINT_SERVICE_SERVICE_CONSTANTS_H_ - -extern const wchar_t kGoogleUpdateId[]; - -extern const wchar_t kSubDirectory[]; - -#endif // CLOUD_PRINT_SERVICE_SERVICE_CONSTANTS_H_ -
diff --git a/cloud_print/service/service_state.cc b/cloud_print/service/service_state.cc deleted file mode 100644 index 1c644ce..0000000 --- a/cloud_print/service/service_state.cc +++ /dev/null
@@ -1,224 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/service/service_state.h" - -#include <stdint.h> -#include <utility> - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "net/base/elements_upload_data_stream.h" -#include "net/base/escape.h" -#include "net/base/io_buffer.h" -#include "net/base/load_flags.h" -#include "net/base/request_priority.h" -#include "net/base/upload_bytes_element_reader.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_builder.h" - -namespace { - -const char kCloudPrintJsonName[] = "cloud_print"; -const char kEnabledOptionName[] = "enabled"; - -const char kEmailOptionName[] = "email"; -const char kProxyIdOptionName[] = "proxy_id"; -const char kRobotEmailOptionName[] = "robot_email"; -const char kRobotTokenOptionName[] = "robot_refresh_token"; -const char kAuthTokenOptionName[] = "auth_token"; -const char kXmppAuthTokenOptionName[] = "xmpp_auth_token"; - -const char kClientLoginUrl[] = "https://www.google.com/accounts/ClientLogin"; - -const int64_t kRequestTimeoutMs = 10 * 1000; - -class ServiceStateURLRequestDelegate : public net::URLRequest::Delegate { - public: - void OnResponseStarted(net::URLRequest* request) override { - if (request->GetResponseCode() == 200) { - Read(request); - if (request->status().is_io_pending()) - return; - } - request->Cancel(); - } - - void OnReadCompleted(net::URLRequest* request, int bytes_read) override { - Read(request); - if (!request->status().is_io_pending()) - base::MessageLoop::current()->QuitWhenIdle(); - } - - const std::string& data() const { - return data_; - } - - private: - void Read(net::URLRequest* request) { - // Read as many bytes as are available synchronously. - const int kBufSize = 100000; - scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufSize)); - int num_bytes = 0; - while (request->Read(buf.get(), kBufSize, &num_bytes)) { - data_.append(buf->data(), buf->data() + num_bytes); - } - } - std::string data_; -}; - - -void SetNotEmptyJsonString(base::DictionaryValue* dictionary, - const std::string& name, - const std::string& value) { - if (!value.empty()) - dictionary->SetString(name, value); -} - -} // namespace - -ServiceState::ServiceState() { - Reset(); -} - -ServiceState::~ServiceState() { -} - -void ServiceState::Reset() { - email_.clear(); - proxy_id_.clear(); - robot_email_.clear(); - robot_token_.clear(); - auth_token_.clear(); - xmpp_auth_token_.clear(); -} - -bool ServiceState::FromString(const std::string& json) { - Reset(); - scoped_ptr<base::Value> data(base::JSONReader::Read(json)); - if (!data.get()) - return false; - - const base::DictionaryValue* services = NULL; - if (!data->GetAsDictionary(&services)) - return false; - - const base::DictionaryValue* cloud_print = NULL; - if (!services->GetDictionary(kCloudPrintJsonName, &cloud_print)) - return false; - - bool valid_file = true; - // Don't exit on fail. Collect all data for re-use by user later. - if (!cloud_print->GetBoolean(kEnabledOptionName, &valid_file)) - valid_file = false; - - cloud_print->GetString(kEmailOptionName, &email_); - cloud_print->GetString(kProxyIdOptionName, &proxy_id_); - cloud_print->GetString(kRobotEmailOptionName, &robot_email_); - cloud_print->GetString(kRobotTokenOptionName, &robot_token_); - cloud_print->GetString(kAuthTokenOptionName, &auth_token_); - cloud_print->GetString(kXmppAuthTokenOptionName, &xmpp_auth_token_); - - return valid_file && IsValid(); -} - -bool ServiceState::IsValid() const { - if (email_.empty() || proxy_id_.empty()) - return false; - bool valid_robot = !robot_email_.empty() && !robot_token_.empty(); - bool valid_auth = !auth_token_.empty() && !xmpp_auth_token_.empty(); - return valid_robot || valid_auth; -} - -std::string ServiceState::ToString() { - scoped_ptr<base::DictionaryValue> cloud_print(new base::DictionaryValue()); - cloud_print->SetBoolean(kEnabledOptionName, true); - - SetNotEmptyJsonString(cloud_print.get(), kEmailOptionName, email_); - SetNotEmptyJsonString(cloud_print.get(), kProxyIdOptionName, proxy_id_); - SetNotEmptyJsonString(cloud_print.get(), kRobotEmailOptionName, robot_email_); - SetNotEmptyJsonString(cloud_print.get(), kRobotTokenOptionName, robot_token_); - SetNotEmptyJsonString(cloud_print.get(), kAuthTokenOptionName, auth_token_); - SetNotEmptyJsonString(cloud_print.get(), kXmppAuthTokenOptionName, - xmpp_auth_token_); - - base::DictionaryValue services; - services.Set(kCloudPrintJsonName, std::move(cloud_print)); - - std::string json; - base::JSONWriter::WriteWithOptions( - services, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json); - return json; -} - -std::string ServiceState::LoginToGoogle(const std::string& service, - const std::string& email, - const std::string& password) { - base::MessageLoopForIO loop; - - net::URLRequestContextBuilder builder; - scoped_ptr<net::URLRequestContext> context(builder.Build()); - - ServiceStateURLRequestDelegate fetcher_delegate; - GURL url(kClientLoginUrl); - - std::string post_body; - post_body += "accountType=GOOGLE"; - post_body += "&Email=" + net::EscapeUrlEncodedData(email, true); - post_body += "&Passwd=" + net::EscapeUrlEncodedData(password, true); - post_body += "&source=" + net::EscapeUrlEncodedData("CP-Service", true); - post_body += "&service=" + net::EscapeUrlEncodedData(service, true); - - scoped_ptr<net::URLRequest> request(context->CreateRequest( - url, net::DEFAULT_PRIORITY, &fetcher_delegate)); - int load_flags = request->load_flags(); - load_flags = load_flags | net::LOAD_DO_NOT_SEND_COOKIES; - load_flags = load_flags | net::LOAD_DO_NOT_SAVE_COOKIES; - request->SetLoadFlags(load_flags); - - scoped_ptr<net::UploadElementReader> reader( - net::UploadOwnedBytesElementReader::CreateWithString(post_body)); - request->set_upload( - net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); - request->SetExtraRequestHeaderByName( - "Content-Type", "application/x-www-form-urlencoded", true); - request->set_method("POST"); - request->Start(); - - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), - base::TimeDelta::FromMilliseconds(kRequestTimeoutMs)); - - base::MessageLoop::current()->Run(); - - const char kAuthStart[] = "Auth="; - for (const base::StringPiece& line : - base::SplitStringPiece(fetcher_delegate.data(), "\r\n", - base::KEEP_WHITESPACE, - base::SPLIT_WANT_NONEMPTY)) { - if (base::StartsWith(line, kAuthStart, - base::CompareCase::INSENSITIVE_ASCII)) - return line.substr(arraysize(kAuthStart) - 1).as_string(); - } - - return std::string(); -} - -bool ServiceState::Configure(const std::string& email, - const std::string& password, - const std::string& proxy_id) { - robot_token_.clear(); - robot_email_.clear(); - email_ = email; - proxy_id_ = proxy_id; - auth_token_ = LoginToGoogle("cloudprint", email_, password); - xmpp_auth_token_ = LoginToGoogle("chromiumsync", email_, password); - return IsValid(); -}
diff --git a/cloud_print/service/service_state.h b/cloud_print/service/service_state.h deleted file mode 100644 index d3e41ee1..0000000 --- a/cloud_print/service/service_state.h +++ /dev/null
@@ -1,101 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CLOUD_PRINT_SERVICE_SERVICE_STATE_H_ -#define CLOUD_PRINT_SERVICE_SERVICE_STATE_H_ - -#include <string> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/values.h" - -// Manages Cloud Print part of Service State. -class ServiceState { - public: - ServiceState(); - virtual ~ServiceState(); - - void Reset(); - - // Initialize object from json. - bool FromString(const std::string& json); - - // Returns object state as json. - std::string ToString(); - - // Setups object using data provided by delegate. - bool Configure(const std::string& email, - const std::string& password, - const std::string& proxy_id); - - // Returns authentication token provided by Google server. - virtual std::string LoginToGoogle(const std::string& service, - const std::string& email, - const std::string& password); - - // Returns true of object state is valid. - bool IsValid() const; - - std::string email() const { - return email_; - }; - - std::string proxy_id() const { - return proxy_id_; - }; - - std::string robot_email() const { - return robot_email_; - }; - - std::string robot_token() const { - return robot_token_; - }; - - std::string auth_token() const { - return auth_token_; - }; - - std::string xmpp_auth_token() const { - return xmpp_auth_token_; - }; - - void set_email(const std::string& value) { - email_ = value; - }; - - void set_proxy_id(const std::string& value) { - proxy_id_ = value; - }; - - void set_robot_email(const std::string& value) { - robot_email_ = value; - }; - - void set_robot_token(const std::string& value) { - robot_token_ = value; - }; - - void set_auth_token(const std::string& value) { - auth_token_ = value; - }; - - void set_xmpp_auth_token(const std::string& value) { - xmpp_auth_token_ = value; - }; - - private: - std::string email_; - std::string proxy_id_; - std::string robot_email_; - std::string robot_token_; - std::string auth_token_; - std::string xmpp_auth_token_; - - DISALLOW_COPY_AND_ASSIGN(ServiceState); -}; - -#endif // CLOUD_PRINT_SERVICE_SERVICE_STATE_H_ -
diff --git a/cloud_print/service/service_state_unittest.cc b/cloud_print/service/service_state_unittest.cc deleted file mode 100644 index d168e8c..0000000 --- a/cloud_print/service/service_state_unittest.cc +++ /dev/null
@@ -1,90 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/service/service_state.h" - -#include "base/macros.h" -#include "base/strings/string_util.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::Exactly; -using ::testing::Return; - -TEST(ServiceStateTest, Empty) { - ServiceState state; - EXPECT_FALSE(state.IsValid()); -} - -TEST(ServiceStateTest, ToString) { - ServiceState state; - EXPECT_STREQ("{\"cloud_print\": {\"enabled\": true}}", - base::CollapseWhitespaceASCII(state.ToString(), true).c_str()); - state.set_email("test@gmail.com"); - state.set_proxy_id("proxy"); - state.set_robot_email("robot@gmail.com"); - state.set_robot_token("abc"); - state.set_auth_token("token1"); - state.set_xmpp_auth_token("token2"); - EXPECT_TRUE(state.IsValid()); - EXPECT_STREQ("{\"cloud_print\": {\"auth_token\": \"token1\",\"email\": " - "\"test@gmail.com\",\"enabled\": true,\"proxy_id\": \"proxy\"," - "\"robot_email\": \"robot@gmail.com\",\"robot_refresh_token\": " - "\"abc\",\"xmpp_auth_token\": \"token2\"}}", - base::CollapseWhitespaceASCII(state.ToString(), true).c_str()); -} - -TEST(ServiceStateTest, FromString) { - ServiceState state; - // Syntax error. - EXPECT_FALSE(state.FromString("<\"cloud_print\": {\"enabled\": true}}")); - // No data. - EXPECT_FALSE(state.FromString("{\"cloud_print\": {\"enabled\": true}}")); - EXPECT_FALSE(state.FromString( - "{\"cloud_print\": {\"email\": \"test@gmail.com\"}}")); - EXPECT_STREQ("test@gmail.com", state.email().c_str()); - - // Good state. - EXPECT_TRUE(state.FromString( - "{\"cloud_print\": {\"email\": \"test2@gmail.com\",\"enabled\": true,\"" - "proxy_id\": \"proxy\",\"robot_email\": \"robot@gmail.com\",\"" - "robot_refresh_token\": \"abc\"}}")); - EXPECT_STREQ("test2@gmail.com", state.email().c_str()); - EXPECT_STREQ("proxy", state.proxy_id().c_str()); - EXPECT_STREQ("robot@gmail.com", state.robot_email().c_str()); - EXPECT_STREQ("abc", state.robot_token().c_str()); -} - -class ServiceStateMock : public ServiceState { - public: - ServiceStateMock() {} - - MOCK_METHOD3(LoginToGoogle, - std::string(const std::string& service, - const std::string& email, - const std::string& password)); - - private: - DISALLOW_COPY_AND_ASSIGN(ServiceStateMock); -}; - -TEST(ServiceStateTest, Configure) { - ServiceStateMock state; - state.set_email("test1@gmail.com"); - state.set_proxy_id("id1"); - - EXPECT_CALL(state, LoginToGoogle("cloudprint", "test2@gmail.com", "abc")) - .Times(Exactly(1)) - .WillOnce(Return("auth1")); - EXPECT_CALL(state, LoginToGoogle("chromiumsync", "test2@gmail.com", "abc")) - .Times(Exactly(1)) - .WillOnce(Return("auth2")); - - EXPECT_TRUE(state.Configure("test2@gmail.com", "abc", "id2")); - - EXPECT_STREQ("id2", state.proxy_id().c_str()); - EXPECT_STREQ("auth1", state.auth_token().c_str()); - EXPECT_STREQ("auth2", state.xmpp_auth_token().c_str()); -}
diff --git a/cloud_print/service/service_switches.cc b/cloud_print/service/service_switches.cc deleted file mode 100644 index 72afdd0..0000000 --- a/cloud_print/service/service_switches.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/service/service_switches.h" - -const char kConsoleSwitch[] = "console"; -const char kDeleteSwitch[] = "delete"; -const char kInstallSwitch[] = "install"; -const char kRequirementsSwitch[] = "requirements"; -const char kServiceSwitch[] = "service"; -const char kStartSwitch[] = "start"; -const char kStopSwitch[] = "stop"; -const char kUninstallSwitch[] = "uninstall"; -
diff --git a/cloud_print/service/service_switches.h b/cloud_print/service/service_switches.h deleted file mode 100644 index b6e11e4b..0000000 --- a/cloud_print/service/service_switches.h +++ /dev/null
@@ -1,18 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CLOUD_PRINT_SERVICE_SERVICE_SWITCHES_H_ -#define CLOUD_PRINT_SERVICE_SERVICE_SWITCHES_H_ - -extern const char kConsoleSwitch[]; -extern const char kDeleteSwitch[]; -extern const char kInstallSwitch[]; -extern const char kRequirementsSwitch[]; -extern const char kServiceSwitch[]; -extern const char kStartSwitch[]; -extern const char kStopSwitch[]; -extern const char kUninstallSwitch[]; - -#endif // CLOUD_PRINT_SERVICE_SERVICE_SWITCHES_H_ -
diff --git a/cloud_print/service/win/BUILD.gn b/cloud_print/service/win/BUILD.gn deleted file mode 100644 index b29b188..0000000 --- a/cloud_print/service/win/BUILD.gn +++ /dev/null
@@ -1,123 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/config/win/manifest.gni") -import("//chrome/version.gni") - -assert(is_win) - -executable("cloud_print_service") { - sources = [ - "cloud_print_service.cc", - ] - - configs += [ - "//build/config/compiler:wexit_time_destructors", - "//cloud_print/service:internal_config", - ] - - deps = [ - ":exe_manifest", - ":exe_version", - "//base", - "//chrome/common:constants", - "//cloud_print/common", - "//cloud_print/service:lib", - "//cloud_print/service:resources", - "//content/public/common:static_switches", - ] - - libs = [ "secur32.lib" ] -} - -windows_manifest("exe_manifest") { - sources = [ - common_controls_manifest, - default_compatibility_manifest, - require_administrator_manifest, - ] - type = "exe" -} - -process_version("exe_version") { - template_file = chrome_version_rc_template - sources = [ - "cloud_print_service_exe.ver", - ] - output = "$target_gen_dir/cloud_print_service_exe_version.rc" -} - -config("cloud_print_service_config_warnings") { - # TODO: Remove once cloud_print_service_config.cc no longer depends on - # atlapp.h, http://crbug.com/5027 - if (is_clang) { - cflags = [ - # atlapp.h contains a global "using namespace WTL;". - "-Wno-header-hygiene", - - # atlgdi.h does an intentional assignment in an if conditional. - "-Wno-parentheses", - - # atlgdi.h fails with -Wreorder enabled. - "-Wno-reorder", - - # atlgdi.h doesn"t use braces around subobject initializers. - "-Wno-missing-braces", - ] - } -} - -executable("cloud_print_service_config") { - sources = [ - "cloud_print_service_config.cc", - ] - - configs -= [ "//build/config/win:console" ] - configs += [ - "//build/config/win:windowed", - ":cloud_print_service_config_warnings", - ] - - deps = [ - ":config_version", - ":exe_manifest", - "//base", - "//chrome/common:constants", - "//cloud_print/common", - "//cloud_print/common:install_utils", - "//cloud_print/service:lib", - "//cloud_print/service:resources", - ] - - libs = [ "secur32.lib" ] -} - -process_version("config_version") { - template_file = chrome_version_rc_template - sources = [ - "cloud_print_service_config_exe.ver", - ] - output = "$target_gen_dir/cloud_print_service_config_version.rc" -} - -executable("cloud_print_service_setup") { - sources = [ - "installer.cc", - "installer.h", - ] - - configs -= [ "//build/config/win:console" ] - configs += [ "//build/config/win:windowed" ] - - deps = [ - ":exe_manifest", - "//base", - "//cloud_print/common", - "//cloud_print/common:install_utils", - "//cloud_print/service:lib", - "//cloud_print/service:resources", - ] - - libs = [ "secur32.lib" ] -}
diff --git a/cloud_print/service/win/chrome_launcher.cc b/cloud_print/service/win/chrome_launcher.cc deleted file mode 100644 index d3863425..0000000 --- a/cloud_print/service/win/chrome_launcher.cc +++ /dev/null
@@ -1,337 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/service/win/chrome_launcher.h" - -#include <stddef.h> - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/process/process.h" -#include "base/values.h" -#include "base/win/registry.h" -#include "base/win/scoped_handle.h" -#include "base/win/scoped_process_information.h" -#include "chrome/common/chrome_constants.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/pref_names.h" -#include "chrome/installer/launcher_support/chrome_launcher_support.h" -#include "cloud_print/common/win/cloud_print_utils.h" -#include "cloud_print/service/service_constants.h" -#include "cloud_print/service/win/service_utils.h" -#include "components/browser_sync/common/browser_sync_switches.h" -#include "components/cloud_devices/common/cloud_devices_urls.h" -#include "content/public/common/content_switches.h" -#include "google_apis/gaia/gaia_urls.h" -#include "net/base/url_util.h" -#include "url/gurl.h" - -namespace { - -const int kShutdownTimeoutMs = 30 * 1000; -const int kUsageUpdateTimeoutMs = 6 * 3600 * 1000; // 6 hours. - -static const base::char16 kAutoRunKeyPath[] = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"; - -// Terminates any process. -void ShutdownChrome(base::Process process, DWORD thread_id) { - if (::PostThreadMessage(thread_id, WM_QUIT, 0, 0) && - WAIT_OBJECT_0 == ::WaitForSingleObject(process.Handle(), - kShutdownTimeoutMs)) { - return; - } - LOG(ERROR) << "Failed to shutdown process."; - process.Terminate(0, true); -} - -BOOL CALLBACK CloseIfPidEqual(HWND wnd, LPARAM lparam) { - DWORD pid = 0; - ::GetWindowThreadProcessId(wnd, &pid); - if (pid == static_cast<DWORD>(lparam)) - ::PostMessage(wnd, WM_CLOSE, 0, 0); - return TRUE; -} - -void CloseAllProcessWindows(HANDLE process) { - ::EnumWindows(&CloseIfPidEqual, GetProcessId(process)); -} - -// Close Chrome browser window. -void CloseChrome(base::Process process, DWORD thread_id) { - CloseAllProcessWindows(process.Handle()); - if (WAIT_OBJECT_0 == - ::WaitForSingleObject(process.Handle(), kShutdownTimeoutMs)) { - return; - } - ShutdownChrome(process.Pass(), thread_id); -} - -bool LaunchProcess(const base::CommandLine& cmdline, - base::win::ScopedHandle* process_handle, - DWORD* thread_id) { - STARTUPINFO startup_info = {}; - startup_info.cb = sizeof(startup_info); - startup_info.dwFlags = STARTF_USESHOWWINDOW; - startup_info.wShowWindow = SW_SHOW; - - PROCESS_INFORMATION temp_process_info = {}; - base::FilePath::StringType writable_cmdline_str( - cmdline.GetCommandLineString()); - if (!CreateProcess(NULL, - &writable_cmdline_str[0], NULL, NULL, - FALSE, 0, NULL, NULL, &startup_info, &temp_process_info)) { - return false; - } - base::win::ScopedProcessInformation process_info(temp_process_info); - - if (process_handle) - process_handle->Set(process_info.TakeProcessHandle()); - - if (thread_id) - *thread_id = process_info.thread_id(); - - return true; -} - -std::string ReadAndUpdateServiceState(const base::FilePath& directory, - const std::string& proxy_id) { - std::string json; - base::FilePath file_path = directory.Append(chrome::kServiceStateFileName); - if (!base::ReadFileToString(file_path, &json)) { - return std::string(); - } - - scoped_ptr<base::Value> service_state(base::JSONReader::Read(json)); - base::DictionaryValue* dictionary = NULL; - if (!service_state->GetAsDictionary(&dictionary) || !dictionary) { - return std::string(); - } - - bool enabled = false; - if (!dictionary->GetBoolean(prefs::kCloudPrintProxyEnabled, &enabled) || - !enabled) { - return std::string(); - } - - std::string refresh_token; - if (!dictionary->GetString(prefs::kCloudPrintRobotRefreshToken, - &refresh_token) || - refresh_token.empty()) { - return std::string(); - } - - // Remove everything except kCloudPrintRoot. - scoped_ptr<base::Value> cloud_print_root; - dictionary->Remove(prefs::kCloudPrintRoot, &cloud_print_root); - dictionary->Clear(); - dictionary->Set(prefs::kCloudPrintRoot, cloud_print_root.release()); - - dictionary->SetBoolean(prefs::kCloudPrintXmppPingEnabled, true); - if (!proxy_id.empty()) // Reuse proxy id if we already had one. - dictionary->SetString(prefs::kCloudPrintProxyId, proxy_id); - std::string result; - base::JSONWriter::WriteWithOptions(*dictionary, - base::JSONWriter::OPTIONS_PRETTY_PRINT, - &result); - return result; -} - -void DeleteAutorunKeys(const base::FilePath& user_data_dir) { - base::win::RegKey key(HKEY_CURRENT_USER, kAutoRunKeyPath, KEY_SET_VALUE); - if (!key.Valid()) - return; - std::vector<base::string16> to_delete; - - base::FilePath abs_user_data_dir = base::MakeAbsoluteFilePath(user_data_dir); - - { - base::win::RegistryValueIterator value(HKEY_CURRENT_USER, kAutoRunKeyPath); - for (; value.Valid(); ++value) { - if (value.Type() == REG_SZ && value.Value()) { - base::CommandLine cmd = base::CommandLine::FromString(value.Value()); - if (cmd.GetSwitchValueASCII(switches::kProcessType) == - switches::kServiceProcess && - cmd.HasSwitch(switches::kUserDataDir)) { - base::FilePath path_from_reg = base::MakeAbsoluteFilePath( - cmd.GetSwitchValuePath(switches::kUserDataDir)); - if (path_from_reg == abs_user_data_dir) { - to_delete.push_back(value.Name()); - } - } - } - } - } - - for (size_t i = 0; i < to_delete.size(); ++i) { - key.DeleteValue(to_delete[i].c_str()); - } -} - -} // namespace - -ChromeLauncher::ChromeLauncher(const base::FilePath& user_data) - : user_data_(user_data), stop_event_(true, true) { -} - -ChromeLauncher::~ChromeLauncher() { -} - -bool ChromeLauncher::Start() { - DeleteAutorunKeys(user_data_); - stop_event_.Reset(); - thread_.reset(new base::DelegateSimpleThread(this, "chrome_launcher")); - thread_->Start(); - return true; -} - -void ChromeLauncher::Stop() { - stop_event_.Signal(); - thread_->Join(); - thread_.reset(); -} - -void ChromeLauncher::Run() { - const base::TimeDelta default_time_out = base::TimeDelta::FromSeconds(1); - const base::TimeDelta max_time_out = base::TimeDelta::FromHours(1); - - base::TimeDelta time_out = default_time_out; - while (1) { - base::FilePath chrome_path = - chrome_launcher_support::GetAnyChromePath(false /* is_sxs */); - - if (!chrome_path.empty()) { - base::CommandLine cmd(chrome_path); - CopyChromeSwitchesFromCurrentProcess(&cmd); - - // Required switches. - cmd.AppendSwitchASCII(switches::kProcessType, switches::kServiceProcess); - cmd.AppendSwitchPath(switches::kUserDataDir, user_data_); - cmd.AppendSwitch(switches::kNoServiceAutorun); - -#if defined(OS_WIN) - cmd.AppendArg(switches::kPrefetchArgumentOther); -#endif // defined(OS_WIN) - - // Optional. - cmd.AppendSwitch(switches::kDisableDefaultApps); - cmd.AppendSwitch(switches::kDisableExtensions); - cmd.AppendSwitch(switches::kDisableGpu); - cmd.AppendSwitch(switches::kDisableSoftwareRasterizer); - cmd.AppendSwitch(switches::kDisableSync); - cmd.AppendSwitch(switches::kNoFirstRun); - cmd.AppendSwitch(switches::kNoStartupWindow); - - base::win::ScopedHandle chrome_handle; - base::Time started = base::Time::Now(); - DWORD thread_id = 0; - LaunchProcess(cmd, &chrome_handle, &thread_id); - base::Process chrome_process; - if (chrome_handle.IsValid()) - chrome_process = base::Process(chrome_handle.Take()); - - HANDLE handles[] = { stop_event_.handle(), chrome_process.Handle() }; - DWORD wait_result = WAIT_TIMEOUT; - while (wait_result == WAIT_TIMEOUT) { - cloud_print::SetGoogleUpdateUsage(kGoogleUpdateId); - wait_result = ::WaitForMultipleObjects(arraysize(handles), handles, - FALSE, kUsageUpdateTimeoutMs); - } - if (wait_result == WAIT_OBJECT_0) { - ShutdownChrome(chrome_process.Pass(), thread_id); - break; - } else if (wait_result == WAIT_OBJECT_0 + 1) { - LOG(ERROR) << "Chrome process exited."; - } else { - LOG(ERROR) << "Error waiting Chrome (" << ::GetLastError() << ")."; - } - if (base::Time::Now() - started > base::TimeDelta::FromHours(1)) { - // Reset timeout because process worked long enough. - time_out = default_time_out; - } - } - if (stop_event_.TimedWait(time_out)) - break; - - time_out = std::min(time_out * 2, max_time_out); - } -} - -std::string ChromeLauncher::CreateServiceStateFile( - const std::string& proxy_id, - const std::vector<std::string>& printers) { - base::ScopedTempDir temp_user_data; - if (!temp_user_data.CreateUniqueTempDir()) { - LOG(ERROR) << "Can't create temp dir."; - return std::string(); - } - - base::FilePath chrome_path = - chrome_launcher_support::GetAnyChromePath(false /* is_sxs */); - if (chrome_path.empty()) { - LOG(ERROR) << "Can't find Chrome."; - return std::string(); - } - - base::FilePath printers_file = temp_user_data.path().Append(L"printers.json"); - - base::ListValue printer_list; - printer_list.AppendStrings(printers); - std::string printers_json; - base::JSONWriter::Write(printer_list, &printers_json); - size_t written = base::WriteFile(printers_file, - printers_json.c_str(), - static_cast<int>(printers_json.size())); - if (written != printers_json.size()) { - LOG(ERROR) << "Can't write file."; - return std::string(); - } - - base::CommandLine cmd(chrome_path); - CopyChromeSwitchesFromCurrentProcess(&cmd); - cmd.AppendSwitchPath(switches::kUserDataDir, temp_user_data.path()); - cmd.AppendSwitchPath(switches::kCloudPrintSetupProxy, printers_file); - cmd.AppendSwitch(switches::kNoServiceAutorun); - - // Optional. - cmd.AppendSwitch(switches::kDisableDefaultApps); - cmd.AppendSwitch(switches::kDisableExtensions); - cmd.AppendSwitch(switches::kDisableSync); - cmd.AppendSwitch(switches::kNoDefaultBrowserCheck); - cmd.AppendSwitch(switches::kNoFirstRun); - - cmd.AppendArg( - cloud_devices::GetCloudPrintEnableWithSigninURL(proxy_id).spec()); - - base::win::ScopedHandle chrome_handle; - DWORD thread_id = 0; - if (!LaunchProcess(cmd, &chrome_handle, &thread_id)) { - LOG(ERROR) << "Unable to launch Chrome."; - return std::string(); - } - base::Process chrome_process(chrome_handle.Take()); - - for (;;) { - DWORD wait_result = ::WaitForSingleObject(chrome_process.Handle(), 500); - std::string json = ReadAndUpdateServiceState(temp_user_data.path(), - proxy_id); - if (wait_result == WAIT_OBJECT_0) { - // Return what we have because browser is closed. - return json; - } - if (wait_result != WAIT_TIMEOUT) { - LOG(ERROR) << "Chrome launch failed."; - return std::string(); - } - if (!json.empty()) { - // Close chrome because Service State is ready. - CloseChrome(chrome_process.Pass(), thread_id); - return json; - } - } -}
diff --git a/cloud_print/service/win/chrome_launcher.h b/cloud_print/service/win/chrome_launcher.h deleted file mode 100644 index b7635df..0000000 --- a/cloud_print/service/win/chrome_launcher.h +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CLOUD_PRINT_SERVICE_CHROME_LAUNCHER_H_ -#define CLOUD_PRINT_SERVICE_CHROME_LAUNCHER_H_ - -#include <string> -#include <vector> - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/threading/simple_thread.h" - -class ChromeLauncher : public base::DelegateSimpleThread::Delegate { - public: - explicit ChromeLauncher(const base::FilePath& user_data); - - ~ChromeLauncher() override; - - bool Start(); - void Stop(); - - // base::DelegateSimpleThread::Delegate: - void Run() override; - - static std::string CreateServiceStateFile( - const std::string& proxy_id, - const std::vector<std::string>& printers); - - private: - base::FilePath user_data_; - base::WaitableEvent stop_event_; - scoped_ptr<base::DelegateSimpleThread> thread_; - - DISALLOW_COPY_AND_ASSIGN(ChromeLauncher); -}; - -#endif // CLOUD_PRINT_SERVICE_CHROME_LAUNCHER_H_
diff --git a/cloud_print/service/win/cloud_print_service.cc b/cloud_print/service/win/cloud_print_service.cc deleted file mode 100644 index 6aa7f92..0000000 --- a/cloud_print/service/win/cloud_print_service.cc +++ /dev/null
@@ -1,431 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Work around warning in atlbase.h -// https://connect.microsoft.com/VisualStudio/feedback/details/1032199/atlbase-h-gives-warning-c4189-when-compiling-with-atl-no-com-support -#pragma warning(push) -#pragma warning(disable:4189) -#include <atlbase.h> -#pragma warning(pop) -#include <security.h> -#include <stddef.h> - -#include <iomanip> -#include <iostream> -#include <iterator> -#include <string> -#include <vector> - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/guid.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/win/scoped_handle.h" -#include "chrome/common/chrome_constants.h" -#include "chrome/common/chrome_switches.h" -#include "cloud_print/common/win/cloud_print_utils.h" -#include "cloud_print/service/service_constants.h" -#include "cloud_print/service/service_state.h" -#include "cloud_print/service/service_switches.h" -#include "cloud_print/service/win/chrome_launcher.h" -#include "cloud_print/service/win/service_controller.h" -#include "cloud_print/service/win/service_listener.h" -#include "cloud_print/service/win/service_utils.h" -#include "cloud_print/service/win/setup_listener.h" -#include "content/public/common/content_switches.h" - -namespace { - -void InvalidUsage() { - base::FilePath service_path; - CHECK(PathService::Get(base::FILE_EXE, &service_path)); - - std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_HELP_TITLE); - std::cout << " " << service_path.BaseName().value(); - std::cout << " ["; - std::cout << "["; - std::cout << "["; - std::cout << " -" << kInstallSwitch; - std::cout << " [ -" << switches::kUserDataDir << "=DIRECTORY ]"; - std::cout << "]"; - std::cout << "]"; - std::cout << " | -" << kUninstallSwitch; - std::cout << " | -" << kStartSwitch; - std::cout << " | -" << kStopSwitch; - std::cout << " ]\n"; - std::cout << cloud_print::LoadLocalString(IDS_COMMAND_LINE_DESCRIPTION); - std::cout << "\n\n"; - - struct { - const char* name; - int description; - } kSwitchHelp[] = {{ - kInstallSwitch, IDS_SWITCH_HELP_INSTALL - }, { - switches::kUserDataDir, IDS_SWITCH_HELP_DATA_DIR - }, { - kUninstallSwitch, IDS_SWITCH_HELP_UNINSTALL - }, { - kStartSwitch, IDS_SWITCH_HELP_START - }, { - kStopSwitch, IDS_SWITCH_HELP_STOP - }}; - - for (size_t i = 0; i < arraysize(kSwitchHelp); ++i) { - std::cout << std::setiosflags(std::ios::left); - std::cout << " -" << std::setw(16) << kSwitchHelp[i].name; - std::cout << cloud_print::LoadLocalString(kSwitchHelp[i].description); - std::cout << "\n"; - } - std::cout << "\n"; -} - -base::string16 GetOption(int string_id, - const base::string16& default_option, - bool secure) { - base::string16 prompt_format = cloud_print::LoadLocalString(string_id); - std::vector<base::string16> substitutions(1, default_option); - std::cout << - base::ReplaceStringPlaceholders(prompt_format, substitutions, NULL); - base::string16 tmp; - if (secure) { - DWORD saved_mode = 0; - // Don't close. - HANDLE stdin_handle = ::GetStdHandle(STD_INPUT_HANDLE); - ::GetConsoleMode(stdin_handle, &saved_mode); - ::SetConsoleMode(stdin_handle, saved_mode & ~ENABLE_ECHO_INPUT); - std::getline(std::wcin, tmp); - ::SetConsoleMode(stdin_handle, saved_mode); - std::cout << "\n"; - } else { - std::getline(std::wcin, tmp); - } - if (tmp.empty()) - return default_option; - return tmp; -} - -HRESULT ReportError(HRESULT hr, int string_id) { - LOG(ERROR) << cloud_print::GetErrorMessage(hr); - std::cerr << cloud_print::LoadLocalString(string_id); - std::cerr << "\n"; - return hr; -} - -base::string16 StateAsString(ServiceController::State state) { - DWORD string_id = 0; - switch(state) { - case ServiceController::STATE_NOT_FOUND: - string_id = IDS_SERVICE_NOT_FOUND; - break; - case ServiceController::STATE_STOPPED: - string_id = IDS_SERVICE_STOPPED; - break; - case ServiceController::STATE_RUNNING: - string_id = IDS_SERVICE_RUNNING; - break; - case ServiceController::STATE_UNKNOWN: - break; - } - return string_id ? cloud_print::LoadLocalString(string_id) : base::string16(); -} - -} // namespace - - -class CloudPrintServiceModule - : public ATL::CAtlServiceModuleT<CloudPrintServiceModule, - IDS_SERVICE_NAME> { - public: - typedef ATL::CAtlServiceModuleT<CloudPrintServiceModule, - IDS_SERVICE_NAME> Base; - - CloudPrintServiceModule() - : check_requirements_(false), - controller_(new ServiceController()) { - } - - static const wchar_t* GetAppIdT() { - return ServiceController::GetAppIdT(); - }; - - HRESULT InitializeSecurity() { - // TODO(gene): Check if we need to call CoInitializeSecurity and provide - // the appropriate security settings for service. - return S_OK; - } - - bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode) { - CHECK(pnRetCode); - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.ParseFromString(lpCmdLine); - - LOG(INFO) << command_line.GetCommandLineString(); - - bool is_service = false; - *pnRetCode = ParseCommandLine(command_line, &is_service); - if (FAILED(*pnRetCode)) { - ReportError(*pnRetCode, IDS_OPERATION_FAILED_TITLE); - } - if (!is_service) { - controller_->UpdateState(); - std::cout << cloud_print::LoadLocalString(IDS_STATE_LABEL); - std::cout << " " << StateAsString(controller_->state()); - } - return is_service; - } - - HRESULT PreMessageLoop(int nShowCmd) { - HRESULT hr = Base::PreMessageLoop(nShowCmd); - if (FAILED(hr)) - return hr; - - if (check_requirements_) { - CheckRequirements(); - } else { - HRESULT hr = StartConnector(); - if (FAILED(hr)) - return hr; - } - - LogEvent(_T("Service started/resumed")); - SetServiceStatus(SERVICE_RUNNING); - - return hr; - } - - HRESULT PostMessageLoop() { - StopConnector(); - setup_listener_.reset(); - return Base::PostMessageLoop(); - } - - private: - HRESULT ParseCommandLine(const base::CommandLine& command_line, - bool* is_service) { - if (!is_service) - return E_INVALIDARG; - *is_service = false; - - user_data_dir_switch_ = - command_line.GetSwitchValuePath(switches::kUserDataDir); - if (!user_data_dir_switch_.empty()) - user_data_dir_switch_ = base::MakeAbsoluteFilePath(user_data_dir_switch_); - - if (command_line.HasSwitch(kStopSwitch)) - return controller_->StopService(); - - if (command_line.HasSwitch(kUninstallSwitch)) - return controller_->UninstallService(); - - if (command_line.HasSwitch(kInstallSwitch)) { - base::string16 run_as_user; - base::string16 run_as_password; - base::FilePath user_data_dir; - std::vector<std::string> printers; - HRESULT hr = SelectWindowsAccount(&run_as_user, &run_as_password, - &user_data_dir, &printers); - if (FAILED(hr)) - return hr; - - DCHECK(user_data_dir_switch_.empty() || - user_data_dir_switch_ == user_data_dir); - - hr = SetupServiceState(user_data_dir, printers); - if (FAILED(hr)) - return hr; - - hr = controller_->InstallConnectorService( - run_as_user, run_as_password, user_data_dir_switch_, - command_line.HasSwitch(switches::kEnableLogging)); - if (SUCCEEDED(hr) && command_line.HasSwitch(kStartSwitch)) - return controller_->StartService(); - - return hr; - } - - if (command_line.HasSwitch(kStartSwitch)) - return controller_->StartService(); - - if (command_line.HasSwitch(kConsoleSwitch)) { - check_requirements_ = command_line.HasSwitch(kRequirementsSwitch); - ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE); - HRESULT hr = Run(); - ::SetConsoleCtrlHandler(NULL, FALSE); - return hr; - } - - if (command_line.HasSwitch(kServiceSwitch) || - command_line.HasSwitch(kRequirementsSwitch)) { - *is_service = true; - check_requirements_ = command_line.HasSwitch(kRequirementsSwitch); - return S_OK; - } - - - InvalidUsage(); - return S_FALSE; - } - - HRESULT SelectWindowsAccount(base::string16* run_as_user, - base::string16* run_as_password, - base::FilePath* user_data_dir, - std::vector<std::string>* printers) { - *run_as_user = GetCurrentUserName(); - std::cout << cloud_print::LoadLocalString(IDS_WINDOWS_USER_PROMPT1) << "\n"; - *run_as_user = GetOption(IDS_WINDOWS_USER_PROMPT2, *run_as_user, false); - *run_as_user = ReplaceLocalHostInName(*run_as_user); - *run_as_password = GetOption(IDS_WINDOWS_PASSWORD_PROMPT, L"", true); - SetupListener setup(*run_as_user); - HRESULT hr = controller_->InstallCheckService(*run_as_user, - *run_as_password, - user_data_dir_switch_); - if (FAILED(hr)) { - return ReportError(hr, IDS_ERROR_FAILED_INSTALL_SERVICE); - } - - { - // Always uninstall service after requirements check. - base::ScopedClosureRunner scoped_uninstall( - base::Bind(base::IgnoreResult(&ServiceController::UninstallService), - base::Unretained(controller_.get()))); - - hr = controller_->StartService(); - if (FAILED(hr)) { - return ReportError(hr, IDS_ERROR_FAILED_START_SERVICE); - } - - if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) { - return ReportError(E_FAIL, IDS_ERROR_FAILED_START_SERVICE); - } - } - - if (setup.user_data_dir().empty()) { - return ReportError(E_FAIL, IDS_ERROR_NO_DATA_DIR); - } - - if (setup.chrome_path().empty()) { - return ReportError(E_FAIL, IDS_ERROR_NO_CHROME); - } - - if (!setup.is_xps_available()) { - return ReportError(E_FAIL, IDS_ERROR_NO_XPS); - } - - std::cout << "\n"; - std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHECK); - std::cout << "\n"; - std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_USER); - std::cout << "\n " << setup.user_name() << "\n"; - std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_CHROME); - std::cout << "\n " << setup.chrome_path().value() << "\n"; - std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_DATADIR); - std::cout << "\n " << setup.user_data_dir().value() << "\n"; - std::cout << cloud_print::LoadLocalString(IDS_SERVICE_ENV_PRINTERS); - std::cout << "\n "; - std::ostream_iterator<std::string> cout_it(std::cout, "\n "); - std::copy(setup.printers().begin(), setup.printers().end(), cout_it); - std::cout << "\n"; - - *user_data_dir = setup.user_data_dir(); - *printers = setup.printers(); - return S_OK; - } - - HRESULT SetupServiceState(const base::FilePath& user_data_dir, - const std::vector<std::string>& printers) { - base::FilePath file = user_data_dir.Append(chrome::kServiceStateFileName); - - std::string contents; - base::ReadFileToString(file, &contents); - ServiceState service_state; - service_state.FromString(contents); - std::string proxy_id = service_state.proxy_id(); - - LOG(INFO) << file.value() << ": " << contents; - - base::string16 message = - cloud_print::LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME); - std::cout << "\n" << message.c_str() << "\n" ; - std::string new_contents = - ChromeLauncher::CreateServiceStateFile(proxy_id, printers); - - if (new_contents.empty()) { - return ReportError(E_FAIL, IDS_ERROR_FAILED_CREATE_CONFIG); - } - - if (new_contents != contents) { - size_t written = base::WriteFile( - file, new_contents.c_str(), static_cast<int>(new_contents.size())); - if (written != new_contents.size()) { - return ReportError(cloud_print::GetLastHResult(), - IDS_ERROR_FAILED_CREATE_CONFIG); - } - } - - return S_OK; - } - - void CheckRequirements() { - setup_listener_.reset(new ServiceListener(GetUserDataDir())); - } - - HRESULT StartConnector() { - chrome_.reset(new ChromeLauncher(GetUserDataDir())); - return chrome_->Start() ? S_OK : E_FAIL; - } - - void StopConnector() { - if (chrome_.get()) { - chrome_->Stop(); - chrome_.reset(); - } - } - - base::FilePath GetUserDataDir() const { - if (!user_data_dir_switch_.empty()) - return user_data_dir_switch_; - base::FilePath result; - CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result)); - return result.Append(kSubDirectory); - } - - static BOOL WINAPI ConsoleCtrlHandler(DWORD type); - - bool check_requirements_; - base::FilePath user_data_dir_switch_; - scoped_ptr<ChromeLauncher> chrome_; - scoped_ptr<ServiceController> controller_; - scoped_ptr<ServiceListener> setup_listener_; -}; - -CloudPrintServiceModule _AtlModule; - -BOOL CloudPrintServiceModule::ConsoleCtrlHandler(DWORD type) { - PostThreadMessage(_AtlModule.m_dwThreadID, WM_QUIT, 0, 0); - return TRUE; -} - -int main(int argc, char** argv) { - base::CommandLine::Init(argc, argv); - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - base::AtExitManager at_exit; - - logging::LoggingSettings settings; - settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; - logging::InitLogging(settings); - - logging::SetMinLogLevel( - command_line->HasSwitch(switches::kEnableLogging) ? - logging::LOG_INFO : logging::LOG_FATAL); - - return _AtlModule.WinMain(0); -} -
diff --git a/cloud_print/service/win/cloud_print_service_config.cc b/cloud_print/service/win/cloud_print_service_config.cc deleted file mode 100644 index c65638af..0000000 --- a/cloud_print/service/win/cloud_print_service_config.cc +++ /dev/null
@@ -1,465 +0,0 @@ -// Copyright 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. - -#include <atlbase.h> -#include <atlapp.h> // NOLINT -#include <stddef.h> -#include <stdint.h> - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_pump_dispatcher.h" -#include "base/run_loop.h" -#include "base/strings/string16.h" -#include "base/threading/thread.h" -#include "chrome/common/chrome_constants.h" -#include "cloud_print/common/win/cloud_print_utils.h" -#include "cloud_print/service/resources.h" -#include "cloud_print/service/service_state.h" -#include "cloud_print/service/win/chrome_launcher.h" -#include "cloud_print/service/win/service_controller.h" -#include "cloud_print/service/win/service_utils.h" -#include "cloud_print/service/win/setup_listener.h" - -using cloud_print::LoadLocalString; -using cloud_print::GetErrorMessage; - -class SetupDialog : public base::RefCounted<SetupDialog>, - public ATL::CDialogImpl<SetupDialog> { - public: - // Enables accelerators. - class Dispatcher : public base::MessagePumpDispatcher { - public: - explicit Dispatcher(SetupDialog* dialog) : dialog_(dialog) {} - ~Dispatcher() override {} - - // MessagePumpDispatcher: - uint32_t Dispatch(const MSG& msg) override { - MSG msg2 = msg; - uint32_t action = POST_DISPATCH_NONE; - if (!dialog_->IsDialogMessage(&msg2)) - action = POST_DISPATCH_PERFORM_DEFAULT; - return action; - } - - private: - scoped_refptr<SetupDialog> dialog_; - }; - - typedef ATL::CDialogImpl<SetupDialog> Base; - enum { IDD = IDD_SETUP_DIALOG }; - - BEGIN_MSG_MAP(SetupDialog) - MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) - MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnCtrColor) - MESSAGE_HANDLER(WM_DESTROY, OnDestroy) - COMMAND_ID_HANDLER(IDCANCEL, OnCancel) - COMMAND_ID_HANDLER(IDC_START, OnStart) - COMMAND_ID_HANDLER(IDC_INSTALL, OnInstall) - COMMAND_ID_HANDLER(IDC_LOGGING, OnLogging) - END_MSG_MAP() - - SetupDialog(); - - private: - friend class base::RefCounted<SetupDialog>; - ~SetupDialog() override {} - - // Window Message Handlers - LRESULT OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam, - BOOL& handled); - LRESULT OnCtrColor(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); - LRESULT OnCancel(UINT, INT nIdentifier, HWND, BOOL& handled); - LRESULT OnStart(UINT, INT nIdentifier, HWND, BOOL& handled); - LRESULT OnInstall(UINT, INT nIdentifier, HWND, BOOL& handled); - LRESULT OnLogging(UINT, INT nIdentifier, HWND, BOOL& handled); - LRESULT OnDestroy(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); - - void PostUITask(const base::Closure& task); - void PostIOTask(const base::Closure& task); - - // UI Calls. - - // Disables all controls after users actions. - void DisableControls(); - // Updates state of controls after when we received service status. - void SetState(ServiceController::State state, const base::string16& user, - bool is_logging_enabled); - // Show message box with error. - void ShowErrorMessageBox(const base::string16& error_message); - // Show use message box instructions how to deal with opened Chrome window. - void AskToCloseChrome(); - base::string16 GetDlgItemText(int id) const; - base::string16 GetUser() const; - base::string16 GetPassword() const; - bool IsLoggingEnabled() const; - bool IsInstalled() const { - return state_ > ServiceController::STATE_NOT_FOUND; - } - - // IO Calls. - // Installs service. - void Install(const base::string16& user, const base::string16& password, - bool enable_logging); - // Starts service. - void Start(); - // Stops service. - void Stop(); - // Uninstall service. - void Uninstall(); - // Update service state. - void UpdateState(); - // Posts task to UI thread to show error using string id. - void ShowError(int string_id); - // Posts task to UI thread to show error using string. - void ShowError(const base::string16& error_message); - // Posts task to UI thread to show error using error code. - void ShowError(HRESULT hr); - - ServiceController::State state_; - base::Thread worker_; - - base::MessageLoop* ui_loop_; - base::MessageLoop* io_loop_; - - ServiceController controller_; -}; - -SetupDialog::SetupDialog() - : state_(ServiceController::STATE_NOT_FOUND), - worker_("worker") { - ui_loop_ = base::MessageLoop::current(); - DCHECK(base::MessageLoopForUI::IsCurrent()); - - worker_.StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); - io_loop_ = worker_.message_loop(); - DCHECK(io_loop_->IsType(base::MessageLoop::TYPE_IO)); -} - -void SetupDialog::PostUITask(const base::Closure& task) { - ui_loop_->PostTask(FROM_HERE, task); -} - -void SetupDialog::PostIOTask(const base::Closure& task) { - io_loop_->PostTask(FROM_HERE, task); -} - -void SetupDialog::ShowErrorMessageBox(const base::string16& error_message) { - DCHECK(base::MessageLoopForUI::IsCurrent()); - MessageBox(error_message.c_str(), - LoadLocalString(IDS_OPERATION_FAILED_TITLE).c_str(), - MB_ICONERROR | MB_OK); -} - -void SetupDialog::AskToCloseChrome() { - DCHECK(base::MessageLoopForUI::IsCurrent()); - MessageBox(LoadLocalString(IDS_ADD_PRINTERS_USING_CHROME).c_str(), - LoadLocalString(IDS_CONTINUE_IN_CHROME_TITLE).c_str(), - MB_OK); -} - -void SetupDialog::SetState(ServiceController::State status, - const base::string16& user, - bool is_logging_enabled) { - DCHECK(base::MessageLoopForUI::IsCurrent()); - state_ = status; - - DWORD status_string = 0; - switch(status) { - case ServiceController::STATE_NOT_FOUND: - status_string = IDS_SERVICE_NOT_FOUND; - break; - case ServiceController::STATE_STOPPED: - status_string = IDS_SERVICE_STOPPED; - break; - case ServiceController::STATE_RUNNING: - status_string = IDS_SERVICE_RUNNING; - break; - case ServiceController::STATE_UNKNOWN: - break; - } - SetDlgItemText(IDC_STATUS, - status_string ? LoadLocalString(status_string).c_str() : L""); - if (IsInstalled()) { - SetDlgItemText(IDC_USER, user.c_str()); - CheckDlgButton(IDC_LOGGING, - is_logging_enabled ? BST_CHECKED : BST_UNCHECKED); - } - - ATL::CWindow start_button = GetDlgItem(IDC_START); - DWORD start_string = (status == ServiceController::STATE_STOPPED) ? - IDS_SERVICE_START : IDS_SERVICE_STOP; - start_button.SetWindowText(LoadLocalString(start_string).c_str()); - start_button.ShowWindow(IsInstalled() ? SW_SHOW : SW_HIDE); - start_button.EnableWindow(TRUE); - - ATL::CWindow install_button = GetDlgItem(IDC_INSTALL); - DWORD install_string = IsInstalled() ? IDS_SERVICE_UNINSTALL : - IDS_SERVICE_INSTALL; - install_button.SetWindowText(LoadLocalString(install_string).c_str()); - install_button.ShowWindow(SW_SHOW); - install_button.EnableWindow(TRUE); - - if (!IsInstalled()) { - GetDlgItem(IDC_USER).EnableWindow(TRUE); - GetDlgItem(IDC_PASSWORD).EnableWindow(TRUE); - GetDlgItem(IDC_LOGGING).EnableWindow(TRUE); - } -} - -LRESULT SetupDialog::OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam, - BOOL& handled) { - ATLVERIFY(CenterWindow()); - - WTL::CIcon icon; - if (icon.LoadIcon(MAKEINTRESOURCE(IDI_ICON))) { - SetIcon(icon); - } - - SetWindowText(LoadLocalString(IDS_SETUP_PROGRAM_NAME).c_str()); - SetDlgItemText(IDC_STATE_LABEL, LoadLocalString(IDS_STATE_LABEL).c_str()); - SetDlgItemText(IDC_USER_LABEL, LoadLocalString(IDS_USER_LABEL).c_str()); - SetDlgItemText(IDC_PASSWORD_LABEL, - LoadLocalString(IDS_PASSWORD_LABEL).c_str()); - SetDlgItemText(IDC_LOGGING, LoadLocalString(IDS_LOGGING_LABEL).c_str()); - SetDlgItemText(IDCANCEL, LoadLocalString(IDS_CLOSE).c_str()); - - SetState(ServiceController::STATE_UNKNOWN, L"", false); - DisableControls(); - - SetDlgItemText(IDC_USER, GetCurrentUserName().c_str()); - - PostIOTask(base::Bind(&SetupDialog::UpdateState, this)); - - return 0; -} - -LRESULT SetupDialog::OnCtrColor(UINT message, WPARAM wparam, LPARAM lparam, - BOOL& handled) { - HWND window = reinterpret_cast<HWND>(lparam); - if (GetDlgItem(IDC_LOGO).m_hWnd == window) { - return reinterpret_cast<LRESULT>(::GetStockObject(WHITE_BRUSH)); - } - return 0; -} - -LRESULT SetupDialog::OnStart(UINT, INT nIdentifier, HWND, BOOL& handled) { - DisableControls(); - DCHECK(IsInstalled()); - if (state_ == ServiceController::STATE_RUNNING) - PostIOTask(base::Bind(&SetupDialog::Stop, this)); - else - PostIOTask(base::Bind(&SetupDialog::Start, this)); - return 0; -} - -LRESULT SetupDialog::OnInstall(UINT, INT nIdentifier, HWND, BOOL& handled) { - DisableControls(); - if (IsInstalled()) { - PostIOTask(base::Bind(&SetupDialog::Uninstall, this)); - } else { - PostIOTask(base::Bind(&SetupDialog::Install, this, GetUser(), - GetPassword(), IsLoggingEnabled())); - } - return 0; -} - -LRESULT SetupDialog::OnLogging(UINT, INT nIdentifier, HWND, BOOL& handled) { - CheckDlgButton(IDC_LOGGING, IsLoggingEnabled()? BST_UNCHECKED : BST_CHECKED); - return 0; -} - -LRESULT SetupDialog::OnCancel(UINT, INT nIdentifier, HWND, BOOL& handled) { - DestroyWindow(); - return 0; -} - -LRESULT SetupDialog::OnDestroy(UINT message, WPARAM wparam, LPARAM lparam, - BOOL& handled) { - base::MessageLoop::current()->PostTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); - return 1; -} - -void SetupDialog::DisableControls() { - GetDlgItem(IDC_START).EnableWindow(FALSE); - GetDlgItem(IDC_INSTALL).EnableWindow(FALSE); - GetDlgItem(IDC_USER).EnableWindow(FALSE); - GetDlgItem(IDC_PASSWORD).EnableWindow(FALSE); - GetDlgItem(IDC_LOGGING).EnableWindow(FALSE); -} - -base::string16 SetupDialog::GetDlgItemText(int id) const { - const ATL::CWindow& item = GetDlgItem(id); - size_t length = item.GetWindowTextLength(); - base::string16 result(length + 1, L'\0'); - result.resize(item.GetWindowText(&result[0], - static_cast<int>(result.size()))); - return result; -} - -base::string16 SetupDialog::GetUser() const { - return GetDlgItemText(IDC_USER); -} - -base::string16 SetupDialog::GetPassword() const { - return GetDlgItemText(IDC_PASSWORD); -} - -bool SetupDialog::IsLoggingEnabled() const{ - return IsDlgButtonChecked(IDC_LOGGING) == BST_CHECKED; -} - -void SetupDialog::UpdateState() { - DCHECK(base::MessageLoopForIO::IsCurrent()); - controller_.UpdateState(); - PostUITask(base::Bind(&SetupDialog::SetState, this, controller_.state(), - controller_.user(), controller_.is_logging_enabled())); -} - -void SetupDialog::ShowError(const base::string16& error_message) { - DCHECK(base::MessageLoopForIO::IsCurrent()); - PostUITask(base::Bind(&SetupDialog::SetState, - this, - ServiceController::STATE_UNKNOWN, - L"", - false)); - PostUITask(base::Bind(&SetupDialog::ShowErrorMessageBox, this, - error_message)); - LOG(ERROR) << error_message; -} - -void SetupDialog::ShowError(int string_id) { - ShowError(cloud_print::LoadLocalString(string_id)); -} - -void SetupDialog::ShowError(HRESULT hr) { - ShowError(GetErrorMessage(hr)); -} - -void SetupDialog::Install(const base::string16& user, - const base::string16& password, - bool enable_logging) { - // Don't forget to update state on exit. - base::ScopedClosureRunner scoped_update_status( - base::Bind(&SetupDialog::UpdateState, this)); - - DCHECK(base::MessageLoopForIO::IsCurrent()); - - SetupListener setup(GetUser()); - HRESULT hr = controller_.InstallCheckService(user, password, - base::FilePath()); - if (FAILED(hr)) - return ShowError(hr); - - { - // Always uninstall service after requirements check. - base::ScopedClosureRunner scoped_uninstall( - base::Bind(base::IgnoreResult(&ServiceController::UninstallService), - base::Unretained(&controller_))); - - hr = controller_.StartService(); - if (FAILED(hr)) - return ShowError(hr); - - if (!setup.WaitResponce(base::TimeDelta::FromSeconds(30))) - return ShowError(IDS_ERROR_FAILED_START_SERVICE); - } - - if (setup.user_data_dir().empty()) - return ShowError(IDS_ERROR_NO_DATA_DIR); - - if (setup.chrome_path().empty()) - return ShowError(IDS_ERROR_NO_CHROME); - - if (!setup.is_xps_available()) - return ShowError(IDS_ERROR_NO_XPS); - - base::FilePath file = setup.user_data_dir(); - file = file.Append(chrome::kServiceStateFileName); - - std::string proxy_id; - std::string contents; - - if (base::ReadFileToString(file, &contents)) { - ServiceState service_state; - if (service_state.FromString(contents)) - proxy_id = service_state.proxy_id(); - } - PostUITask(base::Bind(&SetupDialog::AskToCloseChrome, this)); - contents = ChromeLauncher::CreateServiceStateFile(proxy_id, setup.printers()); - - if (contents.empty()) - return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG); - - size_t written = base::WriteFile(file, contents.c_str(), - static_cast<int>(contents.size())); - if (written != contents.size()) { - DWORD last_error = GetLastError(); - if (!last_error) - return ShowError(IDS_ERROR_FAILED_CREATE_CONFIG); - return ShowError(HRESULT_FROM_WIN32(last_error)); - } - - hr = controller_.InstallConnectorService(user, password, base::FilePath(), - enable_logging); - if (FAILED(hr)) - return ShowError(hr); - - hr = controller_.StartService(); - if (FAILED(hr)) - return ShowError(hr); -} - -void SetupDialog::Start() { - DCHECK(base::MessageLoopForIO::IsCurrent()); - HRESULT hr = controller_.StartService(); - if (FAILED(hr)) - ShowError(hr); - UpdateState(); -} - -void SetupDialog::Stop() { - DCHECK(base::MessageLoopForIO::IsCurrent()); - HRESULT hr = controller_.StopService(); - if (FAILED(hr)) - ShowError(hr); - UpdateState(); -} - -void SetupDialog::Uninstall() { - DCHECK(base::MessageLoopForIO::IsCurrent()); - HRESULT hr = controller_.UninstallService(); - if (FAILED(hr)) - ShowError(hr); - UpdateState(); -} - -class CloudPrintServiceConfigModule - : public ATL::CAtlExeModuleT<CloudPrintServiceConfigModule> { -}; - -CloudPrintServiceConfigModule _AtlModule; - -int WINAPI WinMain(__in HINSTANCE hInstance, - __in HINSTANCE hPrevInstance, - __in LPSTR lpCmdLine, - __in int nCmdShow) { - base::AtExitManager at_exit; - base::CommandLine::Init(0, NULL); - - base::MessageLoopForUI loop; - scoped_refptr<SetupDialog> dialog(new SetupDialog()); - dialog->Create(NULL); - dialog->ShowWindow(SW_SHOW); - SetupDialog::Dispatcher dispatcher(dialog.get()); - base::RunLoop run_loop(&dispatcher); - run_loop.Run(); - return 0; -}
diff --git a/cloud_print/service/win/cloud_print_service_config_exe.ver b/cloud_print/service/win/cloud_print_service_config_exe.ver deleted file mode 100644 index e4a7bcce..0000000 --- a/cloud_print/service/win/cloud_print_service_config_exe.ver +++ /dev/null
@@ -1,3 +0,0 @@ -INTERNAL_NAME=cloud_print_service_config.exe -ORIGINAL_FILENAME=cloud_print_service_config.exe -FILETYPE=0x1L
diff --git a/cloud_print/service/win/cloud_print_service_exe.ver b/cloud_print/service/win/cloud_print_service_exe.ver deleted file mode 100644 index 945062e..0000000 --- a/cloud_print/service/win/cloud_print_service_exe.ver +++ /dev/null
@@ -1,3 +0,0 @@ -INTERNAL_NAME=cloud_print_service.exe -ORIGINAL_FILENAME=cloud_print_service.exe -FILETYPE=0x1L
diff --git a/cloud_print/service/win/cloud_print_service_setup_exe.ver b/cloud_print/service/win/cloud_print_service_setup_exe.ver deleted file mode 100644 index 4e283e3f..0000000 --- a/cloud_print/service/win/cloud_print_service_setup_exe.ver +++ /dev/null
@@ -1,3 +0,0 @@ -INTERNAL_NAME=cloud_print_service_setup.exe -ORIGINAL_FILENAME=cloud_print_service_setup.exe -FILETYPE=0x1L
diff --git a/cloud_print/service/win/common-controls.manifest b/cloud_print/service/win/common-controls.manifest deleted file mode 100644 index 1710196..0000000 --- a/cloud_print/service/win/common-controls.manifest +++ /dev/null
@@ -1,8 +0,0 @@ -<?xml version='1.0' encoding='UTF-8' standalone='yes'?> -<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> - <dependency> - <dependentAssembly> - <assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*' /> - </dependentAssembly> - </dependency> -</assembly>
diff --git a/cloud_print/service/win/installer.cc b/cloud_print/service/win/installer.cc deleted file mode 100644 index e09eca31..0000000 --- a/cloud_print/service/win/installer.cc +++ /dev/null
@@ -1,145 +0,0 @@ -// Copyright 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. - -#include "cloud_print/service/win/installer.h" - -#include <winerror.h> - -#include "base/at_exit.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/path_service.h" -#include "base/win/scoped_com_initializer.h" -#include "base/win/shortcut.h" -#include "cloud_print/common/win/cloud_print_utils.h" -#include "cloud_print/common/win/install_utils.h" -#include "cloud_print/service/resources.h" -#include "cloud_print/service/service_constants.h" -#include "cloud_print/service/service_switches.h" -#include "cloud_print/service/win/service_controller.h" - -namespace { - -const wchar_t kConfigBinaryName[] = L"cloud_print_service_config.exe"; - -base::FilePath GetShortcutPath(int dir_key, bool with_subdir) { - base::FilePath path; - if (!PathService::Get(dir_key, &path)) - return base::FilePath(); - path = path.Append(cloud_print::LoadLocalString(IDS_FULL_PRODUCT_NAME)); - if (with_subdir) - path = path.Append(cloud_print::LoadLocalString(IDS_FULL_PRODUCT_NAME)); - return path.InsertBeforeExtension(L".lnk"); -} - -void CreateShortcut(int dir_key, bool with_subdir, - base::win::ShortcutOperation operation) { - base::FilePath path = GetShortcutPath(dir_key, with_subdir); - if (path.empty()) - return; - base::CreateDirectory(path.DirName()); - base::win::ShortcutProperties properties; - - base::FilePath exe_path; - if (!PathService::Get(base::FILE_EXE, &exe_path)) - return; - exe_path = exe_path.DirName().Append(base::FilePath(kConfigBinaryName)); - properties.set_target(exe_path); - properties.set_working_dir(exe_path.DirName()); - CreateOrUpdateShortcutLink(path, properties, operation); -} - -void CreateShortcuts(bool create_always) { - base::win::ScopedCOMInitializer co_init; - base::win::ShortcutOperation operation = - create_always ? base::win::SHORTCUT_CREATE_ALWAYS : - base::win::SHORTCUT_REPLACE_EXISTING; - CreateShortcut(base::DIR_COMMON_START_MENU, true, operation); - CreateShortcut(base::DIR_COMMON_DESKTOP, false, operation); -} - -void DeleteShortcut(int dir_key, bool with_subdir) { - base::FilePath path = GetShortcutPath(dir_key, with_subdir); - if (path.empty()) - return; - if (with_subdir) - base::DeleteFile(path.DirName(), true); - else - base::DeleteFile(path, false); -} - -void DeleteShortcuts() { - DeleteShortcut(base::DIR_COMMON_START_MENU, true); - DeleteShortcut(base::DIR_COMMON_DESKTOP, false); -} - -} // namespace - -HRESULT ProcessInstallerSwitches() { - const base::CommandLine& command_line( - *base::CommandLine::ForCurrentProcess()); - - if (command_line.HasSwitch(kInstallSwitch)) { - base::FilePath old_location = - cloud_print::GetInstallLocation(kGoogleUpdateId); - - cloud_print::CreateUninstallKey( - kGoogleUpdateId, cloud_print::LoadLocalString(IDS_FULL_PRODUCT_NAME), - kUninstallSwitch); - - ServiceController controller; - HRESULT hr = controller.UpdateBinaryPath(); - if (FAILED(hr)) - return hr; - - if (!old_location.empty() && - cloud_print::IsProgramsFilesParent(old_location) && - old_location != cloud_print::GetInstallLocation(kGoogleUpdateId)) { - base::DeleteFile(old_location, true); - } - - cloud_print::SetGoogleUpdateKeys( - kGoogleUpdateId, cloud_print::LoadLocalString(IDS_FULL_PRODUCT_NAME)); - - CreateShortcuts(old_location.empty()); - - return S_OK; - } else if (command_line.HasSwitch(kUninstallSwitch)) { - ServiceController controller; - HRESULT hr = controller.UninstallService(); - if (FAILED(hr)) - return hr; - - DeleteShortcuts(); - - cloud_print::DeleteGoogleUpdateKeys(kGoogleUpdateId); - cloud_print::DeleteUninstallKey(kGoogleUpdateId); - cloud_print::DeleteProgramDir(kDeleteSwitch); - return S_OK; - } else if (command_line.HasSwitch(kDeleteSwitch)) { - base::FilePath delete_path = command_line.GetSwitchValuePath(kDeleteSwitch); - if (!delete_path.empty() && - cloud_print::IsProgramsFilesParent(delete_path)) { - base::DeleteFile(delete_path, true); - } - return S_OK; - } - - return S_FALSE; -} - -class CloudPrintServiceSetupModule - : public ATL::CAtlExeModuleT<CloudPrintServiceSetupModule> { -}; - -CloudPrintServiceSetupModule _AtlModule; - -int WINAPI WinMain(__in HINSTANCE hInstance, - __in HINSTANCE hPrevInstance, - __in LPSTR lpCmdLine, - __in int nCmdShow) { - base::AtExitManager at_exit; - base::CommandLine::Init(0, NULL); - return ProcessInstallerSwitches(); -}
diff --git a/cloud_print/service/win/installer.h b/cloud_print/service/win/installer.h deleted file mode 100644 index 027e97b..0000000 --- a/cloud_print/service/win/installer.h +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 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 CLOUD_PRINT_SERVICE_INSTALLER_H_ -#define CLOUD_PRINT_SERVICE_INSTALLER_H_ - -#include <wtypes.h> - -// Installs/uninstalls product if related switches provided. -// Returns S_OK on success or error code. -// Returns S_FALE if no related switches were provided. -HRESULT ProcessInstallerSwitches(); - -#endif // CLOUD_PRINT_SERVICE_INSTALLER_H_ \ No newline at end of file
diff --git a/cloud_print/service/win/local_security_policy.cc b/cloud_print/service/win/local_security_policy.cc deleted file mode 100644 index 8718dd09..0000000 --- a/cloud_print/service/win/local_security_policy.cc +++ /dev/null
@@ -1,122 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/service/win/local_security_policy.h" - -#include <atlsecurity.h> -#include <ntsecapi.h> -#include <windows.h> -#include <stddef.h> - -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/string_util.h" - -const wchar_t kSeServiceLogonRight[] = L"SeServiceLogonRight"; - -#ifndef STATUS_SUCCESS -#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -#endif - -namespace { - -template<class T> -class ScopedLsaMemory { - public: - ScopedLsaMemory() : lsa_memory_(NULL) { - } - - ~ScopedLsaMemory() { - Close(); - } - - void Close() { - if (lsa_memory_) { - LsaFreeMemory(lsa_memory_); - lsa_memory_ = NULL; - } - } - - T* Get() const { - return lsa_memory_; - } - - T** Receive() { - Close(); - return &lsa_memory_; - } - - private: - T* lsa_memory_; - DISALLOW_COPY_AND_ASSIGN(ScopedLsaMemory); -}; - -} // namespace - -LocalSecurityPolicy::LocalSecurityPolicy() : policy_(NULL) { -} - -LocalSecurityPolicy::~LocalSecurityPolicy() { - Close(); -} - -void LocalSecurityPolicy::Close() { - if (policy_) { - LsaClose(policy_); - policy_ = NULL; - } -} - -bool LocalSecurityPolicy::Open() { - DCHECK(!policy_); - Close(); - LSA_OBJECT_ATTRIBUTES attributes = {0}; - return STATUS_SUCCESS == - ::LsaOpenPolicy(NULL, &attributes, - POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES, - &policy_); -} - -bool LocalSecurityPolicy::IsPrivilegeSet( - const base::string16& username, - const base::string16& privilage) const { - DCHECK(policy_); - ATL::CSid user_sid; - if (!user_sid.LoadAccount(username.c_str())) { - LOG(ERROR) << "Unable to load Sid for" << username; - return false; - } - ScopedLsaMemory<LSA_UNICODE_STRING> rights; - ULONG count = 0; - NTSTATUS status = ::LsaEnumerateAccountRights( - policy_, const_cast<SID*>(user_sid.GetPSID()), rights.Receive(), &count); - if (STATUS_SUCCESS != status || !rights.Get()) - return false; - for (size_t i = 0; i < count; ++i) { - if (privilage == rights.Get()[i].Buffer) - return true; - } - return false; -} - -bool LocalSecurityPolicy::SetPrivilege(const base::string16& username, - const base::string16& privilage) { - DCHECK(policy_); - ATL::CSid user_sid; - if (!user_sid.LoadAccount(username.c_str())) { - LOG(ERROR) << "Unable to load Sid for" << username; - return false; - } - LSA_UNICODE_STRING privilege_string; - base::string16 privilage_copy(privilage); - privilege_string.Buffer = &privilage_copy[0]; - privilege_string.Length = static_cast<USHORT>( - wcslen(privilege_string.Buffer) * sizeof(privilege_string.Buffer[0])); - privilege_string.MaximumLength = privilege_string.Length + - sizeof(privilege_string.Buffer[0]); - return STATUS_SUCCESS == - ::LsaAddAccountRights(policy_, const_cast<SID*>(user_sid.GetPSID()), - &privilege_string, 1); -} -
diff --git a/cloud_print/service/win/local_security_policy.h b/cloud_print/service/win/local_security_policy.h deleted file mode 100644 index 7dbdb75..0000000 --- a/cloud_print/service/win/local_security_policy.h +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CLOUD_PRINT_SERVICE_WIN_LOCAL_SECURITY_POLICY_H_ -#define CLOUD_PRINT_SERVICE_WIN_LOCAL_SECURITY_POLICY_H_ - -#include <wtypes.h> // Has to be before ntsecapi.h -#include <ntsecapi.h> - -#include "base/macros.h" -#include "base/strings/string16.h" - -extern const wchar_t kSeServiceLogonRight[]; - -class LocalSecurityPolicy { - public: - LocalSecurityPolicy(); - ~LocalSecurityPolicy(); - - bool Open(); - void Close(); - - bool IsPrivilegeSet(const base::string16& username, - const base::string16& privilage) const; - bool SetPrivilege(const base::string16& username, - const base::string16& privilage); - - private: - LSA_HANDLE policy_; - - DISALLOW_COPY_AND_ASSIGN(LocalSecurityPolicy); -}; - -#endif // CLOUD_PRINT_SERVICE_WIN_LOCAL_SECURITY_POLICY_H_
diff --git a/cloud_print/service/win/resources/cloud_print_logo_beta.bmp b/cloud_print/service/win/resources/cloud_print_logo_beta.bmp deleted file mode 100644 index dd54b2a..0000000 --- a/cloud_print/service/win/resources/cloud_print_logo_beta.bmp +++ /dev/null Binary files differ
diff --git a/cloud_print/service/win/resources/cloud_print_service.rgs b/cloud_print/service/win/resources/cloud_print_service.rgs deleted file mode 100644 index 9ebaebe..0000000 --- a/cloud_print/service/win/resources/cloud_print_service.rgs +++ /dev/null
@@ -1,10 +0,0 @@ -HKCR -{ - NoRemove AppID - { - ForceRemove '%APPID%' - { - val LocalService = s 'CloudPrintService' - } - } -}
diff --git a/cloud_print/service/win/resources/cloudprint.ico b/cloud_print/service/win/resources/cloudprint.ico deleted file mode 100644 index e362c72..0000000 --- a/cloud_print/service/win/resources/cloudprint.ico +++ /dev/null Binary files differ
diff --git a/cloud_print/service/win/resources/setup_dialog.rc b/cloud_print/service/win/resources/setup_dialog.rc deleted file mode 100644 index aaf2952dd..0000000 --- a/cloud_print/service/win/resources/setup_dialog.rc +++ /dev/null
@@ -1,39 +0,0 @@ -// 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. - -#include "grit\\generated_resources.h" - -#define APSTUDIO_READONLY_SYMBOLS -#include "cloud_print/version.h" -#include "winres.h" -#include "verrsrc.h" -#undef APSTUDIO_READONLY_SYMBOLS - -#ifdef APSTUDIO_INVOKED -# error Don't open this in the GUI, it'll be massacred on save. -#endif // APSTUDIO_INVOKED - -IDD_SETUP_DIALOG DIALOGEX 0, 0, 280, 156 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK |DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX -EXSTYLE WS_EX_APPWINDOW | WS_EX_COMPOSITED -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - CONTROL IDB_LOGO,IDC_LOGO,"Static",SS_BITMAP|SS_CENTERIMAGE,-100,0,380,44 - CONTROL "",IDC_SPLITTER,"Static",SS_GRAYFRAME, 0,45,280,1,WS_EX_STATICEDGE - - LTEXT "",IDC_STATE_LABEL, 10,49,50,13,SS_CENTERIMAGE - LTEXT "",IDC_STATUS, 60,49,160,13,SS_CENTERIMAGE - - LTEXT "",IDC_USER_LABEL, 10,67,50,13,SS_CENTERIMAGE - EDITTEXT IDC_USER, 60,67,160,13,WS_TABSTOP|ES_AUTOHSCROLL - - LTEXT "",IDC_PASSWORD_LABEL, 10,85,50,13,SS_CENTERIMAGE - EDITTEXT IDC_PASSWORD, 60,85,160,13,WS_TABSTOP|ES_AUTOHSCROLL|ES_PASSWORD - - CHECKBOX "",IDC_LOGGING, 60,103,160,13,WS_TABSTOP - - PUSHBUTTON "",IDC_START, 76,131,60,15,WS_TABSTOP - PUSHBUTTON "",IDC_INSTALL, 143,131,60,15,WS_TABSTOP|BS_DEFPUSHBUTTON - PUSHBUTTON "",IDCANCEL, 210,131,60,15,WS_TABSTOP -END
diff --git a/cloud_print/service/win/service.gyp b/cloud_print/service/win/service.gyp deleted file mode 100644 index 5434c4971..0000000 --- a/cloud_print/service/win/service.gyp +++ /dev/null
@@ -1,129 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -{ - 'target_defaults': { - 'variables': { - 'chromium_code': 1, - 'enable_wexit_time_destructors': 1, - }, - 'include_dirs': [ - '<(DEPTH)', - # To allow including "version.h" - '<(SHARED_INTERMEDIATE_DIR)', - ], - 'defines' : [ - 'COMPILE_CONTENT_STATICALLY', - 'SECURITY_WIN32', - 'STRICT', - '_ATL_APARTMENT_THREADED', - '_ATL_CSTRING_EXPLICIT_CONSTRUCTORS', - '_ATL_NO_COM_SUPPORT', - '_ATL_NO_AUTOMATIC_NAMESPACE', - '_ATL_NO_EXCEPTIONS', - ], - }, - 'targets': [ - { - # GN version: //cloud_print/service/win:cloud_print_service - 'target_name': 'cloud_print_service', - 'type': 'executable', - 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_exe_version.rc', - 'cloud_print_service.cc', - ], - 'includes': [ - 'service_resources.gypi' - ], - 'dependencies': [ - '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_resources', - '<(DEPTH)/cloud_print/service/service.gyp:cloud_print_service_lib', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '1', # Set /SUBSYSTEM:CONSOLE - 'UACExecutionLevel': '2', # /level='requireAdministrator' - 'AdditionalDependencies': [ - 'secur32.lib', - ], - }, - }, - }, - { - # GN version: //cloud_print/service/win:cloud_print_service_config - 'target_name': 'cloud_print_service_config', - 'type': 'executable', - 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_config_exe_version.rc', - 'cloud_print_service_config.cc', - ], - 'includes': [ - 'service_resources.gypi' - ], - 'dependencies': [ - '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_resources', - '<(DEPTH)/cloud_print/common/common.gyp:cloud_print_install_lib', - '<(DEPTH)/cloud_print/service/service.gyp:cloud_print_service_lib', - ], - 'msvs_settings': { - 'VCManifestTool': { - 'AdditionalManifestFiles': [ - 'common-controls.manifest', - ], - }, - 'VCLinkerTool': { - 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS - 'UACExecutionLevel': '2', # /level='requireAdministrator' - 'AdditionalDependencies': [ - 'secur32.lib', - ], - }, - 'conditions': [ - ['clang==1', { - # TODO: Remove once cloud_print_service_config.cc no longer depends - # on atlapp.h, http://crbug.com/5027 - 'VCCLCompilerTool': { - 'AdditionalOptions': [ - # atlapp.h contains a global "using namespace WTL;". - '-Wno-header-hygiene', - # atlgdi.h does an intentional assignment in an if conditional. - '-Wno-parentheses', - # atlgdi.h fails with -Wreorder enabled. - '-Wno-reorder', - # atlgdi.h doesn't use braces around subobject initializers. - '-Wno-missing-braces', - ], - }, - }], - ], - }, - }, - { - # GN version: //cloud_print/service/win:cloud_print_service_setup - 'target_name': 'cloud_print_service_setup', - 'type': 'executable', - 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_setup_exe_version.rc', - 'installer.cc', - 'installer.h', - ], - 'includes': [ - 'service_resources.gypi' - ], - 'dependencies': [ - '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_resources', - '<(DEPTH)/cloud_print/common/common.gyp:cloud_print_install_lib', - '<(DEPTH)/cloud_print/service/service.gyp:cloud_print_service_lib', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS - 'UACExecutionLevel': '2', # /level='requireAdministrator' - 'AdditionalDependencies': [ - 'secur32.lib', - ], - }, - }, - }, - ], -}
diff --git a/cloud_print/service/win/service_controller.cc b/cloud_print/service/win/service_controller.cc deleted file mode 100644 index 2af87ec..0000000 --- a/cloud_print/service/win/service_controller.cc +++ /dev/null
@@ -1,323 +0,0 @@ -// Copyright 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. - -#include "cloud_print/service/win/service_controller.h" - -#include <atlbase.h> -#include <atlcom.h> -#include <atlctl.h> -#include <stdint.h> - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/macros.h" -#include "base/path_service.h" -#include "base/win/scoped_handle.h" -#include "chrome/common/chrome_switches.h" -#include "cloud_print/common/win/cloud_print_utils.h" -#include "cloud_print/service/service_constants.h" -#include "cloud_print/service/service_switches.h" -#include "cloud_print/service/win/chrome_launcher.h" -#include "cloud_print/service/win/local_security_policy.h" -#include "cloud_print/service/win/service_utils.h" -#include "content/public/common/content_switches.h" - -namespace { - -const wchar_t kServiceExeName[] = L"cloud_print_service.exe"; - -// The traits class for Windows Service. -class ServiceHandleTraits { - public: - typedef SC_HANDLE Handle; - - // Closes the handle. - static bool CloseHandle(Handle handle) { - return ::CloseServiceHandle(handle) != FALSE; - } - - static bool IsHandleValid(Handle handle) { - return handle != NULL; - } - - static Handle NullHandle() { - return NULL; - } - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceHandleTraits); -}; - -typedef base::win::GenericScopedHandle< - ServiceHandleTraits, base::win::DummyVerifierTraits> ServiceHandle; - -HRESULT OpenServiceManager(ServiceHandle* service_manager) { - if (!service_manager) - return E_POINTER; - - service_manager->Set(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); - if (!service_manager->IsValid()) - return cloud_print::GetLastHResult(); - - return S_OK; -} - -HRESULT OpenService(const base::string16& name, DWORD access, - ServiceHandle* service) { - if (!service) - return E_POINTER; - - ServiceHandle scm; - HRESULT hr = OpenServiceManager(&scm); - if (FAILED(hr)) - return hr; - - service->Set(::OpenService(scm.Get(), name.c_str(), access)); - - if (!service->IsValid()) - return cloud_print::GetLastHResult(); - - return S_OK; -} - -} // namespace - -ServiceController::ServiceController() - : name_(cloud_print::LoadLocalString(IDS_SERVICE_NAME)), - command_line_(base::CommandLine::NO_PROGRAM) { -} - -ServiceController::~ServiceController() { -} - -HRESULT ServiceController::StartService() { - ServiceHandle service; - HRESULT hr = OpenService(name_, SERVICE_START| SERVICE_QUERY_STATUS, - &service); - if (FAILED(hr)) - return hr; - if (!::StartService(service.Get(), 0, NULL)) - return cloud_print::GetLastHResult(); - SERVICE_STATUS status = {0}; - while (::QueryServiceStatus(service.Get(), &status) && - status.dwCurrentState == SERVICE_START_PENDING) { - Sleep(100); - } - return S_OK; -} - -HRESULT ServiceController::StopService() { - ServiceHandle service; - HRESULT hr = OpenService(name_, SERVICE_STOP | SERVICE_QUERY_STATUS, - &service); - if (FAILED(hr)) - return hr; - SERVICE_STATUS status = {0}; - if (!::ControlService(service.Get(), SERVICE_CONTROL_STOP, &status)) - return cloud_print::GetLastHResult(); - while (::QueryServiceStatus(service.Get(), &status) && - status.dwCurrentState > SERVICE_STOPPED) { - Sleep(500); - ::ControlService(service.Get(), SERVICE_CONTROL_STOP, &status); - } - return S_OK; -} - -base::FilePath ServiceController::GetBinary() const { - base::FilePath service_path; - CHECK(PathService::Get(base::FILE_EXE, &service_path)); - return service_path.DirName().Append(base::FilePath(kServiceExeName)); -} - -HRESULT ServiceController::InstallConnectorService( - const base::string16& user, - const base::string16& password, - const base::FilePath& user_data_dir, - bool enable_logging) { - return InstallService(user, password, true, kServiceSwitch, user_data_dir, - enable_logging); -} - -HRESULT ServiceController::InstallCheckService( - const base::string16& user, - const base::string16& password, - const base::FilePath& user_data_dir) { - return InstallService(user, password, false, kRequirementsSwitch, - user_data_dir, true); -} - -HRESULT ServiceController::InstallService(const base::string16& user, - const base::string16& password, - bool auto_start, - const std::string& run_switch, - const base::FilePath& user_data_dir, - bool enable_logging) { - // TODO(vitalybuka): consider "lite" version if we don't want unregister - // printers here. - HRESULT hr = UninstallService(); - if (FAILED(hr)) - return hr; - - hr = UpdateRegistryAppId(true); - if (FAILED(hr)) - return hr; - - base::FilePath service_path = GetBinary(); - if (!base::PathExists(service_path)) - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - base::CommandLine command_line(service_path); - command_line.AppendSwitch(run_switch); - if (!user_data_dir.empty()) - command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir); - if (enable_logging) { - command_line.AppendSwitch(switches::kEnableLogging); - command_line.AppendSwitchASCII(switches::kV, "1"); - } - - CopyChromeSwitchesFromCurrentProcess(&command_line); - - LocalSecurityPolicy local_security_policy; - if (local_security_policy.Open()) { - if (!local_security_policy.IsPrivilegeSet(user, kSeServiceLogonRight)) { - LOG(WARNING) << "Setting " << kSeServiceLogonRight << " for " << user; - if (!local_security_policy.SetPrivilege(user, kSeServiceLogonRight)) { - LOG(ERROR) << "Failed to set" << kSeServiceLogonRight; - LOG(ERROR) << "Make sure you can run the service as " << user << "."; - } - } - } else { - LOG(ERROR) << "Failed to open security policy."; - } - - ServiceHandle scm; - hr = OpenServiceManager(&scm); - if (FAILED(hr)) - return hr; - - base::string16 display_name = - cloud_print::LoadLocalString(IDS_SERVICE_DISPLAY_NAME); - ServiceHandle service( - ::CreateService( - scm.Get(), name_.c_str(), display_name.c_str(), SERVICE_ALL_ACCESS, - SERVICE_WIN32_OWN_PROCESS, - auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START, - SERVICE_ERROR_NORMAL, command_line.GetCommandLineString().c_str(), - NULL, NULL, NULL, user.empty() ? NULL : user.c_str(), - password.empty() ? NULL : password.c_str())); - - if (!service.IsValid()) { - LOG(ERROR) << "Failed to install service as " << user << "."; - return cloud_print::GetLastHResult(); - } - - base::string16 description_string = - cloud_print::LoadLocalString(IDS_SERVICE_DESCRIPTION); - SERVICE_DESCRIPTION description = {0}; - description.lpDescription = const_cast<wchar_t*>(description_string.c_str()); - ::ChangeServiceConfig2(service.Get(), SERVICE_CONFIG_DESCRIPTION, - &description); - - return S_OK; -} - -HRESULT ServiceController::UninstallService() { - StopService(); - - ServiceHandle service; - OpenService(name_, SERVICE_STOP | DELETE, &service); - HRESULT hr = S_FALSE; - if (service.IsValid()) { - if (!::DeleteService(service.Get())) { - LOG(ERROR) << "Failed to uninstall service"; - hr = cloud_print::GetLastHResult(); - } - } - UpdateRegistryAppId(false); - return hr; -} - -HRESULT ServiceController::UpdateBinaryPath() { - UpdateState(); - ServiceController::State origina_state = state(); - if (origina_state < ServiceController::STATE_STOPPED) - return S_FALSE; - - ServiceHandle service; - HRESULT hr = OpenService(name_, SERVICE_CHANGE_CONFIG, &service); - if (FAILED(hr)) - return hr; - - base::FilePath service_path = GetBinary(); - if (!base::PathExists(service_path)) - return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - - command_line_.SetProgram(service_path); - if (!::ChangeServiceConfig(service.Get(), SERVICE_NO_CHANGE, - SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, - command_line_.GetCommandLineString().c_str(), NULL, - NULL, NULL, NULL, NULL, NULL)) { - return cloud_print::GetLastHResult(); - } - - if (origina_state != ServiceController::STATE_RUNNING) - return S_OK; - - hr = StopService(); - if (FAILED(hr)) - return hr; - - hr = StartService(); - if (FAILED(hr)) - return hr; - - return S_OK; -} - -void ServiceController::UpdateState() { - state_ = STATE_NOT_FOUND; - user_.clear(); - is_logging_enabled_ = false; - - ServiceHandle service; - HRESULT hr = OpenService(name_, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG, - &service); - if (FAILED(hr)) - return; - - state_ = STATE_STOPPED; - SERVICE_STATUS status = {0}; - if (::QueryServiceStatus(service.Get(), &status) && - status.dwCurrentState == SERVICE_RUNNING) { - state_ = STATE_RUNNING; - } - - DWORD config_size = 0; - ::QueryServiceConfig(service.Get(), NULL, 0, &config_size); - if (!config_size) - return; - - std::vector<uint8_t> buffer(config_size, 0); - QUERY_SERVICE_CONFIG* config = - reinterpret_cast<QUERY_SERVICE_CONFIG*>(&buffer[0]); - if (!::QueryServiceConfig(service.Get(), config, - static_cast<DWORD>(buffer.size()), - &config_size) || - config_size != buffer.size()) { - return; - } - - command_line_ = base::CommandLine::FromString(config->lpBinaryPathName); - if (!command_line_.HasSwitch(kServiceSwitch)) { - state_ = STATE_NOT_FOUND; - return; - } - is_logging_enabled_ = command_line_.HasSwitch(switches::kEnableLogging); - user_ = config->lpServiceStartName; -} - -bool ServiceController::is_logging_enabled() const { - return command_line_.HasSwitch(switches::kEnableLogging); -}
diff --git a/cloud_print/service/win/service_controller.h b/cloud_print/service/win/service_controller.h deleted file mode 100644 index fcec4fd..0000000 --- a/cloud_print/service/win/service_controller.h +++ /dev/null
@@ -1,76 +0,0 @@ -// Copyright 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 CLOUD_PRINT_SERVICE_SERVICE_CONTROLLER_H_ -#define CLOUD_PRINT_SERVICE_SERVICE_CONTROLLER_H_ - -#include <atlbase.h> -#include <string> - -#include "base/command_line.h" -#include "base/strings/string16.h" -#include "cloud_print/service/resources.h" - -namespace base { -class FilePath; -} // base - -class ServiceController { - public: - enum State { - STATE_UNKNOWN = 0, - STATE_NOT_FOUND, - STATE_STOPPED, - STATE_RUNNING, - }; - - DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CLOUDPRINTSERVICE, - "{8013FB7C-2E3E-4992-B8BD-05C0C4AB0627}") - - ServiceController(); - ~ServiceController(); - - // Installs temporarily service to check pre-requirements. - HRESULT InstallCheckService(const base::string16& user, - const base::string16& password, - const base::FilePath& user_data_dir); - - // Installs real service that will run connector. - HRESULT InstallConnectorService(const base::string16& user, - const base::string16& password, - const base::FilePath& user_data_dir, - bool enable_logging); - - HRESULT UninstallService(); - - HRESULT StartService(); - HRESULT StopService(); - - HRESULT UpdateBinaryPath(); - - // Query service status and options. Results accessible with getters below. - void UpdateState(); - State state() const { return state_; } - const base::string16& user() const { return user_; } - bool is_logging_enabled() const; - - base::FilePath GetBinary() const; - - private: - HRESULT InstallService(const base::string16& user, - const base::string16& password, - bool auto_start, - const std::string& run_switch, - const base::FilePath& user_data_dir, - bool enable_logging); - - const base::string16 name_; - State state_; - base::string16 user_; - bool is_logging_enabled_; - base::CommandLine command_line_; -}; - -#endif // CLOUD_PRINT_SERVICE_SERVICE_CONTROLLER_H_ -
diff --git a/cloud_print/service/win/service_ipc_unittest.cc b/cloud_print/service/win/service_ipc_unittest.cc deleted file mode 100644 index a9aa666..0000000 --- a/cloud_print/service/win/service_ipc_unittest.cc +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 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. - -#include "base/files/scoped_temp_dir.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "cloud_print/service/win/service_listener.h" -#include "cloud_print/service/win/service_utils.h" -#include "cloud_print/service/win/setup_listener.h" -#include "testing/gtest/include/gtest/gtest.h" - -TEST(ServiceIpcTest, Timeout) { - SetupListener setup(GetCurrentUserName()); - ASSERT_FALSE(setup.WaitResponce(base::TimeDelta::FromSeconds(3))); -} - -TEST(ServiceIpcTest, Sequence) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - SetupListener setup(GetCurrentUserName()); - ServiceListener service(temp_dir.path()); - ASSERT_TRUE(setup.WaitResponce(base::TimeDelta::FromSeconds(30))); - EXPECT_EQ(setup.user_data_dir(), temp_dir.path()); - EXPECT_EQ(setup.user_name(), GetCurrentUserName()); -} - -TEST(ServiceIpcTest, ReverseSequence) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - ServiceListener service(temp_dir.path()); - base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); - SetupListener setup(GetCurrentUserName()); - ASSERT_TRUE(setup.WaitResponce(base::TimeDelta::FromSeconds(30))); - EXPECT_EQ(setup.user_data_dir(), temp_dir.path()); - EXPECT_EQ(setup.user_name(), GetCurrentUserName()); -} - -TEST(ServiceIpcTest, InvaludUser) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - SetupListener setup(L"guest"); - ServiceListener service(temp_dir.path()); - ASSERT_FALSE(setup.WaitResponce(base::TimeDelta::FromSeconds(3))); -} -
diff --git a/cloud_print/service/win/service_listener.cc b/cloud_print/service/win/service_listener.cc deleted file mode 100644 index faf9ca3..0000000 --- a/cloud_print/service/win/service_listener.cc +++ /dev/null
@@ -1,111 +0,0 @@ -// Copyright 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. - -#include "cloud_print/service/win/service_listener.h" - -#include <stddef.h> -#include <stdint.h> - -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/json/json_writer.h" -#include "base/threading/thread.h" -#include "base/values.h" -#include "chrome/installer/launcher_support/chrome_launcher_support.h" -#include "cloud_print/service/win/service_utils.h" -#include "cloud_print/service/win/setup_listener.h" -#include "ipc/ipc_channel.h" -#include "printing/backend/print_backend.h" -#include "printing/backend/win_helper.h" - -namespace { - -std::string GetEnvironment(const base::FilePath& user_data_dir) { - scoped_refptr<printing::PrintBackend> backend( - printing::PrintBackend::CreateInstance(NULL)); - printing::PrinterList printer_list; - backend->EnumeratePrinters(&printer_list); - scoped_ptr<base::ListValue> printers(new base::ListValue()); - for (size_t i = 0; i < printer_list.size(); ++i) { - printers->AppendString(printer_list[i].printer_name); - } - - base::DictionaryValue environment; - environment.Set(SetupListener::kPrintersJsonValueName, printers.release()); - environment.SetBoolean(SetupListener::kXpsAvailableJsonValueName, - printing::XPSModule::Init()); - environment.SetString(SetupListener::kUserNameJsonValueName, - GetCurrentUserName()); - environment.SetString( - SetupListener::kChromePathJsonValueName, - chrome_launcher_support::GetAnyChromePath(false /* is_sxs */).value()); - if (base::CreateDirectory(user_data_dir)) { - base::FilePath temp_file; - if (base::CreateTemporaryFileInDir(user_data_dir, &temp_file)) { - DCHECK(base::PathExists(temp_file)); - environment.SetString(SetupListener::kUserDataDirJsonValueName, - user_data_dir.value()); - base::DeleteFile(temp_file, false); - } - } - - std::string result; - base::JSONWriter::Write(environment, &result); - return result; -} - -} // namespace - -ServiceListener::ServiceListener(const base::FilePath& user_data_dir) - : ipc_thread_(new base::Thread("ipc_thread")), - user_data_dir_(user_data_dir) { - ipc_thread_->StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); - ipc_thread_->message_loop()->PostTask( - FROM_HERE, base::Bind(&ServiceListener::Connect, base::Unretained(this))); -} - -ServiceListener::~ServiceListener() { - ipc_thread_->message_loop()->PostTask(FROM_HERE, - base::Bind(&ServiceListener::Disconnect, - base::Unretained(this))); - ipc_thread_->Stop(); -} - -bool ServiceListener::OnMessageReceived(const IPC::Message& msg) { - return true; -} - -void ServiceListener::OnChannelConnected(int32_t peer_pid) { - IPC::Message* message = new IPC::Message(0, 0, IPC::Message::PRIORITY_NORMAL); - message->WriteString(GetEnvironment(user_data_dir_)); - channel_->Send(message); -} - -void ServiceListener::Disconnect() { - channel_.reset(); -} - -void ServiceListener::Connect() { - base::win::ScopedHandle handle( - ::CreateFile(SetupListener::kSetupPipeName, GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, - SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | - FILE_FLAG_OVERLAPPED, NULL)); - if (handle.IsValid()) { - // This process never sends or receives brokered attachments, so there's no - // need for an attachment broker. - channel_ = - IPC::Channel::CreateClient(IPC::ChannelHandle(handle.Get()), this); - bool connected = channel_->Connect(); - DCHECK(connected); - } else { - ipc_thread_->message_loop()->PostDelayedTask( - FROM_HERE, - base::Bind(&ServiceListener::Connect, base::Unretained(this)), - base::TimeDelta::FromMilliseconds(500)); - } -} -
diff --git a/cloud_print/service/win/service_listener.h b/cloud_print/service/win/service_listener.h deleted file mode 100644 index 15cf06f..0000000 --- a/cloud_print/service/win/service_listener.h +++ /dev/null
@@ -1,43 +0,0 @@ -// Copyright 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 CLOUD_PRINT_SERVICE_SERVICE_LISTENER_H_ -#define CLOUD_PRINT_SERVICE_SERVICE_LISTENER_H_ - -#include <stdint.h> - -#include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/memory/scoped_ptr.h" -#include "ipc/ipc_listener.h" - -namespace base { -class Thread; -} // base - -namespace IPC { -class Channel; -} // IPC - -// Simple IPC listener to run on service side to collect service environment and -// to send back to setup utility. -class ServiceListener : public IPC::Listener { - public: - explicit ServiceListener(const base::FilePath& user_data_dir); - ~ServiceListener() override; - - bool OnMessageReceived(const IPC::Message& msg) override; - void OnChannelConnected(int32_t peer_pid) override; - - private: - void Disconnect(); - void Connect(); - - scoped_ptr<base::Thread> ipc_thread_; - scoped_ptr<IPC::Channel> channel_; - base::FilePath user_data_dir_; -}; - -#endif // CLOUD_PRINT_SERVICE_SERVICE_LISTENER_H_ -
diff --git a/cloud_print/service/win/service_resources.grd b/cloud_print/service/win/service_resources.grd deleted file mode 100644 index f12af83..0000000 --- a/cloud_print/service/win/service_resources.grd +++ /dev/null
@@ -1,167 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- -Copyright 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. ---> - - -<grit latest_public_release="0" current_release="1"> - <outputs> - <output filename="resources.h" type="rc_header"> - <emit emit_type='prepend'></emit> - </output> - <output filename="service_resources_en.rc" type="rc_all" lang="en" language_section="lang"/> - </outputs> - <translations> - </translations> - <release seq="1"> - <messages> - <message name="IDS_SERVICE_NAME" translateable="false"> - CloudPrintService - </message> - <message name="IDS_SERVICE_DISPLAY_NAME"> - Google Cloud Print Service - </message> - <message name="IDS_SERVICE_DESCRIPTION"> - Connects classic printers with Google Cloud Print. - </message> - <message name="IDS_FULL_PRODUCT_NAME" translateable="false"> - Google Cloud Print Service - </message> - <message name="IDS_SETUP_PROGRAM_NAME"> - Service Options - </message> - <message name="IDS_USER_LABEL"> - &User name: - </message> - <message name="IDS_PASSWORD_LABEL"> - &Password: - </message> - <message name="IDS_STATE_LABEL"> - Service state: - </message> - <message name="IDS_LOGGING_LABEL"> - Enable &logging - </message> - <message name="IDS_INSTALL"> - Install - </message> - <message name="IDS_UNINSTALL"> - Uninstall - </message> - <message name="IDS_SERVICE_NOT_FOUND"> - Not registered - </message> - <message name="IDS_SERVICE_STOPPED"> - Stopped - </message> - <message name="IDS_SERVICE_RUNNING"> - Running - </message> - <message name="IDS_SERVICE_START"> - &Start - </message> - <message name="IDS_SERVICE_STOP"> - &Stop - </message> - <message name="IDS_SERVICE_INSTALL"> - &Register - </message> - <message name="IDS_SERVICE_UNINSTALL"> - &Unregister - </message> - <message name="IDS_CLOSE"> - &Close - </message> - <message name="IDS_OPERATION_FAILED_TITLE"> - Operaton failed - </message> - <message name="IDS_CONTINUE_IN_CHROME_TITLE"> - Continue in Chrome - </message> - <message name="IDS_ADD_PRINTERS_USING_CHROME"> - Use Chrome to sign in and add printers. Close Chrome to cancel. - </message> - <message name="IDS_COMMAND_LINE_HELP_TITLE"> - Usage: - </message> - <message name="IDS_COMMAND_LINE_DESCRIPTION"> - Manages Google Cloud Print Service for Windows. - </message> - <message name="IDS_SWITCH_HELP_INSTALL"> - Installs cloud print as service. - </message> - <message name="IDS_SWITCH_HELP_DATA_DIR"> - User data directory with "Service State" file. - </message> - <message name="IDS_SWITCH_HELP_UNINSTALL"> - Uninstalls service. - </message> - <message name="IDS_SWITCH_HELP_START"> - Starts service. May be combined with installation. - </message> - <message name="IDS_SWITCH_HELP_STOP"> - Stops service. - </message> - <message name="IDS_WINDOWS_USER_PROMPT1"> - Windows user in form DOMAIN\USERNAME. - </message> - <message name="IDS_WINDOWS_USER_PROMPT2"> - Press [ENTER] to keep <ph name="USER">'$1'</ph>: - </message> - <message name="IDS_WINDOWS_PASSWORD_PROMPT"> - Password: - </message> - <message name="IDS_SERVICE_ENV_CHECK"> - Service will use following settings: - </message> - <message name="IDS_SERVICE_ENV_USER"> - User name: - </message> - <message name="IDS_SERVICE_ENV_CHROME"> - Chrome path: - </message> - <message name="IDS_SERVICE_ENV_DATADIR"> - Data directory: - </message> - <message name="IDS_SERVICE_ENV_PRINTERS"> - Printers: - </message> - <message name="IDS_ERROR_FAILED_INSTALL_SERVICE"> - Failed to install service. - </message> - <message name="IDS_ERROR_FAILED_START_SERVICE"> - Failed to start service. - </message> - <message name="IDS_ERROR_NO_DATA_DIR"> - Service can't write data directory. - </message> - <message name="IDS_ERROR_NO_CHROME"> - Service can't find Chrome. - </message> - <message name="IDS_ERROR_NO_XPS"> - XPS pack is not installed. - </message> - <message name="IDS_ERROR_FAILED_CREATE_CONFIG"> - Failed to create configuration file. - </message> - </messages> - - <includes> - <if expr="lang == 'en'"> - <include name="IDR_CLOUDPRINTSERVICE" file="resources/cloud_print_service.rgs" type="REGISTRY" translateable="false" /> - <include name="IDI_ICON" file="resources/cloudprint.ico" type="ICON" translateable="false" /> - <include name="IDB_LOGO" file="resources/cloud_print_logo_beta.bmp" type="BITMAP" translateable="false" /> - </if> - </includes> - - <structures fallback_to_english="true"> - <if expr="lang == 'en'"> - <structure name="IDD_SETUP_DIALOG" file="resources/setup_dialog.rc" type="dialog" /> - </if> - </structures> - </release> -</grit> -
diff --git a/cloud_print/service/win/service_resources.gypi b/cloud_print/service/win/service_resources.gypi deleted file mode 100644 index ea981a7..0000000 --- a/cloud_print/service/win/service_resources.gypi +++ /dev/null
@@ -1,5 +0,0 @@ -{ - 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/cloud_print/service/service_resources_en.rc', - ], -}
diff --git a/cloud_print/service/win/service_utils.cc b/cloud_print/service/win/service_utils.cc deleted file mode 100644 index 2f6381e8..0000000 --- a/cloud_print/service/win/service_utils.cc +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright 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. - -#include "cloud_print/service/win/service_utils.h" - -#include <windows.h> -#include <security.h> // NOLINT - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/strings/string_util.h" -#include "chrome/common/chrome_switches.h" -#include "components/cloud_devices/common/cloud_devices_switches.h" -#include "content/public/common/content_switches.h" -#include "google_apis/gaia/gaia_switches.h" - -base::string16 GetLocalComputerName() { - DWORD size = 0; - base::string16 result; - ::GetComputerName(NULL, &size); - result.resize(size); - if (result.empty()) - return result; - if (!::GetComputerName(&result[0], &size)) - return base::string16(); - result.resize(size); - return result; -} - -base::string16 ReplaceLocalHostInName(const base::string16& user_name) { - static const wchar_t kLocalDomain[] = L".\\"; - if (base::StartsWith(user_name, kLocalDomain, - base::CompareCase::SENSITIVE)) { - return GetLocalComputerName() + - user_name.substr(arraysize(kLocalDomain) - 2); - } - return user_name; -} - -base::string16 GetCurrentUserName() { - ULONG size = 0; - base::string16 result; - ::GetUserNameEx(::NameSamCompatible, NULL, &size); - result.resize(size); - if (result.empty()) - return result; - if (!::GetUserNameEx(::NameSamCompatible, &result[0], &size)) - return base::string16(); - result.resize(size); - return result; -} - -void CopyChromeSwitchesFromCurrentProcess(base::CommandLine* destination) { - static const char* const kSwitchesToCopy[] = { - switches::kCloudPrintURL, - switches::kCloudPrintXmppEndpoint, - switches::kEnableCloudPrintXps, - switches::kEnableLogging, - switches::kIgnoreUrlFetcherCertRequests, - switches::kLsoUrl, - switches::kV, - }; - destination->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), - kSwitchesToCopy, - arraysize(kSwitchesToCopy)); -} -
diff --git a/cloud_print/service/win/service_utils.h b/cloud_print/service/win/service_utils.h deleted file mode 100644 index a5097728..0000000 --- a/cloud_print/service/win/service_utils.h +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 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 CLOUD_PRINT_SERVICE_SERVICE_UTILS_H_ -#define CLOUD_PRINT_SERVICE_SERVICE_UTILS_H_ - -#include "base/strings/string16.h" - -namespace base { -class CommandLine; -} - -base::string16 ReplaceLocalHostInName(const base::string16& user_name); -base::string16 GetCurrentUserName(); -void CopyChromeSwitchesFromCurrentProcess(base::CommandLine* destination); - -#endif // CLOUD_PRINT_SERVICE_SERVICE_UTILS_H_ -
diff --git a/cloud_print/service/win/setup_listener.cc b/cloud_print/service/win/setup_listener.cc deleted file mode 100644 index 549cad55..0000000 --- a/cloud_print/service/win/setup_listener.cc +++ /dev/null
@@ -1,127 +0,0 @@ -// Copyright 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. - -#include "cloud_print/service/win/setup_listener.h" - -#include <atlbase.h> -#include <atlsecurity.h> -#include <stddef.h> - -#include "base/bind.h" -#include "base/guid.h" -#include "base/json/json_reader.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" -#include "base/values.h" -#include "cloud_print/service/win/service_utils.h" -#include "ipc/ipc_channel.h" - -const char SetupListener::kXpsAvailableJsonValueName[] = "xps_available"; -const char SetupListener::kChromePathJsonValueName[] = "chrome_path"; -const char SetupListener::kPrintersJsonValueName[] = "printers"; -const char SetupListener::kUserDataDirJsonValueName[] = "user_data_dir"; -const char SetupListener::kUserNameJsonValueName[] = "user_name"; -const wchar_t SetupListener::kSetupPipeName[] = - L"\\\\.\\pipe\\CloudPrintServiceSetup"; - -SetupListener::SetupListener(const base::string16& user) - : is_xps_available_(false), - succeded_(false), - done_event_(new base::WaitableEvent(true, false)), - ipc_thread_(new base::Thread("ipc_thread")) { - ipc_thread_->StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); - ipc_thread_->message_loop()->PostTask( - FROM_HERE, - base::Bind(&SetupListener::Connect, base::Unretained(this), user)); -} - -SetupListener::~SetupListener() { - ipc_thread_->message_loop()->PostTask(FROM_HERE, - base::Bind(&SetupListener::Disconnect, - base::Unretained(this))); - ipc_thread_->Stop(); -} - -bool SetupListener::OnMessageReceived(const IPC::Message& msg) { - base::PickleIterator iter(msg); - std::string json_string; - if (!iter.ReadString(&json_string)) - return false; - scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); - const base::DictionaryValue* dictionary = NULL; - if (!value || !value->GetAsDictionary(&dictionary) || !dictionary) { - LOG(ERROR) << "Invalid response from service"; - return false; - } - - const base::ListValue* printers = NULL; - if (dictionary->GetList(kPrintersJsonValueName, &printers) && printers) { - for (size_t i = 0; i < printers->GetSize(); ++i) { - std::string printer; - if (printers->GetString(i, &printer) && !printer.empty()) - printers_.push_back(printer); - } - } - dictionary->GetBoolean(kXpsAvailableJsonValueName, &is_xps_available_); - dictionary->GetString(kUserNameJsonValueName, &user_name_); - - base::string16 chrome_path; - dictionary->GetString(kChromePathJsonValueName, &chrome_path); - chrome_path_ = base::FilePath(chrome_path); - - base::string16 user_data_dir; - dictionary->GetString(kUserDataDirJsonValueName, &user_data_dir); - user_data_dir_ = base::FilePath(user_data_dir); - - succeded_ = true; - done_event_->Signal(); - return true; -} - -void SetupListener::OnChannelError() { - done_event_->Signal(); -} - -bool SetupListener::WaitResponce(const base::TimeDelta& delta) { - return done_event_->TimedWait(delta) && succeded_; -} - -void SetupListener::Disconnect() { - channel_.reset(); - ipc_thread_->message_loop()->QuitWhenIdle(); -} - -void SetupListener::Connect(const base::string16& user) { - ATL::CDacl dacl; - - ATL::CSid user_sid; - if (!user_sid.LoadAccount(ReplaceLocalHostInName(user).c_str())) { - LOG(ERROR) << "Unable to load Sid for" << user; - } else { - dacl.AddAllowedAce(user_sid, GENERIC_READ | GENERIC_WRITE); - } - - ATL::CSecurityDesc desk; - desk.SetDacl(dacl); - - ATL::CSecurityAttributes attribs(desk); - - base::win::ScopedHandle pipe( - CreateNamedPipe(kSetupPipeName, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | - FILE_FLAG_FIRST_PIPE_INSTANCE, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, - IPC::Channel::kReadBufferSize, - IPC::Channel::kReadBufferSize, 5000, &attribs)); - if (pipe.IsValid()) { - channel_ = IPC::Channel::CreateServer(IPC::ChannelHandle(pipe.Get()), - this); - if (!channel_->Connect()) - done_event_->Signal(); - } -} -
diff --git a/cloud_print/service/win/setup_listener.h b/cloud_print/service/win/setup_listener.h deleted file mode 100644 index b0c2c67..0000000 --- a/cloud_print/service/win/setup_listener.h +++ /dev/null
@@ -1,83 +0,0 @@ -// Copyright 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 CLOUD_PRINT_SERVICE_SETUP_LISTENER_H_ -#define CLOUD_PRINT_SERVICE_SETUP_LISTENER_H_ - -#include <string> -#include <vector> - -#include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string16.h" -#include "ipc/ipc_listener.h" - -namespace base { -class Thread; -class TimeDelta; -class WaitableEvent; -} // base - -namespace IPC { -class Channel; -} // IPC - -// Simple IPC listener to run on setup utility size wait message with data about -// environment from service process. -class SetupListener : public IPC::Listener { - public: - static const char kXpsAvailableJsonValueName[]; - static const char kChromePathJsonValueName[]; - static const char kPrintersJsonValueName[]; - static const char kUserDataDirJsonValueName[]; - static const char kUserNameJsonValueName[]; - static const wchar_t kSetupPipeName[]; - - explicit SetupListener(const base::string16& user); - ~SetupListener() override; - - bool OnMessageReceived(const IPC::Message& msg) override; - void OnChannelError() override; - - bool WaitResponce(const base::TimeDelta& delta); - - const base::FilePath& chrome_path() const { - return chrome_path_; - } - - const base::FilePath& user_data_dir() const { - return user_data_dir_; - } - - const base::string16& user_name() const { - return user_name_; - } - - const std::vector<std::string>& printers() const { - return printers_; - } - - bool is_xps_available() const { - return is_xps_available_; - } - - private: - void Disconnect(); - void Connect(const base::string16& user); - - base::FilePath chrome_path_; - base::FilePath user_data_dir_; - base::string16 user_name_; - std::vector<std::string> printers_; - bool is_xps_available_; - bool succeded_; - - scoped_ptr<base::WaitableEvent> done_event_; - scoped_ptr<base::Thread> ipc_thread_; - scoped_ptr<IPC::Channel> channel_; -}; - -#endif // CLOUD_PRINT_SERVICE_SETUP_LISTENER_H_ -
diff --git a/cloud_print/virtual_driver/gcp_driver.gpd b/cloud_print/virtual_driver/gcp_driver.gpd deleted file mode 100644 index 3e853d6..0000000 --- a/cloud_print/virtual_driver/gcp_driver.gpd +++ /dev/null
@@ -1,1034 +0,0 @@ -*% Copyright (c) 2012 The Chromium Authors. All rights reserved. -*% Use of this source code is governed by a BSD-style license that can be -*% found in the LICENSE file. - -*GPDFileVersion: "1.0" -*GPDSpecVersion: "1.0" -*Include: "StdNames.gpd" -*ResourceDLL: "unires.dll" -*ModelName: "Google Cloud Printer" -*MasterUnits: PAIR(1200, 1200) -*MaxCopies: 1 -*PrintRatePPM: 200 -*PrinterType: PAGE -*IsXPSDriver?: TRUE - -*Feature: ColorMode { - *rcNameID: =COLOR_PRINTING_MODE_DISPLAY - *DefaultOption: 24bpp - *ConcealFromUI?: TRUE - *Option: 24bpp { - *rcNameID: =24BPP_DISPLAY - *DevNumOfPlanes: 1 - *DevBPP: 24 - *DrvBPP: 24 - } -} - -*Feature: Memory { - *rcNameID: =PRINTER_MEMORY_DISPLAY - *DefaultOption: 65536KB - *Option: 16384KB { - *Name: "16MB" - *MemoryConfigKB: PAIR(16384, 16384) - } - *Option: 65536KB { - *Name: "64MB" - *MemoryConfigKB: PAIR(65536, 65536) - } -} - -*Feature: Orientation { - *rcNameID: =ORIENTATION_DISPLAY - *DefaultOption: PORTRAIT - *Option: PORTRAIT { - *rcNameID: =PORTRAIT_DISPLAY - } - *Option: LANDSCAPE_CC270 { - *rcNameID: =LANDSCAPE_DISPLAY - } -} - -*Feature: PaperSize { - *rcNameID: =PAPER_SIZE_DISPLAY - *DefaultOption: LETTER - - *Option: A2 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(19842, 28062) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(28062, 19842) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: A3 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(14031, 19842) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(19842, 14031) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: A4 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(9921, 14031) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(14031, 9921) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: A5 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(6992, 9921) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(9921, 6992) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: A6 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(4960, 6992) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(6992, 4960) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: A4_PLUS { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(9921, 15590) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(15590, 9921) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: A3_EXTRA { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(15212, 21023) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(21023, 15212) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: A4_EXTRA { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(11102, 15212) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(15212, 11102) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: A5_EXTRA { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(8220, 11102) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(11102, 8220) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: B4 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(12141, 17196) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(17196, 12141) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: B5 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(8598, 12141) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(12141, 8598) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_B4 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(11811, 16677) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(16677, 11811) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_B5 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(8314, 11811) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(11811, 8314) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_B6 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5905, 8314) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(8314, 5905) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: B6_JIS { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(6047, 8598) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(8598, 6047) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: B5_EXTRA { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(9496, 13039) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(13039, 9496) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_C3 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(15307, 21637) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(21637, 15307) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_C4 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(10818, 15307) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(15307, 10818) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_C5 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(7653, 10818) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10818, 7653) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_C6 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5385, 7653) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(7653, 5385) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_C65 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5385, 10818) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10818, 5385) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_DL { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5196, 10393) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10393, 5196) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: JENV_CHOU3 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5669, 11102) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(11102, 5669) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: JENV_CHOU4 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(4251, 9685) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(9685, 4251) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: JAPANESE_POSTCARD { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(4724, 6992) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(6992, 4724) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: JENV_KAKU2 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(11338, 15685) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(15685, 11338) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: DBL_JAPANESE_POSTCARD { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(6992, 9448) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(9448, 6992) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: JENV_YOU4 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(4960, 11102) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(11102, 4960) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: 10X11 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(12000, 13200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(13200, 12000) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: 10X14 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(12000, 16800) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(16800, 12000) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: 9X11 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(10800, 13200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(13200, 10800) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: CSHEET { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(20400, 26400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(26400, 20400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: DSHEET { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(26400, 40800) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(40800, 26400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ESHEET { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(40800, 52800) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(52800, 40800) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: EXECUTIVE { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(8700, 12600) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(12600, 8700) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: FANFOLD_STD_GERMAN { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(10200, 14400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(14400, 10200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: FANFOLD_US { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(13200, 17850) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(17850, 13200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: FOLIO { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(10200, 15600) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(15600, 10200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: STATEMENT { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(6600, 10200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10200, 6600) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: TABLOID { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(13200, 20400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(20400, 13200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: LEGAL { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(10200, 16800) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(16800, 10200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: LEGAL_EXTRA { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(11400, 18000) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(18000, 11400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: LETTER { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(10200, 13200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(13200, 10200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: LETTER_EXTRA { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(11400, 14400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(14400, 11400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: LETTER_PLUS { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(10200, 15228) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(15228, 10200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_MONARCH { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(4650, 9000) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(9000, 4650) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_9 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(4650, 10650) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10650, 4650) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_10 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(4950, 11400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(11400, 4950) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_11 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5400, 12450) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(12450, 5400) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_12 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5700, 13200) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(13200, 5700) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_14 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(6000, 13800) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(13800, 6000) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: ENV_INVITE { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(10393, 10393) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10393, 10393) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: PENV_1 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(4818, 7795) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(7795, 4818) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: PENV_3 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5905, 8314) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(8314, 5905) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: PENV_4 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5196, 9826) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(9826, 5196) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: PENV_5 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5196, 10393) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10393, 5196) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: PENV_6 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5669, 10866) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10866, 5669) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: PENV_7 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(7559, 10866) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(10866, 7559) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: PENV_8 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(5669, 14598) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(14598, 5669) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } - - *Option: PENV_10 { - *rcNameID: =RCID_DMPAPER_SYSTEM_NAME - *switch: Orientation { - *case: PORTRAIT { - *PrintableArea: PAIR(15307, 21637) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - *case: LANDSCAPE_CC270 { - *PrintableArea: PAIR(21637, 15307) - *PrintableOrigin: PAIR(0, 0) - *CursorOrigin: PAIR(0, 0) - } - } - } -} - -*Feature: Resolution { - *rcNameID: =RESOLUTION_DISPLAY - *DefaultOption: 600dpi - - *Option: 600dpi { - *Name: "600 x 600 " =DOTS_PER_INCH - *DPI: PAIR(600, 600) - *TextDPI: PAIR(600, 600) - *SpotDiameter: 100 - *Command: CmdBeginRaster { *Cmd: "<1B>*v7S<1B>*r1A" } - *Command: CmdEndRaster { *Cmd: "<1B>*rC" } - *Command: CmdSendBlockData { *Cmd: "<1B>*b" %d {NumOfDataBytes}"W" } - } -} - -*Command: CmdCR { *Cmd: "<0D>" } -*Command: CmdLF { *Cmd: "<0A>" } -*Command: CmdFF { *Cmd: "<0C>" }
diff --git a/cloud_print/virtual_driver/win/BUILD.gn b/cloud_print/virtual_driver/win/BUILD.gn deleted file mode 100644 index 110b5a36..0000000 --- a/cloud_print/virtual_driver/win/BUILD.gn +++ /dev/null
@@ -1,19 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -assert(is_win) - -source_set("win") { - sources = [ - "virtual_driver_consts.cc", - "virtual_driver_consts.h", - "virtual_driver_helpers.cc", - "virtual_driver_helpers.h", - ] - - deps = [ - "//base", - "//cloud_print/common", - ] -}
diff --git a/cloud_print/virtual_driver/win/gcp_portmon64_dll.ver b/cloud_print/virtual_driver/win/gcp_portmon64_dll.ver deleted file mode 100644 index 958a012..0000000 --- a/cloud_print/virtual_driver/win/gcp_portmon64_dll.ver +++ /dev/null
@@ -1,3 +0,0 @@ -INTERNAL_NAME=gcp_portmon64.dll -ORIGINAL_FILENAME=gcp_portmon64.dll -FILETYPE=0x2L
diff --git a/cloud_print/virtual_driver/win/gcp_portmon_dll.ver b/cloud_print/virtual_driver/win/gcp_portmon_dll.ver deleted file mode 100644 index dd75a0f1..0000000 --- a/cloud_print/virtual_driver/win/gcp_portmon_dll.ver +++ /dev/null
@@ -1,3 +0,0 @@ -INTERNAL_NAME=gcp_portmon.dll -ORIGINAL_FILENAME=gcp_portmon.dll -FILETYPE=0x2L
diff --git a/cloud_print/virtual_driver/win/install/BUILD.gn b/cloud_print/virtual_driver/win/install/BUILD.gn deleted file mode 100644 index 754dd6c..0000000 --- a/cloud_print/virtual_driver/win/install/BUILD.gn +++ /dev/null
@@ -1,111 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//chrome/version.gni") -import("//tools/grit/grit_rule.gni") - -assert(is_win) - -executable("virtual_driver_setup") { - sources = [ - "setup.cc", - ] - - configs -= [ "//build/config/win:console" ] - configs += [ "//build/config/win:windowed" ] - - deps = [ - ":copy_gcp_driver", - ":resources", - ":setup_version", - "//base", - "//cloud_print/common", - "//cloud_print/common:install_utils", - "//cloud_print/virtual_driver/win", - ] - - libs = [ "setupapi.lib" ] - ldflags = [ "/DELAYLOAD:winspool.drv" ] -} - -copy("copy_gcp_driver") { - sources = [ - "../../gcp_driver.gpd", - ] - outputs = [ - "$root_build_dir/gcp_driver.gpd", - ] -} - -process_version("setup_version") { - template_file = chrome_version_rc_template - sources = [ - "virtual_driver_setup_exe.ver", - ] - output = "$target_gen_dir/virtual_driver_setup.rc" -} - -grit("resources") { - visibility = [ ":*" ] - - source = "virtual_driver_setup_resources.grd" - - output_dir = "$root_gen_dir/virtual_driver_setup_resources" - - outputs = [ - "grit/virtual_driver_setup_resources.h", - "virtual_driver_setup_resources_ar.rc", - "virtual_driver_setup_resources_bg.rc", - "virtual_driver_setup_resources_bn.rc", - "virtual_driver_setup_resources_ca.rc", - "virtual_driver_setup_resources_cs.rc", - "virtual_driver_setup_resources_da.rc", - "virtual_driver_setup_resources_de.rc", - "virtual_driver_setup_resources_el.rc", - "virtual_driver_setup_resources_en.rc", - "virtual_driver_setup_resources_en-GB.rc", - "virtual_driver_setup_resources_es.rc", - "virtual_driver_setup_resources_es-419.rc", - "virtual_driver_setup_resources_et.rc", - "virtual_driver_setup_resources_fa.rc", - "virtual_driver_setup_resources_fi.rc", - "virtual_driver_setup_resources_fil.rc", - "virtual_driver_setup_resources_fr.rc", - "virtual_driver_setup_resources_gu.rc", - "virtual_driver_setup_resources_he.rc", - "virtual_driver_setup_resources_hi.rc", - "virtual_driver_setup_resources_hr.rc", - "virtual_driver_setup_resources_hu.rc", - "virtual_driver_setup_resources_id.rc", - "virtual_driver_setup_resources_it.rc", - "virtual_driver_setup_resources_ja.rc", - "virtual_driver_setup_resources_kn.rc", - "virtual_driver_setup_resources_ko.rc", - "virtual_driver_setup_resources_lt.rc", - "virtual_driver_setup_resources_lv.rc", - "virtual_driver_setup_resources_ml.rc", - "virtual_driver_setup_resources_mr.rc", - "virtual_driver_setup_resources_ms.rc", - "virtual_driver_setup_resources_nl.rc", - "virtual_driver_setup_resources_nb.rc", - "virtual_driver_setup_resources_pl.rc", - "virtual_driver_setup_resources_pt-BR.rc", - "virtual_driver_setup_resources_pt-PT.rc", - "virtual_driver_setup_resources_ro.rc", - "virtual_driver_setup_resources_ru.rc", - "virtual_driver_setup_resources_sk.rc", - "virtual_driver_setup_resources_sl.rc", - "virtual_driver_setup_resources_sr.rc", - "virtual_driver_setup_resources_sv.rc", - "virtual_driver_setup_resources_sw.rc", - "virtual_driver_setup_resources_ta.rc", - "virtual_driver_setup_resources_te.rc", - "virtual_driver_setup_resources_th.rc", - "virtual_driver_setup_resources_tr.rc", - "virtual_driver_setup_resources_uk.rc", - "virtual_driver_setup_resources_vi.rc", - "virtual_driver_setup_resources_zh-CN.rc", - "virtual_driver_setup_resources_zh-TW.rc", - ] -}
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ar.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ar.xtb deleted file mode 100644 index 280740c..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ar.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ar"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>برنامج تشغيل XPS<ph name="XPS_URL_END"/> غير مثبّت.</translation> -<translation id="4983496916481712098">طابعة متوافقة مع خدمة طباعة في السحاب من Google</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bg.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bg.xtb deleted file mode 100644 index c5eeb0b..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bg.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="bg"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Драйверът XPS<ph name="XPS_URL_END"/> не е инсталиран.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bn.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bn.xtb deleted file mode 100644 index 92c00281..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_bn.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="bn"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ড্রাইভার<ph name="XPS_URL_END"/> ইনস্টল করা নেই৷</translation> -<translation id="4983496916481712098">Google মেঘ মুদ্রণ</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ca.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ca.xtb deleted file mode 100644 index 06c9597..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ca.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ca"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">El <ph name="XPS_URL"/>controlador XPS<ph name="XPS_URL_END"/> no està instal·lat.</translation> -<translation id="4983496916481712098">Impressora de Google Cloud</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_cs.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_cs.xtb deleted file mode 100644 index 49281b001..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_cs.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="cs"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">Není nainstalován <ph name="XPS_URL"/>ovladač XPS<ph name="XPS_URL_END"/>.</translation> -<translation id="4983496916481712098">Tiskárna v cloudu Google</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_da.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_da.xtb deleted file mode 100644 index fa7f7d21..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_da.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="da"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-driver<ph name="XPS_URL_END"/> er ikke installeret.</translation> -<translation id="4983496916481712098">Google Cloudprinter</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_de.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_de.xtb deleted file mode 100644 index 0e298c4..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_de.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="de"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-Treiber<ph name="XPS_URL_END"/> ist nicht installiert.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_el.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_el.xtb deleted file mode 100644 index a54377b..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_el.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="el"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Το πρόγραμμα οδήγησης XPS<ph name="XPS_URL_END"/> δεν είναι εγκατεστημένο.</translation> -<translation id="4983496916481712098">Εκτυπωτής Google Cloud</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_en-GB.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_en-GB.xtb deleted file mode 100644 index 83f468e..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_en-GB.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="en-GB"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS driver<ph name="XPS_URL_END"/> is not installed.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es-419.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es-419.xtb deleted file mode 100644 index 96ac8039..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es-419.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="es-419"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">El <ph name="XPS_URL"/>controlador XPS<ph name="XPS_URL_END"/> no está instalado.</translation> -<translation id="4983496916481712098">Impresora de Google Cloud</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es.xtb deleted file mode 100644 index 75d1a247..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_es.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="es"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">El <ph name="XPS_URL"/>controlador XPS<ph name="XPS_URL_END"/> no está instalado.</translation> -<translation id="4983496916481712098">Impresora en la nube de Google</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_et.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_et.xtb deleted file mode 100644 index d023313..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_et.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="et"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-draiver<ph name="XPS_URL_END"/> on installimata.</translation> -<translation id="4983496916481712098">Google'i pilvprinter</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fa.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fa.xtb deleted file mode 100644 index 28944fd..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fa.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fa"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>راهانداز XPS<ph name="XPS_URL_END"/> نصب نشده است.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fi.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fi.xtb deleted file mode 100644 index 5ecb49d..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fi.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fi"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-ajuria<ph name="XPS_URL_END"/> ei ole asennettu.</translation> -<translation id="4983496916481712098">Google Cloud -tulostin</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fil.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fil.xtb deleted file mode 100644 index 4423db92..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fil.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fil"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">Hindi naka-install ang <ph name="XPS_URL"/>XPS driver<ph name="XPS_URL_END"/>.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fr.xtb deleted file mode 100644 index 8479057..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_fr.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fr"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">Le <ph name="XPS_URL"/>pilote XPS<ph name="XPS_URL_END"/> n'est pas installé.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_gu.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_gu.xtb deleted file mode 100644 index 372d6ca..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_gu.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="gu"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ડ્રાઇવર<ph name="XPS_URL_END"/> ઇન્સ્ટોલ કરેલું નથી.</translation> -<translation id="4983496916481712098">Google મેઘ મુદ્રણ</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_he.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_he.xtb deleted file mode 100644 index d284b854..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_he.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="he"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/> מנהל ההתקן של XPS<ph name="XPS_URL_END"/> לא מותקן.</translation> -<translation id="4983496916481712098">מדפסת Google Cloud Print</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hi.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hi.xtb deleted file mode 100644 index 0ca331d..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hi.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hi"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ड्राइवर<ph name="XPS_URL_END"/> इंस्टॉल नहीं है.</translation> -<translation id="4983496916481712098">Google क्लाउड प्रिंटर</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hr.xtb deleted file mode 100644 index 3a23556..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hr.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hr"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Upravljački program za XPS<ph name="XPS_URL_END"/> nije instaliran.</translation> -<translation id="4983496916481712098">Google pisač u oblaku</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hu.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hu.xtb deleted file mode 100644 index 6a8f56e..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_hu.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hu"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">Nincs telepítve <ph name="XPS_URL"/>XPS illesztőprogram<ph name="XPS_URL_END"/>.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_id.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_id.xtb deleted file mode 100644 index a29c270..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_id.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="id"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Driver XPS<ph name="XPS_URL_END"/> tidak terpasang.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_it.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_it.xtb deleted file mode 100644 index ee07dc3..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_it.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="it"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Il driver XPS<ph name="XPS_URL_END"/> non è installato.</translation> -<translation id="4983496916481712098">Google Cloud Print</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ja.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ja.xtb deleted file mode 100644 index 1cf9c1b..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ja.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ja"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ドライバ<ph name="XPS_URL_END"/>がインストールされていません。</translation> -<translation id="4983496916481712098">Google クラウド プリンタ</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_kn.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_kn.xtb deleted file mode 100644 index e64a2fea..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_kn.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="kn"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ಡ್ರೈವರ್<ph name="XPS_URL_END"/> ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ.</translation> -<translation id="4983496916481712098">Google ಮೇಘ ಮುದ್ರಕ</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ko.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ko.xtb deleted file mode 100644 index a73deef..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ko.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ko"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS 드라이버<ph name="XPS_URL_END"/>가 설치되지 않았습니다.</translation> -<translation id="4983496916481712098">Google 클라우드 프린터</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lt.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lt.xtb deleted file mode 100644 index 712778f..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lt.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="lt"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS tvarkyklė<ph name="XPS_URL_END"/> neįdiegta.</translation> -<translation id="4983496916481712098">„Google“ spausdinimo iš debesies programa</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lv.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lv.xtb deleted file mode 100644 index c4499a4..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_lv.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="lv"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS dzinis<ph name="XPS_URL_END"/> nav instalēts.</translation> -<translation id="4983496916481712098">Google mākoņprinteris</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ml.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ml.xtb deleted file mode 100644 index 7fad033..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ml.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ml"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ഡ്രൈവർ<ph name="XPS_URL_END"/> ഇൻസ്റ്റാളുചെയ്തിട്ടില്ല.</translation> -<translation id="4983496916481712098">Google ക്ലൗഡ് പ്രിന്റർ</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_mr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_mr.xtb deleted file mode 100644 index 9cc832bd..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_mr.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="mr"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS ड्राइव्हर<ph name="XPS_URL_END"/> स्थापित केलेला नाही.</translation> -<translation id="4983496916481712098">Google मेघ प्रिंटर</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ms.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ms.xtb deleted file mode 100644 index 63c3567..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ms.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ms"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Pemacu XPS<ph name="XPS_URL_END"/> tidak dipasang.</translation> -<translation id="4983496916481712098">Pencetak Awan Google</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_nl.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_nl.xtb deleted file mode 100644 index 76f87ad..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_nl.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="nl"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-stuurprogramma<ph name="XPS_URL_END"/> is niet geïnstalleerd.</translation> -<translation id="4983496916481712098">Google Cloudprinter</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_no.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_no.xtb deleted file mode 100644 index ddadb1c..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_no.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="no"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS-driveren<ph name="XPS_URL_END"/> er ikke installert.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pl.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pl.xtb deleted file mode 100644 index 07ee486..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pl.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pl"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Sterownik XPS<ph name="XPS_URL_END"/> nie jest zainstalowany.</translation> -<translation id="4983496916481712098">Drukarka Google Cloud</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-BR.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-BR.xtb deleted file mode 100644 index 9d7aff02..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-BR.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pt-BR"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">O <ph name="XPS_URL"/>driver XPS<ph name="XPS_URL_END"/> não está instalado.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-PT.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-PT.xtb deleted file mode 100644 index 128896c..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_pt-PT.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pt-PT"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">O <ph name="XPS_URL"/>controlador XPS<ph name="XPS_URL_END"/> não está instalado.</translation> -<translation id="4983496916481712098">Impressora do Google Cloud Print</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ro.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ro.xtb deleted file mode 100644 index 7afd35a..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ro.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ro"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Driverul XPS<ph name="XPS_URL_END"/> nu este instalat.</translation> -<translation id="4983496916481712098">Imprimanta Google Cloud Print</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ru.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ru.xtb deleted file mode 100644 index 6828c60..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ru.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ru"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Драйвер XPS<ph name="XPS_URL_END"/> не установлен.</translation> -<translation id="4983496916481712098">Виртуальный принтер Google</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sk.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sk.xtb deleted file mode 100644 index f50b627..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sk.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sk"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Ovládač XPS<ph name="XPS_URL_END"/> nie je nainštalovaný.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sl.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sl.xtb deleted file mode 100644 index 1eba0bd..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sl.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sl"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">Gonilnik <ph name="XPS_URL"/>XPS<ph name="XPS_URL_END"/> ni nameščen.</translation> -<translation id="4983496916481712098">Tiskalnik za Google Tiskanje v oblaku</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sr.xtb deleted file mode 100644 index 5861cb7..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sr.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sr"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Управљачки програм за XPS<ph name="XPS_URL_END"/> није инсталиран.</translation> -<translation id="4983496916481712098">Google Cloud штампач</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sv.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sv.xtb deleted file mode 100644 index 07a9e93..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sv.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sv"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">Ingen <ph name="XPS_URL"/>XPS-drivrutin<ph name="XPS_URL_END"/> är installerad.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sw.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sw.xtb deleted file mode 100644 index 7106d22..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_sw.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sw"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS driver<ph name="XPS_URL_END"/> is not installed.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ta.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ta.xtb deleted file mode 100644 index 612c1908..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_ta.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ta"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS இயக்கி<ph name="XPS_URL_END"/> நிறுவப்படவில்லை.</translation> -<translation id="4983496916481712098">Google மேகக்கணி அச்சுப்பொறி</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_te.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_te.xtb deleted file mode 100644 index 0323475..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_te.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="te"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS డ్రైవర్<ph name="XPS_URL_END"/> ఇన్స్టాల్ చేయబడలేదు.</translation> -<translation id="4983496916481712098">Google మేఘ ప్రింటర్</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_th.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_th.xtb deleted file mode 100644 index 67bf8ce..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_th.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="th"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">ไม่ได้ติดตั้ง<ph name="XPS_URL"/>ไดรเวอร์ XPS<ph name="XPS_URL_END"/></translation> -<translation id="4983496916481712098">Google เครื่องพิมพ์ระบบคลาวด์</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_tr.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_tr.xtb deleted file mode 100644 index 7748a61..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_tr.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="tr"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>XPS sürücüsü<ph name="XPS_URL_END"/> yüklü değil.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_uk.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_uk.xtb deleted file mode 100644 index fff693c5..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_uk.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="uk"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Драйвер XPS<ph name="XPS_URL_END"/> не встановлено.</translation> -<translation id="4983496916481712098">Веб-доступний принтер Google</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_vi.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_vi.xtb deleted file mode 100644 index 93186dc..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_vi.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="vi"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631"><ph name="XPS_URL"/>Trình điều khiển XPS<ph name="XPS_URL_END"/> chưa được cài đặt.</translation> -<translation id="4983496916481712098">Google Cloud Printer</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-CN.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-CN.xtb deleted file mode 100644 index d9eface2..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-CN.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-CN"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">未安装 <ph name="XPS_URL"/>XPS 驱动程序<ph name="XPS_URL_END"/>。</translation> -<translation id="4983496916481712098">Google 云端打印机</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-HK.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-HK.xtb deleted file mode 100644 index 68d7922..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-HK.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-HK"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">未安裝 <ph name="XPS_URL"/>XPS 驅動程式<ph name="XPS_URL_END"/>。</translation> -<translation id="4983496916481712098">Google 雲端印表機</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-TW.xtb b/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-TW.xtb deleted file mode 100644 index 87f03685..0000000 --- a/cloud_print/virtual_driver/win/install/resources/virtual_driver_setup_resources_zh-TW.xtb +++ /dev/null
@@ -1,5 +0,0 @@ -<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-TW"> -<translation id="2424268526671779403">Google</translation> -<translation id="4659506378548526631">未安裝 <ph name="XPS_URL"/>XPS 驅動程式<ph name="XPS_URL_END"/>。</translation> -<translation id="4983496916481712098">Google 雲端印表機</translation> -</translationbundle> \ No newline at end of file
diff --git a/cloud_print/virtual_driver/win/install/setup.cc b/cloud_print/virtual_driver/win/install/setup.cc deleted file mode 100644 index afb5954..0000000 --- a/cloud_print/virtual_driver/win/install/setup.cc +++ /dev/null
@@ -1,561 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <windows.h> -#include <setupapi.h> // Must be included after windows.h -#include <winspool.h> -#include <stddef.h> - -#include <iomanip> - -#include "base/at_exit.h" -#include "base/command_line.h" -#include "base/file_version_info_win.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/process/launch.h" -#include "base/process/process.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" -#include "base/win/registry.h" -#include "base/win/scoped_handle.h" -#include "base/win/windows_version.h" -#include "cloud_print/common/win/cloud_print_utils.h" -#include "cloud_print/common/win/install_utils.h" -#include "cloud_print/virtual_driver/win/virtual_driver_consts.h" -#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h" -#include "grit/virtual_driver_setup_resources.h" - -#include <strsafe.h> // Must be after base headers to avoid deprecation - // warnings. - -namespace cloud_print { - -namespace { - -const wchar_t kNameValue[] = L"GCP Virtual Driver"; -const wchar_t kUninstallId[] = L"{74AA24E0-AC50-4B28-BA46-9CF05467C9B7}"; -const wchar_t kGcpUrl[] = L"http://www.google.com/cloudprint"; - -const wchar_t kDataFileName[] = L"gcp_driver.gpd"; -const wchar_t kDriverName[] = L"MXDWDRV.DLL"; -const wchar_t kUiDriverName[] = L"UNIDRVUI.DLL"; -const wchar_t kHelpName[] = L"UNIDRV.HLP"; -const wchar_t* kDependencyList[] = { - kDriverName, - kHelpName, - kUiDriverName, - L"STDDTYPE.GDL", - L"STDNAMES.GPD", - L"STDSCHEM.GDL", - L"STDSCHMX.GDL", - L"UNIDRV.DLL", - L"UNIRES.DLL", - L"XPSSVCS.DLL", -}; - -const char kDelete[] = "delete"; -const char kInstallSwitch[] = "install"; -const char kRegisterSwitch[] = "register"; -const char kUninstallSwitch[] = "uninstall"; -const char kUnregisterSwitch[] = "unregister"; - -base::FilePath GetSystemPath(const base::string16& binary) { - base::FilePath path; - if (!PathService::Get(base::DIR_SYSTEM, &path)) { - LOG(ERROR) << "Unable to get system path."; - return path; - } - return path.Append(binary); -} - -base::FilePath GetNativeSystemPath(const base::string16& binary) { - if (!IsSystem64Bit()) - return GetSystemPath(binary); - base::FilePath path; - // Sysnative will bypass filesystem redirection and give us - // the location of the 64bit system32 from a 32 bit process. - if (!PathService::Get(base::DIR_WINDOWS, &path)) { - LOG(ERROR) << "Unable to get windows path."; - return path; - } - return path.Append(L"sysnative").Append(binary); -} - -void SpoolerServiceCommand(const char* command) { - base::FilePath net_path = GetNativeSystemPath(L"net"); - if (net_path.empty()) - return; - base::CommandLine command_line(net_path); - command_line.AppendArg(command); - command_line.AppendArg("spooler"); - command_line.AppendArg("/y"); - - base::LaunchOptions options; - options.wait = true; - options.start_hidden = true; - VLOG(0) << command_line.GetCommandLineString(); - base::LaunchProcess(command_line, options); -} - -HRESULT RegisterPortMonitor(bool install, const base::FilePath& install_path) { - DCHECK(install || install_path.empty()); - base::FilePath target_path = GetNativeSystemPath(GetPortMonitorDllName()); - if (target_path.empty()) { - LOG(ERROR) << "Unable to get port monitor target path."; - return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); - } - if (install) { - base::FilePath source_path = - install_path.Append(GetPortMonitorDllName()); - if (!base::CopyFile(source_path, target_path)) { - LOG(ERROR) << "Unable copy port monitor dll from " << - source_path.value() << " to " << target_path.value(); - return GetLastHResult(); - } - } else if (!base::PathExists(target_path)) { - // Already removed. Just "succeed" silently. - return S_OK; - } - - base::FilePath regsvr32_path = GetNativeSystemPath(L"regsvr32.exe"); - if (regsvr32_path.empty()) { - LOG(ERROR) << "Can't find regsvr32.exe."; - return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); - } - - base::CommandLine command_line(regsvr32_path); - command_line.AppendArg("/s"); - if (!install) { - command_line.AppendArg("/u"); - } - - // Use system32 path here because otherwise ::AddMonitor would fail. - command_line.AppendArgPath(GetSystemPath(GetPortMonitorDllName())); - - base::LaunchOptions options; - options.wait = true; - - base::Process regsvr32_process = - base::LaunchProcess(command_line.GetCommandLineString(), options); - if (!regsvr32_process.IsValid()) { - LOG(ERROR) << "Unable to launch regsvr32.exe."; - return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - } - - DWORD exit_code = S_OK; - if (install) { - if (!GetExitCodeProcess(regsvr32_process.Handle(), &exit_code)) { - LOG(ERROR) << "Unable to get regsvr32.exe exit code."; - return GetLastHResult(); - } - if (exit_code != 0) { - LOG(ERROR) << "Regsvr32.exe failed with " << exit_code; - return HRESULT_FROM_WIN32(exit_code); - } - } else { - if (!base::DeleteFile(target_path, false)) { - SpoolerServiceCommand("stop"); - bool deleted = base::DeleteFile(target_path, false); - SpoolerServiceCommand("start"); - - if(!deleted) { - LOG(ERROR) << "Unable to delete " << target_path.value(); - return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); - } - } - } - return S_OK; -} - -DWORDLONG GetVersionNumber() { - DWORDLONG retval = 0; - scoped_ptr<FileVersionInfo> version_info( - CREATE_FILE_VERSION_INFO_FOR_CURRENT_MODULE()); - if (version_info.get()) { - FileVersionInfoWin* version_info_win = - static_cast<FileVersionInfoWin*>(version_info.get()); - VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info(); - retval = fixed_file_info->dwFileVersionMS; - retval <<= 32; - retval |= fixed_file_info->dwFileVersionLS; - } - return retval; -} - -UINT CALLBACK CabinetCallback(PVOID data, - UINT notification, - UINT_PTR param1, - UINT_PTR param2) { - const base::FilePath* temp_path( - reinterpret_cast<const base::FilePath*>(data)); - if (notification == SPFILENOTIFY_FILEINCABINET) { - FILE_IN_CABINET_INFO* info = - reinterpret_cast<FILE_IN_CABINET_INFO*>(param1); - for (size_t i = 0; i < arraysize(kDependencyList); i++) { - base::FilePath base_name(info->NameInCabinet); - base_name = base_name.BaseName(); - if (base::FilePath::CompareEqualIgnoreCase(base_name.value().c_str(), - kDependencyList[i])) { - StringCchCopy(info->FullTargetName, MAX_PATH, - temp_path->Append(kDependencyList[i]).value().c_str()); - return FILEOP_DOIT; - } - } - return FILEOP_SKIP; - } - return NO_ERROR; -} - -void ReadyDriverDependencies(const base::FilePath& destination) { - base::FilePath destination_copy(destination); - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - // GetCorePrinterDrivers and GetPrinterDriverPackagePath only exist on - // Vista and later. Winspool.drv must be delayloaded so these calls don't - // create problems on XP. - DWORD size = MAX_PATH; - wchar_t package_path[MAX_PATH] = {0}; - CORE_PRINTER_DRIVER driver; - GetCorePrinterDrivers(NULL, NULL, L"{D20EA372-DD35-4950-9ED8-A6335AFE79F5}", - 1, &driver); - GetPrinterDriverPackagePath(NULL, NULL, NULL, driver.szPackageID, - package_path, MAX_PATH, &size); - SetupIterateCabinet(package_path, 0, &CabinetCallback, &destination_copy); - } else { - // Driver files are in the sp3 cab. - base::FilePath package_path; - PathService::Get(base::DIR_WINDOWS, &package_path); - package_path = package_path.Append(L"Driver Cache\\i386\\sp3.cab"); - SetupIterateCabinet(package_path.value().c_str(), 0, &CabinetCallback, - &destination_copy); - - // Copy the rest from the driver cache or system dir. - base::FilePath driver_cache_path; - PathService::Get(base::DIR_WINDOWS, &driver_cache_path); - driver_cache_path = driver_cache_path.Append(L"Driver Cache\\i386"); - for (size_t i = 0; i < arraysize(kDependencyList); ++i) { - base::FilePath dst_path = destination.Append(kDependencyList[i]); - if (!base::PathExists(dst_path)) { - base::FilePath src_path = driver_cache_path.Append(kDependencyList[i]); - if (!base::PathExists(src_path)) - src_path = GetSystemPath(kDependencyList[i]); - base::CopyFile(src_path, dst_path); - } - } - } -} - -HRESULT InstallDriver(const base::FilePath& install_path) { - base::ScopedTempDir temp_path; - if (!temp_path.CreateUniqueTempDir()) - return HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE); - ReadyDriverDependencies(temp_path.path()); - - std::vector<base::string16> dependent_array; - // Add all files. AddPrinterDriverEx will removes unnecessary. - for (size_t i = 0; i < arraysize(kDependencyList); ++i) { - base::FilePath file_path = temp_path.path().Append(kDependencyList[i]); - if (base::PathExists(file_path)) - dependent_array.push_back(file_path.value()); - else - LOG(WARNING) << "File is missing: " << file_path.BaseName().value(); - } - - // Set up paths for the files we depend on. - base::FilePath data_file = install_path.Append(kDataFileName); - base::FilePath xps_path = temp_path.path().Append(kDriverName); - base::FilePath ui_path = temp_path.path().Append(kUiDriverName); - base::FilePath ui_help_path = temp_path.path().Append(kHelpName); - - if (!base::PathExists(xps_path)) { - return HRESULT_FROM_WIN32(ERROR_BAD_DRIVER); - } - - DRIVER_INFO_6 driver_info = {0}; - // Set up supported print system version. Must be 3. - driver_info.cVersion = 3; - - // None of the print API structures likes constant strings even though they - // don't modify the string. const_casting is the cleanest option. - driver_info.pDataFile = const_cast<LPWSTR>(data_file.value().c_str()); - driver_info.pHelpFile = const_cast<LPWSTR>(ui_help_path.value().c_str()); - driver_info.pDriverPath = const_cast<LPWSTR>(xps_path.value().c_str()); - driver_info.pConfigFile = const_cast<LPWSTR>(ui_path.value().c_str()); - - base::string16 dependent_files(base::JoinString(dependent_array, L"\n")); - dependent_files.push_back(L'\n'); - std::replace(dependent_files.begin(), dependent_files.end(), L'\n', L'\0'); - driver_info.pDependentFiles = &dependent_files[0]; - - // Set up user visible strings. - base::string16 manufacturer = LoadLocalString(IDS_GOOGLE); - driver_info.pszMfgName = const_cast<LPWSTR>(manufacturer.c_str()); - driver_info.pszProvider = const_cast<LPWSTR>(manufacturer.c_str()); - driver_info.pszOEMUrl = const_cast<LPWSTR>(kGcpUrl); - driver_info.dwlDriverVersion = GetVersionNumber(); - base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME); - driver_info.pName = const_cast<LPWSTR>(driver_name.c_str()); - - if (!::AddPrinterDriverEx(NULL, 6, reinterpret_cast<BYTE*>(&driver_info), - APD_COPY_NEW_FILES | APD_COPY_FROM_DIRECTORY)) { - LOG(ERROR) << "Unable to add printer driver"; - return GetLastHResult(); - } - return S_OK; -} - -HRESULT UninstallDriver() { - int tries = 3; - base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME); - while (!DeletePrinterDriverEx(NULL, - NULL, - const_cast<LPWSTR>(driver_name.c_str()), - DPD_DELETE_UNUSED_FILES, - 0) && tries > 0) { - if (GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER) { - LOG(WARNING) << "Print driver is already uninstalled."; - return S_OK; - } - // After deleting the printer it can take a few seconds before - // the driver is free for deletion. Retry a few times before giving up. - LOG(WARNING) << "Attempt to delete printer driver failed. Retrying."; - tries--; - Sleep(2000); - } - if (tries <= 0) { - HRESULT result = GetLastHResult(); - LOG(ERROR) << "Unable to delete printer driver."; - return result; - } - return S_OK; -} - -HRESULT InstallPrinter(void) { - PRINTER_INFO_2 printer_info = {0}; - - // None of the print API structures likes constant strings even though they - // don't modify the string. const_casting is the cleanest option. - base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME); - printer_info.pDriverName = const_cast<LPWSTR>(driver_name.c_str()); - printer_info.pPrinterName = const_cast<LPWSTR>(driver_name.c_str()); - printer_info.pComment = const_cast<LPWSTR>(driver_name.c_str()); - printer_info.pLocation = const_cast<LPWSTR>(kGcpUrl); - base::string16 port_name; - printer_info.pPortName = const_cast<LPWSTR>(kPortName); - printer_info.Attributes = PRINTER_ATTRIBUTE_DIRECT|PRINTER_ATTRIBUTE_LOCAL; - printer_info.pPrintProcessor = const_cast<LPWSTR>(L"winprint"); - HANDLE handle = AddPrinter(NULL, 2, reinterpret_cast<BYTE*>(&printer_info)); - if (handle == NULL) { - HRESULT result = GetLastHResult(); - LOG(ERROR) << "Unable to add printer"; - return result; - } - ClosePrinter(handle); - return S_OK; -} - -HRESULT UninstallPrinter(void) { - HANDLE handle = NULL; - PRINTER_DEFAULTS printer_defaults = {0}; - printer_defaults.DesiredAccess = PRINTER_ALL_ACCESS; - base::string16 driver_name = LoadLocalString(IDS_DRIVER_NAME); - if (!OpenPrinter(const_cast<LPWSTR>(driver_name.c_str()), - &handle, - &printer_defaults)) { - // If we can't open the printer, it was probably already removed. - LOG(WARNING) << "Unable to open printer"; - return S_OK; - } - if (!DeletePrinter(handle)) { - HRESULT result = GetLastHResult(); - LOG(ERROR) << "Unable to delete printer"; - ClosePrinter(handle); - return result; - } - ClosePrinter(handle); - return S_OK; -} - -bool IsOSSupported() { - // We don't support XP service pack 2 or older. - base::win::Version version = base::win::GetVersion(); - return (version > base::win::VERSION_XP) || - ((version == base::win::VERSION_XP) && - (base::win::OSInfo::GetInstance()->service_pack().major >= 3)); -} - -HRESULT RegisterVirtualDriver(const base::FilePath& install_path) { - HRESULT result = S_OK; - - DCHECK(base::DirectoryExists(install_path)); - if (!IsOSSupported()) { - LOG(ERROR) << "Requires XP SP3 or later."; - return HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION); - } - - result = InstallDriver(install_path); - if (FAILED(result)) { - LOG(ERROR) << "Unable to install driver."; - return result; - } - - result = RegisterPortMonitor(true, install_path); - if (FAILED(result)) { - LOG(ERROR) << "Unable to register port monitor."; - return result; - } - - result = InstallPrinter(); - if (FAILED(result) && - result != HRESULT_FROM_WIN32(ERROR_PRINTER_ALREADY_EXISTS)) { - LOG(ERROR) << "Unable to install printer."; - return result; - } - return S_OK; -} - -HRESULT TryUnregisterVirtualDriver() { - HRESULT result = S_OK; - result = UninstallPrinter(); - if (FAILED(result)) { - LOG(ERROR) << "Unable to delete printer."; - return result; - } - result = UninstallDriver(); - if (FAILED(result)) { - LOG(ERROR) << "Unable to remove driver."; - return result; - } - // The second argument is ignored if the first is false. - result = RegisterPortMonitor(false, base::FilePath()); - if (FAILED(result)) { - LOG(ERROR) << "Unable to remove port monitor."; - return result; - } - return S_OK; -} - -HRESULT UnregisterVirtualDriver() { - HRESULT hr = S_FALSE; - for (int i = 0; i < 2; ++i) { - hr = TryUnregisterVirtualDriver(); - if (SUCCEEDED(hr)) { - break; - } - // Restart spooler and try again. - SpoolerServiceCommand("stop"); - SpoolerServiceCommand("start"); - } - return hr; -} - -HRESULT DoUninstall() { - DeleteGoogleUpdateKeys(kGoogleUpdateProductId); - HRESULT result = UnregisterVirtualDriver(); - if (FAILED(result)) - return result; - DeleteUninstallKey(kUninstallId); - DeleteProgramDir(kDelete); - return S_OK; -} - -HRESULT DoUnregister() { - return UnregisterVirtualDriver(); -} - -HRESULT DoRegister(const base::FilePath& install_path) { - HRESULT result = UnregisterVirtualDriver(); - if (FAILED(result)) - return result; - return RegisterVirtualDriver(install_path); -} - -HRESULT DoDelete(const base::FilePath& install_path) { - if (install_path.value().empty()) - return E_INVALIDARG; - if (!base::DirectoryExists(install_path)) - return S_FALSE; - Sleep(5000); // Give parent some time to exit. - return base::DeleteFile(install_path, true) ? S_OK : E_FAIL; -} - -HRESULT DoInstall(const base::FilePath& install_path) { - HRESULT result = UnregisterVirtualDriver(); - if (FAILED(result)) { - LOG(ERROR) << "Unable to unregister."; - return result; - } - base::FilePath old_install_path = GetInstallLocation(kUninstallId); - if (!old_install_path.value().empty() && - install_path != old_install_path) { - if (base::DirectoryExists(old_install_path)) - base::DeleteFile(old_install_path, true); - } - CreateUninstallKey(kUninstallId, LoadLocalString(IDS_DRIVER_NAME), - kUninstallSwitch); - result = RegisterVirtualDriver(install_path); - if (FAILED(result)) - return result; - SetGoogleUpdateKeys(kGoogleUpdateProductId, kNameValue); - return result; -} - -HRESULT ExecuteCommands() { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - - base::FilePath exe_path; - if (!PathService::Get(base::DIR_EXE, &exe_path) || - !base::DirectoryExists(exe_path)) { - return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); - } - - if (command_line.HasSwitch(kDelete)) { - return DoDelete(command_line.GetSwitchValuePath(kDelete)); - } else if (command_line.HasSwitch(kUninstallSwitch)) { - return DoUninstall(); - } else if (command_line.HasSwitch(kInstallSwitch)) { - return DoInstall(exe_path); - } else if (command_line.HasSwitch(kUnregisterSwitch)) { - return DoUnregister(); - } else if (command_line.HasSwitch(kRegisterSwitch)) { - return DoRegister(exe_path); - } - - return E_INVALIDARG; -} - -} // namespace - -} // namespace cloud_print - -int WINAPI WinMain(__in HINSTANCE hInstance, - __in HINSTANCE hPrevInstance, - __in LPSTR lpCmdLine, - __in int nCmdShow) { - using namespace cloud_print; - - base::AtExitManager at_exit_manager; - base::CommandLine::Init(0, NULL); - - HRESULT retval = ExecuteCommands(); - - if (retval == HRESULT_FROM_WIN32(ERROR_BAD_DRIVER)) { - SetGoogleUpdateError(kGoogleUpdateProductId, - LoadLocalString(IDS_ERROR_NO_XPS)); - } else if (FAILED(retval)) { - SetGoogleUpdateError(kGoogleUpdateProductId, retval); - } - - VLOG(0) << GetErrorMessage(retval) - << " HRESULT=0x" << std::setbase(16) << retval; - - // Installer is silent by default as required by Google Update. - if (base::CommandLine::ForCurrentProcess()->HasSwitch("verbose")) { - DisplayWindowsMessage(NULL, retval, LoadLocalString(IDS_DRIVER_NAME)); - } - return retval; -}
diff --git a/cloud_print/virtual_driver/win/install/virtual_driver_install.gyp b/cloud_print/virtual_driver/win/install/virtual_driver_install.gyp deleted file mode 100644 index a788f1a..0000000 --- a/cloud_print/virtual_driver/win/install/virtual_driver_install.gyp +++ /dev/null
@@ -1,124 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'target_defaults': { - 'variables': { - 'chromium_code': 1, - }, - 'include_dirs': [ - '<(DEPTH)', - ], - }, - 'targets' : [ - { - # GN version: //cloud_print/virtual_driver/win/install:virtual_driver_setup - 'target_name': 'virtual_driver_setup', - 'type': 'executable', - 'include_dirs': [ - # To allow including "version.h" - '<(SHARED_INTERMEDIATE_DIR)', - ], - 'dependencies': [ - '../virtual_driver.gyp:virtual_driver_lib', - '<(DEPTH)/base/base.gyp:base', - '<(DEPTH)/cloud_print/common/common.gyp:cloud_print_install_lib', - '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_resources', - 'virtual_driver_setup_resources', - ], - 'sources': [ - 'setup.cc', - '<(SHARED_INTERMEDIATE_DIR)/cloud_print/virtual_driver_setup_exe_version.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ar.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_bg.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_bn.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ca.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_cs.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_da.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_de.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_el.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_en.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_en-GB.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_es.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_es-419.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_et.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_fa.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_fi.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_fil.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_fr.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_gu.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_he.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_hi.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_hr.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_hu.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_id.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_it.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ja.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_kn.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ko.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_lt.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_lv.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ml.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_mr.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ms.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_nb.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_nl.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_pl.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_pt-BR.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_pt-PT.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ro.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ru.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_sk.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_sl.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_sr.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_sv.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_ta.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_te.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_th.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_tr.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_uk.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_vi.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_zh-CN.rc', - '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources/virtual_driver_setup_resources_zh-TW.rc', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS - 'AdditionalDependencies': [ - 'setupapi.lib', - ], - 'DelayLoadDLLs': [ - 'winspool.drv', - ], - }, - }, - 'copies': [ - { - 'destination': '<(PRODUCT_DIR)', - 'files': [ - '../../gcp_driver.gpd', - ], - }, - ], - }, - { - # GN version: //cloud_print/virtual_driver/win/install:resources - 'target_name': 'virtual_driver_setup_resources', - 'type': 'none', - 'variables': { - 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/virtual_driver_setup_resources', - }, - 'actions': [ - { - 'action_name': 'virtual_driver_setup_resources', - 'variables': { - 'grit_grd_file': 'virtual_driver_setup_resources.grd', - }, - 'includes': [ '../../../../build/grit_action.gypi' ], - }, - ], - 'includes': [ '../../../../build/grit_target.gypi' ], - }, - ], -}
diff --git a/cloud_print/virtual_driver/win/install/virtual_driver_setup_exe.ver b/cloud_print/virtual_driver/win/install/virtual_driver_setup_exe.ver deleted file mode 100644 index 1667c07..0000000 --- a/cloud_print/virtual_driver/win/install/virtual_driver_setup_exe.ver +++ /dev/null
@@ -1,3 +0,0 @@ -INTERNAL_NAME=virtual_driver_setup.exe -ORIGINAL_FILENAME=virtual_driver_setup.exe -FILETYPE=0x1L
diff --git a/cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd b/cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd deleted file mode 100644 index 86e06ced..0000000 --- a/cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd +++ /dev/null
@@ -1,143 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- -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. ---> - - -<grit latest_public_release="0" current_release="1"> - <outputs> - <output filename="grit/virtual_driver_setup_resources.h" type="rc_header"> - <emit emit_type='prepend'></emit> - </output> - <output filename="virtual_driver_setup_resources_ar.rc" type="rc_all" lang="ar" language_section="lang"/> - <output filename="virtual_driver_setup_resources_bg.rc" type="rc_all" lang="bg" language_section="lang"/> - <output filename="virtual_driver_setup_resources_bn.rc" type="rc_all" lang="bn" language_section="lang"/> - <output filename="virtual_driver_setup_resources_ca.rc" type="rc_all" lang="ca" language_section="lang"/> - <output filename="virtual_driver_setup_resources_cs.rc" type="rc_all" lang="cs" language_section="lang"/> - <output filename="virtual_driver_setup_resources_da.rc" type="rc_all" lang="da" language_section="lang"/> - <output filename="virtual_driver_setup_resources_de.rc" type="rc_all" lang="de" language_section="lang"/> - <output filename="virtual_driver_setup_resources_el.rc" type="rc_all" lang="el" language_section="lang"/> - <output filename="virtual_driver_setup_resources_en.rc" type="rc_all" lang="en" language_section="lang"/> - <output filename="virtual_driver_setup_resources_en-GB.rc" type="rc_all" lang="en-GB" language_section="lang"/> - <output filename="virtual_driver_setup_resources_es.rc" type="rc_all" lang="es" language_section="lang"/> - <output filename="virtual_driver_setup_resources_es-419.rc" type="rc_all" lang="es-419" language_section="lang"/> - <output filename="virtual_driver_setup_resources_et.rc" type="rc_all" lang="et" language_section="lang"/> - <output filename="virtual_driver_setup_resources_fa.rc" type="rc_all" lang="fa" language_section="lang"/> - <output filename="virtual_driver_setup_resources_fi.rc" type="rc_all" lang="fi" language_section="lang"/> - <output filename="virtual_driver_setup_resources_fil.rc" type="rc_all" lang="fil" language_section="lang"/> - <output filename="virtual_driver_setup_resources_fr.rc" type="rc_all" lang="fr" language_section="lang"/> - <output filename="virtual_driver_setup_resources_gu.rc" type="rc_all" lang="gu" language_section="lang"/> - <output filename="virtual_driver_setup_resources_he.rc" type="rc_all" lang="he" language_section="lang"/> - <output filename="virtual_driver_setup_resources_hi.rc" type="rc_all" lang="hi" language_section="lang"/> - <output filename="virtual_driver_setup_resources_hr.rc" type="rc_all" lang="hr" language_section="lang"/> - <output filename="virtual_driver_setup_resources_hu.rc" type="rc_all" lang="hu" language_section="lang"/> - <output filename="virtual_driver_setup_resources_id.rc" type="rc_all" lang="id" language_section="lang"/> - <output filename="virtual_driver_setup_resources_it.rc" type="rc_all" lang="it" language_section="lang"/> - <output filename="virtual_driver_setup_resources_ja.rc" type="rc_all" lang="ja" language_section="lang"/> - <output filename="virtual_driver_setup_resources_kn.rc" type="rc_all" lang="kn" language_section="lang"/> - <output filename="virtual_driver_setup_resources_ko.rc" type="rc_all" lang="ko" language_section="lang"/> - <output filename="virtual_driver_setup_resources_lt.rc" type="rc_all" lang="lt" language_section="lang"/> - <output filename="virtual_driver_setup_resources_lv.rc" type="rc_all" lang="lv" language_section="lang"/> - <output filename="virtual_driver_setup_resources_ml.rc" type="rc_all" lang="ml" language_section="lang"/> - <output filename="virtual_driver_setup_resources_mr.rc" type="rc_all" lang="mr" language_section="lang"/> - <output filename="virtual_driver_setup_resources_ms.rc" type="rc_all" lang="ms" language_section="lang"/> - <output filename="virtual_driver_setup_resources_nl.rc" type="rc_all" lang="nl" language_section="lang"/> - <!-- The translation console uses 'no' for Norwegian Bokmål. It should be 'nb'. --> - <output filename="virtual_driver_setup_resources_nb.rc" type="rc_all" lang="no" language_section="lang"/> - <output filename="virtual_driver_setup_resources_pl.rc" type="rc_all" lang="pl" language_section="lang"/> - <output filename="virtual_driver_setup_resources_pt-BR.rc" type="rc_all" lang="pt-BR" language_section="lang"/> - <output filename="virtual_driver_setup_resources_pt-PT.rc" type="rc_all" lang="pt-PT" language_section="lang"/> - <output filename="virtual_driver_setup_resources_ro.rc" type="rc_all" lang="ro" language_section="lang"/> - <output filename="virtual_driver_setup_resources_ru.rc" type="rc_all" lang="ru" language_section="lang"/> - <output filename="virtual_driver_setup_resources_sk.rc" type="rc_all" lang="sk" language_section="lang"/> - <output filename="virtual_driver_setup_resources_sl.rc" type="rc_all" lang="sl" language_section="lang"/> - <output filename="virtual_driver_setup_resources_sr.rc" type="rc_all" lang="sr" language_section="lang"/> - <output filename="virtual_driver_setup_resources_sv.rc" type="rc_all" lang="sv" language_section="lang"/> - <output filename="virtual_driver_setup_resources_sw.rc" type="rc_all" lang="sw" language_section="lang"/> - <output filename="virtual_driver_setup_resources_ta.rc" type="rc_all" lang="ta" language_section="lang"/> - <output filename="virtual_driver_setup_resources_te.rc" type="rc_all" lang="te" language_section="lang"/> - <output filename="virtual_driver_setup_resources_th.rc" type="rc_all" lang="th" language_section="lang"/> - <output filename="virtual_driver_setup_resources_tr.rc" type="rc_all" lang="tr" language_section="lang"/> - <output filename="virtual_driver_setup_resources_uk.rc" type="rc_all" lang="uk" language_section="lang"/> - <output filename="virtual_driver_setup_resources_vi.rc" type="rc_all" lang="vi" language_section="lang"/> - <output filename="virtual_driver_setup_resources_zh-CN.rc" type="rc_all" lang="zh-CN" language_section="lang"/> - <output filename="virtual_driver_setup_resources_zh-TW.rc" type="rc_all" lang="zh-TW" language_section="lang"/> - </outputs> - <translations> - <file path="resources/virtual_driver_setup_resources_ar.xtb" lang="ar"/> - <file path="resources/virtual_driver_setup_resources_bg.xtb" lang="bg"/> - <file path="resources/virtual_driver_setup_resources_bn.xtb" lang="bn"/> - <file path="resources/virtual_driver_setup_resources_ca.xtb" lang="ca"/> - <file path="resources/virtual_driver_setup_resources_cs.xtb" lang="cs"/> - <file path="resources/virtual_driver_setup_resources_da.xtb" lang="da"/> - <file path="resources/virtual_driver_setup_resources_de.xtb" lang="de"/> - <file path="resources/virtual_driver_setup_resources_el.xtb" lang="el"/> - <file path="resources/virtual_driver_setup_resources_en-GB.xtb" lang="en-GB"/> - <file path="resources/virtual_driver_setup_resources_es.xtb" lang="es"/> - <file path="resources/virtual_driver_setup_resources_es-419.xtb" lang="es-419"/> - <file path="resources/virtual_driver_setup_resources_et.xtb" lang="et"/> - <file path="resources/virtual_driver_setup_resources_fa.xtb" lang="fa"/> - <file path="resources/virtual_driver_setup_resources_fi.xtb" lang="fi"/> - <file path="resources/virtual_driver_setup_resources_fil.xtb" lang="fil" /> - <file path="resources/virtual_driver_setup_resources_fr.xtb" lang="fr" /> - <file path="resources/virtual_driver_setup_resources_gu.xtb" lang="gu" /> - <file path="resources/virtual_driver_setup_resources_he.xtb" lang="he" /> - <file path="resources/virtual_driver_setup_resources_hi.xtb" lang="hi" /> - <file path="resources/virtual_driver_setup_resources_hr.xtb" lang="hr" /> - <file path="resources/virtual_driver_setup_resources_hu.xtb" lang="hu" /> - <file path="resources/virtual_driver_setup_resources_id.xtb" lang="id" /> - <file path="resources/virtual_driver_setup_resources_it.xtb" lang="it" /> - <file path="resources/virtual_driver_setup_resources_ja.xtb" lang="ja" /> - <file path="resources/virtual_driver_setup_resources_kn.xtb" lang="kn" /> - <file path="resources/virtual_driver_setup_resources_ko.xtb" lang="ko" /> - <file path="resources/virtual_driver_setup_resources_lt.xtb" lang="lt" /> - <file path="resources/virtual_driver_setup_resources_lv.xtb" lang="lv" /> - <file path="resources/virtual_driver_setup_resources_ml.xtb" lang="ml" /> - <file path="resources/virtual_driver_setup_resources_mr.xtb" lang="mr" /> - <file path="resources/virtual_driver_setup_resources_ms.xtb" lang="ms" /> - <file path="resources/virtual_driver_setup_resources_nl.xtb" lang="nl" /> - <file path="resources/virtual_driver_setup_resources_no.xtb" lang="no" /> - <file path="resources/virtual_driver_setup_resources_pl.xtb" lang="pl" /> - <file path="resources/virtual_driver_setup_resources_pt-BR.xtb" lang="pt-BR" /> - <file path="resources/virtual_driver_setup_resources_pt-PT.xtb" lang="pt-PT" /> - <file path="resources/virtual_driver_setup_resources_ro.xtb" lang="ro" /> - <file path="resources/virtual_driver_setup_resources_ru.xtb" lang="ru" /> - <file path="resources/virtual_driver_setup_resources_sk.xtb" lang="sk" /> - <file path="resources/virtual_driver_setup_resources_sl.xtb" lang="sl" /> - <file path="resources/virtual_driver_setup_resources_sr.xtb" lang="sr" /> - <file path="resources/virtual_driver_setup_resources_sv.xtb" lang="sv" /> - <file path="resources/virtual_driver_setup_resources_sw.xtb" lang="sw" /> - <file path="resources/virtual_driver_setup_resources_ta.xtb" lang="ta" /> - <file path="resources/virtual_driver_setup_resources_te.xtb" lang="te" /> - <file path="resources/virtual_driver_setup_resources_th.xtb" lang="th" /> - <file path="resources/virtual_driver_setup_resources_tr.xtb" lang="tr" /> - <file path="resources/virtual_driver_setup_resources_uk.xtb" lang="uk" /> - <file path="resources/virtual_driver_setup_resources_vi.xtb" lang="vi" /> - <file path="resources/virtual_driver_setup_resources_zh-CN.xtb" lang="zh-CN" /> - <file path="resources/virtual_driver_setup_resources_zh-TW.xtb" lang="zh-TW" /> - </translations> - <release seq="1"> - <messages> - <message name="IDS_GOOGLE" - meaning="Google" desc="The name of the company that built this."> - Google - </message> - <message name="IDS_DRIVER_NAME" - meaning="Driver Name" desc="The user visible name of the printer we create and its associated driver."> - Google Cloud Printer - </message> - <message name="IDS_ERROR_NO_XPS" - meaning="Error message" desc="XPS driver is not installed."> - <ph name="xps_url"><a href="http://www.microsoft.com/download/details.aspx?id=11816"><ex><a href="http://www.microsoft.com/download/details.aspx?id=11816"></ex></ph>XPS driver<ph name="xps_url_end"></a><ex></a></ex></ph> is not installed. - </message> - </messages> - <includes> - <if expr="lang == 'en'"> - <include name="IDI_ICON" file="../../../service/win/resources/cloudprint.ico" type="ICON" translateable="false" /> - </if> - </includes> - </release> -</grit>
diff --git a/cloud_print/virtual_driver/win/port_monitor/BUILD.gn b/cloud_print/virtual_driver/win/port_monitor/BUILD.gn deleted file mode 100644 index 71072589..0000000 --- a/cloud_print/virtual_driver/win/port_monitor/BUILD.gn +++ /dev/null
@@ -1,85 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//chrome/version.gni") - -assert(is_win) - -# When cross-compiling a 64-bit driver for a 32-bit build, some things get name -# mangled with this suffix. -if (target_cpu == "x86" && current_cpu == "x64") { - arch_suffix = "64" -} else { - arch_suffix = "" -} - -# When compiling a 64-bit port monitor from a 32-bit build, copy the DLL to the -# root build directory. When targeting 64-bit CPUs, there is no cross-compiling -# so nothing extra needs to happen. -if (target_cpu == "x86" && current_cpu == "x64") { - copy("port_monitor") { - sources = [ - "$root_out_dir/gcp_portmon64.dll", - ] - outputs = [ - "$root_build_dir/gcp_portmon64.dll", - ] - deps = [ - ":port_monitor_dll", - ] - } -} else { - group("port_monitor") { - public_deps = [ - ":port_monitor_dll", - ] - } -} - -shared_library("port_monitor_dll") { - output_name = "gcp_portmon$arch_suffix" - - sources = [ - "port_monitor.def", - "port_monitor_dll.cc", - ] - - deps = [ - ":lib", - ":resources", - "//base", - "//chrome/common:constants", - "//chrome/common:version_header", - "//cloud_print/common", - "//cloud_print/virtual_driver/win", - ] - - libs = [ "userenv.lib" ] -} - -source_set("lib") { - sources = [ - "port_monitor.cc", - "port_monitor.h", - ] - - deps = [ - "//base", - "//chrome/common:constants", - "//chrome/installer/launcher_support", - "//cloud_print/common", - "//cloud_print/virtual_driver/win", - ] -} - -process_version("resources") { - template_file = chrome_version_rc_template - sources = [ - "../gcp_portmon${arch_suffix}_dll.ver", - ] - - # Note: target_gen_dir will be different for each toolchain so the output - # name doesn't need mangling. - output = "$target_gen_dir/gcp_portmon_dll.rc" -}
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc b/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc deleted file mode 100644 index e9cb31f3..0000000 --- a/cloud_print/virtual_driver/win/port_monitor/port_monitor.cc +++ /dev/null
@@ -1,720 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/virtual_driver/win/port_monitor/port_monitor.h" - -#include <windows.h> -#include <lmcons.h> -#include <shellapi.h> -#include <shlobj.h> -#include <stddef.h> -#include <stdint.h> -#include <strsafe.h> -#include <userenv.h> -#include <winspool.h> - -#include "base/at_exit.h" -#include "base/command_line.h" -#include "base/files/file_enumerator.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/process/launch.h" -#include "base/process/process.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" -#include "base/win/registry.h" -#include "base/win/scoped_handle.h" -#include "base/win/windows_version.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/installer/launcher_support/chrome_launcher_support.h" -#include "cloud_print/common/win/cloud_print_utils.h" -#include "cloud_print/virtual_driver/win/port_monitor/spooler_win.h" -#include "cloud_print/virtual_driver/win/virtual_driver_consts.h" -#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h" - -namespace cloud_print { - -namespace { - -const wchar_t kIePath[] = L"Internet Explorer\\iexplore.exe"; - -const char kChromeInstallUrl[] = - "http://google.com/cloudprint/learn/chrome.html"; - -const wchar_t kCloudPrintRegKey[] = L"Software\\Google\\CloudPrint"; - -const wchar_t kXpsMimeType[] = L"application/vnd.ms-xpsdocument"; - -const wchar_t kAppDataDir[] = L"Google\\Cloud Printer"; - -const wchar_t kDocumentPathPlaceHolder[] = L"%%Document_Path%%"; - -const wchar_t kDocumentTypePlaceHolder[] = L"%%Document_Type%%"; - -const wchar_t kJobTitlePlaceHolder[] = L"%%Job_Title%%"; - -struct MonitorData { - scoped_ptr<base::AtExitManager> at_exit_manager; -}; - -struct PortData { - PortData() : job_id(0), printer_handle(NULL), file(0) { - } - ~PortData() { - Close(); - } - void Close() { - if (printer_handle) { - ClosePrinter(printer_handle); - printer_handle = NULL; - } - if (file) { - base::CloseFile(file); - file = NULL; - } - } - DWORD job_id; - HANDLE printer_handle; - FILE* file; - base::FilePath file_path; -}; - -typedef struct { - ACCESS_MASK granted_access; -} XcvUiData; - - -MONITORUI g_monitor_ui = { - sizeof(MONITORUI), - MonitorUiAddPortUi, - MonitorUiConfigureOrDeletePortUI, - MonitorUiConfigureOrDeletePortUI -}; - -MONITOR2 g_monitor_2 = { - sizeof(MONITOR2), - Monitor2EnumPorts, - Monitor2OpenPort, - NULL, // OpenPortEx is not supported. - Monitor2StartDocPort, - Monitor2WritePort, - Monitor2ReadPort, - Monitor2EndDocPort, - Monitor2ClosePort, - NULL, // AddPort is not supported. - NULL, // AddPortEx is not supported. - NULL, // ConfigurePort is not supported. - NULL, // DeletePort is not supported. - NULL, - NULL, // SetPortTimeOuts is not supported. - Monitor2XcvOpenPort, - Monitor2XcvDataPort, - Monitor2XcvClosePort, - Monitor2Shutdown -}; - -base::FilePath GetLocalAppDataLow() { - wchar_t system_buffer[MAX_PATH]; - if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, - system_buffer))) - return base::FilePath(); - return base::FilePath(system_buffer).DirName().AppendASCII("LocalLow"); -} - -base::FilePath GetAppDataDir() { - base::FilePath file_path; - if (base::win::GetVersion() >= base::win::VERSION_VISTA) - file_path = GetLocalAppDataLow(); - else - PathService::Get(base::DIR_LOCAL_APP_DATA, &file_path); - if (file_path.empty()) { - LOG(ERROR) << "Can't get app data dir"; - } - return file_path.Append(kAppDataDir); -} - -// Delete files which where not deleted by chrome. -void DeleteLeakedFiles(const base::FilePath& dir) { - base::Time delete_before = base::Time::Now() - base::TimeDelta::FromDays(1); - base::FileEnumerator enumerator(dir, false, base::FileEnumerator::FILES); - for (base::FilePath file_path = enumerator.Next(); !file_path.empty(); - file_path = enumerator.Next()) { - if (enumerator.GetInfo().GetLastModifiedTime() < delete_before) - base::DeleteFile(file_path, false); - } -} - -// Attempts to retrieve the title of the specified print job. -// On success returns TRUE and the first title_chars characters of the job title -// are copied into title. -// On failure returns FALSE and title is unmodified. -bool GetJobTitle(HANDLE printer_handle, - DWORD job_id, - base::string16 *title) { - DCHECK(printer_handle != NULL); - DCHECK(title != NULL); - DWORD bytes_needed = 0; - GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed); - if (bytes_needed == 0) { - LOG(ERROR) << "Unable to get bytes needed for job info."; - return false; - } - scoped_ptr<BYTE[]> buffer(new BYTE[bytes_needed]); - if (!GetJob(printer_handle, - job_id, - 1, - buffer.get(), - bytes_needed, - &bytes_needed)) { - LOG(ERROR) << "Unable to get job info."; - return false; - } - JOB_INFO_1* job_info = reinterpret_cast<JOB_INFO_1*>(buffer.get()); - *title = job_info->pDocument; - return true; -} - -// Handler for the UI functions exported by the port monitor. -// Verifies that a valid parent Window exists and then just displays an -// error message to let the user know that there is no interactive -// configuration. -void HandlePortUi(HWND hwnd, const base::string16& caption) { - if (hwnd != NULL && IsWindow(hwnd)) { - DisplayWindowsMessage(hwnd, CO_E_NOT_SUPPORTED, cloud_print::kPortName); - } -} - -// Gets the primary token for the user that submitted the print job. -bool GetUserToken(HANDLE* primary_token) { - HANDLE token = NULL; - if (!OpenThreadToken(GetCurrentThread(), - TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY, - FALSE, - &token)) { - LOG(ERROR) << "Unable to get thread token."; - return false; - } - base::win::ScopedHandle token_scoped(token); - if (!DuplicateTokenEx(token, - TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY, - NULL, - SecurityImpersonation, - TokenPrimary, - primary_token)) { - LOG(ERROR) << "Unable to get primary thread token."; - return false; - } - return true; -} - -bool LaunchCommandAsUser(const base::CommandLine& command) { - HANDLE token = NULL; - if (!GetUserToken(&token)) { - LOG(ERROR) << "Unable to get user token."; - return false; - } - base::win::ScopedHandle primary_token_scoped(token); - base::LaunchOptions options; - options.as_user = primary_token_scoped.Get(); - base::LaunchProcess(command, options); - return true; -} - -// Escape the command line argument as necessary per Microsoft rules. -// See QuoteForCommandLineToArgvW in base/command_line.cc -base::string16 EscapeCommandLineArg(const base::string16& arg) { - base::string16 quotable_chars(L" \\\""); - if (arg.find_first_of(quotable_chars) == base::string16::npos) { - // No quoting necessary. - return arg; - } - - base::string16 out; - out.push_back(L'"'); - for (size_t i = 0; i < arg.size(); ++i) { - if (arg[i] == '\\') { - // Find the extent of this run of backslashes. - size_t start = i, end = start + 1; - for (; end < arg.size() && arg[end] == '\\'; ++end) {} - size_t backslash_count = end - start; - - // Backslashes are escapes only if the run is followed by a double quote. - // Since we also will end the string with a double quote, we escape for - // either a double quote or the end of the string. - if (end == arg.size() || arg[end] == '"') { - // To quote, we need to output 2x as many backslashes. - backslash_count *= 2; - } - for (size_t j = 0; j < backslash_count; ++j) - out.push_back('\\'); - - // Advance i to one before the end to balance i++ in loop. - i = end - 1; - } else if (arg[i] == '"') { - out.push_back('\\'); - out.push_back('"'); - } else { - out.push_back(arg[i]); - } - } - out.push_back('"'); - - return out; -} - -// Launch the print command as specified in the cloud print registry. -bool LaunchPrintCommandFromTemplate(const base::string16& command_template, - const base::FilePath& xps_path, - const base::string16& job_title) { - base::string16 command_string(command_template); - // Substitude the place holder with the document path wrapped in quotes. - base::ReplaceFirstSubstringAfterOffset( - &command_string, 0, kDocumentPathPlaceHolder, - EscapeCommandLineArg(xps_path.value())); - // Substitude the place holder with the document type wrapped in quotes. - base::ReplaceFirstSubstringAfterOffset( - &command_string, 0, kDocumentTypePlaceHolder, kXpsMimeType); - // Substitude the place holder with the job title wrapped in quotes. - base::ReplaceFirstSubstringAfterOffset( - &command_string, 0, kJobTitlePlaceHolder, - EscapeCommandLineArg(job_title)); - - base::CommandLine command = base::CommandLine::FromString(command_string); - - return LaunchCommandAsUser(command); -} - -// Launches a page to allow the user to download chrome. -// TODO(abodenha@chromium.org) Point to a custom page explaining what's wrong -// rather than the generic chrome download page. See -// http://code.google.com/p/chromium/issues/detail?id=112019 -void LaunchChromeDownloadPage() { - if (kIsUnittest) - return; - HANDLE token = NULL; - if (!GetUserToken(&token)) { - LOG(ERROR) << "Unable to get user token."; - return; - } - base::win::ScopedHandle token_scoped(token); - - base::FilePath ie_path; - PathService::Get(base::DIR_PROGRAM_FILESX86, &ie_path); - ie_path = ie_path.Append(kIePath); - base::CommandLine command_line(ie_path); - command_line.AppendArg(kChromeInstallUrl); - - base::LaunchOptions options; - options.as_user = token_scoped.Get(); - base::LaunchProcess(command_line, options); -} - -// Returns false if the print job is being run in a context -// that shouldn't be launching Chrome. -bool ValidateCurrentUser() { - HANDLE token = NULL; - if (!GetUserToken(&token)) { - // If we can't get the token we're probably not impersonating - // the user, so validation should fail. - return false; - } - base::win::ScopedHandle token_scoped(token); - - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - DWORD session_id = 0; - DWORD dummy; - if (!GetTokenInformation(token_scoped.Get(), - TokenSessionId, - reinterpret_cast<void *>(&session_id), - sizeof(DWORD), - &dummy)) { - return false; - } - if (session_id == 0) { - return false; - } - } - return true; -} -} // namespace - -base::string16 ReadStringFromRegistry(HKEY root, const wchar_t* path_name) { - base::win::RegKey gcp_key(root, kCloudPrintRegKey, KEY_READ); - base::string16 data; - gcp_key.ReadValue(path_name, &data); - return data; -} - -base::string16 ReadStringFromAnyRegistry(const wchar_t* path_name) { - base::string16 result = ReadStringFromRegistry(HKEY_CURRENT_USER, path_name); - if (!result.empty()) - return result; - return ReadStringFromRegistry(HKEY_LOCAL_MACHINE, path_name); -} - -base::FilePath GetChromeExePath() { - base::string16 value = ReadStringFromAnyRegistry(kChromeExePathRegValue); - if (!value.empty() && base::PathExists(base::FilePath(value))) - return base::FilePath(value); - return chrome_launcher_support::GetAnyChromePath(false /* is_sxs */); -} - -base::FilePath GetChromeProfilePath() { - base::string16 value = ReadStringFromAnyRegistry(kChromeProfilePathRegValue); - if (!value.empty() && base::DirectoryExists(base::FilePath(value))) - return base::FilePath(value); - return base::FilePath(); -} - -// Launches the Cloud Print dialog in Chrome. -bool LaunchChromePrintDialog(const base::FilePath& xps_path, - const base::string16& job_title) { - base::FilePath chrome_path = GetChromeExePath(); - if (chrome_path.empty()) { - LOG(ERROR) << "Unable to get chrome exe path."; - LaunchChromeDownloadPage(); - return false; - } - - base::CommandLine command_line(chrome_path); - - base::FilePath chrome_profile = GetChromeProfilePath(); - if (!chrome_profile.empty()) - command_line.AppendSwitchPath(switches::kUserDataDir, chrome_profile); - - command_line.AppendSwitchPath(switches::kCloudPrintFile, xps_path); - command_line.AppendSwitchNative(switches::kCloudPrintFileType, kXpsMimeType); - command_line.AppendSwitchNative(switches::kCloudPrintJobTitle, job_title); - - return LaunchCommandAsUser(command_line); -} - -base::string16 GetPrintCommandTemplate() { - return ReadStringFromAnyRegistry(kPrintCommandRegValue); -} - -// Launches the print command. This will either launch Chrome to display the -// Cloud Print dialog or another exe as specified in the cloud print registry. -// xps_path references a file to print. -// job_title is the title to be used for the resulting print job. -bool LaunchPrintCommand(const base::FilePath& xps_path, - const base::string16& job_title) { - base::string16 command_template = GetPrintCommandTemplate(); - if (!command_template.empty()) { - return LaunchPrintCommandFromTemplate( - command_template, xps_path, job_title); - } else { - return LaunchChromePrintDialog(xps_path, job_title); - } -} - -BOOL WINAPI Monitor2EnumPorts(HANDLE, - wchar_t*, - DWORD level, - BYTE* ports, - DWORD ports_size, - DWORD* needed_bytes, - DWORD* returned) { - if (needed_bytes == NULL) { - LOG(ERROR) << "needed_bytes should not be NULL."; - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - if (level == 1) { - *needed_bytes = sizeof(PORT_INFO_1); - } else if (level == 2) { - *needed_bytes = sizeof(PORT_INFO_2); - } else { - LOG(ERROR) << "Level " << level << "is not supported."; - SetLastError(ERROR_INVALID_LEVEL); - return FALSE; - } - *needed_bytes += static_cast<DWORD>(cloud_print::kPortNameSize); - if (ports_size < *needed_bytes) { - LOG(WARNING) << *needed_bytes << " bytes are required. Only " - << ports_size << " were allocated."; - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return FALSE; - } - if (ports == NULL) { - LOG(ERROR) << "ports should not be NULL."; - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - if (returned == NULL) { - LOG(ERROR) << "returned should not be NULL."; - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - // Windows expects any strings refernced by PORT_INFO_X structures to - // appear at the END of the buffer referenced by ports. Placing - // strings immediately after the PORT_INFO_X structure will cause - // EnumPorts to fail until the spooler is restarted. - // This is NOT mentioned in the documentation. - wchar_t* string_target = - reinterpret_cast<wchar_t*>(ports + ports_size - - cloud_print::kPortNameSize); - if (level == 1) { - PORT_INFO_1* port_info = reinterpret_cast<PORT_INFO_1*>(ports); - port_info->pName = string_target; - StringCbCopy(port_info->pName, - cloud_print::kPortNameSize, - cloud_print::kPortName); - } else { - PORT_INFO_2* port_info = reinterpret_cast<PORT_INFO_2*>(ports); - port_info->pPortName = string_target; - StringCbCopy(port_info->pPortName, - cloud_print::kPortNameSize, - cloud_print::kPortName); - port_info->pMonitorName = NULL; - port_info->pDescription = NULL; - port_info->fPortType = PORT_TYPE_WRITE; - port_info->Reserved = 0; - } - *returned = 1; - return TRUE; -} - -BOOL WINAPI Monitor2OpenPort(HANDLE, wchar_t*, HANDLE* handle) { - if (handle == NULL) { - LOG(ERROR) << "handle should not be NULL."; - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - *handle = new PortData(); - return TRUE; -} - -BOOL WINAPI Monitor2StartDocPort(HANDLE port_handle, - wchar_t* printer_name, - DWORD job_id, - DWORD, - BYTE*) { - SetGoogleUpdateUsage(kGoogleUpdateProductId); - if (port_handle == NULL) { - LOG(ERROR) << "port_handle should not be NULL."; - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - if (printer_name == NULL) { - LOG(ERROR) << "printer_name should not be NULL."; - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - if (!ValidateCurrentUser()) { - // TODO(abodenha@chromium.org) Abort the print job. - return FALSE; - } - PortData* port_data = reinterpret_cast<PortData*>(port_handle); - port_data->job_id = job_id; - if (!OpenPrinter(printer_name, &(port_data->printer_handle), NULL)) { - LOG(WARNING) << "Unable to open printer " << printer_name << "."; - // We can continue without a handle to the printer. - // It just means we can't get the job title or tell the spooler that - // the print job is complete. - // This is the normal flow during a unit test. - port_data->printer_handle = NULL; - } - base::FilePath& file_path = port_data->file_path; - base::FilePath app_data_dir = GetAppDataDir(); - if (app_data_dir.empty()) - return FALSE; - DeleteLeakedFiles(app_data_dir); - if (!base::CreateDirectory(app_data_dir) || - !base::CreateTemporaryFileInDir(app_data_dir, &file_path)) { - LOG(ERROR) << "Can't create temporary file in " << app_data_dir.value(); - return FALSE; - } - port_data->file = base::OpenFile(file_path, "wb+"); - if (port_data->file == NULL) { - LOG(ERROR) << "Error opening file " << file_path.value() << "."; - return FALSE; - } - return TRUE; -} - -BOOL WINAPI Monitor2WritePort(HANDLE port_handle, - BYTE* buffer, - DWORD buffer_size, - DWORD* bytes_written) { - PortData* port_data = reinterpret_cast<PortData*>(port_handle); - if (!ValidateCurrentUser()) { - // TODO(abodenha@chromium.org) Abort the print job. - return FALSE; - } - *bytes_written = - static_cast<DWORD>(fwrite(buffer, 1, buffer_size, port_data->file)); - if (*bytes_written > 0) { - return TRUE; - } else { - return FALSE; - } -} - -BOOL WINAPI Monitor2ReadPort(HANDLE, BYTE*, DWORD, DWORD* read_bytes) { - LOG(ERROR) << "Read is not supported."; - *read_bytes = 0; - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - -BOOL WINAPI Monitor2EndDocPort(HANDLE port_handle) { - if (!ValidateCurrentUser()) { - // TODO(abodenha@chromium.org) Abort the print job. - return FALSE; - } - PortData* port_data = reinterpret_cast<PortData*>(port_handle); - if (port_data == NULL) { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - if (port_data->file != NULL) { - base::CloseFile(port_data->file); - port_data->file = NULL; - bool delete_file = true; - int64_t file_size = 0; - base::GetFileSize(port_data->file_path, &file_size); - if (file_size > 0) { - base::string16 job_title; - if (port_data->printer_handle != NULL) { - GetJobTitle(port_data->printer_handle, - port_data->job_id, - &job_title); - } - if (LaunchPrintCommand(port_data->file_path, job_title)) { - delete_file = false; - } - } - if (delete_file) - base::DeleteFile(port_data->file_path, false); - } - if (port_data->printer_handle != NULL) { - // Tell the spooler that the job is complete. - SetJob(port_data->printer_handle, - port_data->job_id, - 0, - NULL, - JOB_CONTROL_SENT_TO_PRINTER); - } - port_data->Close(); - // Return success even if we can't display the dialog. - // TODO(abodenha@chromium.org) Come up with a better way of handling - // this situation. - return TRUE; -} - -BOOL WINAPI Monitor2ClosePort(HANDLE port_handle) { - if (port_handle == NULL) { - LOG(ERROR) << "port_handle should not be NULL."; - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - delete reinterpret_cast<PortData*>(port_handle); - return TRUE; -} - -VOID WINAPI Monitor2Shutdown(HANDLE monitor_handle) { - if (monitor_handle != NULL) { - delete reinterpret_cast<MonitorData*>(monitor_handle); - } -} - -BOOL WINAPI Monitor2XcvOpenPort(HANDLE, - const wchar_t*, - ACCESS_MASK granted_access, - HANDLE* handle) { - if (handle == NULL) { - LOG(ERROR) << "handle should not be NULL."; - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - XcvUiData* xcv_data = new XcvUiData(); - xcv_data->granted_access = granted_access; - *handle = xcv_data; - return TRUE; -} - -DWORD WINAPI Monitor2XcvDataPort(HANDLE xcv_handle, - const wchar_t* data_name, - BYTE*, - DWORD, - BYTE* output_data, - DWORD output_data_bytes, - DWORD* output_data_bytes_needed) { - XcvUiData* xcv_data = reinterpret_cast<XcvUiData*>(xcv_handle); - DWORD ret_val = ERROR_SUCCESS; - if ((xcv_data->granted_access & SERVER_ACCESS_ADMINISTER) == 0) { - return ERROR_ACCESS_DENIED; - } - if (output_data == NULL || output_data_bytes == 0) { - return ERROR_INVALID_PARAMETER; - } - // We don't handle AddPort or DeletePort since we don't support - // dynamic creation of ports. - if (lstrcmp(L"MonitorUI", data_name) == 0) { - DWORD dll_path_len = 0; - base::FilePath dll_path(GetPortMonitorDllName()); - dll_path_len = static_cast<DWORD>(dll_path.value().length()); - if (output_data_bytes_needed != NULL) { - *output_data_bytes_needed = dll_path_len; - } - if (output_data_bytes < dll_path_len) { - return ERROR_INSUFFICIENT_BUFFER; - } else { - ret_val = StringCbCopy(reinterpret_cast<wchar_t*>(output_data), - output_data_bytes, - dll_path.value().c_str()); - } - } else { - return ERROR_INVALID_PARAMETER; - } - return ret_val; -} - -BOOL WINAPI Monitor2XcvClosePort(HANDLE handle) { - delete reinterpret_cast<XcvUiData*>(handle); - return TRUE; -} - -BOOL WINAPI MonitorUiAddPortUi(const wchar_t*, - HWND hwnd, - const wchar_t* monitor_name, - wchar_t**) { - HandlePortUi(hwnd, monitor_name); - return TRUE; -} - -BOOL WINAPI MonitorUiConfigureOrDeletePortUI(const wchar_t*, - HWND hwnd, - const wchar_t* port_name) { - HandlePortUi(hwnd, port_name); - return TRUE; -} - -} // namespace cloud_print - -MONITOR2* WINAPI InitializePrintMonitor2(MONITORINIT*, - HANDLE* handle) { - if (handle == NULL) { - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } - cloud_print::MonitorData* monitor_data = new cloud_print::MonitorData; - *handle = monitor_data; - if (!cloud_print::kIsUnittest) { - // Unit tests set up their own AtExitManager - monitor_data->at_exit_manager.reset(new base::AtExitManager()); - // Single spooler.exe handles verbose users. - PathService::DisableCache(); - } - return &cloud_print::g_monitor_2; -} - -MONITORUI* WINAPI InitializePrintMonitorUI(void) { - return &cloud_print::g_monitor_ui; -} -
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor.def b/cloud_print/virtual_driver/win/port_monitor/port_monitor.def deleted file mode 100644 index 3b2cd17..0000000 --- a/cloud_print/virtual_driver/win/port_monitor/port_monitor.def +++ /dev/null
@@ -1,9 +0,0 @@ -; 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. - -EXPORTS - InitializePrintMonitor2 - InitializePrintMonitorUI - DllRegisterServer PRIVATE - DllUnregisterServer PRIVATE
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor.h b/cloud_print/virtual_driver/win/port_monitor/port_monitor.h deleted file mode 100644 index 29b8432..0000000 --- a/cloud_print/virtual_driver/win/port_monitor/port_monitor.h +++ /dev/null
@@ -1,95 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_PORT_MONITOR_H_ -#define CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_PORT_MONITOR_H_ - -#include <windows.h> -#include <string> -#include "base/files/file_util.h" -#include "base/process/process.h" -#include "base/strings/string16.h" - -namespace cloud_print { - -// Returns path to be used for launching Chrome. -base::FilePath GetChromeExePath(); - -// Returns path to user profile to be used for launching Chrome. -base::FilePath GetChromeProfilePath(); - -// Returns the print command to launch, if set, instead of Chrome. -base::string16 GetPrintCommandTemplate(); - -// Implementations for the function pointers in the MONITOR2 structure -// returned by InitializePrintMonitor2. The prototypes and behaviors -// are as described in the MONITOR2 documentation from Microsoft. - -BOOL WINAPI Monitor2EnumPorts(HANDLE, - wchar_t*, - DWORD level, - BYTE* ports, - DWORD ports_size, - DWORD* needed_bytes, - DWORD* returned); - -BOOL WINAPI Monitor2OpenPort(HANDLE monitor_data, wchar_t*, HANDLE* handle); - -BOOL WINAPI Monitor2StartDocPort(HANDLE port_handle, - wchar_t* printer_name, - DWORD job_id, - DWORD, - BYTE*); - -BOOL WINAPI Monitor2WritePort(HANDLE port, - BYTE* buffer, - DWORD buffer_size, - DWORD* bytes_written); - -BOOL WINAPI Monitor2ReadPort(HANDLE, BYTE*, DWORD, DWORD* bytes_read); - -BOOL WINAPI Monitor2EndDocPort(HANDLE port_handle); - -BOOL WINAPI Monitor2ClosePort(HANDLE port_handle); - -VOID WINAPI Monitor2Shutdown(HANDLE monitor_handle); - -BOOL WINAPI Monitor2XcvOpenPort(HANDLE monitor, - const wchar_t*, - ACCESS_MASK granted_access, - HANDLE* handle); - -DWORD WINAPI Monitor2XcvDataPort(HANDLE xcv_handle, - const wchar_t* data_name, - BYTE*, - DWORD, - BYTE* output_data, - DWORD output_data_bytes, - DWORD* output_data_bytes_needed); - -BOOL WINAPI Monitor2XcvClosePort(HANDLE handle); - -// Implementations for the function pointers in the MONITORUI structure -// returned by InitializePrintMonitorUI. The prototypes and behaviors -// are as described in the MONITORUI documentation from Microsoft. - -BOOL WINAPI MonitorUiAddPortUi(const wchar_t*, - HWND hwnd, - const wchar_t* monitor_name, - wchar_t**); - -BOOL WINAPI MonitorUiConfigureOrDeletePortUI(const wchar_t*, - HWND hwnd, - const wchar_t* port_name); - -extern const wchar_t kChromeExePath[]; -extern const wchar_t kChromeExePathRegValue[]; -extern const wchar_t kChromeProfilePathRegValue[]; -extern const wchar_t kPrintCommandRegValue[]; -extern const bool kIsUnittest; - -} // namespace cloud_print - -#endif // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_PORT_MONITOR_H_ -
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor_dll.cc b/cloud_print/virtual_driver/win/port_monitor/port_monitor_dll.cc deleted file mode 100644 index ca6b97d..0000000 --- a/cloud_print/virtual_driver/win/port_monitor/port_monitor_dll.cc +++ /dev/null
@@ -1,99 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/virtual_driver/win/port_monitor/port_monitor.h" - -#include <windows.h> -#include <lmcons.h> -#include <shellapi.h> -#include <shlobj.h> -#include <strsafe.h> -#include <userenv.h> -#include <winspool.h> - -#include "base/at_exit.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/process/process_info.h" -#include "base/strings/string16.h" -#include "base/win/registry.h" -#include "base/win/scoped_handle.h" -#include "base/win/windows_version.h" -#include "chrome/common/chrome_switches.h" -#include "cloud_print/common/win/cloud_print_utils.h" -#include "cloud_print/virtual_driver/win/port_monitor/spooler_win.h" -#include "cloud_print/virtual_driver/win/virtual_driver_consts.h" -#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h" - -namespace cloud_print { - -const wchar_t kChromeExePath[] = L"google\\chrome\\application\\chrome.exe"; -const wchar_t kChromeExePathRegValue[] = L"PathToChromeExe"; -const wchar_t kChromeProfilePathRegValue[] = L"PathToChromeProfile"; -const wchar_t kPrintCommandRegValue[] = L"PrintCommand"; -const bool kIsUnittest = false; - -namespace { - -// Returns true if Xps support is installed. -bool XpsIsInstalled() { - base::FilePath xps_path; - if (!SUCCEEDED(GetPrinterDriverDir(&xps_path))) { - return false; - } - xps_path = xps_path.Append(L"mxdwdrv.dll"); - if (!base::PathExists(xps_path)) { - return false; - } - return true; -} - -// Returns true if registration/unregistration can be attempted. -bool CanRegister() { - if (!XpsIsInstalled()) { - return false; - } - if (base::win::GetVersion() >= base::win::VERSION_VISTA) { - if (base::GetCurrentProcessIntegrityLevel() != base::HIGH_INTEGRITY) - return false; - } - return true; -} - -} // namespace - - -} // namespace cloud_print - -HRESULT WINAPI DllRegisterServer(void) { - base::AtExitManager at_exit_manager; - if (!cloud_print::CanRegister()) { - return E_ACCESSDENIED; - } - MONITOR_INFO_2 monitor_info = {0}; - // YUCK!!! I can either copy the constant, const_cast, or define my own - // MONITOR_INFO_2 that will take const strings. - base::FilePath dll_path(cloud_print::GetPortMonitorDllName()); - monitor_info.pDLLName = const_cast<LPWSTR>(dll_path.value().c_str()); - monitor_info.pName = const_cast<LPWSTR>(dll_path.value().c_str()); - if (AddMonitor(NULL, 2, reinterpret_cast<BYTE*>(&monitor_info))) { - return S_OK; - } - return cloud_print::GetLastHResult(); -} - -HRESULT WINAPI DllUnregisterServer(void) { - base::AtExitManager at_exit_manager; - if (!cloud_print::CanRegister()) { - return E_ACCESSDENIED; - } - base::FilePath dll_path(cloud_print::GetPortMonitorDllName()); - if (DeleteMonitor(NULL, - NULL, - const_cast<LPWSTR>(dll_path.value().c_str()))) { - return S_OK; - } - return cloud_print::GetLastHResult(); -}
diff --git a/cloud_print/virtual_driver/win/port_monitor/port_monitor_unittest.cc b/cloud_print/virtual_driver/win/port_monitor/port_monitor_unittest.cc deleted file mode 100644 index ef60468..0000000 --- a/cloud_print/virtual_driver/win/port_monitor/port_monitor_unittest.cc +++ /dev/null
@@ -1,282 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/virtual_driver/win/port_monitor/port_monitor.h" - -#include <stddef.h> -#include <winspool.h> - -#include "base/files/file_util.h" -#include "base/macros.h" -#include "base/path_service.h" -#include "base/strings/string16.h" -#include "base/win/registry.h" -#include "base/win/scoped_handle.h" -#include "cloud_print/virtual_driver/win/port_monitor/spooler_win.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cloud_print { - -const wchar_t kChromeExePath[] = L"google\\chrome\\application\\chrometest.exe"; -const wchar_t kChromeExePathRegValue[] = L"PathToChromeTestExe"; -const wchar_t kChromeProfilePathRegValue[] = L"PathToChromeTestProfile"; -const wchar_t kPrintCommandRegValue[] = L"TestPrintCommand"; -const bool kIsUnittest = true; - -namespace { - -const wchar_t kAlternateChromeExePath[] = - L"google\\chrome\\application\\chrometestalternate.exe"; -const wchar_t kTestPrintCommand[] = L"testprintcommand.exe"; - -const wchar_t kCloudPrintRegKey[] = L"Software\\Google\\CloudPrint"; - -} // namespace - -class PortMonitorTest : public testing::Test { - public: - PortMonitorTest() {} - protected: - // Creates a registry entry pointing at a chrome - virtual void SetUpChromeExeRegistry() { - // Create a temporary chrome.exe location value. - base::win::RegKey key(HKEY_CURRENT_USER, - cloud_print::kCloudPrintRegKey, - KEY_ALL_ACCESS); - - base::FilePath path; - PathService::Get(base::DIR_LOCAL_APP_DATA, &path); - path = path.Append(kAlternateChromeExePath); - ASSERT_EQ(ERROR_SUCCESS, - key.WriteValue(cloud_print::kChromeExePathRegValue, - path.value().c_str())); - base::FilePath temp; - PathService::Get(base::DIR_TEMP, &temp); - // Write any dir here. - ASSERT_EQ(ERROR_SUCCESS, - key.WriteValue(cloud_print::kChromeProfilePathRegValue, - temp.value().c_str())); - - ASSERT_EQ(ERROR_SUCCESS, - key.WriteValue(cloud_print::kPrintCommandRegValue, - kTestPrintCommand)); - } - // Deletes the registry entry created in SetUpChromeExeRegistry - virtual void DeleteChromeExeRegistry() { - base::win::RegKey key(HKEY_CURRENT_USER, - cloud_print::kCloudPrintRegKey, - KEY_ALL_ACCESS); - key.DeleteValue(cloud_print::kChromeExePathRegValue); - key.DeleteValue(cloud_print::kChromeProfilePathRegValue); - key.DeleteValue(cloud_print::kPrintCommandRegValue); - } - - virtual void CreateTempChromeExeFiles() { - base::FilePath path; - PathService::Get(base::DIR_LOCAL_APP_DATA, &path); - base::FilePath main_path = path.Append(kChromeExePath); - ASSERT_TRUE(base::CreateDirectory(main_path)); - base::FilePath alternate_path = path.Append(kAlternateChromeExePath); - ASSERT_TRUE(base::CreateDirectory(alternate_path)); - } - - virtual void DeleteTempChromeExeFiles() { - base::FilePath path; - PathService::Get(base::DIR_LOCAL_APP_DATA, &path); - base::FilePath main_path = path.Append(kChromeExePath); - ASSERT_TRUE(base::DeleteFile(main_path, true)); - PathService::Get(base::DIR_LOCAL_APP_DATA, &path); - base::FilePath alternate_path = path.Append(kAlternateChromeExePath); - ASSERT_TRUE(base::DeleteFile(alternate_path, true)); - } - - protected: - void SetUp() override { SetUpChromeExeRegistry(); } - - void TearDown() override { DeleteChromeExeRegistry(); } - - private: - DISALLOW_COPY_AND_ASSIGN(PortMonitorTest); -}; - -TEST_F(PortMonitorTest, GetChromeExePathTest) { - CreateTempChromeExeFiles(); - base::FilePath chrome_path = cloud_print::GetChromeExePath(); - EXPECT_FALSE(chrome_path.empty()); - EXPECT_TRUE( - chrome_path.value().rfind(kAlternateChromeExePath) != std::string::npos); - EXPECT_TRUE(base::PathExists(chrome_path)); - DeleteChromeExeRegistry(); - chrome_path = cloud_print::GetChromeExePath(); - // No Chrome or regular chrome path. - EXPECT_TRUE(chrome_path.empty() || - chrome_path.value().rfind(kChromeExePath) == std::string::npos); -} - -TEST_F(PortMonitorTest, GetPrintCommandTemplateTest) { - base::string16 print_command = cloud_print::GetPrintCommandTemplate(); - EXPECT_FALSE(print_command .empty()); - EXPECT_EQ(print_command, kTestPrintCommand); - DeleteChromeExeRegistry(); - print_command = cloud_print::GetPrintCommandTemplate(); - EXPECT_TRUE(print_command.empty()); -} - -TEST_F(PortMonitorTest, GetChromeProfilePathTest) { - base::FilePath data_path = cloud_print::GetChromeProfilePath(); - EXPECT_FALSE(data_path.empty()); - base::FilePath temp; - PathService::Get(base::DIR_TEMP, &temp); - EXPECT_EQ(data_path, temp); - EXPECT_TRUE(base::DirectoryExists(data_path)); - DeleteChromeExeRegistry(); - data_path = cloud_print::GetChromeProfilePath(); - EXPECT_TRUE(data_path.empty()); -} - -TEST_F(PortMonitorTest, EnumPortsTest) { - DWORD needed_bytes = 0; - DWORD returned = 0; - EXPECT_FALSE(Monitor2EnumPorts(NULL, - NULL, - 1, - NULL, - 0, - &needed_bytes, - &returned)); - EXPECT_EQ(static_cast<DWORD>(ERROR_INSUFFICIENT_BUFFER), GetLastError()); - EXPECT_NE(0u, needed_bytes); - EXPECT_EQ(0u, returned); - - BYTE* buffer = new BYTE[needed_bytes]; - ASSERT_TRUE(buffer != NULL); - EXPECT_TRUE(Monitor2EnumPorts(NULL, - NULL, - 1, - buffer, - needed_bytes, - &needed_bytes, - &returned)); - EXPECT_NE(0u, needed_bytes); - EXPECT_EQ(1u, returned); - PORT_INFO_1* port_info_1 = reinterpret_cast<PORT_INFO_1*>(buffer); - EXPECT_TRUE(port_info_1->pName != NULL); - delete[] buffer; - - returned = 0; - needed_bytes = 0; - EXPECT_FALSE(Monitor2EnumPorts(NULL, - NULL, - 2, - NULL, - 0, - &needed_bytes, - &returned)); - EXPECT_EQ(static_cast<DWORD>(ERROR_INSUFFICIENT_BUFFER), GetLastError()); - EXPECT_NE(0u, needed_bytes); - EXPECT_EQ(0u, returned); - - buffer = new BYTE[needed_bytes]; - ASSERT_TRUE(buffer != NULL); - EXPECT_TRUE(Monitor2EnumPorts(NULL, - NULL, - 2, - buffer, - needed_bytes, - &needed_bytes, - &returned)); - EXPECT_NE(0u, needed_bytes); - EXPECT_EQ(1u, returned); - PORT_INFO_2* port_info_2 = reinterpret_cast<PORT_INFO_2*>(buffer); - EXPECT_TRUE(port_info_2->pPortName != NULL); - delete[] buffer; -} - -TEST_F(PortMonitorTest, FlowTest) { - const wchar_t kXcvDataItem[] = L"MonitorUI"; - MONITORINIT monitor_init = {0}; - HANDLE monitor_handle = NULL; - HANDLE port_handle = NULL; - HANDLE xcv_handle = NULL; - DWORD bytes_processed = 0; - DWORD bytes_needed = 0; - const size_t kBufferSize = 100; - BYTE buffer[kBufferSize] = {0}; - - // Initialize the print monitor - MONITOR2* monitor2 = InitializePrintMonitor2(&monitor_init, &monitor_handle); - EXPECT_TRUE(monitor2 != NULL); - EXPECT_TRUE(monitor_handle != NULL); - - // Test the XCV functions. Used for reporting the location of the - // UI portion of the port monitor. - EXPECT_TRUE(monitor2->pfnXcvOpenPort != NULL); - EXPECT_TRUE(monitor2->pfnXcvOpenPort(monitor_handle, NULL, 0, &xcv_handle)); - EXPECT_TRUE(xcv_handle != NULL); - EXPECT_TRUE(monitor2->pfnXcvDataPort != NULL); - EXPECT_EQ(static_cast<DWORD>(ERROR_ACCESS_DENIED), - monitor2->pfnXcvDataPort(xcv_handle, kXcvDataItem, NULL, 0, buffer, - kBufferSize, &bytes_needed)); - EXPECT_TRUE(monitor2->pfnXcvClosePort != NULL); - EXPECT_TRUE(monitor2->pfnXcvClosePort(xcv_handle)); - EXPECT_TRUE(monitor2->pfnXcvOpenPort(monitor_handle, - NULL, - SERVER_ACCESS_ADMINISTER, - &xcv_handle)); - EXPECT_TRUE(xcv_handle != NULL); - EXPECT_TRUE(monitor2->pfnXcvDataPort != NULL); - EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), - monitor2->pfnXcvDataPort(xcv_handle, kXcvDataItem, NULL, 0, buffer, - kBufferSize, &bytes_needed)); - EXPECT_TRUE(monitor2->pfnXcvClosePort != NULL); - EXPECT_TRUE(monitor2->pfnXcvClosePort(xcv_handle)); - - // Test opening the port and running a print job. - EXPECT_TRUE(monitor2->pfnOpenPort != NULL); - EXPECT_TRUE(monitor2->pfnOpenPort(monitor_handle, NULL, &port_handle)); - EXPECT_TRUE(port_handle != NULL); - EXPECT_TRUE(monitor2->pfnStartDocPort != NULL); - EXPECT_TRUE(monitor2->pfnWritePort != NULL); - EXPECT_TRUE(monitor2->pfnReadPort != NULL); - EXPECT_TRUE(monitor2->pfnEndDocPort != NULL); - - // These functions should fail if we have not impersonated the user. - EXPECT_FALSE(monitor2->pfnStartDocPort( - port_handle, const_cast<wchar_t*>(L""), 0, 0, NULL)); - EXPECT_FALSE(monitor2->pfnWritePort(port_handle, - buffer, - kBufferSize, - &bytes_processed)); - EXPECT_EQ(0u, bytes_processed); - EXPECT_FALSE(monitor2->pfnReadPort(port_handle, - buffer, - sizeof(buffer), - &bytes_processed)); - EXPECT_EQ(0u, bytes_processed); - EXPECT_FALSE(monitor2->pfnEndDocPort(port_handle)); - - // Now impersonate so we can test the success case. - ASSERT_TRUE(ImpersonateSelf(SecurityImpersonation)); - EXPECT_TRUE(monitor2->pfnStartDocPort( - port_handle, const_cast<wchar_t*>(L""), 0, 0, NULL)); - EXPECT_TRUE(monitor2->pfnWritePort(port_handle, - buffer, - kBufferSize, - &bytes_processed)); - EXPECT_EQ(kBufferSize, bytes_processed); - EXPECT_FALSE(monitor2->pfnReadPort(port_handle, - buffer, - sizeof(buffer), - &bytes_processed)); - EXPECT_EQ(0u, bytes_processed); - EXPECT_TRUE(monitor2->pfnEndDocPort(port_handle)); - RevertToSelf(); - EXPECT_TRUE(monitor2->pfnClosePort != NULL); - EXPECT_TRUE(monitor2->pfnClosePort(port_handle)); - // Shutdown the port monitor. - Monitor2Shutdown(monitor_handle); -} - -} // namespace cloud_print -
diff --git a/cloud_print/virtual_driver/win/port_monitor/spooler_win.h b/cloud_print/virtual_driver/win/port_monitor/spooler_win.h deleted file mode 100644 index 7536f9557..0000000 --- a/cloud_print/virtual_driver/win/port_monitor/spooler_win.h +++ /dev/null
@@ -1,108 +0,0 @@ -// 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. - -#ifndef CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_SPOOLER_WIN_H_ -#define CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_SPOOLER_WIN_H_ - -#include <windows.h> - -// Compatible structures and prototypes are also defined in the Windows DDK in -// winsplp.h. -#ifndef _WINSPLP_ - -typedef struct { - DWORD size; - BOOL (WINAPI *pfnEnumPorts)(HANDLE, - wchar_t*, - DWORD level, - BYTE* ports, - DWORD ports_size, - DWORD* needed_bytes, - DWORD* returned); - - BOOL (WINAPI *pfnOpenPort)(HANDLE monitor_data, wchar_t*, HANDLE* handle); - - void* pfnOpenPortEx; // Unused. - - BOOL (WINAPI *pfnStartDocPort)(HANDLE port_handle, - wchar_t* printer_name, - DWORD job_id, - DWORD, - BYTE*); - - BOOL (WINAPI *pfnWritePort)(HANDLE port, - BYTE* buffer, - DWORD buffer_size, - DWORD* bytes_written); - - BOOL (WINAPI *pfnReadPort)(HANDLE, BYTE*, DWORD, DWORD* bytes_read); - - BOOL (WINAPI *pfnEndDocPort)(HANDLE port_handle); - - BOOL (WINAPI *pfnClosePort)(HANDLE port_handle); - - void* pfnAddPort; // Unused. - - void* pfnAddPortEx; // Unused. - - void* pfnConfigurePort; // Unused. - - void* pfnDeletePort; // Unused. - - void* pfnGetPrinterDataFromPort; // Unused. - - void* pfnSetPortTimeOuts; // Unusued. - - BOOL (WINAPI *pfnXcvOpenPort)(HANDLE monitor, - const wchar_t*, - ACCESS_MASK granted_access, - HANDLE* handle); - - DWORD (WINAPI *pfnXcvDataPort)(HANDLE xcv_handle, - const wchar_t* data_name, - BYTE*, - DWORD, - BYTE* output_data, - DWORD output_data_bytes, - DWORD* output_data_bytes_needed); - - BOOL (WINAPI *pfnXcvClosePort)(HANDLE handle); - - VOID (WINAPI *pfnShutdown)(HANDLE monitor_handle); -} MONITOR2; - -typedef struct { - DWORD size; - - BOOL (WINAPI *pfnAddPortUI)(const wchar_t*, - HWND hwnd, - const wchar_t* monitor_name, - wchar_t**); - - BOOL (WINAPI *pfnConfigurePortUI)(const wchar_t*, - HWND hwnd, - const wchar_t* port_name); - - BOOL (WINAPI *pfnDeletePortUI)(const wchar_t*, - HWND hwnd, - const wchar_t* port_name); -} MONITORUI; - -typedef struct { - DWORD cbSize; - HANDLE hSpooler; - HKEY hckRegistryRoot; - void* pMonitorReg; // Unused - BOOL bLocal; - LPCWSTR pszServerName; -} MONITORINIT; - -MONITOR2* WINAPI InitializePrintMonitor2(MONITORINIT* monitor_init, - HANDLE* monitor_handle); - -MONITORUI* WINAPI InitializePrintMonitorUI(void); - -#endif // ifdef USE_WIN_DDK -#endif // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_PORT_MONITOR_SPOOLER_WIN_H_ -
diff --git a/cloud_print/virtual_driver/win/virtual_driver.gyp b/cloud_print/virtual_driver/win/virtual_driver.gyp deleted file mode 100644 index 851746f..0000000 --- a/cloud_print/virtual_driver/win/virtual_driver.gyp +++ /dev/null
@@ -1,20 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'variables': { - 'virtual_driver_suffix': '', - }, - 'target_defaults': { - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base', - '<(DEPTH)/chrome/chrome.gyp:launcher_support', - '<(DEPTH)/chrome/common_constants.gyp:common_constants', - ], - - }, - 'includes': [ - 'virtual_driver.gypi', - ], -}
diff --git a/cloud_print/virtual_driver/win/virtual_driver.gypi b/cloud_print/virtual_driver/win/virtual_driver.gypi deleted file mode 100644 index 886f746..0000000 --- a/cloud_print/virtual_driver/win/virtual_driver.gypi +++ /dev/null
@@ -1,63 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'variables': { - 'chromium_code': 1, - }, - 'target_defaults': { - 'include_dirs': [ - '<(DEPTH)', - ], - 'libraries': [ - 'userenv.lib', - ], - }, - 'targets' : [ - { - # GN version: //cloud_print/virtual_driver/win - 'target_name': 'virtual_driver_lib<(virtual_driver_suffix)', - 'type': 'static_library', - 'sources': [ - '<(DEPTH)/cloud_print/common/win/cloud_print_utils.cc', - '<(DEPTH)/cloud_print/common/win/cloud_print_utils.h', - 'virtual_driver_consts.cc', - 'virtual_driver_consts.h', - 'virtual_driver_helpers.cc', - 'virtual_driver_helpers.h', - ], - }, - { - # GN version: //cloud_print/virtual_driver/win/port_monitor:lib - 'target_name': 'gcp_portmon_lib<(virtual_driver_suffix)', - 'type': 'static_library', - 'sources': [ - 'port_monitor/port_monitor.cc', - 'port_monitor/port_monitor.h', - ], - 'dependencies': [ - 'virtual_driver_lib<(virtual_driver_suffix)', - ], - }, - { - # GN version: //cloud_print/virtual_driver/win/port_monitor - 'target_name': 'gcp_portmon<(virtual_driver_suffix)', - 'type': 'loadable_module', - 'sources': [ - 'port_monitor/port_monitor.def', - 'port_monitor/port_monitor_dll.cc', - '<(SHARED_INTERMEDIATE_DIR)/cloud_print/gcp_portmon<(virtual_driver_suffix)_dll_version.rc', - ], - 'dependencies': [ - 'gcp_portmon_lib<(virtual_driver_suffix)', - '<(DEPTH)/chrome/common_constants.gyp:version_header', - '<(DEPTH)/cloud_print/cloud_print_resources.gyp:cloud_print_version_resources', - ], - 'include_dirs': [ - # To allow including "version.h" - '<(SHARED_INTERMEDIATE_DIR)', - ], - }, - ], -}
diff --git a/cloud_print/virtual_driver/win/virtual_driver64.gyp b/cloud_print/virtual_driver/win/virtual_driver64.gyp deleted file mode 100644 index 26a167f..0000000 --- a/cloud_print/virtual_driver/win/virtual_driver64.gyp +++ /dev/null
@@ -1,28 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'variables': { - 'virtual_driver_suffix': '64', - }, - 'target_defaults': { - 'defines': [ - '<@(nacl_win64_defines)', - ], - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base_win64', - '<(DEPTH)/chrome/chrome.gyp:launcher_support64', - '<(DEPTH)/chrome/common_constants.gyp:common_constants_win64', - ], - 'configurations': { - 'Common_Base': { - 'msvs_target_platform': 'x64', - }, - }, - }, - 'includes': [ - 'virtual_driver.gypi', - ], -} -
diff --git a/cloud_print/virtual_driver/win/virtual_driver_consts.cc b/cloud_print/virtual_driver/win/virtual_driver_consts.cc deleted file mode 100644 index eb64a9c..0000000 --- a/cloud_print/virtual_driver/win/virtual_driver_consts.cc +++ /dev/null
@@ -1,20 +0,0 @@ -// 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. - -#include "cloud_print/virtual_driver/win/virtual_driver_consts.h" - -#include <windows.h> -#include <stddef.h> - -#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h" - -namespace cloud_print { - -const wchar_t kPortName[] = L"GCP:"; -const size_t kPortNameSize = sizeof(kPortName); -const wchar_t kGoogleUpdateProductId[] = - L"{9B13FA92-1F73-4761-AB78-2C6ADAC3660D}"; - -} // namespace cloud_print -
diff --git a/cloud_print/virtual_driver/win/virtual_driver_consts.h b/cloud_print/virtual_driver/win/virtual_driver_consts.h deleted file mode 100644 index 7e5a404..0000000 --- a/cloud_print/virtual_driver/win/virtual_driver_consts.h +++ /dev/null
@@ -1,17 +0,0 @@ -// 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. - -#ifndef CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_CONSTS_H_ -#define CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_CONSTS_H_ - -namespace cloud_print { - -extern const wchar_t kPortName[]; -extern const size_t kPortNameSize; -extern const wchar_t kGoogleUpdateProductId[]; - -} // namespace cloud_print - -#endif // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_CONSTS_H_ -
diff --git a/cloud_print/virtual_driver/win/virtual_driver_helpers.cc b/cloud_print/virtual_driver/win/virtual_driver_helpers.cc deleted file mode 100644 index da4ec5c..0000000 --- a/cloud_print/virtual_driver/win/virtual_driver_helpers.cc +++ /dev/null
@@ -1,59 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cloud_print/virtual_driver/win/virtual_driver_helpers.h" - -#include <windows.h> -#include <winspool.h> - -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/strings/string16.h" -#include "base/win/windows_version.h" -#include "cloud_print/common/win/cloud_print_utils.h" - -namespace cloud_print { - -void DisplayWindowsMessage(HWND hwnd, HRESULT hr, - const base::string16 &caption) { - ::MessageBox(hwnd, GetErrorMessage(hr).c_str(), caption.c_str(), MB_OK); -} - -base::string16 GetPortMonitorDllName() { - if (IsSystem64Bit()) { - return base::string16(L"gcp_portmon64.dll"); - } else { - return base::string16(L"gcp_portmon.dll"); - } -} - -HRESULT GetPrinterDriverDir(base::FilePath* path) { - BYTE driver_dir_buffer[MAX_PATH * sizeof(wchar_t)]; - DWORD needed = 0; - if (!GetPrinterDriverDirectory(NULL, - NULL, - 1, - driver_dir_buffer, - MAX_PATH * sizeof(wchar_t), - &needed)) { - // We could try to allocate a larger buffer if needed > MAX_PATH - // but that really shouldn't happen. - return cloud_print::GetLastHResult(); - } - *path = base::FilePath(reinterpret_cast<wchar_t*>(driver_dir_buffer)); - - // The XPS driver is a "Level 3" driver - *path = path->Append(L"3"); - return S_OK; -} - -bool IsSystem64Bit() { - base::win::OSInfo::WindowsArchitecture arch = - base::win::OSInfo::GetInstance()->architecture(); - return (arch == base::win::OSInfo::X64_ARCHITECTURE) || - (arch == base::win::OSInfo::IA64_ARCHITECTURE); -} - -} -
diff --git a/cloud_print/virtual_driver/win/virtual_driver_helpers.h b/cloud_print/virtual_driver/win/virtual_driver_helpers.h deleted file mode 100644 index 426265f..0000000 --- a/cloud_print/virtual_driver/win/virtual_driver_helpers.h +++ /dev/null
@@ -1,36 +0,0 @@ -// 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. - -#ifndef CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_HELPERS_H_ -#define CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_HELPERS_H_ - -#include <windows.h> - -#include "base/strings/string16.h" - -namespace base { -class FilePath; -} - -namespace cloud_print { - -// Returns TRUE if the current OS is 64 bit. -bool IsSystem64Bit(); - -// Convert an HRESULT to a localized string and display it in a message box. -void DisplayWindowsMessage(HWND hwnd, HRESULT hr, - const base::string16 &caption); - -// Returns the correct port monitor DLL file name for the current machine. -base::string16 GetPortMonitorDllName(); - -// Gets the standard install path for "version 3" print drivers. -HRESULT GetPrinterDriverDir(base::FilePath* path); - -} // namespace cloud_print - -#endif // CLOUD_PRINT_VIRTUAL_DRIVER_WIN_VIRTUAL_DRIVER_HELPERS_H_ - - -
diff --git a/components/app_modal/javascript_app_modal_dialog.cc b/components/app_modal/javascript_app_modal_dialog.cc index 26d1b038..7a48f2a3 100644 --- a/components/app_modal/javascript_app_modal_dialog.cc +++ b/components/app_modal/javascript_app_modal_dialog.cc
@@ -9,9 +9,7 @@ #include "build/build_config.h" #include "components/app_modal/javascript_dialog_manager.h" #include "components/app_modal/javascript_native_dialog_factory.h" -#include "content/public/browser/web_contents.h" #include "ui/gfx/text_elider.h" -#include "url/origin.h" namespace app_modal { namespace { @@ -148,8 +146,7 @@ // The close callback above may delete web_contents_, thus removing the extra // data from the map owned by ::JavaScriptDialogManager. Make sure // to only use the data if still present. http://crbug.com/236476 - ExtraDataMap::iterator extra_data = - extra_data_map_->find(GetSerializedOriginForWebContents(web_contents())); + ExtraDataMap::iterator extra_data = extra_data_map_->find(web_contents()); if (extra_data != extra_data_map_->end()) { extra_data->second.has_already_shown_a_dialog_ = true; extra_data->second.suppress_javascript_messages_ = suppress_js_messages; @@ -174,12 +171,4 @@ } } -// static -std::string JavaScriptAppModalDialog::GetSerializedOriginForWebContents( - content::WebContents* contents) { - if (!contents) - return url::Origin().Serialize(); - return url::Origin(contents->GetLastCommittedURL()).Serialize(); -} - } // namespace app_modal
diff --git a/components/app_modal/javascript_app_modal_dialog.h b/components/app_modal/javascript_app_modal_dialog.h index 76c09e3..0fb4d89978 100644 --- a/components/app_modal/javascript_app_modal_dialog.h +++ b/components/app_modal/javascript_app_modal_dialog.h
@@ -6,7 +6,6 @@ #define COMPONENTS_APP_MODAL_JAVASCRIPT_APP_MODAL_DIALOG_H_ #include <map> -#include <string> #include "base/compiler_specific.h" #include "base/macros.h" @@ -21,7 +20,7 @@ public: ChromeJavaScriptDialogExtraData(); - // True if the user has already seen a JavaScript dialog from the origin. + // True if the user has already seen a JavaScript dialog from the WebContents. bool has_already_shown_a_dialog_; // True if the user has decided to block future JavaScript dialogs. @@ -35,7 +34,7 @@ // onbeforeunload dialog boxes. class JavaScriptAppModalDialog : public AppModalDialog { public: - typedef std::map<std::string, ChromeJavaScriptDialogExtraData> ExtraDataMap; + typedef std::map<void*, ChromeJavaScriptDialogExtraData> ExtraDataMap; JavaScriptAppModalDialog( content::WebContents* web_contents, @@ -67,11 +66,6 @@ // its delegate instead of whatever the UI reports. void SetOverridePromptText(const base::string16& prompt_text); - // The serialized form of the origin of the last committed URL in - // |web_contents_|. See |extra_data_map_|. - static std::string GetSerializedOriginForWebContents( - content::WebContents* contents); - // Accessors content::JavaScriptMessageType javascript_message_type() const { return javascript_message_type_; @@ -90,8 +84,8 @@ void CallDialogClosedCallback(bool success, const base::string16& prompt_text); - // A map of extra Chrome-only data associated with the delegate_. The keys - // come from |GetSerializedOriginForWebContents|. + // A map of extra Chrome-only data associated with the delegate_. Can be + // inspected via |extra_data_map_[web_contents_]|. ExtraDataMap* extra_data_map_; // Information about the message box is held in the following variables.
diff --git a/components/app_modal/javascript_dialog_manager.cc b/components/app_modal/javascript_dialog_manager.cc index 7c4db07..36153dad 100644 --- a/components/app_modal/javascript_dialog_manager.cc +++ b/components/app_modal/javascript_dialog_manager.cc
@@ -4,6 +4,7 @@ #include "components/app_modal/javascript_dialog_manager.h" +#include <algorithm> #include <utility> #include "base/bind.h" @@ -13,7 +14,6 @@ #include "base/strings/utf_string_conversions.h" #include "components/app_modal/app_modal_dialog.h" #include "components/app_modal/app_modal_dialog_queue.h" -#include "components/app_modal/javascript_app_modal_dialog.h" #include "components/app_modal/javascript_dialog_extensions_client.h" #include "components/app_modal/javascript_native_dialog_factory.h" #include "components/app_modal/native_app_modal_dialog.h" @@ -26,6 +26,7 @@ #include "ui/gfx/font_list.h" namespace app_modal { + namespace { #if !defined(OS_ANDROID) @@ -57,6 +58,32 @@ return extra_data->has_already_shown_a_dialog_; } +enum class DialogType { + JAVASCRIPT, + ON_BEFORE_UNLOAD, +}; + +void LogUMAMessageLengthStats(const base::string16& message, DialogType type) { + if (type == DialogType::JAVASCRIPT) { + UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfJSDialogMessageCharacters", + static_cast<int32_t>(message.length())); + } else { + UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfOnBeforeUnloadMessageCharacters", + static_cast<int32_t>(message.length())); + } + + int32_t newline_count = + std::count_if(message.begin(), message.end(), + [](const base::char16& c) { return c == '\n'; }); + if (type == DialogType::JAVASCRIPT) { + UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfJSDialogMessageNewlines", + newline_count); + } else { + UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfOnBeforeUnloadMessageNewlines", + newline_count); + } +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -99,9 +126,7 @@ *did_suppress_message = false; ChromeJavaScriptDialogExtraData* extra_data = - &javascript_dialog_extra_data_ - [JavaScriptAppModalDialog::GetSerializedOriginForWebContents( - web_contents)]; + &javascript_dialog_extra_data_[web_contents]; if (extra_data->suppress_javascript_messages_) { // If a page tries to open dialogs in a tight loop, the number of @@ -148,6 +173,7 @@ extensions_client_->OnDialogOpened(web_contents); + LogUMAMessageLengthStats(message_text, DialogType::JAVASCRIPT); AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog( web_contents, &javascript_dialog_extra_data_, @@ -168,9 +194,7 @@ bool is_reload, const DialogClosedCallback& callback) { ChromeJavaScriptDialogExtraData* extra_data = - &javascript_dialog_extra_data_ - [JavaScriptAppModalDialog::GetSerializedOriginForWebContents( - web_contents)]; + &javascript_dialog_extra_data_[web_contents]; if (extra_data->suppress_javascript_messages_) { // If a site harassed the user enough for them to put it on mute, then it @@ -189,6 +213,7 @@ extensions_client_->OnDialogOpened(web_contents); + LogUMAMessageLengthStats(message_text, DialogType::ON_BEFORE_UNLOAD); AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog( web_contents, &javascript_dialog_extra_data_, @@ -228,9 +253,7 @@ void JavaScriptDialogManager::ResetDialogState( content::WebContents* web_contents) { CancelActiveAndPendingDialogs(web_contents); - javascript_dialog_extra_data_.erase( - JavaScriptAppModalDialog::GetSerializedOriginForWebContents( - web_contents)); + javascript_dialog_extra_data_.erase(web_contents); } base::string16 JavaScriptDialogManager::GetTitle(
diff --git a/components/arc/arc_bridge_service.cc b/components/arc/arc_bridge_service.cc index ed207d3..f8babc2 100644 --- a/components/arc/arc_bridge_service.cc +++ b/components/arc/arc_bridge_service.cc
@@ -36,7 +36,12 @@ // static ArcBridgeService* ArcBridgeService::Get() { - DCHECK(g_arc_bridge_service); + if (!g_arc_bridge_service) { + // ArcBridgeService may be indirectly referenced in unit tests where + // ArcBridgeService is optional. + LOG(ERROR) << "ArcBridgeService is not ready."; + return nullptr; + } DCHECK(g_arc_bridge_service->CalledOnValidThread()); return g_arc_bridge_service; }
diff --git a/components/autofill/content/browser/risk/proto/fingerprint.proto b/components/autofill/content/browser/risk/proto/fingerprint.proto index f176db2..5c85e8c4 100644 --- a/components/autofill/content/browser/risk/proto/fingerprint.proto +++ b/components/autofill/content/browser/risk/proto/fingerprint.proto
@@ -88,8 +88,7 @@ // accept-languages. repeated string requested_language = 8; - // Default charset of the browser. (e.g. ISO-8859-1, obtained from - // document.defaultCharset) + // Default charset of the browser. (e.g. ISO-8859-1) optional string charset = 9; // The number of physical screens.
diff --git a/components/autofill/core/common/form_field_data.cc b/components/autofill/core/common/form_field_data.cc index f3cebf24..9d3bc93 100644 --- a/components/autofill/core/common/form_field_data.cc +++ b/components/autofill/core/common/form_field_data.cc
@@ -57,7 +57,7 @@ iter->ReadString16(&field_data->value) && iter->ReadString(&field_data->form_control_type) && iter->ReadString(&field_data->autocomplete_attribute) && - iter->ReadUInt32(&field_data->max_length) && + iter->ReadUInt64(&field_data->max_length) && iter->ReadBool(&field_data->is_autofilled) && iter->ReadBool(&field_data->is_checked) && iter->ReadBool(&field_data->is_checkable) && @@ -155,7 +155,7 @@ pickle->WriteString16(field_data.value); pickle->WriteString(field_data.form_control_type); pickle->WriteString(field_data.autocomplete_attribute); - pickle->WriteUInt32(field_data.max_length); + pickle->WriteUInt64(field_data.max_length); pickle->WriteBool(field_data.is_autofilled); pickle->WriteBool(field_data.is_checked); pickle->WriteBool(field_data.is_checkable);
diff --git a/components/autofill/core/common/form_field_data.h b/components/autofill/core/common/form_field_data.h index a3e923d..0fa3a25a5 100644 --- a/components/autofill/core/common/form_field_data.h +++ b/components/autofill/core/common/form_field_data.h
@@ -45,10 +45,11 @@ base::string16 value; std::string form_control_type; std::string autocomplete_attribute; - // Note: we use uint32_t instead of size_t because this struct is sent over - // IPC which could span 32 & 64 bit processes. This is fine since the length - // shouldn't exceed UINT32_MAX even on 64 bit builds. - uint32_t max_length; + // Note: we use uint64_t instead of size_t because this struct is sent over + // IPC which could span 32 & 64 bit processes. We chose uint64_t instead of + // uint32_t to maintain compatibility with old code which used size_t + // (base::Pickle used to serialize that as 64 bit). + uint64_t max_length; bool is_autofilled; bool is_checked; bool is_checkable;
diff --git a/components/autofill/core/common/form_field_data_unittest.cc b/components/autofill/core/common/form_field_data_unittest.cc index 3297f73..39b4aedd 100644 --- a/components/autofill/core/common/form_field_data_unittest.cc +++ b/components/autofill/core/common/form_field_data_unittest.cc
@@ -38,7 +38,7 @@ pickle->WriteString16(data.value); pickle->WriteString(data.form_control_type); pickle->WriteString(data.autocomplete_attribute); - pickle->WriteUInt32(data.max_length); + pickle->WriteUInt64(data.max_length); pickle->WriteBool(data.is_autofilled); pickle->WriteBool(data.is_checked); pickle->WriteBool(data.is_checkable);
diff --git a/components/browser_sync.gypi b/components/browser_sync.gypi index f9d4b4c..86244e5 100644 --- a/components/browser_sync.gypi +++ b/components/browser_sync.gypi
@@ -82,10 +82,16 @@ ], 'sources': [ # Note: file list duplicated in GN build. + 'browser_sync/browser/abstract_profile_sync_service_test.cc', + 'browser_sync/browser/abstract_profile_sync_service_test.h', 'browser_sync/browser/profile_sync_service_mock.cc', 'browser_sync/browser/profile_sync_service_mock.h', 'browser_sync/browser/profile_sync_test_util.cc', 'browser_sync/browser/profile_sync_test_util.h', + 'browser_sync/browser/test_http_bridge_factory.cc', + 'browser_sync/browser/test_http_bridge_factory.h', + 'browser_sync/browser/test_profile_sync_service.cc', + 'browser_sync/browser/test_profile_sync_service.h', ], } ],
diff --git a/components/browser_sync/browser/BUILD.gn b/components/browser_sync/browser/BUILD.gn index 683b1fce..653fb889 100644 --- a/components/browser_sync/browser/BUILD.gn +++ b/components/browser_sync/browser/BUILD.gn
@@ -48,7 +48,9 @@ testonly = true sources = [ + "profile_sync_service_autofill_unittest.cc", "profile_sync_service_startup_unittest.cc", + "profile_sync_service_typed_url_unittest.cc", "profile_sync_service_unittest.cc", ] @@ -57,8 +59,11 @@ ":test_support", "//base", "//base/test:test_support", + "//components/autofill/core/browser:test_support", + "//components/autofill/core/common:common", "//components/browser_sync/common", "//components/dom_distiller/core", + "//components/history/core/browser:browser", "//components/invalidation/impl", "//components/invalidation/public", "//components/prefs", @@ -74,6 +79,7 @@ "//components/syncable_prefs:test_support", "//components/version_info", "//components/version_info:generate_version_info", + "//components/webdata_services:test_support", "//google_apis", "//net", "//testing/gmock", @@ -85,16 +91,25 @@ source_set("test_support") { testonly = true sources = [ + "abstract_profile_sync_service_test.cc", + "abstract_profile_sync_service_test.h", "profile_sync_service_mock.cc", "profile_sync_service_mock.h", "profile_sync_test_util.cc", "profile_sync_test_util.h", + "test_http_bridge_factory.cc", + "test_http_bridge_factory.h", + "test_profile_sync_service.cc", + "test_profile_sync_service.h", ] deps = [ ":browser", "//base", "//base/test:test_support", + "//components/history/core/browser:browser", + "//components/invalidation/impl", + "//components/invalidation/impl:test_support", "//components/pref_registry", "//components/signin/core/browser:browser", "//components/signin/core/browser:test_support", @@ -104,6 +119,8 @@ "//components/syncable_prefs:test_support", "//google_apis", "//net:test_support", + "//sync:test_support_sync_core", + "//sync:test_support_sync_internal_api", "//testing/gmock", ] }
diff --git a/components/browser_sync/browser/DEPS b/components/browser_sync/browser/DEPS index 34efff6..a932286 100644 --- a/components/browser_sync/browser/DEPS +++ b/components/browser_sync/browser/DEPS
@@ -17,6 +17,8 @@ "+components/strings", "+components/variations", "+components/version_info", + "+components/webdata/common", + "+components/webdata_services", "+google_apis", "+net", "+sync",
diff --git a/components/browser_sync/browser/abstract_profile_sync_service_test.cc b/components/browser_sync/browser/abstract_profile_sync_service_test.cc new file mode 100644 index 0000000..3358d89 --- /dev/null +++ b/components/browser_sync/browser/abstract_profile_sync_service_test.cc
@@ -0,0 +1,235 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/browser_sync/browser/abstract_profile_sync_service_test.h" + +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/file_path.h" +#include "base/location.h" +#include "base/run_loop.h" +#include "components/browser_sync/browser/test_http_bridge_factory.h" +#include "components/browser_sync/browser/test_profile_sync_service.h" +#include "components/sync_driver/glue/sync_backend_host_core.h" +#include "components/sync_driver/sync_api_component_factory_mock.h" +#include "google_apis/gaia/gaia_constants.h" +#include "sync/internal_api/public/test/sync_manager_factory_for_profile_sync_test.h" +#include "sync/internal_api/public/test/test_internal_components_factory.h" +#include "sync/internal_api/public/test/test_user_share.h" +#include "sync/protocol/sync.pb.h" + +using syncer::ModelType; +using testing::_; +using testing::Return; + +namespace { + +class SyncBackendHostForProfileSyncTest + : public browser_sync::SyncBackendHostImpl { + public: + SyncBackendHostForProfileSyncTest( + const base::FilePath& temp_dir, + sync_driver::SyncClient* sync_client, + const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread, + invalidation::InvalidationService* invalidator, + const base::WeakPtr<sync_driver::SyncPrefs>& sync_prefs, + const base::Closure& callback); + ~SyncBackendHostForProfileSyncTest() override; + + void RequestConfigureSyncer( + syncer::ConfigureReason reason, + syncer::ModelTypeSet to_download, + syncer::ModelTypeSet to_purge, + syncer::ModelTypeSet to_journal, + syncer::ModelTypeSet to_unapply, + syncer::ModelTypeSet to_ignore, + const syncer::ModelSafeRoutingInfo& routing_info, + const base::Callback<void(syncer::ModelTypeSet, syncer::ModelTypeSet)>& + ready_task, + const base::Closure& retry_callback) override; + + protected: + void InitCore(scoped_ptr<browser_sync::DoInitializeOptions> options) override; + + private: + // Invoked at the start of HandleSyncManagerInitializationOnFrontendLoop. + // Allows extra initialization work to be performed before the backend comes + // up. + base::Closure callback_; + + DISALLOW_COPY_AND_ASSIGN(SyncBackendHostForProfileSyncTest); +}; + +SyncBackendHostForProfileSyncTest::SyncBackendHostForProfileSyncTest( + const base::FilePath& temp_dir, + sync_driver::SyncClient* sync_client, + const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread, + invalidation::InvalidationService* invalidator, + const base::WeakPtr<sync_driver::SyncPrefs>& sync_prefs, + const base::Closure& callback) + : browser_sync::SyncBackendHostImpl( + "dummy_debug_name", + sync_client, + ui_thread, + invalidator, + sync_prefs, + temp_dir.Append(base::FilePath(FILE_PATH_LITERAL("test")))), + callback_(callback) {} + +SyncBackendHostForProfileSyncTest::~SyncBackendHostForProfileSyncTest() {} + +void SyncBackendHostForProfileSyncTest::InitCore( + scoped_ptr<browser_sync::DoInitializeOptions> options) { + options->http_bridge_factory = scoped_ptr<syncer::HttpPostProviderFactory>( + new browser_sync::TestHttpBridgeFactory()); + options->sync_manager_factory.reset( + new syncer::SyncManagerFactoryForProfileSyncTest(callback_)); + options->credentials.email = "testuser@gmail.com"; + options->credentials.sync_token = "token"; + options->credentials.scope_set.insert(GaiaConstants::kChromeSyncOAuth2Scope); + options->restored_key_for_bootstrapping.clear(); + + // It'd be nice if we avoided creating the InternalComponentsFactory in the + // first place, but SyncBackendHost will have created one by now so we must + // free it. Grab the switches to pass on first. + syncer::InternalComponentsFactory::Switches factory_switches = + options->internal_components_factory->GetSwitches(); + options->internal_components_factory.reset( + new syncer::TestInternalComponentsFactory( + factory_switches, + syncer::InternalComponentsFactory::STORAGE_IN_MEMORY, nullptr)); + + browser_sync::SyncBackendHostImpl::InitCore(std::move(options)); +} + +void SyncBackendHostForProfileSyncTest::RequestConfigureSyncer( + syncer::ConfigureReason reason, + syncer::ModelTypeSet to_download, + syncer::ModelTypeSet to_purge, + syncer::ModelTypeSet to_journal, + syncer::ModelTypeSet to_unapply, + syncer::ModelTypeSet to_ignore, + const syncer::ModelSafeRoutingInfo& routing_info, + const base::Callback<void(syncer::ModelTypeSet, syncer::ModelTypeSet)>& + ready_task, + const base::Closure& retry_callback) { + syncer::ModelTypeSet failed_configuration_types; + + // The first parameter there should be the set of enabled types. That's not + // something we have access to from this strange test harness. We'll just + // send back the list of newly configured types instead and hope it doesn't + // break anything. + // Posted to avoid re-entrancy issues. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&SyncBackendHostForProfileSyncTest:: + FinishConfigureDataTypesOnFrontendLoop, + base::Unretained(this), + syncer::Difference(to_download, failed_configuration_types), + syncer::Difference(to_download, failed_configuration_types), + failed_configuration_types, ready_task)); +} + +// Helper function for return-type-upcasting of the callback. +sync_driver::SyncService* GetSyncService( + base::Callback<TestProfileSyncService*(void)> get_sync_service_callback) { + return get_sync_service_callback.Run(); +} + +} // namespace + +/* static */ +syncer::ImmutableChangeRecordList +ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList( + int64_t node_id, + syncer::ChangeRecord::Action action) { + syncer::ChangeRecord record; + record.action = action; + record.id = node_id; + syncer::ChangeRecordList records(1, record); + return syncer::ImmutableChangeRecordList(&records); +} + +/* static */ +syncer::ImmutableChangeRecordList +ProfileSyncServiceTestHelper::MakeSingletonDeletionChangeRecordList( + int64_t node_id, + const sync_pb::EntitySpecifics& specifics) { + syncer::ChangeRecord record; + record.action = syncer::ChangeRecord::ACTION_DELETE; + record.id = node_id; + record.specifics = specifics; + syncer::ChangeRecordList records(1, record); + return syncer::ImmutableChangeRecordList(&records); +} + +AbstractProfileSyncServiceTest::AbstractProfileSyncServiceTest() + : data_type_thread_("Extra thread") { + CHECK(temp_dir_.CreateUniqueTempDir()); +} + +AbstractProfileSyncServiceTest::~AbstractProfileSyncServiceTest() { + sync_service_->Shutdown(); +} + +bool AbstractProfileSyncServiceTest::CreateRoot(ModelType model_type) { + return syncer::TestUserShare::CreateRoot(model_type, + sync_service_->GetUserShare()); +} + +void AbstractProfileSyncServiceTest::CreateSyncService( + scoped_ptr<sync_driver::SyncClient> sync_client, + const base::Closure& initialization_success_callback) { + DCHECK(sync_client); + ProfileSyncService::InitParams init_params = + profile_sync_service_bundle_.CreateBasicInitParams( + browser_sync::AUTO_START, std::move(sync_client)); + sync_service_ = + make_scoped_ptr(new TestProfileSyncService(std::move(init_params))); + + SyncApiComponentFactoryMock* components = + profile_sync_service_bundle_.component_factory(); + EXPECT_CALL(*components, CreateSyncBackendHost(_, _, _, _)) + .WillOnce(Return(new SyncBackendHostForProfileSyncTest( + temp_dir_.path(), sync_service_->GetSyncClient(), + base::ThreadTaskRunnerHandle::Get(), + profile_sync_service_bundle_.fake_invalidation_service(), + sync_service_->sync_prefs()->AsWeakPtr(), + initialization_success_callback))); + + sync_service_->SetFirstSetupComplete(); +} + +base::Callback<sync_driver::SyncService*(void)> +AbstractProfileSyncServiceTest::GetSyncServiceCallback() { + return base::Bind(GetSyncService, + base::Bind(&AbstractProfileSyncServiceTest::sync_service, + base::Unretained(this))); +} + +CreateRootHelper::CreateRootHelper(AbstractProfileSyncServiceTest* test, + ModelType model_type) + : callback_(base::Bind(&CreateRootHelper::CreateRootCallback, + base::Unretained(this))), + test_(test), + model_type_(model_type), + success_(false) { +} + +CreateRootHelper::~CreateRootHelper() { +} + +const base::Closure& CreateRootHelper::callback() const { + return callback_; +} + +bool CreateRootHelper::success() { + return success_; +} + +void CreateRootHelper::CreateRootCallback() { + success_ = test_->CreateRoot(model_type_); +}
diff --git a/components/browser_sync/browser/abstract_profile_sync_service_test.h b/components/browser_sync/browser/abstract_profile_sync_service_test.h new file mode 100644 index 0000000..a6ad463 --- /dev/null +++ b/components/browser_sync/browser/abstract_profile_sync_service_test.h
@@ -0,0 +1,106 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_BROWSER_SYNC_BROWSER_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_ +#define COMPONENTS_BROWSER_SYNC_BROWSER_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_ + +#include <stdint.h> + +#include <string> + +#include "base/callback.h" +#include "base/files/scoped_temp_dir.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "components/browser_sync/browser/profile_sync_test_util.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/public/change_record.h" +#include "testing/gtest/include/gtest/gtest.h" + +class TestProfileSyncService; + +namespace syncer { +struct UserShare; +} // namespace syncer + +class ProfileSyncServiceTestHelper { + public: + static syncer::ImmutableChangeRecordList MakeSingletonChangeRecordList( + int64_t node_id, + syncer::ChangeRecord::Action action); + + // Deletions must provide an EntitySpecifics for the deleted data. + static syncer::ImmutableChangeRecordList + MakeSingletonDeletionChangeRecordList( + int64_t node_id, + const sync_pb::EntitySpecifics& specifics); + + private: + DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceTestHelper); +}; + +class AbstractProfileSyncServiceTest : public testing::Test { + public: + AbstractProfileSyncServiceTest(); + ~AbstractProfileSyncServiceTest() override; + + bool CreateRoot(syncer::ModelType model_type); + + protected: + // Creates a TestProfileSyncService instance based on + // |profile_sync_service_bundle_|, with start behavior + // browser_sync::AUTO_START. Passes |callback| down to + // SyncManagerForProfileSyncTest to be used by NotifyInitializationSuccess. + // |sync_client| is passed to the service. The created service is stored in + // |sync_service_|. + void CreateSyncService(scoped_ptr<sync_driver::SyncClient> sync_client, + const base::Closure& initialization_success_callback); + + base::Thread* data_type_thread() { return &data_type_thread_; } + + TestProfileSyncService* sync_service() { return sync_service_.get(); } + + // Returns the callback for the FakeSyncClient builder. It is not possible to + // just Bind() sync_service(), because of Callback not understanding the + // inheritance of its template arguments. + base::Callback<sync_driver::SyncService*(void)> GetSyncServiceCallback(); + + browser_sync::ProfileSyncServiceBundle* profile_sync_service_bundle() { + return &profile_sync_service_bundle_; + } + + private: + // Use |data_type_thread_| for code disallowed on the UI thread. + base::Thread data_type_thread_; + + base::MessageLoop message_loop_; + browser_sync::ProfileSyncServiceBundle profile_sync_service_bundle_; + scoped_ptr<TestProfileSyncService> sync_service_; + + base::ScopedTempDir temp_dir_; // To pass to the backend host. + + DISALLOW_COPY_AND_ASSIGN(AbstractProfileSyncServiceTest); +}; + +class CreateRootHelper { + public: + CreateRootHelper(AbstractProfileSyncServiceTest* test, + syncer::ModelType model_type); + virtual ~CreateRootHelper(); + + const base::Closure& callback() const; + bool success(); + + private: + void CreateRootCallback(); + + base::Closure callback_; + AbstractProfileSyncServiceTest* test_; + syncer::ModelType model_type_; + bool success_; + + DISALLOW_COPY_AND_ASSIGN(CreateRootHelper); +}; + +#endif // COMPONENTS_BROWSER_SYNC_BROWSER_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_
diff --git a/components/browser_sync/browser/profile_sync_service_autofill_unittest.cc b/components/browser_sync/browser/profile_sync_service_autofill_unittest.cc new file mode 100644 index 0000000..7539925 --- /dev/null +++ b/components/browser_sync/browser/profile_sync_service_autofill_unittest.cc
@@ -0,0 +1,1264 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> +#include <stdint.h> + +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/run_loop.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/country_names.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "components/autofill/core/browser/webdata/autocomplete_syncable_service.h" +#include "components/autofill/core/browser/webdata/autofill_change.h" +#include "components/autofill/core/browser/webdata/autofill_data_type_controller.h" +#include "components/autofill/core/browser/webdata/autofill_entry.h" +#include "components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h" +#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h" +#include "components/autofill/core/browser/webdata/autofill_table.h" +#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/autofill/core/common/autofill_pref_names.h" +#include "components/browser_sync/browser/abstract_profile_sync_service_test.h" +#include "components/browser_sync/browser/profile_sync_service.h" +#include "components/browser_sync/browser/test_profile_sync_service.h" +#include "components/sync_driver/data_type_controller.h" +#include "components/sync_driver/data_type_manager_impl.h" +#include "components/sync_driver/sync_api_component_factory_mock.h" +#include "components/syncable_prefs/pref_service_syncable.h" +#include "components/webdata/common/web_database.h" +#include "components/webdata_services/web_data_service_test_util.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/public/data_type_debug_info_listener.h" +#include "sync/internal_api/public/read_node.h" +#include "sync/internal_api/public/read_transaction.h" +#include "sync/internal_api/public/write_node.h" +#include "sync/internal_api/public/write_transaction.h" +#include "sync/protocol/autofill_specifics.pb.h" +#include "sync/syncable/mutable_entry.h" +#include "sync/syncable/syncable_write_transaction.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using autofill::AutocompleteSyncableService; +using autofill::AutofillChange; +using autofill::AutofillChangeList; +using autofill::AutofillEntry; +using autofill::AutofillKey; +using autofill::AutofillProfile; +using autofill::AutofillProfileChange; +using autofill::AutofillProfileSyncableService; +using autofill::AutofillTable; +using autofill::AutofillWebDataService; +using autofill::PersonalDataManager; +using base::Time; +using base::TimeDelta; +using base::WaitableEvent; +using browser_sync::AutofillDataTypeController; +using browser_sync::AutofillProfileDataTypeController; +using syncer::AUTOFILL; +using syncer::AUTOFILL_PROFILE; +using syncer::BaseNode; +using syncer::syncable::CREATE; +using syncer::syncable::GET_TYPE_ROOT; +using syncer::syncable::MutableEntry; +using syncer::syncable::UNITTEST; +using syncer::syncable::WriterTag; +using syncer::syncable::WriteTransaction; +using testing::_; +using testing::DoAll; +using testing::ElementsAre; +using testing::Not; +using testing::SetArgumentPointee; +using testing::Return; + +namespace { + +void RegisterAutofillPrefs(user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref(autofill::prefs::kAutofillEnabled, true); + registry->RegisterBooleanPref(autofill::prefs::kAutofillWalletImportEnabled, + true); +} + +void RunAndSignal(const base::Closure& cb, WaitableEvent* event) { + cb.Run(); + event->Signal(); +} + +AutofillEntry MakeAutofillEntry(const char* name, + const char* value, + int time_shift0, + int time_shift1) { + // Time deep in the past would cause Autocomplete sync to discard the + // entries. + static Time base_time = Time::Now().LocalMidnight(); + + Time date_created = base_time + TimeDelta::FromSeconds(time_shift0); + Time date_last_used = date_created; + if (time_shift1 >= 0) + date_last_used = base_time + TimeDelta::FromSeconds(time_shift1); + return AutofillEntry( + AutofillKey(base::ASCIIToUTF16(name), base::ASCIIToUTF16(value)), + date_created, date_last_used); +} + +AutofillEntry MakeAutofillEntry(const char* name, + const char* value, + int time_shift) { + return MakeAutofillEntry(name, value, time_shift, -1); +} + +} // namespace + +class AutofillTableMock : public AutofillTable { + public: + AutofillTableMock() {} + MOCK_METHOD2(RemoveFormElement, + bool(const base::string16& name, + const base::string16& value)); // NOLINT + MOCK_METHOD1(GetAllAutofillEntries, + bool(std::vector<AutofillEntry>* entries)); // NOLINT + MOCK_METHOD4(GetAutofillTimestamps, + bool(const base::string16& name, // NOLINT + const base::string16& value, + Time* date_created, + Time* date_last_used)); + MOCK_METHOD1(UpdateAutofillEntries, + bool(const std::vector<AutofillEntry>&)); // NOLINT + MOCK_METHOD1(GetAutofillProfiles, + bool(std::vector<AutofillProfile*>*)); // NOLINT + MOCK_METHOD1(UpdateAutofillProfile, + bool(const AutofillProfile&)); // NOLINT + MOCK_METHOD1(AddAutofillProfile, + bool(const AutofillProfile&)); // NOLINT + MOCK_METHOD1(RemoveAutofillProfile, + bool(const std::string&)); // NOLINT +}; + +MATCHER_P(MatchProfiles, profile, "") { + return (profile.Compare(arg) == 0); +} + +class WebDatabaseFake : public WebDatabase { + public: + explicit WebDatabaseFake(AutofillTable* autofill_table) { + AddTable(autofill_table); + } +}; + +class MockAutofillBackend : public autofill::AutofillWebDataBackend { + public: + MockAutofillBackend(WebDatabase* web_database, + const base::Closure& on_changed, + const scoped_refptr<base::SequencedTaskRunner>& ui_thread) + : web_database_(web_database), + on_changed_(on_changed), + ui_thread_(ui_thread) {} + + ~MockAutofillBackend() override {} + WebDatabase* GetDatabase() override { return web_database_; } + void AddObserver( + autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} + void RemoveObserver( + autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} + void RemoveExpiredFormElements() override {} + void NotifyOfMultipleAutofillChanges() override { + DCHECK(!ui_thread_->RunsTasksOnCurrentThread()); + ui_thread_->PostTask(FROM_HERE, on_changed_); + } + + private: + WebDatabase* web_database_; + base::Closure on_changed_; + const scoped_refptr<base::SequencedTaskRunner> ui_thread_; +}; + +class ProfileSyncServiceAutofillTest; + +template<class AutofillProfile> +syncer::ModelType GetModelType() { + return syncer::UNSPECIFIED; +} + +template<> +syncer::ModelType GetModelType<AutofillEntry>() { + return AUTOFILL; +} + +template<> +syncer::ModelType GetModelType<AutofillProfile>() { + return AUTOFILL_PROFILE; +} + +class TokenWebDataServiceFake : public TokenWebData { + public: + TokenWebDataServiceFake( + const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread, + const scoped_refptr<base::SingleThreadTaskRunner>& db_thread) + : TokenWebData(ui_thread, db_thread) {} + + bool IsDatabaseLoaded() override { return true; } + + AutofillWebDataService::Handle GetAllTokens( + WebDataServiceConsumer* consumer) override { + // TODO(tim): It would be nice if WebDataService was injected on + // construction of ProfileOAuth2TokenService rather than fetched by + // Initialize so that this isn't necessary (we could pass a NULL service). + // We currently do return it via EXPECT_CALLs, but without depending on + // order-of-initialization (which seems way more fragile) we can't tell + // which component is asking at what time, and some components in these + // Autofill tests require a WebDataService. + return 0; + } + + private: + ~TokenWebDataServiceFake() override {} + + DISALLOW_COPY_AND_ASSIGN(TokenWebDataServiceFake); +}; + +class WebDataServiceFake : public AutofillWebDataService { + public: + WebDataServiceFake( + const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread, + const scoped_refptr<base::SingleThreadTaskRunner>& db_thread) + : AutofillWebDataService(ui_thread, db_thread), + web_database_(NULL), + autocomplete_syncable_service_(NULL), + autofill_profile_syncable_service_(NULL), + syncable_service_created_or_destroyed_(false, false), + db_thread_(db_thread), + ui_thread_(ui_thread) {} + + void SetDatabase(WebDatabase* web_database) { + web_database_ = web_database; + } + + void StartSyncableService() { + // The |autofill_profile_syncable_service_| must be constructed on the DB + // thread. + const base::Closure& on_changed_callback = base::Bind( + &WebDataServiceFake::NotifyAutofillMultipleChangedOnUIThread, + AsWeakPtr()); + + db_thread_->PostTask( + FROM_HERE, base::Bind(&WebDataServiceFake::CreateSyncableService, + base::Unretained(this), on_changed_callback)); + syncable_service_created_or_destroyed_.Wait(); + } + + void ShutdownSyncableService() { + // The |autofill_profile_syncable_service_| must be destructed on the DB + // thread. + db_thread_->PostTask(FROM_HERE, + base::Bind(&WebDataServiceFake::DestroySyncableService, + base::Unretained(this))); + syncable_service_created_or_destroyed_.Wait(); + } + + bool IsDatabaseLoaded() override { return true; } + + WebDatabase* GetDatabase() override { return web_database_; } + + void OnAutofillEntriesChanged(const AutofillChangeList& changes) { + WaitableEvent event(true, false); + + base::Closure notify_cb = + base::Bind(&AutocompleteSyncableService::AutofillEntriesChanged, + base::Unretained(autocomplete_syncable_service_), + changes); + db_thread_->PostTask(FROM_HERE, + base::Bind(&RunAndSignal, notify_cb, &event)); + event.Wait(); + } + + void OnAutofillProfileChanged(const AutofillProfileChange& changes) { + WaitableEvent event(true, false); + + base::Closure notify_cb = + base::Bind(&AutocompleteSyncableService::AutofillProfileChanged, + base::Unretained(autofill_profile_syncable_service_), + changes); + db_thread_->PostTask(FROM_HERE, + base::Bind(&RunAndSignal, notify_cb, &event)); + event.Wait(); + } + + private: + ~WebDataServiceFake() override {} + + void CreateSyncableService(const base::Closure& on_changed_callback) { + ASSERT_TRUE(db_thread_->RunsTasksOnCurrentThread()); + // These services are deleted in DestroySyncableService(). + backend_.reset(new MockAutofillBackend(GetDatabase(), on_changed_callback, + ui_thread_.get())); + AutocompleteSyncableService::CreateForWebDataServiceAndBackend( + this, backend_.get()); + AutofillProfileSyncableService::CreateForWebDataServiceAndBackend( + this, backend_.get(), "en-US"); + + autocomplete_syncable_service_ = + AutocompleteSyncableService::FromWebDataService(this); + autofill_profile_syncable_service_ = + AutofillProfileSyncableService::FromWebDataService(this); + + syncable_service_created_or_destroyed_.Signal(); + } + + void DestroySyncableService() { + ASSERT_TRUE(db_thread_->RunsTasksOnCurrentThread()); + autocomplete_syncable_service_ = NULL; + autofill_profile_syncable_service_ = NULL; + backend_.reset(); + syncable_service_created_or_destroyed_.Signal(); + } + + WebDatabase* web_database_; + AutocompleteSyncableService* autocomplete_syncable_service_; + AutofillProfileSyncableService* autofill_profile_syncable_service_; + scoped_ptr<autofill::AutofillWebDataBackend> backend_; + + WaitableEvent syncable_service_created_or_destroyed_; + + const scoped_refptr<base::SingleThreadTaskRunner> db_thread_; + const scoped_refptr<base::SingleThreadTaskRunner> ui_thread_; + + DISALLOW_COPY_AND_ASSIGN(WebDataServiceFake); +}; + +ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) { + return new sync_driver::DataTypeManagerImpl( + debug_listener, + arg1, + arg2, + arg3, + arg4); +} + +class MockPersonalDataManager : public PersonalDataManager { + public: + MockPersonalDataManager() : PersonalDataManager("en-US") {} + MOCK_CONST_METHOD0(IsDataLoaded, bool()); + MOCK_METHOD0(LoadProfiles, void()); + MOCK_METHOD0(LoadCreditCards, void()); + MOCK_METHOD0(Refresh, void()); +}; + +template <class T> class AddAutofillHelper; + +class ProfileSyncServiceAutofillTest + : public AbstractProfileSyncServiceTest, + public syncer::DataTypeDebugInfoListener { + public: + // DataTypeDebugInfoListener implementation. + void OnDataTypeConfigureComplete(const std::vector< + syncer::DataTypeConfigurationStats>& configuration_stats) override { + ASSERT_EQ(1u, configuration_stats.size()); + association_stats_ = configuration_stats[0].association_stats; + } + + protected: + ProfileSyncServiceAutofillTest() : debug_ptr_factory_(this) { + autofill::CountryNames::SetLocaleString("en-US"); + RegisterAutofillPrefs( + profile_sync_service_bundle()->pref_service()->registry()); + + data_type_thread()->Start(); + profile_sync_service_bundle()->set_db_thread( + data_type_thread()->task_runner()); + + web_database_.reset(new WebDatabaseFake(&autofill_table_)); + web_data_wrapper_ = make_scoped_ptr(new MockWebDataServiceWrapper( + new WebDataServiceFake(base::ThreadTaskRunnerHandle::Get(), + data_type_thread()->task_runner()), + new TokenWebDataServiceFake(base::ThreadTaskRunnerHandle::Get(), + data_type_thread()->task_runner()))); + web_data_service_ = static_cast<WebDataServiceFake*>( + web_data_wrapper_->GetAutofillWebData().get()); + web_data_service_->SetDatabase(web_database_.get()); + + personal_data_manager_ = make_scoped_ptr(new MockPersonalDataManager()); + + EXPECT_CALL(personal_data_manager(), LoadProfiles()); + EXPECT_CALL(personal_data_manager(), LoadCreditCards()); + + personal_data_manager_->Init( + web_data_service_, profile_sync_service_bundle()->pref_service(), + profile_sync_service_bundle()->account_tracker(), + profile_sync_service_bundle()->signin_manager(), false); + + web_data_service_->StartSyncableService(); + + browser_sync::ProfileSyncServiceBundle::SyncClientBuilder builder( + profile_sync_service_bundle()); + builder.SetPersonalDataManager(personal_data_manager_.get()); + builder.SetSyncServiceCallback(GetSyncServiceCallback()); + builder.SetSyncableServiceCallback( + base::Bind(&ProfileSyncServiceAutofillTest::GetSyncableServiceForType, + base::Unretained(this))); + builder.set_activate_model_creation(); + sync_client_owned_ = builder.Build(); + sync_client_ = sync_client_owned_.get(); + + // When UpdateAutofillEntries() is called with an empty list, the return + // value should be |true|, rather than the default of |false|. + std::vector<AutofillEntry> empty; + EXPECT_CALL(autofill_table_, UpdateAutofillEntries(empty)) + .WillRepeatedly(Return(true)); + } + + ~ProfileSyncServiceAutofillTest() override { + web_data_service_->ShutdownOnUIThread(); + web_data_service_->ShutdownSyncableService(); + web_data_wrapper_->Shutdown(); + web_data_service_ = nullptr; + web_data_wrapper_.reset(); + web_database_.reset(); + // Shut down the service explicitly before some data members from this + // test it needs will be deleted. + sync_service()->Shutdown(); + } + + int GetSyncCount(syncer::ModelType type) { + syncer::ReadTransaction trans(FROM_HERE, sync_service()->GetUserShare()); + syncer::ReadNode node(&trans); + if (node.InitTypeRoot(type) != BaseNode::INIT_OK) + return 0; + return node.GetTotalNodeCount() - 1; + } + + void StartSyncService(const base::Closure& callback, + bool will_fail_association, + syncer::ModelType type) { + SigninManagerBase* signin = profile_sync_service_bundle()->signin_manager(); + signin->SetAuthenticatedAccountInfo("12345", "test_user@gmail.com"); + CreateSyncService(std::move(sync_client_owned_), callback); + + EXPECT_CALL(*profile_sync_service_bundle()->component_factory(), + CreateDataTypeManager(_, _, _, _, _)) + .WillOnce(ReturnNewDataTypeManagerWithDebugListener( + syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr()))); + + EXPECT_CALL(personal_data_manager(), IsDataLoaded()) + .WillRepeatedly(Return(true)); + + // We need tokens to get the tests going + profile_sync_service_bundle()->auth_service()->UpdateCredentials( + signin->GetAuthenticatedAccountId(), "oauth2_login_token"); + + sync_service()->RegisterDataTypeController(CreateDataTypeController(type)); + sync_service()->Initialize(); + base::RunLoop().Run(); + + // It's possible this test triggered an unrecoverable error, in which case + // we can't get the sync count. + if (sync_service()->IsSyncActive()) { + EXPECT_EQ(GetSyncCount(type), + association_stats_.num_sync_items_after_association); + } + EXPECT_EQ(association_stats_.num_sync_items_after_association, + association_stats_.num_sync_items_before_association + + association_stats_.num_sync_items_added - + association_stats_.num_sync_items_deleted); + } + + bool AddAutofillSyncNode(const AutofillEntry& entry) { + syncer::WriteTransaction trans(FROM_HERE, sync_service()->GetUserShare()); + syncer::WriteNode node(&trans); + std::string tag = AutocompleteSyncableService::KeyToTag( + base::UTF16ToUTF8(entry.key().name()), + base::UTF16ToUTF8(entry.key().value())); + syncer::WriteNode::InitUniqueByCreationResult result = + node.InitUniqueByCreation(AUTOFILL, tag); + if (result != syncer::WriteNode::INIT_SUCCESS) + return false; + + sync_pb::EntitySpecifics specifics; + AutocompleteSyncableService::WriteAutofillEntry(entry, &specifics); + node.SetEntitySpecifics(specifics); + return true; + } + + bool AddAutofillSyncNode(const AutofillProfile& profile) { + syncer::WriteTransaction trans(FROM_HERE, sync_service()->GetUserShare()); + syncer::WriteNode node(&trans); + std::string tag = profile.guid(); + syncer::WriteNode::InitUniqueByCreationResult result = + node.InitUniqueByCreation(AUTOFILL_PROFILE, tag); + if (result != syncer::WriteNode::INIT_SUCCESS) + return false; + + sync_pb::EntitySpecifics specifics; + AutofillProfileSyncableService::WriteAutofillProfile(profile, &specifics); + node.SetEntitySpecifics(specifics); + return true; + } + + bool GetAutofillEntriesFromSyncDB(std::vector<AutofillEntry>* entries, + std::vector<AutofillProfile>* profiles) { + syncer::ReadTransaction trans(FROM_HERE, sync_service()->GetUserShare()); + syncer::ReadNode autofill_root(&trans); + if (autofill_root.InitTypeRoot(AUTOFILL) != BaseNode::INIT_OK) { + return false; + } + + int64_t child_id = autofill_root.GetFirstChildId(); + while (child_id != syncer::kInvalidId) { + syncer::ReadNode child_node(&trans); + if (child_node.InitByIdLookup(child_id) != BaseNode::INIT_OK) + return false; + + const sync_pb::AutofillSpecifics& autofill( + child_node.GetEntitySpecifics().autofill()); + if (autofill.has_value()) { + AutofillKey key(base::UTF8ToUTF16(autofill.name()), + base::UTF8ToUTF16(autofill.value())); + std::vector<Time> timestamps; + int timestamps_count = autofill.usage_timestamp_size(); + for (int i = 0; i < timestamps_count; ++i) { + timestamps.push_back(Time::FromInternalValue( + autofill.usage_timestamp(i))); + } + entries->push_back( + AutofillEntry(key, timestamps.front(), timestamps.back())); + } else if (autofill.has_profile()) { + AutofillProfile p; + p.set_guid(autofill.profile().guid()); + AutofillProfileSyncableService::OverwriteProfileWithServerData( + autofill.profile(), &p); + profiles->push_back(p); + } + child_id = child_node.GetSuccessorId(); + } + return true; + } + + bool GetAutofillProfilesFromSyncDBUnderProfileNode( + std::vector<AutofillProfile>* profiles) { + syncer::ReadTransaction trans(FROM_HERE, sync_service()->GetUserShare()); + syncer::ReadNode autofill_root(&trans); + if (autofill_root.InitTypeRoot(AUTOFILL_PROFILE) != BaseNode::INIT_OK) { + return false; + } + + int64_t child_id = autofill_root.GetFirstChildId(); + while (child_id != syncer::kInvalidId) { + syncer::ReadNode child_node(&trans); + if (child_node.InitByIdLookup(child_id) != BaseNode::INIT_OK) + return false; + + const sync_pb::AutofillProfileSpecifics& autofill( + child_node.GetEntitySpecifics().autofill_profile()); + AutofillProfile p; + p.set_guid(autofill.guid()); + AutofillProfileSyncableService::OverwriteProfileWithServerData(autofill, + &p); + profiles->push_back(p); + child_id = child_node.GetSuccessorId(); + } + return true; + } + + void SetIdleChangeProcessorExpectations() { + EXPECT_CALL(autofill_table_, RemoveFormElement(_, _)).Times(0); + EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _, _)).Times(0); + + // Only permit UpdateAutofillEntries() to be called with an empty list. + std::vector<AutofillEntry> empty; + EXPECT_CALL(autofill_table_, UpdateAutofillEntries(Not(empty))).Times(0); + } + + sync_driver::DataTypeController* CreateDataTypeController( + syncer::ModelType type) { + DCHECK(type == AUTOFILL || type == AUTOFILL_PROFILE); + if (type == AUTOFILL) { + return new AutofillDataTypeController(base::ThreadTaskRunnerHandle::Get(), + data_type_thread()->task_runner(), + base::Bind(&base::DoNothing), + sync_client_, web_data_service_); + } else { + return new AutofillProfileDataTypeController( + base::ThreadTaskRunnerHandle::Get(), + data_type_thread()->task_runner(), base::Bind(&base::DoNothing), + sync_client_, web_data_service_); + } + } + + AutofillTableMock& autofill_table() { return autofill_table_; } + + MockPersonalDataManager& personal_data_manager() { + return *personal_data_manager_; + } + + WebDataServiceFake* web_data_service() { return web_data_service_.get(); } + + private: + friend class AddAutofillHelper<AutofillEntry>; + friend class AddAutofillHelper<AutofillProfile>; + + base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType( + syncer::ModelType type) { + DCHECK(type == AUTOFILL || type == AUTOFILL_PROFILE); + if (type == AUTOFILL) { + return AutocompleteSyncableService::FromWebDataService( + web_data_service_.get()) + ->AsWeakPtr(); + } else { + return AutofillProfileSyncableService::FromWebDataService( + web_data_service_.get()) + ->AsWeakPtr(); + } + } + + AutofillTableMock autofill_table_; + scoped_ptr<WebDatabaseFake> web_database_; + scoped_ptr<MockWebDataServiceWrapper> web_data_wrapper_; + scoped_refptr<WebDataServiceFake> web_data_service_; + scoped_ptr<MockPersonalDataManager> personal_data_manager_; + syncer::DataTypeAssociationStats association_stats_; + base::WeakPtrFactory<DataTypeDebugInfoListener> debug_ptr_factory_; + // |sync_client_owned_| keeps the created client until it is passed to the + // created ProfileSyncService. |sync_client_| just keeps a weak reference to + // the client the whole time. + scoped_ptr<sync_driver::FakeSyncClient> sync_client_owned_; + sync_driver::FakeSyncClient* sync_client_; + + DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceAutofillTest); +}; + +template <class T> +class AddAutofillHelper { + public: + AddAutofillHelper(ProfileSyncServiceAutofillTest* test, + const std::vector<T>& entries) + : callback_(base::Bind(&AddAutofillHelper::AddAutofillCallback, + base::Unretained(this), test, entries)), + success_(false) { + } + + const base::Closure& callback() const { return callback_; } + bool success() { return success_; } + + private: + void AddAutofillCallback(ProfileSyncServiceAutofillTest* test, + const std::vector<T>& entries) { + if (!test->CreateRoot(GetModelType<T>())) + return; + + for (size_t i = 0; i < entries.size(); ++i) { + if (!test->AddAutofillSyncNode(entries[i])) + return; + } + success_ = true; + } + + base::Closure callback_; + bool success_; +}; + +// Overload write transaction to use custom NotifyTransactionComplete +class WriteTransactionTest: public WriteTransaction { + public: + WriteTransactionTest(const tracked_objects::Location& from_here, + WriterTag writer, + syncer::syncable::Directory* directory, + WaitableEvent* wait_for_syncapi) + : WriteTransaction(from_here, writer, directory), + wait_for_syncapi_(wait_for_syncapi) {} + + void NotifyTransactionComplete(syncer::ModelTypeSet types) override { + // This is where we differ. Force a thread change here, giving another + // thread a chance to create a WriteTransaction + wait_for_syncapi_->Wait(); + + WriteTransaction::NotifyTransactionComplete(types); + } + + private: + WaitableEvent* const wait_for_syncapi_; +}; + +// Our fake server updater. Needs the RefCountedThreadSafe inheritance so we can +// post tasks with it. +class FakeServerUpdater : public base::RefCountedThreadSafe<FakeServerUpdater> { + public: + FakeServerUpdater(TestProfileSyncService* service, + WaitableEvent* wait_for_start, + WaitableEvent* wait_for_syncapi, + scoped_refptr<base::SequencedTaskRunner> db_thread) + : entry_(MakeAutofillEntry("0", "0", 0)), + service_(service), + wait_for_start_(wait_for_start), + wait_for_syncapi_(wait_for_syncapi), + is_finished_(false, false), + db_thread_(db_thread) {} + + void Update() { + // This gets called in a modelsafeworker thread. + ASSERT_TRUE(db_thread_->RunsTasksOnCurrentThread()); + + syncer::UserShare* user_share = service_->GetUserShare(); + syncer::syncable::Directory* directory = user_share->directory.get(); + + // Create autofill protobuf. + std::string tag = AutocompleteSyncableService::KeyToTag( + base::UTF16ToUTF8(entry_.key().name()), + base::UTF16ToUTF8(entry_.key().value())); + sync_pb::AutofillSpecifics new_autofill; + new_autofill.set_name(base::UTF16ToUTF8(entry_.key().name())); + new_autofill.set_value(base::UTF16ToUTF8(entry_.key().value())); + new_autofill.add_usage_timestamp(entry_.date_created().ToInternalValue()); + if (entry_.date_created() != entry_.date_last_used()) { + new_autofill.add_usage_timestamp( + entry_.date_last_used().ToInternalValue()); + } + + sync_pb::EntitySpecifics entity_specifics; + entity_specifics.mutable_autofill()->CopyFrom(new_autofill); + + { + // Tell main thread we've started + wait_for_start_->Signal(); + + // Create write transaction. + WriteTransactionTest trans(FROM_HERE, UNITTEST, directory, + wait_for_syncapi_); + + // Create actual entry based on autofill protobuf information. + // Simulates effects of UpdateLocalDataFromServerData + MutableEntry parent(&trans, GET_TYPE_ROOT, AUTOFILL); + MutableEntry item(&trans, CREATE, AUTOFILL, parent.GetId(), tag); + ASSERT_TRUE(item.good()); + item.PutSpecifics(entity_specifics); + item.PutServerSpecifics(entity_specifics); + item.PutBaseVersion(1); + syncer::syncable::Id server_item_id = + service_->id_factory()->NewServerId(); + item.PutId(server_item_id); + syncer::syncable::Id new_predecessor; + ASSERT_TRUE(item.PutPredecessor(new_predecessor)); + } + DVLOG(1) << "FakeServerUpdater finishing."; + is_finished_.Signal(); + } + + void CreateNewEntry(const AutofillEntry& entry) { + entry_ = entry; + ASSERT_FALSE(db_thread_->RunsTasksOnCurrentThread()); + if (!db_thread_->PostTask(FROM_HERE, + base::Bind(&FakeServerUpdater::Update, this))) { + NOTREACHED() << "Failed to post task to the db thread."; + return; + } + } + + void WaitForUpdateCompletion() { + is_finished_.Wait(); + } + + private: + friend class base::RefCountedThreadSafe<FakeServerUpdater>; + ~FakeServerUpdater() { } + + AutofillEntry entry_; + TestProfileSyncService* service_; + WaitableEvent* const wait_for_start_; + WaitableEvent* const wait_for_syncapi_; + WaitableEvent is_finished_; + syncer::syncable::Id parent_id_; + scoped_refptr<base::SequencedTaskRunner> db_thread_; + + DISALLOW_COPY_AND_ASSIGN(FakeServerUpdater); +}; + +// TODO(skrul): Test abort startup. +// TODO(skrul): Test processing of cloud changes. +// TODO(tim): Add autofill data type controller test, and a case to cover +// waiting for the PersonalDataManager. +TEST_F(ProfileSyncServiceAutofillTest, FailModelAssociation) { + // Don't create the root autofill node so startup fails. + StartSyncService(base::Closure(), true, AUTOFILL); + EXPECT_TRUE(sync_service()->HasUnrecoverableError()); +} + +TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) { + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .WillOnce(Return(true)); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, AUTOFILL); + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(create_root.callback(), false, AUTOFILL); + EXPECT_TRUE(create_root.success()); + std::vector<AutofillEntry> sync_entries; + std::vector<AutofillProfile> sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); + EXPECT_EQ(0U, sync_entries.size()); + EXPECT_EQ(0U, sync_profiles.size()); +} + +TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) { + std::vector<AutofillEntry> entries; + entries.push_back(MakeAutofillEntry("foo", "bar", 1)); + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, AUTOFILL); + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(create_root.callback(), false, AUTOFILL); + ASSERT_TRUE(create_root.success()); + std::vector<AutofillEntry> sync_entries; + std::vector<AutofillProfile> sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); + ASSERT_EQ(1U, entries.size()); + EXPECT_TRUE(entries[0] == sync_entries[0]); + EXPECT_EQ(0U, sync_profiles.size()); +} + +TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) { + std::vector<AutofillProfile*> profiles; + std::vector<AutofillProfile> expected_profiles; + // Owned by GetAutofillProfiles caller. + AutofillProfile* profile0 = new AutofillProfile; + autofill::test::SetProfileInfoWithGuid(profile0, + "54B3F9AA-335E-4F71-A27D-719C41564230", "Billing", + "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", + "91601", "US", "12345678910"); + profiles.push_back(profile0); + expected_profiles.push_back(*profile0); + EXPECT_CALL(autofill_table(), GetAutofillProfiles(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true))); + EXPECT_CALL(personal_data_manager(), Refresh()); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, AUTOFILL_PROFILE); + StartSyncService(create_root.callback(), false, AUTOFILL_PROFILE); + ASSERT_TRUE(create_root.success()); + std::vector<AutofillProfile> sync_profiles; + ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(&sync_profiles)); + EXPECT_EQ(1U, sync_profiles.size()); + EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[0])); +} + +TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) { + // There is buggy autofill code that allows duplicate name/value + // pairs to exist in the database with separate pair_ids. + std::vector<AutofillEntry> entries; + entries.push_back(MakeAutofillEntry("foo", "bar", 1)); + entries.push_back(MakeAutofillEntry("dup", "", 2)); + entries.push_back(MakeAutofillEntry("dup", "", 3)); + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, AUTOFILL); + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(create_root.callback(), false, AUTOFILL); + ASSERT_TRUE(create_root.success()); + std::vector<AutofillEntry> sync_entries; + std::vector<AutofillProfile> sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); + EXPECT_EQ(2U, sync_entries.size()); +} + +TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) { + AutofillEntry native_entry(MakeAutofillEntry("native", "entry", 1)); + AutofillEntry sync_entry(MakeAutofillEntry("sync", "entry", 2)); + + std::vector<AutofillEntry> native_entries; + native_entries.push_back(native_entry); + + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); + + std::vector<AutofillEntry> sync_entries; + sync_entries.push_back(sync_entry); + + AddAutofillHelper<AutofillEntry> add_autofill(this, sync_entries); + + EXPECT_CALL(autofill_table(), UpdateAutofillEntries(ElementsAre(sync_entry))) + .WillOnce(Return(true)); + + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(add_autofill.callback(), false, AUTOFILL); + ASSERT_TRUE(add_autofill.success()); + + std::set<AutofillEntry> expected_entries; + expected_entries.insert(native_entry); + expected_entries.insert(sync_entry); + + std::vector<AutofillEntry> new_sync_entries; + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); + std::set<AutofillEntry> new_sync_entries_set(new_sync_entries.begin(), + new_sync_entries.end()); + + EXPECT_TRUE(expected_entries == new_sync_entries_set); +} + +TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) { + AutofillEntry native_entry(MakeAutofillEntry("merge", "entry", 1)); + AutofillEntry sync_entry(MakeAutofillEntry("merge", "entry", 2)); + AutofillEntry merged_entry(MakeAutofillEntry("merge", "entry", 1, 2)); + + std::vector<AutofillEntry> native_entries; + native_entries.push_back(native_entry); + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); + + std::vector<AutofillEntry> sync_entries; + sync_entries.push_back(sync_entry); + AddAutofillHelper<AutofillEntry> add_autofill(this, sync_entries); + + EXPECT_CALL(autofill_table(), + UpdateAutofillEntries(ElementsAre(merged_entry))) + .WillOnce(Return(true)); + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(add_autofill.callback(), false, AUTOFILL); + ASSERT_TRUE(add_autofill.success()); + + std::vector<AutofillEntry> new_sync_entries; + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(merged_entry == new_sync_entries[0]); +} + +TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) { + AutofillProfile sync_profile; + autofill::test::SetProfileInfoWithGuid(&sync_profile, + "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", + "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", + "91601", "US", "12345678910"); + + AutofillProfile* native_profile = new AutofillProfile; + autofill::test::SetProfileInfoWithGuid(native_profile, + "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", + "32801", "US", "19482937549"); + + std::vector<AutofillProfile*> native_profiles; + native_profiles.push_back(native_profile); + EXPECT_CALL(autofill_table(), GetAutofillProfiles(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); + + std::vector<AutofillProfile> sync_profiles; + sync_profiles.push_back(sync_profile); + AddAutofillHelper<AutofillProfile> add_autofill(this, sync_profiles); + + EXPECT_CALL(autofill_table(), + UpdateAutofillProfile(MatchProfiles(sync_profile))) + .WillOnce(Return(true)); + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(add_autofill.callback(), false, AUTOFILL_PROFILE); + ASSERT_TRUE(add_autofill.success()); + + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( + &new_sync_profiles)); + ASSERT_EQ(1U, new_sync_profiles.size()); + EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0])); +} + +TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfileCombine) { + AutofillProfile sync_profile; + autofill::test::SetProfileInfoWithGuid(&sync_profile, + "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", + "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", + "91601", "US", "12345678910"); + + AutofillProfile* native_profile = new AutofillProfile; + // Same address, but different names, phones and e-mails. + autofill::test::SetProfileInfoWithGuid(native_profile, + "23355099-1170-4B71-8ED4-144470CC9EBF", "Billing", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", + "91601", "US", "19482937549"); + + AutofillProfile expected_profile(sync_profile); + expected_profile.OverwriteWith(*native_profile, "en-US"); + + std::vector<AutofillProfile*> native_profiles; + native_profiles.push_back(native_profile); + EXPECT_CALL(autofill_table(), GetAutofillProfiles(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); + EXPECT_CALL(autofill_table(), + AddAutofillProfile(MatchProfiles(expected_profile))) + .WillOnce(Return(true)); + EXPECT_CALL(autofill_table(), + RemoveAutofillProfile("23355099-1170-4B71-8ED4-144470CC9EBF")) + .WillOnce(Return(true)); + std::vector<AutofillProfile> sync_profiles; + sync_profiles.push_back(sync_profile); + AddAutofillHelper<AutofillProfile> add_autofill(this, sync_profiles); + + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(add_autofill.callback(), false, AUTOFILL_PROFILE); + ASSERT_TRUE(add_autofill.success()); + + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( + &new_sync_profiles)); + ASSERT_EQ(1U, new_sync_profiles.size()); + // Check that key fields are the same. + EXPECT_TRUE(new_sync_profiles[0].IsSubsetOf(sync_profile, "en-US")); +} + +TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) { + AutofillProfile sync_profile; + + autofill::test::SetProfileInfoWithGuid(&sync_profile, + "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", + "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", + "91601", "US", "12345678910"); + + std::string native_guid = "EDC609ED-7EEE-4F27-B00C-423242A9C44B"; + AutofillProfile* native_profile = new AutofillProfile; + autofill::test::SetProfileInfoWithGuid(native_profile, + native_guid.c_str(), "Billing", + "Mitchell", "Morrison", + "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", + "91601", "US", "12345678910"); + + std::vector<AutofillProfile*> native_profiles; + native_profiles.push_back(native_profile); + EXPECT_CALL(autofill_table(), GetAutofillProfiles(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); + + std::vector<AutofillProfile> sync_profiles; + sync_profiles.push_back(sync_profile); + AddAutofillHelper<AutofillProfile> add_autofill(this, sync_profiles); + + EXPECT_CALL(autofill_table(), AddAutofillProfile(_)).WillOnce(Return(true)); + EXPECT_CALL(autofill_table(), RemoveAutofillProfile(native_guid)) + .WillOnce(Return(true)); + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(add_autofill.callback(), false, AUTOFILL_PROFILE); + ASSERT_TRUE(add_autofill.success()); + + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( + &new_sync_profiles)); + ASSERT_EQ(1U, new_sync_profiles.size()); + EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0])); + EXPECT_EQ(sync_profile.guid(), new_sync_profiles[0].guid()); +} + +TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) { + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .WillOnce(Return(true)); + EXPECT_CALL(personal_data_manager(), Refresh()); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, AUTOFILL); + StartSyncService(create_root.callback(), false, AUTOFILL); + ASSERT_TRUE(create_root.success()); + + AutofillEntry added_entry(MakeAutofillEntry("added", "entry", 1)); + + EXPECT_CALL(autofill_table(), GetAutofillTimestamps(_, _, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(added_entry.date_created()), + SetArgumentPointee<3>(added_entry.date_last_used()), + Return(true))); + + AutofillChangeList changes; + changes.push_back(AutofillChange(AutofillChange::ADD, added_entry.key())); + + web_data_service()->OnAutofillEntriesChanged(changes); + + std::vector<AutofillEntry> new_sync_entries; + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(added_entry == new_sync_entries[0]); +} + +TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) { + EXPECT_CALL(autofill_table(), GetAutofillProfiles(_)).WillOnce(Return(true)); + EXPECT_CALL(personal_data_manager(), Refresh()); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, AUTOFILL_PROFILE); + StartSyncService(create_root.callback(), false, AUTOFILL_PROFILE); + ASSERT_TRUE(create_root.success()); + + AutofillProfile added_profile; + autofill::test::SetProfileInfoWithGuid(&added_profile, + "D6ADA912-D374-4C0A-917D-F5C8EBE43011", "Josephine", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", + "32801", "US", "19482937549"); + + AutofillProfileChange change( + AutofillProfileChange::ADD, added_profile.guid(), &added_profile); + web_data_service()->OnAutofillProfileChanged(change); + + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( + &new_sync_profiles)); + ASSERT_EQ(1U, new_sync_profiles.size()); + EXPECT_EQ(0, added_profile.Compare(new_sync_profiles[0])); +} + +TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) { + AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1)); + std::vector<AutofillEntry> original_entries; + original_entries.push_back(original_entry); + + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL(personal_data_manager(), Refresh()); + CreateRootHelper create_root(this, AUTOFILL); + StartSyncService(create_root.callback(), false, AUTOFILL); + ASSERT_TRUE(create_root.success()); + + AutofillEntry updated_entry(MakeAutofillEntry("my", "entry", 1, 2)); + + EXPECT_CALL(autofill_table(), GetAutofillTimestamps(_, _, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(updated_entry.date_created()), + SetArgumentPointee<3>(updated_entry.date_last_used()), + Return(true))); + + AutofillChangeList changes; + changes.push_back(AutofillChange(AutofillChange::UPDATE, + updated_entry.key())); + web_data_service()->OnAutofillEntriesChanged(changes); + + std::vector<AutofillEntry> new_sync_entries; + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(updated_entry == new_sync_entries[0]); +} + + +TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) { + AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1)); + std::vector<AutofillEntry> original_entries; + original_entries.push_back(original_entry); + + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL(personal_data_manager(), Refresh()); + CreateRootHelper create_root(this, AUTOFILL); + StartSyncService(create_root.callback(), false, AUTOFILL); + ASSERT_TRUE(create_root.success()); + + AutofillChangeList changes; + changes.push_back(AutofillChange(AutofillChange::REMOVE, + original_entry.key())); + web_data_service()->OnAutofillEntriesChanged(changes); + + std::vector<AutofillEntry> new_sync_entries; + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); + ASSERT_EQ(0U, new_sync_entries.size()); +} + +TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) { + AutofillProfile sync_profile; + autofill::test::SetProfileInfoWithGuid(&sync_profile, + "3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", + "32801", "US", "19482937549"); + AutofillProfile* native_profile = new AutofillProfile; + autofill::test::SetProfileInfoWithGuid(native_profile, + "3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz", + "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", + "32801", "US", "19482937549"); + + std::vector<AutofillProfile*> native_profiles; + native_profiles.push_back(native_profile); + EXPECT_CALL(autofill_table(), GetAutofillProfiles(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); + + std::vector<AutofillProfile> sync_profiles; + sync_profiles.push_back(sync_profile); + AddAutofillHelper<AutofillProfile> add_autofill(this, sync_profiles); + EXPECT_CALL(personal_data_manager(), Refresh()); + StartSyncService(add_autofill.callback(), false, AUTOFILL_PROFILE); + ASSERT_TRUE(add_autofill.success()); + + AutofillProfileChange change( + AutofillProfileChange::REMOVE, sync_profile.guid(), NULL); + web_data_service()->OnAutofillProfileChanged(change); + + std::vector<AutofillProfile> new_sync_profiles; + ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( + &new_sync_profiles)); + ASSERT_EQ(0U, new_sync_profiles.size()); +} + +TEST_F(ProfileSyncServiceAutofillTest, ServerChangeRace) { + // Once for MergeDataAndStartSyncing() and twice for ProcessSyncChanges(), via + // LoadAutofillData(). + EXPECT_CALL(autofill_table(), GetAllAutofillEntries(_)) + .Times(3) + .WillRepeatedly(Return(true)); + // On the other hand Autofill and Autocomplete are separated now, so + // GetAutofillProfiles() should not be called. + EXPECT_CALL(autofill_table(), GetAutofillProfiles(_)).Times(0); + EXPECT_CALL(autofill_table(), UpdateAutofillEntries(_)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(personal_data_manager(), Refresh()).Times(3); + CreateRootHelper create_root(this, AUTOFILL); + StartSyncService(create_root.callback(), false, AUTOFILL); + ASSERT_TRUE(create_root.success()); + + // (true, false) means we have to reset after |Signal|, init to unsignaled. + scoped_ptr<WaitableEvent> wait_for_start(new WaitableEvent(true, false)); + scoped_ptr<WaitableEvent> wait_for_syncapi(new WaitableEvent(true, false)); + scoped_refptr<FakeServerUpdater> updater(new FakeServerUpdater( + sync_service(), wait_for_start.get(), wait_for_syncapi.get(), + data_type_thread()->task_runner())); + + // This server side update will stall waiting for CommitWaiter. + updater->CreateNewEntry(MakeAutofillEntry("server", "entry", 1)); + wait_for_start->Wait(); + + AutofillEntry syncapi_entry(MakeAutofillEntry("syncapi", "entry", 2)); + ASSERT_TRUE(AddAutofillSyncNode(syncapi_entry)); + DVLOG(1) << "Syncapi update finished."; + + // If we reach here, it means syncapi succeeded and we didn't deadlock. Yay! + // Signal FakeServerUpdater that it can complete. + wait_for_syncapi->Signal(); + + // Make another entry to ensure nothing broke afterwards and wait for finish + // to clean up. + updater->WaitForUpdateCompletion(); + updater->CreateNewEntry(MakeAutofillEntry("server2", "entry2", 3)); + updater->WaitForUpdateCompletion(); + + // Let callbacks posted on UI thread execute. + base::RunLoop().RunUntilIdle(); + + std::vector<AutofillEntry> sync_entries; + std::vector<AutofillProfile> sync_profiles; + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); + EXPECT_EQ(3U, sync_entries.size()); + EXPECT_EQ(0U, sync_profiles.size()); + for (size_t i = 0; i < sync_entries.size(); i++) { + DVLOG(1) << "Entry " << i << ": " << sync_entries[i].key().name() + << ", " << sync_entries[i].key().value(); + } +}
diff --git a/components/browser_sync/browser/profile_sync_service_typed_url_unittest.cc b/components/browser_sync/browser/profile_sync_service_typed_url_unittest.cc new file mode 100644 index 0000000..4166a80 --- /dev/null +++ b/components/browser_sync/browser/profile_sync_service_typed_url_unittest.cc
@@ -0,0 +1,1087 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> +#include <stdint.h> + +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "components/browser_sync/browser/abstract_profile_sync_service_test.h" +#include "components/browser_sync/browser/test_profile_sync_service.h" +#include "components/history/core/browser/history_backend.h" +#include "components/history/core/browser/history_backend_client.h" +#include "components/history/core/browser/history_backend_notifier.h" +#include "components/history/core/browser/history_db_task.h" +#include "components/history/core/browser/history_service.h" +#include "components/history/core/browser/typed_url_data_type_controller.h" +#include "components/signin/core/browser/signin_manager.h" +#include "components/sync_driver/data_type_error_handler_mock.h" +#include "components/sync_driver/data_type_manager_impl.h" +#include "sync/internal_api/public/read_node.h" +#include "sync/internal_api/public/read_transaction.h" +#include "sync/internal_api/public/write_node.h" +#include "sync/internal_api/public/write_transaction.h" +#include "sync/protocol/typed_url_specifics.pb.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +using browser_sync::TypedUrlDataTypeController; +using history::HistoryBackend; +using history::HistoryBackendNotifier; +using history::TypedUrlSyncableService; +using testing::DoAll; +using testing::Return; +using testing::SetArgumentPointee; +using testing::_; + +namespace { + +const char kDummySavingBrowserHistoryDisabled[] = "dummyPref"; + +// Visits with this timestamp are treated as expired. +static const int EXPIRED_VISIT = -1; + +ACTION(ReturnNewDataTypeManager) { + return new sync_driver::DataTypeManagerImpl(arg0, arg1, arg2, arg3, arg4); +} + +class HistoryBackendMock : public HistoryBackend { + public: + HistoryBackendMock() + : HistoryBackend(nullptr, nullptr, base::ThreadTaskRunnerHandle::Get()) {} + bool IsExpiredVisitTime(const base::Time& time) override { + return time.ToInternalValue() == EXPIRED_VISIT; + } + MOCK_METHOD1(GetAllTypedURLs, bool(history::URLRows* entries)); + MOCK_METHOD3(GetMostRecentVisitsForURL, bool(history::URLID id, + int max_visits, + history::VisitVector* visits)); + MOCK_METHOD2(UpdateURL, bool(history::URLID id, const history::URLRow& url)); + MOCK_METHOD3(AddVisits, bool(const GURL& url, + const std::vector<history::VisitInfo>& visits, + history::VisitSource visit_source)); + MOCK_METHOD2(GetURL, bool(const GURL& url_id, history::URLRow* url_row)); + MOCK_METHOD2(SetPageTitle, void(const GURL& url, + const base::string16& title)); + MOCK_METHOD1(DeleteURL, void(const GURL& url)); + + private: + friend class ProfileSyncServiceTypedUrlTest; + + virtual ~HistoryBackendMock() {} +}; + +class HistoryServiceMock : public history::HistoryService { + public: + HistoryServiceMock() : history::HistoryService(), backend_(nullptr) {} + + base::CancelableTaskTracker::TaskId ScheduleDBTask( + scoped_ptr<history::HistoryDBTask> task, + base::CancelableTaskTracker* tracker) override { + // Explicitly copy out the raw pointer -- compilers might decide to + // evaluate task.release() before the arguments for the first Bind(). + history::HistoryDBTask* task_raw = task.get(); + task_runner_->PostTaskAndReply( + FROM_HERE, + base::Bind(&HistoryServiceMock::RunTaskOnDBThread, + base::Unretained(this), task_raw), + base::Bind(&base::DeletePointer<history::HistoryDBTask>, + task.release())); + return base::CancelableTaskTracker::kBadTaskId; // unused + } + + ~HistoryServiceMock() override {} + + void set_task_runner( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + DCHECK(task_runner.get()); + task_runner_ = task_runner; + } + + void set_backend(scoped_refptr<history::HistoryBackend> backend) { + backend_ = backend; + } + + private: + void RunTaskOnDBThread(history::HistoryDBTask* task) { + EXPECT_TRUE(task->RunOnDBThread(backend_.get(), NULL)); + } + + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + scoped_refptr<history::HistoryBackend> backend_; +}; + +class TestTypedUrlSyncableService : public TypedUrlSyncableService { + // TODO(gangwu): remove TestProfileSyncService or even remove whole test + // suite, and make sure typed_url_syncable_service_unittest.cc and the various + // typed url integration tests. + public: + explicit TestTypedUrlSyncableService(history::HistoryBackend* history_backend) + : TypedUrlSyncableService(history_backend) {} + + static void WriteToSyncNode(const history::URLRow& url, + const history::VisitVector& visits, + syncer::WriteNode* node) { + sync_pb::TypedUrlSpecifics typed_url; + WriteToTypedUrlSpecifics(url, visits, &typed_url); + node->SetTypedUrlSpecifics(typed_url); + } + + protected: + // Don't clear error stats - that way we can verify their values in our + // tests. + void ClearErrorStats() override {} +}; + +class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { + public: + void AddTypedUrlSyncNode(const history::URLRow& url, + const history::VisitVector& visits) { + syncer::WriteTransaction trans(FROM_HERE, sync_service()->GetUserShare()); + + syncer::WriteNode node(&trans); + std::string tag = url.url().spec(); + syncer::WriteNode::InitUniqueByCreationResult result = + node.InitUniqueByCreation(syncer::TYPED_URLS, tag); + ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result); + TestTypedUrlSyncableService::WriteToSyncNode(url, visits, &node); + } + + protected: + ProfileSyncServiceTypedUrlTest() { + profile_sync_service_bundle() + ->pref_service() + ->registry() + ->RegisterBooleanPref(kDummySavingBrowserHistoryDisabled, false); + + data_type_thread()->Start(); + base::RunLoop run_loop; + data_type_thread()->task_runner()->PostTaskAndReply( + FROM_HERE, + base::Bind(&ProfileSyncServiceTypedUrlTest::CreateHistoryService, + base::Unretained(this)), + run_loop.QuitClosure()); + run_loop.Run(); + history_service_ = make_scoped_ptr(new HistoryServiceMock); + history_service_->set_task_runner(data_type_thread()->task_runner()); + history_service_->set_backend(history_backend_); + + browser_sync::ProfileSyncServiceBundle::SyncClientBuilder builder( + profile_sync_service_bundle()); + builder.SetHistoryService(history_service_.get()); + builder.SetSyncServiceCallback(GetSyncServiceCallback()); + builder.SetSyncableServiceCallback( + base::Bind(&ProfileSyncServiceTypedUrlTest::GetSyncableServiceForType, + base::Unretained(this))); + builder.set_activate_model_creation(); + sync_client_ = builder.Build(); + } + + void CreateHistoryService() { + history_backend_ = new HistoryBackendMock(); + syncable_service_ = make_scoped_ptr( + new TestTypedUrlSyncableService(history_backend_.get())); + } + + void DeleteSyncableService() { + syncable_service_.reset(); + history_backend_ = nullptr; + } + + ~ProfileSyncServiceTypedUrlTest() override { + history_service_->Shutdown(); + + // Request stop to get deletion tasks related to the HistoryService posted + // on the Sync thread. It is important to not Shutdown at this moment, + // because after shutdown the Sync thread is not returned to the sync + // service, so we could not get the thread's message loop to wait for the + // deletions to be finished. + sync_service()->RequestStop(sync_driver::SyncService::CLEAR_DATA); + // Spin the sync thread. + { + base::RunLoop run_loop; + sync_service()->GetSyncLoopForTest()->task_runner()->PostTaskAndReply( + FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure()); + run_loop.Run(); + } + + // Spin the loop again for deletion tasks posted from the Sync thread. + base::RunLoop().RunUntilIdle(); + + { + base::RunLoop run_loop; + data_type_thread()->task_runner()->PostTaskAndReply( + FROM_HERE, + base::Bind(&ProfileSyncServiceTypedUrlTest::DeleteSyncableService, + base::Unretained(this)), + run_loop.QuitClosure()); + run_loop.Run(); + } + } + + TypedUrlSyncableService* StartSyncService(const base::Closure& callback) { + if (!sync_service()) { + std::string account_id = + profile_sync_service_bundle()->account_tracker()->SeedAccountInfo( + "gaia_id", "test"); + SigninManagerBase* signin = + profile_sync_service_bundle()->signin_manager(); + signin->SetAuthenticatedAccountInfo("gaia_id", "test"); + CreateSyncService(std::move(sync_client_), callback); + data_type_controller = new TypedUrlDataTypeController( + base::ThreadTaskRunnerHandle::Get(), base::Bind(&base::DoNothing), + sync_service()->GetSyncClient(), kDummySavingBrowserHistoryDisabled); + EXPECT_CALL(*profile_sync_service_bundle()->component_factory(), + CreateDataTypeManager(_, _, _, _, _)) + .WillOnce(ReturnNewDataTypeManager()); + + profile_sync_service_bundle()->auth_service()->UpdateCredentials( + account_id, "oauth2_login_token"); + + sync_service()->RegisterDataTypeController(data_type_controller); + + sync_service()->Initialize(); + base::RunLoop().Run(); + } + return syncable_service_.get(); + } + + void GetTypedUrlsFromSyncDB(history::URLRows* urls) { + urls->clear(); + syncer::ReadTransaction trans(FROM_HERE, sync_service()->GetUserShare()); + syncer::ReadNode typed_url_root(&trans); + if (typed_url_root.InitTypeRoot(syncer::TYPED_URLS) != + syncer::BaseNode::INIT_OK) + return; + + int64_t child_id = typed_url_root.GetFirstChildId(); + while (child_id != syncer::kInvalidId) { + syncer::ReadNode child_node(&trans); + if (child_node.InitByIdLookup(child_id) != syncer::BaseNode::INIT_OK) + return; + + const sync_pb::TypedUrlSpecifics& typed_url( + child_node.GetTypedUrlSpecifics()); + history::URLRow new_url(GURL(typed_url.url())); + + new_url.set_title(base::UTF8ToUTF16(typed_url.title())); + DCHECK(typed_url.visits_size()); + DCHECK_EQ(typed_url.visits_size(), typed_url.visit_transitions_size()); + new_url.set_last_visit(base::Time::FromInternalValue( + typed_url.visits(typed_url.visits_size() - 1))); + new_url.set_hidden(typed_url.hidden()); + + urls->push_back(new_url); + child_id = child_node.GetSuccessorId(); + } + } + + void SetIdleChangeProcessorExpectations() { + EXPECT_CALL((history_backend()), SetPageTitle(_, _)).Times(0); + EXPECT_CALL((history_backend()), UpdateURL(_, _)).Times(0); + EXPECT_CALL((history_backend()), GetURL(_, _)).Times(0); + EXPECT_CALL((history_backend()), DeleteURL(_)).Times(0); + } + + void SendNotification(const base::Closure& task) { + data_type_thread()->task_runner()->PostTaskAndReply( + FROM_HERE, task, + base::Bind(&base::MessageLoop::QuitNow, + base::Unretained(base::MessageLoop::current()))); + base::MessageLoop::current()->Run(); + } + + void SendNotificationURLVisited(ui::PageTransition transition, + const history::URLRow& row) { + base::Time visit_time; + history::RedirectList redirects; + SendNotification( + base::Bind(&HistoryBackendNotifier::NotifyURLVisited, + base::Unretained(history_backend_.get()), + transition, + row, + redirects, + visit_time)); + } + + void SendNotificationURLsModified(const history::URLRows& rows) { + SendNotification(base::Bind(&HistoryBackendNotifier::NotifyURLsModified, + base::Unretained(history_backend_.get()), + rows)); + } + + void SendNotificationURLsDeleted(bool all_history, + bool expired, + const history::URLRows& deleted_rows, + const std::set<GURL>& favicon_urls) { + SendNotification(base::Bind(&HistoryBackendNotifier::NotifyURLsDeleted, + base::Unretained(history_backend_.get()), + all_history, expired, deleted_rows, + favicon_urls)); + } + + static bool URLsEqual(const history::URLRow& lhs, + const history::URLRow& rhs) { + // Only verify the fields we explicitly sync (i.e. don't verify typed_count + // or visit_count because we rely on the history DB to manage those values + // and they are left unchanged by HistoryBackendMock). + return (lhs.url().spec().compare(rhs.url().spec()) == 0) && + (lhs.title().compare(rhs.title()) == 0) && + (lhs.last_visit() == rhs.last_visit()) && + (lhs.hidden() == rhs.hidden()); + } + + static history::URLRow MakeTypedUrlEntry(const char* url, + const char* title, + int typed_count, + int64_t last_visit, + bool hidden, + history::VisitVector* visits) { + // Give each URL a unique ID, to mimic the behavior of the real database. + static int unique_url_id = 0; + GURL gurl(url); + history::URLRow history_url(gurl, ++unique_url_id); + history_url.set_title(base::UTF8ToUTF16(title)); + history_url.set_typed_count(typed_count); + history_url.set_last_visit( + base::Time::FromInternalValue(last_visit)); + history_url.set_hidden(hidden); + visits->push_back(history::VisitRow( + history_url.id(), history_url.last_visit(), 0, + ui::PAGE_TRANSITION_TYPED, 0)); + history_url.set_visit_count(visits->size()); + return history_url; + } + + base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType( + syncer::ModelType type) { + DCHECK_EQ(syncer::TYPED_URLS, type); + return syncable_service_->AsWeakPtr(); + } + + HistoryBackendMock& history_backend() { return *history_backend_.get(); } + + private: + scoped_refptr<HistoryBackendMock> history_backend_; + scoped_ptr<HistoryServiceMock> history_service_; + sync_driver::DataTypeErrorHandlerMock error_handler_; + TypedUrlDataTypeController* data_type_controller; + scoped_ptr<TestTypedUrlSyncableService> syncable_service_; + scoped_ptr<sync_driver::FakeSyncClient> sync_client_; + + DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceTypedUrlTest); +}; + +void AddTypedUrlEntries(ProfileSyncServiceTypedUrlTest* test, + const history::URLRows& entries) { + test->CreateRoot(syncer::TYPED_URLS); + for (size_t i = 0; i < entries.size(); ++i) { + history::VisitVector visits; + visits.push_back(history::VisitRow( + entries[i].id(), entries[i].last_visit(), 0, + ui::PageTransitionFromInt(0), 0)); + test->AddTypedUrlSyncNode(entries[i], visits); + } +} + +} // namespace + +TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeEmptySync) { + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)).WillOnce(Return(true)); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + TypedUrlSyncableService* syncable_service = + StartSyncService(create_root.callback()); + history::URLRows sync_entries; + GetTypedUrlsFromSyncDB(&sync_entries); + EXPECT_EQ(0U, sync_entries.size()); + ASSERT_EQ(0, syncable_service->GetErrorPercentage()); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeEmptySync) { + history::URLRows entries; + history::VisitVector visits; + entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar", + 2, 15, false, &visits)); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true))); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + TypedUrlSyncableService* syncable_service = + StartSyncService(create_root.callback()); + history::URLRows sync_entries; + GetTypedUrlsFromSyncDB(&sync_entries); + ASSERT_EQ(1U, sync_entries.size()); + EXPECT_TRUE(URLsEqual(entries[0], sync_entries[0])); + ASSERT_EQ(0, syncable_service->GetErrorPercentage()); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeErrorReadingVisits) { + history::URLRows entries; + history::VisitVector visits; + history::URLRow native_entry1(MakeTypedUrlEntry("http://foo.com", "bar", + 2, 15, false, &visits)); + history::URLRow native_entry2(MakeTypedUrlEntry("http://foo2.com", "bar", + 3, 15, false, &visits)); + entries.push_back(native_entry1); + entries.push_back(native_entry2); + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); + // Return an error from GetMostRecentVisitsForURL() for the second URL. + EXPECT_CALL((history_backend()), + GetMostRecentVisitsForURL(native_entry1.id(), _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL((history_backend()), + GetMostRecentVisitsForURL(native_entry2.id(), _, _)) + .WillRepeatedly(Return(false)); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + history::URLRows sync_entries; + GetTypedUrlsFromSyncDB(&sync_entries); + ASSERT_EQ(1U, sync_entries.size()); + EXPECT_TRUE(URLsEqual(native_entry1, sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithBlankEmptySync) { + std::vector<history::URLRow> entries; + history::VisitVector visits; + // Add an empty URL. + entries.push_back(MakeTypedUrlEntry("", "bar", + 2, 15, false, &visits)); + entries.push_back(MakeTypedUrlEntry("http://foo.com", "bar", + 2, 15, false, &visits)); + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<2>(visits), Return(true))); + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + std::vector<history::URLRow> sync_entries; + GetTypedUrlsFromSyncDB(&sync_entries); + // The empty URL should be ignored. + ASSERT_EQ(1U, sync_entries.size()); + EXPECT_TRUE(URLsEqual(entries[1], sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncNoMerge) { + history::VisitVector native_visits; + history::VisitVector sync_visits; + history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", + 2, 15, false, &native_visits)); + history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", + 3, 16, false, &sync_visits)); + + history::URLRows native_entries; + native_entries.push_back(native_entry); + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(native_visits), Return(true))); + EXPECT_CALL((history_backend()), AddVisits(_, _, history::SOURCE_SYNCED)) + .WillRepeatedly(Return(true)); + + history::URLRows sync_entries; + sync_entries.push_back(sync_entry); + + EXPECT_CALL((history_backend()), UpdateURL(_, _)) + .WillRepeatedly(Return(true)); + StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); + + std::map<std::string, history::URLRow> expected; + expected[native_entry.url().spec()] = native_entry; + expected[sync_entry.url().spec()] = sync_entry; + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + + EXPECT_TRUE(new_sync_entries.size() == expected.size()); + for (history::URLRows::iterator entry = new_sync_entries.begin(); + entry != new_sync_entries.end(); ++entry) { + EXPECT_TRUE(URLsEqual(expected[entry->url().spec()], *entry)); + } +} + +TEST_F(ProfileSyncServiceTypedUrlTest, EmptyNativeExpiredSync) { + history::VisitVector sync_visits; + history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", + 3, EXPIRED_VISIT, false, + &sync_visits)); + history::URLRows sync_entries; + sync_entries.push_back(sync_entry); + + // Since all our URLs are expired, no backend calls to add new URLs will be + // made. + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)).WillOnce(Return(true)); + SetIdleChangeProcessorExpectations(); + + StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeHasSyncMerge) { + history::VisitVector native_visits; + history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", + 2, 15, false, &native_visits)); + history::VisitVector sync_visits; + history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "name", + 1, 17, false, &sync_visits)); + history::VisitVector merged_visits; + merged_visits.push_back(history::VisitRow( + sync_entry.id(), base::Time::FromInternalValue(15), 0, + ui::PageTransitionFromInt(0), 0)); + + history::URLRow merged_entry(MakeTypedUrlEntry("http://native.com", "name", + 2, 17, false, &merged_visits)); + + history::URLRows native_entries; + native_entries.push_back(native_entry); + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(native_visits), Return(true))); + EXPECT_CALL((history_backend()), AddVisits(_, _, history::SOURCE_SYNCED)) + .WillRepeatedly(Return(true)); + + history::URLRows sync_entries; + sync_entries.push_back(sync_entry); + + EXPECT_CALL((history_backend()), UpdateURL(_, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL((history_backend()), SetPageTitle(_, _)).WillRepeatedly(Return()); + StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(merged_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, HasNativeWithErrorHasSyncMerge) { + history::VisitVector native_visits; + history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "native", + 2, 15, false, &native_visits)); + history::VisitVector sync_visits; + history::URLRow sync_entry(MakeTypedUrlEntry("http://native.com", "sync", + 1, 17, false, &sync_visits)); + + history::URLRows native_entries; + native_entries.push_back(native_entry); + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); + // Return an error getting the visits for the native URL. + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL((history_backend()), GetURL(_, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<1>(native_entry), Return(true))); + EXPECT_CALL((history_backend()), AddVisits(_, _, history::SOURCE_SYNCED)) + .WillRepeatedly(Return(true)); + + history::URLRows sync_entries; + sync_entries.push_back(sync_entry); + + EXPECT_CALL((history_backend()), UpdateURL(_, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL((history_backend()), SetPageTitle(_, _)).WillRepeatedly(Return()); + StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(sync_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAdd) { + history::VisitVector added_visits; + history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", + 2, 15, false, &added_visits)); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)).WillOnce(Return(true)); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); + + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::URLRows changed_urls; + changed_urls.push_back(added_entry); + SendNotificationURLsModified(changed_urls); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddWithBlank) { + history::VisitVector added_visits; + history::URLRow empty_entry(MakeTypedUrlEntry("", "entry", + 2, 15, false, &added_visits)); + history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", + 2, 15, false, &added_visits)); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)).WillOnce(Return(true)); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); + + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::URLRows changed_urls; + changed_urls.push_back(empty_entry); + changed_urls.push_back(added_entry); + SendNotificationURLsModified(changed_urls); + + std::vector<history::URLRow> new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdate) { + history::VisitVector original_visits; + history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", + 2, 15, false, + &original_visits)); + history::URLRows original_entries; + original_entries.push_back(original_entry); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(original_visits), Return(true))); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::VisitVector updated_visits; + history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", + 7, 17, false, + &updated_visits)); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(updated_visits), Return(true))); + + history::URLRows changed_urls; + changed_urls.push_back(updated_entry); + SendNotificationURLsModified(changed_urls); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeAddFromVisit) { + history::VisitVector added_visits; + history::URLRow added_entry(MakeTypedUrlEntry("http://added.com", "entry", + 2, 15, false, &added_visits)); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)).WillOnce(Return(true)); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(added_visits), Return(true))); + + SetIdleChangeProcessorExpectations(); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + SendNotificationURLVisited(ui::PAGE_TRANSITION_TYPED, added_entry); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(added_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeUpdateFromVisit) { + history::VisitVector original_visits; + history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", + 2, 15, false, + &original_visits)); + history::URLRows original_entries; + original_entries.push_back(original_entry); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(original_visits), Return(true))); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::VisitVector updated_visits; + history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", + 7, 17, false, + &updated_visits)); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(updated_visits), Return(true))); + + SendNotificationURLVisited(ui::PAGE_TRANSITION_TYPED, updated_entry); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(updated_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserIgnoreChangeUpdateFromVisit) { + history::VisitVector original_visits; + history::URLRow original_entry(MakeTypedUrlEntry("http://mine.com", "entry", + 2, 15, false, + &original_visits)); + history::URLRows original_entries; + original_entries.push_back(original_entry); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(original_visits), Return(true))); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); + + history::VisitVector updated_visits; + history::URLRow updated_entry(MakeTypedUrlEntry("http://mine.com", "entry", + 7, 15, false, + &updated_visits)); + + // Should ignore this change because it's not TYPED. + SendNotificationURLVisited(ui::PAGE_TRANSITION_RELOAD, updated_entry); + GetTypedUrlsFromSyncDB(&new_sync_entries); + + // Should be no changes to the sync DB from this notification. + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); + + // Now, try updating it with a large number of visits not divisible by 10 + // (should ignore this visit). + history::URLRow twelve_visits(MakeTypedUrlEntry("http://mine.com", "entry", + 12, 15, false, + &updated_visits)); + SendNotificationURLVisited(ui::PAGE_TRANSITION_TYPED, twelve_visits); + GetTypedUrlsFromSyncDB(&new_sync_entries); + + // Should be no changes to the sync DB from this notification. + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(original_entry, new_sync_entries[0])); + + // Now, try updating it with a large number of visits that is divisible by 10 + // (should *not* be ignored). + history::URLRow twenty_visits(MakeTypedUrlEntry("http://mine.com", "entry", + 20, 15, false, + &updated_visits)); + SendNotificationURLVisited(ui::PAGE_TRANSITION_TYPED, twenty_visits); + GetTypedUrlsFromSyncDB(&new_sync_entries); + + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(twenty_visits, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemove) { + history::VisitVector original_visits1; + history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", + 2, 15, false, + &original_visits1)); + history::VisitVector original_visits2; + history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", + "entry2", + 3, 15, false, + &original_visits2)); + history::URLRows original_entries; + original_entries.push_back(original_entry1); + original_entries.push_back(original_entry2); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(original_visits1), Return(true))); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::URLRows rows; + rows.push_back(history::URLRow(GURL("http://mine.com"))); + SendNotificationURLsDeleted(false, false, rows, std::set<GURL>()); + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(original_entry2, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveExpired) { + history::VisitVector original_visits1; + history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", + 2, 15, false, + &original_visits1)); + history::VisitVector original_visits2; + history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", + "entry2", + 3, 15, false, + &original_visits2)); + history::URLRows original_entries; + original_entries.push_back(original_entry1); + original_entries.push_back(original_entry2); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(original_visits1), Return(true))); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + // Setting expired=true should cause the sync code to ignore this deletion. + history::URLRows rows; + rows.push_back(history::URLRow(GURL("http://mine.com"))); + SendNotificationURLsDeleted(false, true, rows, std::set<GURL>()); + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + // Both URLs should still be there. + ASSERT_EQ(2U, new_sync_entries.size()); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, ProcessUserChangeRemoveAll) { + history::VisitVector original_visits1; + history::URLRow original_entry1(MakeTypedUrlEntry("http://mine.com", "entry", + 2, 15, false, + &original_visits1)); + history::VisitVector original_visits2; + history::URLRow original_entry2(MakeTypedUrlEntry("http://mine2.com", + "entry2", + 3, 15, false, + &original_visits2)); + history::URLRows original_entries; + original_entries.push_back(original_entry1); + original_entries.push_back(original_entry2); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(original_visits1), Return(true))); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(2U, new_sync_entries.size()); + + SendNotificationURLsDeleted(true, false, history::URLRows(), + std::set<GURL>()); + + GetTypedUrlsFromSyncDB(&new_sync_entries); + ASSERT_EQ(0U, new_sync_entries.size()); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, FailWriteToHistoryBackend) { + history::VisitVector native_visits; + history::VisitVector sync_visits; + history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", + 2, 15, false, &native_visits)); + history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", + 3, 16, false, &sync_visits)); + + history::URLRows native_entries; + native_entries.push_back(native_entry); + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); + EXPECT_CALL((history_backend()), GetURL(_, _)) + .WillOnce(DoAll(SetArgumentPointee<1>(native_entry), Return(false))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(native_visits), Return(true))); + EXPECT_CALL((history_backend()), AddVisits(_, _, history::SOURCE_SYNCED)) + .WillRepeatedly(Return(false)); + + history::URLRows sync_entries; + sync_entries.push_back(sync_entry); + + EXPECT_CALL((history_backend()), UpdateURL(_, _)) + .WillRepeatedly(Return(false)); + TypedUrlSyncableService* syncable_service = + StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); + // Errors writing to the DB should be recorded, but should not cause an + // unrecoverable error. + ASSERT_FALSE(sync_service()->data_type_status_table().GetFailedTypes().Has( + syncer::TYPED_URLS)); + // Some calls should have succeeded, so the error percentage should be + // somewhere > 0 and < 100. + ASSERT_NE(0, syncable_service->GetErrorPercentage()); + ASSERT_NE(100, syncable_service->GetErrorPercentage()); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, FailToGetTypedURLs) { + history::VisitVector native_visits; + history::VisitVector sync_visits; + history::URLRow native_entry(MakeTypedUrlEntry("http://native.com", "entry", + 2, 15, false, &native_visits)); + history::URLRow sync_entry(MakeTypedUrlEntry("http://sync.com", "entry", + 3, 16, false, &sync_visits)); + + history::URLRows native_entries; + native_entries.push_back(native_entry); + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(false))); + + history::URLRows sync_entries; + sync_entries.push_back(sync_entry); + + StartSyncService(base::Bind(&AddTypedUrlEntries, this, sync_entries)); + // Errors getting typed URLs will cause an unrecoverable error (since we can + // do *nothing* in that case). + ASSERT_TRUE(sync_service()->data_type_status_table().GetFailedTypes().Has( + syncer::TYPED_URLS)); + ASSERT_EQ(1u, + sync_service()->data_type_status_table().GetFailedTypes().Size()); + // Can't check GetErrorPercentage(), because generating an unrecoverable + // error will free the model associator. +} + +TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalFileURL) { + history::VisitVector original_visits; + // Create http and file url. + history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com", + "yey", 12, 15, false, + &original_visits)); + history::URLRow file_entry(MakeTypedUrlEntry("file:///kitty.jpg", + "kitteh", 12, 15, false, + &original_visits)); + + history::URLRows original_entries; + original_entries.push_back(url_entry); + original_entries.push_back(file_entry); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillRepeatedly( + DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(original_visits), Return(true))); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::VisitVector updated_visits; + // Create updates for the previous urls + a new file one. + history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com", + "yey", 20, 15, false, + &updated_visits)); + history::URLRow updated_file_entry(MakeTypedUrlEntry("file:///cat.jpg", + "cat", 20, 15, false, + &updated_visits)); + history::URLRow new_file_entry(MakeTypedUrlEntry("file:///dog.jpg", + "dog", 20, 15, false, + &updated_visits)); + + history::URLRows changed_urls; + changed_urls.push_back(updated_url_entry); + changed_urls.push_back(updated_file_entry); + changed_urls.push_back(new_file_entry); + SendNotificationURLsModified(changed_urls); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + + // We should ignore the local file urls (existing and updated), + // and only be left with the updated http url. + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreLocalhostURL) { + history::VisitVector original_visits; + // Create http and localhost url. + history::URLRow url_entry(MakeTypedUrlEntry("http://yey.com", + "yey", 12, 15, false, + &original_visits)); + history::URLRow localhost_entry(MakeTypedUrlEntry("http://localhost", + "localhost", 12, 15, false, + &original_visits)); + + history::URLRows original_entries; + original_entries.push_back(url_entry); + original_entries.push_back(localhost_entry); + + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillRepeatedly( + DoAll(SetArgumentPointee<0>(original_entries), Return(true))); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly( + DoAll(SetArgumentPointee<2>(original_visits), Return(true))); + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::VisitVector updated_visits; + // Update the previous entries and add a new localhost. + history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com", + "yey", 20, 15, false, + &updated_visits)); + history::URLRow updated_localhost_entry(MakeTypedUrlEntry( + "http://localhost:80", + "localhost", 20, 15, false, + &original_visits)); + history::URLRow localhost_ip_entry(MakeTypedUrlEntry("http://127.0.0.1", + "localhost", 12, 15, false, + &original_visits)); + + history::URLRows changed_urls; + changed_urls.push_back(updated_url_entry); + changed_urls.push_back(updated_localhost_entry); + changed_urls.push_back(localhost_ip_entry); + SendNotificationURLsModified(changed_urls); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + + // We should ignore the localhost urls and left only with http url. + ASSERT_EQ(1U, new_sync_entries.size()); + EXPECT_TRUE(URLsEqual(updated_url_entry, new_sync_entries[0])); +} + +TEST_F(ProfileSyncServiceTypedUrlTest, IgnoreModificationWithoutValidVisit) { + EXPECT_CALL((history_backend()), GetAllTypedURLs(_)) + .WillRepeatedly(Return(true)); + EXPECT_CALL((history_backend()), GetMostRecentVisitsForURL(_, _, _)) + .WillRepeatedly(Return(true)); + + CreateRootHelper create_root(this, syncer::TYPED_URLS); + StartSyncService(create_root.callback()); + + history::VisitVector updated_visits; + history::URLRow updated_url_entry(MakeTypedUrlEntry("http://yey.com", + "yey", 20, 0, false, + &updated_visits)); + + history::URLRows changed_urls; + changed_urls.push_back(updated_url_entry); + SendNotificationURLsModified(changed_urls); + + history::URLRows new_sync_entries; + GetTypedUrlsFromSyncDB(&new_sync_entries); + + // The change should be ignored. + ASSERT_EQ(0U, new_sync_entries.size()); +}
diff --git a/components/browser_sync/browser/profile_sync_test_util.cc b/components/browser_sync/browser/profile_sync_test_util.cc index c8c02d5..cb6c543 100644 --- a/components/browser_sync/browser/profile_sync_test_util.cc +++ b/components/browser_sync/browser/profile_sync_test_util.cc
@@ -4,11 +4,15 @@ #include "components/browser_sync/browser/profile_sync_test_util.h" +#include "components/history/core/browser/history_model_worker.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/signin/core/browser/signin_manager_base.h" +#include "components/sync_driver/glue/browser_thread_model_worker.h" +#include "components/sync_driver/glue/ui_model_worker.h" #include "components/sync_driver/signin_manager_wrapper.h" #include "components/sync_driver/sync_prefs.h" #include "net/url_request/url_request_test_util.h" +#include "sync/internal_api/public/engine/passive_model_worker.h" using sync_driver::ClearBrowsingDataCallback; @@ -18,32 +22,75 @@ class BundleSyncClient : public sync_driver::FakeSyncClient { public: - BundleSyncClient(sync_driver::SyncApiComponentFactory* factory, - PrefService* pref_service, - ClearBrowsingDataCallback clear_browsing_data_callback, - sync_sessions::SyncSessionsClient* sync_sessions_client); + BundleSyncClient( + sync_driver::SyncApiComponentFactory* factory, + PrefService* pref_service, + const ClearBrowsingDataCallback& clear_browsing_data_callback, + sync_sessions::SyncSessionsClient* sync_sessions_client, + autofill::PersonalDataManager* personal_data_manager, + const base::Callback<base::WeakPtr<syncer::SyncableService>( + syncer::ModelType type)>& get_syncable_service_callback, + const base::Callback<sync_driver::SyncService*(void)>& + get_sync_service_callback, + scoped_refptr<base::SingleThreadTaskRunner> db_thread, + scoped_refptr<base::SingleThreadTaskRunner> file_thread, + history::HistoryService* history_service); ~BundleSyncClient() override; PrefService* GetPrefService() override; ClearBrowsingDataCallback GetClearBrowsingDataCallback() override; sync_sessions::SyncSessionsClient* GetSyncSessionsClient() override; + autofill::PersonalDataManager* GetPersonalDataManager() override; + base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType( + syncer::ModelType type) override; + sync_driver::SyncService* GetSyncService() override; + scoped_refptr<syncer::ModelSafeWorker> CreateModelWorkerForGroup( + syncer::ModelSafeGroup group, + syncer::WorkerLoopDestructionObserver* observer) override; + history::HistoryService* GetHistoryService() override; private: PrefService* const pref_service_; const ClearBrowsingDataCallback clear_browsing_data_callback_; sync_sessions::SyncSessionsClient* const sync_sessions_client_; + autofill::PersonalDataManager* const personal_data_manager_; + const base::Callback<base::WeakPtr<syncer::SyncableService>( + syncer::ModelType type)> + get_syncable_service_callback_; + const base::Callback<sync_driver::SyncService*(void)> + get_sync_service_callback_; + // These task runners, if not null, are used in CreateModelWorkerForGroup. + const scoped_refptr<base::SingleThreadTaskRunner> db_thread_; + const scoped_refptr<base::SingleThreadTaskRunner> file_thread_; + history::HistoryService* history_service_; }; BundleSyncClient::BundleSyncClient( sync_driver::SyncApiComponentFactory* factory, PrefService* pref_service, - ClearBrowsingDataCallback clear_browsing_data_callback, - sync_sessions::SyncSessionsClient* sync_sessions_client) + const ClearBrowsingDataCallback& clear_browsing_data_callback, + sync_sessions::SyncSessionsClient* sync_sessions_client, + autofill::PersonalDataManager* personal_data_manager, + const base::Callback<base::WeakPtr<syncer::SyncableService>( + syncer::ModelType type)>& get_syncable_service_callback, + const base::Callback<sync_driver::SyncService*(void)>& + get_sync_service_callback, + scoped_refptr<base::SingleThreadTaskRunner> db_thread, + scoped_refptr<base::SingleThreadTaskRunner> file_thread, + history::HistoryService* history_service) : sync_driver::FakeSyncClient(factory), pref_service_(pref_service), clear_browsing_data_callback_(clear_browsing_data_callback), - sync_sessions_client_(sync_sessions_client) {} + sync_sessions_client_(sync_sessions_client), + personal_data_manager_(personal_data_manager), + get_syncable_service_callback_(get_syncable_service_callback), + get_sync_service_callback_(get_sync_service_callback), + db_thread_(db_thread), + file_thread_(file_thread), + history_service_(history_service) { + DCHECK_EQ(!!db_thread_, !!file_thread_); +} BundleSyncClient::~BundleSyncClient() = default; @@ -59,6 +106,60 @@ return sync_sessions_client_; } +autofill::PersonalDataManager* BundleSyncClient::GetPersonalDataManager() { + return personal_data_manager_; +} + +base::WeakPtr<syncer::SyncableService> +BundleSyncClient::GetSyncableServiceForType(syncer::ModelType type) { + if (get_syncable_service_callback_.is_null()) + return sync_driver::FakeSyncClient::GetSyncableServiceForType(type); + return get_syncable_service_callback_.Run(type); +} + +sync_driver::SyncService* BundleSyncClient::GetSyncService() { + if (get_sync_service_callback_.is_null()) + return sync_driver::FakeSyncClient::GetSyncService(); + return get_sync_service_callback_.Run(); +} + +scoped_refptr<syncer::ModelSafeWorker> +BundleSyncClient::CreateModelWorkerForGroup( + syncer::ModelSafeGroup group, + syncer::WorkerLoopDestructionObserver* observer) { + if (!db_thread_) + return FakeSyncClient::CreateModelWorkerForGroup(group, observer); + DCHECK(file_thread_) << "DB thread was specified but FILE thread was not."; + switch (group) { + case syncer::GROUP_DB: + return new BrowserThreadModelWorker(db_thread_, syncer::GROUP_DB, + observer); + case syncer::GROUP_FILE: + return new BrowserThreadModelWorker(file_thread_, syncer::GROUP_FILE, + observer); + case syncer::GROUP_UI: + return new UIModelWorker(base::ThreadTaskRunnerHandle::Get(), observer); + case syncer::GROUP_PASSIVE: + return new syncer::PassiveModelWorker(observer); + case syncer::GROUP_HISTORY: { + history::HistoryService* history_service = GetHistoryService(); + if (!history_service) + return nullptr; + return new HistoryModelWorker(history_service->AsWeakPtr(), + base::ThreadTaskRunnerHandle::Get(), + observer); + } + default: + return nullptr; + } +} + +history::HistoryService* BundleSyncClient::GetHistoryService() { + if (history_service_) + return history_service_; + return FakeSyncClient::GetHistoryService(); +} + } // namespace void EmptyNetworkTimeUpdate(const base::Time&, @@ -84,15 +185,45 @@ clear_browsing_data_callback_ = clear_browsing_data_callback; } +void ProfileSyncServiceBundle::SyncClientBuilder::SetPersonalDataManager( + autofill::PersonalDataManager* personal_data_manager) { + personal_data_manager_ = personal_data_manager; +} + +// The client will call this callback to produce the service. +void ProfileSyncServiceBundle::SyncClientBuilder::SetSyncableServiceCallback( + const base::Callback<base::WeakPtr<syncer::SyncableService>( + syncer::ModelType type)>& get_syncable_service_callback) { + get_syncable_service_callback_ = get_syncable_service_callback; +} + +// The client will call this callback to produce the service. +void ProfileSyncServiceBundle::SyncClientBuilder::SetSyncServiceCallback( + const base::Callback<sync_driver::SyncService*(void)>& + get_sync_service_callback) { + get_sync_service_callback_ = get_sync_service_callback; +} + +void ProfileSyncServiceBundle::SyncClientBuilder::SetHistoryService( + history::HistoryService* history_service) { + history_service_ = history_service; +} + scoped_ptr<sync_driver::FakeSyncClient> ProfileSyncServiceBundle::SyncClientBuilder::Build() { return make_scoped_ptr(new BundleSyncClient( bundle_->component_factory(), bundle_->pref_service(), - clear_browsing_data_callback_, bundle_->sync_sessions_client())); + clear_browsing_data_callback_, bundle_->sync_sessions_client(), + personal_data_manager_, get_syncable_service_callback_, + get_sync_service_callback_, + activate_model_creation_ ? bundle_->db_thread() : nullptr, + activate_model_creation_ ? base::ThreadTaskRunnerHandle::Get() : nullptr, + history_service_)); } ProfileSyncServiceBundle::ProfileSyncServiceBundle() - : worker_pool_owner_(2, "sync test worker pool"), + : db_thread_(base::ThreadTaskRunnerHandle::Get()), + worker_pool_owner_(2, "sync test worker pool"), signin_client_(&pref_service_), #if defined(OS_CHROMEOS) signin_manager_(&signin_client_, &account_tracker_), @@ -128,7 +259,7 @@ init_params.url_request_context = url_request_context(); init_params.debug_identifier = "dummyDebugName"; init_params.channel = version_info::Channel::UNKNOWN; - init_params.db_thread = base::ThreadTaskRunnerHandle::Get(); + init_params.db_thread = db_thread_; init_params.file_thread = base::ThreadTaskRunnerHandle::Get(); init_params.blocking_pool = worker_pool_owner_.pool().get();
diff --git a/components/browser_sync/browser/profile_sync_test_util.h b/components/browser_sync/browser/profile_sync_test_util.h index b6bf4e7cc..4a55cafe7 100644 --- a/components/browser_sync/browser/profile_sync_test_util.h +++ b/components/browser_sync/browser/profile_sync_test_util.h
@@ -5,12 +5,15 @@ #ifndef COMPONENTS_BROWSER_SYNC_BROWSER_PROFILE_SYNC_TEST_UTIL_H_ #define COMPONENTS_BROWSER_SYNC_BROWSER_PROFILE_SYNC_TEST_UTIL_H_ +#include "base/callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" #include "base/test/sequenced_worker_pool_owner.h" #include "base/time/time.h" #include "components/browser_sync/browser/profile_sync_service.h" +#include "components/invalidation/impl/fake_invalidation_service.h" #include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" #include "components/signin/core/browser/fake_signin_manager.h" @@ -25,6 +28,10 @@ class TimeDelta; } +namespace history { +class HistoryService; +} + namespace net { class URLRequestContextGetter; } @@ -46,7 +53,8 @@ user_prefs::PrefRegistrySyncable* registry); // Aggregate this class to get all necessary support for creating a -// ProfileSyncService in tests. +// ProfileSyncService in tests. The test still needs to have its own +// MessageLoop, though. class ProfileSyncServiceBundle { public: #if defined(OS_CHROMEOS) @@ -56,6 +64,7 @@ #endif ProfileSyncServiceBundle(); + ~ProfileSyncServiceBundle(); // Builders @@ -70,10 +79,28 @@ ~SyncClientBuilder(); - // Set the clear browsing data callback for the client to return. + // Setters for the various additional data for the client to return. void SetClearBrowsingDataCallback( sync_driver::ClearBrowsingDataCallback clear_browsing_data_callback); + void SetPersonalDataManager( + autofill::PersonalDataManager* personal_data_manager); + + // The client will call this callback to produce the SyncableService + // specific to |type|. + void SetSyncableServiceCallback( + const base::Callback<base::WeakPtr<syncer::SyncableService>( + syncer::ModelType type)>& get_syncable_service_callback); + + // The client will call this callback to produce the SyncService for the + // current Profile. + void SetSyncServiceCallback(const base::Callback<sync_driver::SyncService*( + void)>& get_sync_service_callback); + + void SetHistoryService(history::HistoryService* history_service); + + void set_activate_model_creation() { activate_model_creation_ = true; } + scoped_ptr<sync_driver::FakeSyncClient> Build(); private: @@ -81,6 +108,15 @@ ProfileSyncServiceBundle* const bundle_; sync_driver::ClearBrowsingDataCallback clear_browsing_data_callback_; + autofill::PersonalDataManager* personal_data_manager_; + base::Callback<base::WeakPtr<syncer::SyncableService>( + syncer::ModelType type)> + get_syncable_service_callback_; + base::Callback<sync_driver::SyncService*(void)> get_sync_service_callback_; + history::HistoryService* history_service_ = nullptr; + // If set, the built client will be able to build some ModelSafeWorker + // instances. + bool activate_model_creation_ = false; DISALLOW_COPY_AND_ASSIGN(SyncClientBuilder); }; @@ -116,7 +152,19 @@ return &sync_sessions_client_; } + invalidation::FakeInvalidationService* fake_invalidation_service() { + return &fake_invalidation_service_; + } + + base::SingleThreadTaskRunner* db_thread() { return db_thread_.get(); } + + void set_db_thread( + const scoped_refptr<base::SingleThreadTaskRunner>& db_thread) { + db_thread_ = db_thread; + } + private: + scoped_refptr<base::SingleThreadTaskRunner> db_thread_; base::SequencedWorkerPoolOwner worker_pool_owner_; syncable_prefs::TestingPrefServiceSyncable pref_service_; TestSigninClient signin_client_; @@ -125,6 +173,7 @@ FakeProfileOAuth2TokenService auth_service_; SyncApiComponentFactoryMock component_factory_; sync_sessions::FakeSyncSessionsClient sync_sessions_client_; + invalidation::FakeInvalidationService fake_invalidation_service_; scoped_refptr<net::URLRequestContextGetter> url_request_context_; DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceBundle);
diff --git a/components/browser_sync/browser/test_http_bridge_factory.cc b/components/browser_sync/browser/test_http_bridge_factory.cc new file mode 100644 index 0000000..b38bb6e7 --- /dev/null +++ b/components/browser_sync/browser/test_http_bridge_factory.cc
@@ -0,0 +1,46 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/browser_sync/browser/test_http_bridge_factory.h" + +namespace browser_sync { + +bool TestHttpBridge::MakeSynchronousPost(int* error_code, + int* response_code) { + return false; +} + +int TestHttpBridge::GetResponseContentLength() const { + return 0; +} + +const char* TestHttpBridge::GetResponseContent() const { + return 0; +} + +const std::string TestHttpBridge::GetResponseHeaderValue( + const std::string &) const { + return std::string(); +} + +void TestHttpBridge::Abort() { +} + +TestHttpBridgeFactory::TestHttpBridgeFactory() {} + +TestHttpBridgeFactory::~TestHttpBridgeFactory() {} + +void TestHttpBridgeFactory::Init( + const std::string& user_agent, + const syncer::BindToTrackerCallback& bind_to_tracker_callback) {} + +syncer::HttpPostProviderInterface* TestHttpBridgeFactory::Create() { + return new TestHttpBridge(); +} + +void TestHttpBridgeFactory::Destroy(syncer::HttpPostProviderInterface* http) { + delete static_cast<TestHttpBridge*>(http); +} + +} // namespace browser_sync
diff --git a/components/browser_sync/browser/test_http_bridge_factory.h b/components/browser_sync/browser/test_http_bridge_factory.h new file mode 100644 index 0000000..b4c2608 --- /dev/null +++ b/components/browser_sync/browser/test_http_bridge_factory.h
@@ -0,0 +1,52 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_BROWSER_SYNC_BROWSER_TEST_HTTP_BRIDGE_FACTORY_H_ +#define COMPONENTS_BROWSER_SYNC_BROWSER_TEST_HTTP_BRIDGE_FACTORY_H_ + +#include "base/compiler_specific.h" +#include "sync/internal_api/public/http_post_provider_factory.h" +#include "sync/internal_api/public/http_post_provider_interface.h" + +namespace browser_sync { + +class TestHttpBridge : public syncer::HttpPostProviderInterface { + public: + // Begin syncer::HttpPostProviderInterface implementation: + void SetExtraRequestHeaders(const char* headers) override {} + + void SetURL(const char* url, int port) override {} + + void SetPostPayload(const char* content_type, + int content_length, + const char* content) override {} + + bool MakeSynchronousPost(int* error_code, int* response_code) override; + + int GetResponseContentLength() const override; + + const char* GetResponseContent() const override; + + const std::string GetResponseHeaderValue(const std::string&) const override; + + void Abort() override; + // End syncer::HttpPostProviderInterface implementation. +}; + +class TestHttpBridgeFactory : public syncer::HttpPostProviderFactory { + public: + TestHttpBridgeFactory(); + ~TestHttpBridgeFactory() override; + + // syncer::HttpPostProviderFactory: + void Init( + const std::string& user_agent, + const syncer::BindToTrackerCallback& bind_to_tracker_callback) override; + syncer::HttpPostProviderInterface* Create() override; + void Destroy(syncer::HttpPostProviderInterface* http) override; +}; + +} // namespace browser_sync + +#endif // COMPONENTS_BROWSER_SYNC_BROWSER_TEST_HTTP_BRIDGE_FACTORY_H_
diff --git a/components/browser_sync/browser/test_profile_sync_service.cc b/components/browser_sync/browser/test_profile_sync_service.cc new file mode 100644 index 0000000..092fdfe --- /dev/null +++ b/components/browser_sync/browser/test_profile_sync_service.cc
@@ -0,0 +1,36 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/browser_sync/browser/test_profile_sync_service.h" + +#include <utility> + +syncer::TestIdFactory* TestProfileSyncService::id_factory() { + return &id_factory_; +} + +syncer::WeakHandle<syncer::JsEventHandler> +TestProfileSyncService::GetJsEventHandler() { + return syncer::WeakHandle<syncer::JsEventHandler>(); +} + +TestProfileSyncService::TestProfileSyncService( + ProfileSyncService::InitParams init_params) + : ProfileSyncService(std::move(init_params)) {} + +TestProfileSyncService::~TestProfileSyncService() {} + +void TestProfileSyncService::OnConfigureDone( + const sync_driver::DataTypeManager::ConfigureResult& result) { + ProfileSyncService::OnConfigureDone(result); + base::MessageLoop::current()->QuitWhenIdle(); +} + +syncer::UserShare* TestProfileSyncService::GetUserShare() const { + return backend_->GetUserShare(); +} + +bool TestProfileSyncService::NeedBackup() const { + return false; +}
diff --git a/components/browser_sync/browser/test_profile_sync_service.h b/components/browser_sync/browser/test_profile_sync_service.h new file mode 100644 index 0000000..54b0c37a --- /dev/null +++ b/components/browser_sync/browser/test_profile_sync_service.h
@@ -0,0 +1,52 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_BROWSER_SYNC_BROWSER_TEST_PROFILE_SYNC_SERVICE_H_ +#define COMPONENTS_BROWSER_SYNC_BROWSER_TEST_PROFILE_SYNC_SERVICE_H_ + +#include "base/macros.h" +#include "components/browser_sync/browser/profile_sync_service.h" +#include "components/sync_driver/data_type_manager.h" +#include "sync/internal_api/public/util/weak_handle.h" +#include "sync/js/js_event_handler.h" +#include "sync/test/engine/test_id_factory.h" + +namespace sync_driver { +class SyncPrefs; +} // namespace sync_driver + +class TestProfileSyncService : public ProfileSyncService { + public: + explicit TestProfileSyncService(InitParams init_params); + + ~TestProfileSyncService() override; + + void OnConfigureDone( + const sync_driver::DataTypeManager::ConfigureResult& result) override; + + // We implement our own version to avoid some DCHECKs. + syncer::UserShare* GetUserShare() const override; + + syncer::TestIdFactory* id_factory(); + + // Raise visibility to ease testing. + using ProfileSyncService::NotifyObservers; + + sync_driver::SyncPrefs* sync_prefs() { return &sync_prefs_; } + + protected: + // Return NULL handle to use in backend initialization to avoid receiving + // js messages on UI loop when it's being destroyed, which are not deleted + // and cause memory leak in test. + syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler() override; + + bool NeedBackup() const override; + + private: + syncer::TestIdFactory id_factory_; + + DISALLOW_COPY_AND_ASSIGN(TestProfileSyncService); +}; + +#endif // COMPONENTS_BROWSER_SYNC_BROWSER_TEST_PROFILE_SYNC_SERVICE_H_
diff --git a/components/components_strings.grd b/components/components_strings.grd index 9685630..a99cf79 100644 --- a/components/components_strings.grd +++ b/components/components_strings.grd
@@ -197,6 +197,7 @@ <part file="flags_ui_strings.grdp" /> <part file="history_strings.grdp" /> <part file="login_dialog_strings.grdp" /> + <part file="new_or_sad_tab_strings.grdp" /> <part file="omnibox_strings.grdp" /> <part file="pageinfo_strings.grdp" /> <part file="password_manager_strings.grdp" /> @@ -273,6 +274,16 @@ <message name="IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON" desc="Title of the restore button in the session crashed view."> Restore </message> + + <!-- Website Settings UI --> + <message name="IDS_WEBSITE_SETTINGS_NON_SECURE_TRANSPORT" desc="Text that is displayed in the header of the Website Settings popup if the website uses non-secure transport."> + Your connection to this site is not private. + </message> + + <!-- Advanced Section Titles --> + <message name="IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY"> + Privacy + </message> </messages> </release> </grit>
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 9ce4ac0..db3b5420 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp
@@ -76,8 +76,10 @@ 'bookmarks/managed/managed_bookmarks_tracker_unittest.cc', ], 'browser_sync_unittest_sources': [ + 'browser_sync/browser/profile_sync_service_autofill_unittest.cc', 'browser_sync/browser/profile_sync_service_startup_unittest.cc', 'browser_sync/browser/profile_sync_service_unittest.cc', + 'browser_sync/browser/profile_sync_service_typed_url_unittest.cc', ], 'browser_watcher_unittest_sources': [ 'browser_watcher/crash_reporting_metrics_win_unittest.cc', @@ -289,7 +291,6 @@ 'history/core/browser/url_database_unittest.cc', 'history/core/browser/url_utils_unittest.cc', 'history/core/browser/visit_database_unittest.cc', - 'history/core/browser/visit_filter_unittest.cc', 'history/core/browser/visit_tracker_unittest.cc', 'history/core/browser/web_history_service_unittest.cc', 'history/core/common/thumbnail_score_unittest.cc', @@ -349,6 +350,7 @@ 'metrics/cloned_install_detector_unittest.cc', 'metrics/daily_event_unittest.cc', 'metrics/drive_metrics_provider_unittest.cc', + 'metrics/file_metrics_provider_unittest.cc', 'metrics/histogram_encoder_unittest.cc', 'metrics/machine_id_provider_win_unittest.cc', 'metrics/metrics_log_manager_unittest.cc', @@ -1106,6 +1108,7 @@ 'components.gyp:ssl_errors', 'components.gyp:suggestions', 'components.gyp:sync_bookmarks', + 'components.gyp:sync_driver', 'components.gyp:sync_driver_features', 'components.gyp:sync_driver_test_support', 'components.gyp:sync_sessions', @@ -1126,6 +1129,7 @@ 'components.gyp:variations_net', 'components.gyp:variations_service', 'components.gyp:version_info', + 'components.gyp:webdata_services_test_support', 'components.gyp:web_resource', 'components.gyp:web_resource_test_support', 'components_resources.gyp:components_resources',
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn index 34b4cbb..64c3fd2 100644 --- a/components/cronet/android/BUILD.gn +++ b/components/cronet/android/BUILD.gn
@@ -550,6 +550,7 @@ "test/javatests/src/org/chromium/net/TestHttpUrlRequestListener.java", "test/javatests/src/org/chromium/net/TestUploadDataProvider.java", "test/javatests/src/org/chromium/net/TestUrlRequestCallback.java", + "test/javatests/src/org/chromium/net/UploadDataProvidersTest.java", "test/javatests/src/org/chromium/net/UploadTest.java", "test/javatests/src/org/chromium/net/UrlResponseInfoTest.java", "test/javatests/src/org/chromium/net/urlconnection/CronetBufferedOutputStreamTest.java",
diff --git a/components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java b/components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java new file mode 100644 index 0000000..317bc32 --- /dev/null +++ b/components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java
@@ -0,0 +1,172 @@ +// 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. + +package org.chromium.net; + +import android.os.ParcelFileDescriptor; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/** + * Provides implementations of {@link UploadDataProvider} for common use cases. + */ +public final class UploadDataProviders { + /** + * Uploads an entire file. + * + * @param file The file to upload + */ + public static UploadDataProvider create(final File file) { + return new FileUploadProvider(new FileChannelProvider() { + @Override + public FileChannel getChannel() throws IOException { + return new FileInputStream(file).getChannel(); + } + }); + } + + /** + * Uploads an entire file, closing the descriptor when it is no longer needed. + * + * @param fd The file descriptor to upload + * @throws IllegalArgumentException if {@code fd} is not a file. + */ + public static UploadDataProvider create(final ParcelFileDescriptor fd) { + return new FileUploadProvider(new FileChannelProvider() { + @Override + public FileChannel getChannel() throws IOException { + if (fd.getStatSize() != -1) { + return new ParcelFileDescriptor.AutoCloseInputStream(fd).getChannel(); + } else { + fd.close(); + throw new IllegalArgumentException("Not a file: " + fd); + } + } + }); + } + + /** + * Uploads a ByteBuffer, from the current {@code buffer.position()} to {@code buffer.limit()} + */ + public static UploadDataProvider create(ByteBuffer buffer) { + return new ByteBufferUploadProvider(buffer.slice()); + } + + /** Uploads {@code length} bytes from {@code data}, starting from {@code offset} */ + public static UploadDataProvider create(byte[] data, int offset, int length) { + return new ByteBufferUploadProvider(ByteBuffer.wrap(data, offset, length)); + } + + /** Uploads the contents of {@code data} */ + public static UploadDataProvider create(byte[] data) { + return create(data, 0, data.length); + } + + private interface FileChannelProvider { FileChannel getChannel() throws IOException; } + + private static final class FileUploadProvider extends UploadDataProvider { + private volatile FileChannel mChannel; + private final FileChannelProvider mProvider; + /** Guards initalization of {@code mChannel} */ + private final Object mLock = new Object(); + + private FileUploadProvider(FileChannelProvider provider) { + this.mProvider = provider; + } + + @Override + public long getLength() throws IOException { + return getChannel().size(); + } + + @Override + public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) throws IOException { + if (!byteBuffer.hasRemaining()) { + throw new IllegalStateException("Cronet passed a buffer with no bytes remaining"); + } + FileChannel channel = getChannel(); + int bytesRead = 0; + while (bytesRead == 0) { + int read = channel.read(byteBuffer); + if (read == -1) { + break; + } else { + bytesRead += read; + } + } + uploadDataSink.onReadSucceeded(false); + } + + @Override + public void rewind(UploadDataSink uploadDataSink) throws IOException { + getChannel().position(0); + uploadDataSink.onRewindSucceeded(); + } + + /** + * Lazily initializes the channel so that a blocking operation isn't performed on + * a non-executor thread. + */ + private FileChannel getChannel() throws IOException { + if (mChannel == null) { + synchronized (mLock) { + if (mChannel == null) { + mChannel = mProvider.getChannel(); + } + } + } + return mChannel; + } + + @Override + public void close() throws IOException { + FileChannel channel = mChannel; + if (channel != null) { + channel.close(); + } + } + } + + private static final class ByteBufferUploadProvider extends UploadDataProvider { + private final ByteBuffer mUploadBuffer; + + private ByteBufferUploadProvider(ByteBuffer uploadBuffer) { + this.mUploadBuffer = uploadBuffer; + } + + @Override + public long getLength() { + return mUploadBuffer.limit(); + } + + @Override + public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) { + if (!byteBuffer.hasRemaining()) { + throw new IllegalStateException("Cronet passed a buffer with no bytes remaining"); + } + if (byteBuffer.remaining() >= mUploadBuffer.remaining()) { + byteBuffer.put(mUploadBuffer); + } else { + int oldLimit = mUploadBuffer.limit(); + mUploadBuffer.limit(mUploadBuffer.position() + byteBuffer.remaining()); + byteBuffer.put(mUploadBuffer); + mUploadBuffer.limit(oldLimit); + } + uploadDataSink.onReadSucceeded(false); + } + + @Override + public void rewind(UploadDataSink uploadDataSink) { + mUploadBuffer.position(0); + uploadDataSink.onRewindSucceeded(); + } + } + + // Prevent instantiation + private UploadDataProviders() {} +}
diff --git a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java index e32d829..52cfc9b 100644 --- a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java +++ b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java
@@ -16,8 +16,7 @@ import org.chromium.base.Log; import org.chromium.net.CronetEngine; -import org.chromium.net.UploadDataProvider; -import org.chromium.net.UploadDataSink; +import org.chromium.net.UploadDataProviders; import org.chromium.net.UrlRequest; import org.chromium.net.UrlRequestException; import org.chromium.net.UrlResponseInfo; @@ -108,41 +107,6 @@ } } - static class SimpleUploadDataProvider extends UploadDataProvider { - private byte[] mUploadData; - private int mOffset; - - SimpleUploadDataProvider(byte[] uploadData) { - mUploadData = uploadData; - mOffset = 0; - } - - @Override - public long getLength() { - return mUploadData.length; - } - - @Override - public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer) - throws IOException { - if (byteBuffer.remaining() >= mUploadData.length - mOffset) { - byteBuffer.put(mUploadData, mOffset, mUploadData.length - mOffset); - mOffset = mUploadData.length; - } else { - int length = byteBuffer.remaining(); - byteBuffer.put(mUploadData, mOffset, length); - mOffset += length; - } - uploadDataSink.onReadSucceeded(false); - } - - @Override - public void rewind(final UploadDataSink uploadDataSink) throws IOException { - mOffset = 0; - uploadDataSink.onRewindSucceeded(); - } - } - @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -189,11 +153,10 @@ private void applyPostDataToUrlRequestBuilder( UrlRequest.Builder builder, Executor executor, String postData) { if (postData != null && postData.length() > 0) { - UploadDataProvider uploadDataProvider = - new SimpleUploadDataProvider(postData.getBytes()); builder.setHttpMethod("POST"); builder.addHeader("Content-Type", "application/x-www-form-urlencoded"); - builder.setUploadDataProvider(uploadDataProvider, executor); + builder.setUploadDataProvider( + UploadDataProviders.create(postData.getBytes()), executor); } }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java new file mode 100644 index 0000000..c03ba844 --- /dev/null +++ b/components/cronet/android/test/javatests/src/org/chromium/net/UploadDataProvidersTest.java
@@ -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. + +package org.chromium.net; + +import android.os.ParcelFileDescriptor; +import android.os.StrictMode; +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.annotations.SuppressFBWarnings; +import org.chromium.base.test.util.Feature; + +import java.io.File; +import java.io.FileOutputStream; + +/** Test the default provided implementations of {@link UploadDataProvider} */ +public class UploadDataProvidersTest extends CronetTestBase { + private static final String LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + + "Proin elementum, libero laoreet fringilla faucibus, metus tortor vehicula ante, " + + "lacinia lorem eros vel sapien."; + private CronetTestFramework mTestFramework; + private File mFile; + private StrictMode.VmPolicy mOldVmPolicy; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mOldVmPolicy = StrictMode.getVmPolicy(); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() + .detectLeakedClosableObjects() + .penaltyLog() + .penaltyDeath() + .build()); + mTestFramework = startCronetTestFramework(); + assertTrue(NativeTestServer.startNativeTestServer(getContext())); + // Add url interceptors after native application context is initialized. + MockUrlRequestJobFactory.setUp(); + mFile = new File(getContext().getCacheDir().getPath() + "/tmpfile"); + FileOutputStream fileOutputStream = new FileOutputStream(mFile); + try { + fileOutputStream.write(LOREM.getBytes("UTF-8")); + } finally { + fileOutputStream.close(); + } + } + + @SuppressFBWarnings("DM_GC") // Used to trigger strictmode detecting leaked closeables + @Override + protected void tearDown() throws Exception { + try { + NativeTestServer.shutdownNativeTestServer(); + mTestFramework.mCronetEngine.shutdown(); + assertTrue(mFile.delete()); + // Run GC and finalizers a few times to pick up leaked closeables + for (int i = 0; i < 10; i++) { + System.gc(); + System.runFinalization(); + } + System.gc(); + System.runFinalization(); + super.tearDown(); + } finally { + StrictMode.setVmPolicy(mOldVmPolicy); + } + } + + @SmallTest + @Feature({"Cronet"}) + public void testFileProvider() throws Exception { + TestUrlRequestCallback callback = new TestUrlRequestCallback(); + UrlRequest.Builder builder = + new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), callback, + callback.getExecutor(), mTestFramework.mCronetEngine); + UploadDataProvider dataProvider = UploadDataProviders.create(mFile); + builder.setUploadDataProvider(dataProvider, callback.getExecutor()); + builder.addHeader("Content-Type", "useless/string"); + builder.build().start(); + callback.blockForDone(); + assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); + assertEquals(LOREM, callback.mResponseAsString); + } + + @SmallTest + @Feature({"Cronet"}) + public void testFileDescriptorProvider() throws Exception { + ParcelFileDescriptor descriptor = + ParcelFileDescriptor.open(mFile, ParcelFileDescriptor.MODE_READ_ONLY); + assertTrue(descriptor.getFileDescriptor().valid()); + TestUrlRequestCallback callback = new TestUrlRequestCallback(); + UrlRequest.Builder builder = + new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), callback, + callback.getExecutor(), mTestFramework.mCronetEngine); + UploadDataProvider dataProvider = UploadDataProviders.create(descriptor); + builder.setUploadDataProvider(dataProvider, callback.getExecutor()); + builder.addHeader("Content-Type", "useless/string"); + builder.build().start(); + callback.blockForDone(); + assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); + assertEquals(LOREM, callback.mResponseAsString); + } + + @SmallTest + @Feature({"Cronet"}) + public void testBadFileDescriptorProvider() throws Exception { + TestUrlRequestCallback callback = new TestUrlRequestCallback(); + UrlRequest.Builder builder = + new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), callback, + callback.getExecutor(), mTestFramework.mCronetEngine); + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); + try { + UploadDataProvider dataProvider = UploadDataProviders.create(pipe[0]); + builder.setUploadDataProvider(dataProvider, callback.getExecutor()); + builder.addHeader("Content-Type", "useless/string"); + builder.build().start(); + callback.blockForDone(); + + assertTrue(callback.mError.getCause() instanceof IllegalArgumentException); + } finally { + pipe[1].close(); + } + } + + @SmallTest + @Feature({"Cronet"}) + public void testBufferProvider() throws Exception { + TestUrlRequestCallback callback = new TestUrlRequestCallback(); + UrlRequest.Builder builder = + new UrlRequest.Builder(NativeTestServer.getRedirectToEchoBody(), callback, + callback.getExecutor(), mTestFramework.mCronetEngine); + UploadDataProvider dataProvider = UploadDataProviders.create(LOREM.getBytes("UTF-8")); + builder.setUploadDataProvider(dataProvider, callback.getExecutor()); + builder.addHeader("Content-Type", "useless/string"); + builder.build().start(); + callback.blockForDone(); + + assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); + assertEquals(LOREM, callback.mResponseAsString); + } +}
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc index 96d8647e..a3bb98e1 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -62,6 +62,12 @@ const char kUMAConfigServiceAuthExpired[] = "DataReductionProxy.ConfigService.AuthExpired"; +#if defined(OS_ANDROID) +// Maximum duration to wait before fetching the config, while the application +// is in background. +const uint32_t kMaxBackgroundFetchIntervalSeconds = 6 * 60 * 60; // 6 hours. +#endif + #if defined(USE_GOOGLE_API_KEYS) // Used in all Data Reduction Proxy URLs to specify API Key. const char kApiKeyName[] = "key"; @@ -156,6 +162,9 @@ enabled_(false), remote_config_applied_(false), url_request_context_getter_(nullptr), +#if defined(OS_ANDROID) + foreground_fetch_pending_(false), +#endif previous_request_failed_authentication_(false), failed_attempts_before_success_(0) { DCHECK(request_options); @@ -177,8 +186,21 @@ DataReductionProxyConfigServiceClient::CalculateNextConfigRefreshTime( bool fetch_succeeded, const base::TimeDelta& config_expiration_delta, - const base::TimeDelta& backoff_delay) const { + const base::TimeDelta& backoff_delay) { DCHECK(backoff_delay >= base::TimeDelta()); + +#if defined(OS_ANDROID) + foreground_fetch_pending_ = false; + if (!fetch_succeeded && IsApplicationStateBackground()) { + // If Chromium is in background, then fetch the config when Chromium comes + // to foreground or after max of |kMaxBackgroundFetchIntervalSeconds| and + // |backoff_delay|. + foreground_fetch_pending_ = true; + return std::max(backoff_delay, base::TimeDelta::FromSeconds( + kMaxBackgroundFetchIntervalSeconds)); + } +#endif + if (fetch_succeeded) { return std::max(backoff_delay, config_expiration_delta); } @@ -189,6 +211,14 @@ void DataReductionProxyConfigServiceClient::InitializeOnIOThread( net::URLRequestContextGetter* url_request_context_getter) { DCHECK(url_request_context_getter); +#if defined(OS_ANDROID) + // It is okay to use Unretained here because |app_status_listener| would be + // destroyed before |this|. + app_status_listener_.reset( + new base::android::ApplicationStatusListener(base::Bind( + &DataReductionProxyConfigServiceClient::OnApplicationStateChange, + base::Unretained(this)))); +#endif net::NetworkChangeNotifier::AddIPAddressObserver(this); url_request_context_getter_ = url_request_context_getter; } @@ -418,6 +448,7 @@ GetBackoffEntry()->InformOfRequest(succeeded); base::TimeDelta next_config_refresh_time = CalculateNextConfigRefreshTime( succeeded, refresh_duration, GetBackoffEntry()->GetTimeUntilRelease()); + SetConfigRefreshTimer(next_config_refresh_time); event_creator_->EndConfigRequest(bound_net_log_, status.error(), response_code, @@ -462,4 +493,22 @@ return true; } +#if defined(OS_ANDROID) +bool DataReductionProxyConfigServiceClient::IsApplicationStateBackground() + const { + return base::android::ApplicationStatusListener::GetState() != + base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES; +} + +void DataReductionProxyConfigServiceClient::OnApplicationStateChange( + base::android::ApplicationState new_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (new_state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES && + foreground_fetch_pending_) { + foreground_fetch_pending_ = false; + RetrieveConfig(); + } +} +#endif + } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h index 2024a26..3124c6c 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
@@ -22,6 +22,10 @@ #include "net/url_request/url_fetcher_delegate.h" #include "url/gurl.h" +#if defined(OS_ANDROID) +#include "base/android/application_status_listener.h" +#endif // OS_ANDROID + namespace net { class HostPortPair; class HttpRequestHeaders; @@ -110,6 +114,12 @@ // configuration. void SetConfigRefreshTimer(const base::TimeDelta& delay); +#if defined(OS_ANDROID) + // Returns true if Chromium is in background. + // Virtualized for mocking. + virtual bool IsApplicationStateBackground() const; +#endif + private: friend class TestDataReductionProxyConfigServiceClient; @@ -118,7 +128,7 @@ base::TimeDelta CalculateNextConfigRefreshTime( bool fetch_succeeded, const base::TimeDelta& config_expiration, - const base::TimeDelta& backoff_delay) const; + const base::TimeDelta& backoff_delay); // Override of net::NetworkChangeNotifier::IPAddressObserver. void OnIPAddressChanged() override; @@ -153,6 +163,12 @@ // parsed and applied. bool ParseAndApplyProxyConfig(const ClientConfig& config); +#if defined(OS_ANDROID) + // Listens to when Chromium comes to foreground and fetches new client config + // if the config fetch is pending. + void OnApplicationStateChange(base::android::ApplicationState new_state); +#endif + scoped_ptr<DataReductionProxyParams> params_; // The caller must ensure that the |request_options_| outlives this instance. @@ -203,6 +219,16 @@ // configuration. base::TimeTicks config_fetch_start_time_; +#if defined(OS_ANDROID) + // Listens to the application transitions from foreground to background or + // vice versa. + scoped_ptr<base::android::ApplicationStatusListener> app_status_listener_; + + // True if config needs to be fetched when the application comes to + // foreground. + bool foreground_fetch_pending_; +#endif + // Keeps track of whether the previous request to a Data Reduction Proxy // failed to authenticate. This is necessary in the situation where a new // configuration with a bad session key is obtained, but the previous request
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc index e0e678e..f67548f5 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" +#include "base/run_loop.h" #include "base/test/histogram_tester.h" #include "base/test/mock_entropy_provider.h" #include "base/time/time.h" @@ -57,7 +58,13 @@ const char kPersistedSessionKey[] = "PersistedSessionKey"; // Duration (in seconds) after which the config should be refreshed. -const int kConfingRefreshDurationSeconds = 600; +const int kConfigRefreshDurationSeconds = 600; + +#if defined(OS_ANDROID) +// Maximum duration to wait before fetching the config, while the application +// is in background. +const uint32_t kMaxBackgroundFetchIntervalSeconds = 6 * 60 * 60; // 6 hours. +#endif } // namespace @@ -143,20 +150,20 @@ // Set up the various test ClientConfigs. ClientConfig config = - CreateConfig(kSuccessSessionKey, kConfingRefreshDurationSeconds, 0, + CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0, ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer_ProxyScheme_HTTP, "fallback.net", 80); config.SerializeToString(&config_); encoded_config_ = EncodeConfig(config); ClientConfig previous_config = - CreateConfig(kOldSuccessSessionKey, kConfingRefreshDurationSeconds, 0, + CreateConfig(kOldSuccessSessionKey, kConfigRefreshDurationSeconds, 0, ProxyServer_ProxyScheme_HTTPS, "old.origin.net", 443, ProxyServer_ProxyScheme_HTTP, "old.fallback.net", 80); previous_config.SerializeToString(&previous_config_); ClientConfig persisted = - CreateConfig(kPersistedSessionKey, kConfingRefreshDurationSeconds, 0, + CreateConfig(kPersistedSessionKey, kConfigRefreshDurationSeconds, 0, ProxyServer_ProxyScheme_HTTPS, "persisted.net", 443, ProxyServer_ProxyScheme_HTTP, "persisted.net", 80); loaded_config_ = EncodeConfig(persisted); @@ -189,7 +196,7 @@ kSuccessOrigin, net::ProxyServer::SCHEME_HTTP)); expected_http_proxies.push_back(net::ProxyServer::FromURI( kSuccessFallback, net::ProxyServer::SCHEME_HTTP)); - EXPECT_EQ(base::TimeDelta::FromSeconds(kConfingRefreshDurationSeconds), + EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), config_client()->GetDelay()); EXPECT_THAT(configurator()->proxies_for_http(), testing::ContainerEq(expected_http_proxies)); @@ -205,7 +212,7 @@ kOldSuccessOrigin, net::ProxyServer::SCHEME_HTTP)); expected_http_proxies.push_back(net::ProxyServer::FromURI( kOldSuccessFallback, net::ProxyServer::SCHEME_HTTP)); - EXPECT_EQ(base::TimeDelta::FromSeconds(kConfingRefreshDurationSeconds), + EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), config_client()->GetDelay()); EXPECT_THAT(configurator()->proxies_for_http(), testing::ContainerEq(expected_http_proxies)); @@ -386,7 +393,7 @@ config_client()->RetrieveConfig(); RunUntilIdle(); - EXPECT_EQ(base::TimeDelta::FromSeconds(kConfingRefreshDurationSeconds), + EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), config_client()->GetDelay()) << i; @@ -449,6 +456,10 @@ EXPECT_TRUE(configurator()->proxies_for_https().empty()); EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); +#if defined(OS_ANDROID) + EXPECT_FALSE(config_client()->foreground_fetch_pending()); +#endif + // Second attempt should be unsuccessful and backoff time should increase. config_client()->RetrieveConfig(); RunUntilIdle(); @@ -457,6 +468,10 @@ EXPECT_EQ(base::TimeDelta::FromSeconds(40), config_client()->GetDelay()); EXPECT_TRUE(persisted_config().empty()); +#if defined(OS_ANDROID) + EXPECT_FALSE(config_client()->foreground_fetch_pending()); +#endif + EXPECT_EQ(2, config_client()->failed_attempts_before_success()); histogram_tester.ExpectTotalCount( "DataReductionProxy.ConfigService.FetchFailedAttemptsBeforeSuccess", 0); @@ -472,6 +487,9 @@ config_client()->RetrieveConfig(); RunUntilIdle(); VerifyRemoteSuccess(); +#if defined(OS_ANDROID) + EXPECT_FALSE(config_client()->foreground_fetch_pending()); +#endif } // Tests that the config is read successfully on the second attempt. @@ -750,7 +768,7 @@ test_url_request_context()->CreateRequest(GURL(tests[i].url), net::IDLE, &test_delegate)); request->Start(); - RunUntilIdle(); + base::RunLoop().RunUntilIdle(); histogram_tester.ExpectTotalCount( "DataReductionProxy.ConfigService.HTTPRequests", @@ -821,4 +839,80 @@ EXPECT_FALSE(request_options()->GetSecureSession().empty()); } +#if defined(OS_ANDROID) +// Verifies the correctness of fetching config when Chromium is in background +// and foreground. +TEST_F(DataReductionProxyConfigServiceClientTest, FetchConfigOnForeground) { + Init(true); + SetDataReductionProxyEnabled(true); + + { + // Tests that successful config fetches while Chromium is in background, + // does not trigger refetches when Chromium comes to foreground. + base::HistogramTester histogram_tester; + AddMockSuccess(); + config_client()->set_application_state_background(true); + config_client()->RetrieveConfig(); + RunUntilIdle(); + VerifyRemoteSuccess(); + EXPECT_FALSE(config_client()->foreground_fetch_pending()); + histogram_tester.ExpectTotalCount( + "DataReductionProxy.ConfigService.FetchLatency", 1); + EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), + config_client()->GetDelay()); + config_client()->set_application_state_background(false); + config_client()->TriggerApplicationStatusToForeground(); + RunUntilIdle(); + EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), + config_client()->GetDelay()); + histogram_tester.ExpectTotalCount( + "DataReductionProxy.ConfigService.FetchLatency", 1); + } + + { + // Tests that config fetch failures while Chromium is in foreground does not + // trigger refetches when Chromium comes to foreground again. + base::HistogramTester histogram_tester; + AddMockFailure(); + config_client()->set_application_state_background(false); + config_client()->RetrieveConfig(); + RunUntilIdle(); + EXPECT_FALSE(config_client()->foreground_fetch_pending()); + histogram_tester.ExpectTotalCount( + "DataReductionProxy.ConfigService.FetchLatency", 0); + EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); + config_client()->TriggerApplicationStatusToForeground(); + RunUntilIdle(); + histogram_tester.ExpectTotalCount( + "DataReductionProxy.ConfigService.FetchLatency", 0); + EXPECT_EQ(base::TimeDelta::FromSeconds(20), config_client()->GetDelay()); + } + + { + // Tests that config fetch failures while Chromium is in background, trigger + // a refetch when Chromium comes to foreground. + base::HistogramTester histogram_tester; + AddMockFailure(); + AddMockSuccess(); + config_client()->set_application_state_background(true); + config_client()->RetrieveConfig(); + RunUntilIdle(); + EXPECT_TRUE(config_client()->foreground_fetch_pending()); + histogram_tester.ExpectTotalCount( + "DataReductionProxy.ConfigService.FetchLatency", 0); + EXPECT_EQ(base::TimeDelta::FromSeconds(kMaxBackgroundFetchIntervalSeconds), + config_client()->GetDelay()); + config_client()->set_application_state_background(false); + config_client()->TriggerApplicationStatusToForeground(); + RunUntilIdle(); + EXPECT_FALSE(config_client()->foreground_fetch_pending()); + histogram_tester.ExpectTotalCount( + "DataReductionProxy.ConfigService.FetchLatency", 1); + EXPECT_EQ(base::TimeDelta::FromSeconds(kConfigRefreshDurationSeconds), + config_client()->GetDelay()); + VerifyRemoteSuccess(); + } +} +#endif + } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc index cc72d68..d1a7a72 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -110,8 +110,12 @@ event_creator, net_log, config_storer), +#if defined(OS_ANDROID) + is_application_state_background_(false), +#endif tick_clock_(base::Time::UnixEpoch()), - test_backoff_entry_(&kTestBackoffPolicy, &tick_clock_) {} + test_backoff_entry_(&kTestBackoffPolicy, &tick_clock_) { +} TestDataReductionProxyConfigServiceClient:: ~TestDataReductionProxyConfigServiceClient() { @@ -174,6 +178,19 @@ time_ = time; } +#if defined(OS_ANDROID) +bool TestDataReductionProxyConfigServiceClient::IsApplicationStateBackground() + const { + return is_application_state_background_; +} + +void TestDataReductionProxyConfigServiceClient:: + TriggerApplicationStatusToForeground() { + OnApplicationStateChange( + base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); +} +#endif + MockDataReductionProxyService::MockDataReductionProxyService( DataReductionProxySettings* settings, PrefService* prefs,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h index 1e3594b..c10d794 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -121,6 +121,19 @@ int32_t failed_attempts_before_success() const; +#if defined(OS_ANDROID) + bool IsApplicationStateBackground() const override; + + void set_application_state_background(bool new_state) { + is_application_state_background_ = new_state; + } + + bool foreground_fetch_pending() const { return foreground_fetch_pending_; } + + // Triggers the callback for Chromium status change to foreground. + void TriggerApplicationStatusToForeground(); +#endif + protected: // Overrides of DataReductionProxyConfigServiceClient base::Time Now() override; @@ -145,6 +158,10 @@ base::Time time_; }; +#if defined(OS_ANDROID) + bool is_application_state_background_; +#endif + TestTickClock tick_clock_; net::BackoffEntry test_backoff_entry_; };
diff --git a/components/dom_distiller/content/renderer/distillability_agent.cc b/components/dom_distiller/content/renderer/distillability_agent.cc index 99592ef0..93e3736 100644 --- a/components/dom_distiller/content/renderer/distillability_agent.cc +++ b/components/dom_distiller/content/renderer/distillability_agent.cc
@@ -23,6 +23,10 @@ namespace { +const char* const kBlacklist[] = { + "www.reddit.com" +}; + // Returns whether it is necessary to send updates back to the browser. // The number of updates can be from 0 to 2. See the tests in // "distillable_page_utils_browsertest.cc". @@ -50,15 +54,25 @@ return true; } +bool IsBlacklisted(const GURL& url) { + for (size_t i = 0; i < arraysize(kBlacklist); ++i) { + if (base::LowerCaseEqualsASCII(url.host(), kBlacklist[i])) { + return true; + } + } + return false; +} + bool IsDistillablePageAdaboost(WebDocument& doc, const DistillablePageDetector* detector, + const DistillablePageDetector* long_page, bool is_last) { WebDistillabilityFeatures features = doc.distillabilityFeatures(); GURL parsed_url(doc.url()); if (!parsed_url.is_valid()) { return false; } - bool distillable = detector->Classify(CalculateDerivedFeatures( + std::vector<double> derived = CalculateDerivedFeatures( features.openGraph, parsed_url, features.elementCount, @@ -67,7 +81,10 @@ features.mozScore, features.mozScoreAllSqrt, features.mozScoreAllLinear - )); + ); + bool distillable = detector->Classify(derived); + bool long_article = long_page->Classify(derived); + bool blacklisted = IsBlacklisted(parsed_url); int bucket = static_cast<unsigned>(features.isMobileFriendly) | (static_cast<unsigned>(distillable) << 1); @@ -78,7 +95,14 @@ UMA_HISTOGRAM_ENUMERATION("DomDistiller.PageDistillableAfterParsing", bucket, 4); } - return distillable && (!features.isMobileFriendly); + + if (blacklisted) { + return false; + } + if (features.isMobileFriendly) { + return false; + } + return distillable && long_article; } bool IsDistillablePage(WebDocument& doc, bool is_last) { @@ -88,8 +112,9 @@ case DistillerHeuristicsType::OG_ARTICLE: return doc.distillabilityFeatures().openGraph; case DistillerHeuristicsType::ADABOOST_MODEL: - return IsDistillablePageAdaboost( - doc, DistillablePageDetector::GetNewModel(), is_last); + return IsDistillablePageAdaboost(doc, + DistillablePageDetector::GetNewModel(), + DistillablePageDetector::GetLongPageModel(), is_last); case DistillerHeuristicsType::NONE: default: return false;
diff --git a/components/dom_distiller/core/data/long_page_model.bin b/components/dom_distiller/core/data/long_page_model.bin new file mode 100644 index 0000000..1a94d25 --- /dev/null +++ b/components/dom_distiller/core/data/long_page_model.bin Binary files differ
diff --git a/components/dom_distiller/core/distillable_page_detector.cc b/components/dom_distiller/core/distillable_page_detector.cc index c3a333e..5c2a7ee0 100644 --- a/components/dom_distiller/core/distillable_page_detector.cc +++ b/components/dom_distiller/core/distillable_page_detector.cc
@@ -41,6 +41,20 @@ return detector; } +const DistillablePageDetector* DistillablePageDetector::GetLongPageModel() { + static DistillablePageDetector* detector = nullptr; + if (!detector) { + std::string serialized_proto = + ResourceBundle::GetSharedInstance() + .GetRawDataResource(IDR_LONG_PAGE_SERIALIZED_MODEL) + .as_string(); + scoped_ptr<AdaBoostProto> proto(new AdaBoostProto); + CHECK(proto->ParseFromString(serialized_proto)); + detector = new DistillablePageDetector(std::move(proto)); + } + return detector; +} + DistillablePageDetector::DistillablePageDetector( scoped_ptr<AdaBoostProto> proto) : proto_(std::move(proto)), threshold_(0.0) {
diff --git a/components/dom_distiller/core/distillable_page_detector.h b/components/dom_distiller/core/distillable_page_detector.h index dc9c413..893945a 100644 --- a/components/dom_distiller/core/distillable_page_detector.h +++ b/components/dom_distiller/core/distillable_page_detector.h
@@ -21,6 +21,7 @@ public: static const DistillablePageDetector* GetDefault(); static const DistillablePageDetector* GetNewModel(); + static const DistillablePageDetector* GetLongPageModel(); explicit DistillablePageDetector(scoped_ptr<AdaBoostProto> proto); ~DistillablePageDetector();
diff --git a/components/exo.gypi b/components/exo.gypi index f5fb24b..7814ce5 100644 --- a/components/exo.gypi +++ b/components/exo.gypi
@@ -65,6 +65,7 @@ 'dependencies': [ '../base/base.gyp:base', '../skia/skia.gyp:skia', + '../third_party/wayland-protocols/wayland-protocols.gyp:scaler_protocol', '../third_party/wayland-protocols/wayland-protocols.gyp:xdg_shell_protocol', '../third_party/wayland/wayland.gyp:wayland_server', '../ui/events/events.gyp:dom_keycode_converter',
diff --git a/components/exo/pointer.cc b/components/exo/pointer.cc index f739bb32..345a118 100644 --- a/components/exo/pointer.cc +++ b/components/exo/pointer.cc
@@ -132,6 +132,8 @@ case ui::ET_MOUSE_ENTERED: case ui::ET_MOUSE_EXITED: case ui::ET_MOUSE_CAPTURE_CHANGED: + case ui::ET_SCROLL_FLING_START: + case ui::ET_SCROLL_FLING_CANCEL: break; default: NOTREACHED();
diff --git a/components/exo/pointer_unittest.cc b/components/exo/pointer_unittest.cc index ce77dd33..4f8ee500d0 100644 --- a/components/exo/pointer_unittest.cc +++ b/components/exo/pointer_unittest.cc
@@ -236,11 +236,7 @@ generator.MoveMouseTo(location); EXPECT_CALL(delegate, OnPointerWheel(testing::_, gfx::Vector2d(1, 1))); - ui::ScrollEvent scroll_event(ui::ET_SCROLL, location, ui::EventTimeForNow(), - 0 /* flags */, 1 /* x_offset */, - 1 /* y_offset */, 1 /* x_offset_ordinal */, - 1 /* y_offset_ordinal */, 1 /* finger_count */); - generator.Dispatch(&scroll_event); + generator.ScrollSequence(location, base::TimeDelta(), 1, 1, 1, 1); EXPECT_CALL(delegate, OnPointerDestroying(pointer.get())); pointer.reset();
diff --git a/components/exo/surface.cc b/components/exo/surface.cc index 06b3028..5b209b30 100644 --- a/components/exo/surface.cc +++ b/components/exo/surface.cc
@@ -288,6 +288,12 @@ FindListEntry(pending_sub_surfaces_, sub_surface)); } +void Surface::SetViewport(const gfx::Size& viewport) { + TRACE_EVENT1("exo", "Surface::SetViewport", "viewport", viewport.ToString()); + + pending_viewport_ = viewport; +} + void Surface::Commit() { TRACE_EVENT0("exo", "Surface::Commit"); @@ -318,9 +324,14 @@ } if (texture_mailbox_release_callback) { - // Update layer with the new contents. - gfx::Size contents_size(gfx::ScaleToFlooredSize( - texture_mailbox.size_in_pixels(), 1.0f / pending_buffer_scale_)); + // Update layer with the new contents. If a viewport has been set then + // use that to determine the size of the layer and the surface, otherwise + // buffer scale and buffer size determines the size. + gfx::Size contents_size = + pending_viewport_.IsEmpty() + ? gfx::ScaleToFlooredSize(texture_mailbox.size_in_pixels(), + 1.0f / pending_buffer_scale_) + : pending_viewport_; layer()->SetTextureMailbox(texture_mailbox, std::move(texture_mailbox_release_callback), contents_size);
diff --git a/components/exo/surface.h b/components/exo/surface.h index 0f6ce7e..fa104c3 100644 --- a/components/exo/surface.h +++ b/components/exo/surface.h
@@ -80,6 +80,9 @@ void PlaceSubSurfaceAbove(Surface* sub_surface, Surface* reference); void PlaceSubSurfaceBelow(Surface* sub_surface, Surface* sibling); + // This sets the surface viewport for scaling. + void SetViewport(const gfx::Size& viewport); + // Surface state (damage regions, attached buffers, etc.) is double-buffered. // A Commit() call atomically applies all pending state, replacing the // current state. Commit() is not guaranteed to be synchronous. See @@ -184,6 +187,9 @@ using SubSurfaceEntryList = std::list<SubSurfaceEntry>; SubSurfaceEntryList pending_sub_surfaces_; + // The viewport to take effect when Commit() is called. + gfx::Size pending_viewport_; + // The buffer that is currently set as content of surface. base::WeakPtr<Buffer> current_buffer_;
diff --git a/components/exo/surface_unittest.cc b/components/exo/surface_unittest.cc index 2d47a7e..28b7cd2 100644 --- a/components/exo/surface_unittest.cc +++ b/components/exo/surface_unittest.cc
@@ -118,6 +118,21 @@ surface->bounds().size().ToString()); } +TEST_F(SurfaceTest, SetViewport) { + gfx::Size buffer_size(1, 1); + scoped_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + scoped_ptr<Surface> surface(new Surface); + + // This will update the bounds of the surface and take the viewport into + // account. + surface->Attach(buffer.get()); + gfx::Size viewport(256, 256); + surface->SetViewport(viewport); + surface->Commit(); + EXPECT_EQ(viewport.ToString(), surface->bounds().size().ToString()); +} + TEST_F(SurfaceTest, Commit) { scoped_ptr<Surface> surface(new Surface);
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn index d0ac6b5..050e4c9 100644 --- a/components/exo/wayland/BUILD.gn +++ b/components/exo/wayland/BUILD.gn
@@ -27,6 +27,7 @@ "//components/exo", "//skia", "//third_party/wayland:wayland_server", + "//third_party/wayland-protocols:scaler_protocol", "//third_party/wayland-protocols:xdg_shell_protocol", "//ui/events:dom_keycode_converter", "//ui/events:events_base",
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc index 73aa1a7..d211600 100644 --- a/components/exo/wayland/server.cc +++ b/components/exo/wayland/server.cc
@@ -5,11 +5,13 @@ #include "components/exo/wayland/server.h" #include <linux/input.h> +#include <scaler-server-protocol.h> #include <stddef.h> #include <stdint.h> #include <wayland-server-core.h> #include <wayland-server-protocol-core.h> #include <xdg-shell-unstable-v5-server-protocol.h> + #include <algorithm> #include <utility> @@ -80,6 +82,10 @@ // window. If unset, no surface resource is associated with window. DEFINE_WINDOW_PROPERTY_KEY(wl_resource*, kSurfaceResourceKey, nullptr); +// A property key containing a boolean set to true if a viewport is associated +// with window. +DEFINE_WINDOW_PROPERTY_KEY(bool, kSurfaceHasViewportKey, false); + //////////////////////////////////////////////////////////////////////////////// // wl_buffer_interface: @@ -107,10 +113,8 @@ int32_t x, int32_t y) { // TODO(reveman): Implement buffer offset support. - if (x || y) { - wl_resource_post_no_memory(resource); - return; - } + DLOG_IF(WARNING, x || y) << "Unsupported buffer offset: " + << gfx::Point(x, y).ToString(); GetUserDataAs<Surface>(resource) ->Attach(buffer ? GetUserDataAs<Buffer>(buffer) : nullptr); @@ -142,10 +146,6 @@ uint32_t callback) { wl_resource* callback_resource = wl_resource_create(client, &wl_callback_interface, 1, callback); - if (!callback_resource) { - wl_resource_post_no_memory(resource); - return; - } // base::Unretained is safe as the resource owns the callback. scoped_ptr<base::CancelableCallback<void(base::TimeTicks)>> @@ -248,14 +248,9 @@ uint32_t id) { scoped_ptr<Surface> surface = GetUserDataAs<Display>(resource)->CreateSurface(); - DCHECK(surface); wl_resource* surface_resource = wl_resource_create( client, &wl_surface_interface, wl_resource_get_version(resource), id); - if (!surface_resource) { - wl_resource_post_no_memory(resource); - return; - } // Set the surface resource property for type-checking downcast support. surface->SetProperty(kSurfaceResourceKey, surface_resource); @@ -267,16 +262,11 @@ void compositor_create_region(wl_client* client, wl_resource* resource, uint32_t id) { - scoped_ptr<SkRegion> region(new SkRegion); - wl_resource* region_resource = wl_resource_create(client, &wl_region_interface, 1, id); - if (!region_resource) { - wl_resource_post_no_memory(resource); - return; - } - SetImplementation(region_resource, ®ion_implementation, std::move(region)); + SetImplementation(region_resource, ®ion_implementation, + make_scoped_ptr(new SkRegion)); } const struct wl_compositor_interface compositor_implementation = { @@ -291,10 +281,6 @@ wl_resource* resource = wl_resource_create(client, &wl_compositor_interface, std::min(version, compositor_version), id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } wl_resource_set_implementation(resource, &compositor_implementation, data, nullptr); @@ -350,10 +336,6 @@ wl_resource* buffer_resource = wl_resource_create(client, &wl_buffer_interface, 1, id); - if (!buffer_resource) { - wl_resource_post_no_memory(resource); - return; - } buffer->set_release_callback(base::Bind(&HandleBufferReleaseCallback, base::Unretained(buffer_resource))); @@ -390,10 +372,6 @@ wl_resource* shm_pool_resource = wl_resource_create(client, &wl_shm_pool_interface, 1, id); - if (!shm_pool_resource) { - wl_resource_post_no_memory(resource); - return; - } SetImplementation(shm_pool_resource, &shm_pool_implementation, std::move(shared_memory)); @@ -403,10 +381,6 @@ void bind_shm(wl_client* client, void* data, uint32_t version, uint32_t id) { wl_resource* resource = wl_resource_create(client, &wl_shm_interface, 1, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } wl_resource_set_implementation(resource, &shm_implementation, data, nullptr); @@ -498,10 +472,6 @@ wl_resource* buffer_resource = wl_resource_create(client, &wl_buffer_interface, 1, id); - if (!buffer_resource) { - wl_resource_post_no_memory(resource); - return; - } buffer->set_release_callback(base::Bind(&HandleBufferReleaseCallback, base::Unretained(buffer_resource))); @@ -518,10 +488,7 @@ void bind_drm(wl_client* client, void* data, uint32_t version, uint32_t id) { wl_resource* resource = wl_resource_create( client, &wl_drm_interface, std::min(version, drm_version), id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } + wl_resource_set_implementation(resource, &drm_implementation, data, nullptr); if (version >= 2) @@ -588,16 +555,13 @@ GetUserDataAs<Display>(resource)->CreateSubSurface( GetUserDataAs<Surface>(surface), GetUserDataAs<Surface>(parent)); if (!subsurface) { - wl_resource_post_no_memory(resource); + wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, + "invalid surface"); return; } wl_resource* subsurface_resource = wl_resource_create(client, &wl_subsurface_interface, 1, id); - if (!subsurface_resource) { - wl_resource_post_no_memory(resource); - return; - } SetImplementation(subsurface_resource, &subsurface_implementation, std::move(subsurface)); @@ -612,10 +576,7 @@ uint32_t id) { wl_resource* resource = wl_resource_create(client, &wl_subcompositor_interface, 1, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } + wl_resource_set_implementation(resource, &subcompositor_implementation, data, nullptr); } @@ -724,16 +685,13 @@ GetUserDataAs<Display>(resource) ->CreateShellSurface(GetUserDataAs<Surface>(surface)); if (!shell_surface) { - wl_resource_post_no_memory(resource); + wl_resource_post_error(resource, WL_SHELL_ERROR_ROLE, + "surface has already been assigned a role"); return; } wl_resource* shell_surface_resource = wl_resource_create(client, &wl_shell_surface_interface, 1, id); - if (!shell_surface_resource) { - wl_resource_post_no_memory(resource); - return; - } // Shell surfaces are initially disabled and needs to be explicitly mapped // before they are enabled and can become visible. @@ -756,10 +714,7 @@ void bind_shell(wl_client* client, void* data, uint32_t version, uint32_t id) { wl_resource* resource = wl_resource_create(client, &wl_shell_interface, 1, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } + wl_resource_set_implementation(resource, &shell_implementation, data, nullptr); } @@ -772,10 +727,6 @@ void bind_output(wl_client* client, void* data, uint32_t version, uint32_t id) { wl_resource* resource = wl_resource_create( client, &wl_output_interface, std::min(version, output_version), id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } // TODO(reveman): Watch for display changes and report them. // TODO(reveman): Multi-display support. @@ -980,16 +931,13 @@ GetUserDataAs<Display>(resource) ->CreateShellSurface(GetUserDataAs<Surface>(surface)); if (!shell_surface) { - wl_resource_post_no_memory(resource); + wl_resource_post_error(resource, XDG_SHELL_ERROR_ROLE, + "surface has already been assigned a role"); return; } wl_resource* xdg_surface_resource = wl_resource_create(client, &xdg_surface_interface, 1, id); - if (!xdg_surface_resource) { - wl_resource_post_no_memory(resource); - return; - } shell_surface->set_close_callback(base::Bind( &HandleXdgSurfaceCloseCallback, base::Unretained(xdg_surface_resource))); @@ -1028,10 +976,7 @@ uint32_t id) { wl_resource* resource = wl_resource_create(client, &xdg_shell_interface, 1, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } + wl_resource_set_implementation(resource, &xdg_shell_implementation, data, nullptr); } @@ -1073,10 +1018,6 @@ wl_resource* seat_resource) { wl_resource* data_device_resource = wl_resource_create(client, &wl_data_device_interface, 1, id); - if (!data_device_resource) { - wl_client_post_no_memory(client); - return; - } wl_resource_set_implementation(data_device_resource, &data_device_implementation, nullptr, nullptr); @@ -1093,10 +1034,6 @@ uint32_t id) { wl_resource* resource = wl_resource_create(client, &wl_data_device_manager_interface, 1, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } wl_resource_set_implementation(resource, &data_device_manager_implementation, data, nullptr); @@ -1453,10 +1390,6 @@ void seat_get_pointer(wl_client* client, wl_resource* resource, uint32_t id) { wl_resource* pointer_resource = wl_resource_create( client, &wl_pointer_interface, wl_resource_get_version(resource), id); - if (!pointer_resource) { - wl_resource_post_no_memory(resource); - return; - } SetImplementation(pointer_resource, &pointer_implementation, make_scoped_ptr(new Pointer( @@ -1468,10 +1401,6 @@ uint32_t version = wl_resource_get_version(resource); wl_resource* keyboard_resource = wl_resource_create(client, &wl_keyboard_interface, version, id); - if (!keyboard_resource) { - wl_resource_post_no_memory(resource); - return; - } SetImplementation(keyboard_resource, &keyboard_implementation, make_scoped_ptr(new Keyboard( @@ -1488,10 +1417,6 @@ void seat_get_touch(wl_client* client, wl_resource* resource, uint32_t id) { wl_resource* touch_resource = wl_resource_create( client, &wl_touch_interface, wl_resource_get_version(resource), id); - if (!touch_resource) { - wl_resource_post_no_memory(resource); - return; - } SetImplementation( touch_resource, &touch_implementation, @@ -1506,10 +1431,6 @@ void bind_seat(wl_client* client, void* data, uint32_t version, uint32_t id) { wl_resource* resource = wl_resource_create( client, &wl_seat_interface, std::min(version, seat_version), id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } wl_resource_set_implementation(resource, &seat_implementation, data, nullptr); @@ -1523,6 +1444,132 @@ wl_seat_send_capabilities(resource, capabilities); } +//////////////////////////////////////////////////////////////////////////////// +// wl_viewport_interface: + +class Viewport : public SurfaceObserver { + public: + explicit Viewport(Surface* surface) : surface_(surface) { + surface_->AddSurfaceObserver(this); + surface_->SetProperty(kSurfaceHasViewportKey, true); + } + ~Viewport() override { + if (surface_) { + surface_->RemoveSurfaceObserver(this); + surface_->SetViewport(gfx::Size()); + surface_->SetProperty(kSurfaceHasViewportKey, false); + } + } + + void SetDestination(const gfx::Size& size) { + if (surface_) + surface_->SetViewport(size); + } + + // Overridden from SurfaceObserver: + void OnSurfaceDestroying(Surface* surface) override { + surface->RemoveSurfaceObserver(this); + surface_ = nullptr; + } + + private: + Surface* surface_; + + DISALLOW_COPY_AND_ASSIGN(Viewport); +}; + +void viewport_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void viewport_set(wl_client* client, + wl_resource* resource, + wl_fixed_t src_x, + wl_fixed_t src_y, + wl_fixed_t src_width, + wl_fixed_t src_height, + int32_t dst_width, + int32_t dst_height) { + NOTIMPLEMENTED(); +} + +void viewport_set_source(wl_client* client, + wl_resource* resource, + wl_fixed_t x, + wl_fixed_t y, + wl_fixed_t width, + wl_fixed_t height) { + NOTIMPLEMENTED(); +} + +void viewport_set_destination(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + if (width == -1 && height == -1) { + GetUserDataAs<Viewport>(resource)->SetDestination(gfx::Size()); + return; + } + + if (width <= 0 || height <= 0) { + wl_resource_post_error(resource, WL_VIEWPORT_ERROR_BAD_VALUE, + "destination size must be positive (%dx%d)", width, + height); + return; + } + + GetUserDataAs<Viewport>(resource)->SetDestination(gfx::Size(width, height)); +} + +const struct wl_viewport_interface viewport_implementation = { + viewport_destroy, viewport_set, viewport_set_source, + viewport_set_destination}; + +//////////////////////////////////////////////////////////////////////////////// +// wl_scaler_interface: + +void scaler_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void scaler_get_viewport(wl_client* client, + wl_resource* resource, + uint32_t id, + wl_resource* surface_resource) { + Surface* surface = GetUserDataAs<Surface>(surface_resource); + if (surface->GetProperty(kSurfaceHasViewportKey)) { + wl_resource_post_error(resource, WL_SCALER_ERROR_VIEWPORT_EXISTS, + "a viewport for that surface already exists"); + return; + } + + wl_resource* viewport_resource = wl_resource_create( + client, &wl_viewport_interface, wl_resource_get_version(resource), id); + if (!viewport_resource) { + wl_resource_post_no_memory(resource); + return; + } + + SetImplementation(viewport_resource, &viewport_implementation, + make_scoped_ptr(new Viewport(surface))); +} + +const struct wl_scaler_interface scaler_implementation = {scaler_destroy, + scaler_get_viewport}; + +const uint32_t scaler_version = 2; + +void bind_scaler(wl_client* client, void* data, uint32_t version, uint32_t id) { + wl_resource* resource = wl_resource_create( + client, &wl_scaler_interface, std::min(version, scaler_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &scaler_implementation, data, + nullptr); +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -1549,6 +1596,8 @@ display_, bind_data_device_manager); wl_global_create(wl_display_.get(), &wl_seat_interface, seat_version, display_, bind_seat); + wl_global_create(wl_display_.get(), &wl_scaler_interface, scaler_version, + display_, bind_scaler); } Server::~Server() {}
diff --git a/components/filesystem/BUILD.gn b/components/filesystem/BUILD.gn index ffb94dd9..f9d9d05 100644 --- a/components/filesystem/BUILD.gn +++ b/components/filesystem/BUILD.gn
@@ -11,8 +11,6 @@ "directory_impl.h", "file_impl.cc", "file_impl.h", - "file_system_app.cc", - "file_system_app.h", "file_system_impl.cc", "file_system_impl.h", "lock_table.cc", @@ -27,14 +25,16 @@ "//mojo/common", "//mojo/common:common_base", "//mojo/platform_handle", - "//mojo/services/tracing/public/cpp", "//mojo/shell/public/cpp", + "//mojo/shell/public/interfaces", "//url", ] } mojo_native_application("filesystem") { sources = [ + "file_system_app.cc", + "file_system_app.h", "main.cc", ] @@ -42,11 +42,13 @@ ":lib", ":manifest", "//base", + "//components/filesystem/public/interfaces", "//mojo/common", "//mojo/environment:chromium", "//mojo/platform_handle:for_shared_library", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", + "//mojo/services/tracing/public/cpp", "//mojo/shell/public/cpp", ] }
diff --git a/components/filesystem/file_system_app.cc b/components/filesystem/file_system_app.cc index 344a7ca..d5d577a 100644 --- a/components/filesystem/file_system_app.cc +++ b/components/filesystem/file_system_app.cc
@@ -4,17 +4,13 @@ #include "components/filesystem/file_system_app.h" -#include <utility> - -#include "base/bind.h" -#include "base/logging.h" #include "mojo/shell/public/cpp/connection.h" #include "mojo/shell/public/cpp/shell.h" namespace filesystem { FileSystemApp::FileSystemApp() - : shell_(nullptr), lock_table_(new LockTable), in_shutdown_(false) {} + : shell_(nullptr), lock_table_(new LockTable) {} FileSystemApp::~FileSystemApp() {} @@ -29,70 +25,10 @@ return true; } -void FileSystemApp::RegisterDirectoryToClient(DirectoryImpl* directory, - FileSystemClientPtr client) { - directory->set_connection_error_handler( - base::Bind(&FileSystemApp::OnDirectoryConnectionError, - base::Unretained(this), - directory)); - client_mapping_.emplace_back(directory, std::move(client)); -} - -bool FileSystemApp::ShellConnectionLost() { - if (client_mapping_.empty()) { - // If we have no current connections, we can shutdown immediately. - return true; - } - - in_shutdown_ = true; - - // We have live connections. Send a notification to each one indicating that - // they should shutdown. - for (std::vector<Client>::iterator it = client_mapping_.begin(); - it != client_mapping_.end(); ++it) { - it->fs_client_->OnFileSystemShutdown(); - } - - return false; -} - // |InterfaceFactory<Files>| implementation: void FileSystemApp::Create(mojo::Connection* connection, mojo::InterfaceRequest<FileSystem> request) { - new FileSystemImpl(this, connection, std::move(request), lock_table_.get()); -} - -void FileSystemApp::OnDirectoryConnectionError(DirectoryImpl* directory) { - for (std::vector<Client>::iterator it = client_mapping_.begin(); - it != client_mapping_.end(); ++it) { - if (it->directory_ == directory) { - client_mapping_.erase(it); - - if (in_shutdown_ && client_mapping_.empty()) { - // We just cleared the last directory after our shell connection went - // away. Time to shut ourselves down. - shell_->Quit(); - } - - return; - } - } -} - -FileSystemApp::Client::Client(DirectoryImpl* directory, - FileSystemClientPtr fs_client) - : directory_(directory), fs_client_(std::move(fs_client)) {} - -FileSystemApp::Client::Client(Client&& rhs) - : directory_(rhs.directory_), fs_client_(std::move(rhs.fs_client_)) {} - -FileSystemApp::Client::~Client() {} - -FileSystemApp::Client& FileSystemApp::Client::operator=( - FileSystemApp::Client&& rhs) { - directory_ = rhs.directory_; - fs_client_ = std::move(rhs.fs_client_); - return *this; + new FileSystemImpl(connection, std::move(request), lock_table_.get()); } } // namespace filesystem
diff --git a/components/filesystem/file_system_app.h b/components/filesystem/file_system_app.h index 402c359..ae62469 100644 --- a/components/filesystem/file_system_app.h +++ b/components/filesystem/file_system_app.h
@@ -26,50 +26,21 @@ FileSystemApp(); ~FileSystemApp() override; - // Called by individual FileSystem objects to register lifetime events. - void RegisterDirectoryToClient(DirectoryImpl* directory, - FileSystemClientPtr client); - private: - // We set the DirectoryImpl's error handler to this function. We do this so - // that we can QuitNow() once the last DirectoryImpl has closed itself. - void OnDirectoryConnectionError(DirectoryImpl* directory); - // |mojo::ShellClient| override: void Initialize(mojo::Shell* shell, const std::string& url, uint32_t id) override; bool AcceptConnection(mojo::Connection* connection) override; - bool ShellConnectionLost() override; // |InterfaceFactory<Files>| implementation: void Create(mojo::Connection* connection, mojo::InterfaceRequest<FileSystem> request) override; - // Use a vector to work around map not letting us use FileSystemClientPtr as - // a value in a std::map. The move constructors are to allow us to deal with - // FileSystemClientPtr inside a vector. - struct Client { - Client(DirectoryImpl* directory, FileSystemClientPtr fs_client); - Client(Client&& rhs); - ~Client(); - - Client& operator=(Client&& rhs); - - DirectoryImpl* directory_; - FileSystemClientPtr fs_client_; - }; - std::vector<Client> client_mapping_; - mojo::Shell* shell_; mojo::TracingImpl tracing_; scoped_ptr<LockTable> lock_table_; - // Set to true when our shell connection is closed. On connection error, we - // then broadcast a notification to all FileSystemClients that they should - // shut down. Once the final one does, then we QuitNow(). - bool in_shutdown_; - DISALLOW_COPY_AND_ASSIGN(FileSystemApp); };
diff --git a/components/filesystem/file_system_impl.cc b/components/filesystem/file_system_impl.cc index 5a60c5c..cf444b3 100644 --- a/components/filesystem/file_system_impl.cc +++ b/components/filesystem/file_system_impl.cc
@@ -16,7 +16,6 @@ #include "base/memory/scoped_ptr.h" #include "build/build_config.h" #include "components/filesystem/directory_impl.h" -#include "components/filesystem/file_system_app.h" #include "mojo/shell/public/cpp/connection.h" #include "url/gurl.h" @@ -45,12 +44,10 @@ } // namespace filesystem -FileSystemImpl::FileSystemImpl(FileSystemApp* app, - mojo::Connection* connection, +FileSystemImpl::FileSystemImpl(mojo::Connection* connection, mojo::InterfaceRequest<FileSystem> request, LockTable* lock_table) - : app_(app), - remote_application_url_(connection->GetRemoteApplicationURL()), + : remote_application_url_(connection->GetRemoteApplicationURL()), binding_(this, std::move(request)), lock_table_(lock_table) {} @@ -89,9 +86,8 @@ } if (!path.empty()) { - DirectoryImpl* dir_impl = new DirectoryImpl( + new DirectoryImpl( std::move(directory), path, std::move(temp_dir), lock_table_); - app_->RegisterDirectoryToClient(dir_impl, std::move(client)); callback.Run(FileError::OK); } else { callback.Run(FileError::FAILED);
diff --git a/components/filesystem/file_system_impl.h b/components/filesystem/file_system_impl.h index 11ac05d..4c18dd5 100644 --- a/components/filesystem/file_system_impl.h +++ b/components/filesystem/file_system_impl.h
@@ -25,8 +25,7 @@ class FileSystemImpl : public FileSystem { public: - FileSystemImpl(FileSystemApp* app, - mojo::Connection* connection, + FileSystemImpl(mojo::Connection* connection, mojo::InterfaceRequest<FileSystem> request, LockTable* lock_table); ~FileSystemImpl() override; @@ -52,7 +51,6 @@ void BuildSanitizedOrigin(const std::string& origin, std::string* sanitized_origin); - FileSystemApp* app_; const std::string remote_application_url_; mojo::StrongBinding<FileSystem> binding_; LockTable* lock_table_;
diff --git a/components/filesystem/filesystem.gyp b/components/filesystem/filesystem.gyp new file mode 100644 index 0000000..396dac5d --- /dev/null +++ b/components/filesystem/filesystem.gyp
@@ -0,0 +1,67 @@ +# 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. + +{ + 'variables': { + # This turns on e.g. the filename-based detection of which + # platforms to include source files on (e.g. files ending in + # _mac.h or _mac.cc are only compiled on MacOSX). + 'chromium_code': 1, + }, + 'targets': [ + { + # GN version: //components/filesystem:lib + 'target_name': 'filesystem_lib', + 'type': 'static_library', + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'directory_impl.cc', + 'directory_impl.h', + 'file_impl.cc', + 'file_impl.h', + 'file_system_impl.cc', + 'file_system_impl.h', + 'lock_table.cc', + 'lock_table.h', + 'util.cc', + 'util.h', + ], + 'dependencies': [ + 'filesystem_bindings', + '../../mojo/mojo_edk.gyp:mojo_system_impl', + '../../mojo/mojo_public.gyp:mojo_cpp_bindings', + '../../mojo/mojo_platform_handle.gyp:platform_handle', + '../../url/url.gyp:url_lib', + ], + 'export_dependent_settings': [ + 'filesystem_bindings', + ], + }, + { + # GN version: //components/filesystem/public/interfaces + 'target_name': 'filesystem_bindings', + 'type': 'static_library', + 'dependencies': [ + 'filesystem_bindings_mojom', + ], + }, + { + 'target_name': 'filesystem_bindings_mojom', + 'type': 'none', + 'variables': { + 'mojom_files': [ + 'public/interfaces/directory.mojom', + 'public/interfaces/file.mojom', + 'public/interfaces/file_system.mojom', + 'public/interfaces/types.mojom', + ], + }, + 'includes': [ + '../../mojo/mojom_bindings_generator_explicit.gypi', + ], + } + ], +}
diff --git a/components/font_service/font_service_app.cc b/components/font_service/font_service_app.cc index 637410e..1be9054 100644 --- a/components/font_service/font_service_app.cc +++ b/components/font_service/font_service_app.cc
@@ -106,20 +106,20 @@ void FontServiceApp::OpenStream(uint32_t id_number, const OpenStreamCallback& callback) { mojo::ScopedHandle handle; - if (id_number < static_cast<uint32_t>(paths_.count())) { - handle = GetHandleForPath(base::FilePath(paths_[id_number]->c_str())); + if (id_number < static_cast<uint32_t>(paths_.size())) { + handle = GetHandleForPath(base::FilePath(paths_[id_number].c_str())); } callback.Run(std::move(handle)); } int FontServiceApp::FindOrAddPath(const SkString& path) { - int count = paths_.count(); + int count = paths_.size(); for (int i = 0; i < count; ++i) { - if (path == *paths_[i]) + if (path == paths_[i]) return i; } - *paths_.append() = new SkString(path); + paths_.emplace_back(path); return count; }
diff --git a/components/font_service/font_service_app.h b/components/font_service/font_service_app.h index 041d4f8..c9926076 100644 --- a/components/font_service/font_service_app.h +++ b/components/font_service/font_service_app.h
@@ -6,6 +6,7 @@ #define COMPONENTS_FONT_SERVICE_FONT_SERVICE_APP_H_ #include <stdint.h> +#include <vector> #include "base/macros.h" #include "components/font_service/public/interfaces/font_service.mojom.h" @@ -49,7 +50,7 @@ // We don't want to leak paths to our callers; we thus enumerate the paths of // fonts. - SkTDArray<SkString*> paths_; + std::vector<SkString> paths_; DISALLOW_COPY_AND_ASSIGN(FontServiceApp); };
diff --git a/components/gcm_driver/crypto/BUILD.gn b/components/gcm_driver/crypto/BUILD.gn index 248fb72..778c1c8 100644 --- a/components/gcm_driver/crypto/BUILD.gn +++ b/components/gcm_driver/crypto/BUILD.gn
@@ -76,6 +76,7 @@ deps = [ ":crypto", "//base", + "//base/test:test_support", "//components/gcm_driver/common", "//crypto", "//crypto:platform",
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.cc b/components/gcm_driver/crypto/gcm_encryption_provider.cc index bb6d4ab..a89fedf 100644 --- a/components/gcm_driver/crypto/gcm_encryption_provider.cc +++ b/components/gcm_driver/crypto/gcm_encryption_provider.cc
@@ -29,25 +29,27 @@ } // namespace -std::string GCMEncryptionProvider::ToDecryptionFailureDetailsString( - DecryptionFailure reason) { - switch(reason) { - case DECRYPTION_FAILURE_UNKNOWN: - return "Unknown failure"; - case DECRYPTION_FAILURE_INVALID_ENCRYPTION_HEADER: +std::string GCMEncryptionProvider::ToDecryptionResultDetailsString( + DecryptionResult result) { + switch(result) { + case DECRYPTION_RESULT_UNENCRYPTED: + return "Message was not encrypted"; + case DECRYPTION_RESULT_DECRYPTED: + return "Message decrypted"; + case DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER: return "Invalid format for the Encryption header"; - case DECRYPTION_FAILURE_INVALID_CRYPTO_KEY_HEADER: + case DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER: return "Invalid format for the Crypto-Key header"; - case DECRYPTION_FAILURE_NO_KEYS: + case DECRYPTION_RESULT_NO_KEYS: return "There are no associated keys with the subscription"; - case DECRYPTION_FAILURE_INVALID_PUBLIC_KEY: - return "The public key in the Crypto-Key header is invalid"; - case DECRYPTION_FAILURE_INVALID_PAYLOAD: + case DECRYPTION_RESULT_INVALID_SHARED_SECRET: + return "The shared secret cannot be derived from the keying material"; + case DECRYPTION_RESULT_INVALID_PAYLOAD: return "AES-GCM decryption failed"; } NOTREACHED(); - return "(invalid reason)"; + return "(invalid result)"; } GCMEncryptionProvider::GCMEncryptionProvider() @@ -103,14 +105,18 @@ void GCMEncryptionProvider::DecryptMessage( const std::string& app_id, const IncomingMessage& message, - const MessageDecryptedCallback& success_callback, - const DecryptionFailedCallback& failure_callback) { + const MessageCallback& callback) { DCHECK(key_store_); + if (!IsEncryptedMessage(message)) { + callback.Run(DECRYPTION_RESULT_UNENCRYPTED, message); + return; + } + // IsEncryptedMessage() verifies that both the Encryption and Crypto-Key HTTP + // headers have been provided for the |message|. const auto& encryption_header = message.data.find(kEncryptionProperty); const auto& crypto_key_header = message.data.find(kCryptoKeyProperty); - // Callers are expected to call IsEncryptedMessage() prior to this method. DCHECK(encryption_header != message.data.end()); DCHECK(crypto_key_header != message.data.end()); @@ -118,7 +124,8 @@ if (!ParseEncryptionHeader(encryption_header->second, &encryption_header_values)) { DLOG(ERROR) << "Unable to parse the value of the Encryption header"; - failure_callback.Run(DECRYPTION_FAILURE_INVALID_ENCRYPTION_HEADER); + callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, + IncomingMessage()); return; } @@ -126,7 +133,8 @@ encryption_header_values[0].salt.size() != GCMMessageCryptographer::kSaltSize) { DLOG(ERROR) << "Invalid values supplied in the Encryption header"; - failure_callback.Run(DECRYPTION_FAILURE_INVALID_ENCRYPTION_HEADER); + callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, + IncomingMessage()); return; } @@ -134,22 +142,23 @@ if (!ParseCryptoKeyHeader(crypto_key_header->second, &crypto_key_header_values)) { DLOG(ERROR) << "Unable to parse the value of the Crypto-Key header"; - failure_callback.Run(DECRYPTION_FAILURE_INVALID_CRYPTO_KEY_HEADER); + callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, + IncomingMessage()); return; } if (crypto_key_header_values.size() != 1u || !crypto_key_header_values[0].dh.size()) { DLOG(ERROR) << "Invalid values supplied in the Crypto-Key header"; - failure_callback.Run(DECRYPTION_FAILURE_INVALID_CRYPTO_KEY_HEADER); + callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, + IncomingMessage()); return; } key_store_->GetKeys( app_id, base::Bind(&GCMEncryptionProvider::DecryptMessageWithKey, weak_ptr_factory_.GetWeakPtr(), message, - success_callback, failure_callback, - encryption_header_values[0].salt, + callback, encryption_header_values[0].salt, crypto_key_header_values[0].dh, encryption_header_values[0].rs)); } @@ -186,8 +195,7 @@ void GCMEncryptionProvider::DecryptMessageWithKey( const IncomingMessage& message, - const MessageDecryptedCallback& success_callback, - const DecryptionFailedCallback& failure_callback, + const MessageCallback& callback, const std::string& salt, const std::string& dh, uint64_t rs, @@ -195,7 +203,7 @@ const std::string& auth_secret) { if (!pair.IsInitialized()) { DLOG(ERROR) << "Unable to retrieve the keys for the incoming message."; - failure_callback.Run(DECRYPTION_FAILURE_NO_KEYS); + callback.Run(DECRYPTION_RESULT_NO_KEYS, IncomingMessage()); return; } @@ -205,7 +213,7 @@ if (!ComputeSharedP256Secret(pair.private_key(), pair.public_key_x509(), dh, &shared_secret)) { DLOG(ERROR) << "Unable to calculate the shared secret."; - failure_callback.Run(DECRYPTION_FAILURE_INVALID_PUBLIC_KEY); + callback.Run(DECRYPTION_RESULT_INVALID_SHARED_SECRET, IncomingMessage()); return; } @@ -216,7 +224,7 @@ if (!cryptographer.Decrypt(message.raw_data, shared_secret, salt, rs, &plaintext)) { DLOG(ERROR) << "Unable to decrypt the incoming data."; - failure_callback.Run(DECRYPTION_FAILURE_INVALID_PAYLOAD); + callback.Run(DECRYPTION_RESULT_INVALID_PAYLOAD, IncomingMessage()); return; } @@ -230,7 +238,7 @@ // to make sure that we don't end up in an infinite decryption loop. DCHECK_EQ(0u, decrypted_message.data.size()); - success_callback.Run(decrypted_message); + callback.Run(DECRYPTION_RESULT_DECRYPTED, decrypted_message); } } // namespace gcm
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.h b/components/gcm_driver/crypto/gcm_encryption_provider.h index 8c2c41b..294dc6e 100644 --- a/components/gcm_driver/crypto/gcm_encryption_provider.h +++ b/components/gcm_driver/crypto/gcm_encryption_provider.h
@@ -28,38 +28,46 @@ // and decryption of incoming messages. class GCMEncryptionProvider { public: + // Result of decrypting an incoming message. The values of these reasons must + // not be changed, because they are being recorded using UMA. + enum DecryptionResult { + // The message had not been encrypted by the sender. + DECRYPTION_RESULT_UNENCRYPTED = 0, + + // The message had been encrypted by the sender, and could successfully be + // decrypted for the registration it has been received for. + DECRYPTION_RESULT_DECRYPTED = 1, + + // The contents of the Encryption HTTP header could not be parsed. + DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER = 2, + + // The contents of the Crypto-Key HTTP header could not be parsed. + DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER = 3, + + // No public/private key-pair was associated with the app_id. + DECRYPTION_RESULT_NO_KEYS = 4, + + // The shared secret cannot be derived from the keying material. + DECRYPTION_RESULT_INVALID_SHARED_SECRET = 5, + + // The payload could not be decrypted as AES-128-GCM. + DECRYPTION_RESULT_INVALID_PAYLOAD = 6, + + DECRYPTION_RESULT_LAST = DECRYPTION_RESULT_INVALID_PAYLOAD + }; + // Callback to be invoked when the public key and auth secret are available. using EncryptionInfoCallback = base::Callback<void(const std::string&, const std::string&)>; - // Callback to be invoked when a message has been decrypted. - using MessageDecryptedCallback = base::Callback<void(const IncomingMessage&)>; + // Callback to be invoked when a message may have been decrypted, as indicated + // by the |result|. The |message| contains the dispatchable message in success + // cases, or will be initialized to an empty, default state for failure. + using MessageCallback = base::Callback<void(DecryptionResult result, + const IncomingMessage& message)>; - // Reasons why the decryption of an incoming message can fail. - enum DecryptionFailure { - DECRYPTION_FAILURE_UNKNOWN, - - // The contents of the Encryption HTTP header could not be parsed. - DECRYPTION_FAILURE_INVALID_ENCRYPTION_HEADER, - - // The contents of the Crypto-Key HTTP header could not be parsed. - DECRYPTION_FAILURE_INVALID_CRYPTO_KEY_HEADER, - - // No public/private key-pair was associated with the app_id. - DECRYPTION_FAILURE_NO_KEYS, - - // The public key provided in the Crypto-Key header is invalid. - DECRYPTION_FAILURE_INVALID_PUBLIC_KEY, - - // The payload could not be decrypted as AES-128-GCM. - DECRYPTION_FAILURE_INVALID_PAYLOAD - }; - - // Callback to be invoked when a message cannot be decoded. - using DecryptionFailedCallback = base::Callback<void(DecryptionFailure)>; - - // Converts |reason| to a string describing the details of said reason. - static std::string ToDecryptionFailureDetailsString(DecryptionFailure reason); + // Converts |result| to a string describing the details of said result. + static std::string ToDecryptionResultDetailsString(DecryptionResult result); GCMEncryptionProvider(); ~GCMEncryptionProvider(); @@ -84,14 +92,13 @@ // Determines whether |message| contains encrypted content. bool IsEncryptedMessage(const IncomingMessage& message) const; - // Asynchronously decrypts |message|. The |success_callback| will be invoked - // the message could be decrypted successfully, accompanied by the decrypted - // payload of the message. When decryption failed, the |failure_callback| will - // be invoked with the reason that encryption failed. + // Attempts to decrypt the |message|. If the |message| is not encrypted, the + // |callback| will be invoked immediately. Otherwise |callback| will be called + // asynchronously when |message| has been decrypted. A dispatchable message + // will be used in case of success, an empty message in case of failure. void DecryptMessage(const std::string& app_id, const IncomingMessage& message, - const MessageDecryptedCallback& success_callback, - const DecryptionFailedCallback& failure_callback); + const MessageCallback& callback); private: FRIEND_TEST_ALL_PREFIXES(GCMEncryptionProviderTest, EncryptionRoundTrip); @@ -106,8 +113,7 @@ const std::string& auth_secret); void DecryptMessageWithKey(const IncomingMessage& message, - const MessageDecryptedCallback& success_callback, - const DecryptionFailedCallback& failure_callback, + const MessageCallback& callback, const std::string& salt, const std::string& dh, uint64_t rs,
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc index ed4ab936..233e8a4 100644 --- a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc +++ b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
@@ -17,6 +17,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" +#include "base/test/histogram_tester.h" #include "components/gcm_driver/common/gcm_messages.h" #include "components/gcm_driver/crypto/gcm_key_store.h" #include "components/gcm_driver/crypto/gcm_message_cryptographer.h" @@ -76,62 +77,45 @@ } protected: - // Tri-state enumaration listing whether the decryption operation is idle - // (hasn't started yet), succeeded or failed. - enum DecryptionResult { - DECRYPTION_IDLE, - DECRYPTION_SUCCEEDED, - DECRYPTION_FAILED - }; - // Decrypts the |message| and then synchronously waits until either the // success or failure callbacks has been invoked. void Decrypt(const IncomingMessage& message) { - decryption_result_ = DECRYPTION_IDLE; encryption_provider_->DecryptMessage( kExampleAppId, message, - base::Bind(&GCMEncryptionProviderTest::OnDecryptionSucceeded, - base::Unretained(this)), - base::Bind(&GCMEncryptionProviderTest::OnDecryptionFailed, + base::Bind(&GCMEncryptionProviderTest::DidDecryptMessage, base::Unretained(this))); // The encryption keys will be read asynchronously. base::RunLoop().RunUntilIdle(); - - ASSERT_NE(decryption_result_, DECRYPTION_IDLE); } - DecryptionResult decryption_result() { return decryption_result_; } + // Returns the result of the previous decryption operation. + GCMEncryptionProvider::DecryptionResult decryption_result() { + return decryption_result_; + } + // Returns the message resulting from the previous decryption operation. const IncomingMessage& decrypted_message() { return decrypted_message_; } - GCMEncryptionProvider::DecryptionFailure failure_reason() { - return failure_reason_; - } - GCMEncryptionProvider* encryption_provider() { return encryption_provider_.get(); } private: - void OnDecryptionSucceeded(const IncomingMessage& message) { - decryption_result_ = DECRYPTION_SUCCEEDED; + void DidDecryptMessage(GCMEncryptionProvider::DecryptionResult result, + const IncomingMessage& message) { + decryption_result_ = result; decrypted_message_ = message; } - void OnDecryptionFailed(GCMEncryptionProvider::DecryptionFailure reason) { - decryption_result_ = DECRYPTION_FAILED; - failure_reason_ = reason; - } - base::MessageLoop message_loop_; base::ScopedTempDir scoped_temp_dir_; + base::HistogramTester histogram_tester_; scoped_ptr<GCMEncryptionProvider> encryption_provider_; - DecryptionResult decryption_result_ = DECRYPTION_IDLE; - GCMEncryptionProvider::DecryptionFailure failure_reason_ = - GCMEncryptionProvider::DECRYPTION_FAILURE_UNKNOWN; + GCMEncryptionProvider::DecryptionResult decryption_result_ = + GCMEncryptionProvider::DECRYPTION_RESULT_UNENCRYPTED; IncomingMessage decrypted_message_; }; @@ -169,20 +153,20 @@ IncomingMessage invalid_message; invalid_message.data["encryption"] = kInvalidEncryptionHeader; invalid_message.data["crypto-key"] = kValidCryptoKeyHeader; + invalid_message.raw_data = "foo"; ASSERT_NO_FATAL_FAILURE(Decrypt(invalid_message)); - ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); - EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_ENCRYPTION_HEADER, - failure_reason()); + EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, + decryption_result()); IncomingMessage valid_message; valid_message.data["encryption"] = kValidEncryptionHeader; valid_message.data["crypto-key"] = kInvalidCryptoKeyHeader; + valid_message.raw_data = "foo"; ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message)); - ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); - EXPECT_NE(GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_ENCRYPTION_HEADER, - failure_reason()); + EXPECT_NE(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, + decryption_result()); } TEST_F(GCMEncryptionProviderTest, VerifiesCryptoKeyHeaderParsing) { @@ -192,20 +176,20 @@ IncomingMessage invalid_message; invalid_message.data["encryption"] = kValidEncryptionHeader; invalid_message.data["crypto-key"] = kInvalidCryptoKeyHeader; + invalid_message.raw_data = "foo"; ASSERT_NO_FATAL_FAILURE(Decrypt(invalid_message)); - ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); - EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_CRYPTO_KEY_HEADER, - failure_reason()); + EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, + decryption_result()); IncomingMessage valid_message; valid_message.data["encryption"] = kInvalidEncryptionHeader; valid_message.data["crypto-key"] = kValidCryptoKeyHeader; + valid_message.raw_data = "foo"; ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message)); - ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); - EXPECT_NE(GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_CRYPTO_KEY_HEADER, - failure_reason()); + EXPECT_NE(GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, + decryption_result()); } TEST_F(GCMEncryptionProviderTest, VerifiesExistingKeys) { @@ -215,11 +199,11 @@ IncomingMessage message; message.data["encryption"] = kValidEncryptionHeader; message.data["crypto-key"] = kValidCryptoKeyHeader; + message.raw_data = "foo"; ASSERT_NO_FATAL_FAILURE(Decrypt(message)); - ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); - EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_FAILURE_NO_KEYS, - failure_reason()); + EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_RESULT_NO_KEYS, + decryption_result()); std::string public_key, auth_secret; encryption_provider()->GetEncryptionInfo( @@ -234,9 +218,8 @@ ASSERT_GT(auth_secret.size(), 0u); ASSERT_NO_FATAL_FAILURE(Decrypt(message)); - ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); - EXPECT_NE(GCMEncryptionProvider::DECRYPTION_FAILURE_NO_KEYS, - failure_reason()); + EXPECT_NE(GCMEncryptionProvider::DECRYPTION_RESULT_NO_KEYS, + decryption_result()); } TEST_F(GCMEncryptionProviderTest, EncryptionRoundTrip) { @@ -312,7 +295,8 @@ // Decrypt the message, and expect everything to go wonderfully well. ASSERT_NO_FATAL_FAILURE(Decrypt(message)); - ASSERT_EQ(DECRYPTION_SUCCEEDED, decryption_result()); + ASSERT_EQ(GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED, + decryption_result()); EXPECT_TRUE(decrypted_message().decrypted); EXPECT_EQ(kExampleMessage, decrypted_message().raw_data);
diff --git a/components/gcm_driver/crypto/gcm_key_store.cc b/components/gcm_driver/crypto/gcm_key_store.cc index b21d2e56..25cf078 100644 --- a/components/gcm_driver/crypto/gcm_key_store.cc +++ b/components/gcm_driver/crypto/gcm_key_store.cc
@@ -9,6 +9,7 @@ #include <utility> #include "base/logging.h" +#include "base/metrics/histogram_macros.h" #include "components/gcm_driver/crypto/p256_key_util.h" #include "components/leveldb_proto/proto_database_impl.h" #include "crypto/random.h" @@ -54,7 +55,11 @@ const KeysCallback& callback) { DCHECK(state_ == State::INITIALIZED || state_ == State::FAILED); const auto& iter = key_pairs_.find(app_id); - if (iter == key_pairs_.end() || state_ != State::INITIALIZED) { + + const bool success = state_ == State::INITIALIZED && iter != key_pairs_.end(); + UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.GetKeySuccessRate", success); + + if (!success) { callback.Run(KeyPair(), std::string() /* auth_secret */); return; } @@ -128,6 +133,7 @@ const std::string& auth_secret, const KeysCallback& callback, bool success) { + UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.CreateKeySuccessRate", success); DCHECK_EQ(0u, key_pairs_.count(app_id)); if (!success) { @@ -173,7 +179,7 @@ void GCMKeyStore::DidRemoveKeys(const std::string& app_id, const base::Closure& callback, bool success) { - // TODO(peter): Add a histogram for tracking |success|. + UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.RemoveKeySuccessRate", success); if (success) { key_pairs_.erase(app_id); @@ -206,6 +212,7 @@ } void GCMKeyStore::DidInitialize(bool success) { + UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.InitKeyStoreSuccessRate", success); if (!success) { DVLOG(1) << "Unable to initialize the GCM Key Store."; state_ = State::FAILED; @@ -220,6 +227,7 @@ void GCMKeyStore::DidLoadKeys(bool success, scoped_ptr<std::vector<EncryptionData>> entries) { + UMA_HISTOGRAM_BOOLEAN("GCM.Crypto.LoadKeyStoreSuccessRate", success); if (!success) { DVLOG(1) << "Unable to load entries into the GCM Key Store."; state_ = State::FAILED;
diff --git a/components/gcm_driver/crypto/gcm_key_store_unittest.cc b/components/gcm_driver/crypto/gcm_key_store_unittest.cc index f2cd35da..b5962937 100644 --- a/components/gcm_driver/crypto/gcm_key_store_unittest.cc +++ b/components/gcm_driver/crypto/gcm_key_store_unittest.cc
@@ -9,6 +9,7 @@ #include "base/files/scoped_temp_dir.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/test/histogram_tester.h" #include "base/thread_task_runner_handle.h" #include "components/gcm_driver/crypto/p256_key_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -54,15 +55,22 @@ protected: GCMKeyStore* gcm_key_store() { return gcm_key_store_.get(); } + base::HistogramTester* histogram_tester() { return &histogram_tester_; } private: base::MessageLoop message_loop_; base::ScopedTempDir scoped_temp_dir_; + base::HistogramTester histogram_tester_; scoped_ptr<GCMKeyStore> gcm_key_store_; }; -TEST_F(GCMKeyStoreTest, CreatedByDefault) { +TEST_F(GCMKeyStoreTest, EmptyByDefault) { + // The key store is initialized lazily, so this histogram confirms that + // calling the constructor does not in fact cause initialization. + histogram_tester()->ExpectTotalCount( + "GCM.Crypto.InitKeyStoreSuccessRate", 0); + KeyPair pair; std::string auth_secret; gcm_key_store()->GetKeys(kFakeAppId, @@ -75,6 +83,9 @@ ASSERT_FALSE(pair.IsInitialized()); EXPECT_FALSE(pair.has_type()); EXPECT_EQ(0u, auth_secret.size()); + + histogram_tester()->ExpectBucketCount( + "GCM.Crypto.GetKeySuccessRate", 0, 1); // failure } TEST_F(GCMKeyStoreTest, CreateAndGetKeys) { @@ -96,6 +107,9 @@ ASSERT_GT(auth_secret.size(), 0u); + histogram_tester()->ExpectBucketCount( + "GCM.Crypto.CreateKeySuccessRate", 1, 1); // success + KeyPair read_pair; std::string read_auth_secret; gcm_key_store()->GetKeys(kFakeAppId, @@ -112,6 +126,9 @@ EXPECT_EQ(pair.public_key(), read_pair.public_key()); EXPECT_EQ(auth_secret, read_auth_secret); + + histogram_tester()->ExpectBucketCount( + "GCM.Crypto.GetKeySuccessRate", 1, 1); // failure } TEST_F(GCMKeyStoreTest, KeysPersistenceBetweenInstances) { @@ -126,6 +143,11 @@ ASSERT_TRUE(pair.IsInitialized()); + histogram_tester()->ExpectBucketCount( + "GCM.Crypto.InitKeyStoreSuccessRate", 1, 1); // success + histogram_tester()->ExpectBucketCount( + "GCM.Crypto.LoadKeyStoreSuccessRate", 1, 1); // success + // Create a new GCM Key Store instance. CreateKeyStore(); @@ -141,6 +163,11 @@ ASSERT_TRUE(read_pair.IsInitialized()); EXPECT_TRUE(read_pair.has_type()); EXPECT_GT(read_auth_secret.size(), 0u); + + histogram_tester()->ExpectBucketCount( + "GCM.Crypto.InitKeyStoreSuccessRate", 1, 2); // success + histogram_tester()->ExpectBucketCount( + "GCM.Crypto.LoadKeyStoreSuccessRate", 1, 2); // success } TEST_F(GCMKeyStoreTest, CreateAndRemoveKeys) { @@ -171,6 +198,9 @@ base::RunLoop().RunUntilIdle(); + histogram_tester()->ExpectBucketCount( + "GCM.Crypto.RemoveKeySuccessRate", 1, 1); // success + gcm_key_store()->GetKeys(kFakeAppId, base::Bind(&GCMKeyStoreTest::GotKeys, base::Unretained(this), &read_pair,
diff --git a/components/gcm_driver/fake_gcm_client.cc b/components/gcm_driver/fake_gcm_client.cc index ecd7f35..ef2acf05 100644 --- a/components/gcm_driver/fake_gcm_client.cc +++ b/components/gcm_driver/fake_gcm_client.cc
@@ -160,8 +160,8 @@ void FakeGCMClient::RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) { - recorder_.RecordDecryptionFailure(app_id, reason); + GCMEncryptionProvider::DecryptionResult result) { + recorder_.RecordDecryptionFailure(app_id, result); } void FakeGCMClient::SetRecording(bool recording) {
diff --git a/components/gcm_driver/fake_gcm_client.h b/components/gcm_driver/fake_gcm_client.h index 1e28a2693..badaffd 100644 --- a/components/gcm_driver/fake_gcm_client.h +++ b/components/gcm_driver/fake_gcm_client.h
@@ -59,7 +59,7 @@ const std::string& receiver_id, const OutgoingMessage& message) override; void RecordDecryptionFailure(const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) + GCMEncryptionProvider::DecryptionResult result) override; void SetRecording(bool recording) override; void ClearActivityLogs() override;
diff --git a/components/gcm_driver/fake_gcm_driver.cc b/components/gcm_driver/fake_gcm_driver.cc index 9c68c97..390ca1a 100644 --- a/components/gcm_driver/fake_gcm_driver.cc +++ b/components/gcm_driver/fake_gcm_driver.cc
@@ -76,7 +76,7 @@ void FakeGCMDriver::RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) { + GCMEncryptionProvider::DecryptionResult result) { } void FakeGCMDriver::SetAccountTokens(
diff --git a/components/gcm_driver/fake_gcm_driver.h b/components/gcm_driver/fake_gcm_driver.h index d7c63fb..01a16c9 100644 --- a/components/gcm_driver/fake_gcm_driver.h +++ b/components/gcm_driver/fake_gcm_driver.h
@@ -55,7 +55,7 @@ const std::string& receiver_id, const OutgoingMessage& message) override; void RecordDecryptionFailure(const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) + GCMEncryptionProvider::DecryptionResult result) override; private:
diff --git a/components/gcm_driver/gcm_client.h b/components/gcm_driver/gcm_client.h index 5a24c4d..4b21684 100644 --- a/components/gcm_driver/gcm_client.h +++ b/components/gcm_driver/gcm_client.h
@@ -269,10 +269,10 @@ const std::string& receiver_id, const OutgoingMessage& message) = 0; - // Records a decryption failure due to |reason| for the |app_id|. + // Records a decryption failure due to |result| for the |app_id|. virtual void RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) = 0; + GCMEncryptionProvider::DecryptionResult result) = 0; // Enables or disables internal activity recording. virtual void SetRecording(bool recording) = 0;
diff --git a/components/gcm_driver/gcm_client_impl.cc b/components/gcm_driver/gcm_client_impl.cc index 2140b91a..7d92f0a9a 100644 --- a/components/gcm_driver/gcm_client_impl.cc +++ b/components/gcm_driver/gcm_client_impl.cc
@@ -1154,8 +1154,8 @@ void GCMClientImpl::RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) { - recorder_.RecordDecryptionFailure(app_id, reason); + GCMEncryptionProvider::DecryptionResult result) { + recorder_.RecordDecryptionFailure(app_id, result); } void GCMClientImpl::SetRecording(bool recording) {
diff --git a/components/gcm_driver/gcm_client_impl.h b/components/gcm_driver/gcm_client_impl.h index 81ba8064..e7d33d0 100644 --- a/components/gcm_driver/gcm_client_impl.h +++ b/components/gcm_driver/gcm_client_impl.h
@@ -120,7 +120,7 @@ const std::string& receiver_id, const OutgoingMessage& message) override; void RecordDecryptionFailure(const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) + GCMEncryptionProvider::DecryptionResult result) override; void SetRecording(bool recording) override; void ClearActivityLogs() override;
diff --git a/components/gcm_driver/gcm_driver.cc b/components/gcm_driver/gcm_driver.cc index 9594492..5232c0d8 100644 --- a/components/gcm_driver/gcm_driver.cc +++ b/components/gcm_driver/gcm_driver.cc
@@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/files/file_path.h" #include "base/logging.h" +#include "base/metrics/histogram_macros.h" #include "components/gcm_driver/gcm_app_handler.h" namespace gcm { @@ -273,17 +274,33 @@ void GCMDriver::DispatchMessage(const std::string& app_id, const IncomingMessage& message) { - if (!encryption_provider_.IsEncryptedMessage(message)) { - GetAppHandler(app_id)->OnMessage(app_id, message); - return; + encryption_provider_.DecryptMessage( + app_id, message, base::Bind(&GCMDriver::DispatchMessageInternal, + weak_ptr_factory_.GetWeakPtr(), app_id)); +} + +void GCMDriver::DispatchMessageInternal( + const std::string& app_id, + GCMEncryptionProvider::DecryptionResult result, + const IncomingMessage& message) { + UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.DecryptMessageResult", result, + GCMEncryptionProvider::DECRYPTION_RESULT_LAST + 1); + + switch (result) { + case GCMEncryptionProvider::DECRYPTION_RESULT_UNENCRYPTED: + case GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED: + GetAppHandler(app_id)->OnMessage(app_id, message); + return; + case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER: + case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER: + case GCMEncryptionProvider::DECRYPTION_RESULT_NO_KEYS: + case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_SHARED_SECRET: + case GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_PAYLOAD: + RecordDecryptionFailure(app_id, result); + return; } - encryption_provider_.DecryptMessage( - app_id, message, - base::Bind(&GCMDriver::DispatchMessage, - weak_ptr_factory_.GetWeakPtr(), app_id), - base::Bind(&GCMDriver::RecordDecryptionFailure, - weak_ptr_factory_.GetWeakPtr(), app_id)); + NOTREACHED(); } void GCMDriver::RegisterAfterUnregister(
diff --git a/components/gcm_driver/gcm_driver.h b/components/gcm_driver/gcm_driver.h index 12445a8..c33890a 100644 --- a/components/gcm_driver/gcm_driver.h +++ b/components/gcm_driver/gcm_driver.h
@@ -260,7 +260,7 @@ // Platform-specific implementation of recording message decryption failures. virtual void RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) = 0; + GCMEncryptionProvider::DecryptionResult result) = 0; // Runs the Register callback. void RegisterFinished(const std::string& app_id, @@ -298,6 +298,13 @@ const std::string* sender_id, const UnregisterCallback& callback); + // Dispatches the OnMessage event to the app handler associated with |app_id| + // if |result| indicates that it is safe to do so, or will report a decryption + // failure for the |app_id| otherwise. + void DispatchMessageInternal(const std::string& app_id, + GCMEncryptionProvider::DecryptionResult result, + const IncomingMessage& message); + // Called after unregistration completes in order to trigger the pending // registration. void RegisterAfterUnregister(
diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc index c80309c..8f345423 100644 --- a/components/gcm_driver/gcm_driver_android.cc +++ b/components/gcm_driver/gcm_driver_android.cc
@@ -259,8 +259,8 @@ void GCMDriverAndroid::RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) { - recorder_.RecordDecryptionFailure(app_id, reason); + GCMEncryptionProvider::DecryptionResult result) { + recorder_.RecordDecryptionFailure(app_id, result); } } // namespace gcm
diff --git a/components/gcm_driver/gcm_driver_android.h b/components/gcm_driver/gcm_driver_android.h index 20345db..dab0d14 100644 --- a/components/gcm_driver/gcm_driver_android.h +++ b/components/gcm_driver/gcm_driver_android.h
@@ -93,7 +93,7 @@ const std::string& receiver_id, const OutgoingMessage& message) override; void RecordDecryptionFailure(const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) + GCMEncryptionProvider::DecryptionResult result) override; private:
diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc index 56ee56cc..5ecc3cc 100644 --- a/components/gcm_driver/gcm_driver_desktop.cc +++ b/components/gcm_driver/gcm_driver_desktop.cc
@@ -108,7 +108,7 @@ const std::string& scope); void RecordDecryptionFailure(const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason); + GCMEncryptionProvider::DecryptionResult result); // For testing purpose. Can be called from UI thread. Use with care. GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); } @@ -501,9 +501,9 @@ void GCMDriverDesktop::IOWorker::RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) { + GCMEncryptionProvider::DecryptionResult result) { DCHECK(io_thread_->RunsTasksOnCurrentThread()); - gcm_client_->RecordDecryptionFailure(app_id, reason); + gcm_client_->RecordDecryptionFailure(app_id, result); } GCMDriverDesktop::GCMDriverDesktop( @@ -734,13 +734,13 @@ void GCMDriverDesktop::RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) { + GCMEncryptionProvider::DecryptionResult result) { DCHECK(ui_thread_->RunsTasksOnCurrentThread()); io_thread_->PostTask( FROM_HERE, base::Bind(&GCMDriverDesktop::IOWorker::RecordDecryptionFailure, base::Unretained(io_worker_.get()), - app_id, reason)); + app_id, result)); } GCMClient* GCMDriverDesktop::GetGCMClientForTesting() const {
diff --git a/components/gcm_driver/gcm_driver_desktop.h b/components/gcm_driver/gcm_driver_desktop.h index 35ffda4..a162df1 100644 --- a/components/gcm_driver/gcm_driver_desktop.h +++ b/components/gcm_driver/gcm_driver_desktop.h
@@ -122,7 +122,7 @@ const std::string& receiver_id, const OutgoingMessage& message) override; void RecordDecryptionFailure(const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) + GCMEncryptionProvider::DecryptionResult result) override; private:
diff --git a/components/gcm_driver/gcm_stats_recorder_android.cc b/components/gcm_driver/gcm_stats_recorder_android.cc index 918638b..6a6b09fd 100644 --- a/components/gcm_driver/gcm_stats_recorder_android.cc +++ b/components/gcm_driver/gcm_stats_recorder_android.cc
@@ -123,14 +123,16 @@ void GCMStatsRecorderAndroid::RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) { + GCMEncryptionProvider::DecryptionResult result) { + DCHECK_NE(result, GCMEncryptionProvider::DECRYPTION_RESULT_UNENCRYPTED); + DCHECK_NE(result, GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED); if (!is_recording_) return; DecryptionFailureActivity activity; activity.app_id = app_id; activity.details = - GCMEncryptionProvider::ToDecryptionFailureDetailsString(reason); + GCMEncryptionProvider::ToDecryptionResultDetailsString(result); decryption_failure_activities_.push_front(activity); if (decryption_failure_activities_.size() > MAX_LOGGED_ACTIVITY_COUNT)
diff --git a/components/gcm_driver/gcm_stats_recorder_android.h b/components/gcm_driver/gcm_stats_recorder_android.h index ea339aa..c0a6b398 100644 --- a/components/gcm_driver/gcm_stats_recorder_android.h +++ b/components/gcm_driver/gcm_stats_recorder_android.h
@@ -59,9 +59,9 @@ void RecordDataMessageReceived(const std::string& app_id, int message_byte_size); - // Records a message decryption failure caused by |reason| for |app_id|. + // Records a message decryption failure caused by |result| for |app_id|. void RecordDecryptionFailure(const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason); + GCMEncryptionProvider::DecryptionResult result); bool is_recording() const { return is_recording_; } void set_is_recording(bool recording) { is_recording_ = recording; }
diff --git a/components/gcm_driver/gcm_stats_recorder_android_unittest.cc b/components/gcm_driver/gcm_stats_recorder_android_unittest.cc index addca7437..d71f466 100644 --- a/components/gcm_driver/gcm_stats_recorder_android_unittest.cc +++ b/components/gcm_driver/gcm_stats_recorder_android_unittest.cc
@@ -57,7 +57,7 @@ EXPECT_EQ(5u, activity_recorded_calls()); recorder.RecordDecryptionFailure( - kTestAppId, GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_PAYLOAD); + kTestAppId, GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_PAYLOAD); EXPECT_EQ(6u, activity_recorded_calls()); RecordedActivities activities;
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.cc b/components/gcm_driver/gcm_stats_recorder_impl.cc index 803e6baf..d632bd3 100644 --- a/components/gcm_driver/gcm_stats_recorder_impl.cc +++ b/components/gcm_driver/gcm_stats_recorder_impl.cc
@@ -173,7 +173,9 @@ void GCMStatsRecorderImpl::RecordDecryptionFailure( const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason) { + GCMEncryptionProvider::DecryptionResult result) { + DCHECK_NE(result, GCMEncryptionProvider::DECRYPTION_RESULT_UNENCRYPTED); + DCHECK_NE(result, GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED); if (!is_recording_) return; @@ -182,7 +184,7 @@ &decryption_failure_activities_, data); inserted_data->app_id = app_id; inserted_data->details = - GCMEncryptionProvider::ToDecryptionFailureDetailsString(reason); + GCMEncryptionProvider::ToDecryptionResultDetailsString(result); NotifyActivityRecorded(); }
diff --git a/components/gcm_driver/gcm_stats_recorder_impl.h b/components/gcm_driver/gcm_stats_recorder_impl.h index 5cbf63a..a2c46d1f 100644 --- a/components/gcm_driver/gcm_stats_recorder_impl.h +++ b/components/gcm_driver/gcm_stats_recorder_impl.h
@@ -39,9 +39,9 @@ // Clear all recorded activities. void Clear(); - // Records a message decryption failure caused by |reason| for |app_id|. + // Records a message decryption failure caused by |result| for |app_id|. void RecordDecryptionFailure(const std::string& app_id, - GCMEncryptionProvider::DecryptionFailure reason); + GCMEncryptionProvider::DecryptionResult result); // GCMStatsRecorder implementation: void RecordCheckinInitiated(uint64_t android_id) override;
diff --git a/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc b/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc index 5c6fe253..eae8909 100644 --- a/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc +++ b/components/gcm_driver/gcm_stats_recorder_impl_unittest.cc
@@ -99,8 +99,8 @@ static const char kIncomingSendErrorEvent[] = "Received 'send error' msg"; static const char kIncomingSendErrorDetails[] = ""; -static const GCMEncryptionProvider::DecryptionFailure kDecryptionFailureReason = - GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_PAYLOAD; +static const GCMEncryptionProvider::DecryptionResult kDecryptionResultFailure = + GCMEncryptionProvider::DECRYPTION_RESULT_INVALID_PAYLOAD; } // namespace @@ -302,8 +302,8 @@ EXPECT_EQ(kAppId, queue.front().app_id) << remark; EXPECT_EQ( - GCMEncryptionProvider::ToDecryptionFailureDetailsString( - kDecryptionFailureReason), + GCMEncryptionProvider::ToDecryptionResultDetailsString( + kDecryptionResultFailure), queue.front().details) << remark; } @@ -547,7 +547,7 @@ } TEST_F(GCMStatsRecorderImplTest, RecordDecryptionFailureTest) { - recorder_.RecordDecryptionFailure(kAppId, kDecryptionFailureReason); + recorder_.RecordDecryptionFailure(kAppId, kDecryptionResultFailure); VerifyRecordedDecryptionFailureCount(1); VerifyRecordedDecryptionFailure("1st call");
diff --git a/components/history.gypi b/components/history.gypi index 4330ad16..0948c70 100644 --- a/components/history.gypi +++ b/components/history.gypi
@@ -109,8 +109,6 @@ 'history/core/browser/visit_database.h', 'history/core/browser/visit_delegate.cc', 'history/core/browser/visit_delegate.h', - 'history/core/browser/visit_filter.cc', - 'history/core/browser/visit_filter.h', 'history/core/browser/visit_tracker.cc', 'history/core/browser/visit_tracker.h', 'history/core/browser/visitsegment_database.cc',
diff --git a/components/history/content/browser/content_history_backend_db_unittest.cc b/components/history/content/browser/content_history_backend_db_unittest.cc index 0f79c6f..6c43f4c 100644 --- a/components/history/content/browser/content_history_backend_db_unittest.cc +++ b/components/history/content/browser/content_history_backend_db_unittest.cc
@@ -51,31 +51,32 @@ // This represents a list of all reasons we've previously used; // Do Not Remove Any Entries From This List. const InterruptReasonAssociation historical_reasons[] = { - {"FILE_FAILED", 1}, - {"FILE_ACCESS_DENIED", 2}, - {"FILE_NO_SPACE", 3}, - {"FILE_NAME_TOO_LONG", 5}, - {"FILE_TOO_LARGE", 6}, - {"FILE_VIRUS_INFECTED", 7}, - {"FILE_TRANSIENT_ERROR", 10}, - {"FILE_BLOCKED", 11}, - {"FILE_SECURITY_CHECK_FAILED", 12}, - {"FILE_TOO_SHORT", 13}, - {"NETWORK_FAILED", 20}, - {"NETWORK_TIMEOUT", 21}, - {"NETWORK_DISCONNECTED", 22}, - {"NETWORK_SERVER_DOWN", 23}, - {"NETWORK_INVALID_REQUEST", 24}, - {"SERVER_FAILED", 30}, - {"SERVER_NO_RANGE", 31}, - {"SERVER_PRECONDITION", 32}, - {"SERVER_BAD_CONTENT", 33}, - {"SERVER_UNAUTHORIZED", 34}, - {"SERVER_CERT_PROBLEM", 35}, - {"SERVER_FORBIDDEN", 36}, - {"USER_CANCELED", 40}, - {"USER_SHUTDOWN", 41}, - {"CRASH", 50}, + {"FILE_FAILED", 1}, + {"FILE_ACCESS_DENIED", 2}, + {"FILE_NO_SPACE", 3}, + {"FILE_NAME_TOO_LONG", 5}, + {"FILE_TOO_LARGE", 6}, + {"FILE_VIRUS_INFECTED", 7}, + {"FILE_TRANSIENT_ERROR", 10}, + {"FILE_BLOCKED", 11}, + {"FILE_SECURITY_CHECK_FAILED", 12}, + {"FILE_TOO_SHORT", 13}, + {"NETWORK_FAILED", 20}, + {"NETWORK_TIMEOUT", 21}, + {"NETWORK_DISCONNECTED", 22}, + {"NETWORK_SERVER_DOWN", 23}, + {"NETWORK_INVALID_REQUEST", 24}, + {"SERVER_FAILED", 30}, + {"SERVER_NO_RANGE", 31}, + {"SERVER_PRECONDITION", 32}, + {"SERVER_BAD_CONTENT", 33}, + {"SERVER_UNAUTHORIZED", 34}, + {"SERVER_CERT_PROBLEM", 35}, + {"SERVER_FORBIDDEN", 36}, + {"SERVER_UNREACHABLE", 37}, + {"USER_CANCELED", 40}, + {"USER_SHUTDOWN", 41}, + {"CRASH", 50}, }; // Make sure no one has changed a DownloadInterruptReason we've previously
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn index 1c120fc0..bd330ed2 100644 --- a/components/history/core/browser/BUILD.gn +++ b/components/history/core/browser/BUILD.gn
@@ -76,8 +76,6 @@ "visit_database.h", "visit_delegate.cc", "visit_delegate.h", - "visit_filter.cc", - "visit_filter.h", "visit_tracker.cc", "visit_tracker.h", "visitsegment_database.cc", @@ -154,7 +152,6 @@ "url_database_unittest.cc", "url_utils_unittest.cc", "visit_database_unittest.cc", - "visit_filter_unittest.cc", "visit_tracker_unittest.cc", "web_history_service_unittest.cc", ]
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc index bfbbf2c8..89a7baa4 100644 --- a/components/history/core/browser/history_backend.cc +++ b/components/history/core/browser/history_backend.cc
@@ -39,7 +39,6 @@ #include "components/history/core/browser/page_usage_data.h" #include "components/history/core/browser/typed_url_syncable_service.h" #include "components/history/core/browser/url_utils.h" -#include "components/history/core/browser/visit_filter.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "sql/error_delegate_util.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -1368,85 +1367,6 @@ } } -void HistoryBackend::QueryFilteredURLs(int result_count, - const VisitFilter& filter, - bool extended_info, - FilteredURLList* result) { - DCHECK(result); - base::Time request_start = base::Time::Now(); - - result->clear(); - if (!db_) { - // No History Database - return an empty list. - return; - } - - VisitVector visits; - db_->GetDirectVisitsDuringTimes(filter, 0, &visits); - - std::map<URLID, double> score_map; - for (size_t i = 0; i < visits.size(); ++i) { - score_map[visits[i].url_id] += filter.GetVisitScore(visits[i]); - } - - // TODO(georgey): experiment with visit_segment database granularity (it is - // currently 24 hours) to use it directly instead of using visits database, - // which is considerably slower. - ScopedVector<PageUsageData> data; - data.reserve(score_map.size()); - for (std::map<URLID, double>::iterator it = score_map.begin(); - it != score_map.end(); ++it) { - PageUsageData* pud = new PageUsageData(it->first); - pud->SetScore(it->second); - data.push_back(pud); - } - - // Limit to the top |result_count| results. - std::sort(data.begin(), data.end(), PageUsageData::Predicate); - DCHECK_GE(result_count, 0); - if (result_count && data.size() > static_cast<size_t>(result_count)) - data.resize(result_count); - - for (size_t i = 0; i < data.size(); ++i) { - URLRow info; - if (db_->GetURLRow(data[i]->GetID(), &info)) { - data[i]->SetURL(info.url()); - data[i]->SetTitle(info.title()); - } - } - - for (size_t i = 0; i < data.size(); ++i) { - PageUsageData* current_data = data[i]; - FilteredURL url(*current_data); - - if (extended_info) { - VisitVector visits; - db_->GetVisitsForURL(current_data->GetID(), &visits); - if (visits.size() > 0) { - url.extended_info.total_visits = visits.size(); - for (size_t i = 0; i < visits.size(); ++i) { - url.extended_info.duration_opened += - visits[i].visit_duration.InSeconds(); - if (visits[i].visit_time > url.extended_info.last_visit_time) { - url.extended_info.last_visit_time = visits[i].visit_time; - } - } - // TODO(macourteau): implement the url.extended_info.visits stat. - } - } - result->push_back(url); - } - - int delta_time = std::max( - 1, std::min(999, static_cast<int>((base::Time::Now() - request_start) - .InMilliseconds()))); - STATIC_HISTOGRAM_POINTER_BLOCK( - "NewTabPage.SuggestedSitesLoadTime", Add(delta_time), - base::LinearHistogram::FactoryGet( - "NewTabPage.SuggestedSitesLoadTime", 1, 1000, 100, - base::Histogram::kUmaTargetedHistogramFlag)); -} - void HistoryBackend::GetRedirectsFromSpecificVisit(VisitID cur_visit, RedirectList* redirects) { // Follow any redirects from the given visit and add them to the list.
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h index f32fcbfd..2af014a 100644 --- a/components/history/core/browser/history_backend.h +++ b/components/history/core/browser/history_backend.h
@@ -58,7 +58,6 @@ class HistoryDBTask; class InMemoryHistoryBackend; class TypedUrlSyncableService; -class VisitFilter; class HistoryBackendHelper; // The maximum number of icons URLs per page which can be stored in the @@ -277,14 +276,6 @@ int days_back, MostVisitedURLList* result); - // Request the |result_count| URLs and the chain of redirects - // leading to each of these URLs, filterd and sorted based on the |filter|. - // If |debug| is enabled, additional data will be computed and provided. - void QueryFilteredURLs(int result_count, - const VisitFilter& filter, - bool debug, - FilteredURLList* result); - // Statistics ---------------------------------------------------------------- // Gets the number of URLs as seen in chrome://history within the time range
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc index a1574fb..d5df317 100644 --- a/components/history/core/browser/history_backend_unittest.cc +++ b/components/history/core/browser/history_backend_unittest.cc
@@ -41,7 +41,6 @@ #include "components/history/core/browser/in_memory_history_backend.h" #include "components/history/core/browser/keyword_search_term.h" #include "components/history/core/browser/visit_delegate.h" -#include "components/history/core/browser/visit_filter.h" #include "components/history/core/test/database_test_utils.h" #include "components/history/core/test/history_client_fake_bookmarks.h" #include "components/history/core/test/test_history_database.h" @@ -3017,157 +3016,6 @@ EXPECT_TRUE(bitmap_results.empty()); } -TEST_F(HistoryBackendTest, QueryFilteredURLs) { - const char* google = "http://www.google.com/"; - const char* yahoo = "http://www.yahoo.com/"; - const char* yahoo_sports = "http://sports.yahoo.com/"; - const char* yahoo_sports_with_article1 = - "http://sports.yahoo.com/article1.htm"; - const char* yahoo_sports_with_article2 = - "http://sports.yahoo.com/article2.htm"; - const char* yahoo_sports_soccer = "http://sports.yahoo.com/soccer"; - const char* apple = "http://www.apple.com/"; - - // Clear all history. - backend_->DeleteAllHistory(); - - base::Time tested_time = base::Time::Now().LocalMidnight() + - base::TimeDelta::FromHours(4); - base::TimeDelta half_an_hour = base::TimeDelta::FromMinutes(30); - base::TimeDelta one_hour = base::TimeDelta::FromHours(1); - base::TimeDelta one_day = base::TimeDelta::FromDays(1); - - const ui::PageTransition kTypedTransition = - ui::PAGE_TRANSITION_TYPED; - const ui::PageTransition kKeywordGeneratedTransition = - ui::PAGE_TRANSITION_KEYWORD_GENERATED; - - const char* redirect_sequence[2]; - redirect_sequence[1] = NULL; - - redirect_sequence[0] = google; - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, kTypedTransition, - tested_time - one_day - half_an_hour * 2); - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, - kTypedTransition, tested_time - one_day); - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, - kTypedTransition, tested_time - half_an_hour / 2); - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, - kTypedTransition, tested_time); - - // Add a visit with a transition that will make sure that no segment gets - // created for this page (so the subsequent entries will have different URLIDs - // and SegmentIDs). - redirect_sequence[0] = apple; - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, kKeywordGeneratedTransition, - tested_time - one_day + one_hour * 6); - - redirect_sequence[0] = yahoo; - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, kTypedTransition, - tested_time - one_day + half_an_hour); - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, kTypedTransition, - tested_time - one_day + half_an_hour * 2); - - redirect_sequence[0] = yahoo_sports; - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, kTypedTransition, - tested_time - one_day - half_an_hour * 2); - AddRedirectChainWithTransitionAndTime( - redirect_sequence, 0, kTypedTransition, - tested_time - one_day); - int transition1, transition2; - AddClientRedirect(GURL(yahoo_sports), GURL(yahoo_sports_with_article1), false, - tested_time - one_day + half_an_hour, - &transition1, &transition2); - AddClientRedirect(GURL(yahoo_sports_with_article1), - GURL(yahoo_sports_with_article2), - false, - tested_time - one_day + half_an_hour * 2, - &transition1, &transition2); - - redirect_sequence[0] = yahoo_sports_soccer; - AddRedirectChainWithTransitionAndTime(redirect_sequence, 0, - kTypedTransition, - tested_time - half_an_hour); - backend_->Commit(); - - VisitFilter filter; - FilteredURLList filtered_list; - // Time limit is |tested_time| +/- 45 min. - base::TimeDelta three_quarters_of_an_hour = base::TimeDelta::FromMinutes(45); - filter.SetFilterTime(tested_time); - filter.SetFilterWidth(three_quarters_of_an_hour); - backend_->QueryFilteredURLs(100, filter, false, &filtered_list); - - ASSERT_EQ(4U, filtered_list.size()); - EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); - EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec()); - EXPECT_EQ(std::string(yahoo), filtered_list[2].url.spec()); - EXPECT_EQ(std::string(yahoo_sports), filtered_list[3].url.spec()); - - // Time limit is between |tested_time| and |tested_time| + 2 hours. - filter.SetFilterTime(tested_time + one_hour); - filter.SetFilterWidth(one_hour); - backend_->QueryFilteredURLs(100, filter, false, &filtered_list); - - ASSERT_EQ(3U, filtered_list.size()); - EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); - EXPECT_EQ(std::string(yahoo), filtered_list[1].url.spec()); - EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec()); - - // Time limit is between |tested_time| - 2 hours and |tested_time|. - filter.SetFilterTime(tested_time - one_hour); - filter.SetFilterWidth(one_hour); - backend_->QueryFilteredURLs(100, filter, false, &filtered_list); - - ASSERT_EQ(3U, filtered_list.size()); - EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); - EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec()); - EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec()); - - filter.ClearFilters(); - base::Time::Exploded exploded_time; - tested_time.LocalExplode(&exploded_time); - - // Today. - filter.SetFilterTime(tested_time); - filter.SetDayOfTheWeekFilter(static_cast<int>(exploded_time.day_of_week)); - backend_->QueryFilteredURLs(100, filter, false, &filtered_list); - - ASSERT_EQ(2U, filtered_list.size()); - EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); - EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec()); - - // Today + time limit - only yahoo_sports_soccer should fit. - filter.SetFilterTime(tested_time - base::TimeDelta::FromMinutes(40)); - filter.SetFilterWidth(base::TimeDelta::FromMinutes(20)); - backend_->QueryFilteredURLs(100, filter, false, &filtered_list); - - ASSERT_EQ(1U, filtered_list.size()); - EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[0].url.spec()); - - // Make sure we get debug data if we request it. - filter.SetFilterTime(tested_time); - filter.SetFilterWidth(one_hour * 2); - backend_->QueryFilteredURLs(100, filter, true, &filtered_list); - - // If the SegmentID is used by QueryFilteredURLs when generating the debug - // data instead of the URLID, the |total_visits| for the |yahoo_sports_soccer| - // entry will be zero instead of 1. - ASSERT_GE(filtered_list.size(), 2U); - EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); - EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec()); - EXPECT_EQ(4U, filtered_list[0].extended_info.total_visits); - EXPECT_EQ(1U, filtered_list[1].extended_info.total_visits); -} - TEST_F(HistoryBackendTest, TopHosts) { std::vector<GURL> urls; urls.push_back(GURL("http://cnn.com/us"));
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc index 288c011..22f5b67 100644 --- a/components/history/core/browser/history_service.cc +++ b/components/history/core/browser/history_service.cc
@@ -46,7 +46,6 @@ #include "components/history/core/browser/keyword_search_term.h" #include "components/history/core/browser/visit_database.h" #include "components/history/core/browser/visit_delegate.h" -#include "components/history/core/browser/visit_filter.h" #include "components/history/core/browser/web_history_service.h" #include "components/history/core/common/thumbnail_score.h" #include "sync/api/sync_error_factory.h" @@ -828,22 +827,6 @@ base::Bind(callback, base::Owned(result))); } -base::CancelableTaskTracker::TaskId HistoryService::QueryFilteredURLs( - int result_count, - const VisitFilter& filter, - bool extended_info, - const QueryFilteredURLsCallback& callback, - base::CancelableTaskTracker* tracker) { - DCHECK(thread_) << "History service being called after cleanup"; - DCHECK(thread_checker_.CalledOnValidThread()); - FilteredURLList* result = new FilteredURLList(); - return tracker->PostTaskAndReply( - thread_->task_runner().get(), FROM_HERE, - base::Bind(&HistoryBackend::QueryFilteredURLs, history_backend_.get(), - result_count, filter, extended_info, base::Unretained(result)), - base::Bind(callback, base::Owned(result))); -} - void HistoryService::Cleanup() { DCHECK(thread_checker_.CalledOnValidThread()); if (!thread_) {
diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h index 9c8af636..d852485 100644 --- a/components/history/core/browser/history_service.h +++ b/components/history/core/browser/history_service.h
@@ -73,7 +73,6 @@ class PageUsageData; class URLDatabase; class VisitDelegate; -class VisitFilter; class WebHistoryService; // The history service records page titles, and visit times, as well as @@ -150,7 +149,7 @@ // Returns a pointer to the TypedUrlSyncableService owned by HistoryBackend. // This method should only be called from the history thread, because the // returned service is intended to be accessed only via the history thread. - virtual TypedUrlSyncableService* GetTypedUrlSyncableService() const; + TypedUrlSyncableService* GetTypedUrlSyncableService() const; // KeyedService: void Shutdown() override; @@ -338,22 +337,6 @@ const QueryMostVisitedURLsCallback& callback, base::CancelableTaskTracker* tracker); - // Request the |result_count| URLs filtered and sorted based on the |filter|. - // If |extended_info| is true, additional data will be provided in the - // results. Computing this additional data is expensive, likely to become - // more expensive as additional data points are added in future changes, and - // not useful in most cases. Set |extended_info| to true only if you - // explicitly require the additional data. - typedef base::Callback<void(const FilteredURLList*)> - QueryFilteredURLsCallback; - - base::CancelableTaskTracker::TaskId QueryFilteredURLs( - int result_count, - const VisitFilter& filter, - bool extended_info, - const QueryFilteredURLsCallback& callback, - base::CancelableTaskTracker* tracker); - // Statistics ---------------------------------------------------------------- // Gets the number of URLs as seen in chrome://history within the time range
diff --git a/components/history/core/browser/visit_database.cc b/components/history/core/browser/visit_database.cc index 3df3a20..3d99885 100644 --- a/components/history/core/browser/visit_database.cc +++ b/components/history/core/browser/visit_database.cc
@@ -16,7 +16,6 @@ #include "base/strings/string_number_conversions.h" #include "base/time/time.h" #include "components/history/core/browser/url_database.h" -#include "components/history/core/browser/visit_filter.h" #include "sql/statement.h" #include "ui/base/page_transition_types.h" #include "url/url_constants.h" @@ -374,39 +373,6 @@ return FillVisitVectorWithOptions(statement, options, visits); } -void VisitDatabase::GetDirectVisitsDuringTimes(const VisitFilter& time_filter, - int max_results, - VisitVector* visits) { - visits->clear(); - if (max_results) - visits->reserve(max_results); - for (VisitFilter::TimeVector::const_iterator it = time_filter.times().begin(); - it != time_filter.times().end(); ++it) { - sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, - "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits " - "WHERE visit_time >= ? AND visit_time < ? " - "AND (transition & ?) != 0 " // CHAIN_START - "AND (transition & ?) IN (?, ?) " // TYPED or AUTO_BOOKMARK only - "ORDER BY visit_time DESC, id DESC")); - - statement.BindInt64(0, it->first.ToInternalValue()); - statement.BindInt64(1, it->second.ToInternalValue()); - statement.BindInt(2, ui::PAGE_TRANSITION_CHAIN_START); - statement.BindInt(3, ui::PAGE_TRANSITION_CORE_MASK); - statement.BindInt(4, ui::PAGE_TRANSITION_TYPED); - statement.BindInt(5, ui::PAGE_TRANSITION_AUTO_BOOKMARK); - - while (statement.Step()) { - VisitRow visit; - FillVisitRow(statement, &visit); - visits->push_back(visit); - - if (max_results > 0 && static_cast<int>(visits->size()) >= max_results) - return; - } - } -} - VisitID VisitDatabase::GetMostRecentVisitForURL(URLID url_id, VisitRow* visit_row) { // The visit_time values can be duplicated in a redirect chain, so we sort
diff --git a/components/history/core/browser/visit_database.h b/components/history/core/browser/visit_database.h index 75beab8..1bb743a8 100644 --- a/components/history/core/browser/visit_database.h +++ b/components/history/core/browser/visit_database.h
@@ -17,8 +17,6 @@ namespace history { -class VisitFilter; - // A visit database is one which stores visits for URLs, that is, times and // linking information. A visit database must also be a URLDatabase, as this // modifies tables used by URLs directly and could be thought of as inheriting @@ -121,17 +119,6 @@ bool GetVisibleVisitsInRange(const QueryOptions& options, VisitVector* visits); - // Fills all visits in the given time ranges into the given vector that are - // visits made directly by the user (typed or bookmarked visits only). The - // begin time is inclusive, the end time is exclusive. - // - // Up to |max_count| visits will be returned. If there are more visits than - // that, the most recent |max_count| will be returned. If 0, all visits in the - // range will be computed. - void GetDirectVisitsDuringTimes(const VisitFilter& time_filter, - int max_count, - VisitVector* visits); - // Returns the visit ID for the most recent visit of the given URL ID, or 0 // if there is no visit for the URL. //
diff --git a/components/history/core/browser/visit_filter.cc b/components/history/core/browser/visit_filter.cc deleted file mode 100644 index 75582a25..0000000 --- a/components/history/core/browser/visit_filter.cc +++ /dev/null
@@ -1,358 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/history/core/browser/visit_filter.h" - -#include <math.h> - -#include <algorithm> - -#include "base/logging.h" -#include "base/time/time.h" -#include "components/history/core/browser/history_types.h" - -namespace history { - -const double kLn2 = 0.6931471805599453; - -VisitFilter::VisitFilter() - : day_(DAY_UNDEFINED), - max_results_(0), - sorting_order_(ORDER_BY_RECENCY) { -} - -VisitFilter::~VisitFilter() { -} - -void VisitFilter::SetFilterTime(const base::Time& filter_time) { - filter_time_ = filter_time; - UpdateTimeVector(); -} - -void VisitFilter::SetFilterWidth(const base::TimeDelta& filter_width) { - filter_width_ = filter_width; - UpdateTimeVector(); -} - -void VisitFilter::SetDayOfTheWeekFilter(int day) { - day_ = day; - UpdateTimeVector(); -} - -void VisitFilter::SetDayTypeFilter(bool workday) { - day_ = workday ? WORKDAY : HOLIDAY; - UpdateTimeVector(); -} - -void VisitFilter::ClearFilters() { - filter_time_ = base::Time(); - filter_width_ = base::TimeDelta::FromHours(0); - day_ = DAY_UNDEFINED; - UpdateTimeVector(); -} - -bool VisitFilter::UpdateTimeVector() { - - TimeVector days_of_the_week; - if (day_ >= 0 && day_ <= 6) { - GetTimesOnTheDayOfTheWeek(day_, filter_time_, max_results_, - &days_of_the_week); - } else if (day_ == WORKDAY || day_ == HOLIDAY) { - GetTimesOnTheSameDayType( - (day_ == WORKDAY), filter_time_, max_results_, &days_of_the_week); - } - - TimeVector times_of_the_day; - if (filter_width_ != base::TimeDelta::FromSeconds(0)) { - if (sorting_order_ == ORDER_BY_TIME_GAUSSIAN) { - // Limit queries to 5 standard deviations. - GetTimesInRange(filter_time_ - 5 * filter_width_, - filter_time_ + 5 * filter_width_, - max_results_, ×_of_the_day); - } else { - GetTimesInRange(filter_time_ - filter_width_, - filter_time_ + filter_width_, - max_results_, ×_of_the_day); - } - } - - if (times_of_the_day.empty()) { - if (days_of_the_week.empty()) - times_.clear(); - else - times_.swap(days_of_the_week); - } else { - if (days_of_the_week.empty()) - times_.swap(times_of_the_day); - else - IntersectTimeVectors(times_of_the_day, days_of_the_week, ×_); - } - - return !times_.empty(); -} - -// static -void VisitFilter::GetTimesInRange(base::Time begin_time_of_the_day, - base::Time end_time_of_the_day, - size_t max_results, - TimeVector* times) { - DCHECK(times); - times->clear(); - times->reserve(max_results); - const size_t kMaxReturnedResults = 62; // 2 months (<= 62 days). - - if (!max_results) - max_results = kMaxReturnedResults; - - // If range is more than 24 hours, return a contiguous interval covering - // |max_results| days. - base::TimeDelta one_day = base::TimeDelta::FromDays(1); - if (end_time_of_the_day - begin_time_of_the_day >= one_day) { - times->push_back( - std::make_pair(begin_time_of_the_day - one_day * (max_results - 1), - end_time_of_the_day)); - return; - } - - for (int i = 0; i < static_cast<int>(max_results); ++i) { - times->push_back( - std::make_pair(begin_time_of_the_day - base::TimeDelta::FromDays(i), - end_time_of_the_day - base::TimeDelta::FromDays(i))); - } -} - -double VisitFilter::GetVisitScore(const VisitRow& visit) const { - // Decay score by half each week. - base::TimeDelta time_passed = filter_time_ - visit.visit_time; - // Clamp to 0 in case time jumps backwards (e.g. due to DST). - double decay_exponent = std::max(0.0, kLn2 * static_cast<double>( - time_passed.InMicroseconds()) / base::Time::kMicrosecondsPerWeek); - double staleness = 1.0 / exp(decay_exponent); - - double score = 0; - switch (sorting_order()) { - case ORDER_BY_RECENCY: - score = 1.0; // Let the staleness factor take care of it. - break; - case ORDER_BY_VISIT_COUNT: - score = 1.0; // Every visit counts the same. - staleness = 1.0; // No decay on this one. - break; - case ORDER_BY_TIME_GAUSSIAN: { - double offset = - GetTimeOfDayDifference(filter_time_, - visit.visit_time).InMicroseconds(); - double sd = filter_width_.InMicroseconds(); - - // Calculate score using the normal distribution density function. - score = exp(-(offset * offset) / (2 * sd * sd)); - break; - } - case ORDER_BY_TIME_LINEAR: { - base::TimeDelta offset = GetTimeOfDayDifference(filter_time_, - visit.visit_time); - if (offset > filter_width_) { - score = 0; - } else { - score = 1 - offset.InMicroseconds() / static_cast<double>( - filter_width_.InMicroseconds()); - } - break; - } - case ORDER_BY_DURATION_SPENT: - default: - NOTREACHED() << "Not implemented!"; - } - return staleness * score; -} - -base::TimeDelta -VisitFilter::GetTimeOfDayDifference(base::Time t1, base::Time t2) { - base::TimeDelta time_of_day1 = t1 - t1.LocalMidnight(); - base::TimeDelta time_of_day2 = t2 - t2.LocalMidnight(); - - base::TimeDelta difference; - if (time_of_day1 < time_of_day2) - difference = time_of_day2 - time_of_day1; - else - difference = time_of_day1 - time_of_day2; - - // If the difference is more than 12 hours, we'll get closer by 'wrapping' - // around the day barrier. - if (difference > base::TimeDelta::FromHours(12)) - difference = base::TimeDelta::FromHours(24) - difference; - - return difference; -} - -// static -void VisitFilter::GetTimesOnTheDayOfTheWeek(int day, - base::Time week, - size_t max_results, - TimeVector* times) { - DCHECK(times); - - base::Time::Exploded exploded_time; - if (week.is_null()) - week = base::Time::Now(); - week.LocalExplode(&exploded_time); - base::TimeDelta shift = base::TimeDelta::FromDays( - exploded_time.day_of_week - day); - - base::Time day_base = week.LocalMidnight(); - day_base -= shift; - - times->clear(); - times->reserve(max_results); - - base::TimeDelta one_day = base::TimeDelta::FromDays(1); - - const size_t kMaxReturnedResults = 9; // 2 months (<= 9 weeks). - - if (!max_results) - max_results = kMaxReturnedResults; - - for (int i = 0; i < static_cast<int>(max_results); ++i) { - times->push_back( - std::make_pair(day_base - base::TimeDelta::FromDays(i * 7), - day_base + one_day - base::TimeDelta::FromDays(i * 7))); - } -} - -// static -void VisitFilter::GetTimesOnTheSameDayType(bool workday, - base::Time week, - size_t max_results, - TimeVector* times) { - DCHECK(times); - if (week.is_null()) - week = base::Time::Now(); - // TODO(georgey): internationalize workdays/weekends/holidays. - if (!workday) { - TimeVector sunday; - TimeVector saturday; - base::Time::Exploded exploded_time; - week.LocalExplode(&exploded_time); - - GetTimesOnTheDayOfTheWeek(exploded_time.day_of_week ? 7 : 0, week, - max_results, &sunday); - GetTimesOnTheDayOfTheWeek(exploded_time.day_of_week ? 6 : -1, week, - max_results, &saturday); - UniteTimeVectors(sunday, saturday, times); - if (max_results && times->size() > max_results) - times->resize(max_results); - } else { - TimeVector vectors[3]; - GetTimesOnTheDayOfTheWeek(1, week, max_results, &vectors[0]); - for (int i = 2; i <= 5; ++i) { - GetTimesOnTheDayOfTheWeek(i, week, max_results, &vectors[(i - 1) % 3]); - UniteTimeVectors(vectors[(i - 2) % 3], vectors[(i - 1) % 3], - &vectors[i % 3]); - if (max_results && vectors[i % 3].size() > max_results) - vectors[i % 3].resize(max_results); - vectors[i % 3].swap(vectors[(i - 1) % 3]); - } - // 1 == 5 - 1 % 3 - times->swap(vectors[1]); - } -} - -// static -bool VisitFilter::UniteTimeVectors(const TimeVector& vector1, - const TimeVector& vector2, - TimeVector* result) { - // The vectors are sorted going back in time, but each pair has |first| as the - // beginning of time period and |second| as the end, for example: - // { 19:20, 20:00 } { 17:00, 18:10 } { 11:33, 11:35 }... - // The pairs in one vector are guaranteed not to intersect. - DCHECK(result); - result->clear(); - result->reserve(vector1.size() + vector2.size()); - - size_t vi[2]; - const TimeVector* vectors[2] = { &vector1, &vector2 }; - for (vi[0] = 0, vi[1] = 0; - vi[0] < vectors[0]->size() && vi[1] < vectors[1]->size();) { - std::pair<base::Time, base::Time> united_timeslot; - // Check which element occurs later (for the following diagrams time is - // increasing to the right, 'f' means first, 's' means second). - // after the folowing 2 statements: - // vectors[iterator_index][vi[iterator_index]] f---s - // vectors[1 - iterator_index][vi[1 - iterator_index]] f---s - // united_timeslot f---s - // or - // vectors[iterator_index][vi[iterator_index]] f---s - // vectors[1 - iterator_index][vi[1 - iterator_index]] f-s - // united_timeslot f---s - size_t iterator_index = - ((*vectors[0])[vi[0]].second >= (*vectors[1])[vi[1]].second) ? 0 : 1; - united_timeslot = (*vectors[iterator_index])[vi[iterator_index]]; - ++vi[iterator_index]; - bool added_timeslot; - // Merge all timeslots intersecting with |united_timeslot|. - do { - added_timeslot = false; - for (size_t i = 0; i <= 1; ++i) { - if (vi[i] < vectors[i]->size() && - (*vectors[i])[vi[i]].second >= united_timeslot.first) { - // vectors[i][vi[i]] f---s - // united_timeslot f---s - // or - // united_timeslot f------s - added_timeslot = true; - if ((*vectors[i])[vi[i]].first < united_timeslot.first) { - // vectors[i][vi[i]] f---s - // united_timeslot f---s - // results in: - // united_timeslot f-----s - united_timeslot.first = (*vectors[i])[vi[i]].first; - } - ++vi[i]; - } - } - } while (added_timeslot); - result->push_back(united_timeslot); - } - for (size_t i = 0; i <= 1; ++i) { - for (; vi[i] < vectors[i]->size(); ++vi[i]) - result->push_back((*vectors[i])[vi[i]]); - } - return !result->empty(); -} - -// static -bool VisitFilter::IntersectTimeVectors(const TimeVector& vector1, - const TimeVector& vector2, - TimeVector* result) { - DCHECK(result); - result->clear(); - result->reserve(std::max(vector1.size(), vector2.size())); - - TimeVector::const_iterator vi[2]; - for (vi[0] = vector1.begin(), vi[1] = vector2.begin(); - vi[0] != vector1.end() && vi[1] != vector2.end();) { - size_t it_index = (vi[0]->second >= vi[1]->second) ? 0 : 1; - if (vi[it_index]->first >= vi[1 - it_index]->second) { - // vector 1 ++++ - // vector 2 ++ - ++vi[it_index]; - } else if (vi[it_index]->first >= vi[1 - it_index]->first) { - // vector 1 ++++ - // vector 2 +++++ - result->push_back(std::make_pair(vi[it_index]->first, - vi[1 - it_index]->second)); - ++vi[it_index]; - } else { - // vector 1 ++++ - // vector 2 ++ - result->push_back(std::make_pair(vi[1 - it_index]->first, - vi[1 - it_index]->second)); - ++vi[1 - it_index]; - } - } - - return !result->empty(); -} - -} // namespace history
diff --git a/components/history/core/browser/visit_filter.h b/components/history/core/browser/visit_filter.h deleted file mode 100644 index 1b16784..0000000 --- a/components/history/core/browser/visit_filter.h +++ /dev/null
@@ -1,167 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_HISTORY_CORE_BROWSER_VISIT_FILTER_H_ -#define COMPONENTS_HISTORY_CORE_BROWSER_VISIT_FILTER_H_ - -#include <stddef.h> - -#include <vector> - -#include "base/gtest_prod_util.h" -#include "base/time/time.h" - -namespace history { - -class VisitRow; - -// Helper class for creation of filters for VisitDatabase that is used to filter -// out visits by time of the day, day of the week, workdays, holidays, duration -// of the visit, location and the combinations of that. -// It also stores sorting order of the returned resilts. -class VisitFilter { - public: - VisitFilter(); - virtual ~VisitFilter(); - - // Vector of time intervals [begin time, end time]. All of the following - // functions produce vectors that are sorted in order from most recent to - // least recent and have intervals that do not intersect. - // |first| always points to the beginning of the time period, |second| - to - // the end. - typedef std::vector<std::pair<base::Time, base::Time> > TimeVector; - - // Returns time vector associated with the object. - const TimeVector& times() const { - return times_; - } - - // Sets |max_results| of the results to be returned. 0 means "return results - // for the two months prior to passed time". - void set_max_results(size_t max_results) { - max_results_ = max_results; - if (times_.size() > max_results_) - times_.resize(max_results_); - } - - // Sets the time that should be used as a basis for the filter. Normally this - // is the time that a query is made. - void SetFilterTime(const base::Time& filter_time); - - // Sets the amount of time around the filter time to take into account. This - // only applies to the filter time's time-of-day, restrictions on how long - // back in time to look should be controlled by changing |max_results|. - // - // How the filter width is used depends on the sorting order. For - // |ORDER_BY_TIME_LINEAR| it is the distance to the cutoff point, while for - // |ORDER_BY_TIME_GAUSSIAN| it is the standard deviation. - void SetFilterWidth(const base::TimeDelta& filter_width); - - // The following two filters are exclusive - setting one, clears the other - // one. - - // Sets the filter to use only visits that happened on the specified day of - // the week. - // |day| - day of the week: 0 - sunday, 1 - monday, etc. - void SetDayOfTheWeekFilter(int day); - - // Sets the filter to use only visits that happened on a holiday/workday. - // |workday| - if true means Monday-Friday, if false means Saturday-Sunday. - // TODO(georgey) - internationalize it. - void SetDayTypeFilter(bool workday); - - // Sorting order that results after applying this filter are sorted by. - enum SortingOrder { - ORDER_BY_RECENCY, // Most recent visits are most relevant ones. (default) - ORDER_BY_VISIT_COUNT, // Most visited are listed first. - ORDER_BY_DURATION_SPENT, // The sites that user spents more time in are - // sorted first. - ORDER_BY_TIME_GAUSSIAN, // Visits that happened closer to the filter time's - // time-of-day are scored higher. The dropoff in - // score follows a normal distribution curve with - // the filter width as the standard deviation. - ORDER_BY_TIME_LINEAR, // Visits that happened closer to the filter time's - // time-of-day are score higher. The dropoff in score - // is a linear function, with filter width being the - // point where a visit does not count at all anymore. - }; - - double GetVisitScore(const VisitRow& visit) const; - - void set_sorting_order(SortingOrder order) { - sorting_order_ = order; - UpdateTimeVector(); - } - - SortingOrder sorting_order() const { - return sorting_order_; - } - - // Clears all of the filters. - void ClearFilters(); - - private: - FRIEND_TEST_ALL_PREFIXES(VisitFilterTest, CheckFilters); - FRIEND_TEST_ALL_PREFIXES(VisitFilterTest, GetTimesInRange); - FRIEND_TEST_ALL_PREFIXES(VisitFilterTest, GetTimesOnTheDayOfTheWeek); - FRIEND_TEST_ALL_PREFIXES(VisitFilterTest, GetTimesOnTheSameDayType); - FRIEND_TEST_ALL_PREFIXES(VisitFilterTest, UniteTimeVectors); - FRIEND_TEST_ALL_PREFIXES(VisitFilterTest, IntersectTimeVectors); - - // Internal helper for the update. - bool UpdateTimeVector(); - - // Internal helper for getting the times in range. See SetTimeInRangeFilter(). - static void GetTimesInRange(base::Time begin_time_of_the_day, - base::Time end_time_of_the_day, - size_t max_results, - TimeVector* times); - - // Internal helper for getting the days in range. See SetDayOfTheWeekFilter(). - // |day| could be outside of the range: -4 (3 - 7) means Wednesday last week, - // 17 (3 + 2 * 7) means Wednesday in two weeks. - static void GetTimesOnTheDayOfTheWeek(int day, - base::Time week, - size_t max_results, - TimeVector* times); - - // Internal helper for getting the days in range. See SetDayTypeFilter(). - static void GetTimesOnTheSameDayType(bool workday, - base::Time week, - size_t max_results, - TimeVector* times); - - // Unites two vectors, so the new vector has non-intersecting union of the - // original ranges. Returns true if the result is non-empty, false otherwise. - static bool UniteTimeVectors(const TimeVector& vector1, - const TimeVector& vector2, - TimeVector* result); - - // Intersects two vectors, so the new vector has ranges that are covered by - // both of the original ranges. Returns true if the result is non-empty, false - // otherwise. - static bool IntersectTimeVectors(const TimeVector& vector1, - const TimeVector& vector2, - TimeVector* result); - - // Returns the time-of-day difference between the two times. The result will - // always represent a value between 0 and 12 hours inclusive. - static base::TimeDelta GetTimeOfDayDifference(base::Time t1, base::Time t2); - - base::Time filter_time_; - base::TimeDelta filter_width_; - enum { - DAY_UNDEFINED = -1, - WORKDAY = 7, - HOLIDAY = 8, - }; - int day_; - TimeVector times_; - size_t max_results_; - SortingOrder sorting_order_; -}; - -} // history - -#endif // COMPONENTS_HISTORY_CORE_BROWSER_VISIT_FILTER_H_
diff --git a/components/history/core/browser/visit_filter_unittest.cc b/components/history/core/browser/visit_filter_unittest.cc deleted file mode 100644 index fc4e8df..0000000 --- a/components/history/core/browser/visit_filter_unittest.cc +++ /dev/null
@@ -1,315 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/history/core/browser/visit_filter.h" - -#include <math.h> -#include <stddef.h> - -#include "base/logging.h" -#include "base/time/time.h" -#include "components/history/core/browser/history_types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -// So the tests won't go into the other day +/- several hours, return midday of -// today. -base::Time GetClosestMidday() { - return base::Time::Now().LocalMidnight() + base::TimeDelta::FromHours(12); -} - -} // namespace - -namespace history { - -class VisitFilterTest : public testing::Test { - public: - VisitFilterTest(); - - protected: - void SetUp() override; - void TearDown() override; -}; - -VisitFilterTest::VisitFilterTest() { -} - -void VisitFilterTest::SetUp() { -} - -void VisitFilterTest::TearDown() { -} - -TEST_F(VisitFilterTest, CheckFilters) { - base::Time t(GetClosestMidday()); - base::TimeDelta two_hours(base::TimeDelta::FromHours(2)); - VisitFilter f; - f.set_max_results(21U); - f.SetFilterTime(t); - f.SetFilterWidth(two_hours); - EXPECT_EQ(21U, f.times().size()); - for (size_t i = 0; i < f.times().size(); ++i) { - base::Time t_interval(t); - t_interval -= base::TimeDelta::FromDays(i); - EXPECT_EQ(t_interval - two_hours, f.times()[i].first) << - "Fails at index:" << i; - EXPECT_EQ(t_interval + two_hours, f.times()[i].second) << - "Fails at index:" << i; - } - base::Time::Exploded et; - t.LocalExplode(&et); - f.SetDayOfTheWeekFilter(et.day_of_week); - // 3 weeks in 21 days. - ASSERT_EQ(3U, f.times().size()); - for (size_t i = 1; i < f.times().size(); ++i) { - base::Time t_interval(t); - t_interval -= base::TimeDelta::FromDays(i); - EXPECT_EQ(f.times()[i].first + base::TimeDelta::FromDays(7), - f.times()[i - 1].first) << - "Fails at index:" << i; - EXPECT_EQ(f.times()[i].second + base::TimeDelta::FromDays(7), - f.times()[i - 1].second) << - "Fails at index:" << i; - EXPECT_EQ(two_hours * 2, - f.times()[i].second - f.times()[i].first) << - "Fails at index:" << i; - } -} - -TEST_F(VisitFilterTest, GetTimesInRange) { - base::Time::Exploded et = { 2011, 7, 0, 19, 22, 15, 11, 0 }; - base::Time t(base::Time::FromLocalExploded(et)); - base::TimeDelta two_hours(base::TimeDelta::FromHours(2)); - VisitFilter::TimeVector times; - VisitFilter::GetTimesInRange(t - two_hours, t + two_hours, 10U, ×); - EXPECT_GT(11U, times.size()); - for (size_t i = 0; i < times.size(); ++i) { - base::Time t_interval(t); - t_interval -= base::TimeDelta::FromDays(i); - EXPECT_EQ(t_interval - two_hours, times[i].first) << "Fails at index:" << i; - EXPECT_EQ(t_interval + two_hours, times[i].second) << - "Fails at index:" << i; - } -} - -TEST_F(VisitFilterTest, GetTimesOnTheDayOfTheWeek) { - base::Time t(GetClosestMidday()); - VisitFilter::TimeVector times; - base::Time::Exploded et; - t.LocalExplode(&et); - VisitFilter::GetTimesOnTheDayOfTheWeek(et.day_of_week, t, 10U, ×); - EXPECT_GT(11U, times.size()); - et.hour = 0; - et.minute = 0; - et.second = 0; - et.millisecond = 0; - for (size_t i = 0; i < times.size(); ++i) { - base::Time t_interval(base::Time::FromLocalExploded(et)); - t_interval -= base::TimeDelta::FromDays(7 * i); - EXPECT_EQ(t_interval, times[i].first) << "Fails at index:" << i; - EXPECT_EQ(t_interval + base::TimeDelta::FromDays(1), times[i].second) << - "Fails at index:" << i; - } -} - -TEST_F(VisitFilterTest, GetTimesOnTheSameDayType) { - base::Time::Exploded et = { 2011, 7, 0, 19, 22, 15, 11, 0 }; - base::Time t(base::Time::FromLocalExploded(et)); - VisitFilter::TimeVector times; - t.LocalExplode(&et); - VisitFilter::GetTimesOnTheSameDayType(et.day_of_week, t, 10U, ×); - EXPECT_GT(11U, times.size()); - et.hour = 0; - et.minute = 0; - et.second = 0; - et.millisecond = 0; - base::Time t_start(base::Time::FromLocalExploded(et)); - base::TimeDelta t_length; - if (et.day_of_week == 0 || et.day_of_week == 6) { - // Sunday and Saturday. - t_length = base::TimeDelta::FromDays(2); - if (et.day_of_week == 0) - t_start -= base::TimeDelta::FromDays(1); - } else { - t_length = base::TimeDelta::FromDays(5); - if (et.day_of_week != 1) - t_start -= base::TimeDelta::FromDays(et.day_of_week - 1); - } - for (size_t i = 0; i < times.size(); ++i) { - base::Time t_interval(t_start); - t_interval -= base::TimeDelta::FromDays(7 * i); - EXPECT_EQ(t_interval, times[i].first) << "Fails at index:" << i; - EXPECT_EQ(t_interval + t_length, times[i].second) << "Fails at index:" << i; - } -} - -TEST_F(VisitFilterTest, UniteTimeVectors) { - base::Time t(base::Time::Now()); - base::TimeDelta one_hour(base::TimeDelta::FromHours(1)); - base::TimeDelta one_day(base::TimeDelta::FromDays(1)); - VisitFilter::TimeVector times1; - times1.push_back(std::make_pair(t - one_hour, t + one_hour)); - times1.push_back(std::make_pair(t - one_hour - one_day, - t + one_hour - one_day)); - times1.push_back(std::make_pair(t - one_hour - one_day * 2, - t + one_hour - one_day * 2)); - times1.push_back(std::make_pair(t - one_hour - one_day * 3, - t + one_hour - one_day * 3)); - - VisitFilter::TimeVector times2; - // Should lie completely within times1[0]. - times2.push_back(std::make_pair(t - one_hour / 2, t + one_hour / 2)); - // Should lie just before times1[1]. - times2.push_back(std::make_pair(t + one_hour * 2 - one_day, - t + one_hour * 3 - one_day)); - // Should intersect with times1. - times2.push_back(std::make_pair(t - one_day * 2, - t + one_hour * 2 - one_day * 2)); - times2.push_back(std::make_pair(t - one_hour * 2 - one_day * 3, - t - one_day * 3)); - - VisitFilter::TimeVector result; - EXPECT_TRUE(VisitFilter::UniteTimeVectors(times1, times2, &result)); - ASSERT_EQ(5U, result.size()); - EXPECT_EQ(t - one_hour, result[0].first); - EXPECT_EQ(t + one_hour, result[0].second); - EXPECT_EQ(t + one_hour * 2 - one_day, result[1].first); - EXPECT_EQ(t + one_hour * 3 - one_day, result[1].second); - EXPECT_EQ(t - one_hour - one_day, result[2].first); - EXPECT_EQ(t + one_hour - one_day, result[2].second); - EXPECT_EQ(t - one_hour - one_day * 2, result[3].first); - EXPECT_EQ(t + one_hour * 2 - one_day * 2, result[3].second); - EXPECT_EQ(t - one_hour * 2 - one_day * 3, result[4].first); - EXPECT_EQ(t + one_hour - one_day * 3, result[4].second); - - EXPECT_FALSE(VisitFilter::UniteTimeVectors(VisitFilter::TimeVector(), - VisitFilter::TimeVector(), - &result)); - EXPECT_TRUE(result.empty()); -} - -TEST_F(VisitFilterTest, IntersectTimeVectors) { - base::Time t(base::Time::Now()); - base::TimeDelta one_hour(base::TimeDelta::FromHours(1)); - base::TimeDelta one_day(base::TimeDelta::FromDays(1)); - VisitFilter::TimeVector times1; - times1.push_back(std::make_pair(t - one_hour, t + one_hour)); - - VisitFilter::TimeVector times2; - // Should lie just before times1[0]. - times2.push_back(std::make_pair(t + one_hour * 2, - t + one_hour * 3)); - - VisitFilter::TimeVector result; - EXPECT_FALSE(VisitFilter::IntersectTimeVectors(times1, times2, &result)); - EXPECT_TRUE(result.empty()); - - times1.push_back(std::make_pair(t - one_hour - one_day, - t + one_hour - one_day)); - times1.push_back(std::make_pair(t - one_hour - one_day * 2, - t + one_hour - one_day * 2)); - times1.push_back(std::make_pair(t - one_hour - one_day * 3, - t + one_hour - one_day * 3)); - - // Should lie completely within times1[1]. - times2.push_back(std::make_pair(t - one_hour / 2 - one_day, - t + one_hour / 2 - one_day)); - // Should intersect with times1. - times2.push_back(std::make_pair(t - one_day * 2, - t + one_hour * 2 - one_day * 2)); - times2.push_back(std::make_pair(t - one_hour * 2 - one_day * 3, - t - one_day * 3)); - - EXPECT_TRUE(VisitFilter::IntersectTimeVectors(times1, times2, &result)); - ASSERT_EQ(3U, result.size()); - EXPECT_EQ(t - one_hour / 2 - one_day, result[0].first); - EXPECT_EQ(t + one_hour / 2 - one_day, result[0].second); - EXPECT_EQ(t - one_day * 2, result[1].first); - EXPECT_EQ(t + one_hour - one_day * 2, result[1].second); - EXPECT_EQ(t - one_hour - one_day * 3, result[2].first); - EXPECT_EQ(t - one_day * 3, result[2].second); - - // Check that touching ranges do not intersect. - times1.clear(); - times1.push_back(std::make_pair(t - one_hour, t)); - times2.clear(); - times2.push_back(std::make_pair(t, t + one_hour)); - EXPECT_FALSE(VisitFilter::IntersectTimeVectors(times1, times2, &result)); - EXPECT_TRUE(result.empty()); -} - -TEST_F(VisitFilterTest, GetVisitScore) { - base::Time filter_time; - ASSERT_TRUE(base::Time::FromString("Tue, 24 Apr 2012, 12:00:00", - &filter_time)); - VisitFilter filter; - VisitRow visit; - - filter.set_sorting_order(VisitFilter::ORDER_BY_RECENCY); - filter.SetFilterTime(filter_time); - filter.SetFilterWidth(base::TimeDelta::FromHours(1)); - - double one_week_one_hour_staleness = pow(2, -(24.0 * 7.0 + 1.0) / - (24.0 * 7.0)); - - // No decay on current visit. - visit.visit_time = filter_time; - EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); - // Half score after a week. - visit.visit_time = filter_time - base::TimeDelta::FromDays(7); - EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit)); - // Future visits should be treated as current. - visit.visit_time = filter_time + base::TimeDelta::FromDays(1); - EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); - - filter.set_sorting_order(VisitFilter::ORDER_BY_VISIT_COUNT); - // Every visit should score 1 with this filter. - visit.visit_time = filter_time; - EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); - visit.visit_time = filter_time - base::TimeDelta::FromDays(7); - EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); - visit.visit_time = filter_time + base::TimeDelta::FromDays(7); - EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); - - filter.set_sorting_order(VisitFilter::ORDER_BY_TIME_LINEAR); - visit.visit_time = filter_time; - EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); - // Half the filter width forward in time should get half the score for the - // time difference, but no staleness decay. - visit.visit_time = filter_time + base::TimeDelta::FromMinutes(30); - EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit)); - // One week back in time gets full time difference score, but a staleness - // factor of 0.5 - visit.visit_time = filter_time - base::TimeDelta::FromDays(7); - EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit)); - // One week plus half a filter width should have it's score halved before - // the staleness factor. - filter.SetFilterWidth(base::TimeDelta::FromHours(2)); - visit.visit_time = filter_time - base::TimeDelta::FromDays(7) - - base::TimeDelta::FromHours(1); - EXPECT_DOUBLE_EQ(0.5 * one_week_one_hour_staleness, - filter.GetVisitScore(visit)); - filter.SetFilterWidth(base::TimeDelta::FromHours(1)); - - filter.set_sorting_order(VisitFilter::ORDER_BY_TIME_GAUSSIAN); - visit.visit_time = filter_time; - EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); - // Going forward in time to test the normal distribution function. - visit.visit_time = filter_time + base::TimeDelta::FromHours(1); - EXPECT_DOUBLE_EQ(exp(-0.5), filter.GetVisitScore(visit)); - visit.visit_time = filter_time + base::TimeDelta::FromMinutes(30); - EXPECT_DOUBLE_EQ(exp(-0.125), filter.GetVisitScore(visit)); - // One week back in time gets full time difference score, but a staleness - // factor of 0.5 - visit.visit_time = filter_time - base::TimeDelta::FromDays(7); - EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit)); - // One standard deviation of decay, plus the staleness factor. - visit.visit_time = filter_time - base::TimeDelta::FromDays(7) - - base::TimeDelta::FromHours(1); - EXPECT_DOUBLE_EQ(exp(-0.5) * one_week_one_hour_staleness, - filter.GetVisitScore(visit)); -} - -} // namespace history
diff --git a/components/leveldb/BUILD.gn b/components/leveldb/BUILD.gn index db5dd1e..5bdef0a1 100644 --- a/components/leveldb/BUILD.gn +++ b/components/leveldb/BUILD.gn
@@ -9,12 +9,12 @@ sources = [ "env_mojo.cc", "env_mojo.h", - "leveldb_app.cc", - "leveldb_app.h", "leveldb_database_impl.cc", "leveldb_database_impl.h", "leveldb_file_thread.cc", "leveldb_file_thread.h", + "leveldb_service_impl.cc", + "leveldb_service_impl.h", "util.cc", "util.h", ] @@ -25,7 +25,6 @@ "//mojo/common", "//mojo/message_pump", "//mojo/platform_handle", - "//mojo/services/tracing/public/cpp", "//mojo/shell/public/cpp", "//third_party/leveldatabase", ] @@ -33,17 +32,21 @@ mojo_native_application("leveldb") { sources = [ + "leveldb_app.cc", + "leveldb_app.h", "main.cc", ] deps = [ ":lib", ":manifest", + "//components/leveldb/public/interfaces", "//mojo/common", "//mojo/environment:chromium", "//mojo/platform_handle:for_shared_library", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", + "//mojo/services/tracing/public/cpp", "//mojo/shell/public/cpp", ] }
diff --git a/components/leveldb/leveldb.gyp b/components/leveldb/leveldb.gyp new file mode 100644 index 0000000..5d39a72 --- /dev/null +++ b/components/leveldb/leveldb.gyp
@@ -0,0 +1,63 @@ +# 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. + +{ + 'variables': { + # This turns on e.g. the filename-based detection of which + # platforms to include source files on (e.g. files ending in + # _mac.h or _mac.cc are only compiled on MacOSX). + 'chromium_code': 1, + }, + 'targets': [ + { + # GN version: //components/leveldb:lib + 'target_name': 'leveldb_lib', + 'type': 'static_library', + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'env_mojo.cc', + 'env_mojo.h', + 'leveldb_database_impl.cc', + 'leveldb_database_impl.h', + 'leveldb_file_thread.cc', + 'leveldb_file_thread.h', + 'leveldb_service_impl.cc', + 'leveldb_service_impl.h', + 'util.cc', + 'util.h', + ], + 'dependencies': [ + 'leveldb_bindings_mojom', + '../../components/filesystem/filesystem.gyp:filesystem_lib', + '../../mojo/mojo_public.gyp:mojo_cpp_bindings', + '../../third_party/leveldatabase/leveldatabase.gyp:leveldatabase', + ] + }, + { + # GN version: //components/leveldb/public/interfaces + 'target_name': 'leveldb_bindings', + 'type': 'static_library', + 'dependencies': [ + 'leveldb_bindings_mojom', + ], + }, + { + 'target_name': 'leveldb_bindings_mojom', + 'type': 'none', + 'variables': { + 'mojom_files': [ + 'public/interfaces/leveldb.mojom', + ], + }, + 'dependencies': [ + '../../components/filesystem/filesystem.gyp:filesystem_bindings_mojom', + ], + 'includes': [ + '../../mojo/mojom_bindings_generator_explicit.gypi', + ], + } + ], +}
diff --git a/components/leveldb/leveldb_app.cc b/components/leveldb/leveldb_app.cc index bd8ac71..1e9d1682 100644 --- a/components/leveldb/leveldb_app.cc +++ b/components/leveldb/leveldb_app.cc
@@ -4,15 +4,8 @@ #include "components/leveldb/leveldb_app.h" -#include "components/leveldb/env_mojo.h" -#include "components/leveldb/leveldb_database_impl.h" -#include "components/leveldb/util.h" +#include "components/leveldb/leveldb_service_impl.h" #include "mojo/shell/public/cpp/connection.h" -#include "third_party/leveldatabase/env_chromium.h" -#include "third_party/leveldatabase/src/include/leveldb/db.h" -#include "third_party/leveldatabase/src/include/leveldb/env.h" -#include "third_party/leveldatabase/src/include/leveldb/filter_policy.h" -#include "third_party/leveldatabase/src/include/leveldb/slice.h" namespace leveldb { @@ -24,7 +17,7 @@ const std::string& url, uint32_t id) { tracing_.Initialize(shell, url); - thread_ = new LevelDBFileThread; + service_.reset(new LevelDBServiceImpl); } bool LevelDBApp::AcceptConnection(mojo::Connection* connection) { @@ -34,41 +27,7 @@ void LevelDBApp::Create(mojo::Connection* connection, mojo::InterfaceRequest<LevelDBService> request) { - bindings_.AddBinding(this, std::move(request)); -} - -void LevelDBApp::Open(filesystem::DirectoryPtr directory, - const mojo::String& dbname, - mojo::InterfaceRequest<LevelDBDatabase> database, - const OpenCallback& callback) { - // This is the place where we open a database. - leveldb::Options options; - options.create_if_missing = true; - options.paranoid_checks = true; - // TODO(erg): Do we need a filter policy? - options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue; - options.compression = leveldb::kSnappyCompression; - - // For info about the troubles we've run into with this parameter, see: - // https://code.google.com/p/chromium/issues/detail?id=227313#c11 - options.max_open_files = 80; - - // Register our directory with the file thread. - LevelDBFileThread::OpaqueDir* dir = - thread_->RegisterDirectory(std::move(directory)); - - scoped_ptr<MojoEnv> env_mojo(new MojoEnv(thread_, dir)); - options.env = env_mojo.get(); - - leveldb::DB* db = nullptr; - leveldb::Status s = leveldb::DB::Open(options, dbname.To<std::string>(), &db); - - if (s.ok()) { - new LevelDBDatabaseImpl(std::move(database), std::move(env_mojo), - scoped_ptr<leveldb::DB>(db)); - } - - callback.Run(LeveldbStatusToError(s)); + bindings_.AddBinding(service_.get(), std::move(request)); } } // namespace leveldb
diff --git a/components/leveldb/leveldb_app.h b/components/leveldb/leveldb_app.h index 64cec0b..7f5157a 100644 --- a/components/leveldb/leveldb_app.h +++ b/components/leveldb/leveldb_app.h
@@ -5,7 +5,6 @@ #ifndef COMPONENTS_LEVELDB_LEVELDB_APP_H_ #define COMPONENTS_LEVELDB_LEVELDB_APP_H_ -#include "components/leveldb/leveldb_file_thread.h" #include "components/leveldb/public/interfaces/leveldb.mojom.h" #include "mojo/public/cpp/bindings/weak_binding_set.h" #include "mojo/services/tracing/public/cpp/tracing_impl.h" @@ -15,8 +14,7 @@ namespace leveldb { class LevelDBApp : public mojo::ShellClient, - public mojo::InterfaceFactory<LevelDBService>, - public LevelDBService { + public mojo::InterfaceFactory<LevelDBService> { public: LevelDBApp(); ~LevelDBApp() override; @@ -35,20 +33,10 @@ void Create(mojo::Connection* connection, mojo::InterfaceRequest<LevelDBService> request) override; - // Overridden from LevelDBService: - void Open(filesystem::DirectoryPtr directory, - const mojo::String& dbname, - mojo::InterfaceRequest<LevelDBDatabase> database, - const OpenCallback& callback) override; - mojo::TracingImpl tracing_; + scoped_ptr<LevelDBService> service_; mojo::WeakBindingSet<LevelDBService> bindings_; - // Thread to own the mojo message pipe. Because leveldb spawns multiple - // threads that want to call file stuff, we create a dedicated thread to send - // and receive mojo message calls. - scoped_refptr<LevelDBFileThread> thread_; - DISALLOW_COPY_AND_ASSIGN(LevelDBApp); };
diff --git a/components/leveldb/leveldb_service_impl.cc b/components/leveldb/leveldb_service_impl.cc new file mode 100644 index 0000000..404019e9 --- /dev/null +++ b/components/leveldb/leveldb_service_impl.cc
@@ -0,0 +1,58 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/leveldb/leveldb_service_impl.h" + +#include "components/leveldb/env_mojo.h" +#include "components/leveldb/leveldb_database_impl.h" +#include "components/leveldb/util.h" +#include "third_party/leveldatabase/env_chromium.h" +#include "third_party/leveldatabase/src/include/leveldb/db.h" +#include "third_party/leveldatabase/src/include/leveldb/env.h" +#include "third_party/leveldatabase/src/include/leveldb/filter_policy.h" +#include "third_party/leveldatabase/src/include/leveldb/slice.h" + +namespace leveldb { + +LevelDBServiceImpl::LevelDBServiceImpl() + : thread_(new LevelDBFileThread) { +} + +LevelDBServiceImpl::~LevelDBServiceImpl() {} + +void LevelDBServiceImpl::Open(filesystem::DirectoryPtr directory, + const mojo::String& dbname, + mojo::InterfaceRequest<LevelDBDatabase> database, + const OpenCallback& callback) { + // This is the place where we open a database. + leveldb::Options options; + options.create_if_missing = true; + options.paranoid_checks = true; + // TODO(erg): Do we need a filter policy? + options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue; + options.compression = leveldb::kSnappyCompression; + + // For info about the troubles we've run into with this parameter, see: + // https://code.google.com/p/chromium/issues/detail?id=227313#c11 + options.max_open_files = 80; + + // Register our directory with the file thread. + LevelDBFileThread::OpaqueDir* dir = + thread_->RegisterDirectory(std::move(directory)); + + scoped_ptr<MojoEnv> env_mojo(new MojoEnv(thread_, dir)); + options.env = env_mojo.get(); + + leveldb::DB* db = nullptr; + leveldb::Status s = leveldb::DB::Open(options, dbname.To<std::string>(), &db); + + if (s.ok()) { + new LevelDBDatabaseImpl(std::move(database), std::move(env_mojo), + scoped_ptr<leveldb::DB>(db)); + } + + callback.Run(LeveldbStatusToError(s)); +} + +} // namespace leveldb
diff --git a/components/leveldb/leveldb_service_impl.h b/components/leveldb/leveldb_service_impl.h new file mode 100644 index 0000000..54b2942 --- /dev/null +++ b/components/leveldb/leveldb_service_impl.h
@@ -0,0 +1,37 @@ +// 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_LEVELDB_LEVELDB_SERVICE_IMPL_H_ +#define COMPONENTS_LEVELDB_LEVELDB_SERVICE_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "components/leveldb/leveldb_file_thread.h" +#include "components/leveldb/public/interfaces/leveldb.mojom.h" + +namespace leveldb { + +// Creates LevelDBDatabases based scoped to a |directory|/|dbname|. +class LevelDBServiceImpl : public LevelDBService { + public: + LevelDBServiceImpl(); + ~LevelDBServiceImpl() override; + + // Overridden from LevelDBService: + void Open(filesystem::DirectoryPtr directory, + const mojo::String& dbname, + mojo::InterfaceRequest<LevelDBDatabase> database, + const OpenCallback& callback) override; + + private: + // Thread to own the mojo message pipe. Because leveldb spawns multiple + // threads that want to call file stuff, we create a dedicated thread to send + // and receive mojo message calls. + scoped_refptr<LevelDBFileThread> thread_; + + DISALLOW_COPY_AND_ASSIGN(LevelDBServiceImpl); +}; + +} // namespace leveldb + +#endif // COMPONENTS_LEVELDB_LEVELDB_SERVICE_IMPL_H_
diff --git a/components/metrics.gypi b/components/metrics.gypi index b90ebc6..f735b347 100644 --- a/components/metrics.gypi +++ b/components/metrics.gypi
@@ -40,6 +40,8 @@ 'metrics/drive_metrics_provider_linux.cc', 'metrics/drive_metrics_provider_mac.mm', 'metrics/drive_metrics_provider_win.cc', + 'metrics/file_metrics_provider.cc', + 'metrics/file_metrics_provider.h', 'metrics/histogram_encoder.cc', 'metrics/histogram_encoder.h', 'metrics/machine_id_provider.h',
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn index a831839..bb421f1 100644 --- a/components/metrics/BUILD.gn +++ b/components/metrics/BUILD.gn
@@ -22,6 +22,8 @@ "drive_metrics_provider_linux.cc", "drive_metrics_provider_mac.mm", "drive_metrics_provider_win.cc", + "file_metrics_provider.cc", + "file_metrics_provider.h", "histogram_encoder.cc", "histogram_encoder.h", "machine_id_provider.h",
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc new file mode 100644 index 0000000..7fad01c --- /dev/null +++ b/components/metrics/file_metrics_provider.cc
@@ -0,0 +1,290 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/metrics/file_metrics_provider.h" + +#include "base/command_line.h" +#include "base/files/file.h" +#include "base/files/file_util.h" +#include "base/files/memory_mapped_file.h" +#include "base/logging.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/histogram_macros.h" +#include "base/metrics/histogram_persistence.h" +#include "base/metrics/persistent_memory_allocator.h" +#include "base/task_runner.h" +#include "base/time/time.h" +#include "components/metrics/metrics_pref_names.h" +#include "components/metrics/metrics_service.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" + + +namespace metrics { + +// This structure stores all the information about the files being monitored +// and their current reporting state. +struct FileMetricsProvider::FileInfo { + FileInfo() {} + ~FileInfo() {} + + // Where on disk the file is located. + base::FilePath path; + + // How to access this file (atomic/active). + FileType type; + + // Name used inside prefs to persistent metadata. + std::string prefs_key; + + // The last-seen time of this file to detect change. + base::Time last_seen; + + // Once a file has been recognized as needing to be read, it is |mapped| + // into memory. If that file is "atomic" then the data from that file + // will be copied to |data| and the mapped file released. If the file is + // "active", it remains mapped and nothing is copied to local memory. + std::vector<uint8_t> data; + scoped_ptr<base::MemoryMappedFile> mapped; + scoped_ptr<base::PersistentMemoryAllocator> allocator; + + private: + DISALLOW_COPY_AND_ASSIGN(FileInfo); +}; + +FileMetricsProvider::FileMetricsProvider( + const scoped_refptr<base::TaskRunner>& task_runner, + PrefService* local_state) + : task_runner_(task_runner), + pref_service_(local_state), + weak_factory_(this) { +} + +FileMetricsProvider::~FileMetricsProvider() {} + +void FileMetricsProvider::RegisterFile(const base::FilePath& path, + FileType type, + const base::StringPiece prefs_key) { + DCHECK(thread_checker_.CalledOnValidThread()); + + scoped_ptr<FileInfo> file(new FileInfo()); + file->path = path; + file->type = type; + file->prefs_key = prefs_key.as_string(); + + // |prefs_key| may be empty if the caller does not wish to persist the + // state across instances of the program. + if (pref_service_ && !prefs_key.empty()) { + file->last_seen = base::Time::FromInternalValue( + pref_service_->GetInt64(metrics::prefs::kMetricsLastSeenPrefix + + file->prefs_key)); + } + + files_to_check_.push_back(std::move(file)); +} + +// static +void FileMetricsProvider::RegisterPrefs(PrefRegistrySimple* prefs, + const base::StringPiece prefs_key) { + prefs->RegisterInt64Pref(metrics::prefs::kMetricsLastSeenPrefix + + prefs_key.as_string(), 0); +} + +// static +void FileMetricsProvider::CheckAndMapNewMetricFilesOnTaskRunner( + FileMetricsProvider::FileInfoList* files) { + // This method has all state information passed in |files| and is intended + // to run on a worker thread rather than the UI thread. + for (scoped_ptr<FileInfo>& file : *files) { + AccessResult result = CheckAndMapNewMetrics(file.get()); + // Some results are not reported in order to keep the dashboard clean. + if (result != ACCESS_RESULT_DOESNT_EXIST && + result != ACCESS_RESULT_NOT_MODIFIED) { + UMA_HISTOGRAM_ENUMERATION( + "UMA.FileMetricsProvider.AccessResult", result, ACCESS_RESULT_MAX); + } + } +} + +// This method has all state information passed in |file| and is intended +// to run on a worker thread rather than the UI thread. +// static +FileMetricsProvider::AccessResult FileMetricsProvider::CheckAndMapNewMetrics( + FileMetricsProvider::FileInfo* file) { + DCHECK(!file->mapped); + DCHECK(file->data.empty()); + + base::File::Info info; + if (!base::GetFileInfo(file->path, &info)) + return ACCESS_RESULT_DOESNT_EXIST; + + if (info.is_directory || info.size == 0) + return ACCESS_RESULT_INVALID_FILE; + + if (file->last_seen >= info.last_modified) + return ACCESS_RESULT_NOT_MODIFIED; + + // A new file of metrics has been found. Map it into memory. + // TODO(bcwhite): Make this open read/write when supported for "active". + file->mapped.reset(new base::MemoryMappedFile()); + if (!file->mapped->Initialize(file->path)) { + file->mapped.reset(); + return ACCESS_RESULT_SYSTEM_MAP_FAILURE; + } + + // Ensure any problems below don't occur repeatedly. + file->last_seen = info.last_modified; + + // Test the validity of the file contents. + if (!base::FilePersistentMemoryAllocator::IsFileAcceptable(*file->mapped)) + return ACCESS_RESULT_INVALID_CONTENTS; + + // For an "atomic" file, immediately copy the data into local memory and + // release the file so that it is not held open. + if (file->type == FILE_HISTOGRAMS_ATOMIC) { + file->data.assign(file->mapped->data(), + file->mapped->data() + file->mapped->length()); + file->mapped.reset(); + } + + return ACCESS_RESULT_SUCCESS; +} + +void FileMetricsProvider::ScheduleFilesCheck() { + DCHECK(thread_checker_.CalledOnValidThread()); + if (files_to_check_.empty()) + return; + + // Create an independent list of files for checking. This will be Owned() + // by the reply call given to the task-runner, to be deleted when that call + // has returned. It is also passed Unretained() to the task itself, safe + // because that must complete before the reply runs. + FileInfoList* check_list = new FileInfoList(); + std::swap(files_to_check_, *check_list); + task_runner_->PostTaskAndReply( + FROM_HERE, + base::Bind(&FileMetricsProvider::CheckAndMapNewMetricFilesOnTaskRunner, + base::Unretained(check_list)), + base::Bind(&FileMetricsProvider::RecordFilesChecked, + weak_factory_.GetWeakPtr(), base::Owned(check_list))); +} + +void FileMetricsProvider::RecordHistogramSnapshotsFromFile( + base::HistogramSnapshotManager* snapshot_manager, + FileInfo* file) { + DCHECK(thread_checker_.CalledOnValidThread()); + base::PersistentMemoryAllocator::Iterator hist_iter; + file->allocator->CreateIterator(&hist_iter); + + int histogram_count = 0; + while (true) { + scoped_ptr<base::HistogramBase> histogram( + base::GetNextPersistentHistogram(file->allocator.get(), &hist_iter)); + if (!histogram) + break; + if (file->type == FILE_HISTOGRAMS_ATOMIC) + snapshot_manager->PrepareAbsoluteTakingOwnership(std::move(histogram)); + else + snapshot_manager->PrepareDeltaTakingOwnership(std::move(histogram)); + ++histogram_count; + } + + DVLOG(1) << "Reported " << histogram_count << " histograms from " + << file->path.value(); +} + +void FileMetricsProvider::CreateAllocatorForFile(FileInfo* file) { + DCHECK(!file->allocator); + + // File data was validated earlier. Files are not considered "untrusted" + // as some processes might be (e.g. Renderer) so there's no need to check + // again to try to thwart some malicious actor that may have modified the + // data between then and now. + if (file->mapped) { + DCHECK(file->data.empty()); + // TODO(bcwhite): Make this do read/write when supported for "active". + file->allocator.reset(new base::FilePersistentMemoryAllocator( + std::move(file->mapped), 0, std::string())); + } else { + DCHECK(!file->mapped); + file->allocator.reset(new base::PersistentMemoryAllocator( + &file->data[0], file->data.size(), 0, 0, "", true)); + } +} + +void FileMetricsProvider::RecordFilesChecked(FileInfoList* checked) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Move each processed file to either the "to-read" list (for processing) or + // the "to-check" list (for future checking). + for (auto iter = checked->begin(); iter != checked->end();) { + auto temp = iter++; + const FileInfo* file = temp->get(); + if (file->mapped || !file->data.empty()) + files_to_read_.splice(files_to_read_.end(), *checked, temp); + else + files_to_check_.splice(files_to_check_.end(), *checked, temp); + } +} + +void FileMetricsProvider::RecordFileAsSeen(FileInfo* file) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (pref_service_ && !file->prefs_key.empty()) { + pref_service_->SetInt64(metrics::prefs::kMetricsLastSeenPrefix + + file->prefs_key, + file->last_seen.ToInternalValue()); + } +} + +void FileMetricsProvider::OnDidCreateMetricsLog() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Move finished metric files back to list of monitored files. + for (auto iter = files_to_read_.begin(); iter != files_to_read_.end();) { + auto temp = iter++; + const FileInfo* file = temp->get(); + if (!file->allocator && !file->mapped && file->data.empty()) + files_to_check_.splice(files_to_check_.end(), files_to_read_, temp); + } + + // Schedule a check to see if there are new metrics to load. If so, they + // will be reported during the next collection run after this one. The + // check is run off of the worker-pool so as to not cause delays on the + // main UI thread (which is currently where metric collection is done). + ScheduleFilesCheck(); +} + +void FileMetricsProvider::RecordHistogramSnapshots( + base::HistogramSnapshotManager* snapshot_manager) { + DCHECK(thread_checker_.CalledOnValidThread()); + + for (scoped_ptr<FileInfo>& file : files_to_read_) { + // If the file is mapped or loaded then it needs to have an allocator + // attached to it in order to read histograms out of it. + if (file->mapped || !file->data.empty()) + CreateAllocatorForFile(file.get()); + + // A file should not be under "files to read" unless it has an allocator + // or is memory-mapped (at which point it will have received an allocator + // above). However, if this method gets called twice before the scheduled- + // files-check has a chance to clean up, this may trigger. This also + // catches the case where creating an allocator from the file has failed. + if (!file->allocator) + continue; + + // Dump all histograms contained within the file to the snapshot-manager. + RecordHistogramSnapshotsFromFile(snapshot_manager, file.get()); + + // Atomic files are read once and then ignored unless they change. + if (file->type == FILE_HISTOGRAMS_ATOMIC) { + DCHECK(!file->mapped); + file->allocator.reset(); + file->data.clear(); + RecordFileAsSeen(file.get()); + } + } +} + +} // namespace metrics
diff --git a/components/metrics/file_metrics_provider.h b/components/metrics/file_metrics_provider.h new file mode 100644 index 0000000..88c025f --- /dev/null +++ b/components/metrics/file_metrics_provider.h
@@ -0,0 +1,157 @@ +// 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_METRICS_FILE_METRICS_PROVIDER_H_ +#define COMPONENTS_METRICS_FILE_METRICS_PROVIDER_H_ + +#include <list> +#include <string> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "components/metrics/metrics_provider.h" + +class PrefRegistrySimple; +class PrefService; + +namespace base { +class MemoryMappedFile; +class PersistentMemoryAllocator; +class TaskRunner; +} + +namespace metrics { + +// FileMetricsProvider gathers and logs histograms written to files on disk. +// Any number of files can be registered and will be polled once per upload +// cycle (at startup and periodically thereafter -- about every 30 minutes +// for desktop) for data to send. +class FileMetricsProvider : public metrics::MetricsProvider { + public: + enum FileType { + // "Atomic" files are a collection of histograms that are written + // completely in a single atomic operation (typically a write followed + // by an atomic rename) and the file is never updated again except to + // be replaced by a completely new set of histograms. This is the only + // option that can be used if the file is not writeable by *this* + // process. + FILE_HISTOGRAMS_ATOMIC, + + // "Active" files may be open by one or more other processes and updated + // at any time with new samples or new histograms. Such files may also be + // inactive for any period of time only to be opened again and have new + // data written to them. The file should probably never be deleted because + // there would be no guarantee that the data has been reported. + // TODO(bcwhite): Enable when read/write mem-mapped files are supported. + //FILE_HISTOGRAMS_ACTIVE, + }; + + FileMetricsProvider(const scoped_refptr<base::TaskRunner>& task_runner, + PrefService* local_state); + ~FileMetricsProvider() override; + + // Indicates a file to be monitored and how the file is used. Because some + // metadata may persist across process restarts, preferences entries are + // used based on the |prefs_key| name. Call RegisterPrefs() with the same + // name to create the necessary keys in advance. Set |prefs_key| empty + // if no persistence is required. + void RegisterFile(const base::FilePath& path, + FileType type, + const base::StringPiece prefs_key); + + // Registers all necessary preferences for maintaining persistent state + // about a monitored file across process restarts. The |prefs_key| is + // typically the filename. + static void RegisterPrefs(PrefRegistrySimple* prefs, + const base::StringPiece prefs_key); + + private: + friend class FileMetricsProviderTest; + FRIEND_TEST_ALL_PREFIXES(FileMetricsProviderTest, AccessMetrics); + + // The different results that can occur accessing a file. + enum AccessResult { + // File was successfully mapped. + ACCESS_RESULT_SUCCESS, + + // File does not exist. + ACCESS_RESULT_DOESNT_EXIST, + + // File exists but not modified since last read. + ACCESS_RESULT_NOT_MODIFIED, + + // File is not valid: is a directory or zero-size. + ACCESS_RESULT_INVALID_FILE, + + // System could not map file into memory. + ACCESS_RESULT_SYSTEM_MAP_FAILURE, + + // File had invalid contents. + ACCESS_RESULT_INVALID_CONTENTS, + + ACCESS_RESULT_MAX + }; + + // Information about files being monitored; defined and used exclusively + // inside the .cc file. + struct FileInfo; + using FileInfoList = std::list<scoped_ptr<FileInfo>>; + + // Checks a list of files (on a task-runner allowed to do I/O) to see if + // any should be processed during the next histogram collection. + static void CheckAndMapNewMetricFilesOnTaskRunner(FileInfoList* files); + + // Checks an individual file as part of CheckAndMapNewMetricFilesOnTaskRunner. + static AccessResult CheckAndMapNewMetrics(FileInfo* file); + + // Creates a task to check all monitored files for updates. + void ScheduleFilesCheck(); + + // Creates a PersistentMemoryAllocator for a file that has been marked to + // have its metrics collected. + void CreateAllocatorForFile(FileInfo* file); + + // Records all histograms from a given file via a snapshot-manager. + void RecordHistogramSnapshotsFromFile( + base::HistogramSnapshotManager* snapshot_manager, + FileInfo* file); + + // Takes a list of files checked by an external task and determines what + // to do with each. + void RecordFilesChecked(FileInfoList* checked); + + // Updates the persistent state information to show a file as being read. + void RecordFileAsSeen(FileInfo* file); + + // metrics::MetricsDataProvider: + void OnDidCreateMetricsLog() override; + void RecordHistogramSnapshots( + base::HistogramSnapshotManager* snapshot_manager) override; + + // A task-runner capable of performing I/O. + scoped_refptr<base::TaskRunner> task_runner_; + + // A list of files not currently active that need to be checked for changes. + FileInfoList files_to_check_; + + // A list of files that have data to be read and reported. + FileInfoList files_to_read_; + + // The preferences-service used to store persistent state about files. + PrefService* pref_service_; + + base::ThreadChecker thread_checker_; + base::WeakPtrFactory<FileMetricsProvider> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(FileMetricsProvider); +}; + +} // namespace metrics + +#endif // COMPONENTS_METRICS_FILE_METRICS_PROVIDER_H_
diff --git a/components/metrics/file_metrics_provider_unittest.cc b/components/metrics/file_metrics_provider_unittest.cc new file mode 100644 index 0000000..3bffb1c --- /dev/null +++ b/components/metrics/file_metrics_provider_unittest.cc
@@ -0,0 +1,190 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/metrics/file_metrics_provider.h" + +#include "base/files/file_util.h" +#include "base/files/memory_mapped_file.h" +#include "base/files/scoped_temp_dir.h" +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_flattener.h" +#include "base/metrics/histogram_persistence.h" +#include "base/metrics/histogram_snapshot_manager.h" +#include "base/metrics/persistent_memory_allocator.h" +#include "base/metrics/statistics_recorder.h" +#include "base/test/test_simple_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "components/metrics/metrics_pref_names.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const char kMetricsName[] = "TestMetrics"; +const char kMetricsFilename[] = "file.metrics"; +} // namespace + +namespace metrics { + +class HistogramFlattenerDeltaRecorder : public base::HistogramFlattener { + public: + HistogramFlattenerDeltaRecorder() {} + + void RecordDelta(const base::HistogramBase& histogram, + const base::HistogramSamples& snapshot) override { + recorded_delta_histogram_names_.push_back(histogram.histogram_name()); + } + + void InconsistencyDetected(base::HistogramBase::Inconsistency problem) + override { + ASSERT_TRUE(false); + } + + void UniqueInconsistencyDetected( + base::HistogramBase::Inconsistency problem) override { + ASSERT_TRUE(false); + } + + void InconsistencyDetectedInLoggedCount(int amount) override { + ASSERT_TRUE(false); + } + + std::vector<std::string> GetRecordedDeltaHistogramNames() { + return recorded_delta_histogram_names_; + } + + private: + std::vector<std::string> recorded_delta_histogram_names_; + + DISALLOW_COPY_AND_ASSIGN(HistogramFlattenerDeltaRecorder); +}; + +class FileMetricsProviderTest : public testing::Test { + protected: + FileMetricsProviderTest() + : task_runner_(new base::TestSimpleTaskRunner()), + thread_task_runner_handle_(task_runner_), + prefs_(new TestingPrefServiceSimple) { + EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); + FileMetricsProvider::RegisterPrefs(prefs_->registry(), kMetricsName); + } + + TestingPrefServiceSimple* prefs() { return prefs_.get(); } + base::FilePath temp_dir() { return temp_dir_.path(); } + base::FilePath metrics_file() { + return temp_dir_.path().AppendASCII(kMetricsFilename); + } + + FileMetricsProvider* provider() { + if (!provider_) + provider_.reset(new FileMetricsProvider(task_runner_, prefs())); + return provider_.get(); + } + + void RunTasks() { + task_runner_->RunUntilIdle(); + } + + private: + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + base::ThreadTaskRunnerHandle thread_task_runner_handle_; + + base::ScopedTempDir temp_dir_; + scoped_ptr<TestingPrefServiceSimple> prefs_; + scoped_ptr<FileMetricsProvider> provider_; + + DISALLOW_COPY_AND_ASSIGN(FileMetricsProviderTest); +}; + + +TEST_F(FileMetricsProviderTest, AccessMetrics) { + ASSERT_FALSE(PathExists(metrics_file())); + + { + // Get this first so it isn't created inside the persistent allocator. + base::GetCreateHistogramResultHistogram(); + + base::SetPersistentHistogramMemoryAllocator( + new base::LocalPersistentMemoryAllocator(64 << 10, 0, kMetricsName)); + base::HistogramBase* foo = + base::Histogram::FactoryGet("foo", 1, 100, 10, 0); + base::HistogramBase* bar = + base::Histogram::FactoryGet("bar", 1, 100, 10, 0); + foo->Add(42); + bar->Add(84); + + scoped_ptr<base::PersistentMemoryAllocator> allocator( + base::ReleasePersistentHistogramMemoryAllocatorForTesting()); + base::File writer(metrics_file(), + base::File::FLAG_CREATE | base::File::FLAG_WRITE); + ASSERT_TRUE(writer.IsValid()); + ASSERT_EQ(static_cast<int>(allocator->used()), + writer.Write(0, (const char*)allocator->data(), + allocator->used())); + } + + // Register the file and allow the "checker" task to run. + ASSERT_TRUE(PathExists(metrics_file())); + provider()->RegisterFile(metrics_file(), + FileMetricsProvider::FILE_HISTOGRAMS_ATOMIC, + kMetricsName); + + // Record embedded snapshots via snapshot-manager. + provider()->OnDidCreateMetricsLog(); + RunTasks(); + { + HistogramFlattenerDeltaRecorder flattener; + base::HistogramSnapshotManager snapshot_manager(&flattener); + snapshot_manager.StartDeltas(); + provider()->RecordHistogramSnapshots(&snapshot_manager); + snapshot_manager.FinishDeltas(); + EXPECT_EQ(2U, flattener.GetRecordedDeltaHistogramNames().size()); + } + + // Make sure a second call to the snapshot-recorder doesn't break anything. + { + HistogramFlattenerDeltaRecorder flattener; + base::HistogramSnapshotManager snapshot_manager(&flattener); + snapshot_manager.StartDeltas(); + provider()->RecordHistogramSnapshots(&snapshot_manager); + snapshot_manager.FinishDeltas(); + EXPECT_EQ(0U, flattener.GetRecordedDeltaHistogramNames().size()); + } + + // Second full run on the same file should produce nothing. + provider()->OnDidCreateMetricsLog(); + RunTasks(); + { + HistogramFlattenerDeltaRecorder flattener; + base::HistogramSnapshotManager snapshot_manager(&flattener); + snapshot_manager.StartDeltas(); + provider()->RecordHistogramSnapshots(&snapshot_manager); + snapshot_manager.FinishDeltas(); + EXPECT_EQ(0U, flattener.GetRecordedDeltaHistogramNames().size()); + } + + // Update the time-stamp of the file to indicate that it is "new" and + // must be recorded. + { + base::File touch(metrics_file(), + base::File::FLAG_OPEN | base::File::FLAG_WRITE); + ASSERT_TRUE(touch.IsValid()); + base::Time next = base::Time::Now() + base::TimeDelta::FromSeconds(1); + touch.SetTimes(next, next); + } + + // This run should again have "new" histograms. + provider()->OnDidCreateMetricsLog(); + RunTasks(); + { + HistogramFlattenerDeltaRecorder flattener; + base::HistogramSnapshotManager snapshot_manager(&flattener); + snapshot_manager.StartDeltas(); + provider()->RecordHistogramSnapshots(&snapshot_manager); + snapshot_manager.FinishDeltas(); + EXPECT_EQ(2U, flattener.GetRecordedDeltaHistogramNames().size()); + } +} + +} // namespace metrics
diff --git a/components/metrics/metrics_pref_names.cc b/components/metrics/metrics_pref_names.cc index a5ad6a52..6eb58fa8 100644 --- a/components/metrics/metrics_pref_names.cc +++ b/components/metrics/metrics_pref_names.cc
@@ -56,6 +56,11 @@ // The metrics client session ID. const char kMetricsSessionID[] = "user_experience_metrics.session_id"; +// The prefix of the last-seen timestamp for persistent histogram files. +// Values are named for the files themselves. +const char kMetricsLastSeenPrefix[] = + "user_experience_metrics.last_seen."; + // Number of times the browser has been able to register crash reporting. const char kStabilityBreakpadRegistrationSuccess[] = "user_experience_metrics.stability.breakpad_registration_ok";
diff --git a/components/metrics/metrics_pref_names.h b/components/metrics/metrics_pref_names.h index 69c37b7..15f5aca 100644 --- a/components/metrics/metrics_pref_names.h +++ b/components/metrics/metrics_pref_names.h
@@ -26,6 +26,7 @@ extern const char kMetricsReportingEnabled[]; extern const char kMetricsReportingEnabledTimestamp[]; extern const char kMetricsSessionID[]; +extern const char kMetricsLastSeenPrefix[]; extern const char kStabilityBreakpadRegistrationSuccess[]; extern const char kStabilityBreakpadRegistrationFail[]; extern const char kStabilityChildProcessCrashCount[];
diff --git a/components/metrics/metrics_provider.cc b/components/metrics/metrics_provider.cc index fb9f931..5ba7382b 100644 --- a/components/metrics/metrics_provider.cc +++ b/components/metrics/metrics_provider.cc
@@ -12,6 +12,9 @@ MetricsProvider::~MetricsProvider() { } +void MetricsProvider::Init() { +} + void MetricsProvider::OnDidCreateMetricsLog() { } @@ -44,4 +47,8 @@ ChromeUserMetricsExtension* uma_proto) { } +void MetricsProvider::RecordHistogramSnapshots( + base::HistogramSnapshotManager* snapshot_manager) { +} + } // namespace metrics
diff --git a/components/metrics/metrics_provider.h b/components/metrics/metrics_provider.h index 569b1e8..664e5db 100644 --- a/components/metrics/metrics_provider.h +++ b/components/metrics/metrics_provider.h
@@ -7,6 +7,10 @@ #include "base/macros.h" +namespace base { +class HistogramSnapshotManager; +} // namespace base + namespace metrics { class ChromeUserMetricsExtension; @@ -20,6 +24,9 @@ MetricsProvider(); virtual ~MetricsProvider(); + // Called after initialiazation of MetricsService and field trials. + virtual void Init(); + // Called when a new MetricsLog is created. virtual void OnDidCreateMetricsLog(); @@ -63,6 +70,13 @@ virtual void ProvideGeneralMetrics( ChromeUserMetricsExtension* uma_proto); + // Called during collection to explicitly load histogram snapshots using a + // snapshot manager. PrepareDeltas() will have already been called and + // FinishDeltas() will be called later; calls to only PrepareDelta(), not + // PrepareDeltas (plural), should be made. + virtual void RecordHistogramSnapshots( + base::HistogramSnapshotManager* snapshot_manager); + private: DISALLOW_COPY_AND_ASSIGN(MetricsProvider); };
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc index a69d8e8..d17911a0 100644 --- a/components/metrics/metrics_service.cc +++ b/components/metrics/metrics_service.cc
@@ -324,6 +324,9 @@ // MetricsReportingScheduler is tied to the lifetime of |this|. base::Bind(&MetricsServiceClient::GetStandardUploadInterval, base::Unretained(client_)))); + + for (MetricsProvider* provider : metrics_providers_) + provider->Init(); } void MetricsService::Start() { @@ -390,8 +393,8 @@ if (!log_manager_.current_log()) OpenNewLog(); - for (size_t i = 0; i < metrics_providers_.size(); ++i) - metrics_providers_[i]->OnRecordingEnabled(); + for (MetricsProvider* provider : metrics_providers_) + provider->OnRecordingEnabled(); base::RemoveActionCallback(action_callback_); action_callback_ = base::Bind(&MetricsService::OnUserAction, @@ -410,8 +413,8 @@ base::RemoveActionCallback(action_callback_); - for (size_t i = 0; i < metrics_providers_.size(); ++i) - metrics_providers_[i]->OnRecordingDisabled(); + for (MetricsProvider* provider : metrics_providers_) + provider->OnRecordingDisabled(); PushPendingLogsToPersistentStorage(); } @@ -526,8 +529,8 @@ } void MetricsService::ClearSavedStabilityMetrics() { - for (size_t i = 0; i < metrics_providers_.size(); ++i) - metrics_providers_[i]->ClearSavedStabilityMetrics(); + for (MetricsProvider* provider : metrics_providers_) + provider->ClearSavedStabilityMetrics(); // Reset the prefs that are managed by MetricsService/MetricsLog directly. local_state_->SetInteger(prefs::kStabilityCrashCount, 0); @@ -679,8 +682,8 @@ void MetricsService::NotifyOnDidCreateMetricsLog() { DCHECK(IsSingleThreaded()); - for (size_t i = 0; i < metrics_providers_.size(); ++i) - metrics_providers_[i]->OnDidCreateMetricsLog(); + for (MetricsProvider* provider : metrics_providers_) + provider->OnDidCreateMetricsLog(); } //------------------------------------------------------------------------------ @@ -868,8 +871,8 @@ bool MetricsService::ProvidersHaveInitialStabilityMetrics() { // Check whether any metrics provider has initial stability metrics. - for (size_t i = 0; i < metrics_providers_.size(); ++i) { - if (metrics_providers_[i]->HasInitialStabilityMetrics()) + for (MetricsProvider* provider : metrics_providers_) { + if (provider->HasInitialStabilityMetrics()) return true; } @@ -1107,11 +1110,17 @@ void MetricsService::RecordCurrentHistograms() { DCHECK(log_manager_.current_log()); - // "true" indicates that StatisticsRecorder should include histograms in - // persistent storage. - histogram_snapshot_manager_.PrepareDeltas( - base::StatisticsRecorder::begin(true), base::StatisticsRecorder::end(), - base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag); + histogram_snapshot_manager_.StartDeltas(); + // "true" to the begin() call indicates that StatisticsRecorder should include + // histograms held in persistent storage. + auto end = base::StatisticsRecorder::end(); + for (auto it = base::StatisticsRecorder::begin(true); it != end; ++it) { + if ((*it)->flags() & base::Histogram::kUmaTargetedHistogramFlag) + histogram_snapshot_manager_.PrepareDelta(*it); + } + for (MetricsProvider* provider : metrics_providers_) + provider->RecordHistogramSnapshots(&histogram_snapshot_manager_); + histogram_snapshot_manager_.FinishDeltas(); } void MetricsService::RecordCurrentStabilityHistograms() {
diff --git a/components/metrics/metrics_service_unittest.cc b/components/metrics/metrics_service_unittest.cc index 9b734c1..4a8aea7 100644 --- a/components/metrics/metrics_service_unittest.cc +++ b/components/metrics/metrics_service_unittest.cc
@@ -397,4 +397,17 @@ EXPECT_TRUE(test_provider->on_recording_disabled_called()); } +TEST_F(MetricsServiceTest, MetricsProvidersInitialized) { + TestMetricsServiceClient client; + TestMetricsService service( + GetMetricsStateManager(), &client, GetLocalState()); + + TestMetricsProvider* test_provider = new TestMetricsProvider(); + service.RegisterMetricsProvider(scoped_ptr<MetricsProvider>(test_provider)); + + service.InitializeMetricsRecordingState(); + + EXPECT_TRUE(test_provider->init_called()); +} + } // namespace metrics
diff --git a/components/metrics/test_metrics_provider.cc b/components/metrics/test_metrics_provider.cc index 184c288..409d8d12 100644 --- a/components/metrics/test_metrics_provider.cc +++ b/components/metrics/test_metrics_provider.cc
@@ -8,6 +8,10 @@ namespace metrics { +void TestMetricsProvider::Init() { + init_called_ = true; +} + void TestMetricsProvider::OnRecordingDisabled() { on_recording_disabled_called_ = true; }
diff --git a/components/metrics/test_metrics_provider.h b/components/metrics/test_metrics_provider.h index 4d34b24..6536ea23 100644 --- a/components/metrics/test_metrics_provider.h +++ b/components/metrics/test_metrics_provider.h
@@ -15,12 +15,14 @@ class TestMetricsProvider : public MetricsProvider { public: TestMetricsProvider() - : on_recording_disabled_called_(false), + : init_called_(false), + on_recording_disabled_called_(false), has_initial_stability_metrics_(false), provide_initial_stability_metrics_called_(false), provide_stability_metrics_called_(false) {} // MetricsProvider: + void Init() override; void OnRecordingDisabled() override; bool HasInitialStabilityMetrics() override; void ProvideInitialStabilityMetrics( @@ -28,6 +30,7 @@ void ProvideStabilityMetrics( SystemProfileProto* system_profile_proto) override; + bool init_called() { return init_called_; } bool on_recording_disabled_called() { return on_recording_disabled_called_; } void set_has_initial_stability_metrics(bool has_initial_stability_metrics) { has_initial_stability_metrics_ = has_initial_stability_metrics; @@ -40,6 +43,7 @@ } private: + bool init_called_; bool on_recording_disabled_called_; bool has_initial_stability_metrics_; bool provide_initial_stability_metrics_called_;
diff --git a/components/mus/gles2/command_buffer_local.cc b/components/mus/gles2/command_buffer_local.cc index b45b0f9b..8964a8e 100644 --- a/components/mus/gles2/command_buffer_local.cc +++ b/components/mus/gles2/command_buffer_local.cc
@@ -89,6 +89,11 @@ void CommandBufferLocal::Destroy() { DCHECK(CalledOnValidThread()); + // After this |Destroy()| call, this object will not be used by client anymore + // and it will be deleted on the GPU thread. So we have to detach it from the + // client thread first. + DetachFromThread(); + weak_factory_.InvalidateWeakPtrs(); // CommandBufferLocal is initialized on the GPU thread with // InitializeOnGpuThread(), so we need delete memebers on the GPU thread
diff --git a/components/mus/public/cpp/lib/in_flight_change.cc b/components/mus/public/cpp/lib/in_flight_change.cc index 8e3b8de..6b012240 100644 --- a/components/mus/public/cpp/lib/in_flight_change.cc +++ b/components/mus/public/cpp/lib/in_flight_change.cc
@@ -59,30 +59,29 @@ CHECK(false); } -// InFlightFocusChange -------------------------------------------------------- +// InFlightWindowChange ------------------------------------------------------- -InFlightFocusChange::InFlightFocusChange(WindowTreeClientImpl* connection, - Window* window) - : InFlightChange(nullptr, ChangeType::FOCUS), - connection_(connection), +InFlightWindowTreeClientChange::InFlightWindowTreeClientChange( + WindowTreeClientImpl* client_connection, + Window* revert_value, + ChangeType type) + : InFlightChange(nullptr, type), + connection_(client_connection), revert_window_(nullptr) { - SetRevertWindow(window); + SetRevertWindow(revert_value); } -InFlightFocusChange::~InFlightFocusChange() { +InFlightWindowTreeClientChange::~InFlightWindowTreeClientChange() { SetRevertWindow(nullptr); } -void InFlightFocusChange::SetRevertValueFrom(const InFlightChange& change) { - SetRevertWindow( - static_cast<const InFlightFocusChange&>(change).revert_window_); +void InFlightWindowTreeClientChange::SetRevertValueFrom( + const InFlightChange& change) { + SetRevertWindow(static_cast<const InFlightWindowTreeClientChange&>(change) + .revert_window_); } -void InFlightFocusChange::Revert() { - connection_->LocalSetFocus(revert_window_); -} - -void InFlightFocusChange::SetRevertWindow(Window* window) { +void InFlightWindowTreeClientChange::SetRevertWindow(Window* window) { if (revert_window_) revert_window_->RemoveObserver(this); revert_window_ = window; @@ -90,10 +89,40 @@ revert_window_->AddObserver(this); } -void InFlightFocusChange::OnWindowDestroying(Window* window) { +void InFlightWindowTreeClientChange::OnWindowDestroying(Window* window) { SetRevertWindow(nullptr); } +// InFlightCaptureChange ------------------------------------------------------ + +InFlightCaptureChange::InFlightCaptureChange( + WindowTreeClientImpl* client_connection, + Window* revert_value) + : InFlightWindowTreeClientChange(client_connection, + revert_value, + ChangeType::CAPTURE) {} + +InFlightCaptureChange::~InFlightCaptureChange() {} + +void InFlightCaptureChange::Revert() { + connection()->LocalSetCapture(revert_window()); +} + +// InFlightFocusChange -------------------------------------------------------- + +InFlightFocusChange::InFlightFocusChange( + WindowTreeClientImpl* client_connection, + Window* revert_value) + : InFlightWindowTreeClientChange(client_connection, + revert_value, + ChangeType::FOCUS) {} + +InFlightFocusChange::~InFlightFocusChange() {} + +void InFlightFocusChange::Revert() { + connection()->LocalSetFocus(revert_window()); +} + // InFlightPropertyChange ----------------------------------------------------- InFlightPropertyChange::InFlightPropertyChange(
diff --git a/components/mus/public/cpp/lib/in_flight_change.h b/components/mus/public/cpp/lib/in_flight_change.h index d92c300..878ec77 100644 --- a/components/mus/public/cpp/lib/in_flight_change.h +++ b/components/mus/public/cpp/lib/in_flight_change.h
@@ -29,6 +29,7 @@ ADD_CHILD, ADD_TRANSIENT_WINDOW, BOUNDS, + CAPTURE, DELETE_WINDOW, FOCUS, NEW_WINDOW, @@ -155,18 +156,26 @@ DISALLOW_COPY_AND_ASSIGN(CrashInFlightChange); }; -// Focus is really a property of the WindowTreeConnection and not the Window. -// As such, InFlightFocusChange is special in that it is not associated with -// a particular window (InFlightFocusChange::window() returns null). Internally -// InFlightFocusChange tracks a window to give focus to, but it may be null. -class InFlightFocusChange : public InFlightChange, public WindowObserver { +// Use this class for properties that are specific to the connection, and not a +// particular window. For example, only a single window can have focus, so focus +// is specific to the connection. +// +// This does not implement InFlightChange::Revert, subclasses must implement +// that to update the WindowTreeConnection. +class InFlightWindowTreeClientChange : public InFlightChange, + public WindowObserver { public: - InFlightFocusChange(WindowTreeClientImpl* connection, Window* revert_window); - ~InFlightFocusChange() override; + InFlightWindowTreeClientChange(WindowTreeClientImpl* client_connection, + Window* revert_value, + ChangeType type); + ~InFlightWindowTreeClientChange() override; // InFlightChange: void SetRevertValueFrom(const InFlightChange& change) override; - void Revert() override; + + protected: + WindowTreeClientImpl* connection() { return connection_; } + Window* revert_window() { return revert_window_; } private: void SetRevertWindow(Window* window); @@ -177,6 +186,32 @@ WindowTreeClientImpl* connection_; Window* revert_window_; + DISALLOW_COPY_AND_ASSIGN(InFlightWindowTreeClientChange); +}; + +class InFlightCaptureChange : public InFlightWindowTreeClientChange { + public: + InFlightCaptureChange(WindowTreeClientImpl* client_connection, + Window* revert_value); + ~InFlightCaptureChange() override; + + // InFlightChange: + void Revert() override; + + private: + DISALLOW_COPY_AND_ASSIGN(InFlightCaptureChange); +}; + +class InFlightFocusChange : public InFlightWindowTreeClientChange { + public: + InFlightFocusChange(WindowTreeClientImpl* client_connection, + Window* revert_value); + ~InFlightFocusChange() override; + + // InFlightChange: + void Revert() override; + + private: DISALLOW_COPY_AND_ASSIGN(InFlightFocusChange); };
diff --git a/components/mus/public/cpp/lib/window.cc b/components/mus/public/cpp/lib/window.cc index 1f8f3ea3e..b2955f1 100644 --- a/components/mus/public/cpp/lib/window.cc +++ b/components/mus/public/cpp/lib/window.cc
@@ -372,6 +372,20 @@ tree_client()->SetImeVisibility(id_, visible, std::move(state)); } +bool Window::HasCapture() const { + return connection_ && connection_->GetCaptureWindow() == this; +} + +void Window::SetCapture() { + if (connection_) + tree_client()->SetCapture(this); +} + +void Window::ReleaseCapture() { + if (connection_) + tree_client()->ReleaseCapture(this); +} + void Window::SetFocus() { if (connection_ && IsDrawn()) tree_client()->SetFocus(this);
diff --git a/components/mus/public/cpp/lib/window_tree_client_impl.cc b/components/mus/public/cpp/lib/window_tree_client_impl.cc index 2d609fe6..b1107af 100644 --- a/components/mus/public/cpp/lib/window_tree_client_impl.cc +++ b/components/mus/public/cpp/lib/window_tree_client_impl.cc
@@ -119,6 +119,7 @@ next_change_id_(1), delegate_(delegate), window_manager_delegate_(window_manager_delegate), + capture_window_(nullptr), focused_window_(nullptr), binding_(this), tree_(nullptr), @@ -241,11 +242,27 @@ } void WindowTreeClientImpl::SetCapture(Window* window) { - NOTIMPLEMENTED(); + // In order for us to get here we had to have exposed a window, which implies + // we got a connection. + DCHECK(tree_); + if (capture_window_ == window) + return; + const uint32_t change_id = ScheduleInFlightChange( + make_scoped_ptr(new InFlightCaptureChange(this, capture_window_))); + tree_->SetCapture(change_id, window->id()); + LocalSetCapture(window); } void WindowTreeClientImpl::ReleaseCapture(Window* window) { - NOTIMPLEMENTED(); + // In order for us to get here we had to have exposed a window, which implies + // we got a connection. + DCHECK(tree_); + if (capture_window_ != window) + return; + const uint32_t change_id = ScheduleInFlightChange( + make_scoped_ptr(new InFlightCaptureChange(this, window))); + tree_->ReleaseCapture(change_id, window->id()); + LocalSetCapture(nullptr); } void WindowTreeClientImpl::SetClientArea( @@ -346,6 +363,17 @@ tree_->AttachSurface(window_id, type, std::move(surface), std::move(client)); } +void WindowTreeClientImpl::LocalSetCapture(Window* window) { + if (capture_window_ == window) + return; + Window* lost_capture = capture_window_; + capture_window_ = window; + if (lost_capture) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(lost_capture).observers(), + OnWindowLostCapture(lost_capture)); + } +} + void WindowTreeClientImpl::LocalSetFocus(Window* focused) { Window* blurred = focused_window_; // Update |focused_window_| before calling any of the observers, so that the @@ -558,7 +586,15 @@ } void WindowTreeClientImpl::OnLostCapture(Id window_id) { - NOTIMPLEMENTED(); + Window* window = GetWindowById(window_id); + if (!window) + return; + + InFlightCaptureChange reset_change(this, nullptr); + if (ApplyServerChangeToExistingInFlightChange(reset_change)) + return; + + LocalSetCapture(nullptr); } void WindowTreeClientImpl::OnTopLevelCreated(uint32_t change_id, @@ -741,6 +777,10 @@ WindowPrivate(window).LocalDestroy(); } +Window* WindowTreeClientImpl::GetCaptureWindow() { + return capture_window_; +} + void WindowTreeClientImpl::OnWindowVisibilityChanged(Id window_id, bool visible) { Window* window = GetWindowById(window_id);
diff --git a/components/mus/public/cpp/lib/window_tree_client_impl.h b/components/mus/public/cpp/lib/window_tree_client_impl.h index 6e2fc1c..991473bd 100644 --- a/components/mus/public/cpp/lib/window_tree_client_impl.h +++ b/components/mus/public/cpp/lib/window_tree_client_impl.h
@@ -105,6 +105,8 @@ mojo::InterfaceRequest<mojom::Surface> surface, mojom::SurfaceClientPtr client); + // Sets the input capture to |window| without notifying the server. + void LocalSetCapture(Window* window); // Sets focus to |window| without notifying the server. void LocalSetFocus(Window* window); @@ -120,6 +122,9 @@ // the last step of ~Window). void OnWindowDestroyed(Window* window); + // WindowTreeConnection: + Window* GetCaptureWindow() override; + private: friend class WindowTreeClientImplPrivate; @@ -266,6 +271,8 @@ IdToWindowMap windows_; + Window* capture_window_; + Window* focused_window_; mojo::Binding<WindowTreeClient> binding_;
diff --git a/components/mus/public/cpp/tests/test_window_tree.cc b/components/mus/public/cpp/tests/test_window_tree.cc index 114cfb7..398a6613 100644 --- a/components/mus/public/cpp/tests/test_window_tree.cc +++ b/components/mus/public/cpp/tests/test_window_tree.cc
@@ -99,9 +99,15 @@ void TestWindowTree::GetWindowTree(uint32_t window_id, const GetWindowTreeCallback& callback) {} -void TestWindowTree::SetCapture(uint32_t change_id, uint32_t window_id) {} +void TestWindowTree::SetCapture(uint32_t change_id, uint32_t window_id) { + got_change_ = true; + change_id_ = change_id; +} -void TestWindowTree::ReleaseCapture(uint32_t change_id, uint32_t window_id) {} +void TestWindowTree::ReleaseCapture(uint32_t change_id, uint32_t window_id) { + got_change_ = true; + change_id_ = change_id; +} void TestWindowTree::Embed(uint32_t window_id, mojom::WindowTreeClientPtr client,
diff --git a/components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc b/components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc index dc19a84..69c498b 100644 --- a/components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc +++ b/components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc
@@ -689,4 +689,120 @@ EXPECT_EQ(1u, setup.window_tree_connection()->GetRoots().size()); } +// Tests both SetCapture and ReleaseCapture, to ensure that Window is properly +// updated on failures. +TEST_F(WindowTreeClientImplTest, ExplicitCapture) { + WindowTreeSetup setup; + Window* root = setup.GetFirstRoot(); + ASSERT_TRUE(root); + + root->SetCapture(); + EXPECT_TRUE(root->HasCapture()); + uint32_t change_id1; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1)); + setup.window_tree_client()->OnChangeCompleted(change_id1, false); + EXPECT_FALSE(root->HasCapture()); + + root->SetCapture(); + EXPECT_TRUE(root->HasCapture()); + uint32_t change_id2; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id2)); + setup.window_tree_client()->OnChangeCompleted(change_id2, true); + EXPECT_TRUE(root->HasCapture()); + + root->ReleaseCapture(); + EXPECT_FALSE(root->HasCapture()); + uint32_t change_id3; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id3)); + setup.window_tree_client()->OnChangeCompleted(change_id3, false); + EXPECT_TRUE(root->HasCapture()); + + root->ReleaseCapture(); + uint32_t change_id4; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id4)); + setup.window_tree_client()->OnChangeCompleted(change_id4, true); + EXPECT_FALSE(root->HasCapture()); +} + +// Tests that when capture is lost, that the window tree updates properly. +TEST_F(WindowTreeClientImplTest, LostCapture) { + WindowTreeSetup setup; + Window* root = setup.GetFirstRoot(); + ASSERT_TRUE(root); + + root->SetCapture(); + EXPECT_TRUE(root->HasCapture()); + uint32_t change_id1; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1)); + setup.window_tree_client()->OnChangeCompleted(change_id1, true); + EXPECT_TRUE(root->HasCapture()); + + // The second SetCapture should be ignored. + root->SetCapture(); + uint32_t change_id2; + ASSERT_FALSE(setup.window_tree()->GetAndClearChangeId(&change_id2)); + + setup.window_tree_client()->OnLostCapture(root->id()); + EXPECT_FALSE(root->HasCapture()); +} + +// Tests that when capture is lost, while there is a release capture request +// inflight, that the revert value of that request is updated correctly. +TEST_F(WindowTreeClientImplTest, LostCaptureDifferentInFlightChange) { + WindowTreeSetup setup; + Window* root = setup.GetFirstRoot(); + ASSERT_TRUE(root); + + root->SetCapture(); + EXPECT_TRUE(root->HasCapture()); + uint32_t change_id1; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1)); + setup.window_tree_client()->OnChangeCompleted(change_id1, true); + EXPECT_TRUE(root->HasCapture()); + + // The ReleaseCapture should be updated to the revert of the SetCapture. + root->ReleaseCapture(); + uint32_t change_id2; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id2)); + + setup.window_tree_client()->OnLostCapture(root->id()); + EXPECT_FALSE(root->HasCapture()); + + setup.window_tree_client()->OnChangeCompleted(change_id2, false); + EXPECT_FALSE(root->HasCapture()); +} + +// Tests that while two windows can inflight capture requests, that the +// WindowTreeClient only identifies one as having the current capture. +TEST_F(WindowTreeClientImplTest, TwoWindowsRequestCapture) { + WindowTreeSetup setup; + Window* root = setup.GetFirstRoot(); + Window* child = setup.window_tree_connection()->NewWindow(); + child->SetVisible(true); + root->AddChild(child); + + root->SetCapture(); + EXPECT_TRUE(root->HasCapture()); + uint32_t change_id1; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id1)); + + child->SetCapture(); + EXPECT_TRUE(child->HasCapture()); + EXPECT_FALSE(root->HasCapture()); + + uint32_t change_id2; + ASSERT_TRUE(setup.window_tree()->GetAndClearChangeId(&change_id2)); + + setup.window_tree_client()->OnChangeCompleted(change_id1, true); + EXPECT_FALSE(root->HasCapture()); + EXPECT_TRUE(child->HasCapture()); + + setup.window_tree_client()->OnChangeCompleted(change_id2, false); + EXPECT_FALSE(child->HasCapture()); + EXPECT_TRUE(root->HasCapture()); + + setup.window_tree_client()->OnLostCapture(root->id()); + EXPECT_FALSE(root->HasCapture()); +} + } // namespace mus
diff --git a/components/mus/public/cpp/window.h b/components/mus/public/cpp/window.h index 24df811..4b5d711 100644 --- a/components/mus/public/cpp/window.h +++ b/components/mus/public/cpp/window.h
@@ -190,6 +190,10 @@ void SetTextInputState(mojo::TextInputStatePtr state); void SetImeVisibility(bool visible, mojo::TextInputStatePtr state); + bool HasCapture() const; + void SetCapture(); + void ReleaseCapture(); + // Focus. void SetFocus(); bool HasFocus() const;
diff --git a/components/mus/public/cpp/window_observer.h b/components/mus/public/cpp/window_observer.h index 9ea3e6da..54770e7 100644 --- a/components/mus/public/cpp/window_observer.h +++ b/components/mus/public/cpp/window_observer.h
@@ -54,6 +54,7 @@ virtual void OnWindowBoundsChanged(Window* window, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) {} + virtual void OnWindowLostCapture(Window* window) {} virtual void OnWindowClientAreaChanged( Window* window, const gfx::Insets& old_client_area,
diff --git a/components/mus/public/cpp/window_tree_connection.h b/components/mus/public/cpp/window_tree_connection.h index da5b06f7..31528ac 100644 --- a/components/mus/public/cpp/window_tree_connection.h +++ b/components/mus/public/cpp/window_tree_connection.h
@@ -69,6 +69,10 @@ // Returns a Window known to this connection. virtual Window* GetWindowById(Id id) = 0; + // Returns the Window with input capture; null if no window has requested + // input capture, or if another app has capture. + virtual Window* GetCaptureWindow() = 0; + // Returns the focused window; null if focus is not yet known or another app // is focused. virtual Window* GetFocusedWindow() = 0;
diff --git a/components/mus/public/interfaces/input_events.mojom b/components/mus/public/interfaces/input_events.mojom index 2ea63d0..0e205a0 100644 --- a/components/mus/public/interfaces/input_events.mojom +++ b/components/mus/public/interfaces/input_events.mojom
@@ -96,9 +96,10 @@ // |pressure| range is [0,1]. For devices like mice buttons where the // pressure is not available, it will be set to 0.5 if the button is down. float pressure; - // |tilt_y| and |tilt_z| are in degrees. + // |tilt_x| and |tilt_y| are in degrees. See comments in ui::PointerDetails + // for more information. + float tilt_x; float tilt_y; - float tilt_z; }; struct Event {
diff --git a/components/mus/ws/BUILD.gn b/components/mus/ws/BUILD.gn index 1e443a91..59d1e82 100644 --- a/components/mus/ws/BUILD.gn +++ b/components/mus/ws/BUILD.gn
@@ -119,8 +119,11 @@ deps = [ ":apptests", ":window_manager_unittests", - "//components/mus/public/cpp/tests:mojo_view_manager_lib_unittests", ] + if (!is_android) { + deps += + [ "//components/mus/public/cpp/tests:mojo_view_manager_lib_unittests" ] + } } test("window_manager_unittests") {
diff --git a/components/mus/ws/event_dispatcher.cc b/components/mus/ws/event_dispatcher.cc index 5d83169..013208a 100644 --- a/components/mus/ws/event_dispatcher.cc +++ b/components/mus/ws/event_dispatcher.cc
@@ -201,7 +201,9 @@ if (target == window) continue; mojom::EventPtr cancel_event = mojom::Event::New(); - cancel_event->action = mojom::EventType::POINTER_CANCEL; + cancel_event->action = pair.second.is_mouse_event + ? mojom::EventType::MOUSE_EXIT + : mojom::EventType::POINTER_CANCEL; cancel_event->flags = mojom::kEventFlagNone; cancel_event->time_stamp = base::TimeTicks::Now().ToInternalValue(); cancel_event->pointer_data = mojom::PointerData::New(); @@ -422,6 +424,8 @@ gfx::Point location(EventLocationToPoint(event)); pointer_target.window = FindDeepestVisibleWindowForEvents(root_, surface_id_, &location); + pointer_target.is_mouse_event = + event.pointer_data->kind == mojom::PointerKind::MOUSE; pointer_target.in_nonclient_area = IsLocationInNonclientArea(pointer_target.window, location); pointer_target.is_pointer_down =
diff --git a/components/mus/ws/event_dispatcher.h b/components/mus/ws/event_dispatcher.h index 814cf37..df0c78ee 100644 --- a/components/mus/ws/event_dispatcher.h +++ b/components/mus/ws/event_dispatcher.h
@@ -68,12 +68,17 @@ // Keeps track of state associated with an active pointer. struct PointerTarget { PointerTarget() - : window(nullptr), in_nonclient_area(false), is_pointer_down(false) {} + : window(nullptr), + is_mouse_event(false), + in_nonclient_area(false), + is_pointer_down(false) {} // NOTE: this is set to null if the window is destroyed before a // corresponding release/cancel. ServerWindow* window; + bool is_mouse_event; + // Did the pointer event start in the non-client area. bool in_nonclient_area;
diff --git a/components/mus/ws/event_dispatcher_unittest.cc b/components/mus/ws/event_dispatcher_unittest.cc index af6497a68..4fdda98 100644 --- a/components/mus/ws/event_dispatcher_unittest.cc +++ b/components/mus/ws/event_dispatcher_unittest.cc
@@ -942,7 +942,7 @@ ASSERT_TRUE(details); EXPECT_FALSE(event_dispatcher_delegate->has_queued_events()); EXPECT_EQ(child.get(), details->window); - EXPECT_EQ(mojom::EventType::POINTER_CANCEL, details->event->action); + EXPECT_EQ(mojom::EventType::MOUSE_EXIT, details->event->action); const ui::MouseEvent press_event( ui::ET_MOUSE_PRESSED, gfx::Point(15, 15), gfx::Point(15, 15),
diff --git a/components/mus/ws/window_manager_client_apptest.cc b/components/mus/ws/window_manager_client_apptest.cc index ac7e18e6..5d9f111 100644 --- a/components/mus/ws/window_manager_client_apptest.cc +++ b/components/mus/ws/window_manager_client_apptest.cc
@@ -1055,7 +1055,13 @@ ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1, 2)); } -TEST_F(WindowServerTest, EmbedFromEmbedRoot) { +// Flaky failure: http://crbug.com/587868 +#if defined(OS_LINUX) +#define MAYBE_EmbedFromEmbedRoot DISABLED_EmbedFromEmbedRoot +#else +#define MAYBE_EmbedFromEmbedRoot EmbedFromEmbedRoot +#endif +TEST_F(WindowServerTest, MAYBE_EmbedFromEmbedRoot) { Window* embed_window = window_manager()->NewWindow(); GetFirstWMRoot()->AddChild(embed_window);
diff --git a/components/new_or_sad_tab_strings.grdp b/components/new_or_sad_tab_strings.grdp new file mode 100644 index 0000000..e756c86 --- /dev/null +++ b/components/new_or_sad_tab_strings.grdp
@@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<grit-part> + + <message name="IDS_DEFAULT_TAB_TITLE" desc="The default title in a tab."> + Untitled + </message> + + <!-- Sad Tab Strings --> + <message name="IDS_SAD_TAB_TITLE" desc="The title of the sad tab page that is shown when a tab crashes. This is intended to be a humorous exclamation of dismay." formatter_data="android_java"> + Aw, Snap! + </message> + <message name="IDS_SAD_TAB_MESSAGE" desc="The message displayed on the sad tab page." formatter_data="android_java"> + Something went wrong while displaying this webpage. + </message> + <message name="IDS_SAD_TAB_HELP_MESSAGE" desc="The help message displayed on the sad tab page, with IDS_SAD_TAB_HELP_LINK embedded as a link to help."> + If you're seeing this frequently, try these <ph name="HELP_LINK">$1<ex>suggestions</ex></ph>. + </message> + <message name="IDS_SAD_TAB_HELP_LINK" desc="The link text displayed on the sad tab page pointing the users to a help article."> + suggestions + </message> + <message name="IDS_SAD_TAB_RELOAD_LABEL" desc="Button label in the sad tab page for reloading a page." formatter_data="android_java"> + Reload + </message> + + <!-- New Tab --> + <message name="IDS_NEW_TAB_TITLE" + desc="Title of the new tab page, not to be confused with the action of opening a new tab."> + New Tab + </message> + <if expr="not is_android"> + <message name="IDS_NEW_TAB_OTR_HEADING" + desc="Heading used when a person opens an OTR window"> + You’ve gone incognito + </message> + <message name="IDS_NEW_TAB_OTR_DESCRIPTION" + desc="Used when a person opens an OTR window"> + Pages you view in incognito tabs won’t stick around in your browser’s history, cookie store, or search history after you’ve closed all of your incognito tabs. Any files you download or bookmarks you create will be kept. + </message> + <message name="IDS_NEW_TAB_OTR_LEARN_MORE_LINK" + desc="OTR window link text to learn more"> + Learn more + </message> + <message name="IDS_NEW_TAB_OTR_MESSAGE_WARNING" + desc="OTR window warning message. This follows the IDS_NEW_TAB_OTR_DESCRIPTION paragraph"> + However, you aren’t invisible. Going incognito doesn’t hide your browsing from your employer, your internet service provider, or the websites you visit. + </message> + </if> + <message name="IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE" + desc="Action link text to undo removing a thumbnail from the most visited section."> + Undo + </message> + +</grit-part>
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher.cc b/components/password_manager/content/browser/credential_manager_dispatcher.cc index 74950e7..ecf9ddf 100644 --- a/components/password_manager/content/browser/credential_manager_dispatcher.cc +++ b/components/password_manager/content/browser/credential_manager_dispatcher.cc
@@ -99,7 +99,7 @@ DCHECK(request_id); PasswordStore* store = GetPasswordStore(); - if (!store) { + if (!store || !IsUpdatingCredentialAllowed()) { web_contents()->GetRenderViewHost()->Send( new CredentialManagerMsg_AcknowledgeRequireUserMediation( web_contents()->GetRenderViewHost()->GetRoutingID(), request_id)); @@ -228,6 +228,7 @@ if (PasswordStore* store = GetPasswordStore()) { if (info.type != CredentialType::CREDENTIAL_TYPE_EMPTY && IsZeroClickAllowed()) { + DCHECK(IsUpdatingCredentialAllowed()); scoped_ptr<autofill::PasswordForm> form( CreatePasswordFormFromCredentialInfo(info, pending_request_->origin())); @@ -263,4 +264,9 @@ pending_require_user_mediation_.reset(); } +bool CredentialManagerDispatcher::IsUpdatingCredentialAllowed() const { + return !client_->DidLastPageLoadEncounterSSLErrors() && + !client_->IsOffTheRecord(); +} + } // namespace password_manager
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher.h b/components/password_manager/content/browser/credential_manager_dispatcher.h index 4747736c8..0fa7f62 100644 --- a/components/password_manager/content/browser/credential_manager_dispatcher.h +++ b/components/password_manager/content/browser/credential_manager_dispatcher.h
@@ -64,9 +64,6 @@ // content::WebContentsObserver implementation. bool OnMessageReceived(const IPC::Message& message) override; - using CredentialCallback = - base::Callback<void(const autofill::PasswordForm&)>; - // CredentialManagerPendingRequestTaskDelegate: bool IsZeroClickAllowed() const override; GURL GetOrigin() const override; @@ -102,6 +99,9 @@ int request_id, const std::vector<std::string>& android_realms); + // Returns true iff it's OK to update credentials in the password store. + bool IsUpdatingCredentialAllowed() const; + PasswordManagerClient* client_; scoped_ptr<CredentialManagerPasswordFormManager> form_manager_;
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc b/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc index 676c571b..da2ab70 100644 --- a/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc +++ b/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
@@ -464,6 +464,33 @@ } TEST_F(CredentialManagerDispatcherTest, + CredentialManagerOnRequireUserMediationIncognito) { + EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true)); + store_->AddLogin(form_); + RunAllPendingTasks(); + + TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); + ASSERT_EQ(1U, passwords.size()); + ASSERT_EQ(1U, passwords[form_.signon_realm].size()); + EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click); + + dispatcher()->OnRequireUserMediation(kRequestId); + RunAllPendingTasks(); + + const uint32_t kMsgID = + CredentialManagerMsg_AcknowledgeRequireUserMediation::ID; + const IPC::Message* message = + process()->sink().GetFirstMessageMatching(kMsgID); + EXPECT_TRUE(message); + process()->sink().ClearMessages(); + + passwords = store_->stored_passwords(); + ASSERT_EQ(1U, passwords.size()); + ASSERT_EQ(1U, passwords[form_.signon_realm].size()); + EXPECT_FALSE(passwords[form_.signon_realm][0].skip_zero_click); +} + +TEST_F(CredentialManagerDispatcherTest, CredentialManagerOnRequireUserMediationWithAffiliation) { store_->AddLogin(form_); store_->AddLogin(cross_origin_form_); @@ -844,6 +871,38 @@ EXPECT_TRUE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click); } +TEST_F(CredentialManagerDispatcherTest, + NoResetSkipZeroClickAfterPromptInIncognito) { + EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true)); + // Turn on the global zero-click flag which should be overriden by Incognito. + client_->set_zero_click_enabled(true); + form_.skip_zero_click = true; + store_->AddLogin(form_); + RunAllPendingTasks(); + + // Sanity check. + TestPasswordStore::PasswordMap passwords = store_->stored_passwords(); + ASSERT_EQ(1U, passwords.size()); + ASSERT_EQ(1U, passwords[form_.signon_realm].size()); + EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); + + // Trigger a request which should return the credential found in |form_|, and + // wait for it to process. + EXPECT_CALL(*client_, PromptUserToChooseCredentialsPtr(_, _, _, _)) + .Times(testing::Exactly(1)); + EXPECT_CALL(*client_, NotifyUserAutoSigninPtr(_)).Times(testing::Exactly(0)); + + dispatcher()->OnRequestCredential(kRequestId, false, true, + std::vector<GURL>()); + RunAllPendingTasks(); + + // The form shouldn't become a zero-click one. + passwords = store_->stored_passwords(); + ASSERT_EQ(1U, passwords.size()); + ASSERT_EQ(1U, passwords[form_.signon_realm].size()); + EXPECT_TRUE(passwords[form_.signon_realm][0].skip_zero_click); +} + TEST_F(CredentialManagerDispatcherTest, IncognitoZeroClickRequestCredential) { EXPECT_CALL(*client_, IsOffTheRecord()).WillRepeatedly(testing::Return(true)); store_->AddLogin(form_);
diff --git a/components/policy/core/browser/browser_policy_connector.cc b/components/policy/core/browser/browser_policy_connector.cc index f4908c03..a680fc5 100644 --- a/components/policy/core/browser/browser_policy_connector.cc +++ b/components/policy/core/browser/browser_policy_connector.cc
@@ -42,9 +42,7 @@ } // Regexes that match many of the larger public email providers as we know -// these users are not from hosted enterprise domains. Keep this list in sync -// with the EnterpriseDomainRegex enum in histograms.xml (i.e. only add things -// at the end). +// these users are not from hosted enterprise domains. const wchar_t* const kNonManagedDomainPatterns[] = { L"aol\\.com", L"googlemail\\.com", @@ -56,6 +54,7 @@ L"qq\\.com", L"yahoo(\\.co|\\.com|)\\.[^.]+", // yahoo.com, yahoo.co.uk, yahoo.com.tw L"yandex\\.ru", + L"consumer\\.example\\.com", }; // Returns true if |domain| matches the regex |pattern|.
diff --git a/components/printing/renderer/print_web_view_helper.cc b/components/printing/renderer/print_web_view_helper.cc index 91c7230..5b4b4525 100644 --- a/components/printing/renderer/print_web_view_helper.cc +++ b/components/printing/renderer/print_web_view_helper.cc
@@ -1811,19 +1811,6 @@ if (buf_size == 0) return false; -#if defined(OS_WIN) - base::SharedMemory shared_buf; - // Allocate a shared memory buffer to hold the generated metafile data. - if (!shared_buf.CreateAndMapAnonymous(buf_size)) - return false; - - // Copy the bits into shared memory. - if (!metafile.GetData(shared_buf.memory(), buf_size)) - return false; - - *shared_mem_handle = base::SharedMemory::DuplicateHandle(shared_buf.handle()); - return true; -#else scoped_ptr<base::SharedMemory> shared_buf( content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(buf_size)); if (!shared_buf) @@ -1835,9 +1822,9 @@ if (!metafile.GetData(shared_buf->memory(), buf_size)) return false; - return shared_buf->GiveToProcess(base::GetCurrentProcessHandle(), - shared_mem_handle); -#endif // defined(OS_WIN) + *shared_mem_handle = + base::SharedMemory::DuplicateHandle(shared_buf->handle()); + return true; } #if defined(ENABLE_PRINT_PREVIEW) @@ -2024,11 +2011,7 @@ } metafile_.reset(new PdfMetafileSkia); - if (!metafile_->Init()) { - set_error(PREVIEW_ERROR_METAFILE_INIT_FAILED); - LOG(ERROR) << "PdfMetafileSkia Init failed"; - return false; - } + CHECK(metafile_->Init()); current_page_index_ = 0; pages_to_render_ = pages;
diff --git a/components/printing/renderer/print_web_view_helper.h b/components/printing/renderer/print_web_view_helper.h index ca10384..39ef858 100644 --- a/components/printing/renderer/print_web_view_helper.h +++ b/components/printing/renderer/print_web_view_helper.h
@@ -146,9 +146,9 @@ PREVIEW_ERROR_NONE, // Always first. PREVIEW_ERROR_BAD_SETTING, PREVIEW_ERROR_METAFILE_COPY_FAILED, - PREVIEW_ERROR_METAFILE_INIT_FAILED, + PREVIEW_ERROR_METAFILE_INIT_FAILED_DEPRECATED, PREVIEW_ERROR_ZERO_PAGES, - PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED, + PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED_DEPRECATED, PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE, PREVIEW_ERROR_INVALID_PRINTER_SETTINGS, PREVIEW_ERROR_LAST_ENUM // Always last.
diff --git a/components/printing/renderer/print_web_view_helper_linux.cc b/components/printing/renderer/print_web_view_helper_linux.cc index 13178404..d34c9644 100644 --- a/components/printing/renderer/print_web_view_helper_linux.cc +++ b/components/printing/renderer/print_web_view_helper_linux.cc
@@ -23,8 +23,7 @@ bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, int page_count) { PdfMetafileSkia metafile; - if (!metafile.Init()) - return false; + CHECK(metafile.Init()); const PrintMsg_PrintPages_Params& params = *print_pages_params_; std::vector<int> printed_pages = GetPrintedPages(params, page_count);
diff --git a/components/printing/renderer/print_web_view_helper_mac.mm b/components/printing/renderer/print_web_view_helper_mac.mm index 08a1c10..65526661 100644 --- a/components/printing/renderer/print_web_view_helper_mac.mm +++ b/components/printing/renderer/print_web_view_helper_mac.mm
@@ -42,8 +42,7 @@ const PrintMsg_PrintPage_Params& params, blink::WebFrame* frame) { PdfMetafileSkia metafile; - if (!metafile.Init()) - return; + CHECK(metafile.Init()); int page_number = params.page_number; gfx::Size page_size_in_dpi; @@ -82,12 +81,7 @@ if (render_to_draft) { draft_metafile.reset(new PdfMetafileSkia()); - if (!draft_metafile->Init()) { - print_preview_context_.set_error( - PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED); - LOG(ERROR) << "Draft PdfMetafileSkia Init failed"; - return false; - } + CHECK(draft_metafile->Init()); initial_render_metafile = draft_metafile.get(); }
diff --git a/components/printing/renderer/print_web_view_helper_pdf_win.cc b/components/printing/renderer/print_web_view_helper_pdf_win.cc index a674c2b..3b19474a 100644 --- a/components/printing/renderer/print_web_view_helper_pdf_win.cc +++ b/components/printing/renderer/print_web_view_helper_pdf_win.cc
@@ -16,10 +16,6 @@ #if defined(ENABLE_BASIC_PRINTING) bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, int page_count) { - PdfMetafileSkia metafile; - if (!metafile.Init()) - return false; - const PrintMsg_PrintPages_Params& params = *print_pages_params_; std::vector<int> printed_pages = GetPrintedPages(params, page_count); if (printed_pages.empty()) @@ -28,6 +24,9 @@ std::vector<gfx::Size> page_size_in_dpi(printed_pages.size()); std::vector<gfx::Rect> content_area_in_dpi(printed_pages.size()); + PdfMetafileSkia metafile; + CHECK(metafile.Init()); + PrintMsg_PrintPage_Params page_params; page_params.params = params.params; for (size_t i = 0; i < printed_pages.size(); ++i) {
diff --git a/components/printing/test/mock_printer.cc b/components/printing/test/mock_printer.cc index 004e008e..99bdcb36 100644 --- a/components/printing/test/mock_printer.cc +++ b/components/printing/test/mock_printer.cc
@@ -228,10 +228,9 @@ } const MockPrinterPage* MockPrinter::GetPrintedPage(unsigned int pageno) const { - if (pages_.size() > pageno) - return pages_[pageno].get(); - else - return NULL; + if (pageno >= pages_.size()) + return nullptr; + return pages_[pageno].get(); } int MockPrinter::GetWidth(unsigned int page) const {
diff --git a/components/resources/dom_distiller_resources.grdp b/components/resources/dom_distiller_resources.grdp index cc1e30e..d340722 100644 --- a/components/resources/dom_distiller_resources.grdp +++ b/components/resources/dom_distiller_resources.grdp
@@ -13,4 +13,5 @@ <include name="IDR_EXTRACT_PAGE_FEATURES_JS" file="../dom_distiller/core/javascript/extract_features.js" type="BINDATA" /> <include name="IDR_DISTILLABLE_PAGE_SERIALIZED_MODEL" file="../dom_distiller/core/data/distillable_page_model.bin" type="BINDATA" /> <include name="IDR_DISTILLABLE_PAGE_SERIALIZED_MODEL_NEW" file="../dom_distiller/core/data/distillable_page_model_new.bin" type="BINDATA" /> + <include name="IDR_LONG_PAGE_SERIALIZED_MODEL" file="../dom_distiller/core/data/long_page_model.bin" type="BINDATA" /> </grit-part>
diff --git a/components/safe_browsing_db/database_manager.h b/components/safe_browsing_db/database_manager.h index 8167b3ae..fd8a276f 100644 --- a/components/safe_browsing_db/database_manager.h +++ b/components/safe_browsing_db/database_manager.h
@@ -49,6 +49,11 @@ // Called when the result of checking the API blacklist is known. virtual void OnCheckApiBlacklistUrlResult(const GURL& url, const std::string& metadata) {} + + // Called when the result of checking the resource blacklist is known. + virtual void OnCheckResourceUrlResult(const GURL& url, + SBThreatType threat_type, + const std::string& threat_hash) {} }; @@ -90,6 +95,11 @@ virtual bool CheckExtensionIDs(const std::set<std::string>& extension_ids, Client* client) = 0; + // Check if |url| is in the resources blacklist. Returns true if not, false + // if further checks need to be made in which case the result will be passed + // to callback in |client|. + virtual bool CheckResourceUrl(const GURL& url, Client* client) = 0; + // Check if the |url| matches any of the full-length hashes from the client- // side phishing detection whitelist. Returns true if there was a match and // false otherwise. To make sure we are conservative we will return true if
diff --git a/components/safe_browsing_db/remote_database_manager.cc b/components/safe_browsing_db/remote_database_manager.cc index 8b1c480..e699b305 100644 --- a/components/safe_browsing_db/remote_database_manager.cc +++ b/components/safe_browsing_db/remote_database_manager.cc
@@ -213,6 +213,12 @@ return true; } +bool RemoteSafeBrowsingDatabaseManager::CheckResourceUrl(const GURL& url, + Client* client) { + NOTREACHED(); + return true; +} + bool RemoteSafeBrowsingDatabaseManager::IsMalwareKillSwitchOn() { NOTREACHED(); return true;
diff --git a/components/safe_browsing_db/remote_database_manager.h b/components/safe_browsing_db/remote_database_manager.h index 89c71f57..6b2c764 100644 --- a/components/safe_browsing_db/remote_database_manager.h +++ b/components/safe_browsing_db/remote_database_manager.h
@@ -55,6 +55,7 @@ bool MatchDownloadWhitelistString(const std::string& str) override; bool MatchInclusionWhitelistUrl(const GURL& url) override; bool MatchModuleWhitelistString(const std::string& str) override; + bool CheckResourceUrl(const GURL& url, Client* client) override; bool IsMalwareKillSwitchOn() override; bool IsCsdWhitelistKillSwitchOn() override;
diff --git a/components/safe_browsing_db/test_database_manager.cc b/components/safe_browsing_db/test_database_manager.cc index 454c358..d24cb6a 100644 --- a/components/safe_browsing_db/test_database_manager.cc +++ b/components/safe_browsing_db/test_database_manager.cc
@@ -64,6 +64,12 @@ return true; } +bool TestSafeBrowsingDatabaseManager::CheckResourceUrl(const GURL& url, + Client* client) { + NOTIMPLEMENTED(); + return true; +} + bool TestSafeBrowsingDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) { NOTIMPLEMENTED(); return true;
diff --git a/components/safe_browsing_db/test_database_manager.h b/components/safe_browsing_db/test_database_manager.h index aa25c51b..2b01c20c 100644 --- a/components/safe_browsing_db/test_database_manager.h +++ b/components/safe_browsing_db/test_database_manager.h
@@ -31,6 +31,7 @@ Client* client) override; bool CheckExtensionIDs(const std::set<std::string>& extension_ids, Client* client) override; + bool CheckResourceUrl(const GURL& url, Client* client) override; bool MatchCsdWhitelistUrl(const GURL& url) override; bool MatchMalwareIP(const std::string& ip_address) override; bool MatchDownloadWhitelistUrl(const GURL& url) override;
diff --git a/components/safe_browsing_db/util.cc b/components/safe_browsing_db/util.cc index 7289a0f7..2eaf630 100644 --- a/components/safe_browsing_db/util.cc +++ b/components/safe_browsing_db/util.cc
@@ -53,11 +53,12 @@ const char kUnwantedUrlList[] = "goog-unwanted-shavar"; const char kInclusionWhitelist[] = "goog-csdinclusionwhite-sha256"; const char kModuleWhitelist[] = "goog-whitemodule-digest256"; +const char kResourceBlacklist[] = "goog-badresource-shavar"; -const char* kAllLists[10] = { +const char* kAllLists[11] = { kMalwareList, kPhishingList, kBinUrlList, kCsdWhiteList, kDownloadWhiteList, kExtensionBlacklist, kIPBlacklist, kUnwantedUrlList, - kInclusionWhitelist, kModuleWhitelist, + kInclusionWhitelist, kModuleWhitelist, kResourceBlacklist, }; ListType GetListId(const base::StringPiece& name) { @@ -82,6 +83,8 @@ id = INCLUSIONWHITELIST; } else if (name == kModuleWhitelist) { id = MODULEWHITELIST; + } else if (name == kResourceBlacklist) { + id = RESOURCEBLACKLIST; } else { id = INVALID; } @@ -119,6 +122,8 @@ break; case MODULEWHITELIST: *list = kModuleWhitelist; + case RESOURCEBLACKLIST: + *list = kResourceBlacklist; break; default: return false;
diff --git a/components/safe_browsing_db/util.h b/components/safe_browsing_db/util.h index d350e101..643e0a4 100644 --- a/components/safe_browsing_db/util.h +++ b/components/safe_browsing_db/util.h
@@ -47,6 +47,10 @@ // Url detected by the client-side malware IP list. This IP list is part // of the client side detection model. SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL, + + // Url leads to a blacklisted resource script. Note that no warnings should be + // shown on this threat type, but an incident report might be sent. + SB_THREAT_TYPE_BLACKLISTED_RESOURCE, }; @@ -102,8 +106,10 @@ extern const char kInclusionWhitelist[]; // SafeBrowsing module whitelist list name. extern const char kModuleWhitelist[]; -// This array must contain all Safe Browsing lists. -extern const char* kAllLists[10]; +// Blacklisted resource URLs list name. +extern const char kResourceBlacklist[]; +/// This array must contain all Safe Browsing lists. +extern const char* kAllLists[11]; enum ListType { INVALID = -1, @@ -129,6 +135,8 @@ // See above comment. Leave 17 available. MODULEWHITELIST = 18, // See above comment. Leave 19 available. + RESOURCEBLACKLIST = 20, + // See above comment. Leave 21 available. }; inline bool SBFullHashEqual(const SBFullHash& a, const SBFullHash& b) {
diff --git a/components/signin/core/browser/account_tracker_service.h b/components/signin/core/browser/account_tracker_service.h index 9663f9b..b92e0a9 100644 --- a/components/signin/core/browser/account_tracker_service.h +++ b/components/signin/core/browser/account_tracker_service.h
@@ -67,7 +67,9 @@ enum AccountIdMigrationState { MIGRATION_NOT_STARTED, MIGRATION_IN_PROGRESS, - MIGRATION_DONE + MIGRATION_DONE, + // Keep in sync with OAuth2LoginAccountRevokedMigrationState histogram enum. + NUM_MIGRATION_STATES }; AccountTrackerService();
diff --git a/components/strings/components_strings_am.xtb b/components/strings/components_strings_am.xtb index 0a01fe4..70d8cdeec 100644 --- a/components/strings/components_strings_am.xtb +++ b/components/strings/components_strings_am.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">ግዴታ</translation> <translation id="7542995811387359312">ይህ ቅጽ ደህንነቱ የተጠበቀ ግንኙነት ስለማይጠቀም የክሬዲት ካርድ ራስ-መሙላት ተሰናክሏል።</translation> <translation id="7549584377607005141">ይህ ድረ-ገጽ በአግባቡ እንዲታይ ቀደም ብለው ያስገቡት ውሂብ ያስፈልገዋል። ይህን ውሂብ እንደገና መላክ ይችላሉ፣ ነገር ግን ይህን በማድረግዎ ይህ ገጽ ከዚህ በፊት ያከናወነው ማንኛውም እርምጃ ይደግማሉ።</translation> +<translation id="7554791636758816595">አዲስ ትር</translation> <translation id="7567204685887185387">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው በተጭበረበረ ሁኔታ ተሰጥቶ ሊሆን ይችላል። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation> <translation id="7568593326407688803">ይህ ገጽ በ<ph name="ORIGINAL_LANGUAGE" />ነው። መተርጎም ይፈልጋሉ?</translation> <translation id="7569952961197462199">ክሬዲት ካርድ ከChrome ይወገድ?</translation>
diff --git a/components/strings/components_strings_ar.xtb b/components/strings/components_strings_ar.xtb index 3f9d79a5..ba257ab0 100644 --- a/components/strings/components_strings_ar.xtb +++ b/components/strings/components_strings_ar.xtb
@@ -547,6 +547,7 @@ <translation id="7537536606612762813">إلزامية</translation> <translation id="7542995811387359312">تم تعطيل الملء التلقائي لبطاقة الائتمان لأن هذا النموذج لا يستخدم اتصالاً آمنًا.</translation> <translation id="7549584377607005141">تتطلب صفحة الويب هذه البيانات التي أدخلتها في وقت سابق لعرضها بشكل صحيح. يمكنك إرسال هذه المعلومات مرة أخرى ولكن بذلك ستكرر أي إجراء اتخذته هذه الصفحة في وقت سابق.</translation> +<translation id="7554791636758816595">علامة تبويب جديدة</translation> <translation id="7567204685887185387">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />؛ بل إنه شهادة أمان تم إصدارها عن طريق الاحتيال. وربما يكون سبب ذلك خطأ في التكوين أو مهاجمًا يعترض اتصالك.</translation> <translation id="7568593326407688803">تتوفر هذه الصفحة باللغة<ph name="ORIGINAL_LANGUAGE" />فهل تريد ترجمتها؟</translation> <translation id="7569952961197462199">هل تريد إزالة بطاقة الائتمان من Chrome؟</translation>
diff --git a/components/strings/components_strings_bg.xtb b/components/strings/components_strings_bg.xtb index 3766d64..5bd585f7 100644 --- a/components/strings/components_strings_bg.xtb +++ b/components/strings/components_strings_bg.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Задължително</translation> <translation id="7542995811387359312">Автоматичното попълване на кредитната карта е деактивирано, защото този формуляр не използва защитена връзка.</translation> <translation id="7549584377607005141">За да се покаже правилно тази уеб страница, са необходими по-рано въведените от вас данни. Можете да ги изпратите отново, но така ще повторите всяко изпълнено от нея действие.</translation> +<translation id="7554791636758816595">Нов раздел</translation> <translation id="7567204685887185387">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; възможно е сертификатът му за сигурност да е издаден измамнически. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation> <translation id="7568593326407688803">Тази страница е на<ph name="ORIGINAL_LANGUAGE" />Искате ли да я преведете?</translation> <translation id="7569952961197462199">Кредитната карта да се премахне ли от Chrome?</translation>
diff --git a/components/strings/components_strings_bn.xtb b/components/strings/components_strings_bn.xtb index f45d5ed..ecc60af 100644 --- a/components/strings/components_strings_bn.xtb +++ b/components/strings/components_strings_bn.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">বাধ্যতামূলক</translation> <translation id="7542995811387359312">স্বয়ংক্রিয় ক্রেডিট কার্ড পূরণটি অক্ষম রয়েছে কারণ এই ফর্মটি কোনও সুরক্ষিত সংযোগ ব্যবহার করে না৷</translation> <translation id="7549584377607005141">এই ওয়েবপৃষ্ঠার এমন ডেটার দরকার যা আপনি ঠিকভাবে প্রদর্শিত হওয়ার জন্য আগেই প্রবেশ করেছেন৷ আপনি এই ডেটাটি আবার পাঠাতে পারেন, কিন্তু এমনটি করে আপনি যেকোনো পদক্ষেপের পুনরাবৃত্তি করবেন যা এই পৃষ্ঠাটি আগেই সম্পাদনা করেছে৷</translation> +<translation id="7554791636758816595">নতুন ট্যাব</translation> <translation id="7567204685887185387">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্র প্রতারণাপূর্ণভাবে ইস্যু করা হয়ে থাকতে পারে। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation> <translation id="7568593326407688803">এই পৃষ্ঠাটি<ph name="ORIGINAL_LANGUAGE" />ভাষাতে আছে আপনি কি এটিকে অনুবাদ করতে চাইবেন?</translation> <translation id="7569952961197462199">Chrome থেকে ক্রেডিট কার্ড সরাবেন?</translation>
diff --git a/components/strings/components_strings_ca.xtb b/components/strings/components_strings_ca.xtb index 89f47b48..d6503d6f 100644 --- a/components/strings/components_strings_ca.xtb +++ b/components/strings/components_strings_ca.xtb
@@ -545,6 +545,7 @@ <translation id="7537536606612762813">Obligatòria</translation> <translation id="7542995811387359312">L'emplenament automàtic de targetes de crèdit està desactivat perquè el formulari no utilitza una connexió segura.</translation> <translation id="7549584377607005141">Aquesta pàgina web necessita dades que heu introduït anteiorment per poder mostrar-la correctament. Podeu tornar a enviar les dades, però es tornarà a repetir qualsevol acció que la pàgina hagi dut a terme prèviament.</translation> +<translation id="7554791636758816595">Pestanya nova</translation> <translation id="7567204685887185387">Aquest servidor no ha pogut comprovar que sigui <ph name="DOMAIN" /> perquè és possible que el seu certificat de seguretat s'hagi emès de manera fraudulenta. Això pot ser a causa d'una configuració incorrecta o d'un atacant que intercepta la vostra connexió.</translation> <translation id="7568593326407688803">Aquesta pàgina està en<ph name="ORIGINAL_LANGUAGE" />Voleu traduir-la?</translation> <translation id="7569952961197462199">Voleu suprimir la targeta de crèdit de Chrome?</translation>
diff --git a/components/strings/components_strings_cs.xtb b/components/strings/components_strings_cs.xtb index f57a2de..81ddd753 100644 --- a/components/strings/components_strings_cs.xtb +++ b/components/strings/components_strings_cs.xtb
@@ -527,6 +527,7 @@ <translation id="7537536606612762813">Povinná</translation> <translation id="7542995811387359312">Automatické vyplňování údajů platební karty je deaktivováno, protože tento formulář nepoužívá zabezpečené připojení.</translation> <translation id="7549584377607005141">Tato stránka potřebuje ke správnému zobrazení data, která jste zadali dříve. Tyto údaje můžete odeslat znovu, ale zopakujete tím všechny akce, které tato stránka již předtím provedla.</translation> +<translation id="7554791636758816595">Nová karta</translation> <translation id="7567204685887185387">Server nedokázal prokázat, že patří doméně <ph name="DOMAIN" />. Jeho bezpečnostní certifikát byl zřejmě vydán podvodně. Může to být způsobeno nesprávnou konfigurací nebo tím, že vaše připojení zachytává útočník.</translation> <translation id="7568593326407688803">Tato stránka je v jazyce<ph name="ORIGINAL_LANGUAGE" />Chcete ji přeložit?</translation> <translation id="7569952961197462199">Odstranit platební kartu z Chromu?</translation>
diff --git a/components/strings/components_strings_da.xtb b/components/strings/components_strings_da.xtb index 48eb9b2d..c47bc63 100644 --- a/components/strings/components_strings_da.xtb +++ b/components/strings/components_strings_da.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obligatorisk</translation> <translation id="7542995811387359312">Automatisk udfyldning af kreditkort er deaktiveret, fordi formularen ikke bruger en sikker forbindelse.</translation> <translation id="7549584377607005141">Denne webside kræver data, du tidligere har indtastet, før den kan vises korrekt. Du kan sende disse data igen, men hvis du gør dette, gentager du enhver handling, som denne side tidligere har foretaget.</translation> +<translation id="7554791636758816595">Ny fane</translation> <translation id="7567204685887185387">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da sikkerhedscertifikatet er udstedt på ulovlig vis. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation> <translation id="7568593326407688803">Denne side er på<ph name="ORIGINAL_LANGUAGE" />Vil du oversætte den?</translation> <translation id="7569952961197462199">Vil du fjerne kreditkortet fra Chrome?</translation>
diff --git a/components/strings/components_strings_de.xtb b/components/strings/components_strings_de.xtb index 79ae90f..a50225a 100644 --- a/components/strings/components_strings_de.xtb +++ b/components/strings/components_strings_de.xtb
@@ -549,6 +549,7 @@ <translation id="7537536606612762813">Verbindlich</translation> <translation id="7542995811387359312">Die Funktion zur automatischen Ausfüllung der Kreditkartendaten ist deaktiviert, da dieses Formular keine sichere Verbindung nutzt.</translation> <translation id="7549584377607005141">Damit diese Webseite richtig angezeigt wird, werden die Daten benötigt, die Sie zuvor eingegeben haben. Sie können diese Daten erneut senden, dabei werden jedoch sämtliche Aktionen wiederholt, die zuvor durch diese Seite ausgeführt wurden.</translation> +<translation id="7554791636758816595">Neuer Tab</translation> <translation id="7567204685887185387">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat wurde möglicherweise in betrügerischer Absicht ausgegeben. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt.</translation> <translation id="7568593326407688803">Diese Seite ist auf<ph name="ORIGINAL_LANGUAGE" />Soll sie übersetzt werden?</translation> <translation id="7569952961197462199">Kreditkarte aus Chrome entfernen?</translation>
diff --git a/components/strings/components_strings_el.xtb b/components/strings/components_strings_el.xtb index 0c3311d..23f246e 100644 --- a/components/strings/components_strings_el.xtb +++ b/components/strings/components_strings_el.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Υποχρεωτική</translation> <translation id="7542995811387359312">Η αυτόματη συμπλήρωση πιστωτικής κάρτας έχει απενεργοποιηθεί, επειδή αυτή η φόρμα δεν χρησιμοποιεί ασφαλή σύνδεση.</translation> <translation id="7549584377607005141">Για τη σωστή εμφάνιση αυτής της ιστοσελίδας, απαιτούνται δεδομένα που καταχωρίσατε νωρίτερα. Μπορείτε να αποστείλετε ξανά αυτά τα δεδομένα, ωστόσο, έτσι θα επαναλάβετε κάθε ενέργεια που εκτέλεσε νωρίτερα αυτή η σελίδα.</translation> +<translation id="7554791636758816595">Νέα καρτέλα</translation> <translation id="7567204685887185387">Ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του μπορεί να εκδόθηκε παράνομα. Αυτό μπορεί να οφείλεται σε λανθασμένη ρύθμιση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation> <translation id="7568593326407688803">Αυτή η σελίδα είναι στα<ph name="ORIGINAL_LANGUAGE" />Θέλετε να τη μεταφράσετε;</translation> <translation id="7569952961197462199">Κατάργηση πιστωτικής κάρτας από το Chrome;</translation>
diff --git a/components/strings/components_strings_en-GB.xtb b/components/strings/components_strings_en-GB.xtb index 4adf876..e6dc8cd 100644 --- a/components/strings/components_strings_en-GB.xtb +++ b/components/strings/components_strings_en-GB.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Mandatory</translation> <translation id="7542995811387359312">Automatic credit card filling is disabled because this form does not use a secure connection.</translation> <translation id="7549584377607005141">This web page requires data that you entered earlier in order to be properly displayed. You can send this data again, but by doing so you will repeat any action this page previously performed.</translation> +<translation id="7554791636758816595">New Tab</translation> <translation id="7567204685887185387">This server could not prove that it is <ph name="DOMAIN" />; its security certificate might have been issued fraudulently. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation> <translation id="7568593326407688803">This page is in<ph name="ORIGINAL_LANGUAGE" />Would you like to translate it?</translation> <translation id="7569952961197462199">Remove credit card from Chrome?</translation>
diff --git a/components/strings/components_strings_es-419.xtb b/components/strings/components_strings_es-419.xtb index 1bffd68..da26c61 100644 --- a/components/strings/components_strings_es-419.xtb +++ b/components/strings/components_strings_es-419.xtb
@@ -545,6 +545,7 @@ <translation id="7537536606612762813">Obligatoria</translation> <translation id="7542995811387359312">El rellenado automático de la tarjeta de crédito se inhabilitó porque este formulario no usa una conexión segura.</translation> <translation id="7549584377607005141">Esta página web necesita los datos ingresados anteriormente para mostrarse correctamente. Puedes volver a enviar los datos, pero ten en cuenta que se repetirán las acciones que la página haya realizado anteriormente.</translation> +<translation id="7554791636758816595">Nueva pestaña</translation> <translation id="7567204685887185387">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; el certificado de seguridad podría haberse emitido de forma fraudulenta. Es posible que esto se deba a una configuración incorrecta o a que un atacante interceptó la conexión.</translation> <translation id="7568593326407688803">Esta página está en<ph name="ORIGINAL_LANGUAGE" />¿Quieres traducirla?</translation> <translation id="7569952961197462199">¿Confirmas que quieres quitar la tarjeta de crédito de Chrome?</translation>
diff --git a/components/strings/components_strings_es.xtb b/components/strings/components_strings_es.xtb index 59ff6cd..96f98fc 100644 --- a/components/strings/components_strings_es.xtb +++ b/components/strings/components_strings_es.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obligatoria</translation> <translation id="7542995811387359312">La opción de autocompletado de la tarjeta de crédito está inhabilitada porque este formulario no utiliza una conexión segura.</translation> <translation id="7549584377607005141">Esta página web necesita los datos introducidos anteriormente para mostrarse correctamente. Puedes volver a enviar los datos, pero se repetirán las acciones que haya realizado la página.</translation> +<translation id="7554791636758816595">Nueva pestaña</translation> <translation id="7567204685887185387">Este servidor no ha podido probar que su dominio es <ph name="DOMAIN" />, su certificado de seguridad podría haberse emitido de forma fraudulenta. El problema puede deberse a una configuración incorrecta o a que un atacante haya interceptado la conexión.</translation> <translation id="7568593326407688803">Esta página está escrita en<ph name="ORIGINAL_LANGUAGE" />¿Quieres traducirla?</translation> <translation id="7569952961197462199">¿Eliminar tarjeta de crédito de Chrome?</translation>
diff --git a/components/strings/components_strings_et.xtb b/components/strings/components_strings_et.xtb index 8eb6b00..008f7eb 100644 --- a/components/strings/components_strings_et.xtb +++ b/components/strings/components_strings_et.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Kohustuslik</translation> <translation id="7542995811387359312">Automaatne krediitkaardi täide on keelatud, sest see vorm ei kasuta turvalist ühendust.</translation> <translation id="7549584377607005141">See veebileht vajab korralikult kuvamiseks varem sisestatud andmeid. Võite need andmed uuesti saata, kuid seda tehes kordate lehe iga eelnevat toimingut.</translation> +<translation id="7554791636758816595">Uus vaheleht</translation> <translation id="7567204685887185387">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />, selle turvasertifikaat võib olla väljastatud pettuse teel. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation> <translation id="7568593326407688803">See leht on keeles<ph name="ORIGINAL_LANGUAGE" />Kas soovite seda tõlkida?</translation> <translation id="7569952961197462199">Kas eemaldada Chrome'ist krediitkaart?</translation>
diff --git a/components/strings/components_strings_fa.xtb b/components/strings/components_strings_fa.xtb index 8f426c9..13f0a6d 100644 --- a/components/strings/components_strings_fa.xtb +++ b/components/strings/components_strings_fa.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">اجباری</translation> <translation id="7542995811387359312">تکمیل خودکار کارت اعتباری غیر فعال است زیرا این فرم از یک اتصال امن استفاده نمیکند.</translation> <translation id="7549584377607005141">این صفحه وب برای نمایش صحیح به دادههایی نیاز دارد که قبلاً وارد کردهاید. میتوانید این دادهها را دوباره ارسال کنید، اما با انجام این کار، هر اقدامی که این صفحه قبلاً انجام داده است تکرار میشود.</translation> +<translation id="7554791636758816595">برگهٔ جدید</translation> <translation id="7567204685887185387">این سرور نتوانست اثبات کند که این <ph name="DOMAIN" /> است؛ ممکن است گواهی امنیتی آن به صورت تقلبی صادر شده باشد. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجمی اتصال شما را قطع کرده است.</translation> <translation id="7568593326407688803">این صفحه به <ph name="ORIGINAL_LANGUAGE" /> است، آیا مایلید ترجمه شود؟</translation> <translation id="7569952961197462199">کارت اعتباری از Chrome پاک شود؟</translation>
diff --git a/components/strings/components_strings_fi.xtb b/components/strings/components_strings_fi.xtb index c3c420617..807832f 100644 --- a/components/strings/components_strings_fi.xtb +++ b/components/strings/components_strings_fi.xtb
@@ -547,6 +547,7 @@ <translation id="7537536606612762813">Pakollinen</translation> <translation id="7542995811387359312">Automaattinen luottokortin tietojen täyttäminen on poistettu käytöstä, koska tämä lomake ei käytä suojattua yhteyttä.</translation> <translation id="7549584377607005141">Tämän sivun näyttäminen oikein edellyttää aiemmin lähetettyjä tietoja. Voit lähettää tiedot uudelleen, mutta tällöin sivulla mahdollisesti suoritettu toiminto toistetaan.</translation> +<translation id="7554791636758816595">Uusi välilehti</translation> <translation id="7567204685887185387">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />; sen suojausvarmenne on ehkä luotu vilpillisesti. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation> <translation id="7568593326407688803">Tämä sivu on kirjoitettu kielellä<ph name="ORIGINAL_LANGUAGE" />Haluatko kääntää sen?</translation> <translation id="7569952961197462199">Poistetaanko luottokortti Chromen tiedoista?</translation>
diff --git a/components/strings/components_strings_fil.xtb b/components/strings/components_strings_fil.xtb index 74a5ec7c..2505c75 100644 --- a/components/strings/components_strings_fil.xtb +++ b/components/strings/components_strings_fil.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Kinakailangan</translation> <translation id="7542995811387359312">Hindi pinagana ang awtomatikong pagpuno ng credit card dahil ang form na ito ay hindi gumagamit ng secure na koneksyon.</translation> <translation id="7549584377607005141">Kinakailangan ng webpage na ito ng data na inilagay mo dati upang maipakita nang maayos. Maipapadala mong muli ang data na ito, ngunit kapag ginawa mo iyon, mauulit ang anumang pagkilos na isinagawa dati ng pahinang ito.</translation> +<translation id="7554791636758816595">Bagong Tab</translation> <translation id="7567204685887185387">Hindi mapatunayan ng server na ito na ito ay <ph name="DOMAIN" />; maaaring mapanlokong ibinigay ang certificate ng seguridad nito. Maaaring dulot ito ng maling configuration o isang umaatake na hinahadlangan ang iyong koneksyon.</translation> <translation id="7568593326407688803">Ang pahinang ito ay nasa<ph name="ORIGINAL_LANGUAGE" />Gusto mong isalin ito?</translation> <translation id="7569952961197462199">Alisin ang credit card sa Chrome?</translation>
diff --git a/components/strings/components_strings_fr.xtb b/components/strings/components_strings_fr.xtb index 0ef02ff..9e851ac 100644 --- a/components/strings/components_strings_fr.xtb +++ b/components/strings/components_strings_fr.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obligatoire</translation> <translation id="7542995811387359312">La saisie automatique des numéros de carte de paiement est désactivée, car la connexion utilisée par ce formulaire n'est pas sécurisée.</translation> <translation id="7549584377607005141">Pour s'afficher correctement, cette page Web a besoin des données que vous avez saisies précédemment. Vous pouvez envoyer de nouveau ces données. Cependant, en procédant ainsi, vous répéterez toute action déjà effectuée sur cette page.</translation> +<translation id="7554791636758816595">Nouvel onglet</translation> <translation id="7567204685887185387">Impossible de vérifier sur le serveur qu'il s'agit bien du domaine <ph name="DOMAIN" />. Il se peut que son certificat de sécurité ait été émis de manière frauduleuse. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation> <translation id="7568593326407688803">Cette page est en<ph name="ORIGINAL_LANGUAGE" />Voulez-vous la traduire ?</translation> <translation id="7569952961197462199">Supprimer les données de carte de paiement de Chrome ?</translation>
diff --git a/components/strings/components_strings_gu.xtb b/components/strings/components_strings_gu.xtb index fbceb20..6ba2e7d 100644 --- a/components/strings/components_strings_gu.xtb +++ b/components/strings/components_strings_gu.xtb
@@ -547,6 +547,7 @@ <translation id="7537536606612762813">ફરજિયાત</translation> <translation id="7542995811387359312">આપમેળે ક્રેડિટ કાર્ડ ભરણ અક્ષમ કર્યું છે કારણ કે આ ફોર્મ સુરક્ષિત કનેક્શનનો ઉપયોગ કરતું નથી.</translation> <translation id="7549584377607005141">આ વેબપૃષ્ઠને તે ડેટાની જરૂર છે જે તમે પહેલા બરાબર રીતે પ્રદર્શિત થાય તે માટે દાખલ કર્યો હતો. તમે આ ડેટા ફરીથી મોકલી શકો છો, પણ આમ કરીને તમે કોઈપણ ક્રિયા કે જે પૃષ્ઠ પહેલા જ કરી ચુક્યું છે તેનું પુનરાવર્તન કરશો.</translation> +<translation id="7554791636758816595">નવું ટૅબ</translation> <translation id="7567204685887185387">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર કપટપૂર્વક રજૂ કરવામાં આવેલ હોઈ શકે છે. આ કોઈ ખોટી ગોઠવણીને કારણે થયું હશે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો છે.</translation> <translation id="7568593326407688803">આ પૃષ્ઠ<ph name="ORIGINAL_LANGUAGE" />માં છે શું તમે તેને અનુવાદિત કરવા માંગો છો?</translation> <translation id="7569952961197462199">Chrome માંથી ક્રેડિટ કાર્ડ દૂર કરીએ?</translation>
diff --git a/components/strings/components_strings_hi.xtb b/components/strings/components_strings_hi.xtb index 1b5f7d9..4371c7d53 100644 --- a/components/strings/components_strings_hi.xtb +++ b/components/strings/components_strings_hi.xtb
@@ -544,6 +544,7 @@ <translation id="7537536606612762813">आवश्यक</translation> <translation id="7542995811387359312">स्वतः क्रेडिट कार्ड भरना अक्षम किया गया है क्योंकि यह फ़ॉर्म किसी सुरक्षित कनेक्शन का उपयोग नहीं करता है.</translation> <translation id="7549584377607005141">इस वेबपृष्ठ को आपके द्वारा पहले दर्ज किए गए डेटा की आवश्यकता है, ताकि इसे सही रूप में प्रदर्शित किया जा सके. आप यह डेटा पुन: भेज सकते हैं, लेकिन ऐसा करके आप इस पृष्ठ द्वारा पहले की जा चुकी कोई कार्यवाही दोहराएंगे.</translation> +<translation id="7554791636758816595">नया टैब</translation> <translation id="7567204685887185387">यह सर्वर यह प्रमाणित नहीं कर सका कि यह <ph name="DOMAIN" /> है; हो सकता है इसका सुरक्षा प्रमाणपत्र धोखे से जारी किया गया हो. ऐसा गलत कॉन्फ़िगरेशन के कारण या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation> <translation id="7568593326407688803">यह पृष्ठ<ph name="ORIGINAL_LANGUAGE" />में है क्या आप इसका अनुवाद करना चाहेंगे?</translation> <translation id="7569952961197462199">Chrome से क्रेडिट कार्ड निकालें?</translation>
diff --git a/components/strings/components_strings_hr.xtb b/components/strings/components_strings_hr.xtb index c494b05..d5e6c744 100644 --- a/components/strings/components_strings_hr.xtb +++ b/components/strings/components_strings_hr.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obavezno</translation> <translation id="7542995811387359312">Automatsko popunjavanje kreditne kartice onemogućeno je jer se ovaj obrazac ne služi sigurnom vezom.</translation> <translation id="7549584377607005141">Ova web-stranica zahtijeva podatke koje ste ranije unijeli da bi se pravilno prikazala. Te podatke možete poslati ponovo, ali time ćete ponoviti sve radnje koje je ta stranica prethodno izvela.</translation> +<translation id="7554791636758816595">Nova kartica</translation> <translation id="7567204685887185387">Poslužitelj nije mogao dokazati da je <ph name="DOMAIN" />; njegov sigurnosni certifikat možda je lažan. To može biti uzrokovano pogrešnom konfiguracijom ili napadom na vašu vezu.</translation> <translation id="7568593326407688803">Ova je stranica na ovom jeziku:<ph name="ORIGINAL_LANGUAGE" />Želite li je prevesti?</translation> <translation id="7569952961197462199">Želite li s Chromea ukloniti kreditnu karticu?</translation>
diff --git a/components/strings/components_strings_hu.xtb b/components/strings/components_strings_hu.xtb index 4454b796..244c646 100644 --- a/components/strings/components_strings_hu.xtb +++ b/components/strings/components_strings_hu.xtb
@@ -545,6 +545,7 @@ <translation id="7537536606612762813">Kötelező</translation> <translation id="7542995811387359312">Az automatikus bankkártya-kitöltés le van tiltva, mivel ez az űrlap nem biztonságos kapcsolatot használ.</translation> <translation id="7549584377607005141">Ez a weboldal korábban megadott adatokat kér ahhoz, hogy megfelelően jelenjen meg. Az adatokat újra elküldheti, de ezzel meg fog ismételni minden olyan műveletet, amelyet ez az oldal korábban végrehajtott.</translation> +<translation id="7554791636758816595">Új lap</translation> <translation id="7567204685887185387">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványát csalással állíthatták ki. Ennek oka lehet konfigurációs hiba, vagy hogy egy támadó eltérítette az Ön kapcsolódását.</translation> <translation id="7568593326407688803">Az oldal nyelve<ph name="ORIGINAL_LANGUAGE" />Kívánja lefordítani?</translation> <translation id="7569952961197462199">Eltávolítja a hitelkártyát a Chrome-ból?</translation>
diff --git a/components/strings/components_strings_id.xtb b/components/strings/components_strings_id.xtb index b69f154..b71adcbd 100644 --- a/components/strings/components_strings_id.xtb +++ b/components/strings/components_strings_id.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Wajib</translation> <translation id="7542995811387359312">Pengisian kartu kredit otomatis dinonaktifkan karena formulir ini tidak menggunakan sambungan aman.</translation> <translation id="7549584377607005141">Laman web ini membutuhkan data yang Anda masukkan sebelumnya agar dapat ditampilkan dengan benar. Anda dapat mengirimkan data ini lagi, namun dengan begitu Anda akan mengulangi tindakan apa pun yang sebelumnya dilakukan oleh laman ini.</translation> +<translation id="7554791636758816595">Tab Baru</translation> <translation id="7567204685887185387">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya mungkin telah dikeluarkan dengan curang. Hal ini disebabkan oleh kesalahan konfigurasi atau penyerang memotong sambungan Anda.</translation> <translation id="7568593326407688803">Laman ini dalam bahasa<ph name="ORIGINAL_LANGUAGE" />Ingin diterjemahkan?</translation> <translation id="7569952961197462199">Hapus kartu kredit dari Chrome?</translation>
diff --git a/components/strings/components_strings_it.xtb b/components/strings/components_strings_it.xtb index a1114244..378e22f 100644 --- a/components/strings/components_strings_it.xtb +++ b/components/strings/components_strings_it.xtb
@@ -527,6 +527,7 @@ <translation id="7537536606612762813">Obbligatoria</translation> <translation id="7542995811387359312">La compilazione automatica della carta di credito è disattivata perché questo modulo non utilizza una connessione sicura.</translation> <translation id="7549584377607005141">Questa pagina web richiede dati che hai inserito in precedenza per poter essere visualizzata correttamente. Puoi inviare di nuovo i dati, ma in questo caso ripeterai l'azione precedentemente eseguita nella pagina.</translation> +<translation id="7554791636758816595">Nuova scheda</translation> <translation id="7567204685887185387">Questo server non è riuscito a dimostrare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza potrebbe essere stato emesso in modo fraudolento. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione.</translation> <translation id="7568593326407688803">Questa pagina è in<ph name="ORIGINAL_LANGUAGE" />Vuoi tradurla?</translation> <translation id="7569952961197462199">Rimuovere la carta di credito da Chrome?</translation>
diff --git a/components/strings/components_strings_iw.xtb b/components/strings/components_strings_iw.xtb index 1631ca9..a233a673 100644 --- a/components/strings/components_strings_iw.xtb +++ b/components/strings/components_strings_iw.xtb
@@ -555,6 +555,7 @@ <translation id="7537536606612762813">הכרחי</translation> <translation id="7542995811387359312">מילוי אוטומטי של פרטי כרטיס אשראי מושבת כיוון שטופס זה אינו משתמש בחיבור מאובטח.</translation> <translation id="7549584377607005141">דף אינטרנט זה זקוק לנתונים שהזנת קודם לכן כדי שהוא יוצג כראוי. אתה יכול לשלוח שוב את הנתונים, אך פעולה זו תגרום לחזרה על כל פעולה שדף זה ביצע בעבר.</translation> +<translation id="7554791636758816595">כרטיסייה חדשה</translation> <translation id="7567204685887185387">השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />. ייתכן שאישור האבטחה שלו נופק כהונאה. הסיבה לכך עשויה להיות הגדרה שגויה או תוקף המיירט את החיבור שלך.</translation> <translation id="7568593326407688803">זהו דף ב<ph name="ORIGINAL_LANGUAGE" />האם ברצונך לתרגם אותו?</translation> <translation id="7569952961197462199">האם להסיר את כרטיס האשראי מ-Chrome?</translation>
diff --git a/components/strings/components_strings_ja.xtb b/components/strings/components_strings_ja.xtb index 0b6e342..8271e03 100644 --- a/components/strings/components_strings_ja.xtb +++ b/components/strings/components_strings_ja.xtb
@@ -546,6 +546,7 @@ <translation id="7537536606612762813">必須</translation> <translation id="7542995811387359312">このフォームは安全な接続を使用していないため、クレジットカードの自動入力が無効になっています。</translation> <translation id="7549584377607005141">このウェブページを正しく表示するには、先ほど入力したデータが必要です。データは再送信できますが、このページで先ほど行った操作を繰り返すことになります。</translation> +<translation id="7554791636758816595">新しいタブ</translation> <translation id="7567204685887185387">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書は不正に発行されたものである可能性があります。原因としては、不適切な設定や、悪意のあるユーザーによる接続妨害が考えられます。</translation> <translation id="7568593326407688803">これは<ph name="ORIGINAL_LANGUAGE" />のページです。翻訳しますか?</translation> <translation id="7569952961197462199">Chrome からクレジット カードを削除してもよろしいですか?</translation>
diff --git a/components/strings/components_strings_kn.xtb b/components/strings/components_strings_kn.xtb index da81d1c..e624f1ced 100644 --- a/components/strings/components_strings_kn.xtb +++ b/components/strings/components_strings_kn.xtb
@@ -538,6 +538,7 @@ <translation id="7537536606612762813">ಕಡ್ಡಾಯ</translation> <translation id="7542995811387359312">ಈ ಫಾರ್ಮ್ ಸುರಕ್ಷಿತವಾದ ಸಂಪರ್ಕವನ್ನು ಬಳಸುತ್ತಿಲ್ಲವಾದ ಕಾರಣ ಸ್ವಯಂಚಾಲಿತ ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಭರ್ತಿ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ.</translation> <translation id="7549584377607005141">ಈ ವೆಬ್ಪುಟವು ಸರಿಯಾಗಿ ಪ್ರದರ್ಶನಗೊಳ್ಳಲು ಈ ಮೊದಲು ನೀವು ನಮೂದಿಸಿದ ಡೇಟಾದ ಅಗತ್ಯವಿದೆ. ನೀವು ಈ ಡೇಟಾವನ್ನು ಮತ್ತೆ ಕಳುಹಿಸಬಹುದು, ಆದರೆ ಹಾಗೆ ಮಾಡುವುದರಿಂದ ಈ ಪುಟವು ಈ ಮೊದಲು ಪೂರೈಸಿದ ಯಾವುದೇ ಕ್ರಿಯೆಯನ್ನು ನೀವು ಪುನರಾವರ್ತಿಸುತ್ತೀರಿ.</translation> +<translation id="7554791636758816595">ಹೊಸ ಟ್ಯಾಬ್</translation> <translation id="7567204685887185387">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರವನ್ನು ವಂಚನೆಯಿಂದ ನೀಡಿರಬಹುದು. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation> <translation id="7568593326407688803">ಈ ಪುಟವು<ph name="ORIGINAL_LANGUAGE" />ನಲ್ಲಿದೆ ನೀವು ಅದನ್ನು ಭಾಷಾಂತರಿಸಲು ಬಯಸುವಿರಾ?</translation> <translation id="7569952961197462199">Chrome ನಿಂದ ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ತೆಗೆದುಹಾಕುವುದೇ?</translation>
diff --git a/components/strings/components_strings_ko.xtb b/components/strings/components_strings_ko.xtb index d07fae2..9fa4a47 100644 --- a/components/strings/components_strings_ko.xtb +++ b/components/strings/components_strings_ko.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">필수</translation> <translation id="7542995811387359312">양식에 보안 연결이 사용되지 않아 신용카드 자동 채우기가 사용 중지되었습니다.</translation> <translation id="7549584377607005141">이 웹페이지를 제대로 표시하려면 이전에 입력한 데이터가 필요합니다. 이 데이터를 다시 보낼 수 있지만 이 경우 해당 페이지에서 이전에 수행한 작업이 반복됩니다.</translation> +<translation id="7554791636758816595">새 탭</translation> <translation id="7567204685887185387">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 서버의 보안 인증서가 부정한 방식으로 발행되었을 수 있습니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation> <translation id="7568593326407688803">이 페이지는<ph name="ORIGINAL_LANGUAGE" />로 되어 있습니다. 번역하시겠습니까?</translation> <translation id="7569952961197462199">Chrome에서 신용카드를 삭제하시겠습니까?</translation>
diff --git a/components/strings/components_strings_lt.xtb b/components/strings/components_strings_lt.xtb index b376b9f..afb8378f 100644 --- a/components/strings/components_strings_lt.xtb +++ b/components/strings/components_strings_lt.xtb
@@ -549,6 +549,7 @@ <translation id="7537536606612762813">Privaloma</translation> <translation id="7542995811387359312">Automatinis kredito kortelės informacijos pildymas neleidžiamas, nes šiai formai nenaudojamas saugus ryšys.</translation> <translation id="7549584377607005141">Jei norite, kad šis tinklalapis būtų tinkamai pateikiamas, reikalingi anksčiau įvesti duomenys. Galite nusiųsti šiuos duomenis iš naujo, bet taip pakartosite visus anksčiau šiame puslapyje įvykdytus veiksmus.</translation> +<translation id="7554791636758816595">Naujas skirtukas</translation> <translation id="7567204685887185387">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikatas gali būti neteisėtai išduotas. Taip gali nutikti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užgrobėjo.</translation> <translation id="7568593326407688803">Šis puslapis yra<ph name="ORIGINAL_LANGUAGE" />Ar norėtumėte jį išversti?</translation> <translation id="7569952961197462199">Pašalinti kredito kortelės informaciją iš „Chrome“?</translation>
diff --git a/components/strings/components_strings_lv.xtb b/components/strings/components_strings_lv.xtb index b5db0a7..9c79001a 100644 --- a/components/strings/components_strings_lv.xtb +++ b/components/strings/components_strings_lv.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obligāti</translation> <translation id="7542995811387359312">Automātiska kredītkartes numura ievadīšana ir atspējota, jo šai veidlapai netiek izmantots drošs savienojums.</translation> <translation id="7549584377607005141">Lai tīmekļa lapu varētu attēlot pareizi, tai nepieciešami jūsu iepriekš ievadītie dati. Varat atkārtoti nosūtīt šos datus, taču tādā gadījumā tiks atkārtota ikviena darbība, ko pirms tam veica lapa.</translation> +<translation id="7554791636758816595">Jauna cilne</translation> <translation id="7567204685887185387">Šis serveris nevarēja pierādīt, ka šī ir vietne <ph name="DOMAIN" />; tās drošības sertifikāts, iespējams, ir izveidots krāpnieciski. Iespējams, tas ir nepareizas konfigurācijas dēļ vai arī kāds ir ļaunprātīgi izmantojis jūsu savienojumu.</translation> <translation id="7568593326407688803">Šī lapa ir rakstīta<ph name="ORIGINAL_LANGUAGE" />valodā. Vai vēlaties to tulkot?</translation> <translation id="7569952961197462199">Vai noņemt kredītkarti no pārlūka Chrome?</translation>
diff --git a/components/strings/components_strings_ml.xtb b/components/strings/components_strings_ml.xtb index 503af3b..0048969 100644 --- a/components/strings/components_strings_ml.xtb +++ b/components/strings/components_strings_ml.xtb
@@ -546,6 +546,7 @@ <translation id="7537536606612762813">നിർബന്ധിതം</translation> <translation id="7542995811387359312">ഈ ഫോം ഒരു സുരക്ഷിത കണക്ഷന് ഉപയോഗിക്കാത്തതിനാല് സ്വപ്രേരിത ക്രെഡിറ്റ് കാര്ഡ് പൂരിപ്പിക്കല് അപ്രാപ്തമാക്കി.</translation> <translation id="7549584377607005141">ശരിയായി പ്രദർശിപ്പിക്കാൻ ഈ വെബ് പേജിന് നിങ്ങൾ മുമ്പ് നൽകിയ ഡാറ്റ ആവശ്യമാണ്. നിങ്ങൾക്ക് ഈ ഡാറ്റ വീണ്ടും അയയ്ക്കാനാകുമെങ്കിലും, അങ്ങനെ ചെയ്യുന്നത് ഈ പേജിൽ മുമ്പ് ചെയ്ത ഏത് പ്രവർത്തനവും നിങ്ങൾക്ക് ആവർത്തിക്കേണ്ടതായി വരും.</translation> +<translation id="7554791636758816595">പുതിയ ടാബ്</translation> <translation id="7567204685887185387">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; സെർവറിന്റെ സുരക്ഷ സർട്ടിഫിക്കറ്റ് വഞ്ചനാപരമായി ഇഷ്യൂ ചെയ്തിരിക്കാം. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation> <translation id="7568593326407688803">ഈ പേജ്<ph name="ORIGINAL_LANGUAGE" />ലാണ് നിങ്ങളത് വിവര്ത്തനം ചെയ്യാന് താല്പ്പര്യപ്പെടുന്നോ?</translation> <translation id="7569952961197462199">Chrome-ൽ നിന്ന് ക്രെഡിറ്റ് കാർഡ് നീക്കംചെയ്യണോ?</translation>
diff --git a/components/strings/components_strings_mr.xtb b/components/strings/components_strings_mr.xtb index 6be36de..d6dcd89 100644 --- a/components/strings/components_strings_mr.xtb +++ b/components/strings/components_strings_mr.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">अनिवार्य</translation> <translation id="7542995811387359312">स्वयंचलित क्रेडिट कार्ड भरणे अक्षम झाले आहे कारण हा फॉर्म सुरक्षित कनेक्शन वापरत नाही.</translation> <translation id="7549584377607005141">हे वेबपृष्ठ योग्यरितीने प्रदर्शित केले जाण्यासाठी आपण पूर्वी प्रविष्ट केलेला डेटा आवश्यक आहे. आपण हा डेटा पुन्हा पाठवू शकता, परंतु असे केल्याने या पृष्ठाने मागे केलेली कोणत्याही क्रियेची पुनरावृत्ती आपण कराल.</translation> +<translation id="7554791636758816595">नवीन टॅब</translation> <translation id="7567204685887185387">हा सर्व्हर हे <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षितता प्रमाणपत्र कदाचित लबाडीने जारी केले असावे. हे कदाचित एका चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्त्याने आपले कनेक्शन आंतरखंडित केल्यामुळे झाले असू शकते.</translation> <translation id="7568593326407688803">हे पृष्ठ<ph name="ORIGINAL_LANGUAGE" />मध्ये आहे आपण याचा अनुवाद करु इच्छिता?</translation> <translation id="7569952961197462199">Chrome मधून क्रेडिट कार्ड काढायचे?</translation>
diff --git a/components/strings/components_strings_ms.xtb b/components/strings/components_strings_ms.xtb index 7041352..83b755d5 100644 --- a/components/strings/components_strings_ms.xtb +++ b/components/strings/components_strings_ms.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Wajib</translation> <translation id="7542995811387359312">Pengisian kad kredit automatik dilumpuhkan kerana borang ini tidak menggunakan sambungan selamat.</translation> <translation id="7549584377607005141">Halaman web ini memerlukan data yang anda masukkan sebelum ini agar dapat dipaparkan dengan betul. Anda boleh menghantar data ini semula, tetapi dengan berbuat demikian anda akan mengulangi sebarang tindakan terdahulu yang telah dilakukan oleh halaman ini.</translation> +<translation id="7554791636758816595">Tab Baharu</translation> <translation id="7567204685887185387">Pelayan ini tidak dapat membuktikan bahawa domainnya ialah <ph name="DOMAIN" />; sijil keselamatannya mungkin telah dikeluarkan melalui penipuan. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintasi sambungan anda.</translation> <translation id="7568593326407688803">Halaman ini adalah dalam<ph name="ORIGINAL_LANGUAGE" />Adakah anda ingin menterjemahkannya?</translation> <translation id="7569952961197462199">Alih keluar kad kredit daripada Chrome?</translation>
diff --git a/components/strings/components_strings_nl.xtb b/components/strings/components_strings_nl.xtb index 6b010ef..d68d401 100644 --- a/components/strings/components_strings_nl.xtb +++ b/components/strings/components_strings_nl.xtb
@@ -527,6 +527,7 @@ <translation id="7537536606612762813">Verplicht</translation> <translation id="7542995811387359312">Het automatisch invullen van creditcardnummers is uitgeschakeld, omdat dit formulier geen beveiligde verbinding gebruikt.</translation> <translation id="7549584377607005141">Deze webpagina kan alleen correct worden weergegeven op basis van gegevens die je eerder hebt opgegeven. Je kunt deze gegevens opnieuw verzenden, maar hierdoor worden de acties herhaald die eerder op deze pagina zijn uitgevoerd.</translation> +<translation id="7554791636758816595">Nieuw tabblad</translation> <translation id="7567204685887185387">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server is mogelijk frauduleus verstrekt. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept.</translation> <translation id="7568593326407688803">Deze pagina is geschreven in het<ph name="ORIGINAL_LANGUAGE" />Wil je deze laten vertalen?</translation> <translation id="7569952961197462199">Creditcard verwijderen uit Chrome?</translation>
diff --git a/components/strings/components_strings_no.xtb b/components/strings/components_strings_no.xtb index 12a36ad..01acf04 100644 --- a/components/strings/components_strings_no.xtb +++ b/components/strings/components_strings_no.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obligatorisk</translation> <translation id="7542995811387359312">Automatisk utfylling av kredittkort er deaktivert fordi dette skjemaet ikke bruker en sikker tilkobling.</translation> <translation id="7549584377607005141">Denne nettsiden krever data som du har skrevet inn tidligere for å vises korrekt. Du kan sende inn dataene på nytt, men hvis du gjør det, gjentas eventuelle handlinger denne siden utførte.</translation> +<translation id="7554791636758816595">Ny fane</translation> <translation id="7567204685887185387">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Tjenerens sikkerhetssertifikat kan ha blitt utstedt på uredelig vis. Dette kan være forårsaket av en feilkonfigurering eller en angriper som avskjærer tilkoblingen din.</translation> <translation id="7568593326407688803">Denne siden er på<ph name="ORIGINAL_LANGUAGE" />Vil du ha den oversatt?</translation> <translation id="7569952961197462199">Vil du fjerne kredittkortet fra Chrome?</translation>
diff --git a/components/strings/components_strings_pl.xtb b/components/strings/components_strings_pl.xtb index 4a0d5bb..6b26cf1 100644 --- a/components/strings/components_strings_pl.xtb +++ b/components/strings/components_strings_pl.xtb
@@ -546,6 +546,7 @@ <translation id="7537536606612762813">Obowiązkowe</translation> <translation id="7542995811387359312">Automatyczne wypełnianie danych karty kredytowej jest wyłączone, ponieważ ten formularz nie korzysta z bezpiecznego połączenia.</translation> <translation id="7549584377607005141">Do poprawnego wyświetlenia tej strony internetowej wymagane są dane wpisane przez Ciebie wcześniej. Możesz wysłać je ponownie, ale spowoduje to powtórzenie wszystkich działań wykonanych poprzednio przez stronę.</translation> +<translation id="7554791636758816595">Nowa karta</translation> <translation id="7567204685887185387">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa mógł zostać wydany w celu oszustwa. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego.</translation> <translation id="7568593326407688803">Język strony:<ph name="ORIGINAL_LANGUAGE" />Chcesz ją przetłumaczyć?</translation> <translation id="7569952961197462199">Usunąć tę kartę kredytową z Chrome?</translation>
diff --git a/components/strings/components_strings_pt-BR.xtb b/components/strings/components_strings_pt-BR.xtb index 6f29cd59..20598a697 100644 --- a/components/strings/components_strings_pt-BR.xtb +++ b/components/strings/components_strings_pt-BR.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obrigatória</translation> <translation id="7542995811387359312">O preenchimento automático do cartão de crédito está desativado porque este formulário não usa uma conexão segura.</translation> <translation id="7549584377607005141">Esta página da Web requer os dados inseridos anteriormente para ser exibida de modo correto. É possível enviá-los novamente mas, ao fazer isso, você repete qualquer ação realizada anteriormente na página.</translation> +<translation id="7554791636758816595">Nova guia</translation> <translation id="7567204685887185387">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança pode ter sido emitido de forma fraudulenta. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation> <translation id="7568593326407688803">Esta página está em<ph name="ORIGINAL_LANGUAGE" />Deseja traduzi-la?</translation> <translation id="7569952961197462199">Remover cartão de crédito do Chrome?</translation>
diff --git a/components/strings/components_strings_pt-PT.xtb b/components/strings/components_strings_pt-PT.xtb index 63062d2..bf87793 100644 --- a/components/strings/components_strings_pt-PT.xtb +++ b/components/strings/components_strings_pt-PT.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obrigatório</translation> <translation id="7542995811387359312">O preenchimento automático de cartões de crédito está desactivado, porque este formulário não utiliza uma ligação segura.</translation> <translation id="7549584377607005141">Esta página Web requer os dados introduzidos anteriormente para ser corretamente apresentada. Pode enviar novamente esses dados, mas ao fazê-lo, irá repetir as ações que esta página executou anteriormente.</translation> +<translation id="7554791636758816595">Novo separador</translation> <translation id="7567204685887185387">Este servidor não conseguiu provar que é o domínio <ph name="DOMAIN" />; o respetivo certificado de segurança poderá ter sido emitido de forma fraudulenta. Isto pode ser o resultado de uma configuração incorreta ou de um invasor a intercetar a sua ligação.</translation> <translation id="7568593326407688803">Esta página está em<ph name="ORIGINAL_LANGUAGE" />Pretende traduzi-la?</translation> <translation id="7569952961197462199">Pretende remover o cartão de crédito do Chrome?</translation>
diff --git a/components/strings/components_strings_ro.xtb b/components/strings/components_strings_ro.xtb index 2b59e64e..12dda38 100644 --- a/components/strings/components_strings_ro.xtb +++ b/components/strings/components_strings_ro.xtb
@@ -546,6 +546,7 @@ <translation id="7537536606612762813">Obligatorie</translation> <translation id="7542995811387359312">Completarea automată a cardului de credit este dezactivată, deoarece acest formular nu utilizează o conexiune sigură.</translation> <translation id="7549584377607005141">Pentru a fi afișată corespunzător, această pagină web necesită date pe care le-ați introdus anterior. Puteți trimite aceste date din nou, dar astfel veți repeta orice acțiuni realizate anterior de această pagină.</translation> +<translation id="7554791636758816595">Filă nouă</translation> <translation id="7567204685887185387">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; este posibil ca certificatul său de securitate să fi fost emis fraudulos. Cauza poate fi o configurare greșită sau interceptarea conexiunii de către un atacator.</translation> <translation id="7568593326407688803">Această pagină este în <ph name="ORIGINAL_LANGUAGE" /> Vrei să fie tradusă?</translation> <translation id="7569952961197462199">Elimini cardul de credit din Chrome?</translation>
diff --git a/components/strings/components_strings_ru.xtb b/components/strings/components_strings_ru.xtb index e93085b6..dae2f999 100644 --- a/components/strings/components_strings_ru.xtb +++ b/components/strings/components_strings_ru.xtb
@@ -547,6 +547,7 @@ <translation id="7537536606612762813">Обязательная</translation> <translation id="7542995811387359312">Автозаполнение отключено – незащищенное подключение.</translation> <translation id="7549584377607005141">Для корректного отображения веб-страницы требуются введенные ранее данные. Их можно отправить повторно, но в этом случае все действия на странице будут выполнены снова.</translation> +<translation id="7554791636758816595">Новая вкладка</translation> <translation id="7567204685887185387">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Его сертификат безопасности мог быть выдан обманным путем. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation> <translation id="7568593326407688803">Язык этой страницы<ph name="ORIGINAL_LANGUAGE" />Хотите перевести ее?</translation> <translation id="7569952961197462199">Удалить кредитную карту из Chrome?</translation>
diff --git a/components/strings/components_strings_sk.xtb b/components/strings/components_strings_sk.xtb index a6f802c..5a4ddb1b3 100644 --- a/components/strings/components_strings_sk.xtb +++ b/components/strings/components_strings_sk.xtb
@@ -527,6 +527,7 @@ <translation id="7537536606612762813">Povinné</translation> <translation id="7542995811387359312">Automatické dopĺňanie údajov o kreditnej karte je zakázané, pretože tento formulár nepoužíva zabezpečené pripojenie.</translation> <translation id="7549584377607005141">Správne zobrazenie tejto webovej stránky si vyžaduje údaje, ktoré ste zadali v minulosti. Tieto údaje môžete poslať znova, ale v tom prípade zopakujete všetky akcie, ktoré sa na tejto stránke vykonali v minulosti.</translation> +<translation id="7554791636758816595">Nová karta</translation> <translation id="7567204685887185387">Server nedokáže overiť, či ide o doménu <ph name="DOMAIN" />, bol zrejme vydaný falošný bezpečnostný certifikát. Môže to byť spôsobené nesprávnou konfiguráciou alebo tým, že vaše pripojenie zachytil útočník.</translation> <translation id="7568593326407688803">Táto stránka je v jazyku<ph name="ORIGINAL_LANGUAGE" />Chceli by ste ju preložiť?</translation> <translation id="7569952961197462199">Chcete kreditnú kartu odstrániť z prehliadača Chrome?</translation>
diff --git a/components/strings/components_strings_sl.xtb b/components/strings/components_strings_sl.xtb index a87f2354..9e934adb 100644 --- a/components/strings/components_strings_sl.xtb +++ b/components/strings/components_strings_sl.xtb
@@ -549,6 +549,7 @@ <translation id="7537536606612762813">Obvezen</translation> <translation id="7542995811387359312">Samodejno izpolnjevanje podatkov o kreditni kartici je onemogočeno, ker ta obrazec ne uporablja varne povezave.</translation> <translation id="7549584377607005141">Za pravilen prikaz te strani so potrebni podatki, ki ste jih vnesli prej. Podatke lahko pošljete še enkrat, vendar se bodo s tem ponovila vsa prejšnja dejanja strani.</translation> +<translation id="7554791636758816595">Nov zavihek</translation> <translation id="7567204685887185387">Strežniku ni uspelo dokazati, da je <ph name="DOMAIN" />; njegovo varnostno potrdilo je bilo morda izdano z goljufijo. Razlog za to je lahko napačna konfiguracija ali napadalčevo prestrezanje povezave.</translation> <translation id="7568593326407688803">Ta stran je v jeziku:<ph name="ORIGINAL_LANGUAGE" />Jo želite prevesti?</translation> <translation id="7569952961197462199">Želite odstraniti kreditno kartico iz Chroma?</translation>
diff --git a/components/strings/components_strings_sr.xtb b/components/strings/components_strings_sr.xtb index 1fb41d6..e3117b64 100644 --- a/components/strings/components_strings_sr.xtb +++ b/components/strings/components_strings_sr.xtb
@@ -544,6 +544,7 @@ <translation id="7537536606612762813">Обавезно</translation> <translation id="7542995811387359312">Онемогућено је аутоматско попуњавање кредитне картице зато што овај образац не користи безбедну везу.</translation> <translation id="7549584377607005141">Ова веб-страница захтева податке које сте унели раније да би се правилно приказала. Можете поново да пошаљете те податке, али ако то урадите, поновићете било коју радњу коју је ова страница претходно обавила.</translation> +<translation id="7554791636758816595">Нова картица</translation> <translation id="7567204685887185387">Овај сервер не може да докаже да је <ph name="DOMAIN" />; његов безбедносни сертификат је можда лажно издат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation> <translation id="7568593326407688803">Ова страница је на језику:<ph name="ORIGINAL_LANGUAGE" />Желите ли да је преведете?</translation> <translation id="7569952961197462199">Желите ли да уклоните кредитну картицу из Chrome-а?</translation>
diff --git a/components/strings/components_strings_sv.xtb b/components/strings/components_strings_sv.xtb index 513990a..d0608c2 100644 --- a/components/strings/components_strings_sv.xtb +++ b/components/strings/components_strings_sv.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Obligatorisk</translation> <translation id="7542995811387359312">Automatisk ifyllning av kreditkort har inaktiverats eftersom formulärets anslutning inte är säker.</translation> <translation id="7549584377607005141">Den här webbsidan kräver uppgifter som du har angett tidigare för att kunna visas korrekt. Du kan skicka uppgifterna igen, men om du gör det upprepas de åtgärder som har utförts av sidan tidigare.</translation> +<translation id="7554791636758816595">Ny flik</translation> <translation id="7567204685887185387">Servern kunde inte bevisa att den är <ph name="DOMAIN" /> eftersom dess säkerhetscertifikat kan ha utfärdats utan behörighet. Detta kan orsakas av en felaktig konfigurering eller att någon spärrar anslutningen.</translation> <translation id="7568593326407688803">Den här sidan är på<ph name="ORIGINAL_LANGUAGE" />Vill du översätta den?</translation> <translation id="7569952961197462199">Vill du ta bort kreditkortet från Chrome?</translation>
diff --git a/components/strings/components_strings_sw.xtb b/components/strings/components_strings_sw.xtb index 99c822a..07efc4d 100644 --- a/components/strings/components_strings_sw.xtb +++ b/components/strings/components_strings_sw.xtb
@@ -547,6 +547,7 @@ <translation id="7537536606612762813">Lazima</translation> <translation id="7542995811387359312">Mjazo otomatiki wa kadi ya mkopo umelemazwa kwa sababu fomu hii haitumii muunganisho salama.</translation> <translation id="7549584377607005141">Ukurasa huu wa wavuti unahitaji data ambayo uliingiza mapema ili ionyeshwe inavyostahili. Unaweza kutuma tena data hii, lakini kwa kufanya hivyo utarudia hatua yoyote ambayo ukurasa huu ulifanya hapo awali.</translation> +<translation id="7554791636758816595">Kichupo Kipya</translation> <translation id="7567204685887185387">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; huenda cheti chake cha usalama kimetolewa kwa njia ya ulaghai. Hii inaweza kusababishwa na usanidi usiofaa au mvamizi kuingilia muunganisho wako.</translation> <translation id="7568593326407688803">Ukurasa huu umeandikwa kwa<ph name="ORIGINAL_LANGUAGE" />Je, ungependa kuutafsiri?</translation> <translation id="7569952961197462199">Ungependa kuondoa kadi ya malipo kutoka kwenye Chrome?</translation>
diff --git a/components/strings/components_strings_ta.xtb b/components/strings/components_strings_ta.xtb index 16e91e2..edf789f 100644 --- a/components/strings/components_strings_ta.xtb +++ b/components/strings/components_strings_ta.xtb
@@ -538,6 +538,7 @@ <translation id="7537536606612762813">கட்டாயம்</translation> <translation id="7542995811387359312">இந்தப் படிவம் பாதுகாப்பான இணைப்பைப் பயன்படுத்தாத காரணத்தால், தானியங்கு கடன் அட்டை நிரப்புதல் முடக்கப்பட்டிருக்கிறது.</translation> <translation id="7549584377607005141">சரியாக காண்பிக்கப்படுவதற்கு நீங்கள் ஏற்கனவே உள்ளிட்ட தரவு இந்த இணையப்பக்கத்திற்கு தேவைப்படுகிறது. இந்த தரவை நீங்கள் மீண்டும் அனுப்பலாம், ஆனால் அவ்வாறு செய்வதனால் இந்தப் பக்கம் ஏற்கனவே செயற்படுத்திய எல்லாச் செயலையும் மீண்டும் செய்வீர்கள்.</translation> +<translation id="7554791636758816595">புதிய தாவல்</translation> <translation id="7567204685887185387">இது <ph name="DOMAIN" /> தான் என்பதை இந்தச் சேவையகம் உறுதிப்படுத்தவில்லை; இதன் பாதுகாப்புச் சான்றிதழில் மோசடி செய்யப்பட்டிருக்கலாம். இது தவறான உள்ளமைவால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation> <translation id="7568593326407688803">இந்தப் பக்கமானது<ph name="ORIGINAL_LANGUAGE" />இல் உள்ளது இதை மொழிபெயர்க்க விரும்புகிறீர்களா?</translation> <translation id="7569952961197462199">Chrome இலிருந்து கிரெடிட் கார்டை அகற்றவா?</translation>
diff --git a/components/strings/components_strings_te.xtb b/components/strings/components_strings_te.xtb index a7733352..d9e58ffb 100644 --- a/components/strings/components_strings_te.xtb +++ b/components/strings/components_strings_te.xtb
@@ -546,6 +546,7 @@ <translation id="7537536606612762813">తప్పనిసరి</translation> <translation id="7542995811387359312">ఈ ఫారమ్ సురక్షిత కనెక్షన్ని ఉపయోగించనందున స్వయంచాలకంగా క్రెడిట్ కార్డ్ పూర్తి చెయ్యడం ఆపివేయబడింది.</translation> <translation id="7549584377607005141">ఈ వెబ్పేజీ సరిగ్గా ప్రదర్శించబడటానికి మీరు మునుపు నమోదు చేసిన డేటా అవసరం. మీరు ఈ డేటాను మళ్లీ పంపవచ్చు, కానీ అలా చేయడం వలన ఈ పేజీ మునుపు ప్రదర్శించిన ఏదైనా చర్య పునరావృతం కావచ్చు.</translation> +<translation id="7554791636758816595">కొత్త ట్యాబ్</translation> <translation id="7567204685887185387">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రం మోసపూరితంగా జారీ అయ్యి ఉండవచ్చు. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడిచేసే వ్యక్తి మీ కనెక్షన్కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation> <translation id="7568593326407688803">ఈ పేజీ<ph name="ORIGINAL_LANGUAGE" />లో ఉంది మీరు దీన్ని అనువదించాలనుకుంటున్నారా?</translation> <translation id="7569952961197462199">Chrome నుండి క్రెడిట్ కార్డ్ను తీసివేయాలా?</translation>
diff --git a/components/strings/components_strings_th.xtb b/components/strings/components_strings_th.xtb index a162e9d..d69736ae 100644 --- a/components/strings/components_strings_th.xtb +++ b/components/strings/components_strings_th.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">จำเป็น</translation> <translation id="7542995811387359312">การป้อนหมายเลขบัตรเครดิตอัตโนมัติถูกปิดใช้งานเนื่องจากฟอร์มนี้ไม่ได้ใช้การเชื่อมต่อที่ปลอดภัย</translation> <translation id="7549584377607005141">หน้าเว็บนี้ต้องใช้ข้อมูลที่คุณป้อนก่อนหน้านี้เพื่อให้แสดงได้อย่างถูกต้อง คุณสามารถส่งข้อมูลนี้ได้อีกครั้ง แต่การทำเช่นนั้นจะเป็นการทำสิ่งที่หน้าเว็บนี้เคยดำเนินการซ้ำอีกครั้ง</translation> +<translation id="7554791636758816595">แท็บใหม่</translation> <translation id="7567204685887185387">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะอาจมีการออกใบรับรองความปลอดภัยปลอม โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้บุกรุกที่ขัดขวางการเชื่อมต่อของคุณ</translation> <translation id="7568593326407688803">หน้าเว็บนี้เป็น<ph name="ORIGINAL_LANGUAGE" />คุณต้องการแปลหรือไม่</translation> <translation id="7569952961197462199">นำบัตรเครดิตออกจาก Chrome ไหม</translation>
diff --git a/components/strings/components_strings_tr.xtb b/components/strings/components_strings_tr.xtb index a1c9c1e..2cc56cb0 100644 --- a/components/strings/components_strings_tr.xtb +++ b/components/strings/components_strings_tr.xtb
@@ -547,6 +547,7 @@ <translation id="7537536606612762813">Zorunlu</translation> <translation id="7542995811387359312">Bu form güvenli bağlantı kullanmadığından kredi kartı bilgilerini otomatik doldurma özelliği devre dışı bırakıldı.</translation> <translation id="7549584377607005141">Bu Web sayfasının düzgün şekilde görüntülenmesi için, önceden girdiğiniz veriler gerekiyor. Bu verileri tekrar gönderebilirsiniz, ancak bunu yaptığınızda bu sayfanın daha önce gerçekleştirdiği işlemler de tekrar edilir.</translation> +<translation id="7554791636758816595">Yeni Sekme</translation> <translation id="7567204685887185387">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikası hileli bir şekilde yayınlanmış olabilir. Bu durum, bir yanlış yapılandırmadan veya bağlantıya müdahale eden bir saldırgandan kaynaklanıyor olabilir.</translation> <translation id="7568593326407688803">Bu sayfanın dili<ph name="ORIGINAL_LANGUAGE" />Çevrilmesini istiyor musunuz?</translation> <translation id="7569952961197462199">Kredi kartı Chrome'dan kaldırılsın mı?</translation>
diff --git a/components/strings/components_strings_uk.xtb b/components/strings/components_strings_uk.xtb index 736d9f2f..eb16e29 100644 --- a/components/strings/components_strings_uk.xtb +++ b/components/strings/components_strings_uk.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Обов’язкове</translation> <translation id="7542995811387359312">Автоматичне заповнення кредитної картки вимкнено, оскільки ця форма не використовує безпечне з'єднання.</translation> <translation id="7549584377607005141">Для належного відображення цій веб-сторінці потрібні введені вами раніше дані. Можна ще раз надіслати дані, однак зробивши це, ви повторите всі дії, які ця сторінка виконувала раніше.</translation> +<translation id="7554791636758816595">Нова вкладка</translation> <translation id="7567204685887185387">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. Можливо, його сертифікат безпеки видали шахраї. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation> <translation id="7568593326407688803">Мова цієї сторінки:<ph name="ORIGINAL_LANGUAGE" />Перекласти її?</translation> <translation id="7569952961197462199">Видалити дані кредитної картки з Chrome?</translation>
diff --git a/components/strings/components_strings_vi.xtb b/components/strings/components_strings_vi.xtb index b3da126..3b7fe4c 100644 --- a/components/strings/components_strings_vi.xtb +++ b/components/strings/components_strings_vi.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">Bắt buộc</translation> <translation id="7542995811387359312">Tính năng tự động điền thẻ tín dụng đã bị vô hiệu hóa vì biểu mẫu này không sử dụng kết nối an toàn.</translation> <translation id="7549584377607005141">Trang web này yêu cầu dữ liệu mà bạn đã nhập trước đó để được hiển thị đúng cách. Bạn có thể gửi lại dữ liệu này nhưng làm như vậy bạn sẽ lặp lại mọi hoạt động mà trang này đã thực hiện trước đó.</translation> +<translation id="7554791636758816595">Tab mới</translation> <translation id="7567204685887185387">Máy chủ này không chứng minh được rằng đó là <ph name="DOMAIN" />; chứng chỉ bảo mật của máy chủ này có thể đã bị gian lận khi phát hành. Điều này có thể do định cấu hình sai hoặc có kẻ tấn công chặn kết nối của bạn.</translation> <translation id="7568593326407688803">Trang này bằng<ph name="ORIGINAL_LANGUAGE" />Bạn có muốn dịch trang này không?</translation> <translation id="7569952961197462199">Xóa thẻ tín dụng khỏi Chrome?</translation>
diff --git a/components/strings/components_strings_zh-CN.xtb b/components/strings/components_strings_zh-CN.xtb index 2fa6638..4150bc7 100644 --- a/components/strings/components_strings_zh-CN.xtb +++ b/components/strings/components_strings_zh-CN.xtb
@@ -530,6 +530,7 @@ <translation id="7537536606612762813">强制</translation> <translation id="7542995811387359312">由于该表单不使用安全连接,因此自动填写信用卡信息的功能已停用。</translation> <translation id="7549584377607005141">此网页需要使用您之前输入的数据才能正常显示。您可以重新发送这些数据,不过,这么做会重复执行此网页之前执行过的所有操作。</translation> +<translation id="7554791636758816595">打开新的标签页</translation> <translation id="7567204685887185387">此服务器无法证明它是<ph name="DOMAIN" />;其安全证书可能是由骗子发出的。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation> <translation id="7568593326407688803">此网页为<ph name="ORIGINAL_LANGUAGE" />网页,是否需要翻译?</translation> <translation id="7569952961197462199">从 Chrome 中移除信用卡信息?</translation>
diff --git a/components/strings/components_strings_zh-TW.xtb b/components/strings/components_strings_zh-TW.xtb index 2afe5a0..5be4d96 100644 --- a/components/strings/components_strings_zh-TW.xtb +++ b/components/strings/components_strings_zh-TW.xtb
@@ -548,6 +548,7 @@ <translation id="7537536606612762813">強制</translation> <translation id="7542995811387359312">由於這個表單並未採用加密連線方式,所以信用卡自動填入功能已停用。</translation> <translation id="7549584377607005141">這個網頁需要使用您先前輸入的資料才能正確顯示。您可以重新傳送這些資料,不過這麼做會重複執行這個網頁先前執行過的任何動作。</translation> +<translation id="7554791636758816595">新增分頁</translation> <translation id="7567204685887185387">伺服器無法證明其屬於 <ph name="DOMAIN" /> 網域;其安全性憑證是以欺詐方式發行。這可能是因為設定錯誤,或有攻擊者攔截您的連線所致。</translation> <translation id="7568593326407688803">此網頁為<ph name="ORIGINAL_LANGUAGE" />您要翻譯網頁內容嗎?</translation> <translation id="7569952961197462199">要從 Chrome 中移除信用卡嗎?</translation>
diff --git a/components/sync_driver.gypi b/components/sync_driver.gypi index 6b7fbf75..0a4310b9a 100644 --- a/components/sync_driver.gypi +++ b/components/sync_driver.gypi
@@ -21,6 +21,9 @@ 'sync_driver_features', 'version_info', ], + 'export_dependent_settings': [ + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation', + ], 'include_dirs': [ '..', ],
diff --git a/components/sync_driver/BUILD.gn b/components/sync_driver/BUILD.gn index edcb9b48..b20c8bc 100644 --- a/components/sync_driver/BUILD.gn +++ b/components/sync_driver/BUILD.gn
@@ -123,6 +123,7 @@ public_deps = [ ":features", + "//third_party/cacheinvalidation", ] deps = [ "//base", @@ -137,7 +138,6 @@ "//google_apis", "//net", "//sync", - "//third_party/cacheinvalidation", ] if (enable_configuration_policy) {
diff --git a/components/sync_driver/device_info_service.cc b/components/sync_driver/device_info_service.cc index 50b0d1a6..138a7820 100644 --- a/components/sync_driver/device_info_service.cc +++ b/components/sync_driver/device_info_service.cc
@@ -8,15 +8,17 @@ #include <vector> #include "base/bind.h" +#include "base/location.h" #include "sync/api/metadata_batch.h" #include "sync/api/sync_error.h" +#include "sync/internal_api/public/data_batch_impl.h" #include "sync/protocol/data_type_state.pb.h" #include "sync/protocol/sync.pb.h" -#include "sync/util/time.h" namespace sync_driver_v2 { using syncer::SyncError; +using syncer_v2::DataBatchImpl; using syncer_v2::EntityChangeList; using syncer_v2::EntityData; using syncer_v2::EntityDataList; @@ -34,8 +36,7 @@ DeviceInfoService::DeviceInfoService( sync_driver::LocalDeviceInfoProvider* local_device_info_provider, const StoreFactoryFunction& callback) - : local_device_backup_time_(-1), - local_device_info_provider_(local_device_info_provider), + : local_device_info_provider_(local_device_info_provider), weak_factory_(this) { DCHECK(local_device_info_provider); @@ -75,11 +76,40 @@ void DeviceInfoService::GetData(ClientTagList client_tags, DataCallback callback) { - // TODO(skym): crbug.com/543405: Implementation. + if (!has_data_loaded_) { + callback.Run(SyncError(FROM_HERE, SyncError::DATATYPE_ERROR, + "Cannot call GetData before data has loaded.", + syncer::DEVICE_INFO), + scoped_ptr<DataBatchImpl>()); + return; + } + + syncer::SyncError error; + scoped_ptr<DataBatchImpl> batch(new DataBatchImpl()); + for (auto& tag : client_tags) { + auto iter = all_data_.find(tag); + if (iter != all_data_.end()) { + batch->Put(tag, CopyIntoNewEntityData(*iter->second)); + } + } + callback.Run(error, std::move(batch)); } void DeviceInfoService::GetAllData(DataCallback callback) { - // TODO(skym): crbug.com/543405: Implementation. + if (!has_data_loaded_) { + callback.Run(SyncError(FROM_HERE, SyncError::DATATYPE_ERROR, + "Cannot call GetAllData before data has loaded.", + syncer::DEVICE_INFO), + scoped_ptr<DataBatchImpl>()); + return; + } + + syncer::SyncError error; + scoped_ptr<DataBatchImpl> batch(new DataBatchImpl()); + for (auto& kv : all_data_) { + batch->Put(kv.first, CopyIntoNewEntityData(*kv.second)); + } + callback.Run(error, std::move(batch)); } std::string DeviceInfoService::GetClientTag(const EntityData& entity_data) { @@ -128,45 +158,12 @@ FOR_EACH_OBSERVER(Observer, observers_, OnDeviceInfoChange()); } -void DeviceInfoService::UpdateLocalDeviceBackupTime(base::Time backup_time) { - // TODO(skym): crbug.com/582460: Replace with is initialized check, we've - // already started syncing, provider is ready, make sure we have processor. - - // Local device info must be available in advance. - DCHECK(local_device_info_provider_->GetLocalDeviceInfo()); - - // TODO(skym): crbug.com/582460: Less than instead of not equal check? - if (GetLocalDeviceBackupTime() != backup_time) { - // TODO(skym): crbug.com/582460: Storing this field doesn't really make - // sense. - set_local_device_backup_time(syncer::TimeToProtoTime(backup_time)); - scoped_ptr<DeviceInfoSpecifics> new_specifics = CreateLocalSpecifics(); - - // TODO(skym): crbug.com/543406: Create EntityChange and pass to SMTP - // through ProcessChanges. - // TODO(skym): crbug.com/543405: Persist metadata and data. - StoreSpecifics(std::move(new_specifics)); - } - - // Don't call NotifyObservers() because backup time is not part of - // DeviceInfoTracker interface. -} - -base::Time DeviceInfoService::GetLocalDeviceBackupTime() const { - return has_local_device_backup_time() - ? syncer::ProtoTimeToTime(local_device_backup_time()) - : base::Time(); -} - // TODO(skym): crbug.com/543406: It might not make sense for this to be a // scoped_ptr. scoped_ptr<DeviceInfoSpecifics> DeviceInfoService::CreateLocalSpecifics() { const DeviceInfo* info = local_device_info_provider_->GetLocalDeviceInfo(); DCHECK(info); scoped_ptr<DeviceInfoSpecifics> specifics = CreateSpecifics(*info); - if (has_local_device_backup_time()) { - specifics->set_backup_timestamp(local_device_backup_time()); - } // TODO(skym): crbug.com:543406: Local tag and non unique name have no place // to be set now. return specifics; @@ -197,6 +194,14 @@ specifics.device_type(), specifics.signin_scoped_device_id())); } +// Static. +scoped_ptr<EntityData> DeviceInfoService::CopyIntoNewEntityData( + const DeviceInfoSpecifics& specifics) { + scoped_ptr<EntityData> entity_data(new EntityData()); + *entity_data->specifics.mutable_device_info() = specifics; + return entity_data; +} + void DeviceInfoService::StoreSpecifics( scoped_ptr<DeviceInfoSpecifics> specifics) { DVLOG(1) << "Storing DEVICE_INFO for " << specifics->client_name()
diff --git a/components/sync_driver/device_info_service.h b/components/sync_driver/device_info_service.h index f6ab854..13542a6a 100644 --- a/components/sync_driver/device_info_service.h +++ b/components/sync_driver/device_info_service.h
@@ -70,11 +70,6 @@ void AddObserver(Observer* observer) override; void RemoveObserver(Observer* observer) override; - // Called to update local device backup time. - void UpdateLocalDeviceBackupTime(base::Time backup_time); - // Gets the most recently set local device backup time. - base::Time GetLocalDeviceBackupTime() const; - private: friend class DeviceInfoServiceTest; @@ -85,6 +80,9 @@ // Allocate new DeviceInfo from SyncData. static scoped_ptr<sync_driver::DeviceInfo> CreateDeviceInfo( const sync_pb::DeviceInfoSpecifics& specifics); + // Conversion as we prepare to hand data to the processor. + static scoped_ptr<syncer_v2::EntityData> CopyIntoNewEntityData( + const sync_pb::DeviceInfoSpecifics& specifics); // Store SyncData in the cache. void StoreSpecifics(scoped_ptr<sync_pb::DeviceInfoSpecifics> specifics); @@ -118,21 +116,6 @@ // current processor if able. void TryLoadAllMetadata(); - // |local_device_backup_time_| accessors. - int64_t local_device_backup_time() const { return local_device_backup_time_; } - bool has_local_device_backup_time() const { - return local_device_backup_time_ >= 0; - } - void set_local_device_backup_time(int64_t value) { - local_device_backup_time_ = value; - } - void clear_local_device_backup_time() { local_device_backup_time_ = -1; } - - // TODO(skym): crbug.com/582460: Remove once we remove local provider. - // Local device last set backup time (in proto format). - // -1 if the value hasn't been specified - int64_t local_device_backup_time_; - // |local_device_info_provider_| isn't owned. const sync_driver::LocalDeviceInfoProvider* const local_device_info_provider_;
diff --git a/components/sync_driver/device_info_service_unittest.cc b/components/sync_driver/device_info_service_unittest.cc index 562f4765..d9c89cfe 100644 --- a/components/sync_driver/device_info_service_unittest.cc +++ b/components/sync_driver/device_info_service_unittest.cc
@@ -12,7 +12,9 @@ #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "components/sync_driver/local_device_info_provider_mock.h" +#include "sync/api/data_batch.h" #include "sync/api/metadata_batch.h" #include "sync/api/model_type_store.h" #include "sync/internal_api/public/test/model_type_store_test_util.h" @@ -21,12 +23,15 @@ namespace sync_driver_v2 { +using syncer_v2::DataBatch; using syncer_v2::EntityData; using syncer_v2::MetadataBatch; using syncer_v2::MetadataChangeList; using syncer_v2::ModelTypeChangeProcessor; +using syncer_v2::ModelTypeService; using syncer_v2::ModelTypeStore; using syncer_v2::ModelTypeStoreTestUtil; +using syncer_v2::TagAndData; using sync_driver::DeviceInfo; using sync_driver::DeviceInfoTracker; using sync_driver::LocalDeviceInfoProviderMock; @@ -34,6 +39,7 @@ using sync_pb::DeviceInfoSpecifics; using sync_pb::EntitySpecifics; +using ClientTagList = ModelTypeService::ClientTagList; using Result = ModelTypeStore::Result; using WriteBatch = ModelTypeStore::WriteBatch; @@ -43,6 +49,16 @@ ASSERT_EQ(Result::SUCCESS, result); } +void AssertEqual(const DeviceInfoSpecifics& s1, const DeviceInfoSpecifics& s2) { + ASSERT_EQ(s1.cache_guid(), s2.cache_guid()); + ASSERT_EQ(s1.client_name(), s2.client_name()); + ASSERT_EQ(s1.device_type(), s2.device_type()); + ASSERT_EQ(s1.sync_user_agent(), s2.sync_user_agent()); + ASSERT_EQ(s1.chrome_version(), s2.chrome_version()); + ASSERT_EQ(s1.backup_timestamp(), s2.backup_timestamp()); + ASSERT_EQ(s1.signin_scoped_device_id(), s2.signin_scoped_device_id()); +} + void AssertEqual(const DeviceInfoSpecifics& specifics, const DeviceInfo& model) { ASSERT_EQ(specifics.cache_guid(), model.guid()); @@ -54,15 +70,27 @@ model.signin_scoped_device_id()); } -DeviceInfoSpecifics TestSpecifics() { - DeviceInfoSpecifics specifics; - specifics.set_cache_guid("a"); - specifics.set_client_name("b"); - specifics.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_LINUX); - specifics.set_sync_user_agent("d"); - specifics.set_chrome_version("e"); - specifics.set_backup_timestamp(6); - return specifics; +void AssertErrorFromDataBatch(syncer::SyncError error, + scoped_ptr<DataBatch> batch) { + ASSERT_TRUE(error.IsSet()); +} + +void AssertExpectedFromDataBatch( + std::map<std::string, DeviceInfoSpecifics> expected, + syncer::SyncError error, + scoped_ptr<DataBatch> batch) { + ASSERT_FALSE(error.IsSet()); + while (batch->HasNext()) { + const TagAndData& pair = batch->Next(); + std::map<std::string, DeviceInfoSpecifics>::iterator iter = + expected.find(pair.first); + ASSERT_NE(iter, expected.end()); + AssertEqual(iter->second, pair.second->specifics.device_info()); + // Removing allows us to verify we don't see the same item multiple times, + // and that we saw everything we expected. + expected.erase(iter); + } + ASSERT_TRUE(expected.empty()); } // Instead of actually processing anything, simply accumulates all instructions @@ -151,6 +179,22 @@ base::RunLoop().RunUntilIdle(); } + DeviceInfoSpecifics GenerateTestSpecifics() { + int label = ++generated_count_; + DeviceInfoSpecifics specifics; + specifics.set_cache_guid(base::StringPrintf("cache guid %d", label)); + specifics.set_client_name(base::StringPrintf("client name %d", label)); + specifics.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_LINUX); + specifics.set_sync_user_agent( + base::StringPrintf("sync user agent %d", label)); + specifics.set_chrome_version( + base::StringPrintf("chrome version %d", label)); + specifics.set_backup_timestamp(label); + specifics.set_signin_scoped_device_id( + base::StringPrintf("signin scoped device id %d", label)); + return specifics; + } + // Allows access to the store before that will ultimately be used to // initialize the service. ModelTypeStore* store() { @@ -202,6 +246,10 @@ // A non-owning pointer to the processor given to the service. Will be nullptr // before being given to the service, to make ownership easier. FakeModelTypeChangeProcessor* processor_ = nullptr; + + // A monotonically increasing label for generated specifics objects with data + // that is slightly different from eachother. + int generated_count_ = 0; }; TEST_F(DeviceInfoServiceTest, EmptyDataReconciliation) { @@ -226,7 +274,7 @@ set_local_device(make_scoped_ptr(new LocalDeviceInfoProviderMock())); scoped_ptr<WriteBatch> batch = store()->CreateWriteBatch(); - DeviceInfoSpecifics specifics(TestSpecifics()); + DeviceInfoSpecifics specifics(GenerateTestSpecifics()); specifics.set_backup_timestamp(6); store()->WriteData(batch.get(), "tag", specifics.SerializeAsString()); store()->CommitWriteBatch(std::move(batch), @@ -261,7 +309,7 @@ TEST_F(DeviceInfoServiceTest, TestInitStoreThenProc) { scoped_ptr<WriteBatch> batch = store()->CreateWriteBatch(); - DeviceInfoSpecifics specifics(TestSpecifics()); + DeviceInfoSpecifics specifics(GenerateTestSpecifics()); store()->WriteData(batch.get(), "tag", specifics.SerializeAsString()); DataTypeState state; state.set_encryption_key_name("ekn"); @@ -286,7 +334,7 @@ TEST_F(DeviceInfoServiceTest, TestInitProcBeforeStoreFinishes) { scoped_ptr<WriteBatch> batch = store()->CreateWriteBatch(); - DeviceInfoSpecifics specifics(TestSpecifics()); + DeviceInfoSpecifics specifics(GenerateTestSpecifics()); store()->WriteData(batch.get(), "tag", specifics.SerializeAsString()); DataTypeState state; state.set_encryption_key_name("ekn"); @@ -305,6 +353,70 @@ processor()->metadata()->GetDataTypeState().encryption_key_name()); } +TEST_F(DeviceInfoServiceTest, GetData) { + scoped_ptr<WriteBatch> batch = store()->CreateWriteBatch(); + DeviceInfoSpecifics specifics1(GenerateTestSpecifics()); + DeviceInfoSpecifics specifics3(GenerateTestSpecifics()); + store()->WriteData(batch.get(), "tag1", specifics1.SerializeAsString()); + store()->WriteData(batch.get(), "tag2", + GenerateTestSpecifics().SerializeAsString()); + store()->WriteData(batch.get(), "tag3", specifics3.SerializeAsString()); + store()->CommitWriteBatch(std::move(batch), + base::Bind(&AssertResultIsSuccess)); + + InitializeAndPump(); + + std::map<std::string, DeviceInfoSpecifics> expected; + expected["tag1"] = specifics1; + expected["tag3"] = specifics3; + ClientTagList client_tags; + client_tags.push_back("tag1"); + client_tags.push_back("tag3"); + service()->GetData(client_tags, + base::Bind(&AssertExpectedFromDataBatch, expected)); +} + +TEST_F(DeviceInfoServiceTest, GetDataMissing) { + InitializeAndPump(); + std::map<std::string, DeviceInfoSpecifics> expected; + ClientTagList client_tags; + client_tags.push_back("tag1"); + service()->GetData(client_tags, + base::Bind(&AssertExpectedFromDataBatch, expected)); +} + +TEST_F(DeviceInfoServiceTest, GetDataNotInitialized) { + InitializeService(); + ClientTagList client_tags; + service()->GetData(client_tags, base::Bind(&AssertErrorFromDataBatch)); +} + +TEST_F(DeviceInfoServiceTest, GetAllData) { + scoped_ptr<WriteBatch> batch = store()->CreateWriteBatch(); + DeviceInfoSpecifics specifics1(GenerateTestSpecifics()); + DeviceInfoSpecifics specifics2(GenerateTestSpecifics()); + store()->WriteData(batch.get(), "tag1", specifics1.SerializeAsString()); + store()->WriteData(batch.get(), "tag2", specifics2.SerializeAsString()); + store()->CommitWriteBatch(std::move(batch), + base::Bind(&AssertResultIsSuccess)); + + InitializeAndPump(); + + std::map<std::string, DeviceInfoSpecifics> expected; + expected["tag1"] = specifics1; + expected["tag2"] = specifics2; + ClientTagList client_tags; + client_tags.push_back("tag1"); + client_tags.push_back("tag2"); + service()->GetData(client_tags, + base::Bind(&AssertExpectedFromDataBatch, expected)); +} + +TEST_F(DeviceInfoServiceTest, GetAllDataNotInitialized) { + InitializeService(); + service()->GetAllData(base::Bind(&AssertErrorFromDataBatch)); +} + } // namespace } // namespace sync_driver_v2
diff --git a/components/test_runner/accessibility_controller.cc b/components/test_runner/accessibility_controller.cc index f6fd849..9d660b8 100644 --- a/components/test_runner/accessibility_controller.cc +++ b/components/test_runner/accessibility_controller.cc
@@ -8,6 +8,7 @@ #include "gin/handle.h" #include "gin/object_template_builder.h" #include "gin/wrappable.h" +#include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebKit.h" @@ -140,8 +141,6 @@ AccessibilityController::~AccessibilityController() {} void AccessibilityController::Reset() { - root_element_ = blink::WebAXObject(); - focused_element_ = blink::WebAXObject(); elements_.Clear(); notification_callback_.Reset(); log_accessibility_events_ = false; @@ -154,11 +153,6 @@ AccessibilityControllerBindings::Install(weak_factory_.GetWeakPtr(), frame); } -void AccessibilityController::SetFocusedElement( - const blink::WebAXObject& focused_element) { - focused_element_ = focused_element; -} - bool AccessibilityController::ShouldLogAccessibilityEvents() { return log_accessibility_events_; } @@ -228,27 +222,31 @@ } v8::Local<v8::Object> AccessibilityController::FocusedElement() { - if (focused_element_.isNull()) - focused_element_ = web_view_->accessibilityObject(); - return elements_.GetOrCreate(focused_element_); + blink::WebFrame* frame = web_view_->mainFrame(); + if (!frame) + return v8::Local<v8::Object>(); + + blink::WebAXObject focused_element = + frame->document().focusedAccessibilityObject(); + if (focused_element.isNull()) + focused_element = web_view_->accessibilityObject(); + return elements_.GetOrCreate(focused_element); } v8::Local<v8::Object> AccessibilityController::RootElement() { - if (root_element_.isNull()) - root_element_ = web_view_->accessibilityObject(); - return elements_.GetOrCreate(root_element_); + blink::WebAXObject root_element = web_view_->accessibilityObject(); + return elements_.GetOrCreate(root_element); } v8::Local<v8::Object> AccessibilityController::AccessibleElementById(const std::string& id) { - if (root_element_.isNull()) - root_element_ = web_view_->accessibilityObject(); + blink::WebAXObject root_element = web_view_->accessibilityObject(); - if (!root_element_.updateLayoutAndCheckValidity()) + if (!root_element.updateLayoutAndCheckValidity()) return v8::Local<v8::Object>(); return FindAccessibleElementByIdRecursive( - root_element_, blink::WebString::fromUTF8(id.c_str())); + root_element, blink::WebString::fromUTF8(id.c_str())); } v8::Local<v8::Object>
diff --git a/components/test_runner/accessibility_controller.h b/components/test_runner/accessibility_controller.h index 7bc62aa..376f7b08 100644 --- a/components/test_runner/accessibility_controller.h +++ b/components/test_runner/accessibility_controller.h
@@ -31,7 +31,6 @@ void Reset(); void Install(blink::WebFrame* frame); - void SetFocusedElement(const blink::WebAXObject&); bool ShouldLogAccessibilityEvents(); void NotificationReceived(const blink::WebAXObject& target, const std::string& notification_name); @@ -56,9 +55,6 @@ // If true, will log all accessibility notifications. bool log_accessibility_events_; - blink::WebAXObject focused_element_; - blink::WebAXObject root_element_; - WebAXObjectProxyList elements_; v8::Persistent<v8::Function> notification_callback_;
diff --git a/components/test_runner/event_sender.cc b/components/test_runner/event_sender.cc index 4a9b09b..c9b2fb5 100644 --- a/components/test_runner/event_sender.cc +++ b/components/test_runner/event_sender.cc
@@ -487,6 +487,7 @@ float velocity_x, float velocity_y, gin::Arguments* args); + bool IsFlinging() const; void GestureScrollFirstPoint(int x, int y); void TouchStart(); void TouchMove(); @@ -614,6 +615,7 @@ &EventSenderBindings::DumpFilenameBeingDragged) .SetMethod("gestureFlingCancel", &EventSenderBindings::GestureFlingCancel) .SetMethod("gestureFlingStart", &EventSenderBindings::GestureFlingStart) + .SetMethod("isFlinging", &EventSenderBindings::IsFlinging) .SetMethod("gestureScrollFirstPoint", &EventSenderBindings::GestureScrollFirstPoint) .SetMethod("touchStart", &EventSenderBindings::TouchStart) @@ -787,6 +789,12 @@ sender_->GestureFlingStart(x, y, velocity_x, velocity_y, args); } +bool EventSenderBindings::IsFlinging() const { + if (sender_) + return sender_->IsFlinging(); + return false; +} + void EventSenderBindings::GestureScrollFirstPoint(int x, int y) { if (sender_) sender_->GestureScrollFirstPoint(x, y); @@ -1808,6 +1816,10 @@ HandleInputEventOnViewOrPopup(event); } +bool EventSender::IsFlinging() const { + return view_->isFlinging(); +} + void EventSender::GestureScrollFirstPoint(int x, int y) { current_gesture_location_ = WebPoint(x, y); }
diff --git a/components/test_runner/event_sender.h b/components/test_runner/event_sender.h index 54986aa..916cf889 100644 --- a/components/test_runner/event_sender.h +++ b/components/test_runner/event_sender.h
@@ -120,6 +120,7 @@ float velocity_x, float velocity_y, gin::Arguments* args); + bool IsFlinging() const; void GestureScrollFirstPoint(int x, int y); void TouchStart();
diff --git a/components/test_runner/test_runner.cc b/components/test_runner/test_runner.cc index f09284ca..1c63ef63 100644 --- a/components/test_runner/test_runner.cc +++ b/components/test_runner/test_runner.cc
@@ -239,6 +239,7 @@ void OverridePreference(const std::string& key, v8::Local<v8::Value> value); void SetAcceptLanguages(const std::string& accept_languages); void SetPluginsEnabled(bool enabled); + bool AnimationScheduled(); void DumpEditingCallbacks(); void DumpAsMarkup(); void DumpAsText(); @@ -519,6 +520,7 @@ .SetMethod("setScriptsAllowed", &TestRunnerBindings::SetScriptsAllowed) .SetMethod("setStorageAllowed", &TestRunnerBindings::SetStorageAllowed) .SetMethod("setPluginsAllowed", &TestRunnerBindings::SetPluginsAllowed) + .SetMethod("animationScheduled", &TestRunnerBindings::AnimationScheduled) .SetMethod("setAllowDisplayOfInsecureContent", &TestRunnerBindings::SetAllowDisplayOfInsecureContent) .SetMethod("setAllowRunningOfInsecureContent", @@ -1102,6 +1104,13 @@ runner_->SetPluginsEnabled(enabled); } +bool TestRunnerBindings::AnimationScheduled() { + if (runner_) + return runner_->AnimationScheduled(); + else + return false; +} + void TestRunnerBindings::DumpEditingCallbacks() { if (runner_) runner_->DumpEditingCallbacks(); @@ -2685,6 +2694,10 @@ delegate_->ApplyPreferences(); } +bool TestRunner::AnimationScheduled() { + return proxy_->AnimationScheduled(); +} + void TestRunner::DumpEditingCallbacks() { dump_editting_callbacks_ = true; }
diff --git a/components/test_runner/test_runner.h b/components/test_runner/test_runner.h index d9b08a1..c6975a4 100644 --- a/components/test_runner/test_runner.h +++ b/components/test_runner/test_runner.h
@@ -342,6 +342,8 @@ // Enable or disable plugins. void SetPluginsEnabled(bool enabled); + bool AnimationScheduled(); + /////////////////////////////////////////////////////////////////////////// // Methods that modify the state of TestRunner
diff --git a/components/test_runner/web_test_proxy.cc b/components/test_runner/web_test_proxy.cc index 5f55529..9a6d1f8c 100644 --- a/components/test_runner/web_test_proxy.cc +++ b/components/test_runner/web_test_proxy.cc
@@ -659,9 +659,6 @@ if (!test_interfaces_->GetTestRunner()->TestIsRunning()) return; - if (event == blink::WebAXEventFocus) - test_interfaces_->GetAccessibilityController()->SetFocusedElement(obj); - const char* event_name = NULL; switch (event) { case blink::WebAXEventActiveDescendantChanged:
diff --git a/components/test_runner/web_test_proxy.h b/components/test_runner/web_test_proxy.h index 1bf4a07..566ba317 100644 --- a/components/test_runner/web_test_proxy.h +++ b/components/test_runner/web_test_proxy.h
@@ -144,6 +144,8 @@ void PostAccessibilityEvent(const blink::WebAXObject&, blink::WebAXEvent); + bool AnimationScheduled() { return animate_scheduled_; } + protected: WebTestProxyBase(); ~WebTestProxyBase(); @@ -376,9 +378,6 @@ WebTestProxyBase::ShowValidationMessage(main_message, main_message_hint, sub_message, sub_message_hint); } - virtual void postSpellCheckEvent(const blink::WebString& event_name) { - WebTestProxyBase::PostSpellCheckEvent(event_name); - } virtual blink::WebString acceptLanguages() { return WebTestProxyBase::acceptLanguages(); }
diff --git a/components/translate/core/browser/translate_language_list.cc b/components/translate/core/browser/translate_language_list.cc index 881406b..e54d829b 100644 --- a/components/translate/core/browser/translate_language_list.cc +++ b/components/translate/core/browser/translate_language_list.cc
@@ -36,6 +36,7 @@ // excluded because Chrome l10n library does not support it. const char* const kDefaultSupportedLanguages[] = { "af", // Afrikaans + "am", // Amharic "ar", // Arabic "az", // Azerbaijani "be", // Belarusian @@ -44,6 +45,7 @@ "bs", // Bosnian "ca", // Catalan "ceb", // Cebuano + "co", // Corsican "cs", // Czech "cy", // Welsh "da", // Danish @@ -56,11 +58,14 @@ "eu", // Basque "fa", // Persian "fi", // Finnish + "fy", // Frisian "fr", // French "ga", // Irish + "gd", // Scots Gaelic "gl", // Galician "gu", // Gujarati "ha", // Hausa + "haw", // Hawaiian "hi", // Hindi "hr", // Croatian "ht", // Haitian Creole @@ -77,7 +82,10 @@ "km", // Khmer "kn", // Kannada "ko", // Korean + "ku", // Kurdish + "ky", // Kyrgyz "la", // Latin + "lb", // Luxembourgish "lo", // Lao "lt", // Lithuanian "lv", // Latvian @@ -96,12 +104,16 @@ "ny", // Nyanja "pa", // Punjabi "pl", // Polish + "ps", // Pashto "pt", // Portuguese "ro", // Romanian "ru", // Russian + "sd", // Sindhi "si", // Sinhala "sk", // Slovak "sl", // Slovenian + "sm", // Samoan + "sn", // Shona "so", // Somali "sq", // Albanian "sr", // Serbian @@ -120,6 +132,7 @@ "uz", // Uzbek "vi", // Vietnamese "yi", // Yiddish + "xh", // Xhosa "yo", // Yoruba "zh-CN", // Chinese (Simplified) "zh-TW", // Chinese (Traditional)
diff --git a/content/BUILD.gn b/content/BUILD.gn index cb50de5..1d32ad24a 100644 --- a/content/BUILD.gn +++ b/content/BUILD.gn
@@ -43,6 +43,7 @@ "//content/gpu:gpu_sources", "//content/public/browser:browser_sources", "//content/public/child:child_sources", + "//content/public/gpu:gpu_sources", "//content/public/common:common_sources", "//content/public/plugin:plugin_sources", "//content/public/renderer:renderer_sources",
diff --git a/content/app/DEPS b/content/app/DEPS index 7745238..550e9a6 100644 --- a/content/app/DEPS +++ b/content/app/DEPS
@@ -10,5 +10,6 @@ "+gin/public/snapshot_fd_data.h", "+gin/v8_initializer.h", "+media/base", # For initializing media library. - "+media/midi", # For initializing midi library. + "+media/capture/video/android/capture_jni_registrar.h", + "+media/midi/midi_jni_registrar.h", ]
diff --git a/content/app/android/library_loader_hooks.cc b/content/app/android/library_loader_hooks.cc index dd14cc1..699d7e8 100644 --- a/content/app/android/library_loader_hooks.cc +++ b/content/app/android/library_loader_hooks.cc
@@ -28,6 +28,7 @@ #include "device/bluetooth/android/bluetooth_jni_registrar.h" #include "device/usb/android/usb_jni_registrar.h" #include "media/base/android/media_jni_registrar.h" +#include "media/capture/video/android/capture_jni_registrar.h" #include "media/midi/midi_jni_registrar.h" #include "net/android/net_jni_registrar.h" #include "ui/android/ui_android_jni_registrar.h" @@ -82,6 +83,9 @@ if (!media::RegisterJni(env)) return false; + if (!media::RegisterCaptureJni(env)) + return false; + if (!media::midi::RegisterJni(env)) return false;
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc index 59efad3..ba10cbb4 100644 --- a/content/app/content_main_runner.cc +++ b/content/app/content_main_runner.cc
@@ -68,6 +68,7 @@ #include "content/browser/gpu/gpu_process_host.h" #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/utility_process_host_impl.h" +#include "content/public/gpu/content_gpu_client.h" #include "content/public/plugin/content_plugin_client.h" #include "content/public/renderer/content_renderer_client.h" #include "content/public/utility/content_utility_client.h" @@ -134,6 +135,8 @@ #endif // !CHROME_MULTIPLE_DLL_CHILD #if !defined(OS_IOS) && !defined(CHROME_MULTIPLE_DLL_BROWSER) +base::LazyInstance<ContentGpuClient> + g_empty_content_gpu_client = LAZY_INSTANCE_INITIALIZER; base::LazyInstance<ContentPluginClient> g_empty_content_plugin_client = LAZY_INSTANCE_INITIALIZER; base::LazyInstance<ContentRendererClient> @@ -225,6 +228,15 @@ #endif // !CHROME_MULTIPLE_DLL_CHILD #if !defined(OS_IOS) && !defined(CHROME_MULTIPLE_DLL_BROWSER) + if (process_type == switches::kGpuProcess || + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess)) { + if (delegate) + content_client->gpu_ = delegate->CreateContentGpuClient(); + if (!content_client->gpu_) + content_client->gpu_ = &g_empty_content_gpu_client.Get(); + } + if (process_type == switches::kPluginProcess || process_type == switches::kPpapiPluginProcess) { if (delegate)
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index f6a27e6..8ae5275 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -117,6 +117,7 @@ deps += [ "//cc", "//cc/surfaces", + "//components/leveldb:lib", "//components/scheduler:common", "//content/app/resources", "//content/app/strings", @@ -180,7 +181,7 @@ } # TODO(GYP) - # ['OS!="ios" and chrome_multiple_dll!=1', { + # [chrome_multiple_dll!=1', { # 'dependencies': [ # '../third_party/WebKit/public/blink.gyp:blink', # ], @@ -499,13 +500,6 @@ deps += [ "//ui/compositor" ] } - if (!is_ios) { - sources += [ - "compositor/surface_utils.cc", - "compositor/surface_utils.h", - ] - } - if (enable_web_speech) { deps += [ "//third_party/flac" ] } @@ -525,8 +519,8 @@ deps += [ "//third_party/boringssl" ] } - if (enable_mojo_media != "none") { - configs += [ "//media/mojo/services:enable_mojo_media_config" ] + if (enable_mojo_media) { + configs += [ "//media/mojo/services:mojo_media_config" ] } if (enable_webvr) {
diff --git a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc index a8bd541..597631cc 100644 --- a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc +++ b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
@@ -132,7 +132,6 @@ const ui::AXNode* button = button_container->ChildAtIndex(0); EXPECT_EQ(ui::AX_ROLE_BUTTON, button->data().role); - EXPECT_TRUE(button->data().state >> ui::AX_STATE_FOCUSED & 1); } #if defined(OS_ANDROID)
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc index ac52699..f9e3aa55 100644 --- a/content/browser/accessibility/browser_accessibility_android.cc +++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -184,7 +184,7 @@ } bool BrowserAccessibilityAndroid::IsFocused() const { - return manager()->GetFocus(manager()->GetRoot()) == this; + return manager()->GetFocus() == this; } bool BrowserAccessibilityAndroid::IsHeading() const {
diff --git a/content/browser/accessibility/browser_accessibility_auralinux.cc b/content/browser/accessibility/browser_accessibility_auralinux.cc index 880839e..6b5a4f97 100644 --- a/content/browser/accessibility/browser_accessibility_auralinux.cc +++ b/content/browser/accessibility/browser_accessibility_auralinux.cc
@@ -177,7 +177,7 @@ if (!obj) return false; - obj->manager()->SetFocus(obj, true); + obj->manager()->SetFocus(*obj); return true; } @@ -539,7 +539,7 @@ if (state & (1 << ui::AX_STATE_FOCUSABLE)) atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE); - if (obj->manager()->GetFocus(NULL) == obj) + if (obj->manager()->GetFocus() == obj) atk_state_set_add_state(state_set, ATK_STATE_FOCUSED); if (state & (1 << ui::AX_STATE_ENABLED)) atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index cc47352..3ab3d20 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -865,7 +865,7 @@ - (NSNumber*)focused { BrowserAccessibilityManager* manager = browserAccessibility_->manager(); NSNumber* ret = [NSNumber numberWithBool: - manager->GetFocus(NULL) == browserAccessibility_]; + manager->GetFocus() == browserAccessibility_]; return ret; } @@ -1322,7 +1322,9 @@ - (NSArray*)selectedChildren { NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; BrowserAccessibilityManager* manager = browserAccessibility_->manager(); - BrowserAccessibility* focusedChild = manager->GetFocus(browserAccessibility_); + BrowserAccessibility* focusedChild = manager->GetFocus(); + if (!focusedChild->IsDescendantOf(browserAccessibility_)) + focusedChild = nullptr; // If it's not multiselectable, try to skip iterating over the // children. @@ -2361,7 +2363,7 @@ NSNumber* focusedNumber = value; BOOL focused = [focusedNumber intValue]; if (focused) - manager->SetFocus(browserAccessibility_, true); + manager->SetFocus(*browserAccessibility_); } if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { NSRange range = [(NSValue*)value rangeValue];
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc index c893d98..45d5ec2 100644 --- a/content/browser/accessibility/browser_accessibility_manager.cc +++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -123,7 +123,6 @@ : delegate_(delegate), factory_(factory), tree_(new ui::AXSerializableTree()), - focus_(NULL), user_is_navigating_away_(false), osk_state_(OSK_ALLOWED), ax_tree_id_(AXTreeIDRegistry::kNoAXTreeID), @@ -138,7 +137,6 @@ : delegate_(delegate), factory_(factory), tree_(new ui::AXSerializableTree()), - focus_(NULL), user_is_navigating_away_(false), osk_state_(OSK_ALLOWED), ax_tree_id_(AXTreeIDRegistry::kNoAXTreeID), @@ -162,9 +160,6 @@ LOG(FATAL) << tree_->error(); } } - - if (!focus_) - SetFocus(tree_->root(), false); } // static @@ -243,13 +238,15 @@ } void BrowserAccessibilityManager::OnWindowFocused() { - if (focus_) - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); + BrowserAccessibility* focus = GetFocus(); + if (focus) + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, focus); } void BrowserAccessibilityManager::OnWindowBlurred() { - if (focus_) - NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_)); + BrowserAccessibility* focus = GetFocus(); + if (focus) + NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, focus); } void BrowserAccessibilityManager::UserIsNavigatingAway() { @@ -269,11 +266,12 @@ } void BrowserAccessibilityManager::GotMouseDown() { - if (!focus_) + BrowserAccessibility* focus = GetFocus(); + if (!focus) return; osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT; - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); + NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, focus); } bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() { @@ -282,8 +280,6 @@ void BrowserAccessibilityManager::OnAccessibilityEvents( const std::vector<AXEventNotificationDetails>& details) { - bool should_send_initial_focus = false; - // Process all changes to the accessibility tree first. for (uint32_t index = 0; index < details.size(); ++index) { const AXEventNotificationDetails& detail = details[index]; @@ -296,17 +292,8 @@ } return; } - - // Set focus to the root if it's not anywhere else. - if (!focus_) { - SetFocus(tree_->root(), false); - should_send_initial_focus = true; - } } - if (should_send_initial_focus && NativeViewHasFocus()) - NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_)); - // Now iterate over the events again and fire the events. for (uint32_t index = 0; index < details.size(); index++) { const AXEventNotificationDetails& detail = details[index]; @@ -320,8 +307,6 @@ ui::AXEvent event_type = detail.event_type; if (event_type == ui::AX_EVENT_FOCUS || event_type == ui::AX_EVENT_BLUR) { - SetFocus(node, false); - if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN && osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED) osk_state_ = OSK_ALLOWED; @@ -387,20 +372,19 @@ } BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus( - BrowserAccessibility* root) { - BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root); - if (!node) + BrowserAccessibility* focus) { + if (!focus) return NULL; int active_descendant_id; - if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, - &active_descendant_id)) { + if (focus->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, + &active_descendant_id)) { BrowserAccessibility* active_descendant = - node->manager()->GetFromID(active_descendant_id); + focus->manager()->GetFromID(active_descendant_id); if (active_descendant) return active_descendant; } - return node; + return focus; } bool BrowserAccessibilityManager::NativeViewHasFocus() { @@ -410,39 +394,33 @@ return false; } -BrowserAccessibility* BrowserAccessibilityManager::GetFocus( - BrowserAccessibility* root) { - if (!focus_) - return nullptr; +BrowserAccessibility* BrowserAccessibilityManager::GetFocus() { + int32_t focus_id = GetTreeData().focus_id; + BrowserAccessibility* obj = GetFromID(focus_id); + if (!obj) + return GetRoot(); - if (root && !focus_->IsDescendantOf(root->node())) - return nullptr; - - BrowserAccessibility* obj = GetFromAXNode(focus_); - DCHECK(obj); if (obj->HasIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)) { BrowserAccessibilityManager* child_manager = BrowserAccessibilityManager::FromID( obj->GetIntAttribute(ui::AX_ATTR_CHILD_TREE_ID)); if (child_manager) - return child_manager->GetFocus(child_manager->GetRoot()); + return child_manager->GetFocus(); } return obj; } -void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) { - if (focus_ != node) - focus_ = node; - - if (notify && node && delegate_) - delegate_->AccessibilitySetFocus(node->id()); +void BrowserAccessibilityManager::SetFocus(const BrowserAccessibility& node) { + if (delegate_) + delegate_->AccessibilitySetFocus(node.GetId()); } -void BrowserAccessibilityManager::SetFocus( - BrowserAccessibility* obj, bool notify) { - if (obj->node()) - SetFocus(obj->node(), notify); +void BrowserAccessibilityManager::SetFocusLocallyForTesting( + BrowserAccessibility* node) { + ui::AXTreeData data = GetTreeData(); + data.focus_id = node->GetId(); + tree_->UpdateData(data); } void BrowserAccessibilityManager::DoDefaultAction( @@ -678,12 +656,6 @@ void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) { DCHECK(node); - if (node == focus_ && tree_) { - if (node != tree_->root()) - SetFocus(tree_->root(), false); - else - focus_ = NULL; - } if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end()) return; GetFromAXNode(node)->Destroy();
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h index a993de8..c025df5 100644 --- a/content/browser/accessibility/browser_accessibility_manager.h +++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -172,11 +172,12 @@ // occurred in the tab. void GotMouseDown(); - // Update the focused node to |node|, which may be null. - // If |notify| is true, send a message to the renderer to set focus - // to this node. - void SetFocus(ui::AXNode* node, bool notify); - void SetFocus(BrowserAccessibility* node, bool notify); + // Send a message to the renderer to set focus to this node. + void SetFocus(const BrowserAccessibility& node); + + // Pretend that the given node has focus, for testing only. Doesn't + // communicate with the renderer and doesn't fire any events. + void SetFocusLocallyForTesting(BrowserAccessibility* node); // Tell the renderer to do the default action for this node. void DoDefaultAction(const BrowserAccessibility& node); @@ -248,13 +249,12 @@ ToBrowserAccessibilityManagerAuraLinux(); #endif - // Return the object that has focus, if it's a descendant of the - // given root (inclusive). Does not make a new reference. - virtual BrowserAccessibility* GetFocus(BrowserAccessibility* root); + // Return the object that has focus. + virtual BrowserAccessibility* GetFocus(); - // Return the descentant of the given root that has focus, or that object's - // active descendant if it has one. - BrowserAccessibility* GetActiveDescendantFocus(BrowserAccessibility* root); + // Given a focused node |focus|, returns a descendant of that node if it + // has an active descendant, otherwise returns |focus|. + BrowserAccessibility* GetActiveDescendantFocus(BrowserAccessibility* focus); // Returns true if native focus is anywhere in this WebContents or not. bool NativeViewHasFocus(); @@ -365,9 +365,6 @@ // The underlying tree of accessibility objects. scoped_ptr<ui::AXSerializableTree> tree_; - // The node that currently has focus. - ui::AXNode* focus_; - // A mapping from a node id to its wrapper of type BrowserAccessibility. base::hash_map<int32_t, BrowserAccessibility*> id_wrapper_map_;
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc index 6c48f44..99000dd 100644 --- a/content/browser/accessibility/browser_accessibility_manager_android.cc +++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -143,7 +143,7 @@ switch (event_type) { case ui::AX_EVENT_LOAD_COMPLETE: Java_BrowserAccessibilityManager_handlePageLoaded( - env, obj.obj(), focus_->id()); + env, obj.obj(), GetFocus()->GetId()); break; case ui::AX_EVENT_FOCUS: Java_BrowserAccessibilityManager_handleFocusChanged( @@ -183,7 +183,7 @@ break; case ui::AX_EVENT_TEXT_CHANGED: case ui::AX_EVENT_VALUE_CHANGED: - if (android_node->IsEditableText() && GetFocus(GetRoot()) == node) { + if (android_node->IsEditableText() && GetFocus() == node) { Java_BrowserAccessibilityManager_handleEditableTextChanged( env, obj.obj(), node->GetId()); } else if (android_node->IsSlider()) { @@ -496,13 +496,13 @@ jint id) { BrowserAccessibility* node = GetFromID(id); if (node) - SetFocus(node, true); + SetFocus(*node); } void BrowserAccessibilityManagerAndroid::Blur( JNIEnv* env, const JavaParamRef<jobject>& obj) { - SetFocus(GetRoot(), true); + SetFocus(*GetRoot()); } void BrowserAccessibilityManagerAndroid::ScrollToMakeNodeVisible(
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.h b/content/browser/accessibility/browser_accessibility_manager_mac.h index 0cda76d..f659059 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.h +++ b/content/browser/accessibility/browser_accessibility_manager_mac.h
@@ -23,7 +23,7 @@ static ui::AXTreeUpdate GetEmptyDocument(); - BrowserAccessibility* GetFocus(BrowserAccessibility* root) override; + BrowserAccessibility* GetFocus() override; // Implementation of BrowserAccessibilityManager. void NotifyAccessibilityEvent(ui::AXEvent event_type,
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm index 0edf584..3d6ff9af6 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -58,6 +58,8 @@ @"AXLiveRegionChanged"; NSString* const NSAccessibilityMenuItemSelectedNotification = @"AXMenuItemSelected"; + +// Attributes used for NSAccessibilitySelectedTextChangedNotification. NSString* const NSAccessibilityTextStateChangeTypeKey = @"AXTextStateChangeType"; NSString* const NSAccessibilityTextStateSyncKey = @"AXTextStateSync"; @@ -67,8 +69,9 @@ @"AXTextSelectionGranularity"; NSString* const NSAccessibilityTextSelectionChangedFocus = @"AXTextSelectionChangedFocus"; -NSString* const NSAccessibilityTextChangeElement = - @"AXAccessibilityTextChangeElement"; +NSString* const NSAccessibilitySelectedTextMarkerRangeAttribute = + @"AXSelectedTextMarkerRange"; +NSString* const NSAccessibilityTextChangeElement = @"AXTextChangeElement"; } // namespace @@ -106,16 +109,15 @@ return update; } -BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus( - BrowserAccessibility* root) { +BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus() { // On Mac, list boxes should always get focus on the whole list, otherwise // information about the number of selected items will never be reported. - BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root); + BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(); if (node && node->GetRole() == ui::AX_ROLE_LIST_BOX) return node; // For other roles, follow the active descendant. - return GetActiveDescendantFocus(root); + return GetActiveDescendantFocus(node); } void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( @@ -125,8 +127,7 @@ return; if (event_type == ui::AX_EVENT_FOCUS) { - BrowserAccessibility* active_descendant = GetActiveDescendantFocus( - GetRoot()); + BrowserAccessibility* active_descendant = GetActiveDescendantFocus(node); if (active_descendant) node = active_descendant; @@ -135,7 +136,7 @@ node->GetParent() && node->GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) { node = node->GetParent(); - SetFocus(node, false); + SetFocus(*node); } } @@ -187,10 +188,21 @@ // 10.11 or later to notify Voiceover about text selection changes. This // API has been present on versions of OS X since 10.7 but doesn't // appear to be needed by Voiceover before version 10.11. + // WebKit fires a notification both on the focused object and the root. NSDictionary* user_info = GetUserInfoForSelectedTextChangedNotification(); + + BrowserAccessibility* focus = GetFocus(); + if (!focus) + return; NSAccessibilityPostNotificationWithUserInfo( - native_node, mac_notification, user_info); + focus->ToBrowserAccessibilityCocoa(), mac_notification, user_info); + + BrowserAccessibility* root = GetRoot(); + if (!root) + return; + NSAccessibilityPostNotificationWithUserInfo( + root->ToBrowserAccessibilityCocoa(), mac_notification, user_info); return; } break; @@ -298,16 +310,18 @@ forKey:NSAccessibilityTextSelectionGranularity]; [user_info setObject:[NSNumber numberWithBool:YES] forKey:NSAccessibilityTextSelectionChangedFocus]; - // TODO(nektar): Set selected text marker range. int32_t focus_id = GetTreeData().sel_focus_object_id; BrowserAccessibility* focus_object = GetFromID(focus_id); if (focus_object) { focus_object = focus_object->GetClosestPlatformObject(); auto native_focus_object = focus_object->ToBrowserAccessibilityCocoa(); - if (native_focus_object) + if (native_focus_object) { + [user_info setObject:[native_focus_object selectedTextMarkerRange] + forKey:NSAccessibilitySelectedTextMarkerRangeAttribute]; [user_info setObject:native_focus_object forKey:NSAccessibilityTextChangeElement]; + } } return user_info;
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc index d3cd1c4..1e9220c 100644 --- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc +++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -1396,27 +1396,18 @@ ui::AXNodeData node2; node2.id = 2; - node2.state = 1 << ui::AX_STATE_FOCUSED; + ui::AXTreeUpdate initial_state = MakeAXTreeUpdate(root, node2); + initial_state.has_tree_data = true; + initial_state.tree_data.focus_id = 2; scoped_ptr<BrowserAccessibilityManager> manager( BrowserAccessibilityManager::Create( - MakeAXTreeUpdate(root, node2), + initial_state, nullptr, new CountedBrowserAccessibilityFactory())); ASSERT_EQ(1, manager->GetRoot()->GetId()); - ASSERT_EQ(1, manager->GetFocus(manager->GetRoot())->GetId()); - - // Send the focus event for node 2. - std::vector<AXEventNotificationDetails> events; - events.push_back(AXEventNotificationDetails()); - events[0].update = MakeAXTreeUpdate(node2); - events[0].id = 2; - events[0].event_type = ui::AX_EVENT_FOCUS; - manager->OnAccessibilityEvents(events); - - ASSERT_EQ(1, manager->GetRoot()->GetId()); - ASSERT_EQ(2, manager->GetFocus(manager->GetRoot())->GetId()); + ASSERT_EQ(2, manager->GetFocus()->GetId()); // Now replace the tree with a new tree consisting of a single root. ui::AXNodeData root2; @@ -1434,7 +1425,7 @@ // Make sure that the focused node was updated to the new root and // that this doesn't crash. ASSERT_EQ(3, manager->GetRoot()->GetId()); - ASSERT_EQ(3, manager->GetFocus(manager->GetRoot())->GetId()); + ASSERT_EQ(3, manager->GetFocus()->GetId()); } TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash2) { @@ -1449,7 +1440,6 @@ ui::AXNodeData node2; node2.id = 2; - node2.state = 1 << ui::AX_STATE_FOCUSED; ui::AXNodeData node3; node3.id = 3; @@ -1459,25 +1449,17 @@ node4.id = 4; node4.state = 0; + ui::AXTreeUpdate initial_state = MakeAXTreeUpdate(root, node2, node3, node4); + initial_state.has_tree_data = true; + initial_state.tree_data.focus_id = 2; scoped_ptr<BrowserAccessibilityManager> manager( BrowserAccessibilityManager::Create( - MakeAXTreeUpdate(root, node2, node3, node4), + initial_state, nullptr, new CountedBrowserAccessibilityFactory())); ASSERT_EQ(1, manager->GetRoot()->GetId()); - ASSERT_EQ(1, manager->GetFocus(manager->GetRoot())->GetId()); - - // Send the focus event for node 2. - std::vector<AXEventNotificationDetails> events; - events.push_back(AXEventNotificationDetails()); - events[0].update = MakeAXTreeUpdate(node2); - events[0].id = 2; - events[0].event_type = ui::AX_EVENT_FOCUS; - manager->OnAccessibilityEvents(events); - - ASSERT_EQ(1, manager->GetRoot()->GetId()); - ASSERT_EQ(2, manager->GetFocus(manager->GetRoot())->GetId()); + ASSERT_EQ(2, manager->GetFocus()->GetId()); // Now replace the tree with a new tree consisting of a single root. ui::AXNodeData root2; @@ -1497,7 +1479,7 @@ // Make sure that the focused node was updated to the new root and // that this doesn't crash. ASSERT_EQ(3, manager->GetRoot()->GetId()); - ASSERT_EQ(3, manager->GetFocus(manager->GetRoot())->GetId()); + ASSERT_EQ(3, manager->GetFocus()->GetId()); } } // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc index 4f963eb..a02cc9a 100644 --- a/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -173,7 +173,7 @@ // Try to fire a focus event on the root first and then the focused node. // This will clear focus_event_on_root_needed_ if successful. - if (focus_ != tree_->root() && GetRoot()) + if (GetFocus() != GetRoot() && GetRoot()) NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot()); BrowserAccessibilityManager::OnWindowFocused(); inside_on_window_focused_ = false;
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc index 0bfe235..a78d8b2 100644 --- a/content/browser/accessibility/browser_accessibility_win.cc +++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -435,7 +435,7 @@ return E_INVALIDARG; BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>( - manager()->GetFocus(this)); + manager()->GetFocus()); if (focus == this) { focus_child->vt = VT_I4; focus_child->lVal = CHILDID_SELF; @@ -570,7 +570,7 @@ state->vt = VT_I4; state->lVal = target->ia_state(); - if (manager()->GetFocus(NULL) == this) + if (manager()->GetFocus() == this) state->lVal |= STATE_SYSTEM_FOCUSED; return S_OK; @@ -677,7 +677,7 @@ return E_FAIL; if (flags_sel & SELFLAG_TAKEFOCUS) { - manager()->SetFocus(this, true); + manager()->SetFocus(*this); return S_OK; } @@ -4435,8 +4435,6 @@ ia_role = ROLE_SYSTEM_LISTITEM; if (ia_state & STATE_SYSTEM_SELECTABLE) { ia_state |= STATE_SYSTEM_FOCUSABLE; - if (HasState(ui::AX_STATE_FOCUSED)) - ia_state |= STATE_SYSTEM_FOCUSED; } break; case ui::AX_ROLE_LIST_ITEM: @@ -4485,8 +4483,6 @@ ia2_state &= ~(IA2_STATE_EDITABLE); if (ia_state & STATE_SYSTEM_SELECTABLE) { ia_state |= STATE_SYSTEM_FOCUSABLE; - if (HasState(ui::AX_STATE_FOCUSED)) - ia_state |= STATE_SYSTEM_FOCUSED; } break; case ui::AX_ROLE_METER:
diff --git a/content/browser/accessibility/browser_accessibility_win_unittest.cc b/content/browser/accessibility/browser_accessibility_win_unittest.cc index 3d65de64..d86b380 100644 --- a/content/browser/accessibility/browser_accessibility_win_unittest.cc +++ b/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -899,7 +899,7 @@ combo_box.role = ui::AX_ROLE_COMBO_BOX; combo_box_text.role = ui::AX_ROLE_STATIC_TEXT; combo_box.state = (1 << ui::AX_STATE_EDITABLE) | - (1 << ui::AX_STATE_FOCUSABLE) | (1 << ui::AX_STATE_FOCUSED); + (1 << ui::AX_STATE_FOCUSABLE); combo_box_text.state = 1 << ui::AX_STATE_EDITABLE; combo_box.child_ids.push_back(combo_box_text.id); @@ -914,8 +914,7 @@ search_box_text.role = ui::AX_ROLE_STATIC_TEXT; new_line.role = ui::AX_ROLE_LINE_BREAK; search_box.state = (1 << ui::AX_STATE_EDITABLE) | - (1 << ui::AX_STATE_FOCUSABLE) | - (1 << ui::AX_STATE_FOCUSED); + (1 << ui::AX_STATE_FOCUSABLE); search_box_text.state = new_line.state = 1 << ui::AX_STATE_EDITABLE; search_box.child_ids.push_back(search_box_text.id); search_box.child_ids.push_back(new_line.id); @@ -970,9 +969,9 @@ BrowserAccessibilityWin* combo_box_accessible = root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); ASSERT_NE(nullptr, combo_box_accessible); - manager->SetFocus(combo_box_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(combo_box_accessible); ASSERT_EQ(combo_box_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + manager->GetFocus()->ToBrowserAccessibilityWin()); BrowserAccessibilityWin* search_box_accessible = root_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); ASSERT_NE(nullptr, search_box_accessible); @@ -1196,7 +1195,7 @@ combo_box.id = 2; combo_box.role = ui::AX_ROLE_COMBO_BOX; combo_box.state = (1 << ui::AX_STATE_EDITABLE) | - (1 << ui::AX_STATE_FOCUSABLE) | (1 << ui::AX_STATE_FOCUSED); + (1 << ui::AX_STATE_FOCUSABLE); combo_box.SetValue("Test1"); // Place the caret between 't' and 'e'. combo_box.AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, 1); @@ -1231,9 +1230,9 @@ BrowserAccessibilityWin* combo_box_accessible = root_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); ASSERT_NE(nullptr, combo_box_accessible); - manager->SetFocus(combo_box_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(combo_box_accessible); ASSERT_EQ(combo_box_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + manager->GetFocus()->ToBrowserAccessibilityWin()); BrowserAccessibilityWin* text_field_accessible = root_accessible->PlatformGetChild(1)->ToBrowserAccessibilityWin(); ASSERT_NE(nullptr, text_field_accessible); @@ -1254,11 +1253,9 @@ EXPECT_EQ(2L, caret_offset); // Move the focus to the text field. - combo_box.state &= ~(1 << ui::AX_STATE_FOCUSED); - text_field.state |= 1 << ui::AX_STATE_FOCUSED; - manager->SetFocus(text_field_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(text_field_accessible); ASSERT_EQ(text_field_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + manager->GetFocus()->ToBrowserAccessibilityWin()); // The caret should not have moved. hr = text_field_accessible->get_caretOffset(&caret_offset); @@ -1370,10 +1367,9 @@ EXPECT_EQ(6L, caret_offset); // Move the focus to the content editable. - div_editable.state |= 1 << ui::AX_STATE_FOCUSED; - manager->SetFocus(div_editable_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(div_editable_accessible); ASSERT_EQ(div_editable_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + manager->GetFocus()->ToBrowserAccessibilityWin()); BrowserAccessibilityWin* text_accessible = div_editable_accessible->PlatformGetChild(0)->ToBrowserAccessibilityWin(); @@ -1537,10 +1533,9 @@ EXPECT_EQ(7L, caret_offset); // Move the focus to the content editable. - div_editable.state |= 1 << ui::AX_STATE_FOCUSED; - manager->SetFocus(div_editable_accessible, false /* notify */); + manager->SetFocusLocallyForTesting(div_editable_accessible); ASSERT_EQ(div_editable_accessible, - manager->GetFocus(root_accessible)->ToBrowserAccessibilityWin()); + manager->GetFocus()->ToBrowserAccessibilityWin()); // The caret should not have moved. hr = div_editable_accessible->get_caretOffset(&caret_offset);
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc index e4d2fc4..f671691 100644 --- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -213,18 +213,9 @@ } // Flaky on Windows: http://crbug.com/486861 -#if defined(OS_WIN) -#define MAYBE_AccessibilityEventsListboxNext \ - DISABLED_AccessibilityEventsListboxNext -#define MAYBE_AccessibilityEventsMenuListPopup \ - DISABLED_AccessibilityEventsMenuListPopup -#else -#define MAYBE_AccessibilityEventsListboxNext AccessibilityEventsListboxNext -#define MAYBE_AccessibilityEventsMenuListPopup AccessibilityEventsMenuListPopup -#endif - +// Flaky on Mac: http://crbug.com/588271 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, - MAYBE_AccessibilityEventsListboxNext) { + DISABLED_AccessibilityEventsListboxNext) { RunEventTest(FILE_PATH_LITERAL("listbox-next.html")); } @@ -238,8 +229,9 @@ RunEventTest(FILE_PATH_LITERAL("menulist-next.html")); } +// Flaky on Windows: http://crbug.com/486861 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, - MAYBE_AccessibilityEventsMenuListPopup) { + DISABLED_AccessibilityEventsMenuListPopup) { RunEventTest(FILE_PATH_LITERAL("menulist-popup.html")); }
diff --git a/content/browser/android/in_process/synchronous_input_event_filter.cc b/content/browser/android/in_process/synchronous_input_event_filter.cc index 3ce1c61..80f10250 100644 --- a/content/browser/android/in_process/synchronous_input_event_filter.cc +++ b/content/browser/android/in_process/synchronous_input_event_filter.cc
@@ -80,4 +80,8 @@ compositor->DidStopFlinging(); } +void SynchronousInputEventFilter::NonBlockingInputEventHandled( + int routing_id, + blink::WebInputEvent::Type type) {} + } // namespace content
diff --git a/content/browser/android/in_process/synchronous_input_event_filter.h b/content/browser/android/in_process/synchronous_input_event_filter.h index 98375d7..32b6ecbc 100644 --- a/content/browser/android/in_process/synchronous_input_event_filter.h +++ b/content/browser/android/in_process/synchronous_input_event_filter.h
@@ -44,6 +44,8 @@ void DidOverscroll(int routing_id, const DidOverscrollParams& params) override; void DidStopFlinging(int routing_id) override; + void NonBlockingInputEventHandled(int routing_id, + blink::WebInputEvent::Type type) override; private: void SetBoundHandlerOnUIThread(const Handler& handler);
diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.cc b/content/browser/bluetooth/bluetooth_dispatcher_host.cc index e5b96d93d..c4fe0562 100644 --- a/content/browser/bluetooth/bluetooth_dispatcher_host.cc +++ b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
@@ -233,6 +233,34 @@ return services; } +UMARequestDeviceOutcome OutcomeFromChooserEvent(BluetoothChooser::Event event) { + switch (event) { + case BluetoothChooser::Event::DENIED_PERMISSION: + return UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_DENIED_PERMISSION; + case BluetoothChooser::Event::CANCELLED: + return UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED; + case BluetoothChooser::Event::SHOW_OVERVIEW_HELP: + return UMARequestDeviceOutcome::BLUETOOTH_OVERVIEW_HELP_LINK_PRESSED; + case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP: + return UMARequestDeviceOutcome::ADAPTER_OFF_HELP_LINK_PRESSED; + case BluetoothChooser::Event::SHOW_NEED_LOCATION_HELP: + return UMARequestDeviceOutcome::NEED_LOCATION_HELP_LINK_PRESSED; + case BluetoothChooser::Event::SELECTED: + // We can't know if we are going to send a success message yet because + // the device could have vanished. This event should be histogramed + // manually after checking if the device is still around. + NOTREACHED(); + return UMARequestDeviceOutcome::SUCCESS; + case BluetoothChooser::Event::RESCAN: + // Rescanning doesn't result in a IPC message for the request being sent + // so no need to histogram it. + NOTREACHED(); + return UMARequestDeviceOutcome::SUCCESS; + } + NOTREACHED(); + return UMARequestDeviceOutcome::SUCCESS; +} + } // namespace BluetoothDispatcherHost::BluetoothDispatcherHost(int render_process_id) @@ -338,11 +366,13 @@ public: RequestDeviceSession(int thread_id, int request_id, + int frame_routing_id, url::Origin origin, const std::vector<BluetoothScanFilter>& filters, const std::vector<BluetoothUUID>& optional_services) : thread_id(thread_id), request_id(request_id), + frame_routing_id(frame_routing_id), origin(origin), filters(filters), optional_services(optional_services) {} @@ -369,6 +399,7 @@ const int thread_id; const int request_id; + const int frame_routing_id; const url::Origin origin; const std::vector<BluetoothScanFilter> filters; const std::vector<BluetoothUUID> optional_services; @@ -1063,9 +1094,10 @@ // Create storage for the information that backs the chooser, and show the // chooser. - RequestDeviceSession* const session = new RequestDeviceSession( - thread_id, request_id, render_frame_host->GetLastCommittedOrigin(), - filters, optional_services_blacklist_filtered); + RequestDeviceSession* const session = + new RequestDeviceSession(thread_id, request_id, frame_routing_id, + render_frame_host->GetLastCommittedOrigin(), + filters, optional_services_blacklist_filtered); int chooser_id = request_device_sessions_.Add(session); BluetoothChooser::EventHandler chooser_event_handler = @@ -1160,38 +1192,35 @@ switch (event) { case BluetoothChooser::Event::RESCAN: StartDeviceDiscovery(session, chooser_id); - break; + // No need to close the chooser so we return. + return; case BluetoothChooser::Event::DENIED_PERMISSION: case BluetoothChooser::Event::CANCELLED: - case BluetoothChooser::Event::SELECTED: { - // Synchronously ensure nothing else calls into the chooser after it has - // asked to be closed. - session->chooser.reset(); - - // Yield to the event loop to make sure we don't destroy the session - // within a BluetoothDispatcherHost stack frame. - if (!base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&BluetoothDispatcherHost::FinishClosingChooser, - weak_ptr_on_ui_thread_, chooser_id, event, - device_id))) { - LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog."; - } + case BluetoothChooser::Event::SELECTED: break; - } case BluetoothChooser::Event::SHOW_OVERVIEW_HELP: - ShowBluetoothOverviewLink(); - break; - case BluetoothChooser::Event::SHOW_PAIRING_HELP: - ShowBluetoothPairingLink(); + VLOG(1) << "Overview Help link pressed."; break; case BluetoothChooser::Event::SHOW_ADAPTER_OFF_HELP: - ShowBluetoothAdapterOffLink(); + VLOG(1) << "Adapter Off Help link pressed."; break; case BluetoothChooser::Event::SHOW_NEED_LOCATION_HELP: - ShowNeedLocationLink(); + VLOG(1) << "Need Location Help link pressed."; break; } + + // Synchronously ensure nothing else calls into the chooser after it has + // asked to be closed. + session->chooser.reset(); + + // Yield to the event loop to make sure we don't destroy the session + // within a BluetoothDispatcherHost stack frame. + if (!base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&BluetoothDispatcherHost::FinishClosingChooser, + weak_ptr_on_ui_thread_, chooser_id, event, device_id))) { + LOG(WARNING) << "No TaskRunner; not closing requestDevice dialog."; + } } void BluetoothDispatcherHost::FinishClosingChooser( @@ -1202,10 +1231,9 @@ RequestDeviceSession* session = request_device_sessions_.Lookup(chooser_id); DCHECK(session) << "Session removed unexpectedly."; - if (event == BluetoothChooser::Event::CANCELLED) { - RecordRequestDeviceOutcome( - UMARequestDeviceOutcome::BLUETOOTH_CHOOSER_CANCELLED); - VLOG(1) << "Bluetooth chooser cancelled"; + if ((event != BluetoothChooser::Event::DENIED_PERMISSION) && + (event != BluetoothChooser::Event::SELECTED)) { + RecordRequestDeviceOutcome(OutcomeFromChooserEvent(event)); Send(new BluetoothMsg_RequestDeviceError( session->thread_id, session->request_id, WebBluetoothError::ChooserCancelled)); @@ -1500,24 +1528,4 @@ .outcome != CacheQueryOutcome::BAD_RENDERER; } -void BluetoothDispatcherHost::ShowBluetoothOverviewLink() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NOTIMPLEMENTED(); -} - -void BluetoothDispatcherHost::ShowBluetoothPairingLink() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NOTIMPLEMENTED(); -} - -void BluetoothDispatcherHost::ShowBluetoothAdapterOffLink() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NOTIMPLEMENTED(); -} - -void BluetoothDispatcherHost::ShowNeedLocationLink() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - NOTIMPLEMENTED(); -} - } // namespace content
diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.h b/content/browser/bluetooth/bluetooth_dispatcher_host.h index c1db5f1..e8ce203 100644 --- a/content/browser/bluetooth/bluetooth_dispatcher_host.h +++ b/content/browser/bluetooth/bluetooth_dispatcher_host.h
@@ -265,12 +265,6 @@ int frame_routing_id, const std::string& characteristic_instance_id); - // Show help pages from the chooser dialog. - void ShowBluetoothOverviewLink(); - void ShowBluetoothPairingLink(); - void ShowBluetoothAdapterOffLink(); - void ShowNeedLocationLink(); - int render_process_id_; // Maps a (thread_id,request_id) to information about its requestDevice call,
diff --git a/content/browser/bluetooth/bluetooth_metrics.h b/content/browser/bluetooth/bluetooth_metrics.h index fa621a7f..fbbea33 100644 --- a/content/browser/bluetooth/bluetooth_metrics.h +++ b/content/browser/bluetooth/bluetooth_metrics.h
@@ -64,6 +64,9 @@ BLUETOOTH_CHOOSER_CANCELLED = 9, BLUETOOTH_CHOOSER_DENIED_PERMISSION = 10, BLACKLISTED_SERVICE_IN_FILTER = 11, + BLUETOOTH_OVERVIEW_HELP_LINK_PRESSED = 12, + ADAPTER_OFF_HELP_LINK_PRESSED = 13, + NEED_LOCATION_HELP_LINK_PRESSED = 14, // NOTE: Add new requestDevice() outcomes immediately above this line. Make // sure to update the enum list in // tools/metrics/histograms/histograms.xml accordingly.
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index 22c3e58..64cec9e 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc
@@ -975,8 +975,6 @@ #if defined(MOJO_SHELL_CLIENT) MojoShellConnection::Destroy(); #endif - mojo_ipc_support_.reset(); - mojo_shell_context_.reset(); #if !defined(OS_IOS) if (RenderProcessHost::run_renderer_in_process()) @@ -1042,6 +1040,12 @@ #endif #endif // !defined(OS_IOS) + // Shutdown Mojo shell and IPC. +#if !defined(OS_IOS) + mojo_shell_context_.reset(); + mojo_ipc_support_.reset(); +#endif + // Must be size_t so we can subtract from it. for (size_t thread_id = BrowserThread::ID_COUNT - 1; thread_id >= (BrowserThread::UI + 1); @@ -1184,6 +1188,14 @@ TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted"); #if !defined(OS_IOS) + // Bring up Mojo IPC and shell as early as possible. + mojo_ipc_support_.reset(new IPC::ScopedIPCSupport( + BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO) + ->task_runner())); + mojo_shell_context_.reset(new MojoShellContext); +#endif + +#if !defined(OS_IOS) indexed_db_thread_.reset(new base::Thread("IndexedDB")); indexed_db_thread_->Start(); #endif @@ -1325,11 +1337,6 @@ #endif // !defined(OS_IOS) - mojo_shell_context_.reset(new MojoShellContext); - mojo_ipc_support_.reset(new IPC::ScopedIPCSupport( - BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO) - ->task_runner())); - return result_code_; }
diff --git a/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm b/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm index 2b1c3ec..a7e99a2b 100644 --- a/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm +++ b/content/browser/compositor/browser_compositor_overlay_candidate_validator_mac.mm
@@ -6,7 +6,6 @@ #include <stddef.h> -#include "cc/output/overlay_strategy_sandwich.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "gpu/config/gpu_driver_bug_workaround_type.h"
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc index dc965d4..18af2c4 100644 --- a/content/browser/download/download_browsertest.cc +++ b/content/browser/download/download_browsertest.cc
@@ -29,8 +29,11 @@ #include "content/browser/download/download_item_impl.h" #include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_resource_handler.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/power_save_blocker.h" +#include "content/public/browser/resource_dispatcher_host_delegate.h" +#include "content/public/browser/resource_throttle.h" #include "content/public/common/content_features.h" #include "content/public/common/webplugininfo.h" #include "content/public/test/browser_test_utils.h" @@ -118,17 +121,17 @@ class DownloadFileWithDelay : public DownloadFileImpl { public: - DownloadFileWithDelay( - scoped_ptr<DownloadSaveInfo> save_info, - const base::FilePath& default_download_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, - const net::BoundNetLog& bound_net_log, - scoped_ptr<PowerSaveBlocker> power_save_blocker, - base::WeakPtr<DownloadDestinationObserver> observer, - base::WeakPtr<DownloadFileWithDelayFactory> owner); + DownloadFileWithDelay(const DownloadSaveInfo& save_info, + const base::FilePath& default_download_directory, + const GURL& url, + const GURL& referrer_url, + bool calculate_hash, + base::File file, + scoped_ptr<ByteStreamReader> stream, + const net::BoundNetLog& bound_net_log, + scoped_ptr<PowerSaveBlocker> power_save_blocker, + base::WeakPtr<DownloadDestinationObserver> observer, + base::WeakPtr<DownloadFileWithDelayFactory> owner); ~DownloadFileWithDelay() override; @@ -165,11 +168,12 @@ // DownloadFileFactory interface. DownloadFile* CreateFile( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_download_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, + base::File file, scoped_ptr<ByteStreamReader> stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) override; @@ -189,21 +193,23 @@ }; DownloadFileWithDelay::DownloadFileWithDelay( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_download_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, + base::File file, scoped_ptr<ByteStreamReader> stream, const net::BoundNetLog& bound_net_log, scoped_ptr<PowerSaveBlocker> power_save_blocker, base::WeakPtr<DownloadDestinationObserver> observer, base::WeakPtr<DownloadFileWithDelayFactory> owner) - : DownloadFileImpl(std::move(save_info), + : DownloadFileImpl(save_info, default_download_directory, url, referrer_url, calculate_hash, + std::move(file), std::move(stream), bound_net_log, observer), @@ -247,11 +253,12 @@ DownloadFileWithDelayFactory::~DownloadFileWithDelayFactory() {} DownloadFile* DownloadFileWithDelayFactory::CreateFile( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_download_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, + base::File file, scoped_ptr<ByteStreamReader> stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) { @@ -259,8 +266,8 @@ PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, PowerSaveBlocker::kReasonOther, "Download in progress")); return new DownloadFileWithDelay( - std::move(save_info), default_download_directory, url, referrer_url, - calculate_hash, std::move(stream), bound_net_log, std::move(psb), + save_info, default_download_directory, url, referrer_url, calculate_hash, + std::move(file), std::move(stream), bound_net_log, std::move(psb), observer, weak_ptr_factory_.GetWeakPtr()); } @@ -289,20 +296,22 @@ class CountingDownloadFile : public DownloadFileImpl { public: - CountingDownloadFile(scoped_ptr<DownloadSaveInfo> save_info, + CountingDownloadFile(const DownloadSaveInfo& save_info, const base::FilePath& default_downloads_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, + base::File file, scoped_ptr<ByteStreamReader> stream, const net::BoundNetLog& bound_net_log, scoped_ptr<PowerSaveBlocker> power_save_blocker, base::WeakPtr<DownloadDestinationObserver> observer) - : DownloadFileImpl(std::move(save_info), + : DownloadFileImpl(save_info, default_downloads_directory, url, referrer_url, calculate_hash, + std::move(file), std::move(stream), bound_net_log, observer) {} @@ -349,21 +358,22 @@ // DownloadFileFactory interface. DownloadFile* CreateFile( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_downloads_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, + base::File file, scoped_ptr<ByteStreamReader> stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) override { scoped_ptr<PowerSaveBlocker> psb(PowerSaveBlocker::Create( PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, PowerSaveBlocker::kReasonOther, "Download in progress")); - return new CountingDownloadFile( - std::move(save_info), default_downloads_directory, url, referrer_url, - calculate_hash, std::move(stream), bound_net_log, std::move(psb), - observer); + return new CountingDownloadFile(save_info, default_downloads_directory, url, + referrer_url, calculate_hash, + std::move(file), std::move(stream), + bound_net_log, std::move(psb), observer); } }; @@ -602,6 +612,12 @@ .WaitForEvent(); } + void WaitForCancel(DownloadItem* download) { + DownloadUpdatedObserver( + download, base::Bind(&IsDownloadInState, DownloadItem::CANCELLED)) + .WaitForEvent(); + } + // Note: Cannot be used with other alternative DownloadFileFactorys void SetupEnsureNoPendingDownloads() { DownloadManagerForShell(shell())->SetDownloadFileFactoryForTesting( @@ -1123,6 +1139,197 @@ value); } +// Resumption should only attempt to contact the final URL if the download has a +// URL chain. +IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RedirectBeforeResume) { + TestDownloadRequestHandler request_handler_1( + GURL("http://example.com/first-url")); + request_handler_1.StartServingStaticResponse( + "HTTP/1.1 302 Redirect\r\n" + "Location: http://example.com/second-url\r\n" + "\r\n"); + + TestDownloadRequestHandler request_handler_2( + GURL("http://example.com/second-url")); + request_handler_2.StartServingStaticResponse( + "HTTP/1.1 302 Redirect\r\n" + "Location: http://example.com/third-url\r\n" + "\r\n"); + + TestDownloadRequestHandler request_handler_3( + GURL("http://example.com/third-url")); + request_handler_3.StartServingStaticResponse( + "HTTP/1.1 302 Redirect\r\n" + "Location: http://example.com/download\r\n" + "\r\n"); + + TestDownloadRequestHandler resumable_request_handler( + GURL("http://example.com/download")); + TestDownloadRequestHandler::Parameters parameters = + TestDownloadRequestHandler::Parameters::WithSingleInterruption(); + resumable_request_handler.StartServing(parameters); + + DownloadItem* download = StartDownloadAndReturnItem( + initiator_shell_for_resumption(), request_handler_1.url()); + WaitForInterrupt(download); + + EXPECT_EQ(4u, download->GetUrlChain().size()); + EXPECT_EQ(request_handler_1.url(), download->GetOriginalUrl()); + EXPECT_EQ(resumable_request_handler.url(), download->GetURL()); + + // Now that the download is interrupted, make all intermediate servers return + // a 404. The only way a resumption request would succeed if the resumption + // request is sent to the final server in the chain. + const char k404Response[] = "HTTP/1.1 404 Not found\r\n\r\n"; + request_handler_1.StartServingStaticResponse(k404Response); + request_handler_2.StartServingStaticResponse(k404Response); + request_handler_3.StartServingStaticResponse(k404Response); + + PrepareToResume(); + download->Resume(); + WaitForCompletion(download); + + ASSERT_NO_FATAL_FAILURE(ReadAndVerifyFileContents( + parameters.pattern_generator_seed, parameters.size, + download->GetTargetFilePath())); +} + +// If a resumption request results in a redirect, the response should be ignored +// and the download should be marked as interrupted again. +IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, RedirectWhileResume) { + TestDownloadRequestHandler request_handler( + GURL("http://example.com/first-url")); + TestDownloadRequestHandler::Parameters parameters = + TestDownloadRequestHandler::Parameters::WithSingleInterruption(); + ++parameters.pattern_generator_seed; + request_handler.StartServing(parameters); + + // We should never send a request to the decoy. If we do, the request will + // always succeed, which results in behavior that diverges from what we want, + // which is for the download to return to being interrupted. + TestDownloadRequestHandler decoy_request_handler( + GURL("http://example.com/decoy")); + decoy_request_handler.StartServing(TestDownloadRequestHandler::Parameters()); + + DownloadItem* download = StartDownloadAndReturnItem( + initiator_shell_for_resumption(), request_handler.url()); + WaitForInterrupt(download); + + // Upon resumption, the server starts responding with a redirect. This + // response should not be accepted. + request_handler.StartServingStaticResponse( + "HTTP/1.1 302 Redirect\r\n" + "Location: http://example.com/decoy\r\n" + "\r\n"); + PrepareToResume(); + download->Resume(); + WaitForInterrupt(download); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE, + download->GetLastReason()); + + // Back to the original request handler. Resumption should now succeed, and + // use the partial data it had prior to the first interruption. + request_handler.StartServing(parameters); + download->Resume(); + WaitForCompletion(download); + + ASSERT_EQ(parameters.size, download->GetReceivedBytes()); + ASSERT_EQ(parameters.size, download->GetTotalBytes()); + ASSERT_NO_FATAL_FAILURE(ReadAndVerifyFileContents( + parameters.pattern_generator_seed, parameters.size, + download->GetTargetFilePath())); + + // Characterization risk: The next portion of the test examines the requests + // that were sent out while downloading our resource. These requests + // correspond to the requests that were generated by the browser and the + // downloads system and may change as implementation details change. + TestDownloadRequestHandler::CompletedRequests requests; + request_handler.GetCompletedRequestInfo(&requests); + + ASSERT_EQ(3u, requests.size()); + + // None of the request should have transferred the entire resource. The + // redirect response shows up as a response with 0 bytes transferred. + EXPECT_GT(parameters.size, requests[0].transferred_byte_count); + EXPECT_EQ(0, requests[1].transferred_byte_count); + EXPECT_GT(parameters.size, requests[2].transferred_byte_count); +} + +// If the server response for the resumption request specifies a bad range (i.e. +// not the range that was requested or an invalid or missing Content-Range +// header), then the download should be marked as interrupted again without +// discarding the partial state. +IN_PROC_BROWSER_TEST_P(DownloadResumptionContentTest, BadRangeHeader) { + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters = + TestDownloadRequestHandler::Parameters::WithSingleInterruption(); + request_handler.StartServing(parameters); + + DownloadItem* download = StartDownloadAndReturnItem( + initiator_shell_for_resumption(), request_handler.url()); + WaitForInterrupt(download); + + // Upon resumption, the server starts responding with a bad range header. + request_handler.StartServingStaticResponse( + "HTTP/1.1 206 Partial Content\r\n" + "Content-Range: bytes 1000000-2000000/3000000\r\n" + "\r\n"); + PrepareToResume(); + download->Resume(); + WaitForInterrupt(download); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + download->GetLastReason()); + + // Or this time, the server sends a response with an invalid Content-Range + // header. + request_handler.StartServingStaticResponse( + "HTTP/1.1 206 Partial Content\r\n" + "Content-Range: ooga-booga-booga-booga\r\n" + "\r\n"); + download->Resume(); + WaitForInterrupt(download); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + download->GetLastReason()); + + // Or no Content-Range header at all. + request_handler.StartServingStaticResponse( + "HTTP/1.1 206 Partial Content\r\n" + "Some-Headers: ooga-booga-booga-booga\r\n" + "\r\n"); + download->Resume(); + WaitForInterrupt(download); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + download->GetLastReason()); + + // Back to the original request handler. Resumption should now succeed, and + // use the partial data it had prior to the first interruption. + request_handler.StartServing(parameters); + download->Resume(); + WaitForCompletion(download); + + ASSERT_EQ(parameters.size, download->GetReceivedBytes()); + ASSERT_EQ(parameters.size, download->GetTotalBytes()); + ASSERT_NO_FATAL_FAILURE(ReadAndVerifyFileContents( + parameters.pattern_generator_seed, parameters.size, + download->GetTargetFilePath())); + + // Characterization risk: The next portion of the test examines the requests + // that were sent out while downloading our resource. These requests + // correspond to the requests that were generated by the browser and the + // downloads system and may change as implementation details change. + TestDownloadRequestHandler::CompletedRequests requests; + request_handler.GetCompletedRequestInfo(&requests); + + ASSERT_EQ(5u, requests.size()); + + // None of the request should have transferred the entire resource. + EXPECT_GT(parameters.size, requests[0].transferred_byte_count); + EXPECT_EQ(0, requests[1].transferred_byte_count); + EXPECT_EQ(0, requests[2].transferred_byte_count); + EXPECT_EQ(0, requests[3].transferred_byte_count); + EXPECT_GT(parameters.size, requests[4].transferred_byte_count); +} + // A partial resumption results in an HTTP 200 response. I.e. the server ignored // the range request and sent the entire resource instead. For If-Range requests // (as opposed to If-Match), the behavior for a precondition failure is also to @@ -1289,7 +1496,7 @@ download->GetLastReason()); EXPECT_EQ(0, download->GetReceivedBytes()); EXPECT_TRUE(download->GetFullPath().empty()); - EXPECT_TRUE(download->GetTargetFilePath().empty()); + EXPECT_FALSE(download->GetTargetFilePath().empty()); // We need to make sure that any cross-thread downloads communication has // quiesced before clearing and injecting the new errors, as the @@ -1834,6 +2041,69 @@ ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete()); } +// A request for a non-existent resource should still result in a DownloadItem +// that's created in an interrupted state. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeServerError) { + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL download_url = + embedded_test_server()->GetURL("/download/does-not-exist"); + GURL document_url = embedded_test_server()->GetURL( + std::string("/download/download-attribute.html?target=") + + download_url.spec()); + + DownloadItem* download = StartDownloadAndReturnItem(shell(), document_url); + WaitForInterrupt(download); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, + download->GetLastReason()); +} + +namespace { + +void ErrorReturningRequestHandler( + const net::HttpRequestHeaders& headers, + const TestDownloadRequestHandler::OnStartResponseCallback& callback) { + callback.Run(std::string(), net::ERR_INTERNET_DISCONNECTED); +} + +} // namespace + +// A request that fails before it gets a response from the server should also +// result in a DownloadItem that's created in an interrupted state. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeNetworkError) { + ASSERT_TRUE(embedded_test_server()->Start()); + TestDownloadRequestHandler request_handler; + TestDownloadRequestHandler::Parameters parameters; + + parameters.on_start_handler = base::Bind(&ErrorReturningRequestHandler); + request_handler.StartServing(parameters); + + GURL document_url = embedded_test_server()->GetURL( + std::string("/download/download-attribute.html?target=") + + request_handler.url().spec()); + DownloadItem* download = StartDownloadAndReturnItem(shell(), document_url); + WaitForInterrupt(download); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, + download->GetLastReason()); +} + +// A request that fails due to it being rejected by policy should result in a +// DownloadItem that's marked as interrupted. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeInvalidURL) { + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL document_url = embedded_test_server()->GetURL( + "/download/download-attribute.html?target=about:version"); + DownloadItem* download = StartDownloadAndReturnItem(shell(), document_url); + WaitForInterrupt(download); + + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, + download->GetLastReason()); + EXPECT_FALSE(download->CanResume()); +} + // The file empty.bin is served with a MIME type of application/octet-stream. // The content body is empty. Make sure this case is handled properly and we // don't regress on http://crbug.com/320394. @@ -1854,9 +2124,11 @@ EXPECT_TRUE(item->GetOriginalMimeType().empty()); } -IN_PROC_BROWSER_TEST_F(DownloadContentTest, Spam) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DuplicateContentDisposition) { ASSERT_TRUE(embedded_test_server()->Start()); + // double-content-disposition.txt is served with two Content-Disposition + // headers, both of which are identical. NavigateToURLAndWaitForDownload( shell(), embedded_test_server()->GetURL(
diff --git a/content/browser/download/download_create_info.cc b/content/browser/download/download_create_info.cc index c09d713..89aeb98 100644 --- a/content/browser/download/download_create_info.cc +++ b/content/browser/download/download_create_info.cc
@@ -5,7 +5,6 @@ #include "content/browser/download/download_create_info.h" #include <string> -#include <utility> #include "base/format_macros.h" #include "base/strings/stringprintf.h" @@ -13,20 +12,19 @@ namespace content { DownloadCreateInfo::DownloadCreateInfo(const base::Time& start_time, - int64_t total_bytes, const net::BoundNetLog& bound_net_log, scoped_ptr<DownloadSaveInfo> save_info) - : start_time(start_time), - total_bytes(total_bytes), - download_id(DownloadItem::kInvalidId), + : download_id(DownloadItem::kInvalidId), + start_time(start_time), + total_bytes(0), has_user_gesture(false), transition_type(ui::PAGE_TRANSITION_LINK), + result(DOWNLOAD_INTERRUPT_REASON_NONE), save_info(std::move(save_info)), request_bound_net_log(bound_net_log) {} DownloadCreateInfo::DownloadCreateInfo() : DownloadCreateInfo(base::Time(), - 0, net::BoundNetLog(), make_scoped_ptr(new DownloadSaveInfo)) {}
diff --git a/content/browser/download/download_create_info.h b/content/browser/download/download_create_info.h index 1767e45..becd8fd 100644 --- a/content/browser/download/download_create_info.h +++ b/content/browser/download/download_create_info.h
@@ -16,6 +16,7 @@ #include "content/browser/download/download_file.h" #include "content/browser/download/download_request_handle.h" #include "content/common/content_export.h" +#include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_save_info.h" #include "net/log/net_log.h" #include "ui/base/page_transition_types.h" @@ -27,7 +28,6 @@ // want to pass |DownloadItem|s between threads. struct CONTENT_EXPORT DownloadCreateInfo { DownloadCreateInfo(const base::Time& start_time, - int64_t total_bytes, const net::BoundNetLog& bound_net_log, scoped_ptr<DownloadSaveInfo> save_info); DownloadCreateInfo(); @@ -39,6 +39,9 @@ // redirection by the server for |url_chain|. const GURL& url() const; + // The ID of the download. + uint32_t download_id; + // The chain of redirects that leading up to and including the final URL. std::vector<GURL> url_chain; @@ -57,14 +60,35 @@ // The total download size. int64_t total_bytes; - // The ID of the download. - uint32_t download_id; - // True if the download was initiated by user action. bool has_user_gesture; ui::PageTransition transition_type; + // The remote IP address where the download was fetched from. Copied from + // UrlRequest::GetSocketAddress(). + std::string remote_address; + + // If the download is initially created in an interrupted state (because the + // response was in error), then |result| would be something other than + // INTERRUPT_REASON_NONE. + DownloadInterruptReason result; + + // The download file save info. + scoped_ptr<DownloadSaveInfo> save_info; + + // The handle to the URLRequest sourcing this download. + scoped_ptr<DownloadRequestHandleInterface> request_handle; + + // The request's |BoundNetLog|, for "source_dependency" linking with the + // download item's. + const net::BoundNetLog request_bound_net_log; + + // --------------------------------------------------------------------------- + // The remaining fields are Entity-body properties. These are only set if + // |result| is DOWNLOAD_INTERRUPT_REASON_NONE. + // --------------------------------------------------------------------------- + // The content-disposition string from the response header. std::string content_disposition; @@ -81,23 +105,9 @@ // "If-Unmodified-Since" comparison. std::string last_modified; - // For continuing a download, the ETAG of the file. + // For continuing a download, the ETag of the file. std::string etag; - // The download file save info. - scoped_ptr<DownloadSaveInfo> save_info; - - // The remote IP address where the download was fetched from. Copied from - // UrlRequest::GetSocketAddress(). - std::string remote_address; - - // The handle to the URLRequest sourcing this download. - scoped_ptr<DownloadRequestHandleInterface> request_handle; - - // The request's |BoundNetLog|, for "source_dependency" linking with the - // download item's. - const net::BoundNetLog request_bound_net_log; - private: DISALLOW_COPY_AND_ASSIGN(DownloadCreateInfo); };
diff --git a/content/browser/download/download_file_factory.cc b/content/browser/download/download_file_factory.cc index bae88bef..0b09c7b 100644 --- a/content/browser/download/download_file_factory.cc +++ b/content/browser/download/download_file_factory.cc
@@ -13,17 +13,18 @@ DownloadFileFactory::~DownloadFileFactory() {} DownloadFile* DownloadFileFactory::CreateFile( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_downloads_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, + base::File file, + scoped_ptr<ByteStreamReader> byte_stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) { - return new DownloadFileImpl(std::move(save_info), default_downloads_directory, - url, referrer_url, calculate_hash, - std::move(stream), bound_net_log, observer); + return new DownloadFileImpl(save_info, default_downloads_directory, url, + referrer_url, calculate_hash, std::move(file), + std::move(byte_stream), bound_net_log, observer); } } // namespace content
diff --git a/content/browser/download/download_file_factory.h b/content/browser/download/download_file_factory.h index 2600e3f8..bd083bd 100644 --- a/content/browser/download/download_file_factory.h +++ b/content/browser/download/download_file_factory.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_FACTORY_H_ #define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_FILE_FACTORY_H_ +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -29,12 +30,13 @@ virtual ~DownloadFileFactory(); virtual DownloadFile* CreateFile( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_downloads_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, + base::File file, + scoped_ptr<ByteStreamReader> byte_stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer); };
diff --git a/content/browser/download/download_file_impl.cc b/content/browser/download/download_file_impl.cc index e8e31cee..b8d87615 100644 --- a/content/browser/download/download_file_impl.cc +++ b/content/browser/download/download_file_impl.cc
@@ -35,21 +35,22 @@ int DownloadFile::number_active_objects_ = 0; DownloadFileImpl::DownloadFileImpl( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_download_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, + base::File file, scoped_ptr<ByteStreamReader> stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) - : file_(save_info->file_path, + : file_(save_info.file_path, url, referrer_url, - save_info->offset, + save_info.offset, calculate_hash, - save_info->hash_state, - std::move(save_info->file), + save_info.hash_state, + std::move(file), bound_net_log), default_download_directory_(default_download_directory), stream_reader_(std::move(stream)),
diff --git a/content/browser/download/download_file_impl.h b/content/browser/download/download_file_impl.h index 9838112..3103dd4 100644 --- a/content/browser/download/download_file_impl.h +++ b/content/browser/download/download_file_impl.h
@@ -10,6 +10,7 @@ #include <stddef.h> #include <stdint.h> +#include "base/files/file.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -38,15 +39,15 @@ // Note that the DownloadFileImpl automatically reads from the passed in // stream, and sends updates and status of those reads to the // DownloadDestinationObserver. - DownloadFileImpl( - scoped_ptr<DownloadSaveInfo> save_info, - const base::FilePath& default_downloads_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, - const net::BoundNetLog& bound_net_log, - base::WeakPtr<DownloadDestinationObserver> observer); + DownloadFileImpl(const DownloadSaveInfo& save_info, + const base::FilePath& default_downloads_directory, + const GURL& url, + const GURL& referrer_url, + bool calculate_hash, + base::File file, + scoped_ptr<ByteStreamReader> byte_stream, + const net::BoundNetLog& bound_net_log, + base::WeakPtr<DownloadDestinationObserver> observer); ~DownloadFileImpl() override;
diff --git a/content/browser/download/download_file_unittest.cc b/content/browser/download/download_file_unittest.cc index 9630a49..245fdcd 100644 --- a/content/browser/download/download_file_unittest.cc +++ b/content/browser/download/download_file_unittest.cc
@@ -75,19 +75,21 @@ // retries renames failed due to ACCESS_DENIED. class TestDownloadFileImpl : public DownloadFileImpl { public: - TestDownloadFileImpl(scoped_ptr<DownloadSaveInfo> save_info, + TestDownloadFileImpl(const DownloadSaveInfo& save_info, const base::FilePath& default_downloads_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, + base::File file, scoped_ptr<ByteStreamReader> stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) - : DownloadFileImpl(std::move(save_info), + : DownloadFileImpl(save_info, default_downloads_directory, url, referrer_url, calculate_hash, + std::move(file), std::move(stream), bound_net_log, observer) {} @@ -176,15 +178,14 @@ .RetiresOnSaturation(); scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); - scoped_ptr<TestDownloadFileImpl> download_file_impl( - new TestDownloadFileImpl( - std::move(save_info), base::FilePath(), - GURL(), // Source - GURL(), // Referrer - calculate_hash, scoped_ptr<ByteStreamReader>(input_stream_), - net::BoundNetLog(), observer_factory_.GetWeakPtr())); - download_file_impl->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC"); - download_file_ = std::move(download_file_impl); + download_file_.reset(new TestDownloadFileImpl( + *save_info, base::FilePath(), + GURL(), // Source + GURL(), // Referrer + calculate_hash, std::move(save_info->file), + scoped_ptr<ByteStreamReader>(input_stream_), net::BoundNetLog(), + observer_factory_.GetWeakPtr())); + download_file_->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC"); EXPECT_CALL(*input_stream_, Read(_, _)) .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc index 028e6d5..35ef960 100644 --- a/content/browser/download/download_item_impl.cc +++ b/content/browser/download/download_item_impl.cc
@@ -192,7 +192,7 @@ bytes_per_sec_(0), last_modified_time_(info.last_modified), etag_(info.etag), - last_reason_(DOWNLOAD_INTERRUPT_REASON_NONE), + last_reason_(info.result), start_tick_(base::TimeTicks::Now()), state_(INITIAL_INTERNAL), danger_type_(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS), @@ -205,7 +205,7 @@ auto_opened_(false), is_temporary_(!info.save_info->file_path.empty()), all_data_saved_(false), - destination_error_(content::DOWNLOAD_INTERRUPT_REASON_NONE), + destination_error_(DOWNLOAD_INTERRUPT_REASON_NONE), opened_(false), delegate_delayed_complete_(false), bound_net_log_(bound_net_log), @@ -295,6 +295,7 @@ void DownloadItemImpl::UpdateObservers() { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DVLOG(20) << __FUNCTION__ << "()"; FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); } @@ -318,7 +319,9 @@ net::NetLog::TYPE_DOWNLOAD_ITEM_SAFETY_STATE_UPDATED, base::Bind(&ItemCheckedNetLogCallback, GetDangerType())); - UpdateObservers(); + UpdateObservers(); // TODO(asanka): This is potentially unsafe. The download + // may not be in a consistent state or around at all after + // invoking observers. http://crbug.com/586610 MaybeCompleteDownload(); } @@ -351,17 +354,22 @@ return; switch (state_) { - case INITIAL_INTERNAL: - case COMPLETING_INTERNAL: - case COMPLETE_INTERNAL: - case INTERRUPTED_INTERNAL: case CANCELLED_INTERNAL: + case COMPLETE_INTERNAL: + case COMPLETING_INTERNAL: + case INITIAL_INTERNAL: + case INTERRUPTED_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: case RESUMING_INTERNAL: // No active request. + // TODO(asanka): In the case of RESUMING_INTERNAL, consider setting + // is_paused_ even if there's no request currently associated with this + // DII. When a request is assigned (due to a resumption, for example) we + // can honor the is_paused_ setting. return; - case TARGET_PENDING_INTERNAL: case IN_PROGRESS_INTERNAL: + case TARGET_PENDING_INTERNAL: request_handle_->PauseRequest(); is_paused_ = true; UpdateObservers(); @@ -375,15 +383,14 @@ void DownloadItemImpl::Resume() { DCHECK_CURRENTLY_ON(BrowserThread::UI); + DVLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); switch (state_) { - case INITIAL_INTERNAL: - case COMPLETING_INTERNAL: + case CANCELLED_INTERNAL: // Nothing to resume. case COMPLETE_INTERNAL: - case CANCELLED_INTERNAL: - // Nothing to resume. - case RESUMING_INTERNAL: - // Resumption in progress. - DCHECK(!is_paused_); + case COMPLETING_INTERNAL: + case INITIAL_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: + case RESUMING_INTERNAL: // Resumption in progress. return; case TARGET_PENDING_INTERNAL: @@ -398,6 +405,7 @@ case INTERRUPTED_INTERNAL: auto_resume_count_ = 0; // User input resets the counter. ResumeInterruptedDownload(); + UpdateObservers(); return; case MAX_DOWNLOAD_INTERNAL_STATE: @@ -411,6 +419,7 @@ DVLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); Interrupt(user_cancel ? DOWNLOAD_INTERRUPT_REASON_USER_CANCELED : DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN); + UpdateObservers(); } void DownloadItemImpl::Remove() { @@ -419,6 +428,7 @@ delegate_->AssertStateConsistent(this); Interrupt(DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); + UpdateObservers(); delegate_->AssertStateConsistent(this); NotifyRemoved(); @@ -485,6 +495,7 @@ case COMPLETE_INTERNAL: case CANCELLED_INTERNAL: case RESUMING_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: return false; case TARGET_PENDING_INTERNAL: @@ -513,6 +524,7 @@ case COMPLETING_INTERNAL: case RESUMING_INTERNAL: case TARGET_PENDING_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: case TARGET_RESOLVED_INTERNAL: case IN_PROGRESS_INTERNAL: return false; @@ -903,8 +915,8 @@ case DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED: case DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED: case DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN: - case DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST: case DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED: + case DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE: case DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN: case DOWNLOAD_INTERRUPT_REASON_CRASH: if (force_restart) @@ -922,6 +934,7 @@ break; case DOWNLOAD_INTERRUPT_REASON_NONE: + case DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST: case DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED: case DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT: case DOWNLOAD_INTERRUPT_REASON_USER_CANCELED: @@ -937,7 +950,7 @@ return mode; } -void DownloadItemImpl::MergeOriginInfoOnResume( +void DownloadItemImpl::UpdateValidatorsOnResumption( const DownloadCreateInfo& new_create_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(RESUMING_INTERNAL, state_); @@ -1022,7 +1035,8 @@ DCHECK(all_data_saved_); end_time_ = base::Time::Now(); - TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS); + TransitionTo(COMPLETE_INTERNAL); + UpdateObservers(); } void DownloadItemImpl::DestinationUpdate(int64_t bytes_so_far, @@ -1071,10 +1085,12 @@ // Postpone recognition of this error until after file name determination // has completed and the intermediate file has been renamed to simplify // resumption conditions. - if (state_ != IN_PROGRESS_INTERNAL) + if (state_ == TARGET_PENDING_INTERNAL) { destination_error_ = reason; - else - Interrupt(reason); + return; + } + Interrupt(reason); + UpdateObservers(); } void DownloadItemImpl::DestinationCompleted(const std::string& final_hash) { @@ -1131,25 +1147,65 @@ // We're starting the download. void DownloadItemImpl::Start( scoped_ptr<DownloadFile> file, - scoped_ptr<DownloadRequestHandleInterface> req_handle) { + scoped_ptr<DownloadRequestHandleInterface> req_handle, + const DownloadCreateInfo& new_create_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!download_file_.get()); - DCHECK(file.get()); - DCHECK(req_handle.get()); DVLOG(20) << __FUNCTION__ << "() this=" << DebugString(true); download_file_ = std::move(file); request_handle_ = std::move(req_handle); + destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; if (state_ == CANCELLED_INTERNAL) { // The download was in the process of resuming when it was cancelled. Don't // proceed. ReleaseDownloadFile(true); - request_handle_->CancelRequest(); + if (request_handle_) + request_handle_->CancelRequest(); return; } - TransitionTo(TARGET_PENDING_INTERNAL, UPDATE_OBSERVERS); + // The state could be one of the following: + // + // INITIAL_INTERNAL: A normal download attempt. + // + // RESUMING_INTERNAL: A resumption attempt. May or may not have been + // successful. + DCHECK(state_ == INITIAL_INTERNAL || state_ == RESUMING_INTERNAL); + + // If the state_ is INITIAL_INTERNAL, then the target path must be empty. + DCHECK(state_ != INITIAL_INTERNAL || target_path_.empty()); + + // If a resumption attempted failed, or if the download was DOA, then the + // download should go back to being interrupted. + if (new_create_info.result != DOWNLOAD_INTERRUPT_REASON_NONE) { + DCHECK(!download_file_.get()); + + // Interrupted downloads also need a target path. + if (target_path_.empty()) { + TransitionTo(INTERRUPTED_TARGET_PENDING_INTERNAL); + destination_error_ = new_create_info.result; + DetermineDownloadTarget(); + return; + } + + // Otherwise, this was a resumption attempt which ended with an + // interruption. Continue with current target path. + TransitionTo(TARGET_RESOLVED_INTERNAL); + Interrupt(new_create_info.result); + UpdateObservers(); + return; + } + + // Successful download start. + DCHECK(download_file_.get()); + DCHECK(request_handle_.get()); + + if (state_ == RESUMING_INTERNAL) + UpdateValidatorsOnResumption(new_create_info); + + TransitionTo(TARGET_PENDING_INTERNAL); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, @@ -1167,35 +1223,32 @@ DVLOG(20) << __FUNCTION__ << "() result:" << DownloadInterruptReasonToString(result); if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { - // Transition out to TARGET_RESOLVED_INTERNAL since this DownloadItem is - // skipping the download target determination process. - TransitionTo(TARGET_RESOLVED_INTERNAL, DONT_UPDATE_OBSERVERS); - Interrupt(result); - // TODO(rdsmith/asanka): Arguably we should show this in the UI, but - // it's not at all clear what to show--we haven't done filename - // determination, so we don't know what name to display. OTOH, - // the failure mode of not showing the DI if the file initialization - // fails isn't a good one. Can we hack up a name based on the - // URLRequest? We'll need to make sure that initialization happens - // properly. Possibly the right thing is to have the UI handle - // this case specially. - return; + // Whoops. That didn't work. Proceed as an interrupted download. + destination_error_ = result; + TransitionTo(INTERRUPTED_TARGET_PENDING_INTERNAL); } + DetermineDownloadTarget(); +} + +void DownloadItemImpl::DetermineDownloadTarget() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DVLOG(20) << __FUNCTION__ << "() " << DebugString(true); + delegate_->DetermineDownloadTarget( this, base::Bind(&DownloadItemImpl::OnDownloadTargetDetermined, weak_ptr_factory_.GetWeakPtr())); } -// Called by delegate_ when the download target path has been -// determined. +// Called by delegate_ when the download target path has been determined. void DownloadItemImpl::OnDownloadTargetDetermined( const base::FilePath& target_path, TargetDisposition disposition, DownloadDangerType danger_type, const base::FilePath& intermediate_path) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK_EQ(state_, TARGET_PENDING_INTERNAL); + DCHECK(state_ == TARGET_PENDING_INTERNAL || + state_ == INTERRUPTED_TARGET_PENDING_INTERNAL); // If the |target_path| is empty, then we consider this download to be // canceled. @@ -1204,21 +1257,23 @@ return; } - // TODO(rdsmith,asanka): We are ignoring the possibility that the download - // has been interrupted at this point until we finish the intermediate - // rename and set the full path. That's dangerous, because we might race - // with resumption, either manual (because the interrupt is visible to the - // UI) or automatic. If we keep the "ignore an error on download until file - // name determination complete" semantics, we need to make sure that the - // error is kept completely invisible until that point. - - DVLOG(20) << __FUNCTION__ << " " << target_path.value() << " " << disposition - << " " << danger_type << " " << DebugString(true); + DVLOG(20) << __FUNCTION__ << "() target_path:" << target_path.value() + << " disposition:" << disposition << " danger_type:" << danger_type + << " this:" << DebugString(true); target_path_ = target_path; target_disposition_ = disposition; SetDangerType(danger_type); + // This was an interrupted download that was looking for a filename. Now that + // it has one, transition to interrupted. + if (state_ == INTERRUPTED_TARGET_PENDING_INTERNAL) { + Interrupt(destination_error_); + destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; + UpdateObservers(); + return; + } + // We want the intermediate and target paths to refer to the same directory so // that they are both on the same device and subject to same // space/permission/availability constraints. @@ -1261,7 +1316,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(state_, TARGET_PENDING_INTERNAL); DVLOG(20) << __FUNCTION__ << " download=" << DebugString(true); - TransitionTo(TARGET_RESOLVED_INTERNAL, DONT_UPDATE_OBSERVERS); + TransitionTo(TARGET_RESOLVED_INTERNAL); if (DOWNLOAD_INTERRUPT_REASON_NONE != destination_error_) { // Process destination error. If both |reason| and |destination_error_| @@ -1271,15 +1326,21 @@ SetFullPath(full_path); Interrupt(destination_error_); destination_error_ = DOWNLOAD_INTERRUPT_REASON_NONE; + UpdateObservers(); } else if (DOWNLOAD_INTERRUPT_REASON_NONE != reason) { Interrupt(reason); // All file errors result in file deletion above; no need to cleanup. The // current_path_ should be empty. Resuming this download will force a // restart and a re-doing of filename determination. DCHECK(current_path_.empty()); + UpdateObservers(); } else { SetFullPath(full_path); - TransitionTo(IN_PROGRESS_INTERNAL, UPDATE_OBSERVERS); + TransitionTo(IN_PROGRESS_INTERNAL); + // TODO(asanka): Calling UpdateObservers() prior to MaybeCompleteDownload() + // is not safe. The download could be in an underminate state after invoking + // observers. http://crbug.com/586610 + UpdateObservers(); MaybeCompleteDownload(); } } @@ -1327,9 +1388,8 @@ // TODO(rdsmith/benjhayden): Remove as part of SavePackage integration. if (is_save_package_download_) { // Avoid doing anything on the file thread; there's nothing we control - // there. - // Strictly speaking, this skips giving the embedder a chance to open - // the download. But on a save package download, there's no real + // there. Strictly speaking, this skips giving the embedder a chance to + // open the download. But on a save package download, there's no real // concept of opening. Completed(); return; @@ -1370,6 +1430,7 @@ // All file errors should have resulted in in file deletion above. On // resumption we will need to re-do filename determination. DCHECK(current_path_.empty()); + UpdateObservers(); return; } @@ -1389,7 +1450,7 @@ // point we're committed to complete the download. Cancels (or Interrupts, // though it's not clear how they could happen) after this point will be // ignored. - TransitionTo(COMPLETING_INTERNAL, DONT_UPDATE_OBSERVERS); + TransitionTo(COMPLETING_INTERNAL); if (delegate_->ShouldOpenDownload( this, base::Bind(&DownloadItemImpl::DelayedDownloadOpened, @@ -1415,7 +1476,7 @@ DCHECK(all_data_saved_); end_time_ = base::Time::Now(); - TransitionTo(COMPLETE_INTERNAL, UPDATE_OBSERVERS); + TransitionTo(COMPLETE_INTERNAL); RecordDownloadCompleted(start_tick_, received_bytes_); if (auto_opened_) { @@ -1430,24 +1491,8 @@ OpenDownload(); auto_opened_ = true; - UpdateObservers(); } -} - -void DownloadItemImpl::OnResumeRequestStarted( - DownloadItem* item, - DownloadInterruptReason interrupt_reason) { - // If |item| is not NULL, then Start() has been called already, and nothing - // more needs to be done here. - if (item) { - DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); - DCHECK_EQ(static_cast<DownloadItem*>(this), item); - return; - } - // Otherwise, the request failed without passing through - // DownloadResourceHandler::OnResponseStarted. - DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); - Interrupt(interrupt_reason); + UpdateObservers(); } // **** End of Download progression cascade @@ -1482,9 +1527,10 @@ NOTREACHED(); return; + case INTERRUPTED_TARGET_PENDING_INTERNAL: + case IN_PROGRESS_INTERNAL: case TARGET_PENDING_INTERNAL: case TARGET_RESOLVED_INTERNAL: - case IN_PROGRESS_INTERNAL: // last_reason_ needs to be set for GetResumeMode() to work. last_reason_ = reason; @@ -1542,16 +1588,16 @@ } RecordDownloadCount(CANCELLED_COUNT); - TransitionTo(CANCELLED_INTERNAL, DONT_UPDATE_OBSERVERS); - } else { - RecordDownloadInterrupted(reason, received_bytes_, total_bytes_); - if (!GetWebContents()) - RecordDownloadCount(INTERRUPTED_WITHOUT_WEBCONTENTS); - TransitionTo(INTERRUPTED_INTERNAL, DONT_UPDATE_OBSERVERS); - AutoResumeIfValid(); + TransitionTo(CANCELLED_INTERNAL); + return; } - UpdateObservers(); + RecordDownloadInterrupted(reason, received_bytes_, total_bytes_); + if (!GetWebContents()) + RecordDownloadCount(INTERRUPTED_WITHOUT_WEBCONTENTS); + + TransitionTo(INTERRUPTED_INTERNAL); + AutoResumeIfValid(); } void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) { @@ -1597,8 +1643,10 @@ if (IsDangerous()) return false; - // Invariants for the IN_PROGRESS state. DCHECKs here verify that the - // invariants are still true. + // Check for consistency before invoking delegate. Since there are no pending + // target determination calls and the download is in progress, both the target + // and current paths should be non-empty and they should point to the same + // directory. DCHECK(!target_path_.empty()); DCHECK(!current_path_.empty()); DCHECK(target_path_.DirName() == current_path_.DirName()); @@ -1611,8 +1659,7 @@ return true; } -void DownloadItemImpl::TransitionTo(DownloadInternalState new_state, - ShouldUpdateObservers notify_action) { +void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (state_ == new_state) @@ -1634,6 +1681,7 @@ case TARGET_PENDING_INTERNAL: case TARGET_RESOLVED_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: break; case IN_PROGRESS_INTERNAL: @@ -1715,9 +1763,6 @@ this, SRC_ACTIVE_DOWNLOAD, &file_name)); } - - if (notify_action == UPDATE_OBSERVERS) - UpdateObservers(); } void DownloadItemImpl::SetDangerType(DownloadDangerType danger_type) { @@ -1779,6 +1824,10 @@ if (state_ != INTERRUPTED_INTERNAL) return; + // We are starting a new request. Shake off all pending operations. + DCHECK(!download_file_); + weak_ptr_factory_.InvalidateWeakPtrs(); + // Reset the appropriate state if restarting. ResumeMode mode = GetResumeMode(); if (mode == RESUME_MODE_IMMEDIATE_RESTART || @@ -1789,25 +1838,19 @@ etag_ = ""; } - scoped_ptr<DownloadUrlParameters> download_params; - if (GetWebContents()) { - download_params = - DownloadUrlParameters::FromWebContents(GetWebContents(), GetURL()); - } else { - download_params = make_scoped_ptr(new DownloadUrlParameters( - GetURL(), -1, -1, -1, GetBrowserContext()->GetResourceContext())); - } - + // Avoid using the WebContents even if it's still around. Resumption requests + // are consistently routed through the no-renderer code paths so that the + // request will not be dropped if the WebContents (and by extension, the + // associated renderer) goes away before a response is received. + scoped_ptr<DownloadUrlParameters> download_params(new DownloadUrlParameters( + GetURL(), -1, -1, -1, GetBrowserContext()->GetResourceContext())); download_params->set_file_path(GetFullPath()); download_params->set_offset(GetReceivedBytes()); download_params->set_hash_state(GetHashState()); download_params->set_last_modified(GetLastModifiedTime()); download_params->set_etag(GetETag()); - download_params->set_callback( - base::Bind(&DownloadItemImpl::OnResumeRequestStarted, - weak_ptr_factory_.GetWeakPtr())); - TransitionTo(RESUMING_INTERNAL, DONT_UPDATE_OBSERVERS); + TransitionTo(RESUMING_INTERNAL); delegate_->ResumeInterruptedDownload(std::move(download_params), GetId()); // Just in case we were interrupted while paused. is_paused_ = false; @@ -1820,6 +1863,7 @@ case INITIAL_INTERNAL: case TARGET_PENDING_INTERNAL: case TARGET_RESOLVED_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: // TODO(asanka): Introduce an externally visible state to distinguish // between the above states and IN_PROGRESS_INTERNAL. The latter (the // state where the download is active and has a known target) is the state @@ -1870,6 +1914,7 @@ switch (from) { case INITIAL_INTERNAL: case TARGET_PENDING_INTERNAL: + case INTERRUPTED_TARGET_PENDING_INTERNAL: case TARGET_RESOLVED_INTERNAL: case COMPLETING_INTERNAL: case COMPLETE_INTERNAL: @@ -1896,10 +1941,15 @@ #if DCHECK_IS_ON() switch (from) { case INITIAL_INTERNAL: - return to == TARGET_PENDING_INTERNAL || to == INTERRUPTED_INTERNAL; + return to == TARGET_PENDING_INTERNAL || + to == INTERRUPTED_TARGET_PENDING_INTERNAL; case TARGET_PENDING_INTERNAL: - return to == TARGET_RESOLVED_INTERNAL || to == CANCELLED_INTERNAL; + return to == INTERRUPTED_TARGET_PENDING_INTERNAL || + to == TARGET_RESOLVED_INTERNAL || to == CANCELLED_INTERNAL; + + case INTERRUPTED_TARGET_PENDING_INTERNAL: + return to == INTERRUPTED_INTERNAL || to == CANCELLED_INTERNAL; case TARGET_RESOLVED_INTERNAL: return to == IN_PROGRESS_INTERNAL || to == INTERRUPTED_INTERNAL || @@ -1919,7 +1969,9 @@ return to == RESUMING_INTERNAL || to == CANCELLED_INTERNAL; case RESUMING_INTERNAL: - return to == TARGET_PENDING_INTERNAL || to == CANCELLED_INTERNAL; + return to == TARGET_PENDING_INTERNAL || + to == INTERRUPTED_TARGET_PENDING_INTERNAL || + to == TARGET_RESOLVED_INTERNAL || to == CANCELLED_INTERNAL; case CANCELLED_INTERNAL: return false; @@ -1940,6 +1992,8 @@ return "INITIAL"; case TARGET_PENDING_INTERNAL: return "TARGET_PENDING"; + case INTERRUPTED_TARGET_PENDING_INTERNAL: + return "INTERRUPTED_TARGET_PENDING"; case TARGET_RESOLVED_INTERNAL: return "TARGET_RESOLVED"; case IN_PROGRESS_INTERNAL:
diff --git a/content/browser/download/download_item_impl.h b/content/browser/download/download_item_impl.h index 8b6afdc..053d441 100644 --- a/content/browser/download/download_item_impl.h +++ b/content/browser/download/download_item_impl.h
@@ -73,6 +73,8 @@ // Constructing for a regular download. // |bound_net_log| is constructed externally for our use. + // TODO(asanka): Get rid of the DownloadCreateInfo parameter since active + // downloads end up at Start() immediately after creation. DownloadItemImpl(DownloadItemImplDelegate* delegate, uint32_t id, const DownloadCreateInfo& info, @@ -167,18 +169,18 @@ // INTERRUPTED state. virtual ResumeMode GetResumeMode() const; - // Notify the download item that new origin information is available due to a - // resumption request receiving a response. - virtual void MergeOriginInfoOnResume( - const DownloadCreateInfo& new_create_info); - // State transition operations on regular downloads -------------------------- // Start the download. // |download_file| is the associated file on the storage medium. // |req_handle| is the new request handle associated with the download. + // |new_create_info| is a DownloadCreateInfo containing the new response + // parameters. It may be different from the DownloadCreateInfo used to create + // the DownloadItem if Start() is being called in response for a download + // resumption request. virtual void Start(scoped_ptr<DownloadFile> download_file, - scoped_ptr<DownloadRequestHandleInterface> req_handle); + scoped_ptr<DownloadRequestHandleInterface> req_handle, + const DownloadCreateInfo& new_create_info); // Needed because of intertwining with DownloadManagerImpl ------------------- @@ -239,7 +241,7 @@ // // Transitions to (regular): // TARGET_PENDING_INTERNAL: After a successful Start() call. - // INTERRUPTED_INTERNAL: Afater a failed Start() call. + // INTERRUPTED_TARGET_PENDING_INTERNAL: After a failed Start() call. // // Transitions to (SavePackage): // <n/a> SavePackage downloads never reach this state. @@ -252,12 +254,27 @@ // // Transitions to (regular): // TARGET_RESOLVED_INTERNAL: Once the embedder invokes the callback. + // INTERRUPTED_TARGET_PENDING_INTERNAL: An error occurred prior to target + // determination. // CANCELLED_INTERNAL: Cancelled. // // Transitions to (SavePackage): // <n/a> SavePackage downloads never reach this state. TARGET_PENDING_INTERNAL, + // Embedder is in the process of determining the target of the download, and + // the download is in an interrupted state. The interrupted state is not + // exposed to the emedder until target determination is complete. + // + // Transitions to (regular): + // INTERRUPTED_INTERNAL: Once the target is determined, the download + // is marked as interrupted. + // CANCELLED_INTERNAL: Cancelled. + // + // Transitions to (SavePackage): + // <n/a> SavePackage downloads never reach this state. + INTERRUPTED_TARGET_PENDING_INTERNAL, + // Embedder has completed target determination. It is now safe to resolve // the download target as well as process deferred DestinationError events. // This state is differs from TARGET_PENDING_INTERNAL due to it being @@ -328,6 +345,12 @@ // Transitions to (regular): // TARGET_PENDING_INTERNAL: Once a server response is received from a // resumption. + // INTERRUPTED_TARGET_PENDING_INTERNAL: A server response was received, + // but it indicated an error, and the download + // needs to go through target determination. + // TARGET_RESOLVED_INTERNAL: A resumption attempt received an error + // but it was not necessary to go through target + // determination. // CANCELLED_INTERNAL: On cancel. // // Transitions to (SavePackage): @@ -347,13 +370,6 @@ MAX_DOWNLOAD_INTERNAL_STATE, }; - // Used with TransitionTo() to indicate whether or not to call - // UpdateObservers() after the state transition. - enum ShouldUpdateObservers { - UPDATE_OBSERVERS, - DONT_UPDATE_OBSERVERS - }; - // Normal progression of a download ------------------------------------------ // These are listed in approximately chronological order. There are also @@ -366,6 +382,13 @@ // this is. void Init(bool active, DownloadType download_type); + // Callback from file thread when we initialize the DownloadFile. + void OnDownloadFileInitialized(DownloadInterruptReason result); + + // Called to determine the target path. Will cause OnDownloadTargetDetermined + // to be called when the target information is available. + void DetermineDownloadTarget(); + // Called when the target path has been determined. |target_path| is the // suggested target path. |disposition| indicates how the target path should // be used (see TargetDisposition). |danger_type| is the danger level of @@ -377,9 +400,6 @@ DownloadDangerType danger_type, const base::FilePath& intermediate_path); - // Callback from file thread when we initialize the DownloadFile. - void OnDownloadFileInitialized(DownloadInterruptReason result); - void OnDownloadRenamedToIntermediateName( DownloadInterruptReason reason, const base::FilePath& full_path); @@ -404,10 +424,6 @@ // is completed. void Completed(); - // Callback invoked when the URLRequest for a download resumption has started. - void OnResumeRequestStarted(DownloadItem* item, - DownloadInterruptReason interrupt_reason); - // Helper routines ----------------------------------------------------------- // Indicate that an error has occurred on the download. @@ -427,8 +443,7 @@ // Call to transition state; all state transitions should go through this. // |notify_action| specifies whether or not to call UpdateObservers() after // the state transition. - void TransitionTo(DownloadInternalState new_state, - ShouldUpdateObservers notify_action); + void TransitionTo(DownloadInternalState new_state); // Set the |danger_type_| and invoke observers if necessary. void SetDangerType(DownloadDangerType danger_type); @@ -439,6 +454,11 @@ void ResumeInterruptedDownload(); + // Update origin information based on the response to a download resumption + // request. Should only be called if the resumption request was successful. + virtual void UpdateValidatorsOnResumption( + const DownloadCreateInfo& new_create_info); + static DownloadState InternalToExternalState( DownloadInternalState internal_state); static DownloadInternalState ExternalToInternalState(
diff --git a/content/browser/download/download_item_impl_unittest.cc b/content/browser/download/download_item_impl_unittest.cc index 4af5406..c5b1ed2 100644 --- a/content/browser/download/download_item_impl_unittest.cc +++ b/content/browser/download/download_item_impl_unittest.cc
@@ -28,8 +28,11 @@ #include "content/public/browser/download_url_parameters.h" #include "content/public/common/content_features.h" #include "content/public/test/mock_download_item.h" +#include "content/public/test/mock_download_item.h" +#include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/web_contents_tester.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -76,7 +79,6 @@ void(DownloadUrlParameters* params, uint32_t id)); MOCK_CONST_METHOD0(GetBrowserContext, BrowserContext*()); - MOCK_METHOD1(UpdatePersistence, void(DownloadItemImpl*)); MOCK_METHOD1(DownloadOpened, void(DownloadItemImpl*)); MOCK_METHOD1(DownloadRemoved, void(DownloadItemImpl*)); MOCK_CONST_METHOD1(AssertStateConsistent, void(DownloadItemImpl*)); @@ -138,8 +140,8 @@ private: void OnDownloadRemoved(DownloadItem* download) override { - DVLOG(20) << " " << __FUNCTION__ - << " download = " << download->DebugString(false); + SCOPED_TRACE(::testing::Message() << " " << __FUNCTION__ << " download = " + << download->DebugString(false)); removed_ = true; } @@ -213,12 +215,20 @@ class DownloadItemTest : public testing::Test { public: - DownloadItemTest() { + DownloadItemTest() + : delegate_(), next_download_id_(DownloadItem::kInvalidId + 1) { base::FeatureList::ClearInstanceForTesting(); scoped_ptr<base::FeatureList> feature_list(new base::FeatureList); feature_list->InitializeFromCommandLine(features::kDownloadResumption.name, std::string()); base::FeatureList::SetInstance(std::move(feature_list)); + + create_info_.reset(new DownloadCreateInfo()); + create_info_->save_info = + scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()); + create_info_->save_info->prompt_for_save_location = false; + create_info_->url_chain.push_back(GURL("http://example.com/download")); + create_info_->etag = "SomethingToSatisfyResumption"; } ~DownloadItemTest() { @@ -238,36 +248,42 @@ // be torn down at the end of the test unless DestroyDownloadItem is // called. DownloadItemImpl* CreateDownloadItem() { - scoped_ptr<DownloadCreateInfo> info; - - info.reset(new DownloadCreateInfo()); - info->save_info = scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()); - info->save_info->prompt_for_save_location = false; - info->url_chain.push_back(GURL("http://example.com/download")); - info->etag = "SomethingToSatisfyResumption"; - - return CreateDownloadItemWithCreateInfo(std::move(info)); + create_info_->download_id = ++next_download_id_; + DownloadItemImpl* download = + new DownloadItemImpl(&delegate_, create_info_->download_id, + *create_info_, net::BoundNetLog()); + allocated_downloads_.insert(download); + return download; } - // Add DownloadFile to DownloadItem + // Add DownloadFile to DownloadItem. Set |callback| to nullptr if a download + // target determination is not expected. MockDownloadFile* CallDownloadItemStart( DownloadItemImpl* item, DownloadItemImplDelegate::DownloadTargetCallback* callback) { - MockDownloadFile* mock_download_file(new StrictMock<MockDownloadFile>); - scoped_ptr<DownloadFile> download_file(mock_download_file); - EXPECT_CALL(*mock_download_file, Initialize(_)); + MockDownloadFile* mock_download_file = nullptr; + scoped_ptr<DownloadFile> download_file; if (callback) { - // Save the callback. EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)) .WillOnce(SaveArg<1>(callback)); } else { - // Drop it on the floor. - EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)); + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)).Times(0); } - scoped_ptr<DownloadRequestHandleInterface> request_handle( + // Only create a DownloadFile if the request was successful. + if (create_info_->result == DOWNLOAD_INTERRUPT_REASON_NONE) { + mock_download_file = new StrictMock<MockDownloadFile>; + download_file.reset(mock_download_file); + EXPECT_CALL(*mock_download_file, Initialize(_)) + .WillOnce(ScheduleCallbackWithParam(DOWNLOAD_INTERRUPT_REASON_NONE)); + EXPECT_CALL(*mock_download_file, FullPath()) + .WillRepeatedly(Return(base::FilePath())); + } + + scoped_ptr<MockRequestHandle> request_handle( new NiceMock<MockRequestHandle>); - item->Start(std::move(download_file), std::move(request_handle)); + item->Start(std::move(download_file), std::move(request_handle), + *create_info_); RunAllPendingInMessageLoops(); // So that we don't have a function writing to a stack variable @@ -351,10 +367,16 @@ *return_path = path; } + DownloadCreateInfo* create_info() { return create_info_.get(); } + + BrowserContext* browser_context() { return &browser_context_; } + private: - int next_download_id_ = DownloadItem::kInvalidId + 1; StrictMock<MockDelegate> delegate_; std::set<DownloadItem*> allocated_downloads_; + scoped_ptr<DownloadCreateInfo> create_info_; + uint32_t next_download_id_ = DownloadItem::kInvalidId + 1; + TestBrowserContext browser_context_; TestBrowserThreadBundle thread_bundle_; }; @@ -381,18 +403,20 @@ TEST_F(DownloadItemTest, NotificationAfterCancel) { DownloadItemImpl* user_cancel = CreateDownloadItem(); - MockDownloadFile* download_file = CallDownloadItemStart(user_cancel, nullptr); + DownloadItemImplDelegate::DownloadTargetCallback target_callback; + MockDownloadFile* download_file = + CallDownloadItemStart(user_cancel, &target_callback); EXPECT_CALL(*download_file, Cancel()); - TestDownloadItemObserver observer1(user_cancel); + TestDownloadItemObserver observer1(user_cancel); user_cancel->Cancel(true); ASSERT_TRUE(observer1.CheckAndResetDownloadUpdated()); DownloadItemImpl* system_cancel = CreateDownloadItem(); - download_file = CallDownloadItemStart(system_cancel, nullptr); + download_file = CallDownloadItemStart(system_cancel, &target_callback); EXPECT_CALL(*download_file, Cancel()); - TestDownloadItemObserver observer2(system_cancel); + TestDownloadItemObserver observer2(system_cancel); system_cancel->Cancel(false); ASSERT_TRUE(observer2.CheckAndResetDownloadUpdated()); } @@ -438,8 +462,8 @@ ASSERT_TRUE(observer.download_destroyed()); } +// Test that a download is resumed automatcially after a continuable interrupt. TEST_F(DownloadItemTest, ContinueAfterInterrupted) { - TestBrowserContext test_browser_context; DownloadItemImpl* item = CreateDownloadItem(); TestDownloadItemObserver observer(item); MockDownloadFile* download_file = @@ -450,7 +474,7 @@ .WillOnce(Return(base::FilePath())); EXPECT_CALL(*download_file, Detach()); EXPECT_CALL(*mock_delegate(), GetBrowserContext()) - .WillRepeatedly(Return(&test_browser_context)); + .WillRepeatedly(Return(browser_context())); EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _)).Times(1); item->DestinationObserverAsWeakPtr()->DestinationError( DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); @@ -471,7 +495,8 @@ CleanupItem(item, nullptr, DownloadItem::IN_PROGRESS); } -// Same as above, but with a non-continuable interrupt. +// Test that automatic resumption doesn't happen after a non-continuable +// interrupt. TEST_F(DownloadItemTest, RestartAfterInterrupted) { DownloadItemImpl* item = CreateDownloadItem(); TestDownloadItemObserver observer(item); @@ -520,7 +545,6 @@ } TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) { - TestBrowserContext test_browser_context; DownloadItemImpl* item = CreateDownloadItem(); base::WeakPtr<DownloadDestinationObserver> as_observer( item->DestinationObserverAsWeakPtr()); @@ -534,11 +558,11 @@ EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)) .WillRepeatedly(SaveArg<1>(&callback)); EXPECT_CALL(*mock_delegate(), GetBrowserContext()) - .WillRepeatedly(Return(&test_browser_context)); + .WillRepeatedly(Return(browser_context())); EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _)) .Times(DownloadItemImpl::kMaxAutoResumeAttempts); for (int i = 0; i < (DownloadItemImpl::kMaxAutoResumeAttempts + 1); ++i) { - DVLOG(20) << "Loop iteration " << i; + SCOPED_TRACE(::testing::Message() << "Iteration " << i); mock_download_file = new NiceMock<MockDownloadFile>; download_file.reset(mock_download_file); @@ -550,7 +574,8 @@ // Copied key parts of DoIntermediateRename & CallDownloadItemStart // to allow for holding onto the request handle. - item->Start(std::move(download_file), std::move(request_handle)); + item->Start(std::move(download_file), std::move(request_handle), + *create_info()); RunAllPendingInMessageLoops(); base::FilePath target_path(kDummyTargetPath); @@ -571,6 +596,7 @@ item->DestinationObserverAsWeakPtr()->DestinationError( DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); + RunAllPendingInMessageLoops(); ::testing::Mock::VerifyAndClearExpectations(mock_download_file); } @@ -579,19 +605,70 @@ CleanupItem(item, nullptr, DownloadItem::INTERRUPTED); } +// If the download attempts to resume and the resumption request fails, the +// subsequent Start() call shouldn't update the origin state (URL redirect +// chains, Content-Disposition, download URL, etc..) +TEST_F(DownloadItemTest, FailedResumptionDoesntUpdateOriginState) { + const char kContentDisposition[] = "attachment; filename=foo"; + const char kFirstETag[] = "ABC"; + const char kFirstLastModified[] = "Yesterday"; + const char kFirstURL[] = "http://www.example.com/download"; + create_info()->content_disposition = kContentDisposition; + create_info()->etag = kFirstETag; + create_info()->last_modified = kFirstLastModified; + create_info()->url_chain.push_back(GURL(kFirstURL)); + + DownloadItemImpl* item = CreateDownloadItem(); + MockDownloadFile* download_file = + DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); + EXPECT_EQ(kContentDisposition, item->GetContentDisposition()); + EXPECT_EQ(kFirstETag, item->GetETag()); + EXPECT_EQ(kFirstLastModified, item->GetLastModifiedTime()); + EXPECT_EQ(kFirstURL, item->GetURL().spec()); + + EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _)); + EXPECT_CALL(*mock_delegate(), GetBrowserContext()) + .WillRepeatedly(Return(browser_context())); + EXPECT_CALL(*download_file, Detach()); + item->DestinationObserverAsWeakPtr()->DestinationError( + DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); + RunAllPendingInMessageLoops(); + EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); + + // Now change the create info. The changes should not cause the DownloadItem + // to be updated. + const char kSecondContentDisposition[] = "attachment; filename=bar"; + const char kSecondETag[] = "123"; + const char kSecondLastModified[] = "Today"; + const char kSecondURL[] = "http://example.com/another-download"; + create_info()->content_disposition = kSecondContentDisposition; + create_info()->etag = kSecondETag; + create_info()->last_modified = kSecondLastModified; + create_info()->url_chain.clear(); + create_info()->url_chain.push_back(GURL(kSecondURL)); + create_info()->result = DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED; + + // The following ends up calling DownloadItem::Start(), but shouldn't result + // in an origin update. + download_file = CallDownloadItemStart(item, nullptr); + + EXPECT_EQ(kContentDisposition, item->GetContentDisposition()); + EXPECT_EQ(kFirstETag, item->GetETag()); + EXPECT_EQ(kFirstLastModified, item->GetLastModifiedTime()); + EXPECT_EQ(kFirstURL, item->GetURL().spec()); + EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, item->GetLastReason()); +} + // Test that resumption uses the final URL in a URL chain when resuming. TEST_F(DownloadItemTest, ResumeUsingFinalURL) { - TestBrowserContext test_browser_context; - scoped_ptr<DownloadCreateInfo> create_info(new DownloadCreateInfo); - create_info->save_info = scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()); - create_info->save_info->prompt_for_save_location = false; - create_info->etag = "SomethingToSatisfyResumption"; - create_info->url_chain.push_back(GURL("http://example.com/a")); - create_info->url_chain.push_back(GURL("http://example.com/b")); - create_info->url_chain.push_back(GURL("http://example.com/c")); + create_info()->save_info->prompt_for_save_location = false; + create_info()->url_chain.clear(); + create_info()->url_chain.push_back(GURL("http://example.com/a")); + create_info()->url_chain.push_back(GURL("http://example.com/b")); + create_info()->url_chain.push_back(GURL("http://example.com/c")); - DownloadItemImpl* item = - CreateDownloadItemWithCreateInfo(std::move(create_info)); + DownloadItemImpl* item = CreateDownloadItem(); TestDownloadItemObserver observer(item); MockDownloadFile* download_file = DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); @@ -600,7 +677,7 @@ EXPECT_CALL(*download_file, FullPath()).WillOnce(Return(base::FilePath())); EXPECT_CALL(*download_file, Detach()); EXPECT_CALL(*mock_delegate(), GetBrowserContext()) - .WillRepeatedly(Return(&test_browser_context)); + .WillRepeatedly(Return(browser_context())); EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload( Property(&DownloadUrlParameters::url, GURL("http://example.com/c")), @@ -622,7 +699,9 @@ TEST_F(DownloadItemTest, NotificationAfterRemove) { DownloadItemImpl* item = CreateDownloadItem(); - MockDownloadFile* download_file = CallDownloadItemStart(item, nullptr); + DownloadItemImplDelegate::DownloadTargetCallback target_callback; + MockDownloadFile* download_file = + CallDownloadItemStart(item, &target_callback); EXPECT_CALL(*download_file, Cancel()); EXPECT_CALL(*mock_delegate(), DownloadRemoved(_)); TestDownloadItemObserver observer(item); @@ -723,7 +802,8 @@ EXPECT_CALL(*mock_download_file, Initialize(_)); EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _)); - item->Start(std::move(download_file), std::move(request_handle)); + item->Start(std::move(download_file), std::move(request_handle), + *create_info()); item->Pause(); ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); @@ -770,7 +850,8 @@ scoped_ptr<DownloadRequestHandleInterface> request_handle( new NiceMock<MockRequestHandle>); EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)); - item->Start(std::move(download_file), std::move(request_handle)); + item->Start(std::move(download_file), std::move(request_handle), + *create_info()); RunAllPendingInMessageLoops(); CleanupItem(item, mock_download_file, DownloadItem::IN_PROGRESS); @@ -779,6 +860,7 @@ // Download file and the request should be cancelled as a result of download // file initialization failing. TEST_F(DownloadItemTest, InitDownloadFileFails) { + DownloadItemImpl* item = CreateDownloadItem(); scoped_ptr<MockDownloadFile> file(new MockDownloadFile()); scoped_ptr<MockRequestHandle> request_handle(new MockRequestHandle()); EXPECT_CALL(*file, Cancel()); @@ -787,8 +869,19 @@ .WillOnce(ScheduleCallbackWithParam( DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED)); - DownloadItemImpl* item = CreateDownloadItem(); - item->Start(std::move(file), std::move(request_handle)); + base::RunLoop start_download_loop; + DownloadItemImplDelegate::DownloadTargetCallback download_target_callback; + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)) + .WillOnce(DoAll(SaveArg<1>(&download_target_callback), + ScheduleClosure(start_download_loop.QuitClosure()))); + + item->Start(std::move(file), std::move(request_handle), *create_info()); + start_download_loop.Run(); + + download_target_callback.Run(base::FilePath(kDummyTargetPath), + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, + base::FilePath(kDummyIntermediatePath)); RunAllPendingInMessageLoops(); EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); @@ -796,6 +889,38 @@ item->GetLastReason()); } +// Handling of downloads initiated via a failed request. In this case, Start() +// will get called with a DownloadCreateInfo with a non-zero interrupt_reason. +TEST_F(DownloadItemTest, StartFailedDownload) { + create_info()->result = DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED; + DownloadItemImpl* item = CreateDownloadItem(); + + // DownloadFile and DownloadRequestHandleInterface objects aren't created for + // failed downloads. + scoped_ptr<DownloadFile> null_download_file; + scoped_ptr<DownloadRequestHandleInterface> null_request_handle; + DownloadItemImplDelegate::DownloadTargetCallback download_target_callback; + EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)) + .WillOnce(SaveArg<1>(&download_target_callback)); + item->Start(std::move(null_download_file), std::move(null_request_handle), + *create_info()); + EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); + RunAllPendingInMessageLoops(); + + // The DownloadItemImpl should attempt to determine a target path even if the + // download was interrupted. + ASSERT_FALSE(download_target_callback.is_null()); + ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); + base::FilePath target_path(FILE_PATH_LITERAL("foo")); + download_target_callback.Run(target_path, + DownloadItem::TARGET_DISPOSITION_OVERWRITE, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, target_path); + RunAllPendingInMessageLoops(); + + EXPECT_EQ(target_path, item->GetTargetFilePath()); + CleanupItem(item, NULL, DownloadItem::INTERRUPTED); +} + // Test that the delegate is invoked after the download file is renamed. TEST_F(DownloadItemTest, CallbackAfterRename) { DownloadItemImpl* item = CreateDownloadItem(); @@ -978,7 +1103,9 @@ TEST_F(DownloadItemTest, Canceled) { DownloadItemImpl* item = CreateDownloadItem(); - MockDownloadFile* download_file = CallDownloadItemStart(item, nullptr); + DownloadItemImplDelegate::DownloadTargetCallback target_callback; + MockDownloadFile* download_file = + CallDownloadItemStart(item, &target_callback); // Confirm cancel sets state properly. EXPECT_CALL(*download_file, Cancel()); @@ -1051,7 +1178,8 @@ TEST_F(DownloadItemTest, DestinationCompleted) { DownloadItemImpl* item = CreateDownloadItem(); - MockDownloadFile* download_file = CallDownloadItemStart(item, nullptr); + MockDownloadFile* download_file = + DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); base::WeakPtr<DownloadDestinationObserver> as_observer( item->DestinationObserverAsWeakPtr()); TestDownloadItemObserver observer(item); @@ -1070,6 +1198,7 @@ EXPECT_EQ("deadbeef", item->GetHashState()); EXPECT_FALSE(item->AllDataSaved()); + EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)); as_observer->DestinationCompleted("livebeef"); mock_delegate()->VerifyAndClearExpectations(); EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); @@ -1647,7 +1776,7 @@ EXPECT_CALL(*file_, Initialize(_)) .WillOnce(DoAll(SaveArg<0>(&initialize_callback), ScheduleClosure(download_start_loop.QuitClosure()))); - item_->Start(std::move(file_), std::move(request_handle_)); + item_->Start(std::move(file_), std::move(request_handle_), *create_info()); download_start_loop.Run(); base::WeakPtr<DownloadDestinationObserver> destination_observer = @@ -1700,7 +1829,7 @@ .WillOnce(DoAll(SaveArg<0>(&initialize_callback), ScheduleClosure(download_start_loop.QuitClosure()))); - item_->Start(std::move(file_), std::move(request_handle_)); + item_->Start(std::move(file_), std::move(request_handle_), *create_info()); download_start_loop.Run(); base::WeakPtr<DownloadDestinationObserver> destination_observer = item_->DestinationObserverAsWeakPtr(); @@ -1769,7 +1898,7 @@ .WillOnce(DoAll(SaveArg<0>(&initialize_callback), ScheduleClosure(download_start_loop.QuitClosure()))); - item_->Start(std::move(file_), std::move(request_handle_)); + item_->Start(std::move(file_), std::move(request_handle_), *create_info()); download_start_loop.Run(); base::WeakPtr<DownloadDestinationObserver> destination_observer = item_->DestinationObserverAsWeakPtr();
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc index e869880..9f7029d 100644 --- a/content/browser/download/download_manager_impl.cc +++ b/content/browser/download/download_manager_impl.cc
@@ -55,90 +55,47 @@ uint32_t download_id, base::WeakPtr<DownloadManagerImpl> download_manager) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and - // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so - // we must down cast. RDHI is the only subclass of RDH as of 2012 May 4. - scoped_ptr<net::URLRequest> request( - params->resource_context()->GetRequestContext()->CreateRequest( - params->url(), net::DEFAULT_PRIORITY, NULL)); - request->set_method(params->method()); - if (!params->post_body().empty()) { - const std::string& body = params->post_body(); - scoped_ptr<net::UploadElementReader> reader( - net::UploadOwnedBytesElementReader::CreateWithString(body)); - request->set_upload( - net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); - } - if (params->post_id() >= 0) { - // The POST in this case does not have an actual body, and only works - // when retrieving data from cache. This is done because we don't want - // to do a re-POST without user consent, and currently don't have a good - // plan on how to display the UI for that. - DCHECK(params->prefer_cache()); - DCHECK_EQ("POST", params->method()); - std::vector<scoped_ptr<net::UploadElementReader>> element_readers; - request->set_upload(make_scoped_ptr(new net::ElementsUploadDataStream( - std::move(element_readers), params->post_id()))); - } - // If we're not at the beginning of the file, retrieve only the remaining - // portion. - bool has_last_modified = !params->last_modified().empty(); - bool has_etag = !params->etag().empty(); + scoped_ptr<net::URLRequest> url_request = + DownloadRequestCore::CreateRequestOnIOThread(download_id, params.get()); - // If we've asked for a range, we want to make sure that we only - // get that range if our current copy of the information is good. - // We shouldn't be asked to continue if we don't have a verifier. - DCHECK(params->offset() == 0 || has_etag || has_last_modified); - - if (params->offset() > 0 && (has_etag || has_last_modified)) { - request->SetExtraRequestHeaderByName( - "Range", - base::StringPrintf("bytes=%" PRId64 "-", params->offset()), - true); - - // In accordance with RFC 2616 Section 14.27, use If-Range to specify that - // the server return the entire entity if the validator doesn't match. - // Last-Modified can be used in the absence of ETag as a validator if the - // response headers satisfied the HttpUtil::HasStrongValidators() predicate. - // - // This function assumes that HasStrongValidators() was true and that the - // ETag and Last-Modified header values supplied are valid. - request->SetExtraRequestHeaderByName( - "If-Range", has_etag ? params->etag() : params->last_modified(), true); - } - - for (DownloadUrlParameters::RequestHeadersType::const_iterator iter - = params->request_headers_begin(); - iter != params->request_headers_end(); - ++iter) { - request->SetExtraRequestHeaderByName( - iter->first, iter->second, false /*overwrite*/); - } - - scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); - save_info->file_path = params->file_path(); - save_info->suggested_name = params->suggested_name(); - save_info->offset = params->offset(); - save_info->hash_state = params->hash_state(); - save_info->prompt_for_save_location = params->prompt(); - save_info->file = params->GetFile(); - + // If there's a valid renderer process associated with the request, then the + // request should be driven by the ResourceLoader. Pass it over to the + // ResourceDispatcherHostImpl which will in turn pass it along to the + // ResourceLoader. if (params->render_process_host_id() != -1) { - ResourceDispatcherHost::Get()->BeginDownload( - std::move(request), params->referrer(), params->content_initiated(), - params->resource_context(), params->render_process_host_id(), - params->render_view_host_routing_id(), - params->render_frame_host_routing_id(), params->prefer_cache(), - params->do_not_prompt_for_login(), std::move(save_info), download_id, - params->callback()); + DownloadInterruptReason reason = + ResourceDispatcherHostImpl::Get()->BeginDownload( + std::move(url_request), params->referrer(), + params->content_initiated(), params->resource_context(), + params->render_process_host_id(), + params->render_view_host_routing_id(), + params->render_frame_host_routing_id(), + params->do_not_prompt_for_login()); + + // If the download was accepted, the DownloadResourceHandler is now + // responsible for driving the request to completion. + if (reason == DOWNLOAD_INTERRUPT_REASON_NONE) + return nullptr; + + // Otherwise, create an interrupted download. + scoped_ptr<DownloadCreateInfo> failed_created_info( + new DownloadCreateInfo(base::Time::Now(), net::BoundNetLog(), + make_scoped_ptr(new DownloadSaveInfo))); + failed_created_info->url_chain.push_back(params->url()); + failed_created_info->result = reason; + scoped_ptr<ByteStreamReader> empty_byte_stream; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&DownloadManager::StartDownload, download_manager, + base::Passed(&failed_created_info), + base::Passed(&empty_byte_stream), params->callback())); return nullptr; } + return scoped_ptr<UrlDownloader, BrowserThread::DeleteOnIOThread>( - UrlDownloader::BeginDownload(download_manager, std::move(request), - params->referrer(), params->prefer_cache(), - std::move(save_info), download_id, - params->callback()) + UrlDownloader::BeginDownload(download_manager, std::move(url_request), + params->referrer()) .release()); } @@ -344,6 +301,11 @@ const DownloadUrlParameters::OnStartedCallback& on_started) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(info); + // |stream| is only non-nil if the download request was successful. + DCHECK((info->result == DOWNLOAD_INTERRUPT_REASON_NONE && stream.get()) || + (info->result != DOWNLOAD_INTERRUPT_REASON_NONE && !stream.get())); + DVLOG(20) << __FUNCTION__ << "()" + << " result=" << DownloadInterruptReasonToString(info->result); uint32_t download_id = info->download_id; const bool new_download = (download_id == content::DownloadItem::kInvalidId); base::Callback<void(uint32_t)> got_id(base::Bind( @@ -379,13 +341,12 @@ if (!on_started.is_null()) on_started.Run(NULL, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); // The ByteStreamReader lives and dies on the FILE thread. - BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, - stream.release()); + if (info->result == DOWNLOAD_INTERRUPT_REASON_NONE) + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, + stream.release()); return; } download = item_iterator->second; - DCHECK_EQ(download->GetState(), DownloadItem::IN_PROGRESS); - download->MergeOriginInfoOnResume(*info); } base::FilePath default_download_directory; @@ -396,20 +357,24 @@ &default_download_directory, &skip_dir_check); } - // Create the download file and start the download. - scoped_ptr<DownloadFile> download_file(file_factory_->CreateFile( - std::move(info->save_info), default_download_directory, info->url(), - info->referrer_url, delegate_ && delegate_->GenerateFileHash(), - std::move(stream), download->GetBoundNetLog(), - download->DestinationObserverAsWeakPtr())); + scoped_ptr<DownloadFile> download_file; - // Attach the client ID identifying the app to the AV system. - if (download_file.get() && delegate_) { - download_file->SetClientGuid( - delegate_->ApplicationClientIdForFileScanning()); + if (info->result == DOWNLOAD_INTERRUPT_REASON_NONE) { + DCHECK(stream.get()); + download_file.reset(file_factory_->CreateFile( + *info->save_info, default_download_directory, info->url(), + info->referrer_url, delegate_ && delegate_->GenerateFileHash(), + std::move(info->save_info->file), std::move(stream), + download->GetBoundNetLog(), download->DestinationObserverAsWeakPtr())); + + if (download_file.get() && delegate_) { + download_file->SetClientGuid( + delegate_->ApplicationClientIdForFileScanning()); + } } - download->Start(std::move(download_file), std::move(info->request_handle)); + download->Start(std::move(download_file), std::move(info->request_handle), + *info); // For interrupted downloads, Start() will transition the state to // IN_PROGRESS and consumers will be notified via OnDownloadUpdated(). @@ -604,10 +569,6 @@ base::Bind(&RemoveDownloadBetween, remove_begin, remove_end)); } -int DownloadManagerImpl::RemoveDownloads(base::Time remove_begin) { - return RemoveDownloadsBetween(remove_begin, base::Time()); -} - int DownloadManagerImpl::RemoveAllDownloads() { // The null times make the date range unbounded. int num_deleted = RemoveDownloadsBetween(base::Time(), base::Time());
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h index bb51f00..edf604f 100644 --- a/content/browser/download/download_manager_impl.h +++ b/content/browser/download/download_manager_impl.h
@@ -78,7 +78,6 @@ base::Time remove_end) override; int RemoveDownloadsBetween(base::Time remove_begin, base::Time remove_end) override; - int RemoveDownloads(base::Time remove_begin) override; int RemoveAllDownloads() override; void DownloadUrl(scoped_ptr<DownloadUrlParameters> params) override; void AddObserver(Observer* observer) override;
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc index 78d1386..8aeb3bd 100644 --- a/content/browser/download/download_manager_impl_unittest.cc +++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -117,7 +117,8 @@ MOCK_METHOD1(OnAllDataSaved, void(const std::string&)); MOCK_METHOD0(OnDownloadedFileRemoved, void()); void Start(scoped_ptr<DownloadFile> download_file, - scoped_ptr<DownloadRequestHandleInterface> req_handle) override { + scoped_ptr<DownloadRequestHandleInterface> req_handle, + const DownloadCreateInfo& create_info) override { MockStart(download_file.get(), req_handle.get()); } @@ -378,26 +379,22 @@ virtual ~MockDownloadFileFactory() {} // Overridden method from DownloadFileFactory - MOCK_METHOD8(MockCreateFile, MockDownloadFile*( - const DownloadSaveInfo&, - const base::FilePath&, - const GURL&, const GURL&, bool, - ByteStreamReader*, - const net::BoundNetLog&, - base::WeakPtr<DownloadDestinationObserver>)); + MOCK_METHOD3(MockCreateFile, + MockDownloadFile*(const DownloadSaveInfo&, + bool, + ByteStreamReader*)); virtual DownloadFile* CreateFile( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_download_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, + base::File file_stream, + scoped_ptr<ByteStreamReader> byte_stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) { - return MockCreateFile(*save_info.get(), default_download_directory, url, - referrer_url, calculate_hash, - stream.get(), bound_net_log, observer); + return MockCreateFile(save_info, calculate_hash, byte_stream.get()); } }; @@ -445,6 +442,14 @@ MOCK_METHOD2(SelectFileDialogDisplayed, void(DownloadManager*, int32_t)); }; +class MockByteStreamReader : public ByteStreamReader { + public: + virtual ~MockByteStreamReader() {} + MOCK_METHOD2(Read, StreamState(scoped_refptr<net::IOBuffer>*, size_t*)); + MOCK_CONST_METHOD0(GetStatus, int()); + MOCK_METHOD1(RegisterCallback, void(const base::Closure&)); +}; + } // namespace class DownloadManagerTest : public testing::Test { @@ -529,7 +534,7 @@ // we call Start on it immediately, so we need to set that expectation // in the factory. scoped_ptr<DownloadRequestHandleInterface> req_handle; - item.Start(scoped_ptr<DownloadFile>(), std::move(req_handle)); + item.Start(scoped_ptr<DownloadFile>(), std::move(req_handle), info); DCHECK(id < download_urls_.size()); EXPECT_CALL(item, GetURL()).WillRepeatedly(ReturnRef(download_urls_[id])); @@ -605,7 +610,7 @@ // Confirm the appropriate invocations occur when you start a download. TEST_F(DownloadManagerTest, StartDownload) { scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); - scoped_ptr<ByteStreamReader> stream; + scoped_ptr<ByteStreamReader> stream(new MockByteStreamReader); uint32_t local_id(5); // Random value base::FilePath download_path(FILE_PATH_LITERAL("download/path")); @@ -626,8 +631,7 @@ MockDownloadFile* mock_file = new MockDownloadFile; EXPECT_CALL(*mock_file, SetClientGuid("client-id")); EXPECT_CALL(*mock_download_file_factory_.get(), - MockCreateFile(Ref(*info->save_info.get()), _, _, _, true, - stream.get(), _, _)) + MockCreateFile(Ref(*info->save_info.get()), true, stream.get())) .WillOnce(Return(mock_file)); download_manager_->StartDownload(std::move(info), std::move(stream),
diff --git a/content/browser/download/download_request_core.cc b/content/browser/download/download_request_core.cc index 072ba7b..2857716 100644 --- a/content/browser/download/download_request_core.cc +++ b/content/browser/download/download_request_core.cc
@@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/format_macros.h" #include "base/location.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" @@ -21,41 +22,202 @@ #include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_request_handle.h" #include "content/browser/download/download_stats.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_item.h" #include "content/public/browser/download_manager_delegate.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/power_save_blocker.h" +#include "content/public/browser/resource_context.h" #include "content/public/browser/web_contents.h" +#include "net/base/elements_upload_data_stream.h" #include "net/base/io_buffer.h" +#include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/base/upload_bytes_element_reader.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" #include "net/url_request/url_request_context.h" namespace content { +namespace { + +// This is a UserData::Data that will be attached to a URLRequest as a +// side-channel for passing download parameters. +class DownloadRequestData : public base::SupportsUserData::Data { + public: + ~DownloadRequestData() override {} + + static void Attach(net::URLRequest* request, + DownloadUrlParameters* download_parameters, + uint32_t download_id); + static DownloadRequestData* Get(net::URLRequest* request); + static void Detach(net::URLRequest* request); + + scoped_ptr<DownloadSaveInfo> TakeSaveInfo() { return std::move(save_info_); } + uint32_t download_id() const { return download_id_; } + const DownloadUrlParameters::OnStartedCallback& callback() const { + return on_started_callback_; + } + + private: + static const int kKey; + + scoped_ptr<DownloadSaveInfo> save_info_; + uint32_t download_id_ = DownloadItem::kInvalidId; + DownloadUrlParameters::OnStartedCallback on_started_callback_; +}; + +// static +const int DownloadRequestData::kKey = 0; + +// static +void DownloadRequestData::Attach(net::URLRequest* request, + DownloadUrlParameters* parameters, + uint32_t download_id) { + DownloadRequestData* request_data = new DownloadRequestData; + request_data->save_info_.reset(new DownloadSaveInfo); + request_data->save_info_->file_path = parameters->file_path(); + request_data->save_info_->suggested_name = parameters->suggested_name(); + request_data->save_info_->file = parameters->GetFile(); + request_data->save_info_->offset = parameters->offset(); + request_data->save_info_->hash_state = parameters->hash_state(); + request_data->save_info_->prompt_for_save_location = parameters->prompt(); + request_data->download_id_ = download_id; + request_data->on_started_callback_ = parameters->callback(); + request->SetUserData(&kKey, request_data); +} + +// static +DownloadRequestData* DownloadRequestData::Get(net::URLRequest* request) { + return static_cast<DownloadRequestData*>(request->GetUserData(&kKey)); +} + +// static +void DownloadRequestData::Detach(net::URLRequest* request) { + request->RemoveUserData(&kKey); +} + +} // namespace + const int DownloadRequestCore::kDownloadByteStreamSize = 100 * 1024; -DownloadRequestCore::DownloadRequestCore( - net::URLRequest* request, - scoped_ptr<DownloadSaveInfo> save_info, - const base::Closure& on_ready_to_read_callback) - : on_ready_to_read_callback_(on_ready_to_read_callback), +// static +scoped_ptr<net::URLRequest> DownloadRequestCore::CreateRequestOnIOThread( + uint32_t download_id, + DownloadUrlParameters* params) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(download_id == DownloadItem::kInvalidId || + !params->content_initiated()) + << "Content initiated downloads shouldn't specify a download ID"; + + // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and + // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so + // we must down cast. RDHI is the only subclass of RDH as of 2012 May 4. + scoped_ptr<net::URLRequest> request( + params->resource_context()->GetRequestContext()->CreateRequest( + params->url(), net::DEFAULT_PRIORITY, nullptr)); + request->set_method(params->method()); + + if (!params->post_body().empty()) { + const std::string& body = params->post_body(); + scoped_ptr<net::UploadElementReader> reader( + net::UploadOwnedBytesElementReader::CreateWithString(body)); + request->set_upload( + net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); + } + + if (params->post_id() >= 0) { + // The POST in this case does not have an actual body, and only works + // when retrieving data from cache. This is done because we don't want + // to do a re-POST without user consent, and currently don't have a good + // plan on how to display the UI for that. + DCHECK(params->prefer_cache()); + DCHECK_EQ("POST", params->method()); + std::vector<scoped_ptr<net::UploadElementReader>> element_readers; + request->set_upload(make_scoped_ptr(new net::ElementsUploadDataStream( + std::move(element_readers), params->post_id()))); + } + + int load_flags = request->load_flags(); + if (params->prefer_cache()) { + // If there is upload data attached, only retrieve from cache because there + // is no current mechanism to prompt the user for their consent for a + // re-post. For GETs, try to retrieve data from the cache and skip + // validating the entry if present. + if (request->get_upload()) + load_flags |= net::LOAD_ONLY_FROM_CACHE; + else + load_flags |= net::LOAD_PREFERRING_CACHE; + } else { + load_flags |= net::LOAD_DISABLE_CACHE; + } + request->SetLoadFlags(load_flags); + + bool has_last_modified = !params->last_modified().empty(); + bool has_etag = !params->etag().empty(); + + // If we've asked for a range, we want to make sure that we only get that + // range if our current copy of the information is good. We shouldn't be + // asked to continue if we don't have a verifier. + DCHECK(params->offset() == 0 || has_etag || has_last_modified); + + // If we're not at the beginning of the file, retrieve only the remaining + // portion. + if (params->offset() > 0 && (has_etag || has_last_modified)) { + request->SetExtraRequestHeaderByName( + "Range", base::StringPrintf("bytes=%" PRId64 "-", params->offset()), + true); + + // In accordance with RFC 2616 Section 14.27, use If-Range to specify that + // the server return the entire entity if the validator doesn't match. + // Last-Modified can be used in the absence of ETag as a validator if the + // response headers satisfied the HttpUtil::HasStrongValidators() predicate. + // + // This function assumes that HasStrongValidators() was true and that the + // ETag and Last-Modified header values supplied are valid. + request->SetExtraRequestHeaderByName( + "If-Range", has_etag ? params->etag() : params->last_modified(), true); + } + + for (const auto& header : params->request_headers()) + request->SetExtraRequestHeaderByName(header.first, header.second, + false /*overwrite*/); + + DownloadRequestData::Attach(request.get(), std::move(params), download_id); + return request; +} + +DownloadRequestCore::DownloadRequestCore(net::URLRequest* request, + Delegate* delegate) + : delegate_(delegate), request_(request), - save_info_(std::move(save_info)), + download_id_(DownloadItem::kInvalidId), last_buffer_size_(0), bytes_read_(0), pause_count_(0), - was_deferred_(false) { + was_deferred_(false), + is_partial_request_(false), + started_(false), + abort_reason_(DOWNLOAD_INTERRUPT_REASON_NONE) { DCHECK(request_); - DCHECK(save_info_); - DCHECK(!on_ready_to_read_callback_.is_null()); + DCHECK(delegate_); RecordDownloadCount(UNTHROTTLED_COUNT); power_save_blocker_ = PowerSaveBlocker::Create( PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, PowerSaveBlocker::kReasonOther, "Download in progress"); + DownloadRequestData* request_data = DownloadRequestData::Get(request_); + if (request_data) { + save_info_ = request_data->TakeSaveInfo(); + download_id_ = request_data->download_id(); + on_started_callback_ = request_data->callback(); + DownloadRequestData::Detach(request_); + is_partial_request_ = save_info_->offset > 0; + } else { + save_info_.reset(new DownloadSaveInfo); + } } DownloadRequestCore::~DownloadRequestCore() { @@ -68,15 +230,41 @@ base::TimeTicks::Now() - download_start_time_); } -// Send the download creation information to the download thread. -void DownloadRequestCore::OnResponseStarted( - scoped_ptr<DownloadCreateInfo>* create_info, - scoped_ptr<ByteStreamReader>* stream_reader) { +scoped_ptr<DownloadCreateInfo> DownloadRequestCore::CreateDownloadCreateInfo( + DownloadInterruptReason result) { + DCHECK(!started_); + started_ = true; + scoped_ptr<DownloadCreateInfo> create_info(new DownloadCreateInfo( + base::Time::Now(), request()->net_log(), std::move(save_info_))); + + if (result == DOWNLOAD_INTERRUPT_REASON_NONE) + create_info->remote_address = request()->GetSocketAddress().host(); + create_info->url_chain = request()->url_chain(); + create_info->referrer_url = GURL(request()->referrer()); + create_info->result = result; + create_info->download_id = download_id_; + return create_info; +} + +bool DownloadRequestCore::OnResponseStarted( + const std::string& override_mime_type) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(save_info_); DVLOG(20) << __FUNCTION__ << "()" << DebugString(); download_start_time_ = base::TimeTicks::Now(); + DownloadInterruptReason result = + request()->response_headers() + ? HandleSuccessfulServerResponse(*request()->response_headers(), + save_info_.get()) + : DOWNLOAD_INTERRUPT_REASON_NONE; + + scoped_ptr<DownloadCreateInfo> create_info = CreateDownloadCreateInfo(result); + if (result != DOWNLOAD_INTERRUPT_REASON_NONE) { + delegate_->OnStart(std::move(create_info), scoped_ptr<ByteStreamReader>(), + base::ResetAndReturn(&on_started_callback_)); + return false; + } + // If it's a download, we don't want to poison the cache with it. request()->StopCaching(); @@ -90,35 +278,21 @@ int64_t content_length = request()->GetExpectedContentSize() > 0 ? request()->GetExpectedContentSize() : 0; - - // Deleted in DownloadManager. - scoped_ptr<DownloadCreateInfo> info( - new DownloadCreateInfo(base::Time::Now(), content_length, - request()->net_log(), std::move(save_info_))); + create_info->total_bytes = content_length; // Create the ByteStream for sending data to the download sink. + scoped_ptr<ByteStreamReader> stream_reader; CreateByteStream( base::ThreadTaskRunnerHandle::Get(), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), - kDownloadByteStreamSize, &stream_writer_, stream_reader); + kDownloadByteStreamSize, &stream_writer_, &stream_reader); stream_writer_->RegisterCallback( base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); - info->url_chain = request()->url_chain(); - info->referrer_url = GURL(request()->referrer()); - string mime_type; - request()->GetMimeType(&mime_type); - info->mime_type = mime_type; - info->remote_address = request()->GetSocketAddress().host(); - if (request()->response_headers()) { - // Grab the first content-disposition header. There may be more than one, - // though as of this writing, the network stack ensures if there are, they - // are all duplicates. - request()->response_headers()->EnumerateHeader( - nullptr, "Content-Disposition", &info->content_disposition); - } - RecordDownloadMimeType(info->mime_type); - RecordDownloadContentDisposition(info->content_disposition); + if (!override_mime_type.empty()) + create_info->mime_type = override_mime_type; + else + request()->GetMimeType(&create_info->mime_type); // Get the last modified time and etag. const net::HttpResponseHeaders* headers = request()->response_headers(); @@ -127,33 +301,49 @@ // If we don't have strong validators as per RFC 2616 section 13.3.3, then // we neither store nor use them for range requests. if (!headers->EnumerateHeader(nullptr, "Last-Modified", - &info->last_modified)) - info->last_modified.clear(); - if (!headers->EnumerateHeader(nullptr, "ETag", &info->etag)) - info->etag.clear(); + &create_info->last_modified)) + create_info->last_modified.clear(); + if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag)) + create_info->etag.clear(); } - int status = headers->response_code(); - if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { - // Success & not range response; if we asked for a range, we didn't - // get it--reset the file pointers to reflect that. - info->save_info->offset = 0; - info->save_info->hash_state = ""; - } + // Grab the first content-disposition header. There may be more than one, + // though as of this writing, the network stack ensures if there are, they + // are all duplicates. + headers->EnumerateHeader(nullptr, "Content-Disposition", + &create_info->content_disposition); - if (!headers->GetMimeType(&info->original_mime_type)) - info->original_mime_type.clear(); + if (!headers->GetMimeType(&create_info->original_mime_type)) + create_info->original_mime_type.clear(); } // Blink verifies that the requester of this download is allowed to set a - // suggested name for the security origin of the downlaod URL. However, this + // suggested name for the security origin of the download URL. However, this // assumption doesn't hold if there were cross origin redirects. Therefore, // clear the suggested_name for such requests. - if (info->url_chain.size() > 1 && - info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) - info->save_info->suggested_name.clear(); + if (create_info->url_chain.size() > 1 && + create_info->url_chain.front().GetOrigin() != + create_info->url_chain.back().GetOrigin()) + create_info->save_info->suggested_name.clear(); - info.swap(*create_info); + RecordDownloadMimeType(create_info->mime_type); + RecordDownloadContentDisposition(create_info->content_disposition); + + delegate_->OnStart(std::move(create_info), std::move(stream_reader), + base::ResetAndReturn(&on_started_callback_)); + return true; +} + +bool DownloadRequestCore::OnRequestRedirected() { + DVLOG(20) << __FUNCTION__ << "() " << DebugString(); + if (is_partial_request_) { + // A redirect while attempting a partial resumption indicates a potential + // middle box. Trigger another interruption so that the DownloadItem can + // retry. + abort_reason_ = DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE; + return false; + } + return true; } // Create a new buffer, which will be handed to the download thread for file @@ -212,7 +402,13 @@ return true; } -DownloadInterruptReason DownloadRequestCore::OnResponseCompleted( +void DownloadRequestCore::OnWillAbort(DownloadInterruptReason reason) { + DVLOG(20) << __FUNCTION__ << "() reason=" << reason << " " << DebugString(); + DCHECK(!started_); + abort_reason_ = reason; +} + +void DownloadRequestCore::OnResponseCompleted( const net::URLRequestStatus& status) { DCHECK_CURRENTLY_ON(BrowserThread::IO); int response_code = status.is_success() ? request()->GetResponseCode() : 0; @@ -221,80 +417,27 @@ << " status.error() = " << status.error() << " response_code = " << response_code; - net::Error error_code = net::OK; - if (status.status() == net::URLRequestStatus::FAILED || - // Note cancels as failures too. - status.status() == net::URLRequestStatus::CANCELED) { - error_code = static_cast<net::Error>(status.error()); // Normal case. - // Make sure that at least the fact of failure comes through. - if (error_code == net::OK) - error_code = net::ERR_FAILED; - } + DownloadInterruptReason reason = HandleRequestStatus(status); - // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are - // allowed since a number of servers in the wild close the connection too - // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - - // treat downloads as complete in both cases, so we follow their lead. - if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || - error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { - error_code = net::OK; - } - DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( - error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); - - if (status.status() == net::URLRequestStatus::CANCELED && - status.error() == net::ERR_ABORTED) { - // CANCELED + ERR_ABORTED == something outside of the network - // stack cancelled the request. There aren't that many things that - // could do this to a download request (whose lifetime is separated from - // the tab from which it came). We map this to USER_CANCELLED as the - // case we know about (system suspend because of laptop close) corresponds - // to a user action. - // TODO(ahendrickson) -- Find a better set of codes to use here, as - // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. - if (net::IsCertStatusError(request()->ssl_info().cert_status)) - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; - else - reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; - } - - if (status.is_success() && reason == DOWNLOAD_INTERRUPT_REASON_NONE && - request()->response_headers()) { - // Handle server's response codes. - switch (response_code) { - case -1: // Non-HTTP request. - case net::HTTP_OK: - case net::HTTP_CREATED: - case net::HTTP_ACCEPTED: - case net::HTTP_NON_AUTHORITATIVE_INFORMATION: - case net::HTTP_RESET_CONTENT: - case net::HTTP_PARTIAL_CONTENT: - // Expected successful codes. - break; - case net::HTTP_NO_CONTENT: - case net::HTTP_NOT_FOUND: - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; - break; - case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: - // Retry by downloading from the start automatically: - // If we haven't received data when we get this error, we won't. - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; - break; - case net::HTTP_UNAUTHORIZED: - // Server didn't authorize this request. - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; - break; - case net::HTTP_FORBIDDEN: - // Server forbids access to this resource. - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; - break; - default: // All other errors. - // Redirection and informational codes should have been handled earlier - // in the stack. - DCHECK_NE(3, response_code / 100); - DCHECK_NE(1, response_code / 100); - reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; - break; + if (status.status() == net::URLRequestStatus::CANCELED) { + if (abort_reason_ != DOWNLOAD_INTERRUPT_REASON_NONE) { + // If a more specific interrupt reason was specified before the request + // was explicitly cancelled, then use it. + reason = abort_reason_; + } else if (status.error() == net::ERR_ABORTED) { + // CANCELED + ERR_ABORTED == something outside of the network + // stack cancelled the request. There aren't that many things that + // could do this to a download request (whose lifetime is separated from + // the tab from which it came). We map this to USER_CANCELLED as the + // case we know about (system suspend because of laptop close) corresponds + // to a user action. + // TODO(asanka): A lid close or other power event should result in an + // interruption that doesn't discard the partial state, unlike + // USER_CANCELLED. (https://crbug.com/166179) + if (net::IsCertStatusError(request()->ssl_info().cert_status)) + reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; + else + reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; } } @@ -325,7 +468,16 @@ stream_writer_.reset(); // We no longer need the stream. read_buffer_ = nullptr; - return reason; + if (started_) + return; + + // OnResponseCompleted() called without OnResponseStarted(). This should only + // happen when the request was aborted. + DCHECK_NE(reason, DOWNLOAD_INTERRUPT_REASON_NONE); + scoped_ptr<DownloadCreateInfo> create_info = CreateDownloadCreateInfo(reason); + scoped_ptr<ByteStreamReader> empty_byte_stream; + delegate_->OnStart(std::move(create_info), std::move(empty_byte_stream), + base::ResetAndReturn(&on_started_callback_)); } void DownloadRequestCore::PauseRequest() { @@ -351,16 +503,135 @@ last_stream_pause_time_ = base::TimeTicks(); } - on_ready_to_read_callback_.Run(); + delegate_->OnReadyToRead(); } std::string DownloadRequestCore::DebugString() const { return base::StringPrintf( "{" + " this=%p " " url_ = " "\"%s\"" " }", + reinterpret_cast<const void*>(this), request() ? request()->url().spec().c_str() : "<NULL request>"); } +// static +DownloadInterruptReason DownloadRequestCore::HandleRequestStatus( + const net::URLRequestStatus& status) { + net::Error error_code = net::OK; + if (status.status() == net::URLRequestStatus::FAILED || + // Note cancels as failures too. + status.status() == net::URLRequestStatus::CANCELED) { + error_code = static_cast<net::Error>(status.error()); // Normal case. + // Make sure that at least the fact of failure comes through. + if (error_code == net::OK) + error_code = net::ERR_FAILED; + } + + // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are + // allowed since a number of servers in the wild close the connection too + // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - + // treat downloads as complete in both cases, so we follow their lead. + if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || + error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { + error_code = net::OK; + } + DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( + error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); + + return reason; +} + +// static +DownloadInterruptReason DownloadRequestCore::HandleSuccessfulServerResponse( + const net::HttpResponseHeaders& http_headers, + DownloadSaveInfo* save_info) { + switch (http_headers.response_code()) { + case -1: // Non-HTTP request. + case net::HTTP_OK: + case net::HTTP_NON_AUTHORITATIVE_INFORMATION: + case net::HTTP_PARTIAL_CONTENT: + // Expected successful codes. + break; + + case net::HTTP_CREATED: + case net::HTTP_ACCEPTED: + // Per RFC 2616 the entity being transferred is metadata about the + // resource at the target URL and not the resource at that URL (or the + // resource that would be at the URL once processing is completed in the + // case of HTTP_ACCEPTED). However, we currently don't have special + // handling for these response and they are downloaded the same as a + // regular response. + break; + + case net::HTTP_NO_CONTENT: + case net::HTTP_RESET_CONTENT: + // These two status codes don't have an entity (or rather RFC 2616 + // requires that there be no entity). They are treated the same as the + // resource not being found since there is no entity to download. + + case net::HTTP_NOT_FOUND: + return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + break; + + case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: + // Retry by downloading from the start automatically: + // If we haven't received data when we get this error, we won't. + return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; + break; + case net::HTTP_UNAUTHORIZED: + case net::HTTP_PROXY_AUTHENTICATION_REQUIRED: + // Server didn't authorize this request. + return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; + break; + case net::HTTP_FORBIDDEN: + // Server forbids access to this resource. + return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; + break; + default: // All other errors. + // Redirection and informational codes should have been handled earlier + // in the stack. + DCHECK_NE(3, http_headers.response_code() / 100); + DCHECK_NE(1, http_headers.response_code() / 100); + return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; + } + + if (save_info && save_info->offset > 0) { + // The caller is expecting a partial response. + + if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) { + // Requested a partial range, but received the entire response. + save_info->offset = 0; + save_info->hash_state.clear(); + return DOWNLOAD_INTERRUPT_REASON_NONE; + } + + int64_t first_byte = -1; + int64_t last_byte = -1; + int64_t length = -1; + if (!http_headers.GetContentRange(&first_byte, &last_byte, &length)) + return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + DCHECK_GE(first_byte, 0); + + if (first_byte != save_info->offset) { + // The server returned a different range than the one we requested. Assume + // the response is bad. + // + // In the future we should consider allowing offsets that are less than + // the offset we've requested, since in theory we can truncate the partial + // file at the offset and continue. + return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + } + + return DOWNLOAD_INTERRUPT_REASON_NONE; + } + + if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT) + return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; + + return DOWNLOAD_INTERRUPT_REASON_NONE; +} + } // namespace content
diff --git a/content/browser/download/download_request_core.h b/content/browser/download/download_request_core.h index 22ca862e..ac9e9b6 100644 --- a/content/browser/download/download_request_core.h +++ b/content/browser/download/download_request_core.h
@@ -20,7 +20,9 @@ #include "content/public/browser/download_url_parameters.h" namespace net { +class HttpResponseHeaders; class URLRequest; +class URLRequestStatus; } // namespace net namespace content { @@ -38,29 +40,29 @@ class CONTENT_EXPORT DownloadRequestCore : public base::SupportsWeakPtr<DownloadRequestCore> { public: - // Size of the buffer used between the DownloadRequestCore and the - // downstream receiver of its output. - static const int kDownloadByteStreamSize; + class Delegate { + public: + virtual void OnReadyToRead() = 0; + virtual void OnStart( + scoped_ptr<DownloadCreateInfo> download_create_info, + scoped_ptr<ByteStreamReader> stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) = 0; + }; - // |request| *must* outlive the DownloadRequestCore. |save_info| must be - // valid. - // - // Invokes |on_ready_to_read_callback| if a previous call to OnReadCompleted() - // resulted in |defer| being set to true, and DownloadRequestCore is now ready - // to commence reading. - DownloadRequestCore(net::URLRequest* request, - scoped_ptr<DownloadSaveInfo> save_info, - const base::Closure& on_ready_to_read_callback); + // All parameters are required. |request| and |delegate| must outlive + // DownloadRequestCore. + DownloadRequestCore(net::URLRequest* request, Delegate* delegate); ~DownloadRequestCore(); // Should be called when the URLRequest::Delegate receives OnResponseStarted. - // Constructs a DownloadCreateInfo and a ByteStreamReader that should be - // passed into DownloadManagerImpl::StartDownload(). - // - // Only populates the response derived fields of DownloadCreateInfo, with the - // exception of |save_info|. - void OnResponseStarted(scoped_ptr<DownloadCreateInfo>* info, - scoped_ptr<ByteStreamReader>* stream_reader); + // Invokes Delegate::OnStart() with download start parameters. The + // |override_mime_type| is used as the MIME type for the download when + // constructing a DownloadCreateInfo object. + bool OnResponseStarted(const std::string& override_mime_type); + + // Should be called to handle a redirect. The caller should only allow the + // redirect to be followed if the return value is true. + bool OnRequestRedirected(); // Starts a read cycle. Creates a new IOBuffer which can be passed into // URLRequest::Read(). Call OnReadCompleted() when the Read operation @@ -69,20 +71,22 @@ int* buf_size, int min_size); + // Used to notify DownloadRequestCore that the caller is about to abort the + // outer request. |reason| will be used as the final interrupt reason when + // OnResponseCompleted() is called. + void OnWillAbort(DownloadInterruptReason reason); + // Should be called when the Read() operation completes. |defer| will be set // to true if reading is to be suspended. In the latter case, once more data // can be read, invokes the |on_ready_to_read_callback|. bool OnReadCompleted(int bytes_read, bool* defer); - // Called to signal that the response is complete. If the return value is - // something other than DOWNLOAD_INTERRUPT_REASON_NONE, then the download - // should be considered interrupted. + // Called to signal that the response is complete. // // It is expected that once this method is invoked, the DownloadRequestCore // object will be destroyed in short order without invoking any other methods // other than the destructor. - DownloadInterruptReason OnResponseCompleted( - const net::URLRequestStatus& status); + void OnResponseCompleted(const net::URLRequestStatus& status); // Called if the request should suspend reading. A subsequent // OnReadCompleted() will result in |defer| being set to true. @@ -98,13 +102,36 @@ std::string DebugString() const; + static scoped_ptr<net::URLRequest> CreateRequestOnIOThread( + uint32_t download_id, + DownloadUrlParameters* params); + + // Size of the buffer used between the DownloadRequestCore and the + // downstream receiver of its output. + static const int kDownloadByteStreamSize; + protected: net::URLRequest* request() const { return request_; } private: - base::Closure on_ready_to_read_callback_; + static DownloadInterruptReason HandleRequestStatus( + const net::URLRequestStatus& status); + + static DownloadInterruptReason HandleSuccessfulServerResponse( + const net::HttpResponseHeaders& http_headers, + DownloadSaveInfo* save_info); + + scoped_ptr<DownloadCreateInfo> CreateDownloadCreateInfo( + DownloadInterruptReason result); + + Delegate* delegate_; net::URLRequest* request_; + + // "Passthrough" fields. These are only kept here so that they can be used to + // populate the DownloadCreateInfo when the time comes. scoped_ptr<DownloadSaveInfo> save_info_; + uint32_t download_id_; + DownloadUrlParameters::OnStartedCallback on_started_callback_; // Data flow scoped_refptr<net::IOBuffer> read_buffer_; // From URLRequest. @@ -125,6 +152,15 @@ int pause_count_; bool was_deferred_; + bool is_partial_request_; + bool started_; + + // When DownloadRequestCore initiates an abort (by blocking a redirect, for + // example) it expects to eventually receive a OnResponseCompleted() with a + // status indicating that the request was aborted. When this happens, the + // interrupt reason in |abort_reason_| will be used instead of USER_CANCELED + // which is vague. + DownloadInterruptReason abort_reason_; // Each successful OnWillRead will yield a buffer of this size. static const int kReadBufSize = 32768; // bytes
diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc index e06b309..a432ce5e 100644 --- a/content/browser/download/download_resource_handler.cc +++ b/content/browser/download/download_resource_handler.cc
@@ -48,11 +48,11 @@ // NULL in unittests or if the page closed right after starting the // download. if (!started_cb.is_null()) - started_cb.Run(NULL, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); + started_cb.Run(nullptr, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); - // |stream| gets deleted on non-FILE thread, but it's ok since - // we're not using stream_writer_ yet. - + if (stream) + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, + stream.release()); return; } @@ -83,19 +83,10 @@ } // namespace -DownloadResourceHandler::DownloadResourceHandler( - uint32_t id, - net::URLRequest* request, - const DownloadUrlParameters::OnStartedCallback& started_cb, - scoped_ptr<DownloadSaveInfo> save_info) +DownloadResourceHandler::DownloadResourceHandler(net::URLRequest* request) : ResourceHandler(request), - download_id_(id), - started_cb_(started_cb), tab_info_(new DownloadTabInfo()), - core_(request, - std::move(save_info), - base::Bind(&DownloadResourceHandler::OnCoreReadyToRead, - base::Unretained(this))) { + core_(request, this) { // Do UI thread initialization for tab_info_ asap after // DownloadResourceHandler creation since the tab could be navigated // before StartOnUIThread gets called. This is safe because deletion @@ -113,11 +104,6 @@ } DownloadResourceHandler::~DownloadResourceHandler() { - // This won't do anything if the callback was called before. - // If it goes through, it will likely be because OnWillStart() returned - // false somewhere in the chain of resource handlers. - CallStartedCB(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); - if (tab_info_) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -129,46 +115,16 @@ const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) { - return true; + return core_.OnRequestRedirected(); } // Send the download creation information to the download thread. bool DownloadResourceHandler::OnResponseStarted( ResourceResponse* response, bool* defer) { - scoped_ptr<DownloadCreateInfo> create_info; - scoped_ptr<ByteStreamReader> stream_reader; - - core_.OnResponseStarted(&create_info, &stream_reader); - - const ResourceRequestInfoImpl* request_info = GetRequestInfo(); - create_info->download_id = download_id_; - create_info->has_user_gesture = request_info->HasUserGesture(); - create_info->transition_type = request_info->GetPageTransition(); - create_info->request_handle.reset(new DownloadRequestHandle( - AsWeakPtr(), request_info->GetChildID(), request_info->GetRouteID(), - request_info->GetRequestID(), request_info->frame_tree_node_id())); - // The MIME type in ResourceResponse is the product of // MimeTypeResourceHandler. - create_info->mime_type = response->head.mime_type; - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&StartOnUIThread, base::Passed(&create_info), - base::Passed(&tab_info_), base::Passed(&stream_reader), - base::ResetAndReturn(&started_cb_))); - return true; -} - -void DownloadResourceHandler::CallStartedCB( - DownloadInterruptReason interrupt_reason) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (started_cb_.is_null()) - return; - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::ResetAndReturn(&started_cb_), - nullptr, interrupt_reason)); + return core_.OnResponseStarted(response->head.mime_type); } bool DownloadResourceHandler::OnWillStart(const GURL& url, bool* defer) { @@ -197,8 +153,7 @@ const net::URLRequestStatus& status, const std::string& security_info, bool* defer) { - DownloadInterruptReason result = core_.OnResponseCompleted(status); - CallStartedCB(result); + core_.OnResponseCompleted(status); } void DownloadResourceHandler::OnDataDownloaded(int bytes_downloaded) { @@ -213,7 +168,37 @@ core_.ResumeRequest(); } -void DownloadResourceHandler::OnCoreReadyToRead() { +void DownloadResourceHandler::OnStart( + scoped_ptr<DownloadCreateInfo> create_info, + scoped_ptr<ByteStreamReader> stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) { + // If the user cancels the download, then don't call start. Instead ignore the + // download entirely. + if (create_info->result == DOWNLOAD_INTERRUPT_REASON_USER_CANCELED && + create_info->download_id == DownloadItem::kInvalidId) { + if (!callback.is_null()) + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(callback, nullptr, create_info->result)); + return; + } + + const ResourceRequestInfoImpl* request_info = GetRequestInfo(); + create_info->has_user_gesture = request_info->HasUserGesture(); + create_info->transition_type = request_info->GetPageTransition(); + + create_info->request_handle.reset(new DownloadRequestHandle( + AsWeakPtr(), request_info->GetChildID(), request_info->GetRouteID(), + request_info->GetRequestID(), request_info->frame_tree_node_id())); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&StartOnUIThread, base::Passed(&create_info), + base::Passed(&tab_info_), base::Passed(&stream_reader), + callback)); +} + +void DownloadResourceHandler::OnReadyToRead() { DCHECK_CURRENTLY_ON(BrowserThread::IO); controller()->Resume(); }
diff --git a/content/browser/download/download_resource_handler.h b/content/browser/download/download_resource_handler.h index 22a10a8..b874b9a 100644 --- a/content/browser/download/download_resource_handler.h +++ b/content/browser/download/download_resource_handler.h
@@ -33,17 +33,14 @@ // Forwards data to the download thread. class CONTENT_EXPORT DownloadResourceHandler : public ResourceHandler, + public DownloadRequestCore::Delegate, public base::SupportsWeakPtr<DownloadResourceHandler> { public: struct DownloadTabInfo; // started_cb will be called exactly once on the UI thread. // |id| should be invalid if the id should be automatically assigned. - DownloadResourceHandler( - uint32_t id, - net::URLRequest* request, - const DownloadUrlParameters::OnStartedCallback& started_cb, - scoped_ptr<DownloadSaveInfo> save_info); + DownloadResourceHandler(net::URLRequest* request); bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, @@ -84,18 +81,12 @@ private: ~DownloadResourceHandler() override; - // Arrange for started_cb_ to be called on the UI thread with the - // below values, nulling out started_cb_. Should only be called - // on the IO thread. - void CallStartedCB(DownloadInterruptReason interrupt_reason); - - void OnCoreReadyToRead(); - - uint32_t download_id_; - - // This is read only on the IO thread, but may only - // be called on the UI thread. - DownloadUrlParameters::OnStartedCallback started_cb_; + // DownloadRequestCore::Delegate + void OnStart( + scoped_ptr<DownloadCreateInfo> download_create_info, + scoped_ptr<ByteStreamReader> stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) override; + void OnReadyToRead() override; // Stores information about the download that must be acquired on the UI // thread before StartOnUIThread is called.
diff --git a/content/browser/download/drag_download_file.cc b/content/browser/download/drag_download_file.cc index afb533a..dc6b89c 100644 --- a/content/browser/download/drag_download_file.cc +++ b/content/browser/download/drag_download_file.cc
@@ -97,8 +97,8 @@ void OnDownloadStarted(DownloadItem* item, DownloadInterruptReason interrupt_reason) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!item) { - DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); + if (!item || item->GetState() != DownloadItem::IN_PROGRESS) { + DCHECK(!item || item->GetLastReason() != DOWNLOAD_INTERRUPT_REASON_NONE); on_completed_loop_->task_runner()->PostTask( FROM_HERE, base::Bind(on_completed_, false)); return;
diff --git a/content/browser/download/url_downloader.cc b/content/browser/download/url_downloader.cc index b178a1d4..165fe7b4 100644 --- a/content/browser/download/url_downloader.cc +++ b/content/browser/download/url_downloader.cc
@@ -74,62 +74,32 @@ scoped_ptr<UrlDownloader> UrlDownloader::BeginDownload( base::WeakPtr<DownloadManagerImpl> download_manager, scoped_ptr<net::URLRequest> request, - const Referrer& referrer, - bool prefer_cache, - scoped_ptr<DownloadSaveInfo> save_info, - uint32_t download_id, - const DownloadUrlParameters::OnStartedCallback& started_callback) { + const Referrer& referrer) { if (!referrer.url.is_valid()) request->SetReferrer(std::string()); else request->SetReferrer(referrer.url.spec()); - int extra_load_flags = net::LOAD_NORMAL; - if (prefer_cache) { - // If there is upload data attached, only retrieve from cache because there - // is no current mechanism to prompt the user for their consent for a - // re-post. For GETs, try to retrieve data from the cache and skip - // validating the entry if present. - if (request->get_upload() != NULL) - extra_load_flags |= net::LOAD_ONLY_FROM_CACHE; - else - extra_load_flags |= net::LOAD_PREFERRING_CACHE; - } else { - extra_load_flags |= net::LOAD_DISABLE_CACHE; - } - request->SetLoadFlags(request->load_flags() | extra_load_flags); - if (request->url().SchemeIs(url::kBlobScheme)) return nullptr; // From this point forward, the |UrlDownloader| is responsible for // |started_callback|. scoped_ptr<UrlDownloader> downloader( - new UrlDownloader(std::move(request), download_manager, - std::move(save_info), download_id, started_callback)); + new UrlDownloader(std::move(request), download_manager)); downloader->Start(); return downloader; } -UrlDownloader::UrlDownloader( - scoped_ptr<net::URLRequest> request, - base::WeakPtr<DownloadManagerImpl> manager, - scoped_ptr<DownloadSaveInfo> save_info, - uint32_t download_id, - const DownloadUrlParameters::OnStartedCallback& on_started_callback) +UrlDownloader::UrlDownloader(scoped_ptr<net::URLRequest> request, + base::WeakPtr<DownloadManagerImpl> manager) : request_(std::move(request)), manager_(manager), - download_id_(download_id), - on_started_callback_(on_started_callback), - handler_( - request_.get(), - std::move(save_info), - base::Bind(&UrlDownloader::ResumeReading, base::Unretained(this))), + core_(request_.get(), this), weak_ptr_factory_(this) {} UrlDownloader::~UrlDownloader() { - CallStartedCallbackOnFailure(DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); } void UrlDownloader::Start() { @@ -146,7 +116,15 @@ const net::RedirectInfo& redirect_info, bool* defer_redirect) { DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec(); - request_->CancelWithError(net::ERR_ABORTED); + + // We are going to block redirects even if DownloadRequestCore allows it. No + // redirects are expected for download requests that are made without a + // renderer, which are currently exclusively resumption requests. Since there + // is no security policy being applied here, it's safer to block redirects and + // revisit if some previously unknown legitimate use case arises for redirects + // while resuming. + core_.OnWillAbort(DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE); + request_->CancelWithError(net::ERR_UNSAFE_REDIRECT); } void UrlDownloader::OnResponseStarted(net::URLRequest* request) { @@ -157,22 +135,7 @@ return; } - scoped_ptr<DownloadCreateInfo> create_info; - scoped_ptr<ByteStreamReader> stream_reader; - - handler_.OnResponseStarted(&create_info, &stream_reader); - - create_info->download_id = download_id_; - create_info->request_handle.reset( - new RequestHandle(weak_ptr_factory_.GetWeakPtr(), manager_, - base::SequencedTaskRunnerHandle::Get())); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DownloadManagerImpl::StartDownload, manager_, - base::Passed(&create_info), base::Passed(&stream_reader), - base::ResetAndReturn(&on_started_callback_))); - - if (request_->status().is_success()) + if (core_.OnResponseStarted(std::string())) StartReading(false); // Read the first chunk. else ResponseCompleted(); @@ -186,7 +149,7 @@ // doesn't use the buffer. scoped_refptr<net::IOBuffer> buf; int buf_size; - if (!handler_.OnWillRead(&buf, &buf_size, -1)) { + if (!core_.OnWillRead(&buf, &buf_size, -1)) { request_->CancelWithError(net::ERR_ABORTED); base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&UrlDownloader::ResponseCompleted, @@ -229,7 +192,7 @@ DCHECK(request_->status().is_success()); bool defer = false; - if (!handler_.OnReadCompleted(bytes_read, &defer)) { + if (!core_.OnReadCompleted(bytes_read, &defer)) { request_->CancelWithError(net::ERR_ABORTED); return; } else if (defer) { @@ -251,38 +214,43 @@ void UrlDownloader::ResponseCompleted() { DVLOG(1) << "ResponseCompleted: " << request_->url().spec(); - handler_.OnResponseCompleted(request_->status()); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DownloadManagerImpl::RemoveUrlDownloader, manager_, this)); + core_.OnResponseCompleted(request_->status()); + Destroy(); } -void UrlDownloader::ResumeReading() { - if (request_->status().is_success()) { +void UrlDownloader::OnStart( + scoped_ptr<DownloadCreateInfo> create_info, + scoped_ptr<ByteStreamReader> stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) { + create_info->request_handle.reset( + new RequestHandle(weak_ptr_factory_.GetWeakPtr(), manager_, + base::SequencedTaskRunnerHandle::Get())); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&DownloadManagerImpl::StartDownload, + manager_, base::Passed(&create_info), + base::Passed(&stream_reader), callback)); +} + +void UrlDownloader::OnReadyToRead() { + if (request_->status().is_success()) StartReading(false); // Read the next chunk (OK to complete synchronously). - } else { + else ResponseCompleted(); - } -} - -void UrlDownloader::CallStartedCallbackOnFailure( - DownloadInterruptReason result) { - if (on_started_callback_.is_null()) - return; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(base::ResetAndReturn(&on_started_callback_), nullptr, result)); } void UrlDownloader::PauseRequest() { - handler_.PauseRequest(); + core_.PauseRequest(); } void UrlDownloader::ResumeRequest() { - handler_.ResumeRequest(); + core_.ResumeRequest(); } void UrlDownloader::CancelRequest() { + Destroy(); +} + +void UrlDownloader::Destroy() { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&DownloadManagerImpl::RemoveUrlDownloader, manager_, this));
diff --git a/content/browser/download/url_downloader.h b/content/browser/download/url_downloader.h index 787feb4..475d97bf 100644 --- a/content/browser/download/url_downloader.h +++ b/content/browser/download/url_downloader.h
@@ -10,6 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "content/browser/download/download_request_core.h" +#include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_save_info.h" #include "content/public/browser/download_url_parameters.h" #include "content/public/common/referrer.h" @@ -17,26 +18,26 @@ #include "net/url_request/url_request.h" namespace content { +class ByteStreamReader; +struct DownloadCreateInfo; class DownloadManagerImpl; -class UrlDownloader : public net::URLRequest::Delegate { +class UrlDownloader : public net::URLRequest::Delegate, + public DownloadRequestCore::Delegate { public: - UrlDownloader( - scoped_ptr<net::URLRequest> request, - base::WeakPtr<DownloadManagerImpl> manager, - scoped_ptr<DownloadSaveInfo> save_info, - uint32_t download_id, - const DownloadUrlParameters::OnStartedCallback& on_started_callback); + UrlDownloader(scoped_ptr<net::URLRequest> request, + base::WeakPtr<DownloadManagerImpl> manager); ~UrlDownloader() override; static scoped_ptr<UrlDownloader> BeginDownload( base::WeakPtr<DownloadManagerImpl> download_manager, scoped_ptr<net::URLRequest> request, - const Referrer& referrer, - bool prefer_cache, - scoped_ptr<DownloadSaveInfo> save_info, - uint32_t download_id, - const DownloadUrlParameters::OnStartedCallback& started_callback); + const Referrer& referrer); + + private: + class RequestHandle; + + void Start(); // URLRequest::Delegate: void OnReceivedRedirect(net::URLRequest* request, @@ -48,24 +49,25 @@ void StartReading(bool is_continuation); void ResponseCompleted(); - void Start(); - void ResumeReading(); - - void CallStartedCallbackOnFailure(DownloadInterruptReason result); - - private: - class RequestHandle; + // DownloadRequestCore::Delegate + void OnStart( + scoped_ptr<DownloadCreateInfo> download_create_info, + scoped_ptr<ByteStreamReader> stream_reader, + const DownloadUrlParameters::OnStartedCallback& callback) override; + void OnReadyToRead() override; void PauseRequest(); void ResumeRequest(); void CancelRequest(); + // Called when the UrlDownloader is done with the request. Posts a task to + // remove itself from its download manager, which in turn would cause the + // UrlDownloader to be freed. + void Destroy(); + scoped_ptr<net::URLRequest> request_; base::WeakPtr<DownloadManagerImpl> manager_; - uint32_t download_id_; - DownloadUrlParameters::OnStartedCallback on_started_callback_; - - DownloadRequestCore handler_; + DownloadRequestCore core_; base::WeakPtrFactory<UrlDownloader> weak_ptr_factory_; };
diff --git a/content/browser/frame_host/frame_mojo_shell.cc b/content/browser/frame_host/frame_mojo_shell.cc index 7ec676ea..b72123b4 100644 --- a/content/browser/frame_host/frame_mojo_shell.cc +++ b/content/browser/frame_host/frame_mojo_shell.cc
@@ -14,7 +14,7 @@ #include "content/public/browser/site_instance.h" #include "content/public/common/content_client.h" -#if defined(OS_ANDROID) && defined(ENABLE_MOJO_MEDIA) +#if defined(OS_ANDROID) && defined(ENABLE_MOJO_CDM) #include "content/browser/media/android/provision_fetcher_impl.h" #endif @@ -24,7 +24,7 @@ void RegisterFrameMojoShellServices(ServiceRegistry* registry, RenderFrameHost* render_frame_host) { -#if defined(OS_ANDROID) && defined(ENABLE_MOJO_MEDIA) +#if defined(OS_ANDROID) && defined(ENABLE_MOJO_CDM) registry->AddService( base::Bind(&ProvisionFetcherImpl::Create, render_frame_host)); #endif
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc index bd13fd74..8a318070 100644 --- a/content/browser/frame_host/navigator_impl.cc +++ b/content/browser/frame_host/navigator_impl.cc
@@ -440,6 +440,11 @@ FrameTree* frame_tree = render_frame_host->frame_tree_node()->frame_tree(); bool oopifs_possible = SiteIsolationPolicy::AreCrossProcessFramesPossible(); + bool has_embedded_credentials = + params.url.has_username() || params.url.has_password(); + UMA_HISTOGRAM_BOOLEAN("Navigation.FrameHasEmbeddedCredentials", + has_embedded_credentials); + bool is_navigation_within_page = controller_->IsURLInPageNavigation( params.url, params.was_within_same_page, render_frame_host); if (ui::PageTransitionIsMainFrame(params.transition)) { @@ -461,6 +466,9 @@ // Run tasks that must execute just before the commit. delegate_->DidNavigateMainFramePreCommit(is_navigation_within_page); + + UMA_HISTOGRAM_BOOLEAN("Navigation.MainFrameHasEmbeddedCredentials", + has_embedded_credentials); } if (!oopifs_possible)
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc index 1438e6a..88b2e7a 100644 --- a/content/browser/gpu/compositor_util.cc +++ b/content/browser/gpu/compositor_util.cc
@@ -171,12 +171,6 @@ } // namespace -bool IsPropertyTreeVerificationEnabled() { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - return command_line.HasSwitch(cc::switches::kEnablePropertyTreeVerification); -} - int NumberOfRendererRasterThreads() { int num_processors = base::SysInfo::NumberOfProcessors();
diff --git a/content/browser/gpu/compositor_util.h b/content/browser/gpu/compositor_util.h index ebc5331..d6607151 100644 --- a/content/browser/gpu/compositor_util.h +++ b/content/browser/gpu/compositor_util.h
@@ -13,10 +13,6 @@ // Note: When adding a function here, please make sure the logic is not // duplicated in the renderer. -// Returns true if property tree verification is enabled (via flags or platform -// default). -CONTENT_EXPORT bool IsPropertyTreeVerificationEnabled(); - // Returns true if zero-copy uploads is on (via flags, or platform default). // Only one of one-copy and zero-copy can be enabled at a time. CONTENT_EXPORT bool IsZeroCopyUploadEnabled();
diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h index af4f4d8..5091e6f 100644 --- a/content/browser/gpu/gpu_process_host.h +++ b/content/browser/gpu/gpu_process_host.h
@@ -95,6 +95,9 @@ CONTENT_EXPORT static void RegisterGpuMainThreadFactory( GpuMainThreadFactoryFunction create); + // BrowserChildProcessHostDelegate implementation. + ServiceRegistry* GetServiceRegistry() override; + // Get the GPU process host for the GPU process with the given ID. Returns // null if the process no longer exists. static GpuProcessHost* FromID(int host_id); @@ -171,7 +174,6 @@ void OnProcessLaunched() override; void OnProcessLaunchFailed() override; void OnProcessCrashed(int exit_code) override; - ServiceRegistry* GetServiceRegistry() override; // Message handlers. void OnInitialized(bool result, const gpu::GPUInfo& gpu_info);
diff --git a/content/browser/gpu/gpu_process_host_ui_shim.h b/content/browser/gpu/gpu_process_host_ui_shim.h index 251ca2e7..e37eda0 100644 --- a/content/browser/gpu/gpu_process_host_ui_shim.h +++ b/content/browser/gpu/gpu_process_host_ui_shim.h
@@ -21,11 +21,11 @@ #include "base/threading/non_thread_safe.h" #include "build/build_config.h" #include "content/common/content_export.h" -#include "content/common/message_router.h" #include "content/public/common/gpu_memory_stats.h" #include "gpu/config/gpu_info.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_sender.h" +#include "ipc/message_router.h" #if defined(OS_MACOSX) struct GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params;
diff --git a/content/browser/loader/async_resource_handler.cc b/content/browser/loader/async_resource_handler.cc index bb2a918..f4394499 100644 --- a/content/browser/loader/async_resource_handler.cc +++ b/content/browser/loader/async_resource_handler.cc
@@ -449,26 +449,16 @@ buffer_->ShrinkLastAllocation(bytes_read); if (!sent_data_buffer_msg_) { - base::SharedMemoryHandle handle; - int size; - if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size)) - return false; - - // TODO(erikchen): Temporary debugging. http://crbug.com/527588. - CHECK_LE(size, kBufferSize); + base::SharedMemoryHandle handle = base::SharedMemory::DuplicateHandle( + buffer_->GetSharedMemory().handle()); filter->Send(new ResourceMsg_SetDataBuffer( - GetRequestID(), handle, size, filter->peer_pid())); + GetRequestID(), handle, buffer_->GetSharedMemory().mapped_size(), + filter->peer_pid())); sent_data_buffer_msg_ = true; } int data_offset = buffer_->GetLastAllocationOffset(); - // TODO(erikchen): Temporary debugging. http://crbug.com/527588. - CHECK_LE(data_offset, kBufferSize); - - filter->Send(new ResourceMsg_DataReceivedDebug(GetRequestID(), data_offset)); - filter->Send(new ResourceMsg_DataReceivedDebug2( - GetRequestID(), data_offset, bytes_read, encoded_data_length)); filter->Send(new ResourceMsg_DataReceived( GetRequestID(), data_offset, bytes_read, encoded_data_length)); ++pending_data_count_;
diff --git a/content/browser/loader/mime_type_resource_handler.cc b/content/browser/loader/mime_type_resource_handler.cc index 3c69c0a..c9a9bb8 100644 --- a/content/browser/loader/mime_type_resource_handler.cc +++ b/content/browser/loader/mime_type_resource_handler.cc
@@ -385,13 +385,9 @@ // Install download handler info->set_is_download(true); scoped_ptr<ResourceHandler> handler( - host_->CreateResourceHandlerForDownload( - request(), - true, // is_content_initiated - must_download, - DownloadItem::kInvalidId, - scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()), - DownloadUrlParameters::OnStartedCallback())); + host_->CreateResourceHandlerForDownload(request(), + true, // is_content_initiated + must_download)); return UseAlternateNextHandler(std::move(handler), std::string()); }
diff --git a/content/browser/loader/mime_type_resource_handler_unittest.cc b/content/browser/loader/mime_type_resource_handler_unittest.cc index 1b3ddcd..13d98cb 100644 --- a/content/browser/loader/mime_type_resource_handler_unittest.cc +++ b/content/browser/loader/mime_type_resource_handler_unittest.cc
@@ -91,10 +91,7 @@ scoped_ptr<ResourceHandler> CreateResourceHandlerForDownload( net::URLRequest* request, bool is_content_initiated, - bool must_download, - uint32_t id, - scoped_ptr<DownloadSaveInfo> save_info, - const DownloadUrlParameters::OnStartedCallback& started_cb) override { + bool must_download) override { return scoped_ptr<ResourceHandler>(new TestResourceHandler); }
diff --git a/content/browser/loader/resource_buffer.cc b/content/browser/loader/resource_buffer.cc index 9dc63d6..3f5a5c89 100644 --- a/content/browser/loader/resource_buffer.cc +++ b/content/browser/loader/resource_buffer.cc
@@ -61,17 +61,9 @@ return shared_mem_.memory() != NULL; } -bool ResourceBuffer::ShareToProcess( - base::ProcessHandle process_handle, - base::SharedMemoryHandle* shared_memory_handle, - int* shared_memory_size) { +base::SharedMemory& ResourceBuffer::GetSharedMemory() { CHECK(IsInitialized()); - - if (!shared_mem_.ShareToProcess(process_handle, shared_memory_handle)) - return false; - - *shared_memory_size = buf_size_; - return true; + return shared_mem_; } bool ResourceBuffer::CanAllocate() const {
diff --git a/content/browser/loader/resource_buffer.h b/content/browser/loader/resource_buffer.h index e323bf1..e071d19 100644 --- a/content/browser/loader/resource_buffer.h +++ b/content/browser/loader/resource_buffer.h
@@ -71,13 +71,8 @@ int max_allocation_size); bool IsInitialized() const; - // Returns a shared memory handle that can be passed to the given process. - // The shared memory handle is only intended to be interpretted by code - // running in the specified process. NOTE: The caller should ensure that - // this memory eventually be returned to the operating system. - bool ShareToProcess(base::ProcessHandle process_handle, - base::SharedMemoryHandle* shared_memory_handle, - int* shared_memory_size); + // Returns a reference to the underlying shared memory. + base::SharedMemory& GetSharedMemory(); // Returns true if Allocate will succeed. bool CanAllocate() const;
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 172cd5e..da012a1 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -69,6 +69,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/appcache_interfaces.h" #include "content/common/navigation_params.h" +#include "content/common/net/url_request_service_worker_data.h" #include "content/common/resource_messages.h" #include "content/common/site_isolation_policy.h" #include "content/common/ssl_status_serialization.h" @@ -350,20 +351,6 @@ child_id, path); } -DownloadInterruptReason CallbackAndReturn( - const DownloadUrlParameters::OnStartedCallback& started_cb, - DownloadInterruptReason interrupt_reason) { - if (started_cb.is_null()) - return interrupt_reason; - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind( - started_cb, static_cast<DownloadItem*>(NULL), interrupt_reason)); - - return interrupt_reason; -} - int GetCertID(net::URLRequest* request, int child_id) { if (request->ssl_info().cert.get()) { return CertStore::GetInstance()->StoreCert(request->ssl_info().cert.get(), @@ -765,14 +752,9 @@ int child_id, int render_view_route_id, int render_frame_route_id, - bool prefer_cache, - bool do_not_prompt_for_login, - scoped_ptr<DownloadSaveInfo> save_info, - uint32_t download_id, - const DownloadStartedCallback& started_callback) { + bool do_not_prompt_for_login) { if (is_shutdown_) - return CallbackAndReturn(started_callback, - DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN); + return DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN; const GURL& url = request->original_url(); @@ -784,21 +766,6 @@ SetReferrerForRequest(request.get(), referrer); - int extra_load_flags = net::LOAD_NORMAL; - if (prefer_cache) { - // If there is upload data attached, only retrieve from cache because there - // is no current mechanism to prompt the user for their consent for a - // re-post. For GETs, try to retrieve data from the cache and skip - // validating the entry if present. - if (request->get_upload() != NULL) - extra_load_flags |= net::LOAD_ONLY_FROM_CACHE; - else - extra_load_flags |= net::LOAD_PREFERRING_CACHE; - } else { - extra_load_flags |= net::LOAD_DISABLE_CACHE; - } - request->SetLoadFlags(request->load_flags() | extra_load_flags); - // We treat a download as a main frame load, and thus update the policy URL on // redirects. // @@ -811,20 +778,18 @@ // Check if the renderer is permitted to request the requested URL. if (!ChildProcessSecurityPolicyImpl::GetInstance()-> CanRequestURL(child_id, url)) { - VLOG(1) << "Denied unauthorized download request for " - << url.possibly_invalid_spec(); - return CallbackAndReturn(started_callback, - DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST); + DVLOG(1) << "Denied unauthorized download request for " + << url.possibly_invalid_spec(); + return DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST; } request_id_--; const net::URLRequestContext* request_context = context->GetRequestContext(); if (!request_context->job_factory()->IsHandledURL(url)) { - VLOG(1) << "Download request for unsupported protocol: " - << url.possibly_invalid_spec(); - return CallbackAndReturn(started_callback, - DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST); + DVLOG(1) << "Download request for unsupported protocol: " + << url.possibly_invalid_spec(); + return DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST; } ResourceRequestInfoImpl* extra_info = @@ -844,8 +809,7 @@ // From this point forward, the |DownloadResourceHandler| is responsible for // |started_callback|. scoped_ptr<ResourceHandler> handler(CreateResourceHandlerForDownload( - request.get(), is_content_initiated, true, download_id, - std::move(save_info), started_callback)); + request.get(), is_content_initiated, true)); BeginRequestInternal(std::move(request), std::move(handler)); @@ -874,12 +838,8 @@ ResourceDispatcherHostImpl::CreateResourceHandlerForDownload( net::URLRequest* request, bool is_content_initiated, - bool must_download, - uint32_t id, - scoped_ptr<DownloadSaveInfo> save_info, - const DownloadUrlParameters::OnStartedCallback& started_cb) { - scoped_ptr<ResourceHandler> handler(new DownloadResourceHandler( - id, request, started_cb, std::move(save_info))); + bool must_download) { + scoped_ptr<ResourceHandler> handler(new DownloadResourceHandler(request)); if (delegate_) { const ResourceRequestInfoImpl* request_info( ResourceRequestInfoImpl::ForRequest(request)); @@ -1493,6 +1453,11 @@ request_data.first_party_for_cookies); new_request->set_initiator(request_data.request_initiator); + if (request_data.originated_from_service_worker) { + new_request->SetUserData(URLRequestServiceWorkerData::kUserDataKey, + new URLRequestServiceWorkerData()); + } + // If the request is a MAIN_FRAME request, the first-party URL gets updated on // redirects. if (request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME) {
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h index 21d99f7..6d5602b 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.h +++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -128,19 +128,6 @@ // ResourceDispatcherHost implementation: void SetDelegate(ResourceDispatcherHostDelegate* delegate) override; void SetAllowCrossOriginAuthPrompt(bool value) override; - DownloadInterruptReason BeginDownload( - scoped_ptr<net::URLRequest> request, - const Referrer& referrer, - bool is_content_initiated, - ResourceContext* context, - int child_id, - int render_view_route_id, - int render_frame_route_id, - bool prefer_cache, - bool do_not_prompt_for_login, - scoped_ptr<DownloadSaveInfo> save_info, - uint32_t download_id, - const DownloadStartedCallback& started_callback) override; void ClearLoginDelegateForRequest(net::URLRequest* request) override; // Puts the resource dispatcher host in an inactive state (unable to begin @@ -162,6 +149,15 @@ bool OnMessageReceived(const IPC::Message& message, ResourceMessageFilter* filter); + DownloadInterruptReason BeginDownload(scoped_ptr<net::URLRequest> request, + const Referrer& referrer, + bool is_content_initiated, + ResourceContext* context, + int child_id, + int render_view_route_id, + int render_frame_route_id, + bool do_not_prompt_for_login); + // Initiates a save file from the browser process (as opposed to a resource // request from the renderer or another child process). void BeginSaveFile(const GURL& url, @@ -272,10 +268,7 @@ virtual scoped_ptr<ResourceHandler> CreateResourceHandlerForDownload( net::URLRequest* request, bool is_content_initiated, - bool must_download, - uint32_t id, - scoped_ptr<DownloadSaveInfo> save_info, - const DownloadUrlParameters::OnStartedCallback& started_cb); + bool must_download); // 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
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc index 79a8f80..4b1eba5 100644 --- a/content/browser/loader/resource_dispatcher_host_unittest.cc +++ b/content/browser/loader/resource_dispatcher_host_unittest.cc
@@ -1169,8 +1169,6 @@ void ResourceDispatcherHostTest::MakeWebContentsAssociatedDownloadRequest( int request_id, const GURL& url) { - scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); - save_info->prompt_for_save_location = false; net::URLRequestContext* request_context = browser_context_->GetResourceContext()->GetRequestContext(); scoped_ptr<net::URLRequest> request( @@ -1180,9 +1178,7 @@ browser_context_->GetResourceContext(), web_contents_->GetRenderProcessHost()->GetID(), web_contents_->GetRoutingID(), - web_contents_->GetMainFrame()->GetRoutingID(), false, - false, std::move(save_info), DownloadItem::kInvalidId, - ResourceDispatcherHostImpl::DownloadStartedCallback()); + web_contents_->GetMainFrame()->GetRoutingID(), false); } void ResourceDispatcherHostTest::CancelRequest(int request_id) {
diff --git a/content/browser/loader/resource_request_info_impl.cc b/content/browser/loader/resource_request_info_impl.cc index 8511653..82018b7 100644 --- a/content/browser/loader/resource_request_info_impl.cc +++ b/content/browser/loader/resource_request_info_impl.cc
@@ -8,6 +8,7 @@ #include "content/browser/loader/global_routing_id.h" #include "content/browser/loader/resource_message_filter.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/net/url_request_service_worker_data.h" #include "content/common/net/url_request_user_data.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/global_request_id.h" @@ -106,6 +107,13 @@ return true; } +// static +bool ResourceRequestInfo::OriginatedFromServiceWorker( + const net::URLRequest* request) { + return !!request->GetUserData( + content::URLRequestServiceWorkerData::kUserDataKey); +} + // ---------------------------------------------------------------------------- // ResourceRequestInfoImpl
diff --git a/content/browser/media/media_canplaytype_browsertest.cc b/content/browser/media/media_canplaytype_browsertest.cc index b35a082..5639d6d8 100644 --- a/content/browser/media/media_canplaytype_browsertest.cc +++ b/content/browser/media/media_canplaytype_browsertest.cc
@@ -21,25 +21,29 @@ const char kNot[] = ""; #if defined(USE_PROPRIETARY_CODECS) -const char kPropProbably[] = "probably"; -const char kPropMaybe[] = "maybe"; +const char* kPropProbably = kProbably; +const char* kPropMaybe = kMaybe; #else -const char kPropProbably[] = ""; -const char kPropMaybe[] = ""; +const char* kPropProbably = kNot; +const char* kPropMaybe = kNot; #endif // USE_PROPRIETARY_CODECS #if !defined(OS_ANDROID) -const char kOggVideoProbably[] = "probably"; -const char kOggVideoMaybe[] = "maybe"; -const char kTheoraProbably[] = "probably"; -const char kOggOpusProbably[] = "probably"; +const char* kOggVideoProbably = kProbably; +const char* kOggVideoMaybe = kMaybe; +const char* kTheoraProbably = kProbably; +const char* kOggOpusProbably = kProbably; const char* kMpeg2AacProbably = kPropProbably; +const char* kHlsProbably = kNot; +const char* kHlsMaybe = kNot; #else -const char kOggVideoProbably[] = ""; -const char kOggVideoMaybe[] = ""; -const char kTheoraProbably[] = ""; -const char kOggOpusProbably[] = ""; -const char kMpeg2AacProbably[] = ""; +const char* kOggVideoProbably = kNot; +const char* kOggVideoMaybe = kNot; +const char* kTheoraProbably = kNot; +const char* kOggOpusProbably = kNot; +const char* kMpeg2AacProbably = kNot; +const char* kHlsProbably = kProbably; +const char* kHlsMaybe = kMaybe; #endif // !OS_ANDROID #if BUILDFLAG(ENABLE_HEVC_DEMUXING) @@ -136,6 +140,16 @@ EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.300.30\"'")); EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.-1.30\"'")); EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.x.30\"'")); + // Old-style avc1 codec ids are supported only for video/mp2t container. + if (mime != "video/mp2t") { + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.66.13\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.77.30\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc1.100.40\"'")); + } + // Old-style codec ids are not supported for avc3 codec strings. + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3.66.13\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3.77.30\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3.100.40\"'")); // AAC codecs must be followed by one or two valid hexadecimal numbers. EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.no\"'")); @@ -572,7 +586,6 @@ EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc1.4D401E\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"avc1.66.30\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.40.2\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mpeg; codecs=\"mp4a.40.02\"'")); @@ -587,7 +600,6 @@ EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc1.4D401E\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"avc1.66.30\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"mp4a.40.2\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp3; codecs=\"mp4a.40.02\"'")); @@ -602,7 +614,6 @@ EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc1.4D401E\"'")); EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"avc1.66.30\"'")); EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"mp4a.40.2\"'")); EXPECT_EQ(kNot, CanPlay("'audio/x-mp3; codecs=\"mp4a.40.02\"'")); @@ -629,11 +640,6 @@ EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3.42801E\"'")); EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3.42C01E\"'")); - // Old-style avc1 codec ids are supported only for video/mp2t mime type. - EXPECT_EQ(kNot, CanPlay("'video/mp4; codecs=\"avc1.66.13\"'")); - EXPECT_EQ(kNot, CanPlay("'video/mp4; codecs=\"avc1.77.30\"'")); - EXPECT_EQ(kNot, CanPlay("'video/mp4; codecs=\"avc1.100.40\"'")); - EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.40.2\"'")); EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"mp4a.40.02\"'")); EXPECT_EQ(kPropProbably, @@ -710,11 +716,6 @@ EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3.42801E\"'")); EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"avc3.42C01E\"'")); - // Old-style avc1 codec ids are supported only for video/mp2t mime type. - EXPECT_EQ(kNot, CanPlay("'video/x-m4v; codecs=\"avc1.66.13\"'")); - EXPECT_EQ(kNot, CanPlay("'video/x-m4v; codecs=\"avc1.77.30\"'")); - EXPECT_EQ(kNot, CanPlay("'video/x-m4v; codecs=\"avc1.100.40\"'")); - EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"mp4a.40.2\"'")); EXPECT_EQ(kPropProbably, CanPlay("'video/x-m4v; codecs=\"mp4a.40.02\"'")); EXPECT_EQ(kPropProbably, @@ -782,7 +783,6 @@ EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc1.4D401E\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"avc1.66.30\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"hev1.1.6.L93.B0\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"hvc1.1.6.L93.B0\"'")); @@ -813,7 +813,6 @@ EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"avc1.4D401E\"'")); EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"avc3.64001F\"'")); - EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"avc1.66.30\"'")); EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"hev1.1.6.L93.B0\"'")); EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"hvc1.1.6.L93.B0\"'")); @@ -1181,57 +1180,50 @@ } IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_HLS) { - // HLS are supported only on Android IceCreamSandwich and above (API level 14) - std::string probablyCanPlayHLS = kNot; - std::string maybeCanPlayHLS = kNot; -#if defined(OS_ANDROID) - if (base::android::BuildInfo::GetInstance()->sdk_int() > 13) { - probablyCanPlayHLS = kProbably; - maybeCanPlayHLS = kMaybe; - } -#endif - EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/x-mpegurl'")); + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl'")); - EXPECT_EQ(maybeCanPlayHLS, - CanPlay("'application/x-mpegurl; codecs=\"avc1\"'")); - EXPECT_EQ(maybeCanPlayHLS, - CanPlay("'application/x-mpegurl; codecs=\"avc3\"'")); - EXPECT_EQ(maybeCanPlayHLS, - CanPlay("'application/x-mpegurl; codecs=\"mp4a.40\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc1\"'")); + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc3\"'")); + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"mp4a.40\"'")); + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc1, mp4a.40\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc3, mp4a.40\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc1.42E01E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc1.42101E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc1.42701E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc1.42F01E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc3.42E01E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc3.42801E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc3.42C01E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"mp4a.40.2\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"mp4a.40.02\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ( + kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc1.42E01E, mp4a.40.2\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ( + kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc1.42E01E, mp4a.40.02\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ( + kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc3.42E01E, mp4a.40.5\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ( + kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc3.42E01E, mp4a.40.05\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ( + kHlsProbably, CanPlay("'application/x-mpegurl; codecs=\"avc3.42E01E, mp4a.40.29\"'")); EXPECT_EQ(kNot, @@ -1250,75 +1242,81 @@ EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"mp4a.a5\"'")); EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"mp4a.a6\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc1, mp4a.40.2\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc1, mp4a.40.02\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc3, mp4a.40.2\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc3, mp4a.40.02\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ( + kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc1.42E01E, mp4a.40\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ( + kHlsMaybe, CanPlay("'application/x-mpegurl; codecs=\"avc3.42E01E, mp4a.40\"'")); TestMPEGUnacceptableCombinations("application/x-mpegurl"); - EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/vnd.apple.mpegurl'")); + EXPECT_EQ(kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ(kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ( + kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1, mp4a.40\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ( + kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3, mp4a.40\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.42E01E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.42101E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.42701E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1.42F01E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.42E01E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.42801E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3.42C01E\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40.2\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40.02\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40.5\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40.05\"'")); - EXPECT_EQ(probablyCanPlayHLS, + EXPECT_EQ(kHlsProbably, CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.40.29\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ( + kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1, mp4a.40.2\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ( + kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc1, mp4a.40.02\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ( + kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3, mp4a.40.2\"'")); - EXPECT_EQ(maybeCanPlayHLS, + EXPECT_EQ( + kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; codecs=\"avc3, mp4a.40.02\"'")); - EXPECT_EQ(maybeCanPlayHLS, - CanPlay("'application/vnd.apple.mpegurl; " - "codecs=\"avc1.42E01E, mp4a.40\"'")); - EXPECT_EQ(maybeCanPlayHLS, - CanPlay("'application/vnd.apple.mpegurl; " - "codecs=\"avc3.42E01E, mp4a.40\"'")); + EXPECT_EQ(kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; " + "codecs=\"avc1.42E01E, mp4a.40\"'")); + EXPECT_EQ(kHlsMaybe, CanPlay("'application/vnd.apple.mpegurl; " + "codecs=\"avc3.42E01E, mp4a.40\"'")); EXPECT_EQ(kNot, CanPlay("'application/vnd.apple.mpegurl; codecs=\"hev1.1.6.L93.B0\"'"));
diff --git a/content/browser/media/media_internals_unittest.cc b/content/browser/media/media_internals_unittest.cc index 2ab0539..33d0cb8 100644 --- a/content/browser/media/media_internals_unittest.cc +++ b/content/browser/media/media_internals_unittest.cc
@@ -122,7 +122,6 @@ CaptureApiTypeStringMap m; #if defined(OS_LINUX) m[VideoCaptureDeviceName::V4L2_SINGLE_PLANE] = "V4L2 SPLANE"; - m[VideoCaptureDeviceName::V4L2_MULTI_PLANE] = "V4L2 MPLANE"; #elif defined(OS_WIN) m[VideoCaptureDeviceName::MEDIA_FOUNDATION] = "Media Foundation"; m[VideoCaptureDeviceName::DIRECT_SHOW] = "Direct Show";
diff --git a/content/browser/mojo/mojo_app_connection_impl.cc b/content/browser/mojo/mojo_app_connection_impl.cc index 6d130864..14cda96 100644 --- a/content/browser/mojo/mojo_app_connection_impl.cc +++ b/content/browser/mojo/mojo_app_connection_impl.cc
@@ -16,7 +16,7 @@ const char kBrowserMojoAppUrl[] = "system:content_browser"; namespace { -void OnGotRemoteIDs(uint32_t remote_id, uint32_t content_handler_id) {} +void OnGotInstanceID(uint32_t remote_id) {} } // namespace // static @@ -33,7 +33,7 @@ url, requestor_url, mojo::GetProxy(&interfaces_), mojo::shell::mojom::InterfaceProviderPtr(), mojo::shell::GetPermissiveCapabilityFilter(), - base::Bind(&OnGotRemoteIDs)); + base::Bind(&OnGotInstanceID)); } MojoAppConnectionImpl::~MojoAppConnectionImpl() {
diff --git a/content/browser/mojo/mojo_shell_context.cc b/content/browser/mojo/mojo_shell_context.cc index 23bf750..980c7a6 100644 --- a/content/browser/mojo/mojo_shell_context.cc +++ b/content/browser/mojo/mojo_shell_context.cc
@@ -13,6 +13,7 @@ #include "base/thread_task_runner_handle.h" #include "content/browser/gpu/gpu_process_host.h" #include "content/common/gpu/gpu_process_launch_causes.h" +#include "content/common/mojo/static_application_loader.h" #include "content/common/process_control.mojom.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" @@ -27,7 +28,6 @@ #include "mojo/shell/connect_to_application_params.h" #include "mojo/shell/identity.h" #include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/static_application_loader.h" namespace content { @@ -216,7 +216,7 @@ for (const auto& entry : apps) { application_manager_->SetLoaderForURL( scoped_ptr<mojo::shell::ApplicationLoader>( - new mojo::shell::StaticApplicationLoader(entry.second)), + new StaticApplicationLoader(entry.second)), entry.first); } @@ -277,7 +277,7 @@ params->set_source( mojo::shell::Identity(requestor_url, std::string(), mojo::shell::GetPermissiveCapabilityFilter())); - params->SetTarget(mojo::shell::Identity(url, std::string(), filter)); + params->set_target(mojo::shell::Identity(url, std::string(), filter)); params->set_remote_interfaces(std::move(request)); params->set_local_interfaces(std::move(exposed_services)); params->set_on_application_end(base::Bind(&base::DoNothing));
diff --git a/content/browser/permissions/permission_service_impl.cc b/content/browser/permissions/permission_service_impl.cc index 51e75ad..8d07e36 100644 --- a/content/browser/permissions/permission_service_impl.cc +++ b/content/browser/permissions/permission_service_impl.cc
@@ -109,7 +109,6 @@ void PermissionServiceImpl::RequestPermission( PermissionName permission, const mojo::String& origin, - bool user_gesture, const PermissionStatusCallback& callback) { // This condition is valid if the call is coming from a ChildThread instead of // a RenderFrame. Some consumers of the service run in Workers and some in @@ -132,7 +131,6 @@ PermissionNameToPermissionType(permission), context_->render_frame_host(), GURL(origin.get()), - user_gesture, // TODO(mlamouri): should be removed (crbug.com/423770) base::Bind(&PermissionServiceImpl::OnRequestPermissionResponse, weak_factory_.GetWeakPtr(), pending_request_id)); @@ -156,7 +154,6 @@ void PermissionServiceImpl::RequestPermissions( mojo::Array<PermissionName> permissions, const mojo::String& origin, - bool user_gesture, const PermissionsStatusCallback& callback) { if (permissions.is_null()) { callback.Run(mojo::Array<PermissionStatus>()); @@ -193,7 +190,6 @@ types, context_->render_frame_host(), GURL(origin.get()), - user_gesture, // TODO(mlamouri): should be removed (crbug.com/423770) base::Bind(&PermissionServiceImpl::OnRequestPermissionsResponse, weak_factory_.GetWeakPtr(), pending_request_id));
diff --git a/content/browser/permissions/permission_service_impl.h b/content/browser/permissions/permission_service_impl.h index 13761dac..098a9e3 100644 --- a/content/browser/permissions/permission_service_impl.h +++ b/content/browser/permissions/permission_service_impl.h
@@ -74,11 +74,9 @@ const PermissionStatusCallback& callback) override; void RequestPermission(PermissionName permission, const mojo::String& origin, - bool user_gesture, const PermissionStatusCallback& callback) override; void RequestPermissions(mojo::Array<PermissionName> permissions, const mojo::String& origin, - bool user_gesture, const PermissionsStatusCallback& callback) override; void RevokePermission(PermissionName permission, const mojo::String& origin,
diff --git a/content/browser/push_messaging/push_messaging_message_filter.cc b/content/browser/push_messaging/push_messaging_message_filter.cc index 844b724..a2eb895 100644 --- a/content/browser/push_messaging/push_messaging_message_filter.cc +++ b/content/browser/push_messaging/push_messaging_message_filter.cc
@@ -428,7 +428,7 @@ ->GetPermissionManager() ->RequestPermission( PermissionType::PUSH_MESSAGING, render_frame_host, - data.requesting_origin, false /* user_gesture */, + data.requesting_origin, base::Bind(&PushMessagingMessageFilter::Core:: DidRequestPermissionInIncognito, weak_factory_ui_to_ui_.GetWeakPtr(), data)); @@ -917,9 +917,8 @@ request_id, blink::WebPushError::ErrorTypeNotSupported)); return; } - GURL embedding_origin = requesting_origin; - permission_status = push_service->GetPermissionStatus( - requesting_origin, embedding_origin, user_visible); + permission_status = + push_service->GetPermissionStatus(requesting_origin, user_visible); } else if (is_incognito()) { // Return prompt, so the website can't detect incognito mode. permission_status = blink::WebPushPermissionStatusPrompt;
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc index b96fc97..5b01355 100644 --- a/content/browser/renderer_host/compositor_impl_android.cc +++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -354,8 +354,6 @@ command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking)); settings.initial_debug_state.show_fps_counter = command_line->HasSwitch(cc::switches::kUIShowFPSCounter); - if (command_line->HasSwitch(cc::switches::kDisableCompositorPropertyTrees)) - settings.use_property_trees = false; settings.single_thread_proxy_scheduler = true; settings.use_compositor_animation_timelines = !command_line->HasSwitch(
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc index 8ba9287..9f79fb4cb 100644 --- a/content/browser/renderer_host/input/input_router_impl.cc +++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -53,6 +53,8 @@ case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: return "NOT_CONSUMED"; case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: return "NO_CONSUMER_EXISTS"; case INPUT_EVENT_ACK_STATE_IGNORED: return "IGNORED"; + case INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING: + return "SET_NON_BLOCKING"; } DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName."; return ""; @@ -410,8 +412,9 @@ const WebInputEvent* event_to_send = event_in_viewport ? event_in_viewport.get() : &input_event; - if (Send(new InputMsg_HandleInputEvent(routing_id(), event_to_send, - latency_info))) { + if (Send(new InputMsg_HandleInputEvent( + routing_id(), event_to_send, latency_info, + InputEventDispatchType::DISPATCH_TYPE_NORMAL))) { // Ack messages for ignored ack event types should never be sent by the // renderer. Consequently, such event types should not affect event time // or in-flight event count metrics.
diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc index 7655f222..bdd29374 100644 --- a/content/browser/renderer_host/input/input_router_impl_unittest.cc +++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -167,6 +167,18 @@ browser_context_.reset(); } + void SetUpForGestureBasedWheelScrolling(bool enabled) { + CHECK(!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableWheelGestures) && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableWheelGestures)); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + enabled ? switches::kEnableWheelGestures + : switches::kDisableWheelGestures); + TearDown(); + SetUp(); + } + void SetUpForTouchAckTimeoutTest(int desktop_timeout_ms, int mobile_timeout_ms) { config_.touch_config.desktop_touch_ack_timeout_delay = @@ -936,6 +948,8 @@ #endif // defined(USE_AURA) TEST_F(InputRouterImplTest, UnhandledWheelEvent) { + SetUpForGestureBasedWheelScrolling(false); + // Simulate wheel events. SimulateWheelEvent(0, 0, 0, -5, 0, false); // sent directly SimulateWheelEvent(0, 0, 0, -10, 0, false); // enqueued @@ -959,8 +973,49 @@ InputMsg_HandleInputEvent::ID)); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + // Indicate that the wheel event was unhandled. + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + // Check that the correct unhandled wheel event was received. + EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state()); + EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -10); +} + +TEST_F(InputRouterImplTest, UnhandledWheelEventWithGestureScrolling) { + SetUpForGestureBasedWheelScrolling(true); + + // Simulate wheel events. + SimulateWheelEvent(0, 0, 0, -5, 0, false); // sent directly + SimulateWheelEvent(0, 0, 0, -10, 0, false); // enqueued + + // Check that only the first event was sent. + EXPECT_TRUE( + process_->sink().GetUniqueMessageMatching(InputMsg_HandleInputEvent::ID)); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + + // Indicate that the wheel event was unhandled. + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Check that the ack for the MouseWheel and ScrollBegin + // were processed. + EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); + + // There should be a ScrollBegin and ScrollUpdate, MouseWheel sent + EXPECT_EQ(3U, GetSentMessageCountAndResetSink()); + EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_CONSUMED); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Check that the correct unhandled wheel event was received. + EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state()); + EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -10); } TEST_F(InputRouterImplTest, TouchTypesIgnoringAck) {
diff --git a/content/browser/renderer_host/input/mouse_wheel_event_queue.cc b/content/browser/renderer_host/input/mouse_wheel_event_queue.cc index 7f0075b..6d5e787 100644 --- a/content/browser/renderer_host/input/mouse_wheel_event_queue.cc +++ b/content/browser/renderer_host/input/mouse_wheel_event_queue.cc
@@ -124,8 +124,9 @@ // wheel based cancel current one by sending a ScrollEnd. if (scroll_end_timer_.IsRunning() && gesture_event.event.sourceDevice != blink::WebGestureDeviceTouchpad) { + base::Closure task = scroll_end_timer_.user_task(); scroll_end_timer_.Reset(); - SendScrollEnd(); + task.Run(); } scrolling_device_ = gesture_event.event.sourceDevice; } else if (scrolling_device_ == gesture_event.event.sourceDevice && @@ -153,11 +154,13 @@ client_->SendMouseWheelEventImmediately(send_event); } -void MouseWheelEventQueue::SendScrollEnd() { +void MouseWheelEventQueue::SendScrollEnd( + blink::WebGestureEvent::ScrollUnits units) { GestureEventWithLatencyInfo scroll_end; scroll_end.event.type = WebInputEvent::GestureScrollEnd; scroll_end.event.sourceDevice = blink::WebGestureDeviceTouchpad; scroll_end.event.resendingPluginId = -1; + scroll_end.event.data.scrollEnd.deltaUnits = units; SendGesture(scroll_end); } @@ -181,9 +184,12 @@ if (scroll_end_timer_.IsRunning()) { scroll_end_timer_.Reset(); } else { - scroll_end_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds( - scroll_transaction_ms_), - this, &MouseWheelEventQueue::SendScrollEnd); + scroll_end_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(scroll_transaction_ms_), + base::Bind(&MouseWheelEventQueue::SendScrollEnd, + base::Unretained(this), + gesture.event.data.scrollUpdate.deltaUnits)); } break; case WebInputEvent::GestureScrollEnd:
diff --git a/content/browser/renderer_host/input/mouse_wheel_event_queue.h b/content/browser/renderer_host/input/mouse_wheel_event_queue.h index b91bad6..51c2bdf 100644 --- a/content/browser/renderer_host/input/mouse_wheel_event_queue.h +++ b/content/browser/renderer_host/input/mouse_wheel_event_queue.h
@@ -74,7 +74,7 @@ private: void TryForwardNextEventToRenderer(); - void SendScrollEnd(); + void SendScrollEnd(blink::WebGestureEvent::ScrollUnits units); void SendGesture(const GestureEventWithLatencyInfo& gesture); MouseWheelEventQueueClient* client_;
diff --git a/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc b/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc index d24e8bd..d0d010c 100644 --- a/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc +++ b/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc
@@ -51,6 +51,7 @@ void SendGestureEvent(const GestureEventWithLatencyInfo& event) override { sent_events_.push_back(event.event); + sent_gesture_events_.push_back(event.event); } void OnMouseWheelEventAck(const MouseWheelEventWithLatencyInfo& event, @@ -72,11 +73,16 @@ std::vector<WebInputEvent>& all_sent_events() { return sent_events_; } + std::vector<WebGestureEvent>& sent_gesture_events() { + return sent_gesture_events_; + } + const WebMouseWheelEvent& acked_event() const { return last_acked_event_; } size_t GetAndResetSentEventCount() { size_t count = sent_events_.size(); sent_events_.clear(); + sent_gesture_events_.clear(); return count; } @@ -90,10 +96,15 @@ queue_->ProcessMouseWheelAck(ack_result, ui::LatencyInfo()); } - void SendMouseWheel(float x, float y, float dX, float dY, int modifiers) { + void SendMouseWheel(float x, + float y, + float dX, + float dY, + int modifiers, + bool high_precision) { queue_->QueueEvent(MouseWheelEventWithLatencyInfo( SyntheticWebMouseWheelEventBuilder::Build(x, y, dX, dY, modifiers, - false))); + high_precision))); } void SendGestureEvent(WebInputEvent::Type type) { @@ -110,8 +121,47 @@ base::MessageLoop::current()->Run(); } + void GestureSendingTest(bool high_precision) { + const WebGestureEvent::ScrollUnits scroll_units = + high_precision ? WebGestureEvent::PrecisePixels + : WebGestureEvent::Pixels; + SendMouseWheel(10, 10, 1, 1, 0, high_precision); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_TRUE(event_in_flight()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + + // The second mouse wheel should not be sent since one is already in queue. + SendMouseWheel(10, 10, 5, 5, 0, high_precision); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_TRUE(event_in_flight()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + + // Receive an ACK for the mouse wheel event and release the next + // mouse wheel event. + SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_TRUE(event_in_flight()); + EXPECT_EQ(WebInputEvent::MouseWheel, acked_event().type); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + EXPECT_EQ(3U, all_sent_events().size()); + EXPECT_EQ(WebInputEvent::GestureScrollBegin, all_sent_events()[0].type); + EXPECT_EQ(scroll_units, + sent_gesture_events()[0].data.scrollBegin.deltaHintUnits); + EXPECT_EQ(WebInputEvent::GestureScrollUpdate, all_sent_events()[1].type); + EXPECT_EQ(scroll_units, + sent_gesture_events()[1].data.scrollUpdate.deltaUnits); + EXPECT_EQ(WebInputEvent::MouseWheel, all_sent_events()[2].type); + EXPECT_EQ(3U, GetAndResetSentEventCount()); + + RunTasksAndWait(DefaultScrollEndTimeoutDelay() * 2); + EXPECT_EQ(1U, all_sent_events().size()); + EXPECT_EQ(WebInputEvent::GestureScrollEnd, all_sent_events()[0].type); + EXPECT_EQ(scroll_units, sent_gesture_events()[0].data.scrollEnd.deltaUnits); + } + scoped_ptr<MouseWheelEventQueue> queue_; std::vector<WebInputEvent> sent_events_; + std::vector<WebGestureEvent> sent_gesture_events_; size_t acked_event_count_; InputEventAckState last_acked_event_state_; base::MessageLoopForUI message_loop_; @@ -120,13 +170,13 @@ // Tests that mouse wheel events are queued properly. TEST_F(MouseWheelEventQueueTest, Basic) { - SendMouseWheel(10, 10, 1, 1, 0); + SendMouseWheel(10, 10, 1, 1, 0, false); EXPECT_EQ(0U, queued_event_count()); EXPECT_TRUE(event_in_flight()); EXPECT_EQ(1U, GetAndResetSentEventCount()); // The second mouse wheel should not be sent since one is already in queue. - SendMouseWheel(10, 10, 5, 5, 0); + SendMouseWheel(10, 10, 5, 5, 0, false); EXPECT_EQ(1U, queued_event_count()); EXPECT_TRUE(event_in_flight()); EXPECT_EQ(0U, GetAndResetSentEventCount()); @@ -150,38 +200,17 @@ TEST_F(MouseWheelEventQueueTest, GestureSending) { SetUpForGestureTesting(true); - SendMouseWheel(10, 10, 1, 1, 0); - EXPECT_EQ(0U, queued_event_count()); - EXPECT_TRUE(event_in_flight()); - EXPECT_EQ(1U, GetAndResetSentEventCount()); + GestureSendingTest(false); +} - // The second mouse wheel should not be sent since one is already in queue. - SendMouseWheel(10, 10, 5, 5, 0); - EXPECT_EQ(1U, queued_event_count()); - EXPECT_TRUE(event_in_flight()); - EXPECT_EQ(0U, GetAndResetSentEventCount()); - - // Receive an ACK for the mouse wheel event and release the next - // mouse wheel event. - SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(0U, queued_event_count()); - EXPECT_TRUE(event_in_flight()); - EXPECT_EQ(WebInputEvent::MouseWheel, acked_event().type); - EXPECT_EQ(1U, GetAndResetAckedEventCount()); - EXPECT_EQ(3U, all_sent_events().size()); - EXPECT_EQ(WebInputEvent::GestureScrollBegin, all_sent_events()[0].type); - EXPECT_EQ(WebInputEvent::GestureScrollUpdate, all_sent_events()[1].type); - EXPECT_EQ(WebInputEvent::MouseWheel, all_sent_events()[2].type); - EXPECT_EQ(3U, GetAndResetSentEventCount()); - - RunTasksAndWait(DefaultScrollEndTimeoutDelay() * 2); - EXPECT_EQ(1U, all_sent_events().size()); - EXPECT_EQ(WebInputEvent::GestureScrollEnd, all_sent_events()[0].type); +TEST_F(MouseWheelEventQueueTest, GestureSendingPrecisePixels) { + SetUpForGestureTesting(true); + GestureSendingTest(false); } TEST_F(MouseWheelEventQueueTest, GestureSendingInterrupted) { SetUpForGestureTesting(true); - SendMouseWheel(10, 10, 1, 1, 0); + SendMouseWheel(10, 10, 1, 1, 0, false); EXPECT_EQ(0U, queued_event_count()); EXPECT_TRUE(event_in_flight()); EXPECT_EQ(1U, GetAndResetSentEventCount()); @@ -203,7 +232,7 @@ EXPECT_EQ(WebInputEvent::GestureScrollEnd, all_sent_events()[0].type); EXPECT_EQ(1U, GetAndResetSentEventCount()); - SendMouseWheel(10, 10, 1, 1, 0); + SendMouseWheel(10, 10, 1, 1, 0, false); EXPECT_EQ(0U, queued_event_count()); EXPECT_TRUE(event_in_flight()); EXPECT_EQ(1U, GetAndResetSentEventCount()); @@ -220,7 +249,7 @@ SendGestureEvent(WebInputEvent::GestureScrollEnd); EXPECT_EQ(0U, all_sent_events().size()); - SendMouseWheel(10, 10, 1, 1, 0); + SendMouseWheel(10, 10, 1, 1, 0, false); EXPECT_EQ(0U, queued_event_count()); EXPECT_TRUE(event_in_flight()); EXPECT_EQ(1U, GetAndResetSentEventCount());
diff --git a/content/browser/renderer_host/input/non_blocking_event_browsertest.cc b/content/browser/renderer_host/input/non_blocking_event_browsertest.cc new file mode 100644 index 0000000..c6819140 --- /dev/null +++ b/content/browser/renderer_host/input/non_blocking_event_browsertest.cc
@@ -0,0 +1,193 @@ +// 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 <utility> + +#include "base/auto_reset.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_view_base.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/input/synthetic_web_input_event_builders.h" +#include "content/common/input_messages.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/common/content_switches.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/event_switches.h" +#include "ui/events/latency_info.h" + +using blink::WebInputEvent; + +namespace { + +const char kNonBlockingEventDataURL[] = + "data:text/html;charset=utf-8," + "<!DOCTYPE html>" + "<meta name='viewport' content='width=device-width'/>" + "<style>" + "html, body {" + " margin: 0;" + "}" + ".spacer { height: 1000px; }" + "</style>" + "<div class=spacer></div>" + "<script>" + " document.addEventListener('wheel', function(e) { while(true) {} }, " + "{'passive': true});" + " document.addEventListener('touchstart', function(e) { while(true) {} }, " + "{'passive': true});" + " document.title='ready';" + "</script>"; + +} // namespace + +namespace content { + +class NonBlockingEventBrowserTest : public ContentBrowserTest { + public: + NonBlockingEventBrowserTest() {} + ~NonBlockingEventBrowserTest() override {} + + RenderWidgetHostImpl* GetWidgetHost() { + return RenderWidgetHostImpl::From( + shell()->web_contents()->GetRenderViewHost()->GetWidget()); + } + + void OnSyntheticGestureCompleted(SyntheticGesture::Result result) { + EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result); + } + + protected: + void LoadURL() { + const GURL data_url(kNonBlockingEventDataURL); + NavigateToURL(shell(), data_url); + + RenderWidgetHostImpl* host = GetWidgetHost(); + host->GetView()->SetSize(gfx::Size(400, 400)); + + base::string16 ready_title(base::ASCIIToUTF16("ready")); + TitleWatcher watcher(shell()->web_contents(), ready_title); + ignore_result(watcher.WaitAndGetTitle()); + + MainThreadFrameObserver main_thread_sync(host); + main_thread_sync.Wait(); + } + + // ContentBrowserTest: + void SetUpCommandLine(base::CommandLine* cmd) override { + // TODO(dtapuska): Remove this switch once wheel-gestures ships. + cmd->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures); + cmd->AppendSwitch(switches::kEnableWheelGestures); + } + + int ExecuteScriptAndExtractInt(const std::string& script) { + int value = 0; + EXPECT_TRUE(content::ExecuteScriptAndExtractInt( + shell()->web_contents(), "domAutomationController.send(" + script + ")", + &value)); + return value; + } + + int GetScrollTop() { + return ExecuteScriptAndExtractInt("document.scrollingElement.scrollTop"); + } + + void DoWheelScroll() { + EXPECT_EQ(0, GetScrollTop()); + + int scrollHeight = + ExecuteScriptAndExtractInt("document.documentElement.scrollHeight"); + EXPECT_EQ(1000, scrollHeight); + + scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher()); + GetWidgetHost()->GetProcess()->AddFilter(frame_watcher.get()); + scoped_refptr<InputMsgWatcher> input_msg_watcher( + new InputMsgWatcher(GetWidgetHost(), blink::WebInputEvent::MouseWheel)); + + GetWidgetHost()->ForwardWheelEvent( + SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, -53, 0, true)); + + // Runs until we get the InputMsgAck callback + EXPECT_EQ(INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING, + input_msg_watcher->WaitForAck()); + frame_watcher->WaitFrames(1); + + // Expect that the compositor scrolled at least one pixel while the + // main thread was in a busy loop. + EXPECT_LT(0, frame_watcher->LastMetadata().root_scroll_offset.y()); + } + + void DoTouchScroll() { + EXPECT_EQ(0, GetScrollTop()); + + int scrollHeight = + ExecuteScriptAndExtractInt("document.documentElement.scrollHeight"); + EXPECT_EQ(1000, scrollHeight); + + scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher()); + GetWidgetHost()->GetProcess()->AddFilter(frame_watcher.get()); + + SyntheticSmoothScrollGestureParams params; + params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; + params.anchor = gfx::PointF(50, 50); + params.distances.push_back(gfx::Vector2d(0, -45)); + + scoped_ptr<SyntheticSmoothScrollGesture> gesture( + new SyntheticSmoothScrollGesture(params)); + GetWidgetHost()->QueueSyntheticGesture( + std::move(gesture), + base::Bind(&NonBlockingEventBrowserTest::OnSyntheticGestureCompleted, + base::Unretained(this))); + + // Expect that the compositor scrolled at least one pixel while the + // main thread was in a busy loop. + while (frame_watcher->LastMetadata().root_scroll_offset.y() <= 0) + frame_watcher->WaitFrames(1); + } + + private: + DISALLOW_COPY_AND_ASSIGN(NonBlockingEventBrowserTest); +}; + +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_MouseWheel DISABLED_MouseWheel +#else +// Also appears to be flaky under TSan. crbug.com/588199 +#if defined(THREAD_SANITIZER) +#define MAYBE_MouseWheel DISABLED_MouseWheel +#else +#define MAYBE_MouseWheel MouseWheel +#endif +#endif +IN_PROC_BROWSER_TEST_F(NonBlockingEventBrowserTest, MAYBE_MouseWheel) { + LoadURL(); + DoWheelScroll(); +} + +// Disabled on MacOS because it doesn't support touch input. +#if defined(OS_MACOSX) +#define MAYBE_TouchStart DISABLED_TouchStart +#else +#define MAYBE_TouchStart TouchStart +#endif +IN_PROC_BROWSER_TEST_F(NonBlockingEventBrowserTest, MAYBE_TouchStart) { + LoadURL(); + DoTouchScroll(); +} + +} // namespace content
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc index 8fc03c7..29dc460ae 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
@@ -170,11 +170,6 @@ UMA_HISTOGRAM_TOUCH_TO_SCROLL_LATENCY( "Event.Latency.TouchToFirstScrollUpdateSwapBegin", first_original_component, gpu_swap_begin_component); - // TODO(brianderson): Remove this version once we have enough overlapping - // data with the metric above. crbug.com/478845 - UMA_HISTOGRAM_TOUCH_TO_SCROLL_LATENCY( - "Event.Latency.TouchToFirstScrollUpdateSwap", - first_original_component, gpu_swap_end_component); } original_component = first_original_component; } else if (!latency.FindLatency( @@ -190,11 +185,6 @@ UMA_HISTOGRAM_TOUCH_TO_SCROLL_LATENCY( "Event.Latency.TouchToScrollUpdateSwapBegin", original_component, gpu_swap_begin_component); - // TODO(brianderson): Remove this version once we have enough overlapping - // data with the metric above. crbug.com/478845 - UMA_HISTOGRAM_TOUCH_TO_SCROLL_LATENCY( - "Event.Latency.TouchToScrollUpdateSwap", original_component, - gpu_swap_end_component); } // TODO(miletus): Add validation for making sure the following components
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc index baeb15c..3249744 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -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 "base/test/histogram_tester.h" #include "content/browser/renderer_host/input/render_widget_host_latency_tracker.h" #include "content/common/input/synthetic_web_input_event_builders.h" #include "content/public/browser/native_web_keyboard_event.h" @@ -12,73 +13,170 @@ namespace content { namespace { -const int kTestRoutingId = 3; -const int kTestProcessId = 1; +void AddFakeComponents(const RenderWidgetHostLatencyTracker& tracker, + ui::LatencyInfo* latency) { + latency->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0, base::TimeTicks::Now(), 1); + latency->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0, + base::TimeTicks::Now(), 1); + latency->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, 0, + base::TimeTicks::Now(), 1); + latency->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, + tracker.latency_component_id(), 0, base::TimeTicks::Now(), 1); + latency->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, 0, 0, + base::TimeTicks::Now(), 1); + latency->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT, 0, 0, + base::TimeTicks::Now(), 1); +} -} // namespace +void AddRenderingScheduledComponent(ui::LatencyInfo* latency, bool main) { + if (main) { + latency->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT, 0, 0, + base::TimeTicks::Now(), 1); -TEST(RenderWidgetHostLatencyTrackerTest, Basic) { - RenderWidgetHostLatencyTracker tracker; - tracker.Initialize(kTestRoutingId, kTestProcessId); - - { - auto scroll = SyntheticWebGestureEventBuilder::BuildScrollUpdate( - 5.f, -5.f, 0, blink::WebGestureDeviceTouchscreen); - scroll.timeStampSeconds = - (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); - ui::LatencyInfo scroll_latency; - tracker.OnInputEvent(scroll, &scroll_latency); - EXPECT_TRUE( - scroll_latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - tracker.latency_component_id(), nullptr)); - EXPECT_TRUE( - scroll_latency.FindLatency(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, - 0, nullptr)); - EXPECT_EQ(1U, scroll_latency.input_coordinates_size()); - } - - { - auto wheel = SyntheticWebMouseWheelEventBuilder::Build( - blink::WebMouseWheelEvent::PhaseChanged); - wheel.timeStampSeconds = - (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); - ui::LatencyInfo wheel_latency; - tracker.OnInputEvent(wheel, &wheel_latency); - EXPECT_TRUE( - wheel_latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - tracker.latency_component_id(), nullptr)); - EXPECT_TRUE( - wheel_latency.FindLatency(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, - 0, nullptr)); - EXPECT_EQ(1U, wheel_latency.input_coordinates_size()); - } - - { - SyntheticWebTouchEvent touch; - touch.PressPoint(0, 0); - touch.PressPoint(1, 1); - ui::LatencyInfo touch_latency; - tracker.OnInputEvent(touch, &touch_latency); - EXPECT_TRUE( - touch_latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - tracker.latency_component_id(), nullptr)); - EXPECT_TRUE( - touch_latency.FindLatency(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, - 0, nullptr)); - EXPECT_EQ(2U, touch_latency.input_coordinates_size()); + } else { + latency->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT, 0, 0, + base::TimeTicks::Now(), 1); } } -TEST(RenderWidgetHostLatencyTrackerTest, - LatencyTerminatedOnAckIfRenderingNotScheduled) { - RenderWidgetHostLatencyTracker tracker; - tracker.Initialize(kTestRoutingId, kTestProcessId); +} // namespace +class RenderWidgetHostLatencyTrackerTest : public testing::Test { + public: + RenderWidgetHostLatencyTrackerTest() { + tracker_.Initialize(kTestRoutingId, kTestProcessId); + ResetHistograms(); + } + + ::testing::AssertionResult HistogramSizeEq(const char* histogram_name, + int size) { + uint64_t histogram_size = + histogram_tester_->GetAllSamples(histogram_name).size(); + if (static_cast<uint64_t>(size) == histogram_size) { + return ::testing::AssertionSuccess(); + } else { + return ::testing::AssertionFailure() << histogram_name << " expected " + << size << " entries, but had " + << histogram_size; + } + } + + RenderWidgetHostLatencyTracker* tracker() { return &tracker_; } + void ResetHistograms() { + histogram_tester_.reset(new base::HistogramTester()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostLatencyTrackerTest); + const int kTestRoutingId = 3; + const int kTestProcessId = 1; + scoped_ptr<base::HistogramTester> histogram_tester_; + RenderWidgetHostLatencyTracker tracker_; +}; + +TEST_F(RenderWidgetHostLatencyTrackerTest, TestHistograms) { + for (bool rendering_on_main : { false, true }) { + ResetHistograms(); + { + auto scroll = SyntheticWebGestureEventBuilder::BuildScrollUpdate( + 5.f, -5.f, 0, blink::WebGestureDeviceTouchscreen); + scroll.timeStampSeconds = + (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); + ui::LatencyInfo scroll_latency; + AddFakeComponents(*tracker(), &scroll_latency); + AddRenderingScheduledComponent(&scroll_latency, rendering_on_main); + tracker()->OnInputEvent(scroll, &scroll_latency); + EXPECT_TRUE(scroll_latency.FindLatency( + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, + tracker()->latency_component_id(), nullptr)); + EXPECT_TRUE(scroll_latency.FindLatency( + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, nullptr)); + EXPECT_EQ(1U, scroll_latency.input_coordinates_size()); + tracker()->OnInputEventAck(scroll, &scroll_latency); + } + + { + auto wheel = SyntheticWebMouseWheelEventBuilder::Build( + blink::WebMouseWheelEvent::PhaseChanged); + wheel.timeStampSeconds = + (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); + ui::LatencyInfo wheel_latency; + AddFakeComponents(*tracker(), &wheel_latency); + AddRenderingScheduledComponent(&wheel_latency, rendering_on_main); + tracker()->OnInputEvent(wheel, &wheel_latency); + EXPECT_TRUE(wheel_latency.FindLatency( + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, + tracker()->latency_component_id(), nullptr)); + EXPECT_TRUE(wheel_latency.FindLatency( + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, nullptr)); + EXPECT_EQ(1U, wheel_latency.input_coordinates_size()); + tracker()->OnInputEventAck(wheel, &wheel_latency); + } + + { + SyntheticWebTouchEvent touch; + touch.PressPoint(0, 0); + touch.PressPoint(1, 1); + ui::LatencyInfo touch_latency; + AddFakeComponents(*tracker(), &touch_latency); + AddRenderingScheduledComponent(&touch_latency, rendering_on_main); + tracker()->OnInputEvent(touch, &touch_latency); + EXPECT_TRUE(touch_latency.FindLatency( + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, + tracker()->latency_component_id(), nullptr)); + EXPECT_TRUE(touch_latency.FindLatency( + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, nullptr)); + EXPECT_EQ(2U, touch_latency.input_coordinates_size()); + tracker()->OnInputEventAck(touch, &touch_latency); + tracker()->OnFrameSwapped(touch_latency); + } + + EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.WheelUI", 1)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.TouchUI", 1)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.WheelAcked", 1)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.TouchAcked", 1)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.TouchToFirstScrollUpdateSwapBegin", 1)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.TouchToScrollUpdateSwapBegin", 1)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.ScrollUpdate.TouchToHandled_Main", + rendering_on_main ? 1 : 0)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.ScrollUpdate.TouchToHandled_Impl", + rendering_on_main ? 0 : 1)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.ScrollUpdate.HandledToRendererSwap_Main", + rendering_on_main ? 1 : 0)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.ScrollUpdate.HandledToRendererSwap_Impl", + rendering_on_main ? 0 : 1)); + EXPECT_TRUE(HistogramSizeEq( + "Event.Latency.ScrollUpdate.RendererSwapToBrowserNotified", 1)); + EXPECT_TRUE(HistogramSizeEq( + "Event.Latency.ScrollUpdate.BrowserNotifiedToBeforeGpuSwap", 1)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.ScrollUpdate.GpuSwap", 1)); + } +} + +TEST_F(RenderWidgetHostLatencyTrackerTest, + LatencyTerminatedOnAckIfRenderingNotScheduled) { { auto scroll = SyntheticWebGestureEventBuilder::BuildScrollBegin(5.f, -5.f); ui::LatencyInfo scroll_latency; - tracker.OnInputEvent(scroll, &scroll_latency); - tracker.OnInputEventAck(scroll, &scroll_latency); + AddFakeComponents(*tracker(), &scroll_latency); + // Don't include the rendering schedule component, since we're testing the + // case where rendering isn't scheduled. + tracker()->OnInputEvent(scroll, &scroll_latency); + tracker()->OnInputEventAck(scroll, &scroll_latency); EXPECT_TRUE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_TERMINATED_GESTURE_COMPONENT, 0, nullptr)); EXPECT_TRUE(scroll_latency.terminated()); @@ -88,8 +186,9 @@ auto wheel = SyntheticWebMouseWheelEventBuilder::Build( blink::WebMouseWheelEvent::PhaseChanged); ui::LatencyInfo wheel_latency; - tracker.OnInputEvent(wheel, &wheel_latency); - tracker.OnInputEventAck(wheel, &wheel_latency); + AddFakeComponents(*tracker(), &wheel_latency); + tracker()->OnInputEvent(wheel, &wheel_latency); + tracker()->OnInputEventAck(wheel, &wheel_latency); EXPECT_TRUE(wheel_latency.FindLatency( ui::INPUT_EVENT_LATENCY_TERMINATED_MOUSE_WHEEL_COMPONENT, 0, nullptr)); EXPECT_TRUE(wheel_latency.terminated()); @@ -99,19 +198,22 @@ SyntheticWebTouchEvent touch; touch.PressPoint(0, 0); ui::LatencyInfo touch_latency; - tracker.OnInputEvent(touch, &touch_latency); - tracker.OnInputEventAck(touch, &touch_latency); + AddFakeComponents(*tracker(), &touch_latency); + tracker()->OnInputEvent(touch, &touch_latency); + tracker()->OnInputEventAck(touch, &touch_latency); EXPECT_TRUE(touch_latency.FindLatency( ui::INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT, 0, nullptr)); EXPECT_TRUE(touch_latency.terminated()); + tracker()->OnFrameSwapped(touch_latency); } { auto mouse_move = SyntheticWebMouseEventBuilder::Build( blink::WebMouseEvent::MouseMove); ui::LatencyInfo mouse_latency; - tracker.OnInputEvent(mouse_move, &mouse_latency); - tracker.OnInputEventAck(mouse_move, &mouse_latency); + AddFakeComponents(*tracker(), &mouse_latency); + tracker()->OnInputEvent(mouse_move, &mouse_latency); + tracker()->OnInputEventAck(mouse_move, &mouse_latency); EXPECT_TRUE(mouse_latency.FindLatency( ui::INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT, 0, nullptr)); EXPECT_TRUE(mouse_latency.terminated()); @@ -121,25 +223,44 @@ auto key_event = SyntheticWebKeyboardEventBuilder::Build( blink::WebKeyboardEvent::Char); ui::LatencyInfo key_latency; - tracker.OnInputEvent(key_event, &key_latency); - tracker.OnInputEventAck(key_event, &key_latency); + AddFakeComponents(*tracker(), &key_latency); + tracker()->OnInputEvent(key_event, &key_latency); + tracker()->OnInputEventAck(key_event, &key_latency); EXPECT_TRUE(key_latency.FindLatency( ui::INPUT_EVENT_LATENCY_TERMINATED_KEYBOARD_COMPONENT, 0, nullptr)); EXPECT_TRUE(key_latency.terminated()); } + + EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.WheelUI", 1)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.TouchUI", 1)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.WheelAcked", 1)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.Browser.TouchAcked", 1)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.TouchToFirstScrollUpdateSwapBegin", 1)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.TouchToScrollUpdateSwapBegin", 1)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.ScrollUpdate.TouchToHandled_Main", 0)); + EXPECT_TRUE( + HistogramSizeEq("Event.Latency.ScrollUpdate.TouchToHandled_Impl", 0)); + EXPECT_TRUE(HistogramSizeEq( + "Event.Latency.ScrollUpdate.HandledToRendererSwap_Main", 0)); + EXPECT_TRUE(HistogramSizeEq( + "Event.Latency.ScrollUpdate.HandledToRendererSwap_Impl", 0)); + EXPECT_TRUE(HistogramSizeEq( + "Event.Latency.ScrollUpdate.RendererSwapToBrowserNotified", 0)); + EXPECT_TRUE(HistogramSizeEq( + "Event.Latency.ScrollUpdate.BrowserNotifiedToBeforeGpuSwap", 0)); + EXPECT_TRUE(HistogramSizeEq("Event.Latency.ScrollUpdate.GpuSwap", 0)); } -TEST(RenderWidgetHostLatencyTrackerTest, InputCoordinatesPopulated) { - RenderWidgetHostLatencyTracker tracker; - tracker.Initialize(kTestRoutingId, kTestProcessId); - +TEST_F(RenderWidgetHostLatencyTrackerTest, InputCoordinatesPopulated) { { auto event = SyntheticWebMouseWheelEventBuilder::Build(0, 0, -5, 0, 0, true); event.x = 100; event.y = 200; ui::LatencyInfo latency_info; - tracker.OnInputEvent(event, &latency_info); + tracker()->OnInputEvent(event, &latency_info); EXPECT_EQ(1u, latency_info.input_coordinates_size()); EXPECT_EQ(100, latency_info.input_coordinates()[0].x); EXPECT_EQ(200, latency_info.input_coordinates()[0].y); @@ -150,7 +271,7 @@ event.x = 300; event.y = 400; ui::LatencyInfo latency_info; - tracker.OnInputEvent(event, &latency_info); + tracker()->OnInputEvent(event, &latency_info); EXPECT_EQ(1u, latency_info.input_coordinates_size()); EXPECT_EQ(300, latency_info.input_coordinates()[0].x); EXPECT_EQ(400, latency_info.input_coordinates()[0].y); @@ -162,7 +283,7 @@ event.x = 500; event.y = 600; ui::LatencyInfo latency_info; - tracker.OnInputEvent(event, &latency_info); + tracker()->OnInputEvent(event, &latency_info); EXPECT_EQ(1u, latency_info.input_coordinates_size()); EXPECT_EQ(500, latency_info.input_coordinates()[0].x); EXPECT_EQ(600, latency_info.input_coordinates()[0].y); @@ -174,7 +295,7 @@ event.PressPoint(900, 1000); event.PressPoint(1100, 1200); // LatencyInfo only holds two coordinates. ui::LatencyInfo latency_info; - tracker.OnInputEvent(event, &latency_info); + tracker()->OnInputEvent(event, &latency_info); EXPECT_EQ(2u, latency_info.input_coordinates_size()); EXPECT_EQ(700, latency_info.input_coordinates()[0].x); EXPECT_EQ(800, latency_info.input_coordinates()[0].y); @@ -186,23 +307,20 @@ NativeWebKeyboardEvent event; event.type = blink::WebKeyboardEvent::KeyDown; ui::LatencyInfo latency_info; - tracker.OnInputEvent(event, &latency_info); + tracker()->OnInputEvent(event, &latency_info); EXPECT_EQ(0u, latency_info.input_coordinates_size()); } } -TEST(RenderWidgetHostLatencyTrackerTest, ScrollLatency) { - RenderWidgetHostLatencyTracker tracker; - tracker.Initialize(kTestRoutingId, kTestProcessId); - +TEST_F(RenderWidgetHostLatencyTrackerTest, ScrollLatency) { auto scroll_begin = SyntheticWebGestureEventBuilder::BuildScrollBegin(5, -5); ui::LatencyInfo scroll_latency; scroll_latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0); - tracker.OnInputEvent(scroll_begin, &scroll_latency); + tracker()->OnInputEvent(scroll_begin, &scroll_latency); EXPECT_TRUE( scroll_latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - tracker.latency_component_id(), nullptr)); + tracker()->latency_component_id(), nullptr)); EXPECT_EQ(2U, scroll_latency.latency_components().size()); // The first GestureScrollUpdate should be provided with @@ -212,35 +330,35 @@ scroll_latency = ui::LatencyInfo(); scroll_latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0); - tracker.OnInputEvent(first_scroll_update, &scroll_latency); + tracker()->OnInputEvent(first_scroll_update, &scroll_latency); EXPECT_TRUE( scroll_latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - tracker.latency_component_id(), nullptr)); + tracker()->latency_component_id(), nullptr)); EXPECT_TRUE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, - tracker.latency_component_id(), nullptr)); + tracker()->latency_component_id(), nullptr)); EXPECT_FALSE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, - tracker.latency_component_id(), nullptr)); + tracker()->latency_component_id(), nullptr)); EXPECT_EQ(3U, scroll_latency.latency_components().size()); - // Subseqeunt GestureScrollUpdates should be provided with + // Subsequent GestureScrollUpdates should be provided with // INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT. auto scroll_update = SyntheticWebGestureEventBuilder::BuildScrollUpdate( -5.f, 5.f, 0, blink::WebGestureDeviceTouchscreen); scroll_latency = ui::LatencyInfo(); scroll_latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0); - tracker.OnInputEvent(scroll_update, &scroll_latency); + tracker()->OnInputEvent(scroll_update, &scroll_latency); EXPECT_TRUE( scroll_latency.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - tracker.latency_component_id(), nullptr)); + tracker()->latency_component_id(), nullptr)); EXPECT_FALSE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, - tracker.latency_component_id(), nullptr)); + tracker()->latency_component_id(), nullptr)); EXPECT_TRUE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, - tracker.latency_component_id(), nullptr)); + tracker()->latency_component_id(), nullptr)); EXPECT_EQ(3U, scroll_latency.latency_components().size()); }
diff --git a/content/browser/renderer_host/input/touch_input_browsertest.cc b/content/browser/renderer_host/input/touch_input_browsertest.cc index 2c68ac7..7ff1dd0 100644 --- a/content/browser/renderer_host/input/touch_input_browsertest.cc +++ b/content/browser/renderer_host/input/touch_input_browsertest.cc
@@ -19,6 +19,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/common/content_switches.h" +#include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" @@ -88,54 +89,6 @@ namespace content { -class InputEventMessageFilter : public BrowserMessageFilter { - public: - InputEventMessageFilter() - : BrowserMessageFilter(InputMsgStart), - type_(WebInputEvent::Undefined), - state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {} - - void WaitForAck(WebInputEvent::Type type) { - base::RunLoop run_loop; - base::AutoReset<base::Closure> reset_quit(&quit_, run_loop.QuitClosure()); - base::AutoReset<WebInputEvent::Type> reset_type(&type_, type); - run_loop.Run(); - } - - InputEventAckState last_ack_state() const { return state_; } - - protected: - ~InputEventMessageFilter() override {} - - private: - void ReceivedEventAck(WebInputEvent::Type type, InputEventAckState state) { - if (type_ == type) { - state_ = state; - quit_.Run(); - } - } - - // BrowserMessageFilter: - bool OnMessageReceived(const IPC::Message& message) override { - if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) { - InputHostMsg_HandleInputEvent_ACK::Param params; - InputHostMsg_HandleInputEvent_ACK::Read(&message, ¶ms); - WebInputEvent::Type type = base::get<0>(params).type; - InputEventAckState ack = base::get<0>(params).state; - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&InputEventMessageFilter::ReceivedEventAck, - this, type, ack)); - } - return false; - } - - base::Closure quit_; - WebInputEvent::Type type_; - InputEventAckState state_; - - DISALLOW_COPY_AND_ASSIGN(InputEventMessageFilter); -}; - class TouchInputBrowserTest : public ContentBrowserTest { public: TouchInputBrowserTest() {} @@ -146,33 +99,27 @@ shell()->web_contents()->GetRenderViewHost()->GetWidget()); } - InputEventMessageFilter* filter() { return filter_.get(); } + scoped_refptr<InputMsgWatcher> AddFilter(blink::WebInputEvent::Type type) { + return new InputMsgWatcher(GetWidgetHost(), type); + } protected: - void LoadURLAndAddFilter() { + void LoadURL() { const GURL data_url(kTouchEventDataURL); NavigateToURL(shell(), data_url); - WebContentsImpl* web_contents = - static_cast<WebContentsImpl*>(shell()->web_contents()); - RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( - web_contents->GetRenderViewHost()->GetWidget()); + RenderWidgetHostImpl* host = GetWidgetHost(); host->GetView()->SetSize(gfx::Size(400, 400)); // The page is loaded in the renderer, wait for a new frame to arrive. while (!host->ScheduleComposite()) GiveItSomeTime(); - - filter_ = new InputEventMessageFilter(); - host->GetProcess()->AddFilter(filter_.get()); } void SetUpCommandLine(base::CommandLine* cmd) override { cmd->AppendSwitchASCII(switches::kTouchEvents, switches::kTouchEventsEnabled); } - - scoped_refptr<InputEventMessageFilter> filter_; }; #if defined(OS_MACOSX) @@ -182,17 +129,16 @@ #define MAYBE_TouchNoHandler TouchNoHandler #endif IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchNoHandler) { - LoadURLAndAddFilter(); + LoadURL(); SyntheticWebTouchEvent touch; // A press on |first| should be acked with NO_CONSUMER_EXISTS since there is // no touch-handler on it. touch.PressPoint(25, 25); + scoped_refptr<InputMsgWatcher> filter = AddFilter(WebInputEvent::TouchStart); GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(WebInputEvent::TouchStart); - EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, - filter()->last_ack_state()); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, filter->WaitForAck()); // If a touch-press is acked with NO_CONSUMER_EXISTS, then subsequent // touch-points don't need to be dispatched until the touch point is released. @@ -208,20 +154,21 @@ #define MAYBE_TouchHandlerNoConsume TouchHandlerNoConsume #endif IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchHandlerNoConsume) { - LoadURLAndAddFilter(); + LoadURL(); SyntheticWebTouchEvent touch; // Press on |second| should be acked with NOT_CONSUMED since there is a // touch-handler on |second|, but it doesn't consume the event. touch.PressPoint(125, 25); + scoped_refptr<InputMsgWatcher> filter = AddFilter(WebInputEvent::TouchStart); GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(WebInputEvent::TouchStart); - EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state()); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter->WaitForAck()); + filter = AddFilter(WebInputEvent::TouchEnd); touch.ReleasePoint(0); GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(WebInputEvent::TouchEnd); touch.ResetPoints(); + filter->WaitForAck(); } #if defined(OS_CHROMEOS) @@ -231,19 +178,20 @@ #define MAYBE_TouchHandlerConsume TouchHandlerConsume #endif IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchHandlerConsume) { - LoadURLAndAddFilter(); + LoadURL(); SyntheticWebTouchEvent touch; // Press on |third| should be acked with CONSUMED since the touch-handler on // |third| consimes the event. touch.PressPoint(25, 125); + scoped_refptr<InputMsgWatcher> filter = AddFilter(WebInputEvent::TouchStart); GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(WebInputEvent::TouchStart); - EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state()); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter->WaitForAck()); touch.ReleasePoint(0); + filter = AddFilter(WebInputEvent::TouchEnd); GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(WebInputEvent::TouchEnd); + filter->WaitForAck(); } #if defined(OS_CHROMEOS) @@ -256,21 +204,20 @@ #define MAYBE_MultiPointTouchPress MultiPointTouchPress #endif IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_MultiPointTouchPress) { - LoadURLAndAddFilter(); + LoadURL(); SyntheticWebTouchEvent touch; // Press on |first|, which sould be acked with NO_CONSUMER_EXISTS. Then press // on |third|. That point should be acked with CONSUMED. touch.PressPoint(25, 25); + scoped_refptr<InputMsgWatcher> filter = AddFilter(WebInputEvent::TouchStart); GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(WebInputEvent::TouchStart); - EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, - filter()->last_ack_state()); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, filter->WaitForAck()); touch.PressPoint(25, 125); + filter = AddFilter(WebInputEvent::TouchStart); GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(WebInputEvent::TouchStart); - EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state()); + EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter->WaitForAck()); } } // namespace content
diff --git a/content/browser/renderer_host/media/video_capture_device_client.cc b/content/browser/renderer_host/media/video_capture_device_client.cc index b4d142e..03d64d2 100644 --- a/content/browser/renderer_host/media/video_capture_device_client.cc +++ b/content/browser/renderer_host/media/video_capture_device_client.cc
@@ -188,10 +188,10 @@ NOTREACHED() << "RGB24 is only available in Linux and Windows platforms"; #endif #if defined(OS_WIN) - // TODO(wjia): Currently, for RGB24 on WIN, capture device always - // passes in positive src_width and src_height. Remove this hardcoded - // value when nagative src_height is supported. The negative src_height - // indicates that vertical flipping is needed. + // TODO(wjia): Currently, for RGB24 on WIN, capture device always passes + // in positive src_width and src_height. Remove this hardcoded value when + // negative src_height is supported. The negative src_height indicates + // that vertical flipping is needed. flip = true; #endif break; @@ -256,56 +256,6 @@ OnIncomingCapturedBuffer(std::move(buffer), output_format, timestamp); } -void VideoCaptureDeviceClient::OnIncomingCapturedYuvData( - const uint8_t* y_data, - const uint8_t* u_data, - const uint8_t* v_data, - size_t y_stride, - size_t u_stride, - size_t v_stride, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - const base::TimeTicks& timestamp) { - TRACE_EVENT0("video", "VideoCaptureDeviceClient::OnIncomingCapturedYuvData"); - DCHECK_EQ(media::PIXEL_FORMAT_I420, frame_format.pixel_format); - DCHECK_EQ(media::PIXEL_STORAGE_CPU, frame_format.pixel_storage); - DCHECK_EQ(0, clockwise_rotation) << "Rotation not supported"; - - uint8_t *y_plane_data, *u_plane_data, *v_plane_data; - scoped_ptr<Buffer> buffer(ReserveI420OutputBuffer( - frame_format.frame_size, frame_format.pixel_storage, &y_plane_data, - &u_plane_data, &v_plane_data)); - if (!buffer.get()) - return; - - const size_t dst_y_stride = - VideoFrame::RowBytes(VideoFrame::kYPlane, media::PIXEL_FORMAT_I420, - frame_format.frame_size.width()); - const size_t dst_u_stride = - VideoFrame::RowBytes(VideoFrame::kUPlane, media::PIXEL_FORMAT_I420, - frame_format.frame_size.width()); - const size_t dst_v_stride = - VideoFrame::RowBytes(VideoFrame::kVPlane, media::PIXEL_FORMAT_I420, - frame_format.frame_size.width()); - DCHECK_GE(y_stride, dst_y_stride); - DCHECK_GE(u_stride, dst_u_stride); - DCHECK_GE(v_stride, dst_v_stride); - - if (libyuv::I420Copy(y_data, y_stride, - u_data, u_stride, - v_data, v_stride, - y_plane_data, dst_y_stride, - u_plane_data, dst_u_stride, - v_plane_data, dst_v_stride, - frame_format.frame_size.width(), - frame_format.frame_size.height())) { - DLOG(WARNING) << "Failed to copy buffer"; - return; - } - - OnIncomingCapturedBuffer(std::move(buffer), frame_format, timestamp); -}; - scoped_ptr<media::VideoCaptureDevice::Client::Buffer> VideoCaptureDeviceClient::ReserveOutputBuffer( const gfx::Size& frame_size,
diff --git a/content/browser/renderer_host/media/video_capture_device_client.h b/content/browser/renderer_host/media/video_capture_device_client.h index 8215f5642..1b982a27 100644 --- a/content/browser/renderer_host/media/video_capture_device_client.h +++ b/content/browser/renderer_host/media/video_capture_device_client.h
@@ -49,15 +49,6 @@ const media::VideoCaptureFormat& frame_format, int rotation, const base::TimeTicks& timestamp) override; - void OnIncomingCapturedYuvData(const uint8_t* y_data, - const uint8_t* u_data, - const uint8_t* v_data, - size_t y_stride, - size_t u_stride, - size_t v_stride, - const media::VideoCaptureFormat& frame_format, - int clockwise_rotation, - const base::TimeTicks& timestamp) override; scoped_ptr<Buffer> ReserveOutputBuffer( const gfx::Size& dimensions, media::VideoPixelFormat format,
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 1463b26..e5847f1 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
@@ -5,6 +5,7 @@ #include <stddef.h> #include "base/bind.h" +#include "base/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/run_loop.h" @@ -79,6 +80,7 @@ media::PIXEL_FORMAT_I420, media::PIXEL_STORAGE_CPU); DCHECK(device_client_.get()); + EXPECT_CALL(*controller_, DoLogOnIOThread(_)).Times(1); EXPECT_CALL(*controller_, MockDoIncomingCapturedVideoFrameOnIOThread(_)) .Times(1); device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes, @@ -100,6 +102,7 @@ media::VideoPixelStorage::PIXEL_STORAGE_CPU); DCHECK(device_client_.get()); // Expect the the call to fail silently inside the VideoCaptureDeviceClient. + EXPECT_CALL(*controller_, DoLogOnIOThread(_)).Times(1); EXPECT_CALL(*controller_, MockDoIncomingCapturedVideoFrameOnIOThread(_)) .Times(0); device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes, @@ -119,6 +122,7 @@ media::PIXEL_STORAGE_CPU); // We expect the second frame to be silently dropped, so these should // only be called once despite the two frames. + EXPECT_CALL(*controller_, DoLogOnIOThread(_)).Times(1); EXPECT_CALL(*controller_, MockDoIncomingCapturedVideoFrameOnIOThread(_)) .Times(1); // Pass two frames. The second will be dropped. @@ -132,45 +136,42 @@ Mock::VerifyAndClearExpectations(controller_.get()); } -// Tests that buffer-based capture API accepts all memory-backed pixel formats. -TEST_F(VideoCaptureDeviceClientTest, DataCaptureInEachVideoFormatInSequence) { +// Tests that buffer-based capture API accepts some memory-backed pixel formats. +TEST_F(VideoCaptureDeviceClientTest, DataCaptureGoodPixelFormats) { // The usual ReserveOutputBuffer() -> OnIncomingCapturedVideoFrame() cannot // be used since it does not accept all pixel formats. The memory backed // buffer OnIncomingCapturedData() is used instead, with a dummy scratchpad // buffer. const size_t kScratchpadSizeInBytes = 400; unsigned char data[kScratchpadSizeInBytes] = {}; - const gfx::Size capture_resolution(10, 10); - ASSERT_GE(kScratchpadSizeInBytes, capture_resolution.GetArea() * 4u) + const gfx::Size kCaptureResolution(10, 10); + ASSERT_GE(kScratchpadSizeInBytes, kCaptureResolution.GetArea() * 4u) << "Scratchpad is too small to hold the largest pixel format (ARGB)."; - for (int format = 0; format < media::PIXEL_FORMAT_MAX; - ++format) { - // Conversion from some formats are unsupported. - if (format == media::PIXEL_FORMAT_UNKNOWN || - format == media::PIXEL_FORMAT_YV16 || - format == media::PIXEL_FORMAT_YV12A || - format == media::PIXEL_FORMAT_YV24 || - format == media::PIXEL_FORMAT_ARGB || - format == media::PIXEL_FORMAT_XRGB || - format == media::PIXEL_FORMAT_MJPEG || - format == media::PIXEL_FORMAT_MT21 || - format == media::PIXEL_FORMAT_YUV420P9 || - format == media::PIXEL_FORMAT_YUV420P10 || - format == media::PIXEL_FORMAT_YUV422P9 || - format == media::PIXEL_FORMAT_YUV422P10 || - format == media::PIXEL_FORMAT_YUV444P9 || - format == media::PIXEL_FORMAT_YUV444P10) { - continue; - } -#if !defined(OS_LINUX) && !defined(OS_WIN) - if (format == media::PIXEL_FORMAT_RGB24) { - continue; - } + media::VideoCaptureParams params; + params.requested_format = media::VideoCaptureFormat( + kCaptureResolution, 30.0f, media::PIXEL_FORMAT_UNKNOWN); + + // Only use the VideoPixelFormats that we know supported. Do not add + // PIXEL_FORMAT_MJPEG since it would need a real JPEG header. + const media::VideoPixelFormat kSupportedFormats[] = { + media::PIXEL_FORMAT_I420, + media::PIXEL_FORMAT_YV12, + media::PIXEL_FORMAT_NV12, + media::PIXEL_FORMAT_NV21, + media::PIXEL_FORMAT_YUY2, + media::PIXEL_FORMAT_UYVY, +#if defined(OS_WIN) || defined(OS_LINUX) + media::PIXEL_FORMAT_RGB24, #endif - media::VideoCaptureParams params; - params.requested_format = media::VideoCaptureFormat( - capture_resolution, 30.0f, media::VideoPixelFormat(format)); + media::PIXEL_FORMAT_RGB32, + media::PIXEL_FORMAT_ARGB + }; + + for (media::VideoPixelFormat format : kSupportedFormats) { + params.requested_format.pixel_format = format; + + EXPECT_CALL(*controller_, DoLogOnIOThread(_)).Times(1); EXPECT_CALL(*controller_, MockDoIncomingCapturedVideoFrameOnIOThread(_)) .Times(1); device_client_->OnIncomingCapturedData( @@ -204,6 +205,8 @@ const size_t kScratchpadSizeInBytes = 400; unsigned char data[kScratchpadSizeInBytes] = {}; + EXPECT_CALL(*controller_, DoLogOnIOThread(_)).Times(1); + media::VideoCaptureParams params; for (const auto& size_and_rotation : kSizeAndRotations) { ASSERT_GE(kScratchpadSizeInBytes,
diff --git a/content/browser/renderer_host/overscroll_controller.cc b/content/browser/renderer_host/overscroll_controller.cc index b622dc85..34c631a5 100644 --- a/content/browser/renderer_host/overscroll_controller.cc +++ b/content/browser/renderer_host/overscroll_controller.cc
@@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "content/browser/renderer_host/overscroll_controller_delegate.h" +#include "content/common/input/input_event_utils.h" #include "content/public/browser/overscroll_configuration.h" #include "content/public/common/content_switches.h" @@ -19,6 +20,13 @@ switches::kScrollEndEffect) == "1"; } +bool IsGestureEventFromTouchpad(const blink::WebInputEvent& event) { + DCHECK(blink::WebInputEvent::isGestureEventType(event.type)); + const blink::WebGestureEvent& gesture = + static_cast<const blink::WebGestureEvent&>(event); + return gesture.sourceDevice == blink::WebGestureDeviceTouchpad; +} + } // namespace namespace content { @@ -28,18 +36,64 @@ scroll_state_(STATE_UNKNOWN), overscroll_delta_x_(0.f), overscroll_delta_y_(0.f), - delegate_(NULL) { -} + delegate_(NULL), + use_gesture_wheel_scrolling_(UseGestureBasedWheelScrolling()) {} OverscrollController::~OverscrollController() { } +bool OverscrollController::ShouldProcessEvent( + const blink::WebInputEvent& event) { + if (use_gesture_wheel_scrolling_) { + switch (event.type) { + case blink::WebInputEvent::MouseWheel: + return false; + case blink::WebInputEvent::GestureScrollBegin: + case blink::WebInputEvent::GestureScrollUpdate: + case blink::WebInputEvent::GestureScrollEnd: { + const blink::WebGestureEvent& gesture = + static_cast<const blink::WebGestureEvent&>(event); + if (gesture.sourceDevice == blink::WebGestureDeviceTouchpad) + return true; + blink::WebGestureEvent::ScrollUnits scrollUnits; + switch (event.type) { + case blink::WebInputEvent::GestureScrollBegin: + scrollUnits = gesture.data.scrollBegin.deltaHintUnits; + break; + case blink::WebInputEvent::GestureScrollUpdate: + scrollUnits = gesture.data.scrollUpdate.deltaUnits; + break; + case blink::WebInputEvent::GestureScrollEnd: + scrollUnits = gesture.data.scrollEnd.deltaUnits; + break; + default: + scrollUnits = blink::WebGestureEvent::Pixels; + break; + } + + return scrollUnits == blink::WebGestureEvent::PrecisePixels; + } + default: + break; + } + } + return true; +} + bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) { + if (!ShouldProcessEvent(event)) + return false; + bool reset_scroll_state = false; if (scroll_state_ != STATE_UNKNOWN || overscroll_delta_x_ || overscroll_delta_y_) { switch (event.type) { case blink::WebInputEvent::GestureScrollEnd: + // Avoid resetting the state on GestureScrollEnd generated + // from the touchpad since it is sent based on a timeout. + reset_scroll_state = !IsGestureEventFromTouchpad(event); + break; + case blink::WebInputEvent::GestureFlingStart: reset_scroll_state = true; break; @@ -95,6 +149,9 @@ void OverscrollController::ReceivedEventACK(const blink::WebInputEvent& event, bool processed) { + if (!ShouldProcessEvent(event)) + return; + if (processed) { // If a scroll event is consumed by the page, i.e. some content on the page // has been scrolled, then there is not going to be an overscroll gesture, @@ -142,6 +199,13 @@ event.type != blink::WebInputEvent::GestureFlingStart) return false; + // Avoid completing the action on GestureScrollEnd generated + // from the touchpad since it is sent based on a timeout not + // when the user has stopped interacting. + if (event.type == blink::WebInputEvent::GestureScrollEnd && + IsGestureEventFromTouchpad(event)) + return false; + if (!delegate_) return false; @@ -199,6 +263,12 @@ return !wheel.hasPreciseScrollingDeltas; } + // Avoid resetting overscroll on GestureScrollBegin/End generated + // from the touchpad since it is sent based on a timeout. + case blink::WebInputEvent::GestureScrollBegin: + case blink::WebInputEvent::GestureScrollEnd: + return !IsGestureEventFromTouchpad(event); + case blink::WebInputEvent::GestureScrollUpdate: case blink::WebInputEvent::GestureFlingCancel: return false; @@ -221,16 +291,15 @@ break; event_processed = ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX, - wheel.deltaY * wheel.accelerationRatioY, - wheel.type); + wheel.deltaY * wheel.accelerationRatioY, true); break; } case blink::WebInputEvent::GestureScrollUpdate: { const blink::WebGestureEvent& gesture = static_cast<const blink::WebGestureEvent&>(event); - event_processed = ProcessOverscroll(gesture.data.scrollUpdate.deltaX, - gesture.data.scrollUpdate.deltaY, - gesture.type); + event_processed = ProcessOverscroll( + gesture.data.scrollUpdate.deltaX, gesture.data.scrollUpdate.deltaY, + gesture.sourceDevice == blink::WebGestureDeviceTouchpad); break; } case blink::WebInputEvent::GestureFlingStart: { @@ -270,15 +339,14 @@ bool OverscrollController::ProcessOverscroll(float delta_x, float delta_y, - blink::WebInputEvent::Type type) { + bool is_touchpad) { if (scroll_state_ != STATE_CONTENT_SCROLLING) overscroll_delta_x_ += delta_x; overscroll_delta_y_ += delta_y; float horiz_threshold = GetOverscrollConfig( - WebInputEvent::isGestureEventType(type) ? - OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN : - OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD); + is_touchpad ? OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD + : OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN); float vert_threshold = GetOverscrollConfig( OVERSCROLL_CONFIG_VERT_THRESHOLD_START); if (fabs(overscroll_delta_x_) <= horiz_threshold &&
diff --git a/content/browser/renderer_host/overscroll_controller.h b/content/browser/renderer_host/overscroll_controller.h index 92fa093..90f310f 100644 --- a/content/browser/renderer_host/overscroll_controller.h +++ b/content/browser/renderer_host/overscroll_controller.h
@@ -92,9 +92,7 @@ // and the over scroll amount (i.e. |overscroll_mode_|, |overscroll_delta_x_| // and |overscroll_delta_y_|). Returns true if overscroll was handled by the // delegate. - bool ProcessOverscroll(float delta_x, - float delta_y, - blink::WebInputEvent::Type event_type); + bool ProcessOverscroll(float delta_x, float delta_y, bool is_touchpad); // Completes the desired action from the current gesture. void CompleteAction(); @@ -103,6 +101,9 @@ // appropriate). void SetOverscrollMode(OverscrollMode new_mode); + // Whether this event should be processed or not handled by the controller. + bool ShouldProcessEvent(const blink::WebInputEvent& event); + // The current state of overscroll gesture. OverscrollMode overscroll_mode_; @@ -121,6 +122,7 @@ // The delegate that receives the overscroll updates. The delegate is not // owned by this controller. OverscrollControllerDelegate* delegate_; + bool use_gesture_wheel_scrolling_; DISALLOW_COPY_AND_ASSIGN(OverscrollController); };
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index c355fa23..5e09f0c 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1261,9 +1261,6 @@ } static void AppendCompositorCommandLineFlags(base::CommandLine* command_line) { - if (IsPropertyTreeVerificationEnabled()) - command_line->AppendSwitch(cc::switches::kEnablePropertyTreeVerification); - command_line->AppendSwitchASCII( switches::kNumRasterThreads, base::IntToString(NumberOfRendererRasterThreads())); @@ -1429,6 +1426,7 @@ switches::kDisableTouchAdjustment, switches::kDisableTouchDragDrop, switches::kDisableV8IdleTasks, + switches::kDisableWheelGestures, switches::kDomAutomationController, switches::kEnableBlinkFeatures, switches::kEnableBrowserSideNavigation, @@ -1522,7 +1520,6 @@ // also be added to chrome/browser/chromeos/login/chrome_restart_request.cc. cc::switches::kDisableCachedPictureRaster, cc::switches::kDisableCompositedAntialiasing, - cc::switches::kDisableCompositorPropertyTrees, cc::switches::kDisableMainFrameBeforeActivation, cc::switches::kDisableThreadedAnimation, cc::switches::kEnableBeginFrameScheduling,
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index 2dc2f74c..5e36080 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -479,8 +479,9 @@ !command_line.HasSwitch(switches::kDisableTouchAdjustment); prefs.enable_scroll_animator = - !command_line.HasSwitch(switches::kDisableSmoothScrolling) && - gfx::Animation::ScrollAnimationsEnabledBySystem(); + command_line.HasSwitch(switches::kEnableSmoothScrolling) || + (!command_line.HasSwitch(switches::kDisableSmoothScrolling) && + gfx::Animation::ScrollAnimationsEnabledBySystem()); // Certain GPU features might have been blacklisted. GpuDataManagerImpl::GetInstance()->UpdateRendererWebPrefs(&prefs); @@ -682,11 +683,9 @@ .append(register_name)); } - const gfx::Point client_pt_in_viewport = ConvertDIPToViewport(client_pt); - - Send(new DragMsg_TargetDragEnter(GetRoutingID(), filtered_data, - client_pt_in_viewport, screen_pt, - operations_allowed, key_modifiers)); + Send(new DragMsg_TargetDragEnter(GetRoutingID(), filtered_data, client_pt, + screen_pt, operations_allowed, + key_modifiers)); } void RenderViewHostImpl::DragTargetDragOver( @@ -694,10 +693,8 @@ const gfx::Point& screen_pt, WebDragOperationsMask operations_allowed, int key_modifiers) { - const gfx::Point client_pt_in_viewport = ConvertDIPToViewport(client_pt); - Send(new DragMsg_TargetDragOver(GetRoutingID(), client_pt_in_viewport, - screen_pt, operations_allowed, - key_modifiers)); + Send(new DragMsg_TargetDragOver(GetRoutingID(), client_pt, screen_pt, + operations_allowed, key_modifiers)); } void RenderViewHostImpl::DragTargetDragLeave() { @@ -708,18 +705,17 @@ const gfx::Point& client_pt, const gfx::Point& screen_pt, int key_modifiers) { - const gfx::Point client_pt_in_viewport = ConvertDIPToViewport(client_pt); - Send(new DragMsg_TargetDrop(GetRoutingID(), client_pt_in_viewport, screen_pt, + Send(new DragMsg_TargetDrop(GetRoutingID(), client_pt, screen_pt, key_modifiers)); } void RenderViewHostImpl::DragSourceEndedAt( int client_x, int client_y, int screen_x, int screen_y, WebDragOperation operation) { - const gfx::Point client_pt_in_viewport = - ConvertDIPToViewport(gfx::Point(client_x, client_y)); - Send(new DragMsg_SourceEnded(GetRoutingID(), client_pt_in_viewport, - gfx::Point(screen_x, screen_y), operation)); + Send(new DragMsg_SourceEnded(GetRoutingID(), + gfx::Point(client_x, client_y), + gfx::Point(screen_x, screen_y), + operation)); } void RenderViewHostImpl::DragSourceSystemDragEnded() { @@ -1368,12 +1364,4 @@ delegate_->RenderViewReady(this); } -gfx::Point RenderViewHostImpl::ConvertDIPToViewport(const gfx::Point& point) { - // The point in guest view is already converted. - if (!render_widget_host_->scale_input_to_viewport()) - return point; - float scale = GetWidget()->GetView()->current_device_scale_factor(); - return gfx::Point(point.x() * scale, point.y() * scale); -} - } // namespace content
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h index 8349198..0a23a007 100644 --- a/content/browser/renderer_host/render_view_host_impl.h +++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -358,8 +358,6 @@ // files without the user's consent. void GrantFileAccessFromPageState(const PageState& validated_state); - gfx::Point ConvertDIPToViewport(const gfx::Point& point); - // The RenderWidgetHost. scoped_ptr<RenderWidgetHostImpl> render_widget_host_;
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 d78031f..d59eea5 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -2032,6 +2032,11 @@ void RenderWidgetHostViewAura::OnMouseEvent(ui::MouseEvent* event) { TRACE_EVENT0("input", "RenderWidgetHostViewAura::OnMouseEvent"); + ForwardMouseEventToParent(event); + // TODO(mgiuca): Return if event->handled() returns true. This currently + // breaks drop-down lists which means something is incorrectly setting + // event->handled to true (http://crbug.com/577983). + if (mouse_locked_) { aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(window_->GetRootWindow()); @@ -2189,18 +2194,6 @@ break; } - // Needed to propagate mouse event to |window_->parent()->delegate()|, but - // note that it might be something other than a WebContentsViewAura instance. - // TODO(pkotwicz): Find a better way of doing this. - // In fullscreen mode which is typically used by flash, don't forward - // the mouse events to the parent. The renderer and the plugin process - // handle these events. - if (!is_fullscreen_ && window_->parent() && window_->parent()->delegate() && - !(event->flags() & ui::EF_FROM_TOUCH)) { - event->ConvertLocationToTarget(window_, window_->parent()); - window_->parent()->delegate()->OnMouseEvent(event); - } - if (!IsXButtonUpEvent(event)) event->SetHandled(); } @@ -2963,6 +2956,33 @@ } } +void RenderWidgetHostViewAura::ForwardMouseEventToParent( + ui::MouseEvent* event) { + // Needed to propagate mouse event to |window_->parent()->delegate()|, but + // note that it might be something other than a WebContentsViewAura instance. + // TODO(pkotwicz): Find a better way of doing this. + // In fullscreen mode which is typically used by flash, don't forward + // the mouse events to the parent. The renderer and the plugin process + // handle these events. + if (is_fullscreen_) + return; + + if (event->flags() & ui::EF_FROM_TOUCH) + return; + + if (!window_->parent() || !window_->parent()->delegate()) + return; + + // Take a copy of |event|, to avoid ConvertLocationToTarget mutating the + // event. + scoped_ptr<ui::Event> event_copy = ui::Event::Clone(*event); + ui::MouseEvent* mouse_event = static_cast<ui::MouseEvent*>(event_copy.get()); + mouse_event->ConvertLocationToTarget(window_, window_->parent()); + window_->parent()->delegate()->OnMouseEvent(mouse_event); + if (mouse_event->handled()) + event->SetHandled(); +} + RenderViewHostDelegateView* RenderWidgetHostViewAura::GetRenderViewHostDelegateView() { // Use RenderViewHostDelegate to get to the WebContentsViewAura, which will
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 44e36900..871d712 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -522,6 +522,9 @@ // handled if it should not be further processed. void HandleGestureForTouchSelection(ui::GestureEvent* event); + // Forwards a mouse event to this view's parent window delegate. + void ForwardMouseEventToParent(ui::MouseEvent* event); + // Returns the RenderViewHostDelegateView instance for this view. Returns // NULL on failure. RenderViewHostDelegateView* GetRenderViewHostDelegateView();
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc index e4d31b23..3ba47ed 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -28,6 +28,7 @@ #include "content/browser/frame_host/render_widget_host_view_guest.h" #include "content/browser/gpu/compositor_util.h" #include "content/browser/renderer_host/input/input_router.h" +#include "content/browser/renderer_host/input/mouse_wheel_event_queue.h" #include "content/browser/renderer_host/input/web_input_event_util.h" #include "content/browser/renderer_host/overscroll_controller.h" #include "content/browser/renderer_host/overscroll_controller_delegate.h" @@ -38,6 +39,7 @@ #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/gpu_messages.h" #include "content/common/host_shared_bitmap_manager.h" +#include "content/common/input/input_event_utils.h" #include "content/common/input/synthetic_web_input_event_builders.h" #include "content/common/input_messages.h" #include "content/common/view_messages.h" @@ -590,12 +592,27 @@ protected: void SetUpOverscrollEnvironmentWithDebounce(int debounce_interval_in_ms) { - SetUpOverscrollEnvironmentImpl(debounce_interval_in_ms); + SetUpOverscrollEnvironmentImpl(debounce_interval_in_ms, false); } - void SetUpOverscrollEnvironment() { SetUpOverscrollEnvironmentImpl(0); } + void SetUpOverscrollEnvironmentWithWheelGestures() { + SetUpOverscrollEnvironmentImpl(0, true); + } - void SetUpOverscrollEnvironmentImpl(int debounce_interval_in_ms) { + void SetUpOverscrollEnvironment() { + SetUpOverscrollEnvironmentImpl(0, false); + } + + void SetUpOverscrollEnvironmentImpl(int debounce_interval_in_ms, + bool enable_wheel_gestures) { + CHECK(!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableWheelGestures) && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableWheelGestures)); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + enable_wheel_gestures ? switches::kEnableWheelGestures + : switches::kDisableWheelGestures); + ui::GestureConfiguration::GetInstance()->set_scroll_debounce_interval_in_ms( debounce_interval_in_ms); @@ -2575,6 +2592,70 @@ EXPECT_EQ(1U, sink_->message_count()); } +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_WheelScrollEventOverscrollsWithWheelGestures \ + DISABLED_WheelScrollEventOverscrollsWithWheelGestures +#else +#define MAYBE_WheelScrollEventOverscrollsWithWheelGestures \ + WheelScrollEventOverscrollsWithWheelGestures +#endif +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + MAYBE_WheelScrollEventOverscrollsWithWheelGestures) { + SetUpOverscrollEnvironmentWithWheelGestures(); + + // Simulate wheel events. + SimulateWheelEvent(-5, 0, 0, true); // sent directly + SimulateWheelEvent(-1, 1, 0, true); // enqueued + SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event + SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event + SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event + SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + + // Receive ACK the first wheel event as not processed. + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // ScrollBegin, ScrollUpdate, MouseWheel will be queued events + EXPECT_EQ(3U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + + // Receive ACK for the second (coalesced) event as not processed. This will + // start a back navigation. However, this will also cause the queued next + // event to be sent to the renderer. But since overscroll navigation has + // started, that event will also be included in the overscroll computation + // instead of being sent to the renderer. So the result will be an overscroll + // back navigation, and no event will be sent to the renderer. + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + // ScrollUpdate, MouseWheel will be queued events + EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode()); + EXPECT_EQ(-81.f, overscroll_delta_x()); + EXPECT_EQ(-31.f, overscroll_delegate()->delta_x()); + EXPECT_EQ(0.f, overscroll_delegate()->delta_y()); + EXPECT_EQ(0U, sink_->message_count()); + + // Send a mouse-move event. This should cancel the overscroll navigation. + SimulateMouseMove(5, 10, 0); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, sink_->message_count()); +} + // Tests that if some scroll events are consumed towards the start, then // subsequent scrolls do not horizontal overscroll. TEST_F(RenderWidgetHostViewAuraOverscrollTest, @@ -2639,6 +2720,65 @@ EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode()); } +// Tests that if some scroll events are consumed towards the start, then +// subsequent scrolls do not horizontal overscroll. +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_WheelScrollConsumedDoNotHorizOverscrollWithWheelGestures \ + DISABLED_WheelScrollConsumedDoNotHorizOverscrollWithWheelGestures +#else +#define MAYBE_WheelScrollConsumedDoNotHorizOverscrollWithWheelGestures \ + WheelScrollConsumedDoNotHorizOverscrollWithWheelGestures +#endif +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + MAYBE_WheelScrollConsumedDoNotHorizOverscrollWithWheelGestures) { + SetUpOverscrollEnvironmentWithWheelGestures(); + + // Simulate wheel events. + SimulateWheelEvent(-5, 0, 0, true); // sent directly + SimulateWheelEvent(-1, -1, 0, true); // enqueued + SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event + SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event + SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event + SimulateWheelEvent(-20, 6, 1, true); // enqueued, different modifiers + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + + // Receive ACK the first wheel event as processed. + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + // ScrollBegin, ScrollUpdate, MouseWheel will be queued events + EXPECT_EQ(3U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_CONSUMED); + + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + + // Receive ACK for the second (coalesced) event as not processed. This should + // not initiate overscroll, since the beginning of the scroll has been + // consumed. The queued event with different modifiers should be sent to the + // renderer. + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + // ScrollUpdate, MouseWheel will be queued events + EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + // ScrollUpdate will be queued events + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + EXPECT_EQ(0U, sink_->message_count()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); +} + // Tests that wheel-scrolling correctly turns overscroll on and off. TEST_F(RenderWidgetHostViewAuraOverscrollTest, WheelScrollOverscrollToggle) { SetUpOverscrollEnvironment(); @@ -2698,6 +2838,95 @@ EXPECT_EQ(0.f, overscroll_delegate()->delta_y()); } +// Tests that wheel-scrolling correctly turns overscroll on and off. +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_WheelScrollOverscrollToggleWithWheelGestures \ + DISABLED_WheelScrollOverscrollToggleWithWheelGestures +#else +#define MAYBE_WheelScrollOverscrollToggleWithWheelGestures \ + WheelScrollOverscrollToggleWithWheelGestures +#endif +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + MAYBE_WheelScrollOverscrollToggleWithWheelGestures) { + SetUpOverscrollEnvironmentWithWheelGestures(); + + // Send a wheel event. ACK the event as not processed. This should not + // initiate an overscroll gesture since it doesn't cross the threshold yet. + SimulateWheelEvent(10, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + + // Scroll some more so as to not overscroll. + SimulateWheelEvent(10, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + + // Scroll some more to initiate an overscroll. + SimulateWheelEvent(40, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode()); + EXPECT_EQ(60.f, overscroll_delta_x()); + EXPECT_EQ(10.f, overscroll_delegate()->delta_x()); + EXPECT_EQ(0.f, overscroll_delegate()->delta_y()); + + // Scroll in the reverse direction enough to abort the overscroll. + SimulateWheelEvent(-20, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, sink_->message_count()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + + // Continue to scroll in the reverse direction. + SimulateWheelEvent(-20, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + + // Continue to scroll in the reverse direction enough to initiate overscroll + // in that direction. + SimulateWheelEvent(-55, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); + EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode()); + EXPECT_EQ(-75.f, overscroll_delta_x()); + EXPECT_EQ(-25.f, overscroll_delegate()->delta_x()); + EXPECT_EQ(0.f, overscroll_delegate()->delta_y()); +} + TEST_F(RenderWidgetHostViewAuraOverscrollTest, ScrollEventsOverscrollWithFling) { SetUpOverscrollEnvironment(); @@ -2741,6 +2970,68 @@ EXPECT_EQ(2U, sink_->message_count()); } +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_ScrollEventsOverscrollWithFlingAndWheelGestures \ + DISABLED_ScrollEventsOverscrollWithFlingAndWheelGestures +#else +#define MAYBE_ScrollEventsOverscrollWithFlingAndWheelGestures \ + ScrollEventsOverscrollWithFlingAndWheelGestures +#endif +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + MAYBE_ScrollEventsOverscrollWithFlingAndWheelGestures) { + SetUpOverscrollEnvironmentWithWheelGestures(); + + // Send a wheel event. ACK the event as not processed. This should not + // initiate an overscroll gesture since it doesn't cross the threshold yet. + SimulateWheelEvent(10, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); + + // Scroll some more so as to not overscroll. + SimulateWheelEvent(20, 0, 0, true); + EXPECT_EQ(1U, sink_->message_count()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + sink_->ClearMessages(); + + // Scroll some more to initiate an overscroll. + SimulateWheelEvent(30, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode()); + EXPECT_EQ(60.f, overscroll_delta_x()); + EXPECT_EQ(10.f, overscroll_delegate()->delta_x()); + EXPECT_EQ(0.f, overscroll_delegate()->delta_y()); + EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); + + // Send a fling start, but with a small velocity, so that the overscroll is + // aborted. The fling should proceed to the renderer, through the gesture + // event filter. + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + blink::WebGestureDeviceTouchscreen); + SimulateGestureFlingStartEvent(0.f, 0.1f, blink::WebGestureDeviceTouchpad); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(3U, sink_->message_count()); +} + // Same as ScrollEventsOverscrollWithFling, but with zero velocity. Checks that // the zero-velocity fling does not reach the renderer. TEST_F(RenderWidgetHostViewAuraOverscrollTest, @@ -2785,6 +3076,70 @@ EXPECT_EQ(2U, sink_->message_count()); } +// Same as ScrollEventsOverscrollWithFling, but with zero velocity. Checks that +// the zero-velocity fling does not reach the renderer. +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_ScrollEventsOverscrollWithZeroFlingAndWheelGestures \ + DISABLED_ScrollEventsOverscrollWithZeroFlingAndWheelGestures +#else +#define MAYBE_ScrollEventsOverscrollWithZeroFlingAndWheelGestures \ + ScrollEventsOverscrollWithZeroFlingAndWheelGestures +#endif +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + MAYBE_ScrollEventsOverscrollWithZeroFlingAndWheelGestures) { + SetUpOverscrollEnvironmentWithWheelGestures(); + + // Send a wheel event. ACK the event as not processed. This should not + // initiate an overscroll gesture since it doesn't cross the threshold yet. + SimulateWheelEvent(10, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); + + // Scroll some more so as to not overscroll. + SimulateWheelEvent(20, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + + // Scroll some more to initiate an overscroll. + SimulateWheelEvent(30, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode()); + EXPECT_EQ(60.f, overscroll_delta_x()); + EXPECT_EQ(10.f, overscroll_delegate()->delta_x()); + EXPECT_EQ(0.f, overscroll_delegate()->delta_y()); + EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); + + // Send a fling start, but with a small velocity, so that the overscroll is + // aborted. The fling should proceed to the renderer, through the gesture + // event filter. + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + blink::WebGestureDeviceTouchscreen); + SimulateGestureFlingStartEvent(10.f, 0.f, blink::WebGestureDeviceTouchpad); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(3U, sink_->message_count()); +} + // Tests that a fling in the opposite direction of the overscroll cancels the // overscroll nav instead of completing it. TEST_F(RenderWidgetHostViewAuraOverscrollTest, ReverseFlingCancelsOverscroll) { @@ -3257,6 +3612,61 @@ EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); } +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_OverscrollDirectionChangeMouseWheelWithWheelGestures \ + DISABLED_OverscrollDirectionChangeMouseWheelWithWheelGestures +#else +#define MAYBE_OverscrollDirectionChangeMouseWheelWithWheelGestures \ + OverscrollDirectionChangeMouseWheelWithWheelGestures +#endif +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + MAYBE_OverscrollDirectionChangeMouseWheelWithWheelGestures) { + SetUpOverscrollEnvironmentWithWheelGestures(); + + // Send wheel event and receive ack as not consumed. + SimulateWheelEvent(125, -5, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // ScrollBegin, ScrollUpdate messages. + EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode()); + + // Send another wheel event, but in the reverse direction. The overscroll + // controller will not consume the event, because it is not triggering + // gesture-nav. + + SimulateWheelEvent(-260, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Since it was unhandled; the overscroll should now be west + EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode()); + + SimulateWheelEvent(-20, 0, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // wheel event ack generates gesture scroll update; which gets consumed + // solely by the overflow controller. + EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); + EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode()); +} + // Tests that if a mouse-move event completes the overscroll gesture, future // move events do reach the renderer. TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollMouseMoveCompletion) { @@ -3337,6 +3747,101 @@ INPUT_EVENT_ACK_STATE_NOT_CONSUMED); } +// Tests that if a mouse-move event completes the overscroll gesture, future +// move events do reach the renderer. +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_OverscrollMouseMoveCompletionWheelGestures \ + DISABLED_OverscrollMouseMoveCompletionWheelGestures +#else +#define MAYBE_OverscrollMouseMoveCompletionWheelGestures \ + OverscrollMouseMoveCompletionWheelGestures +#endif +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + MAYBE_OverscrollMouseMoveCompletionWheelGestures) { + SetUpOverscrollEnvironmentWithWheelGestures(); + + SimulateWheelEvent(5, 0, 0, true); // sent directly + SimulateWheelEvent(-1, 0, 0, true); // enqueued + SimulateWheelEvent(-10, -3, 0, true); // coalesced into previous event + SimulateWheelEvent(-15, -1, 0, true); // coalesced into previous event + SimulateWheelEvent(-30, -3, 0, true); // coalesced into previous event + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + + // Receive ACK the first wheel event as not processed. + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(3U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + + // Receive ACK for the second (coalesced) event as not processed. This will + // start an overcroll gesture. + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_WEST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_WEST, overscroll_delegate()->current_mode()); + EXPECT_EQ(0U, sink_->message_count()); + + // Send a mouse-move event. This should cancel the overscroll navigation + // (since the amount overscrolled is not above the threshold), and so the + // mouse-move should reach the renderer. + SimulateMouseMove(5, 10, 0); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->completed_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + + SendInputEventACK(WebInputEvent::MouseMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // Moving the mouse more should continue to send the events to the renderer. + SimulateMouseMove(5, 10, 0); + SendInputEventACK(WebInputEvent::MouseMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + + // Now try with gestures. + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + blink::WebGestureDeviceTouchscreen); + SimulateGestureScrollUpdateEvent(300, -5, 0); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode()); + sink_->ClearMessages(); + + // Overscroll gesture is in progress. Send a mouse-move now. This should + // complete the gesture (because the amount overscrolled is above the + // threshold). + SimulateMouseMove(5, 10, 0); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->completed_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + SimulateGestureEvent(WebInputEvent::GestureScrollEnd, + blink::WebGestureDeviceTouchscreen); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_delegate()->current_mode()); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + + // Move mouse some more. The mouse-move events should reach the renderer. + SimulateMouseMove(5, 10, 0); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + + SendInputEventACK(WebInputEvent::MouseMove, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); +} + // Tests that if a page scrolled, then the overscroll controller's states are // reset after the end of the scroll. TEST_F(RenderWidgetHostViewAuraOverscrollTest, @@ -3536,7 +4041,7 @@ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event); // Check if the canScroll set to true when no modifier is applied to the // mouse wheel event. - EXPECT_TRUE(wheel_event->canScroll); + EXPECT_EQ(!UseGestureBasedWheelScrolling(), wheel_event->canScroll); sink_->ClearMessages(); SendInputEventACK(blink::WebInputEvent::MouseWheel, @@ -3551,7 +4056,7 @@ wheel_event = static_cast<const WebMouseWheelEvent*>(input_event); // Check if the canScroll set to true when ctrl-touchpad-scroll is generated // from scroll event. - EXPECT_TRUE(wheel_event->canScroll); + EXPECT_EQ(!UseGestureBasedWheelScrolling(), wheel_event->canScroll); } // Ensures that the mapping from ui::TouchEvent to blink::WebTouchEvent doesn't @@ -3624,6 +4129,112 @@ EXPECT_EQ(0.f, overscroll_delta_y()); } +// Tests that the scroll deltas stored within the overscroll controller get +// reset at the end of the overscroll gesture even if the overscroll threshold +// isn't surpassed and the overscroll mode stays OVERSCROLL_NONE. +// Disabled on MacOS because it doesn't support wheel gestures +// just yet. +#if defined(OS_MACOSX) +#define MAYBE_ScrollDeltasResetOnEndWithWheelGestures \ + DISABLED_ScrollDeltasResetOnEndWithWheelGestures +#else +#define MAYBE_ScrollDeltasResetOnEndWithWheelGestures \ + ScrollDeltasResetOnEndWithWheelGestures +#endif +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + MAYBE_ScrollDeltasResetOnEndWithWheelGestures) { + SetUpOverscrollEnvironmentWithWheelGestures(); + // Wheel event scroll ending with mouse move. + SimulateWheelEvent(-30, -10, 0, true); // sent directly + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(-30.f, overscroll_delta_x()); + EXPECT_EQ(-10.f, overscroll_delta_y()); + SimulateMouseMove(5, 10, 0); + EXPECT_EQ(0.f, overscroll_delta_x()); + EXPECT_EQ(0.f, overscroll_delta_y()); + + // Scroll gesture. + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + blink::WebGestureDeviceTouchscreen); + SimulateGestureScrollUpdateEvent(-30, -5, 0); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(-30.f, overscroll_delta_x()); + EXPECT_EQ(-5.f, overscroll_delta_y()); + SimulateGestureEvent(WebInputEvent::GestureScrollEnd, + blink::WebGestureDeviceTouchscreen); + EXPECT_EQ(0.f, overscroll_delta_x()); + EXPECT_EQ(0.f, overscroll_delta_y()); + + // Wheel event scroll ending with a fling. + SimulateWheelEvent(5, 0, 0, true); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SimulateWheelEvent(10, -5, 0, true); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SendInputEventACK(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + EXPECT_EQ(15.f, overscroll_delta_x()); + EXPECT_EQ(-5.f, overscroll_delta_y()); + SimulateGestureEvent(WebInputEvent::GestureScrollBegin, + blink::WebGestureDeviceTouchscreen); + SimulateGestureFlingStartEvent(0.f, 0.1f, blink::WebGestureDeviceTouchpad); + EXPECT_EQ(0.f, overscroll_delta_x()); + EXPECT_EQ(0.f, overscroll_delta_y()); +} + +TEST_F(RenderWidgetHostViewAuraTest, ForwardMouseEvent) { + aura::Window* root = parent_view_->GetNativeView()->GetRootWindow(); + + // Set up test delegate and window hierarchy. + aura::test::EventCountDelegate delegate; + scoped_ptr<aura::Window> parent(new aura::Window(&delegate)); + parent->Init(ui::LAYER_TEXTURED); + root->AddChild(parent.get()); + view_->InitAsChild(parent.get()); + + // Simulate mouse events, ensure they are forwarded to delegate. + ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + 0); + view_->OnMouseEvent(&mouse_event); + EXPECT_EQ("1 0", delegate.GetMouseButtonCountsAndReset()); + + // Simulate mouse events, ensure they are forwarded to delegate. + mouse_event = ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), 0, 0); + view_->OnMouseEvent(&mouse_event); + EXPECT_EQ("0 1 0", delegate.GetMouseMotionCountsAndReset()); + + // Lock the mouse, simulate, and ensure they are forwarded. + view_->LockMouse(); + + mouse_event = + ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); + view_->OnMouseEvent(&mouse_event); + EXPECT_EQ("1 0", delegate.GetMouseButtonCountsAndReset()); + + mouse_event = ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), 0, 0); + view_->OnMouseEvent(&mouse_event); + EXPECT_EQ("0 1 0", delegate.GetMouseMotionCountsAndReset()); + + view_->UnlockMouse(); + + // view_ will be destroyed when parent is destroyed. + view_ = nullptr; +} + // Tests the RenderWidgetHostImpl sends the correct surface ID namespace to // the renderer process. TEST_F(RenderWidgetHostViewAuraTest, SurfaceIdNamespaceInitialized) {
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 a73a09ff..866a803f 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -2829,7 +2829,7 @@ renderWidgetHostView_->render_widget_host_ ->GetRootBrowserAccessibilityManager(); if (manager) { - BrowserAccessibility* focused_item = manager->GetFocus(NULL); + BrowserAccessibility* focused_item = manager->GetFocus(); DCHECK(focused_item); if (focused_item) { BrowserAccessibilityCocoa* focused_item_cocoa =
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm index 5178d8c4..e7a7ace 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -1000,7 +1000,9 @@ break; } DCHECK(message); - base::Tuple<IPC::WebInputEventPointer, ui::LatencyInfo> data; + base::Tuple<IPC::WebInputEventPointer, ui::LatencyInfo, + InputEventDispatchType> + data; InputMsg_HandleInputEvent::Read(message, &data); IPC::WebInputEventPointer ipc_event = base::get<0>(data); const blink::WebGestureEvent* gesture_event =
diff --git a/content/browser/renderer_host/sandbox_ipc_linux.cc b/content/browser/renderer_host/sandbox_ipc_linux.cc index 465ddbd..8fb50d4 100644 --- a/content/browser/renderer_host/sandbox_ipc_linux.cc +++ b/content/browser/renderer_host/sandbox_ipc_linux.cc
@@ -167,12 +167,12 @@ } int SandboxIPCHandler::FindOrAddPath(const SkString& path) { - int count = paths_.count(); + int count = paths_.size(); for (int i = 0; i < count; ++i) { - if (path == *paths_[i]) + if (path == paths_[i]) return i; } - *paths_.append() = new SkString(path); + paths_.emplace_back(path); return count; } @@ -221,9 +221,9 @@ uint32_t index; if (!iter.ReadUInt32(&index)) return; - if (index >= static_cast<uint32_t>(paths_.count())) + if (index >= static_cast<uint32_t>(paths_.size())) return; - const int result_fd = open(paths_[index]->c_str(), O_RDONLY); + const int result_fd = open(paths_[index].c_str(), O_RDONLY); base::Pickle reply; if (result_fd == -1) { @@ -434,7 +434,6 @@ } SandboxIPCHandler::~SandboxIPCHandler() { - paths_.deleteAll(); if (blink_platform_impl_) blink::shutdownWithoutV8();
diff --git a/content/browser/renderer_host/sandbox_ipc_linux.h b/content/browser/renderer_host/sandbox_ipc_linux.h index 5a487ef..2a836e9 100644 --- a/content/browser/renderer_host/sandbox_ipc_linux.h +++ b/content/browser/renderer_host/sandbox_ipc_linux.h
@@ -71,7 +71,7 @@ const int lifeline_fd_; const int browser_socket_; scoped_ptr<BlinkPlatformImpl> blink_platform_impl_; - SkTDArray<SkString*> paths_; + std::vector<SkString> paths_; DISALLOW_COPY_AND_ASSIGN(SandboxIPCHandler); };
diff --git a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc index 94bc88e6..260fd9d 100644 --- a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc +++ b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
@@ -17,6 +17,7 @@ #include "content/browser/service_worker/service_worker_registration.h" #include "content/browser/service_worker/service_worker_write_to_cache_job.h" #include "content/common/service_worker/service_worker_utils.h" +#include "content/public/browser/resource_request_info.h" #include "content/public/test/test_browser_thread_bundle.h" #include "net/base/load_flags.h" #include "net/url_request/url_request_context.h" @@ -160,4 +161,30 @@ EXPECT_TRUE(sw_job->net_request_->load_flags() & net::LOAD_BYPASS_CACHE); } +TEST_F(ServiceWorkerContextRequestHandlerTest, + ServiceWorkerDataRequestAnnotation) { + version_->SetStatus(ServiceWorkerVersion::NEW); + provider_host_->running_hosted_version_ = version_; + + // Conduct a resource fetch for the main script. + const GURL kScriptUrl("http://host/script.js"); + scoped_ptr<net::URLRequest> request = url_request_context_.CreateRequest( + kScriptUrl, net::DEFAULT_PRIORITY, &url_request_delegate_); + scoped_ptr<ServiceWorkerContextRequestHandler> handler( + new ServiceWorkerContextRequestHandler( + context()->AsWeakPtr(), provider_host_, + base::WeakPtr<storage::BlobStorageContext>(), + RESOURCE_TYPE_SERVICE_WORKER)); + scoped_ptr<net::URLRequestJob> job( + handler->MaybeCreateJob(request.get(), nullptr, nullptr)); + ASSERT_TRUE(job.get()); + ServiceWorkerWriteToCacheJob* sw_job = + static_cast<ServiceWorkerWriteToCacheJob*>(job.get()); + + // Verify that the request is properly annotated as originating from a + // Service Worker. + EXPECT_TRUE(ResourceRequestInfo::OriginatedFromServiceWorker( + sw_job->net_request_.get())); +} + } // namespace content
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h index 2748e29..a40ffc2 100644 --- a/content/browser/service_worker/service_worker_provider_host.h +++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -270,6 +270,8 @@ UpdateAfter24Hours); FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, UpdateForceBypassCache); + FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, + ServiceWorkerDataRequestAnnotation); struct OneShotGetReadyCallback { GetRegistrationForReadyCallback callback;
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc index 5cdf913..96e9e8f 100644 --- a/content/browser/service_worker/service_worker_write_to_cache_job.cc +++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc
@@ -13,6 +13,7 @@ #include "content/browser/service_worker/service_worker_context_core.h" #include "content/browser/service_worker/service_worker_disk_cache.h" #include "content/browser/service_worker/service_worker_metrics.h" +#include "content/common/net/url_request_service_worker_data.h" #include "content/common/service_worker/service_worker_types.h" #include "content/common/service_worker/service_worker_utils.h" #include "net/base/io_buffer.h" @@ -184,6 +185,8 @@ request()->first_party_for_cookies()); net_request_->set_initiator(request()->initiator()); net_request_->SetReferrer(request()->referrer()); + net_request_->SetUserData(URLRequestServiceWorkerData::kUserDataKey, + new URLRequestServiceWorkerData()); if (extra_load_flags) net_request_->SetLoadFlags(net_request_->load_flags() | extra_load_flags);
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.h b/content/browser/service_worker/service_worker_write_to_cache_job.h index 152dd84..314fcb2 100644 --- a/content/browser/service_worker/service_worker_write_to_cache_job.h +++ b/content/browser/service_worker/service_worker_write_to_cache_job.h
@@ -67,6 +67,8 @@ UpdateAfter24Hours); FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, UpdateForceBypassCache); + FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, + ServiceWorkerDataRequestAnnotation); ~ServiceWorkerWriteToCacheJob() override;
diff --git a/content/browser/shared_worker/worker_browsertest.cc b/content/browser/shared_worker/worker_browsertest.cc index 553d129..278d0eb 100644 --- a/content/browser/shared_worker/worker_browsertest.cc +++ b/content/browser/shared_worker/worker_browsertest.cc
@@ -172,7 +172,8 @@ net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); https_server.ServeFilesFromSourceDirectory("content/test/data"); net::SSLServerConfig ssl_config; - ssl_config.require_client_cert = true; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); ASSERT_TRUE(https_server.Start()); @@ -192,7 +193,8 @@ net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); https_server.ServeFilesFromSourceDirectory("content/test/data"); net::SSLServerConfig ssl_config; - ssl_config.require_client_cert = true; + ssl_config.client_cert_type = + net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); ASSERT_TRUE(https_server.Start());
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index c8fa716..578ffbd2 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc
@@ -3078,6 +3078,47 @@ EXPECT_TRUE(node3->current_frame_host()->IsRenderFrameLive()); } +// Ensure that the renderer does not crash when a local frame with a remote +// parent frame is swapped from local to remote, then back to local again. +// See https://crbug.com/585654. +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + NavigateSiblingsToSameProcess) { + GURL main_url( + embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html")); + NavigateToURL(shell(), main_url); + + // It is safe to obtain the root frame tree node here, as it doesn't change. + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root(); + + FrameTreeNode* node2 = root->child_at(0); + FrameTreeNode* node3 = root->child_at(1); + + // Navigate the second iframe to the same process as the first. + GURL frame_url = embedded_test_server()->GetURL("bar.com", "/title1.html"); + NavigateFrameToURL(node3, frame_url); + + // Verify that they are in the same process. + EXPECT_EQ(node2->current_frame_host()->GetSiteInstance(), + node3->current_frame_host()->GetSiteInstance()); + EXPECT_NE(root->current_frame_host()->GetSiteInstance(), + node3->current_frame_host()->GetSiteInstance()); + + // Navigate the first iframe into its parent's process. + GURL title_url = embedded_test_server()->GetURL("/title2.html"); + NavigateFrameToURL(node2, title_url); + EXPECT_NE(node2->current_frame_host()->GetSiteInstance(), + node3->current_frame_host()->GetSiteInstance()); + + // Return the first iframe to the same process as its sibling, and ensure + // that it does not crash. + NavigateFrameToURL(node2, frame_url); + EXPECT_EQ(node2->current_frame_host()->GetSiteInstance(), + node3->current_frame_host()->GetSiteInstance()); + EXPECT_TRUE(node2->current_frame_host()->IsRenderFrameLive()); +} + // Verify that load events for iframe elements work when the child frame is // out-of-process. In such cases, the load event is forwarded from the child // frame to the parent frame via the browser process.
diff --git a/content/child/OWNERS b/content/child/OWNERS index b212a3f..62b2db6 100644 --- a/content/child/OWNERS +++ b/content/child/OWNERS
@@ -1,3 +1,6 @@ +# For Blink API usage +esprehn@chromium.org + per-file appcache*=michaeln@chromium.org # WebSocket
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc index 06d4afc..bdc346d 100644 --- a/content/child/child_thread_impl.cc +++ b/content/child/child_thread_impl.cc
@@ -308,7 +308,7 @@ bool ChildThreadImpl::ChildThreadMessageRouter::RouteMessage( const IPC::Message& msg) { - bool handled = MessageRouter::RouteMessage(msg); + bool handled = IPC::MessageRouter::RouteMessage(msg); #if defined(OS_ANDROID) if (!handled && msg.is_sync()) { IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); @@ -551,7 +551,7 @@ } #endif -MessageRouter* ChildThreadImpl::GetRouter() { +IPC::MessageRouter* ChildThreadImpl::GetRouter() { DCHECK(base::MessageLoop::current() == message_loop()); return &router_; } @@ -567,18 +567,11 @@ size_t buf_size, IPC::Sender* sender) { scoped_ptr<base::SharedMemory> shared_buf; -#if defined(OS_WIN) - shared_buf.reset(new base::SharedMemory); - if (!shared_buf->CreateAnonymous(buf_size)) { - NOTREACHED(); - return nullptr; - } -#else - // On POSIX, we need to ask the browser to create the shared memory for us, - // since this is blocked by the sandbox. + // Ask the browser to create the shared memory, since this is blocked by the + // sandbox on most platforms. base::SharedMemoryHandle shared_mem_handle; if (sender->Send(new ChildProcessHostMsg_SyncAllocateSharedMemory( - buf_size, &shared_mem_handle))) { + buf_size, &shared_mem_handle))) { if (base::SharedMemory::IsHandleValid(shared_mem_handle)) { shared_buf.reset(new base::SharedMemory(shared_mem_handle, false)); } else { @@ -589,7 +582,6 @@ // Send is allowed to fail during shutdown. Return null in this case. return nullptr; } -#endif return shared_buf; }
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h index 2429e9e..462af37f 100644 --- a/content/child/child_thread_impl.h +++ b/content/child/child_thread_impl.h
@@ -20,11 +20,11 @@ #include "build/build_config.h" #include "content/child/mojo/mojo_application.h" #include "content/common/content_export.h" -#include "content/common/message_router.h" #include "content/common/mojo/channel_init.h" #include "content/public/child/child_thread.h" #include "ipc/ipc_message.h" // For IPC_MESSAGE_LOG_ENABLED. #include "ipc/ipc_platform_file.h" +#include "ipc/message_router.h" namespace base { class MessageLoop; @@ -92,7 +92,7 @@ IPC::SyncChannel* channel() { return channel_.get(); } - MessageRouter* GetRouter(); + IPC::MessageRouter* GetRouter(); // Allocates a block of shared memory of the given size. Returns NULL on // failure. @@ -203,7 +203,7 @@ scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner(); private: - class ChildThreadMessageRouter : public MessageRouter { + class ChildThreadMessageRouter : public IPC::MessageRouter { public: // |sender| must outlive this object. explicit ChildThreadMessageRouter(IPC::Sender* sender);
diff --git a/content/child/font_warmup_win.cc b/content/child/font_warmup_win.cc index 0769155..33d1807 100644 --- a/content/child/font_warmup_win.cc +++ b/content/child/font_warmup_win.cc
@@ -30,7 +30,6 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/ports/SkFontMgr.h" #include "third_party/skia/include/ports/SkTypeface_win.h" -#include "ui/gfx/hud_font.h" namespace content { @@ -494,14 +493,12 @@ // code to use these objects after warmup. SetDefaultSkiaFactory(GetPreSandboxWarmupFontMgr()); - // We need to warm up *some* font for DirectWrite. We also need to pass one - // down for the CC HUD code, so use the same one here. Note that we don't use + // We need to warm up *some* font for DirectWrite. Note that we don't use // a monospace as would be nice in an attempt to avoid a small startup time // regression, see http://crbug.com/463613. skia::RefPtr<SkTypeface> hud_typeface = skia::AdoptRef( GetPreSandboxWarmupFontMgr()->legacyCreateTypeface("Times New Roman", 0)); DoPreSandboxWarmupForTypeface(hud_typeface.get()); - gfx::SetHudTypeface(hud_typeface); } } // namespace content
diff --git a/content/child/npapi/np_channel_base.h b/content/child/npapi/np_channel_base.h index ff77bd2..fd38a93 100644 --- a/content/child/npapi/np_channel_base.h +++ b/content/child/npapi/np_channel_base.h
@@ -15,9 +15,9 @@ #include "base/memory/scoped_ptr.h" #include "base/process/process.h" #include "content/child/npapi/npobject_base.h" -#include "content/common/message_router.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_sync_channel.h" +#include "ipc/message_router.h" namespace base { class SingleThreadTaskRunner; @@ -170,7 +170,7 @@ // Used to implement message routing functionality to WebPlugin[Delegate] // objects - MessageRouter router_; + IPC::MessageRouter router_; // A channel is invalid if it is disconnected as a result of a channel // error. This flag is used to indicate the same.
diff --git a/content/child/permissions/permission_dispatcher.cc b/content/child/permissions/permission_dispatcher.cc index 59f479d..d626887 100644 --- a/content/child/permissions/permission_dispatcher.cc +++ b/content/child/permissions/permission_dispatcher.cc
@@ -12,7 +12,6 @@ #include "content/public/common/service_registry.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/modules/permissions/WebPermissionObserver.h" -#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" using blink::WebPermissionObserver; @@ -268,7 +267,6 @@ GetPermissionServicePtr()->RequestPermission( GetPermissionName(type), origin, - blink::WebUserGestureIndicator::isProcessingUserGesture(), base::Bind(&PermissionDispatcher::OnPermissionResponse, base::Unretained(this), worker_thread_id, @@ -294,7 +292,6 @@ GetPermissionServicePtr()->RequestPermissions( std::move(names), origin, - blink::WebUserGestureIndicator::isProcessingUserGesture(), base::Bind(&PermissionDispatcher::OnRequestPermissionsResponse, base::Unretained(this), worker_thread_id, callback_key)); }
diff --git a/content/child/process_control_impl.cc b/content/child/process_control_impl.cc index 9f4b474..6cf00e5 100644 --- a/content/child/process_control_impl.cc +++ b/content/child/process_control_impl.cc
@@ -7,8 +7,8 @@ #include <utility> #include "base/stl_util.h" +#include "content/common/mojo/static_application_loader.h" #include "content/public/common/content_client.h" -#include "mojo/shell/static_application_loader.h" #include "url/gurl.h" namespace content {
diff --git a/content/child/request_extra_data.cc b/content/child/request_extra_data.cc index 5105c339..b26120b 100644 --- a/content/child/request_extra_data.cc +++ b/content/child/request_extra_data.cc
@@ -23,6 +23,7 @@ transferred_request_child_id_(-1), transferred_request_request_id_(-1), service_worker_provider_id_(kInvalidServiceWorkerProviderId), + originated_from_service_worker_(false), lofi_state_(LOFI_UNSPECIFIED) { }
diff --git a/content/child/request_extra_data.h b/content/child/request_extra_data.h index 0905789a..64117c8 100644 --- a/content/child/request_extra_data.h +++ b/content/child/request_extra_data.h
@@ -90,6 +90,14 @@ int service_worker_provider_id) { service_worker_provider_id_ = service_worker_provider_id; } + // true if the request originated from within a service worker e.g. due to + // a fetch() in the service worker script. + bool originated_from_service_worker() const { + return originated_from_service_worker_; + } + void set_originated_from_service_worker(bool originated_from_service_worker) { + originated_from_service_worker_ = originated_from_service_worker; + } LoFiState lofi_state() const { return lofi_state_; } @@ -137,6 +145,7 @@ int transferred_request_child_id_; int transferred_request_request_id_; int service_worker_provider_id_; + bool originated_from_service_worker_; blink::WebString custom_user_agent_; blink::WebString requested_with_; scoped_ptr<StreamOverrideParameters> stream_override_;
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc index 78237a5..137d2ce 100644 --- a/content/child/resource_dispatcher.cc +++ b/content/child/resource_dispatcher.cc
@@ -222,31 +222,6 @@ request_info->buffer_size = shm_size; } -void ResourceDispatcher::OnReceivedDataDebug(int request_id, int data_offset) { - PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); - if (request_info) { - CHECK_GE(data_offset, 0); - CHECK_LE(data_offset, 512 * 1024); - request_info->data_offset = data_offset; - } -} - -void ResourceDispatcher::OnReceivedDataDebug2(int request_id, - int data_offset, - int data_length, - int encoded_data_length) { - PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); - if (request_info) { - // TODO(erikchen): Temporary debugging. http://crbug.com/527588. - // ResourceMsg_DataReceivedDebug2 should be indistinguishable from - // ResourceMsg_DataReceived, which means that data_offset should exceed - // 512k. The second assertion is expected to fail for some users. - CHECK_GE(data_offset, 0); - CHECK_LE(data_offset, 512 * 1024); - request_info->data_offset2 = data_offset; - } -} - void ResourceDispatcher::OnReceivedInlinedDataChunk( int request_id, const std::vector<char>& data, @@ -287,22 +262,6 @@ bool send_ack = true; if (request_info && data_length > 0) { CHECK(base::SharedMemory::IsHandleValid(request_info->buffer->handle())); - - // TODO(erikchen): Temporary debugging. http://crbug.com/527588. - CHECK_GE(request_info->buffer_size, 0); - CHECK_LE(request_info->buffer_size, 512 * 1024); - CHECK_GE(data_length, 0); - CHECK_LE(data_length, 512 * 1024); - - if (data_offset > 512 * 1024) { - int cached_data_offset = request_info->data_offset; - base::debug::Alias(&cached_data_offset); - int cached_data_offset2 = request_info->data_offset2; - base::debug::Alias(&cached_data_offset2); - CHECK_EQ(cached_data_offset, cached_data_offset2); - CHECK(false); - } - CHECK_GE(request_info->buffer_size, data_offset + data_length); base::TimeTicks time_start = base::TimeTicks::Now(); @@ -603,8 +562,6 @@ OnReceivedCachedMetadata) IPC_MESSAGE_HANDLER(ResourceMsg_ReceivedRedirect, OnReceivedRedirect) IPC_MESSAGE_HANDLER(ResourceMsg_SetDataBuffer, OnSetDataBuffer) - IPC_MESSAGE_HANDLER(ResourceMsg_DataReceivedDebug, OnReceivedDataDebug) - IPC_MESSAGE_HANDLER(ResourceMsg_DataReceivedDebug2, OnReceivedDataDebug2) IPC_MESSAGE_HANDLER(ResourceMsg_InlinedDataChunkReceived, OnReceivedInlinedDataChunk) IPC_MESSAGE_HANDLER(ResourceMsg_DataReceived, OnReceivedData) @@ -898,6 +855,8 @@ extra_data->transferred_request_request_id(); request->service_worker_provider_id = extra_data->service_worker_provider_id(); + request->originated_from_service_worker = + extra_data->originated_from_service_worker(); request->lofi_state = extra_data->lofi_state(); request->request_body = request_body; request->resource_body_stream_url = request_info.resource_body_stream_url;
diff --git a/content/child/resource_dispatcher.h b/content/child/resource_dispatcher.h index 0c33d65..17502b2b 100644 --- a/content/child/resource_dispatcher.h +++ b/content/child/resource_dispatcher.h
@@ -176,10 +176,6 @@ scoped_refptr<SharedMemoryReceivedDataFactory> received_data_factory; scoped_ptr<SiteIsolationResponseMetaData> site_isolation_metadata; int buffer_size; - - // Debugging for https://code.google.com/p/chromium/issues/detail?id=527588. - int data_offset = -1; - int data_offset2 = -1; }; using PendingRequestMap = std::map<int, scoped_ptr<PendingRequestInfo>>; @@ -201,11 +197,6 @@ base::SharedMemoryHandle shm_handle, int shm_size, base::ProcessId renderer_pid); - void OnReceivedDataDebug(int request_id, int data_offset); - void OnReceivedDataDebug2(int request_id, - int data_offset, - int data_length, - int encoded_data_length); void OnReceivedInlinedDataChunk(int request_id, const std::vector<char>& data, int encoded_data_length);
diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc index c599773b..9c593d5 100644 --- a/content/child/resource_dispatcher_unittest.cc +++ b/content/child/resource_dispatcher_unittest.cc
@@ -271,10 +271,6 @@ data.length()); EXPECT_TRUE(dispatcher_->OnMessageReceived( - ResourceMsg_DataReceivedDebug(request_id, 0))); - EXPECT_TRUE(dispatcher_->OnMessageReceived(ResourceMsg_DataReceivedDebug2( - request_id, 0, data.length(), data.length()))); - EXPECT_TRUE(dispatcher_->OnMessageReceived( ResourceMsg_DataReceived(request_id, 0, data.length(), data.length()))); }
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 1ad1981e..b9fed49 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -31,9 +31,8 @@ static void SetRuntimeFeatureDefaultsForPlatform() { #if defined(OS_ANDROID) - // MSE/EME implementation needs Android MediaCodec API. + // EME implementation needs Android MediaCodec API. if (!media::MediaCodecUtil::IsMediaCodecAvailable()) { - WebRuntimeFeatures::enableMediaSource(false); WebRuntimeFeatures::enablePrefixedEncryptedMedia(false); WebRuntimeFeatures::enableEncryptedMedia(false); }
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index 6534ca2a..af8f370 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -374,7 +374,7 @@ ] } - if (enable_mojo_media == "gpu") { + if (mojo_media_host == "gpu") { deps += [ "//media/mojo/services:cdm_service" ] } }
diff --git a/content/common/accessibility_messages.h b/content/common/accessibility_messages.h index f30b5e41..5a0a598 100644 --- a/content/common/accessibility_messages.h +++ b/content/common/accessibility_messages.h
@@ -49,6 +49,7 @@ IPC_STRUCT_TRAITS_MEMBER(doctype) IPC_STRUCT_TRAITS_MEMBER(loaded) IPC_STRUCT_TRAITS_MEMBER(loading_progress) + IPC_STRUCT_TRAITS_MEMBER(focus_id) IPC_STRUCT_TRAITS_MEMBER(sel_anchor_object_id) IPC_STRUCT_TRAITS_MEMBER(sel_anchor_offset) IPC_STRUCT_TRAITS_MEMBER(sel_focus_object_id)
diff --git a/content/common/gpu/client/gl_helper_benchmark.cc b/content/common/gpu/client/gl_helper_benchmark.cc index 9e751b3..43a1dbc 100644 --- a/content/common/gpu/client/gl_helper_benchmark.cc +++ b/content/common/gpu/client/gl_helper_benchmark.cc
@@ -24,11 +24,8 @@ #include "base/macros.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" -#include "build/build_config.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/client/gl_helper_scaling.h" -#include "content/public/test/unittest_test_suite.h" -#include "content/test/content_test_suite.h" #include "gpu/blink/webgraphicscontext3d_in_process_command_buffer_impl.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -36,10 +33,6 @@ #include "ui/gfx/codec/png_codec.h" #include "ui/gl/gl_surface.h" -#if defined(OS_MACOSX) -#include "base/mac/scoped_nsautorelease_pool.h" -#endif - namespace content { using blink::WebGLId; @@ -295,16 +288,3 @@ } } // namespace - -// These tests needs to run against a proper GL environment, so we -// need to set it up before we can run the tests. -int main(int argc, char** argv) { - base::CommandLine::Init(argc, argv); - base::TestSuite* suite = new content::ContentTestSuite(argc, argv); -#if defined(OS_MACOSX) - base::mac::ScopedNSAutoreleasePool pool; -#endif - gfx::GLSurface::InitializeOneOff(); - - return content::UnitTestTestSuite(suite).Run(); -}
diff --git a/content/common/gpu/client/gl_helper_unittest.cc b/content/common/gpu/client/gl_helper_unittest.cc index a3fb99e..758f2e1 100644 --- a/content/common/gpu/client/gl_helper_unittest.cc +++ b/content/common/gpu/client/gl_helper_unittest.cc
@@ -28,12 +28,9 @@ #include "base/test/test_suite.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" -#include "build/build_config.h" #include "content/common/gpu/client/gl_helper.h" #include "content/common/gpu/client/gl_helper_readback_support.h" #include "content/common/gpu/client/gl_helper_scaling.h" -#include "content/public/test/unittest_test_suite.h" -#include "content/test/content_test_suite.h" #include "gpu/blink/webgraphicscontext3d_in_process_command_buffer_impl.h" #include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" @@ -41,10 +38,6 @@ #include "third_party/skia/include/core/SkTypes.h" #include "ui/gl/gl_implementation.h" -#if defined(OS_MACOSX) -#include "base/mac/scoped_nsautorelease_pool.h" -#endif - namespace content { using blink::WebGLId; @@ -2000,28 +1993,3 @@ } } // namespace content - -namespace { - -int RunHelper(base::TestSuite* test_suite) { - content::UnitTestTestSuite runner(test_suite); - base::MessageLoopForIO message_loop; - return runner.Run(); -} - -} // namespace - -// These tests needs to run against a proper GL environment, so we -// need to set it up before we can run the tests. -int main(int argc, char** argv) { - base::CommandLine::Init(argc, argv); - base::TestSuite* suite = new content::ContentTestSuite(argc, argv); -#if defined(OS_MACOSX) - base::mac::ScopedNSAutoreleasePool pool; -#endif - - return base::LaunchUnitTestsSerially( - argc, - argv, - base::Bind(&RunHelper, base::Unretained(suite))); -}
diff --git a/content/common/gpu/client/gpu_channel_host.h b/content/common/gpu/client/gpu_channel_host.h index a8b5fac..36c9ac3d 100644 --- a/content/common/gpu/client/gpu_channel_host.h +++ b/content/common/gpu/client/gpu_channel_host.h
@@ -22,11 +22,11 @@ #include "content/common/content_export.h" #include "content/common/gpu/gpu_process_launch_causes.h" #include "content/common/gpu/gpu_stream_priority.h" -#include "content/common/message_router.h" #include "gpu/config/gpu_info.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_sync_channel.h" #include "ipc/message_filter.h" +#include "ipc/message_router.h" #include "media/video/jpeg_decode_accelerator.h" #include "ui/events/latency_info.h" #include "ui/gfx/geometry/size.h"
diff --git a/content/common/gpu/client/gpu_in_process_context_tests.cc b/content/common/gpu/client/gpu_in_process_context_tests.cc index 6e74057..f9733e3d 100644 --- a/content/common/gpu/client/gpu_in_process_context_tests.cc +++ b/content/common/gpu/client/gpu_in_process_context_tests.cc
@@ -7,7 +7,6 @@ #include <string> #include <vector> -#include "content/public/test/unittest_test_suite.h" #include "gpu/blink/webgraphicscontext3d_in_process_command_buffer_impl.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_surface.h"
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc index 83edce6..d6aaf4a8 100644 --- a/content/common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc +++ b/content/common/gpu/client/gpu_memory_buffer_impl_io_surface_unittest.cc
@@ -3,7 +3,7 @@ // found in the LICENSE file. #include "content/common/gpu/client/gpu_memory_buffer_impl_io_surface.h" -#include "content/test/gpu_memory_buffer_impl_test_template.h" +#include "content/common/gpu/client/gpu_memory_buffer_impl_test_template.h" namespace content { namespace {
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc index 08295c51..11568e8 100644 --- a/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc +++ b/content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap_unittest.cc
@@ -3,7 +3,7 @@ // found in the LICENSE file. #include "content/common/gpu/client/gpu_memory_buffer_impl_ozone_native_pixmap.h" -#include "content/test/gpu_memory_buffer_impl_test_template.h" +#include "content/common/gpu/client/gpu_memory_buffer_impl_test_template.h" namespace content { namespace {
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc index 251e16c..999bd5c 100644 --- a/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc +++ b/content/common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc
@@ -3,7 +3,7 @@ // found in the LICENSE file. #include "content/common/gpu/client/gpu_memory_buffer_impl_shared_memory.h" -#include "content/test/gpu_memory_buffer_impl_test_template.h" +#include "content/common/gpu/client/gpu_memory_buffer_impl_test_template.h" namespace content { namespace {
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture_unittest.cc b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture_unittest.cc index 1cd513d..6734eb5 100644 --- a/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture_unittest.cc +++ b/content/common/gpu/client/gpu_memory_buffer_impl_surface_texture_unittest.cc
@@ -3,7 +3,7 @@ // found in the LICENSE file. #include "content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h" -#include "content/test/gpu_memory_buffer_impl_test_template.h" +#include "content/common/gpu/client/gpu_memory_buffer_impl_test_template.h" namespace content { namespace {
diff --git a/content/common/gpu/client/gpu_memory_buffer_impl_test_template.h b/content/common/gpu/client/gpu_memory_buffer_impl_test_template.h new file mode 100644 index 0000000..75b6b34 --- /dev/null +++ b/content/common/gpu/client/gpu_memory_buffer_impl_test_template.h
@@ -0,0 +1,207 @@ +// 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. + +// This file defines tests that implementations of GpuMemoryBufferFactory should +// pass in order to be conformant. + +#ifndef CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_ +#define CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_ + +#include <stddef.h> +#include <string.h> + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/buffer_format_util.h" + +namespace content { + +template <typename GpuMemoryBufferImplType> +class GpuMemoryBufferImplTest : public testing::Test { + public: + GpuMemoryBufferImpl::DestructionCallback AllocateGpuMemoryBuffer( + const gfx::Size& size, + gfx::BufferFormat format, + gfx::BufferUsage usage, + gfx::GpuMemoryBufferHandle* handle, + bool* destroyed) { + return base::Bind(&GpuMemoryBufferImplTest::FreeGpuMemoryBuffer, + base::Unretained(this), + GpuMemoryBufferImplType::AllocateForTesting( + size, format, usage, handle), + base::Unretained(destroyed)); + } + + private: + void FreeGpuMemoryBuffer(const base::Closure& free_callback, + bool* destroyed, + const gpu::SyncToken& sync_token) { + free_callback.Run(); + if (destroyed) + *destroyed = true; + } +}; + +TYPED_TEST_CASE_P(GpuMemoryBufferImplTest); + +TYPED_TEST_P(GpuMemoryBufferImplTest, CreateFromHandle) { + const gfx::Size kBufferSize(8, 8); + + for (auto format : gfx::GetBufferFormatsForTesting()) { + gfx::BufferUsage usages[] = { + gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT, + gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, + gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT}; + for (auto usage : usages) { + if (!TypeParam::IsConfigurationSupported(format, usage)) + continue; + + bool destroyed = false; + gfx::GpuMemoryBufferHandle handle; + GpuMemoryBufferImpl::DestructionCallback destroy_callback = + TestFixture::AllocateGpuMemoryBuffer(kBufferSize, format, usage, + &handle, &destroyed); + scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( + handle, kBufferSize, format, usage, destroy_callback)); + ASSERT_TRUE(buffer); + EXPECT_EQ(buffer->GetFormat(), format); + + // Check if destruction callback is executed when deleting the buffer. + buffer.reset(); + ASSERT_TRUE(destroyed); + } + } +} + +TYPED_TEST_P(GpuMemoryBufferImplTest, Map) { + // Use a multiple of 4 for both dimensions to support compressed formats. + const gfx::Size kBufferSize(4, 4); + + for (auto format : gfx::GetBufferFormatsForTesting()) { + if (!TypeParam::IsConfigurationSupported( + format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE)) { + continue; + } + + gfx::GpuMemoryBufferHandle handle; + GpuMemoryBufferImpl::DestructionCallback destroy_callback = + TestFixture::AllocateGpuMemoryBuffer( + kBufferSize, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, + &handle, nullptr); + scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( + handle, kBufferSize, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, + destroy_callback)); + ASSERT_TRUE(buffer); + + const size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format); + + // Map buffer into user space. + ASSERT_TRUE(buffer->Map()); + + // Copy and compare mapped buffers. + for (size_t plane = 0; plane < num_planes; ++plane) { + const size_t row_size_in_bytes = + gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); + EXPECT_GT(row_size_in_bytes, 0u); + + scoped_ptr<char[]> data(new char[row_size_in_bytes]); + memset(data.get(), 0x2a + plane, row_size_in_bytes); + + size_t height = kBufferSize.height() / + gfx::SubsamplingFactorForBufferFormat(format, plane); + for (size_t y = 0; y < height; ++y) { + memcpy(static_cast<char*>(buffer->memory(plane)) + + y * buffer->stride(plane), + data.get(), row_size_in_bytes); + EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + + y * buffer->stride(plane), + data.get(), row_size_in_bytes)); + } + } + + buffer->Unmap(); + } +} + +TYPED_TEST_P(GpuMemoryBufferImplTest, PersistentMap) { + // Use a multiple of 4 for both dimensions to support compressed formats. + const gfx::Size kBufferSize(4, 4); + + for (auto format : gfx::GetBufferFormatsForTesting()) { + if (!TypeParam::IsConfigurationSupported( + format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT)) { + continue; + } + + gfx::GpuMemoryBufferHandle handle; + GpuMemoryBufferImpl::DestructionCallback destroy_callback = + TestFixture::AllocateGpuMemoryBuffer( + kBufferSize, format, + gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT, &handle, + nullptr); + scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( + handle, kBufferSize, format, + gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT, + destroy_callback)); + ASSERT_TRUE(buffer); + + // Map buffer into user space. + ASSERT_TRUE(buffer->Map()); + + // Copy and compare mapped buffers. + size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format); + for (size_t plane = 0; plane < num_planes; ++plane) { + const size_t row_size_in_bytes = + gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); + EXPECT_GT(row_size_in_bytes, 0u); + + scoped_ptr<char[]> data(new char[row_size_in_bytes]); + memset(data.get(), 0x2a + plane, row_size_in_bytes); + + size_t height = kBufferSize.height() / + gfx::SubsamplingFactorForBufferFormat(format, plane); + for (size_t y = 0; y < height; ++y) { + memcpy(static_cast<char*>(buffer->memory(plane)) + + y * buffer->stride(plane), + data.get(), row_size_in_bytes); + EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + + y * buffer->stride(plane), + data.get(), row_size_in_bytes)); + } + } + + buffer->Unmap(); + + // Remap the buffer, and compare again. It should contain the same data. + ASSERT_TRUE(buffer->Map()); + + for (size_t plane = 0; plane < num_planes; ++plane) { + const size_t row_size_in_bytes = + gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); + + scoped_ptr<char[]> data(new char[row_size_in_bytes]); + memset(data.get(), 0x2a + plane, row_size_in_bytes); + + size_t height = kBufferSize.height() / + gfx::SubsamplingFactorForBufferFormat(format, plane); + for (size_t y = 0; y < height; ++y) { + EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + + y * buffer->stride(plane), + data.get(), row_size_in_bytes)); + } + } + + buffer->Unmap(); + } +} + +// The GpuMemoryBufferImplTest test case verifies behavior that is expected +// from a GpuMemoryBuffer implementation in order to be conformant. +REGISTER_TYPED_TEST_CASE_P(GpuMemoryBufferImplTest, + CreateFromHandle, + Map, + PersistentMap); + +} // namespace content + +#endif // CONTENT_COMMON_GPU_CLIENT_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_
diff --git a/content/common/gpu/gpu_channel.h b/content/common/gpu/gpu_channel.h index 0c70d69..427b0d8 100644 --- a/content/common/gpu/gpu_channel.h +++ b/content/common/gpu/gpu_channel.h
@@ -23,9 +23,9 @@ #include "content/common/gpu/gpu_command_buffer_stub.h" #include "content/common/gpu/gpu_memory_manager.h" #include "content/common/gpu/gpu_stream_priority.h" -#include "content/common/message_router.h" #include "gpu/command_buffer/service/valuebuffer_manager.h" #include "ipc/ipc_sync_channel.h" +#include "ipc/message_router.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/native_widget_types.h" #include "ui/gl/gl_share_group.h" @@ -254,7 +254,7 @@ std::string channel_id_; // Used to implement message routing functionality to CommandBuffer objects - MessageRouter router_; + IPC::MessageRouter router_; // Whether the processing of IPCs on this channel is stalled and we should // preempt other GpuChannels.
diff --git a/content/common/gpu/gpu_channel_manager.cc b/content/common/gpu/gpu_channel_manager.cc index dd7646b..51b4e09 100644 --- a/content/common/gpu/gpu_channel_manager.cc +++ b/content/common/gpu/gpu_channel_manager.cc
@@ -17,7 +17,6 @@ #include "content/common/gpu/gpu_memory_buffer_factory.h" #include "content/common/gpu/gpu_memory_manager.h" #include "content/common/gpu/gpu_messages.h" -#include "content/common/message_router.h" #include "content/public/common/content_switches.h" #include "gpu/command_buffer/common/value_state.h" #include "gpu/command_buffer/service/feature_info.h" @@ -27,6 +26,7 @@ #include "gpu/command_buffer/service/shader_translator_cache.h" #include "gpu/command_buffer/service/sync_point_manager.h" #include "ipc/message_filter.h" +#include "ipc/message_router.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_share_group.h"
diff --git a/content/common/gpu/gpu_channel_manager.h b/content/common/gpu/gpu_channel_manager.h index 0fcf7ca..dbda9b4 100644 --- a/content/common/gpu/gpu_channel_manager.h +++ b/content/common/gpu/gpu_channel_manager.h
@@ -20,9 +20,9 @@ #include "content/common/content_export.h" #include "content/common/content_param_traits.h" #include "content/common/gpu/gpu_memory_manager.h" -#include "content/common/message_router.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_sender.h" +#include "ipc/message_router.h" #include "ui/gfx/gpu_memory_buffer.h" #include "ui/gfx/native_widget_types.h" #include "ui/gl/gl_surface.h" @@ -173,7 +173,7 @@ // Used to send and receive IPC messages from the browser process. IPC::SyncChannel* const channel_; - MessageRouter router_; + IPC::MessageRouter router_; GpuWatchdog* watchdog_;
diff --git a/content/common/gpu/gpu_process_launch_causes.h b/content/common/gpu/gpu_process_launch_causes.h index 533a5943..1fcdc27 100644 --- a/content/common/gpu/gpu_process_launch_causes.h +++ b/content/common/gpu/gpu_process_launch_causes.h
@@ -23,6 +23,7 @@ CAUSE_FOR_GPU_LAUNCH_GPU_MEMORY_BUFFER_ALLOCATE, CAUSE_FOR_GPU_LAUNCH_JPEGDECODEACCELERATOR_INITIALIZE, CAUSE_FOR_GPU_LAUNCH_MOJO_SETUP, + CAUSE_FOR_GPU_LAUNCH_GET_GPU_SERVICE_REGISTRY, // All new values should be inserted above this point so that // existing values continue to match up with those in histograms.xml.
diff --git a/content/common/gpu/media/android_video_decode_accelerator.cc b/content/common/gpu/media/android_video_decode_accelerator.cc index b860b4ac..bf99f48 100644 --- a/content/common/gpu/media/android_video_decode_accelerator.cc +++ b/content/common/gpu/media/android_video_decode_accelerator.cc
@@ -22,9 +22,11 @@ #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/command_buffer/service/mailbox_manager.h" +#include "media/base/android/media_codec_util.h" #include "media/base/bind_to_current_loop.h" #include "media/base/bitstream_buffer.h" #include "media/base/limits.h" +#include "media/base/media.h" #include "media/base/media_switches.h" #include "media/base/timestamp_constants.h" #include "media/base/video_decoder_config.h" @@ -371,6 +373,8 @@ TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", pending_bitstream_buffers_.size()); + DCHECK_NE(state_, ERROR); + state_ = WAITING_FOR_EOS; media_codec_->QueueEOS(input_buf_index); return true; } @@ -534,11 +538,23 @@ if (eos) { DVLOG(3) << __FUNCTION__ << ": Resetting codec state after EOS"; - ResetCodecState(); - base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(&AndroidVideoDecodeAccelerator::NotifyFlushDone, - weak_this_factory_.GetWeakPtr())); + // If we were waiting for an EOS, clear the state and reset the MediaCodec + // as normal. Otherwise, enter the ERROR state which will force destruction + // of MediaCodec during ResetCodecState(). + // + // Some Android platforms seem to send an EOS buffer even when we're not + // expecting it. In this case, destroy and reset the codec but don't notify + // flush done since it violates the state machine. http://crbug.com/585959. + const bool was_waiting_for_eos = state_ == WAITING_FOR_EOS; + state_ = was_waiting_for_eos ? NO_ERROR : ERROR; + + ResetCodecState(); + if (was_waiting_for_eos) { + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&AndroidVideoDecodeAccelerator::NotifyFlushDone, + weak_this_factory_.GetWeakPtr())); + } return false; } @@ -991,15 +1007,19 @@ SupportedProfile profile; - profile.profile = media::VP8PROFILE_ANY; - profile.min_resolution.SetSize(0, 0); - profile.max_resolution.SetSize(1920, 1088); - profiles.push_back(profile); + if (media::MediaCodecUtil::IsVp8DecoderAvailable()) { + profile.profile = media::VP8PROFILE_ANY; + profile.min_resolution.SetSize(0, 0); + profile.max_resolution.SetSize(1920, 1088); + profiles.push_back(profile); + } - profile.profile = media::VP9PROFILE_ANY; - profile.min_resolution.SetSize(0, 0); - profile.max_resolution.SetSize(1920, 1088); - profiles.push_back(profile); + if (media::PlatformHasVp9Support()) { + profile.profile = media::VP9PROFILE_ANY; + profile.min_resolution.SetSize(0, 0); + profile.max_resolution.SetSize(1920, 1088); + profiles.push_back(profile); + } for (const auto& supported_profile : kSupportedH264Profiles) { SupportedProfile profile;
diff --git a/content/common/gpu/media/android_video_decode_accelerator.h b/content/common/gpu/media/android_video_decode_accelerator.h index e70c09ee..c3f226bd 100644 --- a/content/common/gpu/media/android_video_decode_accelerator.h +++ b/content/common/gpu/media/android_video_decode_accelerator.h
@@ -137,6 +137,7 @@ NO_ERROR, ERROR, WAITING_FOR_KEY, + WAITING_FOR_EOS, }; static const base::TimeDelta kDecodePollDelay;
diff --git a/content/common/gpu/media/dxva_video_decode_accelerator_win.cc b/content/common/gpu/media/dxva_video_decode_accelerator_win.cc index a2f28f23..e01c114 100644 --- a/content/common/gpu/media/dxva_video_decode_accelerator_win.cc +++ b/content/common/gpu/media/dxva_video_decode_accelerator_win.cc
@@ -1139,11 +1139,12 @@ void DXVAVideoDecodeAccelerator::PreSandboxInitialization() { ::LoadLibrary(L"MFPlat.dll"); ::LoadLibrary(L"msmpeg2vdec.dll"); + ::LoadLibrary(L"mf.dll"); + ::LoadLibrary(L"dxva2.dll"); if (base::win::GetVersion() > base::win::VERSION_WIN7) { LoadLibrary(L"msvproc.dll"); } else { - LoadLibrary(L"dxva2.dll"); #if defined(ENABLE_DX11_FOR_WIN7) LoadLibrary(L"mshtmlmedia.dll"); #endif
diff --git a/content/common/gpu/media/v4l2_video_decode_accelerator.cc b/content/common/gpu/media/v4l2_video_decode_accelerator.cc index d58ecde8..2c43e6e9 100644 --- a/content/common/gpu/media/v4l2_video_decode_accelerator.cc +++ b/content/common/gpu/media/v4l2_video_decode_accelerator.cc
@@ -253,16 +253,9 @@ const __u32 kCapsRequired = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps); if ((caps.capabilities & kCapsRequired) != kCapsRequired) { - // This cap combination is deprecated, but some older drivers may still be - // returning it. - const __u32 kCapsRequiredCompat = V4L2_CAP_VIDEO_CAPTURE_MPLANE | - V4L2_CAP_VIDEO_OUTPUT_MPLANE | - V4L2_CAP_STREAMING; - if ((caps.capabilities & kCapsRequiredCompat) != kCapsRequiredCompat) { - LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP" - ", caps check failed: 0x" << std::hex << caps.capabilities; - return false; - } + LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP" + ", caps check failed: 0x" << std::hex << caps.capabilities; + return false; } if (!SetupFormats())
diff --git a/content/common/gpu/media/v4l2_video_encode_accelerator.cc b/content/common/gpu/media/v4l2_video_encode_accelerator.cc index 8c97f1f..85f54d0 100644 --- a/content/common/gpu/media/v4l2_video_encode_accelerator.cc +++ b/content/common/gpu/media/v4l2_video_encode_accelerator.cc
@@ -128,16 +128,9 @@ const __u32 kCapsRequired = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps); if ((caps.capabilities & kCapsRequired) != kCapsRequired) { - // This cap combination is deprecated, but some older drivers may still be - // returning it. - const __u32 kCapsRequiredCompat = V4L2_CAP_VIDEO_CAPTURE_MPLANE | - V4L2_CAP_VIDEO_OUTPUT_MPLANE | - V4L2_CAP_STREAMING; - if ((caps.capabilities & kCapsRequiredCompat) != kCapsRequiredCompat) { - LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " - "caps check failed: 0x" << std::hex << caps.capabilities; - return false; - } + LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " + "caps check failed: 0x" << std::hex << caps.capabilities; + return false; } if (!SetFormats(input_format, output_profile)) {
diff --git a/content/common/input/input_event_ack_state.h b/content/common/input/input_event_ack_state.h index 5b9c706..c8e8f83 100644 --- a/content/common/input/input_event_ack_state.h +++ b/content/common/input/input_event_ack_state.h
@@ -14,7 +14,8 @@ INPUT_EVENT_ACK_STATE_NOT_CONSUMED, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, INPUT_EVENT_ACK_STATE_IGNORED, - INPUT_EVENT_ACK_STATE_MAX = INPUT_EVENT_ACK_STATE_IGNORED + INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING, + INPUT_EVENT_ACK_STATE_MAX = INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING }; } // namespace content
diff --git a/content/common/input/input_event_dispatch_type.h b/content/common/input/input_event_dispatch_type.h new file mode 100644 index 0000000..e4acb4c1 --- /dev/null +++ b/content/common/input/input_event_dispatch_type.h
@@ -0,0 +1,18 @@ +// 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_COMMON_INPUT_INPUT_EVENT_DISPATCH_TYPE_H_ +#define CONTENT_COMMON_INPUT_INPUT_EVENT_DISPATCH_TYPE_H_ + +namespace content { + +enum InputEventDispatchType { + DISPATCH_TYPE_NORMAL, // Dispatch a normal event. + DISPATCH_TYPE_NON_BLOCKING, // Dispatch a non-blocking event. + DISPATCH_TYPE_MAX = DISPATCH_TYPE_NON_BLOCKING +}; + +} // namespace content + +#endif // CONTENT_COMMON_INPUT_INPUT_EVENT_DISPATCH_TYPE_H_
diff --git a/content/common/input/input_event_utils.cc b/content/common/input/input_event_utils.cc index 925452f5..6bd9eae 100644 --- a/content/common/input/input_event_utils.cc +++ b/content/common/input/input_event_utils.cc
@@ -11,7 +11,13 @@ bool UseGestureBasedWheelScrolling() { base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); +// TODO(dtapuska): OSX has special code to deal with mouse wheel +// and overscroll that needs to be fixed. crbug.com/587979 +#if defined(OS_MACOSX) return cmd->HasSwitch(switches::kEnableWheelGestures); +#else + return !cmd->HasSwitch(switches::kDisableWheelGestures); +#endif } } // namespace content
diff --git a/content/common/input/web_input_event_queue.h b/content/common/input/web_input_event_queue.h new file mode 100644 index 0000000..fa85bd4f7 --- /dev/null +++ b/content/common/input/web_input_event_queue.h
@@ -0,0 +1,79 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_COMMON_INPUT_WEB_INPUT_EVENT_QUEUE_H_ +#define CONTENT_COMMON_INPUT_WEB_INPUT_EVENT_QUEUE_H_ + +#include <deque> + +#include "base/memory/scoped_ptr.h" + +namespace content { + +enum class WebInputEventQueueState { ITEM_PENDING, ITEM_NOT_PENDING }; + +// WebInputEventQueue is a coalescing queue with the addition of a state +// variable that represents whether an item is pending to be processed. +// The desired usage sending with this queue is: +// if (queue.state() == WebInputEventQueueState::ITEM_PENDING) { +// queue.Queue(T); +// } else { +// send T +// queue.set_state(WebInputEventQueueState::ITEM_PENDING); +// } +// +// Processing the event response: +// if (!queue.empty()) { +// T = queue.Pop(); +// send T now +// } else { +// queue.set_state(WebInputEventQueueState::ITEM_NOT_PENDING); +// } +// +template <typename T> +class WebInputEventQueue { + public: + WebInputEventQueue() : state_(WebInputEventQueueState::ITEM_NOT_PENDING) {} + + // Adds an event to the queue. The event may be coalesced with previously + // queued events. + void Queue(const T& event) { + if (!queue_.empty()) { + scoped_ptr<T>& last_event = queue_.back(); + if (last_event->CanCoalesceWith(event)) { + last_event->CoalesceWith(event); + return; + } + } + queue_.emplace_back(scoped_ptr<T>(new T(event))); + } + + scoped_ptr<T> Pop() { + scoped_ptr<T> result; + if (!queue_.empty()) { + result.reset(queue_.front().release()); + queue_.pop_front(); + } + return result; + } + + bool empty() const { return queue_.empty(); } + + size_t size() const { return queue_.size(); } + + void set_state(WebInputEventQueueState state) { state_ = state; } + + WebInputEventQueueState state() const WARN_UNUSED_RESULT { return state_; } + + private: + typedef std::deque<scoped_ptr<T>> EventQueue; + EventQueue queue_; + WebInputEventQueueState state_; + + DISALLOW_COPY_AND_ASSIGN(WebInputEventQueue); +}; + +} // namespace content + +#endif // CONTENT_COMMON_INPUT_WEB_INPUT_EVENT_QUEUE_H_
diff --git a/content/common/input_messages.h b/content/common/input_messages.h index 7f05b9f..ed0f5f2 100644 --- a/content/common/input_messages.h +++ b/content/common/input_messages.h
@@ -15,6 +15,7 @@ #include "content/common/input/input_event.h" #include "content/common/input/input_event_ack.h" #include "content/common/input/input_event_ack_state.h" +#include "content/common/input/input_event_dispatch_type.h" #include "content/common/input/input_param_traits.h" #include "content/common/input/synthetic_gesture_packet.h" #include "content/common/input/synthetic_gesture_params.h" @@ -48,6 +49,8 @@ IPC_ENUM_TRAITS_MAX_VALUE( content::SyntheticGestureParams::GestureType, content::SyntheticGestureParams::SYNTHETIC_GESTURE_TYPE_MAX) +IPC_ENUM_TRAITS_MAX_VALUE(content::InputEventDispatchType, + content::InputEventDispatchType::DISPATCH_TYPE_MAX) IPC_ENUM_TRAITS_VALIDATE(content::TouchAction, ( value >= 0 && value <= content::TOUCH_ACTION_MAX && @@ -114,9 +117,10 @@ IPC_STRUCT_TRAITS_END() // Sends an input event to the render widget. -IPC_MESSAGE_ROUTED2(InputMsg_HandleInputEvent, +IPC_MESSAGE_ROUTED3(InputMsg_HandleInputEvent, IPC::WebInputEventPointer /* event */, - ui::LatencyInfo /* latency_info */) + ui::LatencyInfo /* latency_info */, + content::InputEventDispatchType) // Sends the cursor visibility state to the render widget. IPC_MESSAGE_ROUTED1(InputMsg_CursorVisibilityChange,
diff --git a/content/common/message_router.cc b/content/common/message_router.cc deleted file mode 100644 index ac6361e..0000000 --- a/content/common/message_router.cc +++ /dev/null
@@ -1,57 +0,0 @@ -// 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. - -#include "content/common/message_router.h" - -#include "ipc/ipc_message.h" - -namespace content { - -MessageRouter::MessageRouter() { -} - -MessageRouter::~MessageRouter() { -} - -bool MessageRouter::OnControlMessageReceived(const IPC::Message& msg) { - NOTREACHED() << - "should override in subclass if you care about control messages"; - return false; -} - -bool MessageRouter::Send(IPC::Message* msg) { - NOTREACHED() << - "should override in subclass if you care about sending messages"; - return false; -} - -bool MessageRouter::AddRoute(int32_t routing_id, IPC::Listener* listener) { - if (routes_.Lookup(routing_id)) { - DLOG(ERROR) << "duplicate routing ID"; - return false; - } - routes_.AddWithID(listener, routing_id); - return true; -} - -void MessageRouter::RemoveRoute(int32_t routing_id) { - routes_.Remove(routing_id); -} - -bool MessageRouter::OnMessageReceived(const IPC::Message& msg) { - if (msg.routing_id() == MSG_ROUTING_CONTROL) - return OnControlMessageReceived(msg); - - return RouteMessage(msg); -} - -bool MessageRouter::RouteMessage(const IPC::Message& msg) { - IPC::Listener* listener = routes_.Lookup(msg.routing_id()); - if (!listener) - return false; - - return listener->OnMessageReceived(msg); -} - -} // namespace content
diff --git a/content/common/message_router.h b/content/common/message_router.h deleted file mode 100644 index fea7325..0000000 --- a/content/common/message_router.h +++ /dev/null
@@ -1,71 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_MESSAGE_ROUTER_H_ -#define CONTENT_COMMON_MESSAGE_ROUTER_H_ - -#include <stdint.h> - -#include "base/id_map.h" -#include "base/macros.h" -#include "content/common/content_export.h" -#include "ipc/ipc_listener.h" -#include "ipc/ipc_sender.h" - -// The MessageRouter handles all incoming messages sent to it by routing them -// to the correct listener. Routing is based on the Message's routing ID. -// Since routing IDs are typically assigned asynchronously by the browser -// process, the MessageRouter has the notion of pending IDs for listeners that -// have not yet been assigned a routing ID. -// -// When a message arrives, the routing ID is used to index the set of routes to -// find a listener. If a listener is found, then the message is passed to it. -// Otherwise, the message is ignored if its routing ID is not equal to -// MSG_ROUTING_CONTROL. -// -// The MessageRouter supports the IPC::Sender interface for outgoing messages, -// but does not define a meaningful implementation of it. The subclass of -// MessageRouter is intended to provide that if appropriate. -// -// The MessageRouter can be used as a concrete class provided its Send method -// is not called and it does not receive any control messages. - -namespace content { - -class CONTENT_EXPORT MessageRouter : public IPC::Listener, public IPC::Sender { - public: - MessageRouter(); - ~MessageRouter() override; - - // Implemented by subclasses to handle control messages - virtual bool OnControlMessageReceived(const IPC::Message& msg); - - // IPC::Listener implementation: - bool OnMessageReceived(const IPC::Message& msg) override; - - // Like OnMessageReceived, except it only handles routed messages. Returns - // true if the message was dispatched, or false if there was no listener for - // that route id. - virtual bool RouteMessage(const IPC::Message& msg); - - // IPC::Sender implementation: - bool Send(IPC::Message* msg) override; - - // Called to add a listener for a particular message routing ID. - // Returns true if succeeded. - bool AddRoute(int32_t routing_id, IPC::Listener* listener); - - // Called to remove a listener for a particular message routing ID. - void RemoveRoute(int32_t routing_id); - - private: - // A list of all listeners with assigned routing IDs. - IDMap<IPC::Listener> routes_; - - DISALLOW_COPY_AND_ASSIGN(MessageRouter); -}; - -} // namespace content - -#endif // CONTENT_COMMON_MESSAGE_ROUTER_H_
diff --git a/content/common/mojo/DEPS b/content/common/mojo/DEPS index 906af32..0f2f39e 100644 --- a/content/common/mojo/DEPS +++ b/content/common/mojo/DEPS
@@ -1,7 +1,5 @@ include_rules = [ "+mojo/converters/network", "+mojo/edk/embedder", - "+mojo/shell/public/cpp", - "+mojo/shell/public/interfaces", - "+mojo/shell/runner/child", + "+mojo/shell", ]
diff --git a/content/common/mojo/channel_init.cc b/content/common/mojo/channel_init.cc index 72a7a6e..c8172ab2 100644 --- a/content/common/mojo/channel_init.cc +++ b/content/common/mojo/channel_init.cc
@@ -25,7 +25,6 @@ mojo::ScopedMessagePipeHandle ChannelInit::Init( base::PlatformFile file, scoped_refptr<base::TaskRunner> io_thread_task_runner) { - ipc_support_.reset(new IPC::ScopedIPCSupport(io_thread_task_runner)); return mojo::edk::CreateMessagePipe( mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(file))); }
diff --git a/content/common/mojo/channel_init.h b/content/common/mojo/channel_init.h index 3f4c191..6b9daae 100644 --- a/content/common/mojo/channel_init.h +++ b/content/common/mojo/channel_init.h
@@ -9,7 +9,6 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "content/common/content_export.h" -#include "ipc/mojo/scoped_ipc_support.h" #include "mojo/public/cpp/system/message_pipe.h" namespace base { @@ -20,6 +19,9 @@ // ChannelInit handles creation and destruction of the Mojo channel. It is not // thread-safe, but may be used on any single thread with a MessageLoop. +// +// TODO(rockot): Get rid of this class ASAP (i.e. once the patch which includes +// this TODO has stuck for a bit) since it's no longer necessary. class CONTENT_EXPORT ChannelInit { public: ChannelInit(); @@ -31,8 +33,6 @@ scoped_refptr<base::TaskRunner> io_thread_task_runner); private: - scoped_ptr<IPC::ScopedIPCSupport> ipc_support_; - DISALLOW_COPY_AND_ASSIGN(ChannelInit); };
diff --git a/content/common/mojo/static_application_loader.cc b/content/common/mojo/static_application_loader.cc new file mode 100644 index 0000000..263236c4 --- /dev/null +++ b/content/common/mojo/static_application_loader.cc
@@ -0,0 +1,96 @@ +// 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. + +#include "content/common/mojo/static_application_loader.h" + +#include <utility> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/simple_thread.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/shell/public/cpp/application_runner.h" +#include "mojo/shell/public/cpp/shell_client.h" +#include "mojo/shell/public/interfaces/shell_client.mojom.h" + +namespace content { + +namespace { + +class RunnerThread : public base::SimpleThread { + public: + RunnerThread(const GURL& url, + mojo::shell::mojom::ShellClientRequest request, + scoped_refptr<base::TaskRunner> exit_task_runner, + const base::Closure& exit_callback, + const StaticApplicationLoader::ApplicationFactory& factory) + : base::SimpleThread("Mojo Application: " + url.spec()), + request_(std::move(request)), + exit_task_runner_(exit_task_runner), + exit_callback_(exit_callback), + factory_(factory) {} + + void Run() override { + scoped_ptr<mojo::ApplicationRunner> runner( + new mojo::ApplicationRunner(factory_.Run().release())); + runner->Run(request_.PassMessagePipe().release().value(), + false /* init_base */); + exit_task_runner_->PostTask(FROM_HERE, exit_callback_); + } + + private: + mojo::shell::mojom::ShellClientRequest request_; + scoped_refptr<base::TaskRunner> exit_task_runner_; + base::Closure exit_callback_; + StaticApplicationLoader::ApplicationFactory factory_; + + DISALLOW_COPY_AND_ASSIGN(RunnerThread); +}; + +} // namespace + +StaticApplicationLoader::StaticApplicationLoader( + const ApplicationFactory& factory) + : StaticApplicationLoader(factory, base::Closure()) { +} + +StaticApplicationLoader::StaticApplicationLoader( + const ApplicationFactory& factory, + const base::Closure& quit_callback) + : factory_(factory), quit_callback_(quit_callback), weak_factory_(this) { +} + +StaticApplicationLoader::~StaticApplicationLoader() { + if (thread_) + StopAppThread(); +} + +void StaticApplicationLoader::Load( + const GURL& url, + mojo::shell::mojom::ShellClientRequest request) { + if (thread_) + return; + + // If the application's thread quits on its own before this loader dies, we + // reset the Thread object, allowing future Load requests to be fulfilled + // with a new app instance. + auto exit_callback = base::Bind(&StaticApplicationLoader::StopAppThread, + weak_factory_.GetWeakPtr()); + thread_.reset(new RunnerThread(url, std::move(request), + base::ThreadTaskRunnerHandle::Get(), + exit_callback, factory_)); + thread_->Start(); +} + +void StaticApplicationLoader::StopAppThread() { + thread_->Join(); + thread_.reset(); + if (!quit_callback_.is_null()) + quit_callback_.Run(); +} + +} // namespace content
diff --git a/content/common/mojo/static_application_loader.h b/content/common/mojo/static_application_loader.h new file mode 100644 index 0000000..44402c56 --- /dev/null +++ b/content/common/mojo/static_application_loader.h
@@ -0,0 +1,67 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_COMMON_MOJO_STATIC_APPLICATION_LOADER_H_ +#define CONTENT_COMMON_MOJO_STATIC_APPLICATION_LOADER_H_ + +#include <list> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "mojo/shell/application_loader.h" + +namespace base { +class SimpleThread; +} + +namespace mojo { +class ShellClient; +} + +namespace content { + +// An ApplicationLoader which loads a single type of app from a given +// mojo::ShellClient factory. A Load() request is fulfilled by creating an +// instance of the app on a new thread. Only one instance of the app will run at +// a time. Any Load requests received while the app is running will be dropped. +class StaticApplicationLoader : public mojo::shell::ApplicationLoader { + public: + using ApplicationFactory = + base::Callback<scoped_ptr<mojo::ShellClient>()>; + + // Constructs a static loader for |factory|. + explicit StaticApplicationLoader(const ApplicationFactory& factory); + + // Constructs a static loader for |factory| with a closure that will be called + // when the loaded application quits. + StaticApplicationLoader(const ApplicationFactory& factory, + const base::Closure& quit_callback); + + ~StaticApplicationLoader() override; + + // mojo::shell::ApplicationLoader: + void Load(const GURL& url, + mojo::shell::mojom::ShellClientRequest request) override; + + private: + void StopAppThread(); + + // The factory used t create new instances of the application delegate. + ApplicationFactory factory_; + + // If not null, this is run when the loaded application quits. + base::Closure quit_callback_; + + // Thread for the application if currently running. + scoped_ptr<base::SimpleThread> thread_; + + base::WeakPtrFactory<StaticApplicationLoader> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(StaticApplicationLoader); +}; + +} // namespace content + +#endif // CONTENT_COMMON_MOJO_STATIC_APPLICATION_LOADER_H_
diff --git a/content/common/net/url_request_service_worker_data.cc b/content/common/net/url_request_service_worker_data.cc new file mode 100644 index 0000000..cbf2a87 --- /dev/null +++ b/content/common/net/url_request_service_worker_data.cc
@@ -0,0 +1,17 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/common/net/url_request_service_worker_data.h" + +namespace content { + +URLRequestServiceWorkerData::URLRequestServiceWorkerData() {} + +URLRequestServiceWorkerData::~URLRequestServiceWorkerData() {} + +// static +const void* URLRequestServiceWorkerData::kUserDataKey = + static_cast<const void*>(&URLRequestServiceWorkerData::kUserDataKey); + +} // namespace content
diff --git a/content/common/net/url_request_service_worker_data.h b/content/common/net/url_request_service_worker_data.h new file mode 100644 index 0000000..4d72321d --- /dev/null +++ b/content/common/net/url_request_service_worker_data.h
@@ -0,0 +1,28 @@ +// 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_COMMON_NET_URL_REQUEST_SERVICE_WORKER_DATA_H_ +#define CONTENT_COMMON_NET_URL_REQUEST_SERVICE_WORKER_DATA_H_ + +#include "base/supports_user_data.h" + +namespace content { + +// Used to annotate all URLRequests for which the request originated in the +// Service Worker and for initial Service Worker script loads. +// Summarized this includes requests due to: +// - fetching Service Worker script itself for installation, +// - importing other scripts from within a Service Worker script, +// - calling fetch() from within a Service Worker script. +class URLRequestServiceWorkerData : public base::SupportsUserData::Data { + public: + URLRequestServiceWorkerData(); + ~URLRequestServiceWorkerData() override; + + static const void* kUserDataKey; +}; + +} // namespace content + +#endif // CONTENT_COMMON_NET_URL_REQUEST_SERVICE_WORKER_DATA_H_
diff --git a/content/common/permission_service.mojom b/content/common/permission_service.mojom index 5a617879..ef25cf6b2 100644 --- a/content/common/permission_service.mojom +++ b/content/common/permission_service.mojom
@@ -24,9 +24,9 @@ interface PermissionService { HasPermission(PermissionName permission, string origin) => (PermissionStatus status); - RequestPermission(PermissionName permission, string origin, bool user_gesture) + RequestPermission(PermissionName permission, string origin) => (PermissionStatus status); - RequestPermissions(array<PermissionName> permission, string origin, bool user_gesture) + RequestPermissions(array<PermissionName> permission, string origin) => (array<PermissionStatus> statuses); RevokePermission(PermissionName permission, string origin) => (PermissionStatus status);
diff --git a/content/common/resource_messages.h b/content/common/resource_messages.h index 20b0475..ee5b93f 100644 --- a/content/common/resource_messages.h +++ b/content/common/resource_messages.h
@@ -223,6 +223,10 @@ // or kInvalidServiceWorkerProviderId. IPC_STRUCT_MEMBER(int, service_worker_provider_id) + // True if the request originated from a Service Worker, e.g. due to a + // fetch() in the Service Worker script. + IPC_STRUCT_MEMBER(bool, originated_from_service_worker) + // True if the request should not be handled by the ServiceWorker. IPC_STRUCT_MEMBER(bool, skip_service_worker)
diff --git a/content/common/url_schemes.cc b/content/common/url_schemes.cc index 6d4c58c6..c985e85 100644 --- a/content/common/url_schemes.cc +++ b/content/common/url_schemes.cc
@@ -18,35 +18,37 @@ namespace { -void AddStandardSchemeHelper(const url::SchemeWithType& scheme) { - url::AddStandardScheme(scheme.scheme, scheme.type); -} - } // namespace namespace content { -void RegisterContentSchemes(bool lock_standard_schemes) { +void RegisterContentSchemes(bool lock_schemes) { std::vector<url::SchemeWithType> additional_standard_schemes; + std::vector<url::SchemeWithType> additional_referrer_schemes; std::vector<std::string> additional_savable_schemes; + GetContentClient()->AddAdditionalSchemes(&additional_standard_schemes, + &additional_referrer_schemes, &additional_savable_schemes); url::AddStandardScheme(kChromeDevToolsScheme, url::SCHEME_WITHOUT_PORT); url::AddStandardScheme(kChromeUIScheme, url::SCHEME_WITHOUT_PORT); url::AddStandardScheme(kGuestScheme, url::SCHEME_WITHOUT_PORT); url::AddStandardScheme(kMetadataScheme, url::SCHEME_WITHOUT_AUTHORITY); - std::for_each(additional_standard_schemes.begin(), - additional_standard_schemes.end(), - AddStandardSchemeHelper); - // Prevent future modification of the standard schemes list. This is to - // prevent accidental creation of data races in the program. AddStandardScheme - // isn't threadsafe so must be called when GURL isn't used on any other - // thread. This is really easy to mess up, so we say that all calls to - // AddStandardScheme in Chrome must be inside this function. - if (lock_standard_schemes) - url::LockStandardSchemes(); + for (const url::SchemeWithType& scheme : additional_standard_schemes) + url::AddStandardScheme(scheme.scheme, scheme.type); + + for (const url::SchemeWithType& scheme : additional_referrer_schemes) + url::AddReferrerScheme(scheme.scheme, scheme.type); + + // Prevent future modification of the scheme lists. This is to prevent + // accidental creation of data races in the program. Add*Scheme aren't + // threadsafe so must be called when GURL isn't used on any other thread. This + // is really easy to mess up, so we say that all calls to Add*Scheme in Chrome + // must be inside this function. + if (lock_schemes) + url::LockSchemeRegistries(); // We rely on the above lock to protect this part from being invoked twice. if (!additional_savable_schemes.empty()) {
diff --git a/content/common/url_schemes.h b/content/common/url_schemes.h index 571c660f..1441c9c 100644 --- a/content/common/url_schemes.h +++ b/content/common/url_schemes.h
@@ -10,16 +10,16 @@ namespace content { // Note: ContentMainRunner calls this method internally as part of main -// initialziation, so this function generally should not be called by +// initialization, so this function generally should not be called by // embedders. It's exported to facilitate test harnesses that do not // utilize ContentMainRunner and that do not wish to lock the set // of standard schemes at init time. // -// Called near the beginning of startup to register URL schemes that should -// be parsed as "standard" with the src/url/ library. Optionally, the set -// of standard schemes is locked down. The embedder can add additional -// schemes by overriding the ContentClient::AddAdditionalSchemes method. -CONTENT_EXPORT void RegisterContentSchemes(bool lock_standard_schemes); +// Called near the beginning of startup to register URL schemes that should be +// parsed as "standard" or "referrer" with the src/url/ library. Optionally, the +// sets of schemes are locked down. The embedder can add additional schemes by +// overriding the ContentClient::AddAdditionalSchemes method. +CONTENT_EXPORT void RegisterContentSchemes(bool lock_schemes); } // namespace content
diff --git a/content/content.gyp b/content/content.gyp index 6b11ac2f..232814f6 100644 --- a/content/content.gyp +++ b/content/content.gyp
@@ -46,13 +46,11 @@ ], }, ], + 'includes': [ + '../build/win_precompile.gypi', + 'content_resources.gypi', + ], 'conditions': [ - ['OS != "ios"', { - 'includes': [ - '../build/win_precompile.gypi', - 'content_resources.gypi', - ], - }], ['OS == "win"', { 'targets': [ { @@ -89,23 +87,17 @@ 'type': 'none', 'dependencies': [ 'content_browser', + 'content_child', 'content_common', + 'content_gpu', + 'content_plugin', + 'content_ppapi_plugin', + 'content_renderer', + 'content_utility', ], 'export_dependent_settings': [ 'content_common', ], - 'conditions': [ - ['OS != "ios"', { - 'dependencies': [ - 'content_child', - 'content_gpu', - 'content_plugin', - 'content_ppapi_plugin', - 'content_renderer', - 'content_utility', - ], - }], - ], }, { # GN version: //content/app:browser @@ -179,6 +171,7 @@ ], 'dependencies': [ 'content_common', + 'content_resources', ], 'export_dependent_settings': [ 'content_common', @@ -195,11 +188,6 @@ 'content_utility', ], }], - ['OS != "ios"', { - 'dependencies': [ - 'content_resources', - ], - }], ], }, { @@ -207,116 +195,106 @@ 'target_name': 'content_common', 'type': 'static_library', 'variables': { 'enable_wexit_time_destructors': 1, }, + 'dependencies': [ + 'content_resources', + ], 'includes': [ 'content_common.gypi', ], - 'conditions': [ - ['OS != "ios"', { - 'dependencies': [ - 'content_resources', - ], - }], - ], # Disable c4267 warnings until we fix size_t to int truncations. 'msvs_disabled_warnings': [ 4267, ], }, - ], - 'conditions': [ - ['OS != "ios"', { - 'targets': [ - { + { # GN version: //content/child and //content/public/child - 'target_name': 'content_child', - 'type': 'static_library', - 'variables': { 'enable_wexit_time_destructors': 1, }, - 'includes': [ - 'content_child.gypi', - ], - 'dependencies': [ - 'content_resources', - ], - # Disable c4267 warnings until we fix size_t to int truncations. - 'msvs_disabled_warnings': [ 4267, ], - }, - { - # GN version: //content/gpu - 'target_name': 'content_gpu', - 'type': 'static_library', - 'variables': { 'enable_wexit_time_destructors': 1, }, - 'includes': [ - 'content_gpu.gypi', - ], - 'dependencies': [ - 'content_child', - 'content_common', - ], - }, - { - # GN version: //content/plugin and //content/public/plugin - 'target_name': 'content_plugin', - 'type': 'static_library', - 'variables': { 'enable_wexit_time_destructors': 1, }, - 'includes': [ - 'content_plugin.gypi', - ], - 'dependencies': [ - 'content_child', - 'content_common', - ], - }, - { - # GN version: //content/ppapi_plugin - 'target_name': 'content_ppapi_plugin', - 'type': 'static_library', - 'variables': { 'enable_wexit_time_destructors': 1, }, - 'includes': [ - 'content_ppapi_plugin.gypi', - ], - # Disable c4267 warnings until we fix size_t to int truncations. - 'msvs_disabled_warnings': [ 4267, ], - }, - { - # GN version: //content/renderer and //content/public/renderer - 'target_name': 'content_renderer', - 'type': 'static_library', - 'variables': { 'enable_wexit_time_destructors': 1, }, - 'includes': [ - 'content_renderer.gypi', - ], - 'dependencies': [ - '../third_party/webrtc/modules/modules.gyp:webrtc_h264', - 'content_child', - 'content_common', - 'content_resources', - 'renderer_features', - ], - 'export_dependent_settings': [ - 'content_common', - ], - 'conditions': [ - ['chromium_enable_vtune_jit_for_v8==1', { - 'dependencies': [ - '../v8/src/third_party/vtune/v8vtune.gyp:v8_vtune', - ], - }], - ], - }, - { - # GN version: //content/utility and //content/public/utility - 'target_name': 'content_utility', - 'type': 'static_library', - 'variables': { 'enable_wexit_time_destructors': 1, }, - 'includes': [ - 'content_utility.gypi', - ], - 'dependencies': [ - 'content_child', - 'content_common', - 'content_common_mojo_bindings.gyp:content_common_mojo_bindings', - ], - }, + 'target_name': 'content_child', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'includes': [ + 'content_child.gypi', ], - }], + 'dependencies': [ + 'content_resources', + ], + # Disable c4267 warnings until we fix size_t to int truncations. + 'msvs_disabled_warnings': [ 4267, ], + }, + { + # GN version: //content/gpu + 'target_name': 'content_gpu', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'includes': [ + 'content_gpu.gypi', + ], + 'dependencies': [ + 'content_child', + 'content_common', + ], + }, + { + # GN version: //content/plugin and //content/public/plugin + 'target_name': 'content_plugin', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'includes': [ + 'content_plugin.gypi', + ], + 'dependencies': [ + 'content_child', + 'content_common', + ], + }, + { + # GN version: //content/ppapi_plugin + 'target_name': 'content_ppapi_plugin', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'includes': [ + 'content_ppapi_plugin.gypi', + ], + # Disable c4267 warnings until we fix size_t to int truncations. + 'msvs_disabled_warnings': [ 4267, ], + }, + { + # GN version: //content/renderer and //content/public/renderer + 'target_name': 'content_renderer', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'includes': [ + 'content_renderer.gypi', + ], + 'dependencies': [ + '../third_party/webrtc/modules/modules.gyp:webrtc_h264', + 'content_child', + 'content_common', + 'content_resources', + 'renderer_features', + ], + 'export_dependent_settings': [ + 'content_common', + ], + 'conditions': [ + ['chromium_enable_vtune_jit_for_v8==1', { + 'dependencies': [ + '../v8/src/third_party/vtune/v8vtune.gyp:v8_vtune', + ], + }], + ], + }, + { + # GN version: //content/utility and //content/public/utility + 'target_name': 'content_utility', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'includes': [ + 'content_utility.gypi', + ], + 'dependencies': [ + 'content_child', + 'content_common', + 'content_common_mojo_bindings.gyp:content_common_mojo_bindings', + ], + }, ], }, { # component != static_library
diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 80620c1..95ba8499 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi
@@ -5,26 +5,43 @@ { 'dependencies': [ '../base/base.gyp:base_static', + '../cc/cc.gyp:cc', + '../cc/cc.gyp:cc_surfaces', + '../components/filesystem/filesystem.gyp:filesystem_lib', + '../components/leveldb/leveldb.gyp:leveldb_lib', + '../components/mime_util/mime_util.gyp:mime_util', + '../components/scheduler/scheduler.gyp:scheduler_common', '../components/url_formatter/url_formatter.gyp:url_formatter', '../crypto/crypto.gyp:crypto', '../device/battery/battery.gyp:device_battery', '../device/battery/battery.gyp:device_battery_mojo_bindings', + '../device/bluetooth/bluetooth.gyp:device_bluetooth', + '../device/usb/usb.gyp:device_usb', '../device/vibration/vibration.gyp:device_vibration', '../device/vibration/vibration.gyp:device_vibration_mojo_bindings', + '../gin/gin.gyp:gin', '../google_apis/google_apis.gyp:google_apis', '../mojo/mojo_base.gyp:mojo_application_base', '../mojo/mojo_base.gyp:mojo_geometry_lib', '../mojo/mojo_base.gyp:mojo_url_type_converters', '../mojo/mojo_public.gyp:mojo_cpp_bindings', + '../mojo/mojo_public.gyp:mojo_js_bindings', '../mojo/mojo_services.gyp:network_service_bindings_lib', '../mojo/mojo_services.gyp:updater_bindings_lib', '../mojo/mojo_shell.gyp:mojo_shell_lib', + '../net/net.gyp:http_server', '../net/net.gyp:net', '../net/net.gyp:net_extras', + '../sandbox/sandbox.gyp:sandbox', '../skia/skia.gyp:skia', '../skia/skia.gyp:skia_mojo', '../sql/sql.gyp:sql', + '../storage/storage_browser.gyp:storage', + '../storage/storage_common.gyp:storage_common', + '../third_party/angle/src/angle.gyp:commit_id', '../third_party/kasko/kasko.gyp:kasko_features', + '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase', + '../third_party/libyuv/libyuv.gyp:libyuv', '../third_party/re2/re2.gyp:re2', '../third_party/zlib/google/zip.gyp:zip', '../third_party/zlib/zlib.gyp:zlib', @@ -33,6 +50,7 @@ '../ui/accessibility/accessibility.gyp:ax_gen', '../ui/base/ui_base.gyp:ui_base', '../ui/base/ime/ui_base_ime.gyp:ui_base_ime', + '../ui/events/blink/events_blink.gyp:events_blink', '../ui/events/events.gyp:events', '../ui/events/events.gyp:events_base', '../ui/events/events.gyp:gesture_detection', @@ -40,14 +58,21 @@ '../ui/gfx/gfx.gyp:gfx_geometry', '../ui/resources/ui_resources.gyp:ui_resources', '../ui/snapshot/snapshot.gyp:snapshot', + '../ui/surface/surface.gyp:surface', + '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection', + 'app/resources/content_resources.gyp:content_resources', + 'app/strings/content_strings.gyp:content_strings', 'browser/background_sync/background_sync_proto.gyp:background_sync_proto', 'browser/cache_storage/cache_storage_proto.gyp:cache_storage_proto', + 'browser/devtools/devtools_resources.gyp:devtools_resources', + 'browser/devtools/devtools.gyp:devtools_protocol_handler', 'browser/notifications/notification_proto.gyp:notification_proto', 'browser/service_worker/service_worker_proto.gyp:service_worker_proto', 'browser/speech/proto/speech_proto.gyp:speech_proto', 'content_common_mojo_bindings.gyp:content_common_mojo_bindings', ], 'export_dependent_settings': [ + '../mojo/mojo_public.gyp:mojo_cpp_bindings', '../ui/accessibility/accessibility.gyp:ax_gen', # The public content API headers directly include Blink API headers, so we # have to export the blink header settings so that relative paths in these @@ -156,6 +181,8 @@ 'public/browser/global_request_id.h', 'public/browser/gpu_data_manager.h', 'public/browser/gpu_data_manager_observer.h', + 'public/browser/gpu_service_registry.cc', + 'public/browser/gpu_service_registry.h', 'public/browser/histogram_fetcher.h', 'public/browser/host_zoom_map.h', 'public/browser/indexed_db_context.h', @@ -504,6 +531,11 @@ 'browser/cocoa/system_hotkey_helper_mac.mm', 'browser/cocoa/system_hotkey_map.h', 'browser/cocoa/system_hotkey_map.mm', + # NOTE: These files are here instead of in compositor_browser_sources + # because the latter is not built on Android, whereas these files are + # needed on all platforms. + 'browser/compositor/surface_utils.cc', + 'browser/compositor/surface_utils.h', 'browser/device_monitor_mac.h', 'browser/device_monitor_mac.mm', 'browser/device_sensors/ambient_light_mac.cc', @@ -1848,43 +1880,6 @@ 'browser/gamepad/gamepad_platform_data_fetcher.cc', ] }], - ['OS=="ios"', { - 'sources/': [ - # iOS only needs a small portion of content; exclude all the - # implementation, and re-include what is used. - ['exclude', '\\.(cc|mm)$'], - ['include', '^public/browser/navigation_details\\.cc$'], - ['include', '^public/browser/page_navigator\\.cc$'], - ['include', '^browser/browser_context\\.cc$'], - ], - }, { # OS!="ios" - 'dependencies': [ - 'app/resources/content_resources.gyp:content_resources', - 'app/strings/content_strings.gyp:content_strings', - 'browser/devtools/devtools_resources.gyp:devtools_resources', - 'browser/devtools/devtools.gyp:devtools_protocol_handler', - '../cc/cc.gyp:cc', - '../cc/cc.gyp:cc_surfaces', - '../components/mime_util/mime_util.gyp:mime_util', - '../components/scheduler/scheduler.gyp:scheduler_common', - '../device/bluetooth/bluetooth.gyp:device_bluetooth', - '../device/usb/usb.gyp:device_usb', - '../gin/gin.gyp:gin', - '../mojo/mojo_public.gyp:mojo_cpp_bindings', - '../mojo/mojo_public.gyp:mojo_js_bindings', - '../net/net.gyp:http_server', - '../storage/storage_browser.gyp:storage', - '../storage/storage_common.gyp:storage_common', - '../third_party/angle/src/angle.gyp:commit_id', - '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase', - '../ui/events/blink/events_blink.gyp:events_blink', - '../ui/surface/surface.gyp:surface', - '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection', - ], - 'export_dependent_settings': [ - '../mojo/mojo_public.gyp:mojo_cpp_bindings', - ], - }], ['debug_devtools==1', { 'defines': [ 'DEBUG_DEVTOOLS=1', @@ -1895,17 +1890,12 @@ '../printing/printing.gyp:printing', ], }], - ['OS!="ios" and chrome_multiple_dll!=1', { + ['chrome_multiple_dll!=1', { 'dependencies': [ '../third_party/WebKit/public/blink.gyp:blink', ], }], - ['OS!="ios"', { - 'dependencies': [ - '../sandbox/sandbox.gyp:sandbox', - ], - }], - ['OS!="android" and OS!="ios"', { + ['OS!="android"', { 'dependencies': [ 'browser/tracing/tracing_resources.gyp:tracing_resources', '../ui/compositor/compositor.gyp:compositor', @@ -1925,17 +1915,6 @@ 'browser/tracing/power_tracing_agent.h', ], }], - ['OS!="ios"', { - 'sources': [ - 'browser/compositor/surface_utils.cc', - 'browser/compositor/surface_utils.h', - ] - }], - ['OS!="ios"', { - 'dependencies': [ - '../third_party/libyuv/libyuv.gyp:libyuv', - ], - }], ['enable_webrtc==1', { 'dependencies': [ '../jingle/jingle.gyp:jingle_glue',
diff --git a/content/content_child.gypi b/content/content_child.gypi index dc179bb..d1b0a8f 100644 --- a/content/content_child.gypi +++ b/content/content_child.gypi
@@ -6,17 +6,26 @@ 'dependencies': [ '../base/base.gyp:base', '../components/mime_util/mime_util.gyp:mime_util', + '../components/scheduler/scheduler.gyp:scheduler', '../components/tracing.gyp:tracing', '../components/webcrypto/webcrypto.gyp:webcrypto', + '../ipc/ipc.gyp:ipc', '../mojo/mojo_base.gyp:mojo_environment_chromium', '../mojo/mojo_base.gyp:mojo_common_lib', '../mojo/mojo_base.gyp:mojo_message_pump_lib', '../skia/skia.gyp:skia', + '../storage/storage_common.gyp:storage_common', + '../third_party/WebKit/public/blink.gyp:blink', + '../third_party/WebKit/public/blink_resources.gyp:blink_image_resources', + '../third_party/WebKit/public/blink_resources.gyp:blink_resources', + '../third_party/npapi/npapi.gyp:npapi', '../ui/base/ui_base.gyp:ui_base', '../ui/events/events.gyp:gestures_blink', '../ui/gfx/gfx.gyp:gfx', '../ui/gfx/gfx.gyp:gfx_geometry', '../url/url.gyp:url_lib', + 'app/resources/content_resources.gyp:content_resources', + 'app/strings/content_strings.gyp:content_strings', 'content_common_mojo_bindings.gyp:content_common_mojo_bindings', ], 'include_dirs': [ @@ -315,24 +324,6 @@ ['exclude', '^child/npapi/webplugin_'], ], }], - ['OS=="ios"', { - 'sources/': [ - # iOS only needs a small portion of content; exclude all the - # implementation, and re-include what is used. - ['exclude', '\\.(cc|mm)$'], - ], - }, { # OS!="ios" - 'dependencies': [ - 'app/resources/content_resources.gyp:content_resources', - 'app/strings/content_strings.gyp:content_strings', - '../components/scheduler/scheduler.gyp:scheduler', - '../storage/storage_common.gyp:storage_common', - '../third_party/WebKit/public/blink.gyp:blink', - '../third_party/WebKit/public/blink_resources.gyp:blink_image_resources', - '../third_party/WebKit/public/blink_resources.gyp:blink_resources', - '../third_party/npapi/npapi.gyp:npapi', - ], - }], ['use_aura==1', { 'sources!': [ 'child/npapi/webplugin_delegate_impl_mac.mm',
diff --git a/content/content_common.gypi b/content/content_common.gypi index 482e46e9..703edad 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi
@@ -5,11 +5,34 @@ { 'dependencies': [ '../base/base.gyp:base', + '../cc/cc.gyp:cc', '../components/tracing.gyp:tracing', + '../device/bluetooth/bluetooth.gyp:device_bluetooth', + '../gpu/blink/gpu_blink.gyp:gpu_blink', '../gpu/command_buffer/command_buffer.gyp:gles2_utils', + '../gpu/gpu.gyp:command_buffer_service', + '../gpu/gpu.gyp:gles2_c_lib', + '../gpu/gpu.gyp:gles2_implementation', + # TODO: the dependency on gl_in_process_context should be decoupled from + # content and moved to android_webview. See crbug.com/365797. + '../gpu/gpu.gyp:gl_in_process_context', + '../gpu/gpu.gyp:gpu_ipc', + '../gpu/skia_bindings/skia_bindings.gyp:gpu_skia_bindings', + '../ipc/ipc.gyp:ipc', + '../ipc/mojo/ipc_mojo.gyp:ipc_mojo', + '../media/media.gyp:media', + '../media/media.gyp:shared_memory_support', + '../media/midi/midi.gyp:midi', + '../mojo/mojo_base.gyp:mojo_application_bindings', + '../mojo/mojo_base.gyp:mojo_environment_chromium', + '../mojo/mojo_edk.gyp:mojo_system_impl', + '../mojo/mojo_public.gyp:mojo_cpp_bindings', '../net/net.gyp:net', '../skia/skia.gyp:skia', + '../storage/storage_common.gyp:storage_common', + '../third_party/WebKit/public/blink.gyp:blink', '../third_party/WebKit/public/blink_headers.gyp:blink_headers', + '../third_party/boringssl/boringssl.gyp:boringssl', '../third_party/icu/icu.gyp:icuuc', '../ui/accessibility/accessibility.gyp:accessibility', '../ui/accessibility/accessibility.gyp:ax_gen', @@ -19,14 +42,42 @@ '../ui/gfx/gfx.gyp:gfx', '../ui/gfx/gfx.gyp:gfx_geometry', '../ui/gfx/ipc/gfx_ipc.gyp:gfx_ipc', + '../ui/gl/gl.gyp:gl', '../ui/shell_dialogs/shell_dialogs.gyp:shell_dialogs', '../url/url.gyp:url_lib', + 'content_common_mojo_bindings.gyp:content_common_mojo_bindings', ], 'include_dirs': [ '..', ], + 'actions': [ + { + 'action_name': 'generate_webkit_version', + 'inputs': [ + '<(script)', + '<(lastchange)', + '<(template)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/build/util/webkit_version.h', + ], + 'action': ['python', + '<(script)', + '-f', '<(lastchange)', + '<(template)', + '<@(_outputs)', + ], + 'variables': { + 'script': '<(DEPTH)/build/util/version.py', + 'lastchange': '<(DEPTH)/build/util/LASTCHANGE.blink', + 'template': '<(DEPTH)/build/util/webkit_version.h.in', + }, + }, + ], 'export_dependent_settings': [ '../base/base.gyp:base', + '../mojo/mojo_base.gyp:mojo_application_bindings', + '../mojo/mojo_public.gyp:mojo_cpp_bindings', # The public content API headers directly include Blink API headers, so we # have to export the blink header settings so that relative paths in these # headers resolve correctly. @@ -391,6 +442,7 @@ 'common/input/input_event.h', 'common/input/input_event_ack.cc', 'common/input/input_event_ack.h', + 'common/input/input_event_dispatch_type.h', 'common/input/input_event_stream_validator.cc', 'common/input/input_event_stream_validator.h', 'common/input/input_event_utils.cc', @@ -415,6 +467,7 @@ 'common/input/synthetic_web_input_event_builders.h', 'common/input/touch_event_stream_validator.cc', 'common/input/touch_event_stream_validator.h', + 'common/input/web_input_event_queue.h', 'common/input/web_input_event_traits.cc', 'common/input/web_input_event_traits.h', 'common/input/web_touch_event_traits.cc', @@ -449,18 +502,20 @@ 'common/media/webrtc_identity_messages.h', 'common/memory_messages.h', 'common/message_port_messages.h', - 'common/message_router.cc', - 'common/message_router.h', 'common/mime_registry_messages.h', 'common/mojo/channel_init.cc', 'common/mojo/channel_init.h', 'common/mojo/mojo_messages.h', 'common/mojo/service_registry_impl.cc', 'common/mojo/service_registry_impl.h', + 'common/mojo/static_application_loader.cc', + 'common/mojo/static_application_loader.h', 'common/navigation_gesture.h', 'common/navigation_params.cc', 'common/navigation_params.h', 'common/net/url_fetcher.cc', + 'common/net/url_request_service_worker_data.cc', + 'common/net/url_request_service_worker_data.h', 'common/net/url_request_user_data.cc', 'common/net/url_request_user_data.h', 'common/notification_constants.h', @@ -587,94 +642,6 @@ }], ], 'conditions': [ - ['OS=="ios"', { - # iOS has different user-agent construction utilities, since the - # version strings is not derived from webkit_version, and follows - # a different format. - 'sources!': [ - 'common/user_agent.cc', - ], - 'sources/': [ - # iOS only needs a small portion of content; exclude all the - # implementation, and re-include what is used. - ['exclude', '\\.(cc|mm)$'], - ['include', '_ios\\.(cc|mm)$'], - ['include', '^public/common/content_client\\.cc$'], - ['include', '^public/common/content_constants\\.cc$'], - ['include', '^public/common/content_switches\\.cc$'], - ['include', '^public/common/frame_navigate_params\\.cc$'], - ['include', '^public/common/media_stream_request\\.cc$'], - ['include', '^public/common/page_state\\.cc$'], - ['include', '^public/common/password_form\\.cc$'], - ['include', '^public/common/signed_certificate_timestamp_id_and_status\\.cc$'], - ['include', '^public/common/speech_recognition_result\\.cc$'], - ['include', '^public/common/ssl_status\\.cc$'], - ['include', '^public/common/url_constants\\.cc$'], - ['include', '^common/content_paths\\.cc$'], - ['include', '^common/media/media_stream_options\\.cc$'], - ['include', '^common/net/url_fetcher\\.cc$'], - ['include', '^common/net/url_request_user_data\\.cc$'], - ['include', '^common/page_state_serialization\\.cc$'], - ['include', '^common/savable_url_schemes\\.cc$'], - ['include', '^common/url_schemes\\.cc$'], - ], - }, { # OS!="ios" - 'dependencies': [ - '../cc/cc.gyp:cc', - '../device/bluetooth/bluetooth.gyp:device_bluetooth', - '../gpu/blink/gpu_blink.gyp:gpu_blink', - '../gpu/gpu.gyp:command_buffer_service', - '../gpu/gpu.gyp:gles2_c_lib', - '../gpu/gpu.gyp:gles2_implementation', - # TODO: the dependency on gl_in_process_context should be decoupled from - # content and moved to android_webview. See crbug.com/365797. - '../gpu/gpu.gyp:gl_in_process_context', - '../gpu/gpu.gyp:gpu_ipc', - '../gpu/skia_bindings/skia_bindings.gyp:gpu_skia_bindings', - '../ipc/ipc.gyp:ipc', - '../ipc/mojo/ipc_mojo.gyp:ipc_mojo', - '../media/media.gyp:media', - '../media/media.gyp:shared_memory_support', - '../media/midi/midi.gyp:midi', - '../mojo/mojo_base.gyp:mojo_application_bindings', - '../mojo/mojo_base.gyp:mojo_environment_chromium', - '../mojo/mojo_edk.gyp:mojo_system_impl', - '../mojo/mojo_public.gyp:mojo_cpp_bindings', - '../storage/storage_common.gyp:storage_common', - '../third_party/WebKit/public/blink.gyp:blink', - '../third_party/boringssl/boringssl.gyp:boringssl', - '../ui/gl/gl.gyp:gl', - 'content_common_mojo_bindings.gyp:content_common_mojo_bindings', - ], - 'export_dependent_settings' : [ - '../mojo/mojo_base.gyp:mojo_application_bindings', - '../mojo/mojo_public.gyp:mojo_cpp_bindings', - ], - 'actions': [ - { - 'action_name': 'generate_webkit_version', - 'inputs': [ - '<(script)', - '<(lastchange)', - '<(template)', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/build/util/webkit_version.h', - ], - 'action': ['python', - '<(script)', - '-f', '<(lastchange)', - '<(template)', - '<@(_outputs)', - ], - 'variables': { - 'script': '<(DEPTH)/build/util/version.py', - 'lastchange': '<(DEPTH)/build/util/LASTCHANGE.blink', - 'template': '<(DEPTH)/build/util/webkit_version.h.in', - }, - }, - ], - }], ['OS=="mac"', { 'dependencies': [ '../media/media.gyp:media',
diff --git a/content/content_gpu.gypi b/content/content_gpu.gypi index 1a6b7e6..f9dd636 100644 --- a/content/content_gpu.gypi +++ b/content/content_gpu.gypi
@@ -21,6 +21,7 @@ 'gpu/gpu_watchdog_thread.h', 'gpu/in_process_gpu_thread.cc', 'gpu/in_process_gpu_thread.h', + 'public/gpu/content_gpu_client.h', ], 'include_dirs': [ '..',
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 0592cf2..b017e22 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi
@@ -19,6 +19,7 @@ '../device/vibration/vibration.gyp:device_vibration_mojo_bindings', '../gin/gin.gyp:gin', '../gpu/gpu.gyp:gpu', + '../ipc/ipc.gyp:ipc', '../jingle/jingle.gyp:jingle_glue', '../media/blink/media_blink.gyp:media_blink', '../media/media.gyp:media', @@ -250,6 +251,8 @@ 'renderer/input/input_handler_wrapper.h', 'renderer/input/main_thread_input_event_filter.cc', 'renderer/input/main_thread_input_event_filter.h', + 'renderer/input/non_blocking_event_queue.cc', + 'renderer/input/non_blocking_event_queue.h', 'renderer/input/render_widget_input_handler.cc', 'renderer/input/render_widget_input_handler.h', 'renderer/input/render_widget_input_handler_delegate.h',
diff --git a/content/content_shell.gypi b/content/content_shell.gypi index 70dc03f..43b456d 100644 --- a/content/content_shell.gypi +++ b/content/content_shell.gypi
@@ -543,7 +543,6 @@ 'sources': [ 'shell/tools/plugin/PluginObject.cpp', 'shell/tools/plugin/PluginObject.h', - 'shell/tools/plugin/PluginObjectMac.mm', 'shell/tools/plugin/PluginTest.cpp', 'shell/tools/plugin/PluginTest.h', 'shell/tools/plugin/TestObject.cpp',
diff --git a/content/content_shell_and_tests.gyp b/content/content_shell_and_tests.gyp index b3c3c4c2..d331767a 100644 --- a/content/content_shell_and_tests.gyp +++ b/content/content_shell_and_tests.gyp
@@ -19,14 +19,8 @@ ], }, 'includes': [ + '../build/win_precompile.gypi', + 'content_shell.gypi', 'content_tests.gypi', ], - 'conditions': [ - ['OS != "ios"', { - 'includes': [ - '../build/win_precompile.gypi', - 'content_shell.gypi', - ], - }], - ], }
diff --git a/content/content_tests.gypi b/content/content_tests.gypi index a66a4167..42e66383 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi
@@ -243,6 +243,7 @@ 'browser/mojo_shell_browsertest.cc', 'browser/net_info_browsertest.cc', 'browser/renderer_host/input/composited_scrolling_browsertest.cc', + 'browser/renderer_host/input/non_blocking_event_browsertest.cc', 'browser/renderer_host/input/touch_action_browsertest.cc', 'browser/renderer_host/input/touch_input_browsertest.cc', 'browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc', @@ -662,8 +663,9 @@ 'common/dom_storage/dom_storage_map_unittest.cc', 'common/dwrite_font_platform_win_unittest.cc', 'common/fileapi/file_system_util_unittest.cc', - 'common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc', 'common/gpu/ca_layer_tree_unittest_mac.mm', + 'common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc', + 'common/gpu/client/gpu_memory_buffer_impl_test_template.h', 'common/gpu/gpu_channel_manager_unittest.cc', 'common/gpu/gpu_channel_test_common.cc', 'common/gpu/gpu_channel_test_common.h', @@ -708,6 +710,7 @@ 'renderer/gpu/render_widget_compositor_unittest.cc', 'renderer/ico_image_decoder_unittest.cc', 'renderer/input/input_event_filter_unittest.cc', + 'renderer/input/non_blocking_event_queue_unittest.cc', 'renderer/manifest/manifest_parser_unittest.cc', 'renderer/media/android/media_info_loader_unittest.cc', 'renderer/media/audio_message_filter_unittest.cc', @@ -788,6 +791,7 @@ 'renderer/media/mock_media_stream_video_sink.h', 'renderer/media/mock_media_stream_video_source.cc', 'renderer/media/mock_media_stream_video_source.h', + 'renderer/media/peer_connection_tracker_unittest.cc', 'renderer/media/rtc_data_channel_handler_unittest.cc', 'renderer/media/rtc_peer_connection_handler_unittest.cc', 'renderer/media/rtc_video_decoder_unittest.cc', @@ -1662,6 +1666,7 @@ 'sources': [ 'common/gpu/client/gl_helper_unittest.cc', 'common/gpu/client/gpu_in_process_context_tests.cc', + 'test/run_all_gl_tests.cc', ], 'conditions': [ ['OS=="android"', { @@ -1698,6 +1703,7 @@ ], 'sources': [ 'common/gpu/client/gl_helper_benchmark.cc', + 'test/run_gl_benchmark.cc', ], }, ], @@ -2188,6 +2194,7 @@ 'public/android/junit/', ], 'test_type': 'junit', + 'wrapper_script_name': 'helper/<(_target_name)', }, 'includes': [ '../build/android/test_runner.gypi',
diff --git a/content/gpu/BUILD.gn b/content/gpu/BUILD.gn index 91600f1..3bc1845 100644 --- a/content/gpu/BUILD.gn +++ b/content/gpu/BUILD.gn
@@ -53,8 +53,8 @@ "//content/common:mojo_bindings", ] - if (enable_mojo_media == "gpu") { - deps += [ "//media/mojo/services:application" ] + if (mojo_media_host == "gpu") { + deps += [ "//media/mojo/services:application_factory" ] } if (is_win) {
diff --git a/content/gpu/DEPS b/content/gpu/DEPS index f9f4c04..0a68a119 100644 --- a/content/gpu/DEPS +++ b/content/gpu/DEPS
@@ -1,6 +1,7 @@ include_rules = [ "+components/tracing", "+content/child", + "+content/public/gpu", "+libEGL", "+libGLESv2", "+media/mojo/services",
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc index 2552d82b..6ea7e2e 100644 --- a/content/gpu/gpu_child_thread.cc +++ b/content/gpu/gpu_child_thread.cc
@@ -20,6 +20,7 @@ #include "content/gpu/gpu_watchdog_thread.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" +#include "content/public/gpu/content_gpu_client.h" #include "gpu/config/gpu_info_collector.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_sync_message_filter.h" @@ -205,6 +206,9 @@ // will be destroyed before GpuChildThread is destructed. service_registry()->AddService(base::Bind( &GpuChildThread::BindProcessControlRequest, base::Unretained(this))); + + if (GetContentClient()->gpu()) // NULL in tests. + GetContentClient()->gpu()->RegisterMojoServices(service_registry()); } bool GpuChildThread::Send(IPC::Message* msg) {
diff --git a/content/gpu/gpu_process_control_impl.cc b/content/gpu/gpu_process_control_impl.cc index cb3175b8..615c63b 100644 --- a/content/gpu/gpu_process_control_impl.cc +++ b/content/gpu/gpu_process_control_impl.cc
@@ -7,8 +7,8 @@ #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) #include "base/bind.h" #include "base/bind_helpers.h" -#include "media/mojo/services/mojo_media_application.h" -#include "mojo/shell/static_application_loader.h" +#include "content/common/mojo/static_application_loader.h" +#include "media/mojo/services/mojo_media_application_factory.h" #endif namespace content { @@ -21,8 +21,8 @@ URLToLoaderMap* url_to_loader_map) { #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) (*url_to_loader_map)[GURL("mojo:media")] = - new mojo::shell::StaticApplicationLoader( - base::Bind(&media::MojoMediaApplication::CreateApp), + new StaticApplicationLoader( + base::Bind(&media::CreateMojoMediaApplication), base::Bind(&base::DoNothing)); #endif }
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index e726c463..5f0ca17 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -39,6 +39,7 @@ "//device/vibration:mojo_bindings_java", "//device/vibration/android:vibration_manager_android", "//media/base/android:media_java", + "//media/capture/video/android:capture_java", "//media/midi:midi_java", "//mojo/android:system_java", "//mojo/public/java:bindings", @@ -177,6 +178,7 @@ "//device/vibration:mojo_bindings_java", "//device/vibration/android:vibration_manager_android", "//media/base/android:media_java", + "//media/capture/video/android:capture_java", "//mojo/android:system_java", "//mojo/public/interfaces/bindings/tests:test_interfaces_java", "//mojo/public/java:bindings",
diff --git a/content/public/app/DEPS b/content/public/app/DEPS index 255cb72..47482a5 100644 --- a/content/public/app/DEPS +++ b/content/public/app/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+content/public/browser/content_browser_client.h", + "+content/public/gpu/content_gpu_client.h", "+content/public/plugin/content_plugin_client.h", "+content/public/renderer/content_renderer_client.h", "+content/public/utility/content_utility_client.h",
diff --git a/content/public/app/content_main_delegate.cc b/content/public/app/content_main_delegate.cc index 3c979be..16fd8044 100644 --- a/content/public/app/content_main_delegate.cc +++ b/content/public/app/content_main_delegate.cc
@@ -11,6 +11,7 @@ #endif #if !defined(OS_IOS) +#include "content/public/gpu/content_gpu_client.h" #include "content/public/plugin/content_plugin_client.h" #include "content/public/renderer/content_renderer_client.h" #include "content/public/utility/content_utility_client.h" @@ -64,6 +65,14 @@ #endif } +ContentGpuClient* ContentMainDelegate::CreateContentGpuClient() { +#if defined(OS_IOS) || defined(CHROME_MULTIPLE_DLL_BROWSER) + return NULL; +#else + return new ContentGpuClient(); +#endif +} + ContentPluginClient* ContentMainDelegate::CreateContentPluginClient() { #if defined(OS_IOS) || defined(CHROME_MULTIPLE_DLL_BROWSER) return NULL;
diff --git a/content/public/app/content_main_delegate.h b/content/public/app/content_main_delegate.h index 6bd725d..7fecaea 100644 --- a/content/public/app/content_main_delegate.h +++ b/content/public/app/content_main_delegate.h
@@ -16,6 +16,7 @@ namespace content { class ContentBrowserClient; +class ContentGpuClient; class ContentPluginClient; class ContentRendererClient; class ContentUtilityClient; @@ -88,6 +89,7 @@ // content. If an embedder wants the default (empty) implementation, don't // override this. virtual ContentBrowserClient* CreateContentBrowserClient(); + virtual ContentGpuClient* CreateContentGpuClient(); virtual ContentPluginClient* CreateContentPluginClient(); virtual ContentRendererClient* CreateContentRendererClient(); virtual ContentUtilityClient* CreateContentUtilityClient();
diff --git a/content/public/browser/bluetooth_chooser.h b/content/public/browser/bluetooth_chooser.h index b9f44a48..aec3c2f 100644 --- a/content/public/browser/bluetooth_chooser.h +++ b/content/public/browser/bluetooth_chooser.h
@@ -28,8 +28,6 @@ RESCAN, // Show overview page for Bluetooth. SHOW_OVERVIEW_HELP, - // Show help page explaining what Bluetooth pairing means. - SHOW_PAIRING_HELP, // Show help page explaining why scanning failed because Bluetooth is off. SHOW_ADAPTER_OFF_HELP, // Show help page explaining why Chromium needs the Location permission to @@ -46,9 +44,9 @@ // // The EventHandler won't be called after the chooser object is destroyed. // - // After the EventHandler is called with Event::CANCELLED or Event::SELECTED, - // it won't be called again, and users must not call any more BluetoothChooser - // methods. + // After the EventHandler is called with Event::CANCELLED, Event::SELECTED, + // Event::DENIED_PERMISSION or Event::SHOW_*, it won't be called again, and + // users must not call any more BluetoothChooser methods. typedef base::Callback<void(Event, const std::string& opt_device_id)> EventHandler;
diff --git a/content/public/browser/download_interrupt_reason_values.h b/content/public/browser/download_interrupt_reason_values.h index 95c4f4a..485b09c 100644 --- a/content/public/browser/download_interrupt_reason_values.h +++ b/content/public/browser/download_interrupt_reason_values.h
@@ -106,6 +106,10 @@ // Server access forbidden. INTERRUPT_REASON(SERVER_FORBIDDEN, 36) +// Unexpected server response. This might indicate that the responding server +// may not be the intended server. +INTERRUPT_REASON(SERVER_UNREACHABLE, 37) + // User input. // The user canceled the download.
diff --git a/content/public/browser/download_manager.h b/content/public/browser/download_manager.h index ca367eb..16d161b 100644 --- a/content/public/browser/download_manager.h +++ b/content/public/browser/download_manager.h
@@ -126,11 +126,6 @@ virtual int RemoveDownloadsBetween(base::Time remove_begin, base::Time remove_end) = 0; - // Remove downloads will delete all downloads that have a timestamp that is - // the same or more recent than |remove_begin|. The number of downloads - // deleted is returned back to the caller. - virtual int RemoveDownloads(base::Time remove_begin) = 0; - // Remove all downloads will delete all downloads. The number of downloads // deleted is returned back to the caller. virtual int RemoveAllDownloads() = 0;
diff --git a/content/public/browser/download_url_parameters.h b/content/public/browser/download_url_parameters.h index a2b9b00d..5734e91c 100644 --- a/content/public/browser/download_url_parameters.h +++ b/content/public/browser/download_url_parameters.h
@@ -41,65 +41,126 @@ class CONTENT_EXPORT DownloadUrlParameters { public: - // If there is an error, then |item| will be nullptr. + // An OnStartedCallback is invoked when a response is available for the + // download request. For new downloads, this callback is invoked after the + // OnDownloadCreated notification is issued by the DownloadManager. If the + // download fails, then the DownloadInterruptReason parameter will indicate + // the failure. + // + // DownloadItem* may be nullptr if no DownloadItem was created. DownloadItems + // are not created when a resource throttle or a resource handler blocks the + // download request. I.e. the download triggered a warning of some sort and + // the user chose to not to proceed with the download as a result. typedef base::Callback<void(DownloadItem*, DownloadInterruptReason)> OnStartedCallback; typedef std::pair<std::string, std::string> RequestHeadersNameValuePair; typedef std::vector<RequestHeadersNameValuePair> RequestHeadersType; + // Construct DownloadUrlParameters for downloading the resource at |url| and + // associating the download with |web_contents|. static scoped_ptr<DownloadUrlParameters> FromWebContents( WebContents* web_contents, const GURL& url); - DownloadUrlParameters( - const GURL& url, - int render_process_host_id, - int render_view_host_routing_id, - int render_frame_host_routing_id, - content::ResourceContext* resource_context); + // Construct DownloadUrlParameters for downloading the resource at |url| and + // associating the download with the WebContents identified by + // |render_process_host_id| and |render_view_host_routing_id|. + // + // If the download is not associated with a WebContents, then set the IDs to + // -1. + // NOTE: This is not safe and should only be done in a limited set of cases + // where the download URL has been previously vetted. A download that's + // initiated without associating it with a WebContents don't receive the same + // security checks as a request that's associated with one. Hence, downloads + // that are not associated with a WebContents should only be made for URLs + // that are either trusted or URLs that have previously been successfully + // issued using a non-privileged WebContents. + DownloadUrlParameters(const GURL& url, + int render_process_host_id, + int render_view_host_routing_id, + int render_frame_host_routing_id, + ResourceContext* resource_context); ~DownloadUrlParameters(); + // Should be set to true if the download was initiated by a script or a web + // page. I.e. if the download request cannot be attributed to an explicit user + // request for a download, then set this value to true. void set_content_initiated(bool content_initiated) { content_initiated_ = content_initiated; } void add_request_header(const std::string& name, const std::string& value) { request_headers_.push_back(make_pair(name, value)); } + + // HTTP Referrer and referrer encoding. void set_referrer(const Referrer& referrer) { referrer_ = referrer; } void set_referrer_encoding(const std::string& referrer_encoding) { referrer_encoding_ = referrer_encoding; } + + // If this is a request for resuming an HTTP/S download, |last_modified| + // should be the value of the last seen Last-Modified response header. void set_last_modified(const std::string& last_modified) { last_modified_ = last_modified; } + + // If this is a request for resuming an HTTP/S download, |etag| should be the + // last seen Etag response header. void set_etag(const std::string& etag) { etag_ = etag; } + + // HTTP method to use. void set_method(const std::string& method) { method_ = method; } + + // Body of the HTTP POST request. void set_post_body(const std::string& post_body) { post_body_ = post_body; } + + // If |prefer_cache| is true and the response to |url| is in the HTTP cache, + // it will be used without validation. If |method| is POST, then |post_id_| + // shoud be set via |set_post_id()| below to the identifier of the POST + // transaction used to originally retrieve the resource. void set_prefer_cache(bool prefer_cache) { prefer_cache_ = prefer_cache; } + + // See set_prefer_cache() above. void set_post_id(int64_t post_id) { post_id_ = post_id; } + + // See OnStartedCallback above. void set_callback(const OnStartedCallback& callback) { callback_ = callback; } + + // If not empty, specifies the full target path for the download. This value + // overrides the filename suggested by a Content-Disposition headers. It + // should only be set for programmatic downloads where the caller can verify + // the safety of the filename and the resulting download. void set_file_path(const base::FilePath& file_path) { save_info_.file_path = file_path; } + + // Suggested filename for the download. The suggestion can be overridden by + // either a Content-Disposition response header or a |file_path|. void set_suggested_name(const base::string16& suggested_name) { save_info_.suggested_name = suggested_name; } + + // If |offset| is non-zero, then a byte range request will be issued to fetch + // the range of bytes starting at |offset| through to the end of thedownload. void set_offset(int64_t offset) { save_info_.offset = offset; } void set_hash_state(const std::string& hash_state) { save_info_.hash_state = hash_state; } + + // If |prompt| is true, then the user will be prompted for a filename. Ignored + // if |file_path| is non-empty. void set_prompt(bool prompt) { save_info_.prompt_for_save_location = prompt; } void set_file(base::File file) { save_info_.file = std::move(file); } void set_do_not_prompt_for_login(bool do_not_prompt) { @@ -123,15 +184,8 @@ int render_frame_host_routing_id() const { return render_frame_host_routing_id_; } - RequestHeadersType::const_iterator request_headers_begin() const { - return request_headers_.begin(); - } - RequestHeadersType::const_iterator request_headers_end() const { - return request_headers_.end(); - } - content::ResourceContext* resource_context() const { - return resource_context_; - } + const RequestHeadersType& request_headers() const { return request_headers_; } + ResourceContext* resource_context() const { return resource_context_; } const base::FilePath& file_path() const { return save_info_.file_path; } const base::string16& suggested_name() const { return save_info_.suggested_name;
diff --git a/content/public/browser/gpu_service_registry.cc b/content/public/browser/gpu_service_registry.cc new file mode 100644 index 0000000..e6a2ba74 --- /dev/null +++ b/content/public/browser/gpu_service_registry.cc
@@ -0,0 +1,18 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/public/browser/gpu_service_registry.h" + +#include "content/browser/gpu/gpu_process_host.h" + +namespace content { + +ServiceRegistry* GetGpuServiceRegistry() { + GpuProcessHost* host = + GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, + CAUSE_FOR_GPU_LAUNCH_GET_GPU_SERVICE_REGISTRY); + return host->GetServiceRegistry(); +} + +} // namespace content
diff --git a/content/public/browser/gpu_service_registry.h b/content/public/browser/gpu_service_registry.h new file mode 100644 index 0000000..0e025ee --- /dev/null +++ b/content/public/browser/gpu_service_registry.h
@@ -0,0 +1,19 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_PUBLIC_BROWSER_GPU_SERVICE_REGISTRY_H_ +#define CONTENT_PUBLIC_BROWSER_GPU_SERVICE_REGISTRY_H_ + +#include "content/common/content_export.h" + +namespace content { +class ServiceRegistry; + +// Get ServiceRegistry registered via ContentGpuClient::RegisterMojoServices(). +// This must be called on IO thread. +CONTENT_EXPORT ServiceRegistry* GetGpuServiceRegistry(); + +} // namespace content + +#endif // CONTENT_PUBLIC_BROWSER_GPU_SERVICE_REGISTRY_H_
diff --git a/content/public/browser/permission_manager.h b/content/public/browser/permission_manager.h index 32c8f7f..d876e9d0 100644 --- a/content/public/browser/permission_manager.h +++ b/content/public/browser/permission_manager.h
@@ -37,7 +37,6 @@ PermissionType permission, RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(PermissionStatus)>& callback) = 0; // Requests multiple permissions on behalf of a frame identified by @@ -54,7 +53,6 @@ const std::vector<PermissionType>& permission, RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<PermissionStatus>&)>& callback) = 0;
diff --git a/content/public/browser/push_messaging_service.h b/content/public/browser/push_messaging_service.h index 31163d4..86aa34e 100644 --- a/content/public/browser/push_messaging_service.h +++ b/content/public/browser/push_messaging_service.h
@@ -83,13 +83,11 @@ const std::string& sender_id, const UnregisterCallback& callback) = 0; - // Checks the permission status for the requesting origin. Permission is only - // ever granted when the requesting origin matches the top level embedding - // origin. The |user_visible| boolean indicates whether the permission status - // only has to cover push messages resulting in visible effects to the user. + // Checks the permission status for the |origin|. The |user_visible| boolean + // indicates whether the permission status only has to cover push messages + // resulting in visible effects to the user. virtual blink::WebPushPermissionStatus GetPermissionStatus( - const GURL& requesting_origin, - const GURL& embedding_origin, + const GURL& origin, bool user_visible) = 0; // Returns whether subscriptions that do not mandate user visible UI upon
diff --git a/content/public/browser/resource_dispatcher_host.h b/content/public/browser/resource_dispatcher_host.h index 7ad7631b..4936674 100644 --- a/content/public/browser/resource_dispatcher_host.h +++ b/content/public/browser/resource_dispatcher_host.h
@@ -9,7 +9,7 @@ #include "base/callback_forward.h" #include "base/memory/scoped_ptr.h" -#include "content/public/browser/download_interrupt_reasons.h" +#include "content/common/content_export.h" namespace net { class URLRequest; @@ -26,9 +26,6 @@ class CONTENT_EXPORT ResourceDispatcherHost { public: - typedef base::Callback<void(DownloadItem*, DownloadInterruptReason)> - DownloadStartedCallback; - // Returns the singleton instance of the ResourceDispatcherHost. static ResourceDispatcherHost* Get(); @@ -50,30 +47,6 @@ // dialog boxes. virtual void SetAllowCrossOriginAuthPrompt(bool value) = 0; - // Initiates a download by explicit request of the renderer (e.g. due to - // alt-clicking a link) or some other chrome subsystem. - // |is_content_initiated| is used to indicate that the request was generated - // from a web page, and hence may not be as trustworthy as a browser - // generated request. If |download_id| is invalid, a download id will be - // automatically assigned to the request, otherwise the specified download id - // will be used. (Note that this will result in re-use of an existing - // download item if the download id was already assigned.) If the download - // is started, |started_callback| will be called on the UI thread with the - // DownloadItem; otherwise an interrupt reason will be returned. - virtual DownloadInterruptReason BeginDownload( - scoped_ptr<net::URLRequest> request, - const Referrer& referrer, - bool is_content_initiated, - ResourceContext* context, - int child_id, - int render_view_route_id, - int render_frame_route_id, - bool prefer_cache, - bool do_not_prompt_for_login, - scoped_ptr<DownloadSaveInfo> save_info, - uint32_t download_id, - const DownloadStartedCallback& started_callback) = 0; - // Clears the ResourceDispatcherHostLoginDelegate associated with the request. virtual void ClearLoginDelegateForRequest(net::URLRequest* request) = 0;
diff --git a/content/public/browser/resource_request_info.h b/content/public/browser/resource_request_info.h index c21f104..68c32fb 100644 --- a/content/public/browser/resource_request_info.h +++ b/content/public/browser/resource_request_info.h
@@ -52,6 +52,10 @@ int* render_process_id, int* render_frame_id); + // Returns true if the request originated from a Service Worker. + CONTENT_EXPORT static bool OriginatedFromServiceWorker( + const net::URLRequest* request); + // A callback that returns a pointer to a WebContents. The callback can // always be used, but it may return nullptr: if the info used to // instantiate the callback can no longer be used to return a WebContents,
diff --git a/content/public/common/content_client.cc b/content/public/common/content_client.cc index e7f50c8..75ed9979f 100644 --- a/content/public/common/content_client.cc +++ b/content/public/common/content_client.cc
@@ -62,8 +62,11 @@ } ContentClient::ContentClient() - : browser_(NULL), plugin_(NULL), renderer_(NULL), utility_(NULL) { -} + : browser_(NULL), + gpu_(NULL), + plugin_(NULL), + renderer_(NULL), + utility_(NULL) {} ContentClient::~ContentClient() { }
diff --git a/content/public/common/content_client.h b/content/public/common/content_client.h index 9d51d5b..eabd3da 100644 --- a/content/public/common/content_client.h +++ b/content/public/common/content_client.h
@@ -42,6 +42,7 @@ class ContentBrowserClient; class ContentClient; +class ContentGpuClient; class ContentPluginClient; class ContentRendererClient; class ContentUtilityClient; @@ -72,6 +73,7 @@ virtual ~ContentClient(); ContentBrowserClient* browser() { return browser_; } + ContentGpuClient* gpu() { return gpu_; } ContentPluginClient* plugin() { return plugin_; } ContentRendererClient* renderer() { return renderer_; } ContentUtilityClient* utility() { return utility_; } @@ -86,10 +88,11 @@ virtual void AddPepperPlugins( std::vector<content::PepperPluginInfo>* plugins) {} - // Gives the embedder a chance to register its own standard and saveable - // url schemes early on in the startup sequence. + // Gives the embedder a chance to register its own standard, referrer and + // saveable url schemes early on in the startup sequence. virtual void AddAdditionalSchemes( std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* savable_schemes) {} // Returns whether the given message should be sent in a swapped out renderer. @@ -159,6 +162,8 @@ // The embedder API for participating in browser logic. ContentBrowserClient* browser_; + // The embedder API for participating in gpu logic. + ContentGpuClient* gpu_; // The embedder API for participating in plugin logic. ContentPluginClient* plugin_; // The embedder API for participating in renderer logic.
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index befedc87..bfc08b6b 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc
@@ -293,6 +293,9 @@ // Disables Blink's XSSAuditor. The XSSAuditor mitigates reflective XSS. const char kDisableXSSAuditor[] = "disable-xss-auditor"; +// Disable gesture generation for wheel events. +const char kDisableWheelGestures[] = "disable-wheel-gestures"; + // Disable rasterizer that writes directly to GPU memory associated with tiles. const char kDisableZeroCopy[] = "disable-zero-copy";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index d4c11cf..f2cb6fd4 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h
@@ -95,6 +95,7 @@ extern const char kDisableV8IdleTasks[]; CONTENT_EXPORT extern const char kDisableWebSecurity[]; extern const char kDisableXSSAuditor[]; +CONTENT_EXPORT extern const char kDisableWheelGestures[]; CONTENT_EXPORT extern const char kDisableZeroCopy[]; CONTENT_EXPORT extern const char kDomAutomationController[]; CONTENT_EXPORT extern const char kDownloadProcess[];
diff --git a/content/public/common/referrer.cc b/content/public/common/referrer.cc index 183c950..bd145a2 100644 --- a/content/public/common/referrer.cc +++ b/content/public/common/referrer.cc
@@ -14,7 +14,7 @@ Referrer sanitized_referrer(referrer.url.GetAsReferrer(), referrer.policy); if (!request.SchemeIsHTTPOrHTTPS() || - !sanitized_referrer.url.SchemeIsHTTPOrHTTPS()) { + !sanitized_referrer.url.SchemeIsValidForReferrer()) { sanitized_referrer.url = GURL(); return sanitized_referrer; }
diff --git a/content/public/gpu/BUILD.gn b/content/public/gpu/BUILD.gn new file mode 100644 index 0000000..8de310ca --- /dev/null +++ b/content/public/gpu/BUILD.gn
@@ -0,0 +1,30 @@ +# 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. + +# See //content/BUILD.gn for how this works. +group("gpu") { + if (is_component_build) { + public_deps = [ + "//content", + ] + } else { + public_deps = [ + ":gpu_sources", + ] + } +} + +source_set("gpu_sources") { + visibility = [ "//content/*" ] + + sources = [ + "content_gpu_client.h", + ] + + deps = [ + "//base", + "//content:export", + "//content/gpu:gpu_sources", + ] +}
diff --git a/content/public/gpu/content_gpu_client.h b/content/public/gpu/content_gpu_client.h new file mode 100644 index 0000000..3b0095b1 --- /dev/null +++ b/content/public/gpu/content_gpu_client.h
@@ -0,0 +1,27 @@ +// 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_PUBLIC_GPU_CONTENT_GPU_CLIENT_H_ +#define CONTENT_PUBLIC_GPU_CONTENT_GPU_CLIENT_H_ + +#include "content/public/common/content_client.h" + +namespace content { + +class ServiceRegistry; + +// Embedder API for participating in gpu logic. +class CONTENT_EXPORT ContentGpuClient { + public: + virtual ~ContentGpuClient() {} + + // Allows client to register Mojo services in |registry| on the GPU process. + // The registered services will be exposed to the browser process through + // GpuProcessHost. + virtual void RegisterMojoServices(ServiceRegistry* registry) {} +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_GPU_CONTENT_GPU_CLIENT_H_
diff --git a/content/public/test/DEPS b/content/public/test/DEPS index 8d005cf..8be56b6 100644 --- a/content/public/test/DEPS +++ b/content/public/test/DEPS
@@ -16,6 +16,7 @@ "+content", "+gin/v8_initializer.h", "+media/base", + "+media/capture/video/android/capture_jni_registrar.h", "+third_party/iaccessible2", "+ui/base/resource/resource_bundle.h", ],
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc index be9b2e0..51ac846 100644 --- a/content/public/test/browser_test_utils.cc +++ b/content/public/test/browser_test_utils.cc
@@ -25,6 +25,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_contents/web_contents_view.h" #include "content/common/input/synthetic_web_input_event_builders.h" +#include "content/common/input_messages.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/histogram_fetcher.h" @@ -1073,16 +1074,24 @@ FrameWatcher::~FrameWatcher() { } -void FrameWatcher::ReceivedFrameSwap() { +void FrameWatcher::ReceivedFrameSwap(cc::CompositorFrameMetadata metadata) { --frames_to_wait_; + last_metadata_ = metadata; if (frames_to_wait_ == 0) quit_.Run(); } bool FrameWatcher::OnMessageReceived(const IPC::Message& message) { if (message.type() == ViewHostMsg_SwapCompositorFrame::ID) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&FrameWatcher::ReceivedFrameSwap, this)); + ViewHostMsg_SwapCompositorFrame::Param param; + if (!ViewHostMsg_SwapCompositorFrame::Read(&message, ¶m)) + return false; + scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); + base::get<1>(param).AssignTo(frame.get()); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&FrameWatcher::ReceivedFrameSwap, this, frame->metadata)); } return false; } @@ -1103,6 +1112,10 @@ run_loop.Run(); } +const cc::CompositorFrameMetadata& FrameWatcher::LastMetadata() { + return last_metadata_; +} + MainThreadFrameObserver::MainThreadFrameObserver( RenderWidgetHost* render_widget_host) : render_widget_host_(render_widget_host), @@ -1140,4 +1153,48 @@ return true; } +InputMsgWatcher::InputMsgWatcher(RenderWidgetHost* render_widget_host, + blink::WebInputEvent::Type type) + : BrowserMessageFilter(InputMsgStart), + wait_for_type_(type), + ack_result_(INPUT_EVENT_ACK_STATE_UNKNOWN) { + render_widget_host->GetProcess()->AddFilter(this); +} + +InputMsgWatcher::~InputMsgWatcher() {} + +void InputMsgWatcher::ReceivedAck(blink::WebInputEvent::Type ack_type, + uint32_t ack_state) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (wait_for_type_ == ack_type) { + ack_result_ = ack_state; + if (!quit_.is_null()) + quit_.Run(); + } +} + +bool InputMsgWatcher::OnMessageReceived(const IPC::Message& message) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) { + InputHostMsg_HandleInputEvent_ACK::Param params; + InputHostMsg_HandleInputEvent_ACK::Read(&message, ¶ms); + blink::WebInputEvent::Type ack_type = base::get<0>(params).type; + InputEventAckState ack_state = base::get<0>(params).state; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&InputMsgWatcher::ReceivedAck, this, ack_type, ack_state)); + } + return false; +} + +uint32_t InputMsgWatcher::WaitForAck() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (ack_result_ != INPUT_EVENT_ACK_STATE_UNKNOWN) + return ack_result_; + base::RunLoop run_loop; + base::AutoReset<base::Closure> reset_quit(&quit_, run_loop.QuitClosure()); + run_loop.Run(); + return ack_result_; +} + } // namespace content
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h index ed6aa65..1793726 100644 --- a/content/public/test/browser_test_utils.h +++ b/content/public/test/browser_test_utils.h
@@ -17,6 +17,7 @@ #include "base/process/process.h" #include "base/strings/string16.h" #include "build/build_config.h" +#include "cc/output/compositor_frame.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -425,16 +426,21 @@ // Wait for |frames_to_wait| swap mesages from the compositor. void WaitFrames(int frames_to_wait); + // Return the meta data received in the last compositor + // swap frame. + const cc::CompositorFrameMetadata& LastMetadata(); + private: ~FrameWatcher() override; // Overridden BrowserMessageFilter methods. bool OnMessageReceived(const IPC::Message& message) override; - void ReceivedFrameSwap(); + void ReceivedFrameSwap(cc::CompositorFrameMetadata meta_data); int frames_to_wait_; base::Closure quit_; + cc::CompositorFrameMetadata last_metadata_; DISALLOW_COPY_AND_ASSIGN(FrameWatcher); }; @@ -463,6 +469,31 @@ DISALLOW_COPY_AND_ASSIGN(MainThreadFrameObserver); }; +// Watches for an input msg to be consumed. +class InputMsgWatcher : public BrowserMessageFilter { + public: + InputMsgWatcher(RenderWidgetHost* render_widget_host, + blink::WebInputEvent::Type type); + + // Wait until ack message occurs, returning the ack result from + // the message. + uint32_t WaitForAck(); + + private: + ~InputMsgWatcher() override; + + // Overridden BrowserMessageFilter methods. + bool OnMessageReceived(const IPC::Message& message) override; + + void ReceivedAck(blink::WebInputEvent::Type ack_type, uint32_t ack_state); + + blink::WebInputEvent::Type wait_for_type_; + uint32_t ack_result_; + base::Closure quit_; + + DISALLOW_COPY_AND_ASSIGN(InputMsgWatcher); +}; + } // namespace content #endif // CONTENT_PUBLIC_TEST_BROWSER_TEST_UTILS_H_
diff --git a/content/public/test/content_test_suite_base.cc b/content/public/test/content_test_suite_base.cc index a6bf5ea3d..1b2914c 100644 --- a/content/public/test/content_test_suite_base.cc +++ b/content/public/test/content_test_suite_base.cc
@@ -34,6 +34,7 @@ #include "content/browser/android/browser_jni_registrar.h" #include "content/common/android/common_jni_registrar.h" #include "media/base/android/media_jni_registrar.h" +#include "media/capture/video/android/capture_jni_registrar.h" #include "net/android/net_jni_registrar.h" #include "ui/android/ui_android_jni_registrar.h" #include "ui/base/android/ui_base_jni_registrar.h" @@ -88,6 +89,7 @@ content::android::RegisterCommonJni(env); content::android::RegisterBrowserJni(env); gfx::android::RegisterJni(env); + media::RegisterCaptureJni(env); media::RegisterJni(env); net::android::RegisterJni(env); ui::android::RegisterJni(env);
diff --git a/content/public/test/mock_download_manager.h b/content/public/test/mock_download_manager.h index ae9511d..8dadb52 100644 --- a/content/public/test/mock_download_manager.h +++ b/content/public/test/mock_download_manager.h
@@ -96,7 +96,6 @@ base::Time remove_end)); MOCK_METHOD2(RemoveDownloadsBetween, int(base::Time remove_begin, base::Time remove_end)); - MOCK_METHOD1(RemoveDownloads, int(base::Time remove_begin)); MOCK_METHOD0(RemoveAllDownloads, int()); MOCK_METHOD1(DownloadUrlMock, void(DownloadUrlParameters*)); void DownloadUrl(scoped_ptr<DownloadUrlParameters> params) override {
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc index 2706d71e..c622ee8 100644 --- a/content/public/test/render_view_test.cc +++ b/content/public/test/render_view_test.cc
@@ -441,14 +441,16 @@ const blink::WebKeyboardEvent& key_event) { RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); impl->OnMessageReceived( - InputMsg_HandleInputEvent(0, &key_event, ui::LatencyInfo())); + InputMsg_HandleInputEvent(0, &key_event, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); } void RenderViewTest::SendWebMouseEvent( const blink::WebMouseEvent& mouse_event) { RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); impl->OnMessageReceived( - InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo())); + InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); } const char* const kGetCoordinatesScript = @@ -515,10 +517,12 @@ mouse_event.clickCount = 1; RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); impl->OnMessageReceived( - InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo())); + InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); mouse_event.type = WebInputEvent::MouseUp; impl->OnMessageReceived( - InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo())); + InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); } @@ -539,10 +543,12 @@ mouse_event.clickCount = 1; RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); impl->OnMessageReceived( - InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo())); + InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); mouse_event.type = WebInputEvent::MouseUp; impl->OnMessageReceived( - InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo())); + InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); } void RenderViewTest::SimulateRectTap(const gfx::Rect& rect) { @@ -556,7 +562,8 @@ gesture_event.sourceDevice = blink::WebGestureDeviceTouchpad; RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); impl->OnMessageReceived( - InputMsg_HandleInputEvent(0, &gesture_event, ui::LatencyInfo())); + InputMsg_HandleInputEvent(0, &gesture_event, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); impl->FocusChangeComplete(); }
diff --git a/content/public/test/test_download_request_handler.cc b/content/public/test/test_download_request_handler.cc index ea6dc35..22c83198 100644 --- a/content/public/test/test_download_request_handler.cc +++ b/content/public/test/test_download_request_handler.cc
@@ -113,7 +113,7 @@ net::NetworkDelegate* network_delegate); ~PartialResponseJob() override; - void ReportCompletedRequest(int64_t transferred_byte_count); + void ReportCompletedRequest(); static void OnStartResponseCallbackOnPossiblyIncorrectThread( base::WeakPtr<PartialResponseJob> job, const std::string& headers, @@ -149,6 +149,7 @@ int64_t offset_of_next_read_ = -1; int64_t requested_range_begin_ = -1; int64_t requested_range_end_ = -1; + int64_t read_byte_count_ = 0; base::WeakPtrFactory<PartialResponseJob> weak_factory_; DISALLOW_COPY_AND_ASSIGN(PartialResponseJob); @@ -222,7 +223,9 @@ DCHECK_NE(-1, parameters_->pattern_generator_seed); } -TestDownloadRequestHandler::PartialResponseJob::~PartialResponseJob() {} +TestDownloadRequestHandler::PartialResponseJob::~PartialResponseJob() { + ReportCompletedRequest(); +} void TestDownloadRequestHandler::PartialResponseJob::Start() { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -272,7 +275,6 @@ // requested_range_begin_ == -1 implies that the body was empty. if (offset_of_next_read_ > requested_range_end_ || requested_range_begin_ == -1) { - ReportCompletedRequest(requested_range_end_ - requested_range_begin_ + 1); DVLOG(1) << "Done reading."; return 0; } @@ -286,7 +288,6 @@ if (offset_of_next_read_ == injected_error.offset) { int error = injected_error.error; DVLOG(1) << "Returning error " << net::ErrorToString(error); - ReportCompletedRequest(injected_error.offset - requested_range_begin_); parameters_->injected_errors.pop(); return error; } @@ -303,14 +304,14 @@ DVLOG(1) << "Read " << bytes_to_copy << " bytes at offset " << offset_of_next_read_; offset_of_next_read_ += bytes_to_copy; + read_byte_count_ += bytes_to_copy; return bytes_to_copy; } -void TestDownloadRequestHandler::PartialResponseJob::ReportCompletedRequest( - int64_t transferred_byte_count) { +void TestDownloadRequestHandler::PartialResponseJob::ReportCompletedRequest() { if (interceptor_.get()) { TestDownloadRequestHandler::CompletedRequest completed_request; - completed_request.transferred_byte_count = transferred_byte_count; + completed_request.transferred_byte_count = read_byte_count_; completed_request.request_headers = request()->extra_request_headers(); interceptor_->AddCompletedRequest(completed_request); }
diff --git a/content/public/test/test_file_error_injector.cc b/content/public/test/test_file_error_injector.cc index 96fcd95..5d5d8850 100644 --- a/content/public/test/test_file_error_injector.cc +++ b/content/public/test/test_file_error_injector.cc
@@ -28,18 +28,18 @@ typedef base::Callback<void(const GURL& url)> ConstructionCallback; typedef base::Callback<void(const GURL& url)> DestructionCallback; - DownloadFileWithErrors( - scoped_ptr<DownloadSaveInfo> save_info, - const base::FilePath& default_download_directory, - const GURL& url, - const GURL& referrer_url, - bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, - const net::BoundNetLog& bound_net_log, - base::WeakPtr<DownloadDestinationObserver> observer, - const TestFileErrorInjector::FileErrorInfo& error_info, - const ConstructionCallback& ctor_callback, - const DestructionCallback& dtor_callback); + DownloadFileWithErrors(const DownloadSaveInfo& save_info, + const base::FilePath& default_download_directory, + const GURL& url, + const GURL& referrer_url, + bool calculate_hash, + base::File file, + scoped_ptr<ByteStreamReader> byte_stream, + const net::BoundNetLog& bound_net_log, + base::WeakPtr<DownloadDestinationObserver> observer, + const TestFileErrorInjector::FileErrorInfo& error_info, + const ConstructionCallback& ctor_callback, + const DestructionCallback& dtor_callback); ~DownloadFileWithErrors() override; @@ -101,23 +101,25 @@ } DownloadFileWithErrors::DownloadFileWithErrors( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_download_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, + base::File file, + scoped_ptr<ByteStreamReader> byte_stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer, const TestFileErrorInjector::FileErrorInfo& error_info, const ConstructionCallback& ctor_callback, const DestructionCallback& dtor_callback) - : DownloadFileImpl(std::move(save_info), + : DownloadFileImpl(save_info, default_download_directory, url, referrer_url, calculate_hash, - std::move(stream), + std::move(file), + std::move(byte_stream), bound_net_log, observer), source_url_(url), @@ -262,12 +264,13 @@ // DownloadFileFactory interface. DownloadFile* CreateFile( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_download_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, + base::File file, + scoped_ptr<ByteStreamReader> byte_stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) override; @@ -296,12 +299,13 @@ } DownloadFile* DownloadFileWithErrorsFactory::CreateFile( - scoped_ptr<DownloadSaveInfo> save_info, + const DownloadSaveInfo& save_info, const base::FilePath& default_download_directory, const GURL& url, const GURL& referrer_url, bool calculate_hash, - scoped_ptr<ByteStreamReader> stream, + base::File file, + scoped_ptr<ByteStreamReader> byte_stream, const net::BoundNetLog& bound_net_log, base::WeakPtr<DownloadDestinationObserver> observer) { if (injected_errors_.find(url.spec()) == injected_errors_.end()) { @@ -316,8 +320,8 @@ } return new DownloadFileWithErrors( - std::move(save_info), default_download_directory, url, referrer_url, - calculate_hash, std::move(stream), bound_net_log, observer, + save_info, default_download_directory, url, referrer_url, calculate_hash, + std::move(file), std::move(byte_stream), bound_net_log, observer, injected_errors_[url.spec()], construction_callback_, destruction_callback_); }
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index c304490..c4f4510 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -220,7 +220,7 @@ ] } - if (enable_mojo_media != "none") { + if (enable_mojo_media) { deps += [ "//media/mojo/interfaces", "//media/mojo/services:proxy",
diff --git a/content/renderer/OWNERS b/content/renderer/OWNERS index c7ecacd..d285f815 100644 --- a/content/renderer/OWNERS +++ b/content/renderer/OWNERS
@@ -1,3 +1,6 @@ +# For Blink API usage +esprehn@chromium.org + # Mac Sandbox profiles. per-file *.sb=set noparent per-file *.sb=rsesek@chromium.org
diff --git a/content/renderer/accessibility/blink_ax_enum_conversion.cc b/content/renderer/accessibility/blink_ax_enum_conversion.cc index ac99237..1ad022c 100644 --- a/content/renderer/accessibility/blink_ax_enum_conversion.cc +++ b/content/renderer/accessibility/blink_ax_enum_conversion.cc
@@ -24,9 +24,6 @@ if (o.canSetFocusAttribute()) state |= (1 << ui::AX_STATE_FOCUSABLE); - if (o.isFocused()) - state |= (1 << ui::AX_STATE_FOCUSED); - if (o.role() == blink::WebAXRolePopUpButton || o.ariaHasPopup()) state |= (1 << ui::AX_STATE_HASPOPUP);
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc index eab8b34..1936535 100644 --- a/content/renderer/accessibility/blink_ax_tree_source.cc +++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -145,6 +145,10 @@ tree_data.loading_progress = root.estimatedLoadingProgress(); tree_data.doctype = "html"; + WebAXObject focus = document.focusedAccessibilityObject(); + if (!focus.isNull()) + tree_data.focus_id = focus.axID(); + WebAXObject anchor_object, focus_object; int anchor_offset, focus_offset; root.selection(anchor_object, anchor_offset, focus_object, focus_offset); @@ -402,12 +406,6 @@ if (src.posInSet()) dst->AddIntAttribute(ui::AX_ATTR_POS_IN_SET, src.posInSet()); - // Treat the active list box item as focused. - if (dst->role == ui::AX_ROLE_LIST_BOX_OPTION && - src.isSelectedOptionActive()) { - dst->state |= (1 << ui::AX_STATE_FOCUSED); - } - if (src.canvasHasFallbackContent()) dst->AddBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK, true);
diff --git a/content/renderer/android/synchronous_compositor_filter.cc b/content/renderer/android/synchronous_compositor_filter.cc index 5f4c9d27..54dfed3 100644 --- a/content/renderer/android/synchronous_compositor_filter.cc +++ b/content/renderer/android/synchronous_compositor_filter.cc
@@ -255,6 +255,10 @@ Send(new InputHostMsg_DidStopFlinging(routing_id)); } +void SynchronousCompositorFilter::NonBlockingInputEventHandled( + int routing_id, + blink::WebInputEvent::Type type) {} + SynchronousCompositorFilter::Entry::Entry() : begin_frame_source(nullptr), output_surface(nullptr),
diff --git a/content/renderer/android/synchronous_compositor_filter.h b/content/renderer/android/synchronous_compositor_filter.h index 89d83002..bfab1f6 100644 --- a/content/renderer/android/synchronous_compositor_filter.h +++ b/content/renderer/android/synchronous_compositor_filter.h
@@ -66,6 +66,8 @@ void DidOverscroll(int routing_id, const DidOverscrollParams& params) override; void DidStopFlinging(int routing_id) override; + void NonBlockingInputEventHandled(int routing_id, + blink::WebInputEvent::Type type) override; private: ~SynchronousCompositorFilter() override;
diff --git a/content/renderer/bluetooth/bluetooth_dispatcher.cc b/content/renderer/bluetooth/bluetooth_dispatcher.cc index 966320fa..d24a056 100644 --- a/content/renderer/bluetooth/bluetooth_dispatcher.cc +++ b/content/renderer/bluetooth/bluetooth_dispatcher.cc
@@ -16,16 +16,16 @@ #include "third_party/WebKit/public/platform/WebPassOwnPtr.h" #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothDevice.h" #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothError.h" -#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristic.h" -#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristicInit.h" -#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTService.h" +#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristic.h" +#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h" +#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTService.h" #include "third_party/WebKit/public/platform/modules/bluetooth/WebRequestDeviceOptions.h" using blink::WebBluetoothDevice; using blink::WebBluetoothError; -using blink::WebBluetoothGATTCharacteristicInit; -using blink::WebBluetoothGATTServerConnectCallbacks; -using blink::WebBluetoothGATTService; +using blink::WebBluetoothRemoteGATTCharacteristicInit; +using blink::WebBluetoothRemoteGATTServerConnectCallbacks; +using blink::WebBluetoothRemoteGATTService; using blink::WebBluetoothReadValueCallbacks; using blink::WebBluetoothRequestDeviceCallbacks; using blink::WebBluetoothScanFilter; @@ -81,7 +81,7 @@ BluetoothNotificationsRequest( int frame_routing_id, const std::string characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, blink::WebBluetoothNotificationsCallbacks* callbacks, NotificationsRequestType type) : frame_routing_id(frame_routing_id), @@ -99,7 +99,7 @@ // destroyed, which in turn calls characteristicObjectRemoved. // characteristicObjectRemoved will null any pointers to the object // and queue a stop notifications request if necessary. - blink::WebBluetoothGATTCharacteristic* characteristic; + blink::WebBluetoothRemoteGATTCharacteristic* characteristic; scoped_ptr<blink::WebBluetoothNotificationsCallbacks> callbacks; NotificationsRequestType type; }; @@ -232,7 +232,7 @@ void BluetoothDispatcher::connect( int frame_routing_id, const blink::WebString& device_id, - blink::WebBluetoothGATTServerConnectCallbacks* callbacks) { + blink::WebBluetoothRemoteGATTServerConnectCallbacks* callbacks) { int request_id = pending_connect_requests_.Add(callbacks); Send(new BluetoothHostMsg_GATTServerConnect( CurrentWorkerId(), request_id, frame_routing_id, device_id.utf8())); @@ -295,7 +295,7 @@ void BluetoothDispatcher::startNotifications( int frame_routing_id, const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, blink::WebBluetoothNotificationsCallbacks* callbacks) { int request_id = QueueNotificationRequest( frame_routing_id, characteristic_instance_id.utf8(), characteristic, @@ -314,7 +314,7 @@ void BluetoothDispatcher::stopNotifications( int frame_routing_id, const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, blink::WebBluetoothNotificationsCallbacks* callbacks) { int request_id = QueueNotificationRequest( frame_routing_id, characteristic_instance_id.utf8(), characteristic, @@ -330,7 +330,7 @@ void BluetoothDispatcher::characteristicObjectRemoved( int frame_routing_id, const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic) { + blink::WebBluetoothRemoteGATTCharacteristic* characteristic) { // We need to remove references to the object from the following: // 1) The set of active characteristics // 2) The queue waiting for a response @@ -394,7 +394,7 @@ void BluetoothDispatcher::registerCharacteristicObject( int frame_routing_id, const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic) { + blink::WebBluetoothRemoteGATTCharacteristic* characteristic) { // TODO(ortuno): After the Object manager is implemented, there will // only be one object per characteristic. But for now we remove // the previous object. @@ -415,7 +415,7 @@ int BluetoothDispatcher::QueueNotificationRequest( int frame_routing_id, const std::string& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, blink::WebBluetoothNotificationsCallbacks* callbacks, NotificationsRequestType type) { int request_id = @@ -478,14 +478,14 @@ void BluetoothDispatcher::AddToActiveNotificationSubscriptions( const std::string& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic) { + blink::WebBluetoothRemoteGATTCharacteristic* characteristic) { active_notification_subscriptions_[characteristic_instance_id].insert( characteristic); } bool BluetoothDispatcher::RemoveFromActiveNotificationSubscriptions( const std::string& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic) { + blink::WebBluetoothRemoteGATTCharacteristic* characteristic) { auto active_map = active_notification_subscriptions_.find(characteristic_instance_id); @@ -510,7 +510,7 @@ const int frame_routing_id = request->frame_routing_id; const std::string& characteristic_instance_id = request->characteristic_instance_id; - blink::WebBluetoothGATTCharacteristic* characteristic = + blink::WebBluetoothRemoteGATTCharacteristic* characteristic = request->characteristic; blink::WebBluetoothNotificationsCallbacks* callbacks = request->callbacks.get(); @@ -544,7 +544,7 @@ const int frame_routing_id = request->frame_routing_id; const std::string& characteristic_instance_id = request->characteristic_instance_id; - blink::WebBluetoothGATTCharacteristic* characteristic = + blink::WebBluetoothRemoteGATTCharacteristic* characteristic = request->characteristic; blink::WebBluetoothNotificationsCallbacks* callbacks = request->callbacks.get(); @@ -631,9 +631,10 @@ DCHECK(pending_primary_service_requests_.Lookup(request_id)) << request_id; BluetoothPrimaryServiceRequest* request = pending_primary_service_requests_.Lookup(request_id); - request->callbacks->onSuccess(blink::adoptWebPtr(new WebBluetoothGATTService( - WebString::fromUTF8(service_instance_id), request->service_uuid, - true /* isPrimary */, request->device_id))); + request->callbacks->onSuccess( + blink::adoptWebPtr(new WebBluetoothRemoteGATTService( + WebString::fromUTF8(service_instance_id), request->service_uuid, + true /* isPrimary */, request->device_id))); pending_primary_service_requests_.Remove(request_id); } @@ -657,7 +658,7 @@ BluetoothCharacteristicRequest* request = pending_characteristic_requests_.Lookup(request_id); request->callbacks->onSuccess( - blink::adoptWebPtr(new WebBluetoothGATTCharacteristicInit( + blink::adoptWebPtr(new WebBluetoothRemoteGATTCharacteristicInit( WebString::fromUTF8(characteristic_instance_id), request->service_instance_id, request->characteristic_uuid, characteristic_properties)));
diff --git a/content/renderer/bluetooth/bluetooth_dispatcher.h b/content/renderer/bluetooth/bluetooth_dispatcher.h index 4668b5bd..1054ac4b 100644 --- a/content/renderer/bluetooth/bluetooth_dispatcher.h +++ b/content/renderer/bluetooth/bluetooth_dispatcher.h
@@ -24,7 +24,7 @@ } namespace blink { -class WebBluetoothGATTCharacteristic; +class WebBluetoothRemoteGATTCharacteristic; } namespace IPC { @@ -67,7 +67,7 @@ blink::WebBluetoothRequestDeviceCallbacks* callbacks); void connect(int frame_routing_id, const blink::WebString& device_id, - blink::WebBluetoothGATTServerConnectCallbacks* callbacks); + blink::WebBluetoothRemoteGATTServerConnectCallbacks* callbacks); void disconnect(int frame_routing_id, const blink::WebString& device_id); void getPrimaryService( int frame_routing_id, @@ -89,20 +89,20 @@ blink::WebBluetoothWriteValueCallbacks*); void startNotifications(int frame_routing_id, const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* delegate, + blink::WebBluetoothRemoteGATTCharacteristic* delegate, blink::WebBluetoothNotificationsCallbacks*); void stopNotifications(int frame_routing_id, const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* delegate, + blink::WebBluetoothRemoteGATTCharacteristic* delegate, blink::WebBluetoothNotificationsCallbacks*); void characteristicObjectRemoved( int frame_routing_id, const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* delegate); + blink::WebBluetoothRemoteGATTCharacteristic* delegate); void registerCharacteristicObject( int frame_routing_id, const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic); + blink::WebBluetoothRemoteGATTCharacteristic* characteristic); // WorkerThread::Observer implementation. void WillStopCurrentWorkerThread() override; @@ -126,7 +126,7 @@ int QueueNotificationRequest( int frame_routing_id, const std::string& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, blink::WebBluetoothNotificationsCallbacks* callbacks, NotificationsRequestType type); // Pops the last requests and runs the next request in the queue. @@ -143,13 +143,13 @@ // notifications. void AddToActiveNotificationSubscriptions( const std::string& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic); + blink::WebBluetoothRemoteGATTCharacteristic* characteristic); // Removes the object from the set of subscribed object to the // characteristic's notifications. Returns true if the subscription // becomes inactive. bool RemoveFromActiveNotificationSubscriptions( const std::string& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic); + blink::WebBluetoothRemoteGATTCharacteristic* characteristic); // The following functions decide whether to resolve the request immediately // or send an IPC to change the subscription state. @@ -228,7 +228,7 @@ pending_requests_; // Tracks requests to connect to a device. // Owns callback objects. - IDMap<blink::WebBluetoothGATTServerConnectCallbacks, IDMapOwnPointer> + IDMap<blink::WebBluetoothRemoteGATTServerConnectCallbacks, IDMapOwnPointer> pending_connect_requests_; // Tracks requests to get a primary service from a device. // Owns request objects. @@ -246,19 +246,20 @@ pending_notifications_requests_; // Map of characteristic_instance_id to a set of - // WebBluetoothGATTCharacteristic pointers. Keeps track of which + // WebBluetoothRemoteGATTCharacteristic pointers. Keeps track of which // objects are subscribed to notifications. - std::map<std::string, std::set<blink::WebBluetoothGATTCharacteristic*>> + std::map<std::string, std::set<blink::WebBluetoothRemoteGATTCharacteristic*>> active_notification_subscriptions_; - // Map of characteristic_instance_ids to WebBluetoothGATTCharacteristics. + // Map of characteristic_instance_ids to + // WebBluetoothRemoteGATTCharacteristics. // Keeps track of what characteristics have listeners. // TODO(ortuno): We are assuming that there exists a single frame per // dispatcher, so there could be at most one characteristic object per // characteristic_instance_id. Change to a set when we support multiple // frames. // http://crbug.com/541388 - std::map<std::string, blink::WebBluetoothGATTCharacteristic*> + std::map<std::string, blink::WebBluetoothRemoteGATTCharacteristic*> active_characteristics_; DISALLOW_COPY_AND_ASSIGN(BluetoothDispatcher);
diff --git a/content/renderer/bluetooth/web_bluetooth_impl.cc b/content/renderer/bluetooth/web_bluetooth_impl.cc index 09fd817..d138492 100644 --- a/content/renderer/bluetooth/web_bluetooth_impl.cc +++ b/content/renderer/bluetooth/web_bluetooth_impl.cc
@@ -29,7 +29,7 @@ void WebBluetoothImpl::connect( const blink::WebString& device_id, - blink::WebBluetoothGATTServerConnectCallbacks* callbacks) { + blink::WebBluetoothRemoteGATTServerConnectCallbacks* callbacks) { GetDispatcher()->connect(frame_routing_id_, device_id, callbacks); } @@ -70,7 +70,7 @@ void WebBluetoothImpl::startNotifications( const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, blink::WebBluetoothNotificationsCallbacks* callbacks) { GetDispatcher()->startNotifications( frame_routing_id_, characteristic_instance_id, characteristic, callbacks); @@ -78,7 +78,7 @@ void WebBluetoothImpl::stopNotifications( const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, blink::WebBluetoothNotificationsCallbacks* callbacks) { GetDispatcher()->stopNotifications( frame_routing_id_, characteristic_instance_id, characteristic, callbacks); @@ -86,14 +86,14 @@ void WebBluetoothImpl::characteristicObjectRemoved( const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic) { + blink::WebBluetoothRemoteGATTCharacteristic* characteristic) { GetDispatcher()->characteristicObjectRemoved( frame_routing_id_, characteristic_instance_id, characteristic); } void WebBluetoothImpl::registerCharacteristicObject( const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic) { + blink::WebBluetoothRemoteGATTCharacteristic* characteristic) { GetDispatcher()->registerCharacteristicObject( frame_routing_id_, characteristic_instance_id, characteristic); }
diff --git a/content/renderer/bluetooth/web_bluetooth_impl.h b/content/renderer/bluetooth/web_bluetooth_impl.h index ca40070..5ba96eb 100644 --- a/content/renderer/bluetooth/web_bluetooth_impl.h +++ b/content/renderer/bluetooth/web_bluetooth_impl.h
@@ -16,7 +16,7 @@ #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetooth.h" namespace blink { -class WebBluetoothGATTCharacteristic; +class WebBluetoothRemoteGATTCharacteristic; } namespace content { @@ -39,7 +39,7 @@ blink::WebBluetoothRequestDeviceCallbacks* callbacks) override; void connect( const blink::WebString& device_id, - blink::WebBluetoothGATTServerConnectCallbacks* callbacks) override; + blink::WebBluetoothRemoteGATTServerConnectCallbacks* callbacks) override; void disconnect(const blink::WebString& device_id) override; void getPrimaryService( const blink::WebString& device_id, @@ -54,18 +54,20 @@ void writeValue(const blink::WebString& characteristic_instance_id, const blink::WebVector<uint8_t>& value, blink::WebBluetoothWriteValueCallbacks*) override; - void startNotifications(const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, - blink::WebBluetoothNotificationsCallbacks*) override; - void stopNotifications(const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic, - blink::WebBluetoothNotificationsCallbacks*) override; + void startNotifications( + const blink::WebString& characteristic_instance_id, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, + blink::WebBluetoothNotificationsCallbacks*) override; + void stopNotifications( + const blink::WebString& characteristic_instance_id, + blink::WebBluetoothRemoteGATTCharacteristic* characteristic, + blink::WebBluetoothNotificationsCallbacks*) override; void characteristicObjectRemoved( const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic) override; + blink::WebBluetoothRemoteGATTCharacteristic* characteristic) override; void registerCharacteristicObject( const blink::WebString& characteristic_instance_id, - blink::WebBluetoothGATTCharacteristic* characteristic) override; + blink::WebBluetoothRemoteGATTCharacteristic* characteristic) override; private: BluetoothDispatcher* GetDispatcher();
diff --git a/content/renderer/geolocation_dispatcher.cc b/content/renderer/geolocation_dispatcher.cc index 20d3add2..1c61eca7 100644 --- a/content/renderer/geolocation_dispatcher.cc +++ b/content/renderer/geolocation_dispatcher.cc
@@ -12,7 +12,6 @@ #include "third_party/WebKit/public/web/WebGeolocationClient.h" #include "third_party/WebKit/public/web/WebGeolocationPosition.h" #include "third_party/WebKit/public/web/WebGeolocationError.h" -#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" using blink::WebGeolocationController; using blink::WebGeolocationError; @@ -83,7 +82,6 @@ permission_service_->RequestPermission( PermissionName::GEOLOCATION, permissionRequest.securityOrigin().toString().utf8(), - blink::WebUserGestureIndicator::isProcessingUserGesture(), base::Bind(&GeolocationDispatcher::OnPermissionSet, base::Unretained(this), permission_request_id)); }
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc index 66a8d40..004addd 100644 --- a/content/renderer/gpu/render_widget_compositor.cc +++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -334,10 +334,6 @@ settings.top_controls_hide_threshold = hide_threshold; } - settings.verify_property_trees = - cmd->HasSwitch(cc::switches::kEnablePropertyTreeVerification); - if (cmd->HasSwitch(cc::switches::kDisableCompositorPropertyTrees)) - settings.use_property_trees = false; settings.renderer_settings.allow_antialiasing &= !cmd->HasSwitch(cc::switches::kDisableCompositedAntialiasing); // The means the renderer compositor has 2 possible modes:
diff --git a/content/renderer/idle_user_detector.cc b/content/renderer/idle_user_detector.cc index 339f68a5..9ca3570 100644 --- a/content/renderer/idle_user_detector.cc +++ b/content/renderer/idle_user_detector.cc
@@ -25,8 +25,10 @@ return false; } -void IdleUserDetector::OnHandleInputEvent(const blink::WebInputEvent* event, - const ui::LatencyInfo& latency_info) { +void IdleUserDetector::OnHandleInputEvent( + const blink::WebInputEvent* event, + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type) { if (GetContentClient()->renderer()->RunIdleHandlerWhenWidgetsHidden()) { RenderThreadImpl* render_thread = RenderThreadImpl::current(); if (render_thread != NULL) {
diff --git a/content/renderer/idle_user_detector.h b/content/renderer/idle_user_detector.h index 3e3beba..cbe85a0 100644 --- a/content/renderer/idle_user_detector.h +++ b/content/renderer/idle_user_detector.h
@@ -6,6 +6,7 @@ #define CONTENT_RENDERER_IDLE_USER_DETECTOR_H_ #include "base/macros.h" +#include "content/common/input/input_event_dispatch_type.h" #include "content/public/renderer/render_view_observer.h" namespace blink { @@ -30,7 +31,8 @@ bool OnMessageReceived(const IPC::Message& message) override; void OnHandleInputEvent(const blink::WebInputEvent* event, - const ui::LatencyInfo& latency_info); + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type); DISALLOW_COPY_AND_ASSIGN(IdleUserDetector); };
diff --git a/content/renderer/input/input_event_filter.cc b/content/renderer/input/input_event_filter.cc index 927a402..9a90141 100644 --- a/content/renderer/input/input_event_filter.cc +++ b/content/renderer/input/input_event_filter.cc
@@ -65,11 +65,13 @@ synchronous_input_handler_proxy) { base::AutoLock locked(routes_lock_); routes_.insert(routing_id); + route_queues_[routing_id].reset(new NonBlockingEventQueue(routing_id, this)); } void InputEventFilter::DidRemoveInputHandler(int routing_id) { base::AutoLock locked(routes_lock_); routes_.erase(routing_id); + route_queues_.erase(routing_id); } void InputEventFilter::DidOverscroll(int routing_id, @@ -87,6 +89,17 @@ SendMessage(make_scoped_ptr(new InputHostMsg_DidStopFlinging(routing_id))); } +void InputEventFilter::NonBlockingInputEventHandled( + int routing_id, + blink::WebInputEvent::Type type) { + DCHECK(target_task_runner_->BelongsToCurrentThread()); + RouteQueueMap::iterator iter = route_queues_.find(routing_id); + if (iter == route_queues_.end() || !iter->second) + return; + + iter->second->EventHandled(type); +} + void InputEventFilter::OnFilterAdded(IPC::Sender* sender) { io_task_runner_ = base::ThreadTaskRunnerHandle::Get(); sender_ = sender; @@ -152,9 +165,11 @@ return; const WebInputEvent* event = base::get<0>(params); ui::LatencyInfo latency_info = base::get<1>(params); + InputEventDispatchType dispatch_type = base::get<2>(params); DCHECK(event); + DCHECK_EQ(DISPATCH_TYPE_NORMAL, dispatch_type); - const bool send_ack = WebInputEventTraits::WillReceiveAckFromRenderer(*event); + bool send_ack = WebInputEventTraits::WillReceiveAckFromRenderer(*event); // Intercept |DidOverscroll| notifications, bundling any triggered overscroll // response with the input event ack. @@ -165,16 +180,21 @@ InputEventAckState ack_state = handler_.Run(routing_id, event, &latency_info); - if (ack_state == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) { + if (ack_state == INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING) { + DCHECK(!overscroll_params); + RouteQueueMap::iterator iter = route_queues_.find(routing_id); + if (iter != route_queues_.end()) + iter->second->HandleEvent(event, latency_info); + } else if (ack_state == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) { DCHECK(!overscroll_params); TRACE_EVENT_INSTANT0( - "input", - "InputEventFilter::ForwardToHandler::ForwardToMainListener", + "input", "InputEventFilter::ForwardToHandler::ForwardToMainListener", TRACE_EVENT_SCOPE_THREAD); IPC::Message new_msg = - InputMsg_HandleInputEvent(routing_id, event, latency_info); + InputMsg_HandleInputEvent(routing_id, event, latency_info, + InputEventDispatchType::DISPATCH_TYPE_NORMAL); main_task_runner_->PostTask(FROM_HERE, base::Bind(main_listener_, new_msg)); - return; + send_ack = false; } if (!send_ack) @@ -204,4 +224,15 @@ sender_->Send(message.release()); } +void InputEventFilter::SendNonBlockingEvent(int routing_id, + const blink::WebInputEvent* event, + const ui::LatencyInfo& latency) { + TRACE_EVENT_INSTANT0("input", "InputEventFilter::SendNonBlockingEvent", + TRACE_EVENT_SCOPE_THREAD); + IPC::Message new_msg = InputMsg_HandleInputEvent( + routing_id, event, latency, + InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING); + main_task_runner_->PostTask(FROM_HERE, base::Bind(main_listener_, new_msg)); +} + } // namespace content
diff --git a/content/renderer/input/input_event_filter.h b/content/renderer/input/input_event_filter.h index e2d18f39..71d22f76 100644 --- a/content/renderer/input/input_event_filter.h +++ b/content/renderer/input/input_event_filter.h
@@ -7,12 +7,14 @@ #include <queue> #include <set> +#include <unordered_map> #include "base/callback.h" #include "base/synchronization/lock.h" #include "content/common/content_export.h" #include "content/common/input/input_event_ack_state.h" #include "content/renderer/input/input_handler_manager_client.h" +#include "content/renderer/input/non_blocking_event_queue.h" #include "ipc/message_filter.h" #include "third_party/WebKit/public/web/WebInputEvent.h" @@ -40,7 +42,8 @@ namespace content { class CONTENT_EXPORT InputEventFilter : public InputHandlerManagerClient, - public IPC::MessageFilter { + public IPC::MessageFilter, + public NonBlockingEventQueueClient { public: InputEventFilter( const base::Callback<void(const IPC::Message&)>& main_listener, @@ -66,6 +69,8 @@ void DidOverscroll(int routing_id, const DidOverscrollParams& params) override; void DidStopFlinging(int routing_id) override; + void NonBlockingInputEventHandled(int routing_id, + blink::WebInputEvent::Type type) override; // IPC::MessageFilter methods: void OnFilterAdded(IPC::Sender* sender) override; @@ -73,6 +78,11 @@ void OnChannelClosing() override; bool OnMessageReceived(const IPC::Message& message) override; + // NonBlockingEventQueueClient methods: + void SendNonBlockingEvent(int routing_id, + const blink::WebInputEvent* event, + const ui::LatencyInfo& latency) override; + private: ~InputEventFilter() override; @@ -97,6 +107,10 @@ // Indicates the routing_ids for which input events should be filtered. std::set<int> routes_; + using RouteQueueMap = + std::unordered_map<int, scoped_ptr<NonBlockingEventQueue>>; + RouteQueueMap route_queues_; + // Used to intercept overscroll notifications while an event is being // dispatched. If the event causes overscroll, the overscroll metadata can be // bundled in the event ack, saving an IPC. Note that we must continue
diff --git a/content/renderer/input/input_event_filter_unittest.cc b/content/renderer/input/input_event_filter_unittest.cc index 1aace83d..8027752a 100644 --- a/content/renderer/input/input_event_filter_unittest.cc +++ b/content/renderer/input/input_event_filter_unittest.cc
@@ -23,6 +23,8 @@ using blink::WebInputEvent; using blink::WebMouseEvent; +using blink::WebMouseWheelEvent; +using blink::WebTouchEvent; namespace content { namespace { @@ -34,12 +36,13 @@ InputEventRecorder() : filter_(NULL), handle_events_(false), - send_to_widget_(false) { - } + send_to_widget_(false), + passive_(false) {} void set_filter(InputEventFilter* filter) { filter_ = filter; } void set_handle_events(bool value) { handle_events_ = value; } void set_send_to_widget(bool value) { send_to_widget_ = value; } + void set_passive(bool value) { passive_ = value; } size_t record_count() const { return records_.size(); } @@ -61,9 +64,13 @@ if (handle_events_) { return INPUT_EVENT_ACK_STATE_CONSUMED; + } else if (send_to_widget_) { + if (passive_) + return INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING; + else + return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; } else { - return send_to_widget_ ? INPUT_EVENT_ACK_STATE_NOT_CONSUMED - : INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; + return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; } } @@ -79,6 +86,7 @@ InputEventFilter* filter_; bool handle_events_; bool send_to_widget_; + bool passive_; std::vector<Record> records_; }; @@ -112,13 +120,15 @@ base::MessageLoop::current()->RunUntilIdle(); } +template <typename T> void AddEventsToFilter(IPC::MessageFilter* message_filter, - const WebMouseEvent events[], + const T events[], size_t count) { std::vector<IPC::Message> messages; for (size_t i = 0; i < count; ++i) { - messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID, &events[i], - ui::LatencyInfo())); + messages.push_back(InputMsg_HandleInputEvent( + kTestRoutingID, &events[i], ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); } AddMessagesToFilter(message_filter, messages); @@ -254,8 +264,9 @@ SyntheticWebMouseEventBuilder::Build(WebMouseEvent::MouseUp); std::vector<IPC::Message> messages; - messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID, &mouse_down, - ui::LatencyInfo())); + messages.push_back( + InputMsg_HandleInputEvent(kTestRoutingID, &mouse_down, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); // Control where input events are delivered. messages.push_back(InputMsg_MouseCaptureLost(kTestRoutingID)); messages.push_back(InputMsg_SetFocus(kTestRoutingID, true)); @@ -282,7 +293,8 @@ messages.push_back(InputMsg_MoveCaret(kTestRoutingID, gfx::Point())); messages.push_back( - InputMsg_HandleInputEvent(kTestRoutingID, &mouse_up, ui::LatencyInfo())); + InputMsg_HandleInputEvent(kTestRoutingID, &mouse_up, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL)); AddMessagesToFilter(filter_.get(), messages); // We should have sent all messages back to the main thread and preserved @@ -293,4 +305,156 @@ } } +TEST_F(InputEventFilterTest, NonBlockingWheel) { + WebMouseWheelEvent kEvents[4] = { + SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, 53, 0, false), + SyntheticWebMouseWheelEventBuilder::Build(20, 20, 0, 53, 0, false), + SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false), + SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false), + }; + + filter_->DidAddInputHandler(kTestRoutingID, nullptr); + event_recorder_.set_send_to_widget(true); + event_recorder_.set_passive(true); + + AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents)); + EXPECT_EQ(arraysize(kEvents), event_recorder_.record_count()); + ASSERT_EQ(4u, ipc_sink_.message_count()); + + // First event is sent right away. + EXPECT_EQ(1u, message_recorder_.message_count()); + + // Second event was queued; ack the first. + filter_->NonBlockingInputEventHandled(kTestRoutingID, + WebInputEvent::MouseWheel); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(4u, ipc_sink_.message_count()); + EXPECT_EQ(2u, message_recorder_.message_count()); + + // Third event won't be coalesced into the second because modifiers are + // different. + filter_->NonBlockingInputEventHandled(kTestRoutingID, + WebInputEvent::MouseWheel); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_EQ(3u, message_recorder_.message_count()); + + // The last events will be coalesced. + filter_->NonBlockingInputEventHandled(kTestRoutingID, + WebInputEvent::MouseWheel); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_EQ(3u, message_recorder_.message_count()); + + // First two messages should be identical. + for (size_t i = 0; i < 2; ++i) { + const IPC::Message& message = message_recorder_.message_at(i); + + ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type()); + InputMsg_HandleInputEvent::Param params; + EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, ¶ms)); + const WebInputEvent* event = base::get<0>(params); + InputEventDispatchType dispatch_type = base::get<2>(params); + + EXPECT_EQ(kEvents[i].size, event->size); + EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0); + EXPECT_EQ(InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING, + dispatch_type); + } + + // Third message is coalesced. + { + const IPC::Message& message = message_recorder_.message_at(2); + + ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type()); + InputMsg_HandleInputEvent::Param params; + EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, ¶ms)); + const WebMouseWheelEvent* event = + static_cast<const WebMouseWheelEvent*>(base::get<0>(params)); + InputEventDispatchType dispatch_type = base::get<2>(params); + + EXPECT_EQ(kEvents[2].size, event->size); + EXPECT_EQ(kEvents[2].deltaX + kEvents[3].deltaX, event->deltaX); + EXPECT_EQ(kEvents[2].deltaY + kEvents[3].deltaY, event->deltaY); + EXPECT_EQ(InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING, + dispatch_type); + } +} + +TEST_F(InputEventFilterTest, NonBlockingTouch) { + SyntheticWebTouchEvent kEvents[4]; + kEvents[0].PressPoint(10, 10); + kEvents[1].PressPoint(10, 10); + kEvents[1].modifiers = 1; + kEvents[1].MovePoint(0, 20, 20); + kEvents[2].PressPoint(10, 10); + kEvents[2].MovePoint(0, 30, 30); + kEvents[3].PressPoint(10, 10); + kEvents[3].MovePoint(0, 35, 35); + + filter_->DidAddInputHandler(kTestRoutingID, nullptr); + event_recorder_.set_send_to_widget(true); + event_recorder_.set_passive(true); + + AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents)); + EXPECT_EQ(arraysize(kEvents), event_recorder_.record_count()); + ASSERT_EQ(4u, ipc_sink_.message_count()); + + // First event is sent right away. + EXPECT_EQ(1u, message_recorder_.message_count()); + + // Second event was queued; ack the first. + filter_->NonBlockingInputEventHandled(kTestRoutingID, + WebInputEvent::TouchStart); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(4u, ipc_sink_.message_count()); + EXPECT_EQ(2u, message_recorder_.message_count()); + + // Third event won't be coalesced into the second because modifiers are + // different. + filter_->NonBlockingInputEventHandled(kTestRoutingID, + WebInputEvent::TouchMove); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_EQ(3u, message_recorder_.message_count()); + + // The last events will be coalesced. + filter_->NonBlockingInputEventHandled(kTestRoutingID, + WebInputEvent::TouchMove); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_EQ(3u, message_recorder_.message_count()); + + // First two messages should be identical. + for (size_t i = 0; i < 2; ++i) { + const IPC::Message& message = message_recorder_.message_at(i); + + ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type()); + InputMsg_HandleInputEvent::Param params; + EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, ¶ms)); + const WebInputEvent* event = base::get<0>(params); + InputEventDispatchType dispatch_type = base::get<2>(params); + + EXPECT_EQ(kEvents[i].size, event->size); + EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0); + EXPECT_EQ(InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING, + dispatch_type); + } + + // Third message is coalesced. + { + const IPC::Message& message = message_recorder_.message_at(2); + + ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type()); + InputMsg_HandleInputEvent::Param params; + EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, ¶ms)); + const WebTouchEvent* event = + static_cast<const WebTouchEvent*>(base::get<0>(params)); + InputEventDispatchType dispatch_type = base::get<2>(params); + + EXPECT_EQ(kEvents[3].size, event->size); + EXPECT_EQ(1u, kEvents[3].touchesLength); + EXPECT_EQ(kEvents[3].touches[0].position.x, event->touches[0].position.x); + EXPECT_EQ(kEvents[3].touches[0].position.y, event->touches[0].position.y); + EXPECT_EQ(InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING, + dispatch_type); + } +} + } // namespace content
diff --git a/content/renderer/input/input_handler_manager.cc b/content/renderer/input/input_handler_manager.cc index 35ef3b4..58fd95f 100644 --- a/content/renderer/input/input_handler_manager.cc +++ b/content/renderer/input/input_handler_manager.cc
@@ -36,6 +36,8 @@ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; case InputHandlerProxy::DROP_EVENT: return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; + case InputHandlerProxy::DID_HANDLE_NON_BLOCKING: + return INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING; } NOTREACHED(); return INPUT_EVENT_ACK_STATE_UNKNOWN; @@ -63,20 +65,20 @@ int routing_id, const base::WeakPtr<cc::InputHandler>& input_handler, const base::WeakPtr<RenderViewImpl>& render_view_impl, - bool enable_smooth_scrolling) { + bool enable_smooth_scrolling, + bool enable_wheel_gestures) { if (task_runner_->BelongsToCurrentThread()) { - AddInputHandlerOnCompositorThread(routing_id, - base::ThreadTaskRunnerHandle::Get(), - input_handler, render_view_impl, - enable_smooth_scrolling); + AddInputHandlerOnCompositorThread( + routing_id, base::ThreadTaskRunnerHandle::Get(), input_handler, + render_view_impl, enable_smooth_scrolling, enable_wheel_gestures); } else { task_runner_->PostTask( FROM_HERE, base::Bind(&InputHandlerManager::AddInputHandlerOnCompositorThread, base::Unretained(this), routing_id, base::ThreadTaskRunnerHandle::Get(), input_handler, - render_view_impl, - enable_smooth_scrolling)); + render_view_impl, enable_smooth_scrolling, + enable_wheel_gestures)); } } @@ -85,7 +87,8 @@ const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner, const base::WeakPtr<cc::InputHandler>& input_handler, const base::WeakPtr<RenderViewImpl>& render_view_impl, - bool enable_smooth_scrolling) { + bool enable_smooth_scrolling, + bool enable_wheel_gestures) { DCHECK(task_runner_->BelongsToCurrentThread()); // The handler could be gone by this point if the compositor has shut down. @@ -101,7 +104,7 @@ "result", "AddingRoute"); scoped_ptr<InputHandlerWrapper> wrapper(new InputHandlerWrapper( this, routing_id, main_task_runner, input_handler, render_view_impl, - enable_smooth_scrolling)); + enable_smooth_scrolling, enable_wheel_gestures)); client_->DidAddInputHandler(routing_id, wrapper->input_handler_proxy()); input_handlers_.add(routing_id, std::move(wrapper)); } @@ -141,6 +144,27 @@ wheel_event, scroll_result); } +void InputHandlerManager::NonBlockingInputEventHandledOnMainThread( + int routing_id, + blink::WebInputEvent::Type type) { + task_runner_->PostTask( + FROM_HERE, + base::Bind( + &InputHandlerManager::NonBlockingInputEventHandledOnCompositorThread, + base::Unretained(this), routing_id, type)); +} + +void InputHandlerManager::NonBlockingInputEventHandledOnCompositorThread( + int routing_id, + blink::WebInputEvent::Type handled_type) { + DCHECK(task_runner_->BelongsToCurrentThread()); + auto it = input_handlers_.find(routing_id); + if (it == input_handlers_.end()) + return; + + client_->NonBlockingInputEventHandled(routing_id, handled_type); +} + InputEventAckState InputHandlerManager::HandleInputEvent( int routing_id, const WebInputEvent* input_event,
diff --git a/content/renderer/input/input_handler_manager.h b/content/renderer/input/input_handler_manager.h index 6905a88..c8bfb752 100644 --- a/content/renderer/input/input_handler_manager.h +++ b/content/renderer/input/input_handler_manager.h
@@ -50,17 +50,20 @@ ~InputHandlerManager(); // Callable from the main thread only. - void AddInputHandler( - int routing_id, - const base::WeakPtr<cc::InputHandler>& input_handler, - const base::WeakPtr<RenderViewImpl>& render_view_impl, - bool enable_smooth_scrolling); + void AddInputHandler(int routing_id, + const base::WeakPtr<cc::InputHandler>& input_handler, + const base::WeakPtr<RenderViewImpl>& render_view_impl, + bool enable_smooth_scrolling, + bool enable_wheel_gestures); void ObserveWheelEventAndResultOnMainThread( int routing_id, const blink::WebMouseWheelEvent& wheel_event, const cc::InputHandlerScrollResult& scroll_result); + void NonBlockingInputEventHandledOnMainThread(int routing_id, + blink::WebInputEvent::Type); + // Callback only from the compositor's thread. void RemoveInputHandler(int routing_id); @@ -85,13 +88,18 @@ const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner, const base::WeakPtr<cc::InputHandler>& input_handler, const base::WeakPtr<RenderViewImpl>& render_view_impl, - bool enable_smooth_scrolling); + bool enable_smooth_scrolling, + bool enable_wheel_gestures); void ObserveWheelEventAndResultOnCompositorThread( int routing_id, const blink::WebMouseWheelEvent& wheel_event, const cc::InputHandlerScrollResult& scroll_result); + void NonBlockingInputEventHandledOnCompositorThread( + int routing_id, + blink::WebInputEvent::Type); + typedef base::ScopedPtrHashMap<int, // routing_id scoped_ptr<InputHandlerWrapper>> InputHandlerMap;
diff --git a/content/renderer/input/input_handler_manager_client.h b/content/renderer/input/input_handler_manager_client.h index cf4fe00..c794bd2 100644 --- a/content/renderer/input/input_handler_manager_client.h +++ b/content/renderer/input/input_handler_manager_client.h
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "content/common/content_export.h" #include "content/common/input/input_event_ack_state.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/gfx/geometry/vector2d_f.h" namespace ui { @@ -20,10 +21,6 @@ class InputHandler; } -namespace blink { -class WebInputEvent; -} - namespace ui { class SynchronousInputHandlerProxy; } @@ -54,6 +51,9 @@ virtual void DidOverscroll(int routing_id, const DidOverscrollParams& params) = 0; virtual void DidStopFlinging(int routing_id) = 0; + virtual void NonBlockingInputEventHandled( + int routing_id, + blink::WebInputEvent::Type type) = 0; protected: InputHandlerManagerClient() {}
diff --git a/content/renderer/input/input_handler_wrapper.cc b/content/renderer/input/input_handler_wrapper.cc index 6265744..c8280e4 100644 --- a/content/renderer/input/input_handler_wrapper.cc +++ b/content/renderer/input/input_handler_wrapper.cc
@@ -20,7 +20,8 @@ const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner, const base::WeakPtr<cc::InputHandler>& input_handler, const base::WeakPtr<RenderViewImpl>& render_view_impl, - bool enable_smooth_scrolling) + bool enable_smooth_scrolling, + bool enable_wheel_gestures) : input_handler_manager_(input_handler_manager), routing_id_(routing_id), input_handler_proxy_(input_handler.get(), this), @@ -28,6 +29,8 @@ render_view_impl_(render_view_impl) { DCHECK(input_handler); input_handler_proxy_.set_smooth_scroll_enabled(enable_smooth_scrolling); + input_handler_proxy_.set_use_gesture_events_for_mouse_wheel( + enable_wheel_gestures); } InputHandlerWrapper::~InputHandlerWrapper() {
diff --git a/content/renderer/input/input_handler_wrapper.h b/content/renderer/input/input_handler_wrapper.h index bc864d9..d3781315 100644 --- a/content/renderer/input/input_handler_wrapper.h +++ b/content/renderer/input/input_handler_wrapper.h
@@ -28,7 +28,8 @@ const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner, const base::WeakPtr<cc::InputHandler>& input_handler, const base::WeakPtr<RenderViewImpl>& render_view_impl, - bool enable_smooth_scrolling); + bool enable_smooth_scrolling, + bool enable_wheel_gestures); ~InputHandlerWrapper() override; int routing_id() const { return routing_id_; }
diff --git a/content/renderer/input/non_blocking_event_queue.cc b/content/renderer/input/non_blocking_event_queue.cc new file mode 100644 index 0000000..4fc5d11 --- /dev/null +++ b/content/renderer/input/non_blocking_event_queue.cc
@@ -0,0 +1,60 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/input/non_blocking_event_queue.h" + +namespace content { + +NonBlockingEventQueue::NonBlockingEventQueue( + int routing_id, + NonBlockingEventQueueClient* client) + : routing_id_(routing_id), client_(client) {} + +NonBlockingEventQueue::~NonBlockingEventQueue() {} + +void NonBlockingEventQueue::HandleEvent(const blink::WebInputEvent* event, + const ui::LatencyInfo& latency) { + if (event->type == blink::WebInputEvent::MouseWheel) { + if (wheel_events_.state() == WebInputEventQueueState::ITEM_PENDING) { + wheel_events_.Queue(MouseWheelEventWithLatencyInfo( + *static_cast<const blink::WebMouseWheelEvent*>(event), latency)); + } else { + wheel_events_.set_state(WebInputEventQueueState::ITEM_PENDING); + client_->SendNonBlockingEvent(routing_id_, event, latency); + } + } else if (blink::WebInputEvent::isTouchEventType(event->type)) { + if (touch_events_.state() == WebInputEventQueueState::ITEM_PENDING) { + touch_events_.Queue(TouchEventWithLatencyInfo( + *static_cast<const blink::WebTouchEvent*>(event), latency)); + } else { + touch_events_.set_state(WebInputEventQueueState::ITEM_PENDING); + client_->SendNonBlockingEvent(routing_id_, event, latency); + } + } else { + NOTREACHED() << "Invalid passive event type"; + } +} + +void NonBlockingEventQueue::EventHandled(blink::WebInputEvent::Type type) { + if (type == blink::WebInputEvent::MouseWheel) { + if (!wheel_events_.empty()) { + scoped_ptr<MouseWheelEventWithLatencyInfo> event = wheel_events_.Pop(); + + client_->SendNonBlockingEvent(routing_id_, &event->event, event->latency); + } else { + wheel_events_.set_state(WebInputEventQueueState::ITEM_NOT_PENDING); + } + } else if (blink::WebInputEvent::isTouchEventType(type)) { + if (!touch_events_.empty()) { + scoped_ptr<TouchEventWithLatencyInfo> event = touch_events_.Pop(); + client_->SendNonBlockingEvent(routing_id_, &event->event, event->latency); + } else { + touch_events_.set_state(WebInputEventQueueState::ITEM_NOT_PENDING); + } + } else { + NOTREACHED() << "Invalid passive event type"; + } +} + +} // namespace content
diff --git a/content/renderer/input/non_blocking_event_queue.h b/content/renderer/input/non_blocking_event_queue.h new file mode 100644 index 0000000..14b99fd --- /dev/null +++ b/content/renderer/input/non_blocking_event_queue.h
@@ -0,0 +1,62 @@ +// 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_RENDERER_INPUT_NON_BLOCKING_EVENT_QUEUE_H_ +#define CONTENT_RENDERER_INPUT_NON_BLOCKING_EVENT_QUEUE_H_ + +#include <deque> +#include "content/common/content_export.h" +#include "content/common/input/event_with_latency_info.h" +#include "content/common/input/web_input_event_queue.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "ui/events/latency_info.h" + +namespace content { + +class CONTENT_EXPORT NonBlockingEventQueueClient { + public: + // Send an |event| that was previously queued (possibly + // coalesced with another event) to the |routing_id|'s + // channel. Implementors must implement this callback. + virtual void SendNonBlockingEvent(int routing_id, + const blink::WebInputEvent* event, + const ui::LatencyInfo& latency) = 0; +}; + +// NonBlockingEventQueue implements a series of queues (one touch +// and one mouse wheel) for events that need to be queued between +// the compositor and main threads. When a non-blocking event is sent +// from the compositor to main it can either be sent directly if no +// outstanding events of that type are in flight; or it needs to +// wait in a queue until the main thread has finished processing +// the in-flight event. This class tracks the state and queues +// for the event types. Methods on this class should only be called +// from the compositor thread. +// +class CONTENT_EXPORT NonBlockingEventQueue { + public: + NonBlockingEventQueue(int routing_id, NonBlockingEventQueueClient* client); + ~NonBlockingEventQueue(); + + // Called once compositor has handled |event| and indicated that it is + // a non-blocking event to be queued to the main thread. + void HandleEvent(const blink::WebInputEvent* event, + const ui::LatencyInfo& latency); + + // Call once main thread has handled outstanding |type| event in flight. + void EventHandled(blink::WebInputEvent::Type type); + + private: + friend class NonBlockingEventQueueTest; + int routing_id_; + NonBlockingEventQueueClient* client_; + WebInputEventQueue<MouseWheelEventWithLatencyInfo> wheel_events_; + WebInputEventQueue<TouchEventWithLatencyInfo> touch_events_; + + DISALLOW_COPY_AND_ASSIGN(NonBlockingEventQueue); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_INPUT_NON_BLOCKING_EVENT_QUEUE_H_
diff --git a/content/renderer/input/non_blocking_event_queue_unittest.cc b/content/renderer/input/non_blocking_event_queue_unittest.cc new file mode 100644 index 0000000..8953d858 --- /dev/null +++ b/content/renderer/input/non_blocking_event_queue_unittest.cc
@@ -0,0 +1,120 @@ +// 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 <stddef.h> + +#include <new> +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "build/build_config.h" +#include "content/common/input/synthetic_web_input_event_builders.h" +#include "content/renderer/input/non_blocking_event_queue.h" +#include "testing/gtest/include/gtest/gtest.h" + +using blink::WebInputEvent; +using blink::WebMouseEvent; +using blink::WebMouseWheelEvent; +using blink::WebTouchEvent; + +namespace content { +namespace { + +const int kTestRoutingID = 13; +} + +class NonBlockingEventQueueTest : public testing::Test, + public NonBlockingEventQueueClient { + public: + NonBlockingEventQueueTest() : queue_(kTestRoutingID, this) {} + + void SendNonBlockingEvent(int routing_id, + const blink::WebInputEvent* event, + const ui::LatencyInfo& latency) override { + ASSERT_EQ(kTestRoutingID, routing_id); + const unsigned char* eventPtr = + reinterpret_cast<const unsigned char*>(event); + last_event_.assign(eventPtr, eventPtr + event->size); + } + + WebInputEventQueue<MouseWheelEventWithLatencyInfo>& wheel_event_queue() { + return queue_.wheel_events_; + } + + WebInputEventQueue<TouchEventWithLatencyInfo>& touch_event_queue() { + return queue_.touch_events_; + } + + protected: + NonBlockingEventQueue queue_; + std::vector<unsigned char> last_event_; +}; + +TEST_F(NonBlockingEventQueueTest, NonBlockingWheel) { + WebMouseWheelEvent kEvents[4] = { + SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, 53, 0, false), + SyntheticWebMouseWheelEventBuilder::Build(20, 20, 0, 53, 0, false), + SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false), + SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false), + }; + + ASSERT_EQ(WebInputEventQueueState::ITEM_NOT_PENDING, + wheel_event_queue().state()); + queue_.HandleEvent(&kEvents[0], ui::LatencyInfo()); + ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, wheel_event_queue().state()); + queue_.HandleEvent(&kEvents[1], ui::LatencyInfo()); + ASSERT_EQ(kEvents[0].size, last_event_.size()); + ASSERT_TRUE(memcmp(&last_event_[0], &kEvents[0], kEvents[0].size) == 0); + queue_.EventHandled(blink::WebInputEvent::MouseWheel); + ASSERT_EQ(kEvents[1].size, last_event_.size()); + ASSERT_TRUE(memcmp(&last_event_[0], &kEvents[1], kEvents[1].size) == 0); + ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, wheel_event_queue().state()); + queue_.EventHandled(blink::WebInputEvent::MouseWheel); + ASSERT_EQ(WebInputEventQueueState::ITEM_NOT_PENDING, + wheel_event_queue().state()); + + // Ensure that coalescing takes place. + queue_.HandleEvent(&kEvents[0], ui::LatencyInfo()); + queue_.HandleEvent(&kEvents[2], ui::LatencyInfo()); + queue_.HandleEvent(&kEvents[3], ui::LatencyInfo()); + ASSERT_EQ(1u, wheel_event_queue().size()); + ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, wheel_event_queue().state()); +} + +TEST_F(NonBlockingEventQueueTest, NonBlockingTouch) { + SyntheticWebTouchEvent kEvents[4]; + kEvents[0].PressPoint(10, 10); + kEvents[1].PressPoint(10, 10); + kEvents[1].modifiers = 1; + kEvents[1].MovePoint(0, 20, 20); + kEvents[2].PressPoint(10, 10); + kEvents[2].MovePoint(0, 30, 30); + kEvents[3].PressPoint(10, 10); + kEvents[3].MovePoint(0, 35, 35); + + ASSERT_EQ(WebInputEventQueueState::ITEM_NOT_PENDING, + touch_event_queue().state()); + queue_.HandleEvent(&kEvents[0], ui::LatencyInfo()); + ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, touch_event_queue().state()); + queue_.HandleEvent(&kEvents[1], ui::LatencyInfo()); + ASSERT_EQ(kEvents[0].size, last_event_.size()); + ASSERT_TRUE(memcmp(&last_event_[0], &kEvents[0], kEvents[0].size) == 0); + queue_.EventHandled(blink::WebInputEvent::TouchStart); + ASSERT_EQ(kEvents[1].size, last_event_.size()); + ASSERT_TRUE(memcmp(&last_event_[0], &kEvents[1], kEvents[1].size) == 0); + ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, touch_event_queue().state()); + queue_.EventHandled(blink::WebInputEvent::TouchMove); + ASSERT_EQ(WebInputEventQueueState::ITEM_NOT_PENDING, + touch_event_queue().state()); + + // Ensure that coalescing takes place. + queue_.HandleEvent(&kEvents[0], ui::LatencyInfo()); + queue_.HandleEvent(&kEvents[2], ui::LatencyInfo()); + queue_.HandleEvent(&kEvents[3], ui::LatencyInfo()); + ASSERT_EQ(1u, touch_event_queue().size()); + ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, touch_event_queue().state()); +} + +} // namespace content
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc index 54f2b9c..385d388e 100644 --- a/content/renderer/input/render_widget_input_handler.cc +++ b/content/renderer/input/render_widget_input_handler.cc
@@ -131,7 +131,7 @@ } void LogPassiveEventListenersUma(WebInputEventResult result, - bool passive, + bool non_blocking, bool cancelable, double event_timestamp, const ui::LatencyInfo& latency_info) { @@ -145,7 +145,7 @@ }; int enum_value; - if (passive) + if (non_blocking) enum_value = PASSIVE_LISTENER_UMA_ENUM_PASSIVE; else if (!cancelable) enum_value = PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE; @@ -191,9 +191,8 @@ void RenderWidgetInputHandler::HandleInputEvent( const WebInputEvent& input_event, - const ui::LatencyInfo& latency_info) { - // TODO(dtapuska): Passive support not implemented yet crbug.com/489802 - bool passive = false; + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type) { base::AutoReset<bool> handling_input_event_resetter(&handling_input_event_, true); base::AutoReset<WebInputEvent::Type> handling_event_type_resetter( @@ -314,6 +313,8 @@ processed = widget_->webwidget()->handleInputEvent(input_event); } + bool non_blocking = + dispatch_type == InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING; // TODO(dtapuska): Use the input_event.timeStampSeconds as the start // ideally this should be when the event was sent by the compositor to the // renderer. crbug.com/565348 @@ -321,11 +322,11 @@ input_event.type == WebInputEvent::TouchMove || input_event.type == WebInputEvent::TouchEnd) { LogPassiveEventListenersUma( - processed, passive, + processed, non_blocking, static_cast<const WebTouchEvent&>(input_event).cancelable, input_event.timeStampSeconds, latency_info); } else if (input_event.type == WebInputEvent::MouseWheel) { - LogPassiveEventListenersUma(processed, passive, !passive, + LogPassiveEventListenersUma(processed, non_blocking, !non_blocking, input_event.timeStampSeconds, latency_info); } @@ -362,11 +363,15 @@ // Send mouse wheel events and their disposition to the compositor thread, so // that they can be used to produce the elastic overscroll effect on Mac. if (input_event.type == WebInputEvent::MouseWheel) { - delegate_->ObserveWheelEventAndResult( - static_cast<const WebMouseWheelEvent&>(input_event), - event_overscroll ? event_overscroll->latest_overscroll_delta - : gfx::Vector2dF(), - processed != WebInputEventResult::NotHandled); + const WebMouseWheelEvent& wheel_event = + static_cast<const WebMouseWheelEvent&>(input_event); + if (wheel_event.canScroll) { + delegate_->ObserveWheelEventAndResult( + wheel_event, + event_overscroll ? event_overscroll->latest_overscroll_delta + : gfx::Vector2dF(), + processed != WebInputEventResult::NotHandled); + } } bool frame_pending = @@ -390,7 +395,12 @@ // by reentrant calls for events after the paused one. bool no_ack = ignore_ack_for_mouse_move_from_debugger_ && input_event.type == WebInputEvent::MouseMove; - if (WebInputEventTraits::WillReceiveAckFromRenderer(input_event) && !no_ack) { + if (non_blocking) { + // |non_blocking| means it was ack'd already by the InputHandlerProxy + // so let the delegate know the event has been handled. + delegate_->NonBlockingInputEventHandled(input_event.type); + } else if (WebInputEventTraits::WillReceiveAckFromRenderer(input_event) && + !no_ack) { scoped_ptr<InputEventAck> response(new InputEventAck( input_event.type, ack_result, swap_latency_info, std::move(event_overscroll),
diff --git a/content/renderer/input/render_widget_input_handler.h b/content/renderer/input/render_widget_input_handler.h index f8ff1d3..3be968cb 100644 --- a/content/renderer/input/render_widget_input_handler.h +++ b/content/renderer/input/render_widget_input_handler.h
@@ -10,6 +10,7 @@ #include "base/time/time.h" #include "content/common/input/did_overscroll_params.h" #include "content/common/input/input_event_ack.h" +#include "content/common/input/input_event_dispatch_type.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/base/ui_base_types.h" @@ -37,7 +38,8 @@ // Handle input events from the input event provider. void HandleInputEvent(const blink::WebInputEvent& input_event, - const ui::LatencyInfo& latency_info); + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type); // Handle overscroll from Blink. void DidOverscrollFromBlink(
diff --git a/content/renderer/input/render_widget_input_handler_delegate.h b/content/renderer/input/render_widget_input_handler_delegate.h index d00a080..7a7f544 100644 --- a/content/renderer/input/render_widget_input_handler_delegate.h +++ b/content/renderer/input/render_widget_input_handler_delegate.h
@@ -58,6 +58,11 @@ // Called when an ACK is ready to be sent to the input event provider. virtual void OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) = 0; + // Called when a non-blocking event (DISPATCH_TYPE_NON_BLOCKING) of + // |handled_type| has been processed by the main thread. + virtual void NonBlockingInputEventHandled( + blink::WebInputEvent::Type handled_type) = 0; + // Notifies the delegate of the |input_handler| managing it. virtual void SetInputHandler(RenderWidgetInputHandler* input_handler) = 0;
diff --git a/content/renderer/media/audio_renderer_mixer_manager.cc b/content/renderer/media/audio_renderer_mixer_manager.cc index 384eca2e..23b67a6 100644 --- a/content/renderer/media/audio_renderer_mixer_manager.cc +++ b/content/renderer/media/audio_renderer_mixer_manager.cc
@@ -84,14 +84,13 @@ return nullptr; } - // On ChromeOS and Android, as well as when a fake device is used, we can rely - // on the playback device to handle resampling, so don't waste cycles on it - // here. + // On ChromeOS as well as when a fake device is used, we can rely on the + // playback device to handle resampling, so don't waste cycles on it here. int sample_rate = params.sample_rate(); int buffer_size = media::AudioHardwareConfig::GetHighLatencyBufferSize(sample_rate, 0); -#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) +#if !defined(OS_CHROMEOS) media::AudioParameters hardware_params = sink->GetOutputDevice()->GetOutputParameters();
diff --git a/content/renderer/media/media_permission_dispatcher.cc b/content/renderer/media/media_permission_dispatcher.cc index 89baa3f..ab6d7ec 100644 --- a/content/renderer/media/media_permission_dispatcher.cc +++ b/content/renderer/media/media_permission_dispatcher.cc
@@ -9,7 +9,6 @@ #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "media/base/bind_to_current_loop.h" -#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" #include "url/gurl.h" namespace { @@ -99,7 +98,6 @@ permission_service_->RequestPermission( MediaPermissionTypeToPermissionName(type), security_origin.spec(), - blink::WebUserGestureIndicator::isProcessingUserGesture(), base::Bind(&MediaPermissionDispatcher::OnPermissionStatus, weak_ptr_, request_id)); }
diff --git a/content/renderer/media/media_recorder_handler.cc b/content/renderer/media/media_recorder_handler.cc index e28306aa..e43502e4 100644 --- a/content/renderer/media/media_recorder_handler.cc +++ b/content/renderer/media/media_recorder_handler.cc
@@ -22,7 +22,7 @@ #include "media/base/bind_to_current_loop.h" #include "media/base/mime_util.h" #include "media/base/video_frame.h" -#include "media/capture/webm_muxer.h" +#include "media/muxers/webm_muxer.h" #include "third_party/WebKit/public/platform/WebMediaRecorderHandlerClient.h" #include "third_party/WebKit/public/platform/WebString.h"
diff --git a/content/renderer/media/midi_dispatcher.cc b/content/renderer/media/midi_dispatcher.cc index b5aa220..4cb43e4 100644 --- a/content/renderer/media/midi_dispatcher.cc +++ b/content/renderer/media/midi_dispatcher.cc
@@ -9,7 +9,6 @@ #include "content/public/renderer/render_frame.h" #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/platform/WebString.h" -#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" #include "third_party/WebKit/public/web/modules/webmidi/WebMIDIOptions.h" #include "third_party/WebKit/public/web/modules/webmidi/WebMIDIPermissionRequest.h" @@ -42,7 +41,6 @@ permission_service_->RequestPermission( permission_name, request.securityOrigin().toString().utf8(), - blink::WebUserGestureIndicator::isProcessingUserGesture(), base::Bind(&MidiDispatcher::OnPermissionSet, base::Unretained(this), permission_request_id)); }
diff --git a/content/renderer/media/peer_connection_tracker.cc b/content/renderer/media/peer_connection_tracker.cc index 787315f..130f077c 100644 --- a/content/renderer/media/peer_connection_tracker.cc +++ b/content/renderer/media/peer_connection_tracker.cc
@@ -10,7 +10,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/thread_task_runner_handle.h" #include "content/common/media/peer_connection_tracker_messages.h" -#include "content/renderer/media/rtc_media_constraints.h" #include "content/renderer/media/rtc_peer_connection_handler.h" #include "content/renderer/render_thread_impl.h" #include "third_party/WebKit/public/platform/WebMediaConstraints.h" @@ -18,12 +17,12 @@ #include "third_party/WebKit/public/platform/WebMediaStreamSource.h" #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" #include "third_party/WebKit/public/platform/WebRTCICECandidate.h" +#include "third_party/WebKit/public/platform/WebRTCOfferOptions.h" #include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandlerClient.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebUserMediaRequest.h" -using std::string; using webrtc::MediaConstraintsInterface; using webrtc::StatsReport; using webrtc::StatsReports; @@ -31,9 +30,15 @@ namespace content { -static string SerializeServers( +// TODO(hta): This module should be redesigned to reduce string copies. + +static const char* SerializeBoolean(bool value) { + return value ? "true" : "false"; +} + +static std::string SerializeServers( const std::vector<webrtc::PeerConnectionInterface::IceServer>& servers) { - string result = "["; + std::string result = "["; for (size_t i = 0; i < servers.size(); ++i) { result += servers[i].uri; if (i != servers.size() - 1) @@ -43,67 +48,34 @@ return result; } -static RTCMediaConstraints GetNativeMediaConstraints( +static std::string SerializeMediaConstraints( const blink::WebMediaConstraints& constraints) { - RTCMediaConstraints native_constraints; - - if (constraints.isNull()) - return native_constraints; - - blink::WebVector<blink::WebMediaConstraint> mandatory; - constraints.getMandatoryConstraints(mandatory); - for (size_t i = 0; i < mandatory.size(); ++i) { - native_constraints.AddMandatory( - mandatory[i].m_name.utf8(), mandatory[i].m_value.utf8(), false); - } - - blink::WebVector<blink::WebMediaConstraint> optional; - constraints.getOptionalConstraints(optional); - for (size_t i = 0; i < optional.size(); ++i) { - native_constraints.AddOptional( - optional[i].m_name.utf8(), optional[i].m_value.utf8(), false); - } - return native_constraints; + return constraints.toString().utf8(); } -static string SerializeMediaConstraints( - const RTCMediaConstraints& constraints) { - string result; - MediaConstraintsInterface::Constraints mandatory = constraints.GetMandatory(); - if (!mandatory.empty()) { - result += "mandatory: {"; - for (size_t i = 0; i < mandatory.size(); ++i) { - result += mandatory[i].key + ":" + mandatory[i].value; - if (i != mandatory.size() - 1) - result += ", "; - } - result += "}"; - } - MediaConstraintsInterface::Constraints optional = constraints.GetOptional(); - if (!optional.empty()) { - if (!result.empty()) - result += ", "; - result += "optional: {"; - for (size_t i = 0; i < optional.size(); ++i) { - result += optional[i].key + ":" + optional[i].value; - if (i != optional.size() - 1) - result += ", "; - } - result += "}"; - } - return result; +static std::string SerializeOfferOptions( + const blink::WebRTCOfferOptions& options) { + if (options.isNull()) + return "null"; + + std::ostringstream result; + result << "offerToReceiveVideo: " << options.offerToReceiveVideo() + << ", offerToReceiveAudio: " << options.offerToReceiveAudio() + << ", voiceActivityDetection: " + << SerializeBoolean(options.voiceActivityDetection()) + << ", iceRestart: " << SerializeBoolean(options.iceRestart()); + return result.str(); } -static string SerializeMediaStreamComponent( - const blink::WebMediaStreamTrack component) { - string id = base::UTF16ToUTF8(base::StringPiece16(component.source().id())); - return id; +static std::string SerializeMediaStreamComponent( + const blink::WebMediaStreamTrack& component) { + return base::UTF16ToUTF8(base::StringPiece16(component.source().id())); } -static string SerializeMediaDescriptor( +static std::string SerializeMediaDescriptor( const blink::WebMediaStream& stream) { - string label = base::UTF16ToUTF8(base::StringPiece16(stream.id())); - string result = "label: " + label; + std::string label = base::UTF16ToUTF8(base::StringPiece16(stream.id())); + std::string result = "label: " + label; blink::WebVector<blink::WebMediaStreamTrack> tracks; stream.audioTracks(tracks); if (!tracks.isEmpty()) { @@ -128,9 +100,9 @@ return result; } -static std::string SerializeIceTransportType( +static const char* SerializeIceTransportType( webrtc::PeerConnectionInterface::IceTransportsType type) { - string transport_type; + const char* transport_type = ""; switch (type) { case webrtc::PeerConnectionInterface::kNone: transport_type = "none"; @@ -150,9 +122,9 @@ return transport_type; } -static std::string SerializeBundlePolicy( +static const char* SerializeBundlePolicy( webrtc::PeerConnectionInterface::BundlePolicy policy) { - string policy_str; + const char* policy_str = ""; switch (policy) { case webrtc::PeerConnectionInterface::kBundlePolicyBalanced: policy_str = "balanced"; @@ -169,9 +141,9 @@ return policy_str; } -static std::string SerializeRtcpMuxPolicy( +static const char* SerializeRtcpMuxPolicy( webrtc::PeerConnectionInterface::RtcpMuxPolicy policy) { - string policy_str; + const char* policy_str = ""; switch (policy) { case webrtc::PeerConnectionInterface::kRtcpMuxPolicyNegotiate: policy_str = "negotiate"; @@ -190,9 +162,9 @@ result = #state; \ break; -static string GetSignalingStateString( +static const char* GetSignalingStateString( WebRTCPeerConnectionHandlerClient::SignalingState state) { - string result; + const char* result = ""; switch (state) { GET_STRING_OF_STATE(SignalingStateStable) GET_STRING_OF_STATE(SignalingStateHaveLocalOffer) @@ -207,9 +179,9 @@ return result; } -static string GetIceConnectionStateString( +static const char* GetIceConnectionStateString( WebRTCPeerConnectionHandlerClient::ICEConnectionState state) { - string result; + const char* result = ""; switch (state) { GET_STRING_OF_STATE(ICEConnectionStateStarting) GET_STRING_OF_STATE(ICEConnectionStateChecking) @@ -225,9 +197,9 @@ return result; } -static string GetIceGatheringStateString( +static const char* GetIceGatheringStateString( WebRTCPeerConnectionHandlerClient::ICEGatheringState state) { - string result; + const char* result = ""; switch (state) { GET_STRING_OF_STATE(ICEGatheringStateNew) GET_STRING_OF_STATE(ICEGatheringStateGathering) @@ -347,8 +319,8 @@ const scoped_refptr<base::SingleThreadTaskRunner> main_thread_; }; -PeerConnectionTracker::PeerConnectionTracker() : next_local_id_(1) { -} +PeerConnectionTracker::PeerConnectionTracker() + : next_local_id_(1), send_target_for_test_(nullptr) {} PeerConnectionTracker::~PeerConnectionTracker() { } @@ -381,6 +353,13 @@ } } +RenderThread* PeerConnectionTracker::SendTarget() { + if (send_target_for_test_) { + return send_target_for_test_; + } + return RenderThreadImpl::current(); +} + void PeerConnectionTracker::OnSuspend() { DCHECK(main_thread_.CalledOnValidThread()); for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin(); @@ -392,7 +371,7 @@ void PeerConnectionTracker::RegisterPeerConnection( RTCPeerConnectionHandler* pc_handler, const webrtc::PeerConnectionInterface::RTCConfiguration& config, - const RTCMediaConstraints& constraints, + const blink::WebMediaConstraints& constraints, const blink::WebFrame* frame) { DCHECK(main_thread_.CalledOnValidThread()); DCHECK_EQ(GetLocalIDForHandler(pc_handler), -1); @@ -407,9 +386,11 @@ "rtcpMuxPolicy: " + SerializeRtcpMuxPolicy(config.rtcp_mux_policy) + " }"; info.constraints = SerializeMediaConstraints(constraints); - info.url = frame->document().url().string().utf8(); - RenderThreadImpl::current()->Send( - new PeerConnectionTrackerHost_AddPeerConnection(info)); + if (frame) + info.url = frame->document().url().string().utf8(); + else + info.url = "test:testing"; + SendTarget()->Send(new PeerConnectionTrackerHost_AddPeerConnection(info)); peer_connection_id_map_.insert(std::make_pair(pc_handler, info.lid)); } @@ -428,7 +409,7 @@ return; } - RenderThreadImpl::current()->Send( + SendTarget()->Send( new PeerConnectionTrackerHost_RemovePeerConnection(it->second)); peer_connection_id_map_.erase(it); @@ -436,7 +417,19 @@ void PeerConnectionTracker::TrackCreateOffer( RTCPeerConnectionHandler* pc_handler, - const RTCMediaConstraints& constraints) { + const blink::WebRTCOfferOptions& options) { + DCHECK(main_thread_.CalledOnValidThread()); + int id = GetLocalIDForHandler(pc_handler); + if (id == -1) + return; + SendPeerConnectionUpdate( + id, "createOffer", + "constraints: {" + SerializeOfferOptions(options) + "}"); +} + +void PeerConnectionTracker::TrackCreateOffer( + RTCPeerConnectionHandler* pc_handler, + const blink::WebMediaConstraints& constraints) { DCHECK(main_thread_.CalledOnValidThread()); int id = GetLocalIDForHandler(pc_handler); if (id == -1) @@ -448,7 +441,7 @@ void PeerConnectionTracker::TrackCreateAnswer( RTCPeerConnectionHandler* pc_handler, - const RTCMediaConstraints& constraints) { + const blink::WebMediaConstraints& constraints) { DCHECK(main_thread_.CalledOnValidThread()); int id = GetLocalIDForHandler(pc_handler); if (id == -1) @@ -465,7 +458,7 @@ int id = GetLocalIDForHandler(pc_handler); if (id == -1) return; - string value = "type: " + type + ", sdp: " + sdp; + std::string value = "type: " + type + ", sdp: " + sdp; SendPeerConnectionUpdate( id, source == SOURCE_LOCAL ? "setLocalDescription" : "setRemoteDescription", @@ -473,33 +466,26 @@ } void PeerConnectionTracker::TrackUpdateIce( - RTCPeerConnectionHandler* pc_handler, - const webrtc::PeerConnectionInterface::RTCConfiguration& config, - const RTCMediaConstraints& options) { + RTCPeerConnectionHandler* pc_handler, + const webrtc::PeerConnectionInterface::RTCConfiguration& config, + const blink::WebMediaConstraints& options) { DCHECK(main_thread_.CalledOnValidThread()); int id = GetLocalIDForHandler(pc_handler); if (id == -1) return; - string servers_string = "servers: " + SerializeServers(config.servers); - string transport_type = - "iceTransportType: " + SerializeIceTransportType(config.type); - - string bundle_policy = - "bundlePolicy: " + SerializeBundlePolicy(config.bundle_policy); - - string rtcp_mux_policy = - "rtcpMuxPolicy: " + SerializeRtcpMuxPolicy(config.rtcp_mux_policy); - - string constraints = - "constraints: {" + SerializeMediaConstraints(options) + "}"; + std::ostringstream result; + result << "servers: " << SerializeServers(config.servers) + << "iceTransportType: " << SerializeIceTransportType(config.type) + << "bundlePolicy: " << SerializeBundlePolicy(config.bundle_policy) + << "rtcpMuxPolicy: " + << SerializeRtcpMuxPolicy(config.rtcp_mux_policy) + << "constraints: {" << SerializeMediaConstraints(options) << "}"; SendPeerConnectionUpdate( id, "updateIce", - servers_string + ", " + transport_type + ", " + - bundle_policy + ", " + rtcp_mux_policy + ", " + - constraints); + result.str()); } void PeerConnectionTracker::TrackAddIceCandidate( @@ -511,10 +497,9 @@ int id = GetLocalIDForHandler(pc_handler); if (id == -1) return; - string value = - "sdpMid: " + - base::UTF16ToUTF8(base::StringPiece16(candidate.sdpMid())) + ", " + - "sdpMLineIndex: " + base::UintToString(candidate.sdpMLineIndex()) + + std::string value = + "sdpMid: " + base::UTF16ToUTF8(base::StringPiece16(candidate.sdpMid())) + + ", " + "sdpMLineIndex: " + base::UintToString(candidate.sdpMLineIndex()) + ", " + "candidate: " + base::UTF16ToUTF8(base::StringPiece16(candidate.candidate())); @@ -563,8 +548,8 @@ int id = GetLocalIDForHandler(pc_handler); if (id == -1) return; - string value = "label: " + data_channel->label() + - ", reliable: " + (data_channel->reliable() ? "true" : "false"); + std::string value = "label: " + data_channel->label() + ", reliable: " + + SerializeBoolean(data_channel->reliable()); SendPeerConnectionUpdate( id, source == SOURCE_LOCAL ? "createLocalDataChannel" : "onRemoteDataChannel", @@ -615,13 +600,15 @@ } void PeerConnectionTracker::TrackSessionDescriptionCallback( - RTCPeerConnectionHandler* pc_handler, Action action, - const string& callback_type, const string& value) { + RTCPeerConnectionHandler* pc_handler, + Action action, + const std::string& callback_type, + const std::string& value) { DCHECK(main_thread_.CalledOnValidThread()); int id = GetLocalIDForHandler(pc_handler); if (id == -1) return; - string update_type; + std::string update_type; switch (action) { case ACTION_SET_LOCAL_DESCRIPTION: update_type = "setLocalDescription"; @@ -667,17 +654,12 @@ void PeerConnectionTracker::TrackGetUserMedia( const blink::WebUserMediaRequest& user_media_request) { DCHECK(main_thread_.CalledOnValidThread()); - RTCMediaConstraints audio_constraints( - GetNativeMediaConstraints(user_media_request.audioConstraints())); - RTCMediaConstraints video_constraints( - GetNativeMediaConstraints(user_media_request.videoConstraints())); - RenderThreadImpl::current()->Send(new PeerConnectionTrackerHost_GetUserMedia( + SendTarget()->Send(new PeerConnectionTrackerHost_GetUserMedia( user_media_request.securityOrigin().toString().utf8(), - user_media_request.audio(), - user_media_request.video(), - SerializeMediaConstraints(audio_constraints), - SerializeMediaConstraints(video_constraints))); + user_media_request.audio(), user_media_request.video(), + SerializeMediaConstraints(user_media_request.audioConstraints()), + SerializeMediaConstraints(user_media_request.videoConstraints()))); } int PeerConnectionTracker::GetNextLocalID() { @@ -702,9 +684,12 @@ const char* callback_type, const std::string& value) { DCHECK(main_thread_.CalledOnValidThread()); - RenderThreadImpl::current()->Send( - new PeerConnectionTrackerHost_UpdatePeerConnection( - local_id, std::string(callback_type), value)); + SendTarget()->Send(new PeerConnectionTrackerHost_UpdatePeerConnection( + local_id, std::string(callback_type), value)); +} + +void PeerConnectionTracker::OverrideSendTargetForTesting(RenderThread* target) { + send_target_for_test_ = target; } } // namespace content
diff --git a/content/renderer/media/peer_connection_tracker.h b/content/renderer/media/peer_connection_tracker.h index bb01fb93..218f624 100644 --- a/content/renderer/media/peer_connection_tracker.h +++ b/content/renderer/media/peer_connection_tracker.h
@@ -19,7 +19,9 @@ namespace blink { class WebFrame; +class WebMediaConstraints; class WebRTCICECandidate; +class WebRTCOfferOptions; class WebString; class WebRTCSessionDescription; class WebUserMediaRequest; @@ -32,6 +34,7 @@ namespace content { class RTCMediaConstraints; class RTCPeerConnectionHandler; +class RenderThread; // This class collects data about each peer connection, // sends it to the browser process, and handles messages @@ -75,7 +78,7 @@ void RegisterPeerConnection( RTCPeerConnectionHandler* pc_handler, const webrtc::PeerConnectionInterface::RTCConfiguration& config, - const RTCMediaConstraints& constraints, + const blink::WebMediaConstraints& constraints, const blink::WebFrame* frame); // Sends an update when a PeerConnection has been destroyed. @@ -85,9 +88,12 @@ // The |pc_handler| is the handler object associated with the PeerConnection, // the |constraints| is the media constraints used to create the offer/answer. virtual void TrackCreateOffer(RTCPeerConnectionHandler* pc_handler, - const RTCMediaConstraints& constraints); + const blink::WebRTCOfferOptions& options); + // TODO(hta): Get rid of the version below. + virtual void TrackCreateOffer(RTCPeerConnectionHandler* pc_handler, + const blink::WebMediaConstraints& options); virtual void TrackCreateAnswer(RTCPeerConnectionHandler* pc_handler, - const RTCMediaConstraints& constraints); + const blink::WebMediaConstraints& constraints); // Sends an update when setLocalDescription or setRemoteDescription is called. virtual void TrackSetSessionDescription( @@ -98,7 +104,7 @@ virtual void TrackUpdateIce( RTCPeerConnectionHandler* pc_handler, const webrtc::PeerConnectionInterface::RTCConfiguration& config, - const RTCMediaConstraints& options); + const blink::WebMediaConstraints& options); // Sends an update when an Ice candidate is added. virtual void TrackAddIceCandidate( @@ -160,6 +166,9 @@ virtual void TrackGetUserMedia( const blink::WebUserMediaRequest& user_media_request); + // For testing: Override the class that gets posted messages. + void OverrideSendTargetForTesting(RenderThread* target); + private: // Assign a local ID to a peer connection so that the browser process can // uniquely identify a peer connection in the renderer process. @@ -191,6 +200,8 @@ const char* callback_type, const std::string& value); + RenderThread* SendTarget(); + // This map stores the local ID assigned to each RTCPeerConnectionHandler. typedef std::map<RTCPeerConnectionHandler*, int> PeerConnectionIdMap; PeerConnectionIdMap peer_connection_id_map_; @@ -198,6 +209,7 @@ // This keeps track of the next available local ID. int next_local_id_; base::ThreadChecker main_thread_; + RenderThread* send_target_for_test_; DISALLOW_COPY_AND_ASSIGN(PeerConnectionTracker); };
diff --git a/content/renderer/media/peer_connection_tracker_unittest.cc b/content/renderer/media/peer_connection_tracker_unittest.cc new file mode 100644 index 0000000..829f2c7 --- /dev/null +++ b/content/renderer/media/peer_connection_tracker_unittest.cc
@@ -0,0 +1,84 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/media/peer_connection_tracker.h" + +#include "content/common/media/peer_connection_tracker_messages.h" +#include "content/public/test/mock_render_thread.h" +#include "content/renderer/media/mock_web_rtc_peer_connection_handler_client.h" +#include "content/renderer/media/rtc_media_constraints.h" +#include "content/renderer/media/rtc_peer_connection_handler.h" +#include "ipc/ipc_message_macros.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebMediaConstraints.h" +#include "third_party/WebKit/public/platform/WebRTCOfferOptions.h" + +using ::testing::_; + +namespace content { + +namespace { + +class MockSendTargetThread : public MockRenderThread { + public: + MOCK_METHOD3(OnUpdatePeerConnection, void(int, std::string, std::string)); + MOCK_METHOD1(OnAddPeerConnection, void(PeerConnectionInfo)); + + private: + bool OnMessageReceived(const IPC::Message& msg) override; +}; + +bool MockSendTargetThread::OnMessageReceived(const IPC::Message& msg) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(MockSendTargetThread, msg) + IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_UpdatePeerConnection, + OnUpdatePeerConnection) + IPC_MESSAGE_HANDLER(PeerConnectionTrackerHost_AddPeerConnection, + OnAddPeerConnection) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +class MockPeerConnectionHandler : public RTCPeerConnectionHandler { + public: + MockPeerConnectionHandler() : RTCPeerConnectionHandler(&client_, nullptr) {} + + private: + MockWebRTCPeerConnectionHandlerClient client_; +}; + +} // namespace + +TEST(PeerConnectionTrackerTest, CreatingObject) { + PeerConnectionTracker tracker; +} + +TEST(PeerConnectionTrackerTest, TrackCreateOffer) { + PeerConnectionTracker tracker; + // Note: blink::WebRTCOfferOptions is not mockable. So we can't write + // tests for anything but a null options parameter. + blink::WebRTCOfferOptions options(0, 0, false, false); + // Initialization stuff. This can be separated into a test class. + MockPeerConnectionHandler pc_handler; + MockSendTargetThread target_thread; + webrtc::PeerConnectionInterface::RTCConfiguration config; + blink::WebMediaConstraints constraints; + tracker.OverrideSendTargetForTesting(&target_thread); + EXPECT_CALL(target_thread, OnAddPeerConnection(_)); + tracker.RegisterPeerConnection(&pc_handler, config, constraints, nullptr); + // Back to the test. + EXPECT_CALL( + target_thread, + OnUpdatePeerConnection( + _, "createOffer", + "constraints: {offerToReceiveVideo: 0, offerToReceiveAudio: 0, " + "voiceActivityDetection: false, iceRestart: false}")); + tracker.TrackCreateOffer(&pc_handler, options); +} + +// TODO(hta): Write tests for the other tracking functions. + +} // namespace
diff --git a/content/renderer/media/rtc_peer_connection_handler.cc b/content/renderer/media/rtc_peer_connection_handler.cc index 8d6924d..9b694e8c 100644 --- a/content/renderer/media/rtc_peer_connection_handler.cc +++ b/content/renderer/media/rtc_peer_connection_handler.cc
@@ -892,8 +892,8 @@ } if (peer_connection_tracker_) { - peer_connection_tracker_->RegisterPeerConnection( - this, config, constraints, frame_); + peer_connection_tracker_->RegisterPeerConnection(this, config, options, + frame_); } uma_observer_ = new rtc::RefCountedObject<PeerConnectionUMAObserver>(); @@ -938,7 +938,7 @@ native_peer_connection_->CreateOffer(description_request.get(), &constraints); if (peer_connection_tracker_) - peer_connection_tracker_->TrackCreateOffer(this, constraints); + peer_connection_tracker_->TrackCreateOffer(this, options); } void RTCPeerConnectionHandler::createOffer( @@ -959,7 +959,7 @@ native_peer_connection_->CreateOffer(description_request.get(), &constraints); if (peer_connection_tracker_) - peer_connection_tracker_->TrackCreateOffer(this, constraints); + peer_connection_tracker_->TrackCreateOffer(this, options); } void RTCPeerConnectionHandler::createAnswer( @@ -978,7 +978,7 @@ &constraints); if (peer_connection_tracker_) - peer_connection_tracker_->TrackCreateAnswer(this, constraints); + peer_connection_tracker_->TrackCreateAnswer(this, options); } bool IsOfferOrAnswer(const webrtc::SessionDescriptionInterface* native_desc) { @@ -1141,7 +1141,7 @@ RTCMediaConstraints constraints(options); if (peer_connection_tracker_) - peer_connection_tracker_->TrackUpdateIce(this, config, constraints); + peer_connection_tracker_->TrackUpdateIce(this, config, options); return native_peer_connection_->UpdateIce(config.servers, &constraints); }
diff --git a/content/renderer/media/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/rtc_peer_connection_handler_unittest.cc index 697f484..f9de40bb 100644 --- a/content/renderer/media/rtc_peer_connection_handler_unittest.cc +++ b/content/renderer/media/rtc_peer_connection_handler_unittest.cc
@@ -140,10 +140,10 @@ // TODO(jiayl): add coverage for the following methods MOCK_METHOD2(TrackCreateOffer, void(RTCPeerConnectionHandler* pc_handler, - const RTCMediaConstraints& constraints)); + const blink::WebMediaConstraints& constraints)); MOCK_METHOD2(TrackCreateAnswer, void(RTCPeerConnectionHandler* pc_handler, - const RTCMediaConstraints& constraints)); + const blink::WebMediaConstraints& constraints)); MOCK_METHOD4(TrackSetSessionDescription, void(RTCPeerConnectionHandler* pc_handler, const std::string& sdp, const std::string& type, @@ -152,7 +152,7 @@ TrackUpdateIce, void(RTCPeerConnectionHandler* pc_handler, const webrtc::PeerConnectionInterface::RTCConfiguration& config, - const RTCMediaConstraints& options)); + const blink::WebMediaConstraints& options)); MOCK_METHOD4(TrackAddIceCandidate, void(RTCPeerConnectionHandler* pc_handler, const blink::WebRTCICECandidate& candidate,
diff --git a/content/renderer/mus/render_widget_mus_connection.cc b/content/renderer/mus/render_widget_mus_connection.cc index c7dbac8..3ac0808 100644 --- a/content/renderer/mus/render_widget_mus_connection.cc +++ b/content/renderer/mus/render_widget_mus_connection.cc
@@ -123,6 +123,11 @@ pending_ack_.Reset(); } +void RenderWidgetMusConnection::NonBlockingInputEventHandled( + blink::WebInputEvent::Type handled_type) { + NOTIMPLEMENTED(); +} + void RenderWidgetMusConnection::SetInputHandler( RenderWidgetInputHandler* input_handler) { DCHECK(!input_handler_); @@ -171,7 +176,8 @@ pending_ack_ = ack; // TODO(fsamuel, sadrul): Track real latency info. ui::LatencyInfo latency_info; - input_handler_->HandleInputEvent(*input_event, latency_info); + input_handler_->HandleInputEvent(*input_event, latency_info, + DISPATCH_TYPE_NORMAL); } } // namespace content
diff --git a/content/renderer/mus/render_widget_mus_connection.h b/content/renderer/mus/render_widget_mus_connection.h index c6bb362..26f1b4b 100644 --- a/content/renderer/mus/render_widget_mus_connection.h +++ b/content/renderer/mus/render_widget_mus_connection.h
@@ -46,6 +46,8 @@ void OnDidHandleKeyEvent() override; void OnDidOverscroll(const DidOverscrollParams& params) override; void OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) override; + void NonBlockingInputEventHandled( + blink::WebInputEvent::Type handled_type) override; void SetInputHandler(RenderWidgetInputHandler* input_handler) override; void UpdateTextInputState(ShowIme show_ime, ChangeSource change_source) override;
diff --git a/content/renderer/notification_permission_dispatcher.cc b/content/renderer/notification_permission_dispatcher.cc index 98a8ed0..5d170fb 100644 --- a/content/renderer/notification_permission_dispatcher.cc +++ b/content/renderer/notification_permission_dispatcher.cc
@@ -11,7 +11,6 @@ #include "content/public/renderer/render_frame.h" #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/platform/WebString.h" -#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" #include "third_party/WebKit/public/web/modules/notifications/WebNotificationPermissionCallback.h" using blink::WebNotificationPermissionCallback; @@ -38,7 +37,6 @@ // callbacks, will be deleted before the "this" instance is deleted. permission_service_->RequestPermission( PermissionName::NOTIFICATIONS, origin.toString().utf8(), - blink::WebUserGestureIndicator::isProcessingUserGesture(), base::Bind(&NotificationPermissionDispatcher::OnPermissionRequestComplete, base::Unretained(this), base::Passed(std::move(owned_callback))));
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 2eb1416..e2afa2a 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -94,7 +94,6 @@ #include "content/renderer/internal_document_state_data.h" #include "content/renderer/manifest/manifest_manager.h" #include "content/renderer/media/audio_device_factory.h" -#include "content/renderer/media/cdm/render_cdm_factory.h" #include "content/renderer/media/media_permission_dispatcher.h" #include "content/renderer/media/media_stream_dispatcher.h" #include "content/renderer/media/media_stream_renderer_factory_impl.h" @@ -132,6 +131,7 @@ #include "gin/modules/module_registry.h" #include "media/audio/audio_output_device.h" #include "media/base/audio_renderer_mixer_input.h" +#include "media/base/media.h" #include "media/base/media_log.h" #include "media/base/media_switches.h" #include "media/blink/url_index.h" @@ -217,13 +217,18 @@ #endif #if defined(ENABLE_MOJO_MEDIA) -#include "media/mojo/services/mojo_cdm_factory.h" // nogncheck #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/shell/public/cpp/connect.h" #include "mojo/shell/public/interfaces/shell.mojom.h" #endif -#if defined(ENABLE_MOJO_MEDIA) && !defined(OS_ANDROID) +#if defined(ENABLE_MOJO_CDM) +#include "media/mojo/services/mojo_cdm_factory.h" // nogncheck +#else +#include "content/renderer/media/cdm/render_cdm_factory.h" +#endif + +#if defined(ENABLE_MOJO_RENDERER) #include "media/mojo/services/mojo_renderer_factory.h" // nogncheck #else #include "media/renderers/default_renderer_factory.h" @@ -613,7 +618,7 @@ RenderFrameImpl::CreateRenderFrameImplFunction g_create_render_frame_impl = nullptr; -void OnGotRemoteIDs(uint32_t remote_id, uint32_t content_handler_id) {} +void OnGotInstanceID(uint32_t instance_id) {} WebString ConvertRelativePathToHtmlAttribute(const base::FilePath& path) { DCHECK(!path.IsAbsolute()); @@ -743,42 +748,29 @@ } #if defined(OS_ANDROID) -// Returns true if WMPI must be used for playback because WMPA will not work. -bool MustUseWebMediaPlayerImpl(blink::WebMediaPlayer::LoadType load_type, - const GURL& url) { - // WMPA can't play MSE if MediaCodec is unavailable. In this case WMPI may - // still work (via libvpx). - return (load_type == blink::WebMediaPlayer::LoadTypeMediaSource && - !media::MediaCodecUtil::IsMediaCodecAvailable()); -} - -// Returns true if WMPI can be used for playback, false if it may not work. +// Returns true if WMPI should be used for playback, false otherwise. // -// Note that HLS and WebM detection are pre-redirect and path-based. It is +// Note that HLS and MP4 detection are pre-redirect and path-based. It is // possible to load such a URL and find different content. -bool CanUseWebMediaPlayerImpl(blink::WebMediaPlayer::LoadType load_type, - const GURL& url) { - if (MustUseWebMediaPlayerImpl(load_type, url)) - return true; +bool UseWebMediaPlayerImpl(blink::WebMediaPlayer::LoadType load_type, + const GURL& url) { + if (load_type == blink::WebMediaPlayer::LoadTypeMediaSource) + return media::IsUnifiedMediaPipelineEnabledForMse(); // WMPI does not support HLS. if (media::MediaCodecUtil::IsHLSPath(url)) return false; - // Otherwise --enable-unified-media-pipeline always enables WMPI. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableUnifiedMediaPipeline)) { - return true; + // Don't use WMPI if the container likely contains a codec we can't decode in + // software and hardware decoders are not available. + if (base::EndsWith(url.path(), ".mp4", + base::CompareCase::INSENSITIVE_ASCII) && + !media::HasPlatformDecoderSupport()) { + return false; } - // WMPI can always play WebM (via libvpx). - if (base::EndsWith(url.path(), ".webm", base::CompareCase::INSENSITIVE_ASCII)) - return true; - - // Otherwise, WMPI can only be used if AVDA is working. - return (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableAcceleratedVideoDecode) && - media::MediaCodecUtil::IsMediaCodecAvailable()); + // Otherwise enable WMPI if indicated via experiment or command line. + return media::IsUnifiedMediaPipelineEnabled(); } #endif // defined(OS_ANDROID) @@ -1003,8 +995,8 @@ #if defined(OS_ANDROID) media_player_manager_(NULL), media_session_manager_(NULL), - media_surface_manager_(nullptr), #endif + media_surface_manager_(nullptr), #if defined(ENABLE_BROWSER_CDMS) cdm_manager_(NULL), #endif @@ -2499,30 +2491,11 @@ GetMediaPermission(), initial_cdm, media_surface_manager_); #if defined(OS_ANDROID) - if (!CanUseWebMediaPlayerImpl(load_type, url)) { + if (!UseWebMediaPlayerImpl(load_type, url)) return CreateAndroidWebMediaPlayer(client, encrypted_client, params); - } else if (!MustUseWebMediaPlayerImpl(load_type, url)) { - // TODO(dalecurtis): This experiment is temporary and should be removed once - // we have enough data to support the primacy of the unified media pipeline; - // see http://crbug.com/533190 for details. - // - // Note: It's important to query the field trial state first, to ensure that - // UMA reports the correct group. - const std::string group_name = - base::FieldTrialList::FindFullName("UnifiedMediaPipelineTrial"); - const bool enabled_via_cli = - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableUnifiedMediaPipeline); - const bool enable_unified_media_pipeline = - enabled_via_cli || - base::StartsWith(group_name, "Enabled", base::CompareCase::SENSITIVE); - - if (!enable_unified_media_pipeline) - return CreateAndroidWebMediaPlayer(client, encrypted_client, params); - } #endif // defined(OS_ANDROID) -#if defined(ENABLE_MOJO_MEDIA) && !defined(OS_ANDROID) +#if defined(ENABLE_MOJO_RENDERER) scoped_ptr<media::RendererFactory> media_renderer_factory( new media::MojoRendererFactory(GetMediaServiceFactory())); #else @@ -2535,7 +2508,7 @@ media_log, render_thread->GetGpuFactories(), *render_thread->GetAudioHardwareConfig())); } -#endif // defined(ENABLE_MOJO_MEDIA) && !defined(OS_ANDROID) +#endif // defined(ENABLE_MOJO_RENDERER) if (!url_index_.get() || url_index_->frame() != frame_) url_index_.reset(new media::UrlIndex(frame_)); @@ -5993,7 +5966,7 @@ // this. // media_service_factory_.reset(); } -#endif +#endif // defined(ENABLE_MOJO_MEDIA) bool RenderFrameImpl::AreSecureCodecsSupported() { #if defined(OS_ANDROID) @@ -6014,7 +5987,7 @@ if (!cdm_factory_) { DCHECK(frame_); -#if defined(ENABLE_MOJO_MEDIA) +#if defined(ENABLE_MOJO_CDM) cdm_factory_.reset(new media::MojoCdmFactory(GetMediaServiceFactory())); #else cdm_factory_.reset(new RenderCdmFactory( @@ -6023,8 +5996,8 @@ #elif defined(ENABLE_BROWSER_CDMS) cdm_manager_ #endif - )); -#endif // defined(ENABLE_MOJO_MEDIA) + )); +#endif // defined(ENABLE_MOJO_CDM) } return cdm_factory_.get(); @@ -6058,7 +6031,7 @@ filter->filter.insert("*", std::move(all_interfaces)); mojo_shell_->ConnectToApplication( std::move(request), GetProxy(&interface_provider), nullptr, - std::move(filter), base::Bind(&OnGotRemoteIDs)); + std::move(filter), base::Bind(&OnGotInstanceID)); return interface_provider; }
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc index f127ee1..26dca26a 100644 --- a/content/renderer/render_frame_proxy.cc +++ b/content/renderer/render_frame_proxy.cc
@@ -58,8 +58,18 @@ // follow later. blink::WebRemoteFrame* web_frame = blink::WebRemoteFrame::create(scope, proxy.get()); - proxy->Init(web_frame, frame_to_replace->render_view(), - frame_to_replace->GetRenderWidget()); + + // If frame_to_replace has a RenderFrameProxy parent, then its + // RenderWidget will be destroyed along with it, so the new + // RenderFrameProxy uses its parent's RenderWidget. + RenderWidget* widget = + (!frame_to_replace->GetWebFrame()->parent() || + frame_to_replace->GetWebFrame()->parent()->isWebLocalFrame()) + ? frame_to_replace->GetRenderWidget() + : RenderFrameProxy::FromWebFrame( + frame_to_replace->GetWebFrame()->parent()) + ->render_widget(); + proxy->Init(web_frame, frame_to_replace->render_view(), widget); return proxy.release(); }
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 0fec9ed..12e31bd 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -172,6 +172,7 @@ #include "content/renderer/android/synchronous_compositor_filter.h" #include "content/renderer/media/android/renderer_demuxer_android.h" #include "content/renderer/media/android/stream_texture_factory_impl.h" +#include "media/base/android/media_codec_util.h" #endif #if defined(OS_MACOSX) @@ -809,6 +810,13 @@ // been initialized by the Zygote before this instance became a Renderer. media::InitializeMediaLibrary(); +#if defined(OS_ANDROID) + if (!command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode) && + media::MediaCodecUtil::IsMediaCodecAvailable()) { + media::EnablePlatformDecoderSupport(); + } +#endif + memory_pressure_listener_.reset(new base::MemoryPressureListener( base::Bind(&RenderThreadImpl::OnMemoryPressure, base::Unretained(this))));
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index e2e7603..90c24f7 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc
@@ -48,6 +48,7 @@ #include "content/common/frame_messages.h" #include "content/common/frame_replication_state.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" +#include "content/common/input/input_event_utils.h" #include "content/common/input_messages.h" #include "content/common/pepper_messages.h" #include "content/common/site_isolation_policy.h" @@ -615,7 +616,6 @@ send_preferred_size_changes_(false), navigation_gesture_(NavigationGestureUnknown), opened_by_user_gesture_(true), - opener_suppressed_(false), suppress_dialogs_until_swap_out_(false), page_id_(-1), next_page_id_(params.next_page_id), @@ -1700,9 +1700,6 @@ RenderViewImpl::Create(compositor_deps_, view_params, true); view->opened_by_user_gesture_ = params.user_gesture; - // Record whether the creator frame is trying to suppress the opener field. - view->opener_suppressed_ = params.opener_suppressed; - return view->webview(); } @@ -2161,7 +2158,8 @@ if (input_handler_manager) { input_handler_manager->AddInputHandler( routing_id(), rwc->GetInputHandler(), AsWeakPtr(), - webkit_preferences_.enable_scroll_animator); + webkit_preferences_.enable_scroll_animator, + UseGestureBasedWheelScrolling()); } } } @@ -2212,6 +2210,13 @@ return device_scale_factor_; } +gfx::Point RenderViewImpl::ConvertWindowPointToViewport( + const gfx::Point& point) { + blink::WebFloatRect point_in_viewport(point.x(), point.y(), 0, 0); + convertWindowToViewport(&point_in_viewport); + return gfx::Point(point_in_viewport.x, point_in_viewport.y); +} + void RenderViewImpl::didChangeIcon(WebLocalFrame* frame, WebIconURL::Type icon_type) { if (frame->parent()) @@ -2413,7 +2418,7 @@ int key_modifiers) { WebDragOperation operation = webview()->dragTargetDragEnter( DropDataToWebDragData(drop_data), - client_point, + ConvertWindowPointToViewport(client_point), screen_point, ops, key_modifiers); @@ -2426,7 +2431,7 @@ WebDragOperationsMask ops, int key_modifiers) { WebDragOperation operation = webview()->dragTargetDragOver( - client_point, + ConvertWindowPointToViewport(client_point), screen_point, ops, key_modifiers); @@ -2441,13 +2446,15 @@ void RenderViewImpl::OnDragTargetDrop(const gfx::Point& client_point, const gfx::Point& screen_point, int key_modifiers) { - webview()->dragTargetDrop(client_point, screen_point, key_modifiers); + webview()->dragTargetDrop( + ConvertWindowPointToViewport(client_point), screen_point, key_modifiers); } void RenderViewImpl::OnDragSourceEnded(const gfx::Point& client_point, const gfx::Point& screen_point, WebDragOperation op) { - webview()->dragSourceEndedAt(client_point, screen_point, op); + webview()->dragSourceEndedAt( + ConvertWindowPointToViewport(client_point), screen_point, op); } void RenderViewImpl::OnDragSourceSystemDragEnded() {
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index bee1c65..c25a408 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h
@@ -457,6 +457,8 @@ gfx::RectF ElementBoundsInWindow(const blink::WebElement& element) override; float GetDeviceScaleFactorForTest() const override; + gfx::Point ConvertWindowPointToViewport(const gfx::Point& point); + bool uses_temporary_zoom_level() const { return uses_temporary_zoom_level_; } // Please do not add your stuff randomly to the end here. If there is an @@ -803,11 +805,6 @@ // Used for popups. bool opened_by_user_gesture_; - // Whether this RenderView was created by a frame that was suppressing its - // opener. If so, we may want to load pages in a separate process. See - // decidePolicyForNavigation for details. - bool opener_suppressed_; - // Whether we must stop creating nested message loops for modal dialogs until // OnSwapOut is called. This is necessary because modal dialogs have a // PageGroupLoadDeferrer on the stack that interferes with swapping out.
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 85cb9f3..9709d2e 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc
@@ -656,10 +656,11 @@ } void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event, - const ui::LatencyInfo& latency_info) { + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type) { if (!input_event) return; - input_handler_->HandleInputEvent(*input_event, latency_info); + input_handler_->HandleInputEvent(*input_event, latency_info, dispatch_type); } void RenderWidget::OnCursorVisibilityChange(bool is_visible) { @@ -959,6 +960,17 @@ Send(new InputHostMsg_HandleInputEvent_ACK(routing_id_, *input_event_ack)); } +void RenderWidget::NonBlockingInputEventHandled( + blink::WebInputEvent::Type handled_type) { + RenderThreadImpl* render_thread = RenderThreadImpl::current(); + InputHandlerManager* input_handler_manager = + render_thread ? render_thread->input_handler_manager() : NULL; + if (input_handler_manager) { + input_handler_manager->NonBlockingInputEventHandledOnMainThread( + routing_id_, handled_type); + } +} + void RenderWidget::SetInputHandler(RenderWidgetInputHandler* input_handler) { // Nothing to do here. RenderWidget created the |input_handler| and will take // ownership of it. We just verify here that we don't already have an input
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index 1185fa2b..a7bf289 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h
@@ -207,6 +207,8 @@ void OnDidHandleKeyEvent() override; void OnDidOverscroll(const DidOverscrollParams& params) override; void OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) override; + void NonBlockingInputEventHandled( + blink::WebInputEvent::Type handled_type) override; void SetInputHandler(RenderWidgetInputHandler* input_handler) override; void UpdateTextInputState(ShowIme show_ime, ChangeSource change_source) override; @@ -412,7 +414,8 @@ // RenderWidget IPC message handlers void OnHandleInputEvent(const blink::WebInputEvent* event, - const ui::LatencyInfo& latency_info); + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type); void OnCursorVisibilityChange(bool is_visible); void OnMouseCaptureLost(); virtual void OnSetFocus(bool enable);
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc index cdd195a..afd9cd8 100644 --- a/content/renderer/render_widget_unittest.cc +++ b/content/renderer/render_widget_unittest.cc
@@ -38,7 +38,8 @@ } void SendInputEvent(const blink::WebInputEvent& event) { - OnHandleInputEvent(&event, ui::LatencyInfo()); + OnHandleInputEvent(&event, ui::LatencyInfo(), + InputEventDispatchType::DISPATCH_TYPE_NORMAL); } void set_always_overscroll(bool overscroll) {
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc index 5d0f45b..34e20a7 100644 --- a/content/renderer/service_worker/service_worker_context_client.cc +++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -100,6 +100,7 @@ static_cast<DataSourceExtraData*>(data_source->extraData())); scoped_ptr<RequestExtraData> extra_data(new RequestExtraData); extra_data->set_service_worker_provider_id(provider->provider_id()); + extra_data->set_originated_from_service_worker(true); request.setExtraData(extra_data.release()); } };
diff --git a/content/renderer/websharedworker_proxy.cc b/content/renderer/websharedworker_proxy.cc index c4439de..46bbfe1 100644 --- a/content/renderer/websharedworker_proxy.cc +++ b/content/renderer/websharedworker_proxy.cc
@@ -7,15 +7,15 @@ #include <stddef.h> #include "content/child/webmessageportchannel_impl.h" -#include "content/common/message_router.h" #include "content/common/view_messages.h" #include "content/common/worker_messages.h" +#include "ipc/message_router.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/web/WebSharedWorkerClient.h" namespace content { -WebSharedWorkerProxy::WebSharedWorkerProxy(MessageRouter* router, +WebSharedWorkerProxy::WebSharedWorkerProxy(IPC::MessageRouter* router, int route_id) : route_id_(route_id), router_(router),
diff --git a/content/renderer/websharedworker_proxy.h b/content/renderer/websharedworker_proxy.h index a413c39..cb6acb9 100644 --- a/content/renderer/websharedworker_proxy.h +++ b/content/renderer/websharedworker_proxy.h
@@ -14,9 +14,11 @@ #include "third_party/WebKit/public/web/WebSharedWorkerConnector.h" #include "url/gurl.h" -namespace content { - +namespace IPC { class MessageRouter; +} + +namespace content { // Implementation of the WebSharedWorker APIs. This object is intended to only // live long enough to allow the caller to send a "connect" event to the worker @@ -27,8 +29,7 @@ private IPC::Listener { public: // If the worker not loaded yet, route_id == MSG_ROUTING_NONE - WebSharedWorkerProxy(MessageRouter* router, - int route_id); + WebSharedWorkerProxy(IPC::MessageRouter* router, int route_id); ~WebSharedWorkerProxy() override; // Implementations of WebSharedWorkerConnector APIs @@ -62,7 +63,7 @@ // routing ids). int route_id_; - MessageRouter* const router_; + IPC::MessageRouter* const router_; // Stores messages that were sent before the StartWorkerContext message. std::vector<IPC::Message*> queued_messages_;
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 85b22f2b..57538e9 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn
@@ -245,8 +245,8 @@ "//ppapi:blink_test_plugin", ] } - if (enable_mojo_media == "browser") { - deps += [ "//media/mojo/services:application" ] + if (mojo_media_host == "browser") { + deps += [ "//media/mojo/services:application_factory" ] } if (is_win) {
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn index 3c6af4d..616a84d 100644 --- a/content/shell/android/BUILD.gn +++ b/content/shell/android/BUILD.gn
@@ -46,6 +46,7 @@ "//base:base_java", "//content/public/android:content_java", "//media/base/android:media_java", + "//media/capture/video/android:capture_java", "//mojo/public/java:system", "//net/android:net_java", "//ui/android:ui_java", @@ -79,7 +80,7 @@ ":content_shell_java", "//base:base_java", "//content/public/android:content_java", - "//media/base/android:media_java", + "//media/capture/video/android:capture_java", "//net/android:net_java", "//ui/android:ui_java", ] @@ -109,7 +110,7 @@ ":libcontent_shell_content_view", "//base:base_java", "//content/public/android:content_java", - "//media/base/android:media_java", + "//media/capture/video/android:capture_java", "//net/android:net_java", "//third_party/mesa:osmesa", "//ui/android:ui_java",
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc index 1c9f074..0454c56 100644 --- a/content/shell/app/shell_main_delegate.cc +++ b/content/shell/app/shell_main_delegate.cc
@@ -194,8 +194,6 @@ command_line.AppendSwitch(switches::kEnableInbandTextTracks); command_line.AppendSwitch(switches::kMuteAudio); - command_line.AppendSwitch(cc::switches::kEnablePropertyTreeVerification); - command_line.AppendSwitch(switches::kEnablePreciseMemoryInfo); command_line.AppendSwitchASCII(switches::kHostResolverRules,
diff --git a/content/shell/browser/layout_test/layout_test_permission_manager.cc b/content/shell/browser/layout_test/layout_test_permission_manager.cc index df6f87b9..63c5a8de 100644 --- a/content/shell/browser/layout_test/layout_test_permission_manager.cc +++ b/content/shell/browser/layout_test/layout_test_permission_manager.cc
@@ -65,7 +65,6 @@ PermissionType permission, RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(PermissionStatus)>& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -80,7 +79,6 @@ const std::vector<PermissionType>& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<PermissionStatus>&)>& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/shell/browser/layout_test/layout_test_permission_manager.h b/content/shell/browser/layout_test/layout_test_permission_manager.h index 938ff3b..2ea014f8 100644 --- a/content/shell/browser/layout_test/layout_test_permission_manager.h +++ b/content/shell/browser/layout_test/layout_test_permission_manager.h
@@ -27,13 +27,11 @@ PermissionType permission, RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(PermissionStatus)>& callback) override; int RequestPermissions( const std::vector<PermissionType>& permission, RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<PermissionStatus>&)>& callback) override; void CancelPermissionRequest(int request_id) override;
diff --git a/content/shell/browser/layout_test/layout_test_push_messaging_service.cc b/content/shell/browser/layout_test/layout_test_push_messaging_service.cc index e4a9e680..11554e94 100644 --- a/content/shell/browser/layout_test/layout_test_push_messaging_service.cc +++ b/content/shell/browser/layout_test/layout_test_push_messaging_service.cc
@@ -82,7 +82,7 @@ const std::string& sender_id, bool user_visible, const PushMessagingService::RegisterCallback& callback) { - if (GetPermissionStatus(requesting_origin, requesting_origin, user_visible) == + if (GetPermissionStatus(requesting_origin, user_visible) == blink::WebPushPermissionStatusGranted) { std::vector<uint8_t> p256dh( kTestP256Key, kTestP256Key + arraysize(kTestP256Key)); @@ -111,16 +111,12 @@ } blink::WebPushPermissionStatus -LayoutTestPushMessagingService::GetPermissionStatus( - const GURL& requesting_origin, - const GURL& embedding_origin, - bool user_visible) { +LayoutTestPushMessagingService::GetPermissionStatus(const GURL& origin, + bool user_visible) { return ToWebPushPermissionStatus(LayoutTestContentBrowserClient::Get() - ->GetLayoutTestBrowserContext() - ->GetLayoutTestPermissionManager() - ->GetPermissionStatus(PermissionType::PUSH_MESSAGING, - requesting_origin, - embedding_origin)); + ->browser_context() + ->GetPermissionManager() + ->GetPermissionStatus(PermissionType::PUSH_MESSAGING, origin, origin)); } bool LayoutTestPushMessagingService::SupportNonVisibleMessages() {
diff --git a/content/shell/browser/layout_test/layout_test_push_messaging_service.h b/content/shell/browser/layout_test/layout_test_push_messaging_service.h index 189f6f42..eb5e7e4 100644 --- a/content/shell/browser/layout_test/layout_test_push_messaging_service.h +++ b/content/shell/browser/layout_test/layout_test_push_messaging_service.h
@@ -42,10 +42,9 @@ const GURL& origin, int64_t service_worker_registration_id, const PushMessagingService::EncryptionInfoCallback& callback) override; - blink::WebPushPermissionStatus GetPermissionStatus( - const GURL& requesting_origin, - const GURL& embedding_origin, - bool user_visible) override; + blink::WebPushPermissionStatus GetPermissionStatus(const GURL& origin, + bool user_visible) + override; bool SupportNonVisibleMessages() override; void Unsubscribe(const GURL& requesting_origin, int64_t service_worker_registration_id,
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index 1f317da..faea432 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc
@@ -60,7 +60,7 @@ #endif #if defined(ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) -#include "media/mojo/services/mojo_media_application.h" +#include "media/mojo/services/mojo_media_application_factory.h" #endif namespace content { @@ -219,8 +219,8 @@ void ShellContentBrowserClient::RegisterInProcessMojoApplications( StaticMojoApplicationMap* apps) { #if (ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS) - apps->insert(std::make_pair( - GURL("mojo:media"), base::Bind(&media::MojoMediaApplication::CreateApp))); + apps->insert(std::make_pair(GURL("mojo:media"), + base::Bind(&media::CreateMojoMediaApplication))); #endif }
diff --git a/content/shell/browser/shell_javascript_dialog_win.cc b/content/shell/browser/shell_javascript_dialog_win.cc index d7da87a..562194a 100644 --- a/content/shell/browser/shell_javascript_dialog_win.cc +++ b/content/shell/browser/shell_javascript_dialog_win.cc
@@ -44,7 +44,7 @@ GetWindowLongPtr(dialog, DWLP_USER)); base::string16 user_input; bool finish = false; - bool result; + bool result = false; switch (LOWORD(wparam)) { case IDOK: finish = true;
diff --git a/content/shell/browser/shell_permission_manager.cc b/content/shell/browser/shell_permission_manager.cc index 5d666467..f41b3267 100644 --- a/content/shell/browser/shell_permission_manager.cc +++ b/content/shell/browser/shell_permission_manager.cc
@@ -23,7 +23,6 @@ PermissionType permission, RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(PermissionStatus)>& callback) { callback.Run(permission == PermissionType::GEOLOCATION ? PermissionStatus::GRANTED @@ -35,7 +34,6 @@ const std::vector<PermissionType>& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<PermissionStatus>&)>& callback) { std::vector<PermissionStatus> result(permissions.size());
diff --git a/content/shell/browser/shell_permission_manager.h b/content/shell/browser/shell_permission_manager.h index b210a16..480bd35 100644 --- a/content/shell/browser/shell_permission_manager.h +++ b/content/shell/browser/shell_permission_manager.h
@@ -21,13 +21,11 @@ PermissionType permission, RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void(PermissionStatus)>& callback) override; int RequestPermissions( const std::vector<PermissionType>& permission, RenderFrameHost* render_frame_host, const GURL& requesting_origin, - bool user_gesture, const base::Callback<void( const std::vector<PermissionStatus>&)>& callback) override; void CancelPermissionRequest(int request_id) override;
diff --git a/content/shell/tools/plugin/PluginObject.h b/content/shell/tools/plugin/PluginObject.h index 0c037e82..bc904a5 100644 --- a/content/shell/tools/plugin/PluginObject.h +++ b/content/shell/tools/plugin/PluginObject.h
@@ -70,9 +70,6 @@ #ifdef XP_MACOSX NPEventModel eventModel; #endif -#ifdef XP_MACOSX - void* coreAnimationLayer; -#endif NPWindow lastWindow; NPBool alwaysFilterEvents; } PluginObject; @@ -94,8 +91,4 @@ extern bool testDocumentOpen(NPP npp); extern bool testWindowOpen(NPP npp); -#ifdef XP_MACOSX -extern void* createCoreAnimationLayer(); -#endif - #endif // PluginObject_h
diff --git a/content/shell/tools/plugin/PluginObjectMac.mm b/content/shell/tools/plugin/PluginObjectMac.mm deleted file mode 100644 index d69908b..0000000 --- a/content/shell/tools/plugin/PluginObjectMac.mm +++ /dev/null
@@ -1,111 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "PluginObject.h" - - -#include <QuartzCore/QuartzCore.h> - -@interface TestPluginLayer : CALayer -@end - -@implementation TestPluginLayer - -- (void)drawInContext:(CGContextRef)context -{ - CGRect bounds = [self bounds]; - const char* text = "Test Plugin"; - CGContextSelectFont(context, "Helvetica", 24, kCGEncodingMacRoman); - CGContextShowTextAtPoint(context, - bounds.origin.x + 3.0f, - bounds.origin.y + bounds.size.height - 30.0f, - text, - strlen(text)); -} - -@end - -void* createCoreAnimationLayer() -{ - CALayer *caLayer = [[TestPluginLayer alloc] init]; - - NSNull *nullValue = [NSNull null]; - NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys: - nullValue, @"anchorPoint", - nullValue, @"bounds", - nullValue, @"contents", - nullValue, @"contentsRect", - nullValue, @"opacity", - nullValue, @"position", - nullValue, @"shadowColor", - nullValue, @"sublayerTransform", - nullValue, @"sublayers", - nullValue, @"transform", - nullValue, @"zPosition", - nil]; - // Turn off default animations. - [caLayer - setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]]; - [caLayer setNeedsDisplayOnBoundsChange:YES]; - - [caLayer setBounds:CGRectMake(0, 0, 200, 100)]; - [caLayer setAnchorPoint:CGPointZero]; - - CGColorRef color = CGColorCreateGenericRGB(0.5, 0.5, 1, 1); - [caLayer setBackgroundColor:color]; - CGColorRelease(color); - - [caLayer setLayoutManager:[CAConstraintLayoutManager layoutManager]]; - - CALayer *sublayer = [CALayer layer]; - // Turn off default animations. - [sublayer - setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]]; - - color = CGColorCreateGenericRGB(0, 0, 0, 0.75); - [sublayer setBackgroundColor:color]; - CGColorRelease(color); - [sublayer setBounds:CGRectMake(0, 0, 180, 20)]; - - [sublayer - addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY - relativeTo:@"superlayer" - attribute:kCAConstraintMinY]]; - [sublayer - addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinX - relativeTo:@"superlayer" - attribute:kCAConstraintMinX]]; - [sublayer - addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxX - relativeTo:@"superlayer" - attribute:kCAConstraintMaxX]]; - - [caLayer addSublayer:sublayer]; - return caLayer; -}
diff --git a/content/shell/tools/plugin/main.cpp b/content/shell/tools/plugin/main.cpp index a36ff353d..bd3f4cdc 100644 --- a/content/shell/tools/plugin/main.cpp +++ b/content/shell/tools/plugin/main.cpp
@@ -162,12 +162,6 @@ if (!supportsCoreGraphics) return NPERR_INCOMPATIBLE_VERSION_ERROR; - NPDrawingModel drawingModelToUse = NPDrawingModelCoreGraphics; - - NPBool supportsCoreAnimation; - if (browser->getvalue(instance, NPNVsupportsCoreAnimationBool, &supportsCoreAnimation) != NPERR_NO_ERROR) - supportsCoreAnimation = false; - #ifndef NP_NO_CARBON NPBool supportsCarbon = false; #endif @@ -200,7 +194,6 @@ #ifdef XP_MACOSX obj->eventModel = eventModel; - obj->coreAnimationLayer = 0; #endif // XP_MACOSX obj->alwaysFilterEvents = false; @@ -253,23 +246,7 @@ obj->onDestroy = base::strdup(argv[i]); else if (strcasecmp(argn[i], "testwindowopen") == 0) obj->testWindowOpen = true; - else if (strcasecmp(argn[i], "drawingmodel") == 0) { -#ifdef XP_MACOSX - const char* value = argv[i]; - if (strcasecmp(value, "coreanimation") == 0) { - if (supportsCoreAnimation) - drawingModelToUse = NPDrawingModelCoreAnimation; - else - return NPERR_INCOMPATIBLE_VERSION_ERROR; - } else if (strcasecmp(value, "coregraphics") == 0) { - if (supportsCoreGraphics) - drawingModelToUse = NPDrawingModelCoreGraphics; - else - return NPERR_INCOMPATIBLE_VERSION_ERROR; - } else - return NPERR_INCOMPATIBLE_VERSION_ERROR; -#endif - } else if (strcasecmp(argn[i], "testGetURLOnDestroy") == 0) { + else if (strcasecmp(argn[i], "testGetURLOnDestroy") == 0) { #if defined(XP_WIN) // FIXME: When https://bugs.webkit.org/show_bug.cgi?id=41831 is fixed, this #ifdef can be removed. obj->testGetURLOnDestroy = TRUE; @@ -302,9 +279,7 @@ } #ifdef XP_MACOSX - browser->setvalue(instance, NPPVpluginDrawingModel, (void *)drawingModelToUse); - if (drawingModelToUse == NPDrawingModelCoreAnimation) - obj->coreAnimationLayer = createCoreAnimationLayer(); + browser->setvalue(instance, NPPVpluginDrawingModel, (void *)NPDrawingModelCoreGraphics); #endif obj->pluginTest = PluginTest::create(instance, testIdentifier); @@ -358,11 +333,6 @@ fflush(stdout); } -#ifdef XP_MACOSX - if (obj->coreAnimationLayer) - CFRelease(obj->coreAnimationLayer); -#endif - if (obj->pluginTest) obj->pluginTest->NPP_Destroy(save); @@ -868,17 +838,6 @@ return NPERR_NO_ERROR; } -#ifdef XP_MACOSX - if (variable == NPPVpluginCoreAnimationLayer) { - if (!obj->coreAnimationLayer) - return NPERR_GENERIC_ERROR; - - void **v = (void **)value; - *v = (void*)CFRetain(obj->coreAnimationLayer); - return NPERR_NO_ERROR; - } -#endif - return NPERR_GENERIC_ERROR; }
diff --git a/content/shell/utility/shell_content_utility_client.cc b/content/shell/utility/shell_content_utility_client.cc index a21c083..a9819eee 100644 --- a/content/shell/utility/shell_content_utility_client.cc +++ b/content/shell/utility/shell_content_utility_client.cc
@@ -6,8 +6,8 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" +#include "content/common/mojo/static_application_loader.h" #include "content/public/test/test_mojo_app.h" -#include "mojo/shell/static_application_loader.h" namespace content {
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index ef28230..6132d49 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -837,6 +837,7 @@ sources = [ "../common/gpu/client/gl_helper_unittest.cc", "../common/gpu/client/gpu_in_process_context_tests.cc", + "run_all_gl_tests.cc", ] deps = [ @@ -872,6 +873,7 @@ test("content_gl_benchmark") { sources = [ "../common/gpu/client/gl_helper_benchmark.cc", + "run_gl_benchmark.cc", ] deps = [ @@ -923,6 +925,7 @@ "//gpu:test_support", "//media/base/android", "//media/base/android:media_java", + "//media/capture/video/android:capture_java", "//testing/gmock", "//ui/android:ui_java", ]
diff --git a/content/test/data/accessibility/event/listbox-focus-expected-mac.txt b/content/test/data/accessibility/event/listbox-focus-expected-mac.txt index 7843b85..f8b4127b 100644 --- a/content/test/data/accessibility/event/listbox-focus-expected-mac.txt +++ b/content/test/data/accessibility/event/listbox-focus-expected-mac.txt
@@ -1 +1,3 @@ AXFocusedUIElementChanged on AXList +AXFocusedUIElementChanged on AXList +AXFocusedUIElementChanged on AXList
diff --git a/content/test/data/accessibility/event/listbox-next-expected-mac.txt b/content/test/data/accessibility/event/listbox-next-expected-mac.txt index f2a806dc..ff42ff9 100644 --- a/content/test/data/accessibility/event/listbox-next-expected-mac.txt +++ b/content/test/data/accessibility/event/listbox-next-expected-mac.txt
@@ -1,3 +1,5 @@ +AXFocusedUIElementChanged on AXList +AXFocusedUIElementChanged on AXList AXValueChanged on AXStaticText AXValue="Orange" AXSelectedChildrenChanged on AXList AXValueChanged on AXStaticText AXValue="Apple"
diff --git a/content/test/data/accessibility/event/menulist-next-expected-win.txt b/content/test/data/accessibility/event/menulist-next-expected-win.txt index 8e6d5392..6a2b0f0 100644 --- a/content/test/data/accessibility/event/menulist-next-expected-win.txt +++ b/content/test/data/accessibility/event/menulist-next-expected-win.txt
@@ -2,4 +2,4 @@ EVENT_OBJECT_STATECHANGE on role=ROLE_SYSTEM_LISTITEM name="Apple" INVISIBLE,FOCUSABLE,SELECTABLE EVENT_OBJECT_STATECHANGE on role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSABLE,SELECTABLE EVENT_OBJECT_SELECTION on role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSABLE,SELECTABLE -EVENT_OBJECT_FOCUS on role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE +EVENT_OBJECT_FOCUS on role=ROLE_SYSTEM_LISTITEM name="Orange" SELECTED,FOCUSABLE,SELECTABLE
diff --git a/content/test/data/media/mediarecorder_test.html b/content/test/data/media/mediarecorder_test.html index bd242c6..077f5ae 100644 --- a/content/test/data/media/mediarecorder_test.html +++ b/content/test/data/media/mediarecorder_test.html
@@ -49,6 +49,7 @@ remotePeerConnection.createAnswer(function(description) { remotePeerConnection.setLocalDescription(description); localPeerConnection.setRemoteDescription(description); + }, function(error) { }); } @@ -77,6 +78,7 @@ localPeerConnection.setLocalDescription(description); remotePeerConnection.setRemoteDescription(description); createAnswer(description); + }, function(error) { }); document.getElementById("video").src = URL.createObjectURL(localStream);
diff --git a/content/test/data/media/peerconnection-call.html b/content/test/data/media/peerconnection-call.html index bf2459c0..2d4c4c4 100644 --- a/content/test/data/media/peerconnection-call.html +++ b/content/test/data/media/peerconnection-call.html
@@ -812,9 +812,11 @@ connectOnIceCandidate(caller, callee); caller.createOffer( - function (offer) { - onOfferCreated(offer, caller, callee); - }); + function (offer) { + onOfferCreated(offer, caller, callee); + }, + function(error) {} + ); } function iceCandidateIsLoopback(candidate) { @@ -902,7 +904,10 @@ callee.createAnswer( function (answer) { onAnswerCreated(answer, caller, callee); - }); + }, + function(error) { + } + ); }, onRemoteDescriptionError); }
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py index 4c640db..0e41bc4 100755 --- a/content/test/gpu/generate_buildbot_json.py +++ b/content/test/gpu/generate_buildbot_json.py
@@ -416,6 +416,16 @@ 'swarming': True, 'os_type': 'mac', }, + 'Optional Mac 10.10 Retina Release (AMD)': { + 'swarming_dimensions': { + 'gpu': '1002:6821', + 'hidpi': '1', + 'os': 'Mac-10.10' + }, + 'build_config': 'Release', + 'swarming': True, + 'os_type': 'mac', + }, 'Optional Linux Release (NVIDIA)': { 'swarming_dimensions': { 'gpu': '10de:104a',
diff --git a/content/test/gpu_memory_buffer_impl_test_template.h b/content/test/gpu_memory_buffer_impl_test_template.h deleted file mode 100644 index 41f5060..0000000 --- a/content/test/gpu_memory_buffer_impl_test_template.h +++ /dev/null
@@ -1,207 +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. - -// This file defines tests that implementations of GpuMemoryBufferFactory should -// pass in order to be conformant. - -#ifndef CONTENT_TEST_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_ -#define CONTENT_TEST_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_ - -#include <stddef.h> -#include <string.h> - -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/buffer_format_util.h" - -namespace content { - -template <typename GpuMemoryBufferImplType> -class GpuMemoryBufferImplTest : public testing::Test { - public: - GpuMemoryBufferImpl::DestructionCallback AllocateGpuMemoryBuffer( - const gfx::Size& size, - gfx::BufferFormat format, - gfx::BufferUsage usage, - gfx::GpuMemoryBufferHandle* handle, - bool* destroyed) { - return base::Bind(&GpuMemoryBufferImplTest::FreeGpuMemoryBuffer, - base::Unretained(this), - GpuMemoryBufferImplType::AllocateForTesting( - size, format, usage, handle), - base::Unretained(destroyed)); - } - - private: - void FreeGpuMemoryBuffer(const base::Closure& free_callback, - bool* destroyed, - const gpu::SyncToken& sync_token) { - free_callback.Run(); - if (destroyed) - *destroyed = true; - } -}; - -TYPED_TEST_CASE_P(GpuMemoryBufferImplTest); - -TYPED_TEST_P(GpuMemoryBufferImplTest, CreateFromHandle) { - const gfx::Size kBufferSize(8, 8); - - for (auto format : gfx::GetBufferFormatsForTesting()) { - gfx::BufferUsage usages[] = { - gfx::BufferUsage::GPU_READ, gfx::BufferUsage::SCANOUT, - gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, - gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT}; - for (auto usage : usages) { - if (!TypeParam::IsConfigurationSupported(format, usage)) - continue; - - bool destroyed = false; - gfx::GpuMemoryBufferHandle handle; - GpuMemoryBufferImpl::DestructionCallback destroy_callback = - TestFixture::AllocateGpuMemoryBuffer(kBufferSize, format, usage, - &handle, &destroyed); - scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( - handle, kBufferSize, format, usage, destroy_callback)); - ASSERT_TRUE(buffer); - EXPECT_EQ(buffer->GetFormat(), format); - - // Check if destruction callback is executed when deleting the buffer. - buffer.reset(); - ASSERT_TRUE(destroyed); - } - } -} - -TYPED_TEST_P(GpuMemoryBufferImplTest, Map) { - // Use a multiple of 4 for both dimensions to support compressed formats. - const gfx::Size kBufferSize(4, 4); - - for (auto format : gfx::GetBufferFormatsForTesting()) { - if (!TypeParam::IsConfigurationSupported( - format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE)) { - continue; - } - - gfx::GpuMemoryBufferHandle handle; - GpuMemoryBufferImpl::DestructionCallback destroy_callback = - TestFixture::AllocateGpuMemoryBuffer( - kBufferSize, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, - &handle, nullptr); - scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( - handle, kBufferSize, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, - destroy_callback)); - ASSERT_TRUE(buffer); - - const size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format); - - // Map buffer into user space. - ASSERT_TRUE(buffer->Map()); - - // Copy and compare mapped buffers. - for (size_t plane = 0; plane < num_planes; ++plane) { - const size_t row_size_in_bytes = - gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); - EXPECT_GT(row_size_in_bytes, 0u); - - scoped_ptr<char[]> data(new char[row_size_in_bytes]); - memset(data.get(), 0x2a + plane, row_size_in_bytes); - - size_t height = kBufferSize.height() / - gfx::SubsamplingFactorForBufferFormat(format, plane); - for (size_t y = 0; y < height; ++y) { - memcpy(static_cast<char*>(buffer->memory(plane)) + - y * buffer->stride(plane), - data.get(), row_size_in_bytes); - EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + - y * buffer->stride(plane), - data.get(), row_size_in_bytes)); - } - } - - buffer->Unmap(); - } -} - -TYPED_TEST_P(GpuMemoryBufferImplTest, PersistentMap) { - // Use a multiple of 4 for both dimensions to support compressed formats. - const gfx::Size kBufferSize(4, 4); - - for (auto format : gfx::GetBufferFormatsForTesting()) { - if (!TypeParam::IsConfigurationSupported( - format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT)) { - continue; - } - - gfx::GpuMemoryBufferHandle handle; - GpuMemoryBufferImpl::DestructionCallback destroy_callback = - TestFixture::AllocateGpuMemoryBuffer( - kBufferSize, format, - gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT, &handle, - nullptr); - scoped_ptr<TypeParam> buffer(TypeParam::CreateFromHandle( - handle, kBufferSize, format, - gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT, - destroy_callback)); - ASSERT_TRUE(buffer); - - // Map buffer into user space. - ASSERT_TRUE(buffer->Map()); - - // Copy and compare mapped buffers. - size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format); - for (size_t plane = 0; plane < num_planes; ++plane) { - const size_t row_size_in_bytes = - gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); - EXPECT_GT(row_size_in_bytes, 0u); - - scoped_ptr<char[]> data(new char[row_size_in_bytes]); - memset(data.get(), 0x2a + plane, row_size_in_bytes); - - size_t height = kBufferSize.height() / - gfx::SubsamplingFactorForBufferFormat(format, plane); - for (size_t y = 0; y < height; ++y) { - memcpy(static_cast<char*>(buffer->memory(plane)) + - y * buffer->stride(plane), - data.get(), row_size_in_bytes); - EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + - y * buffer->stride(plane), - data.get(), row_size_in_bytes)); - } - } - - buffer->Unmap(); - - // Remap the buffer, and compare again. It should contain the same data. - ASSERT_TRUE(buffer->Map()); - - for (size_t plane = 0; plane < num_planes; ++plane) { - const size_t row_size_in_bytes = - gfx::RowSizeForBufferFormat(kBufferSize.width(), format, plane); - - scoped_ptr<char[]> data(new char[row_size_in_bytes]); - memset(data.get(), 0x2a + plane, row_size_in_bytes); - - size_t height = kBufferSize.height() / - gfx::SubsamplingFactorForBufferFormat(format, plane); - for (size_t y = 0; y < height; ++y) { - EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) + - y * buffer->stride(plane), - data.get(), row_size_in_bytes)); - } - } - - buffer->Unmap(); - } -} - -// The GpuMemoryBufferImplTest test case verifies behavior that is expected -// from a GpuMemoryBuffer implementation in order to be conformant. -REGISTER_TYPED_TEST_CASE_P(GpuMemoryBufferImplTest, - CreateFromHandle, - Map, - PersistentMap); - -} // namespace content - -#endif // CONTENT_TEST_GPU_MEMORY_BUFFER_IMPL_TEST_TEMPLATE_H_
diff --git a/content/test/run_all_gl_tests.cc b/content/test/run_all_gl_tests.cc new file mode 100644 index 0000000..bae2dd4 --- /dev/null +++ b/content/test/run_all_gl_tests.cc
@@ -0,0 +1,39 @@ +// 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/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "build/build_config.h" +#include "content/public/test/unittest_test_suite.h" +#include "content/test/content_test_suite.h" + +#if defined(OS_MACOSX) +#include "base/mac/scoped_nsautorelease_pool.h" +#endif + +namespace { + +int RunHelper(base::TestSuite* test_suite) { + content::UnitTestTestSuite runner(test_suite); + base::MessageLoopForIO message_loop; + return runner.Run(); +} + +} // namespace + +// These tests needs to run against a proper GL environment, so we +// need to set it up before we can run the tests. +int main(int argc, char** argv) { + base::CommandLine::Init(argc, argv); + base::TestSuite* suite = new content::ContentTestSuite(argc, argv); +#if defined(OS_MACOSX) + base::mac::ScopedNSAutoreleasePool pool; +#endif + + return base::LaunchUnitTestsSerially( + argc, + argv, + base::Bind(&RunHelper, base::Unretained(suite))); +}
diff --git a/content/test/run_gl_benchmark.cc b/content/test/run_gl_benchmark.cc new file mode 100644 index 0000000..0a67a6c --- /dev/null +++ b/content/test/run_gl_benchmark.cc
@@ -0,0 +1,22 @@ +// 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/command_line.h" +#include "build/build_config.h" +#include "content/public/test/unittest_test_suite.h" +#include "content/test/content_test_suite.h" + +#if defined(OS_MACOSX) +#include "base/mac/scoped_nsautorelease_pool.h" +#endif + +int main(int argc, char** argv) { + base::CommandLine::Init(argc, argv); + base::TestSuite* suite = new content::ContentTestSuite(argc, argv); +#if defined(OS_MACOSX) + base::mac::ScopedNSAutoreleasePool pool; +#endif + + return content::UnitTestTestSuite(suite).Run(); +}
diff --git a/content/utility/BUILD.gn b/content/utility/BUILD.gn index 8bb058a..9aea9db2 100644 --- a/content/utility/BUILD.gn +++ b/content/utility/BUILD.gn
@@ -31,7 +31,7 @@ "//url", ] - if (enable_mojo_media == "utility") { - deps += [ "//media/mojo/services:application" ] + if (mojo_media_host == "utility") { + deps += [ "//media/mojo/services:application_factory" ] } }
diff --git a/content/utility/utility_process_control_impl.cc b/content/utility/utility_process_control_impl.cc index 1a886e1..422b0a45 100644 --- a/content/utility/utility_process_control_impl.cc +++ b/content/utility/utility_process_control_impl.cc
@@ -5,14 +5,14 @@ #include "content/utility/utility_process_control_impl.h" #include "base/bind.h" +#include "content/common/mojo/static_application_loader.h" #include "content/public/common/content_client.h" #include "content/public/utility/content_utility_client.h" #include "content/public/utility/utility_thread.h" #include "content/utility/utility_thread_impl.h" -#include "mojo/shell/static_application_loader.h" #if defined(ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS) -#include "media/mojo/services/mojo_media_application.h" +#include "media/mojo/services/mojo_media_application_factory.h" #endif namespace content { @@ -38,14 +38,13 @@ GetContentClient()->utility()->RegisterMojoApplications(&apps); for (const auto& entry : apps) { - map_ref[entry.first] = new mojo::shell::StaticApplicationLoader( + map_ref[entry.first] = new StaticApplicationLoader( entry.second, base::Bind(&QuitProcess)); } #if defined(ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS) - map_ref[GURL("mojo:media")] = new mojo::shell::StaticApplicationLoader( - base::Bind(&media::MojoMediaApplication::CreateApp), - base::Bind(&QuitProcess)); + map_ref[GURL("mojo:media")] = new StaticApplicationLoader( + base::Bind(&media::CreateMojoMediaApplication), base::Bind(&QuitProcess)); #endif }
diff --git a/device/usb/usb.gyp b/device/usb/usb.gyp index 7f5e676..ee6d34f 100644 --- a/device/usb/usb.gyp +++ b/device/usb/usb.gyp
@@ -11,6 +11,7 @@ 'target_name': 'device_usb', 'type': 'static_library', 'dependencies': [ + 'device_usb_mojo_bindings', '../../components/components.gyp:device_event_log_component', '../../net/net.gyp:net', '../../third_party/libusb/libusb.gyp:libusb',
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_api_socket.cc b/extensions/browser/api/bluetooth_socket/bluetooth_api_socket.cc index 9bee22d..c1a400463 100644 --- a/extensions/browser/api/bluetooth_socket/bluetooth_api_socket.cc +++ b/extensions/browser/api/bluetooth_socket/bluetooth_api_socket.cc
@@ -129,7 +129,8 @@ device::BluetoothSocket::ErrorReason reason, const std::string& message) { DCHECK_CURRENTLY_ON(kThreadId); - BluetoothApiSocket::ErrorReason error_reason; + BluetoothApiSocket::ErrorReason error_reason = + BluetoothApiSocket::kSystemError; switch (reason) { case device::BluetoothSocket::kIOPending: error_reason = BluetoothApiSocket::kIOPending;
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json index 2d52ff4..494731d 100644 --- a/extensions/common/api/_permission_features.json +++ b/extensions/common/api/_permission_features.json
@@ -272,6 +272,7 @@ "B11A93E7E5B541F8010245EBDE2C74647D6C14B9", // Canary "F155646B5D1CA545F7E1E4E20D573DFDD44C2540", // Google Cast Beta "16CA7A47AAE4BE49B1E75A6B960C3875E945B264", // Google Cast Stable + "226CF815E39A363090A1E547D53063472B8279FA", // http://crbug.com/588179 "7AE714FFD394E073F0294CFA134C9F91DB5FBAA4", // CCD Development "C7DA3A55C2355F994D3FDDAD120B426A0DF63843", // CCD Testing "75E3CFFFC530582C583E4690EF97C70B9C8423B7", // CCD Release @@ -477,7 +478,8 @@ "F155646B5D1CA545F7E1E4E20D573DFDD44C2540", // http://crbug.com/500075 "FA01E0B81978950F2BC5A50512FD769725F57510", // http://crbug.com/500075 "B11A93E7E5B541F8010245EBDE2C74647D6C14B9", // http://crbug.com/500075 - "63ED55E43214C211F82122ED56407FF1A807F2A3" // http://crbug.com/500075 + "63ED55E43214C211F82122ED56407FF1A807F2A3", // http://crbug.com/500075 + "226CF815E39A363090A1E547D53063472B8279FA" // http://crbug.com/500075 ] }], "webRequest": {
diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc index 41e1d27..77e5ad1 100644 --- a/extensions/common/switches.cc +++ b/extensions/common/switches.cc
@@ -54,6 +54,9 @@ // Enables extensions to hide bookmarks UI elements. const char kEnableOverrideBookmarksUI[] = "enable-override-bookmarks-ui"; +// Enables tab for desktop sharing. +const char kEnableTabForDesktopShare[] = "enable-tab-for-desktop-share"; + // Allows the ErrorConsole to collect runtime and manifest errors, and display // them in the chrome:extensions page. const char kErrorConsole[] = "error-console";
diff --git a/extensions/common/switches.h b/extensions/common/switches.h index 7c891d8..af2a0f4 100644 --- a/extensions/common/switches.h +++ b/extensions/common/switches.h
@@ -23,6 +23,7 @@ extern const char kEnableMojoSerialService[]; extern const char kEnableOverrideBookmarksUI[]; extern const char kEnableBLEAdvertising[]; +extern const char kEnableTabForDesktopShare[]; extern const char kErrorConsole[]; extern const char kExtensionActionRedesign[]; extern const char kExtensionProcess[];
diff --git a/extensions/shell/common/shell_content_client.cc b/extensions/shell/common/shell_content_client.cc index 946fe83..e180da8 100644 --- a/extensions/shell/common/shell_content_client.cc +++ b/extensions/shell/common/shell_content_client.cc
@@ -83,6 +83,7 @@ void ShellContentClient::AddAdditionalSchemes( std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* savable_schemes) { for (int i = 0; i < kNumShellStandardURLSchemes; i++) standard_schemes->push_back(kShellStandardURLSchemes[i]);
diff --git a/extensions/shell/common/shell_content_client.h b/extensions/shell/common/shell_content_client.h index f105511d..471272a 100644 --- a/extensions/shell/common/shell_content_client.h +++ b/extensions/shell/common/shell_content_client.h
@@ -20,6 +20,7 @@ void AddPepperPlugins( std::vector<content::PepperPluginInfo>* plugins) override; void AddAdditionalSchemes(std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* saveable_shemes) override; std::string GetUserAgent() const override; base::string16 GetLocalizedString(int message_id) const override;
diff --git a/extensions/test/extensions_unittests_main.cc b/extensions/test/extensions_unittests_main.cc index bb9cc00..f0ea868 100644 --- a/extensions/test/extensions_unittests_main.cc +++ b/extensions/test/extensions_unittests_main.cc
@@ -39,6 +39,7 @@ // content::ContentClient overrides: void AddAdditionalSchemes( std::vector<url::SchemeWithType>* standard_schemes, + std::vector<url::SchemeWithType>* referrer_schemes, std::vector<std::string>* savable_schemes) override { for (int i = 0; i < kNumExtensionStandardURLSchemes; i++) standard_schemes->push_back(kExtensionStandardURLSchemes[i]);
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index 0bf669a4..c3f82eb 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn
@@ -166,6 +166,7 @@ "command_buffer/tests/gl_program_unittest.cc", "command_buffer/tests/gl_query_unittest.cc", "command_buffer/tests/gl_readback_unittest.cc", + "command_buffer/tests/gl_request_extension_unittest.cc", "command_buffer/tests/gl_shared_resources_unittest.cc", "command_buffer/tests/gl_stream_draw_unittest.cc", "command_buffer/tests/gl_test_utils.cc",
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h index e3451faa..aa7febf 100644 --- a/gpu/GLES2/gl2chromium_autogen.h +++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -118,6 +118,7 @@ #define glGetShaderPrecisionFormat GLES2_GET_FUN(GetShaderPrecisionFormat) #define glGetShaderSource GLES2_GET_FUN(GetShaderSource) #define glGetString GLES2_GET_FUN(GetString) +#define glGetStringi GLES2_GET_FUN(GetStringi) #define glGetSynciv GLES2_GET_FUN(GetSynciv) #define glGetTexParameterfv GLES2_GET_FUN(GetTexParameterfv) #define glGetTexParameteriv GLES2_GET_FUN(GetTexParameteriv)
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 5fcec4e..8f755ec 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1452,6 +1452,14 @@ 'GL_EXTENSIONS', ], }, + 'IndexedStringType': { + 'type': 'GLenum', + 'is_complete': True, + 'valid': [ + 'GL_EXTENSIONS', + ], + }, + 'TextureParameter': { 'type': 'GLenum', 'valid': [ @@ -3230,6 +3238,12 @@ 'client_test': False, 'cmd_args': 'GLenumStringType name, uint32_t bucket_id', }, + 'GetStringi': { + 'type': 'Custom', + 'client_test': False, + 'gen_cmd': False, + 'unsafe': True, + }, 'GetSynciv': { 'type': 'GETn', 'cmd_args': 'GLuint sync, GLenumSyncParameter pname, void* values',
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h index 5cafe6e..e35f6d1 100644 --- a/gpu/command_buffer/client/gles2_c_lib_autogen.h +++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -522,6 +522,9 @@ const GLubyte* GL_APIENTRY GLES2GetString(GLenum name) { return gles2::GetGLContext()->GetString(name); } +const GLubyte* GL_APIENTRY GLES2GetStringi(GLenum name, GLuint index) { + return gles2::GetGLContext()->GetStringi(name, index); +} void GL_APIENTRY GLES2GetSynciv(GLsync sync, GLenum pname, GLsizei bufsize, @@ -2100,6 +2103,9 @@ "glGetString", reinterpret_cast<GLES2FunctionPointer>(glGetString), }, { + "glGetStringi", reinterpret_cast<GLES2FunctionPointer>(glGetStringi), + }, + { "glGetSynciv", reinterpret_cast<GLES2FunctionPointer>(glGetSynciv), }, {
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index fc051b1..aeaa5655 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -18,6 +18,7 @@ #include <sstream> #include <string> #include "base/compiler_specific.h" +#include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/sys_info.h" #include "base/thread_task_runner_handle.h" @@ -131,6 +132,7 @@ gpu_control_(gpu_control), capabilities_(gpu_control->GetCapabilities()), aggressively_free_resources_(false), + cached_extension_string_(nullptr), weak_ptr_factory_(this) { DCHECK(helper); DCHECK(transfer_buffer); @@ -950,7 +952,8 @@ *params = capabilities_.minor_version; return true; case GL_NUM_EXTENSIONS: - *params = capabilities_.num_extensions; + UpdateCachedExtensionsIfNeeded(); + *params = cached_extensions_.size(); return true; case GL_NUM_PROGRAM_BINARY_FORMATS: *params = capabilities_.num_program_binary_formats; @@ -3292,6 +3295,9 @@ } const GLubyte* GLES2Implementation::GetStringHelper(GLenum name) { + if (name == GL_EXTENSIONS && cached_extension_string_) { + return reinterpret_cast<const GLubyte*>(cached_extension_string_); + } const char* result = NULL; // Clears the bucket so if the command fails nothing will be in it. helper_->SetBucketSize(kResultBucketId, 0); @@ -3299,40 +3305,33 @@ std::string str; if (GetBucketAsString(kResultBucketId, &str)) { // Adds extensions implemented on client side only. - switch (name) { - case GL_EXTENSIONS: - str += std::string(str.empty() ? "" : " ") + - "GL_EXT_unpack_subimage " - "GL_CHROMIUM_map_sub"; - if (capabilities_.image) - str += " GL_CHROMIUM_image GL_CHROMIUM_gpu_memory_buffer_image"; - if (capabilities_.future_sync_points) - str += " GL_CHROMIUM_future_sync_point"; - break; - default: - break; + if (name == GL_EXTENSIONS) { + str += std::string(str.empty() ? "" : " ") + + "GL_EXT_unpack_subimage " + "GL_CHROMIUM_map_sub"; + if (capabilities_.image) + str += " GL_CHROMIUM_image GL_CHROMIUM_gpu_memory_buffer_image"; + if (capabilities_.future_sync_points) + str += " GL_CHROMIUM_future_sync_point"; } // Because of WebGL the extensions can change. We have to cache each unique // result since we don't know when the client will stop referring to a // previous one it queries. - GLStringMap::iterator it = gl_strings_.find(name); - if (it == gl_strings_.end()) { - std::set<std::string> strings; - std::pair<GLStringMap::iterator, bool> insert_result = - gl_strings_.insert(std::make_pair(name, strings)); - DCHECK(insert_result.second); - it = insert_result.first; - } - std::set<std::string>& string_set = it->second; - std::set<std::string>::const_iterator sit = string_set.find(str); - if (sit != string_set.end()) { - result = sit->c_str(); - } else { - std::pair<std::set<std::string>::const_iterator, bool> insert_result = - string_set.insert(str); - DCHECK(insert_result.second); - result = insert_result.first->c_str(); + // TODO: Here we could save memory by defining RequestExtensions + // invalidating the GL_EXTENSIONS string. http://crbug.com/586414 + const std::string& cache = *gl_strings_.insert(str).first; + result = cache.c_str(); + + if (name == GL_EXTENSIONS) { + cached_extension_string_ = result; + std::vector<std::string> extensions = + base::SplitString(cache, base::kWhitespaceASCII, + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + for (const std::string& extension : extensions) { + cached_extensions_.push_back( + gl_strings_.insert(extension).first->c_str()); + } } } return reinterpret_cast<const GLubyte*>(result); @@ -3349,6 +3348,28 @@ return result; } +const GLubyte* GLES2Implementation::GetStringi(GLenum name, GLuint index) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glGetStringi(" + << GLES2Util::GetStringStringType(name) << "," << index + << ")"); + TRACE_EVENT0("gpu", "GLES2::GetStringi"); + UpdateCachedExtensionsIfNeeded(); + if (name != GL_EXTENSIONS) { + SetGLError(GL_INVALID_ENUM, "glGetStringi", "name"); + return nullptr; + } + if (index >= cached_extensions_.size()) { + SetGLError(GL_INVALID_VALUE, "glGetStringi", "index too large"); + return nullptr; + } + + const char* result = cached_extensions_[index]; + GPU_CLIENT_LOG(" returned " << result); + CheckGLError(); + return reinterpret_cast<const GLubyte*>(result); +} + bool GLES2Implementation::GetTransformFeedbackVaryingHelper( GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) { @@ -4815,16 +4836,9 @@ // them. Because we don't know when the client will stop referring // to a previous one it queries (see GetString) we need to cache // the unique results. - std::set<std::string>::const_iterator sit = - requestable_extensions_set_.find(str); - if (sit != requestable_extensions_set_.end()) { - result = sit->c_str(); - } else { - std::pair<std::set<std::string>::const_iterator, bool> insert_result = - requestable_extensions_set_.insert(str); - DCHECK(insert_result.second); - result = insert_result.first->c_str(); - } + // TODO: Here we could save memory by defining RequestExtensions + // invalidating the GL_EXTENSIONS string. http://crbug.com/586414 + result = gl_strings_.insert(str).first->c_str(); } GPU_CLIENT_LOG(" returned " << result); return reinterpret_cast<const GLchar*>(result); @@ -4836,6 +4850,7 @@ GPU_CLIENT_SINGLE_THREAD_CHECK(); GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glRequestExtensionCHROMIUM(" << extension << ")"); + InvalidateCachedExtensions(); SetBucketAsCString(kResultBucketId, extension); helper_->RequestExtensionCHROMIUM(kResultBucketId); helper_->SetBucketSize(kResultBucketId, 0); @@ -6547,6 +6562,18 @@ CheckGLError(); } +void GLES2Implementation::UpdateCachedExtensionsIfNeeded() { + if (cached_extension_string_) { + return; + } + GetStringHelper(GL_EXTENSIONS); +} + +void GLES2Implementation::InvalidateCachedExtensions() { + cached_extension_string_ = nullptr; + cached_extensions_.clear(); +} + // Include the auto-generated part of this file. We split this because it means // we can easily edit the non-auto generated parts right here in this file // instead of having to edit some template or the code generator.
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h index 1e4d7ad2..e32b3c15 100644 --- a/gpu/command_buffer/client/gles2_implementation.h +++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -690,6 +690,8 @@ void DrawElementsImpl(GLenum mode, GLsizei count, GLenum type, const void* indices, const char* func_name); + void UpdateCachedExtensionsIfNeeded(); + void InvalidateCachedExtensions(); GLES2Util util_; GLES2CmdHelper* helper_; @@ -788,14 +790,9 @@ // constrained. const uint32_t max_extra_transfer_buffer_size_; - // Map of GLenum to Strings for glGetString. We need to cache these because + // Set of strings returned from glGetString. We need to cache these because // the pointer passed back to the client has to remain valid for eternity. - typedef std::map<uint32_t, std::set<std::string>> GLStringMap; - GLStringMap gl_strings_; - - // Similar cache for glGetRequestableExtensionsCHROMIUM. We don't - // have an enum for this so handle it separately. - std::set<std::string> requestable_extensions_set_; + std::set<std::string> gl_strings_; typedef std::map<const void*, MappedBuffer> MappedBufferMap; MappedBufferMap mapped_buffers_; @@ -829,6 +826,16 @@ // whether it should aggressively free them. bool aggressively_free_resources_; + // Result of last GetString(GL_EXTENSIONS), used to keep + // GetString(GL_EXTENSIONS), GetStringi(GL_EXTENSIONS, index) and + // GetIntegerv(GL_NUM_EXTENSIONS) in sync. This points to gl_strings, valid + // forever. + const char* cached_extension_string_; + + // Populated if cached_extension_string_ != nullptr. These point to + // gl_strings, valid forever. + std::vector<const char*> cached_extensions_; + base::WeakPtrFactory<GLES2Implementation> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(GLES2Implementation);
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h index 9a4bf4e..2a4914f 100644 --- a/gpu/command_buffer/client/gles2_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -381,6 +381,8 @@ const GLubyte* GetString(GLenum name) override; +const GLubyte* GetStringi(GLenum name, GLuint index) override; + void GetSynciv(GLsync sync, GLenum pname, GLsizei bufsize,
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h index 6837d6b..6cd390d 100644 --- a/gpu/command_buffer/client/gles2_interface_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -285,6 +285,7 @@ GLsizei* length, char* source) = 0; virtual const GLubyte* GetString(GLenum name) = 0; +virtual const GLubyte* GetStringi(GLenum name, GLuint index) = 0; virtual void GetSynciv(GLsync sync, GLenum pname, GLsizei bufsize,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h index 75def7d..84611a6 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -280,6 +280,7 @@ GLsizei* length, char* source) override; const GLubyte* GetString(GLenum name) override; +const GLubyte* GetStringi(GLenum name, GLuint index) override; void GetSynciv(GLsync sync, GLenum pname, GLsizei bufsize,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h index 304cafd..b43c773 100644 --- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -341,6 +341,10 @@ const GLubyte* GLES2InterfaceStub::GetString(GLenum /* name */) { return 0; } +const GLubyte* GLES2InterfaceStub::GetStringi(GLenum /* name */, + GLuint /* index */) { + return 0; +} void GLES2InterfaceStub::GetSynciv(GLsync /* sync */, GLenum /* pname */, GLsizei /* bufsize */,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h index ee71c58..24c96c6 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -280,6 +280,7 @@ GLsizei* length, char* source) override; const GLubyte* GetString(GLenum name) override; +const GLubyte* GetStringi(GLenum name, GLuint index) override; void GetSynciv(GLsync sync, GLenum pname, GLsizei bufsize,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h index e07c703..bc2a21bb 100644 --- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h +++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -738,6 +738,11 @@ return gl_->GetString(name); } +const GLubyte* GLES2TraceImplementation::GetStringi(GLenum name, GLuint index) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::GetStringi"); + return gl_->GetStringi(name, index); +} + void GLES2TraceImplementation::GetSynciv(GLsync sync, GLenum pname, GLsizei bufsize,
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt index bbf73e1..809a841 100644 --- a/gpu/command_buffer/cmd_buffer_functions.txt +++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -108,6 +108,7 @@ GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat (GLenumShaderType shadertype, GLenumShaderPrecision precisiontype, GLint* range, GLint* precision); GL_APICALL void GL_APIENTRY glGetShaderSource (GLidShader shader, GLsizeiNotNegative bufsize, GLsizeiOptional* length, char* source); GL_APICALL const GLubyte* GL_APIENTRY glGetString (GLenumStringType name); +GL_APICALL const GLubyte* GL_APIENTRY glGetStringi (GLenumIndexedStringType name, GLuint index); GL_APICALL void GL_APIENTRY glGetSynciv (GLsync sync, GLenumSyncParameter pname, GLsizeiNotNegative bufsize, GLsizeiOptional* length, GLint* values); GL_APICALL void GL_APIENTRY glGetTexParameterfv (GLenumGetTexParamTarget target, GLenumTextureParameter pname, GLfloat* params); GL_APICALL void GL_APIENTRY glGetTexParameteriv (GLenumGetTexParamTarget target, GLenumTextureParameter pname, GLint* params);
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h index a0563f8..1ba42ab 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_utils_autogen.h
@@ -44,6 +44,7 @@ static std::string GetStringIndexType(uint32_t value); static std::string GetStringIndexedBufferTarget(uint32_t value); static std::string GetStringIndexedGLState(uint32_t value); +static std::string GetStringIndexedStringType(uint32_t value); static std::string GetStringInternalFormatParameter(uint32_t value); static std::string GetStringMapBufferAccess(uint32_t value); static std::string GetStringMatrixMode(uint32_t value);
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h index 86c181bb..0260dd5 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -4049,6 +4049,14 @@ arraysize(string_table), value); } +std::string GLES2Util::GetStringIndexedStringType(uint32_t value) { + static const EnumToString string_table[] = { + {GL_EXTENSIONS, "GL_EXTENSIONS"}, + }; + return GLES2Util::GetQualifiedEnumString(string_table, + arraysize(string_table), value); +} + std::string GLES2Util::GetStringInternalFormatParameter(uint32_t value) { static const EnumToString string_table[] = { {GL_NUM_SAMPLE_COUNTS, "GL_NUM_SAMPLE_COUNTS"},
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc index c749422..fd237c3 100644 --- a/gpu/command_buffer/service/feature_info.cc +++ b/gpu/command_buffer/service/feature_info.cc
@@ -654,10 +654,13 @@ validators_.read_pixel_format.AddValue(GL_BGRA_EXT); } - // We only support timer queries if we also support glGetInteger64v. - // For GL_EXT_disjoint_timer_query, glGetInteger64v is only support under ES3. - if ((gl_version_info_->is_es3 && - extensions.Contains("GL_EXT_disjoint_timer_query")) || + // glGetInteger64v for timestamps is implemented on the client side in a way + // that it does not depend on a driver-level implementation of + // glGetInteger64v. The GPUTimer class which implements timer queries can also + // fallback to an implementation that does not depend on glGetInteger64v on + // ES2. Thus we can enable GL_EXT_disjoint_timer_query on ES2 contexts even + // though it does not support glGetInteger64v due to a specification bug. + if (extensions.Contains("GL_EXT_disjoint_timer_query") || extensions.Contains("GL_ARB_timer_query") || extensions.Contains("GL_EXT_timer_query")) { AddExtensionString("GL_EXT_disjoint_timer_query");
diff --git a/gpu/command_buffer/tests/es3_misc_functions_unittest.cc b/gpu/command_buffer/tests/es3_misc_functions_unittest.cc index 6b3ffa96..634bf1d 100644 --- a/gpu/command_buffer/tests/es3_misc_functions_unittest.cc +++ b/gpu/command_buffer/tests/es3_misc_functions_unittest.cc
@@ -8,6 +8,7 @@ #include <GLES3/gl3.h> #include "base/command_line.h" +#include "base/strings/string_split.h" #include "gpu/command_buffer/tests/gl_manager.h" #include "gpu/command_buffer/tests/gl_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" @@ -87,4 +88,26 @@ glDeleteProgram(program); } +TEST_F(OpenGLES3FunctionTest, GetStringiTest) { + if (!IsApplicable()) { + return; + } + std::string extensionString = + reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + std::vector<std::string> extensions = + base::SplitString(extensionString, base::kWhitespaceASCII, + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + int num_extensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + EXPECT_EQ(extensions.size(), static_cast<size_t>(num_extensions)); + std::set<std::string> extensions_from_string(extensions.begin(), + extensions.end()); + std::set<std::string> extensions_from_stringi; + for (int i = 0; i < num_extensions; ++i) { + extensions_from_stringi.insert( + reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))); + } + EXPECT_EQ(extensions_from_string, extensions_from_stringi); +} + } // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_request_extension_unittest.cc b/gpu/command_buffer/tests/gl_request_extension_unittest.cc new file mode 100644 index 0000000..c0a581f --- /dev/null +++ b/gpu/command_buffer/tests/gl_request_extension_unittest.cc
@@ -0,0 +1,105 @@ +// 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 <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES2/gl2extchromium.h> +#include <GLES3/gl3.h> + +#include "base/command_line.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "gpu/command_buffer/tests/gl_manager.h" +#include "gpu/command_buffer/tests/gl_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/gl_switches.h" + +namespace gpu { + +class RequestExtensionCHROMIUMTest + : public testing::TestWithParam<gles2::ContextType> { + protected: + void SetUp() override { + base::CommandLine command_line(*base::CommandLine::ForCurrentProcess()); + command_line.AppendSwitch(switches::kEnableUnsafeES3APIs); + GLManager::Options options; + options.context_type = GetParam(); + gl_.InitializeWithCommandLine(options, &command_line); + } + void TearDown() override { gl_.Destroy(); } + bool IsApplicable() const { return gl_.IsInitialized(); } + GLManager gl_; +}; + +TEST_P(RequestExtensionCHROMIUMTest, Basic) { + if (!IsApplicable()) { + return; + } + // Test to test that requesting an extension does not delete any extensions. + // Also tests that WebGL2 and GLES3 contexts keep glGetString(GL_EXTENSIONS) + // and glGetStringi(GL_EXTENSIONS, index) in sync. + + std::string requestable_extensions_string = + reinterpret_cast<const char*>(glGetRequestableExtensionsCHROMIUM()); + std::vector<std::string> requestable = + base::SplitString(requestable_extensions_string, base::kWhitespaceASCII, + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + for (const auto& to_request : requestable) { + std::string extension_string = + reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + extension_string += " "; + size_t extensions_size_before_request = + base::SplitString(extension_string, base::kWhitespaceASCII, + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY) + .size(); + + if (extension_string.find(to_request + " ") != std::string::npos) { + // Somewhat counterintuitively, requestable extensions contain every + // extension available. + continue; + } + + glRequestExtensionCHROMIUM(to_request.c_str()); + + extension_string = + reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + std::vector<std::string> extensions = + base::SplitString(extension_string, base::kWhitespaceASCII, + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + std::set<std::string> extensions_from_string(extensions.begin(), + extensions.end()); + + if (GetParam() == gles2::CONTEXT_TYPE_WEBGL2 || + GetParam() == gles2::CONTEXT_TYPE_OPENGLES3) { + // Test that GetString(GL_EXTENSIONS) is consistent with + // GetStringi(GL_EXTENSIONS, index) + GLint num_extensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + EXPECT_EQ(static_cast<size_t>(num_extensions), extensions.size()); + std::set<std::string> extensions_from_stringi; + for (int i = 0; i < num_extensions; ++i) { + extensions_from_stringi.insert( + reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))); + } + EXPECT_EQ(extensions_from_string, extensions_from_stringi); + } + + // Somewhat counterintuitively, requesting an extension that is offered + // does not necessarily make it appear in the extensions list. + // This is due to the fact that offered extensions list contains every + // available extension, while WebGL2 filters extension list + // based on what's in core WebGL2. + + // Test that RequestExtension does not erase any extensions. + EXPECT_GE(extensions.size(), extensions_size_before_request); + } +} +INSTANTIATE_TEST_CASE_P(WithContextTypes, + RequestExtensionCHROMIUMTest, + ::testing::Values(gles2::CONTEXT_TYPE_WEBGL1, + gles2::CONTEXT_TYPE_WEBGL2, + gles2::CONTEXT_TYPE_OPENGLES2, + gles2::CONTEXT_TYPE_OPENGLES3)); +}
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc index 165da5d..b15ece3 100644 --- a/gpu/config/gpu_driver_bug_list_json.cc +++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -19,7 +19,7 @@ { "name": "gpu driver bug list", // Please update the version number whenever you change this file. - "version": "8.44", + "version": "8.45", "entries": [ { "id": 1, @@ -1449,7 +1449,7 @@ "type": "android" }, "driver_version": { - "op": "=", + "op": ">=", "value": "103.0" }, "gl_renderer": "Adreno \\(TM\\) 4.*",
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp index eef53827..2ad775d 100644 --- a/gpu/gpu.gyp +++ b/gpu/gpu.gyp
@@ -367,6 +367,7 @@ 'command_buffer/tests/gl_program_unittest.cc', 'command_buffer/tests/gl_query_unittest.cc', 'command_buffer/tests/gl_readback_unittest.cc', + 'command_buffer/tests/gl_request_extension_unittest.cc', 'command_buffer/tests/gl_shared_resources_unittest.cc', 'command_buffer/tests/gl_stream_draw_unittest.cc', 'command_buffer/tests/gl_test_utils.cc', @@ -422,6 +423,7 @@ 'type': 'shared_library', 'dependencies': [ '../base/base.gyp:base', + '../gpu/command_buffer/command_buffer.gyp:gles2_utils', '../gpu/gpu.gyp:command_buffer_service', '../ui/gfx/gfx.gyp:gfx_geometry', '../ui/gl/gl.gyp:gl',
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index 268fd2c..cde0c980 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg
@@ -16,6 +16,7 @@ verifiers { reviewer_lgtm { committer_list: "external/project-chromium-committers" + dry_run_access_list: "project-chromium-tryjob-access" } tree_status {
diff --git a/ios/build/bots/chromium.mac/iOS_Device_GN.json b/ios/build/bots/chromium.mac/iOS_Device_GN.json index 776160f..1325aa5 100644 --- a/ios/build/bots/chromium.mac/iOS_Device_GN.json +++ b/ios/build/bots/chromium.mac/iOS_Device_GN.json
@@ -9,15 +9,20 @@ "xcode version": "7.0", "GYP_DEFINES": { "chromium_ios_signing": "0", - "target_subarch": "both" + "gomadir": "$(goma_dir)", + "target_subarch": "both", + "use_goma": "1" }, "gn_args": [ + "goma_dir=\"$(goma_dir)\"", "ios_enable_code_signing=false", "target_cpu=\"arm\"", - "target_os=\"ios\"" + "target_os=\"ios\"", + "use_goma=true" ], "mb_type": "gn", "compiler": "ninja", + "additional_compile_targets": ["gn_all"], "configuration": "Release", "sdk": "iphoneos9.0", "tests": [
diff --git "a/ios/build/bots/chromium.mac/iOS_Simulator_GN_\050dbg\051.json" "b/ios/build/bots/chromium.mac/iOS_Simulator_GN_\050dbg\051.json" index 7ca333e..c6a9dfbe 100644 --- "a/ios/build/bots/chromium.mac/iOS_Simulator_GN_\050dbg\051.json" +++ "b/ios/build/bots/chromium.mac/iOS_Simulator_GN_\050dbg\051.json"
@@ -9,13 +9,18 @@ "xcode version": "7.0", "GYP_DEFINES": { "chromium_ios_signing": "0", - "target_subarch": "both" + "gomadir": "$(goma_dir)", + "target_subarch": "both", + "use_goma": "1" }, "gn_args": [ - "target_os=\"ios\"" + "goma_dir=\"$(goma_dir)\"", + "target_os=\"ios\"", + "use_goma=true" ], "mb_type": "gn", "compiler": "ninja", + "additional_compile_targets": ["gn_all"], "configuration": "Debug", "sdk": "iphonesimulator9.0", "tests": [
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_gn.json b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_gn.json index 7ca333e..5c90caa 100644 --- a/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_gn.json +++ b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_gn.json
@@ -9,10 +9,14 @@ "xcode version": "7.0", "GYP_DEFINES": { "chromium_ios_signing": "0", - "target_subarch": "both" + "gomadir": "$(goma_dir)", + "target_subarch": "both", + "use_goma": "1" }, "gn_args": [ - "target_os=\"ios\"" + "goma_dir=\"$(goma_dir)\"", + "target_os=\"ios\"", + "use_goma=true" ], "mb_type": "gn", "compiler": "ninja",
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_rel_device_gn.json b/ios/build/bots/tryserver.chromium.mac/ios_rel_device_gn.json index 776160f..e87edf8c 100644 --- a/ios/build/bots/tryserver.chromium.mac/ios_rel_device_gn.json +++ b/ios/build/bots/tryserver.chromium.mac/ios_rel_device_gn.json
@@ -9,12 +9,16 @@ "xcode version": "7.0", "GYP_DEFINES": { "chromium_ios_signing": "0", - "target_subarch": "both" + "gomadir": "$(gomadir)", + "target_subarch": "both", + "use_goma": "1" }, "gn_args": [ + "goma_dir=\"$(goma_dir)\"", "ios_enable_code_signing=false", "target_cpu=\"arm\"", - "target_os=\"ios\"" + "target_os=\"ios\"", + "use_goma=true" ], "mb_type": "gn", "compiler": "ninja",
diff --git a/ios/chrome/browser/application_context_impl.cc b/ios/chrome/browser/application_context_impl.cc index 0ee0b9c..9fc8e8a2 100644 --- a/ios/chrome/browser/application_context_impl.cc +++ b/ios/chrome/browser/application_context_impl.cc
@@ -32,12 +32,10 @@ #include "components/update_client/configurator.h" #include "components/update_client/update_query_params.h" #include "components/variations/service/variations_service.h" -#include "components/web_resource/promo_resource_service.h" #include "components/web_resource/web_resource_pref_names.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.h" #include "ios/chrome/browser/chrome_paths.h" -#include "ios/chrome/browser/chrome_switches.h" #include "ios/chrome/browser/component_updater/ios_component_updater_configurator.h" #include "ios/chrome/browser/history/history_service_factory.h" #include "ios/chrome/browser/ios_chrome_io_thread.h" @@ -55,12 +53,6 @@ #include "net/socket/client_socket_pool_manager.h" #include "net/url_request/url_request_context_getter.h" -namespace { -// Dummy flag because iOS does not support disabling background networking. -extern const char kDummyDisableBackgroundNetworking[] = - "dummy-disable-background-networking"; -} - ApplicationContextImpl::ApplicationContextImpl( base::SequencedTaskRunner* local_state_task_runner, const base::CommandLine& command_line, @@ -107,21 +99,11 @@ void ApplicationContextImpl::PreMainMessageLoopRun() { DCHECK(thread_checker_.CalledOnValidThread()); - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - if (!command_line.HasSwitch(switches::kDisableIOSWebResources)) { - DCHECK(!promo_resource_service_.get()); - promo_resource_service_.reset(new web_resource::PromoResourceService( - GetLocalState(), ::GetChannel(), GetApplicationLocale(), - GetSystemURLRequestContext(), kDummyDisableBackgroundNetworking, - web_resource::GetIOSChromeParseJSONCallback())); - promo_resource_service_->StartAfterDelay(); - } } void ApplicationContextImpl::StartTearDown() { DCHECK(thread_checker_.CalledOnValidThread()); - // We need to destroy the MetricsServicesManager, PromoResourceService and + // We need to destroy the MetricsServicesManager and // SafeBrowsing before the IO thread gets destroyed, since their destructors // can call the URLFetcher destructor, which does a PostDelayedTask operation // on the IO thread. (The IO thread will handle that URLFetcher operation @@ -134,10 +116,6 @@ // Need to clear browser states before the IO thread. chrome_browser_state_manager_.reset(); - // PromoResourceService must be destroyed after the keyed services and before - // the IO thread. - promo_resource_service_.reset(); - // The GCMDriver must shut down while the IO thread is still alive. if (gcm_driver_) gcm_driver_->Shutdown(); @@ -298,7 +276,7 @@ web_resource::PromoResourceService* ApplicationContextImpl::GetPromoResourceService() { DCHECK(thread_checker_.CalledOnValidThread()); - return promo_resource_service_.get(); + return nullptr; } component_updater::ComponentUpdateService*
diff --git a/ios/chrome/browser/application_context_impl.h b/ios/chrome/browser/application_context_impl.h index ac4b244e..02bd11c5 100644 --- a/ios/chrome/browser/application_context_impl.h +++ b/ios/chrome/browser/application_context_impl.h
@@ -85,7 +85,6 @@ scoped_ptr<metrics_services_manager::MetricsServicesManager> metrics_services_manager_; scoped_ptr<gcm::GCMDriver> gcm_driver_; - scoped_ptr<web_resource::PromoResourceService> promo_resource_service_; scoped_ptr<component_updater::ComponentUpdateService> component_updater_; scoped_refptr<CRLSetFetcher> crl_set_fetcher_; scoped_refptr<safe_browsing::SafeBrowsingService> safe_browsing_service_;
diff --git a/ios/chrome/browser/chrome_switches.cc b/ios/chrome/browser/chrome_switches.cc index 2a23c651..c9223b47 100644 --- a/ios/chrome/browser/chrome_switches.cc +++ b/ios/chrome/browser/chrome_switches.cc
@@ -31,9 +31,6 @@ const char kDisableIOSPasswordSuggestions[] = "disable-ios-password-suggestions"; -// Disables the backend service for web resources. -const char kDisableIOSWebResources[] = "disable-web-resources"; - // Disables the use of WKWebView instead of UIWebView. const char kDisableIOSWKWebView[] = "disable-wkwebview";
diff --git a/ios/chrome/browser/chrome_switches.h b/ios/chrome/browser/chrome_switches.h index e2acb17..627574a 100644 --- a/ios/chrome/browser/chrome_switches.h +++ b/ios/chrome/browser/chrome_switches.h
@@ -14,7 +14,6 @@ extern const char kDisableIOSFeatures[]; extern const char kDisableIOSPasswordGeneration[]; extern const char kDisableIOSPasswordSuggestions[]; -extern const char kDisableIOSWebResources[]; extern const char kDisableIOSWKWebView[]; extern const char kDisableLRUSnapshotCache[]; extern const char kDisableNTPFavicons[];
diff --git a/ios/chrome/browser/passwords/resources/credential_manager.js b/ios/chrome/browser/passwords/resources/credential_manager.js index 8e2e8407..c2e8a29 100644 --- a/ios/chrome/browser/passwords/resources/credential_manager.js +++ b/ios/chrome/browser/passwords/resources/credential_manager.js
@@ -505,8 +505,8 @@ /** * Requests a credential from the credential manager. - * @param {{suppressUI: boolean, federations: Array<string>}=} opt_options An - * optional dictionary of parameters for the request. If |suppressUI| is + * @param {{unmediated: boolean, federations: Array<string>}=} opt_options An + * optional dictionary of parameters for the request. If |unmediated| is * true, the returned promise will only be resolved with a credential if * this is possible without user interaction; otherwise, the returned * promise will be resolved with |undefined|. |federations| specifies a @@ -517,7 +517,7 @@ */ CredentialsContainer.prototype.request = function(opt_options) { var options = { - 'suppressUI': !!opt_options && !!opt_options['suppressUI'], + 'unmediated': !!opt_options && !!opt_options['unmediated'], 'federations': (!!opt_options && opt_options['federations']) || [] }; return __gCrWeb['credentialManager'].invokeOnHost_(
diff --git a/ios/chrome/browser/ui/commands/ios_command_ids.h b/ios/chrome/browser/ui/commands/ios_command_ids.h index c4e15b09..c04703a2 100644 --- a/ios/chrome/browser/ui/commands/ios_command_ids.h +++ b/ios/chrome/browser/ui/commands/ios_command_ids.h
@@ -12,6 +12,7 @@ // If you change a given command's number, any NIB files that refer to it will // also need to be updated. +// clang-format off #define IDC_BACK 33000 #define IDC_FORWARD 33001 #define IDC_RELOAD 33002 @@ -72,5 +73,7 @@ #define IDC_SHOW_SAVE_PASSWORDS_SETTINGS 40945 #define IDC_READER_MODE 40947 #define IDC_RATE_THIS_APP 40948 +#define IDC_ADD_READING_LIST 40949 +// clang-format on #endif // IOS_CHROME_BROWSER_UI_COMMANDS_IOS_COMMAND_IDS_H_
diff --git a/ios/chrome/browser/ui/rtl_geometry.mm b/ios/chrome/browser/ui/rtl_geometry.mm index 25df7564..0ff4660 100644 --- a/ios/chrome/browser/ui/rtl_geometry.mm +++ b/ios/chrome/browser/ui/rtl_geometry.mm
@@ -121,7 +121,7 @@ LayoutRect leadingLayout; leadingLayout.position.leading = 0; leadingLayout.boundingWidth = layout.boundingWidth; - leadingLayout.position.originY = leadingLayout.position.originY; + leadingLayout.position.originY = layout.position.originY; leadingLayout.size = CGSizeMake(layout.position.leading, layout.size.height); return leadingLayout; } @@ -131,7 +131,7 @@ CGFloat trailing = LayoutRectGetTrailingEdge(layout); leadingLayout.position.leading = trailing; leadingLayout.boundingWidth = layout.boundingWidth; - leadingLayout.position.originY = leadingLayout.position.originY; + leadingLayout.position.originY = layout.position.originY; leadingLayout.size = CGSizeMake((layout.boundingWidth - trailing), layout.size.height); return leadingLayout;
diff --git a/ios/chrome/browser/ui/uikit_ui_util.h b/ios/chrome/browser/ui/uikit_ui_util.h index 3173be2..bbaf8ce 100644 --- a/ios/chrome/browser/ui/uikit_ui_util.h +++ b/ios/chrome/browser/ui/uikit_ui_util.h
@@ -169,55 +169,69 @@ UIColor* secondColor, CGFloat fraction); -// Applies all |constraints| to all views in |subviewsDictionary| in the -// superview |parentView|. +// General note on the following constraint utility functions: +// Directly adding constraints to views has been deprecated in favor of just +// activating constrainst since iOS8. All of these methods now use +// [NSLayoutConstraint activateConstraints:] for efficiency. The superview +// arguments are thus superfluous, but the methods that use them are retained +// here for backwards compatibility until all downstream code can be updated. + +// Applies all |constraints| to views in |subviewsDictionary|. +void ApplyVisualConstraints(NSArray* constraints, + NSDictionary* subviewsDictionary); +// Deprecated version: void ApplyVisualConstraints(NSArray* constraints, NSDictionary* subviewsDictionary, - UIView* parentView); + UIView* unused_parentView); -// Applies all |constraints| with |options| to all views in |subviewsDictionary| -// in the superview |parentView|. +// Applies all |constraints| with |options| to views in |subviewsDictionary|. +void ApplyVisualConstraintsWithOptions(NSArray* constraints, + NSDictionary* subviewsDictionary, + NSLayoutFormatOptions options); +// Deprecated version: void ApplyVisualConstraintsWithOptions(NSArray* constraints, NSDictionary* subviewsDictionary, NSLayoutFormatOptions options, - UIView* parentView); + UIView* unused_parentView); -// Applies all |constraints| with |metrics| to all views in |subviewsDictionary| -// in the superview |parentView| +// Applies all |constraints| with |metrics| to views in |subviewsDictionary|. +void ApplyVisualConstraintsWithMetrics(NSArray* constraints, + NSDictionary* subviewsDictionary, + NSDictionary* metrics); +// Deprecated version: void ApplyVisualConstraintsWithMetrics(NSArray* constraints, NSDictionary* subviewsDictionary, NSDictionary* metrics, - UIView* parentView); + UIView* unused_parentView); -// Applies all |constraints| with |metrics| and |options| to all views in -// |subviewsDictionary| in the superview |parentView| +// Applies all |constraints| with |metrics| and |options| to views in +// |subviewsDictionary|. +void ApplyVisualConstraintsWithMetricsAndOptions( + NSArray* constraints, + NSDictionary* subviewsDictionary, + NSDictionary* metrics, + NSLayoutFormatOptions options); +// Deprecated version: void ApplyVisualConstraintsWithMetricsAndOptions( NSArray* constraints, NSDictionary* subviewsDictionary, NSDictionary* metrics, NSLayoutFormatOptions options, - UIView* parentView); + UIView* unused_parentView); -// Adds a constraint that |subview| is center aligned horizontally in -// |parentView|. -// |subview| must be a subview of |parentView|. -void AddSameCenterXConstraint(UIView* parentView, UIView* subview); +// Adds a constraint that |view1| and |view2| are center-aligned horizontally. +// |view1| and |view2| must be in the same view hierarchy. +void AddSameCenterXConstraint(UIView* view1, UIView* view2); +// Deprecated version: +void AddSameCenterXConstraint(UIView* unused_parentView, + UIView* subview1, + UIView* subview2); -// Adds a constraint that |subview1| and |subview2| are center aligned -// horizontally on |parentView|. -// |subview1| and |subview2| must be subview of |parentView|. -void AddSameCenterXConstraint(UIView *parentView, UIView *subview1, - UIView *subview2); - -// Adds a constraint that |subview| is center aligned vertically in -// |parentView|. -// |subview| must be a subview of |parentView|. -void AddSameCenterYConstraint(UIView* parentView, UIView* subview); - -// Adds a constraint that |subview1| and |subview2| are center aligned -// vertically on |parentView|. -// |subview1| and |subview2| must be subview of |parentView|. -void AddSameCenterYConstraint(UIView* parentView, +// Adds a constraint that |view1| and |view2| are center-aligned vertically. +// |view1| and |view2| must be in the same view hierarchy. +void AddSameCenterYConstraint(UIView* view1, UIView* view2); +// Deprecated version: +void AddSameCenterYConstraint(UIView* unused_parentView, UIView* subview1, UIView* subview2);
diff --git a/ios/chrome/browser/ui/uikit_ui_util.mm b/ios/chrome/browser/ui/uikit_ui_util.mm index 72a45f7..47cc2f8e 100644 --- a/ios/chrome/browser/ui/uikit_ui_util.mm +++ b/ios/chrome/browser/ui/uikit_ui_util.mm
@@ -15,6 +15,7 @@ #include "base/ios/ios_util.h" #include "base/logging.h" #include "base/mac/foundation_util.h" +#include "base/mac/scoped_nsobject.h" #include "ios/chrome/browser/experimental_flags.h" #include "ios/chrome/browser/ui/ui_util.h" #include "ios/web/public/web_thread.h" @@ -470,26 +471,62 @@ } void ApplyVisualConstraints(NSArray* constraints, - NSDictionary* subviewsDictionary, - UIView* parentView) { + NSDictionary* subviewsDictionary) { ApplyVisualConstraintsWithMetricsAndOptions(constraints, subviewsDictionary, - nil, 0, parentView); + nil, 0); +} + +void ApplyVisualConstraints(NSArray* constraints, + NSDictionary* subviewsDictionary, + UIView* unused_parentView) { + ApplyVisualConstraints(constraints, subviewsDictionary); +} + +void ApplyVisualConstraintsWithOptions(NSArray* constraints, + NSDictionary* subviewsDictionary, + NSLayoutFormatOptions options) { + ApplyVisualConstraintsWithMetricsAndOptions(constraints, subviewsDictionary, + nil, options); } void ApplyVisualConstraintsWithOptions(NSArray* constraints, NSDictionary* subviewsDictionary, NSLayoutFormatOptions options, - UIView* parentView) { + UIView* unused_parentView) { + ApplyVisualConstraintsWithOptions(constraints, subviewsDictionary, options); +} + +void ApplyVisualConstraintsWithMetrics(NSArray* constraints, + NSDictionary* subviewsDictionary, + NSDictionary* metrics) { ApplyVisualConstraintsWithMetricsAndOptions(constraints, subviewsDictionary, - nil, options, parentView); + metrics, 0); } void ApplyVisualConstraintsWithMetrics(NSArray* constraints, NSDictionary* subviewsDictionary, NSDictionary* metrics, - UIView* parentView) { - ApplyVisualConstraintsWithMetricsAndOptions(constraints, subviewsDictionary, - metrics, 0, parentView); + UIView* unused_parentView) { + ApplyVisualConstraintsWithMetrics(constraints, subviewsDictionary, metrics); +} + +void ApplyVisualConstraintsWithMetricsAndOptions( + NSArray* constraints, + NSDictionary* subviewsDictionary, + NSDictionary* metrics, + NSLayoutFormatOptions options) { + base::scoped_nsobject<NSMutableArray> layoutConstraints( + [[NSMutableArray arrayWithCapacity:constraints.count * 3] retain]); + for (NSString* constraint in constraints) { + DCHECK([constraint isKindOfClass:[NSString class]]); + [layoutConstraints addObjectsFromArray: + [NSLayoutConstraint + constraintsWithVisualFormat:constraint + options:options + metrics:metrics + views:subviewsDictionary]]; + } + [NSLayoutConstraint activateConstraints:layoutConstraints]; } void ApplyVisualConstraintsWithMetricsAndOptions( @@ -497,71 +534,31 @@ NSDictionary* subviewsDictionary, NSDictionary* metrics, NSLayoutFormatOptions options, - UIView* parentView) { - for (NSString* constraint in constraints) { - DCHECK([constraint isKindOfClass:[NSString class]]); - [parentView - addConstraints:[NSLayoutConstraint - constraintsWithVisualFormat:constraint - options:options - metrics:metrics - views:subviewsDictionary]]; - } + UIView* unused_parentView) { + ApplyVisualConstraintsWithMetricsAndOptions(constraints, subviewsDictionary, + metrics, options); } -void AddSameCenterXConstraint(UIView* parentView, UIView* subview) { - DCHECK_EQ(parentView, [subview superview]); - [parentView addConstraint:[NSLayoutConstraint - constraintWithItem:subview - attribute:NSLayoutAttributeCenterX - relatedBy:NSLayoutRelationEqual - toItem:parentView - attribute:NSLayoutAttributeCenterX - multiplier:1 - constant:0]]; +void AddSameCenterXConstraint(UIView* view1, UIView* view2) { + [view1.centerXAnchor constraintEqualToAnchor:view2.centerXAnchor].active = + YES; } -void AddSameCenterXConstraint(UIView *parentView, UIView *subview1, - UIView *subview2) { - DCHECK_EQ(parentView, [subview1 superview]); - DCHECK_EQ(parentView, [subview2 superview]); - DCHECK_NE(subview1, subview2); - [parentView addConstraint:[NSLayoutConstraint - constraintWithItem:subview1 - attribute:NSLayoutAttributeCenterX - relatedBy:NSLayoutRelationEqual - toItem:subview2 - attribute:NSLayoutAttributeCenterX - multiplier:1 - constant:0]]; -} - -void AddSameCenterYConstraint(UIView* parentView, UIView* subview) { - DCHECK_EQ(parentView, [subview superview]); - [parentView addConstraint:[NSLayoutConstraint - constraintWithItem:subview - attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual - toItem:parentView - attribute:NSLayoutAttributeCenterY - multiplier:1 - constant:0]]; -} - -void AddSameCenterYConstraint(UIView* parentView, +void AddSameCenterXConstraint(UIView* unused_parentView, UIView* subview1, UIView* subview2) { - DCHECK_EQ(parentView, [subview1 superview]); - DCHECK_EQ(parentView, [subview2 superview]); - DCHECK_NE(subview1, subview2); - [parentView addConstraint:[NSLayoutConstraint - constraintWithItem:subview1 - attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual - toItem:subview2 - attribute:NSLayoutAttributeCenterY - multiplier:1 - constant:0]]; + AddSameCenterXConstraint(subview1, subview2); +} + +void AddSameCenterYConstraint(UIView* view1, UIView* view2) { + [view1.centerYAnchor constraintEqualToAnchor:view2.centerYAnchor].active = + YES; +} + +void AddSameCenterYConstraint(UIView* unused_parentView, + UIView* subview1, + UIView* subview2) { + AddSameCenterYConstraint(subview1, subview2); } bool IsCompact(id<UITraitEnvironment> environment) {
diff --git a/ios/third_party/gcdwebserver/BUILD.gn b/ios/third_party/gcdwebserver/BUILD.gn index 65d0d66..21b993c 100644 --- a/ios/third_party/gcdwebserver/BUILD.gn +++ b/ios/third_party/gcdwebserver/BUILD.gn
@@ -18,6 +18,11 @@ config("internal_config") { visibility = [ ":gcdwebserver" ] + + # TODO(crbug.com/569158): Suppresses warnings that are treated as errors + # when minimum iOS version support is increased to iOS 9 and up. + # This should be removed once all deprecation violations have been fixed. + cflags = [ "-Wno-deprecated-declarations" ] cflags_objc = [ "-fobjc-arc" ] }
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn index 013c02c..86238456 100644 --- a/ios/web/BUILD.gn +++ b/ios/web/BUILD.gn
@@ -343,6 +343,8 @@ "public/test/response_providers/file_based_response_provider_impl.mm", "public/test/response_providers/response_provider.h", "public/test/response_providers/response_provider.mm", + "public/test/scoped_Testing_web_client.h", + "public/test/scoped_Testing_web_client.mm", "public/test/test_browser_state.cc", "public/test/test_browser_state.h", "public/test/test_web_client.h",
diff --git a/ios/web/public/test/web_test_util.h b/ios/web/public/test/web_test_util.h index cdefd3ad3..fe93d073 100644 --- a/ios/web/public/test/web_test_util.h +++ b/ios/web/public/test/web_test_util.h
@@ -17,60 +17,4 @@ if (!web::IsWKWebViewSupported()) \ return -// Defines a web test that uses two test fixtures and single testing code. -// -// The first two parameters are the corresponding names of UIWebView-based and -// WKWebView-based test fixture classes. Those will also be the test case names. -// The third parameter is the name of the test within the test case. -// -// A test fixture class must be declared earlier. Test code goes between braces -// after using this macro. Example: -// -// typedef web::WebTest UIViewTest; -// typedef web::WKWebViewWebTest WKViewTest; -// -// WEB_TEST_F(UIViewTest, WKViewTest, ControllerInitializesCorrectly) { -// EXPECT_TRUE(this->webController_); -// } - -// Makes the name of a parent test class for WK and UI test fixtures. -// Only intended for use by WEB_TEST_F, not for direct use. -#define WEB_TEST_BASE_CLASS_(ui_fixture, wk_test_case, test_name)\ - ui_fixture##wk_test_case##_##test_name##_WebTest - -// Makes the name of a concrete WK or UI test fixture. -// Only intended for use by WEB_TEST_F, not for direct use. -#define WEB_TEST_TEST_CLASS_(test_case, test_name)\ - test_case##_##test_name##_WebTest - -// Makes the testing template class that runs WebTestBody function as the -// testing code. -// Only intended for use by WEB_TEST_F, not for direct use. -#define GTEST_WEB_TEST_(test_case, test_name, is_wk_web_view) \ - GTEST_TEST_(test_case, test_name, \ - WEB_TEST_TEST_CLASS_(test_case, test_name), \ - ::testing::internal::GetTypeId<test_case>()) { \ - if (!is_wk_web_view || web::IsWKWebViewSupported()) \ - WebTestBody(); \ - }; - -#define WEB_TEST_F(ui_fixture, wk_fixture, test_name) \ - template <typename T> \ - class WEB_TEST_BASE_CLASS_(ui_fixture, wk_fixture, test_name) \ - : public T { \ - protected: \ - void WebTestBody(); \ - }; \ - typedef WEB_TEST_BASE_CLASS_( \ - ui_fixture, wk_fixture, \ - test_name) <ui_fixture> WEB_TEST_TEST_CLASS_(ui_fixture, test_name); \ - typedef WEB_TEST_BASE_CLASS_( \ - ui_fixture, wk_fixture, \ - test_name) <wk_fixture> WEB_TEST_TEST_CLASS_(wk_fixture, test_name); \ - GTEST_WEB_TEST_(ui_fixture, test_name, false) \ - GTEST_WEB_TEST_(wk_fixture, test_name, true) \ - template <typename T> \ - void WEB_TEST_BASE_CLASS_(ui_fixture, wk_fixture, \ - test_name) <T>::WebTestBody() - #endif // IOS_WEB_PUBLIC_TEST_WEB_TEST_UTIL_H_
diff --git a/ios/web/public/url_schemes.h b/ios/web/public/url_schemes.h index ccbc99c4..6532916 100644 --- a/ios/web/public/url_schemes.h +++ b/ios/web/public/url_schemes.h
@@ -15,10 +15,10 @@ // // Called near the beginning of startup to register URL schemes that // should be parsed as "standard" with the src/url/ library. The set -// of standard scheme is locked if |lock_standard_schemes| is true. +// of standard scheme is locked if |lock_schemes| is true. // The embedder can add additional schemes by overriding the // WebClient::AddAditionalSchemes method. -void RegisterWebSchemes(bool lock_standard_schemes); +void RegisterWebSchemes(bool lock_schemes); } // namespace web
diff --git a/ios/web/public/url_schemes.mm b/ios/web/public/url_schemes.mm index 638ba7f..e501ee2 100644 --- a/ios/web/public/url_schemes.mm +++ b/ios/web/public/url_schemes.mm
@@ -18,19 +18,19 @@ } } // namespace -void RegisterWebSchemes(bool lock_standard_schemes) { +void RegisterWebSchemes(bool lock_schemes) { std::vector<url::SchemeWithType> additional_standard_schemes; GetWebClient()->AddAdditionalSchemes(&additional_standard_schemes); std::for_each(additional_standard_schemes.begin(), additional_standard_schemes.end(), AddStandardSchemeHelper); - // Prevent future modification of the standard schemes list. This is to - // prevent accidental creation of data races in the program. AddStandardScheme - // isn't threadsafe so must be called when GURL isn't used on any other - // thread. This is really easy to mess up, so we say that all calls to - // AddStandardScheme in Chrome must be inside this function. - if (lock_standard_schemes) - url::LockStandardSchemes(); + // Prevent future modification of the schemes lists. This is to prevent + // accidental creation of data races in the program. Add*Scheme aren't + // threadsafe so must be called when GURL isn't used on any other thread. This + // is really easy to mess up, so we say that all calls to Add*Scheme in Chrome + // must be inside this function. + if (lock_schemes) + url::LockSchemeRegistries(); } } // namespace web
diff --git a/ios/web/public/web_state/web_state_observer.h b/ios/web/public/web_state/web_state_observer.h index c56dde4..c48eed6 100644 --- a/ios/web/public/web_state/web_state_observer.h +++ b/ios/web/public/web_state/web_state_observer.h
@@ -88,7 +88,7 @@ virtual void FaviconUrlUpdated(const std::vector<FaviconURL>& candidates) {} // Notifies the observer that the credential manager API was invoked from - // |source_url| to request a credential from the browser. If |suppress_ui| + // |source_url| to request a credential from the browser. If |unmediated| // is true, the browser MUST NOT show any UI to the user. If this means that // no credential will be returned to the page, so be it. Otherwise, the // browser may show the user any UI that is necessary to get a Credential and @@ -98,7 +98,7 @@ // provide the specified |request_id|. virtual void CredentialsRequested(int request_id, const GURL& source_url, - bool suppress_ui, + bool unmediated, const std::vector<std::string>& federations, bool is_user_initiated) {}
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h index 78a0e2b..34ea9ee4 100644 --- a/ios/web/web_state/ui/crw_web_controller.h +++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -80,7 +80,6 @@ CRWRequestTrackerDelegate, CRWTouchTrackingDelegate, CRWWebControllerScripting, - UIActionSheetDelegate, UIGestureRecognizerDelegate> // Whether or not a UIWebView is allowed to exist in this CRWWebController.
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 8a2cccc..cb001f9 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -2312,9 +2312,9 @@ DLOG(WARNING) << "JS message parameter not found: requestId"; return NO; } - bool suppress_ui = false; - if (!message->GetBoolean("suppressUI", &suppress_ui)) { - DLOG(WARNING) << "JS message parameter not found: suppressUI"; + bool unmediated = false; + if (!message->GetBoolean("unmediated", &unmediated)) { + DLOG(WARNING) << "JS message parameter not found: unmediated"; return NO; } base::ListValue* federations_value = nullptr; @@ -2333,7 +2333,7 @@ } DCHECK(context[web::kUserIsInteractingKey]); _webStateImpl->OnCredentialsRequested( - request_id, net::GURLWithNSURL(context[web::kOriginURLKey]), suppress_ui, + request_id, net::GURLWithNSURL(context[web::kOriginURLKey]), unmediated, federations, [context[web::kUserIsInteractingKey] boolValue]); return YES; }
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm index ea12d40..4e9274f 100644 --- a/ios/web/web_state/ui/crw_web_controller_unittest.mm +++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -32,7 +32,6 @@ #import "ios/web/test/wk_web_view_crash_utils.h" #include "ios/web/web_state/blocked_popup_info.h" #import "ios/web/web_state/js/crw_js_invoke_parameter_queue.h" -#import "ios/web/web_state/ui/crw_ui_web_view_web_controller.h" #import "ios/web/web_state/ui/crw_web_controller+protected.h" #import "ios/web/web_state/ui/crw_web_controller_container_view.h" #import "ios/web/web_state/web_state_impl.h"
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h index cf4ae71..dab28e04 100644 --- a/ios/web/web_state/web_state_impl.h +++ b/ios/web/web_state/web_state_impl.h
@@ -119,7 +119,7 @@ // Called when the page requests a credential. void OnCredentialsRequested(int request_id, const GURL& source_url, - bool suppress_ui, + bool unmediated, const std::vector<std::string>& federations, bool user_interaction);
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm index 5f3914d..0c15a33f 100644 --- a/ios/web/web_state/web_state_impl.mm +++ b/ios/web/web_state/web_state_impl.mm
@@ -195,11 +195,11 @@ void WebStateImpl::OnCredentialsRequested( int request_id, const GURL& source_url, - bool suppress_ui, + bool unmediated, const std::vector<std::string>& federations, bool user_interaction) { FOR_EACH_OBSERVER(WebStateObserver, observers_, - CredentialsRequested(request_id, source_url, suppress_ui, + CredentialsRequested(request_id, source_url, unmediated, federations, user_interaction)); }
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn index 2451d6f..c3d3bb45 100644 --- a/ipc/BUILD.gn +++ b/ipc/BUILD.gn
@@ -91,6 +91,8 @@ "message_filter.h", "message_filter_router.cc", "message_filter_router.h", + "message_router.cc", + "message_router.h", "param_traits_log_macros.h", "param_traits_macros.h", "param_traits_read_macros.h",
diff --git a/ipc/ipc.gypi b/ipc/ipc.gypi index 6b7cf93..da570d2 100644 --- a/ipc/ipc.gypi +++ b/ipc/ipc.gypi
@@ -96,6 +96,8 @@ 'message_filter.h', 'message_filter_router.cc', 'message_filter_router.h', + 'message_router.cc', + 'message_router.h', 'param_traits_log_macros.h', 'param_traits_macros.h', 'param_traits_read_macros.h',
diff --git a/ipc/message_router.cc b/ipc/message_router.cc new file mode 100644 index 0000000..aaf4a1f --- /dev/null +++ b/ipc/message_router.cc
@@ -0,0 +1,55 @@ +// 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. + +#include "ipc/message_router.h" + +#include "ipc/ipc_message.h" + +namespace IPC { + +MessageRouter::MessageRouter() {} + +MessageRouter::~MessageRouter() {} + +bool MessageRouter::OnControlMessageReceived(const IPC::Message& msg) { + NOTREACHED() + << "should override in subclass if you care about control messages"; + return false; +} + +bool MessageRouter::Send(IPC::Message* msg) { + NOTREACHED() + << "should override in subclass if you care about sending messages"; + return false; +} + +bool MessageRouter::AddRoute(int32_t routing_id, IPC::Listener* listener) { + if (routes_.Lookup(routing_id)) { + DLOG(ERROR) << "duplicate routing ID"; + return false; + } + routes_.AddWithID(listener, routing_id); + return true; +} + +void MessageRouter::RemoveRoute(int32_t routing_id) { + routes_.Remove(routing_id); +} + +bool MessageRouter::OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) + return OnControlMessageReceived(msg); + + return RouteMessage(msg); +} + +bool MessageRouter::RouteMessage(const IPC::Message& msg) { + IPC::Listener* listener = routes_.Lookup(msg.routing_id()); + if (!listener) + return false; + + return listener->OnMessageReceived(msg); +} + +} // namespace IPC
diff --git a/ipc/message_router.h b/ipc/message_router.h new file mode 100644 index 0000000..db4bc27 --- /dev/null +++ b/ipc/message_router.h
@@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_MESSAGE_ROUTER_H_ +#define IPC_MESSAGE_ROUTER_H_ + +#include <stdint.h> + +#include "base/id_map.h" +#include "base/macros.h" +#include "ipc/ipc_export.h" +#include "ipc/ipc_listener.h" +#include "ipc/ipc_sender.h" + +// The MessageRouter handles all incoming messages sent to it by routing them +// to the correct listener. Routing is based on the Message's routing ID. +// Since routing IDs are typically assigned asynchronously by the browser +// process, the MessageRouter has the notion of pending IDs for listeners that +// have not yet been assigned a routing ID. +// +// When a message arrives, the routing ID is used to index the set of routes to +// find a listener. If a listener is found, then the message is passed to it. +// Otherwise, the message is ignored if its routing ID is not equal to +// MSG_ROUTING_CONTROL. +// +// The MessageRouter supports the IPC::Sender interface for outgoing messages, +// but does not define a meaningful implementation of it. The subclass of +// MessageRouter is intended to provide that if appropriate. +// +// The MessageRouter can be used as a concrete class provided its Send method +// is not called and it does not receive any control messages. + +namespace IPC { + +class IPC_EXPORT MessageRouter : public Listener, public Sender { + public: + MessageRouter(); + ~MessageRouter() override; + + // Implemented by subclasses to handle control messages + virtual bool OnControlMessageReceived(const Message& msg); + + // Listener implementation: + bool OnMessageReceived(const Message& msg) override; + + // Like OnMessageReceived, except it only handles routed messages. Returns + // true if the message was dispatched, or false if there was no listener for + // that route id. + virtual bool RouteMessage(const Message& msg); + + // Sender implementation: + bool Send(Message* msg) override; + + // Called to add a listener for a particular message routing ID. + // Returns true if succeeded. + bool AddRoute(int32_t routing_id, Listener* listener); + + // Called to remove a listener for a particular message routing ID. + void RemoveRoute(int32_t routing_id); + + private: + // A list of all listeners with assigned routing IDs. + IDMap<Listener> routes_; + + DISALLOW_COPY_AND_ASSIGN(MessageRouter); +}; + +} // namespace IPC + +#endif // IPC_MESSAGE_ROUTER_H_
diff --git a/ipc/mojo/scoped_ipc_support.cc b/ipc/mojo/scoped_ipc_support.cc index bcaa3a1..65947bec 100644 --- a/ipc/mojo/scoped_ipc_support.cc +++ b/ipc/mojo/scoped_ipc_support.cc
@@ -4,17 +4,9 @@ #include "ipc/mojo/scoped_ipc_support.h" -#include <stddef.h> - -#include "base/bind.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" -#include "base/thread_task_runner_handle.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/embedder/process_delegate.h" @@ -23,134 +15,34 @@ namespace { class IPCSupportInitializer : public mojo::edk::ProcessDelegate { public: - IPCSupportInitializer() - : init_count_(0), - shutting_down_(false), - was_shut_down_(false), - observer_(nullptr) {} + IPCSupportInitializer() {} + ~IPCSupportInitializer() override {} - ~IPCSupportInitializer() override { DCHECK(!observer_); } + void Init(scoped_refptr<base::TaskRunner> io_thread_task_runner) { + CHECK(!io_thread_task_runner_); + CHECK(io_thread_task_runner); + io_thread_task_runner_ = io_thread_task_runner; - void Init(scoped_refptr<base::TaskRunner> io_thread_task_runner); - void ShutDown(bool force); + mojo::edk::InitIPCSupport(this, io_thread_task_runner_); + } + + void ShutDown() { + CHECK(io_thread_task_runner_); + mojo::edk::ShutdownIPCSupport(); + } private: - // This watches for destruction of the MessageLoop that IPCSupportInitializer - // uses for IO, and guarantees that the initializer is shut down if it still - // exists when the loop is being destroyed. - class MessageLoopObserver : public base::MessageLoop::DestructionObserver { - public: - MessageLoopObserver(IPCSupportInitializer* initializer) - : initializer_(initializer) {} - - ~MessageLoopObserver() override { - base::MessageLoop::current()->RemoveDestructionObserver(this); - } - - private: - // base::MessageLoop::DestructionObserver: - void WillDestroyCurrentMessageLoop() override { - initializer_->ShutDown(true); - } - - IPCSupportInitializer* initializer_; - - DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver); - }; - - void ShutDownOnIOThread(); - // mojo::edk::ProcessDelegate: - void OnShutdownComplete() override {} - - static void WatchMessageLoopOnIOThread(MessageLoopObserver* observer); - - base::Lock lock_; - size_t init_count_; - bool shutting_down_; - - // This is used to track whether shutdown has occurred yet, since we can be - // shut down by either the scoper or IO MessageLoop destruction. - bool was_shut_down_; - - // The message loop destruction observer we have watching our IO loop. This - // is created on the initializer's own thread but is used and destroyed on the - // IO thread. - MessageLoopObserver* observer_; + void OnShutdownComplete() override { + // TODO(rockot): We should ensure that IO runner shutdown is blocked until + // this is called. + } scoped_refptr<base::TaskRunner> io_thread_task_runner_; DISALLOW_COPY_AND_ASSIGN(IPCSupportInitializer); }; -void IPCSupportInitializer::Init( - scoped_refptr<base::TaskRunner> io_thread_task_runner) { - base::AutoLock locker(lock_); - DCHECK((init_count_ == 0 && !io_thread_task_runner_) || - io_thread_task_runner_ == io_thread_task_runner); - - if (shutting_down_) { - // If reinitialized before a pending shutdown task is executed, we - // effectively cancel the shutdown task. - DCHECK(init_count_ == 1); - shutting_down_ = false; - return; - } - - init_count_++; - if (init_count_ == 1) { - was_shut_down_ = false; - observer_ = new MessageLoopObserver(this); - io_thread_task_runner_ = io_thread_task_runner; - io_thread_task_runner_->PostTask( - FROM_HERE, base::Bind(&WatchMessageLoopOnIOThread, observer_)); - mojo::edk::InitIPCSupport(this, io_thread_task_runner_); - } -} - -void IPCSupportInitializer::ShutDown(bool force) { - base::AutoLock locker(lock_); - if (shutting_down_ || was_shut_down_) - return; - DCHECK(init_count_ > 0); - if (init_count_ > 1 && !force) { - init_count_--; - return; - } - - shutting_down_ = true; - if (base::MessageLoop::current() && - base::MessageLoop::current()->task_runner() == io_thread_task_runner_) { - base::AutoUnlock unlocker_(lock_); - ShutDownOnIOThread(); - } else { - io_thread_task_runner_->PostTask( - FROM_HERE, base::Bind(&IPCSupportInitializer::ShutDownOnIOThread, - base::Unretained(this))); - } -} - -void IPCSupportInitializer::ShutDownOnIOThread() { - base::AutoLock locker(lock_); - if (shutting_down_ && !was_shut_down_) { - mojo::edk::ShutdownIPCSupportOnIOThread(); - init_count_ = 0; - shutting_down_ = false; - io_thread_task_runner_ = nullptr; - was_shut_down_ = true; - if (observer_) { - delete observer_; - observer_ = nullptr; - } - } -} - -// static -void IPCSupportInitializer::WatchMessageLoopOnIOThread( - MessageLoopObserver* observer) { - base::MessageLoop::current()->AddDestructionObserver(observer); -} - base::LazyInstance<IPCSupportInitializer>::Leaky ipc_support_initializer; } // namespace @@ -161,7 +53,7 @@ } ScopedIPCSupport::~ScopedIPCSupport() { - ipc_support_initializer.Get().ShutDown(false); + ipc_support_initializer.Get().ShutDown(); } } // namespace IPC
diff --git a/ipc/mojo/scoped_ipc_support.h b/ipc/mojo/scoped_ipc_support.h index 0ed1b4e6..29c50bf 100644 --- a/ipc/mojo/scoped_ipc_support.h +++ b/ipc/mojo/scoped_ipc_support.h
@@ -12,14 +12,12 @@ namespace IPC { -// Perform any necessary Mojo IPC initialization. A ScopedIPCSupport object -// should be instantiated and retained by any component which makes direct calls -// to the Mojo EDK. This is used to ensure that the EDK is initialized within -// the current process and that it is shutdown cleanly when no longer in use. +// Performs any necessary Mojo IPC initialization on construction, and shuts +// down Mojo IPC on destruction. // -// NOTE: Unless you are making explicit calls to functions in the -// mojo::edk namespace, you almost definitely DO NOT need this and should -// not be using it. +// This should be instantiated once per process and retained as long as Mojo IPC +// is needed. The TaskRunner passed to the constructor should outlive this +// object. class IPC_MOJO_EXPORT ScopedIPCSupport { public: ScopedIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner);
diff --git a/media/BUILD.gn b/media/BUILD.gn index b92755ac..1ebff098 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn
@@ -115,6 +115,10 @@ "capture/content/thread_safe_capture_oracle.h", "capture/content/video_capture_oracle.cc", "capture/content/video_capture_oracle.h", + "capture/video/android/video_capture_device_android.cc", + "capture/video/android/video_capture_device_android.h", + "capture/video/android/video_capture_device_factory_android.cc", + "capture/video/android/video_capture_device_factory_android.h", "capture/video/fake_video_capture_device.cc", "capture/video/fake_video_capture_device.h", "capture/video/fake_video_capture_device_factory.cc", @@ -125,10 +129,6 @@ "capture/video/file_video_capture_device_factory.h", "capture/video/linux/v4l2_capture_delegate.cc", "capture/video/linux/v4l2_capture_delegate.h", - "capture/video/linux/v4l2_capture_delegate_multi_plane.cc", - "capture/video/linux/v4l2_capture_delegate_multi_plane.h", - "capture/video/linux/v4l2_capture_delegate_single_plane.cc", - "capture/video/linux/v4l2_capture_delegate_single_plane.h", "capture/video/linux/video_capture_device_chromeos.cc", "capture/video/linux/video_capture_device_chromeos.h", "capture/video/linux/video_capture_device_factory_linux.cc", @@ -169,8 +169,6 @@ "capture/video/win/video_capture_device_mf_win.h", "capture/video/win/video_capture_device_win.cc", "capture/video/win/video_capture_device_win.h", - "capture/webm_muxer.cc", - "capture/webm_muxer.h", "cdm/aes_decryptor.cc", "cdm/aes_decryptor.h", "cdm/cdm_adapter.cc", @@ -278,6 +276,8 @@ "formats/webm/webm_video_client.h", "formats/webm/webm_webvtt_parser.cc", "formats/webm/webm_webvtt_parser.h", + "muxers/webm_muxer.cc", + "muxers/webm_muxer.h", "renderers/audio_renderer_impl.cc", "renderers/audio_renderer_impl.h", "renderers/default_renderer_factory.cc", @@ -391,10 +391,6 @@ ] } sources += [ - "capture/video/android/video_capture_device_android.cc", - "capture/video/android/video_capture_device_android.h", - "capture/video/android/video_capture_device_factory_android.cc", - "capture/video/android/video_capture_device_factory_android.h", "filters/android/media_codec_audio_decoder.cc", "filters/android/media_codec_audio_decoder.h", ] @@ -407,7 +403,7 @@ deps += [ "//media/base/android", "//media/base/android:media_jni_headers", - "//media/base/android:video_capture_jni_headers", + "//media/capture/video/android:capture_jni_headers", ] # Only 64 bit builds are using android-21 NDK library, check common.gypi @@ -581,6 +577,11 @@ "//third_party/opus", ] + # TODO(mcasas) Remove this after http://crbug.com/584797 + if (is_android) { + public_deps += [ "//media/capture/video/android" ] + } + deps += [ "//base", "//base:i18n", @@ -630,7 +631,6 @@ "capture/content/video_capture_oracle_unittest.cc", "capture/video/fake_video_capture_device_unittest.cc", "capture/video/video_capture_device_unittest.cc", - "capture/webm_muxer_unittest.cc", "cdm/aes_decryptor_unittest.cc", "cdm/external_clear_key_test_helper.cc", "cdm/external_clear_key_test_helper.h", @@ -677,6 +677,7 @@ "formats/webm/webm_parser_unittest.cc", "formats/webm/webm_tracks_parser_unittest.cc", "formats/webm/webm_webvtt_parser_unittest.cc", + "muxers/webm_muxer_unittest.cc", "renderers/audio_renderer_impl_unittest.cc", "renderers/renderer_impl_unittest.cc", "renderers/skcanvas_video_renderer_unittest.cc", @@ -725,6 +726,7 @@ deps += [ "//media/base/android:media_java", "//media/base/android:unittests", + "//media/capture/video/android:capture_java", "//ui/android:ui_java", ] }
diff --git a/media/DEPS b/media/DEPS index c3a4f49..29edae32 100644 --- a/media/DEPS +++ b/media/DEPS
@@ -8,7 +8,6 @@ "+jni", "+skia/ext", "+third_party/ffmpeg", - "+third_party/libwebm", "+third_party/libvpx_new", "+third_party/libyuv", "+third_party/opus",
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn index 1d1747c2..9c4177f 100644 --- a/media/base/BUILD.gn +++ b/media/base/BUILD.gn
@@ -249,9 +249,7 @@ if (is_android) { public_deps = [ "//media/base/android", - "//media/base/android:media_java", "//media/base/android:media_jni_headers", - "//media/base/android:video_capture_jni_headers", ] allow_circular_includes_from += [ "//media/base/android" ] }
diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn index 10bfdfe..7ac7d972 100644 --- a/media/base/android/BUILD.gn +++ b/media/base/android/BUILD.gn
@@ -115,27 +115,10 @@ jni_package = "media" } -generate_jni("video_capture_jni_headers") { - sources = [ - "java/src/org/chromium/media/VideoCapture.java", - "java/src/org/chromium/media/VideoCaptureFactory.java", - ] - jni_package = "media" -} - -java_cpp_enum("media_java_enums_srcjar") { - sources = [ - "//media/capture/video/android/video_capture_device_android.h", - "//media/capture/video/video_capture_device.h", - ] -} - android_library("media_java") { deps = [ "//base:base_java", ] - srcjar_deps = [ ":media_java_enums_srcjar" ] - DEPRECATED_java_in_dir = "java/src" }
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java index 1049828..83835836 100644 --- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java +++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -275,7 +275,11 @@ * @param mime MIME type as passed to mediaCodec.createDecoderByType(mime). * @return true if this codec is supported for decoder on this device. */ - private static boolean isDecoderSupportedForDevice(String mime) { + @CalledByNative + static boolean isDecoderSupportedForDevice(String mime) { + // ************************************************************* + // *** DO NOT ADD ANY NEW CODECS WITHOUT UPDATING MIME_UTIL. *** + // ************************************************************* if (mime.equals("video/x-vnd.on2.vp8")) { // Some Samsung devices cannot render VP8 video directly to the surface. if (Build.MANUFACTURER.toLowerCase(Locale.getDefault()).equals("samsung")) { @@ -297,7 +301,9 @@ } } } - + // ************************************************************* + // *** DO NOT ADD ANY NEW CODECS WITHOUT UPDATING MIME_UTIL. *** + // ************************************************************* return true; }
diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java b/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java index 0cac2e0..403860bb 100644 --- a/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java +++ b/media/base/android/java/src/org/chromium/media/MediaPlayerListener.java
@@ -25,6 +25,7 @@ private static final int MEDIA_ERROR_DECODE = 1; private static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 2; private static final int MEDIA_ERROR_INVALID_CODE = 3; + private static final int MEDIA_ERROR_SERVER_DIED = 4; // These values are copied from android media player. public static final int MEDIA_ERROR_MALFORMED = -1007; @@ -59,6 +60,9 @@ case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: errorType = MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK; break; + case MediaPlayer.MEDIA_ERROR_SERVER_DIED: + errorType = MEDIA_ERROR_SERVER_DIED; + break; default: // There are some undocumented error codes for android media player. // For example, when surfaceTexture got deleted before we setVideoSuface
diff --git a/media/base/android/java/src/org/chromium/media/OWNERS b/media/base/android/java/src/org/chromium/media/OWNERS deleted file mode 100644 index 85e4365..0000000 --- a/media/base/android/java/src/org/chromium/media/OWNERS +++ /dev/null
@@ -1,2 +0,0 @@ -# VideoCapture classes under Android -per-file VideoCapture*.java=mcasas@chromium.org
diff --git a/media/base/android/java/src/org/chromium/media/VideoCapture.java b/media/base/android/java/src/org/chromium/media/VideoCapture.java deleted file mode 100644 index bc8351f..0000000 --- a/media/base/android/java/src/org/chromium/media/VideoCapture.java +++ /dev/null
@@ -1,126 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.media; - -import android.content.Context; -import android.graphics.ImageFormat; -import android.view.Surface; -import android.view.WindowManager; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -/** - * Video Capture Device base class, defines a set of methods that native code - * needs to use to configure, start capture, and to be reached by callbacks and - * provides some neccesary data type(s) with accessors. - **/ -@JNINamespace("media") -public abstract class VideoCapture { - - // The angle (0, 90, 180, 270) that the image needs to be rotated to show in - // the display's native orientation. - protected int mCameraNativeOrientation; - // In some occasions we need to invert the device rotation readings, see the - // individual implementations. - protected boolean mInvertDeviceOrientationReadings; - - protected VideoCaptureFormat mCaptureFormat = null; - protected final Context mContext; - protected final int mId; - // Native callback context variable. - protected final long mNativeVideoCaptureDeviceAndroid; - - VideoCapture(Context context, - int id, - long nativeVideoCaptureDeviceAndroid) { - mContext = context; - mId = id; - mNativeVideoCaptureDeviceAndroid = nativeVideoCaptureDeviceAndroid; - } - - // Allocate necessary resources for capture. - @CalledByNative - public abstract boolean allocate(int width, int height, int frameRate); - - // Starts actual capture. - @CalledByNative - public abstract boolean startCapture(); - - // Stops current capture. - @CalledByNative - public abstract boolean stopCapture(); - - @CalledByNative - public abstract void deallocate(); - - @CalledByNative - public final int queryWidth() { - return mCaptureFormat.mWidth; - } - - @CalledByNative - public final int queryHeight() { - return mCaptureFormat.mHeight; - } - - @CalledByNative - public final int queryFrameRate() { - return mCaptureFormat.mFramerate; - } - - @CalledByNative - public final int getColorspace() { - switch (mCaptureFormat.mPixelFormat) { - case ImageFormat.YV12: - return AndroidImageFormat.YV12; - case ImageFormat.YUV_420_888: - return AndroidImageFormat.YUV_420_888; - case ImageFormat.NV21: - return AndroidImageFormat.NV21; - case ImageFormat.UNKNOWN: - default: - return AndroidImageFormat.UNKNOWN; - } - } - - protected final int getCameraRotation() { - int rotation = mInvertDeviceOrientationReadings - ? (360 - getDeviceRotation()) : getDeviceRotation(); - return (mCameraNativeOrientation + rotation) % 360; - } - - protected final int getDeviceRotation() { - if (mContext == null) return 0; - final int orientation; - WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - switch(wm.getDefaultDisplay().getRotation()) { - case Surface.ROTATION_90: - orientation = 90; - break; - case Surface.ROTATION_180: - orientation = 180; - break; - case Surface.ROTATION_270: - orientation = 270; - break; - case Surface.ROTATION_0: - default: - orientation = 0; - break; - } - return orientation; - } - - // Method for VideoCapture implementations to call back native code. - public native void nativeOnFrameAvailable(long nativeVideoCaptureDeviceAndroid, - byte[] data, - int length, - int rotation); - - // Method for VideoCapture implementations to signal an asynchronous error. - public native void nativeOnError(long nativeVideoCaptureDeviceAndroid, - String message); -}
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java b/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java deleted file mode 100644 index 35ec09f..0000000 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java +++ /dev/null
@@ -1,183 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.media; - -import android.content.Context; -import android.graphics.ImageFormat; - -import org.chromium.base.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * This class extends the VideoCaptureCamera base class for manipulating normal - * video capture devices in Android, including receiving copies of preview - * frames via Java-allocated buffers. It also includes class BuggyDeviceHack to - * deal with troublesome devices. - **/ -@SuppressWarnings("deprecation") -public class VideoCaptureAndroid extends VideoCaptureCamera { - - // Some devices don't support YV12 format correctly, even with JELLY_BEAN or - // newer OS. To work around the issues on those devices, we have to request - // NV21. This is supposed to be a temporary hack. - private static class BuggyDeviceHack { - private static final String[] COLORSPACE_BUGGY_DEVICE_LIST = { - "SAMSUNG-SGH-I747", - "ODROID-U2", - // See https://crbug.com/577435 for more info. - "XT1092", - "XT1095", - "XT1096", - }; - - static int getImageFormat() { - for (String buggyDevice : COLORSPACE_BUGGY_DEVICE_LIST) { - if (buggyDevice.contentEquals(android.os.Build.MODEL)) { - return ImageFormat.NV21; - } - } - return ImageFormat.YV12; - } - } - - private int mExpectedFrameSize; - private static final int NUM_CAPTURE_BUFFERS = 3; - private static final String TAG = "cr.media"; - - static int getNumberOfCameras() { - return android.hardware.Camera.getNumberOfCameras(); - } - - static int getCaptureApiType(int id) { - if (VideoCaptureCamera.getCameraInfo(id) == null) { - return CaptureApiType.API_TYPE_UNKNOWN; - } - return CaptureApiType.API1; - } - - static String getName(int id) { - android.hardware.Camera.CameraInfo cameraInfo = VideoCaptureCamera.getCameraInfo(id); - if (cameraInfo == null) return null; - - return "camera " + id + ", facing " + (cameraInfo.facing - == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? "front" : "back"); - } - - static VideoCaptureFormat[] getDeviceSupportedFormats(int id) { - android.hardware.Camera camera; - try { - camera = android.hardware.Camera.open(id); - } catch (RuntimeException ex) { - Log.e(TAG, "Camera.open: ", ex); - return null; - } - android.hardware.Camera.Parameters parameters = getCameraParameters(camera); - if (parameters == null) { - return null; - } - - ArrayList<VideoCaptureFormat> formatList = new ArrayList<VideoCaptureFormat>(); - // getSupportedPreview{Formats,FpsRange,PreviewSizes}() returns Lists - // with at least one element, but when the camera is in bad state, they - // can return null pointers; in that case we use a 0 entry, so we can - // retrieve as much information as possible. - List<Integer> pixelFormats = parameters.getSupportedPreviewFormats(); - if (pixelFormats == null) { - pixelFormats = new ArrayList<Integer>(); - } - if (pixelFormats.size() == 0) { - pixelFormats.add(ImageFormat.UNKNOWN); - } - for (Integer previewFormat : pixelFormats) { - int pixelFormat = AndroidImageFormat.UNKNOWN; - if (previewFormat == ImageFormat.YV12) { - pixelFormat = AndroidImageFormat.YV12; - } else if (previewFormat == ImageFormat.NV21) { - continue; - } - - List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange(); - if (listFpsRange == null) { - listFpsRange = new ArrayList<int[]>(); - } - if (listFpsRange.size() == 0) { - listFpsRange.add(new int[] {0, 0}); - } - for (int[] fpsRange : listFpsRange) { - List<android.hardware.Camera.Size> supportedSizes = - parameters.getSupportedPreviewSizes(); - if (supportedSizes == null) { - supportedSizes = new ArrayList<android.hardware.Camera.Size>(); - } - if (supportedSizes.size() == 0) { - supportedSizes.add(camera.new Size(0, 0)); - } - for (android.hardware.Camera.Size size : supportedSizes) { - formatList.add(new VideoCaptureFormat(size.width, - size.height, - (fpsRange[1] + 999) / 1000, - pixelFormat)); - } - } - } - camera.release(); - return formatList.toArray(new VideoCaptureFormat[formatList.size()]); - } - - VideoCaptureAndroid(Context context, - int id, - long nativeVideoCaptureDeviceAndroid) { - super(context, id, nativeVideoCaptureDeviceAndroid); - } - - @Override - protected void setCaptureParameters( - int width, - int height, - int frameRate, - android.hardware.Camera.Parameters cameraParameters) { - mCaptureFormat = new VideoCaptureFormat( - width, height, frameRate, BuggyDeviceHack.getImageFormat()); - } - - @Override - protected void allocateBuffers() { - mExpectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight - * ImageFormat.getBitsPerPixel(mCaptureFormat.mPixelFormat) / 8; - for (int i = 0; i < NUM_CAPTURE_BUFFERS; i++) { - byte[] buffer = new byte[mExpectedFrameSize]; - mCamera.addCallbackBuffer(buffer); - } - } - - @Override - protected void setPreviewCallback(android.hardware.Camera.PreviewCallback cb) { - mCamera.setPreviewCallbackWithBuffer(cb); - } - - @Override - public void onPreviewFrame(byte[] data, android.hardware.Camera camera) { - mPreviewBufferLock.lock(); - try { - if (!mIsRunning) { - return; - } - if (data.length == mExpectedFrameSize) { - nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, - data, mExpectedFrameSize, getCameraRotation()); - } - } finally { - mPreviewBufferLock.unlock(); - if (camera != null) { - camera.addCallbackBuffer(data); - } - } - } - - // TODO(wjia): investigate whether reading from texture could give better - // performance and frame rate, using onFrameAvailable(). -}
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureCamera.java b/media/base/android/java/src/org/chromium/media/VideoCaptureCamera.java deleted file mode 100644 index 9e10c68..0000000 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureCamera.java +++ /dev/null
@@ -1,294 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.media; - -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.SurfaceTexture; -import android.opengl.GLES20; -import android.os.Build; - -import org.chromium.base.Log; -import org.chromium.base.annotations.JNINamespace; - -import java.io.IOException; -import java.util.List; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Video Capture Device extension of VideoCapture to provide common functionality - * for capture using android.hardware.Camera API (deprecated in API 21). Normal - * Android and Tango devices are extensions of this class. - **/ -@JNINamespace("media") -@SuppressWarnings("deprecation") -//TODO: is this class only used on ICS MR1 (or some later version) and above? -@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) -public abstract class VideoCaptureCamera extends VideoCapture - implements android.hardware.Camera.PreviewCallback { - - protected android.hardware.Camera mCamera; - // Lock to mutually exclude execution of OnPreviewFrame() and {start/stop}Capture(). - protected ReentrantLock mPreviewBufferLock = new ReentrantLock(); - // True when native code has started capture. - protected boolean mIsRunning = false; - - protected int[] mGlTextures = null; - protected SurfaceTexture mSurfaceTexture = null; - protected static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; - - private static final String TAG = "cr.media"; - - protected static android.hardware.Camera.CameraInfo getCameraInfo(int id) { - android.hardware.Camera.CameraInfo cameraInfo = - new android.hardware.Camera.CameraInfo(); - try { - android.hardware.Camera.getCameraInfo(id, cameraInfo); - } catch (RuntimeException ex) { - Log.e(TAG, "getCameraInfo: Camera.getCameraInfo: " + ex); - return null; - } - return cameraInfo; - } - - protected static android.hardware.Camera.Parameters getCameraParameters( - android.hardware.Camera camera) { - android.hardware.Camera.Parameters parameters; - try { - parameters = camera.getParameters(); - } catch (RuntimeException ex) { - Log.e(TAG, "getCameraParameters: android.hardware.Camera.getParameters: " + ex); - if (camera != null) camera.release(); - return null; - } - return parameters; - } - - VideoCaptureCamera(Context context, - int id, - long nativeVideoCaptureDeviceAndroid) { - super(context, id, nativeVideoCaptureDeviceAndroid); - } - - @Override - public boolean allocate(int width, int height, int frameRate) { - Log.d(TAG, "allocate: requested (%d x %d) @%dfps", width, height, frameRate); - try { - mCamera = android.hardware.Camera.open(mId); - } catch (RuntimeException ex) { - Log.e(TAG, "allocate: Camera.open: " + ex); - return false; - } - - android.hardware.Camera.CameraInfo cameraInfo = VideoCaptureCamera.getCameraInfo(mId); - if (cameraInfo == null) { - mCamera.release(); - mCamera = null; - return false; - } - mCameraNativeOrientation = cameraInfo.orientation; - // For Camera API, the readings of back-facing camera need to be inverted. - mInvertDeviceOrientationReadings = - (cameraInfo.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); - Log.d(TAG, "allocate: Rotation dev=%d, cam=%d, facing back? %s", getDeviceRotation(), - mCameraNativeOrientation, mInvertDeviceOrientationReadings); - - android.hardware.Camera.Parameters parameters = getCameraParameters(mCamera); - if (parameters == null) { - mCamera = null; - return false; - } - - // getSupportedPreviewFpsRange() returns a List with at least one - // element, but when camera is in bad state, it can return null pointer. - List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange(); - if (listFpsRange == null || listFpsRange.size() == 0) { - Log.e(TAG, "allocate: no fps range found"); - return false; - } - // API fps ranges are scaled up x1000 to avoid floating point. - int frameRateScaled = frameRate * 1000; - // Use the first range as the default chosen range. - int[] chosenFpsRange = listFpsRange.get(0); - int frameRateNearest = Math.abs(frameRateScaled - chosenFpsRange[0]) - < Math.abs(frameRateScaled - chosenFpsRange[1]) - ? chosenFpsRange[0] : chosenFpsRange[1]; - int chosenFrameRate = (frameRateNearest + 999) / 1000; - int fpsRangeSize = Integer.MAX_VALUE; - for (int[] fpsRange : listFpsRange) { - if (fpsRange[0] <= frameRateScaled && frameRateScaled <= fpsRange[1] - && (fpsRange[1] - fpsRange[0]) <= fpsRangeSize) { - chosenFpsRange = fpsRange; - chosenFrameRate = frameRate; - fpsRangeSize = fpsRange[1] - fpsRange[0]; - } - } - Log.d(TAG, "allocate: fps set to %d, [%d-%d]", chosenFrameRate, - chosenFpsRange[0], chosenFpsRange[1]); - - // Calculate size. - List<android.hardware.Camera.Size> listCameraSize = - parameters.getSupportedPreviewSizes(); - int minDiff = Integer.MAX_VALUE; - int matchedWidth = width; - int matchedHeight = height; - for (android.hardware.Camera.Size size : listCameraSize) { - int diff = Math.abs(size.width - width) - + Math.abs(size.height - height); - Log.d(TAG, "allocate: supported (%d, %d), diff=%d", size.width, size.height, diff); - // TODO(wjia): Remove this hack (forcing width to be multiple - // of 32) by supporting stride in video frame buffer. - // Right now, VideoCaptureController requires compact YV12 - // (i.e., with no padding). - if (diff < minDiff && (size.width % 32 == 0)) { - minDiff = diff; - matchedWidth = size.width; - matchedHeight = size.height; - } - } - if (minDiff == Integer.MAX_VALUE) { - Log.e(TAG, "allocate: can not find a multiple-of-32 resolution"); - return false; - } - Log.d(TAG, "allocate: matched (%d x %d)", matchedWidth, matchedHeight); - - if (parameters.isVideoStabilizationSupported()) { - Log.d(TAG, "Image stabilization supported, currently: " - + parameters.getVideoStabilization() + ", setting it."); - parameters.setVideoStabilization(true); - } else { - Log.d(TAG, "Image stabilization not supported."); - } - - if (parameters.getSupportedFocusModes().contains( - android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { - parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); - } else { - Log.d(TAG, "Continuous focus mode not supported."); - } - - setCaptureParameters(matchedWidth, matchedHeight, chosenFrameRate, parameters); - parameters.setPictureSize(matchedWidth, matchedHeight); - parameters.setPreviewSize(matchedWidth, matchedHeight); - parameters.setPreviewFpsRange(chosenFpsRange[0], chosenFpsRange[1]); - parameters.setPreviewFormat(mCaptureFormat.mPixelFormat); - try { - mCamera.setParameters(parameters); - } catch (RuntimeException ex) { - Log.e(TAG, "setParameters: " + ex); - return false; - } - - // Set SurfaceTexture. Android Capture needs a SurfaceTexture even if - // it is not going to be used. - mGlTextures = new int[1]; - // Generate one texture pointer and bind it as an external texture. - GLES20.glGenTextures(1, mGlTextures, 0); - GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mGlTextures[0]); - // No mip-mapping with camera source. - GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, - GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, - GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - // Clamp to edge is only option. - GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, - GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, - GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - - mSurfaceTexture = new SurfaceTexture(mGlTextures[0]); - mSurfaceTexture.setOnFrameAvailableListener(null); - try { - mCamera.setPreviewTexture(mSurfaceTexture); - } catch (IOException ex) { - Log.e(TAG, "allocate: " + ex); - return false; - } - - allocateBuffers(); - return true; - } - - @Override - public boolean startCapture() { - if (mCamera == null) { - Log.e(TAG, "startCapture: camera is null"); - return false; - } - - mPreviewBufferLock.lock(); - try { - if (mIsRunning) { - return true; - } - mIsRunning = true; - } finally { - mPreviewBufferLock.unlock(); - } - setPreviewCallback(this); - try { - mCamera.startPreview(); - } catch (RuntimeException ex) { - Log.e(TAG, "startCapture: Camera.startPreview: " + ex); - return false; - } - return true; - } - - @Override - public boolean stopCapture() { - if (mCamera == null) { - Log.e(TAG, "stopCapture: camera is null"); - return true; - } - - mPreviewBufferLock.lock(); - try { - if (!mIsRunning) { - return true; - } - mIsRunning = false; - } finally { - mPreviewBufferLock.unlock(); - } - - mCamera.stopPreview(); - setPreviewCallback(null); - return true; - } - - @Override - public void deallocate() { - if (mCamera == null) return; - - stopCapture(); - try { - mCamera.setPreviewTexture(null); - if (mGlTextures != null) GLES20.glDeleteTextures(1, mGlTextures, 0); - mCaptureFormat = null; - mCamera.release(); - mCamera = null; - } catch (IOException ex) { - Log.e(TAG, "deallocate: failed to deallocate camera, " + ex); - return; - } - } - - // Local hook to allow derived classes to configure and plug capture - // buffers if needed. - abstract void allocateBuffers(); - - // Local hook to allow derived classes to fill capture format and modify - // camera parameters as they see fit. - abstract void setCaptureParameters( - int width, - int height, - int frameRate, - android.hardware.Camera.Parameters cameraParameters); - - // Local method to be overriden with the particular setPreviewCallback to be - // used in the implementations. - abstract void setPreviewCallback(android.hardware.Camera.PreviewCallback cb); -}
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java deleted file mode 100644 index 0c66ede..0000000 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java +++ /dev/null
@@ -1,503 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.media; - -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.ImageFormat; -import android.hardware.camera2.CameraAccessException; -import android.hardware.camera2.CameraCaptureSession; -import android.hardware.camera2.CameraCharacteristics; -import android.hardware.camera2.CameraDevice; -import android.hardware.camera2.CameraManager; -import android.hardware.camera2.CameraMetadata; -import android.hardware.camera2.CaptureRequest; -import android.hardware.camera2.params.StreamConfigurationMap; -import android.media.Image; -import android.media.ImageReader; -import android.os.Build; -import android.os.Handler; -import android.os.HandlerThread; -import android.util.Size; -import android.view.Surface; - -import org.chromium.base.Log; -import org.chromium.base.annotations.JNINamespace; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -/** - * This class implements Video Capture using Camera2 API, introduced in Android - * API 21 (L Release). Capture takes place in the current Looper, while pixel - * download takes place in another thread used by ImageReader. A number of - * static methods are provided to retrieve information on current system cameras - * and their capabilities, using android.hardware.camera2.CameraManager. - **/ -@JNINamespace("media") -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public class VideoCaptureCamera2 extends VideoCapture { - - // Inner class to extend a CameraDevice state change listener. - private class CrStateListener extends CameraDevice.StateCallback { - @Override - public void onOpened(CameraDevice cameraDevice) { - mCameraDevice = cameraDevice; - changeCameraStateAndNotify(CameraState.CONFIGURING); - if (!createCaptureObjects()) { - changeCameraStateAndNotify(CameraState.STOPPED); - nativeOnError(mNativeVideoCaptureDeviceAndroid, - "Error configuring camera"); - } - } - - @Override - public void onDisconnected(CameraDevice cameraDevice) { - cameraDevice.close(); - mCameraDevice = null; - changeCameraStateAndNotify(CameraState.STOPPED); - } - - @Override - public void onError(CameraDevice cameraDevice, int error) { - cameraDevice.close(); - mCameraDevice = null; - changeCameraStateAndNotify(CameraState.STOPPED); - nativeOnError(mNativeVideoCaptureDeviceAndroid, - "Camera device error " + Integer.toString(error)); - } - }; - - // Inner class to extend a Capture Session state change listener. - private class CrCaptureSessionListener extends CameraCaptureSession.StateCallback { - @Override - public void onConfigured(CameraCaptureSession cameraCaptureSession) { - Log.d(TAG, "onConfigured"); - mCaptureSession = cameraCaptureSession; - createCaptureRequest(); - changeCameraStateAndNotify(CameraState.STARTED); - } - - @Override - public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { - // TODO(mcasas): When signalling error, C++ will tear us down. Is there need for - // cleanup? - changeCameraStateAndNotify(CameraState.STOPPED); - nativeOnError(mNativeVideoCaptureDeviceAndroid, - "Camera session configuration error"); - } - }; - - // Internal class implementing the ImageReader listener. Gets pinged when a - // new frame is been captured and downloaded to memory-backed buffers. - private class CrImageReaderListener implements ImageReader.OnImageAvailableListener { - @Override - public void onImageAvailable(ImageReader reader) { - Image image = null; - try { - image = reader.acquireLatestImage(); - if (image == null) return; - if (image.getFormat() != ImageFormat.YUV_420_888 - || image.getPlanes().length != 3) { - Log.e(TAG, "Unexpected image format: %d or #planes: %d", - image.getFormat(), image.getPlanes().length); - return; - } - - if (reader.getWidth() != image.getWidth() - || reader.getHeight() != image.getHeight()) { - throw new IllegalStateException("ImageReader size " + reader.getWidth() + "x" - + reader.getHeight() + " did not match Image size " + image.getWidth() - + "x" + image.getHeight()); - } - readImageIntoBuffer(image, mCapturedData); - nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, - mCapturedData, - mCapturedData.length, - getCameraRotation()); - } catch (IllegalStateException ex) { - Log.e(TAG, "acquireLatestImage():" + ex); - return; - } finally { - if (image != null) { - image.close(); - } - } - } - }; - - private byte[] mCapturedData; - - private CameraDevice mCameraDevice = null; - private CaptureRequest.Builder mPreviewBuilder = null; - private CameraCaptureSession mCaptureSession = null; - private ImageReader mImageReader = null; - - private static final double kNanoSecondsToFps = 1.0E-9; - private static final String TAG = "cr.media"; - - private static enum CameraState {OPENING, CONFIGURING, STARTED, STOPPED} - private CameraState mCameraState = CameraState.STOPPED; - private final Object mCameraStateLock = new Object(); - - // Service function to grab CameraCharacteristics and handle exceptions. - private static CameraCharacteristics getCameraCharacteristics(Context appContext, int id) { - final CameraManager manager = - (CameraManager) appContext.getSystemService(Context.CAMERA_SERVICE); - try { - return manager.getCameraCharacteristics(Integer.toString(id)); - } catch (CameraAccessException ex) { - Log.e(TAG, "getNumberOfCameras: getCameraIdList(): " + ex); - } - return null; - } - - private boolean createCaptureObjects() { - Log.d(TAG, "createCaptureObjects"); - if (mCameraDevice == null) return false; - - // Create an ImageReader and plug a thread looper into it to have - // readback take place on its own thread. - final int maxImages = 2; - mImageReader = ImageReader.newInstance(mCaptureFormat.getWidth(), - mCaptureFormat.getHeight(), - mCaptureFormat.getPixelFormat(), - maxImages); - HandlerThread thread = new HandlerThread("CameraPreview"); - thread.start(); - final Handler backgroundHandler = new Handler(thread.getLooper()); - final CrImageReaderListener imageReaderListener = new CrImageReaderListener(); - mImageReader.setOnImageAvailableListener(imageReaderListener, - backgroundHandler); - - // The Preview template specifically means "high frame rate is given - // priority over the highest-quality post-processing". - try { - mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); - } catch (CameraAccessException ex) { - Log.e(TAG, "createCaptureRequest: " + ex); - return false; - } catch (IllegalArgumentException ex) { - Log.e(TAG, "createCaptureRequest: " + ex); - return false; - } catch (SecurityException ex) { - Log.e(TAG, "createCaptureRequest: " + ex); - return false; - } - if (mPreviewBuilder == null) { - Log.e(TAG, "mPreviewBuilder error"); - return false; - } - // Construct an ImageReader Surface and plug it into our CaptureRequest.Builder. - mPreviewBuilder.addTarget(mImageReader.getSurface()); - - // A series of configuration options in the PreviewBuilder - mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, - CameraMetadata.CONTROL_MODE_AUTO); - mPreviewBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, - CameraMetadata.NOISE_REDUCTION_MODE_FAST); - mPreviewBuilder.set(CaptureRequest.EDGE_MODE, CameraMetadata.EDGE_MODE_FAST); - mPreviewBuilder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, - CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON); - // SENSOR_EXPOSURE_TIME ? - - List<Surface> surfaceList = new ArrayList<Surface>(1); - surfaceList.add(mImageReader.getSurface()); - final CrCaptureSessionListener captureSessionListener = new CrCaptureSessionListener(); - try { - mCameraDevice.createCaptureSession(surfaceList, captureSessionListener, null); - } catch (CameraAccessException ex) { - Log.e(TAG, "createCaptureSession: " + ex); - return false; - } catch (IllegalArgumentException ex) { - Log.e(TAG, "createCaptureSession: " + ex); - return false; - } catch (SecurityException ex) { - Log.e(TAG, "createCaptureSession: " + ex); - return false; - } - // Wait for trigger on CrCaptureSessionListener.onConfigured(); - return true; - } - - private boolean createCaptureRequest() { - Log.d(TAG, "createCaptureRequest"); - try { - // This line triggers the capture. No |listener| is registered, so - // we will not get notified of capture events, instead, ImageReader - // will trigger every time a downloaded image is ready. Since - //|handler| is null, we'll work on the current Thread Looper. - mCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), null, null); - } catch (CameraAccessException ex) { - Log.e(TAG, "setRepeatingRequest: " + ex); - return false; - } catch (IllegalArgumentException ex) { - Log.e(TAG, "setRepeatingRequest: " + ex); - return false; - } catch (SecurityException ex) { - Log.e(TAG, "setRepeatingRequest: " + ex); - return false; - } - // Now wait for trigger on CrImageReaderListener.onImageAvailable(); - return true; - } - - private static void readImageIntoBuffer(Image image, byte[] data) { - final int imageWidth = image.getWidth(); - final int imageHeight = image.getHeight(); - final Image.Plane[] planes = image.getPlanes(); - - int offset = 0; - for (int plane = 0; plane < planes.length; ++plane) { - final ByteBuffer buffer = planes[plane].getBuffer(); - final int rowStride = planes[plane].getRowStride(); - // Experimentally, U and V planes have |pixelStride| = 2, which - // essentially means they are packed. That's silly, because we are - // forced to unpack here. - final int pixelStride = planes[plane].getPixelStride(); - final int planeWidth = (plane == 0) ? imageWidth : imageWidth / 2; - final int planeHeight = (plane == 0) ? imageHeight : imageHeight / 2; - - if (pixelStride == 1 && rowStride == planeWidth) { - // Copy whole plane from buffer into |data| at once. - buffer.get(data, offset, planeWidth * planeHeight); - offset += planeWidth * planeHeight; - } else { - // Copy pixels one by one respecting pixelStride and rowStride. - byte[] rowData = new byte[rowStride]; - for (int row = 0; row < planeHeight - 1; ++row) { - buffer.get(rowData, 0, rowStride); - for (int col = 0; col < planeWidth; ++col) { - data[offset++] = rowData[col * pixelStride]; - } - } - - // Last row is special in some devices and may not contain the full - // |rowStride| bytes of data. See http://crbug.com/458701. - buffer.get(rowData, 0, Math.min(rowStride, buffer.remaining())); - for (int col = 0; col < planeWidth; ++col) { - data[offset++] = rowData[col * pixelStride]; - } - } - } - } - - private void changeCameraStateAndNotify(CameraState state) { - synchronized (mCameraStateLock) { - mCameraState = state; - mCameraStateLock.notifyAll(); - } - } - - static boolean isLegacyDevice(Context appContext, int id) { - final CameraCharacteristics cameraCharacteristics = - getCameraCharacteristics(appContext, id); - return cameraCharacteristics != null - && cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) - == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; - } - - static int getNumberOfCameras(Context appContext) { - final CameraManager manager = - (CameraManager) appContext.getSystemService(Context.CAMERA_SERVICE); - try { - return manager.getCameraIdList().length; - } catch (CameraAccessException ex) { - Log.e(TAG, "getNumberOfCameras: getCameraIdList(): " + ex); - return 0; - } - } - - static int getCaptureApiType(int id, Context appContext) { - final CameraCharacteristics cameraCharacteristics = - getCameraCharacteristics(appContext, id); - if (cameraCharacteristics == null) { - return CaptureApiType.API_TYPE_UNKNOWN; - } - - final int supportedHWLevel = cameraCharacteristics.get( - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); - switch (supportedHWLevel) { - case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY: - return CaptureApiType.API2_LEGACY; - case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL: - return CaptureApiType.API2_FULL; - case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED: - return CaptureApiType.API2_LIMITED; - default: - return CaptureApiType.API2_LEGACY; - } - } - - static String getName(int id, Context appContext) { - final CameraCharacteristics cameraCharacteristics = - getCameraCharacteristics(appContext, id); - if (cameraCharacteristics == null) return null; - final int facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); - return "camera2 " + id + ", facing " - + ((facing == CameraCharacteristics.LENS_FACING_FRONT) ? "front" : "back"); - } - - static VideoCaptureFormat[] getDeviceSupportedFormats(Context appContext, int id) { - final CameraCharacteristics cameraCharacteristics = - getCameraCharacteristics(appContext, id); - if (cameraCharacteristics == null) return null; - - final int[] capabilities = cameraCharacteristics.get( - CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); - // Per-format frame rate via getOutputMinFrameDuration() is only available if the - // property REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR is set. - boolean minFrameDurationAvailable = false; - for (int cap : capabilities) { - if (cap == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) { - minFrameDurationAvailable = true; - break; - } - } - - ArrayList<VideoCaptureFormat> formatList = new ArrayList<VideoCaptureFormat>(); - final StreamConfigurationMap streamMap = - cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - final int[] formats = streamMap.getOutputFormats(); - for (int format : formats) { - final Size[] sizes = streamMap.getOutputSizes(format); - if (sizes == null) continue; - for (Size size : sizes) { - double minFrameRate = 0.0f; - if (minFrameDurationAvailable) { - final long minFrameDuration = streamMap.getOutputMinFrameDuration(format, size); - minFrameRate = (minFrameDuration == 0) - ? 0.0f - : (1.0 / kNanoSecondsToFps * minFrameDuration); - } else { - // TODO(mcasas): find out where to get the info from in this case. - // Hint: perhaps using SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS. - minFrameRate = 0.0; - } - formatList.add(new VideoCaptureFormat( - size.getWidth(), size.getHeight(), (int) minFrameRate, 0)); - } - } - return formatList.toArray(new VideoCaptureFormat[formatList.size()]); - } - - VideoCaptureCamera2(Context context, - int id, - long nativeVideoCaptureDeviceAndroid) { - super(context, id, nativeVideoCaptureDeviceAndroid); - } - - @Override - public boolean allocate(int width, int height, int frameRate) { - Log.d(TAG, "allocate: requested (%d x %d) @%dfps", width, height, frameRate); - synchronized (mCameraStateLock) { - if (mCameraState == CameraState.OPENING || mCameraState == CameraState.CONFIGURING) { - Log.e(TAG, "allocate() invoked while Camera is busy opening/configuring."); - return false; - } - } - final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mContext, mId); - final StreamConfigurationMap streamMap = - cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - - // Find closest supported size. - final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.YUV_420_888); - if (supportedSizes == null) return false; - Size closestSupportedSize = null; - int minDiff = Integer.MAX_VALUE; - for (Size size : supportedSizes) { - final int diff = - Math.abs(size.getWidth() - width) + Math.abs(size.getHeight() - height); - if (diff < minDiff) { - minDiff = diff; - closestSupportedSize = size; - } - } - if (minDiff == Integer.MAX_VALUE) { - Log.e(TAG, "No supported resolutions."); - return false; - } - Log.d(TAG, "allocate: matched (%d x %d)", closestSupportedSize.getWidth(), - closestSupportedSize.getHeight()); - - // |mCaptureFormat| is also used to configure the ImageReader. - mCaptureFormat = new VideoCaptureFormat(closestSupportedSize.getWidth(), - closestSupportedSize.getHeight(), frameRate, ImageFormat.YUV_420_888); - int expectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight - * ImageFormat.getBitsPerPixel(mCaptureFormat.mPixelFormat) / 8; - mCapturedData = new byte[expectedFrameSize]; - mCameraNativeOrientation = - cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); - // TODO(mcasas): The following line is correct for N5 with prerelease Build, - // but NOT for N7 with a dev Build. Figure out which one to support. - mInvertDeviceOrientationReadings = - cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) - == CameraCharacteristics.LENS_FACING_BACK; - return true; - } - - @Override - public boolean startCapture() { - Log.d(TAG, "startCapture"); - changeCameraStateAndNotify(CameraState.OPENING); - final CameraManager manager = - (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); - final Handler mainHandler = new Handler(mContext.getMainLooper()); - final CrStateListener stateListener = new CrStateListener(); - try { - manager.openCamera(Integer.toString(mId), stateListener, mainHandler); - } catch (CameraAccessException ex) { - Log.e(TAG, "allocate: manager.openCamera: " + ex); - return false; - } catch (IllegalArgumentException ex) { - Log.e(TAG, "allocate: manager.openCamera: " + ex); - return false; - } catch (SecurityException ex) { - Log.e(TAG, "allocate: manager.openCamera: " + ex); - return false; - } - - return true; - } - - @Override - public boolean stopCapture() { - Log.d(TAG, "stopCapture"); - - // With Camera2 API, the capture is started asynchronously, which will cause problem if - // stopCapture comes too quickly. Without stopping the previous capture properly, the next - // startCapture will fail and make Chrome no-responding. So wait camera to be STARTED. - synchronized (mCameraStateLock) { - while (mCameraState != CameraState.STARTED && mCameraState != CameraState.STOPPED) { - try { - mCameraStateLock.wait(); - } catch (InterruptedException ex) { - Log.e(TAG, "CaptureStartedEvent: " + ex); - } - } - if (mCameraState == CameraState.STOPPED) return true; - } - - try { - mCaptureSession.abortCaptures(); - } catch (CameraAccessException ex) { - Log.e(TAG, "abortCaptures: " + ex); - return false; - } catch (IllegalStateException ex) { - Log.e(TAG, "abortCaptures: " + ex); - return false; - } - if (mCameraDevice == null) return false; - mCameraDevice.close(); - changeCameraStateAndNotify(CameraState.STOPPED); - return true; - } - - @Override - public void deallocate() { - Log.d(TAG, "deallocate"); - } -}
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java b/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java deleted file mode 100644 index 7c70c9d..0000000 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java +++ /dev/null
@@ -1,172 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.media; - -import android.Manifest; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Build; - -import org.chromium.base.Log; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -/** - * This class implements a factory of Android Video Capture objects for Chrome. - * The static createVideoCapture() returns either a "normal" VideoCaptureAndroid - * or a "special" VideoCaptureTango. Cameras are identified by |id|, where Tango - * cameras have |id| above the standard ones. Video Capture objects allocated - * via createVideoCapture() are explicitly owned by the caller. - * ChromiumCameraInfo is an internal class with some static methods needed from - * the rest of the class to manipulate the |id|s of normal and special devices. - **/ -@JNINamespace("media") -@SuppressWarnings("deprecation") -class VideoCaptureFactory { - - // Internal class to encapsulate camera device id manipulations. - static class ChromiumCameraInfo { - // Special devices have more cameras than usual. Those devices are - // identified by model & device. Currently only the Tango is supported. - // Note that these devices have no Camera.CameraInfo. - private static final String[][] SPECIAL_DEVICE_LIST = { - {"Peanut", "peanut"}, - }; - private static int sNumberOfSystemCameras = -1; - private static final String TAG = "cr.media"; - - private static boolean isSpecialDevice() { - for (String[] device : SPECIAL_DEVICE_LIST) { - if (device[0].contentEquals(android.os.Build.MODEL) - && device[1].contentEquals(android.os.Build.DEVICE)) { - return true; - } - } - return false; - } - - private static boolean isSpecialCamera(int id) { - return id >= sNumberOfSystemCameras; - } - - private static int toSpecialCameraId(int id) { - assert isSpecialCamera(id); - return id - sNumberOfSystemCameras; - } - - private static int getNumberOfCameras(Context appContext) { - if (sNumberOfSystemCameras == -1) { - // getNumberOfCameras() would not fail due to lack of permission, but the - // following operations on camera would. "No permission" isn't a fatal - // error in WebView, specially for those applications which have no purpose - // to use a camera, but "load page" requires it. So, output a warning log - // and carry on pretending the system has no camera(s). This optimization - // applies only to pre-M on Android because that is when runtime permissions - // were introduced. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M - && appContext.getPackageManager().checkPermission( - Manifest.permission.CAMERA, appContext.getPackageName()) - != PackageManager.PERMISSION_GRANTED) { - sNumberOfSystemCameras = 0; - Log.w(TAG, "Missing android.permission.CAMERA permission, " - + "no system camera available."); - } else { - if (isLReleaseOrLater()) { - sNumberOfSystemCameras = - VideoCaptureCamera2.getNumberOfCameras(appContext); - } else { - sNumberOfSystemCameras = VideoCaptureAndroid.getNumberOfCameras(); - if (isSpecialDevice()) { - Log.d(TAG, "Special device: %s", android.os.Build.MODEL); - sNumberOfSystemCameras += VideoCaptureTango.numberOfCameras(); - } - } - } - } - return sNumberOfSystemCameras; - } - } - - private static boolean isLReleaseOrLater() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; - } - - // Factory methods. - @CalledByNative - static VideoCapture createVideoCapture( - Context context, int id, long nativeVideoCaptureDeviceAndroid) { - if (isLReleaseOrLater() && !VideoCaptureCamera2.isLegacyDevice(context, id)) { - return new VideoCaptureCamera2(context, - id, - nativeVideoCaptureDeviceAndroid); - } - if (!ChromiumCameraInfo.isSpecialCamera(id)) { - return new VideoCaptureAndroid(context, - id, - nativeVideoCaptureDeviceAndroid); - } - return new VideoCaptureTango(context, - ChromiumCameraInfo.toSpecialCameraId(id), - nativeVideoCaptureDeviceAndroid); - } - - @CalledByNative - static int getNumberOfCameras(Context appContext) { - return ChromiumCameraInfo.getNumberOfCameras(appContext); - } - - @CalledByNative - static int getCaptureApiType(int id, Context appContext) { - if (isLReleaseOrLater()) { - return VideoCaptureCamera2.getCaptureApiType(id, appContext); - } else if (ChromiumCameraInfo.isSpecialCamera(id)) { - return VideoCaptureTango.getCaptureApiType( - ChromiumCameraInfo.toSpecialCameraId(id)); - } else { - return VideoCaptureAndroid.getCaptureApiType(id); - } - } - - @CalledByNative - static String getDeviceName(int id, Context appContext) { - if (isLReleaseOrLater() && !VideoCaptureCamera2.isLegacyDevice(appContext, id)) { - return VideoCaptureCamera2.getName(id, appContext); - } - return (ChromiumCameraInfo.isSpecialCamera(id)) - ? VideoCaptureTango.getName(ChromiumCameraInfo.toSpecialCameraId(id)) - : VideoCaptureAndroid.getName(id); - } - - @CalledByNative - static VideoCaptureFormat[] getDeviceSupportedFormats(Context appContext, int id) { - if (isLReleaseOrLater() && !VideoCaptureCamera2.isLegacyDevice(appContext, id)) { - return VideoCaptureCamera2.getDeviceSupportedFormats(appContext, id); - } - return ChromiumCameraInfo.isSpecialCamera(id) - ? VideoCaptureTango.getDeviceSupportedFormats( - ChromiumCameraInfo.toSpecialCameraId(id)) - : VideoCaptureAndroid.getDeviceSupportedFormats(id); - } - - @CalledByNative - static int getCaptureFormatWidth(VideoCaptureFormat format) { - return format.getWidth(); - } - - @CalledByNative - static int getCaptureFormatHeight(VideoCaptureFormat format) { - return format.getHeight(); - } - - @CalledByNative - static int getCaptureFormatFramerate(VideoCaptureFormat format) { - return format.getFramerate(); - } - - @CalledByNative - static int getCaptureFormatPixelFormat(VideoCaptureFormat format) { - return format.getPixelFormat(); - } -}
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureFormat.java b/media/base/android/java/src/org/chromium/media/VideoCaptureFormat.java deleted file mode 100644 index 6eb3f78..0000000 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureFormat.java +++ /dev/null
@@ -1,36 +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. - -package org.chromium.media; - -class VideoCaptureFormat { - int mWidth; - int mHeight; - final int mFramerate; - final int mPixelFormat; - - public VideoCaptureFormat( - int width, int height, int framerate, int pixelformat) { - mWidth = width; - mHeight = height; - mFramerate = framerate; - mPixelFormat = pixelformat; - } - - public int getWidth() { - return mWidth; - } - - public int getHeight() { - return mHeight; - } - - public int getFramerate() { - return mFramerate; - } - - public int getPixelFormat() { - return mPixelFormat; - } -}
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java b/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java deleted file mode 100644 index c85cf0f..0000000 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java +++ /dev/null
@@ -1,197 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.media; - -import android.content.Context; -import android.graphics.ImageFormat; - -import org.chromium.base.Log; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; - -/** - * This class extends the VideoCapture base class for manipulating a Tango - * device's cameras, namely the associated Depth (z-Buffer), Fisheye and back- - * facing 4MP video capture devices. These devices are differentiated via the - * |id| passed on constructor, according to the index correspondence in - * |s_CAM_PARAMS|; all devices |id| are index 0 towards the parent VideoCapture. - **/ -@SuppressWarnings("deprecation") -public class VideoCaptureTango extends VideoCaptureCamera { - - private static class CamParams { - final int mId; - final String mName; - final int mWidth; - final int mHeight; - - CamParams(int id, String name, int width, int height) { - mId = id; - mName = name; - mWidth = width; - mHeight = height; - } - } - - private ByteBuffer mFrameBuffer = null; - private final int mTangoCameraId; - // The indexes must coincide with |CAM_PARAMS| defined below. - private static final int DEPTH_CAMERA_ID = 0; - private static final int FISHEYE_CAMERA_ID = 1; - private static final int FOURMP_CAMERA_ID = 2; - private static final CamParams CAM_PARAMS[] = { - new CamParams(DEPTH_CAMERA_ID, "depth", 320, 240), - new CamParams(FISHEYE_CAMERA_ID, "fisheye", 640, 480), - new CamParams(FOURMP_CAMERA_ID, "4MP", 1280, 720) }; - - // SuperFrame size definitions. Note that total size is the amount of lines - // multiplied by 3/2 due to Chroma components following. - private static final int SF_WIDTH = 1280; - private static final int SF_HEIGHT = 1168; - private static final int SF_FULL_HEIGHT = SF_HEIGHT * 3 / 2; - private static final int SF_LINES_HEADER = 16; - private static final int SF_LINES_FISHEYE = 240; - private static final int SF_LINES_RESERVED = 80; // Spec says 96. - private static final int SF_LINES_DEPTH = 60; - private static final int SF_LINES_DEPTH_PADDED = 112; // Spec says 96. - private static final int SF_LINES_BIGIMAGE = 720; - private static final int SF_OFFSET_4MP_CHROMA = 112; - - private static final byte CHROMA_ZERO_LEVEL = 127; - private static final String TAG = "cr.media"; - - static int numberOfCameras() { - return CAM_PARAMS.length; - } - - static int getCaptureApiType(int index) { - if (index >= CAM_PARAMS.length) { - return CaptureApiType.API1; - } - return CaptureApiType.TANGO; - } - - static String getName(int index) { - if (index >= CAM_PARAMS.length) return ""; - return CAM_PARAMS[index].mName; - } - - static VideoCaptureFormat[] getDeviceSupportedFormats(int id) { - ArrayList<VideoCaptureFormat> formatList = new ArrayList<VideoCaptureFormat>(); - if (id == DEPTH_CAMERA_ID) { - formatList.add(new VideoCaptureFormat(320, 180, 5, ImageFormat.YV12)); - } else if (id == FISHEYE_CAMERA_ID) { - formatList.add(new VideoCaptureFormat(640, 480, 30, ImageFormat.YV12)); - } else if (id == FOURMP_CAMERA_ID) { - formatList.add(new VideoCaptureFormat(1280, 720, 20, ImageFormat.YV12)); - } - return formatList.toArray(new VideoCaptureFormat[formatList.size()]); - } - - VideoCaptureTango(Context context, - int id, - long nativeVideoCaptureDeviceAndroid) { - // All Tango cameras are like the back facing one for the generic VideoCapture code. - super(context, 0, nativeVideoCaptureDeviceAndroid); - mTangoCameraId = id; - } - - @Override - protected void setCaptureParameters( - int width, - int height, - int frameRate, - android.hardware.Camera.Parameters cameraParameters) { - mCaptureFormat = new VideoCaptureFormat(CAM_PARAMS[mTangoCameraId].mWidth, - CAM_PARAMS[mTangoCameraId].mHeight, - frameRate, - ImageFormat.YV12); - // Connect Tango SuperFrame mode. Available sf modes are "all", - // "big-rgb", "small-rgb", "depth", "ir". - cameraParameters.set("sf-mode", "all"); - } - - @Override - protected void allocateBuffers() { - mFrameBuffer = ByteBuffer.allocateDirect( - mCaptureFormat.mWidth * mCaptureFormat.mHeight * 3 / 2); - // Prefill Chroma to their zero-equivalent for the cameras that only - // provide Luma component. - Arrays.fill(mFrameBuffer.array(), CHROMA_ZERO_LEVEL); - } - - @Override - protected void setPreviewCallback(android.hardware.Camera.PreviewCallback cb) { - mCamera.setPreviewCallback(cb); - } - - @Override - public void onPreviewFrame(byte[] data, android.hardware.Camera camera) { - mPreviewBufferLock.lock(); - try { - if (!mIsRunning) return; - if (data.length == SF_WIDTH * SF_FULL_HEIGHT) { - if (mTangoCameraId == DEPTH_CAMERA_ID) { - int sizeY = SF_WIDTH * SF_LINES_DEPTH; - int startY = - SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE + SF_LINES_RESERVED); - // Depth is composed of 16b samples in which only 12b are - // used. Throw away lowest 4 resolution bits. Android - // platforms are big endian, LSB in lowest address. In this - // case Chroma components are unused. No need to write them - // explicitly since they're filled to 128 on creation. - byte depthsample; - for (int j = startY; j < startY + 2 * sizeY; j += 2) { - depthsample = (byte) ((data[j + 1] << 4) | ((data[j] & 0xF0) >> 4)); - mFrameBuffer.put(depthsample); - } - for (int j = 0; j < mCaptureFormat.mWidth * mCaptureFormat.mHeight - sizeY; - ++j) { - mFrameBuffer.put((byte) 0); - } - } else if (mTangoCameraId == FISHEYE_CAMERA_ID) { - int sizeY = SF_WIDTH * SF_LINES_FISHEYE; - int startY = SF_WIDTH * SF_LINES_HEADER; - // Fisheye is black and white so Chroma components are unused. No need to write - // them explicitly since they're filled to 128 on creation. - ByteBuffer.wrap(data, startY, sizeY).get(mFrameBuffer.array(), 0, sizeY); - } else if (mTangoCameraId == FOURMP_CAMERA_ID) { - int startY = SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE - + SF_LINES_RESERVED + SF_LINES_DEPTH_PADDED); - int sizeY = SF_WIDTH * SF_LINES_BIGIMAGE; - - // The spec is completely inaccurate on the location, sizes - // and format of these channels. - int startU = SF_WIDTH * (SF_HEIGHT + SF_OFFSET_4MP_CHROMA); - int sizeU = SF_WIDTH * SF_LINES_BIGIMAGE / 4; - int startV = (SF_WIDTH * SF_HEIGHT * 5 / 4) + SF_WIDTH * SF_OFFSET_4MP_CHROMA; - int sizeV = SF_WIDTH * SF_LINES_BIGIMAGE / 4; - - // Equivalent to the following |for| loop but much faster: - // for (int i = START; i < START + SIZE; ++i) - // mFrameBuffer.put(data[i]); - ByteBuffer.wrap(data, startY, sizeY) - .get(mFrameBuffer.array(), 0, sizeY); - ByteBuffer.wrap(data, startU, sizeU) - .get(mFrameBuffer.array(), sizeY, sizeU); - ByteBuffer.wrap(data, startV, sizeV) - .get(mFrameBuffer.array(), sizeY + sizeU, sizeV); - } else { - Log.e(TAG, "Unknown camera, #id: %d", mTangoCameraId); - return; - } - mFrameBuffer.rewind(); // Important! - nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, - mFrameBuffer.array(), - mFrameBuffer.capacity(), - getCameraRotation()); - } - } finally { - mPreviewBufferLock.unlock(); - } - } -}
diff --git a/media/base/android/media_codec_util.cc b/media/base/android/media_codec_util.cc index 7650ecd..27a2b5ab 100644 --- a/media/base/android/media_codec_util.cc +++ b/media/base/android/media_codec_util.cc
@@ -245,4 +245,14 @@ return RegisterNativesImpl(env); } +// static +bool MediaCodecUtil::IsVp8DecoderAvailable() { + if (!IsMediaCodecAvailable()) + return false; + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, "vp8"); + return Java_MediaCodecUtil_isDecoderSupportedForDevice(env, j_mime.obj()); +} + } // namespace media
diff --git a/media/base/android/media_codec_util.h b/media/base/android/media_codec_util.h index 3c2d020..04b83a9 100644 --- a/media/base/android/media_codec_util.h +++ b/media/base/android/media_codec_util.h
@@ -65,6 +65,9 @@ static bool IsHLSPath(const GURL& url); static bool RegisterMediaCodecUtil(JNIEnv* env); + + // Indicates if the vp8 decoder is available on this device. + static bool IsVp8DecoderAvailable(); }; } // namespace media
diff --git a/media/base/android/media_jni_registrar.cc b/media/base/android/media_jni_registrar.cc index e230784..3ef0f75 100644 --- a/media/base/android/media_jni_registrar.cc +++ b/media/base/android/media_jni_registrar.cc
@@ -15,8 +15,6 @@ #include "media/base/android/media_player_bridge.h" #include "media/base/android/media_player_listener.h" #include "media/base/android/sdk_media_codec_bridge.h" -#include "media/capture/video/android/video_capture_device_android.h" -#include "media/capture/video/android/video_capture_device_factory_android.h" namespace media { @@ -28,10 +26,6 @@ {"MediaPlayerListener", MediaPlayerListener::RegisterMediaPlayerListener}, {"SdkMediaCodecBridge", SdkMediaCodecBridge::RegisterSdkMediaCodecBridge}, {"MediaCodecUtil", MediaCodecUtil::RegisterMediaCodecUtil}, - {"VideoCaptureDevice", - VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice}, - {"VideoCaptureDeviceFactory", - VideoCaptureDeviceFactoryAndroid::RegisterVideoCaptureDeviceFactory}, }; bool RegisterJni(JNIEnv* env) {
diff --git a/media/base/android/media_player_android.h b/media/base/android/media_player_android.h index a474c088..16982d6 100644 --- a/media/base/android/media_player_android.h +++ b/media/base/android/media_player_android.h
@@ -36,6 +36,7 @@ MEDIA_ERROR_DECODE, MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK, MEDIA_ERROR_INVALID_CODE, + MEDIA_ERROR_SERVER_DIED, }; static const double kDefaultVolumeMultiplier;
diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc index 5db0499..dc9d147 100644 --- a/media/base/android/media_player_bridge.cc +++ b/media/base/android/media_player_bridge.cc
@@ -10,6 +10,7 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/logging.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" #include "jni/MediaPlayerBridge_jni.h" #include "media/base/android/media_common_android.h" @@ -23,6 +24,16 @@ namespace media { +namespace { + +enum UMAExitStatus { + UMA_EXIT_SUCCESS = 0, + UMA_EXIT_ERROR, + UMA_EXIT_STATUS_MAX = UMA_EXIT_ERROR, +}; + +} // namespace + MediaPlayerBridge::MediaPlayerBridge( int player_id, const GURL& url, @@ -50,6 +61,9 @@ can_seek_forward_(true), can_seek_backward_(true), allow_credentials_(allow_credentials), + is_active_(false), + has_error_(false), + has_ever_started_(false), weak_factory_(this) {} MediaPlayerBridge::~MediaPlayerBridge() { @@ -59,6 +73,12 @@ Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj()); } Release(); + + if (has_ever_started_) { + UMA_HISTOGRAM_ENUMERATION("Media.Android.MediaPlayerSuccess", + has_error_ ? UMA_EXIT_ERROR : UMA_EXIT_SUCCESS, + UMA_EXIT_STATUS_MAX + 1); + } } void MediaPlayerBridge::Initialize() { @@ -274,6 +294,17 @@ } void MediaPlayerBridge::Start() { + // A second Start() call after an error is considered another attempt for UMA + // and causes UMA reporting. + if (has_ever_started_ && has_error_) { + UMA_HISTOGRAM_ENUMERATION("Media.Android.MediaPlayerSuccess", + UMA_EXIT_ERROR, UMA_EXIT_STATUS_MAX + 1); + } + + has_ever_started_ = true; + has_error_ = false; + is_active_ = true; + if (j_media_player_bridge_.is_null()) { pending_play_ = true; Prepare(); @@ -294,6 +325,8 @@ else pending_play_ = false; } + + is_active_ = false; } bool MediaPlayerBridge::IsPlaying() { @@ -364,6 +397,8 @@ } void MediaPlayerBridge::Release() { + is_active_ = false; + on_decoder_resources_released_cb_.Run(player_id()); if (j_media_player_bridge_.is_null()) return; @@ -401,6 +436,22 @@ MediaPlayerAndroid::OnVideoSizeChanged(width, height); } +void MediaPlayerBridge::OnMediaError(int error_type) { + // Gather errors for UMA only in the active state. + // The MEDIA_ERROR_INVALID_CODE is reported by MediaPlayerListener.java in + // the situations that are considered normal, and is ignored by upper level. + if (is_active_ && error_type != MEDIA_ERROR_INVALID_CODE) + has_error_ = true; + + // Do not propagate MEDIA_ERROR_SERVER_DIED. If it happens in the active state + // we want the playback to stall. It can be recovered by pressing the Play + // button again. + if (error_type == MEDIA_ERROR_SERVER_DIED) + error_type = MEDIA_ERROR_INVALID_CODE; + + MediaPlayerAndroid::OnMediaError(error_type); +} + void MediaPlayerBridge::OnPlaybackComplete() { time_update_timer_.Stop(); MediaPlayerAndroid::OnPlaybackComplete();
diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h index 3f5b1a60..0652bc1e 100644 --- a/media/base/android/media_player_bridge.h +++ b/media/base/android/media_player_bridge.h
@@ -95,6 +95,7 @@ // MediaPlayerAndroid implementation. void OnVideoSizeChanged(int width, int height) override; + void OnMediaError(int error_type) override; void OnPlaybackComplete() override; void OnMediaInterrupted() override; void OnMediaPrepared() override; @@ -199,6 +200,19 @@ // Whether user credentials are allowed to be passed. bool allow_credentials_; + // Helper variables for UMA reporting. + + // Whether the preparation for playback or the playback is currently going on. + // This flag is set in Start() and cleared in Pause() and Release(). Used for + // UMA reporting only. + bool is_active_; + + // Whether there has been any errors in the active state. + bool has_error_; + + // The flag is set if Start() has been called at least once. + bool has_ever_started_; + // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory<MediaPlayerBridge> weak_factory_;
diff --git a/media/base/audio_decoder.cc b/media/base/audio_decoder.cc index 5212794d..6e92458 100644 --- a/media/base/audio_decoder.cc +++ b/media/base/audio_decoder.cc
@@ -12,4 +12,8 @@ AudioDecoder::~AudioDecoder() {} +bool AudioDecoder::NeedsBitstreamConversion() const { + return false; +} + } // namespace media
diff --git a/media/base/audio_decoder.h b/media/base/audio_decoder.h index 592747c3..5c6efd6c 100644 --- a/media/base/audio_decoder.h +++ b/media/base/audio_decoder.h
@@ -85,6 +85,9 @@ // aborted before |closure| is called. virtual void Reset(const base::Closure& closure) = 0; + // Returns true if the decoder needs bitstream conversion before decoding. + virtual bool NeedsBitstreamConversion() const; + private: DISALLOW_COPY_AND_ASSIGN(AudioDecoder); };
diff --git a/media/base/media.cc b/media/base/media.cc index f55d1c4..efc495b 100644 --- a/media/base/media.cc +++ b/media/base/media.cc
@@ -4,15 +4,19 @@ #include "media/base/media.h" -#include "base/files/file_path.h" +#include "base/command_line.h" #include "base/lazy_instance.h" #include "base/macros.h" -#include "base/path_service.h" -#include "base/synchronization/lock.h" +#include "base/metrics/field_trial.h" #include "base/trace_event/trace_event.h" -#include "build/build_config.h" +#include "media/base/media_switches.h" #include "media/base/yuv_convert.h" +#if defined(OS_ANDROID) +#include "base/android/build_info.h" +#include "media/base/android/media_codec_util.h" +#endif + #if !defined(MEDIA_DISABLE_FFMPEG) #include "media/ffmpeg/ffmpeg_common.h" #endif @@ -21,6 +25,13 @@ // Media must only be initialized once, so use a LazyInstance to ensure this. class MediaInitializer { + public: + void enable_platform_decoder_support() { + has_platform_decoder_support_ = true; + } + + bool has_platform_decoder_support() { return has_platform_decoder_support_; } + private: friend struct base::DefaultLazyInstanceTraits<MediaInitializer>; @@ -51,6 +62,8 @@ NOTREACHED() << "MediaInitializer should be leaky!"; } + bool has_platform_decoder_support_ = false; + DISALLOW_COPY_AND_ASSIGN(MediaInitializer); }; @@ -61,4 +74,47 @@ g_media_library.Get(); } +#if defined(OS_ANDROID) +void EnablePlatformDecoderSupport() { + g_media_library.Pointer()->enable_platform_decoder_support(); +} + +bool HasPlatformDecoderSupport() { + return g_media_library.Pointer()->has_platform_decoder_support(); +} + +bool PlatformHasOpusSupport() { + return base::android::BuildInfo::GetInstance()->sdk_int() >= 21; +} + +bool PlatformHasVp9Support() { + return base::android::BuildInfo::GetInstance()->sdk_int() >= 19; +} + +bool IsUnifiedMediaPipelineEnabled() { + // TODO(dalecurtis): This experiment is temporary and should be removed once + // we have enough data to support the primacy of the unified media pipeline; + // see http://crbug.com/533190 for details. + // + // Note: It's important to query the field trial state first, to ensure that + // UMA reports the correct group. + const std::string group_name = + base::FieldTrialList::FindFullName("UnifiedMediaPipelineTrial"); + const bool enabled_via_cli = + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableUnifiedMediaPipeline); + return enabled_via_cli || + base::StartsWith(group_name, "Enabled", base::CompareCase::SENSITIVE); +} + +bool IsUnifiedMediaPipelineEnabledForMse() { + // Don't check IsUnifiedMediaPipelineEnabled() here since we don't want MSE to + // be enabled via experiment yet; only when the existing implementation can't + // be used (i.e. MediaCodec unavailable). + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableUnifiedMediaPipeline) || + !MediaCodecUtil::IsMediaCodecAvailable(); +} +#endif + } // namespace media
diff --git a/media/base/media.h b/media/base/media.h index 01a913b..65a7a69 100644 --- a/media/base/media.h +++ b/media/base/media.h
@@ -8,6 +8,7 @@ #ifndef MEDIA_BASE_MEDIA_H_ #define MEDIA_BASE_MEDIA_H_ +#include "build/build_config.h" #include "media/base/media_export.h" namespace base { @@ -20,6 +21,33 @@ // features. MEDIA_EXPORT void InitializeMediaLibrary(); +#if defined(OS_ANDROID) +// Tells the media library it has support for OS level decoders. Should only be +// used for actual decoders (e.g. MediaCodec) and not full-featured players +// (e.g. MediaPlayer). +MEDIA_EXPORT void EnablePlatformDecoderSupport(); +MEDIA_EXPORT bool HasPlatformDecoderSupport(); + +// Indicates if the platform supports Opus or VP9. Determined *ONLY* by the +// platform version, so does not guarantee that either can actually be played. +MEDIA_EXPORT bool PlatformHasOpusSupport(); +MEDIA_EXPORT bool PlatformHasVp9Support(); + +// Returns true if the unified media pipeline is enabled; the pipeline may still +// not work for all codecs if HasPlatformDecoderSupport() is false. Please see +// MimeUtil for an exhaustive listing of supported codecs. +// +// TODO(dalecurtis): These methods are temporary and should be removed once the +// unified media pipeline is supported everywhere. http://crbug.com/580626. +MEDIA_EXPORT bool IsUnifiedMediaPipelineEnabled(); + +// Similar to IsUnifiedMediaPipelineEnabled() but will also return true if +// MediaCodec is not available (allowing the unified pipeline to take over for +// cases where existing pipeline has no support). As above, codecs requiring +// platform support may not be available. +MEDIA_EXPORT bool IsUnifiedMediaPipelineEnabledForMse(); +#endif + } // namespace media #endif // MEDIA_BASE_MEDIA_H_
diff --git a/media/base/mime_util.cc b/media/base/mime_util.cc index e1b74e5..6eba4fc 100644 --- a/media/base/mime_util.cc +++ b/media/base/mime_util.cc
@@ -19,7 +19,15 @@ SupportsType IsSupportedMediaFormat(const std::string& mime_type, const std::vector<std::string>& codecs) { - return g_media_mime_util.Pointer()->IsSupportedMediaFormat(mime_type, codecs); + return g_media_mime_util.Pointer()->IsSupportedMediaFormat(mime_type, codecs, + false); +} + +SupportsType IsSupportedEncryptedMediaFormat( + const std::string& mime_type, + const std::vector<std::string>& codecs) { + return g_media_mime_util.Pointer()->IsSupportedMediaFormat(mime_type, codecs, + true); } void ParseCodecString(const std::string& codecs,
diff --git a/media/base/mime_util.h b/media/base/mime_util.h index 147d401..6a49eba 100644 --- a/media/base/mime_util.h +++ b/media/base/mime_util.h
@@ -25,19 +25,16 @@ std::vector<std::string>* codecs_out, bool strip); -// Indicates that the MIME type and (possible codec string) are supported by the -// underlying platform. +// Indicates that the MIME type and (possible codec string) are supported. enum SupportsType { - // The underlying platform is known not to support the given MIME type and - // codec combination. + // The given MIME type and codec combination is not supported. IsNotSupported, - // The underlying platform is known to support the given MIME type and codec - // combination. + // The given MIME type and codec combination is supported. IsSupported, - // The underlying platform is unsure whether the given MIME type and codec - // combination can be rendered or not before actually trying to play it. + // There's not enough information to determine if the given MIME type and + // codec combination can be rendered or not before actually trying to play it. MayBeSupported }; @@ -56,6 +53,11 @@ IsSupportedMediaFormat(const std::string& mime_type, const std::vector<std::string>& codecs); +// Similar to the above, but for encrypted formats. +MEDIA_EXPORT SupportsType +IsSupportedEncryptedMediaFormat(const std::string& mime_type, + const std::vector<std::string>& codecs); + // Test only method that removes proprietary media types and codecs from the // list of supported MIME types and codecs. These types and codecs must be // removed to ensure consistent layout test results across all Chromium
diff --git a/media/base/mime_util_internal.cc b/media/base/mime_util_internal.cc index 7901721..a0ce3c49 100644 --- a/media/base/mime_util_internal.cc +++ b/media/base/mime_util_internal.cc
@@ -8,11 +8,13 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "build/build_config.h" +#include "media/base/media.h" #include "media/base/video_codecs.h" #include "media/media_features.h" #if defined(OS_ANDROID) #include "base/android/build_info.h" +#include "media/base/android/media_codec_util.h" #endif namespace media { @@ -266,6 +268,22 @@ #endif MimeUtil::MimeUtil() : allow_proprietary_codecs_(false) { +#if defined(OS_ANDROID) + platform_info_.is_unified_media_pipeline_enabled = + IsUnifiedMediaPipelineEnabled(); + // When the unified media pipeline is enabled, we need support for both GPU + // video decoders and MediaCodec; indicated by HasPlatformDecoderSupport(). + // When the Android pipeline is used, we only need access to MediaCodec. + platform_info_.has_platform_decoders = + platform_info_.is_unified_media_pipeline_enabled + ? HasPlatformDecoderSupport() + : MediaCodecUtil::IsMediaCodecAvailable(); + platform_info_.has_platform_vp8_decoder = + MediaCodecUtil::IsVp8DecoderAvailable(); + platform_info_.supports_opus = PlatformHasOpusSupport(); + platform_info_.supports_vp9 = PlatformHasVp9Support(); +#endif + InitializeMimeTypeMaps(); } @@ -273,7 +291,9 @@ SupportsType MimeUtil::AreSupportedCodecs( const CodecSet& supported_codecs, - const std::vector<std::string>& codecs) const { + const std::vector<std::string>& codecs, + const std::string& mime_type_lower_case, + bool is_encrypted) const { DCHECK(!supported_codecs.empty()); DCHECK(!codecs.empty()); @@ -284,7 +304,7 @@ if (!StringToCodec(codecs[i], &codec, &is_ambiguous)) return IsNotSupported; - if (!IsCodecSupported(codec) || + if (!IsCodecSupported(codec, mime_type_lower_case, is_encrypted) || supported_codecs.find(codec) == supported_codecs.end()) { return IsNotSupported; } @@ -361,7 +381,8 @@ SupportsType MimeUtil::IsSupportedMediaFormat( const std::string& mime_type, - const std::vector<std::string>& codecs) const { + const std::vector<std::string>& codecs, + bool is_encrypted) const { const std::string mime_type_lower_case = base::ToLowerASCII(mime_type); MediaFormatMappings::const_iterator it_media_format_map = media_format_map_.find(mime_type_lower_case); @@ -370,8 +391,8 @@ if (it_media_format_map->second.empty()) { // We get here if the mimetype does not expect a codecs parameter. - return (codecs.empty() && - IsDefaultCodecSupportedLowerCase(mime_type_lower_case)) + return (codecs.empty() && IsDefaultCodecSupportedLowerCase( + mime_type_lower_case, is_encrypted)) ? IsSupported : IsNotSupported; } @@ -385,7 +406,9 @@ if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec)) return MayBeSupported; - return IsCodecSupported(default_codec) ? IsSupported : IsNotSupported; + return IsCodecSupported(default_codec, mime_type_lower_case, is_encrypted) + ? IsSupported + : IsNotSupported; } #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER) @@ -394,11 +417,13 @@ for (const auto& codec_id : codecs) { codecs_to_check.push_back(TranslateLegacyAvc1CodecIds(codec_id)); } - return AreSupportedCodecs(it_media_format_map->second, codecs_to_check); + return AreSupportedCodecs(it_media_format_map->second, codecs_to_check, + mime_type_lower_case, is_encrypted); } #endif - return AreSupportedCodecs(it_media_format_map->second, codecs); + return AreSupportedCodecs(it_media_format_map->second, codecs, + mime_type_lower_case, is_encrypted); } void MimeUtil::RemoveProprietaryMediaTypesAndCodecs() { @@ -408,6 +433,125 @@ allow_proprietary_codecs_ = false; } +// static +bool MimeUtil::IsCodecSupportedOnPlatform( + Codec codec, + const std::string& mime_type_lower_case, + bool is_encrypted, + const PlatformInfo& platform_info) { + DCHECK_NE(mime_type_lower_case, ""); + + // Encrypted block support is never available without platform decoders. + if (is_encrypted && !platform_info.has_platform_decoders) + return false; + + // NOTE: We do not account for Media Source Extensions (MSE) within these + // checks since it has its own isTypeSupported() which will handle platform + // specific codec rejections. See http://crbug.com/587303. + + switch (codec) { + // ---------------------------------------------------------------------- + // The following codecs are never supported. + // ---------------------------------------------------------------------- + case INVALID_CODEC: + case AC3: + case EAC3: + case THEORA: + return false; + + // ---------------------------------------------------------------------- + // The remaining codecs may be supported depending on platform abilities. + // ---------------------------------------------------------------------- + + case PCM: + case MP3: + case MPEG4_AAC_LC: + case MPEG4_AAC_SBR_v1: + case MPEG4_AAC_SBR_PS_v2: + case VORBIS: + // These codecs are always supported; via a platform decoder (when used + // with MSE/EME), a software decoder (the unified pipeline), or with + // MediaPlayer. + DCHECK(!is_encrypted || platform_info.has_platform_decoders); + return true; + + case MPEG2_AAC_LC: + case MPEG2_AAC_MAIN: + case MPEG2_AAC_SSR: + // MPEG-2 variants of AAC are not supported on Android unless the unified + // media pipeline can be used. These codecs will be decoded in software. + return !is_encrypted && platform_info.is_unified_media_pipeline_enabled; + + case OPUS: + // If clear, the unified pipeline can always decode Opus in software. + if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled) + return true; + + // Otherwise, platform support is required. + if (!platform_info.supports_opus) + return false; + + // MediaPlayer does not support Opus in ogg containers. + if (base::EndsWith(mime_type_lower_case, "ogg", + base::CompareCase::SENSITIVE)) { + return false; + } + + DCHECK(!is_encrypted || platform_info.has_platform_decoders); + return true; + + case H264: + // The unified pipeline requires platform support for h264. + if (platform_info.is_unified_media_pipeline_enabled) + return platform_info.has_platform_decoders; + + // When MediaPlayer or MediaCodec is used, h264 is always supported. + DCHECK(!is_encrypted || platform_info.has_platform_decoders); + return true; + + case HEVC_MAIN: +#if BUILDFLAG(ENABLE_HEVC_DEMUXING) + if (platform_info.is_unified_media_pipeline_enabled && + !platform_info.has_platform_decoders) { + return false; + } + +#if defined(OS_ANDROID) + // HEVC/H.265 is supported in Lollipop+ (API Level 21), according to + // http://developer.android.com/reference/android/media/MediaFormat.html + return base::android::BuildInfo::GetInstance()->sdk_int() >= 21; +#else + return true; +#endif // defined(OS_ANDROID) +#else + return false; +#endif // BUILDFLAG(ENABLE_HEVC_DEMUXING) + + case VP8: + // If clear, the unified pipeline can always decode VP8 in software. + if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled) + return true; + + if (is_encrypted) + return platform_info.has_platform_vp8_decoder; + + // MediaPlayer can always play VP8. Note: This is incorrect for MSE, but + // MSE does not use this code. http://crbug.com/587303. + return true; + + case VP9: { + // If clear, the unified pipeline can always decode VP9 in software. + if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled) + return true; + + // Otherwise, platform support is required. + return platform_info.supports_vp9; + } + } + + return false; +} + bool MimeUtil::StringToCodec(const std::string& codec_id, Codec* codec, bool* is_ambiguous) const { @@ -444,12 +588,16 @@ return false; } -bool MimeUtil::IsCodecSupported(Codec codec) const { +bool MimeUtil::IsCodecSupported(Codec codec, + const std::string& mime_type_lower_case, + bool is_encrypted) const { DCHECK_NE(codec, INVALID_CODEC); #if defined(OS_ANDROID) - if (!IsCodecSupportedOnAndroid(codec)) + if (!IsCodecSupportedOnPlatform(codec, mime_type_lower_case, is_encrypted, + platform_info_)) { return false; + } #endif return allow_proprietary_codecs_ || !IsCodecProprietary(codec); @@ -501,64 +649,13 @@ } bool MimeUtil::IsDefaultCodecSupportedLowerCase( - const std::string& mime_type_lower_case) const { + const std::string& mime_type_lower_case, + bool is_encrypted) const { Codec default_codec = Codec::INVALID_CODEC; if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec)) return false; - return IsCodecSupported(default_codec); + return IsCodecSupported(default_codec, mime_type_lower_case, is_encrypted); } -#if defined(OS_ANDROID) -bool MimeUtil::IsCodecSupportedOnAndroid(Codec codec) const { - switch (codec) { - case INVALID_CODEC: - return false; - - case PCM: - case MP3: - case MPEG4_AAC_LC: - case MPEG4_AAC_SBR_v1: - case MPEG4_AAC_SBR_PS_v2: - case VORBIS: - case H264: - case VP8: - return true; - - case AC3: - case EAC3: - // TODO(servolk): Revisit this for AC3/EAC3 support on AndroidTV - return false; - - case MPEG2_AAC_LC: - case MPEG2_AAC_MAIN: - case MPEG2_AAC_SSR: - // MPEG-2 variants of AAC are not supported on Android. - return false; - - case OPUS: - // Opus is supported only in Lollipop+ (API Level 21). - return base::android::BuildInfo::GetInstance()->sdk_int() >= 21; - - case HEVC_MAIN: -#if BUILDFLAG(ENABLE_HEVC_DEMUXING) - // HEVC/H.265 is supported in Lollipop+ (API Level 21), according to - // http://developer.android.com/reference/android/media/MediaFormat.html - return base::android::BuildInfo::GetInstance()->sdk_int() >= 21; -#else - return false; -#endif - - case VP9: - // VP9 is supported only in KitKat+ (API Level 19). - return base::android::BuildInfo::GetInstance()->sdk_int() >= 19; - - case THEORA: - return false; - } - - return false; -} -#endif - } // namespace internal } // namespace media
diff --git a/media/base/mime_util_internal.h b/media/base/mime_util_internal.h index 5750ef77..cf7f44f 100644 --- a/media/base/mime_util_internal.h +++ b/media/base/mime_util_internal.h
@@ -11,6 +11,7 @@ #include "base/containers/hash_tables.h" #include "base/macros.h" +#include "media/base/media_export.h" #include "media/base/mime_util.h" namespace media { @@ -18,7 +19,7 @@ // Internal utility class for handling mime types. Should only be invoked by // tests and the functions within mime_util.cc -- NOT for direct use by others. -class MimeUtil { +class MEDIA_EXPORT MimeUtil { public: MimeUtil(); ~MimeUtil(); @@ -41,7 +42,20 @@ HEVC_MAIN, VP8, VP9, - THEORA + THEORA, + LAST_CODEC = THEORA + }; + + // Platform configuration structure. Controls which codecs are supported at + // runtime. Also used by tests to simulate platform differences. + struct PlatformInfo { + bool has_platform_decoders = false; + + bool has_platform_vp8_decoder = false; + bool supports_opus = false; + bool supports_vp9 = false; + + bool is_unified_media_pipeline_enabled = false; }; // See mime_util.h for more information on these methods. @@ -49,12 +63,23 @@ void ParseCodecString(const std::string& codecs, std::vector<std::string>* codecs_out, bool strip); - SupportsType IsSupportedMediaFormat( - const std::string& mime_type, - const std::vector<std::string>& codecs) const; + SupportsType IsSupportedMediaFormat(const std::string& mime_type, + const std::vector<std::string>& codecs, + bool is_encrypted) const; void RemoveProprietaryMediaTypesAndCodecs(); + // Checks special platform specific codec restrictions. Returns true if + // |codec| is supported when contained in |mime_type_lower_case|. + // |is_encrypted| means the codec will be used with encrypted blocks. + // |platform_info| describes the availability of various platform features; + // see PlatformInfo for more details. + static bool IsCodecSupportedOnPlatform( + Codec codec, + const std::string& mime_type_lower_case, + bool is_encrypted, + const PlatformInfo& platform_info); + private: typedef base::hash_set<int> CodecSet; typedef std::map<std::string, CodecSet> MediaFormatMappings; @@ -69,13 +94,16 @@ // For faster lookup, keep hash sets. void InitializeMimeTypeMaps(); - // Returns IsSupported if all codec IDs in |codecs| are unambiguous - // and are supported by the platform. MayBeSupported is returned if - // at least one codec ID in |codecs| is ambiguous but all the codecs - // are supported by the platform. IsNotSupported is returned if at - // least one codec ID is not supported by the platform. + // Returns IsSupported if all codec IDs in |codecs| are unambiguous and are + // supported in |mime_type_lower_case|. MayBeSupported is returned if at least + // one codec ID in |codecs| is ambiguous but all the codecs are supported. + // IsNotSupported is returned if |mime_type_lower_case| is not supported or at + // least one is not supported in |mime_type_lower_case|. |is_encrypted| means + // the codec will be used with encrypted blocks. SupportsType AreSupportedCodecs(const CodecSet& supported_codecs, - const std::vector<std::string>& codecs) const; + const std::vector<std::string>& codecs, + const std::string& mime_type_lower_case, + bool is_encrypted) const; // Converts a codec ID into an Codec enum value and indicates // whether the conversion was ambiguous. @@ -89,10 +117,13 @@ Codec* codec, bool* is_ambiguous) const; - // Returns true if |codec| is supported by the platform. - // Note: This method will return false if the platform supports proprietary - // codecs but |allow_proprietary_codecs_| is set to false. - bool IsCodecSupported(Codec codec) const; + // Returns true if |codec| is supported when contained in + // |mime_type_lower_case|. Note: This method will always return false for + // proprietary codecs if |allow_proprietary_codecs_| is set to false. + // |is_encrypted| means the codec will be used with encrypted blocks. + bool IsCodecSupported(Codec codec, + const std::string& mime_type_lower_case, + bool is_encrypted) const; // Returns true if |codec| refers to a proprietary codec. bool IsCodecProprietary(Codec codec) const; @@ -105,12 +136,13 @@ // Returns true if |mime_type_lower_case| has a default codec associated with // it and IsCodecSupported() returns true for that particular codec. - bool IsDefaultCodecSupportedLowerCase( - const std::string& mime_type_lower_case) const; + // |is_encrypted| means the codec will be used with encrypted blocks. + bool IsDefaultCodecSupportedLowerCase(const std::string& mime_type_lower_case, + bool is_encrypted) const; #if defined(OS_ANDROID) - // Checks special Android only codec restrictions. - bool IsCodecSupportedOnAndroid(Codec codec) const; + // Indicates the support of various codecs within the platform. + PlatformInfo platform_info_; #endif // A map of mime_types and hash map of the supported codecs for the mime_type.
diff --git a/media/base/mime_util_unittest.cc b/media/base/mime_util_unittest.cc index 34a2ea3..ca1f3937 100644 --- a/media/base/mime_util_unittest.cc +++ b/media/base/mime_util_unittest.cc
@@ -6,12 +6,115 @@ #include "base/macros.h" #include "base/strings/string_split.h" +#include "base/strings/stringprintf.h" #include "build/build_config.h" #include "media/base/mime_util.h" +#include "media/base/mime_util_internal.h" #include "media/media_features.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_ANDROID) +#include "base/android/build_info.h" +#endif + namespace media { +namespace internal { + +// MIME type for use with IsCodecSupportedOnPlatform() test; type is ignored in +// all cases except for when paired with the Opus codec. +const char kTestMimeType[] = "foo/foo"; + +// Helper method for creating a multi-value vector of |kTestStates| if +// |test_all_values| is true or if false, a single value vector containing +// |single_value|. +static std::vector<bool> CreateTestVector(bool test_all_values, + bool single_value) { + const bool kTestStates[] = {true, false}; + if (test_all_values) + return std::vector<bool>(kTestStates, kTestStates + arraysize(kTestStates)); + return std::vector<bool>(1, single_value); +} + +// Helper method for running IsCodecSupportedOnPlatform() tests that will +// iterate over all possible field values for a MimeUtil::PlatformInfo struct. +// +// To request a field be varied, set its value to true in the |states_to_vary| +// struct. If false, the only value tested will be the field value from +// |test_states|. +// +// |test_func| should have the signature <void(const MimeUtil::PlatformInfo&, +// MimeUtil::Codec)>. +template <typename TestCallback> +static void RunCodecSupportTest(const MimeUtil::PlatformInfo& states_to_vary, + const MimeUtil::PlatformInfo& test_states, + TestCallback test_func) { +#define MAKE_TEST_VECTOR(name) \ + std::vector<bool> name##_states = \ + CreateTestVector(states_to_vary.name, test_states.name) + + // Stuff states to test into vectors for easy for_each() iteration. + MAKE_TEST_VECTOR(has_platform_decoders); + MAKE_TEST_VECTOR(has_platform_vp8_decoder); + MAKE_TEST_VECTOR(supports_opus); + MAKE_TEST_VECTOR(supports_vp9); + MAKE_TEST_VECTOR(is_unified_media_pipeline_enabled); +#undef MAKE_TEST_VECTOR + + MimeUtil::PlatformInfo info; + +#define RUN_TEST_VECTOR(name) \ + size_t name##_index = 0; \ + for (info.name = name##_states[name##_index]; \ + name##_index < name##_states.size(); ++name##_index) + + RUN_TEST_VECTOR(has_platform_decoders) { + RUN_TEST_VECTOR(has_platform_vp8_decoder) { + RUN_TEST_VECTOR(supports_opus) { + RUN_TEST_VECTOR(supports_vp9) { + RUN_TEST_VECTOR(is_unified_media_pipeline_enabled) { + for (int codec = MimeUtil::INVALID_CODEC; + codec <= MimeUtil::LAST_CODEC; ++codec) { + SCOPED_TRACE(base::StringPrintf( + "has_platform_decoders=%d, has_platform_vp8_decoder=%d, " + "supports_opus=%d, " + "supports_vp9=%d, is_unified_media_pipeline_enabled=%d, " + "codec=%d", + info.has_platform_decoders, info.has_platform_vp8_decoder, + info.supports_opus, info.supports_vp9, + info.is_unified_media_pipeline_enabled, codec)); + test_func(info, static_cast<MimeUtil::Codec>(codec)); + } + } + } + } + } + } +#undef RUN_TEST_VECTOR +} + +// Helper method for generating the |states_to_vary| value used by +// RunPlatformCodecTest(). Marks all fields to be varied. +static MimeUtil::PlatformInfo VaryAllFields() { + MimeUtil::PlatformInfo states_to_vary; + states_to_vary.has_platform_vp8_decoder = true; + states_to_vary.supports_opus = true; + states_to_vary.supports_vp9 = true; + states_to_vary.is_unified_media_pipeline_enabled = true; + states_to_vary.has_platform_decoders = true; + return states_to_vary; +} + +static bool HasHevcSupport() { +#if BUILDFLAG(ENABLE_HEVC_DEMUXING) +#if defined(OS_ANDROID) + return base::android::BuildInfo::GetInstance()->sdk_int() >= 21; +#else + return true; +#endif // defined(OS_ANDROID) +#else + return false; +#endif // BUILDFLAG(ENABLE_HEVC_DEMUXING) +} TEST(MimeUtilTest, CommonMediaMimeType) { EXPECT_TRUE(IsSupportedMediaMimeType("audio/webm")); @@ -111,4 +214,202 @@ EXPECT_EQ("mp4a.40.2", codecs_out[1]); } +TEST(IsCodecSupportedOnPlatformTest, + EncryptedCodecsFailWithoutPlatformSupport) { + // Vary all parameters except |has_platform_decoders|. + MimeUtil::PlatformInfo states_to_vary = VaryAllFields(); + states_to_vary.has_platform_decoders = false; + + // Disable platform decoders. + MimeUtil::PlatformInfo test_states; + test_states.has_platform_decoders = false; + + // Every codec should fail since platform support is missing and we've + // requested encrypted codecs. + RunCodecSupportTest( + states_to_vary, test_states, + [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) { + EXPECT_FALSE(MimeUtil::IsCodecSupportedOnPlatform(codec, kTestMimeType, + true, info)); + }); +} + +TEST(IsCodecSupportedOnPlatformTest, EncryptedCodecBehavior) { + // Vary all parameters except |has_platform_decoders|. + MimeUtil::PlatformInfo states_to_vary = VaryAllFields(); + states_to_vary.has_platform_decoders = false; + + // Enable platform decoders. + MimeUtil::PlatformInfo test_states; + test_states.has_platform_decoders = true; + + RunCodecSupportTest( + states_to_vary, test_states, + [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) { + const bool result = MimeUtil::IsCodecSupportedOnPlatform( + codec, kTestMimeType, true, info); + switch (codec) { + // These codecs are never supported by the Android platform. + case MimeUtil::INVALID_CODEC: + case MimeUtil::AC3: + case MimeUtil::EAC3: + case MimeUtil::MPEG2_AAC_LC: + case MimeUtil::MPEG2_AAC_MAIN: + case MimeUtil::MPEG2_AAC_SSR: + case MimeUtil::THEORA: + EXPECT_FALSE(result); + break; + + // These codecs are always available with platform decoder support. + case MimeUtil::PCM: + case MimeUtil::MP3: + case MimeUtil::MPEG4_AAC_LC: + case MimeUtil::MPEG4_AAC_SBR_v1: + case MimeUtil::MPEG4_AAC_SBR_PS_v2: + case MimeUtil::VORBIS: + case MimeUtil::H264: + EXPECT_TRUE(result); + break; + + // The remaining codecs are not available on all platforms even when + // a platform decoder is available. + case MimeUtil::OPUS: + EXPECT_EQ(info.supports_opus, result); + break; + + case MimeUtil::VP8: + EXPECT_EQ(info.has_platform_vp8_decoder, result); + break; + + case MimeUtil::VP9: + EXPECT_EQ(info.supports_vp9, result); + break; + + case MimeUtil::HEVC_MAIN: + EXPECT_EQ(HasHevcSupport(), result); + break; + } + }); +} + +TEST(IsCodecSupportedOnPlatformTest, ClearCodecBehaviorWithAndroidPipeline) { + // Vary all parameters except |is_unified_media_pipeline_enabled|. + MimeUtil::PlatformInfo states_to_vary = VaryAllFields(); + states_to_vary.is_unified_media_pipeline_enabled = false; + + // Disable the unified pipeline. + MimeUtil::PlatformInfo test_states; + test_states.is_unified_media_pipeline_enabled = false; + + RunCodecSupportTest( + states_to_vary, test_states, + [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) { + const bool result = MimeUtil::IsCodecSupportedOnPlatform( + codec, kTestMimeType, false, info); + switch (codec) { + // These codecs are never supported by the Android platform. + case MimeUtil::INVALID_CODEC: + case MimeUtil::AC3: + case MimeUtil::EAC3: + case MimeUtil::MPEG2_AAC_LC: + case MimeUtil::MPEG2_AAC_MAIN: + case MimeUtil::MPEG2_AAC_SSR: + case MimeUtil::THEORA: + EXPECT_FALSE(result); + break; + + // These codecs are always available via MediaPlayer. + case MimeUtil::PCM: + case MimeUtil::MP3: + case MimeUtil::MPEG4_AAC_LC: + case MimeUtil::MPEG4_AAC_SBR_v1: + case MimeUtil::MPEG4_AAC_SBR_PS_v2: + case MimeUtil::VORBIS: + case MimeUtil::H264: + case MimeUtil::VP8: + EXPECT_TRUE(result); + break; + + // The remaining codecs depend on the platform version. + case MimeUtil::OPUS: + EXPECT_EQ(info.supports_opus, result); + break; + + case MimeUtil::VP9: + EXPECT_EQ(info.supports_vp9, result); + break; + + case MimeUtil::HEVC_MAIN: + EXPECT_EQ(HasHevcSupport(), result); + break; + } + }); +} + +TEST(IsCodecSupportedOnPlatformTest, ClearCodecBehaviorWithUnifiedPipeline) { + // Vary all parameters except |is_unified_media_pipeline_enabled|. + MimeUtil::PlatformInfo states_to_vary = VaryAllFields(); + states_to_vary.is_unified_media_pipeline_enabled = false; + + // Enable the unified pipeline. + MimeUtil::PlatformInfo test_states; + test_states.is_unified_media_pipeline_enabled = true; + + RunCodecSupportTest( + states_to_vary, test_states, + [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) { + const bool result = MimeUtil::IsCodecSupportedOnPlatform( + codec, kTestMimeType, false, info); + switch (codec) { + // These codecs are never supported by the Android platform. + case MimeUtil::INVALID_CODEC: + case MimeUtil::AC3: + case MimeUtil::EAC3: + case MimeUtil::THEORA: + EXPECT_FALSE(result); + break; + + // These codecs are always supported with the unified pipeline. + case MimeUtil::PCM: + case MimeUtil::MPEG2_AAC_LC: + case MimeUtil::MPEG2_AAC_MAIN: + case MimeUtil::MPEG2_AAC_SSR: + case MimeUtil::MP3: + case MimeUtil::MPEG4_AAC_LC: + case MimeUtil::MPEG4_AAC_SBR_v1: + case MimeUtil::MPEG4_AAC_SBR_PS_v2: + case MimeUtil::OPUS: + case MimeUtil::VORBIS: + case MimeUtil::VP8: + case MimeUtil::VP9: + EXPECT_TRUE(result); + break; + + // These codecs are only supported if platform decoders are supported. + case MimeUtil::H264: + EXPECT_EQ(info.has_platform_decoders, result); + break; + + case MimeUtil::HEVC_MAIN: + EXPECT_EQ(HasHevcSupport() && info.has_platform_decoders, result); + break; + } + }); +} + +TEST(IsCodecSupportedOnPlatformTest, OpusOggSupport) { + // Vary all parameters; thus use default initial state. + MimeUtil::PlatformInfo states_to_vary = VaryAllFields(); + MimeUtil::PlatformInfo test_states; + + RunCodecSupportTest( + states_to_vary, test_states, + [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) { + EXPECT_EQ(info.is_unified_media_pipeline_enabled, + MimeUtil::IsCodecSupportedOnPlatform( + MimeUtil::OPUS, "audio/ogg", false, info)); + }); +} + +} // namespace internal } // namespace media
diff --git a/media/base/run_all_unittests.cc b/media/base/run_all_unittests.cc index 97e799b..cf00555a 100644 --- a/media/base/run_all_unittests.cc +++ b/media/base/run_all_unittests.cc
@@ -15,6 +15,7 @@ #if defined(OS_ANDROID) #include "base/android/jni_android.h" #include "media/base/android/media_jni_registrar.h" +#include "media/capture/video/android/capture_jni_registrar.h" #include "ui/gl/android/gl_jni_registrar.h" #endif @@ -43,6 +44,7 @@ // Needed for surface texture support. ui::gl::android::RegisterJni(env); media::RegisterJni(env); + media::RegisterCaptureJni(env); #endif // Run this here instead of main() to ensure an AtExitManager is already
diff --git a/media/blink/buffered_data_source.cc b/media/blink/buffered_data_source.cc index c6e11a6..b26f0f13 100644 --- a/media/blink/buffered_data_source.cc +++ b/media/blink/buffered_data_source.cc
@@ -541,7 +541,7 @@ if (assume_fully_buffered()) return; - bool is_downloading_data; + bool is_downloading_data = false; switch (state) { case BufferedResourceLoader::kLoading: is_downloading_data = true;
diff --git a/media/blink/key_system_config_selector.cc b/media/blink/key_system_config_selector.cc index 9c21228..766feda6 100644 --- a/media/blink/key_system_config_selector.cc +++ b/media/blink/key_system_config_selector.cc
@@ -282,7 +282,7 @@ std::vector<std::string> codec_vector; media::ParseCodecString(codecs, &codec_vector, false); media::SupportsType support_result = - media::IsSupportedMediaFormat(container_mime_type, codec_vector); + media::IsSupportedEncryptedMediaFormat(container_mime_type, codec_vector); switch (support_result) { case media::IsSupported: return true;
diff --git a/media/capture/video/android/BUILD.gn b/media/capture/video/android/BUILD.gn new file mode 100644 index 0000000..496481e --- /dev/null +++ b/media/capture/video/android/BUILD.gn
@@ -0,0 +1,50 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/config.gni") +import("//build/config/android/rules.gni") +import("//build/config/arm.gni") +import("//build/config/ui.gni") +import("//media/media_options.gni") + +assert(is_android) + +source_set("android") { + sources = [ + "capture_jni_registrar.cc", + "capture_jni_registrar.h", + ] + configs += [ + "//media:media_config", + "//media:media_implementation", + ] + deps = [ + ":capture_jni_headers", + ] +} + +generate_jni("capture_jni_headers") { + sources = [ + "java/src/org/chromium/media/VideoCapture.java", + "java/src/org/chromium/media/VideoCaptureFactory.java", + ] + jni_package = "media" +} + +java_cpp_enum("media_java_enums_srcjar") { + sources = [ + "../video_capture_device.h", + "video_capture_device_android.h", + ] +} + +android_library("capture_java") { + deps = [ + "//base:base_java", + ] + + srcjar_deps = [ ":media_java_enums_srcjar" ] + + DEPRECATED_java_in_dir = "java/src" +}
diff --git a/media/capture/video/android/capture_jni_registrar.cc b/media/capture/video/android/capture_jni_registrar.cc new file mode 100644 index 0000000..027583a --- /dev/null +++ b/media/capture/video/android/capture_jni_registrar.cc
@@ -0,0 +1,28 @@ +// 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/video/android/capture_jni_registrar.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "base/macros.h" + +#include "media/capture/video/android/video_capture_device_android.h" +#include "media/capture/video/android/video_capture_device_factory_android.h" + +namespace media { + +static base::android::RegistrationMethod kCaptureRegisteredMethods[] = { + {"VideoCaptureDevice", + VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice}, + {"VideoCaptureDeviceFactory", + VideoCaptureDeviceFactoryAndroid::RegisterVideoCaptureDeviceFactory}, +}; + +bool RegisterCaptureJni(JNIEnv* env) { + return base::android::RegisterNativeMethods( + env, kCaptureRegisteredMethods, arraysize(kCaptureRegisteredMethods)); +} + +} // namespace media
diff --git a/media/capture/video/android/capture_jni_registrar.h b/media/capture/video/android/capture_jni_registrar.h new file mode 100644 index 0000000..df15a10 --- /dev/null +++ b/media/capture/video/android/capture_jni_registrar.h
@@ -0,0 +1,19 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_CAPTURE_VIDEO_ANDROID_CAPTURE_JNI_REGISTRAR_H_ +#define MEDIA_CAPTURE_VIDEO_ANDROID_CAPTURE_JNI_REGISTRAR_H_ + +#include <jni.h> + +#include "media/base/media_export.h" + +namespace media { + +// Register all JNI bindings necessary for capture. +MEDIA_EXPORT bool RegisterCaptureJni(JNIEnv* env); + +} // namespace media + +#endif // MEDIA_CAPTURE_VIDEO_ANDROID_CAPTURE_JNI_REGISTRAR_H_
diff --git a/media/capture/video/android/java/src/org/chromium/media/OWNERS b/media/capture/video/android/java/src/org/chromium/media/OWNERS new file mode 100644 index 0000000..66e61fe4 --- /dev/null +++ b/media/capture/video/android/java/src/org/chromium/media/OWNERS
@@ -0,0 +1,2 @@ +mcasas@chromium.org +qinmin@chromium.org
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java b/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java new file mode 100644 index 0000000..2e2a289 --- /dev/null +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCapture.java
@@ -0,0 +1,120 @@ +// 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. + +package org.chromium.media; + +import android.content.Context; +import android.graphics.ImageFormat; +import android.view.Surface; +import android.view.WindowManager; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +/** + * Video Capture Device base class, defines a set of methods that native code + * needs to use to configure, start capture, and to be reached by callbacks and + * provides some neccesary data type(s) with accessors. + **/ +@JNINamespace("media") +public abstract class VideoCapture { + // The angle (0, 90, 180, 270) that the image needs to be rotated to show in + // the display's native orientation. + protected int mCameraNativeOrientation; + // In some occasions we need to invert the device rotation readings, see the + // individual implementations. + protected boolean mInvertDeviceOrientationReadings; + + protected VideoCaptureFormat mCaptureFormat = null; + protected final Context mContext; + protected final int mId; + // Native callback context variable. + protected final long mNativeVideoCaptureDeviceAndroid; + + VideoCapture(Context context, int id, long nativeVideoCaptureDeviceAndroid) { + mContext = context; + mId = id; + mNativeVideoCaptureDeviceAndroid = nativeVideoCaptureDeviceAndroid; + } + + // Allocate necessary resources for capture. + @CalledByNative + public abstract boolean allocate(int width, int height, int frameRate); + + // Starts actual capture. + @CalledByNative + public abstract boolean startCapture(); + + // Stops current capture. + @CalledByNative + public abstract boolean stopCapture(); + + @CalledByNative + public abstract void deallocate(); + + @CalledByNative + public final int queryWidth() { + return mCaptureFormat.mWidth; + } + + @CalledByNative + public final int queryHeight() { + return mCaptureFormat.mHeight; + } + + @CalledByNative + public final int queryFrameRate() { + return mCaptureFormat.mFramerate; + } + + @CalledByNative + public final int getColorspace() { + switch (mCaptureFormat.mPixelFormat) { + case ImageFormat.YV12: + return AndroidImageFormat.YV12; + case ImageFormat.YUV_420_888: + return AndroidImageFormat.YUV_420_888; + case ImageFormat.NV21: + return AndroidImageFormat.NV21; + case ImageFormat.UNKNOWN: + default: + return AndroidImageFormat.UNKNOWN; + } + } + + protected final int getCameraRotation() { + int rotation = mInvertDeviceOrientationReadings ? (360 - getDeviceRotation()) + : getDeviceRotation(); + return (mCameraNativeOrientation + rotation) % 360; + } + + protected final int getDeviceRotation() { + if (mContext == null) return 0; + final int orientation; + WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + switch (wm.getDefaultDisplay().getRotation()) { + case Surface.ROTATION_90: + orientation = 90; + break; + case Surface.ROTATION_180: + orientation = 180; + break; + case Surface.ROTATION_270: + orientation = 270; + break; + case Surface.ROTATION_0: + default: + orientation = 0; + break; + } + return orientation; + } + + // Method for VideoCapture implementations to call back native code. + public native void nativeOnFrameAvailable( + long nativeVideoCaptureDeviceAndroid, byte[] data, int length, int rotation); + + // Method for VideoCapture implementations to signal an asynchronous error. + public native void nativeOnError(long nativeVideoCaptureDeviceAndroid, String message); +}
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureAndroid.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureAndroid.java new file mode 100644 index 0000000..ce384d1 --- /dev/null +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureAndroid.java
@@ -0,0 +1,174 @@ +// 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. + +package org.chromium.media; + +import android.content.Context; +import android.graphics.ImageFormat; + +import org.chromium.base.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class extends the VideoCaptureCamera base class for manipulating normal + * video capture devices in Android, including receiving copies of preview + * frames via Java-allocated buffers. It also includes class BuggyDeviceHack to + * deal with troublesome devices. + **/ +@SuppressWarnings("deprecation") +public class VideoCaptureAndroid extends VideoCaptureCamera { + // Some devices don't support YV12 format correctly, even with JELLY_BEAN or + // newer OS. To work around the issues on those devices, we have to request + // NV21. This is supposed to be a temporary hack. + private static class BuggyDeviceHack { + private static final String[] COLORSPACE_BUGGY_DEVICE_LIST = { + "SAMSUNG-SGH-I747", "ODROID-U2", + // See https://crbug.com/577435 for more info. + "XT1092", "XT1095", "XT1096", + }; + + static int getImageFormat() { + for (String buggyDevice : COLORSPACE_BUGGY_DEVICE_LIST) { + if (buggyDevice.contentEquals(android.os.Build.MODEL)) { + return ImageFormat.NV21; + } + } + return ImageFormat.YV12; + } + } + + private int mExpectedFrameSize; + private static final int NUM_CAPTURE_BUFFERS = 3; + private static final String TAG = "cr.media"; + + static int getNumberOfCameras() { + return android.hardware.Camera.getNumberOfCameras(); + } + + static int getCaptureApiType(int id) { + if (VideoCaptureCamera.getCameraInfo(id) == null) { + return CaptureApiType.API_TYPE_UNKNOWN; + } + return CaptureApiType.API1; + } + + static String getName(int id) { + android.hardware.Camera.CameraInfo cameraInfo = VideoCaptureCamera.getCameraInfo(id); + if (cameraInfo == null) return null; + + return "camera " + id + ", facing " + + (cameraInfo.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT + ? "front" + : "back"); + } + + static VideoCaptureFormat[] getDeviceSupportedFormats(int id) { + android.hardware.Camera camera; + try { + camera = android.hardware.Camera.open(id); + } catch (RuntimeException ex) { + Log.e(TAG, "Camera.open: ", ex); + return null; + } + android.hardware.Camera.Parameters parameters = getCameraParameters(camera); + if (parameters == null) { + return null; + } + + ArrayList<VideoCaptureFormat> formatList = new ArrayList<VideoCaptureFormat>(); + // getSupportedPreview{Formats,FpsRange,PreviewSizes}() returns Lists + // with at least one element, but when the camera is in bad state, they + // can return null pointers; in that case we use a 0 entry, so we can + // retrieve as much information as possible. + List<Integer> pixelFormats = parameters.getSupportedPreviewFormats(); + if (pixelFormats == null) { + pixelFormats = new ArrayList<Integer>(); + } + if (pixelFormats.size() == 0) { + pixelFormats.add(ImageFormat.UNKNOWN); + } + for (Integer previewFormat : pixelFormats) { + int pixelFormat = AndroidImageFormat.UNKNOWN; + if (previewFormat == ImageFormat.YV12) { + pixelFormat = AndroidImageFormat.YV12; + } else if (previewFormat == ImageFormat.NV21) { + continue; + } + + List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange(); + if (listFpsRange == null) { + listFpsRange = new ArrayList<int[]>(); + } + if (listFpsRange.size() == 0) { + listFpsRange.add(new int[] {0, 0}); + } + for (int[] fpsRange : listFpsRange) { + List<android.hardware.Camera.Size> supportedSizes = + parameters.getSupportedPreviewSizes(); + if (supportedSizes == null) { + supportedSizes = new ArrayList<android.hardware.Camera.Size>(); + } + if (supportedSizes.size() == 0) { + supportedSizes.add(camera.new Size(0, 0)); + } + for (android.hardware.Camera.Size size : supportedSizes) { + formatList.add(new VideoCaptureFormat( + size.width, size.height, (fpsRange[1] + 999) / 1000, pixelFormat)); + } + } + } + camera.release(); + return formatList.toArray(new VideoCaptureFormat[formatList.size()]); + } + + VideoCaptureAndroid(Context context, int id, long nativeVideoCaptureDeviceAndroid) { + super(context, id, nativeVideoCaptureDeviceAndroid); + } + + @Override + protected void setCaptureParameters(int width, int height, int frameRate, + android.hardware.Camera.Parameters cameraParameters) { + mCaptureFormat = + new VideoCaptureFormat(width, height, frameRate, BuggyDeviceHack.getImageFormat()); + } + + @Override + protected void allocateBuffers() { + mExpectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight + * ImageFormat.getBitsPerPixel(mCaptureFormat.mPixelFormat) / 8; + for (int i = 0; i < NUM_CAPTURE_BUFFERS; i++) { + byte[] buffer = new byte[mExpectedFrameSize]; + mCamera.addCallbackBuffer(buffer); + } + } + + @Override + protected void setPreviewCallback(android.hardware.Camera.PreviewCallback cb) { + mCamera.setPreviewCallbackWithBuffer(cb); + } + + @Override + public void onPreviewFrame(byte[] data, android.hardware.Camera camera) { + mPreviewBufferLock.lock(); + try { + if (!mIsRunning) { + return; + } + if (data.length == mExpectedFrameSize) { + nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, data, mExpectedFrameSize, + getCameraRotation()); + } + } finally { + mPreviewBufferLock.unlock(); + if (camera != null) { + camera.addCallbackBuffer(data); + } + } + } + + // TODO(wjia): investigate whether reading from texture could give better + // performance and frame rate, using onFrameAvailable(). +}
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java new file mode 100644 index 0000000..afe58d3 --- /dev/null +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera.java
@@ -0,0 +1,286 @@ +// 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. + +package org.chromium.media; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.opengl.GLES20; +import android.os.Build; + +import org.chromium.base.Log; +import org.chromium.base.annotations.JNINamespace; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Video Capture Device extension of VideoCapture to provide common functionality + * for capture using android.hardware.Camera API (deprecated in API 21). Normal + * Android and Tango devices are extensions of this class. + **/ +@JNINamespace("media") +@SuppressWarnings("deprecation") +// TODO: is this class only used on ICS MR1 (or some later version) and above? +@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) +public abstract class VideoCaptureCamera + extends VideoCapture implements android.hardware.Camera.PreviewCallback { + protected android.hardware.Camera mCamera; + // Lock to mutually exclude execution of OnPreviewFrame() and {start/stop}Capture(). + protected ReentrantLock mPreviewBufferLock = new ReentrantLock(); + // True when native code has started capture. + protected boolean mIsRunning = false; + + protected int[] mGlTextures = null; + protected SurfaceTexture mSurfaceTexture = null; + protected static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; + + private static final String TAG = "cr.media"; + + protected static android.hardware.Camera.CameraInfo getCameraInfo(int id) { + android.hardware.Camera.CameraInfo cameraInfo = new android.hardware.Camera.CameraInfo(); + try { + android.hardware.Camera.getCameraInfo(id, cameraInfo); + } catch (RuntimeException ex) { + Log.e(TAG, "getCameraInfo: Camera.getCameraInfo: " + ex); + return null; + } + return cameraInfo; + } + + protected static android.hardware.Camera.Parameters getCameraParameters( + android.hardware.Camera camera) { + android.hardware.Camera.Parameters parameters; + try { + parameters = camera.getParameters(); + } catch (RuntimeException ex) { + Log.e(TAG, "getCameraParameters: android.hardware.Camera.getParameters: " + ex); + if (camera != null) camera.release(); + return null; + } + return parameters; + } + + VideoCaptureCamera(Context context, int id, long nativeVideoCaptureDeviceAndroid) { + super(context, id, nativeVideoCaptureDeviceAndroid); + } + + @Override + public boolean allocate(int width, int height, int frameRate) { + Log.d(TAG, "allocate: requested (%d x %d) @%dfps", width, height, frameRate); + try { + mCamera = android.hardware.Camera.open(mId); + } catch (RuntimeException ex) { + Log.e(TAG, "allocate: Camera.open: " + ex); + return false; + } + + android.hardware.Camera.CameraInfo cameraInfo = VideoCaptureCamera.getCameraInfo(mId); + if (cameraInfo == null) { + mCamera.release(); + mCamera = null; + return false; + } + mCameraNativeOrientation = cameraInfo.orientation; + // For Camera API, the readings of back-facing camera need to be inverted. + mInvertDeviceOrientationReadings = + (cameraInfo.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); + Log.d(TAG, "allocate: Rotation dev=%d, cam=%d, facing back? %s", getDeviceRotation(), + mCameraNativeOrientation, mInvertDeviceOrientationReadings); + + android.hardware.Camera.Parameters parameters = getCameraParameters(mCamera); + if (parameters == null) { + mCamera = null; + return false; + } + + // getSupportedPreviewFpsRange() returns a List with at least one + // element, but when camera is in bad state, it can return null pointer. + List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange(); + if (listFpsRange == null || listFpsRange.size() == 0) { + Log.e(TAG, "allocate: no fps range found"); + return false; + } + // API fps ranges are scaled up x1000 to avoid floating point. + int frameRateScaled = frameRate * 1000; + // Use the first range as the default chosen range. + int[] chosenFpsRange = listFpsRange.get(0); + int frameRateNearest = Math.abs(frameRateScaled - chosenFpsRange[0]) + < Math.abs(frameRateScaled - chosenFpsRange[1]) + ? chosenFpsRange[0] + : chosenFpsRange[1]; + int chosenFrameRate = (frameRateNearest + 999) / 1000; + int fpsRangeSize = Integer.MAX_VALUE; + for (int[] fpsRange : listFpsRange) { + if (fpsRange[0] <= frameRateScaled && frameRateScaled <= fpsRange[1] + && (fpsRange[1] - fpsRange[0]) <= fpsRangeSize) { + chosenFpsRange = fpsRange; + chosenFrameRate = frameRate; + fpsRangeSize = fpsRange[1] - fpsRange[0]; + } + } + Log.d(TAG, "allocate: fps set to %d, [%d-%d]", chosenFrameRate, chosenFpsRange[0], + chosenFpsRange[1]); + + // Calculate size. + List<android.hardware.Camera.Size> listCameraSize = parameters.getSupportedPreviewSizes(); + int minDiff = Integer.MAX_VALUE; + int matchedWidth = width; + int matchedHeight = height; + for (android.hardware.Camera.Size size : listCameraSize) { + int diff = Math.abs(size.width - width) + Math.abs(size.height - height); + Log.d(TAG, "allocate: supported (%d, %d), diff=%d", size.width, size.height, diff); + // TODO(wjia): Remove this hack (forcing width to be multiple + // of 32) by supporting stride in video frame buffer. + // Right now, VideoCaptureController requires compact YV12 + // (i.e., with no padding). + if (diff < minDiff && (size.width % 32 == 0)) { + minDiff = diff; + matchedWidth = size.width; + matchedHeight = size.height; + } + } + if (minDiff == Integer.MAX_VALUE) { + Log.e(TAG, "allocate: can not find a multiple-of-32 resolution"); + return false; + } + Log.d(TAG, "allocate: matched (%d x %d)", matchedWidth, matchedHeight); + + if (parameters.isVideoStabilizationSupported()) { + Log.d(TAG, "Image stabilization supported, currently: " + + parameters.getVideoStabilization() + ", setting it."); + parameters.setVideoStabilization(true); + } else { + Log.d(TAG, "Image stabilization not supported."); + } + + if (parameters.getSupportedFocusModes().contains( + android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { + parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); + } else { + Log.d(TAG, "Continuous focus mode not supported."); + } + + setCaptureParameters(matchedWidth, matchedHeight, chosenFrameRate, parameters); + parameters.setPictureSize(matchedWidth, matchedHeight); + parameters.setPreviewSize(matchedWidth, matchedHeight); + parameters.setPreviewFpsRange(chosenFpsRange[0], chosenFpsRange[1]); + parameters.setPreviewFormat(mCaptureFormat.mPixelFormat); + try { + mCamera.setParameters(parameters); + } catch (RuntimeException ex) { + Log.e(TAG, "setParameters: " + ex); + return false; + } + + // Set SurfaceTexture. Android Capture needs a SurfaceTexture even if + // it is not going to be used. + mGlTextures = new int[1]; + // Generate one texture pointer and bind it as an external texture. + GLES20.glGenTextures(1, mGlTextures, 0); + GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mGlTextures[0]); + // No mip-mapping with camera source. + GLES20.glTexParameterf( + GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf( + GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + // Clamp to edge is only option. + GLES20.glTexParameteri( + GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri( + GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + + mSurfaceTexture = new SurfaceTexture(mGlTextures[0]); + mSurfaceTexture.setOnFrameAvailableListener(null); + try { + mCamera.setPreviewTexture(mSurfaceTexture); + } catch (IOException ex) { + Log.e(TAG, "allocate: " + ex); + return false; + } + + allocateBuffers(); + return true; + } + + @Override + public boolean startCapture() { + if (mCamera == null) { + Log.e(TAG, "startCapture: camera is null"); + return false; + } + + mPreviewBufferLock.lock(); + try { + if (mIsRunning) { + return true; + } + mIsRunning = true; + } finally { + mPreviewBufferLock.unlock(); + } + setPreviewCallback(this); + try { + mCamera.startPreview(); + } catch (RuntimeException ex) { + Log.e(TAG, "startCapture: Camera.startPreview: " + ex); + return false; + } + return true; + } + + @Override + public boolean stopCapture() { + if (mCamera == null) { + Log.e(TAG, "stopCapture: camera is null"); + return true; + } + + mPreviewBufferLock.lock(); + try { + if (!mIsRunning) { + return true; + } + mIsRunning = false; + } finally { + mPreviewBufferLock.unlock(); + } + + mCamera.stopPreview(); + setPreviewCallback(null); + return true; + } + + @Override + public void deallocate() { + if (mCamera == null) return; + + stopCapture(); + try { + mCamera.setPreviewTexture(null); + if (mGlTextures != null) GLES20.glDeleteTextures(1, mGlTextures, 0); + mCaptureFormat = null; + mCamera.release(); + mCamera = null; + } catch (IOException ex) { + Log.e(TAG, "deallocate: failed to deallocate camera, " + ex); + return; + } + } + + // Local hook to allow derived classes to configure and plug capture + // buffers if needed. + abstract void allocateBuffers(); + + // Local hook to allow derived classes to fill capture format and modify + // camera parameters as they see fit. + abstract void setCaptureParameters(int width, int height, int frameRate, + android.hardware.Camera.Parameters cameraParameters); + + // Local method to be overriden with the particular setPreviewCallback to be + // used in the implementations. + abstract void setPreviewCallback(android.hardware.Camera.PreviewCallback cb); +}
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java new file mode 100644 index 0000000..58510275 --- /dev/null +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -0,0 +1,491 @@ +// 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. + +package org.chromium.media; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.Image; +import android.media.ImageReader; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Size; +import android.view.Surface; + +import org.chromium.base.Log; +import org.chromium.base.annotations.JNINamespace; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +/** + * This class implements Video Capture using Camera2 API, introduced in Android + * API 21 (L Release). Capture takes place in the current Looper, while pixel + * download takes place in another thread used by ImageReader. A number of + * static methods are provided to retrieve information on current system cameras + * and their capabilities, using android.hardware.camera2.CameraManager. + **/ +@JNINamespace("media") +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class VideoCaptureCamera2 extends VideoCapture { + // Inner class to extend a CameraDevice state change listener. + private class CrStateListener extends CameraDevice.StateCallback { + @Override + public void onOpened(CameraDevice cameraDevice) { + mCameraDevice = cameraDevice; + changeCameraStateAndNotify(CameraState.CONFIGURING); + if (!createCaptureObjects()) { + changeCameraStateAndNotify(CameraState.STOPPED); + nativeOnError(mNativeVideoCaptureDeviceAndroid, "Error configuring camera"); + } + } + + @Override + public void onDisconnected(CameraDevice cameraDevice) { + cameraDevice.close(); + mCameraDevice = null; + changeCameraStateAndNotify(CameraState.STOPPED); + } + + @Override + public void onError(CameraDevice cameraDevice, int error) { + cameraDevice.close(); + mCameraDevice = null; + changeCameraStateAndNotify(CameraState.STOPPED); + nativeOnError(mNativeVideoCaptureDeviceAndroid, + "Camera device error " + Integer.toString(error)); + } + }; + + // Inner class to extend a Capture Session state change listener. + private class CrCaptureSessionListener extends CameraCaptureSession.StateCallback { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + Log.d(TAG, "onConfigured"); + mCaptureSession = cameraCaptureSession; + createCaptureRequest(); + changeCameraStateAndNotify(CameraState.STARTED); + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + // TODO(mcasas): When signalling error, C++ will tear us down. Is there need for + // cleanup? + changeCameraStateAndNotify(CameraState.STOPPED); + nativeOnError(mNativeVideoCaptureDeviceAndroid, "Camera session configuration error"); + } + }; + + // Internal class implementing the ImageReader listener. Gets pinged when a + // new frame is been captured and downloaded to memory-backed buffers. + private class CrImageReaderListener implements ImageReader.OnImageAvailableListener { + @Override + public void onImageAvailable(ImageReader reader) { + Image image = null; + try { + image = reader.acquireLatestImage(); + if (image == null) return; + if (image.getFormat() != ImageFormat.YUV_420_888 || image.getPlanes().length != 3) { + Log.e(TAG, "Unexpected image format: %d or #planes: %d", image.getFormat(), + image.getPlanes().length); + return; + } + + if (reader.getWidth() != image.getWidth() + || reader.getHeight() != image.getHeight()) { + throw new IllegalStateException("ImageReader size " + reader.getWidth() + "x" + + reader.getHeight() + " did not match Image size " + image.getWidth() + + "x" + image.getHeight()); + } + readImageIntoBuffer(image, mCapturedData); + nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, mCapturedData, + mCapturedData.length, getCameraRotation()); + } catch (IllegalStateException ex) { + Log.e(TAG, "acquireLatestImage():" + ex); + return; + } finally { + if (image != null) { + image.close(); + } + } + } + }; + + private byte[] mCapturedData; + + private CameraDevice mCameraDevice = null; + private CaptureRequest.Builder mPreviewBuilder = null; + private CameraCaptureSession mCaptureSession = null; + private ImageReader mImageReader = null; + + private static final double kNanoSecondsToFps = 1.0E-9; + private static final String TAG = "cr.media"; + + private static enum CameraState { OPENING, CONFIGURING, STARTED, STOPPED } + private CameraState mCameraState = CameraState.STOPPED; + private final Object mCameraStateLock = new Object(); + + // Service function to grab CameraCharacteristics and handle exceptions. + private static CameraCharacteristics getCameraCharacteristics(Context appContext, int id) { + final CameraManager manager = + (CameraManager) appContext.getSystemService(Context.CAMERA_SERVICE); + try { + return manager.getCameraCharacteristics(Integer.toString(id)); + } catch (CameraAccessException ex) { + Log.e(TAG, "getNumberOfCameras: getCameraIdList(): " + ex); + } + return null; + } + + private boolean createCaptureObjects() { + Log.d(TAG, "createCaptureObjects"); + if (mCameraDevice == null) return false; + + // Create an ImageReader and plug a thread looper into it to have + // readback take place on its own thread. + final int maxImages = 2; + mImageReader = ImageReader.newInstance(mCaptureFormat.getWidth(), + mCaptureFormat.getHeight(), mCaptureFormat.getPixelFormat(), maxImages); + HandlerThread thread = new HandlerThread("CameraPreview"); + thread.start(); + final Handler backgroundHandler = new Handler(thread.getLooper()); + final CrImageReaderListener imageReaderListener = new CrImageReaderListener(); + mImageReader.setOnImageAvailableListener(imageReaderListener, backgroundHandler); + + // The Preview template specifically means "high frame rate is given + // priority over the highest-quality post-processing". + try { + mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + } catch (CameraAccessException ex) { + Log.e(TAG, "createCaptureRequest: " + ex); + return false; + } catch (IllegalArgumentException ex) { + Log.e(TAG, "createCaptureRequest: " + ex); + return false; + } catch (SecurityException ex) { + Log.e(TAG, "createCaptureRequest: " + ex); + return false; + } + if (mPreviewBuilder == null) { + Log.e(TAG, "mPreviewBuilder error"); + return false; + } + // Construct an ImageReader Surface and plug it into our CaptureRequest.Builder. + mPreviewBuilder.addTarget(mImageReader.getSurface()); + + // A series of configuration options in the PreviewBuilder + mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); + mPreviewBuilder.set( + CaptureRequest.NOISE_REDUCTION_MODE, CameraMetadata.NOISE_REDUCTION_MODE_FAST); + mPreviewBuilder.set(CaptureRequest.EDGE_MODE, CameraMetadata.EDGE_MODE_FAST); + mPreviewBuilder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, + CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON); + // SENSOR_EXPOSURE_TIME ? + + List<Surface> surfaceList = new ArrayList<Surface>(1); + surfaceList.add(mImageReader.getSurface()); + final CrCaptureSessionListener captureSessionListener = new CrCaptureSessionListener(); + try { + mCameraDevice.createCaptureSession(surfaceList, captureSessionListener, null); + } catch (CameraAccessException ex) { + Log.e(TAG, "createCaptureSession: " + ex); + return false; + } catch (IllegalArgumentException ex) { + Log.e(TAG, "createCaptureSession: " + ex); + return false; + } catch (SecurityException ex) { + Log.e(TAG, "createCaptureSession: " + ex); + return false; + } + // Wait for trigger on CrCaptureSessionListener.onConfigured(); + return true; + } + + private boolean createCaptureRequest() { + Log.d(TAG, "createCaptureRequest"); + try { + // This line triggers the capture. No |listener| is registered, so + // we will not get notified of capture events, instead, ImageReader + // will trigger every time a downloaded image is ready. Since + //|handler| is null, we'll work on the current Thread Looper. + mCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), null, null); + } catch (CameraAccessException ex) { + Log.e(TAG, "setRepeatingRequest: " + ex); + return false; + } catch (IllegalArgumentException ex) { + Log.e(TAG, "setRepeatingRequest: " + ex); + return false; + } catch (SecurityException ex) { + Log.e(TAG, "setRepeatingRequest: " + ex); + return false; + } + // Now wait for trigger on CrImageReaderListener.onImageAvailable(); + return true; + } + + private static void readImageIntoBuffer(Image image, byte[] data) { + final int imageWidth = image.getWidth(); + final int imageHeight = image.getHeight(); + final Image.Plane[] planes = image.getPlanes(); + + int offset = 0; + for (int plane = 0; plane < planes.length; ++plane) { + final ByteBuffer buffer = planes[plane].getBuffer(); + final int rowStride = planes[plane].getRowStride(); + // Experimentally, U and V planes have |pixelStride| = 2, which + // essentially means they are packed. That's silly, because we are + // forced to unpack here. + final int pixelStride = planes[plane].getPixelStride(); + final int planeWidth = (plane == 0) ? imageWidth : imageWidth / 2; + final int planeHeight = (plane == 0) ? imageHeight : imageHeight / 2; + + if (pixelStride == 1 && rowStride == planeWidth) { + // Copy whole plane from buffer into |data| at once. + buffer.get(data, offset, planeWidth * planeHeight); + offset += planeWidth * planeHeight; + } else { + // Copy pixels one by one respecting pixelStride and rowStride. + byte[] rowData = new byte[rowStride]; + for (int row = 0; row < planeHeight - 1; ++row) { + buffer.get(rowData, 0, rowStride); + for (int col = 0; col < planeWidth; ++col) { + data[offset++] = rowData[col * pixelStride]; + } + } + + // Last row is special in some devices and may not contain the full + // |rowStride| bytes of data. See http://crbug.com/458701. + buffer.get(rowData, 0, Math.min(rowStride, buffer.remaining())); + for (int col = 0; col < planeWidth; ++col) { + data[offset++] = rowData[col * pixelStride]; + } + } + } + } + + private void changeCameraStateAndNotify(CameraState state) { + synchronized (mCameraStateLock) { + mCameraState = state; + mCameraStateLock.notifyAll(); + } + } + + static boolean isLegacyDevice(Context appContext, int id) { + final CameraCharacteristics cameraCharacteristics = + getCameraCharacteristics(appContext, id); + return cameraCharacteristics != null + && cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) + == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; + } + + static int getNumberOfCameras(Context appContext) { + final CameraManager manager = + (CameraManager) appContext.getSystemService(Context.CAMERA_SERVICE); + try { + return manager.getCameraIdList().length; + } catch (CameraAccessException ex) { + Log.e(TAG, "getNumberOfCameras: getCameraIdList(): " + ex); + return 0; + } + } + + static int getCaptureApiType(int id, Context appContext) { + final CameraCharacteristics cameraCharacteristics = + getCameraCharacteristics(appContext, id); + if (cameraCharacteristics == null) { + return CaptureApiType.API_TYPE_UNKNOWN; + } + + final int supportedHWLevel = + cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); + switch (supportedHWLevel) { + case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY: + return CaptureApiType.API2_LEGACY; + case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL: + return CaptureApiType.API2_FULL; + case CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED: + return CaptureApiType.API2_LIMITED; + default: + return CaptureApiType.API2_LEGACY; + } + } + + static String getName(int id, Context appContext) { + final CameraCharacteristics cameraCharacteristics = + getCameraCharacteristics(appContext, id); + if (cameraCharacteristics == null) return null; + final int facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); + return "camera2 " + id + ", facing " + + ((facing == CameraCharacteristics.LENS_FACING_FRONT) ? "front" : "back"); + } + + static VideoCaptureFormat[] getDeviceSupportedFormats(Context appContext, int id) { + final CameraCharacteristics cameraCharacteristics = + getCameraCharacteristics(appContext, id); + if (cameraCharacteristics == null) return null; + + final int[] capabilities = + cameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); + // Per-format frame rate via getOutputMinFrameDuration() is only available if the + // property REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR is set. + boolean minFrameDurationAvailable = false; + for (int cap : capabilities) { + if (cap == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) { + minFrameDurationAvailable = true; + break; + } + } + + ArrayList<VideoCaptureFormat> formatList = new ArrayList<VideoCaptureFormat>(); + final StreamConfigurationMap streamMap = + cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + final int[] formats = streamMap.getOutputFormats(); + for (int format : formats) { + final Size[] sizes = streamMap.getOutputSizes(format); + if (sizes == null) continue; + for (Size size : sizes) { + double minFrameRate = 0.0f; + if (minFrameDurationAvailable) { + final long minFrameDuration = streamMap.getOutputMinFrameDuration(format, size); + minFrameRate = (minFrameDuration == 0) + ? 0.0f + : (1.0 / kNanoSecondsToFps * minFrameDuration); + } else { + // TODO(mcasas): find out where to get the info from in this case. + // Hint: perhaps using SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS. + minFrameRate = 0.0; + } + formatList.add(new VideoCaptureFormat( + size.getWidth(), size.getHeight(), (int) minFrameRate, 0)); + } + } + return formatList.toArray(new VideoCaptureFormat[formatList.size()]); + } + + VideoCaptureCamera2(Context context, int id, long nativeVideoCaptureDeviceAndroid) { + super(context, id, nativeVideoCaptureDeviceAndroid); + } + + @Override + public boolean allocate(int width, int height, int frameRate) { + Log.d(TAG, "allocate: requested (%d x %d) @%dfps", width, height, frameRate); + synchronized (mCameraStateLock) { + if (mCameraState == CameraState.OPENING || mCameraState == CameraState.CONFIGURING) { + Log.e(TAG, "allocate() invoked while Camera is busy opening/configuring."); + return false; + } + } + final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(mContext, mId); + final StreamConfigurationMap streamMap = + cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + + // Find closest supported size. + final Size[] supportedSizes = streamMap.getOutputSizes(ImageFormat.YUV_420_888); + if (supportedSizes == null) return false; + Size closestSupportedSize = null; + int minDiff = Integer.MAX_VALUE; + for (Size size : supportedSizes) { + final int diff = + Math.abs(size.getWidth() - width) + Math.abs(size.getHeight() - height); + if (diff < minDiff) { + minDiff = diff; + closestSupportedSize = size; + } + } + if (minDiff == Integer.MAX_VALUE) { + Log.e(TAG, "No supported resolutions."); + return false; + } + Log.d(TAG, "allocate: matched (%d x %d)", closestSupportedSize.getWidth(), + closestSupportedSize.getHeight()); + + // |mCaptureFormat| is also used to configure the ImageReader. + mCaptureFormat = new VideoCaptureFormat(closestSupportedSize.getWidth(), + closestSupportedSize.getHeight(), frameRate, ImageFormat.YUV_420_888); + int expectedFrameSize = mCaptureFormat.mWidth * mCaptureFormat.mHeight + * ImageFormat.getBitsPerPixel(mCaptureFormat.mPixelFormat) / 8; + mCapturedData = new byte[expectedFrameSize]; + mCameraNativeOrientation = + cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + // TODO(mcasas): The following line is correct for N5 with prerelease Build, + // but NOT for N7 with a dev Build. Figure out which one to support. + mInvertDeviceOrientationReadings = + cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) + == CameraCharacteristics.LENS_FACING_BACK; + return true; + } + + @Override + public boolean startCapture() { + Log.d(TAG, "startCapture"); + changeCameraStateAndNotify(CameraState.OPENING); + final CameraManager manager = + (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); + final Handler mainHandler = new Handler(mContext.getMainLooper()); + final CrStateListener stateListener = new CrStateListener(); + try { + manager.openCamera(Integer.toString(mId), stateListener, mainHandler); + } catch (CameraAccessException ex) { + Log.e(TAG, "allocate: manager.openCamera: " + ex); + return false; + } catch (IllegalArgumentException ex) { + Log.e(TAG, "allocate: manager.openCamera: " + ex); + return false; + } catch (SecurityException ex) { + Log.e(TAG, "allocate: manager.openCamera: " + ex); + return false; + } + + return true; + } + + @Override + public boolean stopCapture() { + Log.d(TAG, "stopCapture"); + + // With Camera2 API, the capture is started asynchronously, which will cause problem if + // stopCapture comes too quickly. Without stopping the previous capture properly, the next + // startCapture will fail and make Chrome no-responding. So wait camera to be STARTED. + synchronized (mCameraStateLock) { + while (mCameraState != CameraState.STARTED && mCameraState != CameraState.STOPPED) { + try { + mCameraStateLock.wait(); + } catch (InterruptedException ex) { + Log.e(TAG, "CaptureStartedEvent: " + ex); + } + } + if (mCameraState == CameraState.STOPPED) return true; + } + + try { + mCaptureSession.abortCaptures(); + } catch (CameraAccessException ex) { + Log.e(TAG, "abortCaptures: " + ex); + return false; + } catch (IllegalStateException ex) { + Log.e(TAG, "abortCaptures: " + ex); + return false; + } + if (mCameraDevice == null) return false; + mCameraDevice.close(); + changeCameraStateAndNotify(CameraState.STOPPED); + return true; + } + + @Override + public void deallocate() { + Log.d(TAG, "deallocate"); + } +}
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFactory.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFactory.java new file mode 100644 index 0000000..c88f9f7 --- /dev/null +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFactory.java
@@ -0,0 +1,164 @@ +// 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. + +package org.chromium.media; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; + +import org.chromium.base.Log; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +/** + * This class implements a factory of Android Video Capture objects for Chrome. + * The static createVideoCapture() returns either a "normal" VideoCaptureAndroid + * or a "special" VideoCaptureTango. Cameras are identified by |id|, where Tango + * cameras have |id| above the standard ones. Video Capture objects allocated + * via createVideoCapture() are explicitly owned by the caller. + * ChromiumCameraInfo is an internal class with some static methods needed from + * the rest of the class to manipulate the |id|s of normal and special devices. + **/ +@JNINamespace("media") +@SuppressWarnings("deprecation") +class VideoCaptureFactory { + // Internal class to encapsulate camera device id manipulations. + static class ChromiumCameraInfo { + // Special devices have more cameras than usual. Those devices are + // identified by model & device. Currently only the Tango is supported. + // Note that these devices have no Camera.CameraInfo. + private static final String[][] SPECIAL_DEVICE_LIST = { + {"Peanut", "peanut"}, + }; + private static int sNumberOfSystemCameras = -1; + private static final String TAG = "cr.media"; + + private static boolean isSpecialDevice() { + for (String[] device : SPECIAL_DEVICE_LIST) { + if (device[0].contentEquals(android.os.Build.MODEL) + && device[1].contentEquals(android.os.Build.DEVICE)) { + return true; + } + } + return false; + } + + private static boolean isSpecialCamera(int id) { + return id >= sNumberOfSystemCameras; + } + + private static int toSpecialCameraId(int id) { + assert isSpecialCamera(id); + return id - sNumberOfSystemCameras; + } + + private static int getNumberOfCameras(Context appContext) { + if (sNumberOfSystemCameras == -1) { + // getNumberOfCameras() would not fail due to lack of permission, but the + // following operations on camera would. "No permission" isn't a fatal + // error in WebView, specially for those applications which have no purpose + // to use a camera, but "load page" requires it. So, output a warning log + // and carry on pretending the system has no camera(s). This optimization + // applies only to pre-M on Android because that is when runtime permissions + // were introduced. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M + && appContext.getPackageManager().checkPermission( + Manifest.permission.CAMERA, appContext.getPackageName()) + != PackageManager.PERMISSION_GRANTED) { + sNumberOfSystemCameras = 0; + Log.w(TAG, "Missing android.permission.CAMERA permission, " + + "no system camera available."); + } else { + if (isLReleaseOrLater()) { + sNumberOfSystemCameras = VideoCaptureCamera2.getNumberOfCameras(appContext); + } else { + sNumberOfSystemCameras = VideoCaptureAndroid.getNumberOfCameras(); + if (isSpecialDevice()) { + Log.d(TAG, "Special device: %s", android.os.Build.MODEL); + sNumberOfSystemCameras += VideoCaptureTango.numberOfCameras(); + } + } + } + } + return sNumberOfSystemCameras; + } + } + + private static boolean isLReleaseOrLater() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + } + + // Factory methods. + @CalledByNative + static VideoCapture createVideoCapture( + Context context, int id, long nativeVideoCaptureDeviceAndroid) { + if (isLReleaseOrLater() && !VideoCaptureCamera2.isLegacyDevice(context, id)) { + return new VideoCaptureCamera2(context, id, nativeVideoCaptureDeviceAndroid); + } + if (!ChromiumCameraInfo.isSpecialCamera(id)) { + return new VideoCaptureAndroid(context, id, nativeVideoCaptureDeviceAndroid); + } + return new VideoCaptureTango( + context, ChromiumCameraInfo.toSpecialCameraId(id), nativeVideoCaptureDeviceAndroid); + } + + @CalledByNative + static int getNumberOfCameras(Context appContext) { + return ChromiumCameraInfo.getNumberOfCameras(appContext); + } + + @CalledByNative + static int getCaptureApiType(int id, Context appContext) { + if (isLReleaseOrLater()) { + return VideoCaptureCamera2.getCaptureApiType(id, appContext); + } else if (ChromiumCameraInfo.isSpecialCamera(id)) { + return VideoCaptureTango.getCaptureApiType(ChromiumCameraInfo.toSpecialCameraId(id)); + } else { + return VideoCaptureAndroid.getCaptureApiType(id); + } + } + + @CalledByNative + static String getDeviceName(int id, Context appContext) { + if (isLReleaseOrLater() && !VideoCaptureCamera2.isLegacyDevice(appContext, id)) { + return VideoCaptureCamera2.getName(id, appContext); + } + return (ChromiumCameraInfo.isSpecialCamera(id)) + ? VideoCaptureTango.getName(ChromiumCameraInfo.toSpecialCameraId(id)) + : VideoCaptureAndroid.getName(id); + } + + @CalledByNative + static VideoCaptureFormat[] getDeviceSupportedFormats(Context appContext, int id) { + if (isLReleaseOrLater() && !VideoCaptureCamera2.isLegacyDevice(appContext, id)) { + return VideoCaptureCamera2.getDeviceSupportedFormats(appContext, id); + } + return ChromiumCameraInfo.isSpecialCamera(id) + ? VideoCaptureTango.getDeviceSupportedFormats( + ChromiumCameraInfo.toSpecialCameraId(id)) + : VideoCaptureAndroid.getDeviceSupportedFormats(id); + } + + @CalledByNative + static int getCaptureFormatWidth(VideoCaptureFormat format) { + return format.getWidth(); + } + + @CalledByNative + static int getCaptureFormatHeight(VideoCaptureFormat format) { + return format.getHeight(); + } + + @CalledByNative + static int getCaptureFormatFramerate(VideoCaptureFormat format) { + return format.getFramerate(); + } + + @CalledByNative + static int getCaptureFormatPixelFormat(VideoCaptureFormat format) { + return format.getPixelFormat(); + } +}
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFormat.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFormat.java new file mode 100644 index 0000000..a8758437 --- /dev/null +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFormat.java
@@ -0,0 +1,35 @@ +// 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. + +package org.chromium.media; + +class VideoCaptureFormat { + int mWidth; + int mHeight; + final int mFramerate; + final int mPixelFormat; + + public VideoCaptureFormat(int width, int height, int framerate, int pixelformat) { + mWidth = width; + mHeight = height; + mFramerate = framerate; + mPixelFormat = pixelformat; + } + + public int getWidth() { + return mWidth; + } + + public int getHeight() { + return mHeight; + } + + public int getFramerate() { + return mFramerate; + } + + public int getPixelFormat() { + return mPixelFormat; + } +}
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureTango.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureTango.java new file mode 100644 index 0000000..b48539e --- /dev/null +++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureTango.java
@@ -0,0 +1,185 @@ +// 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. + +package org.chromium.media; + +import android.content.Context; +import android.graphics.ImageFormat; + +import org.chromium.base.Log; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * This class extends the VideoCapture base class for manipulating a Tango + * device's cameras, namely the associated Depth (z-Buffer), Fisheye and back- + * facing 4MP video capture devices. These devices are differentiated via the + * |id| passed on constructor, according to the index correspondence in + * |s_CAM_PARAMS|; all devices |id| are index 0 towards the parent VideoCapture. + **/ +@SuppressWarnings("deprecation") +public class VideoCaptureTango extends VideoCaptureCamera { + private static class CamParams { + final int mId; + final String mName; + final int mWidth; + final int mHeight; + + CamParams(int id, String name, int width, int height) { + mId = id; + mName = name; + mWidth = width; + mHeight = height; + } + } + + private ByteBuffer mFrameBuffer = null; + private final int mTangoCameraId; + // The indexes must coincide with |CAM_PARAMS| defined below. + private static final int DEPTH_CAMERA_ID = 0; + private static final int FISHEYE_CAMERA_ID = 1; + private static final int FOURMP_CAMERA_ID = 2; + private static final CamParams CAM_PARAMS[] = { + new CamParams(DEPTH_CAMERA_ID, "depth", 320, 240), + new CamParams(FISHEYE_CAMERA_ID, "fisheye", 640, 480), + new CamParams(FOURMP_CAMERA_ID, "4MP", 1280, 720)}; + + // SuperFrame size definitions. Note that total size is the amount of lines + // multiplied by 3/2 due to Chroma components following. + private static final int SF_WIDTH = 1280; + private static final int SF_HEIGHT = 1168; + private static final int SF_FULL_HEIGHT = SF_HEIGHT * 3 / 2; + private static final int SF_LINES_HEADER = 16; + private static final int SF_LINES_FISHEYE = 240; + private static final int SF_LINES_RESERVED = 80; // Spec says 96. + private static final int SF_LINES_DEPTH = 60; + private static final int SF_LINES_DEPTH_PADDED = 112; // Spec says 96. + private static final int SF_LINES_BIGIMAGE = 720; + private static final int SF_OFFSET_4MP_CHROMA = 112; + + private static final byte CHROMA_ZERO_LEVEL = 127; + private static final String TAG = "cr.media"; + + static int numberOfCameras() { + return CAM_PARAMS.length; + } + + static int getCaptureApiType(int index) { + if (index >= CAM_PARAMS.length) { + return CaptureApiType.API1; + } + return CaptureApiType.TANGO; + } + + static String getName(int index) { + if (index >= CAM_PARAMS.length) return ""; + return CAM_PARAMS[index].mName; + } + + static VideoCaptureFormat[] getDeviceSupportedFormats(int id) { + ArrayList<VideoCaptureFormat> formatList = new ArrayList<VideoCaptureFormat>(); + if (id == DEPTH_CAMERA_ID) { + formatList.add(new VideoCaptureFormat(320, 180, 5, ImageFormat.YV12)); + } else if (id == FISHEYE_CAMERA_ID) { + formatList.add(new VideoCaptureFormat(640, 480, 30, ImageFormat.YV12)); + } else if (id == FOURMP_CAMERA_ID) { + formatList.add(new VideoCaptureFormat(1280, 720, 20, ImageFormat.YV12)); + } + return formatList.toArray(new VideoCaptureFormat[formatList.size()]); + } + + VideoCaptureTango(Context context, int id, long nativeVideoCaptureDeviceAndroid) { + // All Tango cameras are like the back facing one for the generic VideoCapture code. + super(context, 0, nativeVideoCaptureDeviceAndroid); + mTangoCameraId = id; + } + + @Override + protected void setCaptureParameters(int width, int height, int frameRate, + android.hardware.Camera.Parameters cameraParameters) { + mCaptureFormat = new VideoCaptureFormat(CAM_PARAMS[mTangoCameraId].mWidth, + CAM_PARAMS[mTangoCameraId].mHeight, frameRate, ImageFormat.YV12); + // Connect Tango SuperFrame mode. Available sf modes are "all", + // "big-rgb", "small-rgb", "depth", "ir". + cameraParameters.set("sf-mode", "all"); + } + + @Override + protected void allocateBuffers() { + mFrameBuffer = + ByteBuffer.allocateDirect(mCaptureFormat.mWidth * mCaptureFormat.mHeight * 3 / 2); + // Prefill Chroma to their zero-equivalent for the cameras that only + // provide Luma component. + Arrays.fill(mFrameBuffer.array(), CHROMA_ZERO_LEVEL); + } + + @Override + protected void setPreviewCallback(android.hardware.Camera.PreviewCallback cb) { + mCamera.setPreviewCallback(cb); + } + + @Override + public void onPreviewFrame(byte[] data, android.hardware.Camera camera) { + mPreviewBufferLock.lock(); + try { + if (!mIsRunning) return; + if (data.length == SF_WIDTH * SF_FULL_HEIGHT) { + if (mTangoCameraId == DEPTH_CAMERA_ID) { + int sizeY = SF_WIDTH * SF_LINES_DEPTH; + int startY = + SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE + SF_LINES_RESERVED); + // Depth is composed of 16b samples in which only 12b are + // used. Throw away lowest 4 resolution bits. Android + // platforms are big endian, LSB in lowest address. In this + // case Chroma components are unused. No need to write them + // explicitly since they're filled to 128 on creation. + byte depthsample; + for (int j = startY; j < startY + 2 * sizeY; j += 2) { + depthsample = (byte) ((data[j + 1] << 4) | ((data[j] & 0xF0) >> 4)); + mFrameBuffer.put(depthsample); + } + for (int j = 0; j < mCaptureFormat.mWidth * mCaptureFormat.mHeight - sizeY; + ++j) { + mFrameBuffer.put((byte) 0); + } + } else if (mTangoCameraId == FISHEYE_CAMERA_ID) { + int sizeY = SF_WIDTH * SF_LINES_FISHEYE; + int startY = SF_WIDTH * SF_LINES_HEADER; + // Fisheye is black and white so Chroma components are unused. No need to write + // them explicitly since they're filled to 128 on creation. + ByteBuffer.wrap(data, startY, sizeY).get(mFrameBuffer.array(), 0, sizeY); + } else if (mTangoCameraId == FOURMP_CAMERA_ID) { + int startY = SF_WIDTH * (SF_LINES_HEADER + SF_LINES_FISHEYE + SF_LINES_RESERVED + + SF_LINES_DEPTH_PADDED); + int sizeY = SF_WIDTH * SF_LINES_BIGIMAGE; + + // The spec is completely inaccurate on the location, sizes + // and format of these channels. + int startU = SF_WIDTH * (SF_HEIGHT + SF_OFFSET_4MP_CHROMA); + int sizeU = SF_WIDTH * SF_LINES_BIGIMAGE / 4; + int startV = (SF_WIDTH * SF_HEIGHT * 5 / 4) + SF_WIDTH * SF_OFFSET_4MP_CHROMA; + int sizeV = SF_WIDTH * SF_LINES_BIGIMAGE / 4; + + // Equivalent to the following |for| loop but much faster: + // for (int i = START; i < START + SIZE; ++i) + // mFrameBuffer.put(data[i]); + ByteBuffer.wrap(data, startY, sizeY).get(mFrameBuffer.array(), 0, sizeY); + ByteBuffer.wrap(data, startU, sizeU).get(mFrameBuffer.array(), sizeY, sizeU); + ByteBuffer.wrap(data, startV, sizeV) + .get(mFrameBuffer.array(), sizeY + sizeU, sizeV); + } else { + Log.e(TAG, "Unknown camera, #id: %d", mTangoCameraId); + return; + } + mFrameBuffer.rewind(); // Important! + nativeOnFrameAvailable(mNativeVideoCaptureDeviceAndroid, mFrameBuffer.array(), + mFrameBuffer.capacity(), getCameraRotation()); + } + } finally { + mPreviewBufferLock.unlock(); + } + } +}
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc index f4ef6ad..5b61f9c 100644 --- a/media/capture/video/fake_video_capture_device.cc +++ b/media/capture/video/fake_video_capture_device.cc
@@ -73,10 +73,8 @@ } FakeVideoCaptureDevice::FakeVideoCaptureDevice(BufferOwnership buffer_ownership, - BufferPlanarity planarity, float fake_capture_rate) : buffer_ownership_(buffer_ownership), - planarity_(planarity), fake_capture_rate_(fake_capture_rate), weak_factory_(this) {} @@ -105,15 +103,9 @@ capture_format_.frame_size.SetSize(320, 240); if (buffer_ownership_ == BufferOwnership::CLIENT_BUFFERS) { - if (planarity_ == BufferPlanarity::PACKED) { - capture_format_.pixel_storage = PIXEL_STORAGE_CPU; - capture_format_.pixel_format = PIXEL_FORMAT_ARGB; - DVLOG(1) << "starting with client argb buffers"; - } else if (planarity_ == BufferPlanarity::TRIPLANAR) { - capture_format_.pixel_storage = PIXEL_STORAGE_GPUMEMORYBUFFER; - capture_format_.pixel_format = PIXEL_FORMAT_I420; - DVLOG(1) << "starting with gmb I420 buffers"; - } + capture_format_.pixel_storage = PIXEL_STORAGE_CPU; + capture_format_.pixel_format = PIXEL_FORMAT_ARGB; + DVLOG(1) << "starting with client argb buffers"; } else if (buffer_ownership_ == BufferOwnership::OWN_BUFFERS) { capture_format_.pixel_storage = PIXEL_STORAGE_CPU; capture_format_.pixel_format = PIXEL_FORMAT_I420; @@ -155,20 +147,9 @@ fake_capture_rate_, capture_format_.frame_size); // Give the captured frame to the client. - if (planarity_ == BufferPlanarity::PACKED) { - client_->OnIncomingCapturedData(fake_frame_.get(), frame_size, - capture_format_, 0 /* rotation */, - base::TimeTicks::Now()); - } else if (planarity_ == BufferPlanarity::TRIPLANAR) { - client_->OnIncomingCapturedYuvData( - fake_frame_.get(), - fake_frame_.get() + capture_format_.frame_size.GetArea(), - fake_frame_.get() + capture_format_.frame_size.GetArea() * 5 / 4, - capture_format_.frame_size.width(), - capture_format_.frame_size.width() / 2, - capture_format_.frame_size.width() / 2, capture_format_, - 0 /* rotation */, base::TimeTicks::Now()); - } + client_->OnIncomingCapturedData(fake_frame_.get(), frame_size, + capture_format_, 0 /* rotation */, + base::TimeTicks::Now()); BeepAndScheduleNextCapture( expected_execution_time, base::Bind(&FakeVideoCaptureDevice::CaptureUsingOwnBuffers,
diff --git a/media/capture/video/fake_video_capture_device.h b/media/capture/video/fake_video_capture_device.h index f6cc44b0..0112642 100644 --- a/media/capture/video/fake_video_capture_device.h +++ b/media/capture/video/fake_video_capture_device.h
@@ -16,7 +16,6 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "base/threading/thread.h" #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "media/capture/video/video_capture_device.h" @@ -30,15 +29,7 @@ CLIENT_BUFFERS, }; - enum class BufferPlanarity { - PACKED, - TRIPLANAR, - }; - FakeVideoCaptureDevice(BufferOwnership buffer_ownership, - BufferPlanarity planarity); - FakeVideoCaptureDevice(BufferOwnership buffer_ownership, - BufferPlanarity planarity, float fake_capture_rate); ~FakeVideoCaptureDevice() override; @@ -59,7 +50,6 @@ base::ThreadChecker thread_checker_; const BufferOwnership buffer_ownership_; - const BufferPlanarity planarity_; // Frame rate of the fake video device. const float fake_capture_rate_;
diff --git a/media/capture/video/fake_video_capture_device_factory.cc b/media/capture/video/fake_video_capture_device_factory.cc index e97f29b0..e228a5d 100644 --- a/media/capture/video/fake_video_capture_device_factory.cc +++ b/media/capture/video/fake_video_capture_device_factory.cc
@@ -24,7 +24,6 @@ FakeVideoCaptureDeviceFactory::FakeVideoCaptureDeviceFactory() : number_of_devices_(1), fake_vcd_ownership_(FakeVideoCaptureDevice::BufferOwnership::OWN_BUFFERS), - fake_vcd_planarity_(FakeVideoCaptureDevice::BufferPlanarity::PACKED), frame_rate_(kFakeCaptureDefaultFrameRate) {} scoped_ptr<VideoCaptureDevice> FakeVideoCaptureDeviceFactory::Create( @@ -36,8 +35,8 @@ for (int n = 0; n < number_of_devices_; ++n) { std::string possible_id = base::StringPrintf("/dev/video%d", n); if (device_name.id().compare(possible_id) == 0) { - return scoped_ptr<VideoCaptureDevice>(new FakeVideoCaptureDevice( - fake_vcd_ownership_, fake_vcd_planarity_, frame_rate_)); + return scoped_ptr<VideoCaptureDevice>( + new FakeVideoCaptureDevice(fake_vcd_ownership_, frame_rate_)); } } return scoped_ptr<VideoCaptureDevice>(); @@ -108,9 +107,6 @@ base::EqualsCaseInsensitiveASCII(param.back(), "client")) { fake_vcd_ownership_ = FakeVideoCaptureDevice::BufferOwnership::CLIENT_BUFFERS; - } else if (base::EqualsCaseInsensitiveASCII(param.front(), "planarity") && - base::EqualsCaseInsensitiveASCII(param.back(), "triplanar")) { - fake_vcd_planarity_ = FakeVideoCaptureDevice::BufferPlanarity::TRIPLANAR; } else if (base::EqualsCaseInsensitiveASCII(param.front(), "fps")) { double fps = 0; if (base::StringToDouble(param.back(), &fps)) {
diff --git a/media/capture/video/fake_video_capture_device_factory.h b/media/capture/video/fake_video_capture_device_factory.h index 9402580..e780f2a 100644 --- a/media/capture/video/fake_video_capture_device_factory.h +++ b/media/capture/video/fake_video_capture_device_factory.h
@@ -41,7 +41,6 @@ int number_of_devices_; FakeVideoCaptureDevice::BufferOwnership fake_vcd_ownership_; - FakeVideoCaptureDevice::BufferPlanarity fake_vcd_planarity_; float frame_rate_; };
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc index f61b085..eff85fc4 100644 --- a/media/capture/video/fake_video_capture_device_unittest.cc +++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -75,18 +75,6 @@ const base::TimeTicks& timestamp) { frame_cb_.Run(format); } - void OnIncomingCapturedYuvData(const uint8_t* y_data, - const uint8_t* u_data, - const uint8_t* v_data, - size_t y_stride, - size_t u_stride, - size_t v_stride, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - const base::TimeTicks& timestamp) { - frame_cb_.Run(frame_format); - } - // Virtual methods for capturing using Client's Buffers. scoped_ptr<Buffer> ReserveOutputBuffer(const gfx::Size& dimensions, media::VideoPixelFormat format, @@ -187,9 +175,7 @@ class FakeVideoCaptureDeviceTest : public FakeVideoCaptureDeviceBase, public ::testing::WithParamInterface< - ::testing::tuple<FakeVideoCaptureDevice::BufferOwnership, - FakeVideoCaptureDevice::BufferPlanarity, - float>> {}; + ::testing::tuple<FakeVideoCaptureDevice::BufferOwnership, float>> {}; struct CommandLineTestData { // Command line argument @@ -207,19 +193,18 @@ ASSERT_FALSE(names->empty()); scoped_ptr<VideoCaptureDevice> device(new FakeVideoCaptureDevice( - testing::get<0>(GetParam()), testing::get<1>(GetParam()), - testing::get<2>(GetParam()))); + testing::get<0>(GetParam()), testing::get<1>(GetParam()))); ASSERT_TRUE(device); VideoCaptureParams capture_params; capture_params.requested_format.frame_size.SetSize(640, 480); - capture_params.requested_format.frame_rate = testing::get<2>(GetParam()); + capture_params.requested_format.frame_rate = testing::get<1>(GetParam()); device->AllocateAndStart(capture_params, std::move(client_)); WaitForCapturedFrame(); EXPECT_EQ(last_format().frame_size.width(), 640); EXPECT_EQ(last_format().frame_size.height(), 480); - EXPECT_EQ(last_format().frame_rate, testing::get<2>(GetParam())); + EXPECT_EQ(last_format().frame_rate, testing::get<1>(GetParam())); device->StopAndDeAllocate(); } @@ -228,8 +213,6 @@ FakeVideoCaptureDeviceTest, Combine(Values(FakeVideoCaptureDevice::BufferOwnership::OWN_BUFFERS, FakeVideoCaptureDevice::BufferOwnership::CLIENT_BUFFERS), - Values(FakeVideoCaptureDevice::BufferPlanarity::PACKED, - FakeVideoCaptureDevice::BufferPlanarity::TRIPLANAR), Values(20, 29.97, 30, 50, 60))); TEST_F(FakeVideoCaptureDeviceTest, GetDeviceSupportedFormats) {
diff --git a/media/capture/video/linux/v4l2_capture_delegate.cc b/media/capture/video/linux/v4l2_capture_delegate.cc index 612672b..c49ecb7 100644 --- a/media/capture/video/linux/v4l2_capture_delegate.cc +++ b/media/capture/video/linux/v4l2_capture_delegate.cc
@@ -16,8 +16,6 @@ #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "media/base/bind_to_current_loop.h" -#include "media/capture/video/linux/v4l2_capture_delegate_multi_plane.h" -#include "media/capture/video/linux/v4l2_capture_delegate_single_plane.h" #include "media/capture/video/linux/video_capture_device_linux.h" namespace media { @@ -48,44 +46,76 @@ {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2, 1}, {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY, 1}, {V4L2_PIX_FMT_RGB24, PIXEL_FORMAT_RGB24, 1}, -#if !defined(OS_OPENBSD) - // TODO(mcasas): add V4L2_PIX_FMT_YVU420M when available in bots. - {V4L2_PIX_FMT_YUV420M, PIXEL_FORMAT_I420, 3}, -#endif // MJPEG is usually sitting fairly low since we don't want to have to - // decode. - // However, is needed for large resolutions due to USB bandwidth - // limitations, - // so GetListOfUsableFourCcs() can duplicate it on top, see that method. + // decode. However, it is needed for large resolutions due to USB bandwidth + // limitations, so GetListOfUsableFourCcs() can duplicate it on top, see + // that method. {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG, 1}, // JPEG works as MJPEG on some gspca webcams from field reports, see // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the - // least - // preferred format. + // least preferred format. {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1}, }; -// static -scoped_refptr<V4L2CaptureDelegate> -V4L2CaptureDelegate::CreateV4L2CaptureDelegate( - const VideoCaptureDevice::Name& device_name, - const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, - int power_line_frequency) { - switch (device_name.capture_api_type()) { - case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE: - return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane( - device_name, v4l2_task_runner, power_line_frequency)); - case VideoCaptureDevice::Name::V4L2_MULTI_PLANE: -#if !defined(OS_OPENBSD) - return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane( - device_name, v4l2_task_runner, power_line_frequency)); - default: -#endif - NOTIMPLEMENTED() << "Unknown V4L2 capture API type"; - return scoped_refptr<V4L2CaptureDelegate>(); - } +// Fill in |format| with the given parameters. +static void FillV4L2Format(v4l2_format* format, + uint32_t width, + uint32_t height, + uint32_t pixelformat_fourcc) { + memset(format, 0, sizeof(*format)); + format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + format->fmt.pix.width = width; + format->fmt.pix.height = height; + format->fmt.pix.pixelformat = pixelformat_fourcc; } +// Fills all parts of |buffer|. +static void FillV4L2Buffer(v4l2_buffer* buffer, int index) { + memset(buffer, 0, sizeof(*buffer)); + buffer->memory = V4L2_MEMORY_MMAP; + buffer->index = index; + buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +} + +static void FillV4L2RequestBuffer(v4l2_requestbuffers* request_buffer, + int count) { + memset(request_buffer, 0, sizeof(*request_buffer)); + request_buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + request_buffer->memory = V4L2_MEMORY_MMAP; + request_buffer->count = count; +} + +// Returns the input |fourcc| as a std::string four char representation. +static std::string FourccToString(uint32_t fourcc) { + return base::StringPrintf("%c%c%c%c", fourcc & 0xFF, (fourcc >> 8) & 0xFF, + (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF); +} + +// Class keeping track of a SPLANE V4L2 buffer, mmap()ed on construction and +// munmap()ed on destruction. +class V4L2CaptureDelegate::BufferTracker + : public base::RefCounted<BufferTracker> { + public: + BufferTracker(); + // Abstract method to mmap() given |fd| according to |buffer|. + bool Init(int fd, const v4l2_buffer& buffer); + + const uint8_t* start() const { return start_; } + size_t payload_size() const { return payload_size_; } + void set_payload_size(size_t payload_size) { + DCHECK_LE(payload_size, length_); + payload_size_ = payload_size; + } + + private: + friend class base::RefCounted<BufferTracker>; + virtual ~BufferTracker(); + + uint8_t* start_; + size_t length_; + size_t payload_size_; +}; + // static size_t V4L2CaptureDelegate::GetNumPlanesForFourCc(uint32_t fourcc) { for (const auto& fourcc_and_pixel_format : kSupportedFormatsAndPlanarity) { @@ -124,51 +154,16 @@ return supported_formats; } -// static -std::string V4L2CaptureDelegate::FourccToString(uint32_t fourcc) { - return base::StringPrintf("%c%c%c%c", fourcc & 0xFF, (fourcc >> 8) & 0xFF, - (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF); -} - -V4L2CaptureDelegate::BufferTracker::BufferTracker() { -} - -V4L2CaptureDelegate::BufferTracker::~BufferTracker() { - for (const auto& plane : planes_) { - if (plane.start == nullptr) - continue; - const int result = munmap(plane.start, plane.length); - PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer"; - } -} - -void V4L2CaptureDelegate::BufferTracker::AddMmapedPlane(uint8_t* const start, - size_t length) { - Plane plane; - plane.start = start; - plane.length = length; - plane.payload_size = 0; - planes_.push_back(plane); -} - V4L2CaptureDelegate::V4L2CaptureDelegate( const VideoCaptureDevice::Name& device_name, const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, int power_line_frequency) - : capture_type_((device_name.capture_api_type() == - VideoCaptureDevice::Name::V4L2_SINGLE_PLANE) - ? V4L2_BUF_TYPE_VIDEO_CAPTURE - : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), - v4l2_task_runner_(v4l2_task_runner), + : v4l2_task_runner_(v4l2_task_runner), device_name_(device_name), power_line_frequency_(power_line_frequency), is_capturing_(false), timeout_count_(0), - rotation_(0) { -} - -V4L2CaptureDelegate::~V4L2CaptureDelegate() { -} + rotation_(0) {} void V4L2CaptureDelegate::AllocateAndStart( int width, @@ -188,23 +183,21 @@ v4l2_capability cap = {}; if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) && - ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE || - cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) && - !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) && - !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) { + ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && + !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)))) { device_fd_.reset(); SetErrorState(FROM_HERE, "This is not a V4L2 video capture device"); return; } - // Get supported video formats in preferred order. - // For large resolutions, favour mjpeg over raw formats. + // Get supported video formats in preferred order. For large resolutions, + // favour mjpeg over raw formats. const std::list<uint32_t>& desired_v4l2_formats = GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight); std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end(); v4l2_fmtdesc fmtdesc = {}; - fmtdesc.type = capture_type_; + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0; ++fmtdesc.index) { best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat); @@ -215,12 +208,7 @@ } DVLOG(1) << "Chosen pixel format is " << FourccToString(*best); - - video_fmt_.type = capture_type_; - if (!FillV4L2Format(&video_fmt_, width, height, *best)) { - SetErrorState(FROM_HERE, "Failed filling in V4L2 Format"); - return; - } + FillV4L2Format(&video_fmt_, width, height, *best); if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt_)) < 0) { SetErrorState(FROM_HERE, "Failed to set video capture format"); @@ -235,7 +223,7 @@ // Set capture framerate in the form of capture interval. v4l2_streamparm streamparm = {}; - streamparm.type = capture_type_; + streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // The following line checks that the driver knows about framerate get/set. if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { // Now check if the device is able to accept a capture framerate set. @@ -279,10 +267,8 @@ capture_format_.frame_rate = frame_rate; capture_format_.pixel_format = pixel_format; - v4l2_requestbuffers r_buffer = {}; - r_buffer.type = capture_type_; - r_buffer.memory = V4L2_MEMORY_MMAP; - r_buffer.count = kNumVideoBuffers; + v4l2_requestbuffers r_buffer; + FillV4L2RequestBuffer(&r_buffer, kNumVideoBuffers); if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { SetErrorState(FROM_HERE, "Error requesting MMAP buffers from V4L2"); return; @@ -294,7 +280,8 @@ } } - if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) < + v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type)) < 0) { SetErrorState(FROM_HERE, "VIDIOC_STREAMON failed"); return; @@ -310,7 +297,8 @@ DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); // The order is important: stop streaming, clear |buffer_pool_|, // thus munmap()ing the v4l2_buffers, and then return them to the OS. - if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_)) < + v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type)) < 0) { SetErrorState(FROM_HERE, "VIDIOC_STREAMOFF failed"); return; @@ -318,10 +306,8 @@ buffer_tracker_pool_.clear(); - v4l2_requestbuffers r_buffer = {}; - r_buffer.type = capture_type_; - r_buffer.memory = V4L2_MEMORY_MMAP; - r_buffer.count = 0; + v4l2_requestbuffers r_buffer; + FillV4L2RequestBuffer(&r_buffer, 0); if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0"); @@ -338,6 +324,8 @@ rotation_ = rotation; } +V4L2CaptureDelegate::~V4L2CaptureDelegate() {} + bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { v4l2_buffer buffer; FillV4L2Buffer(&buffer, index); @@ -347,7 +335,7 @@ return false; } - const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker(); + const scoped_refptr<BufferTracker> buffer_tracker(new BufferTracker()); if (!buffer_tracker->Init(device_fd_.get(), buffer)) { DLOG(ERROR) << "Error creating BufferTracker"; return false; @@ -362,13 +350,6 @@ return true; } -void V4L2CaptureDelegate::FillV4L2Buffer(v4l2_buffer* buffer, int i) const { - memset(buffer, 0, sizeof(*buffer)); - buffer->memory = V4L2_MEMORY_MMAP; - buffer->index = i; - FinishFillingV4L2Buffer(buffer); -} - void V4L2CaptureDelegate::DoCapture() { DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); if (!is_capturing_) @@ -406,8 +387,12 @@ return; } - SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); - SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); + buffer_tracker_pool_[buffer.index]->set_payload_size(buffer.bytesused); + const scoped_refptr<BufferTracker>& buffer_tracker = + buffer_tracker_pool_[buffer.index]; + client_->OnIncomingCapturedData( + buffer_tracker->start(), buffer_tracker->payload_size(), + capture_format_, rotation_, base::TimeTicks::Now()); if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { SetErrorState(FROM_HERE, "Failed to enqueue capture buffer"); @@ -427,4 +412,29 @@ client_->OnError(from_here, reason); } +V4L2CaptureDelegate::BufferTracker::BufferTracker() {} + +V4L2CaptureDelegate::BufferTracker::~BufferTracker() { + if (start_ == nullptr) + return; + const int result = munmap(start_, length_); + PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer"; +} + +bool V4L2CaptureDelegate::BufferTracker::Init(int fd, + const v4l2_buffer& buffer) { + // Some devices require mmap() to be called with both READ and WRITE. + // See http://crbug.com/178582. + void* const start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, buffer.m.offset); + if (start == MAP_FAILED) { + DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace"; + return false; + } + start_ = static_cast<uint8_t*>(start); + length_ = buffer.length; + payload_size_ = 0; + return true; +} + } // namespace media
diff --git a/media/capture/video/linux/v4l2_capture_delegate.h b/media/capture/video/linux/v4l2_capture_delegate.h index 56af8e55c..9d4a8b3 100644 --- a/media/capture/video/linux/v4l2_capture_delegate.h +++ b/media/capture/video/linux/v4l2_capture_delegate.h
@@ -29,15 +29,10 @@ // Class doing the actual Linux capture using V4L2 API. V4L2 SPLANE/MPLANE // capture specifics are implemented in derived classes. Created and destroyed // on the owner's thread, otherwise living and operating on |v4l2_task_runner_|. -class V4L2CaptureDelegate +// TODO(mcasas): Make this class a non-ref-counted. +class V4L2CaptureDelegate final : public base::RefCountedThreadSafe<V4L2CaptureDelegate> { public: - // Creates the appropiate VideoCaptureDelegate according to parameters. - static scoped_refptr<V4L2CaptureDelegate> CreateV4L2CaptureDelegate( - const VideoCaptureDevice::Name& device_name, - const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, - int power_line_frequency); - // Retrieves the #planes for a given |fourcc|, or 0 if unknown. static size_t GetNumPlanesForFourCc(uint32_t fourcc); // Returns the Chrome pixel format for |v4l2_fourcc| or PIXEL_FORMAT_UNKNOWN. @@ -48,6 +43,11 @@ // preference, with MJPEG prioritised depending on |prefer_mjpeg|. static std::list<uint32_t> GetListOfUsableFourCcs(bool prefer_mjpeg); + V4L2CaptureDelegate( + const VideoCaptureDevice::Name& device_name, + const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, + int power_line_frequency); + // Forward-to versions of VideoCaptureDevice virtual methods. void AllocateAndStart(int width, int height, @@ -57,99 +57,19 @@ void SetRotation(int rotation); - protected: - // Class keeping track of SPLANE/MPLANE V4L2 buffers, mmap()ed on construction - // and munmap()ed on destruction. Destruction is syntactically equal for - // S/MPLANE but not construction, so this is implemented in derived classes. - // Internally it has a vector of planes, which for SPLANE will contain only - // one element. - class BufferTracker : public base::RefCounted<BufferTracker> { - public: - BufferTracker(); - // Abstract method to mmap() given |fd| according to |buffer|, planarity - // specific. - virtual bool Init(int fd, const v4l2_buffer& buffer) = 0; - - const uint8_t* GetPlaneStart(size_t plane) const { - DCHECK_LT(plane, planes_.size()); - return planes_[plane].start; - } - - size_t GetPlanePayloadSize(size_t plane) const { - DCHECK_LT(plane, planes_.size()); - return planes_[plane].payload_size; - } - - void SetPlanePayloadSize(size_t plane, size_t payload_size) { - DCHECK_LT(plane, planes_.size()); - DCHECK_LE(payload_size, planes_[plane].length); - planes_[plane].payload_size = payload_size; - } - - protected: - friend class base::RefCounted<BufferTracker>; - virtual ~BufferTracker(); - // Adds a given mmap()ed plane to |planes_|. - void AddMmapedPlane(uint8_t* const start, size_t length); - - private: - struct Plane { - uint8_t* start; - size_t length; - size_t payload_size; - }; - std::vector<Plane> planes_; - }; - - V4L2CaptureDelegate( - const VideoCaptureDevice::Name& device_name, - const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, - int power_line_frequency); - virtual ~V4L2CaptureDelegate(); - - // Creates the necessary, planarity-specific, internal tracking schemes, - virtual scoped_refptr<BufferTracker> CreateBufferTracker() const = 0; - - // Fill in |format| with the given parameters, in a planarity dependent way. - virtual bool FillV4L2Format(v4l2_format* format, - uint32_t width, - uint32_t height, - uint32_t pixelformat_fourcc) const = 0; - - // Finish filling |buffer| struct with planarity-dependent data. - virtual void FinishFillingV4L2Buffer(v4l2_buffer* buffer) const = 0; - - // Fetch the number of bytes occupied by data in |buffer| and set to - // |buffer_tracker|. - virtual void SetPayloadSize( - const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_buffer& buffer) const = 0; - - // Sends the captured |buffer| to the |client_|, synchronously. - virtual void SendBuffer(const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_format& format) const = 0; - - // A few accessors for SendBuffer()'s to access private member variables. - VideoCaptureFormat capture_format() const { return capture_format_; } - VideoCaptureDevice::Client* client() const { return client_.get(); } - int rotation() const { return rotation_; } - private: friend class base::RefCountedThreadSafe<V4L2CaptureDelegate>; + ~V4L2CaptureDelegate(); - // Returns the input |fourcc| as a std::string four char representation. - static std::string FourccToString(uint32_t fourcc); // VIDIOC_QUERYBUFs a buffer from V4L2, creates a BufferTracker for it and // enqueues it (VIDIOC_QBUF) back into V4L2. bool MapAndQueueBuffer(int index); - // Fills all common parts of |buffer|. Delegates to FinishFillingV4L2Buffer() - // for filling in the planar-dependent parts. - void FillV4L2Buffer(v4l2_buffer* buffer, int i) const; + void DoCapture(); + void SetErrorState(const tracked_objects::Location& from_here, const std::string& reason); - const v4l2_buf_type capture_type_; const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner_; const VideoCaptureDevice::Name device_name_; const int power_line_frequency_; @@ -161,6 +81,7 @@ base::ScopedFD device_fd_; // Vector of BufferTracker to keep track of mmap()ed pointers and their use. + class BufferTracker; std::vector<scoped_refptr<BufferTracker>> buffer_tracker_pool_; bool is_capturing_;
diff --git a/media/capture/video/linux/v4l2_capture_delegate_multi_plane.cc b/media/capture/video/linux/v4l2_capture_delegate_multi_plane.cc deleted file mode 100644 index 22e2af93..0000000 --- a/media/capture/video/linux/v4l2_capture_delegate_multi_plane.cc +++ /dev/null
@@ -1,100 +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. - -#include "media/capture/video/linux/v4l2_capture_delegate_multi_plane.h" - -#include <stddef.h> -#include <sys/mman.h> - -namespace media { - -V4L2CaptureDelegateMultiPlane::V4L2CaptureDelegateMultiPlane( - const VideoCaptureDevice::Name& device_name, - const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, - int power_line_frequency) - : V4L2CaptureDelegate(device_name, v4l2_task_runner, power_line_frequency) { -} - -V4L2CaptureDelegateMultiPlane::~V4L2CaptureDelegateMultiPlane() { -} - -scoped_refptr<V4L2CaptureDelegate::BufferTracker> -V4L2CaptureDelegateMultiPlane::CreateBufferTracker() const { - return make_scoped_refptr(new BufferTrackerMPlane()); -} - -bool V4L2CaptureDelegateMultiPlane::FillV4L2Format( - v4l2_format* format, - uint32_t width, - uint32_t height, - uint32_t pixelformat_fourcc) const { - format->fmt.pix_mp.width = width; - format->fmt.pix_mp.height = height; - format->fmt.pix_mp.pixelformat = pixelformat_fourcc; - - const size_t num_v4l2_planes = - V4L2CaptureDelegate::GetNumPlanesForFourCc(pixelformat_fourcc); - if (num_v4l2_planes == 0u) - return false; - DCHECK_LE(num_v4l2_planes, static_cast<size_t>(VIDEO_MAX_PLANES)); - format->fmt.pix_mp.num_planes = num_v4l2_planes; - - v4l2_planes_.resize(num_v4l2_planes); - return true; -} - -void V4L2CaptureDelegateMultiPlane::FinishFillingV4L2Buffer( - v4l2_buffer* buffer) const { - buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - buffer->length = v4l2_planes_.size(); - - static const struct v4l2_plane empty_plane = {}; - std::fill(v4l2_planes_.begin(), v4l2_planes_.end(), empty_plane); - buffer->m.planes = v4l2_planes_.data(); -} - -void V4L2CaptureDelegateMultiPlane::SetPayloadSize( - const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_buffer& buffer) const { - for (size_t i = 0; i < v4l2_planes_.size() && i < buffer.length; i++) - buffer_tracker->SetPlanePayloadSize(i, buffer.m.planes[i].bytesused); -} - -void V4L2CaptureDelegateMultiPlane::SendBuffer( - const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_format& format) const { - DCHECK_EQ(capture_format().pixel_format, PIXEL_FORMAT_I420); - const size_t y_stride = format.fmt.pix_mp.plane_fmt[0].bytesperline; - const size_t u_stride = format.fmt.pix_mp.plane_fmt[1].bytesperline; - const size_t v_stride = format.fmt.pix_mp.plane_fmt[2].bytesperline; - DCHECK_GE(y_stride, 1u * capture_format().frame_size.width()); - DCHECK_GE(u_stride, 1u * capture_format().frame_size.width() / 2); - DCHECK_GE(v_stride, 1u * capture_format().frame_size.width() / 2); - client()->OnIncomingCapturedYuvData( - buffer_tracker->GetPlaneStart(0), buffer_tracker->GetPlaneStart(1), - buffer_tracker->GetPlaneStart(2), y_stride, u_stride, v_stride, - capture_format(), rotation(), base::TimeTicks::Now()); -} - -bool V4L2CaptureDelegateMultiPlane::BufferTrackerMPlane::Init( - int fd, - const v4l2_buffer& buffer) { - for (size_t p = 0; p < buffer.length; ++p) { - // Some devices require mmap() to be called with both READ and WRITE. - // See http://crbug.com/178582. - void* const start = - mmap(NULL, buffer.m.planes[p].length, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, buffer.m.planes[p].m.mem_offset); - if (start == MAP_FAILED) { - DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace"; - return false; - } - AddMmapedPlane(static_cast<uint8_t*>(start), buffer.m.planes[p].length); - DVLOG(3) << "Mmap()ed plane #" << p << " of " << buffer.m.planes[p].length - << "B"; - } - return true; -} - -} // namespace media
diff --git a/media/capture/video/linux/v4l2_capture_delegate_multi_plane.h b/media/capture/video/linux/v4l2_capture_delegate_multi_plane.h deleted file mode 100644 index 265f6b5a..0000000 --- a/media/capture/video/linux/v4l2_capture_delegate_multi_plane.h +++ /dev/null
@@ -1,64 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_VIDEO_CAPTURE_LINUX_V4L2_CAPTURE_DELEGATE_MULTI_PLANE_H_ -#define MEDIA_VIDEO_CAPTURE_LINUX_V4L2_CAPTURE_DELEGATE_MULTI_PLANE_H_ - -#include <stdint.h> - -#include "base/memory/ref_counted.h" -#include "build/build_config.h" -#include "media/capture/video/linux/v4l2_capture_delegate.h" - -#if defined(OS_OPENBSD) -#error "OpenBSD does not support MPlane capture API." -#endif - -namespace base { -class SingleThreadTaskRunner; -} // namespace base - -namespace media { - -// V4L2 specifics for MPLANE API. -class V4L2CaptureDelegateMultiPlane final : public V4L2CaptureDelegate { - public: - V4L2CaptureDelegateMultiPlane( - const VideoCaptureDevice::Name& device_name, - const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, - int power_line_frequency); - - private: - // BufferTracker derivation to implement construction semantics for MPLANE. - class BufferTrackerMPlane final : public BufferTracker { - public: - bool Init(int fd, const v4l2_buffer& buffer) override; - - private: - ~BufferTrackerMPlane() override {} - }; - - ~V4L2CaptureDelegateMultiPlane() override; - - // V4L2CaptureDelegate virtual methods implementation. - scoped_refptr<BufferTracker> CreateBufferTracker() const override; - bool FillV4L2Format(v4l2_format* format, - uint32_t width, - uint32_t height, - uint32_t pixelformat_fourcc) const override; - void FinishFillingV4L2Buffer(v4l2_buffer* buffer) const override; - void SetPayloadSize(const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_buffer& buffer) const override; - void SendBuffer(const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_format& format) const override; - - // Vector to allocate and track as many v4l2_plane structs as planes, needed - // for v4l2_buffer.m.planes. This is a scratchpad marked mutable to enable - // using it in otherwise const methods. - mutable std::vector<struct v4l2_plane> v4l2_planes_; -}; - -} // namespace media - -#endif // MEDIA_VIDEO_CAPTURE_LINUX_V4L2_CAPTURE_DELEGATE_SINGLE_PLANE_H_
diff --git a/media/capture/video/linux/v4l2_capture_delegate_single_plane.cc b/media/capture/video/linux/v4l2_capture_delegate_single_plane.cc deleted file mode 100644 index 722eedcd..0000000 --- a/media/capture/video/linux/v4l2_capture_delegate_single_plane.cc +++ /dev/null
@@ -1,61 +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. - -#include "media/capture/video/linux/v4l2_capture_delegate_single_plane.h" - -#include <sys/mman.h> - -namespace media { - -scoped_refptr<V4L2CaptureDelegate::BufferTracker> -V4L2CaptureDelegateSinglePlane::CreateBufferTracker() const { - return make_scoped_refptr(new BufferTrackerSPlane()); -} - -bool V4L2CaptureDelegateSinglePlane::FillV4L2Format( - v4l2_format* format, - uint32_t width, - uint32_t height, - uint32_t pixelformat_fourcc) const { - format->fmt.pix.width = width; - format->fmt.pix.height = height; - format->fmt.pix.pixelformat = pixelformat_fourcc; - return true; -} - -void V4L2CaptureDelegateSinglePlane::FinishFillingV4L2Buffer( - v4l2_buffer* buffer) const { - buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -} - -void V4L2CaptureDelegateSinglePlane::SetPayloadSize( - const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_buffer& buffer) const { - buffer_tracker->SetPlanePayloadSize(0, buffer.bytesused); -} - -void V4L2CaptureDelegateSinglePlane::SendBuffer( - const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_format& format) const { - client()->OnIncomingCapturedData( - buffer_tracker->GetPlaneStart(0), buffer_tracker->GetPlanePayloadSize(0), - capture_format(), rotation(), base::TimeTicks::Now()); -} - -bool V4L2CaptureDelegateSinglePlane::BufferTrackerSPlane::Init( - int fd, - const v4l2_buffer& buffer) { - // Some devices require mmap() to be called with both READ and WRITE. - // See http://crbug.com/178582. - void* const start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, buffer.m.offset); - if (start == MAP_FAILED) { - DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace"; - return false; - } - AddMmapedPlane(static_cast<uint8_t*>(start), buffer.length); - return true; -} - -} // namespace media
diff --git a/media/capture/video/linux/v4l2_capture_delegate_single_plane.h b/media/capture/video/linux/v4l2_capture_delegate_single_plane.h deleted file mode 100644 index 9ddb9b6..0000000 --- a/media/capture/video/linux/v4l2_capture_delegate_single_plane.h +++ /dev/null
@@ -1,58 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_VIDEO_CAPTURE_LINUX_V4L2_CAPTURE_DELEGATE_SINGLE_PLANE_H_ -#define MEDIA_VIDEO_CAPTURE_LINUX_V4L2_CAPTURE_DELEGATE_SINGLE_PLANE_H_ - -#include <stdint.h> - -#include "base/memory/ref_counted.h" -#include "media/capture/video/linux/v4l2_capture_delegate.h" -#include "media/capture/video/video_capture_device.h" - -namespace base { -class SingleThreadTaskRunner; -} // namespace base - -namespace media { - -// V4L2 specifics for SPLANE API. -class V4L2CaptureDelegateSinglePlane final : public V4L2CaptureDelegate { - public: - V4L2CaptureDelegateSinglePlane( - const VideoCaptureDevice::Name& device_name, - const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner, - int power_line_frequency) - : V4L2CaptureDelegate(device_name, - v4l2_task_runner, - power_line_frequency) {} - - private: - // BufferTracker derivation to implement construction semantics for SPLANE. - class BufferTrackerSPlane final : public BufferTracker { - public: - bool Init(int fd, const v4l2_buffer& buffer) override; - - private: - ~BufferTrackerSPlane() override {} - }; - - ~V4L2CaptureDelegateSinglePlane() override {} - - // V4L2CaptureDelegate virtual methods implementation. - scoped_refptr<BufferTracker> CreateBufferTracker() const override; - bool FillV4L2Format(v4l2_format* format, - uint32_t width, - uint32_t height, - uint32_t pixelformat_fourcc) const override; - void FinishFillingV4L2Buffer(v4l2_buffer* buffer) const override; - void SetPayloadSize(const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_buffer& buffer) const override; - void SendBuffer(const scoped_refptr<BufferTracker>& buffer_tracker, - const v4l2_format& format) const override; -}; - -} // namespace media - -#endif // MEDIA_VIDEO_CAPTURE_LINUX_V4L2_CAPTURE_DELEGATE_MULTI_PLANE_H_
diff --git a/media/capture/video/linux/video_capture_device_factory_linux.cc b/media/capture/video/linux/video_capture_device_factory_linux.cc index 901b071..000f548 100644 --- a/media/capture/video/linux/video_capture_device_factory_linux.cc +++ b/media/capture/video/linux/video_capture_device_factory_linux.cc
@@ -28,28 +28,21 @@ namespace media { static bool HasUsableFormats(int fd, uint32_t capabilities) { + if (!(capabilities & V4L2_CAP_VIDEO_CAPTURE)) + return false; + const std::list<uint32_t>& usable_fourccs = VideoCaptureDeviceLinux::GetListOfUsableFourCCs(false); - - static const struct { - int capability; - v4l2_buf_type buf_type; - } kCapabilityAndBufferTypes[] = { - {V4L2_CAP_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_CAPTURE}, - {V4L2_CAP_VIDEO_CAPTURE_MPLANE, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE}}; - - for (const auto& capability_and_buffer_type : kCapabilityAndBufferTypes) { - v4l2_fmtdesc fmtdesc = {}; - if (capabilities & capability_and_buffer_type.capability) { - fmtdesc.type = capability_and_buffer_type.buf_type; - for (; HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0; - ++fmtdesc.index) { - if (std::find(usable_fourccs.begin(), usable_fourccs.end(), - fmtdesc.pixelformat) != usable_fourccs.end()) - return true; - } + v4l2_fmtdesc fmtdesc = {}; + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + for (; HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0; + ++fmtdesc.index) { + if (std::find(usable_fourccs.begin(), usable_fourccs.end(), + fmtdesc.pixelformat) != usable_fourccs.end()) { + return true; } } + DLOG(ERROR) << "No usable formats found"; return false; } @@ -89,10 +82,9 @@ static void GetSupportedFormatsForV4L2BufferType( int fd, - v4l2_buf_type buf_type, media::VideoCaptureFormats* supported_formats) { v4l2_fmtdesc v4l2_format = {}; - v4l2_format.type = buf_type; + v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; for (; HANDLE_EINTR(ioctl(fd, VIDIOC_ENUM_FMT, &v4l2_format)) == 0; ++v4l2_format.index) { VideoCaptureFormat supported_format; @@ -182,16 +174,12 @@ // http://crbug.com/139356. v4l2_capability cap; if ((HANDLE_EINTR(ioctl(fd.get(), VIDIOC_QUERYCAP, &cap)) == 0) && - ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE || - cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) && - !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) && - !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) && + (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE && + !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) && HasUsableFormats(fd.get(), cap.capabilities)) { device_names->push_back(VideoCaptureDevice::Name( reinterpret_cast<char*>(cap.card), unique_id, - (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) - ? VideoCaptureDevice::Name::V4L2_MULTI_PLANE - : VideoCaptureDevice::Name::V4L2_SINGLE_PLANE)); + VideoCaptureDevice::Name::V4L2_SINGLE_PLANE)); } } } @@ -209,13 +197,7 @@ DCHECK_NE(device.capture_api_type(), VideoCaptureDevice::Name::API_TYPE_UNKNOWN); - const v4l2_buf_type buf_type = - (device.capture_api_type() == VideoCaptureDevice::Name::V4L2_MULTI_PLANE) - ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE - : V4L2_BUF_TYPE_VIDEO_CAPTURE; - GetSupportedFormatsForV4L2BufferType(fd.get(), buf_type, supported_formats); - - return; + GetSupportedFormatsForV4L2BufferType(fd.get(), supported_formats); } // static
diff --git a/media/capture/video/linux/video_capture_device_linux.cc b/media/capture/video/linux/video_capture_device_linux.cc index 32f30e04..f2a1d1d 100644 --- a/media/capture/video/linux/video_capture_device_linux.cc +++ b/media/capture/video/linux/video_capture_device_linux.cc
@@ -81,8 +81,7 @@ } VideoCaptureDeviceLinux::VideoCaptureDeviceLinux(const Name& device_name) - : v4l2_thread_("V4L2CaptureThread"), device_name_(device_name) { -} + : v4l2_thread_("V4L2CaptureThread"), device_name_(device_name) {} VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() { // Check if the thread is running. @@ -101,7 +100,7 @@ const int line_frequency = TranslatePowerLineFrequencyToV4L2(GetPowerLineFrequency(params)); - capture_impl_ = V4L2CaptureDelegate::CreateV4L2CaptureDelegate( + capture_impl_ = new V4L2CaptureDelegate( device_name_, v4l2_thread_.task_runner(), line_frequency); if (!capture_impl_) { client->OnError(FROM_HERE, "Failed to create VideoCaptureDelegate");
diff --git a/media/capture/video/video_capture_device.cc b/media/capture/video/video_capture_device.cc index d624775..f3fc565 100644 --- a/media/capture/video/video_capture_device.cc +++ b/media/capture/video/video_capture_device.cc
@@ -87,8 +87,6 @@ switch (capture_api_type()) { case V4L2_SINGLE_PLANE: return "V4L2 SPLANE"; - case V4L2_MULTI_PLANE: - return "V4L2 MPLANE"; default: NOTREACHED() << "Unknown Video Capture API type!"; return "Unknown API";
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h index 2caa669..7d8ac1f 100644 --- a/media/capture/video/video_capture_device.h +++ b/media/capture/video/video_capture_device.h
@@ -55,7 +55,6 @@ // Linux/CrOS targets Capture Api type: it can only be set on construction. enum CaptureApiType { V4L2_SINGLE_PLANE, - V4L2_MULTI_PLANE, API_TYPE_UNKNOWN }; #elif defined(OS_WIN) @@ -213,19 +212,6 @@ int clockwise_rotation, const base::TimeTicks& timestamp) = 0; - // Captured a 3 planar YUV frame. Planes are possibly disjoint. - // |frame_format| must indicate I420. - virtual void OnIncomingCapturedYuvData( - const uint8_t* y_data, - const uint8_t* u_data, - const uint8_t* v_data, - size_t y_stride, - size_t u_stride, - size_t v_stride, - const VideoCaptureFormat& frame_format, - int clockwise_rotation, - const base::TimeTicks& timestamp) = 0; - // Reserve an output buffer into which contents can be captured directly. // The returned Buffer will always be allocated with a memory size suitable // for holding a packed video frame with pixels of |format| format, of
diff --git a/media/capture/webm_muxer.cc b/media/capture/webm_muxer.cc deleted file mode 100644 index c017032..0000000 --- a/media/capture/webm_muxer.cc +++ /dev/null
@@ -1,289 +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. - -#include "media/capture/webm_muxer.h" - -#include "base/bind.h" -#include "media/audio/audio_parameters.h" -#include "media/base/limits.h" -#include "media/base/video_frame.h" -#include "media/filters/opus_constants.h" -#include "ui/gfx/geometry/size.h" - -namespace media { - -namespace { - -void WriteOpusHeader(const media::AudioParameters& params, uint8_t* header) { - // See https://wiki.xiph.org/OggOpus#ID_Header. - // Set magic signature. - std::string label = "OpusHead"; - memcpy(header + OPUS_EXTRADATA_LABEL_OFFSET, label.c_str(), label.size()); - // Set Opus version. - header[OPUS_EXTRADATA_VERSION_OFFSET] = 1; - // Set channel count. - header[OPUS_EXTRADATA_CHANNELS_OFFSET] = params.channels(); - // Set pre-skip - uint16_t skip = 0; - memcpy(header + OPUS_EXTRADATA_SKIP_SAMPLES_OFFSET, &skip, sizeof(uint16_t)); - // Set original input sample rate in Hz. - uint32_t sample_rate = params.sample_rate(); - memcpy(header + OPUS_EXTRADATA_SAMPLE_RATE_OFFSET, &sample_rate, - sizeof(uint32_t)); - // Set output gain in dB. - uint16_t gain = 0; - memcpy(header + OPUS_EXTRADATA_GAIN_OFFSET, &gain, 2); - - // Set channel mapping. - if (params.channels() > 2) { - // Also possible to have a multistream, not supported for now. - DCHECK_LE(params.channels(), OPUS_MAX_VORBIS_CHANNELS); - header[OPUS_EXTRADATA_CHANNEL_MAPPING_OFFSET] = 1; - // Assuming no coupled streams. This should actually be - // channels() - |coupled_streams|. - header[OPUS_EXTRADATA_NUM_STREAMS_OFFSET] = params.channels(); - header[OPUS_EXTRADATA_NUM_COUPLED_OFFSET] = 0; - // Set the actual stream map. - for (int i = 0; i < params.channels(); ++i) { - header[OPUS_EXTRADATA_STREAM_MAP_OFFSET + i] = - kOpusVorbisChannelMap[params.channels() - 1][i]; - } - } else { - header[OPUS_EXTRADATA_CHANNEL_MAPPING_OFFSET] = 0; - } -} - -static double GetFrameRate(const scoped_refptr<VideoFrame>& video_frame) { - const double kZeroFrameRate = 0.0; - const double kDefaultFrameRate = 30.0; - - double frame_rate = kDefaultFrameRate; - if (!video_frame->metadata()->GetDouble( - VideoFrameMetadata::FRAME_RATE, &frame_rate) || - frame_rate <= kZeroFrameRate || - frame_rate > media::limits::kMaxFramesPerSecond) { - frame_rate = kDefaultFrameRate; - } - return frame_rate; -} - -} // anonymous namespace - -WebmMuxer::WebmMuxer(VideoCodec codec, - bool has_video, - bool has_audio, - const WriteDataCB& write_data_callback) - : use_vp9_(codec == kCodecVP9), - video_track_index_(0), - audio_track_index_(0), - has_video_(has_video), - has_audio_(has_audio), - write_data_callback_(write_data_callback), - position_(0) { - DCHECK(has_video_ || has_audio_); - DCHECK(!write_data_callback_.is_null()); - DCHECK(codec == kCodecVP8 || codec == kCodecVP9) - << " Only Vp8 and VP9 are supported in WebmMuxer"; - - segment_.Init(this); - segment_.set_mode(mkvmuxer::Segment::kLive); - segment_.OutputCues(false); - - mkvmuxer::SegmentInfo* const info = segment_.GetSegmentInfo(); - info->set_writing_app("Chrome"); - info->set_muxing_app("Chrome"); - - // Creation is done on a different thread than main activities. - thread_checker_.DetachFromThread(); -} - -WebmMuxer::~WebmMuxer() { - // No need to segment_.Finalize() since is not Seekable(), i.e. a live - // stream, but is a good practice. - DCHECK(thread_checker_.CalledOnValidThread()); - segment_.Finalize(); -} - -void WebmMuxer::OnEncodedVideo(const scoped_refptr<VideoFrame>& video_frame, - scoped_ptr<std::string> encoded_data, - base::TimeTicks timestamp, - bool is_key_frame) { - DVLOG(1) << __FUNCTION__ << " - " << encoded_data->size() << "B"; - DCHECK(thread_checker_.CalledOnValidThread()); - - if (!video_track_index_) { - // |track_index_|, cannot be zero (!), initialize WebmMuxer in that case. - // http://www.matroska.org/technical/specs/index.html#Tracks - AddVideoTrack(video_frame->visible_rect().size(), - GetFrameRate(video_frame)); - if (first_frame_timestamp_.is_null()) - first_frame_timestamp_ = timestamp; - } - - // TODO(ajose): Support multiple tracks: http://crbug.com/528523 - if (has_audio_ && !audio_track_index_) { - DVLOG(1) << __FUNCTION__ << ": delaying until audio track ready."; - if (is_key_frame) // Upon Key frame reception, empty the encoded queue. - encoded_frames_queue_.clear(); - - encoded_frames_queue_.push_back(make_scoped_ptr(new EncodedVideoFrame( - std::move(encoded_data), timestamp, is_key_frame))); - return; - } - - // Dump all saved encoded video frames if any. - while (!encoded_frames_queue_.empty()) { - AddFrame(std::move(encoded_frames_queue_.front()->data), video_track_index_, - encoded_frames_queue_.front()->timestamp, - encoded_frames_queue_.front()->is_keyframe); - encoded_frames_queue_.pop_front(); - } - - AddFrame(std::move(encoded_data), video_track_index_, timestamp, - is_key_frame); -} - -void WebmMuxer::OnEncodedAudio(const media::AudioParameters& params, - scoped_ptr<std::string> encoded_data, - base::TimeTicks timestamp) { - DVLOG(1) << __FUNCTION__ << " - " << encoded_data->size() << "B"; - DCHECK(thread_checker_.CalledOnValidThread()); - - if (!audio_track_index_) { - AddAudioTrack(params); - if (first_frame_timestamp_.is_null()) - first_frame_timestamp_ = timestamp; - } - - // TODO(ajose): Don't drop audio data: http://crbug.com/547948 - // TODO(ajose): Support multiple tracks: http://crbug.com/528523 - if (has_video_ && !video_track_index_) { - DVLOG(1) << __FUNCTION__ << ": delaying until video track ready."; - return; - } - - // Dump all saved encoded video frames if any. - while (!encoded_frames_queue_.empty()) { - AddFrame(std::move(encoded_frames_queue_.front()->data), video_track_index_, - encoded_frames_queue_.front()->timestamp, - encoded_frames_queue_.front()->is_keyframe); - encoded_frames_queue_.pop_front(); - } - - AddFrame(std::move(encoded_data), audio_track_index_, timestamp, - true /* is_key_frame -- always true for audio */); -} - -void WebmMuxer::AddVideoTrack(const gfx::Size& frame_size, double frame_rate) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK_EQ(0u, video_track_index_) - << "WebmMuxer can only be initialized once."; - - video_track_index_ = - segment_.AddVideoTrack(frame_size.width(), frame_size.height(), 0); - DCHECK_GT(video_track_index_, 0u); - - mkvmuxer::VideoTrack* const video_track = - reinterpret_cast<mkvmuxer::VideoTrack*>( - segment_.GetTrackByNumber(video_track_index_)); - DCHECK(video_track); - video_track->set_codec_id(use_vp9_ ? mkvmuxer::Tracks::kVp9CodecId - : mkvmuxer::Tracks::kVp8CodecId); - DCHECK_EQ(0ull, video_track->crop_right()); - DCHECK_EQ(0ull, video_track->crop_left()); - DCHECK_EQ(0ull, video_track->crop_top()); - DCHECK_EQ(0ull, video_track->crop_bottom()); - - video_track->set_frame_rate(frame_rate); - video_track->set_default_duration(base::Time::kNanosecondsPerSecond / - frame_rate); - // Segment's timestamps should be in milliseconds, DCHECK it. See - // http://www.webmproject.org/docs/container/#muxer-guidelines - DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale()); -} - -void WebmMuxer::AddAudioTrack(const media::AudioParameters& params) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK_EQ(0u, audio_track_index_) - << "WebmMuxer audio can only be initialised once."; - - audio_track_index_ = - segment_.AddAudioTrack(params.sample_rate(), params.channels(), 0); - DCHECK_GT(audio_track_index_, 0u); - - mkvmuxer::AudioTrack* const audio_track = - reinterpret_cast<mkvmuxer::AudioTrack*>( - segment_.GetTrackByNumber(audio_track_index_)); - DCHECK(audio_track); - audio_track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId); - - DCHECK_EQ(params.sample_rate(), audio_track->sample_rate()); - DCHECK_EQ(params.channels(), static_cast<int>(audio_track->channels())); - - uint8_t opus_header[OPUS_EXTRADATA_SIZE]; - WriteOpusHeader(params, opus_header); - - if (!audio_track->SetCodecPrivate(opus_header, OPUS_EXTRADATA_SIZE)) - LOG(ERROR) << __FUNCTION__ << ": failed to set opus header."; - - // Segment's timestamps should be in milliseconds, DCHECK it. See - // http://www.webmproject.org/docs/container/#muxer-guidelines - DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale()); -} - -mkvmuxer::int32 WebmMuxer::Write(const void* buf, mkvmuxer::uint32 len) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(buf); - write_data_callback_.Run( - base::StringPiece(reinterpret_cast<const char*>(buf), len)); - position_ += len; - return 0; -} - -mkvmuxer::int64 WebmMuxer::Position() const { - return position_.ValueOrDie(); -} - -mkvmuxer::int32 WebmMuxer::Position(mkvmuxer::int64 position) { - // The stream is not Seekable() so indicate we cannot set the position. - return -1; -} - -bool WebmMuxer::Seekable() const { - return false; -} - -void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id, - mkvmuxer::int64 position) { - // This method gets pinged before items are sent to |write_data_callback_|. - DCHECK_GE(position, position_.ValueOrDefault(0)) - << "Can't go back in a live WebM stream."; -} - -void WebmMuxer::AddFrame(scoped_ptr<std::string> encoded_data, - uint8_t track_index, - base::TimeTicks timestamp, - bool is_key_frame) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!has_video_ || video_track_index_); - DCHECK(!has_audio_ || audio_track_index_); - - most_recent_timestamp_ = - std::max(most_recent_timestamp_, timestamp - first_frame_timestamp_); - - segment_.AddFrame(reinterpret_cast<const uint8_t*>(encoded_data->data()), - encoded_data->size(), track_index, - most_recent_timestamp_.InMicroseconds() * - base::Time::kNanosecondsPerMicrosecond, - is_key_frame); -} - -WebmMuxer::EncodedVideoFrame::EncodedVideoFrame(scoped_ptr<std::string> data, - base::TimeTicks timestamp, - bool is_keyframe) - : data(std::move(data)), timestamp(timestamp), is_keyframe(is_keyframe) {} - -WebmMuxer::EncodedVideoFrame::~EncodedVideoFrame() {} - -} // namespace media
diff --git a/media/capture/webm_muxer.h b/media/capture/webm_muxer.h deleted file mode 100644 index 1f5e3d33..0000000 --- a/media/capture/webm_muxer.h +++ /dev/null
@@ -1,142 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_FILTERS_LIBWEBM_MUXER_H_ -#define MEDIA_FILTERS_LIBWEBM_MUXER_H_ - -#include <stdint.h> - -#include <deque> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/numerics/safe_math.h" -#include "base/strings/string_piece.h" -#include "base/threading/thread_checker.h" -#include "base/time/time.h" -#include "media/base/media_export.h" -#include "media/base/video_codecs.h" -#include "third_party/libwebm/source/mkvmuxer.hpp" - -namespace gfx { -class Size; -} // namespace gfx - -namespace media { - -class VideoFrame; -class AudioParameters; - -// Adapter class to manage a WebM container [1], a simplified version of a -// Matroska container [2], composed of an EBML header, and a single Segment -// including at least a Track Section and a number of SimpleBlocks each -// containing a single encoded video or audio frame. WebM container has no -// Trailer. -// Clients will push encoded VPx video frames and Opus audio frames one by one -// via OnEncoded{Video|Audio}(). libwebm will eventually ping the WriteDataCB -// passed on contructor with the wrapped encoded data. -// WebmMuxer is designed for use on a single thread. -// [1] http://www.webmproject.org/docs/container/ -// [2] http://www.matroska.org/technical/specs/index.html -class MEDIA_EXPORT WebmMuxer : public NON_EXPORTED_BASE(mkvmuxer::IMkvWriter) { - public: - // Callback to be called when WebmMuxer is ready to write a chunk of data, - // either any file header or a SingleBlock. - using WriteDataCB = base::Callback<void(base::StringPiece)>; - - // |codec| can be VP8 or VP9 and should coincide with whatever is sent in - // OnEncodedVideo(). - WebmMuxer(VideoCodec codec, - bool has_video_, - bool has_audio_, - const WriteDataCB& write_data_callback); - ~WebmMuxer() override; - - // Functions to add video and audio frames with |encoded_data.data()| - // to WebM Segment. - void OnEncodedVideo(const scoped_refptr<VideoFrame>& video_frame, - scoped_ptr<std::string> encoded_data, - base::TimeTicks timestamp, - bool is_key_frame); - void OnEncodedAudio(const media::AudioParameters& params, - scoped_ptr<std::string> encoded_data, - base::TimeTicks timestamp); - - private: - friend class WebmMuxerTest; - - // Methods for creating and adding video and audio tracks, called upon - // receiving the first frame of a given Track. - // AddVideoTrack adds |frame_size| and |frame_rate| to the Segment - // info, although individual frames passed to OnEncodedVideo() can have any - // frame size. - void AddVideoTrack(const gfx::Size& frame_size, double frame_rate); - void AddAudioTrack(const media::AudioParameters& params); - - // IMkvWriter interface. - mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) override; - mkvmuxer::int64 Position() const override; - mkvmuxer::int32 Position(mkvmuxer::int64 position) override; - bool Seekable() const override; - void ElementStartNotify(mkvmuxer::uint64 element_id, - mkvmuxer::int64 position) override; - - // Helper to simplify saving frames. - void AddFrame(scoped_ptr<std::string> encoded_data, - uint8_t track_index, - base::TimeTicks timestamp, - bool is_key_frame); - - // Used to DCHECK that we are called on the correct thread. - base::ThreadChecker thread_checker_; - - // Video Codec configured: VP9 if true, otherwise VP8 is used by default. - const bool use_vp9_; - - // Caller-side identifiers to interact with |segment_|, initialised upon - // first frame arrival to Add{Video, Audio}Track(). - uint8_t video_track_index_; - uint8_t audio_track_index_; - - // Origin of times for frame timestamps. - base::TimeTicks first_frame_timestamp_; - base::TimeDelta most_recent_timestamp_; - - // TODO(ajose): Change these when support is added for multiple tracks. - // http://crbug.com/528523 - const bool has_video_; - const bool has_audio_; - - // Callback to dump written data as being called by libwebm. - const WriteDataCB write_data_callback_; - - // Rolling counter of the position in bytes of the written goo. - base::CheckedNumeric<mkvmuxer::int64> position_; - - // The MkvMuxer active element. - mkvmuxer::Segment segment_; - - // Hold on to all encoded video frames to dump them with and when audio is - // received, if expected, since WebM headers can only be written once. - struct EncodedVideoFrame { - EncodedVideoFrame(scoped_ptr<std::string> data, - base::TimeTicks timestamp, - bool is_keyframe); - ~EncodedVideoFrame(); - - scoped_ptr<std::string> data; - base::TimeTicks timestamp; - bool is_keyframe; - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedVideoFrame); - }; - std::deque<scoped_ptr<EncodedVideoFrame>> encoded_frames_queue_; - - DISALLOW_COPY_AND_ASSIGN(WebmMuxer); -}; - -} // namespace media - -#endif // MEDIA_FILTERS_LIBWEBM_MUXER_H_
diff --git a/media/capture/webm_muxer_unittest.cc b/media/capture/webm_muxer_unittest.cc deleted file mode 100644 index 19b1e70..0000000 --- a/media/capture/webm_muxer_unittest.cc +++ /dev/null
@@ -1,252 +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. - -#include <stddef.h> -#include <stdint.h> - -#include "base/bind.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "media/audio/audio_parameters.h" -#include "media/base/channel_layout.h" -#include "media/base/video_frame.h" -#include "media/capture/webm_muxer.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::AllOf; -using ::testing::AnyNumber; -using ::testing::AtLeast; -using ::testing::Eq; -using ::testing::InSequence; -using ::testing::Mock; -using ::testing::Not; -using ::testing::Sequence; -using ::testing::TestWithParam; -using ::testing::ValuesIn; -using ::testing::WithArgs; - -namespace media { - -struct kTestParams { - VideoCodec codec; - size_t num_video_tracks; - size_t num_audio_tracks; -}; - -class WebmMuxerTest : public TestWithParam<kTestParams> { - public: - WebmMuxerTest() - : webm_muxer_(GetParam().codec, - GetParam().num_video_tracks, - GetParam().num_audio_tracks, - base::Bind(&WebmMuxerTest::WriteCallback, - base::Unretained(this))), - last_encoded_length_(0), - accumulated_position_(0) { - EXPECT_EQ(webm_muxer_.Position(), 0); - const mkvmuxer::int64 kRandomNewPosition = 333; - EXPECT_EQ(webm_muxer_.Position(kRandomNewPosition), -1); - EXPECT_FALSE(webm_muxer_.Seekable()); - } - - MOCK_METHOD1(WriteCallback, void(base::StringPiece)); - - void SaveEncodedDataLen(const base::StringPiece& encoded_data) { - last_encoded_length_ = encoded_data.size(); - accumulated_position_ += encoded_data.size(); - } - - mkvmuxer::int64 GetWebmMuxerPosition() const { - return webm_muxer_.Position(); - } - - mkvmuxer::Segment::Mode GetWebmSegmentMode() const { - return webm_muxer_.segment_.mode(); - } - - mkvmuxer::int32 WebmMuxerWrite(const void* buf, mkvmuxer::uint32 len) { - return webm_muxer_.Write(buf, len); - } - - WebmMuxer webm_muxer_; - - size_t last_encoded_length_; - int64_t accumulated_position_; - - private: - DISALLOW_COPY_AND_ASSIGN(WebmMuxerTest); -}; - -// Checks that the WriteCallback is called with appropriate params when -// WebmMuxer::Write() method is called. -TEST_P(WebmMuxerTest, Write) { - const base::StringPiece encoded_data("abcdefghijklmnopqrstuvwxyz"); - - EXPECT_CALL(*this, WriteCallback(encoded_data)); - WebmMuxerWrite(encoded_data.data(), encoded_data.size()); - - EXPECT_EQ(GetWebmMuxerPosition(), static_cast<int64_t>(encoded_data.size())); -} - -// This test sends two frames and checks that the WriteCallback is called with -// appropriate params in both cases. -TEST_P(WebmMuxerTest, OnEncodedVideoTwoFrames) { - if (GetParam().num_audio_tracks > 0) - return; - - const gfx::Size frame_size(160, 80); - const scoped_refptr<VideoFrame> video_frame = - VideoFrame::CreateBlackFrame(frame_size); - const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); - - EXPECT_CALL(*this, WriteCallback(_)) - .Times(AtLeast(1)) - .WillRepeatedly( - WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); - webm_muxer_.OnEncodedVideo(video_frame, - make_scoped_ptr(new std::string(encoded_data)), - base::TimeTicks::Now(), - false /* keyframe */); - - // First time around WriteCallback() is pinged a number of times to write the - // Matroska header, but at the end it dumps |encoded_data|. - EXPECT_EQ(last_encoded_length_, encoded_data.size()); - EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); - EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); - EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); - - const int64_t begin_of_second_block = accumulated_position_; - EXPECT_CALL(*this, WriteCallback(_)) - .Times(AtLeast(1)) - .WillRepeatedly( - WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); - webm_muxer_.OnEncodedVideo(video_frame, - make_scoped_ptr(new std::string(encoded_data)), - base::TimeTicks::Now(), - false /* keyframe */); - - // The second time around the callbacks should include a SimpleBlock header, - // namely the track index, a timestamp and a flags byte, for a total of 6B. - EXPECT_EQ(last_encoded_length_, encoded_data.size()); - EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); - const uint32_t kSimpleBlockSize = 6u; - EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + - encoded_data.size()), - accumulated_position_); -} - -TEST_P(WebmMuxerTest, OnEncodedAudioTwoFrames) { - if (GetParam().num_video_tracks > 0) - return; - - const int sample_rate = 48000; - const int bits_per_sample = 16; - const int frames_per_buffer = 480; - media::AudioParameters audio_params( - media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, - media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, - frames_per_buffer); - - const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); - - EXPECT_CALL(*this, WriteCallback(_)) - .Times(AtLeast(1)) - .WillRepeatedly( - WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); - webm_muxer_.OnEncodedAudio(audio_params, - make_scoped_ptr(new std::string(encoded_data)), - base::TimeTicks::Now()); - - // First time around WriteCallback() is pinged a number of times to write the - // Matroska header, but at the end it dumps |encoded_data|. - EXPECT_EQ(last_encoded_length_, encoded_data.size()); - EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); - EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); - EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); - - const int64_t begin_of_second_block = accumulated_position_; - EXPECT_CALL(*this, WriteCallback(_)) - .Times(AtLeast(1)) - .WillRepeatedly( - WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); - webm_muxer_.OnEncodedAudio(audio_params, - make_scoped_ptr(new std::string(encoded_data)), - base::TimeTicks::Now()); - - // The second time around the callbacks should include a SimpleBlock header, - // namely the track index, a timestamp and a flags byte, for a total of 6B. - EXPECT_EQ(last_encoded_length_, encoded_data.size()); - EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); - const uint32_t kSimpleBlockSize = 6u; - EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + - encoded_data.size()), - accumulated_position_); -} - -// This test verifies that when video data comes before audio data, we save the -// encoded video frames and add it to the video track when audio data arrives. -TEST_P(WebmMuxerTest, VideoIsStoredWhileWaitingForAudio) { - // This test is only relevant if we have both kinds of tracks. - if (GetParam().num_video_tracks == 0 || GetParam().num_audio_tracks == 0) - return; - - // First send a video keyframe - const gfx::Size frame_size(160, 80); - const scoped_refptr<VideoFrame> video_frame = - VideoFrame::CreateBlackFrame(frame_size); - const std::string encoded_video("thisisanencodedvideopacket"); - webm_muxer_.OnEncodedVideo(video_frame, - make_scoped_ptr(new std::string(encoded_video)), - base::TimeTicks::Now(), true /* keyframe */); - // A few encoded non key frames. - const int kNumNonKeyFrames = 2; - for (int i = 0; i < kNumNonKeyFrames; ++i) { - webm_muxer_.OnEncodedVideo(video_frame, - make_scoped_ptr(new std::string(encoded_video)), - base::TimeTicks::Now(), false /* keyframe */); - } - - // Send some audio. The header will be written and muxing will proceed - // normally. - const int sample_rate = 48000; - const int bits_per_sample = 16; - const int frames_per_buffer = 480; - media::AudioParameters audio_params( - media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, - media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, - frames_per_buffer); - const std::string encoded_audio("thisisanencodedaudiopacket"); - - // We should first get the encoded video frames, then the encoded audio frame. - Sequence s; - EXPECT_CALL(*this, WriteCallback(Eq(encoded_video))) - .Times(1 + kNumNonKeyFrames) - .InSequence(s); - EXPECT_CALL(*this, WriteCallback(Eq(encoded_audio))).Times(1).InSequence(s); - // We'll also get lots of other header-related stuff. - EXPECT_CALL(*this, WriteCallback( - AllOf(Not(Eq(encoded_video)), Not(Eq(encoded_audio))))) - .Times(AnyNumber()); - webm_muxer_.OnEncodedAudio(audio_params, - make_scoped_ptr(new std::string(encoded_audio)), - base::TimeTicks::Now()); -} - -const kTestParams kTestCases[] = { - // TODO: consider not enumerating every combination by hand. - {kCodecVP8, 1 /* num_video_tracks */, 0 /*num_audio_tracks*/}, - {kCodecVP8, 0, 1}, - {kCodecVP8, 1, 1}, - {kCodecVP9, 1, 0}, - {kCodecVP9, 0, 1}, - {kCodecVP9, 1, 1}, -}; - -INSTANTIATE_TEST_CASE_P(, WebmMuxerTest, ValuesIn(kTestCases)); - -} // namespace media
diff --git a/media/filters/android/media_codec_audio_decoder.cc b/media/filters/android/media_codec_audio_decoder.cc index c231635..f23452a4 100644 --- a/media/filters/android/media_codec_audio_decoder.cc +++ b/media/filters/android/media_codec_audio_decoder.cc
@@ -176,6 +176,12 @@ task_runner_->PostTask(FROM_HERE, closure); } +bool MediaCodecAudioDecoder::NeedsBitstreamConversion() const { + // An AAC stream needs to be converted as ADTS stream. + DCHECK_NE(config_.codec(), kUnknownAudioCodec); + return config_.codec() == kCodecAAC; +} + void MediaCodecAudioDecoder::OnKeyAdded() { DVLOG(1) << __FUNCTION__;
diff --git a/media/filters/android/media_codec_audio_decoder.h b/media/filters/android/media_codec_audio_decoder.h index 8e6974c8e..39aca8c 100644 --- a/media/filters/android/media_codec_audio_decoder.h +++ b/media/filters/android/media_codec_audio_decoder.h
@@ -112,6 +112,7 @@ void Decode(const scoped_refptr<DecoderBuffer>& buffer, const DecodeCB& decode_cb) override; void Reset(const base::Closure& closure) override; + bool NeedsBitstreamConversion() const override; private: // Possible states.
diff --git a/media/filters/audio_clock.cc b/media/filters/audio_clock.cc index 19c5c2c..0bd0a6c 100644 --- a/media/filters/audio_clock.cc +++ b/media/filters/audio_clock.cc
@@ -8,6 +8,7 @@ #include <stddef.h> #include <algorithm> +#include <cmath> #include "base/logging.h" @@ -19,9 +20,8 @@ static_cast<double>(base::Time::kMicrosecondsPerSecond) / sample_rate), total_buffered_frames_(0), - front_timestamp_(start_timestamp), - back_timestamp_(start_timestamp) { -} + front_timestamp_micros_(start_timestamp.InMicroseconds()), + back_timestamp_micros_(start_timestamp.InMicroseconds()) {} AudioClock::~AudioClock() { } @@ -36,8 +36,10 @@ DCHECK_GE(playback_rate, 0); // First write: initialize buffer with silence. - if (start_timestamp_ == front_timestamp_ && buffered_.empty()) + if (start_timestamp_.InMicroseconds() == front_timestamp_micros_ && + buffered_.empty()) { PushBufferedAudioData(delay_frames, 0.0); + } // Move frames from |buffered_| into the computed timestamp based on // |delay_frames|. @@ -53,14 +55,16 @@ // Update our front and back timestamps. The back timestamp is considered the // authoritative source of truth, so base the front timestamp on range of data // buffered. Doing so avoids accumulation errors on the front timestamp. - back_timestamp_ += base::TimeDelta::FromMicroseconds( - frames_written * playback_rate * microseconds_per_frame_); + back_timestamp_micros_ += + frames_written * playback_rate * microseconds_per_frame_; + // Don't let front timestamp move earlier in time, as could occur due to delay // frames pushed in the first write, above. - front_timestamp_ = std::max(front_timestamp_, - back_timestamp_ - ComputeBufferedMediaDuration()); - DCHECK_GE(front_timestamp_, start_timestamp_); - DCHECK_LE(front_timestamp_, back_timestamp_); + front_timestamp_micros_ = + std::max(front_timestamp_micros_, + back_timestamp_micros_ - ComputeBufferedMediaDurationMicros()); + DCHECK_GE(front_timestamp_micros_, start_timestamp_.InMicroseconds()); + DCHECK_LE(front_timestamp_micros_, back_timestamp_micros_); } void AudioClock::CompensateForSuspendedWrites(base::TimeDelta elapsed, @@ -81,12 +85,15 @@ } base::TimeDelta AudioClock::TimeUntilPlayback(base::TimeDelta timestamp) const { - DCHECK_GE(timestamp, front_timestamp_); - DCHECK_LE(timestamp, back_timestamp_); + // Use front/back_timestamp() methods rather than internal members. The public + // methods round to the nearest microsecond for conversion to TimeDelta and + // the rounded value will likely be used by the caller. + DCHECK_GE(timestamp, front_timestamp()); + DCHECK_LE(timestamp, back_timestamp()); int64_t frames_until_timestamp = 0; double timestamp_us = timestamp.InMicroseconds(); - double media_time_us = front_timestamp_.InMicroseconds(); + double media_time_us = front_timestamp().InMicroseconds(); for (size_t i = 0; i < buffered_.size(); ++i) { // Leading silence is always accounted prior to anything else. @@ -113,8 +120,8 @@ frames_until_timestamp += buffered_[i].frames; } - return base::TimeDelta::FromMicroseconds(frames_until_timestamp * - microseconds_per_frame_); + return base::TimeDelta::FromMicroseconds( + std::round(frames_until_timestamp * microseconds_per_frame_)); } void AudioClock::ContiguousAudioDataBufferedForTesting( @@ -179,12 +186,11 @@ } } -base::TimeDelta AudioClock::ComputeBufferedMediaDuration() const { +double AudioClock::ComputeBufferedMediaDurationMicros() const { double scaled_frames = 0; for (const auto& buffer : buffered_) scaled_frames += buffer.frames * buffer.playback_rate; - return base::TimeDelta::FromMicroseconds(scaled_frames * - microseconds_per_frame_); + return scaled_frames * microseconds_per_frame_; } } // namespace media
diff --git a/media/filters/audio_clock.h b/media/filters/audio_clock.h index 024c791..42ee2b6 100644 --- a/media/filters/audio_clock.h +++ b/media/filters/audio_clock.h
@@ -7,6 +7,7 @@ #include <stdint.h> +#include <cmath> #include <deque> #include "base/macros.h" @@ -89,8 +90,14 @@ // |start_timestamp| since no amount of media frames tracked // media data has been played yet. by AudioClock, which would be // 1000 + 500 + 250 = 1750 ms. - base::TimeDelta front_timestamp() const { return front_timestamp_; } - base::TimeDelta back_timestamp() const { return back_timestamp_; } + base::TimeDelta front_timestamp() const { + return base::TimeDelta::FromMicroseconds( + std::round(front_timestamp_micros_)); + } + base::TimeDelta back_timestamp() const { + return base::TimeDelta::FromMicroseconds( + std::round(back_timestamp_micros_)); + } // Returns the amount of wall time until |timestamp| will be played by the // audio hardware. @@ -117,7 +124,7 @@ // Helpers for operating on |buffered_|. void PushBufferedAudioData(int64_t frames, double playback_rate); void PopBufferedAudioData(int64_t frames); - base::TimeDelta ComputeBufferedMediaDuration() const; + double ComputeBufferedMediaDurationMicros() const; const base::TimeDelta start_timestamp_; const double microseconds_per_frame_; @@ -125,8 +132,14 @@ std::deque<AudioData> buffered_; int64_t total_buffered_frames_; - base::TimeDelta front_timestamp_; - base::TimeDelta back_timestamp_; + // Use double rather than TimeDelta to avoid loss of partial microseconds when + // converting between frames-written/delayed and time-passed (see conversion + // in WroteAudio()). Particularly for |back_timestamp|, which accumulates more + // time with each call to WroteAudio(), the loss of precision can accumulate + // to create noticeable audio/video sync drift for longer (2-3 hr) videos. + // See http://crbug.com/564604. + double front_timestamp_micros_; + double back_timestamp_micros_; DISALLOW_COPY_AND_ASSIGN(AudioClock); };
diff --git a/media/filters/audio_clock_unittest.cc b/media/filters/audio_clock_unittest.cc index 303e8e3..312ba61 100644 --- a/media/filters/audio_clock_unittest.cc +++ b/media/filters/audio_clock_unittest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "media/base/audio_timestamp_helper.h" #include "media/filters/audio_clock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -11,8 +12,7 @@ class AudioClockTest : public testing::Test { public: - AudioClockTest() - : sample_rate_(10), clock_(base::TimeDelta(), sample_rate_) {} + AudioClockTest() { SetupClock(base::TimeDelta(), 10); } ~AudioClockTest() override {} @@ -20,45 +20,51 @@ int frames_requested, int delay_frames, double playback_rate) { - clock_.WroteAudio( - frames_written, frames_requested, delay_frames, playback_rate); + clock_->WroteAudio(frames_written, frames_requested, delay_frames, + playback_rate); } - int FrontTimestampInDays() { return clock_.front_timestamp().InDays(); } + void SetupClock(base::TimeDelta start_time, int sample_rate) { + sample_rate_ = sample_rate; + clock_.reset(new AudioClock(start_time, sample_rate_)); + } + + int FrontTimestampInDays() { return clock_->front_timestamp().InDays(); } int FrontTimestampInMilliseconds() { - return clock_.front_timestamp().InMilliseconds(); + return clock_->front_timestamp().InMilliseconds(); } int BackTimestampInMilliseconds() { - return clock_.back_timestamp().InMilliseconds(); + return clock_->back_timestamp().InMilliseconds(); } int TimeUntilPlaybackInMilliseconds(int timestamp_ms) { - return clock_.TimeUntilPlayback(base::TimeDelta::FromMilliseconds( - timestamp_ms)).InMilliseconds(); + return clock_ + ->TimeUntilPlayback(base::TimeDelta::FromMilliseconds(timestamp_ms)) + .InMilliseconds(); } int ContiguousAudioDataBufferedInDays() { base::TimeDelta total, same_rate_total; - clock_.ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); + clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); return total.InDays(); } int ContiguousAudioDataBufferedInMilliseconds() { base::TimeDelta total, same_rate_total; - clock_.ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); + clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); return total.InMilliseconds(); } int ContiguousAudioDataBufferedAtSameRateInMilliseconds() { base::TimeDelta total, same_rate_total; - clock_.ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); + clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); return same_rate_total.InMilliseconds(); } - const int sample_rate_; - AudioClock clock_; + int sample_rate_; + scoped_ptr<AudioClock> clock_; private: DISALLOW_COPY_AND_ASSIGN(AudioClockTest); @@ -337,8 +343,8 @@ // Elapsing frames less than we have buffered should do nothing. const int kDelayFrames = 2; for (int i = 1000; i <= kBaseTimeMs; i += 1000) { - clock_.CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(i), - kDelayFrames); + clock_->CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(i), + kDelayFrames); EXPECT_EQ(kBaseTimeMs - (i - 1000), TimeUntilPlaybackInMilliseconds(0)); // Write silence to simulate maintaining a 7s output buffer. @@ -347,9 +353,26 @@ // Exhausting all frames should advance timestamps and prime the buffer with // our delay frames value. - clock_.CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(7000), - kDelayFrames); + clock_->CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(7000), + kDelayFrames); EXPECT_EQ(kDelayFrames * 100, TimeUntilPlaybackInMilliseconds(1000)); } +TEST_F(AudioClockTest, FramesToTimePrecision) { + SetupClock(base::TimeDelta(), 48000); + double micros_per_frame = base::Time::kMicrosecondsPerSecond / 48000.0; + int frames_written = 0; + + // Write ~2 hours of data to clock to give any error a significant chance to + // accumulate. + while (clock_->back_timestamp() <= base::TimeDelta::FromHours(2)) { + frames_written += 1024; + WroteAudio(1024, 1024, 0, 1); + } + + // Verify no error accumulated. + EXPECT_EQ(std::round(frames_written * micros_per_frame), + clock_->back_timestamp().InMicroseconds()); +} + } // namespace media
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 374d618c..1afbc6d 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc
@@ -295,7 +295,7 @@ lock_.AssertAcquired(); DCHECK(!read_cb_.is_null()); - DemuxerStream::Status status; + DemuxerStream::Status status = DemuxerStream::kAborted; scoped_refptr<StreamParserBuffer> buffer; switch (state_) {
diff --git a/media/filters/decoder_stream_traits.cc b/media/filters/decoder_stream_traits.cc index a5f6c84..131fc2d8 100644 --- a/media/filters/decoder_stream_traits.cc +++ b/media/filters/decoder_stream_traits.cc
@@ -29,6 +29,11 @@ output_cb); } +bool DecoderStreamTraits<DemuxerStream::AUDIO>::NeedsBitstreamConversion( + DecoderType* decoder) { + return decoder->NeedsBitstreamConversion(); +} + void DecoderStreamTraits<DemuxerStream::AUDIO>::ReportStatistics( const StatisticsCB& statistics_cb, int bytes_decoded) {
diff --git a/media/filters/decoder_stream_traits.h b/media/filters/decoder_stream_traits.h index e098906..7d53899 100644 --- a/media/filters/decoder_stream_traits.h +++ b/media/filters/decoder_stream_traits.h
@@ -37,7 +37,7 @@ CdmContext* cdm_context, const InitCB& init_cb, const OutputCB& output_cb); - static bool NeedsBitstreamConversion(DecoderType* decoder) { return false; } + static bool NeedsBitstreamConversion(DecoderType* decoder); static void ReportStatistics(const StatisticsCB& statistics_cb, int bytes_decoded); static scoped_refptr<OutputType> CreateEOSOutput();
diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc index 87b8454..92ccbf4 100644 --- a/media/filters/stream_parser_factory.cc +++ b/media/filters/stream_parser_factory.cc
@@ -14,6 +14,7 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "build/build_config.h" +#include "media/base/media.h" #include "media/base/media_switches.h" #include "media/formats/mpeg/adts_stream_parser.h" #include "media/formats/mpeg/mpeg1_audio_stream_parser.h" @@ -21,7 +22,7 @@ #include "media/media_features.h" #if defined(OS_ANDROID) -#include "base/android/build_info.h" +#include "media/base/android/media_codec_util.h" #endif #if defined(USE_PROPRIETARY_CODECS) @@ -330,14 +331,30 @@ return true; case CodecInfo::VIDEO: #if defined(OS_ANDROID) - // VP9 is only supported on KitKat+ (API Level 19). - if (codec_info->tag == CodecInfo::HISTOGRAM_VP9 && - base::android::BuildInfo::GetInstance()->sdk_int() < 19) { + // TODO(wolenetz, dalecurtis): This should instead use MimeUtil() to avoid + // duplication of subtle Android behavior. http://crbug.com/587303. + if (codec_info->tag == CodecInfo::HISTOGRAM_H264) { + if (media::IsUnifiedMediaPipelineEnabledForMse() && + !media::HasPlatformDecoderSupport()) { + return false; + } + + if (!MediaCodecUtil::IsMediaCodecAvailable()) + return false; + } + if (codec_info->tag == CodecInfo::HISTOGRAM_VP8 && + !media::MediaCodecUtil::IsVp8DecoderAvailable() && + !media::IsUnifiedMediaPipelineEnabledForMse()) { return false; } - // Opus is only supported on Lollipop+ (API Level 21). + if (codec_info->tag == CodecInfo::HISTOGRAM_VP9 && + !media::PlatformHasVp9Support() && + !media::IsUnifiedMediaPipelineEnabledForMse()) { + return false; + } if (codec_info->tag == CodecInfo::HISTOGRAM_OPUS && - base::android::BuildInfo::GetInstance()->sdk_int() < 21) { + !media::PlatformHasOpusSupport() && + !media::IsUnifiedMediaPipelineEnabledForMse()) { return false; } #endif
diff --git a/media/media.gyp b/media/media.gyp index 8cbf9c4..c968fcd 100644 --- a/media/media.gyp +++ b/media/media.gyp
@@ -451,10 +451,10 @@ 'capture/content/feedback_signal_accumulator.h', 'capture/content/screen_capture_device_core.cc', 'capture/content/screen_capture_device_core.h', - 'capture/content/thread_safe_capture_oracle.cc', - 'capture/content/thread_safe_capture_oracle.h', 'capture/content/smooth_event_sampler.cc', 'capture/content/smooth_event_sampler.h', + 'capture/content/thread_safe_capture_oracle.cc', + 'capture/content/thread_safe_capture_oracle.h', 'capture/content/video_capture_oracle.cc', 'capture/content/video_capture_oracle.h', 'capture/video/android/video_capture_device_android.cc', @@ -471,10 +471,6 @@ 'capture/video/file_video_capture_device_factory.h', 'capture/video/linux/v4l2_capture_delegate.cc', 'capture/video/linux/v4l2_capture_delegate.h', - 'capture/video/linux/v4l2_capture_delegate_multi_plane.cc', - 'capture/video/linux/v4l2_capture_delegate_multi_plane.h', - 'capture/video/linux/v4l2_capture_delegate_single_plane.cc', - 'capture/video/linux/v4l2_capture_delegate_single_plane.h', 'capture/video/linux/video_capture_device_chromeos.cc', 'capture/video/linux/video_capture_device_chromeos.h', 'capture/video/linux/video_capture_device_factory_linux.cc', @@ -515,8 +511,6 @@ 'capture/video/win/video_capture_device_mf_win.h', 'capture/video/win/video_capture_device_win.cc', 'capture/video/win/video_capture_device_win.h', - 'capture/webm_muxer.cc', - 'capture/webm_muxer.h', 'cdm/aes_decryptor.cc', 'cdm/aes_decryptor.h', 'cdm/cdm_adapter.cc', @@ -584,10 +578,10 @@ 'filters/h264_bit_reader.h', 'filters/h264_parser.cc', 'filters/h264_parser.h', - 'filters/ivf_parser.cc', - 'filters/ivf_parser.h', 'filters/in_memory_url_protocol.cc', 'filters/in_memory_url_protocol.h', + 'filters/ivf_parser.cc', + 'filters/ivf_parser.h', 'filters/jpeg_parser.cc', 'filters/jpeg_parser.h', 'filters/media_source_state.cc', @@ -644,6 +638,8 @@ 'formats/webm/webm_video_client.cc', 'formats/webm/webm_video_client.h', 'formats/webm/webm_webvtt_parser.cc', + 'muxers/webm_muxer.cc', + 'muxers/webm_muxer.h', 'ozone/media_ozone_platform.cc', 'ozone/media_ozone_platform.h', 'renderers/audio_renderer_impl.cc', @@ -749,6 +745,7 @@ }], ['OS=="android"', { 'dependencies': [ + 'capture_java', 'media_android_jni_headers', 'media_java', 'player_android', @@ -1296,7 +1293,6 @@ 'capture/content/video_capture_oracle_unittest.cc', 'capture/video/fake_video_capture_device_unittest.cc', 'capture/video/video_capture_device_unittest.cc', - 'capture/webm_muxer_unittest.cc', 'cdm/aes_decryptor_unittest.cc', 'cdm/external_clear_key_test_helper.cc', 'cdm/external_clear_key_test_helper.h', @@ -1351,14 +1347,15 @@ 'formats/webm/webm_parser_unittest.cc', 'formats/webm/webm_tracks_parser_unittest.cc', 'formats/webm/webm_webvtt_parser_unittest.cc', + 'muxers/webm_muxer_unittest.cc', 'renderers/audio_renderer_impl_unittest.cc', 'renderers/renderer_impl_unittest.cc', 'renderers/skcanvas_video_renderer_unittest.cc', 'renderers/video_renderer_impl_unittest.cc', 'test/pipeline_integration_test.cc', 'test/pipeline_integration_test_base.cc', - 'video/h264_poc_unittest.cc', 'video/gpu_memory_buffer_video_frame_pool_unittest.cc', + 'video/h264_poc_unittest.cc', ], 'include_dirs': [ # Needed by media_drm_bridge.cc. @@ -1824,6 +1821,7 @@ 'target_name': 'media_unittests_apk', 'type': 'none', 'dependencies': [ + 'capture_java', 'media_java', 'media_unittests', ], @@ -1838,6 +1836,7 @@ 'target_name': 'media_perftests_apk', 'type': 'none', 'dependencies': [ + 'capture_java', 'media_java', 'media_perftests', ], @@ -1848,7 +1847,7 @@ 'includes': ['../build/apk_test.gypi'], }, { - # GN: //media/base/android:media_android_jni_headers + # GN: //media/base/android:media_jni_headers 'target_name': 'media_android_jni_headers', 'type': 'none', 'sources': [ @@ -1866,12 +1865,12 @@ 'includes': ['../build/jni_generator.gypi'], }, { - # GN: //media/base/android:video_capture_android_jni_headers + # GN: //media/capture/video/android:capture_jni_headers 'target_name': 'video_capture_android_jni_headers', 'type': 'none', 'sources': [ - 'base/android/java/src/org/chromium/media/VideoCapture.java', - 'base/android/java/src/org/chromium/media/VideoCaptureFactory.java', + 'capture/video/android/java/src/org/chromium/media/VideoCapture.java', + 'capture/video/android/java/src/org/chromium/media/VideoCaptureFactory.java', ], 'variables': { 'jni_gen_package': 'media', @@ -1936,6 +1935,8 @@ 'base/android/video_decoder_job.h', 'base/android/video_media_codec_decoder.cc', 'base/android/video_media_codec_decoder.h', + 'capture/video/android/capture_jni_registrar.cc', + 'capture/video/android/capture_jni_registrar.h', ], 'conditions': [ # Only 64 bit builds are using android-21 NDK library, check common.gypi @@ -1964,8 +1965,8 @@ ], }, { - # GN: //media/base/android:media_java - 'target_name': 'media_java', + # GN: //media/capture/video/android:capture_java + 'target_name': 'capture_java', 'type': 'none', 'dependencies': [ '../base/base.gyp:base', @@ -1976,6 +1977,21 @@ '../base/base.gyp:base', ], 'variables': { + 'java_in_dir': 'capture/video/android/java', + }, + 'includes': ['../build/java.gypi'], + }, + { + # GN: //media/base/android:media_java + 'target_name': 'media_java', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base', + ], + 'export_dependent_settings': [ + '../base/base.gyp:base', + ], + 'variables': { 'java_in_dir': 'base/android/java', }, 'includes': ['../build/java.gypi'],
diff --git a/media/media_options.gni b/media/media_options.gni index 449825e..2e91d75 100644 --- a/media/media_options.gni +++ b/media/media_options.gni
@@ -44,18 +44,11 @@ # platform. Enable by default for Chromecast. enable_hevc_demuxing = proprietary_codecs && is_chromecast - # Experiment to enable mojo media application: http://crbug.com/431776 - # Valid options are: - # - "none": Do not use mojo media application. - # - "browser": Use mojo media application hosted in the browser process. - # - "gpu": Use mojo media application hosted in the gpu process. - # - "utility": Use mojo media application hosted in the utility process. - enable_mojo_media = "none" - # TODO(GYP): This should be a platform define. is_openbsd = false } +# Use a second declare_args() to pick up possible overrides of |use_cras|. declare_args() { # Enables runtime selection of PulseAudio library. use_pulseaudio = false @@ -73,3 +66,55 @@ } } } + +declare_args() { + # Experiment to enable mojo media services (e.g. "renderer", "cdm", see + # |mojo_media_services|). When enabled, selected mojo paths will be enabled in + # the media pipeline and corresponding services will hosted in the selected + # remote process (e.g. "utility" process, see |mojo_media_host|). + enable_mojo_media = false + + # Enable the TestMojoMediaClient to be used in MojoMediaApplication. This is + # for testing only and will override the default platform MojoMediaClient. + enable_test_mojo_media_client = false +} + +# Use a second declare_args() to pick up possible overrides of enable_mojo_media +# from --args command line flags. See "gn help declare_args". +declare_args() { + # A list of mojo media services that should be used in the media pipeline. + # Must not be empty if |enable_mojo_media| is true. + # Valid entries in the list are: + # - "renderer": Use mojo based media Renderer service. + # - "cdm": Use mojo based Content Decryption Module. + # TODO(xhwang): Support mojo base audio/video decoders here. + # See http://crbug.com/542910 and http://crbug.com/522298 + mojo_media_services = [] + + # The process to host the mojo media application. + # Valid options are: + # - "none": Do not use mojo media application. + # - "browser": Use mojo media application hosted in the browser process. + # - "gpu": Use mojo media application hosted in the gpu process. + # - "utility": Use mojo media application hosted in the utility process. + mojo_media_host = "none" + + # Default mojo_media_services and mojo_media_host on various platforms. + # Can be overridden by gn build arguments from the --args command line flag + # for local testing. + if (enable_mojo_media) { + if (is_android) { + mojo_media_services = [ "cdm" ] + mojo_media_host = "gpu" + } else if (is_chromecast) { + mojo_media_services = [ + "cdm", + "renderer", + ] + mojo_media_host = "browser" + } else { + mojo_media_services = [ "cdm" ] + mojo_media_host = "utility" + } + } +}
diff --git a/media/mojo/BUILD.gn b/media/mojo/BUILD.gn index fb983772..b41c748 100644 --- a/media/mojo/BUILD.gn +++ b/media/mojo/BUILD.gn
@@ -2,13 +2,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -group("mojo") { - public_deps = [ - "//media/mojo/interfaces", - "//media/mojo/services", - ] -} - group("tests") { testonly = true deps = [
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn index 09092dc4..7736ff37 100644 --- a/media/mojo/services/BUILD.gn +++ b/media/mojo/services/BUILD.gn
@@ -13,20 +13,33 @@ # - unittests: Unit tests for a particular class/file. # - test: Tests for a particular app, e.g. media. -config("enable_mojo_media_config") { - assert(enable_mojo_media == "none" || enable_mojo_media == "browser" || - enable_mojo_media == "gpu" || enable_mojo_media == "utility") - - if (enable_mojo_media != "none") { +config("mojo_media_config") { + if (!enable_mojo_media) { + assert(mojo_media_services == [], "Mojo media is not enabled") + assert(mojo_media_host == "none", "Mojo media is not enabled") + } else { defines = [ "ENABLE_MOJO_MEDIA" ] - } - if (enable_mojo_media == "browser") { - defines += [ "ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS" ] - } else if (enable_mojo_media == "gpu") { - defines += [ "ENABLE_MOJO_MEDIA_IN_GPU_PROCESS" ] - } else if (enable_mojo_media == "utility") { - defines += [ "ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS" ] + assert(mojo_media_services != [], "No mojo media service specified") + foreach(service, mojo_media_services) { + if (service == "renderer") { + defines += [ "ENABLE_MOJO_RENDERER" ] + } else if (service == "cdm") { + defines += [ "ENABLE_MOJO_CDM" ] + } else { + assert(false, "Invalid mojo media service: $service") + } + } + + if (mojo_media_host == "browser") { + defines += [ "ENABLE_MOJO_MEDIA_IN_BROWSER_PROCESS" ] + } else if (mojo_media_host == "gpu") { + defines += [ "ENABLE_MOJO_MEDIA_IN_GPU_PROCESS" ] + } else if (mojo_media_host == "utility") { + defines += [ "ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS" ] + } else { + assert(false, "Invalid mojo media host: $mojo_media_host") + } } } @@ -66,7 +79,7 @@ "mojo_type_trait.h", ] - public_configs = [ ":enable_mojo_media_config" ] + public_configs = [ ":mojo_media_config" ] deps = [ ":converters", @@ -115,7 +128,7 @@ # which is a bit hacky since we need to access CdmService directly # from C++ code (AVDA). In the future we'll make those decoders part of # MojoMediaApplication, then we won't need this. - public_configs = [ ":enable_mojo_media_config" ] + public_configs = [ ":mojo_media_config" ] configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] } @@ -150,7 +163,38 @@ "service_factory_impl.h", ] - if (is_android) { + public_configs = [ ":mojo_media_config" ] + + deps = [ + ":cdm_service", + ":renderer_service", + "//base", + "//media", + "//mojo/shell/public/cpp", + ] +} + +source_set("application_factory") { + sources = [ + "mojo_media_application_factory.cc", + "mojo_media_application_factory.h", + ] + + public_configs = [ ":mojo_media_config" ] + + deps = [ + ":application", + "//base", + "//media", + ] + + if (enable_test_mojo_media_client) { + defines = [ "ENABLE_TEST_MOJO_MEDIA_CLIENT" ] + sources += [ + "test_mojo_media_client.cc", + "test_mojo_media_client.h", + ] + } else if (is_android) { sources += [ "android_mojo_media_client.cc", "android_mojo_media_client.h", @@ -161,14 +205,6 @@ "default_mojo_media_client.h", ] } - - public_configs = [ ":enable_mojo_media_config" ] - - deps = [ - ":cdm_service", - ":renderer_service", - "//mojo/shell/public/cpp", - ] } test("media_mojo_unittests") { @@ -192,8 +228,12 @@ } mojo_native_application("media") { + testonly = true + sources = [ "main.cc", + "test_mojo_media_client.cc", + "test_mojo_media_client.h", ] deps = [ @@ -243,13 +283,6 @@ ] } -group("services") { - public_deps = [ - ":media", - ":proxy", - ] -} - group("tests") { testonly = true deps = [
diff --git a/media/mojo/services/default_mojo_media_client.cc b/media/mojo/services/default_mojo_media_client.cc index 890448b..6716e56 100644 --- a/media/mojo/services/default_mojo_media_client.cc +++ b/media/mojo/services/default_mojo_media_client.cc
@@ -4,15 +4,6 @@ #include "media/mojo/services/default_mojo_media_client.h" -#include "media/audio/audio_manager_base.h" -#include "media/audio/audio_output_stream_sink.h" -#include "media/base/audio_hardware_config.h" -#include "media/base/media.h" -#include "media/base/null_video_sink.h" -#include "media/cdm/default_cdm_factory.h" -#include "media/renderers/default_renderer_factory.h" -#include "media/renderers/gpu_video_accelerator_factories.h" - namespace media { DefaultMojoMediaClient::DefaultMojoMediaClient() {} @@ -20,50 +11,14 @@ DefaultMojoMediaClient::~DefaultMojoMediaClient() {} void DefaultMojoMediaClient::Initialize() { - InitializeMediaLibrary(); - // TODO(dalecurtis): We should find a single owner per process for the audio - // manager or make it a lazy instance. It's not safe to call Get()/Create() - // across multiple threads... - // - // TODO(dalecurtis): Eventually we'll want something other than a fake audio - // log factory here too. We should probably at least DVLOG() such info. - AudioManager* audio_manager = AudioManager::Get(); - if (!audio_manager) - audio_manager = media::AudioManager::Create(&fake_audio_log_factory_); - - audio_hardware_config_.reset(new AudioHardwareConfig( - audio_manager->GetInputStreamParameters( - AudioManagerBase::kDefaultDeviceId), - audio_manager->GetDefaultOutputStreamParameters())); -} - -scoped_ptr<RendererFactory> DefaultMojoMediaClient::CreateRendererFactory( - const scoped_refptr<MediaLog>& media_log) { - return make_scoped_ptr( - new DefaultRendererFactory(media_log, nullptr, *audio_hardware_config_)); -} - -AudioRendererSink* DefaultMojoMediaClient::CreateAudioRendererSink() { - if (!audio_renderer_sink_) - audio_renderer_sink_ = new AudioOutputStreamSink(); - - return audio_renderer_sink_.get(); -} - -VideoRendererSink* DefaultMojoMediaClient::CreateVideoRendererSink( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { - if (!video_renderer_sink_) { - video_renderer_sink_ = make_scoped_ptr( - new NullVideoSink(false, base::TimeDelta::FromSecondsD(1.0 / 60), - NullVideoSink::NewFrameCB(), task_runner)); - } - - return video_renderer_sink_.get(); + // TODO(jrummell): Do one-time initialization work here. } scoped_ptr<CdmFactory> DefaultMojoMediaClient::CreateCdmFactory( mojo::shell::mojom::InterfaceProvider* /* interface_provider */) { - return make_scoped_ptr(new DefaultCdmFactory()); + DVLOG(1) << __FUNCTION__; + // TODO(jrummell): Return a CdmFactory that can create CdmAdapter here. + return nullptr; } } // namespace media
diff --git a/media/mojo/services/default_mojo_media_client.h b/media/mojo/services/default_mojo_media_client.h index 5aff2ff..fc73d63c 100644 --- a/media/mojo/services/default_mojo_media_client.h +++ b/media/mojo/services/default_mojo_media_client.h
@@ -6,17 +6,10 @@ #define MEDIA_MOJO_SERVICES_DEFAULT_MOJO_MEDIA_CLIENT_H_ #include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "media/audio/fake_audio_log_factory.h" #include "media/mojo/services/mojo_media_client.h" namespace media { -class AudioHardwareConfig; -class AudioRendererSink; -class VideoRendererSink; - // Default MojoMediaClient for MojoMediaApplication. class DefaultMojoMediaClient : public MojoMediaClient { public: @@ -25,20 +18,10 @@ // MojoMediaClient implementation. void Initialize() final; - scoped_ptr<RendererFactory> CreateRendererFactory( - const scoped_refptr<MediaLog>& media_log) final; - AudioRendererSink* CreateAudioRendererSink() final; - VideoRendererSink* CreateVideoRendererSink( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) final; scoped_ptr<CdmFactory> CreateCdmFactory( mojo::shell::mojom::InterfaceProvider* /* interface_provider */) final; private: - FakeAudioLogFactory fake_audio_log_factory_; - scoped_ptr<AudioHardwareConfig> audio_hardware_config_; - scoped_refptr<AudioRendererSink> audio_renderer_sink_; - scoped_ptr<VideoRendererSink> video_renderer_sink_; - DISALLOW_COPY_AND_ASSIGN(DefaultMojoMediaClient); };
diff --git a/media/mojo/services/main.cc b/media/mojo/services/main.cc index 2ab7d06..5f2d6a4 100644 --- a/media/mojo/services/main.cc +++ b/media/mojo/services/main.cc
@@ -4,6 +4,7 @@ #include "base/at_exit.h" #include "media/mojo/services/mojo_media_application.h" +#include "media/mojo/services/test_mojo_media_client.h" #include "mojo/logging/init_logging.h" #include "mojo/public/c/system/main.h" #include "mojo/shell/public/cpp/application_runner.h" @@ -14,8 +15,7 @@ mojo::ApplicationRunner::InitBaseCommandLine(); mojo::InitLogging(); - scoped_ptr<mojo::ShellClient> shell_client = - media::MojoMediaApplication::CreateApp(); - mojo::ApplicationRunner runner(shell_client.release()); + mojo::ApplicationRunner runner(new media::MojoMediaApplication( + make_scoped_ptr(new media::TestMojoMediaClient()))); return runner.Run(mojo_handle, false /* init_base */); }
diff --git a/media/mojo/services/mojo_media_application.cc b/media/mojo/services/mojo_media_application.cc index 8a92b022..e343efdb 100644 --- a/media/mojo/services/mojo_media_application.cc +++ b/media/mojo/services/mojo_media_application.cc
@@ -12,38 +12,8 @@ #include "mojo/shell/public/cpp/connection.h" #include "mojo/shell/public/cpp/shell.h" -#if defined(OS_ANDROID) -#include "media/mojo/services/android_mojo_media_client.h" -#else -#include "media/mojo/services/default_mojo_media_client.h" -#endif - namespace media { -#if defined(OS_ANDROID) -using DefaultClient = AndroidMojoMediaClient; -#else -using DefaultClient = DefaultMojoMediaClient; -#endif - -// static -scoped_ptr<mojo::ShellClient> MojoMediaApplication::CreateApp() { - return scoped_ptr<mojo::ShellClient>( - new MojoMediaApplication(make_scoped_ptr(new DefaultClient()))); -} - -// static -scoped_ptr<mojo::ShellClient> MojoMediaApplication::CreateAppWithClient( - const CreateMojoMediaClientCB& create_mojo_media_client_cb) { - scoped_ptr<MojoMediaClient> mojo_media_client = - create_mojo_media_client_cb.Run(); - if (!mojo_media_client) - return nullptr; - - return scoped_ptr<mojo::ShellClient>( - new MojoMediaApplication(std::move(mojo_media_client))); -} - // TODO(xhwang): Hook up MediaLog when possible. MojoMediaApplication::MojoMediaApplication( scoped_ptr<MojoMediaClient> mojo_media_client)
diff --git a/media/mojo/services/mojo_media_application.h b/media/mojo/services/mojo_media_application.h index c1e24dffa..6cf98c9 100644 --- a/media/mojo/services/mojo_media_application.h +++ b/media/mojo/services/mojo_media_application.h
@@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#ifndef MEDIA_MOJO_SERVICES_MOJO_MEDIA_APPLICATION_H_ +#define MEDIA_MOJO_SERVICES_MOJO_MEDIA_APPLICATION_H_ + #include "base/callback.h" #include "base/memory/ref_counted.h" #include "media/mojo/interfaces/service_factory.mojom.h" @@ -18,22 +21,10 @@ : public mojo::ShellClient, public mojo::InterfaceFactory<interfaces::ServiceFactory> { public: - // Callback to create a MojoMediaClient. - using CreateMojoMediaClientCB = base::Callback<scoped_ptr<MojoMediaClient>()>; - - // Creates MojoMediaApplication using the default MojoMediaClient. - static scoped_ptr<mojo::ShellClient> CreateApp(); - - // Creates MojoMediaApplication using the MojoMediaClient provided by - // |create_mojo_media_client_cb|. - static scoped_ptr<mojo::ShellClient> CreateAppWithClient( - const CreateMojoMediaClientCB& create_mojo_media_client_cb); - + explicit MojoMediaApplication(scoped_ptr<MojoMediaClient> mojo_media_client); ~MojoMediaApplication() final; private: - explicit MojoMediaApplication(scoped_ptr<MojoMediaClient> mojo_media_client); - // mojo::ShellClient implementation. void Initialize(mojo::Shell* shell, const std::string& url, @@ -54,3 +45,5 @@ }; } // namespace media + +#endif // MEDIA_MOJO_SERVICES_MOJO_MEDIA_APPLICATION_H_
diff --git a/media/mojo/services/mojo_media_application_factory.cc b/media/mojo/services/mojo_media_application_factory.cc new file mode 100644 index 0000000..5ffaa5ad --- /dev/null +++ b/media/mojo/services/mojo_media_application_factory.cc
@@ -0,0 +1,28 @@ +// 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/mojo/services/mojo_media_application_factory.h" + +#include "media/mojo/services/mojo_media_application.h" + +#if defined(ENABLE_TEST_MOJO_MEDIA_CLIENT) +#include "media/mojo/services/test_mojo_media_client.h" +using DefaultClient = media::TestMojoMediaClient; +#elif defined(OS_ANDROID) +#include "media/mojo/services/android_mojo_media_client.h" +using DefaultClient = media::AndroidMojoMediaClient; +#else +#include "media/mojo/services/default_mojo_media_client.h" +using DefaultClient = media::DefaultMojoMediaClient; +#endif + +namespace media { + +// static +scoped_ptr<mojo::ShellClient> CreateMojoMediaApplication() { + return scoped_ptr<mojo::ShellClient>( + new MojoMediaApplication(make_scoped_ptr(new DefaultClient()))); +} + +} // namespace media
diff --git a/media/mojo/services/mojo_media_application_factory.h b/media/mojo/services/mojo_media_application_factory.h new file mode 100644 index 0000000..9ecbdf9 --- /dev/null +++ b/media/mojo/services/mojo_media_application_factory.h
@@ -0,0 +1,18 @@ +// 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 MEDIA_MOJO_SERVICES_MOJO_MEDIA_APPLICATION_FACTORY_H_ +#define MEDIA_MOJO_SERVICES_MOJO_MEDIA_APPLICATION_FACTORY_H_ + +#include "base/memory/scoped_ptr.h" +#include "mojo/shell/public/cpp/shell_client.h" + +namespace media { + +// Creates a MojoMediaApplication instance using the default MojoMediaClient. +scoped_ptr<mojo::ShellClient> CreateMojoMediaApplication(); + +} // namespace media + +#endif // MEDIA_MOJO_SERVICES_MOJO_MEDIA_APPLICATION_FACTORY_H_
diff --git a/media/mojo/services/test_mojo_media_client.cc b/media/mojo/services/test_mojo_media_client.cc new file mode 100644 index 0000000..b1ea14d6 --- /dev/null +++ b/media/mojo/services/test_mojo_media_client.cc
@@ -0,0 +1,71 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/mojo/services/test_mojo_media_client.h" + +#include "media/audio/audio_manager_base.h" +#include "media/audio/audio_output_stream_sink.h" +#include "media/base/audio_hardware_config.h" +#include "media/base/media.h" +#include "media/base/null_video_sink.h" +#include "media/cdm/default_cdm_factory.h" +#include "media/renderers/default_renderer_factory.h" +#include "media/renderers/gpu_video_accelerator_factories.h" + +namespace media { + +TestMojoMediaClient::TestMojoMediaClient() {} + +TestMojoMediaClient::~TestMojoMediaClient() {} + +void TestMojoMediaClient::Initialize() { + InitializeMediaLibrary(); + // TODO(dalecurtis): We should find a single owner per process for the audio + // manager or make it a lazy instance. It's not safe to call Get()/Create() + // across multiple threads... + // + // TODO(dalecurtis): Eventually we'll want something other than a fake audio + // log factory here too. We should probably at least DVLOG() such info. + AudioManager* audio_manager = AudioManager::Get(); + if (!audio_manager) + audio_manager = media::AudioManager::Create(&fake_audio_log_factory_); + + audio_hardware_config_.reset(new AudioHardwareConfig( + audio_manager->GetInputStreamParameters( + AudioManagerBase::kDefaultDeviceId), + audio_manager->GetDefaultOutputStreamParameters())); +} + +scoped_ptr<RendererFactory> TestMojoMediaClient::CreateRendererFactory( + const scoped_refptr<MediaLog>& media_log) { + DVLOG(1) << __FUNCTION__; + return make_scoped_ptr( + new DefaultRendererFactory(media_log, nullptr, *audio_hardware_config_)); +} + +AudioRendererSink* TestMojoMediaClient::CreateAudioRendererSink() { + if (!audio_renderer_sink_) + audio_renderer_sink_ = new AudioOutputStreamSink(); + + return audio_renderer_sink_.get(); +} + +VideoRendererSink* TestMojoMediaClient::CreateVideoRendererSink( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { + if (!video_renderer_sink_) { + video_renderer_sink_ = make_scoped_ptr( + new NullVideoSink(false, base::TimeDelta::FromSecondsD(1.0 / 60), + NullVideoSink::NewFrameCB(), task_runner)); + } + + return video_renderer_sink_.get(); +} + +scoped_ptr<CdmFactory> TestMojoMediaClient::CreateCdmFactory( + mojo::shell::mojom::InterfaceProvider* /* interface_provider */) { + DVLOG(1) << __FUNCTION__; + return make_scoped_ptr(new DefaultCdmFactory()); +} + +} // namespace media
diff --git a/media/mojo/services/test_mojo_media_client.h b/media/mojo/services/test_mojo_media_client.h new file mode 100644 index 0000000..fe41c36 --- /dev/null +++ b/media/mojo/services/test_mojo_media_client.h
@@ -0,0 +1,47 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_MOJO_SERVICES_TEST_MOJO_MEDIA_CLIENT_H_ +#define MEDIA_MOJO_SERVICES_TEST_MOJO_MEDIA_CLIENT_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "media/audio/fake_audio_log_factory.h" +#include "media/mojo/services/mojo_media_client.h" + +namespace media { + +class AudioHardwareConfig; +class AudioRendererSink; +class VideoRendererSink; + +// Default MojoMediaClient for MojoMediaApplication. +class TestMojoMediaClient : public MojoMediaClient { + public: + TestMojoMediaClient(); + ~TestMojoMediaClient() final; + + // MojoMediaClient implementation. + void Initialize() final; + scoped_ptr<RendererFactory> CreateRendererFactory( + const scoped_refptr<MediaLog>& media_log) final; + AudioRendererSink* CreateAudioRendererSink() final; + VideoRendererSink* CreateVideoRendererSink( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) final; + scoped_ptr<CdmFactory> CreateCdmFactory( + mojo::shell::mojom::InterfaceProvider* /* interface_provider */) final; + + private: + FakeAudioLogFactory fake_audio_log_factory_; + scoped_ptr<AudioHardwareConfig> audio_hardware_config_; + scoped_refptr<AudioRendererSink> audio_renderer_sink_; + scoped_ptr<VideoRendererSink> video_renderer_sink_; + + DISALLOW_COPY_AND_ASSIGN(TestMojoMediaClient); +}; + +} // namespace media + +#endif // MEDIA_MOJO_SERVICES_TEST_MOJO_MEDIA_CLIENT_H_
diff --git a/media/muxers/DEPS b/media/muxers/DEPS new file mode 100644 index 0000000..dd27068a --- /dev/null +++ b/media/muxers/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+third_party/libwebm", +]
diff --git a/media/muxers/OWNERS b/media/muxers/OWNERS new file mode 100644 index 0000000..088dd9cf6 --- /dev/null +++ b/media/muxers/OWNERS
@@ -0,0 +1,2 @@ +mcasas@chromium.org +miu@chromium.org \ No newline at end of file
diff --git a/media/muxers/webm_muxer.cc b/media/muxers/webm_muxer.cc new file mode 100644 index 0000000..e00982a --- /dev/null +++ b/media/muxers/webm_muxer.cc
@@ -0,0 +1,289 @@ +// 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. + +#include "media/muxers/webm_muxer.h" + +#include "base/bind.h" +#include "media/audio/audio_parameters.h" +#include "media/base/limits.h" +#include "media/base/video_frame.h" +#include "media/filters/opus_constants.h" +#include "ui/gfx/geometry/size.h" + +namespace media { + +namespace { + +void WriteOpusHeader(const media::AudioParameters& params, uint8_t* header) { + // See https://wiki.xiph.org/OggOpus#ID_Header. + // Set magic signature. + std::string label = "OpusHead"; + memcpy(header + OPUS_EXTRADATA_LABEL_OFFSET, label.c_str(), label.size()); + // Set Opus version. + header[OPUS_EXTRADATA_VERSION_OFFSET] = 1; + // Set channel count. + header[OPUS_EXTRADATA_CHANNELS_OFFSET] = params.channels(); + // Set pre-skip + uint16_t skip = 0; + memcpy(header + OPUS_EXTRADATA_SKIP_SAMPLES_OFFSET, &skip, sizeof(uint16_t)); + // Set original input sample rate in Hz. + uint32_t sample_rate = params.sample_rate(); + memcpy(header + OPUS_EXTRADATA_SAMPLE_RATE_OFFSET, &sample_rate, + sizeof(uint32_t)); + // Set output gain in dB. + uint16_t gain = 0; + memcpy(header + OPUS_EXTRADATA_GAIN_OFFSET, &gain, 2); + + // Set channel mapping. + if (params.channels() > 2) { + // Also possible to have a multistream, not supported for now. + DCHECK_LE(params.channels(), OPUS_MAX_VORBIS_CHANNELS); + header[OPUS_EXTRADATA_CHANNEL_MAPPING_OFFSET] = 1; + // Assuming no coupled streams. This should actually be + // channels() - |coupled_streams|. + header[OPUS_EXTRADATA_NUM_STREAMS_OFFSET] = params.channels(); + header[OPUS_EXTRADATA_NUM_COUPLED_OFFSET] = 0; + // Set the actual stream map. + for (int i = 0; i < params.channels(); ++i) { + header[OPUS_EXTRADATA_STREAM_MAP_OFFSET + i] = + kOpusVorbisChannelMap[params.channels() - 1][i]; + } + } else { + header[OPUS_EXTRADATA_CHANNEL_MAPPING_OFFSET] = 0; + } +} + +static double GetFrameRate(const scoped_refptr<VideoFrame>& video_frame) { + const double kZeroFrameRate = 0.0; + const double kDefaultFrameRate = 30.0; + + double frame_rate = kDefaultFrameRate; + if (!video_frame->metadata()->GetDouble(VideoFrameMetadata::FRAME_RATE, + &frame_rate) || + frame_rate <= kZeroFrameRate || + frame_rate > media::limits::kMaxFramesPerSecond) { + frame_rate = kDefaultFrameRate; + } + return frame_rate; +} + +} // anonymous namespace + +WebmMuxer::WebmMuxer(VideoCodec codec, + bool has_video, + bool has_audio, + const WriteDataCB& write_data_callback) + : use_vp9_(codec == kCodecVP9), + video_track_index_(0), + audio_track_index_(0), + has_video_(has_video), + has_audio_(has_audio), + write_data_callback_(write_data_callback), + position_(0) { + DCHECK(has_video_ || has_audio_); + DCHECK(!write_data_callback_.is_null()); + DCHECK(codec == kCodecVP8 || codec == kCodecVP9) + << " Only Vp8 and VP9 are supported in WebmMuxer"; + + segment_.Init(this); + segment_.set_mode(mkvmuxer::Segment::kLive); + segment_.OutputCues(false); + + mkvmuxer::SegmentInfo* const info = segment_.GetSegmentInfo(); + info->set_writing_app("Chrome"); + info->set_muxing_app("Chrome"); + + // Creation is done on a different thread than main activities. + thread_checker_.DetachFromThread(); +} + +WebmMuxer::~WebmMuxer() { + // No need to segment_.Finalize() since is not Seekable(), i.e. a live + // stream, but is a good practice. + DCHECK(thread_checker_.CalledOnValidThread()); + segment_.Finalize(); +} + +void WebmMuxer::OnEncodedVideo(const scoped_refptr<VideoFrame>& video_frame, + scoped_ptr<std::string> encoded_data, + base::TimeTicks timestamp, + bool is_key_frame) { + DVLOG(1) << __FUNCTION__ << " - " << encoded_data->size() << "B"; + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!video_track_index_) { + // |track_index_|, cannot be zero (!), initialize WebmMuxer in that case. + // http://www.matroska.org/technical/specs/index.html#Tracks + AddVideoTrack(video_frame->visible_rect().size(), + GetFrameRate(video_frame)); + if (first_frame_timestamp_.is_null()) + first_frame_timestamp_ = timestamp; + } + + // TODO(ajose): Support multiple tracks: http://crbug.com/528523 + if (has_audio_ && !audio_track_index_) { + DVLOG(1) << __FUNCTION__ << ": delaying until audio track ready."; + if (is_key_frame) // Upon Key frame reception, empty the encoded queue. + encoded_frames_queue_.clear(); + + encoded_frames_queue_.push_back(make_scoped_ptr(new EncodedVideoFrame( + std::move(encoded_data), timestamp, is_key_frame))); + return; + } + + // Dump all saved encoded video frames if any. + while (!encoded_frames_queue_.empty()) { + AddFrame(std::move(encoded_frames_queue_.front()->data), video_track_index_, + encoded_frames_queue_.front()->timestamp, + encoded_frames_queue_.front()->is_keyframe); + encoded_frames_queue_.pop_front(); + } + + AddFrame(std::move(encoded_data), video_track_index_, timestamp, + is_key_frame); +} + +void WebmMuxer::OnEncodedAudio(const media::AudioParameters& params, + scoped_ptr<std::string> encoded_data, + base::TimeTicks timestamp) { + DVLOG(1) << __FUNCTION__ << " - " << encoded_data->size() << "B"; + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!audio_track_index_) { + AddAudioTrack(params); + if (first_frame_timestamp_.is_null()) + first_frame_timestamp_ = timestamp; + } + + // TODO(ajose): Don't drop audio data: http://crbug.com/547948 + // TODO(ajose): Support multiple tracks: http://crbug.com/528523 + if (has_video_ && !video_track_index_) { + DVLOG(1) << __FUNCTION__ << ": delaying until video track ready."; + return; + } + + // Dump all saved encoded video frames if any. + while (!encoded_frames_queue_.empty()) { + AddFrame(std::move(encoded_frames_queue_.front()->data), video_track_index_, + encoded_frames_queue_.front()->timestamp, + encoded_frames_queue_.front()->is_keyframe); + encoded_frames_queue_.pop_front(); + } + + AddFrame(std::move(encoded_data), audio_track_index_, timestamp, + true /* is_key_frame -- always true for audio */); +} + +void WebmMuxer::AddVideoTrack(const gfx::Size& frame_size, double frame_rate) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(0u, video_track_index_) + << "WebmMuxer can only be initialized once."; + + video_track_index_ = + segment_.AddVideoTrack(frame_size.width(), frame_size.height(), 0); + DCHECK_GT(video_track_index_, 0u); + + mkvmuxer::VideoTrack* const video_track = + reinterpret_cast<mkvmuxer::VideoTrack*>( + segment_.GetTrackByNumber(video_track_index_)); + DCHECK(video_track); + video_track->set_codec_id(use_vp9_ ? mkvmuxer::Tracks::kVp9CodecId + : mkvmuxer::Tracks::kVp8CodecId); + DCHECK_EQ(0ull, video_track->crop_right()); + DCHECK_EQ(0ull, video_track->crop_left()); + DCHECK_EQ(0ull, video_track->crop_top()); + DCHECK_EQ(0ull, video_track->crop_bottom()); + + video_track->set_frame_rate(frame_rate); + video_track->set_default_duration(base::Time::kNanosecondsPerSecond / + frame_rate); + // Segment's timestamps should be in milliseconds, DCHECK it. See + // http://www.webmproject.org/docs/container/#muxer-guidelines + DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale()); +} + +void WebmMuxer::AddAudioTrack(const media::AudioParameters& params) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(0u, audio_track_index_) + << "WebmMuxer audio can only be initialised once."; + + audio_track_index_ = + segment_.AddAudioTrack(params.sample_rate(), params.channels(), 0); + DCHECK_GT(audio_track_index_, 0u); + + mkvmuxer::AudioTrack* const audio_track = + reinterpret_cast<mkvmuxer::AudioTrack*>( + segment_.GetTrackByNumber(audio_track_index_)); + DCHECK(audio_track); + audio_track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId); + + DCHECK_EQ(params.sample_rate(), audio_track->sample_rate()); + DCHECK_EQ(params.channels(), static_cast<int>(audio_track->channels())); + + uint8_t opus_header[OPUS_EXTRADATA_SIZE]; + WriteOpusHeader(params, opus_header); + + if (!audio_track->SetCodecPrivate(opus_header, OPUS_EXTRADATA_SIZE)) + LOG(ERROR) << __FUNCTION__ << ": failed to set opus header."; + + // Segment's timestamps should be in milliseconds, DCHECK it. See + // http://www.webmproject.org/docs/container/#muxer-guidelines + DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale()); +} + +mkvmuxer::int32 WebmMuxer::Write(const void* buf, mkvmuxer::uint32 len) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(buf); + write_data_callback_.Run( + base::StringPiece(reinterpret_cast<const char*>(buf), len)); + position_ += len; + return 0; +} + +mkvmuxer::int64 WebmMuxer::Position() const { + return position_.ValueOrDie(); +} + +mkvmuxer::int32 WebmMuxer::Position(mkvmuxer::int64 position) { + // The stream is not Seekable() so indicate we cannot set the position. + return -1; +} + +bool WebmMuxer::Seekable() const { + return false; +} + +void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id, + mkvmuxer::int64 position) { + // This method gets pinged before items are sent to |write_data_callback_|. + DCHECK_GE(position, position_.ValueOrDefault(0)) + << "Can't go back in a live WebM stream."; +} + +void WebmMuxer::AddFrame(scoped_ptr<std::string> encoded_data, + uint8_t track_index, + base::TimeTicks timestamp, + bool is_key_frame) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!has_video_ || video_track_index_); + DCHECK(!has_audio_ || audio_track_index_); + + most_recent_timestamp_ = + std::max(most_recent_timestamp_, timestamp - first_frame_timestamp_); + + segment_.AddFrame(reinterpret_cast<const uint8_t*>(encoded_data->data()), + encoded_data->size(), track_index, + most_recent_timestamp_.InMicroseconds() * + base::Time::kNanosecondsPerMicrosecond, + is_key_frame); +} + +WebmMuxer::EncodedVideoFrame::EncodedVideoFrame(scoped_ptr<std::string> data, + base::TimeTicks timestamp, + bool is_keyframe) + : data(std::move(data)), timestamp(timestamp), is_keyframe(is_keyframe) {} + +WebmMuxer::EncodedVideoFrame::~EncodedVideoFrame() {} + +} // namespace media
diff --git a/media/muxers/webm_muxer.h b/media/muxers/webm_muxer.h new file mode 100644 index 0000000..227ce5622 --- /dev/null +++ b/media/muxers/webm_muxer.h
@@ -0,0 +1,142 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_MUXERS_WEBM_MUXER_H_ +#define MEDIA_MUXERS_WEBM_MUXER_H_ + +#include <stdint.h> + +#include <deque> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/numerics/safe_math.h" +#include "base/strings/string_piece.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "media/base/media_export.h" +#include "media/base/video_codecs.h" +#include "third_party/libwebm/source/mkvmuxer.hpp" + +namespace gfx { +class Size; +} // namespace gfx + +namespace media { + +class VideoFrame; +class AudioParameters; + +// Adapter class to manage a WebM container [1], a simplified version of a +// Matroska container [2], composed of an EBML header, and a single Segment +// including at least a Track Section and a number of SimpleBlocks each +// containing a single encoded video or audio frame. WebM container has no +// Trailer. +// Clients will push encoded VPx video frames and Opus audio frames one by one +// via OnEncoded{Video|Audio}(). libwebm will eventually ping the WriteDataCB +// passed on contructor with the wrapped encoded data. +// WebmMuxer is designed for use on a single thread. +// [1] http://www.webmproject.org/docs/container/ +// [2] http://www.matroska.org/technical/specs/index.html +class MEDIA_EXPORT WebmMuxer : public NON_EXPORTED_BASE(mkvmuxer::IMkvWriter) { + public: + // Callback to be called when WebmMuxer is ready to write a chunk of data, + // either any file header or a SingleBlock. + using WriteDataCB = base::Callback<void(base::StringPiece)>; + + // |codec| can be VP8 or VP9 and should coincide with whatever is sent in + // OnEncodedVideo(). + WebmMuxer(VideoCodec codec, + bool has_video_, + bool has_audio_, + const WriteDataCB& write_data_callback); + ~WebmMuxer() override; + + // Functions to add video and audio frames with |encoded_data.data()| + // to WebM Segment. + void OnEncodedVideo(const scoped_refptr<VideoFrame>& video_frame, + scoped_ptr<std::string> encoded_data, + base::TimeTicks timestamp, + bool is_key_frame); + void OnEncodedAudio(const media::AudioParameters& params, + scoped_ptr<std::string> encoded_data, + base::TimeTicks timestamp); + + private: + friend class WebmMuxerTest; + + // Methods for creating and adding video and audio tracks, called upon + // receiving the first frame of a given Track. + // AddVideoTrack adds |frame_size| and |frame_rate| to the Segment + // info, although individual frames passed to OnEncodedVideo() can have any + // frame size. + void AddVideoTrack(const gfx::Size& frame_size, double frame_rate); + void AddAudioTrack(const media::AudioParameters& params); + + // IMkvWriter interface. + mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) override; + mkvmuxer::int64 Position() const override; + mkvmuxer::int32 Position(mkvmuxer::int64 position) override; + bool Seekable() const override; + void ElementStartNotify(mkvmuxer::uint64 element_id, + mkvmuxer::int64 position) override; + + // Helper to simplify saving frames. + void AddFrame(scoped_ptr<std::string> encoded_data, + uint8_t track_index, + base::TimeTicks timestamp, + bool is_key_frame); + + // Used to DCHECK that we are called on the correct thread. + base::ThreadChecker thread_checker_; + + // Video Codec configured: VP9 if true, otherwise VP8 is used by default. + const bool use_vp9_; + + // Caller-side identifiers to interact with |segment_|, initialised upon + // first frame arrival to Add{Video, Audio}Track(). + uint8_t video_track_index_; + uint8_t audio_track_index_; + + // Origin of times for frame timestamps. + base::TimeTicks first_frame_timestamp_; + base::TimeDelta most_recent_timestamp_; + + // TODO(ajose): Change these when support is added for multiple tracks. + // http://crbug.com/528523 + const bool has_video_; + const bool has_audio_; + + // Callback to dump written data as being called by libwebm. + const WriteDataCB write_data_callback_; + + // Rolling counter of the position in bytes of the written goo. + base::CheckedNumeric<mkvmuxer::int64> position_; + + // The MkvMuxer active element. + mkvmuxer::Segment segment_; + + // Hold on to all encoded video frames to dump them with and when audio is + // received, if expected, since WebM headers can only be written once. + struct EncodedVideoFrame { + EncodedVideoFrame(scoped_ptr<std::string> data, + base::TimeTicks timestamp, + bool is_keyframe); + ~EncodedVideoFrame(); + + scoped_ptr<std::string> data; + base::TimeTicks timestamp; + bool is_keyframe; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedVideoFrame); + }; + std::deque<scoped_ptr<EncodedVideoFrame>> encoded_frames_queue_; + + DISALLOW_COPY_AND_ASSIGN(WebmMuxer); +}; + +} // namespace media + +#endif // MEDIA_MUXERS_WEBM_MUXER_H_
diff --git a/media/muxers/webm_muxer_unittest.cc b/media/muxers/webm_muxer_unittest.cc new file mode 100644 index 0000000..03634de --- /dev/null +++ b/media/muxers/webm_muxer_unittest.cc
@@ -0,0 +1,250 @@ +// 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. + +#include <stddef.h> +#include <stdint.h> + +#include "base/bind.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "media/audio/audio_parameters.h" +#include "media/base/channel_layout.h" +#include "media/base/video_frame.h" +#include "media/muxers/webm_muxer.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::AllOf; +using ::testing::AnyNumber; +using ::testing::AtLeast; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::Mock; +using ::testing::Not; +using ::testing::Sequence; +using ::testing::TestWithParam; +using ::testing::ValuesIn; +using ::testing::WithArgs; + +namespace media { + +struct kTestParams { + VideoCodec codec; + size_t num_video_tracks; + size_t num_audio_tracks; +}; + +class WebmMuxerTest : public TestWithParam<kTestParams> { + public: + WebmMuxerTest() + : webm_muxer_( + GetParam().codec, + GetParam().num_video_tracks, + GetParam().num_audio_tracks, + base::Bind(&WebmMuxerTest::WriteCallback, base::Unretained(this))), + last_encoded_length_(0), + accumulated_position_(0) { + EXPECT_EQ(webm_muxer_.Position(), 0); + const mkvmuxer::int64 kRandomNewPosition = 333; + EXPECT_EQ(webm_muxer_.Position(kRandomNewPosition), -1); + EXPECT_FALSE(webm_muxer_.Seekable()); + } + + MOCK_METHOD1(WriteCallback, void(base::StringPiece)); + + void SaveEncodedDataLen(const base::StringPiece& encoded_data) { + last_encoded_length_ = encoded_data.size(); + accumulated_position_ += encoded_data.size(); + } + + mkvmuxer::int64 GetWebmMuxerPosition() const { + return webm_muxer_.Position(); + } + + mkvmuxer::Segment::Mode GetWebmSegmentMode() const { + return webm_muxer_.segment_.mode(); + } + + mkvmuxer::int32 WebmMuxerWrite(const void* buf, mkvmuxer::uint32 len) { + return webm_muxer_.Write(buf, len); + } + + WebmMuxer webm_muxer_; + + size_t last_encoded_length_; + int64_t accumulated_position_; + + private: + DISALLOW_COPY_AND_ASSIGN(WebmMuxerTest); +}; + +// Checks that the WriteCallback is called with appropriate params when +// WebmMuxer::Write() method is called. +TEST_P(WebmMuxerTest, Write) { + const base::StringPiece encoded_data("abcdefghijklmnopqrstuvwxyz"); + + EXPECT_CALL(*this, WriteCallback(encoded_data)); + WebmMuxerWrite(encoded_data.data(), encoded_data.size()); + + EXPECT_EQ(GetWebmMuxerPosition(), static_cast<int64_t>(encoded_data.size())); +} + +// This test sends two frames and checks that the WriteCallback is called with +// appropriate params in both cases. +TEST_P(WebmMuxerTest, OnEncodedVideoTwoFrames) { + if (GetParam().num_audio_tracks > 0) + return; + + const gfx::Size frame_size(160, 80); + const scoped_refptr<VideoFrame> video_frame = + VideoFrame::CreateBlackFrame(frame_size); + const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); + + EXPECT_CALL(*this, WriteCallback(_)) + .Times(AtLeast(1)) + .WillRepeatedly( + WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); + webm_muxer_.OnEncodedVideo(video_frame, + make_scoped_ptr(new std::string(encoded_data)), + base::TimeTicks::Now(), false /* keyframe */); + + // First time around WriteCallback() is pinged a number of times to write the + // Matroska header, but at the end it dumps |encoded_data|. + EXPECT_EQ(last_encoded_length_, encoded_data.size()); + EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); + EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); + EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); + + const int64_t begin_of_second_block = accumulated_position_; + EXPECT_CALL(*this, WriteCallback(_)) + .Times(AtLeast(1)) + .WillRepeatedly( + WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); + webm_muxer_.OnEncodedVideo(video_frame, + make_scoped_ptr(new std::string(encoded_data)), + base::TimeTicks::Now(), false /* keyframe */); + + // The second time around the callbacks should include a SimpleBlock header, + // namely the track index, a timestamp and a flags byte, for a total of 6B. + EXPECT_EQ(last_encoded_length_, encoded_data.size()); + EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); + const uint32_t kSimpleBlockSize = 6u; + EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + + encoded_data.size()), + accumulated_position_); +} + +TEST_P(WebmMuxerTest, OnEncodedAudioTwoFrames) { + if (GetParam().num_video_tracks > 0) + return; + + const int sample_rate = 48000; + const int bits_per_sample = 16; + const int frames_per_buffer = 480; + media::AudioParameters audio_params( + media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, + media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, + frames_per_buffer); + + const std::string encoded_data("abcdefghijklmnopqrstuvwxyz"); + + EXPECT_CALL(*this, WriteCallback(_)) + .Times(AtLeast(1)) + .WillRepeatedly( + WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); + webm_muxer_.OnEncodedAudio(audio_params, + make_scoped_ptr(new std::string(encoded_data)), + base::TimeTicks::Now()); + + // First time around WriteCallback() is pinged a number of times to write the + // Matroska header, but at the end it dumps |encoded_data|. + EXPECT_EQ(last_encoded_length_, encoded_data.size()); + EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); + EXPECT_GE(GetWebmMuxerPosition(), static_cast<int64_t>(last_encoded_length_)); + EXPECT_EQ(GetWebmSegmentMode(), mkvmuxer::Segment::kLive); + + const int64_t begin_of_second_block = accumulated_position_; + EXPECT_CALL(*this, WriteCallback(_)) + .Times(AtLeast(1)) + .WillRepeatedly( + WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen))); + webm_muxer_.OnEncodedAudio(audio_params, + make_scoped_ptr(new std::string(encoded_data)), + base::TimeTicks::Now()); + + // The second time around the callbacks should include a SimpleBlock header, + // namely the track index, a timestamp and a flags byte, for a total of 6B. + EXPECT_EQ(last_encoded_length_, encoded_data.size()); + EXPECT_EQ(GetWebmMuxerPosition(), accumulated_position_); + const uint32_t kSimpleBlockSize = 6u; + EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize + + encoded_data.size()), + accumulated_position_); +} + +// This test verifies that when video data comes before audio data, we save the +// encoded video frames and add it to the video track when audio data arrives. +TEST_P(WebmMuxerTest, VideoIsStoredWhileWaitingForAudio) { + // This test is only relevant if we have both kinds of tracks. + if (GetParam().num_video_tracks == 0 || GetParam().num_audio_tracks == 0) + return; + + // First send a video keyframe + const gfx::Size frame_size(160, 80); + const scoped_refptr<VideoFrame> video_frame = + VideoFrame::CreateBlackFrame(frame_size); + const std::string encoded_video("thisisanencodedvideopacket"); + webm_muxer_.OnEncodedVideo(video_frame, + make_scoped_ptr(new std::string(encoded_video)), + base::TimeTicks::Now(), true /* keyframe */); + // A few encoded non key frames. + const int kNumNonKeyFrames = 2; + for (int i = 0; i < kNumNonKeyFrames; ++i) { + webm_muxer_.OnEncodedVideo(video_frame, + make_scoped_ptr(new std::string(encoded_video)), + base::TimeTicks::Now(), false /* keyframe */); + } + + // Send some audio. The header will be written and muxing will proceed + // normally. + const int sample_rate = 48000; + const int bits_per_sample = 16; + const int frames_per_buffer = 480; + media::AudioParameters audio_params( + media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, + media::CHANNEL_LAYOUT_MONO, sample_rate, bits_per_sample, + frames_per_buffer); + const std::string encoded_audio("thisisanencodedaudiopacket"); + + // We should first get the encoded video frames, then the encoded audio frame. + Sequence s; + EXPECT_CALL(*this, WriteCallback(Eq(encoded_video))) + .Times(1 + kNumNonKeyFrames) + .InSequence(s); + EXPECT_CALL(*this, WriteCallback(Eq(encoded_audio))).Times(1).InSequence(s); + // We'll also get lots of other header-related stuff. + EXPECT_CALL(*this, WriteCallback( + AllOf(Not(Eq(encoded_video)), Not(Eq(encoded_audio))))) + .Times(AnyNumber()); + webm_muxer_.OnEncodedAudio(audio_params, + make_scoped_ptr(new std::string(encoded_audio)), + base::TimeTicks::Now()); +} + +const kTestParams kTestCases[] = { + // TODO: consider not enumerating every combination by hand. + {kCodecVP8, 1 /* num_video_tracks */, 0 /*num_audio_tracks*/}, + {kCodecVP8, 0, 1}, + {kCodecVP8, 1, 1}, + {kCodecVP9, 1, 0}, + {kCodecVP9, 0, 1}, + {kCodecVP9, 1, 1}, +}; + +INSTANTIATE_TEST_CASE_P(, WebmMuxerTest, ValuesIn(kTestCases)); + +} // namespace media
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc index 335dd2e..9a1eeaa1 100644 --- a/media/renderers/audio_renderer_impl.cc +++ b/media/renderers/audio_renderer_impl.cc
@@ -359,10 +359,10 @@ int sample_rate = hw_params.sample_rate(); int preferred_buffer_size = hw_params.frames_per_buffer(); -#if defined(OS_CHROMEOS) || defined(OS_ANDROID) - // On ChromeOS and Android let the OS level resampler handle resampling - // unless the initial sample rate is too low; this allows support for - // sample rate adaptations where necessary. +#if defined(OS_CHROMEOS) + // On ChromeOS let the OS level resampler handle resampling unless the + // initial sample rate is too low; this allows support for sample rate + // adaptations where necessary. if (stream->audio_decoder_config().samples_per_second() >= 44100) { sample_rate = stream->audio_decoder_config().samples_per_second(); preferred_buffer_size = 0; // No preference.
diff --git a/media/test/data/eme_player_js/file_io_test_player.js b/media/test/data/eme_player_js/file_io_test_player.js index 583d422..5136e7d 100644 --- a/media/test/data/eme_player_js/file_io_test_player.js +++ b/media/test/data/eme_player_js/file_io_test_player.js
@@ -15,18 +15,21 @@ FileIOTestPlayer.prototype.registerEventListeners = function() { // Returns a promise. - return PlayerUtils.registerPrefixedEMEEventListeners(this); + return PlayerUtils.registerEMEEventListeners(this).then(function(result) { + return PlayerUtils.registerPrefixedEMEEventListeners(this); + }); }; handleMessage = function(message) { // The test result is either '0' or '1' appended to the header. - if (Utils.hasPrefix(message.message, FILE_IO_TEST_RESULT_HEADER)) { - if (message.message.length != FILE_IO_TEST_RESULT_HEADER.length + 1) { - Utils.failTest('Unexpected FileIOTest CDM message' + message.message); + var msg = Utils.convertToUint8Array(message.message); + if (Utils.hasPrefix(msg, FILE_IO_TEST_RESULT_HEADER)) { + if (msg.length != FILE_IO_TEST_RESULT_HEADER.length + 1) { + Utils.failTest('Unexpected FileIOTest CDM message' + msg); return; } var result_index = FILE_IO_TEST_RESULT_HEADER.length; - var success = String.fromCharCode(message.message[result_index]) == 1; + var success = String.fromCharCode(msg[result_index]) == 1; Utils.timeLog('CDM file IO test: ' + (success ? 'Success' : 'Fail')); if (success) Utils.setResultInTitle(FILE_IO_TEST_SUCCESS);
diff --git a/media/test/data/eme_player_js/player_utils.js b/media/test/data/eme_player_js/player_utils.js index b9f1732c..81332ca 100644 --- a/media/test/data/eme_player_js/player_utils.js +++ b/media/test/data/eme_player_js/player_utils.js
@@ -98,6 +98,11 @@ player.video.receivedKeyMessage = false; Utils.timeLog('Setting video media keys: ' + player.testConfig.keySystem); var config = {}; + // The File IO test requires persistent state support. + if (player.testConfig.keySystem == + 'org.chromium.externalclearkey.fileiotest') { + config = {persistentState: "required"}; + } if (player.testConfig.sessionToLoad) { config = { persistentState: "required",
diff --git a/media/test/data/eme_player_js/utils.js b/media/test/data/eme_player_js/utils.js index 00e23ac4..41a4d45 100644 --- a/media/test/data/eme_player_js/utils.js +++ b/media/test/data/eme_player_js/utils.js
@@ -175,7 +175,7 @@ }; Utils.hasPrefix = function(msg, prefix) { - var message = String.fromCharCode.apply(null, msg); + var message = String.fromCharCode.apply(null, Utils.convertToUint8Array(msg)); return message.substring(0, prefix.length) == prefix; }; @@ -199,8 +199,7 @@ // For the prefixed API renewal messages are determined by looking at the // message and finding a known string. Utils.isRenewalMessagePrefixed = function(msg) { - return Utils.hasPrefix(Utils.convertToUint8Array(msg), - PREFIXED_EME_RENEWAL_MESSAGE_HEADER); + return Utils.hasPrefix(msg, PREFIXED_EME_RENEWAL_MESSAGE_HEADER); }; Utils.resetTitleChange = function() {
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn index e5e15a3c..db4cd0e0 100644 --- a/mojo/BUILD.gn +++ b/mojo/BUILD.gn
@@ -43,13 +43,9 @@ "//mojo/edk/test:mojo_public_system_unittests", "//mojo/edk/test:mojo_public_utility_unittests", "//mojo/services/network:apptests", - "//mojo/shell:apptests", - "//mojo/shell:mojo_shell_unittests", "//mojo/shell/public/cpp/tests:mojo_public_application_unittests", "//mojo/shell/runner/host:mojo_runner_host_unittests", + "//mojo/shell/tests:apptests", + "//mojo/shell/tests:mojo_shell_unittests", ] - - if (is_android) { - deps += [ "//mojo/shell/standalone:mojo_shell_standalone_apptests_apk" ] - } }
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn index 6a12118..ad49a59 100644 --- a/mojo/common/BUILD.gn +++ b/mojo/common/BUILD.gn
@@ -113,3 +113,14 @@ configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ] } } + +source_set("mojo_scheme_register") { + sources = [ + "mojo_scheme_register.cc", + "mojo_scheme_register.h", + ] + + deps = [ + "//url", + ] +}
diff --git a/mojo/common/mojo_scheme_register.cc b/mojo/common/mojo_scheme_register.cc new file mode 100644 index 0000000..db1224b --- /dev/null +++ b/mojo/common/mojo_scheme_register.cc
@@ -0,0 +1,23 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/common/mojo_scheme_register.h" + +#include "url/gurl.h" +#include "url/url_util.h" + +namespace mojo { + +void RegisterMojoSchemes() { + static bool registered = false; + + if (registered) + return; + + registered = true; + url::AddStandardScheme("mojo", url::SCHEME_WITHOUT_AUTHORITY); + url::AddStandardScheme("exe", url::SCHEME_WITHOUT_AUTHORITY); +} + +} // namespace mojo
diff --git a/mojo/common/mojo_scheme_register.h b/mojo/common/mojo_scheme_register.h new file mode 100644 index 0000000..7ca4981 --- /dev/null +++ b/mojo/common/mojo_scheme_register.h
@@ -0,0 +1,15 @@ +// 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 MOJO_COMMON_MOJO_SCHEME_REGISTER_H_ +#define MOJO_COMMON_MOJO_SCHEME_REGISTER_H_ + +namespace mojo { + +// Registers the schemes as used by mojo. +void RegisterMojoSchemes(); + +} // namespace mojo + +#endif // MOJO_COMMON_MOJO_SCHEME_REGISTER_H_
diff --git a/mojo/converters/input_events/input_events_type_converters.cc b/mojo/converters/input_events/input_events_type_converters.cc index ce704ef..8d6e7250 100644 --- a/mojo/converters/input_events/input_events_type_converters.cc +++ b/mojo/converters/input_events/input_events_type_converters.cc
@@ -244,8 +244,8 @@ brush_data->height = touch_event->pointer_details().radius_y; // TODO(rjk): update for touch_event->rotation_angle(); brush_data->pressure = touch_event->pointer_details().force; + brush_data->tilt_x = 0; brush_data->tilt_y = 0; - brush_data->tilt_z = 0; pointer_data->brush_data = std::move(brush_data); event->pointer_data = std::move(pointer_data);
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc index fa4e6ea..1feadda 100644 --- a/mojo/edk/embedder/embedder.cc +++ b/mojo/edk/embedder/embedder.cc
@@ -94,9 +94,6 @@ internal::g_process_delegate = process_delegate; } -void ShutdownIPCSupportOnIOThread() { -} - void ShutdownIPCSupport() { CHECK(internal::g_process_delegate); CHECK(internal::g_core); @@ -107,17 +104,17 @@ ScopedMessagePipeHandle CreateMessagePipe( ScopedPlatformHandle platform_handle) { - DCHECK(internal::g_core); + CHECK(internal::g_process_delegate); return internal::g_core->CreateMessagePipe(std::move(platform_handle)); } ScopedMessagePipeHandle CreateParentMessagePipe(const std::string& token) { - DCHECK(internal::g_core); + CHECK(internal::g_process_delegate); return internal::g_core->CreateParentMessagePipe(token); } ScopedMessagePipeHandle CreateChildMessagePipe(const std::string& token) { - DCHECK(internal::g_core); + CHECK(internal::g_process_delegate); return internal::g_core->CreateChildMessagePipe(token); }
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h index 6c010f4..e34ef5c 100644 --- a/mojo/edk/embedder/embedder.h +++ b/mojo/edk/embedder/embedder.h
@@ -100,30 +100,23 @@ // making the IPC functions (in the following section) available and functional. // (This may only be done after |Init()|.) // -// This subsystem may be shut down, using |ShutdownIPCSupportOnIOThread()| or -// |ShutdownIPCSupport()|. None of the IPC functions may be called while or -// after either of these is called. +// This subsystem may be shut down using |ShutdownIPCSupport()|. None of the IPC +// functions may be called after this is called. // Initializes a process of the given type; to be called after |Init()|. // - |process_delegate| must be a process delegate of the appropriate type // corresponding to |process_type|; its methods will be called on the same // thread as Shutdown. // - |process_delegate|, and |io_thread_task_runner| should live at least -// until |ShutdownIPCSupport()|'s callback has been run or -// |ShutdownIPCSupportOnIOThread()| has completed. +// until |ShutdownIPCSupport()|'s callback has been run. MOJO_SYSTEM_IMPL_EXPORT void InitIPCSupport( ProcessDelegate* process_delegate, scoped_refptr<base::TaskRunner> io_thread_task_runner); -// Shuts down the subsystem initialized by |InitIPCSupport()|. This must be -// called on the I/O thread (given to |InitIPCSupport()|). This completes -// synchronously and does not result in a call to the process delegate's -// |OnShutdownComplete()|. -MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupportOnIOThread(); - -// Like |ShutdownIPCSupportOnIOThread()|, but may be called from any thread, -// signalling shutdown completion via the process delegate's -// |OnShutdownComplete()|. +// Shuts down the subsystem initialized by |InitIPCSupport()|. It be called from +// any thread and will attempt to complete shutdown on the I/O thread with which +// the system was initialized. Upon completion the ProcessDelegate's +// |OnShutdownComplete()| method is invoked. MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport(); // Creates a message pipe over an arbitrary platform channel. The other end of
diff --git a/mojo/edk/embedder/process_delegate.h b/mojo/edk/embedder/process_delegate.h index e5f90af..1357a762c 100644 --- a/mojo/edk/embedder/process_delegate.h +++ b/mojo/edk/embedder/process_delegate.h
@@ -14,8 +14,7 @@ // An interface for process delegates. class MOJO_SYSTEM_IMPL_EXPORT ProcessDelegate { public: - // Called when |ShutdownIPCSupport()| has "completed". Note that this is NOT - // called if |ShutdownIPCSupportOnIOThread()| is used instead. + // Called when |ShutdownIPCSupport()| has completed work on the I/O thread. virtual void OnShutdownComplete() = 0; protected:
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc index 20fa6693..11892be2 100644 --- a/mojo/edk/system/core.cc +++ b/mojo/edk/system/core.cc
@@ -69,6 +69,7 @@ } NodeController* Core::GetNodeController() { + base::AutoLock lock(node_controller_lock_); if (!node_controller_) node_controller_.reset(new NodeController(this)); return node_controller_.get();
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h index 0e08a8c..938726a 100644 --- a/mojo/edk/system/core.h +++ b/mojo/edk/system/core.h
@@ -219,6 +219,16 @@ static void PassNodeControllerToIOThread( scoped_ptr<NodeController> node_controller); + // Guards node_controller_. + // + // TODO(rockot): Consider removing this. It's only needed because we + // initialize node_controller_ lazily and that may happen on any thread. + // Otherwise it's effectively const and shouldn't need to be guarded. + // + // We can get rid of lazy initialization if we defer Mojo initialization far + // enough that zygotes don't do it. The zygote can't create a NodeController. + base::Lock node_controller_lock_; + // This is lazily initialized on first access. Always use GetNodeController() // to access it. scoped_ptr<NodeController> node_controller_;
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc index d26c912..c7b4a32 100644 --- a/mojo/edk/system/message_pipe_dispatcher.cc +++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -487,7 +487,7 @@ bool MessagePipeDispatcher::BeginTransit() { base::AutoLock lock(signal_lock_); - if (in_transit_) + if (in_transit_ || port_closed_) return false; in_transit_ = true; return in_transit_;
diff --git a/mojo/edk/system/node_controller.cc b/mojo/edk/system/node_controller.cc index ca568c37..4c211ac 100644 --- a/mojo/edk/system/node_controller.cc +++ b/mojo/edk/system/node_controller.cc
@@ -192,16 +192,16 @@ scoped_refptr<PlatformSharedBuffer> NodeController::CreateSharedBuffer( size_t num_bytes) { - scoped_refptr<PlatformSharedBuffer> buffer = - PlatformSharedBuffer::Create(num_bytes); #if defined(OS_POSIX) - if (!buffer && broker_) { - // On POSIX, creating a shared buffer in a sandboxed process will fail, so - // fall back to the broker if there is one. - buffer = broker_->GetSharedBuffer(num_bytes); + // Shared buffer creation failure is fatal, so always use the broker when we + // have one. This does mean that a non-root process that has children will use + // the broker for shared buffer creation even though that process is + // privileged. + if (broker_) { + return broker_->GetSharedBuffer(num_bytes); } #endif - return buffer; + return PlatformSharedBuffer::Create(num_bytes); } void NodeController::RequestShutdown(const base::Closure& callback) {
diff --git a/mojo/edk/system/ports/node.cc b/mojo/edk/system/ports/node.cc index 2ac4a20..f26e8316 100644 --- a/mojo/edk/system/ports/node.cc +++ b/mojo/edk/system/ports/node.cc
@@ -6,6 +6,7 @@ #include <string.h> +#include "base/debug/alias.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/synchronization/lock.h" @@ -954,9 +955,12 @@ { for (size_t i = 0; i < message->num_ports(); ++i) { - ports[i] = GetPort_Locked(message->ports()[i]); - ports[i]->lock.Acquire(); + PortName port_name = message->ports()[i]; + base::debug::Alias(&port_name); + ports[i] = GetPort_Locked(port_name); + CHECK(ports[i]); + ports[i]->lock.Acquire(); int error = OK; if (ports[i]->state != Port::kReceiving) error = ERROR_PORT_STATE_UNEXPECTED;
diff --git a/mojo/edk/system/remote_message_pipe_bootstrap.cc b/mojo/edk/system/remote_message_pipe_bootstrap.cc index 2342166..eb38674c 100644 --- a/mojo/edk/system/remote_message_pipe_bootstrap.cc +++ b/mojo/edk/system/remote_message_pipe_bootstrap.cc
@@ -32,6 +32,8 @@ NodeController* node_controller, ScopedPlatformHandle platform_handle, const ports::PortRef& port) { + CHECK(node_controller); + CHECK(node_controller->io_task_runner()); if (node_controller->io_task_runner()->RunsTasksOnCurrentThread()) { // Owns itself. new RemoteMessagePipeBootstrap(
diff --git a/mojo/edk/test/scoped_ipc_support.cc b/mojo/edk/test/scoped_ipc_support.cc index 68e7ac2fa..d7ff28b 100644 --- a/mojo/edk/test/scoped_ipc_support.cc +++ b/mojo/edk/test/scoped_ipc_support.cc
@@ -19,13 +19,8 @@ } ScopedIPCSupportHelper::~ScopedIPCSupportHelper() { - if (base::MessageLoop::current() && - base::MessageLoop::current()->task_runner() == io_thread_task_runner_) { - ShutdownIPCSupportOnIOThread(); - } else { - ShutdownIPCSupport(); - run_loop_.Run(); - } + ShutdownIPCSupport(); + run_loop_.Run(); } void ScopedIPCSupportHelper::Init(
diff --git a/mojo/edk/test/scoped_ipc_support.h b/mojo/edk/test/scoped_ipc_support.h index 132ed44..e920265 100644 --- a/mojo/edk/test/scoped_ipc_support.h +++ b/mojo/edk/test/scoped_ipc_support.h
@@ -40,8 +40,7 @@ } // namespace internal // A simple class that calls |InitIPCSupport()| on construction and -// |ShutdownIPCSupport()| on destruction (or |ShutdownIPCSupportOnIOThread()| -// if destroyed on the I/O thread). +// |ShutdownIPCSupport()| on destruction. class ScopedIPCSupport : public ProcessDelegate { public: explicit ScopedIPCSupport(
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.cc b/mojo/gpu/mojo_gles2_impl_autogen.cc index 3ee22aef..453f9e2 100644 --- a/mojo/gpu/mojo_gles2_impl_autogen.cc +++ b/mojo/gpu/mojo_gles2_impl_autogen.cc
@@ -576,6 +576,10 @@ MojoGLES2MakeCurrent(context_); return glGetString(name); } +const GLubyte* MojoGLES2Impl::GetStringi(GLenum name, GLuint index) { + NOTREACHED() << "Unimplemented GetStringi."; + return 0; +} void MojoGLES2Impl::GetSynciv(GLsync sync, GLenum pname, GLsizei bufsize,
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.h b/mojo/gpu/mojo_gles2_impl_autogen.h index 6b9a08f9..e3de37b 100644 --- a/mojo/gpu/mojo_gles2_impl_autogen.h +++ b/mojo/gpu/mojo_gles2_impl_autogen.h
@@ -294,6 +294,7 @@ GLsizei* length, char* source) override; const GLubyte* GetString(GLenum name) override; + const GLubyte* GetStringi(GLenum name, GLuint index) override; void GetSynciv(GLsync sync, GLenum pname, GLsizei bufsize,
diff --git a/mojo/message_pump/handle_watcher.cc b/mojo/message_pump/handle_watcher.cc index 97d6d88..7c01ead 100644 --- a/mojo/message_pump/handle_watcher.cc +++ b/mojo/message_pump/handle_watcher.cc
@@ -17,7 +17,6 @@ #include "base/memory/singleton.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" -#include "base/profiler/scoped_tracker.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "base/thread_task_runner_handle.h" @@ -255,11 +254,6 @@ } } - // TODO(amistry): Remove ScopedTracker below once http://crbug.com/554761 is - // fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION( - "554761 WatcherThreadManager::StopWatching")); RequestData request_data; request_data.type = REQUEST_STOP; request_data.stop_id = watcher_id;
diff --git a/mojo/mojo_platform_handle.gyp b/mojo/mojo_platform_handle.gyp new file mode 100644 index 0000000..247c699 --- /dev/null +++ b/mojo/mojo_platform_handle.gyp
@@ -0,0 +1,34 @@ +# 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. + +{ + 'variables': { + # This turns on e.g. the filename-based detection of which + # platforms to include source files on (e.g. files ending in + # _mac.h or _mac.cc are only compiled on MacOSX). + 'chromium_code': 1, + }, + 'targets': [ + { + # GN version: //mojo/platform_handle:platform_handle/platform_handle_impl + 'target_name': 'platform_handle', + 'type': 'static_library', + 'include_dirs': [ + '../..', + ], + 'defines': [ + 'PLATFORM_HANDLE_IMPLEMENTATION' + ], + 'sources': [ + 'platform_handle/platform_handle.h', + 'platform_handle/platform_handle_functions.h', + 'platform_handle/platform_handle_functions.cc', + ], + 'dependencies': [ + 'mojo_edk.gyp:mojo_system_impl', + 'mojo_public.gyp:mojo_cpp_bindings', + ] + } + ] +}
diff --git a/mojo/mojo_shell.gyp b/mojo/mojo_shell.gyp index 1172097..69074b448 100644 --- a/mojo/mojo_shell.gyp +++ b/mojo/mojo_shell.gyp
@@ -7,6 +7,8 @@ 'target_name': 'mojo_shell_lib', 'type': 'static_library', 'sources': [ + 'common/mojo_scheme_register.cc', + 'common/mojo_scheme_register.h', 'services/package_manager/loader.cc', 'services/package_manager/loader.h', 'services/package_manager/package_manager.cc', @@ -25,14 +27,6 @@ 'shell/identity.cc', 'shell/identity.h', 'shell/native_runner.h', - 'shell/shell_application_delegate.cc', - 'shell/shell_application_delegate.h', - 'shell/shell_application_loader.cc', - 'shell/shell_application_loader.h', - 'shell/shell_client_factory_connection.cc', - 'shell/shell_client_factory_connection.h', - 'shell/static_application_loader.cc', - 'shell/static_application_loader.h', 'shell/switches.cc', 'shell/switches.cc', 'util/filename_util.cc', @@ -52,8 +46,8 @@ 'target_name': 'mojo_shell_unittests', 'type': 'executable', 'sources': [ - 'shell/application_manager_unittest.cc', - 'shell/capability_filter_unittest.cc', + 'shell/tests/application_manager_unittest.cc', + 'shell/tests/capability_filter_unittest.cc', ], 'dependencies': [ '<(DEPTH)/mojo/mojo_shell.gyp:mojo_shell_lib', @@ -73,8 +67,8 @@ 'type': 'static_library', 'variables': { 'mojom_files': [ - 'shell/capability_filter_unittest.mojom', - 'shell/test.mojom', + 'shell/tests/capability_filter_unittest.mojom', + 'shell/tests/test.mojom', ], }, 'includes': [
diff --git a/mojo/services/network/BUILD.gn b/mojo/services/network/BUILD.gn index e5c3ef2..aad26845 100644 --- a/mojo/services/network/BUILD.gn +++ b/mojo/services/network/BUILD.gn
@@ -59,8 +59,6 @@ "http_connection_impl.h", "http_server_impl.cc", "http_server_impl.h", - "mojo_persistent_cookie_store.cc", - "mojo_persistent_cookie_store.h", "net_adapters.cc", "net_adapters.h", "net_address_type_converters.cc", @@ -92,7 +90,6 @@ deps = [ "//base", - "//components/filesystem/public/interfaces", "//mojo/common:common_base", "//mojo/common:url_type_converters", "//mojo/message_pump", @@ -104,7 +101,6 @@ "//net", "//net:extras", "//net:http_server", - "//sql/mojo", "//url", ] }
diff --git a/mojo/services/network/mojo_persistent_cookie_store.cc b/mojo/services/network/mojo_persistent_cookie_store.cc deleted file mode 100644 index ac37194..0000000 --- a/mojo/services/network/mojo_persistent_cookie_store.cc +++ /dev/null
@@ -1,100 +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. - -#include "mojo/services/network/mojo_persistent_cookie_store.h" - -#include "base/bind.h" -#include "base/synchronization/waitable_event.h" -#include "mojo/services/network/network_service_delegate.h" -#include "net/extras/sqlite/sqlite_persistent_cookie_store.h" - -namespace mojo { - -MojoPersistentCookieStore::MojoPersistentCookieStore( - NetworkServiceDelegate* network_service_delegate, - const base::FilePath& path, - const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, - const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, - bool restore_old_session_cookies, - net::CookieCryptoDelegate* crypto_delegate) - : network_service_delegate_(network_service_delegate) { - sql_cookie_store_ = new net::SQLitePersistentCookieStore( - path, - client_task_runner, - background_task_runner, - restore_old_session_cookies, - crypto_delegate); - network_service_delegate_->AddObserver(this); -} - -void MojoPersistentCookieStore::Load(const LoadedCallback& loaded_callback) { - if (sql_cookie_store_) - sql_cookie_store_->Load(loaded_callback); -} - -void MojoPersistentCookieStore::LoadCookiesForKey( - const std::string& key, - const LoadedCallback& callback) { - if (sql_cookie_store_) - sql_cookie_store_->LoadCookiesForKey(key, callback); -} - -void MojoPersistentCookieStore::AddCookie(const net::CanonicalCookie& cc) { - if (sql_cookie_store_) - sql_cookie_store_->AddCookie(cc); -} - -void MojoPersistentCookieStore::UpdateCookieAccessTime( - const net::CanonicalCookie& cc) { - if (sql_cookie_store_) - sql_cookie_store_->UpdateCookieAccessTime(cc); -} - -void MojoPersistentCookieStore::DeleteCookie(const net::CanonicalCookie& cc) { - if (sql_cookie_store_) - sql_cookie_store_->DeleteCookie(cc); -} - -void MojoPersistentCookieStore::SetForceKeepSessionState() { - if (sql_cookie_store_) - sql_cookie_store_->SetForceKeepSessionState(); -} - -void MojoPersistentCookieStore::Flush(const base::Closure& callback) { - if (sql_cookie_store_) - sql_cookie_store_->Flush(callback); -} - -MojoPersistentCookieStore::~MojoPersistentCookieStore() { - RemoveObserver(); -} - -void MojoPersistentCookieStore::RemoveObserver() { - if (network_service_delegate_) { - network_service_delegate_->RemoveObserver(this); - network_service_delegate_ = nullptr; - } -} - -namespace { - -void SignalComplete(base::WaitableEvent* event) { - event->Signal(); -} - -} // namespace - -void MojoPersistentCookieStore::OnIOWorkerThreadShutdown() { - // We need to shut down synchronously here. This will block our thread until - // the backend has shut down. - base::WaitableEvent done_event(false, false); - sql_cookie_store_->Close(base::Bind(&SignalComplete, &done_event)); - done_event.Wait(); - - sql_cookie_store_ = NULL; - - RemoveObserver(); -} - -} // namespace mojo
diff --git a/mojo/services/network/mojo_persistent_cookie_store.h b/mojo/services/network/mojo_persistent_cookie_store.h deleted file mode 100644 index 97e8b6d02..0000000 --- a/mojo/services/network/mojo_persistent_cookie_store.h +++ /dev/null
@@ -1,65 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SERVICES_NETWORK_MOJO_PERSISTENT_COOKIE_STORE_H_ -#define MOJO_SERVICES_NETWORK_MOJO_PERSISTENT_COOKIE_STORE_H_ - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/sequenced_task_runner.h" -#include "mojo/services/network/network_service_delegate_observer.h" -#include "net/cookies/cookie_monster.h" -#include "net/extras/sqlite/cookie_crypto_delegate.h" - -namespace net { -class SQLitePersistentCookieStore; -} // namespace net - -namespace mojo { -class NetworkServiceDelegate; - -// A PersistentCookieStore that listens to NetworkContext's and tries to -// gracefully shutdown when our Connection is about to be closed. -class MojoPersistentCookieStore - : public net::CookieMonster::PersistentCookieStore, - public NetworkServiceDelegateObserver { - public: - MojoPersistentCookieStore( - NetworkServiceDelegate* network_service_delegate, - const base::FilePath& path, - const scoped_refptr<base::SequencedTaskRunner>& client_task_runner, - const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, - bool restore_old_session_cookies, - net::CookieCryptoDelegate* crypto_delegate); - - // CookieMonster::PersistentCookieStore: - void Load(const LoadedCallback& loaded_callback) override; - void LoadCookiesForKey(const std::string& key, - const LoadedCallback& callback) override; - void AddCookie(const net::CanonicalCookie& cc) override; - void UpdateCookieAccessTime(const net::CanonicalCookie& cc) override; - void DeleteCookie(const net::CanonicalCookie& cc) override; - void SetForceKeepSessionState() override; - void Flush(const base::Closure& callback) override; - - // NetworkServiceDelegateObserver: - void OnIOWorkerThreadShutdown() override; - - private: - ~MojoPersistentCookieStore() override; - - void RemoveObserver(); - - NetworkServiceDelegate* network_service_delegate_; - - // We own the |sql_cookie_store_| that we proxy for. We delete this during - // OnIOWorkerThreadShutdown(). - scoped_refptr<net::SQLitePersistentCookieStore> sql_cookie_store_; - - DISALLOW_COPY_AND_ASSIGN(MojoPersistentCookieStore); -}; - -} // namespace mojo - -#endif // MOJO_SERVICES_NETWORK_MOJO_PERSISTENT_COOKIE_STORE_H_
diff --git a/mojo/services/network/network_context.cc b/mojo/services/network/network_context.cc index 6129ed4..2272238 100644 --- a/mojo/services/network/network_context.cc +++ b/mojo/services/network/network_context.cc
@@ -19,7 +19,6 @@ #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "mojo/common/user_agent.h" -#include "mojo/services/network/mojo_persistent_cookie_store.h" #include "mojo/services/network/url_loader_impl.h" #include "net/cookies/cookie_monster.h" #include "net/dns/host_resolver.h" @@ -110,10 +109,8 @@ NetworkContext::NetworkContext( const base::FilePath& base_path, - const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, NetworkServiceDelegate* delegate) - : NetworkContext(MakeURLRequestContext(base_path, background_task_runner, - delegate)) { + : NetworkContext(MakeURLRequestContext(base_path, delegate)) { } NetworkContext::~NetworkContext() { @@ -148,7 +145,6 @@ // static scoped_ptr<net::URLRequestContext> NetworkContext::MakeURLRequestContext( const base::FilePath& base_path, - const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, NetworkServiceDelegate* delegate) { net::URLRequestContextBuilder builder; net::URLRequestContextBuilder::HttpNetworkSessionParams params; @@ -197,23 +193,6 @@ builder.EnableHttpCache(cache_params); builder.set_file_enabled(true); - if (background_task_runner) { - // TODO(erg): This only gets run on non-android system. Currently, any - // attempts from the network_service trying to access the filesystem break - // the apptests on android. (And only the apptests on android. Mandoline - // shell works fine on android, as does apptests on desktop.) - MojoPersistentCookieStore* cookie_store = - new MojoPersistentCookieStore( - delegate, - base::FilePath(FILE_PATH_LITERAL("Cookies")), - base::MessageLoop::current()->task_runner(), - background_task_runner, - false, // TODO(erg): Make RESTORED_SESSION_COOKIES configurable. - nullptr); - builder.SetCookieAndChannelIdStores( - new net::CookieMonster(cookie_store, nullptr), nullptr); - } - return builder.Build(); }
diff --git a/mojo/services/network/network_context.h b/mojo/services/network/network_context.h index e01f0bef..36da6d9 100644 --- a/mojo/services/network/network_context.h +++ b/mojo/services/network/network_context.h
@@ -33,7 +33,6 @@ scoped_ptr<net::URLRequestContext> url_request_context); NetworkContext( const base::FilePath& base_path, - const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, NetworkServiceDelegate* delegate); ~NetworkContext(); @@ -52,7 +51,6 @@ static scoped_ptr<net::URLRequestContext> MakeURLRequestContext( const base::FilePath& base_path, - const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, NetworkServiceDelegate* delegate); class MojoNetLog;
diff --git a/mojo/services/network/network_service_delegate.cc b/mojo/services/network/network_service_delegate.cc index 84334fc..b1920f7 100644 --- a/mojo/services/network/network_service_delegate.cc +++ b/mojo/services/network/network_service_delegate.cc
@@ -22,55 +22,17 @@ #include "mojo/services/network/web_socket_factory_impl.h" #include "mojo/shell/public/cpp/connection.h" #include "mojo/util/capture_util.h" -#include "sql/mojo/mojo_vfs.h" namespace { -const char kSQLThreadName[] = "SQL_IO_Thread"; const char kUserDataDir[] = "user-data-dir"; -// SQL blocks on the filesystem service, so perform all SQL functions on a -// separate thread. -class SQLThread : public base::Thread { - public: - SQLThread(filesystem::DirectoryPtr directory) - : base::Thread(kSQLThreadName), - directory_info_(directory.PassInterface()) { - base::Thread::Options options; - options.message_pump_factory = - base::Bind(&mojo::common::MessagePumpMojo::Create); - StartWithOptions(options); - } - ~SQLThread() override { Stop(); } - - void Init() override { - filesystem::DirectoryPtr directory; - directory.Bind(std::move(directory_info_)); - vfs_.reset(new sql::ScopedMojoFilesystemVFS(std::move(directory))); - } - - void CleanUp() override { - vfs_.reset(); - } - - private: - // Our VFS which wraps sqlite so that we can reuse the current sqlite code. - scoped_ptr<sql::ScopedMojoFilesystemVFS> vfs_; - - // This member is used to safely pass data from one thread to another. It is - // set in the constructor and is consumed in Init(). - mojo::InterfacePtrInfo<filesystem::Directory> directory_info_; - - DISALLOW_COPY_AND_ASSIGN(SQLThread); -}; - } // namespace namespace mojo { NetworkServiceDelegate::NetworkServiceDelegate() - : shell_(nullptr), - binding_(this) { + : shell_(nullptr) { } NetworkServiceDelegate::~NetworkServiceDelegate() { @@ -90,24 +52,6 @@ uint32_t id) { shell_ = shell; -#if !defined(OS_ANDROID) - // TODO(erg): The following doesn't work when running the android - // apptests. It works in the mandoline shell (on desktop and on android), and - // in the apptests on desktop. However, on android, whenever we make the call - // to OpenFileSystem, the entire mojo system hangs to the point where writes - // to stderr that previously would have printed to our console aren't. The - // apptests are also fairly resistant to being run under gdb on android. - shell_->ConnectToInterface("mojo:filesystem", &files_); - - filesystem::FileError error = filesystem::FileError::FAILED; - filesystem::DirectoryPtr directory; - files_->OpenFileSystem("origin", GetProxy(&directory), - binding_.CreateInterfacePtrAndBind(), Capture(&error)); - files_.WaitForIncomingResponse(); - - io_worker_thread_.reset(new SQLThread(std::move(directory))); -#endif - // TODO(erg): Find everything else that writes to the filesystem and // transition it to proxying mojo:filesystem. We shouldn't have any path // calculation code here, but sadly need it until the transition is done. In @@ -123,11 +67,7 @@ base_path = base_path.Append(FILE_PATH_LITERAL("network_service")); } - scoped_refptr<base::SequencedTaskRunner> worker_thread; -#if !defined(OS_ANDROID) - worker_thread = io_worker_thread_->task_runner(); -#endif - context_.reset(new NetworkContext(base_path, worker_thread, this)); + context_.reset(new NetworkContext(base_path, this)); tracing_.Initialize(shell_, url); } @@ -141,13 +81,10 @@ } bool NetworkServiceDelegate::ShellConnectionLost() { - EnsureIOThreadShutdown(); return true; } void NetworkServiceDelegate::Quit() { - EnsureIOThreadShutdown(); - // Destroy the NetworkContext now as it requires MessageLoop::current() upon // destruction and it is the last moment we know for sure that it is // running. @@ -180,22 +117,4 @@ std::move(request)); } -void NetworkServiceDelegate::OnFileSystemShutdown() { - EnsureIOThreadShutdown(); -} - -void NetworkServiceDelegate::EnsureIOThreadShutdown() { - if (io_worker_thread_) { - // Broadcast to the entire system that we have to shut down anything - // depending on the worker thread. Either we're shutting down or the - // filesystem service is shutting down. - FOR_EACH_OBSERVER(NetworkServiceDelegateObserver, observers_, - OnIOWorkerThreadShutdown()); - - // Destroy the io worker thread here so that we can commit any pending - // cookies here. - io_worker_thread_.reset(); - } -} - } // namespace mojo
diff --git a/mojo/services/network/network_service_delegate.h b/mojo/services/network/network_service_delegate.h index 367eb9a..7890c22 100644 --- a/mojo/services/network/network_service_delegate.h +++ b/mojo/services/network/network_service_delegate.h
@@ -7,7 +7,6 @@ #include "base/observer_list.h" #include "base/threading/thread.h" -#include "components/filesystem/public/interfaces/file_system.mojom.h" #include "mojo/services/network/network_context.h" #include "mojo/services/network/public/interfaces/cookie_store.mojom.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h" @@ -18,10 +17,6 @@ #include "mojo/shell/public/cpp/shell.h" #include "mojo/shell/public/cpp/shell_client.h" -namespace sql { -class ScopedMojoFilesystemVFS; -} - namespace mojo { class NetworkServiceDelegateObserver; @@ -29,8 +24,7 @@ public InterfaceFactory<NetworkService>, public InterfaceFactory<CookieStore>, public InterfaceFactory<WebSocketFactory>, - public InterfaceFactory<URLLoaderFactory>, - public filesystem::FileSystemClient { + public InterfaceFactory<URLLoaderFactory> { public: NetworkServiceDelegate(); ~NetworkServiceDelegate() override; @@ -39,10 +33,6 @@ void RemoveObserver(NetworkServiceDelegateObserver* observer); private: - // Notifies all of our observers of a Shuts down our IO thread. Safe to call - // multiple times. - void EnsureIOThreadShutdown(); - // mojo::ShellClient implementation. void Initialize(Shell* shell, const std::string& url, uint32_t id) override; bool AcceptConnection(Connection* connection) override; @@ -65,9 +55,6 @@ void Create(Connection* connection, InterfaceRequest<URLLoaderFactory> request) override; - // Overridden from FileSystemClient: - void OnFileSystemShutdown() override; - private: Shell* shell_; mojo::TracingImpl tracing_; @@ -75,15 +62,6 @@ // Observers that want notifications that our worker thread is going away. base::ObserverList<NetworkServiceDelegateObserver> observers_; - Binding<filesystem::FileSystemClient> binding_; - - // A worker thread that blocks for file IO. - scoped_ptr<base::Thread> io_worker_thread_; - - // Our connection to the filesystem service, which stores our cookies and - // other data. - filesystem::FileSystemPtr files_; - scoped_ptr<NetworkContext> context_; };
diff --git a/mojo/services/package_manager/BUILD.gn b/mojo/services/package_manager/BUILD.gn index c9ae84e..d88f1b6 100644 --- a/mojo/services/package_manager/BUILD.gn +++ b/mojo/services/package_manager/BUILD.gn
@@ -36,6 +36,7 @@ deps = [ "//base", + "//mojo/common:mojo_scheme_register", "//mojo/common:url_type_converters", "//mojo/services/package_manager/public/interfaces", "//mojo/shell/public/cpp",
diff --git a/mojo/services/package_manager/package_manager.cc b/mojo/services/package_manager/package_manager.cc index 744ec0d..21f032e 100644 --- a/mojo/services/package_manager/package_manager.cc +++ b/mojo/services/package_manager/package_manager.cc
@@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/json/json_file_value_serializer.h" #include "base/task_runner_util.h" +#include "mojo/common/mojo_scheme_register.h" #include "mojo/common/url_type_converters.h" #include "mojo/util/filename_util.h" #include "net/base/filename_util.h" @@ -60,8 +61,17 @@ (*value)->Set("capabilities", make_scoped_ptr(capabilities)); } +scoped_ptr<base::Value> ReadManifest(const base::FilePath& manifest_path) { + JSONFileValueDeserializer deserializer(manifest_path); + int error = 0; + std::string message; + // TODO(beng): probably want to do more detailed error checking. This should + // be done when figuring out if to unblock connection completion. + return deserializer.Deserialize(&error, &message); } +} // namespace + ApplicationInfo::ApplicationInfo() {} ApplicationInfo::~ApplicationInfo() {} @@ -69,11 +79,11 @@ PackageManager::PackageManager(base::TaskRunner* blocking_pool, bool register_schemes) - : blocking_pool_(blocking_pool), catalog_store_(nullptr) { - if (register_schemes) { - url::AddStandardScheme("mojo", url::SCHEME_WITHOUT_AUTHORITY); - url::AddStandardScheme("exe", url::SCHEME_WITHOUT_AUTHORITY); - } + : blocking_pool_(blocking_pool), + catalog_store_(nullptr), + weak_factory_(this) { + if (register_schemes) + mojo::RegisterMojoSchemes(); base::FilePath shell_dir; PathService::Get(base::DIR_MODULE, &shell_dir); @@ -130,17 +140,19 @@ void PackageManager::ResolveMojoURL(const mojo::String& mojo_url, const ResolveMojoURLCallback& callback) { GURL resolved_url = mojo_url.To<GURL>(); - CHECK(resolved_url.SchemeIs("mojo") || resolved_url.SchemeIs("exe")); - auto alias_iter = mojo_url_aliases_.find(mojo_url); - if (alias_iter != mojo_url_aliases_.end()) + std::string qualifier; + if (alias_iter != mojo_url_aliases_.end()) { resolved_url = GURL(alias_iter->second.first); + qualifier = alias_iter->second.second; + } - EnsureURLInCatalog(resolved_url, callback); + EnsureURLInCatalog(resolved_url, qualifier, callback); } void PackageManager::CompleteResolveMojoURL( const GURL& resolved_url, + const std::string& qualifier, const ResolveMojoURLCallback& callback) { auto info_iter = catalog_.find(resolved_url.spec()); CHECK(info_iter != catalog_.end()); @@ -166,8 +178,8 @@ all_interfaces.push_back("*"); filter->filter.insert("*", std::move(all_interfaces)); - callback.Run(resolved_url.spec(), file_url.spec(), info_iter->second.name, - std::move(filter)); + callback.Run(resolved_url.spec(), qualifier, file_url.spec(), + info_iter->second.name, std::move(filter)); } bool PackageManager::IsURLInCatalog(const GURL& url) const { @@ -176,23 +188,29 @@ void PackageManager::EnsureURLInCatalog( const GURL& url, + const std::string& qualifier, const ResolveMojoURLCallback& callback) { if (IsURLInCatalog(url)) { - CompleteResolveMojoURL(url, callback); + CompleteResolveMojoURL(url, qualifier, callback); return; } GURL manifest_url = GetManifestURL(url); - if (manifest_url.is_empty()) + if (manifest_url.is_empty()) { + // The URL is of some form that can't be resolved to a manifest (e.g. some + // scheme used for tests). Just pass it back to the caller so it can be + // loaded with a custom loader. + callback.Run(url.spec(), nullptr, nullptr, nullptr, nullptr); return; + } + + CHECK(url.SchemeIs("mojo") || url.SchemeIs("exe")); base::FilePath manifest_path; CHECK(net::FileURLToFilePath(manifest_url, &manifest_path)); base::PostTaskAndReplyWithResult( - blocking_pool_, FROM_HERE, - base::Bind(&PackageManager::ReadManifest, base::Unretained(this), - manifest_path), - base::Bind(&PackageManager::OnReadManifest, - base::Unretained(this), url, callback)); + blocking_pool_, FROM_HERE, base::Bind(&ReadManifest, manifest_path), + base::Bind(&PackageManager::OnReadManifest, weak_factory_.GetWeakPtr(), + url, qualifier, callback)); } void PackageManager::DeserializeCatalog() { @@ -255,19 +273,25 @@ return GURL(); } -scoped_ptr<base::Value> PackageManager::ReadManifest( - const base::FilePath& manifest_path) { - JSONFileValueDeserializer deserializer(manifest_path); - int error = 0; - std::string message; - // TODO(beng): probably want to do more detailed error checking. This should - // be done when figuring out if to unblock connection completion. - return deserializer.Deserialize(&error, &message); -} - -void PackageManager::OnReadManifest(const GURL& url, +// static +void PackageManager::OnReadManifest(base::WeakPtr<PackageManager> pm, + const GURL& url, + const std::string& qualifier, const ResolveMojoURLCallback& callback, scoped_ptr<base::Value> manifest) { + if (!pm) { + // The PackageManager was destroyed, we're likely in shutdown. Run the + // callback so we don't trigger a DCHECK. + callback.Run(url.spec(), nullptr, nullptr, nullptr, nullptr); + return; + } + pm->OnReadManifestImpl(url, qualifier, callback, std::move(manifest)); +} + +void PackageManager::OnReadManifestImpl(const GURL& url, + const std::string& qualifier, + const ResolveMojoURLCallback& callback, + scoped_ptr<base::Value> manifest) { if (manifest) { base::DictionaryValue* dictionary = nullptr; CHECK(manifest->GetAsDictionary(&dictionary)); @@ -279,7 +303,7 @@ catalog_[info.url] = info; } SerializeCatalog(); - CompleteResolveMojoURL(url, callback); + CompleteResolveMojoURL(url, qualifier, callback); } } // namespace package_manager
diff --git a/mojo/services/package_manager/package_manager.h b/mojo/services/package_manager/package_manager.h index ec6ca02a..e267598 100644 --- a/mojo/services/package_manager/package_manager.h +++ b/mojo/services/package_manager/package_manager.h
@@ -6,6 +6,7 @@ #define MOJO_SERVICES_PACKAGE_MANAGER_PACKAGE_MANAGER_H_ #include "base/files/file_path.h" +#include "base/memory/weak_ptr.h" #include "base/path_service.h" #include "base/values.h" #include "mojo/public/cpp/bindings/weak_binding_set.h" @@ -96,6 +97,7 @@ // Completes resolving a Mojo URL from the Shell after the resolved URL has // been added to the catalog and the manifest read. void CompleteResolveMojoURL(const GURL& resolved_url, + const std::string& qualifier, const ResolveMojoURLCallback& callback); bool IsURLInCatalog(const GURL& url) const; @@ -103,6 +105,7 @@ // Called from ResolveMojoURL(). // If |url| is not in the catalog, attempts to load a manifest for it. void EnsureURLInCatalog(const GURL& url, + const std::string& qualifier, const ResolveMojoURLCallback& callback); // Populate/serialize the catalog from/to the supplied store. @@ -115,12 +118,19 @@ GURL GetManifestURL(const GURL& url); - // Reads a manifest in the blocking pool and returns a base::Value with its - // contents via OnReadManifest(). - scoped_ptr<base::Value> ReadManifest(const base::FilePath& manifest_path); - void OnReadManifest(const GURL& url, - const ResolveMojoURLCallback& callback, - scoped_ptr<base::Value> manifest); + // Called once the manifest has been read. |pm| may be null at this point, + // but |callback| must be run. + static void OnReadManifest(base::WeakPtr<PackageManager> pm, + const GURL& url, + const std::string& qualifier, + const ResolveMojoURLCallback& callback, + scoped_ptr<base::Value> manifest); + + // Called once the manifest is read and |this| hasn't been deleted. + void OnReadManifestImpl(const GURL& url, + const std::string& qualifier, + const ResolveMojoURLCallback& callback, + scoped_ptr<base::Value> manifest); base::TaskRunner* blocking_pool_; GURL system_package_dir_; @@ -131,8 +141,13 @@ ApplicationCatalogStore* catalog_store_; std::map<std::string, ApplicationInfo> catalog_; + // Used when an app handles multiple urls. Maps from app (as url) to url of + // app that is responsible for handling it. The value is a pair of the + // url of the handler along with a qualifier. MojoURLAliasMap mojo_url_aliases_; + base::WeakPtrFactory<PackageManager> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(PackageManager); };
diff --git a/mojo/services/package_manager/public/interfaces/shell_resolver.mojom b/mojo/services/package_manager/public/interfaces/shell_resolver.mojom index eed2360..c4a1b30 100644 --- a/mojo/services/package_manager/public/interfaces/shell_resolver.mojom +++ b/mojo/services/package_manager/public/interfaces/shell_resolver.mojom
@@ -19,9 +19,14 @@ // loaded from |resolved_mojo_url|'s manifest. // |filter| - the base CapabilityFilter within which an instance of // |resolved_mojo_url| must be run for |mojo_url|. - // TODO(beng): return qualifier too! - ResolveMojoURL(string mojo_url) => (string resolved_mojo_url, - string mojo_file_url, - string application_name, - mojo.shell.mojom.CapabilityFilter filter); + // If |mojo_url| can't be resolved (i.e. not a mojo: or exe: scheme), then + // the callback will be run with null |mojo_file_url|, |application_name| and + // |filter|. + // TODO(beng): return qualifier too! + ResolveMojoURL(string mojo_url) => + (string resolved_mojo_url, + string? qualifier, + string? mojo_file_url, + string? application_name, + mojo.shell.mojom.CapabilityFilter? filter); };
diff --git a/mojo/shell/BUILD.gn b/mojo/shell/BUILD.gn index b2c93d4..892029d 100644 --- a/mojo/shell/BUILD.gn +++ b/mojo/shell/BUILD.gn
@@ -2,8 +2,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//mojo/public/mojo_application.gni") -import("//mojo/public/mojo_application_manifest.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") @@ -11,7 +9,11 @@ testonly = true deps = [ ":shell", + "//mojo/shell/background", + "//mojo/shell/background", "//mojo/shell/runner", + "//mojo/shell/standalone", + "//mojo/shell/tests", ] } @@ -32,14 +34,6 @@ "identity.cc", "identity.h", "native_runner.h", - "shell_application_delegate.cc", - "shell_application_delegate.h", - "shell_application_loader.cc", - "shell_application_loader.h", - "shell_client_factory_connection.cc", - "shell_client_factory_connection.h", - "static_application_loader.cc", - "static_application_loader.h", "switches.cc", "switches.h", ] @@ -68,173 +62,3 @@ # For mojo/shell/application_loader.h allow_circular_includes_from = [ "//mojo/services/package_manager:lib" ] } - -source_set("test_support") { - testonly = true - sources = [ - "capability_filter_test.cc", - "capability_filter_test.h", - ] - - deps = [ - ":shell", - ":test_bindings", - "//mojo/shell/public/cpp", - "//mojo/shell/public/interfaces", - "//testing/gtest", - ] -} - -# TODO(beng): this target should just be called "unittests" but I am having -# difficulty with the android _apk generator. -test("mojo_shell_unittests") { - sources = [ - "application_manager_unittest.cc", - "capability_filter_unittest.cc", - ] - - deps = [ - ":shell", - ":test_bindings", - ":test_support", - "//base", - "//mojo/edk/system:test_utils", - "//mojo/edk/test:run_all_unittests", - "//mojo/public/cpp/system", - "//mojo/shell/public/cpp", - "//mojo/util:filename_util", - "//testing/gtest", - "//url", - ] -} - -mojom("test_bindings") { - sources = [ - "application_manager_apptests.mojom", - "capability_filter_unittest.mojom", - "package_test.mojom", - "test.mojom", - ] -} - -mojo_native_application("apptests") { - output_name = "mojo_shell_apptests" - testonly = true - - sources = [ - "application_manager_apptest.cc", - "package_apptest.cc", - ] - - deps = [ - ":apptests_manifest", - ":test_bindings", - "//base", - "//base/test:test_config", - "//mojo/common:common_base", - "//mojo/converters/network", - "//mojo/shell/public/cpp:sources", - "//mojo/shell/public/cpp:test_support", - "//mojo/shell/public/interfaces", - ] - - data_deps = [ - ":application_manager_apptest_driver", - ":application_manager_apptest_target", - ":package_test_package", - ] -} - -mojo_application_manifest("apptests_manifest") { - application_name = "mojo_shell_apptests" - source = "application_manager_apptest_manifest.json" -} - -mojo_application_manifest("package_test_a_manifest") { - application_name = "package_test_a" - source = "package_test_app_a_manifest.json" -} - -mojo_application_manifest("package_test_b_manifest") { - application_name = "package_test_b" - source = "package_test_app_b_manifest.json" -} - -mojo_native_application("package_test_package") { - testonly = true - sources = [ - "package_test_package.cc", - ] - deps = [ - ":package_test_package_manifest", - ":test_bindings", - "//base", - "//mojo/common:common_base", - "//mojo/shell/public/cpp:sources", - "//mojo/shell/public/interfaces", - ] -} - -mojo_application_manifest("package_test_package_manifest") { - application_name = "package_test_package" - source = "package_test_package_manifest.json" - deps = [ - ":package_test_a_manifest", - ":package_test_b_manifest", - ] - packaged_applications = [ - "package_test_a", - "package_test_b", - ] -} - -executable("application_manager_apptest_driver") { - testonly = true - - sources = [ - "application_manager_apptest_driver.cc", - ] - - deps = [ - ":copy_application_manager_apptest_driver_manifest", - ":test_bindings", - "//base", - "//base:base_static", - "//build/config/sanitizers:deps", - "//mojo/common:common_base", - "//mojo/converters/network", - "//mojo/edk/system", - "//mojo/shell/public/cpp", - "//mojo/shell/public/interfaces", - "//mojo/shell/runner:init", - "//mojo/shell/runner/child:test_native_main", - "//mojo/shell/runner/common", - ] -} - -copy("copy_application_manager_apptest_driver_manifest") { - sources = [ - "application_manager_apptest_driver_manifest.json", - ] - outputs = [ - "${root_out_dir}/{{source_file_part}}", - ] -} - -executable("application_manager_apptest_target") { - testonly = true - - sources = [ - "application_manager_apptest_target.cc", - ] - - deps = [ - ":test_bindings", - "//base", - "//build/config/sanitizers:deps", - "//mojo/common:common_base", - "//mojo/converters/network", - "//mojo/shell/public/cpp", - "//mojo/shell/runner/child:test_native_main", - ] -}
diff --git a/mojo/shell/application_instance.cc b/mojo/shell/application_instance.cc index 0d5ccb8..cd2bc5ea 100644 --- a/mojo/shell/application_instance.cc +++ b/mojo/shell/application_instance.cc
@@ -22,8 +22,6 @@ mojom::ShellClientPtr shell_client, ApplicationManager* manager, const Identity& identity, - uint32_t requesting_shell_client_factory_id, - const mojom::Shell::ConnectToApplicationCallback& connect_callback, const base::Closure& on_application_end, const String& application_name) : manager_(manager), @@ -31,8 +29,6 @@ identity_(identity), allow_any_application_(identity.filter().size() == 1 && identity.filter().count("*") == 1), - requesting_shell_client_factory_id_(requesting_shell_client_factory_id), - connect_callback_(connect_callback), on_application_end_(on_application_end), shell_client_(std::move(shell_client)), binding_(this), @@ -45,10 +41,8 @@ } ApplicationInstance::~ApplicationInstance() { - for (auto request : queued_client_requests_) { - request->connect_callback().Run(kInvalidApplicationID, - kInvalidApplicationID); - } + for (auto request : queued_client_requests_) + request->connect_callback().Run(kInvalidApplicationID); STLDeleteElements(&queued_client_requests_); } @@ -77,11 +71,6 @@ pid_receiver_binding_.Bind(std::move(pid_receiver)); } -void ApplicationInstance::RunConnectCallback() { - if (!connect_callback_.is_null()) - connect_callback_.Run(id_, requesting_shell_client_factory_id_); -} - // Shell implementation: void ApplicationInstance::ConnectToApplication( URLRequestPtr app_request, @@ -93,7 +82,7 @@ GURL url(url_string); if (!url.is_valid()) { LOG(ERROR) << "Error: invalid URL: " << url_string; - callback.Run(kInvalidApplicationID, kInvalidApplicationID); + callback.Run(kInvalidApplicationID); return; } if (allow_any_application_ || @@ -106,9 +95,7 @@ new ConnectToApplicationParams); params->SetSource(this); GURL app_url(app_request->url.get()); - params->SetTargetURLRequest( - std::move(app_request), - Identity(app_url, std::string(), capability_filter)); + params->set_target(Identity(app_url, std::string(), capability_filter)); params->set_remote_interfaces(std::move(remote_interfaces)); params->set_local_interfaces(std::move(local_interfaces)); params->set_connect_callback(callback); @@ -116,7 +103,7 @@ } else { LOG(WARNING) << "CapabilityFilter prevented connection from: " << identity_.url() << " to: " << url.spec(); - callback.Run(kInvalidApplicationID, kInvalidApplicationID); + callback.Run(kInvalidApplicationID); } } @@ -141,7 +128,7 @@ void ApplicationInstance::CallAcceptConnection( scoped_ptr<ConnectToApplicationParams> params) { - params->connect_callback().Run(id_, requesting_shell_client_factory_id_); + params->connect_callback().Run(id_); AllowedInterfaces interfaces; interfaces.insert("*"); if (!params->source().is_null()) @@ -165,32 +152,8 @@ // If any queued requests came to shell during time it was shutting down, // start them now. - for (auto request : queued_client_requests) { - // Unfortunately, it is possible that |request->target_url_request()| is - // null at this point. Consider the following sequence: - // 1) connect_request_1 arrives at the application manager; the manager - // decides to fetch the app. - // 2) connect_request_2 arrives for the same app; because the app is not - // running yet, the manager decides to fetch the app again. - // 3) The fetch for step (1) completes and an application instance app_a is - // registered. - // 4) app_a goes into two-phase shutdown. - // 5) The fetch for step (2) completes; the manager finds that there is a - // running app already, so it connects to app_a. - // 6) connect_request_2 is queued (and eventually gets here), but its - // original_request field was already lost to NetworkFetcher at step (2). - // - // TODO(yzshen): It seems we should register a pending application instance - // before starting the fetch. So at step (2) the application manager knows - // that it can wait for the first fetch to complete instead of doing a - // second one directly. - if (!request->target_url_request()) { - URLRequestPtr url_request = mojo::URLRequest::New(); - url_request->url = request->target().url().spec(); - request->SetTargetURLRequest(std::move(url_request), request->target()); - } + for (auto request : queued_client_requests) manager->ConnectToApplication(make_scoped_ptr(request)); - } } void ApplicationInstance::OnQuitRequestedResult(bool can_quit) {
diff --git a/mojo/shell/application_instance.h b/mojo/shell/application_instance.h index 6c761393..4e97173 100644 --- a/mojo/shell/application_instance.h +++ b/mojo/shell/application_instance.h
@@ -33,15 +33,10 @@ class ApplicationInstance : public mojom::Shell, public mojom::PIDReceiver { public: - // |requesting_shell_client_factory_id| is the id of the factory that - // loaded this app. If the app was not loaded by a factory the id - // is kInvalidApplicationID. ApplicationInstance( mojom::ShellClientPtr shell_client, ApplicationManager* manager, const Identity& identity, - uint32_t requesting_shell_client_factory_id, - const mojom::Shell::ConnectToApplicationCallback& connect_callback, const base::Closure& on_application_end, const String& application_name); @@ -56,20 +51,12 @@ void BindPIDReceiver(InterfaceRequest<mojom::PIDReceiver> pid_receiver); - void RunConnectCallback(); - mojom::ShellClient* shell_client() { return shell_client_.get(); } const Identity& identity() const { return identity_; } uint32_t id() const { return id_; } base::ProcessId pid() const { return pid_; } void set_pid(base::ProcessId pid) { pid_ = pid; } base::Closure on_application_end() const { return on_application_end_; } - void set_requesting_shell_client_factory_id(uint32_t id) { - requesting_shell_client_factory_id_ = id; - } - uint32_t requesting_shell_client_factory_id() const { - return requesting_shell_client_factory_id_; - } const String& application_name() const { return application_name_; } private: @@ -102,8 +89,6 @@ const uint32_t id_; const Identity identity_; const bool allow_any_application_; - uint32_t requesting_shell_client_factory_id_; - mojom::Shell::ConnectToApplicationCallback connect_callback_; base::Closure on_application_end_; mojom::ShellClientPtr shell_client_; Binding<mojom::Shell> binding_;
diff --git a/mojo/shell/application_manager.cc b/mojo/shell/application_manager.cc index 3e0664d..8d01333 100644 --- a/mojo/shell/application_manager.cc +++ b/mojo/shell/application_manager.cc
@@ -22,8 +22,7 @@ #include "mojo/shell/application_instance.h" #include "mojo/shell/connect_util.h" #include "mojo/shell/public/cpp/connect.h" -#include "mojo/shell/shell_application_loader.h" -#include "mojo/shell/shell_client_factory_connection.h" +#include "mojo/shell/public/cpp/shell_connection.h" #include "mojo/shell/switches.h" #include "mojo/util/filename_util.h" @@ -32,15 +31,33 @@ namespace { -// Used by TestAPI. -bool has_created_instance = false; +void OnEmptyOnConnectCallback(uint32_t remote_id) {} -void OnEmptyOnConnectCallback(uint32_t remote_id, - uint32_t shell_client_factory_id) { -} +class ShellApplicationLoader : public ApplicationLoader { + public: + explicit ShellApplicationLoader(ApplicationManager* manager) + : manager_(manager) {} + ~ShellApplicationLoader() override {} + + private: + // Overridden from ApplicationLoader: + void Load(const GURL& url, mojom::ShellClientRequest request) override { + DCHECK(request.is_pending()); + shell_connection_.reset(new ShellConnection(manager_, std::move(request))); + } + + ApplicationManager* manager_; + scoped_ptr<ShellConnection> shell_connection_; + + DISALLOW_COPY_AND_ASSIGN(ShellApplicationLoader); +}; } // namespace +mojom::Shell::ConnectToApplicationCallback EmptyConnectCallback() { + return base::Bind(&OnEmptyOnConnectCallback); +} + // static ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager) : manager_(manager) { @@ -49,16 +66,15 @@ ApplicationManager::TestAPI::~TestAPI() { } -bool ApplicationManager::TestAPI::HasCreatedInstance() { - return has_created_instance; -} - bool ApplicationManager::TestAPI::HasRunningInstanceForURL( const GURL& url) const { return manager_->identity_to_instance_.find(Identity(url)) != manager_->identity_to_instance_.end(); } +//////////////////////////////////////////////////////////////////////////////// +// ApplicationManager, public: + ApplicationManager::ApplicationManager( bool register_mojo_url_schemes) : ApplicationManager(nullptr, nullptr, register_mojo_url_schemes) {} @@ -73,11 +89,7 @@ SetLoaderForURL(make_scoped_ptr(new ShellApplicationLoader(this)), GURL("mojo://shell/")); - GURL package_manager_url("mojo://package_manager/"); - SetLoaderForURL(make_scoped_ptr(new package_manager::Loader( - task_runner_, register_mojo_url_schemes)), package_manager_url); - - ConnectToInterface(this, package_manager_url, &shell_resolver_); + InitPackageManager(register_mojo_url_schemes); } ApplicationManager::~ApplicationManager() { @@ -87,10 +99,6 @@ runner.reset(); } -void ApplicationManager::TerminateShellConnections() { - STLDeleteValues(&identity_to_instance_); -} - void ApplicationManager::ConnectToApplication( scoped_ptr<ConnectToApplicationParams> params) { TRACE_EVENT_INSTANT1("mojo_shell", "ApplicationManager::ConnectToApplication", @@ -102,20 +110,6 @@ if (ConnectToRunningApplication(¶ms)) return; - // TODO(beng): seems like this should be able to move to OnGotResolvedURL(). - ApplicationLoader* loader = GetLoaderForURL(params->target().url()); - if (loader) { - GURL url = params->target().url(); - mojom::ShellClientRequest request; - // TODO(beng): move this to OnGotResolvedURL & read from manifest. - std::string application_name = url.spec(); - ApplicationInstance* instance = CreateAndConnectToInstance( - std::move(params), nullptr, nullptr, application_name, &request); - loader->Load(url, std::move(request)); - instance->RunConnectCallback(); - return; - } - std::string url = params->target().url().spec(); shell_resolver_->ResolveMojoURL( url, @@ -123,228 +117,6 @@ weak_ptr_factory_.GetWeakPtr(), base::Passed(¶ms))); } -bool ApplicationManager::ConnectToRunningApplication( - scoped_ptr<ConnectToApplicationParams>* params) { - ApplicationInstance* instance = GetApplicationInstance((*params)->target()); - if (!instance) - return false; - - // TODO(beng): CHECK() that the target URL is already in the application - // catalog. - instance->ConnectToClient(std::move(*params)); - return true; -} - -ApplicationInstance* ApplicationManager::GetApplicationInstance( - const Identity& identity) const { - const auto& it = identity_to_instance_.find(identity); - return it != identity_to_instance_.end() ? it->second : nullptr; -} - -void ApplicationManager::CreateInstanceForHandle( - ScopedHandle channel, - const GURL& url, - mojom::CapabilityFilterPtr filter, - InterfaceRequest<mojom::PIDReceiver> pid_receiver) { - // We don't call ConnectToClient() here since the instance was created - // manually by other code, not in response to a Connect() request. The newly - // created instance is identified by |url| and may be subsequently reached by - // client code using this identity. - CapabilityFilter local_filter = filter->filter.To<CapabilityFilter>(); - Identity target_id(url, std::string(), local_filter); - mojom::ShellClientRequest request; - // TODO(beng): do better than url.spec() for application name. - ApplicationInstance* instance = CreateInstance( - target_id, EmptyConnectCallback(), base::Closure(), - url.spec(), &request); - instance->BindPIDReceiver(std::move(pid_receiver)); - scoped_ptr<NativeRunner> runner = - native_runner_factory_->Create(base::FilePath()); - runner->InitHost(std::move(channel), std::move(request)); - instance->SetNativeRunner(runner.get()); - native_runners_.push_back(std::move(runner)); -} - -void ApplicationManager::AddListener( - mojom::ApplicationManagerListenerPtr listener) { - Array<mojom::ApplicationInfoPtr> applications; - for (auto& entry : identity_to_instance_) - applications.push_back(CreateApplicationInfoForInstance(entry.second)); - listener->SetRunningApplications(std::move(applications)); - - listeners_.AddInterfacePtr(std::move(listener)); -} - -void ApplicationManager::ApplicationPIDAvailable( - uint32_t id, - base::ProcessId pid) { - for (auto& instance : identity_to_instance_) { - if (instance.second->id() == id) { - instance.second->set_pid(pid); - break; - } - } - listeners_.ForAllPtrs( - [this, id, pid](mojom::ApplicationManagerListener* listener) { - listener->ApplicationPIDAvailable(id, pid); - }); -} - -ApplicationInstance* ApplicationManager::CreateAndConnectToInstance( - scoped_ptr<ConnectToApplicationParams> params, - Identity* source, - Identity* target, - const std::string& application_name, - mojom::ShellClientRequest* request) { - if (source) - *source = params->source(); - if (target) - *target = params->target(); - ApplicationInstance* instance = CreateInstance( - params->target(), params->connect_callback(), - params->on_application_end(), - application_name, - request); - params->set_connect_callback(EmptyConnectCallback()); - instance->ConnectToClient(std::move(params)); - return instance; -} - -ApplicationInstance* ApplicationManager::CreateInstance( - const Identity& target_id, - const mojom::Shell::ConnectToApplicationCallback& connect_callback, - const base::Closure& on_application_end, - const String& application_name, - mojom::ShellClientRequest* request) { - mojom::ShellClientPtr shell_client; - *request = GetProxy(&shell_client); - ApplicationInstance* instance = new ApplicationInstance( - std::move(shell_client), this, target_id, - mojom::Shell::kInvalidApplicationID, connect_callback,on_application_end, - application_name); - DCHECK(identity_to_instance_.find(target_id) == - identity_to_instance_.end()); - identity_to_instance_[target_id] = instance; - mojom::ApplicationInfoPtr application_info = - CreateApplicationInfoForInstance(instance); - listeners_.ForAllPtrs( - [this, &application_info](mojom::ApplicationManagerListener* listener) { - listener->ApplicationInstanceCreated(application_info.Clone()); - }); - instance->InitializeApplication(); - return instance; -} - -uint32_t ApplicationManager::StartShellClientFactory( - const Identity& source, - const Identity& shell_client_factory, - const GURL& url, - mojom::ShellClientRequest request) { - ShellClientFactoryConnection* connection = - GetShellClientFactory(shell_client_factory, source); - connection->CreateShellClient(std::move(request), url); - return connection->id(); -} - -ShellClientFactoryConnection* ApplicationManager::GetShellClientFactory( - const Identity& shell_client_factory_identity, - const Identity& source_identity) { - auto it = identity_to_shell_client_factory_.find( - shell_client_factory_identity); - if (it != identity_to_shell_client_factory_.end()) - return it->second; - - ShellClientFactoryConnection* connection = new ShellClientFactoryConnection( - this, source_identity, - shell_client_factory_identity, - ++shell_client_factory_id_counter_, - base::Bind(&ApplicationManager::OnShellClientFactoryConnectionClosed, - weak_ptr_factory_.GetWeakPtr())); - identity_to_shell_client_factory_[shell_client_factory_identity] = connection; - return connection; -} - -void ApplicationManager::OnShellClientFactoryConnectionClosed( - ShellClientFactoryConnection* connection) { - // Remove the mapping. - auto it = identity_to_shell_client_factory_.find(connection->identity()); - DCHECK(it != identity_to_shell_client_factory_.end()); - identity_to_shell_client_factory_.erase(it); -} - -void ApplicationManager::OnGotResolvedURL( - scoped_ptr<ConnectToApplicationParams> params, - const String& resolved_url, - const String& file_url, - const String& application_name, - mojom::CapabilityFilterPtr base_filter) { - // It's possible that when this manifest request was issued, another one was - // already in-progress and completed by the time this one did, and so the - // requested application may already be running. - if (ConnectToRunningApplication(¶ms)) - return; - - GURL resolved_gurl = resolved_url.To<GURL>(); - if (params->target().url().spec() != resolved_url) { - CapabilityFilter capability_filter = GetPermissiveCapabilityFilter(); - if (!base_filter.is_null()) - capability_filter = base_filter->filter.To<CapabilityFilter>(); - - // TODO(beng): For now, we just use the legacy PackageManagerImpl to manage - // the ShellClientFactory connection. Once we get rid of the - // non-remote package manager path we will have to fold this in - // here. - Identity source, target; - mojom::ShellClientRequest request; - ApplicationInstance* instance = CreateAndConnectToInstance( - std::move(params), &source, &target, application_name, &request); - - uint32_t shell_client_factory_id = StartShellClientFactory( - source, Identity(resolved_gurl, target.qualifier(), capability_filter), - target.url(), std::move(request)); - CHECK(shell_client_factory_id != mojom::Shell::kInvalidApplicationID); - instance->set_requesting_shell_client_factory_id(shell_client_factory_id); - instance->RunConnectCallback(); - return; - } - CreateAndRunLocalApplication(std::move(params), application_name, - file_url.To<GURL>()); -} - -void ApplicationManager::CreateAndRunLocalApplication( - scoped_ptr<ConnectToApplicationParams> params, - const String& application_name, - const GURL& file_url) { - Identity source, target; - mojom::ShellClientRequest request; - ApplicationInstance* instance = CreateAndConnectToInstance( - std::move(params), &source, &target, application_name, &request); - - bool start_sandboxed = false; - RunNativeApplication(std::move(request), start_sandboxed, instance, - util::UrlToFilePath(file_url)); - instance->RunConnectCallback(); -} - -void ApplicationManager::RunNativeApplication( - InterfaceRequest<mojom::ShellClient> request, - bool start_sandboxed, - ApplicationInstance* instance, - const base::FilePath& path) { - DCHECK(request.is_pending()); - - TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path", - path.AsUTF8Unsafe()); - scoped_ptr<NativeRunner> runner = native_runner_factory_->Create(path); - runner->Start(path, start_sandboxed, std::move(request), - base::Bind(&ApplicationManager::ApplicationPIDAvailable, - weak_ptr_factory_.GetWeakPtr(), instance->id()), - base::Bind(&ApplicationManager::CleanupRunner, - weak_ptr_factory_.GetWeakPtr(), runner.get())); - instance->SetNativeRunner(runner.get()); - native_runners_.push_back(std::move(runner)); -} - void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, const GURL& url) { URLToLoaderMap::iterator it = url_to_loader_.find(url); @@ -353,25 +125,8 @@ url_to_loader_[url] = loader.release(); } -ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) { - auto url_it = url_to_loader_.find(url); - if (url_it != url_to_loader_.end()) - return url_it->second; - return default_loader_.get(); -} - -mojom::ApplicationInfoPtr ApplicationManager::CreateApplicationInfoForInstance( - ApplicationInstance* instance) const { - mojom::ApplicationInfoPtr info(mojom::ApplicationInfo::New()); - info->id = instance->id(); - info->url = instance->identity().url().spec(); - info->qualifier = instance->identity().qualifier(); - info->name = instance->application_name(); - if (instance->identity().url().spec() == "mojo://shell/") - info->pid = base::Process::Current().Pid(); - else - info->pid = instance->pid(); - return info; +void ApplicationManager::TerminateShellConnections() { + STLDeleteValues(&identity_to_instance_); } void ApplicationManager::OnApplicationInstanceError( @@ -393,6 +148,236 @@ on_application_end.Run(); } +ApplicationInstance* ApplicationManager::GetApplicationInstance( + const Identity& identity) const { + const auto& it = identity_to_instance_.find(identity); + return it != identity_to_instance_.end() ? it->second : nullptr; +} + +void ApplicationManager::ApplicationPIDAvailable( + uint32_t id, + base::ProcessId pid) { + for (auto& instance : identity_to_instance_) { + if (instance.second->id() == id) { + instance.second->set_pid(pid); + break; + } + } + listeners_.ForAllPtrs( + [this, id, pid](mojom::ApplicationManagerListener* listener) { + listener->ApplicationPIDAvailable(id, pid); + }); +} + +//////////////////////////////////////////////////////////////////////////////// +// ApplicationManager, ShellClient implementation: + +bool ApplicationManager::AcceptConnection(Connection* connection) { + connection->AddInterface<mojom::ApplicationManager>(this); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// ApplicationManager, InterfaceFactory<mojom::ApplicationManager> +// implementation: + +void ApplicationManager::Create( + Connection* connection, + mojom::ApplicationManagerRequest request) { + bindings_.AddBinding(this, std::move(request)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ApplicationManager, mojom::ApplicationManager implemetation: + +void ApplicationManager::CreateInstanceForHandle( + ScopedHandle channel, + const String& url, + mojom::CapabilityFilterPtr filter, + mojom::PIDReceiverRequest pid_receiver) { + // We don't call ConnectToClient() here since the instance was created + // manually by other code, not in response to a Connect() request. The newly + // created instance is identified by |url| and may be subsequently reached by + // client code using this identity. + CapabilityFilter local_filter = filter->filter.To<CapabilityFilter>(); + Identity target_id(url.To<GURL>(), std::string(), local_filter); + mojom::ShellClientRequest request; + // TODO(beng): do better than url.spec() for application name. + ApplicationInstance* instance = + CreateInstance(target_id, base::Closure(), url, &request); + instance->BindPIDReceiver(std::move(pid_receiver)); + scoped_ptr<NativeRunner> runner = + native_runner_factory_->Create(base::FilePath()); + runner->InitHost(std::move(channel), std::move(request)); + instance->SetNativeRunner(runner.get()); + native_runners_.push_back(std::move(runner)); +} + +void ApplicationManager::AddListener( + mojom::ApplicationManagerListenerPtr listener) { + Array<mojom::ApplicationInfoPtr> applications; + for (auto& entry : identity_to_instance_) + applications.push_back(CreateApplicationInfoForInstance(entry.second)); + listener->SetRunningApplications(std::move(applications)); + + listeners_.AddInterfacePtr(std::move(listener)); +} + +//////////////////////////////////////////////////////////////////////////////// +// ApplicationManager, private: + +void ApplicationManager::InitPackageManager(bool register_mojo_url_schemes) { + scoped_ptr<ApplicationLoader> loader( + new package_manager::Loader(task_runner_, register_mojo_url_schemes)); + + mojom::ShellClientRequest request; + GURL url("mojo://package_manager/"); + CreateInstance(Identity(url), base::Closure(), url.spec(), &request); + loader->Load(url, std::move(request)); + + SetLoaderForURL(std::move(loader), url); + + ConnectToInterface(this, CreateShellIdentity(), url, &shell_resolver_); +} + +bool ApplicationManager::ConnectToRunningApplication( + scoped_ptr<ConnectToApplicationParams>* params) { + ApplicationInstance* instance = GetApplicationInstance((*params)->target()); + if (!instance) + return false; + + // TODO(beng): CHECK() that the target URL is already in the application + // catalog. + instance->ConnectToClient(std::move(*params)); + return true; +} + +ApplicationInstance* ApplicationManager::CreateInstance( + const Identity& target_id, + const base::Closure& on_application_end, + const String& application_name, + mojom::ShellClientRequest* request) { + mojom::ShellClientPtr shell_client; + *request = GetProxy(&shell_client); + ApplicationInstance* instance = new ApplicationInstance( + std::move(shell_client), this, target_id, on_application_end, + application_name); + DCHECK(identity_to_instance_.find(target_id) == + identity_to_instance_.end()); + identity_to_instance_[target_id] = instance; + mojom::ApplicationInfoPtr application_info = + CreateApplicationInfoForInstance(instance); + listeners_.ForAllPtrs( + [this, &application_info](mojom::ApplicationManagerListener* listener) { + listener->ApplicationInstanceCreated(application_info.Clone()); + }); + instance->InitializeApplication(); + return instance; +} + +void ApplicationManager::CreateShellClient( + const Identity& source, + const Identity& shell_client_factory, + const GURL& url, + mojom::ShellClientRequest request) { + mojom::ShellClientFactory* factory = + GetShellClientFactory(shell_client_factory, source); + factory->CreateShellClient(std::move(request), url.spec()); +} + +mojom::ShellClientFactory* ApplicationManager::GetShellClientFactory( + const Identity& shell_client_factory_identity, + const Identity& source_identity) { + auto it = shell_client_factories_.find(shell_client_factory_identity); + if (it != shell_client_factories_.end()) + return it->second.get(); + + mojom::ShellClientFactoryPtr factory; + // TODO(beng): we should forward the original source identity! + ConnectToInterface(this, source_identity, shell_client_factory_identity, + &factory); + mojom::ShellClientFactory* factory_interface = factory.get(); + factory.set_connection_error_handler( + base::Bind(&ApplicationManager::OnShellClientFactoryLost, + weak_ptr_factory_.GetWeakPtr(), + shell_client_factory_identity)); + shell_client_factories_[shell_client_factory_identity] = std::move(factory); + return factory_interface; +} + +void ApplicationManager::OnShellClientFactoryLost(const Identity& which) { + // Remove the mapping. + auto it = shell_client_factories_.find(which); + DCHECK(it != shell_client_factories_.end()); + shell_client_factories_.erase(it); +} + +void ApplicationManager::OnGotResolvedURL( + scoped_ptr<ConnectToApplicationParams> params, + const String& resolved_url, + const String& qualifier, + const String& file_url, + const String& application_name, + mojom::CapabilityFilterPtr base_filter) { + // It's possible that when this manifest request was issued, another one was + // already in-progress and completed by the time this one did, and so the + // requested application may already be running. + if (ConnectToRunningApplication(¶ms)) + return; + + Identity source = params->source(), target = params->target(); + mojom::ShellClientRequest request; + ApplicationInstance* instance = CreateInstance( + params->target(), params->on_application_end(), application_name, + &request); + instance->ConnectToClient(std::move(params)); + + if (LoadWithLoader(target, &request)) + return; + + CHECK(!file_url.is_null() && !application_name.is_null() && + !base_filter.is_null()); + + GURL resolved_gurl = resolved_url.To<GURL>(); + if (target.url().spec() != resolved_url) { + // TODO(beng): this clobbers the CapabilityFilter passed via Connect(). + CapabilityFilter capability_filter = GetPermissiveCapabilityFilter(); + if (!base_filter.is_null()) + capability_filter = base_filter->filter.To<CapabilityFilter>(); + + CreateShellClient(source, + Identity(resolved_gurl, qualifier, capability_filter), + target.url(), std::move(request)); + } else { + bool start_sandboxed = false; + base::FilePath path = util::UrlToFilePath(file_url.To<GURL>()); + scoped_ptr<NativeRunner> runner = native_runner_factory_->Create(path); + runner->Start(path, start_sandboxed, std::move(request), + base::Bind(&ApplicationManager::ApplicationPIDAvailable, + weak_ptr_factory_.GetWeakPtr(), instance->id()), + base::Bind(&ApplicationManager::CleanupRunner, + weak_ptr_factory_.GetWeakPtr(), runner.get())); + instance->SetNativeRunner(runner.get()); + native_runners_.push_back(std::move(runner)); + } +} + +bool ApplicationManager::LoadWithLoader(const Identity& target, + mojom::ShellClientRequest* request) { + ApplicationLoader* loader = GetLoaderForURL(target.url()); + if (!loader) + return false; + loader->Load(target.url(), std::move(*request)); + return true; +} + +ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) { + auto url_it = url_to_loader_.find(url); + if (url_it != url_to_loader_.end()) + return url_it->second; + return default_loader_.get(); +} + void ApplicationManager::CleanupRunner(NativeRunner* runner) { for (auto it = native_runners_.begin(); it != native_runners_.end(); ++it) { if (it->get() == runner) { @@ -402,8 +387,18 @@ } } -mojom::Shell::ConnectToApplicationCallback EmptyConnectCallback() { - return base::Bind(&OnEmptyOnConnectCallback); +mojom::ApplicationInfoPtr ApplicationManager::CreateApplicationInfoForInstance( + ApplicationInstance* instance) const { + mojom::ApplicationInfoPtr info(mojom::ApplicationInfo::New()); + info->id = instance->id(); + info->url = instance->identity().url().spec(); + info->qualifier = instance->identity().qualifier(); + info->name = instance->application_name(); + if (instance->identity().url().spec() == "mojo://shell/") + info->pid = base::Process::Current().Pid(); + else + info->pid = instance->pid(); + return info; } } // namespace shell
diff --git a/mojo/shell/application_manager.h b/mojo/shell/application_manager.h index acbb206..2708c642 100644 --- a/mojo/shell/application_manager.h +++ b/mojo/shell/application_manager.h
@@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/weak_binding_set.h" #include "mojo/public/cpp/bindings/weak_interface_ptr_set.h" #include "mojo/services/package_manager/public/interfaces/shell_resolver.mojom.h" #include "mojo/shell/application_loader.h" @@ -18,10 +19,13 @@ #include "mojo/shell/connect_to_application_params.h" #include "mojo/shell/identity.h" #include "mojo/shell/native_runner.h" +#include "mojo/shell/public/cpp/interface_factory.h" +#include "mojo/shell/public/cpp/shell_client.h" #include "mojo/shell/public/interfaces/application_manager.mojom.h" #include "mojo/shell/public/interfaces/interface_provider.mojom.h" #include "mojo/shell/public/interfaces/shell.mojom.h" #include "mojo/shell/public/interfaces/shell_client.mojom.h" +#include "mojo/shell/public/interfaces/shell_client_factory.mojom.h" #include "url/gurl.h" namespace base { @@ -35,7 +39,9 @@ class ApplicationInstance; class ShellClientFactoryConnection; -class ApplicationManager { +class ApplicationManager : public ShellClient, + public InterfaceFactory<mojom::ApplicationManager>, + public mojom::ApplicationManager { public: // API for testing. class TestAPI { @@ -43,8 +49,6 @@ explicit TestAPI(ApplicationManager* manager); ~TestAPI(); - // Returns true if the shared instance has been created. - static bool HasCreatedInstance(); // Returns true if there is a ApplicationInstance for this URL. bool HasRunningInstanceForURL(const GURL& url) const; private: @@ -66,7 +70,7 @@ ApplicationManager(scoped_ptr<NativeRunnerFactory> native_runner_factory, base::TaskRunner* task_runner, bool register_mojo_url_schemes); - ~ApplicationManager(); + ~ApplicationManager() override; // Loads a service if necessary and establishes a new client connection. // Please see the comments in connect_to_application_params.h for more details @@ -91,20 +95,32 @@ ApplicationInstance* GetApplicationInstance(const Identity& identity) const; - void CreateInstanceForHandle( - ScopedHandle channel, - const GURL& url, - mojom::CapabilityFilterPtr filter, - InterfaceRequest<mojom::PIDReceiver> pid_receiver); - void AddListener(mojom::ApplicationManagerListenerPtr listener); - void ApplicationPIDAvailable(uint32_t id, base::ProcessId pid); private: using IdentityToInstanceMap = std::map<Identity, ApplicationInstance*>; using URLToLoaderMap = std::map<GURL, ApplicationLoader*>; using IdentityToShellClientFactoryMap = - std::map<Identity, ShellClientFactoryConnection*>; + std::map<Identity, mojom::ShellClientFactoryPtr>; + + // ShellClient: + bool AcceptConnection(Connection* connection) override; + + // InterfaceFactory<mojom::ApplicationManager>: + void Create( + Connection* connection, + InterfaceRequest<mojom::ApplicationManager> request) override; + + // mojom::ApplicationManager: + void CreateInstanceForHandle( + ScopedHandle channel, + const String& url, + mojom::CapabilityFilterPtr filter, + InterfaceRequest<mojom::PIDReceiver> pid_receiver) override; + void AddListener( + mojom::ApplicationManagerListenerPtr listener) override; + + void InitPackageManager(bool register_mojo_url_schemes); // Takes the contents of |params| only when it returns true. bool ConnectToRunningApplication( @@ -112,28 +128,24 @@ ApplicationInstance* CreateAndConnectToInstance( scoped_ptr<ConnectToApplicationParams> params, - Identity* source, - Identity* target, const std::string& application_name, mojom::ShellClientRequest* request); ApplicationInstance* CreateInstance( const Identity& target_id, - const mojom::Shell::ConnectToApplicationCallback& connect_callback, const base::Closure& on_application_end, const String& application_name, mojom::ShellClientRequest* request); - uint32_t StartShellClientFactory(const Identity& source, - const Identity& shell_client_factory, - const GURL& url, - mojom::ShellClientRequest request); + void CreateShellClient(const Identity& source, + const Identity& shell_client_factory, + const GURL& url, + mojom::ShellClientRequest request); // Returns a running ShellClientFactory for |shell_client_factory_identity|, // if there is not one running one is started for |source_identity|. - ShellClientFactoryConnection* GetShellClientFactory( + mojom::ShellClientFactory* GetShellClientFactory( const Identity& shell_client_factory_identity, const Identity& source_identity); - void OnShellClientFactoryConnectionClosed( - ShellClientFactoryConnection* shell_client_factory); + void OnShellClientFactoryLost(const Identity& which);; // Callback when remote PackageManager resolves mojo:foo to mojo:bar. // |params| are the params passed to Connect(). @@ -146,21 +158,15 @@ // run with, from its manifest. void OnGotResolvedURL(scoped_ptr<ConnectToApplicationParams> params, const String& resolved_url, + const String& qualifier, const String& file_url, const String& application_name, mojom::CapabilityFilterPtr base_filter); - // In response to a request via Connect() with |params|, creates an - // ApplicationInstance and runs the application at |file_url|. - void CreateAndRunLocalApplication( - scoped_ptr<ConnectToApplicationParams> params, - const String& application_name, - const GURL& file_url); - - void RunNativeApplication(InterfaceRequest<mojom::ShellClient> request, - bool start_sandboxed, - ApplicationInstance* instance, - const base::FilePath& file_path); + // Tries to load |target| with an ApplicationLoader. Returns true if one was + // registered and it was loaded, in which case |request| is taken. + bool LoadWithLoader(const Identity& target, + mojom::ShellClientRequest* request); // Returns the appropriate loader for |url|, or the default loader if there is // no loader configured for the URL. @@ -180,7 +186,7 @@ IdentityToInstanceMap identity_to_instance_; - IdentityToShellClientFactoryMap identity_to_shell_client_factory_; + IdentityToShellClientFactoryMap shell_client_factories_; // Counter used to assign ids to content handlers. uint32_t shell_client_factory_id_counter_; @@ -189,6 +195,7 @@ base::TaskRunner* task_runner_; scoped_ptr<NativeRunnerFactory> native_runner_factory_; std::vector<scoped_ptr<NativeRunner>> native_runners_; + WeakBindingSet<mojom::ApplicationManager> bindings_; base::WeakPtrFactory<ApplicationManager> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ApplicationManager);
diff --git a/mojo/shell/application_manager_apptest.cc b/mojo/shell/application_manager_apptest.cc deleted file mode 100644 index 9fbfdf94..0000000 --- a/mojo/shell/application_manager_apptest.cc +++ /dev/null
@@ -1,218 +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. - -#include <stddef.h> -#include <stdint.h> - -#include <utility> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/process/process_handle.h" -#include "mojo/converters/network/network_type_converters.h" -#include "mojo/public/cpp/bindings/weak_binding_set.h" -#include "mojo/shell/application_manager_apptests.mojom.h" -#include "mojo/shell/public/cpp/application_test_base.h" -#include "mojo/shell/public/cpp/interface_factory.h" -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/shell/public/interfaces/application_manager.mojom.h" - -using mojo::shell::test::mojom::CreateInstanceForHandleTest; - -namespace mojo { -namespace shell { -namespace { - -class ApplicationManagerAppTestDelegate - : public ShellClient, - public InterfaceFactory<CreateInstanceForHandleTest>, - public CreateInstanceForHandleTest { - public: - ApplicationManagerAppTestDelegate() - : target_id_(mojom::Shell::kInvalidApplicationID), - binding_(this) {} - ~ApplicationManagerAppTestDelegate() override {} - - uint32_t target_id() const { return target_id_; } - - private: - // mojo::ShellClient: - void Initialize(Shell* shell, const std::string& url, uint32_t id) override {} - bool AcceptConnection(Connection* connection) override { - connection->AddInterface<CreateInstanceForHandleTest>(this); - return true; - } - - // InterfaceFactory<CreateInstanceForHandleTest>: - void Create(Connection* connection, - InterfaceRequest<CreateInstanceForHandleTest> request) override { - binding_.Bind(std::move(request)); - } - - // CreateInstanceForHandleTest: - void SetTargetID(uint32_t target_id) override { - target_id_ = target_id; - base::MessageLoop::current()->QuitWhenIdle(); - } - - uint32_t target_id_; - - Binding<CreateInstanceForHandleTest> binding_; - - DISALLOW_COPY_AND_ASSIGN(ApplicationManagerAppTestDelegate); -}; - -} // namespace - -class ApplicationManagerAppTest : public mojo::test::ApplicationTestBase, - public mojom::ApplicationManagerListener { - public: - ApplicationManagerAppTest() : delegate_(nullptr), binding_(this) {} - ~ApplicationManagerAppTest() override {} - - void OnDriverQuit() { - base::MessageLoop::current()->QuitNow(); - } - - protected: - struct ApplicationInfo { - ApplicationInfo(uint32_t id, - const std::string& url, - const std::string& name) - : id(id), url(url), pid(base::kNullProcessId), name(name) {} - - uint32_t id; - std::string url; - base::ProcessId pid; - std::string name; - }; - - void AddListenerAndWaitForApplications() { - mojom::ApplicationManagerPtr application_manager; - shell()->ConnectToInterface("mojo:shell", &application_manager); - - application_manager->AddListener(binding_.CreateInterfacePtrAndBind()); - binding_.WaitForIncomingMethodCall(); - } - - bool ContainsApplicationNamed(const std::string& name) const { - for (const auto& application : initial_applications_) { - if (application.name == name) - return true; - } - for (const auto& application : applications_) { - if (application.name == name) - return true; - } - return false; - } - - uint32_t target_id() const { - DCHECK(delegate_); - return delegate_->target_id(); - } - - const std::vector<ApplicationInfo>& applications() const { - return applications_; - } - - ApplicationManagerAppTestDelegate* delegate() { return delegate_; } - - private: - // test::ApplicationTestBase: - ShellClient* GetShellClient() override { - delegate_ = new ApplicationManagerAppTestDelegate; - return delegate_; - } - - // mojom::ApplicationManagerListener: - void SetRunningApplications( - Array<mojom::ApplicationInfoPtr> applications) override { - for (size_t i = 0; i < applications.size(); ++i) { - initial_applications_.push_back(ApplicationInfo(applications[i]->id, - applications[i]->url, - applications[i]->name)); - } - } - void ApplicationInstanceCreated( - mojom::ApplicationInfoPtr application) override { - applications_.push_back(ApplicationInfo(application->id, application->url, - application->name)); - } - void ApplicationInstanceDestroyed(uint32_t id) override { - for (auto it = applications_.begin(); it != applications_.end(); ++it) { - auto& application = *it; - if (application.id == id) { - applications_.erase(it); - break; - } - } - } - void ApplicationPIDAvailable(uint32_t id, uint32_t pid) override { - for (auto& application : applications_) { - if (application.id == id) { - application.pid = pid; - break; - } - } - } - - ApplicationManagerAppTestDelegate* delegate_; - Binding<mojom::ApplicationManagerListener> binding_; - std::vector<ApplicationInfo> applications_; - std::vector<ApplicationInfo> initial_applications_; - - DISALLOW_COPY_AND_ASSIGN(ApplicationManagerAppTest); -}; - -TEST_F(ApplicationManagerAppTest, CreateInstanceForHandle) { - AddListenerAndWaitForApplications(); - - // 1. Launch a process. (Actually, have the runner launch a process that - // launches a process. #becauselinkerrors). - mojo::shell::test::mojom::DriverPtr driver; - scoped_ptr<Connection> connection = - shell()->Connect("exe:application_manager_apptest_driver"); - connection->GetInterface(&driver); - - // 2. Wait for the target to connect to us. (via - // mojo:application_manager_apptests) - base::MessageLoop::current()->Run(); - - uint32_t remote_id = mojom::Shell::kInvalidApplicationID; - EXPECT_TRUE(connection->GetRemoteApplicationID(&remote_id)); - EXPECT_NE(mojom::Shell::kInvalidApplicationID, remote_id); - - // 3. Validate that this test suite's pretty name was consumed from its - // manifest. - EXPECT_TRUE(ContainsApplicationNamed("Application Manager Apptests")); - - // 4. Validate that the right applications/processes were created. - // Note that the target process will be created even if the tests are - // run with --single-process. - EXPECT_EQ(2u, applications().size()); - { - auto& application = applications().front(); - EXPECT_EQ(remote_id, application.id); - EXPECT_EQ("exe://application_manager_apptest_driver/", application.url); - EXPECT_NE(base::kNullProcessId, application.pid); - } - { - auto& application = applications().back(); - // We learn about the target process id via a ping from it. - EXPECT_EQ(target_id(), application.id); - EXPECT_EQ("exe://application_manager_apptest_target/", application.url); - EXPECT_NE(base::kNullProcessId, application.pid); - } - - driver.set_connection_error_handler( - base::Bind(&ApplicationManagerAppTest::OnDriverQuit, - base::Unretained(this))); - driver->QuitDriver(); - base::MessageLoop::current()->Run(); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/application_manager_apptest_driver.cc b/mojo/shell/application_manager_apptest_driver.cc deleted file mode 100644 index 2ac6584..0000000 --- a/mojo/shell/application_manager_apptest_driver.cc +++ /dev/null
@@ -1,156 +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. - -#include <stdint.h> - -#include <utility> - -#include "base/at_exit.h" -#include "base/base_paths.h" -#include "base/base_switches.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/path_service.h" -#include "base/process/process.h" -#include "base/thread_task_runner_handle.h" -#include "mojo/converters/network/network_type_converters.h" -#include "mojo/edk/embedder/embedder.h" -#include "mojo/edk/embedder/platform_channel_pair.h" -#include "mojo/edk/embedder/scoped_platform_handle.h" -#include "mojo/public/cpp/bindings/weak_binding_set.h" -#include "mojo/shell/application_manager_apptests.mojom.h" -#include "mojo/shell/public/cpp/connection.h" -#include "mojo/shell/public/cpp/interface_factory.h" -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/public/interfaces/application_manager.mojom.h" -#include "mojo/shell/runner/child/test_native_main.h" -#include "mojo/shell/runner/common/switches.h" -#include "mojo/shell/runner/init.h" - -using mojo::shell::test::mojom::CreateInstanceForHandleTestPtr; -using mojo::shell::test::mojom::Driver; - -namespace { - -class TargetApplicationDelegate : public mojo::ShellClient, - public mojo::InterfaceFactory<Driver>, - public Driver { - public: - TargetApplicationDelegate() : shell_(nullptr), weak_factory_(this) {} - ~TargetApplicationDelegate() override {} - - private: - // mojo::ShellClient: - void Initialize(mojo::Shell* shell, const std::string& url, - uint32_t id) override { - shell_ = shell; - - base::FilePath target_path; - CHECK(base::PathService::Get(base::DIR_EXE, &target_path)); - #if defined(OS_WIN) - target_path = target_path.Append( - FILE_PATH_LITERAL("application_manager_apptest_target.exe")); - #else - target_path = target_path.Append( - FILE_PATH_LITERAL("application_manager_apptest_target")); - #endif - - base::CommandLine child_command_line(target_path); - // Forward the wait-for-debugger flag but nothing else - we don't want to - // stamp on the platform-channel flag. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kWaitForDebugger)) { - child_command_line.AppendSwitch(switches::kWaitForDebugger); - } - - mojo::shell::mojom::PIDReceiverPtr receiver; - mojo::InterfaceRequest<mojo::shell::mojom::PIDReceiver> request = - GetProxy(&receiver); - - // Create the channel to be shared with the target process. Pass one end - // on the command line. - mojo::edk::PlatformChannelPair platform_channel_pair; - mojo::edk::HandlePassingInformation handle_passing_info; - platform_channel_pair.PrepareToPassClientHandleToChildProcess( - &child_command_line, &handle_passing_info); - - // Generate a token for the child to find and connect to a primordial pipe - // and pass that as well. - std::string primordial_pipe_token = mojo::edk::GenerateRandomToken(); - child_command_line.AppendSwitchASCII(switches::kPrimordialPipeToken, - primordial_pipe_token); - - // Allocate the pipe locally. - mojo::ScopedMessagePipeHandle pipe = - mojo::edk::CreateParentMessagePipe(primordial_pipe_token); - - mojo::shell::mojom::CapabilityFilterPtr filter( - mojo::shell::mojom::CapabilityFilter::New()); - mojo::Array<mojo::String> test_interfaces; - test_interfaces.push_back( - mojo::shell::test::mojom::CreateInstanceForHandleTest::Name_); - filter->filter.insert("mojo:mojo_shell_apptests", - std::move(test_interfaces)); - - mojo::shell::mojom::ApplicationManagerPtr application_manager; - shell_->ConnectToInterface("mojo:shell", &application_manager); - application_manager->CreateInstanceForHandle( - mojo::ScopedHandle(mojo::Handle(pipe.release().value())), - "exe:application_manager_apptest_target", std::move(filter), - std::move(request)); - - base::LaunchOptions options; - #if defined(OS_WIN) - options.handles_to_inherit = &handle_passing_info; - #elif defined(OS_POSIX) - options.fds_to_remap = &handle_passing_info; - #endif - target_ = base::LaunchProcess(child_command_line, options); - DCHECK(target_.IsValid()); - receiver->SetPID(target_.Pid()); - mojo::edk::ChildProcessLaunched(target_.Handle(), - platform_channel_pair.PassServerHandle()); - } - - bool AcceptConnection(mojo::Connection* connection) override { - connection->AddInterface<Driver>(this); - return true; - } - - // mojo::InterfaceFactory<Driver>: - void Create(mojo::Connection* connection, - mojo::InterfaceRequest<Driver> request) override { - bindings_.AddBinding(this, std::move(request)); - } - - // Driver: - void QuitDriver() override { - target_.Terminate(0, false); - shell_->Quit(); - } - - mojo::Shell* shell_; - base::Process target_; - mojo::WeakBindingSet<Driver> bindings_; - base::WeakPtrFactory<TargetApplicationDelegate> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(TargetApplicationDelegate); -}; - -} // namespace - -int main(int argc, char** argv) { - base::AtExitManager at_exit; - base::CommandLine::Init(argc, argv); - - mojo::shell::InitializeLogging(); - - TargetApplicationDelegate delegate; - return mojo::shell::TestNativeMain(&delegate); -}
diff --git a/mojo/shell/application_manager_apptest_target.cc b/mojo/shell/application_manager_apptest_target.cc deleted file mode 100644 index 133b547..0000000 --- a/mojo/shell/application_manager_apptest_target.cc +++ /dev/null
@@ -1,46 +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. - -#include "base/at_exit.h" -#include "base/command_line.h" -#include "base/macros.h" -#include "mojo/shell/application_manager_apptests.mojom.h" -#include "mojo/shell/public/cpp/connection.h" -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/runner/child/test_native_main.h" -#include "mojo/shell/runner/init.h" - -using mojo::shell::test::mojom::CreateInstanceForHandleTestPtr; - -namespace { - -class TargetApplicationDelegate : public mojo::ShellClient { - public: - TargetApplicationDelegate() {} - ~TargetApplicationDelegate() override {} - - private: - // mojo::ShellClient: - void Initialize(mojo::Shell* shell, const std::string& url, - uint32_t id) override { - CreateInstanceForHandleTestPtr service; - shell->ConnectToInterface("mojo:mojo_shell_apptests", &service); - service->SetTargetID(id); - } - - DISALLOW_COPY_AND_ASSIGN(TargetApplicationDelegate); -}; - -} // namespace - -int main(int argc, char** argv) { - base::AtExitManager at_exit; - base::CommandLine::Init(argc, argv); - - mojo::shell::InitializeLogging(); - - TargetApplicationDelegate delegate; - return mojo::shell::TestNativeMain(&delegate); -}
diff --git a/mojo/shell/application_manager_unittest.cc b/mojo/shell/application_manager_unittest.cc deleted file mode 100644 index 2ce0b3a..0000000 --- a/mojo/shell/application_manager_unittest.cc +++ /dev/null
@@ -1,615 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/shell/application_manager.h" - -#include <utility> - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/macros.h" -#include "base/memory/scoped_vector.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "mojo/public/cpp/bindings/strong_binding.h" -#include "mojo/shell/application_loader.h" -#include "mojo/shell/connect_util.h" -#include "mojo/shell/public/cpp/interface_factory.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/public/cpp/shell_connection.h" -#include "mojo/shell/public/interfaces/interface_provider.mojom.h" -#include "mojo/shell/test.mojom.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace shell { -namespace test { - -const char kTestURLString[] = "test:testService"; -const char kTestAURLString[] = "test:TestA"; -const char kTestBURLString[] = "test:TestB"; - -struct TestContext { - TestContext() : num_impls(0), num_loader_deletes(0) {} - std::string last_test_string; - int num_impls; - int num_loader_deletes; -}; - -void QuitClosure(bool* value) { - *value = true; - base::MessageLoop::current()->QuitWhenIdle(); -} - -class TestServiceImpl : public TestService { - public: - TestServiceImpl(TestContext* context, InterfaceRequest<TestService> request) - : context_(context), binding_(this, std::move(request)) { - ++context_->num_impls; - } - - ~TestServiceImpl() override { - --context_->num_impls; - if (!base::MessageLoop::current()->is_running()) - return; - base::MessageLoop::current()->QuitWhenIdle(); - } - - // TestService implementation: - void Test(const String& test_string, - const Callback<void()>& callback) override { - context_->last_test_string = test_string; - callback.Run(); - } - - private: - TestContext* context_; - StrongBinding<TestService> binding_; -}; - -class TestClient { - public: - explicit TestClient(TestServicePtr service) - : service_(std::move(service)), quit_after_ack_(false) {} - - void AckTest() { - if (quit_after_ack_) - base::MessageLoop::current()->QuitWhenIdle(); - } - - void Test(const std::string& test_string) { - quit_after_ack_ = true; - service_->Test(test_string, - base::Bind(&TestClient::AckTest, base::Unretained(this))); - } - - private: - TestServicePtr service_; - bool quit_after_ack_; - DISALLOW_COPY_AND_ASSIGN(TestClient); -}; - -class TestApplicationLoader : public ApplicationLoader, - public ShellClient, - public InterfaceFactory<TestService> { - public: - TestApplicationLoader() - : context_(nullptr), num_loads_(0) {} - - ~TestApplicationLoader() override { - if (context_) - ++context_->num_loader_deletes; - shell_connection_.reset(); - } - - void set_context(TestContext* context) { context_ = context; } - int num_loads() const { return num_loads_; } - const GURL& last_requestor_url() const { return last_requestor_url_; } - - private: - // ApplicationLoader implementation. - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override { - ++num_loads_; - shell_connection_.reset(new ShellConnection(this, std::move(request))); - } - - // mojo::ShellClient implementation. - bool AcceptConnection(Connection* connection) override { - connection->AddInterface<TestService>(this); - last_requestor_url_ = GURL(connection->GetRemoteApplicationURL()); - return true; - } - - // InterfaceFactory<TestService> implementation. - void Create(Connection* connection, - InterfaceRequest<TestService> request) override { - new TestServiceImpl(context_, std::move(request)); - } - - scoped_ptr<ShellConnection> shell_connection_; - TestContext* context_; - int num_loads_; - GURL last_requestor_url_; - - DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader); -}; - -class ClosingApplicationLoader : public ApplicationLoader { - private: - // ApplicationLoader implementation. - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override { - } -}; - -class TesterContext { - public: - explicit TesterContext(base::MessageLoop* loop) - : num_b_calls_(0), - num_c_calls_(0), - num_a_deletes_(0), - num_b_deletes_(0), - num_c_deletes_(0), - tester_called_quit_(false), - a_called_quit_(false), - loop_(loop) {} - - void IncrementNumBCalls() { - base::AutoLock lock(lock_); - num_b_calls_++; - } - - void IncrementNumCCalls() { - base::AutoLock lock(lock_); - num_c_calls_++; - } - - void IncrementNumADeletes() { - base::AutoLock lock(lock_); - num_a_deletes_++; - } - - void IncrementNumBDeletes() { - base::AutoLock lock(lock_); - num_b_deletes_++; - } - - void IncrementNumCDeletes() { - base::AutoLock lock(lock_); - num_c_deletes_++; - } - - void set_tester_called_quit() { - base::AutoLock lock(lock_); - tester_called_quit_ = true; - } - - void set_a_called_quit() { - base::AutoLock lock(lock_); - a_called_quit_ = true; - } - - int num_b_calls() { - base::AutoLock lock(lock_); - return num_b_calls_; - } - int num_c_calls() { - base::AutoLock lock(lock_); - return num_c_calls_; - } - int num_a_deletes() { - base::AutoLock lock(lock_); - return num_a_deletes_; - } - int num_b_deletes() { - base::AutoLock lock(lock_); - return num_b_deletes_; - } - int num_c_deletes() { - base::AutoLock lock(lock_); - return num_c_deletes_; - } - bool tester_called_quit() { - base::AutoLock lock(lock_); - return tester_called_quit_; - } - bool a_called_quit() { - base::AutoLock lock(lock_); - return a_called_quit_; - } - - void QuitSoon() { - loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); - } - - private: - // lock_ protects all members except for loop_ which must be unchanged for the - // lifetime of this class. - base::Lock lock_; - int num_b_calls_; - int num_c_calls_; - int num_a_deletes_; - int num_b_deletes_; - int num_c_deletes_; - bool tester_called_quit_; - bool a_called_quit_; - - base::MessageLoop* loop_; -}; - -// Used to test that the requestor url will be correctly passed. -class TestAImpl : public TestA { - public: - TestAImpl(Shell* shell, - TesterContext* test_context, - InterfaceRequest<TestA> request, - InterfaceFactory<TestC>* factory) - : test_context_(test_context), binding_(this, std::move(request)) { - connection_ = shell->Connect(kTestBURLString); - connection_->AddInterface<TestC>(factory); - connection_->GetInterface(&b_); - } - - ~TestAImpl() override { - test_context_->IncrementNumADeletes(); - if (base::MessageLoop::current()->is_running()) - Quit(); - } - - private: - void CallB() override { - b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this))); - } - - void CallCFromB() override { - b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this))); - } - - void Quit() { - base::MessageLoop::current()->QuitWhenIdle(); - test_context_->set_a_called_quit(); - test_context_->QuitSoon(); - } - - scoped_ptr<Connection> connection_; - TesterContext* test_context_; - TestBPtr b_; - StrongBinding<TestA> binding_; -}; - -class TestBImpl : public TestB { - public: - TestBImpl(Connection* connection, - TesterContext* test_context, - InterfaceRequest<TestB> request) - : test_context_(test_context), binding_(this, std::move(request)) { - connection->GetInterface(&c_); - } - - ~TestBImpl() override { - test_context_->IncrementNumBDeletes(); - if (base::MessageLoop::current()->is_running()) - base::MessageLoop::current()->QuitWhenIdle(); - test_context_->QuitSoon(); - } - - private: - void B(const Callback<void()>& callback) override { - test_context_->IncrementNumBCalls(); - callback.Run(); - } - - void CallC(const Callback<void()>& callback) override { - test_context_->IncrementNumBCalls(); - c_->C(callback); - } - - TesterContext* test_context_; - TestCPtr c_; - StrongBinding<TestB> binding_; -}; - -class TestCImpl : public TestC { - public: - TestCImpl(Connection* connection, - TesterContext* test_context, - InterfaceRequest<TestC> request) - : test_context_(test_context), binding_(this, std::move(request)) {} - - ~TestCImpl() override { test_context_->IncrementNumCDeletes(); } - - private: - void C(const Callback<void()>& callback) override { - test_context_->IncrementNumCCalls(); - callback.Run(); - } - - TesterContext* test_context_; - StrongBinding<TestC> binding_; -}; - -class Tester : public ShellClient, - public ApplicationLoader, - public InterfaceFactory<TestA>, - public InterfaceFactory<TestB>, - public InterfaceFactory<TestC> { - public: - Tester(TesterContext* context, const std::string& requestor_url) - : context_(context), requestor_url_(requestor_url) {} - ~Tester() override {} - - private: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override { - app_.reset(new ShellConnection(this, std::move(request))); - } - - bool AcceptConnection(Connection* connection) override { - if (!requestor_url_.empty() && - requestor_url_ != connection->GetRemoteApplicationURL()) { - context_->set_tester_called_quit(); - context_->QuitSoon(); - base::MessageLoop::current()->QuitWhenIdle(); - return false; - } - // If we're coming from A, then add B, otherwise A. - if (connection->GetRemoteApplicationURL() == kTestAURLString) - connection->AddInterface<TestB>(this); - else - connection->AddInterface<TestA>(this); - return true; - } - - void Create(Connection* connection, - InterfaceRequest<TestA> request) override { - a_bindings_.push_back( - new TestAImpl(app_.get(), context_, std::move(request), this)); - } - - void Create(Connection* connection, - InterfaceRequest<TestB> request) override { - new TestBImpl(connection, context_, std::move(request)); - } - - void Create(Connection* connection, - InterfaceRequest<TestC> request) override { - new TestCImpl(connection, context_, std::move(request)); - } - - TesterContext* context_; - scoped_ptr<ShellConnection> app_; - std::string requestor_url_; - ScopedVector<TestAImpl> a_bindings_; -}; - -class ApplicationManagerTest : public testing::Test { - public: - ApplicationManagerTest() : tester_context_(&loop_) {} - - ~ApplicationManagerTest() override {} - - void SetUp() override { - application_manager_.reset(new ApplicationManager(true)); - test_loader_ = new TestApplicationLoader; - test_loader_->set_context(&context_); - application_manager_->set_default_loader( - scoped_ptr<ApplicationLoader>(test_loader_)); - - TestServicePtr service_proxy; - ConnectToInterface(application_manager_.get(), GURL(kTestURLString), - &service_proxy); - test_client_.reset(new TestClient(std::move(service_proxy))); - } - - void TearDown() override { - test_client_.reset(); - application_manager_.reset(); - } - - void AddLoaderForURL(const GURL& url, const std::string& requestor_url) { - application_manager_->SetLoaderForURL( - make_scoped_ptr(new Tester(&tester_context_, requestor_url)), url); - } - - bool HasRunningInstanceForURL(const GURL& url) { - ApplicationManager::TestAPI manager_test_api(application_manager_.get()); - return manager_test_api.HasRunningInstanceForURL(url); - } - - protected: - base::ShadowingAtExitManager at_exit_; - TestApplicationLoader* test_loader_; - TesterContext tester_context_; - TestContext context_; - base::MessageLoop loop_; - scoped_ptr<TestClient> test_client_; - scoped_ptr<ApplicationManager> application_manager_; - DISALLOW_COPY_AND_ASSIGN(ApplicationManagerTest); -}; - -TEST_F(ApplicationManagerTest, Basic) { - test_client_->Test("test"); - loop_.Run(); - EXPECT_EQ(std::string("test"), context_.last_test_string); -} - -TEST_F(ApplicationManagerTest, ClientError) { - test_client_->Test("test"); - EXPECT_TRUE(HasRunningInstanceForURL(GURL(kTestURLString))); - loop_.Run(); - EXPECT_EQ(1, context_.num_impls); - test_client_.reset(); - loop_.Run(); - EXPECT_EQ(0, context_.num_impls); - EXPECT_TRUE(HasRunningInstanceForURL(GURL(kTestURLString))); -} - -TEST_F(ApplicationManagerTest, Deletes) { - { - ApplicationManager am(true); - TestApplicationLoader* default_loader = new TestApplicationLoader; - default_loader->set_context(&context_); - TestApplicationLoader* url_loader1 = new TestApplicationLoader; - TestApplicationLoader* url_loader2 = new TestApplicationLoader; - url_loader1->set_context(&context_); - url_loader2->set_context(&context_); - am.set_default_loader(scoped_ptr<ApplicationLoader>(default_loader)); - am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader1), - GURL("test:test1")); - am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader2), - GURL("test:test1")); - } - EXPECT_EQ(3, context_.num_loader_deletes); -} - -// Test for SetLoaderForURL() & set_default_loader(). -TEST_F(ApplicationManagerTest, SetLoaders) { - TestApplicationLoader* default_loader = new TestApplicationLoader; - TestApplicationLoader* url_loader = new TestApplicationLoader; - application_manager_->set_default_loader( - scoped_ptr<ApplicationLoader>(default_loader)); - application_manager_->SetLoaderForURL( - scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1")); - - // test::test1 should go to url_loader. - TestServicePtr test_service; - ConnectToInterface(application_manager_.get(), GURL("test:test1"), - &test_service); - EXPECT_EQ(1, url_loader->num_loads()); - EXPECT_EQ(0, default_loader->num_loads()); - - // http::test1 should go to default loader. - ConnectToInterface(application_manager_.get(), GURL("http:test1"), - &test_service); - EXPECT_EQ(1, url_loader->num_loads()); - EXPECT_EQ(1, default_loader->num_loads()); -} - -// Confirm that the url of a service is correctly passed to another service that -// it loads. -TEST_F(ApplicationManagerTest, ACallB) { - // Any url can load a. - AddLoaderForURL(GURL(kTestAURLString), std::string()); - - // Only a can load b. - AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); - - TestAPtr a; - ConnectToInterface(application_manager_.get(), GURL(kTestAURLString), &a); - a->CallB(); - loop_.Run(); - EXPECT_EQ(1, tester_context_.num_b_calls()); - EXPECT_TRUE(tester_context_.a_called_quit()); -} - -// A calls B which calls C. -TEST_F(ApplicationManagerTest, BCallC) { - // Any url can load a. - AddLoaderForURL(GURL(kTestAURLString), std::string()); - - // Only a can load b. - AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); - - TestAPtr a; - ConnectToInterface(application_manager_.get(), GURL(kTestAURLString), &a); - a->CallCFromB(); - loop_.Run(); - - EXPECT_EQ(1, tester_context_.num_b_calls()); - EXPECT_EQ(1, tester_context_.num_c_calls()); - EXPECT_TRUE(tester_context_.a_called_quit()); -} - -// Confirm that a service impl will be deleted if the app that connected to -// it goes away. -TEST_F(ApplicationManagerTest, BDeleted) { - AddLoaderForURL(GURL(kTestAURLString), std::string()); - AddLoaderForURL(GURL(kTestBURLString), std::string()); - - TestAPtr a; - ConnectToInterface(application_manager_.get(), GURL(kTestAURLString), &a); - - a->CallB(); - loop_.Run(); - - // Kills the a app. - application_manager_->SetLoaderForURL(scoped_ptr<ApplicationLoader>(), - GURL(kTestAURLString)); - loop_.Run(); - - EXPECT_EQ(1, tester_context_.num_b_deletes()); -} - -// Confirm that the url of a service is correctly passed to another service that -// it loads, and that it can be rejected. -TEST_F(ApplicationManagerTest, ANoLoadB) { - // Any url can load a. - AddLoaderForURL(GURL(kTestAURLString), std::string()); - - // Only c can load b, so this will fail. - AddLoaderForURL(GURL(kTestBURLString), "test:TestC"); - - TestAPtr a; - ConnectToInterface(application_manager_.get(), GURL(kTestAURLString), &a); - a->CallB(); - loop_.Run(); - EXPECT_EQ(0, tester_context_.num_b_calls()); - - EXPECT_FALSE(tester_context_.a_called_quit()); - EXPECT_TRUE(tester_context_.tester_called_quit()); -} - -TEST_F(ApplicationManagerTest, NoServiceNoLoad) { - AddLoaderForURL(GURL(kTestAURLString), std::string()); - - // There is no TestC service implementation registered with - // ApplicationManager, so this cannot succeed (but also shouldn't crash). - TestCPtr c; - ConnectToInterface(application_manager_.get(), GURL(kTestAURLString), &c); - c.set_connection_error_handler( - []() { base::MessageLoop::current()->QuitWhenIdle(); }); - - loop_.Run(); - EXPECT_TRUE(c.encountered_error()); -} - -TEST_F(ApplicationManagerTest, TestEndApplicationClosure) { - ClosingApplicationLoader* loader = new ClosingApplicationLoader(); - application_manager_->SetLoaderForURL( - scoped_ptr<ApplicationLoader>(loader), GURL("test:test")); - - bool called = false; - scoped_ptr<ConnectToApplicationParams> params(new ConnectToApplicationParams); - params->SetTargetURL(GURL("test:test")); - params->set_on_application_end( - base::Bind(&QuitClosure, base::Unretained(&called))); - application_manager_->ConnectToApplication(std::move(params)); - loop_.Run(); - EXPECT_TRUE(called); -} - -TEST_F(ApplicationManagerTest, SameIdentityShouldNotCauseDuplicateLoad) { - // 1 because ApplicationManagerTest connects once at startup. - EXPECT_EQ(1, test_loader_->num_loads()); - - TestServicePtr test_service; - ConnectToInterface(application_manager_.get(), - GURL("mojo:foo"), &test_service); - EXPECT_EQ(2, test_loader_->num_loads()); - - // Exactly the same URL as above. - ConnectToInterface(application_manager_.get(), - GURL("mojo:foo"), &test_service); - EXPECT_EQ(2, test_loader_->num_loads()); - - // A different identity because the domain is different. - ConnectToInterface(application_manager_.get(), - GURL("mojo:bar"), &test_service); - EXPECT_EQ(3, test_loader_->num_loads()); -} - -} // namespace test -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/background/BUILD.gn b/mojo/shell/background/BUILD.gn index c5a1faa5..be73bd5 100644 --- a/mojo/shell/background/BUILD.gn +++ b/mojo/shell/background/BUILD.gn
@@ -2,6 +2,14 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +group("background") { + testonly = true + deps = [ + ":lib", + ":main", + ] +} + source_set("lib") { sources = [ "background_shell.cc",
diff --git a/mojo/shell/background/background_shell.cc b/mojo/shell/background/background_shell.cc index 0c5adda..50eef9ce 100644 --- a/mojo/shell/background/background_shell.cc +++ b/mojo/shell/background/background_shell.cc
@@ -29,27 +29,27 @@ return make_scoped_ptr(new common::MessagePumpMojo); } -// Used to obtain the InterfaceRequest for an application. +// Used to obtain the ShellClientRequest for an application. When +// ApplicationLoader::Load() is called a callback is run with the +// ShellClientRequest. class BackgroundApplicationLoader : public ApplicationLoader { public: - BackgroundApplicationLoader() {} + using Callback = base::Callback<void(mojom::ShellClientRequest)>; + + explicit BackgroundApplicationLoader(const Callback& callback) + : callback_(callback) {} ~BackgroundApplicationLoader() override {} - bool got_request() const { return got_request_; } - InterfaceRequest<mojom::ShellClient> TakeApplicationRequest() { - return std::move(request_); - } - // ApplicationLoader: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override { - got_request_ = true; - request_ = std::move(request); + void Load(const GURL& url, mojom::ShellClientRequest request) override { + DCHECK(!callback_.is_null()); // Callback should only be run once. + Callback callback = callback_; + callback_.Reset(); + callback.Run(std::move(request)); } private: - bool got_request_ = false; - InterfaceRequest<mojom::ShellClient> request_; + Callback callback_; DISALLOW_COPY_AND_ASSIGN(BackgroundApplicationLoader); }; @@ -72,26 +72,28 @@ // Manages the thread to startup mojo. class BackgroundShell::MojoThread : public base::SimpleThread { public: - MojoThread() : SimpleThread("mojo-background-shell") {} + explicit MojoThread( + const std::vector<CommandLineSwitch>& command_line_switches) + : SimpleThread("mojo-background-shell"), + command_line_switches_(command_line_switches) {} ~MojoThread() override {} void CreateShellClientRequest(base::WaitableEvent* signal, scoped_ptr<ConnectToApplicationParams> params, - InterfaceRequest<mojom::ShellClient>* request) { + mojom::ShellClientRequest* request) { // Only valid to call this on the background thread. DCHECK_EQ(message_loop_, base::MessageLoop::current()); // Ownership of |loader| passes to ApplicationManager. - BackgroundApplicationLoader* loader = new BackgroundApplicationLoader; const GURL url = params->target().url(); + BackgroundApplicationLoader* loader = new BackgroundApplicationLoader( + base::Bind(&MojoThread::OnGotApplicationRequest, base::Unretained(this), + url, signal, request)); context_->application_manager()->SetLoaderForURL(make_scoped_ptr(loader), url); context_->application_manager()->ConnectToApplication(std::move(params)); - DCHECK(loader->got_request()); - *request = loader->TakeApplicationRequest(); - // Trigger destruction of the loader. - context_->application_manager()->SetLoaderForURL(nullptr, url); - signal->Signal(); + // The request is asynchronously processed. When processed + // OnGotApplicationRequest() is called and we'll signal |signal|. } base::MessageLoop* message_loop() { return message_loop_; } @@ -124,6 +126,7 @@ scoped_ptr<Context> context(new Context); context_ = context.get(); + context_->set_command_line_switches(command_line_switches_); context_->Init(shell_dir); message_loop_->Run(); @@ -137,12 +140,24 @@ } private: + void OnGotApplicationRequest(const GURL& url, + base::WaitableEvent* signal, + mojom::ShellClientRequest* request_result, + mojom::ShellClientRequest actual_request) { + *request_result = std::move(actual_request); + // Trigger destruction of the loader. + context_->application_manager()->SetLoaderForURL(nullptr, url); + signal->Signal(); + } + // We own this. It's created on the main thread, but destroyed on the // background thread. MojoMessageLoop* message_loop_ = nullptr; // Created in Run() on the background thread. Context* context_ = nullptr; + const std::vector<CommandLineSwitch> command_line_switches_; + DISALLOW_COPY_AND_ASSIGN(MojoThread); }; @@ -152,18 +167,19 @@ thread_->Stop(); } -void BackgroundShell::Init() { +void BackgroundShell::Init( + const std::vector<CommandLineSwitch>& command_line_switches) { DCHECK(!thread_); - thread_.reset(new MojoThread); + thread_.reset(new MojoThread(command_line_switches)); thread_->Start(); } -InterfaceRequest<mojom::ShellClient> BackgroundShell::CreateShellClientRequest( +mojom::ShellClientRequest BackgroundShell::CreateShellClientRequest( const GURL& url) { scoped_ptr<ConnectToApplicationParams> params(new ConnectToApplicationParams); - params->SetTarget( + params->set_target( Identity(url, std::string(), GetPermissiveCapabilityFilter())); - InterfaceRequest<mojom::ShellClient> request; + mojom::ShellClientRequest request; base::WaitableEvent signal(true, false); thread_->message_loop()->task_runner()->PostTask( FROM_HERE, base::Bind(&MojoThread::CreateShellClientRequest,
diff --git a/mojo/shell/background/background_shell.h b/mojo/shell/background/background_shell.h index e2163f4..d1e9c412 100644 --- a/mojo/shell/background/background_shell.h +++ b/mojo/shell/background/background_shell.h
@@ -5,6 +5,8 @@ #ifndef MOJO_SHELL_BACKGROUND_BACKGROUND_SHELL_H_ #define MOJO_SHELL_BACKGROUND_BACKGROUND_SHELL_H_ +#include <vector> + #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "mojo/public/cpp/bindings/interface_request.h" @@ -15,6 +17,8 @@ namespace mojo { namespace shell { +struct CommandLineSwitch; + // BackgroundShell starts up the mojo shell on a background thread, and // destroys the thread in the destructor. Once created use CreateApplication() // to obtain an InterfaceRequest for the Application. The InterfaceRequest can @@ -24,7 +28,9 @@ BackgroundShell(); ~BackgroundShell(); - void Init(); + // Starts the background shell. |command_line_switches| are additional + // switches applied to any processes spawned by this call. + void Init(const std::vector<CommandLineSwitch>& command_line_switches); // Obtains an InterfaceRequest for the specified url. InterfaceRequest<mojom::ShellClient> CreateShellClientRequest(
diff --git a/mojo/shell/capability_filter_test.cc b/mojo/shell/capability_filter_test.cc deleted file mode 100644 index 56e6d07..0000000 --- a/mojo/shell/capability_filter_test.cc +++ /dev/null
@@ -1,351 +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. - -#include "mojo/shell/capability_filter_test.h" - -#include <utility> - -#include "base/macros.h" -#include "base/stl_util.h" -#include "base/strings/stringprintf.h" -#include "mojo/public/cpp/bindings/strong_binding.h" -#include "mojo/public/cpp/bindings/weak_binding_set.h" -#include "mojo/shell/application_loader.h" -#include "mojo/shell/public/cpp/connection.h" -#include "mojo/shell/public/cpp/interface_factory.h" -#include "mojo/shell/public/cpp/shell_connection.h" - -namespace mojo { -namespace shell { -namespace test { - -// Lives on the main thread of the test. -// Listens for interfaces exposed/blocked and for application connections being -// closed. Quits |loop| when all expectations are met. -class ConnectionValidator : public ApplicationLoader, - public ShellClient, - public InterfaceFactory<Validator>, - public Validator { - public: - ConnectionValidator(const std::set<std::string>& expectations, - base::MessageLoop* loop) - : app_(nullptr), - expectations_(expectations), - loop_(loop) {} - ~ConnectionValidator() override {} - - bool expectations_met() { - return unexpected_.empty() && expectations_.empty(); - } - - void PrintUnmetExpectations() { - for (auto expectation : expectations_) - ADD_FAILURE() << "Unmet: " << expectation; - for (auto unexpected : unexpected_) - ADD_FAILURE() << "Unexpected: " << unexpected; - } - - private: - // Overridden from ApplicationLoader: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override { - app_.reset(new ShellConnection(this, std::move(request))); - } - - // Overridden from ShellClient: - bool AcceptConnection(Connection* connection) override { - connection->AddInterface<Validator>(this); - return true; - } - - // Overridden from InterfaceFactory<Validator>: - void Create(Connection* connection, - InterfaceRequest<Validator> request) override { - validator_bindings_.AddBinding(this, std::move(request)); - } - - // Overridden from Validator: - void AddInterfaceCalled(const String& app_url, - const String& service_url, - const String& name, - bool blocked) override { - Validate(base::StringPrintf("%s %s %s %s", - blocked ? "B" : "E", app_url.data(), service_url.data(), name.data())); - } - void ConnectionClosed(const String& app_url, - const String& service_url) override { - Validate(base::StringPrintf("C %s %s", app_url.data(), service_url.data())); - } - - void Validate(const std::string& result) { - DVLOG(1) << "Validate: " << result; - auto i = expectations_.find(result); - if (i != expectations_.end()) { - expectations_.erase(i); - if (expectations_.empty()) - loop_->QuitWhenIdle(); - } else { - // This is a test failure, and will result in PrintUnexpectedExpecations() - // being called. - unexpected_.insert(result); - loop_->QuitWhenIdle(); - } - } - - scoped_ptr<ShellConnection> app_; - std::set<std::string> expectations_; - std::set<std::string> unexpected_; - base::MessageLoop* loop_; - WeakBindingSet<Validator> validator_bindings_; - - DISALLOW_COPY_AND_ASSIGN(ConnectionValidator); -}; - -// This class models a system service that exposes two interfaces, Safe and -// Unsafe. The interface Unsafe is not to be exposed to untrusted applications. -class ServiceApplication : public ShellClient, - public InterfaceFactory<Safe>, - public InterfaceFactory<Unsafe>, - public Safe, - public Unsafe { - public: - ServiceApplication() : shell_(nullptr) {} - ~ServiceApplication() override {} - - private: - // Overridden from ShellClient: - void Initialize(Shell* shell, const std::string& url, uint32_t id) override { - shell_ = shell; - // ServiceApplications have no capability filter and can thus connect - // directly to the validator application. - shell_->ConnectToInterface("test:validator", &validator_); - } - bool AcceptConnection(Connection* connection) override { - AddInterface<Safe>(connection); - AddInterface<Unsafe>(connection); - return true; - } - - // Overridden from InterfaceFactory<Safe>: - void Create(Connection* connection, - InterfaceRequest<Safe> request) override { - safe_bindings_.AddBinding(this, std::move(request)); - } - - // Overridden from InterfaceFactory<Unsafe>: - void Create(Connection* connection, - InterfaceRequest<Unsafe> request) override { - unsafe_bindings_.AddBinding(this, std::move(request)); - } - - template <typename Interface> - void AddInterface(Connection* connection) { - validator_->AddInterfaceCalled(connection->GetRemoteApplicationURL(), - connection->GetConnectionURL(), - Interface::Name_, - !connection->AddInterface<Interface>(this)); - } - - Shell* shell_; - ValidatorPtr validator_; - WeakBindingSet<Safe> safe_bindings_; - WeakBindingSet<Unsafe> unsafe_bindings_; - - DISALLOW_COPY_AND_ASSIGN(ServiceApplication); -}; - -//////////////////////////////////////////////////////////////////////////////// -// TestApplication: - -TestApplication::TestApplication() : shell_(nullptr) {} -TestApplication::~TestApplication() {} - -void TestApplication::Initialize(Shell* shell, const std::string& url, - uint32_t id) { - shell_ = shell; - url_ = url; -} -bool TestApplication::AcceptConnection(Connection* connection) { - // TestApplications receive their Validator via the inbound connection. - connection->GetInterface(&validator_); - - connection1_ = shell_->Connect("test:service"); - connection1_->SetRemoteInterfaceProviderConnectionErrorHandler( - base::Bind(&TestApplication::ConnectionClosed, - base::Unretained(this), "test:service")); - - connection2_ = shell_->Connect("test:service2"); - connection2_->SetRemoteInterfaceProviderConnectionErrorHandler( - base::Bind(&TestApplication::ConnectionClosed, - base::Unretained(this), "test:service2")); - return true; -} - -void TestApplication::ConnectionClosed(const std::string& service_url) { - validator_->ConnectionClosed(url_, service_url); -} - -//////////////////////////////////////////////////////////////////////////////// -// TestLoader: - -TestLoader::TestLoader(ShellClient* delegate) : delegate_(delegate) {} -TestLoader::~TestLoader() {} - -void TestLoader::Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) { - app_.reset(new ShellConnection(delegate_.get(), std::move(request))); -} - -//////////////////////////////////////////////////////////////////////////////// -// CapabilityFilterTest: - -CapabilityFilterTest::CapabilityFilterTest() : validator_(nullptr) {} -CapabilityFilterTest::~CapabilityFilterTest() {} - -void CapabilityFilterTest::RunBlockingTest() { - std::set<std::string> expectations; - expectations.insert("E test:trusted test:service mojo::shell::Safe"); - expectations.insert("E test:trusted test:service mojo::shell::Unsafe"); - expectations.insert("E test:trusted test:service2 mojo::shell::Safe"); - expectations.insert("E test:trusted test:service2 mojo::shell::Unsafe"); - expectations.insert("E test:untrusted test:service mojo::shell::Safe"); - expectations.insert("B test:untrusted test:service mojo::shell::Unsafe"); - expectations.insert("C test:untrusted test:service2"); - InitValidator(expectations); - - // This first application can only connect to test:service. Connections to - // test:service2 will be blocked. It also will only be able to see the - // "Safe" interface exposed by test:service. It will be blocked from seeing - // "Unsafe". - AllowedInterfaces interfaces; - interfaces.insert(Safe::Name_); - CapabilityFilter filter; - filter["test:service"] = interfaces; - RunApplication("test:untrusted", filter); - - // This second application can connect to both test:service and - // test:service2. It can connect to both "Safe" and "Unsafe" interfaces. - RunApplication("test:trusted", GetPermissiveCapabilityFilter()); - - RunTest(); -} - -void CapabilityFilterTest::RunWildcardTest() { - std::set<std::string> expectations; - expectations.insert("E test:wildcard test:service mojo::shell::Safe"); - expectations.insert("E test:wildcard test:service mojo::shell::Unsafe"); - expectations.insert("E test:wildcard test:service2 mojo::shell::Safe"); - expectations.insert("E test:wildcard test:service2 mojo::shell::Unsafe"); - expectations.insert("C test:blocked test:service"); - expectations.insert("C test:blocked test:service2"); - expectations.insert("B test:wildcard2 test:service mojo::shell::Safe"); - expectations.insert("B test:wildcard2 test:service mojo::shell::Unsafe"); - expectations.insert("B test:wildcard2 test:service2 mojo::shell::Safe"); - expectations.insert("B test:wildcard2 test:service2 mojo::shell::Unsafe"); - expectations.insert("E test:wildcard3 test:service mojo::shell::Safe"); - expectations.insert("E test:wildcard3 test:service mojo::shell::Unsafe"); - expectations.insert("E test:wildcard3 test:service2 mojo::shell::Safe"); - expectations.insert("B test:wildcard3 test:service2 mojo::shell::Unsafe"); - InitValidator(expectations); - - // This application is allowed to connect to any application because of a - // wildcard rule, and any interface exposed because of a wildcard rule in - // the interface array. - RunApplication("test:wildcard", GetPermissiveCapabilityFilter()); - - // This application is allowed to connect to no other applications because - // of an empty capability filter. - RunApplication("test:blocked", CapabilityFilter()); - - // This application is allowed to connect to any application because of a - // wildcard rule but may not connect to any interfaces because of an empty - // interface array. - CapabilityFilter filter1; - filter1["*"] = AllowedInterfaces(); - RunApplication("test:wildcard2", filter1); - - // This application is allowed to connect to both test:service and - // test:service2, and may see any interface exposed by test:service but only - // the Safe interface exposed by test:service2. - AllowedInterfaces interfaces2; - interfaces2.insert("*"); - CapabilityFilter filter2; - filter2["test:service"] = interfaces2; - AllowedInterfaces interfaces3; - interfaces3.insert(Safe::Name_); - filter2["test:service2"] = interfaces3; - RunApplication("test:wildcard3", filter2); -} - - -void CapabilityFilterTest::SetUp() { - application_manager_.reset(new ApplicationManager(true)); - CreateLoader<ServiceApplication>("test:service"); - CreateLoader<ServiceApplication>("test:service2"); -} - -void CapabilityFilterTest::TearDown() { - application_manager_.reset(); -} - -class InterfaceProviderImpl : public shell::mojom::InterfaceProvider { - public: - explicit InterfaceProviderImpl( - shell::mojom::InterfaceProviderRequest interfaces, - InterfaceFactory<Validator>* factory) - : binding_(this, std::move(interfaces)), - factory_(factory) {} - ~InterfaceProviderImpl() override {} - - private: - // shell::mojom::InterfaceProvider method. - void GetInterface(const mojo::String& interface_name, - ScopedMessagePipeHandle client_handle) override { - if (interface_name == Validator::Name_) { - factory_->Create(nullptr, - MakeRequest<Validator>(std::move(client_handle))); - } - } - - Binding<InterfaceProvider> binding_; - InterfaceFactory<Validator>* factory_; - - DISALLOW_COPY_AND_ASSIGN(InterfaceProviderImpl); -}; - -void CapabilityFilterTest::RunApplication(const std::string& url, - const CapabilityFilter& filter) { - shell::mojom::InterfaceProviderPtr remote_interfaces; - - // We expose Validator to the test application via ConnectToApplication - // because we don't allow the test application to connect to test:validator. - // Adding it to the CapabilityFilter would interfere with the test. - shell::mojom::InterfaceProviderPtr local_interfaces; - new InterfaceProviderImpl(GetProxy(&local_interfaces), validator_); - scoped_ptr<ConnectToApplicationParams> params( - new ConnectToApplicationParams); - params->SetTarget(Identity(GURL(url), std::string(), filter)); - params->set_remote_interfaces(GetProxy(&remote_interfaces)); - params->set_local_interfaces(std::move(local_interfaces)); - params->set_on_application_end(base::MessageLoop::QuitWhenIdleClosure()); - application_manager_->ConnectToApplication(std::move(params)); -} - -void CapabilityFilterTest::InitValidator( - const std::set<std::string>& expectations) { - validator_ = new ConnectionValidator(expectations, &loop_); - application_manager()->SetLoaderForURL(make_scoped_ptr(validator_), - GURL("test:validator")); -} - -void CapabilityFilterTest::RunTest() { - loop()->Run(); - EXPECT_TRUE(validator_->expectations_met()); - if (!validator_->expectations_met()) - validator_->PrintUnmetExpectations(); -} - -} // namespace test -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/capability_filter_test.h b/mojo/shell/capability_filter_test.h deleted file mode 100644 index 118f6cb..0000000 --- a/mojo/shell/capability_filter_test.h +++ /dev/null
@@ -1,107 +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. - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "mojo/shell/application_loader.h" -#include "mojo/shell/application_manager.h" -#include "mojo/shell/capability_filter_unittest.mojom.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/public/cpp/shell_connection.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace shell { -namespace test { - -class ConnectionValidator; - -// This class models an application who will use the shell to interact with a -// system service. The shell may limit this application's visibility of the full -// set of interfaces exposed by that service. -class TestApplication : public ShellClient { - public: - TestApplication(); - ~TestApplication() override; - - private: - // Overridden from ShellClient: - void Initialize(Shell* shell, const std::string& url, uint32_t id) override; - bool AcceptConnection(Connection* connection) override; - - void ConnectionClosed(const std::string& service_url); - - Shell* shell_; - std::string url_; - ValidatorPtr validator_; - scoped_ptr<Connection> connection1_; - scoped_ptr<Connection> connection2_; - - DISALLOW_COPY_AND_ASSIGN(TestApplication); -}; - -class TestLoader : public ApplicationLoader { - public: - explicit TestLoader(ShellClient* delegate); - ~TestLoader() override; - - private: - // Overridden from ApplicationLoader: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override; - - scoped_ptr<ShellClient> delegate_; - scoped_ptr<ShellConnection> app_; - - DISALLOW_COPY_AND_ASSIGN(TestLoader); -}; - -class CapabilityFilterTest : public testing::Test { - public: - CapabilityFilterTest(); - ~CapabilityFilterTest() override; - - protected: - template <class T> - void CreateLoader(const std::string& url) { - application_manager_->SetLoaderForURL( - make_scoped_ptr(new TestLoader(new T)), GURL(url)); - } - - void RunBlockingTest(); - void RunWildcardTest(); - - // Overridden from testing::Test: - void SetUp() override; - void TearDown() override; - - base::MessageLoop* loop() { return &loop_; } - ApplicationManager* application_manager() { - return application_manager_.get(); - } - ConnectionValidator* validator() { return validator_; } - - private: - void RunApplication(const std::string& url, const CapabilityFilter& filter); - void InitValidator(const std::set<std::string>& expectations); - void RunTest(); - - template<class T> - scoped_ptr<ShellClient> CreateShellClient() { - return scoped_ptr<ShellClient>(new T); - } - - base::ShadowingAtExitManager at_exit_; - base::MessageLoop loop_; - scoped_ptr<ApplicationManager> application_manager_; - ConnectionValidator* validator_; - - DISALLOW_COPY_AND_ASSIGN(CapabilityFilterTest); -}; - -} // namespace test -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/capability_filter_unittest.cc b/mojo/shell/capability_filter_unittest.cc deleted file mode 100644 index 8c54e6e4..0000000 --- a/mojo/shell/capability_filter_unittest.cc +++ /dev/null
@@ -1,38 +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. - -#include "base/macros.h" -#include "mojo/shell/capability_filter_test.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace shell { -namespace test { - -class CapabilityFilterApplicationTest : public CapabilityFilterTest { - public: - CapabilityFilterApplicationTest() {} - ~CapabilityFilterApplicationTest() override {} - - private: - DISALLOW_COPY_AND_ASSIGN(CapabilityFilterApplicationTest); -}; - -TEST_F(CapabilityFilterApplicationTest, Blocking) { - CreateLoader<TestApplication>("test:trusted"); - CreateLoader<TestApplication>("test:untrusted"); - RunBlockingTest(); -} - -TEST_F(CapabilityFilterApplicationTest, Wildcards) { - CreateLoader<TestApplication>("test:wildcard"); - CreateLoader<TestApplication>("test:blocked"); - CreateLoader<TestApplication>("test:wildcard2"); - CreateLoader<TestApplication>("test:wildcard3"); - RunWildcardTest(); -} - -} // namespace test -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/connect_to_application_params.cc b/mojo/shell/connect_to_application_params.cc index 9d7c3a4..481c2a63 100644 --- a/mojo/shell/connect_to_application_params.cc +++ b/mojo/shell/connect_to_application_params.cc
@@ -24,29 +24,8 @@ source_ = source->identity(); } -void ConnectToApplicationParams::SetTarget(const Identity& target) { - target_ = target; - target_url_request_ = URLRequest::New(); - target_url_request_->url = target_.url().spec(); -} - void ConnectToApplicationParams::SetTargetURL(const GURL& target_url) { target_ = Identity(target_url, target_.qualifier(), target_.filter()); - target_url_request_ = URLRequest::New(); - target_url_request_->url = target_.url().spec(); -} - -void ConnectToApplicationParams::SetTargetURLRequest(URLRequestPtr request) { - Identity target = request ? Identity(GURL(request->url.get()), - target_.qualifier(), target_.filter()) - : Identity(); - SetTargetURLRequest(std::move(request), target); -} - -void ConnectToApplicationParams::SetTargetURLRequest(URLRequestPtr request, - const Identity& target) { - target_url_request_ = std::move(request); - target_ = target; } } // namespace shell
diff --git a/mojo/shell/connect_to_application_params.h b/mojo/shell/connect_to_application_params.h index 28a1ed6..8bfa21d 100644 --- a/mojo/shell/connect_to_application_params.h +++ b/mojo/shell/connect_to_application_params.h
@@ -11,7 +11,6 @@ #include "base/callback.h" #include "base/macros.h" #include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/services/network/public/interfaces/url_loader.mojom.h" #include "mojo/shell/identity.h" #include "mojo/shell/public/interfaces/interface_provider.mojom.h" #include "mojo/shell/public/interfaces/shell.mojom.h" @@ -33,23 +32,13 @@ void SetSource(ApplicationInstance* source); // The following methods set both |target_| and |target_url_request_|. - void SetTarget(const Identity& target); void SetTargetURL(const GURL& target_url); - void SetTargetURLRequest(URLRequestPtr request); - void SetTargetURLRequest(URLRequestPtr request, const Identity& target); void set_source(const Identity& source) { source_ = source; } const Identity& source() const { return source_; } + void set_target(const Identity& target) { target_ = target; } const Identity& target() const { return target_; } - const URLRequest* target_url_request() const { - return target_url_request_.get(); - } - // NOTE: This doesn't reset |target_|. - URLRequestPtr TakeTargetURLRequest() { - return std::move(target_url_request_); - } - void set_remote_interfaces(shell::mojom::InterfaceProviderRequest value) { remote_interfaces_ = std::move(value); } @@ -86,10 +75,6 @@ Identity source_; // The identity of the application being connected to. Identity target_; - // The URL request to fetch the application. It may contain more information - // than |target_| (e.g., headers, request body). When it is taken, |target_| - // remains unchanged. - URLRequestPtr target_url_request_; shell::mojom::InterfaceProviderRequest remote_interfaces_; shell::mojom::InterfaceProviderPtr local_interfaces_;
diff --git a/mojo/shell/connect_util.cc b/mojo/shell/connect_util.cc index d33fad8..f435635 100644 --- a/mojo/shell/connect_util.cc +++ b/mojo/shell/connect_util.cc
@@ -15,14 +15,13 @@ ScopedMessagePipeHandle ConnectToInterfaceByName( ApplicationManager* application_manager, - const GURL& application_url, + const Identity& source, + const Identity& target, const std::string& interface_name) { shell::mojom::InterfaceProviderPtr remote_interfaces; scoped_ptr<ConnectToApplicationParams> params(new ConnectToApplicationParams); - params->set_source(Identity(GURL("mojo://shell/"), std::string(), - GetPermissiveCapabilityFilter())); - params->SetTarget(Identity(application_url, std::string(), - GetPermissiveCapabilityFilter())); + params->set_source(source); + params->set_target(target); params->set_remote_interfaces(GetProxy(&remote_interfaces)); application_manager->ConnectToApplication(std::move(params)); MessagePipe pipe;
diff --git a/mojo/shell/connect_util.h b/mojo/shell/connect_util.h index e3938f19..036ecde9 100644 --- a/mojo/shell/connect_util.h +++ b/mojo/shell/connect_util.h
@@ -9,6 +9,7 @@ #include "mojo/public/cpp/bindings/interface_ptr.h" #include "mojo/public/cpp/system/handle.h" +#include "mojo/shell/identity.h" class GURL; @@ -19,7 +20,8 @@ ScopedMessagePipeHandle ConnectToInterfaceByName( ApplicationManager* application_manager, - const GURL& application_url, + const Identity& source, + const Identity& target, const std::string& interface_name); // Must only be used by shell internals and test code as it does not forward @@ -27,11 +29,23 @@ // filter. template <typename Interface> inline void ConnectToInterface(ApplicationManager* application_manager, + const Identity& source, + const Identity& target, + InterfacePtr<Interface>* ptr) { + ScopedMessagePipeHandle service_handle = ConnectToInterfaceByName( + application_manager, source, target, Interface::Name_); + ptr->Bind(InterfacePtrInfo<Interface>(std::move(service_handle), 0u)); +} + +template <typename Interface> +inline void ConnectToInterface(ApplicationManager* application_manager, + const Identity& source, const GURL& application_url, InterfacePtr<Interface>* ptr) { - ScopedMessagePipeHandle service_handle = - ConnectToInterfaceByName(application_manager, application_url, - Interface::Name_); + ScopedMessagePipeHandle service_handle = ConnectToInterfaceByName( + application_manager, source, + Identity(application_url, std::string(), GetPermissiveCapabilityFilter()), + Interface::Name_); ptr->Bind(InterfacePtrInfo<Interface>(std::move(service_handle), 0u)); }
diff --git a/mojo/shell/identity.cc b/mojo/shell/identity.cc index fef8658..c0e12fb 100644 --- a/mojo/shell/identity.cc +++ b/mojo/shell/identity.cc
@@ -53,5 +53,10 @@ return qualifier_ < other.qualifier_; } +Identity CreateShellIdentity() { + return Identity(GURL("mojo://shell/"), std::string(), + GetPermissiveCapabilityFilter()); +} + } // namespace shell } // namespace mojo
diff --git a/mojo/shell/identity.h b/mojo/shell/identity.h index 826ef60..e7a2d27 100644 --- a/mojo/shell/identity.h +++ b/mojo/shell/identity.h
@@ -47,6 +47,10 @@ CapabilityFilter filter_; }; +// Creates an identity for the Shell, used when the Shell connects to +// applications. +Identity CreateShellIdentity(); + } // namespace shell } // namespace mojo
diff --git a/mojo/shell/package_apptest.cc b/mojo/shell/package_apptest.cc deleted file mode 100644 index 1086fe3..0000000 --- a/mojo/shell/package_apptest.cc +++ /dev/null
@@ -1,84 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <utility> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/run_loop.h" -#include "mojo/shell/package_test.mojom.h" -#include "mojo/shell/public/cpp/application_test_base.h" -#include "mojo/shell/public/interfaces/application_manager.mojom.h" - -// Tests that multiple applications can be packaged in a single Mojo application -// implementing ShellClientFactory; that these applications can be specified by -// the package's manifest and are thus registered with the PackageManager. - -namespace mojo { -namespace shell { -namespace { -void ReceiveName(std::string* out_name, - base::RunLoop* loop, - const String& name) { - *out_name = name; - loop->Quit(); -} -} // namespace - -using PackageApptest = mojo::test::ApplicationTestBase; - -TEST_F(PackageApptest, Basic) { - std::set<uint32_t> ids; - { - // We need to do this to force the shell to read the test app's manifest and - // register aliases. - test::mojom::PackageTestServicePtr root_service; - scoped_ptr<Connection> connection = - shell()->Connect("mojo:package_test_package"); - connection->GetInterface(&root_service); - base::RunLoop run_loop; - std::string root_name; - root_service->GetName(base::Bind(&ReceiveName, &root_name, &run_loop)); - run_loop.Run(); - uint32_t id = mojom::Shell::kInvalidApplicationID; - EXPECT_TRUE(connection->GetRemoteApplicationID(&id)); - ids.insert(id); - } - - { - // Now subsequent connects to applications provided by the root app will be - // resolved correctly. - test::mojom::PackageTestServicePtr service_a; - scoped_ptr<Connection> connection = shell()->Connect("mojo:package_test_a"); - connection->GetInterface(&service_a); - base::RunLoop run_loop; - std::string a_name; - service_a->GetName(base::Bind(&ReceiveName, &a_name, &run_loop)); - run_loop.Run(); - EXPECT_EQ("A", a_name); - uint32_t id = mojom::Shell::kInvalidApplicationID; - EXPECT_TRUE(connection->GetRemoteApplicationID(&id)); - ids.insert(id); - } - - { - test::mojom::PackageTestServicePtr service_b; - scoped_ptr<Connection> connection = shell()->Connect("mojo:package_test_b"); - connection->GetInterface(&service_b); - base::RunLoop run_loop; - std::string b_name; - service_b->GetName(base::Bind(&ReceiveName, &b_name, &run_loop)); - run_loop.Run(); - EXPECT_EQ("B", b_name); - uint32_t id = mojom::Shell::kInvalidApplicationID; - EXPECT_TRUE(connection->GetRemoteApplicationID(&id)); - ids.insert(id); - } -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/package_test_package.cc b/mojo/shell/package_test_package.cc deleted file mode 100644 index 010cb0dc..0000000 --- a/mojo/shell/package_test_package.cc +++ /dev/null
@@ -1,171 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <utility> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/run_loop.h" -#include "base/threading/simple_thread.h" -#include "mojo/public/c/system/main.h" -#include "mojo/public/cpp/bindings/weak_binding_set.h" -#include "mojo/shell/package_test.mojom.h" -#include "mojo/shell/public/cpp/application_runner.h" -#include "mojo/shell/public/cpp/interface_factory.h" -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/public/interfaces/shell_client_factory.mojom.h" - -// Tests that multiple applications can be packaged in a single Mojo application -// implementing ShellClientFactory; that these applications can be specified by -// the package's manifest and are thus registered with the PackageManager. - -namespace mojo { -namespace shell { - -using GetNameCallback = test::mojom::PackageTestService::GetNameCallback; - -class ProvidedShellClient - : public ShellClient, - public InterfaceFactory<test::mojom::PackageTestService>, - public test::mojom::PackageTestService, - public base::SimpleThread { - public: - ProvidedShellClient(const std::string& name, - mojom::ShellClientRequest request, - const Callback<void()>& destruct_callback) - : base::SimpleThread(name), - name_(name), - request_(std::move(request)), - destruct_callback_(destruct_callback), - shell_(nullptr) { - Start(); - } - ~ProvidedShellClient() override { - Join(); - destruct_callback_.Run(); - } - - private: - // mojo::ShellClient: - void Initialize(Shell* shell, const std::string& url, uint32_t id) override { - shell_ = shell; - bindings_.set_connection_error_handler( - base::Bind(&ProvidedShellClient::OnConnectionError, - base::Unretained(this))); - } - bool AcceptConnection(Connection* connection) override { - connection->AddInterface<test::mojom::PackageTestService>( - this); - return true; - } - - // InterfaceFactory<test::mojom::PackageTestService>: - void Create(Connection* connection, - test::mojom::PackageTestServiceRequest request) override { - bindings_.AddBinding(this, std::move(request)); - } - - // test::mojom::PackageTestService: - void GetName(const GetNameCallback& callback) override { - callback.Run(name_); - } - - // base::SimpleThread: - void Run() override { - ApplicationRunner(this).Run(request_.PassMessagePipe().release().value(), - false); - delete this; - } - - void OnConnectionError() { - if (bindings_.empty()) - shell_->Quit(); - } - - const std::string name_; - mojom::ShellClientRequest request_; - const Callback<void()> destruct_callback_; - Shell* shell_; - WeakBindingSet<test::mojom::PackageTestService> bindings_; - - DISALLOW_COPY_AND_ASSIGN(ProvidedShellClient); -}; - -class PackageTestShellClient - : public ShellClient, - public InterfaceFactory<mojom::ShellClientFactory>, - public InterfaceFactory<test::mojom::PackageTestService>, - public mojom::ShellClientFactory, - public test::mojom::PackageTestService { - public: - PackageTestShellClient() : shell_(nullptr) {} - ~PackageTestShellClient() override {} - - private: - // mojo::ShellClient: - void Initialize(Shell* shell, const std::string& url, uint32_t id) override { - shell_ = shell; - bindings_.set_connection_error_handler( - base::Bind(&PackageTestShellClient::OnConnectionError, - base::Unretained(this))); - } - bool AcceptConnection(Connection* connection) override { - connection->AddInterface<ShellClientFactory>(this); - connection->AddInterface<test::mojom::PackageTestService>(this); - return true; - } - - // InterfaceFactory<mojom::ShellClientFactory>: - void Create(Connection* connection, - mojom::ShellClientFactoryRequest request) override { - shell_client_factory_bindings_.AddBinding(this, std::move(request)); - } - - // InterfaceFactory<test::mojom::PackageTestService>: - void Create(Connection* connection, - test::mojom::PackageTestServiceRequest request) override { - bindings_.AddBinding(this, std::move(request)); - } - - // mojom::ShellClientFactory: - void CreateShellClient(mojom::ShellClientRequest request, - const String& url, - const Callback<void()>& destruct_callback) override { - if (url == "mojo://package_test_a/") - new ProvidedShellClient("A", std::move(request), destruct_callback); - else if (url == "mojo://package_test_b/") - new ProvidedShellClient("B", std::move(request), destruct_callback); - } - - // test::mojom::PackageTestService: - void GetName(const GetNameCallback& callback) override { - callback.Run("ROOT"); - } - - void OnConnectionError() { - if (bindings_.empty()) - shell_->Quit(); - } - - Shell* shell_; - std::vector<scoped_ptr<ShellClient>> delegates_; - WeakBindingSet<mojom::ShellClientFactory> shell_client_factory_bindings_; - WeakBindingSet<test::mojom::PackageTestService> bindings_; - - DISALLOW_COPY_AND_ASSIGN(PackageTestShellClient); -}; - -} // namespace shell -} // namespace mojo - - -MojoResult MojoMain(MojoHandle shell_handle) { - MojoResult rv = mojo::ApplicationRunner( - new mojo::shell::PackageTestShellClient).Run(shell_handle); - return rv; -}
diff --git a/mojo/shell/public/cpp/BUILD.gn b/mojo/shell/public/cpp/BUILD.gn index 3afe21a..eeea0d1 100644 --- a/mojo/shell/public/cpp/BUILD.gn +++ b/mojo/shell/public/cpp/BUILD.gn
@@ -62,24 +62,6 @@ ] } -source_set("shell_client_factory") { - sources = [ - "lib/shell_client_factory.cc", - "shell_client_factory.h", - ] - deps = [ - ":cpp", - - # TODO: this code should not depend on base. - "//base", - "//mojo/common:url_type_converters", - "//mojo/message_pump", - "//mojo/services/network/public/interfaces", - "//mojo/shell/public/interfaces:interfaces_cpp_sources", - "//url", - ] -} - source_set("test_support") { testonly = true sources = [ @@ -104,7 +86,4 @@ ] data_deps = [] - if (is_android) { - data_deps += [ "//mojo/android" ] - } }
diff --git a/mojo/shell/public/cpp/connection.h b/mojo/shell/public/cpp/connection.h index a2f6b77..e05243fc 100644 --- a/mojo/shell/public/cpp/connection.h +++ b/mojo/shell/public/cpp/connection.h
@@ -95,14 +95,6 @@ // been established. virtual bool GetRemoteApplicationID(uint32_t* remote_id) const = 0; - // Returns the id of the deepest shell client factory used in connecting to - // the application. See GetRemoteApplicationID() for details about the return - // value. A |shell_client_factory_id| value of Shell::kInvalidApplicationID - // indicates no shell client factory was used in connecting to the - // application. - virtual bool GetRemoteShellClientFactoryID( - uint32_t* shell_client_factory_id) const = 0; - // See description in GetRemoteApplicationID()/ // GetRemoteShellClientFactoryID(). If the ids are available, |callback| is // run immediately.
diff --git a/mojo/shell/public/cpp/lib/connection_impl.cc b/mojo/shell/public/cpp/lib/connection_impl.cc index f301711..d0faa51 100644 --- a/mojo/shell/public/cpp/lib/connection_impl.cc +++ b/mojo/shell/public/cpp/lib/connection_impl.cc
@@ -29,7 +29,6 @@ : connection_url_(connection_url), remote_url_(remote_url), remote_id_(remote_id), - shell_client_factory_id_(0u), remote_ids_valid_(false), local_registry_(std::move(local_interfaces), this), remote_interfaces_(std::move(remote_interfaces)), @@ -40,7 +39,6 @@ ConnectionImpl::ConnectionImpl() : remote_id_(shell::mojom::Shell::kInvalidApplicationID), - shell_client_factory_id_(shell::mojom::Shell::kInvalidApplicationID), remote_ids_valid_(false), local_registry_(shell::mojom::InterfaceProviderRequest(), this), allow_all_interfaces_(true), @@ -50,7 +48,7 @@ shell::mojom::Shell::ConnectToApplicationCallback ConnectionImpl::GetConnectToApplicationCallback() { - return base::Bind(&ConnectionImpl::OnGotRemoteIDs, + return base::Bind(&ConnectionImpl::OnGotInstanceID, weak_factory_.GetWeakPtr()); } @@ -78,15 +76,6 @@ return true; } -bool ConnectionImpl::GetRemoteShellClientFactoryID( - uint32_t* shell_client_factory_id) const { - if (!remote_ids_valid_) - return false; - - *shell_client_factory_id = shell_client_factory_id_; - return true; -} - void ConnectionImpl::AddRemoteIDCallback(const Closure& callback) { if (remote_ids_valid_) { callback.Run(); @@ -114,13 +103,11 @@ //////////////////////////////////////////////////////////////////////////////// // ConnectionImpl, private: -void ConnectionImpl::OnGotRemoteIDs(uint32_t target_application_id, - uint32_t shell_client_factory_id) { +void ConnectionImpl::OnGotInstanceID(uint32_t target_application_id) { DCHECK(!remote_ids_valid_); remote_ids_valid_ = true; remote_id_ = target_application_id; - shell_client_factory_id_ = shell_client_factory_id; std::vector<Closure> callbacks; callbacks.swap(remote_id_callbacks_); for (auto callback : callbacks)
diff --git a/mojo/shell/public/cpp/lib/connection_impl.h b/mojo/shell/public/cpp/lib/connection_impl.h index 1346e257..0cc2247 100644 --- a/mojo/shell/public/cpp/lib/connection_impl.h +++ b/mojo/shell/public/cpp/lib/connection_impl.h
@@ -45,24 +45,18 @@ void SetRemoteInterfaceProviderConnectionErrorHandler( const Closure& handler) override; bool GetRemoteApplicationID(uint32_t* remote_id) const override; - bool GetRemoteShellClientFactoryID( - uint32_t* shell_client_factory_id) const override; void AddRemoteIDCallback(const Closure& callback) override; bool AllowsInterface(const std::string& interface_name) const override; shell::mojom::InterfaceProvider* GetRemoteInterfaces() override; InterfaceRegistry* GetLocalRegistry() override; base::WeakPtr<Connection> GetWeakPtr() override; - void OnGotRemoteIDs(uint32_t target_application_id, - uint32_t shell_client_factory_id); + void OnGotInstanceID(uint32_t target_application_id); const std::string connection_url_; const std::string remote_url_; uint32_t remote_id_; - // The id of the shell_client_factory is only available once the callback from - // establishing the connection is made. - uint32_t shell_client_factory_id_; bool remote_ids_valid_; std::vector<Closure> remote_id_callbacks_;
diff --git a/mojo/shell/public/cpp/lib/shell_client_factory.cc b/mojo/shell/public/cpp/lib/shell_client_factory.cc deleted file mode 100644 index 14a3e79..0000000 --- a/mojo/shell/public/cpp/lib/shell_client_factory.cc +++ /dev/null
@@ -1,141 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <set> -#include <utility> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/thread_task_runner_handle.h" -#include "base/threading/platform_thread.h" -#include "mojo/common/url_type_converters.h" -#include "mojo/message_pump/message_pump_mojo.h" -#include "mojo/public/cpp/bindings/strong_binding.h" -#include "mojo/shell/public/cpp/connection.h" -#include "mojo/shell/public/cpp/interface_factory_impl.h" -#include "mojo/shell/public/cpp/shell_client_factory.h" -#include "url/gurl.h" - -namespace mojo { - -namespace { - -class ApplicationThread : public base::PlatformThread::Delegate { - public: - ApplicationThread( - scoped_refptr<base::SingleThreadTaskRunner> handler_thread, - const base::Callback<void(ApplicationThread*)>& termination_callback, - ShellClientFactory::Delegate* handler_delegate, - InterfaceRequest<shell::mojom::ShellClient> request, - const GURL& url, - const Callback<void()>& destruct_callback) - : handler_thread_(handler_thread), - termination_callback_(termination_callback), - handler_delegate_(handler_delegate), - request_(std::move(request)), - url_(url), - destruct_callback_(destruct_callback) {} - - ~ApplicationThread() override { - destruct_callback_.Run(); - } - - private: - void ThreadMain() override { - handler_delegate_->CreateShellClient(std::move(request_), url_); - handler_thread_->PostTask(FROM_HERE, - base::Bind(termination_callback_, this)); - } - - scoped_refptr<base::SingleThreadTaskRunner> handler_thread_; - base::Callback<void(ApplicationThread*)> termination_callback_; - ShellClientFactory::Delegate* handler_delegate_; - InterfaceRequest<shell::mojom::ShellClient> request_; - GURL url_; - Callback<void()> destruct_callback_; - - DISALLOW_COPY_AND_ASSIGN(ApplicationThread); -}; - -class ShellClientFactoryImpl : public shell::mojom::ShellClientFactory { - public: - ShellClientFactoryImpl(mojo::ShellClientFactory::Delegate* delegate, - shell::mojom::ShellClientFactoryRequest request) - : delegate_(delegate), - binding_(this, std::move(request)), - weak_factory_(this) {} - ~ShellClientFactoryImpl() override { - // We're shutting down and doing cleanup. Cleanup may trigger calls back to - // OnThreadEnd(). As we're doing the cleanup here we don't want to do it in - // OnThreadEnd() as well. InvalidateWeakPtrs() ensures we don't get any - // calls to OnThreadEnd(). - weak_factory_.InvalidateWeakPtrs(); - for (auto thread : active_threads_) { - base::PlatformThread::Join(thread.second); - delete thread.first; - } - } - - private: - // Overridden from shell::mojom::ShellClientFactory: - void CreateShellClient(shell::mojom::ShellClientRequest request, - const String& url, - const Callback<void()>& destruct_callback) override { - ApplicationThread* thread = - new ApplicationThread(base::ThreadTaskRunnerHandle::Get(), - base::Bind(&ShellClientFactoryImpl::OnThreadEnd, - weak_factory_.GetWeakPtr()), - delegate_, std::move(request), url.To<GURL>(), - destruct_callback); - base::PlatformThreadHandle handle; - bool launched = base::PlatformThread::Create(0, thread, &handle); - DCHECK(launched); - active_threads_[thread] = handle; - } - - void OnThreadEnd(ApplicationThread* thread) { - DCHECK(active_threads_.find(thread) != active_threads_.end()); - base::PlatformThreadHandle handle = active_threads_[thread]; - active_threads_.erase(thread); - base::PlatformThread::Join(handle); - delete thread; - } - - mojo::ShellClientFactory::Delegate* delegate_; - std::map<ApplicationThread*, base::PlatformThreadHandle> active_threads_; - StrongBinding<shell::mojom::ShellClientFactory> binding_; - base::WeakPtrFactory<ShellClientFactoryImpl> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(ShellClientFactoryImpl); -}; - -} // namespace - -ShellClientFactory::ShellClientFactory(Delegate* delegate) - : delegate_(delegate) { -} - -ShellClientFactory::~ShellClientFactory() { -} - -void ShellClientFactory::ManagedDelegate::CreateShellClient( - shell::mojom::ShellClientRequest request, - const GURL& url) { - base::MessageLoop loop(common::MessagePumpMojo::Create()); - auto application = this->CreateShellClientManaged(std::move(request), url); - if (application) - loop.Run(); -} - -void ShellClientFactory::Create( - Connection* connection, - shell::mojom::ShellClientFactoryRequest request) { - new ShellClientFactoryImpl(delegate_, std::move(request)); -} - -} // namespace mojo
diff --git a/mojo/shell/public/cpp/shell_client_factory.h b/mojo/shell/public/cpp/shell_client_factory.h deleted file mode 100644 index 5c6e878..0000000 --- a/mojo/shell/public/cpp/shell_client_factory.h +++ /dev/null
@@ -1,84 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_PUBLIC_CPP_SHELL_CLIENT_FACTORY_H_ -#define MOJO_SHELL_PUBLIC_CPP_SHELL_CLIENT_FACTORY_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/services/network/public/interfaces/url_loader.mojom.h" -#include "mojo/shell/public/cpp/interface_factory.h" -#include "mojo/shell/public/interfaces/shell.mojom.h" -#include "mojo/shell/public/interfaces/shell_client_factory.mojom.h" - -class GURL; - -namespace mojo { - -class ShellClientFactory - : public InterfaceFactory<shell::mojom::ShellClientFactory> { - public: - class HandledApplicationHolder { - public: - virtual ~HandledApplicationHolder() {} - }; - - class Delegate { - public: - virtual ~Delegate() {} - // Implement this method to create the Application. This method will be - // called on a new thread. Leaving this method will quit the application. - virtual void CreateShellClient( - shell::mojom::ShellClientRequest request, - const GURL& url) = 0; - }; - - class ManagedDelegate : public Delegate { - public: - ~ManagedDelegate() override {} - // Implement this method to create the Application for the given content. - // This method will be called on a new thread. The application will be run - // on this new thread, and the returned value will be kept alive until the - // application ends. - virtual scoped_ptr<HandledApplicationHolder> CreateShellClientManaged( - shell::mojom::ShellClientRequest request, - const GURL& url) = 0; - - private: - void CreateShellClient(shell::mojom::ShellClientRequest request, - const GURL& url) override; - }; - - explicit ShellClientFactory(Delegate* delegate); - ~ShellClientFactory() override; - - private: - // InterfaceFactory<shell::mojom::ShellClientFactory>: - void Create(Connection* connection, - shell::mojom::ShellClientFactoryRequest request) override; - - Delegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(ShellClientFactory); -}; - -template <class A> -class HandledApplicationHolderImpl - : public ShellClientFactory::HandledApplicationHolder { - public: - explicit HandledApplicationHolderImpl(A* value) : value_(value) {} - - private: - scoped_ptr<A> value_; -}; - -template <class A> -scoped_ptr<ShellClientFactory::HandledApplicationHolder> -make_handled_factory_holder(A* value) { - return make_scoped_ptr(new HandledApplicationHolderImpl<A>(value)); -} - -} // namespace mojo - -#endif // MOJO_SHELL_PUBLIC_CPP_SHELL_CLIENT_FACTORY_H_
diff --git a/mojo/shell/public/interfaces/shell.mojom b/mojo/shell/public/interfaces/shell.mojom index c3f15be..c512606 100644 --- a/mojo/shell/public/interfaces/shell.mojom +++ b/mojo/shell/public/interfaces/shell.mojom
@@ -56,16 +56,10 @@ // |filter| is a whitelist of application URLs and services that the target // application is permitted to connect to. See documentation for // CapabilityFilter above. - // - // If the connection to |application_url| involves a content handler, then - // |content_handler_id| is the id of the deepest content handler used to - // establish the connection to |application_url|. If no content handler is - // used |content_handler_id| is kInvalidApplicationID. ConnectToApplication(mojo.URLRequest application_url, InterfaceProvider&? remote_interfaces, InterfaceProvider? local_interfaces, - CapabilityFilter filter) => - (uint32 application_id, uint32 content_handler_id); + CapabilityFilter filter) => (uint32 application_id); // When there are no more instantiated services in an application, it should // start its shutdown process by calling this method. Additionally, it should
diff --git a/mojo/shell/public/interfaces/shell_client_factory.mojom b/mojo/shell/public/interfaces/shell_client_factory.mojom index 71799468..8137271d 100644 --- a/mojo/shell/public/interfaces/shell_client_factory.mojom +++ b/mojo/shell/public/interfaces/shell_client_factory.mojom
@@ -9,5 +9,5 @@ // Implemented by a package containing multiple applications identified by // unique URLs. interface ShellClientFactory { - CreateShellClient(ShellClient& shell_client, string url) => (); + CreateShellClient(ShellClient& shell_client, string url); };
diff --git a/mojo/shell/runner/host/BUILD.gn b/mojo/shell/runner/host/BUILD.gn index 8741563..1f0d194 100644 --- a/mojo/shell/runner/host/BUILD.gn +++ b/mojo/shell/runner/host/BUILD.gn
@@ -37,12 +37,31 @@ check_includes = false } +source_set("child_process_base") { + sources = [ + "child_process_base.cc", + "child_process_base.h", + ] + + deps = [ + "//base", + "//mojo/edk/system", + "//mojo/message_pump", + "//mojo/platform_handle:platform_handle_impl", + "//mojo/shell", + "//mojo/shell/runner:init", + "//mojo/shell/runner/child:interfaces", + "//mojo/shell/runner/common", + ] +} + source_set("lib") { sources = [ "child_process.cc", "child_process.h", "child_process_host.cc", "child_process_host.h", + "command_line_switch.h", "in_process_native_runner.cc", "in_process_native_runner.h", "out_of_process_native_runner.cc", @@ -50,6 +69,7 @@ ] deps = [ + ":child_process_base", ":native_application_support", "//base", "//base:base_static",
diff --git a/mojo/shell/runner/host/child_process.cc b/mojo/shell/runner/host/child_process.cc index 8f8c8a10..5111064 100644 --- a/mojo/shell/runner/host/child_process.cc +++ b/mojo/shell/runner/host/child_process.cc
@@ -35,6 +35,7 @@ #include "mojo/public/cpp/system/core.h" #include "mojo/shell/runner/child/child_controller.mojom.h" #include "mojo/shell/runner/common/switches.h" +#include "mojo/shell/runner/host/child_process_base.h" #include "mojo/shell/runner/host/native_application_support.h" #include "mojo/shell/runner/init.h" @@ -49,221 +50,6 @@ namespace { -// Blocker --------------------------------------------------------------------- - -// Blocks a thread until another thread unblocks it, at which point it unblocks -// and runs a closure provided by that thread. -class Blocker { - public: - class Unblocker { - public: - explicit Unblocker(Blocker* blocker = nullptr) : blocker_(blocker) {} - ~Unblocker() {} - - void Unblock(base::Closure run_after) { - DCHECK(blocker_); - DCHECK(blocker_->run_after_.is_null()); - blocker_->run_after_ = run_after; - blocker_->event_.Signal(); - blocker_ = nullptr; - } - - private: - Blocker* blocker_; - - // Copy and assign allowed. - }; - - Blocker() : event_(true, false) {} - ~Blocker() {} - - void Block() { - DCHECK(run_after_.is_null()); - event_.Wait(); - if (!run_after_.is_null()) - run_after_.Run(); - } - - Unblocker GetUnblocker() { return Unblocker(this); } - - private: - base::WaitableEvent event_; - base::Closure run_after_; - - DISALLOW_COPY_AND_ASSIGN(Blocker); -}; - -// AppContext ------------------------------------------------------------------ - -class ChildControllerImpl; - -// Should be created and initialized on the main thread. -class AppContext : public edk::ProcessDelegate { - public: - AppContext() - : io_thread_("io_thread"), controller_thread_("controller_thread") {} - ~AppContext() override {} - - void Init() { - // Initialize Mojo before starting any threads. - edk::Init(); - - // Create and start our I/O thread. - base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0); - CHECK(io_thread_.StartWithOptions(io_thread_options)); - io_runner_ = io_thread_.task_runner().get(); - CHECK(io_runner_.get()); - - // TODO(vtl): This should be SLAVE, not NONE. - // This must be created before controller_thread_ since MessagePumpMojo will - // create a message pipe which requires this code to be run first. - edk::InitIPCSupport(this, io_runner_); - } - - void StartControllerThread() { - // Create and start our controller thread. - base::Thread::Options controller_thread_options; - controller_thread_options.message_loop_type = - base::MessageLoop::TYPE_CUSTOM; - controller_thread_options.message_pump_factory = - base::Bind(&common::MessagePumpMojo::Create); - CHECK(controller_thread_.StartWithOptions(controller_thread_options)); - controller_runner_ = controller_thread_.task_runner().get(); - CHECK(controller_runner_.get()); - } - - void Shutdown() { - Blocker blocker; - shutdown_unblocker_ = blocker.GetUnblocker(); - controller_runner_->PostTask( - FROM_HERE, base::Bind(&AppContext::ShutdownOnControllerThread, - base::Unretained(this))); - blocker.Block(); - } - - base::SingleThreadTaskRunner* io_runner() const { return io_runner_.get(); } - - base::SingleThreadTaskRunner* controller_runner() const { - return controller_runner_.get(); - } - - ChildControllerImpl* controller() const { return controller_.get(); } - - void set_controller(scoped_ptr<ChildControllerImpl> controller) { - controller_ = std::move(controller); - } - - private: - void ShutdownOnControllerThread() { - // First, destroy the controller. - controller_.reset(); - - // Next shutdown IPC. We'll unblock the main thread in OnShutdownComplete(). - edk::ShutdownIPCSupport(); - } - - // ProcessDelegate implementation. - void OnShutdownComplete() override { - shutdown_unblocker_.Unblock(base::Closure()); - } - - base::Thread io_thread_; - scoped_refptr<base::SingleThreadTaskRunner> io_runner_; - - base::Thread controller_thread_; - scoped_refptr<base::SingleThreadTaskRunner> controller_runner_; - - // Accessed only on the controller thread. - scoped_ptr<ChildControllerImpl> controller_; - - // Used to unblock the main thread on shutdown. - Blocker::Unblocker shutdown_unblocker_; - - DISALLOW_COPY_AND_ASSIGN(AppContext); -}; - -// ChildControllerImpl ------------------------------------------------------ - -class ChildControllerImpl : public mojom::ChildController { - public: - ~ChildControllerImpl() override { - DCHECK(thread_checker_.CalledOnValidThread()); - - // TODO(vtl): Pass in the result from |MainMain()|. - on_app_complete_.Run(MOJO_RESULT_UNIMPLEMENTED); - } - - // To be executed on the controller thread. Creates the |ChildController|, - // etc. - static void Init(AppContext* app_context, - base::NativeLibrary app_library, - ScopedMessagePipeHandle host_message_pipe, - const Blocker::Unblocker& unblocker) { - DCHECK(app_context); - DCHECK(host_message_pipe.is_valid()); - - DCHECK(!app_context->controller()); - - scoped_ptr<ChildControllerImpl> impl( - new ChildControllerImpl(app_context, app_library, unblocker)); - - impl->Bind(std::move(host_message_pipe)); - - app_context->set_controller(std::move(impl)); - } - - void Bind(ScopedMessagePipeHandle handle) { - binding_.Bind(std::move(handle)); - binding_.set_connection_error_handler([this]() { OnConnectionError(); }); - } - - void OnConnectionError() { - // A connection error means the connection to the shell is lost. This is not - // recoverable. - LOG(ERROR) << "Connection error to the shell."; - _exit(1); - } - - // |ChildController| methods: - void StartApp(InterfaceRequest<mojom::ShellClient> request, - const StartAppCallback& on_app_complete) override { - DCHECK(thread_checker_.CalledOnValidThread()); - - on_app_complete_ = on_app_complete; - unblocker_.Unblock(base::Bind(&ChildControllerImpl::StartAppOnMainThread, - base::Unretained(app_library_), - base::Passed(&request))); - } - - void ExitNow(int32_t exit_code) override { - DVLOG(2) << "ChildControllerImpl::ExitNow(" << exit_code << ")"; - _exit(exit_code); - } - - private: - ChildControllerImpl(AppContext* app_context, - base::NativeLibrary app_library, - const Blocker::Unblocker& unblocker) - : app_library_(app_library), unblocker_(unblocker), binding_(this) {} - - static void StartAppOnMainThread( - base::NativeLibrary app_library, - InterfaceRequest<mojom::ShellClient> request) { - if (!RunNativeApplication(app_library, std::move(request))) { - LOG(ERROR) << "Failure to RunNativeApplication()"; - } - } - - base::ThreadChecker thread_checker_; - base::NativeLibrary app_library_; - Blocker::Unblocker unblocker_; - StartAppCallback on_app_complete_; - - Binding<ChildController> binding_; - - DISALLOW_COPY_AND_ASSIGN(ChildControllerImpl); -}; - #if defined(OS_LINUX) && !defined(OS_ANDROID) scoped_ptr<mojo::shell::LinuxSandbox> InitializeSandbox() { using sandbox::syscall_broker::BrokerFilePermission; @@ -290,14 +76,12 @@ } #endif -ScopedMessagePipeHandle InitializeHostMessagePipe( - edk::ScopedPlatformHandle platform_channel, - scoped_refptr<base::TaskRunner> io_task_runner) { - edk::SetParentPipeHandle(std::move(platform_channel)); - std::string primordial_pipe_token = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kPrimordialPipeToken); - return edk::CreateChildMessagePipe(primordial_pipe_token); +void RunNativeLibrary( + base::NativeLibrary app_library, + InterfaceRequest<mojom::ShellClient> shell_client_request) { + if (!RunNativeApplication(app_library, std::move(shell_client_request))) { + LOG(ERROR) << "Failure to RunNativeApplication()"; + } } } // namespace @@ -330,28 +114,7 @@ sandbox = InitializeSandbox(); #endif - edk::ScopedPlatformHandle platform_channel = - edk::PlatformChannelPair::PassClientHandleFromParentProcess(command_line); - CHECK(platform_channel.is_valid()); - - DCHECK(!base::MessageLoop::current()); - - Blocker blocker; - AppContext app_context; - app_context.Init(); - app_context.StartControllerThread(); - - ScopedMessagePipeHandle host_pipe = InitializeHostMessagePipe( - std::move(platform_channel), app_context.io_runner()); - app_context.controller_runner()->PostTask( - FROM_HERE, - base::Bind(&ChildControllerImpl::Init, &app_context, app_library, - base::Passed(&host_pipe), blocker.GetUnblocker())); - - // This will block, then run whatever the controller wants. - blocker.Block(); - - app_context.Shutdown(); + ChildProcessMain(base::Bind(&RunNativeLibrary, app_library)); return 0; }
diff --git a/mojo/shell/runner/host/child_process_base.cc b/mojo/shell/runner/host/child_process_base.cc new file mode 100644 index 0000000..a1c3f1e --- /dev/null +++ b/mojo/shell/runner/host/child_process_base.cc
@@ -0,0 +1,288 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/shell/runner/host/child_process_base.h" + +#include <stdint.h> + +#include <utility> + +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/command_line.h" +#include "base/debug/stack_trace.h" +#include "base/files/file_path.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/waitable_event.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/thread.h" +#include "base/threading/thread_checker.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/platform_channel_pair.h" +#include "mojo/edk/embedder/process_delegate.h" +#include "mojo/edk/embedder/scoped_platform_handle.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/shell/runner/child/child_controller.mojom.h" +#include "mojo/shell/runner/common/switches.h" +#include "mojo/shell/runner/init.h" + +namespace mojo { +namespace shell { + +namespace { + +// Blocker --------------------------------------------------------------------- + +// Blocks a thread until another thread unblocks it, at which point it unblocks +// and runs a closure provided by that thread. +class Blocker { + public: + class Unblocker { + public: + explicit Unblocker(Blocker* blocker = nullptr) : blocker_(blocker) {} + ~Unblocker() {} + + void Unblock(base::Closure run_after) { + DCHECK(blocker_); + DCHECK(blocker_->run_after_.is_null()); + blocker_->run_after_ = run_after; + blocker_->event_.Signal(); + blocker_ = nullptr; + } + + private: + Blocker* blocker_; + + // Copy and assign allowed. + }; + + Blocker() : event_(true, false) {} + ~Blocker() {} + + void Block() { + DCHECK(run_after_.is_null()); + event_.Wait(); + if (!run_after_.is_null()) + run_after_.Run(); + } + + Unblocker GetUnblocker() { return Unblocker(this); } + + private: + base::WaitableEvent event_; + base::Closure run_after_; + + DISALLOW_COPY_AND_ASSIGN(Blocker); +}; + +// AppContext ------------------------------------------------------------------ + +class ChildControllerImpl; + +// Should be created and initialized on the main thread. +class AppContext : public edk::ProcessDelegate { + public: + AppContext() + : io_thread_("io_thread"), controller_thread_("controller_thread") {} + ~AppContext() override {} + + void Init() { + // Initialize Mojo before starting any threads. + edk::Init(); + + // Create and start our I/O thread. + base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0); + CHECK(io_thread_.StartWithOptions(io_thread_options)); + io_runner_ = io_thread_.task_runner().get(); + CHECK(io_runner_.get()); + + // TODO(vtl): This should be SLAVE, not NONE. + // This must be created before controller_thread_ since MessagePumpMojo will + // create a message pipe which requires this code to be run first. + edk::InitIPCSupport(this, io_runner_); + } + + void StartControllerThread() { + // Create and start our controller thread. + base::Thread::Options controller_thread_options; + controller_thread_options.message_loop_type = + base::MessageLoop::TYPE_CUSTOM; + controller_thread_options.message_pump_factory = + base::Bind(&common::MessagePumpMojo::Create); + CHECK(controller_thread_.StartWithOptions(controller_thread_options)); + controller_runner_ = controller_thread_.task_runner().get(); + CHECK(controller_runner_.get()); + } + + void Shutdown() { + Blocker blocker; + shutdown_unblocker_ = blocker.GetUnblocker(); + controller_runner_->PostTask( + FROM_HERE, base::Bind(&AppContext::ShutdownOnControllerThread, + base::Unretained(this))); + blocker.Block(); + } + + base::SingleThreadTaskRunner* io_runner() const { return io_runner_.get(); } + + base::SingleThreadTaskRunner* controller_runner() const { + return controller_runner_.get(); + } + + ChildControllerImpl* controller() const { return controller_.get(); } + + void set_controller(scoped_ptr<ChildControllerImpl> controller) { + controller_ = std::move(controller); + } + + private: + void ShutdownOnControllerThread() { + // First, destroy the controller. + controller_.reset(); + + // Next shutdown IPC. We'll unblock the main thread in OnShutdownComplete(). + edk::ShutdownIPCSupport(); + } + + // ProcessDelegate implementation. + void OnShutdownComplete() override { + shutdown_unblocker_.Unblock(base::Closure()); + } + + base::Thread io_thread_; + scoped_refptr<base::SingleThreadTaskRunner> io_runner_; + + base::Thread controller_thread_; + scoped_refptr<base::SingleThreadTaskRunner> controller_runner_; + + // Accessed only on the controller thread. + scoped_ptr<ChildControllerImpl> controller_; + + // Used to unblock the main thread on shutdown. + Blocker::Unblocker shutdown_unblocker_; + + DISALLOW_COPY_AND_ASSIGN(AppContext); +}; + +// ChildControllerImpl ------------------------------------------------------ + +class ChildControllerImpl : public mojom::ChildController { + public: + ~ChildControllerImpl() override { + DCHECK(thread_checker_.CalledOnValidThread()); + + // TODO(vtl): Pass in the result from |MainMain()|. + on_app_complete_.Run(MOJO_RESULT_UNIMPLEMENTED); + } + + // To be executed on the controller thread. Creates the |ChildController|, + // etc. + static void Init(AppContext* app_context, + const RunCallback& run_callback, + ScopedMessagePipeHandle host_message_pipe, + const Blocker::Unblocker& unblocker) { + DCHECK(app_context); + DCHECK(host_message_pipe.is_valid()); + + DCHECK(!app_context->controller()); + + scoped_ptr<ChildControllerImpl> impl( + new ChildControllerImpl(app_context, run_callback, unblocker)); + + impl->Bind(std::move(host_message_pipe)); + + app_context->set_controller(std::move(impl)); + } + + void Bind(ScopedMessagePipeHandle handle) { + binding_.Bind(std::move(handle)); + binding_.set_connection_error_handler([this]() { OnConnectionError(); }); + } + + void OnConnectionError() { + // A connection error means the connection to the shell is lost. This is not + // recoverable. + LOG(ERROR) << "Connection error to the shell."; + _exit(1); + } + + // |ChildController| methods: + void StartApp(InterfaceRequest<mojom::ShellClient> request, + const StartAppCallback& on_app_complete) override { + DCHECK(thread_checker_.CalledOnValidThread()); + + on_app_complete_ = on_app_complete; + unblocker_.Unblock(base::Bind(run_callback_, base::Passed(&request))); + } + + void ExitNow(int32_t exit_code) override { + DVLOG(2) << "ChildControllerImpl::ExitNow(" << exit_code << ")"; + _exit(exit_code); + } + + private: + ChildControllerImpl(AppContext* app_context, + const RunCallback& run_callback, + const Blocker::Unblocker& unblocker) + : run_callback_(run_callback), unblocker_(unblocker), binding_(this) {} + + base::ThreadChecker thread_checker_; + RunCallback run_callback_; + Blocker::Unblocker unblocker_; + StartAppCallback on_app_complete_; + + Binding<ChildController> binding_; + + DISALLOW_COPY_AND_ASSIGN(ChildControllerImpl); +}; + +ScopedMessagePipeHandle InitializeHostMessagePipe( + edk::ScopedPlatformHandle platform_channel, + scoped_refptr<base::TaskRunner> io_task_runner) { + edk::SetParentPipeHandle(std::move(platform_channel)); + std::string primordial_pipe_token = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kPrimordialPipeToken); + return edk::CreateChildMessagePipe(primordial_pipe_token); +} + +} // namespace + +void ChildProcessMain(const RunCallback& callback) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + edk::ScopedPlatformHandle platform_channel = + edk::PlatformChannelPair::PassClientHandleFromParentProcess(command_line); + CHECK(platform_channel.is_valid()); + + DCHECK(!base::MessageLoop::current()); + + Blocker blocker; + AppContext app_context; + app_context.Init(); + app_context.StartControllerThread(); + + ScopedMessagePipeHandle host_pipe = InitializeHostMessagePipe( + std::move(platform_channel), app_context.io_runner()); + app_context.controller_runner()->PostTask( + FROM_HERE, base::Bind(&ChildControllerImpl::Init, &app_context, callback, + base::Passed(&host_pipe), blocker.GetUnblocker())); + + // This will block, then run whatever the controller wants. + blocker.Block(); + + app_context.Shutdown(); +} + +} // namespace shell +} // namespace mojo
diff --git a/mojo/shell/runner/host/child_process_base.h b/mojo/shell/runner/host/child_process_base.h new file mode 100644 index 0000000..49c5465 --- /dev/null +++ b/mojo/shell/runner/host/child_process_base.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 MOJO_SHELL_RUNNER_HOST_CHILD_PROCESS_BASE_H_ +#define MOJO_SHELL_RUNNER_HOST_CHILD_PROCESS_BASE_H_ + +#include "base/callback.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/shell/public/interfaces/shell_client.mojom.h" + +namespace mojo { +namespace shell { + +// Child processes call this to establish the connection to the shell and obtain +// the ShellClientRequest. Once the connection has been established |callback| +// is run. ChildProcessMain() returns once the the callback completes. +using RunCallback = base::Callback<void(mojom::ShellClientRequest)>; +void ChildProcessMain(const RunCallback& callback); + +} // namespace shell +} // namespace mojo + +#endif // MOJO_SHELL_RUNNER_HOST_CHILD_PROCESS_BASE_H_
diff --git a/mojo/shell/runner/host/child_process_host.cc b/mojo/shell/runner/host/child_process_host.cc index df45660..ae79312 100644 --- a/mojo/shell/runner/host/child_process_host.cc +++ b/mojo/shell/runner/host/child_process_host.cc
@@ -22,6 +22,7 @@ #include "mojo/public/cpp/bindings/interface_ptr_info.h" #include "mojo/public/cpp/system/core.h" #include "mojo/shell/runner/common/switches.h" +#include "mojo/shell/runner/host/command_line_switch.h" #if defined(OS_LINUX) && !defined(OS_ANDROID) #include "sandbox/linux/services/namespace_sandbox.h" @@ -34,13 +35,16 @@ namespace mojo { namespace shell { -ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, - bool start_sandboxed, - const base::FilePath& app_path) +ChildProcessHost::ChildProcessHost( + base::TaskRunner* launch_process_runner, + bool start_sandboxed, + const base::FilePath& app_path, + const std::vector<CommandLineSwitch>& command_line_switches) : launch_process_runner_(launch_process_runner), start_sandboxed_(start_sandboxed), app_path_(app_path), start_child_process_event_(false, false), + command_line_switches_(command_line_switches), weak_factory_(this) { node_channel_.reset(new edk::PlatformChannelPair); primordial_pipe_token_ = edk::GenerateRandomToken(); @@ -145,6 +149,9 @@ child_command_line.AppendSwitchASCII(switches::kPrimordialPipeToken, primordial_pipe_token_); + for (const CommandLineSwitch& pair : command_line_switches_) + child_command_line.AppendSwitchASCII(pair.key, pair.value); + base::LaunchOptions options; #if defined(OS_WIN) if (base::win::GetVersion() >= base::win::VERSION_VISTA) { @@ -158,6 +165,9 @@ options.stdin_handle = INVALID_HANDLE_VALUE; options.stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); options.stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + // Always inherit stdout/stderr as a pair. + if (!options.stdout_handle || !options.stdin_handle) + options.stdin_handle = options.stdout_handle = nullptr; // Pseudo handles are used when stdout and stderr redirect to the console. In // that case, they're automatically inherited by child processes. See @@ -166,10 +176,13 @@ // to fail. When this process is launched from Python // (i.e. by apptest_runner.py) then a real handle is used. In that case, we do // want to add it to the list of handles that is inherited. - if (GetFileType(options.stdout_handle) != FILE_TYPE_CHAR) + if (options.stdout_handle && + GetFileType(options.stdout_handle) != FILE_TYPE_CHAR) { handle_passing_info_.push_back(options.stdout_handle); - if (GetFileType(options.stderr_handle) != FILE_TYPE_CHAR && - options.stdout_handle != options.stdout_handle) { + } + if (options.stderr_handle && + GetFileType(options.stderr_handle) != FILE_TYPE_CHAR && + options.stdout_handle != options.stderr_handle) { handle_passing_info_.push_back(options.stderr_handle); } #elif defined(OS_POSIX)
diff --git a/mojo/shell/runner/host/child_process_host.h b/mojo/shell/runner/host/child_process_host.h index 27e0196..69e7e4fa 100644 --- a/mojo/shell/runner/host/child_process_host.h +++ b/mojo/shell/runner/host/child_process_host.h
@@ -29,6 +29,8 @@ namespace mojo { namespace shell { +struct CommandLineSwitch; + // This class represents a "child process host". Handles launching and // connecting a platform-specific "pipe" to the child, and supports joining the // child process. Currently runs a single app (loaded from the file system). @@ -48,7 +50,8 @@ // mojo application we wish to start. ChildProcessHost(base::TaskRunner* launch_process_runner, bool start_sandboxed, - const base::FilePath& app_path); + const base::FilePath& app_path, + const std::vector<CommandLineSwitch>& switches); // Allows a ChildProcessHost to be instantiated for an existing channel // created by someone else (e.g. an app that launched its own process). explicit ChildProcessHost(ScopedHandle channel); @@ -95,6 +98,8 @@ // A token the child can use to connect a primordial pipe to the host. std::string primordial_pipe_token_; + const std::vector<CommandLineSwitch> command_line_switches_; + base::WeakPtrFactory<ChildProcessHost> weak_factory_; DISALLOW_COPY_AND_ASSIGN(ChildProcessHost);
diff --git a/mojo/shell/runner/host/child_process_host_unittest.cc b/mojo/shell/runner/host/child_process_host_unittest.cc index 01789bc..64a63fe 100644 --- a/mojo/shell/runner/host/child_process_host_unittest.cc +++ b/mojo/shell/runner/host/child_process_host_unittest.cc
@@ -18,6 +18,7 @@ #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/embedder/process_delegate.h" #include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/shell/runner/host/command_line_switch.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { @@ -64,7 +65,8 @@ edk::InitIPCSupport(&delegate, io_thread.task_runner()); ChildProcessHost child_process_host(blocking_pool.get(), false, - base::FilePath()); + base::FilePath(), + std::vector<CommandLineSwitch>()); base::RunLoop run_loop; child_process_host.Start( base::Bind(&ProcessReadyCallbackAdapater, run_loop.QuitClosure()));
diff --git a/mojo/shell/runner/host/command_line_switch.h b/mojo/shell/runner/host/command_line_switch.h new file mode 100644 index 0000000..8e5224e2 --- /dev/null +++ b/mojo/shell/runner/host/command_line_switch.h
@@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_RUNNER_HOST_COMMAND_LINE_SWITCH_H_ +#define MOJO_SHELL_RUNNER_HOST_COMMAND_LINE_SWITCH_H_ + +#include <string> + +namespace mojo { +namespace shell { + +struct CommandLineSwitch { + CommandLineSwitch() : is_switch(true) {} + + // If false only the key is used and the switch is treated as a single value. + bool is_switch; + std::string key; + std::string value; +}; + +} // namespace shell +} // namespace mojo + +#endif // MOJO_SHELL_RUNNER_HOST_COMMAND_LINE_SWITCH_H_
diff --git a/mojo/shell/runner/host/in_process_native_runner.cc b/mojo/shell/runner/host/in_process_native_runner.cc index 56630d3..69738a69 100644 --- a/mojo/shell/runner/host/in_process_native_runner.cc +++ b/mojo/shell/runner/host/in_process_native_runner.cc
@@ -12,6 +12,7 @@ #include "base/task_runner.h" #include "base/thread_task_runner_handle.h" #include "base/threading/platform_thread.h" +#include "mojo/shell/runner/host/command_line_switch.h" #include "mojo/shell/runner/host/native_application_support.h" #include "mojo/shell/runner/host/out_of_process_native_runner.h" #include "mojo/shell/runner/init.h" @@ -82,8 +83,8 @@ const base::FilePath& app_path) { // Non-Mojo apps are always run in a new process. if (!app_path.MatchesExtension(FILE_PATH_LITERAL(".mojo"))) { - return make_scoped_ptr( - new OutOfProcessNativeRunner(launch_process_runner_)); + return make_scoped_ptr(new OutOfProcessNativeRunner( + launch_process_runner_, std::vector<CommandLineSwitch>())); } return make_scoped_ptr(new InProcessNativeRunner); }
diff --git a/mojo/shell/runner/host/out_of_process_native_runner.cc b/mojo/shell/runner/host/out_of_process_native_runner.cc index b34f5ee5..3ac446c5 100644 --- a/mojo/shell/runner/host/out_of_process_native_runner.cc +++ b/mojo/shell/runner/host/out_of_process_native_runner.cc
@@ -14,14 +14,17 @@ #include "base/logging.h" #include "base/task_runner.h" #include "mojo/shell/runner/host/child_process_host.h" +#include "mojo/shell/runner/host/command_line_switch.h" #include "mojo/shell/runner/host/in_process_native_runner.h" namespace mojo { namespace shell { OutOfProcessNativeRunner::OutOfProcessNativeRunner( - base::TaskRunner* launch_process_runner) - : launch_process_runner_(launch_process_runner) {} + base::TaskRunner* launch_process_runner, + const std::vector<CommandLineSwitch>& command_line_switches) + : launch_process_runner_(launch_process_runner), + command_line_switches_(command_line_switches) {} OutOfProcessNativeRunner::~OutOfProcessNativeRunner() { if (child_process_host_ && !app_path_.empty()) @@ -39,8 +42,9 @@ DCHECK(app_completed_callback_.is_null()); app_completed_callback_ = app_completed_callback; - child_process_host_.reset( - new ChildProcessHost(launch_process_runner_, start_sandboxed, app_path)); + child_process_host_.reset(new ChildProcessHost(launch_process_runner_, + start_sandboxed, app_path, + command_line_switches_)); child_process_host_->Start(base::Bind( &OutOfProcessNativeRunner::OnProcessLaunched, base::Unretained(this), base::Passed(&request), pid_available_callback)); @@ -80,9 +84,17 @@ pid_available_callback.Run(pid); } +OutOfProcessNativeRunnerFactory::OutOfProcessNativeRunnerFactory( + base::TaskRunner* launch_process_runner, + const std::vector<CommandLineSwitch>& command_line_switches) + : launch_process_runner_(launch_process_runner), + command_line_switches_(command_line_switches) {} +OutOfProcessNativeRunnerFactory::~OutOfProcessNativeRunnerFactory() {} + scoped_ptr<shell::NativeRunner> OutOfProcessNativeRunnerFactory::Create( const base::FilePath& app_path) { - return make_scoped_ptr(new OutOfProcessNativeRunner(launch_process_runner_)); + return make_scoped_ptr(new OutOfProcessNativeRunner(launch_process_runner_, + command_line_switches_)); } } // namespace shell
diff --git a/mojo/shell/runner/host/out_of_process_native_runner.h b/mojo/shell/runner/host/out_of_process_native_runner.h index 139dbdd..0edb0aa 100644 --- a/mojo/shell/runner/host/out_of_process_native_runner.h +++ b/mojo/shell/runner/host/out_of_process_native_runner.h
@@ -7,6 +7,8 @@ #include <stdint.h> +#include <vector> + #include "base/callback.h" #include "base/files/file_path.h" #include "base/macros.h" @@ -21,12 +23,15 @@ namespace shell { class ChildProcessHost; +struct CommandLineSwitch; // An implementation of |NativeRunner| that loads/runs the given app (from the // file system) in a separate process (of its own). class OutOfProcessNativeRunner : public NativeRunner { public: - explicit OutOfProcessNativeRunner(base::TaskRunner* launch_process_runner); + OutOfProcessNativeRunner( + base::TaskRunner* launch_process_runner, + const std::vector<CommandLineSwitch>& command_line_switches); ~OutOfProcessNativeRunner() override; // NativeRunner: @@ -55,6 +60,8 @@ base::FilePath app_path_; base::Closure app_completed_callback_; + std::vector<CommandLineSwitch> command_line_switches_; + scoped_ptr<ChildProcessHost> child_process_host_; DISALLOW_COPY_AND_ASSIGN(OutOfProcessNativeRunner); @@ -62,15 +69,16 @@ class OutOfProcessNativeRunnerFactory : public NativeRunnerFactory { public: - explicit OutOfProcessNativeRunnerFactory( - base::TaskRunner* launch_process_runner) - : launch_process_runner_(launch_process_runner) {} - ~OutOfProcessNativeRunnerFactory() override {} + OutOfProcessNativeRunnerFactory( + base::TaskRunner* launch_process_runner, + const std::vector<CommandLineSwitch>& command_line_switches); + ~OutOfProcessNativeRunnerFactory() override; scoped_ptr<NativeRunner> Create(const base::FilePath& app_path) override; private: base::TaskRunner* const launch_process_runner_; + std::vector<CommandLineSwitch> command_line_switches_; DISALLOW_COPY_AND_ASSIGN(OutOfProcessNativeRunnerFactory); };
diff --git a/mojo/shell/shell_application_delegate.cc b/mojo/shell/shell_application_delegate.cc deleted file mode 100644 index b4886104..0000000 --- a/mojo/shell/shell_application_delegate.cc +++ /dev/null
@@ -1,51 +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. - -#include "mojo/shell/shell_application_delegate.h" - -#include <stdint.h> - -#include <utility> - -#include "base/process/process.h" -#include "mojo/shell/application_manager.h" -#include "mojo/shell/public/cpp/connection.h" - -namespace mojo { -namespace shell { - -ShellApplicationDelegate::ShellApplicationDelegate( - mojo::shell::ApplicationManager* manager) - : manager_(manager) {} -ShellApplicationDelegate::~ShellApplicationDelegate() {} - -void ShellApplicationDelegate::Initialize(Shell* shell, const std::string& url, - uint32_t id) {} -bool ShellApplicationDelegate::AcceptConnection(Connection* connection) { - connection->AddInterface<mojom::ApplicationManager>(this); - return true; -} - -void ShellApplicationDelegate::Create( - Connection* connection, - InterfaceRequest<mojom::ApplicationManager> request) { - bindings_.AddBinding(this, std::move(request)); -} - -void ShellApplicationDelegate::CreateInstanceForHandle( - ScopedHandle channel, - const String& url, - mojom::CapabilityFilterPtr filter, - InterfaceRequest<mojom::PIDReceiver> pid_receiver) { - manager_->CreateInstanceForHandle(std::move(channel), GURL(url.get()), - std::move(filter), std::move(pid_receiver)); -} - -void ShellApplicationDelegate::AddListener( - mojom::ApplicationManagerListenerPtr listener) { - manager_->AddListener(std::move(listener)); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/shell_application_delegate.h b/mojo/shell/shell_application_delegate.h deleted file mode 100644 index 4f0a558..0000000 --- a/mojo/shell/shell_application_delegate.h +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_SHELL_APPLICATION_DELEGATE_H_ -#define MOJO_SHELL_SHELL_APPLICATION_DELEGATE_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "mojo/public/cpp/bindings/weak_binding_set.h" -#include "mojo/shell/public/cpp/interface_factory.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/public/interfaces/application_manager.mojom.h" - -namespace mojo { -namespace shell { -class ApplicationManager; - -class ShellApplicationDelegate - : public ShellClient, - public InterfaceFactory<mojom::ApplicationManager>, - public mojom::ApplicationManager { - public: - explicit ShellApplicationDelegate(mojo::shell::ApplicationManager* manager); - ~ShellApplicationDelegate() override; - - private: - // Overridden from ShellClient: - void Initialize(Shell* shell, const std::string& url, uint32_t id) override; - bool AcceptConnection(Connection* connection) override; - - // Overridden from InterfaceFactory<mojom::ApplicationManager>: - void Create( - Connection* connection, - InterfaceRequest<mojom::ApplicationManager> request) override; - - // Overridden from mojom::ApplicationManager: - void CreateInstanceForHandle( - ScopedHandle channel, - const String& url, - mojom::CapabilityFilterPtr filter, - InterfaceRequest<mojom::PIDReceiver> pid_receiver) override; - void AddListener( - mojom::ApplicationManagerListenerPtr listener) override; - - mojo::shell::ApplicationManager* manager_; - - WeakBindingSet<mojom::ApplicationManager> bindings_; - - DISALLOW_COPY_AND_ASSIGN(ShellApplicationDelegate); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_SHELL_APPLICATION_DELEGATE_H_
diff --git a/mojo/shell/shell_application_loader.cc b/mojo/shell/shell_application_loader.cc deleted file mode 100644 index 7b55a8d..0000000 --- a/mojo/shell/shell_application_loader.cc +++ /dev/null
@@ -1,28 +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. - -#include "mojo/shell/shell_application_loader.h" - -#include <utility> - -#include "mojo/shell/public/cpp/shell_connection.h" -#include "mojo/shell/shell_application_delegate.h" - -namespace mojo { -namespace shell { - -ShellApplicationLoader::ShellApplicationLoader(ApplicationManager* manager) - : manager_(manager) {} -ShellApplicationLoader::~ShellApplicationLoader() {} - -void ShellApplicationLoader::Load( - const GURL& url, - InterfaceRequest<mojom::ShellClient> request) { - DCHECK(request.is_pending()); - shell_connection_.reset(new ShellConnection( - new ShellApplicationDelegate(manager_), std::move(request))); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/shell_application_loader.h b/mojo/shell/shell_application_loader.h deleted file mode 100644 index 6c3258a..0000000 --- a/mojo/shell/shell_application_loader.h +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_SHELL_APPLICATION_LOADER_H_ -#define MOJO_SHELL_SHELL_APPLICATION_LOADER_H_ - -#include "base/macros.h" -#include "mojo/shell/application_loader.h" - -namespace mojo { -class ShellConnection; -namespace shell { - -class ShellApplicationLoader : public ApplicationLoader { - public: - explicit ShellApplicationLoader(ApplicationManager* manager); - ~ShellApplicationLoader() override; - - private: - // Overridden from ApplicationLoader: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override; - - scoped_ptr<ShellConnection> shell_connection_; - - ApplicationManager* manager_; - - DISALLOW_COPY_AND_ASSIGN(ShellApplicationLoader); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_SHELL_APPLICATION_LOADER_H_
diff --git a/mojo/shell/shell_client_factory_connection.cc b/mojo/shell/shell_client_factory_connection.cc deleted file mode 100644 index 521d3a04..0000000 --- a/mojo/shell/shell_client_factory_connection.cc +++ /dev/null
@@ -1,78 +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. - -#include "mojo/shell/shell_client_factory_connection.h" - -#include <stdint.h> - -#include <utility> - -#include "base/bind.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/shell/application_manager.h" -#include "mojo/shell/connect_to_application_params.h" -#include "mojo/shell/identity.h" - -namespace mojo { -namespace shell { - -ShellClientFactoryConnection::ShellClientFactoryConnection( - ApplicationManager* manager, - const Identity& source, - const Identity& shell_client_factory, - uint32_t id, - const ClosedCallback& connection_closed_callback) - : connection_closed_callback_(connection_closed_callback), - identity_(shell_client_factory), - connection_closed_(false), - id_(id), - ref_count_(0) { - shell::mojom::InterfaceProviderPtr remote_interfaces; - - scoped_ptr<ConnectToApplicationParams> params(new ConnectToApplicationParams); - params->set_source(source); - params->SetTarget(identity_); - params->set_remote_interfaces(GetProxy(&remote_interfaces)); - manager->ConnectToApplication(std::move(params)); - - MessagePipe pipe; - shell_client_factory_.Bind( - InterfacePtrInfo<mojom::ShellClientFactory>(std::move(pipe.handle0), 0u)); - remote_interfaces->GetInterface(mojom::ShellClientFactory::Name_, - std::move(pipe.handle1)); - shell_client_factory_.set_connection_error_handler( - [this]() { CloseConnection(); }); -} - -void ShellClientFactoryConnection::CreateShellClient( - mojom::ShellClientRequest request, - const GURL& url) { - shell_client_factory_->CreateShellClient( - std::move(request), url.spec(), - base::Bind(&ShellClientFactoryConnection::ApplicationDestructed, - base::Unretained(this))); - ref_count_++; -} - -void ShellClientFactoryConnection::CloseConnection() { - if (connection_closed_) - return; - connection_closed_ = true; - connection_closed_callback_.Run(this); - delete this; -} - -ShellClientFactoryConnection::~ShellClientFactoryConnection() { - // If this DCHECK fails then something has tried to delete this object without - // calling CloseConnection. - DCHECK(connection_closed_); -} - -void ShellClientFactoryConnection::ApplicationDestructed() { - if (!--ref_count_) - CloseConnection(); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/shell_client_factory_connection.h b/mojo/shell/shell_client_factory_connection.h deleted file mode 100644 index e35bda7..0000000 --- a/mojo/shell/shell_client_factory_connection.h +++ /dev/null
@@ -1,66 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_SHELL_CLIENT_FACTORY_CONNECTION_H_ -#define MOJO_SHELL_SHELL_CLIENT_FACTORY_CONNECTION_H_ - -#include <stdint.h> - -#include <string> - -#include "base/callback.h" -#include "base/macros.h" -#include "mojo/shell/identity.h" -#include "mojo/shell/public/interfaces/shell_client_factory.mojom.h" -#include "url/gurl.h" - -namespace mojo { -namespace shell { -class ApplicationManager; - -// A ShellClientFactoryConnection is responsible for creating and maintaining a -// connection to an app which provides the ShellClientFactory service. -// A ShellClientFactoryConnection can only be destroyed via CloseConnection. -// A ShellClientFactoryConnection manages its own lifetime and cannot be used -// with a scoped_ptr to avoid reentrant calls into ApplicationManager late in -// destruction. -class ShellClientFactoryConnection { - public: - using ClosedCallback = base::Callback<void(ShellClientFactoryConnection*)>; - // |id| is a unique identifier for this content handler. - ShellClientFactoryConnection(ApplicationManager* manager, - const Identity& source, - const Identity& shell_client_factory, - uint32_t id, - const ClosedCallback& connection_closed_callback); - - void CreateShellClient(mojom::ShellClientRequest request, const GURL& url); - - // Closes the connection and destroys |this| object. - void CloseConnection(); - - const Identity& identity() const { return identity_; } - uint32_t id() const { return id_; } - - private: - ~ShellClientFactoryConnection(); - - void ApplicationDestructed(); - - ClosedCallback connection_closed_callback_; - Identity identity_; - - mojom::ShellClientFactoryPtr shell_client_factory_; - bool connection_closed_; - // The id for this content handler. - const uint32_t id_; - int ref_count_; - - DISALLOW_COPY_AND_ASSIGN(ShellClientFactoryConnection); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_SHELL_CLIENT_FACTORY_CONNECTION_H_
diff --git a/mojo/shell/standalone/BUILD.gn b/mojo/shell/standalone/BUILD.gn index 82160a1..09a4fa9a 100644 --- a/mojo/shell/standalone/BUILD.gn +++ b/mojo/shell/standalone/BUILD.gn
@@ -6,85 +6,33 @@ import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") -if (is_android) { - import("//build/config/android/config.gni") - import("//build/config/android/rules.gni") -} - -# main() entrypoint definition is structured into a static lib for Android's -# benefit, as it is shared between the standalone executable and other -# executables (e.g. Mandoline). -source_set("main_lib") { - sources = [] - +executable("standalone") { + output_name = "mojo_runner" + sources = [ + "desktop/main.cc", + ] deps = [ ":lib", "//base", "//build/config/sanitizers:deps", + "//components/tracing:startup_tracing", "//mojo/common", "//mojo/environment:chromium", "//mojo/message_pump", "//mojo/shell/runner/common", "//mojo/shell/runner/host:lib", + "//third_party/icu:icudata", ] - - if (!is_android) { - sources += [ "desktop/main.cc" ] - deps += [ - "//components/tracing:startup_tracing", - "//third_party/icu:icudata", - ] - } else { - sources += [ - "android/context_init.h", - "android/library_loader.cc", - "android/main.cc", - "android/main.h", - ] - - deps += [ - ":jni_headers", - "//base:i18n", - "//components/mus", - "//components/mus/ws:lib", - "//mojo/shell", - "//ui/gl", - "//ui/platform_window/android", - ] - } -} - -executable("standalone") { - output_name = "mojo_runner" - deps = [ - ":main_lib", - "//build/config/sanitizers:deps", - ] - - if (is_android) { - sources = [ - "android/context_init.cc", - ] - - deps += [ - ":lib", - "//mojo/edk/system", - ] - - # On android, the executable is also the native library used by the apk. - # It means dynamic symbols must be preserved and exported. - ldflags = [ "-Wl,--export-dynamic" ] - } } source_set("lib") { sources = [ "context.cc", "context.h", - "scoped_user_data_dir.cc", - "scoped_user_data_dir.h", - "task_runners.cc", - "task_runners.h", + "desktop/launcher_process.cc", + "desktop/launcher_process.h", + "desktop/main_helper.cc", + "desktop/main_helper.h", "tracer.cc", "tracer.h", ] @@ -96,7 +44,6 @@ "//components/tracing:startup_tracing", "//mojo/edk/system", "//mojo/message_pump", - "//mojo/services/network/public/interfaces", "//mojo/services/tracing/public/cpp", "//mojo/services/tracing/public/interfaces", "//mojo/shell", @@ -110,7 +57,6 @@ ] public_deps = [ - ":switches", "//mojo/shell", "//mojo/shell/runner:init", ] @@ -121,33 +67,6 @@ ] } - if (is_android) { - sources += [ - "android/android_handler.cc", - "android/android_handler.h", - "android/android_handler_loader.cc", - "android/android_handler_loader.h", - "android/background_application_loader.cc", - "android/background_application_loader.h", - "android/ui_application_loader_android.cc", - "android/ui_application_loader_android.h", - ] - - deps += [ - ":jni_headers", - ":run_android_application_function", - "//components/mus/gles2", - "//mojo/shell/public/cpp:shell_client_factory", - ] - } else { - sources += [ - "desktop/launcher_process.cc", - "desktop/launcher_process.h", - "desktop/main_helper.cc", - "desktop/main_helper.h", - ] - } - # This target includes some files behind #ifdef OS... guards. Since gn is not # smart enough to understand preprocess includes, it does complains about # these includes when not using the build files for that OS. Suppress checking @@ -156,133 +75,3 @@ # separate source_set so we can leave checking on for the rest of the target. check_includes = false } - -source_set("switches") { - sources = [ - "switches.cc", - "switches.h", - ] - - deps = [ - "//base", - ] -} - -if (is_android) { - generate_jni("jni_headers") { - sources = [ - "android/apk/src/org/chromium/mojo/shell/AndroidHandler.java", - "android/apk/src/org/chromium/mojo/shell/Bootstrap.java", - "android/apk/src/org/chromium/mojo/shell/ShellMain.java", - ] - jni_package = "mojo/shell" - } - - android_library("bootstrap_java") { - java_files = [ "android/apk/src/org/chromium/mojo/shell/Bootstrap.java" ] - - deps = [ - "//base:base_java", - ] - - dex_path = "$target_out_dir/bootstrap_java.dex.jar" - } - - shared_library("bootstrap") { - sources = [ - "android/bootstrap.cc", - ] - deps = [ - ":jni_headers", - ":lib", - ":run_android_application_function", - "//base", - "//build/config/sanitizers:deps", - ] - } - - # Shared header between the bootstrap and the main shell .so. - source_set("run_android_application_function") { - sources = [ - "android/run_android_application_function.h", - ] - - deps = [ - "//base", - ] - } - - android_library("java") { - java_files = [ - "android/apk/src/org/chromium/mojo/shell/AndroidHandler.java", - "android/apk/src/org/chromium/mojo/shell/FileHelper.java", - "android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java", - "android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java", - "android/apk/src/org/chromium/mojo/shell/ShellMain.java", - ] - - deps = [ - ":resources", - "//base:base_java", - ] - } - - android_resources("resources") { - resource_dirs = [ "android/apk/res" ] - custom_package = "org.chromium.mojo.shell" - } - - android_assets("android_assets") { - deps = [ - ":bootstrap", - ":bootstrap_java", - ] - sources = [ - "$root_out_dir/obj/mojo/shell/standalone/bootstrap_java.dex.jar", - "$root_shlib_dir/${shlib_prefix}bootstrap$shlib_extension", - ] - } - - android_assets("mojo_shell_standalone_apptests_assets") { - testonly = true - deps = [ - ":android_assets", - "//components/mus/ws:apptests_assets", - "//components/resource_provider:apptests_assets", - "//components/resource_provider:resource_provider_assets", - "//mojo/services/network:apptests_assets", - "//mojo/services/network:network_assets", - "//third_party/icu:icu_assets", - ] - } - - copy("copy_mojo_shell_standalone") { - sources = [ - "$root_out_dir/mojo_runner", - ] - outputs = [ - "$root_shlib_dir/${shlib_prefix}mojo_runner$shlib_extension", - ] - deps = [ - ":standalone", - ] - } - - android_apk("mojo_shell_standalone_apptests_apk") { - testonly = true - apk_name = "MojoRunnerApptests" - android_manifest = "android/apk/AndroidManifest.xml" - native_libs = [ "${shlib_prefix}mojo_runner$shlib_extension" ] - write_asset_list = true - - deps = [ - ":copy_mojo_shell_standalone", - ":java", - ":mojo_shell_standalone_apptests_assets", - ":resources", - "//base:base_java", - "//ui/platform_window/android:platform_window_java", - google_play_services_resources, - ] - } -}
diff --git a/mojo/shell/standalone/PRESUBMIT.py b/mojo/shell/standalone/PRESUBMIT.py deleted file mode 100644 index fb19030..0000000 --- a/mojo/shell/standalone/PRESUBMIT.py +++ /dev/null
@@ -1,16 +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. - -"""Presubmit script for shell. - -See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts -for more details about the presubmit API built into depot_tools. -""" - -def CheckChangeOnUpload(input_api, output_api): - results = [] - results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api, - output_api) - results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api) - return results
diff --git a/mojo/shell/standalone/android/android_handler.cc b/mojo/shell/standalone/android/android_handler.cc deleted file mode 100644 index 9e3de03..0000000 --- a/mojo/shell/standalone/android/android_handler.cc +++ /dev/null
@@ -1,180 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/shell/standalone/android/android_handler.h" - -#include <stddef.h> -#include <utility> - -#include "base/android/context_utils.h" -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/scoped_native_library.h" -#include "jni/AndroidHandler_jni.h" -#include "mojo/common/data_pipe_utils.h" -#include "mojo/public/c/system/main.h" -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/shell/runner/host/native_application_support.h" -#include "mojo/shell/standalone/android/run_android_application_function.h" -#include "mojo/util/filename_util.h" -#include "url/gurl.h" - -using base::android::AttachCurrentThread; -using base::android::ScopedJavaLocalRef; -using base::android::ConvertJavaStringToUTF8; -using base::android::ConvertUTF8ToJavaString; -using base::android::GetApplicationContext; - -namespace mojo { -namespace shell { - -namespace { - -// This function loads the application library, sets the application context and -// thunks and calls into the application MojoMain. To ensure that the thunks are -// set correctly we keep it in the Mojo shell .so and pass the function pointer -// to the helper libbootstrap.so. -void RunAndroidApplication(JNIEnv* env, - jobject j_context, - const base::FilePath& app_path, - jint j_handle) { - InterfaceRequest<mojom::ShellClient> request = - MakeRequest<mojom::ShellClient>( - MakeScopedHandle(MessagePipeHandle(j_handle))); - - // Load the library, so that we can set the application context there if - // needed. - // TODO(vtl): We'd use a ScopedNativeLibrary, but it doesn't have .get()! - base::NativeLibrary app_library = LoadNativeApplication(app_path); - if (!app_library) - return; - - // Set the application context if needed. Most applications will need to - // access the Android ApplicationContext in which they are run. If the - // application library exports the InitApplicationContext function, we will - // set it there. - const char* init_application_context_name = "InitApplicationContext"; - typedef void (*InitApplicationContextFn)( - const base::android::JavaRef<jobject>&); - InitApplicationContextFn init_application_context = - reinterpret_cast<InitApplicationContextFn>( - base::GetFunctionPointerFromNativeLibrary( - app_library, init_application_context_name)); - if (init_application_context) { - base::android::ScopedJavaLocalRef<jobject> scoped_context(env, j_context); - init_application_context(scoped_context); - } - - // Run the application. - RunNativeApplication(app_library, std::move(request)); - // TODO(vtl): See note about unloading and thread-local destructors above - // declaration of |LoadNativeApplication()|. - base::UnloadNativeLibrary(app_library); -} - -// Returns true if |url| denotes a cached app. If true |app_dir| is set to the -// path of the directory for the app and |path_to_mojo| the path of the app's -// .mojo file. -bool IsCachedApp(JNIEnv* env, - const GURL& url, - base::FilePath* app_dir, - base::FilePath* path_to_mojo) { - ScopedJavaLocalRef<jstring> j_cached_apps_dir = - Java_AndroidHandler_getCachedAppsDir(env, GetApplicationContext()); - const base::FilePath cached_apps_fp( - ConvertJavaStringToUTF8(env, j_cached_apps_dir.obj())); - const std::string cached_apps(util::FilePathToFileURL(cached_apps_fp).spec()); - const std::string response_url(url.spec()); - if (response_url.size() <= cached_apps.size() || - cached_apps.compare(0u, cached_apps.size(), response_url, 0u, - cached_apps.size()) != 0) { - return false; - } - - const std::string mojo_suffix(".mojo"); - // app_rel_path is either something like html_viewer/html_viewer.mojo, or - // html_viewer.mojo, depending upon whether the app has a package. - const std::string app_rel_path(response_url.substr(cached_apps.size() + 1)); - const size_t slash_index = app_rel_path.find('/'); - if (slash_index != std::string::npos) { - const std::string tail = - app_rel_path.substr(slash_index + 1, std::string::npos); - const std::string head = app_rel_path.substr(0, slash_index); - if (head.find('/') != std::string::npos || - tail.size() <= mojo_suffix.size() || - tail.compare(tail.size() - mojo_suffix.size(), tail.size(), - mojo_suffix) != 0) { - return false; - } - *app_dir = cached_apps_fp.Append(head); - *path_to_mojo = app_dir->Append(tail); - return true; - } - if (app_rel_path.find('/') != std::string::npos || - app_rel_path.size() <= mojo_suffix.size() || - app_rel_path.compare(app_rel_path.size() - mojo_suffix.size(), - mojo_suffix.size(), mojo_suffix) != 0) { - return false; - } - - *app_dir = cached_apps_fp.Append( - app_rel_path.substr(0, app_rel_path.size() - mojo_suffix.size())); - *path_to_mojo = cached_apps_fp.Append(app_rel_path); - return true; -} - -} // namespace - -AndroidHandler::AndroidHandler() : shell_client_factory_(this) {} - -AndroidHandler::~AndroidHandler() {} - -void AndroidHandler::CreateShellClient(mojom::ShellClientRequest request, - const GURL& url) { - JNIEnv* env = AttachCurrentThread(); - RunAndroidApplicationFn run_android_application_fn = &RunAndroidApplication; - base::FilePath internal_app_path; - base::FilePath path_to_mojo; - if (IsCachedApp(env, url, &internal_app_path, &path_to_mojo)) { - ScopedJavaLocalRef<jstring> j_internal_app_path( - ConvertUTF8ToJavaString(env, internal_app_path.value())); - ScopedJavaLocalRef<jstring> j_path_to_mojo( - ConvertUTF8ToJavaString(env, path_to_mojo.value())); - Java_AndroidHandler_bootstrapCachedApp( - env, GetApplicationContext(), j_path_to_mojo.obj(), - j_internal_app_path.obj(), - request.PassMessagePipe().release().value(), - reinterpret_cast<jlong>(run_android_application_fn)); - return; - } - ScopedJavaLocalRef<jstring> j_archive_path = - Java_AndroidHandler_getNewTempArchivePath(env, GetApplicationContext()); - base::FilePath archive_path( - ConvertJavaStringToUTF8(env, j_archive_path.obj())); - - // TODO(beng): fix or remove. - //common::BlockingCopyToFile(std::move(response->body), archive_path); - Java_AndroidHandler_bootstrap( - env, GetApplicationContext(), j_archive_path.obj(), - request.PassMessagePipe().release().value(), - reinterpret_cast<jlong>(run_android_application_fn)); -} - -void AndroidHandler::Initialize(Shell* shell, - const std::string& url, - uint32_t id) {} - -bool AndroidHandler::AcceptConnection(Connection* connection) { - connection->AddInterface(&shell_client_factory_); - return true; -} - -bool RegisterAndroidHandlerJni(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/standalone/android/android_handler.h b/mojo/shell/standalone/android/android_handler.h deleted file mode 100644 index 2e1bad0..0000000 --- a/mojo/shell/standalone/android/android_handler.h +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_STANDALONE_ANDROID_ANDROID_HANDLER_H_ -#define MOJO_SHELL_STANDALONE_ANDROID_ANDROID_HANDLER_H_ - -#include <jni.h> - -#include "mojo/shell/public/cpp/interface_factory_impl.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/public/cpp/shell_client_factory.h" -#include "mojo/shell/public/interfaces/shell_client_factory.mojom.h" - -namespace base { -class FilePath; -} - -namespace mojo { -namespace shell { - -class AndroidHandler : public ShellClient, - public ShellClientFactory::Delegate { - public: - AndroidHandler(); - ~AndroidHandler(); - - private: - // mojo::ShellClient: - void Initialize(Shell* shell, const std::string& url, uint32_t id) override; - bool AcceptConnection(Connection* connection) override; - - // ShellClientFactory::Delegate: - void CreateShellClient(mojom::ShellClientRequest request, - const GURL& url) override; - - ShellClientFactory shell_client_factory_; - MOJO_DISALLOW_COPY_AND_ASSIGN(AndroidHandler); -}; - -bool RegisterAndroidHandlerJni(JNIEnv* env); - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_ANDROID_ANDROID_HANDLER_H_
diff --git a/mojo/shell/standalone/android/android_handler_loader.cc b/mojo/shell/standalone/android/android_handler_loader.cc deleted file mode 100644 index 4ff4bab..0000000 --- a/mojo/shell/standalone/android/android_handler_loader.cc +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/shell/standalone/android/android_handler_loader.h" - -#include <utility> - -namespace mojo { -namespace shell { - -AndroidHandlerLoader::AndroidHandlerLoader() {} - -AndroidHandlerLoader::~AndroidHandlerLoader() {} - -void AndroidHandlerLoader::Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) { - DCHECK(request.is_pending()); - shell_client_.reset( - new ShellConnection(&android_handler_, std::move(request))); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/standalone/android/android_handler_loader.h b/mojo/shell/standalone/android/android_handler_loader.h deleted file mode 100644 index 5aac6b1f..0000000 --- a/mojo/shell/standalone/android/android_handler_loader.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_STANDALONE_ANDROID_ANDROID_HANDLER_LOADER_H_ -#define MOJO_SHELL_STANDALONE_ANDROID_ANDROID_HANDLER_LOADER_H_ - -#include "base/containers/scoped_ptr_hash_map.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/shell/application_loader.h" -#include "mojo/shell/public/cpp/shell_connection.h" -#include "mojo/shell/standalone/android/android_handler.h" - -namespace mojo { -namespace shell { - -class AndroidHandlerLoader : public ApplicationLoader { - public: - AndroidHandlerLoader(); - virtual ~AndroidHandlerLoader(); - - private: - // ApplicationLoader overrides: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override; - - AndroidHandler android_handler_; - scoped_ptr<ShellConnection> shell_client_; - - DISALLOW_COPY_AND_ASSIGN(AndroidHandlerLoader); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_ANDROID_ANDROID_HANDLER_LOADER_H_
diff --git a/mojo/shell/standalone/android/apk/AndroidManifest.xml b/mojo/shell/standalone/android/apk/AndroidManifest.xml deleted file mode 100644 index 95a704d0..0000000 --- a/mojo/shell/standalone/android/apk/AndroidManifest.xml +++ /dev/null
@@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- Copyright 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. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.chromium.mojo.shell"> - - <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" /> - <uses-permission android:name="android.permission.INTERNET"/> - <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> - - <application android:name="MojoShellApplication" - android:label="Mojo Shell"> - <meta-data android:name="com.google.android.gms.version" - android:value="@integer/google_play_services_version" /> - <meta-data android:name="mojo_lib" - android:value="libmojo_runner.so" /> - <activity android:name="MojoShellActivity" - android:launchMode="singleTask" - android:theme="@android:style/Theme.Holo.Light.NoActionBar" - android:configChanges="orientation|keyboardHidden|keyboard|screenSize" - android:hardwareAccelerated="true"> - <intent-filter> - <action android:name="android.intent.action.VIEW"/> - </intent-filter> - </activity> - </application> - -</manifest>
diff --git a/mojo/shell/standalone/android/apk/res/values/strings.xml b/mojo/shell/standalone/android/apk/res/values/strings.xml deleted file mode 100644 index ff3f8bb..0000000 --- a/mojo/shell/standalone/android/apk/res/values/strings.xml +++ /dev/null
@@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- Copyright 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. - --> - -<resources> -</resources>
diff --git a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java b/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java deleted file mode 100644 index 7a09c04..0000000 --- a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/AndroidHandler.java +++ /dev/null
@@ -1,258 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.mojo.shell; - -import android.content.Context; - -import dalvik.system.DexClassLoader; - -import org.chromium.base.Log; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.lang.reflect.Constructor; - -/** - * Content handler for archives containing native libraries bundled with Java code. - * <p> - * TODO(ppi): create a separate instance for each application being bootstrapped to keep track of - * the temporary files and clean them up once the execution finishes. - */ -@JNINamespace("mojo::shell") -public class AndroidHandler { - private static final String TAG = "AndroidHandler"; - - // Bootstrap native and java libraries are packaged with the MojoShell APK as assets. - private static final String BOOTSTRAP_JAVA_LIBRARY = "bootstrap_java.dex.jar"; - private static final String BOOTSTRAP_NATIVE_LIBRARY = "libbootstrap.so"; - // Name of the bootstrapping runnable shipped in the packaged Java library. - private static final String BOOTSTRAP_CLASS = "org.chromium.mojo.shell.Bootstrap"; - - // File extensions used to identify application libraries in the provided archive. - private static final String JAVA_LIBRARY_SUFFIX = ".dex.jar"; - private static final String NATIVE_LIBRARY_SUFFIX = ".so"; - // Filename sections used for naming temporary files holding application files. - private static final String ARCHIVE_PREFIX = "archive"; - private static final String ARCHIVE_SUFFIX = ".zip"; - - // Directories used to hold temporary files. These are cleared when clearTemporaryFiles() is - // called. - private static final String DEX_OUTPUT_DIRECTORY = "dex_output"; - private static final String APP_DIRECTORY = "applications"; - private static final String ASSET_DIRECTORY = "assets"; - - private static final String INTERNAL_DIRECTORY = "internal"; - - /** - * Deletes directories holding the temporary files. This should be called early on shell startup - * to clean up after the previous run. - */ - static void clearTemporaryFiles(Context context) { - FileHelper.deleteRecursively(getDexOutputDir(context)); - FileHelper.deleteRecursively(getAppDir(context)); - FileHelper.deleteRecursively(getAssetDir(context)); - } - - /** - * Returns the path at which the native part should save the application archive. - */ - @CalledByNative - private static String getNewTempArchivePath(Context context) throws IOException { - return File.createTempFile(ARCHIVE_PREFIX, ARCHIVE_SUFFIX, getAppDir(context)) - .getAbsolutePath(); - } - - /** - * Extracts and runs the application libraries contained by the indicated archive. - * @param context the application context - * @param archivePath the path of the archive containing the application to be run - * @param handle handle to the shell to be passed to the native application. On the Java side - * this is opaque payload. - * @param runApplicationPtr pointer to the function that will set the native thunks and call - * into the application MojoMain. On the Java side this is opaque - * payload. - */ - @CalledByNative - private static boolean bootstrap( - Context context, String archivePath, int handle, long runApplicationPtr) { - File bootstrapJavaLibrary; - File bootstrapNativeLibrary; - try { - bootstrapJavaLibrary = FileHelper.extractFromAssets(context, BOOTSTRAP_JAVA_LIBRARY, - getAssetDir(context), FileHelper.FileType.TEMPORARY); - bootstrapNativeLibrary = FileHelper.extractFromAssets(context, BOOTSTRAP_NATIVE_LIBRARY, - getAssetDir(context), FileHelper.FileType.TEMPORARY); - } catch (Exception e) { - Log.e(TAG, "Extraction of bootstrap files from assets failed.", e); - return false; - } - - File applicationJavaLibrary; - File applicationNativeLibrary; - try { - File archive = new File(archivePath); - applicationJavaLibrary = - FileHelper.extractFromArchive(archive, JAVA_LIBRARY_SUFFIX, getAppDir(context), - FileHelper.FileType.TEMPORARY, FileHelper.ArchiveType.NORMAL); - applicationNativeLibrary = FileHelper.extractFromArchive(archive, NATIVE_LIBRARY_SUFFIX, - getAppDir(context), FileHelper.FileType.TEMPORARY, - FileHelper.ArchiveType.NORMAL); - } catch (Exception e) { - Log.e(TAG, "Extraction of application files from the archive failed.", e); - return false; - } - - return runApp(context, getDexOutputDir(context), applicationJavaLibrary, - applicationNativeLibrary, bootstrapJavaLibrary, bootstrapNativeLibrary, handle, - runApplicationPtr); - } - - private static File findFileInDirectoryMatchingSuffix( - File dir, final String suffix, final String ignore) { - File[] matchingFiles = dir.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return !name.equals(ignore) && !name.equals(BOOTSTRAP_JAVA_LIBRARY) - && !name.equals(BOOTSTRAP_NATIVE_LIBRARY) && name.endsWith(suffix); - } - }); - return matchingFiles != null && matchingFiles.length == 1 ? matchingFiles[0] : null; - } - - /** - * Extracts and runs a cached application. - * - * @param context the application context - * @param archivePath the path of the archive containing the application to be run - * @param appPathString path where the cached app's resources and other files are - * @param handle handle to the shell to be passed to the native application. On the Java side - * this is opaque payload. - * @param runApplicationPtr pointer to the function that will set the native thunks and call - * into the application MojoMain. On the Java side this is opaque - * payload. - */ - @CalledByNative - private static boolean bootstrapCachedApp(Context context, String archivePath, - String appPathString, int handle, long runApplicationPtr) { - final String appName = new File(appPathString).getName(); - final File internalDir = new File(new File(appPathString), INTERNAL_DIRECTORY); - if (!internalDir.exists() && !internalDir.mkdirs()) { - Log.e(TAG, "Unable to create output dir " + internalDir.getAbsolutePath()); - return false; - } - final File timestamp = FileHelper.prepareDirectoryForAssets(context, internalDir); - // We make the bootstrap library have a unique name on disk as otherwise we end up sharing - // bootstrap.so, - // which doesn't work. - // TODO(sky): figure out why android is caching the names. - final String bootstrapNativeLibraryName = appName + "-" + BOOTSTRAP_NATIVE_LIBRARY; - File bootstrapJavaLibrary = new File(internalDir, BOOTSTRAP_JAVA_LIBRARY); - File bootstrapNativeLibrary = new File(internalDir, bootstrapNativeLibraryName); - try { - // Use the files on disk if we have them, if not extract from the archive. - if (!bootstrapJavaLibrary.exists()) { - bootstrapJavaLibrary = FileHelper.extractFromAssets(context, BOOTSTRAP_JAVA_LIBRARY, - internalDir, FileHelper.FileType.PERMANENT); - } - if (!bootstrapNativeLibrary.exists()) { - final File extractedBootstrapNativeLibrary = FileHelper.extractFromAssets(context, - BOOTSTRAP_NATIVE_LIBRARY, internalDir, FileHelper.FileType.PERMANENT); - if (!extractedBootstrapNativeLibrary.renameTo(bootstrapNativeLibrary)) { - Log.e(TAG, "Unable to rename bootstrap library " - + bootstrapNativeLibrary.getAbsolutePath()); - return false; - } - } - } catch (Exception e) { - Log.e(TAG, "Extraction of bootstrap files from assets failed.", e); - return false; - } - - // Because we allow the .so and .dex.jar to be anything we have to search in the internal - // directory for matching files. - // If we find one, we assume it's the one we want. - File applicationJavaLibrary = - findFileInDirectoryMatchingSuffix(internalDir, JAVA_LIBRARY_SUFFIX, ""); - File applicationNativeLibrary = findFileInDirectoryMatchingSuffix( - internalDir, NATIVE_LIBRARY_SUFFIX, bootstrapNativeLibraryName); - try { - File archive = new File(archivePath); - if (applicationJavaLibrary == null) { - applicationJavaLibrary = FileHelper.extractFromArchive(archive, JAVA_LIBRARY_SUFFIX, - internalDir, FileHelper.FileType.PERMANENT, - FileHelper.ArchiveType.CONTENT_HANDLER); - } - if (applicationNativeLibrary == null) { - applicationNativeLibrary = FileHelper.extractFromArchive(archive, - NATIVE_LIBRARY_SUFFIX, internalDir, FileHelper.FileType.PERMANENT, - FileHelper.ArchiveType.CONTENT_HANDLER); - } - } catch (Exception e) { - Log.e(TAG, "Extraction of application files from the archive failed.", e); - return false; - } - - FileHelper.createTimestampIfNecessary(timestamp); - - return runApp(context, new File(internalDir, DEX_OUTPUT_DIRECTORY), applicationJavaLibrary, - applicationNativeLibrary, bootstrapJavaLibrary, bootstrapNativeLibrary, handle, - runApplicationPtr); - } - - /** - * Creates the class loader containing the bootstrap classes and runs it. - * - * @return true if successfully ran, false if encounteres some sort of error. - */ - private static boolean runApp(Context context, File dexOutputDir, File applicationJavaLibrary, - File applicationNativeLibrary, File bootstrapJavaLibrary, File bootstrapNativeLibrary, - int handle, long runApplicationPtr) { - final String dexPath = bootstrapJavaLibrary.getAbsolutePath() + File.pathSeparator - + applicationJavaLibrary.getAbsolutePath(); - if (!dexOutputDir.exists() && !dexOutputDir.mkdirs()) { - Log.e(TAG, "Unable to create dex output dir " + dexOutputDir.getAbsolutePath()); - return false; - } - // TODO(sky): third arg is a path, but appears to have no effect, figure out if this relates - // to weird caching - // logic mentioned above. - DexClassLoader bootstrapLoader = new DexClassLoader( - dexPath, dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader()); - - try { - Class<?> loadedClass = bootstrapLoader.loadClass(BOOTSTRAP_CLASS); - Class<? extends Runnable> bootstrapClass = loadedClass.asSubclass(Runnable.class); - Constructor<? extends Runnable> constructor = bootstrapClass.getConstructor( - Context.class, File.class, File.class, Integer.class, Long.class); - Runnable bootstrapRunnable = constructor.newInstance(context, bootstrapNativeLibrary, - applicationNativeLibrary, Integer.valueOf(handle), - Long.valueOf(runApplicationPtr)); - bootstrapRunnable.run(); - } catch (Throwable t) { - Log.e(TAG, "Running Bootstrap failed.", t); - return false; - } - return true; - } - - @CalledByNative - static String getCachedAppsDir(Context context) { - return ShellMain.getCachedAppsDir(context).getAbsolutePath(); - } - - private static File getDexOutputDir(Context context) { - return context.getDir(DEX_OUTPUT_DIRECTORY, Context.MODE_PRIVATE); - } - - private static File getAppDir(Context context) { - return context.getDir(APP_DIRECTORY, Context.MODE_PRIVATE); - } - - private static File getAssetDir(Context context) { - return context.getDir(ASSET_DIRECTORY, Context.MODE_PRIVATE); - } -}
diff --git a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/Bootstrap.java b/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/Bootstrap.java deleted file mode 100644 index 3899bc7..0000000 --- a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/Bootstrap.java +++ /dev/null
@@ -1,45 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.mojo.shell; - -import android.content.Context; - -import org.chromium.base.annotations.JNINamespace; - -import java.io.File; - -/** - * Runnable used to bootstrap execution of Android Mojo application. For the JNI to work, we need a - * Java class with the application classloader in the call stack. We load this class in the - * application classloader and call into native from it to achieve that. - */ -@JNINamespace("mojo::shell") -public class Bootstrap implements Runnable { - private final Context mContext; - private final File mBootstrapNativeLibrary; - private final File mApplicationNativeLibrary; - private final int mHandle; - private final long mRunApplicationPtr; - - public Bootstrap(Context context, File bootstrapNativeLibrary, File applicationNativeLibrary, - Integer handle, Long runApplicationPtr) { - mContext = context; - mBootstrapNativeLibrary = bootstrapNativeLibrary; - mApplicationNativeLibrary = applicationNativeLibrary; - mHandle = handle; - mRunApplicationPtr = runApplicationPtr; - } - - @Override - public void run() { - System.load(mBootstrapNativeLibrary.getAbsolutePath()); - System.load(mApplicationNativeLibrary.getAbsolutePath()); - nativeBootstrap( - mContext, mApplicationNativeLibrary.getAbsolutePath(), mHandle, mRunApplicationPtr); - } - - native void nativeBootstrap( - Context context, String libraryPath, int handle, long runApplicationPtr); -}
diff --git a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/FileHelper.java b/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/FileHelper.java deleted file mode 100644 index b8da063..0000000 --- a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/FileHelper.java +++ /dev/null
@@ -1,257 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.mojo.shell; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; - -import org.chromium.base.Log; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -/** - * Helper methods for file extraction from APK assets and zip archives. - */ -class FileHelper { - public static final String TAG = "MojoFileHelper"; - - // Size of the buffer used in streaming file operations. - private static final int BUFFER_SIZE = 1024 * 1024; - // Prefix used when naming temporary files. - private static final String TEMP_FILE_PREFIX = "temp-"; - // Prefix used when naming timestamp files. - private static final String TIMESTAMP_PREFIX = "asset_timestamp-"; - // Name of the file listing assets to extract with one asset file per line. - private static final String ASSETS_LIST_NAME = "assets_list"; - // Directory where applications cached with the shell will be extracted. - private static final String CACHED_APP_DIRECTORY = "cached_apps"; - - /** - * Used to indicate the type of destination file that should be created. - */ - public enum FileType { - TEMPORARY, - PERMANENT, - } - - public enum ArchiveType { - /** - * The archive was created for a content handler (contains the mojo escape sequence). - */ - CONTENT_HANDLER, - NORMAL, - } - - /** - * Looks for a timestamp file on disk that indicates the version of the APK that the resource - * assets were extracted from. Returns null if a timestamp was found and it indicates that the - * resources match the current APK. Otherwise returns the file to create. - */ - private static File findAssetTimestamp(Context context, File outputDir) { - PackageManager pm = context.getPackageManager(); - PackageInfo pi = null; - - try { - pi = pm.getPackageInfo(context.getPackageName(), 0); - } catch (PackageManager.NameNotFoundException e) { - } - - if (pi == null) { - return new File(outputDir, TIMESTAMP_PREFIX); - } - - final File expectedTimestamp = - new File(outputDir, TIMESTAMP_PREFIX + pi.versionCode + "-" + pi.lastUpdateTime); - return expectedTimestamp.exists() ? null : expectedTimestamp; - } - - /** - * Returns the directory where cached applications will be extracted. - */ - public static File getCachedAppsDir(Context context) { - return context.getDir(CACHED_APP_DIRECTORY, Context.MODE_PRIVATE); - } - - /** - * Returns the names of the assets in ASSETS_LIST_NAME. - */ - public static List<String> getAssetsList(Context context) throws IOException { - List<String> results = new ArrayList<String>(); - BufferedReader reader = new BufferedReader(new InputStreamReader( - context.getAssets().open(ASSETS_LIST_NAME), Charset.forName("UTF-8"))); - - try { - String line; - while ((line = reader.readLine()) != null) { - line = line.trim(); - // These two are read by the system and don't need to be extracted. - if (!line.isEmpty() && !line.equals("bootstrap_java.dex.jar") - && !line.equals("libbootstrap.so")) { - results.add(line); - } - } - } finally { - reader.close(); - } - return results; - } - - /** - * Invoke prior to extracting any assets into {@code directory}. If necessary deletes all the - * files in the specified directory. The return value must be supplied to {@link - *createTimestampIfNecessary}. - * - * @param directory directory assets will be extracted to - * @return non-null if a file with the specified name needs to be created after assets have - * been extracted. - */ - public static File prepareDirectoryForAssets(Context context, File directory) { - final File timestamp = findAssetTimestamp(context, directory); - if (timestamp == null) { - return null; - } - for (File child : directory.listFiles()) { - deleteRecursively(child); - } - return timestamp; - } - - /** - * Creates a file used as a timestamp. The supplied file comes from {@link - *prepareDirectoryForAssets}. - * - * @param timestamp path of file to create, or null if a file does not need to be created - */ - public static void createTimestampIfNecessary(File timestamp) { - if (timestamp == null) { - return; - } - try { - timestamp.createNewFile(); - } catch (IOException e) { - // In the worst case we don't write a timestamp, so we'll re-extract the asset next - // time. - Log.w(TAG, "Failed to write asset timestamp!"); - } - } - - public static File extractFromAssets(Context context, String assetName, File outputDirectory, - FileType fileType) throws IOException, FileNotFoundException { - File outputFile; - if (fileType == FileType.TEMPORARY) { - // Make the original filename part of the temp file name. - // TODO(ppi): do we need to sanitize the suffix? - String suffix = "-" + assetName; - outputFile = File.createTempFile(TEMP_FILE_PREFIX, suffix, outputDirectory); - } else { - outputFile = new File(outputDirectory, assetName); - if (outputFile.exists()) { - return outputFile; - } - outputFile.getParentFile().mkdirs(); - } - - BufferedInputStream inputStream = - new BufferedInputStream(context.getAssets().open(assetName)); - try { - writeStreamToFile(inputStream, outputFile); - } finally { - inputStream.close(); - } - return outputFile; - } - - /** - * Extracts the file of the given extension from the archive. Throws FileNotFoundException if no - * matching file is found. - * - * @return path of extracted file - */ - static File extractFromArchive(File archive, String suffixToMatch, File outputDirectory, - FileType fileType, ArchiveType archiveType) throws IOException, FileNotFoundException { - if (!outputDirectory.exists() && !outputDirectory.mkdirs()) { - Log.e(TAG, "extractFromArchive unable to create directory " - + outputDirectory.getAbsolutePath()); - throw new FileNotFoundException(); - } - - BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(archive)); - if (archiveType == ArchiveType.CONTENT_HANDLER) { - int currentChar; - do { - currentChar = inputStream.read(); - } while (currentChar != -1 && currentChar != '\n'); - if (currentChar == -1) { - throw new FileNotFoundException(); - } - inputStream = new BufferedInputStream(inputStream); - } - ZipInputStream zip = new ZipInputStream(inputStream); - ZipEntry entry; - while ((entry = zip.getNextEntry()) != null) { - if (entry.getName().endsWith(suffixToMatch)) { - // TODO(sky): sanitize name. - final String name = new File(entry.getName()).getName(); - File extractedFile; - // Make the original filename part of the temp file name. - if (fileType == FileType.TEMPORARY) { - final String suffix = "-" + name; - extractedFile = File.createTempFile(TEMP_FILE_PREFIX, suffix, outputDirectory); - } else { - extractedFile = new File(outputDirectory, name); - } - writeStreamToFile(zip, extractedFile); - zip.close(); - return extractedFile; - } - } - zip.close(); - throw new FileNotFoundException(); - } - - /** - * Deletes a file or directory. Directory will be deleted even if not empty. - */ - static void deleteRecursively(File file) { - if (file.isDirectory()) { - for (File child : file.listFiles()) { - deleteRecursively(child); - } - } - if (!file.delete()) { - Log.w(TAG, "Unable to delete file: " + file.getAbsolutePath()); - } - } - - private static void writeStreamToFile(InputStream inputStream, File outputFile) - throws IOException { - byte[] buffer = new byte[BUFFER_SIZE]; - OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFile)); - try { - int read; - while ((read = inputStream.read(buffer, 0, BUFFER_SIZE)) > 0) { - outputStream.write(buffer, 0, read); - } - } finally { - outputStream.close(); - } - } -}
diff --git a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java b/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java deleted file mode 100644 index 7e148ac..0000000 --- a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 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. - -package org.chromium.mojo.shell; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; - -import org.chromium.base.Log; - -/** - * Activity for managing the Mojo Shell. - */ -public class MojoShellActivity extends Activity { - private static final String TAG = "MojoShellActivity"; - private static final String EXTRAS = "org.chromium.mojo.shell.extras"; - - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - String[] parameters = getIntent().getStringArrayExtra(EXTRAS); - if (parameters != null) { - for (String s : parameters) { - s = s.replace("\\,", ","); - } - } - if (Intent.ACTION_VIEW.equals(getIntent().getAction())) { - Uri uri = getIntent().getData(); - if (uri != null) { - Log.i(TAG, "MojoShellActivity opening " + uri); - if (parameters == null) { - parameters = new String[] {uri.toString()}; - } else { - String[] newParameters = new String[parameters.length + 1]; - System.arraycopy(parameters, 0, newParameters, 0, parameters.length); - newParameters[parameters.length] = uri.toString(); - parameters = newParameters; - } - } - } - - // TODO(ppi): Gotcha - the call below will work only once per process lifetime, but the OS - // has no obligation to kill the application process between destroying and restarting the - // activity. If the application process is kept alive, initialization parameters sent with - // the intent will be stale. - ShellMain.ensureInitialized(this, parameters); - ShellMain.start(); - } -}
diff --git a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java b/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java deleted file mode 100644 index 75f4df4..0000000 --- a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright 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. - -package org.chromium.mojo.shell; - -import org.chromium.base.BaseChromiumApplication; -import org.chromium.base.Log; -import org.chromium.base.PathUtils; -import org.chromium.base.library_loader.LibraryLoader; -import org.chromium.base.library_loader.LibraryProcessType; -import org.chromium.base.library_loader.ProcessInitException; - -/** - * MojoShell implementation of {@link android.app.Application}, managing application-level global - * state and initializations. - */ -public class MojoShellApplication extends BaseChromiumApplication { - private static final String TAG = "MojoShellApplication"; - private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "mojo_shell"; - - @Override - public void onCreate() { - super.onCreate(); - clearTemporaryFiles(); - initializeJavaUtils(); - initializeNative(); - } - - /** - * Deletes the temporary files and directories created in the previous run of the application. - * This is important regardless of cleanups on exit, as the previous run could have crashed. - */ - private void clearTemporaryFiles() { - AndroidHandler.clearTemporaryFiles(this); - } - - /** - * Initializes Java-side utils. - */ - private void initializeJavaUtils() { - PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, this); - } - - /** - * Loads the native library. - */ - private void initializeNative() { - try { - LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER) - .ensureInitialized(getApplicationContext()); - } catch (ProcessInitException e) { - Log.e(TAG, "libmojo_runner initialization failed.", e); - throw new RuntimeException(e); - } - } -}
diff --git a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/ShellMain.java b/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/ShellMain.java deleted file mode 100644 index 4659e3a..0000000 --- a/mojo/shell/standalone/android/apk/src/org/chromium/mojo/shell/ShellMain.java +++ /dev/null
@@ -1,111 +0,0 @@ -// Copyright 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. - -package org.chromium.mojo.shell; - -import android.app.Activity; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.util.Log; - -import org.chromium.base.ContextUtils; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A placeholder class to call native functions. - **/ -@JNINamespace("mojo::shell") -public class ShellMain { - private static final String TAG = "ShellMain"; - - // The key to the library to run in forked processes when running multi-process. - private static final String MOJO_LIB_KEY = "mojo_lib"; - - /** - * A guard flag for calling nativeInit() only once. - **/ - private static boolean sInitialized = false; - - /** - * Initializes the native system. - **/ - public static void ensureInitialized(Context context, String[] parameters) { - if (sInitialized) return; - File cachedAppsDir = getCachedAppsDir(context); - try { - final File timestamp = FileHelper.prepareDirectoryForAssets(context, cachedAppsDir); - for (String assetPath : FileHelper.getAssetsList(context)) { - FileHelper.extractFromAssets( - context, assetPath, cachedAppsDir, FileHelper.FileType.PERMANENT); - } - ApplicationInfo ai = context.getPackageManager().getApplicationInfo( - context.getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = ai.metaData; - String mojo_lib = bundle.getString(MOJO_LIB_KEY); - - FileHelper.createTimestampIfNecessary(timestamp); - File mojoShell = new File(context.getApplicationInfo().nativeLibraryDir, mojo_lib); - - List<String> parametersList = new ArrayList<String>(); - // Program name. - if (parameters != null) { - parametersList.addAll(Arrays.asList(parameters)); - } - - ContextUtils.initApplicationContext(context.getApplicationContext()); - nativeInit(context, mojoShell.getAbsolutePath(), - parametersList.toArray(new String[parametersList.size()]), - cachedAppsDir.getAbsolutePath(), getTmpDir(context).getAbsolutePath()); - sInitialized = true; - } catch (Exception e) { - Log.e(TAG, "ShellMain initialization failed.", e); - throw new RuntimeException(e); - } - } - - /** - * Starts the specified application in the specified context. - **/ - public static void start() { - nativeStart(); - } - - /** - * Adds the given URL to the set of mojo applications to run on start. - */ - static void addApplicationURL(String url) { - nativeAddApplicationURL(url); - } - - static File getCachedAppsDir(Context context) { - return FileHelper.getCachedAppsDir(context); - } - - private static File getTmpDir(Context context) { - return new File(context.getCacheDir(), "tmp"); - } - - @CalledByNative - private static void finishActivity(Activity activity) { - activity.finish(); - } - - /** - * Initializes the native system. This API should be called only once per process. - **/ - private static native void nativeInit(Context context, String mojoShellPath, - String[] parameters, String cachedAppsDirectory, String tmpDir); - - private static native void nativeStart(); - - private static native void nativeAddApplicationURL(String url); -}
diff --git a/mojo/shell/standalone/android/background_application_loader.cc b/mojo/shell/standalone/android/background_application_loader.cc deleted file mode 100644 index 446ce42..0000000 --- a/mojo/shell/standalone/android/background_application_loader.cc +++ /dev/null
@@ -1,73 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/shell/standalone/android/background_application_loader.h" - -#include <utility> - -#include "base/bind.h" -#include "base/run_loop.h" -#include "mojo/shell/application_manager.h" - -namespace mojo { -namespace shell { - -BackgroundApplicationLoader::BackgroundApplicationLoader( - scoped_ptr<ApplicationLoader> real_loader, - const std::string& thread_name, - base::MessageLoop::Type message_loop_type) - : loader_(std::move(real_loader)), - message_loop_type_(message_loop_type), - thread_name_(thread_name), - message_loop_created_(true, false) {} - -BackgroundApplicationLoader::~BackgroundApplicationLoader() { - if (thread_) - thread_->Join(); -} - -void BackgroundApplicationLoader::Load( - const GURL& url, - InterfaceRequest<mojom::ShellClient> request) { - DCHECK(request.is_pending()); - if (!thread_) { - // TODO(tim): It'd be nice if we could just have each Load call - // result in a new thread like DynamicService{Loader, Runner}. But some - // loaders are creating multiple ShellConnections (NetworkApplicationLoader) - // sharing a delegate (etc). So we have to keep it single threaded, wait - // for the thread to initialize, and post to the TaskRunner for subsequent - // Load calls for now. - thread_.reset(new base::DelegateSimpleThread(this, thread_name_)); - thread_->Start(); - message_loop_created_.Wait(); - DCHECK(task_runner_.get()); - } - - task_runner_->PostTask( - FROM_HERE, - base::Bind(&BackgroundApplicationLoader::LoadOnBackgroundThread, - base::Unretained(this), url, base::Passed(&request))); -} - -void BackgroundApplicationLoader::Run() { - base::MessageLoop message_loop(message_loop_type_); - base::RunLoop loop; - task_runner_ = message_loop.task_runner(); - quit_closure_ = loop.QuitClosure(); - message_loop_created_.Signal(); - loop.Run(); - - // Destroy |loader_| on the thread it's actually used on. - loader_.reset(); -} - -void BackgroundApplicationLoader::LoadOnBackgroundThread( - const GURL& url, - InterfaceRequest<mojom::ShellClient> request) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - loader_->Load(url, std::move(request)); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/standalone/android/background_application_loader.h b/mojo/shell/standalone/android/background_application_loader.h deleted file mode 100644 index ed6f73c1..0000000 --- a/mojo/shell/standalone/android/background_application_loader.h +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_STANDALONE_ANDROID_BACKGROUND_APPLICATION_LOADER_H_ -#define MOJO_SHELL_STANDALONE_ANDROID_BACKGROUND_APPLICATION_LOADER_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/simple_thread.h" -#include "mojo/shell/application_loader.h" - -namespace mojo { -namespace shell { - -class BackgroundApplicationLoader - : public ApplicationLoader, - public base::DelegateSimpleThread::Delegate { - public: - BackgroundApplicationLoader(scoped_ptr<ApplicationLoader> real_loader, - const std::string& thread_name, - base::MessageLoop::Type message_loop_type); - ~BackgroundApplicationLoader() override; - - // ApplicationLoader overrides: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override; - - private: - // |base::DelegateSimpleThread::Delegate| method: - void Run() override; - - // These functions are exected on the background thread. They call through - // to |background_loader_| to do the actual loading. - void LoadOnBackgroundThread(const GURL& url, - InterfaceRequest<mojom::ShellClient> request); - bool quit_on_shutdown_; - scoped_ptr<ApplicationLoader> loader_; - - const base::MessageLoop::Type message_loop_type_; - const std::string thread_name_; - - // Created on |thread_| during construction of |this|. Protected against - // uninitialized use by |message_loop_created_|, and protected against - // use-after-free by holding a reference to the thread-safe object. Note - // that holding a reference won't hold |thread_| from exiting. - scoped_refptr<base::TaskRunner> task_runner_; - base::WaitableEvent message_loop_created_; - - // Lives on |thread_|. - base::Closure quit_closure_; - - scoped_ptr<base::DelegateSimpleThread> thread_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundApplicationLoader); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_ANDROID_BACKGROUND_APPLICATION_LOADER_H_
diff --git a/mojo/shell/standalone/android/background_application_loader_unittest.cc b/mojo/shell/standalone/android/background_application_loader_unittest.cc deleted file mode 100644 index fcea757..0000000 --- a/mojo/shell/standalone/android/background_application_loader_unittest.cc +++ /dev/null
@@ -1,51 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/shell/standalone/android/background_application_loader.h" - -#include "mojo/shell/public/interfaces/shell_client.mojom.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace shell { -namespace { - -class DummyLoader : public ApplicationLoader { - public: - DummyLoader() : simulate_app_quit_(true) {} - ~DummyLoader() override {} - - // ApplicationLoader overrides: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override { - if (simulate_app_quit_) - base::MessageLoop::current()->QuitWhenIdle(); - } - - void DontSimulateAppQuit() { simulate_app_quit_ = false; } - - private: - bool simulate_app_quit_; -}; - -// Tests that the loader can start and stop gracefully. -TEST(BackgroundApplicationLoaderTest, StartStop) { - scoped_ptr<ApplicationLoader> real_loader(new DummyLoader()); - BackgroundApplicationLoader loader(std::move(real_loader), "test", - base::MessageLoop::TYPE_DEFAULT); -} - -// Tests that the loader can load a service that is well behaved (quits -// itself). -TEST(BackgroundApplicationLoaderTest, Load) { - scoped_ptr<ApplicationLoader> real_loader(new DummyLoader()); - BackgroundApplicationLoader loader(std::move(real_loader), "test", - base::MessageLoop::TYPE_DEFAULT); - ApplicationPtr application; - loader.Load(GURL(), GetProxy(&application)); -} - -} // namespace -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/standalone/android/bootstrap.cc b/mojo/shell/standalone/android/bootstrap.cc deleted file mode 100644 index 39b6901d..0000000 --- a/mojo/shell/standalone/android/bootstrap.cc +++ /dev/null
@@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "jni/Bootstrap_jni.h" -#include "mojo/shell/standalone/android/run_android_application_function.h" - -namespace mojo { -namespace shell { - -void Bootstrap(JNIEnv* env, - const JavaParamRef<jobject>&, - const JavaParamRef<jobject>& j_context, - const JavaParamRef<jstring>& j_native_library_path, - jint j_handle, - jlong j_run_application_ptr) { - base::FilePath app_path( - base::android::ConvertJavaStringToUTF8(env, j_native_library_path)); - RunAndroidApplicationFn run_android_application_fn = - reinterpret_cast<RunAndroidApplicationFn>(j_run_application_ptr); - run_android_application_fn(env, j_context, app_path, j_handle); -} - -bool RegisterBootstrapJni(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -} // namespace shell -} // namespace mojo - -JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - base::android::InitVM(vm); - JNIEnv* env = base::android::AttachCurrentThread(); - - if (!mojo::shell::RegisterBootstrapJni(env)) - return -1; - - return JNI_VERSION_1_4; -}
diff --git a/mojo/shell/standalone/android/context_init.cc b/mojo/shell/standalone/android/context_init.cc deleted file mode 100644 index 14ac0562..0000000 --- a/mojo/shell/standalone/android/context_init.cc +++ /dev/null
@@ -1,13 +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. - -#include "mojo/shell/standalone/android/context_init.h" - -namespace mojo { -namespace shell { - -void InitContext(Context* context) {} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/standalone/android/context_init.h b/mojo/shell/standalone/android/context_init.h deleted file mode 100644 index edf55cf..0000000 --- a/mojo/shell/standalone/android/context_init.h +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_STANDALONE_ANDROID_CONTEXT_INIT_H_ -#define MOJO_SHELL_STANDALONE_ANDROID_CONTEXT_INIT_H_ - -namespace mojo { -namespace shell { - -class Context; - -// Any Context specific initialization goes here. -void InitContext(Context* context); - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_ANDROID_CONTEXT_INIT_H_
diff --git a/mojo/shell/standalone/android/library_loader.cc b/mojo/shell/standalone/android/library_loader.cc deleted file mode 100644 index 9ef3d97..0000000 --- a/mojo/shell/standalone/android/library_loader.cc +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 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. - -#include "base/android/base_jni_onload.h" -#include "base/android/base_jni_registrar.h" -#include "base/android/jni_android.h" -#include "base/android/jni_registrar.h" -#include "base/bind.h" -#include "mojo/shell/standalone/android/android_handler.h" -#include "mojo/shell/standalone/android/main.h" -#include "ui/platform_window/android/platform_ime_controller_android.h" -#include "ui/platform_window/android/platform_window_android.h" - -namespace { - -base::android::RegistrationMethod kMojoRegisteredMethods[] = { - {"AndroidHandler", mojo::shell::RegisterAndroidHandlerJni}, - {"PlatformImeControllerAndroid", - ui::PlatformImeControllerAndroid::Register}, - {"PlatformWindowAndroid", ui::PlatformWindowAndroid::Register}, - {"ShellMain", mojo::shell::RegisterShellMain}, -}; - -bool RegisterJNI(JNIEnv* env) { - if (!base::android::RegisterJni(env)) - return false; - - return RegisterNativeMethods(env, kMojoRegisteredMethods, - arraysize(kMojoRegisteredMethods)); -} - -} // namespace - -// This is called by the VM when the shared library is first loaded. -JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - std::vector<base::android::RegisterCallback> register_callbacks; - register_callbacks.push_back(base::Bind(&RegisterJNI)); - if (!base::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks) || - !base::android::OnJNIOnLoadInit( - std::vector<base::android::InitCallback>())) { - return -1; - } - - return JNI_VERSION_1_4; -}
diff --git a/mojo/shell/standalone/android/main.cc b/mojo/shell/standalone/android/main.cc deleted file mode 100644 index 6be9f8a..0000000 --- a/mojo/shell/standalone/android/main.cc +++ /dev/null
@@ -1,191 +0,0 @@ -// Copyright 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. - -#include "mojo/shell/standalone/android/main.h" - -#include "base/android/fifo_utils.h" -#include "base/android/jni_android.h" -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/debug/stack_trace.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/i18n/icu_util.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "build/build_config.h" -#include "components/mus/android_loader.h" -#include "jni/ShellMain_jni.h" -#include "mojo/message_pump/message_pump_mojo.h" -#include "mojo/shell/application_loader.h" -#include "mojo/shell/runner/host/child_process.h" -#include "mojo/shell/runner/init.h" -#include "mojo/shell/standalone/android/android_handler_loader.h" -#include "mojo/shell/standalone/android/background_application_loader.h" -#include "mojo/shell/standalone/android/context_init.h" -#include "mojo/shell/standalone/android/ui_application_loader_android.h" -#include "mojo/shell/standalone/context.h" -#include "ui/gl/gl_surface_egl.h" - -using base::LazyInstance; - -namespace mojo { -namespace shell { - -namespace { - -// Command line argument for the communication fifo. -const char kFifoPath[] = "fifo-path"; - -LazyInstance<scoped_ptr<base::MessageLoop>> g_java_message_loop = - LAZY_INSTANCE_INITIALIZER; - -LazyInstance<scoped_ptr<Context>> g_context = LAZY_INSTANCE_INITIALIZER; - -LazyInstance<base::android::ScopedJavaGlobalRef<jobject>> g_main_activiy = - LAZY_INSTANCE_INITIALIZER; - -void ConfigureAndroidServices(Context* context) { - CHECK(context->application_manager()); - context->application_manager()->SetLoaderForURL( - make_scoped_ptr( - new UIApplicationLoader(make_scoped_ptr(new mus::AndroidLoader()), - g_java_message_loop.Get().get())), - GURL("mojo:mus")); - - // Android handler is bundled with the Mojo shell, because it uses the - // MojoShell application as the JNI bridge to bootstrap execution of other - // Android Mojo apps that need JNI. - context->application_manager()->SetLoaderForURL( - make_scoped_ptr(new BackgroundApplicationLoader( - make_scoped_ptr(new AndroidHandlerLoader()), "android_handler", - base::MessageLoop::TYPE_DEFAULT)), - GURL("mojo:android_handler")); -} - -void ExitShell() { - Java_ShellMain_finishActivity(base::android::AttachCurrentThread(), - g_main_activiy.Get().obj()); - exit(0); -} - -// Initialize stdout redirection if the command line switch is present. -void InitializeRedirection() { - if (!base::CommandLine::ForCurrentProcess()->HasSwitch(kFifoPath)) - return; - - base::FilePath fifo_path = - base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(kFifoPath); - base::FilePath directory = fifo_path.DirName(); - CHECK(base::CreateDirectoryAndGetError(directory, nullptr)) - << "Unable to create directory: " << directory.value(); - unlink(fifo_path.value().c_str()); - CHECK(base::android::CreateFIFO(fifo_path, 0666)) << "Unable to create fifo: " - << fifo_path.value(); - CHECK(base::android::RedirectStream(stdout, fifo_path, "w")) - << "Failed to redirect stdout to file: " << fifo_path.value(); - CHECK(dup2(STDOUT_FILENO, STDERR_FILENO) != -1) - << "Unable to redirect stderr to stdout."; -} - -} // namespace - -static void Init(JNIEnv* env, - const JavaParamRef<jclass>& clazz, - const JavaParamRef<jobject>& activity, - const JavaParamRef<jstring>& mojo_shell_path, - const JavaParamRef<jobjectArray>& jparameters, - const JavaParamRef<jstring>& j_local_apps_directory, - const JavaParamRef<jstring>& j_tmp_dir) { - g_main_activiy.Get().Reset(env, activity); - - // Setting the TMPDIR environment variable so that applications can use it. - // TODO(qsr) We will need our subprocesses to inherit this. - int return_value = - setenv("TMPDIR", - base::android::ConvertJavaStringToUTF8(env, j_tmp_dir).c_str(), 1); - DCHECK_EQ(return_value, 0); - - std::vector<std::string> parameters; - parameters.push_back( - base::android::ConvertJavaStringToUTF8(env, mojo_shell_path)); - base::android::AppendJavaStringArrayToStringVector(env, jparameters, - ¶meters); - base::CommandLine::Init(0, nullptr); - base::CommandLine::ForCurrentProcess()->InitFromArgv(parameters); - - InitializeLogging(); - WaitForDebuggerIfNecessary(); - - InitializeRedirection(); - - // We want ~MessageLoop to happen prior to ~Context. Initializing - // LazyInstances is akin to stack-allocating objects; their destructors - // will be invoked first-in-last-out. - base::FilePath shell_file_root( - base::android::ConvertJavaStringToUTF8(env, j_local_apps_directory)); - Context* shell_context = new Context; - g_context.Get().reset(shell_context); - - g_java_message_loop.Get().reset(new base::MessageLoopForUI); - base::MessageLoopForUI::current()->Start(); - - CHECK(base::i18n::InitializeICU()); - - shell_context->Init(shell_file_root); - ConfigureAndroidServices(shell_context); - - // This is done after the main message loop is started since it may post - // tasks. This is consistent with the ordering from the desktop version of - // this file (../desktop/launcher_process.cc). - InitContext(shell_context); - - // TODO(abarth): At which point should we switch to cross-platform - // initialization? - - gfx::GLSurface::InitializeOneOff(); -} - -static void Start(JNIEnv* env, const JavaParamRef<jclass>& clazz) { -#if defined(MOJO_SHELL_DEBUG_URL) - base::CommandLine::ForCurrentProcess()->AppendArg(MOJO_SHELL_DEBUG_URL); - // Sleep for 5 seconds to give the debugger a chance to attach. - sleep(5); -#endif - - Context* context = g_context.Pointer()->get(); - context->RunCommandLineApplication(base::Bind(ExitShell)); -} - -static void AddApplicationURL(JNIEnv* env, - const JavaParamRef<jclass>& clazz, - const JavaParamRef<jstring>& jurl) { - base::CommandLine::ForCurrentProcess()->AppendArg( - base::android::ConvertJavaStringToUTF8(env, jurl)); -} - -bool RegisterShellMain(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -Context* GetContext() { - return g_context.Get().get(); -} - -} // namespace shell -} // namespace mojo - -int main(int argc, char** argv) { - base::AtExitManager at_exit; - base::CommandLine::Init(argc, argv); - - mojo::shell::InitializeLogging(); - return mojo::shell::ChildProcessMain(); -}
diff --git a/mojo/shell/standalone/android/main.h b/mojo/shell/standalone/android/main.h deleted file mode 100644 index a66aaea..0000000 --- a/mojo/shell/standalone/android/main.h +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 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 MOJO_SHELL_STANDALONE_ANDROID_MAIN_H_ -#define MOJO_SHELL_STANDALONE_ANDROID_MAIN_H_ - -#include <jni.h> - -namespace mojo { -namespace shell { - -class Context; - -bool RegisterShellMain(JNIEnv* env); - -Context* GetContext(); - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_ANDROID_MAIN_H_
diff --git a/mojo/shell/standalone/android/run_android_application_function.h b/mojo/shell/standalone/android/run_android_application_function.h deleted file mode 100644 index cedfd45..0000000 --- a/mojo/shell/standalone/android/run_android_application_function.h +++ /dev/null
@@ -1,27 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_STANDALONE_ANDROID_RUN_ANDROID_APPLICATION_FUNCTION_H_ -#define MOJO_SHELL_STANDALONE_ANDROID_RUN_ANDROID_APPLICATION_FUNCTION_H_ - -#include "base/android/jni_android.h" -#include "base/files/file_path.h" - -namespace mojo { -namespace shell { - -// Type of the function that we inject from the main .so of the Mojo shell to -// the helper libbootstrap.so. This function will set the thunks in the -// application .so and call into application MojoMain. Injecting the function -// from the main .so ensures that the thunks are set correctly. - -typedef void (*RunAndroidApplicationFn)(JNIEnv* env, - jobject j_context, - const base::FilePath& app_path, - jint j_handle); - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_ANDROID_RUN_ANDROID_APPLICATION_FUNCTION_H_
diff --git a/mojo/shell/standalone/android/ui_application_loader_android.cc b/mojo/shell/standalone/android/ui_application_loader_android.cc deleted file mode 100644 index a848b2e..0000000 --- a/mojo/shell/standalone/android/ui_application_loader_android.cc +++ /dev/null
@@ -1,48 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/shell/standalone/android/ui_application_loader_android.h" - -#include <utility> - -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "mojo/shell/application_manager.h" - -namespace mojo { -namespace shell { - -UIApplicationLoader::UIApplicationLoader( - scoped_ptr<ApplicationLoader> real_loader, - base::MessageLoop* ui_message_loop) - : loader_(std::move(real_loader)), ui_message_loop_(ui_message_loop) {} - -UIApplicationLoader::~UIApplicationLoader() { - ui_message_loop_->PostTask( - FROM_HERE, base::Bind(&UIApplicationLoader::ShutdownOnUIThread, - base::Unretained(this))); -} - -void UIApplicationLoader::Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) { - DCHECK(request.is_pending()); - ui_message_loop_->PostTask( - FROM_HERE, - base::Bind(&UIApplicationLoader::LoadOnUIThread, base::Unretained(this), - url, base::Passed(&request))); -} - -void UIApplicationLoader::LoadOnUIThread( - const GURL& url, - InterfaceRequest<mojom::ShellClient> request) { - loader_->Load(url, std::move(request)); -} - -void UIApplicationLoader::ShutdownOnUIThread() { - // Destroy |loader_| on the thread it's actually used on. - loader_.reset(); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/standalone/android/ui_application_loader_android.h b/mojo/shell/standalone/android/ui_application_loader_android.h deleted file mode 100644 index 5f9b5e15..0000000 --- a/mojo/shell/standalone/android/ui_application_loader_android.h +++ /dev/null
@@ -1,54 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_STANDALONE_ANDROID_UI_APPLICATION_LOADER_ANDROID_H_ -#define MOJO_SHELL_STANDALONE_ANDROID_UI_APPLICATION_LOADER_ANDROID_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/shell/application_loader.h" - -namespace base { -class MessageLoop; -} - -namespace mojo { -namespace shell { - -class ApplicationManager; - -// ApplicationLoader implementation that creates a background thread and issues -// load -// requests there. -class UIApplicationLoader : public ApplicationLoader { - public: - UIApplicationLoader(scoped_ptr<ApplicationLoader> real_loader, - base::MessageLoop* ui_message_loop); - ~UIApplicationLoader() override; - - // ApplicationLoader overrides: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override; - - private: - class UILoader; - - // These functions are exected on the background thread. They call through - // to |background_loader_| to do the actual loading. - // TODO: having this code take a |manager| is fragile (as ApplicationManager - // isn't thread safe). - void LoadOnUIThread(const GURL& url, - InterfaceRequest<mojom::ShellClient> request); - void ShutdownOnUIThread(); - - scoped_ptr<ApplicationLoader> loader_; - base::MessageLoop* ui_message_loop_; - - DISALLOW_COPY_AND_ASSIGN(UIApplicationLoader); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_ANDROID_UI_APPLICATION_LOADER_ANDROID_H_
diff --git a/mojo/shell/standalone/context.cc b/mojo/shell/standalone/context.cc index adc285ddf..b33a27f 100644 --- a/mojo/shell/standalone/context.cc +++ b/mojo/shell/standalone/context.cc
@@ -22,6 +22,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "components/tracing/tracing_switches.h" @@ -33,9 +34,9 @@ #include "mojo/services/tracing/public/interfaces/tracing.mojom.h" #include "mojo/shell/application_loader.h" #include "mojo/shell/connect_to_application_params.h" +#include "mojo/shell/runner/host/command_line_switch.h" #include "mojo/shell/runner/host/in_process_native_runner.h" #include "mojo/shell/runner/host/out_of_process_native_runner.h" -#include "mojo/shell/standalone/switches.h" #include "mojo/shell/standalone/tracer.h" #include "mojo/shell/switches.h" #include "mojo/util/filename_util.h" @@ -79,12 +80,25 @@ DISALLOW_COPY_AND_ASSIGN(TracingInterfaceProvider); }; +const size_t kMaxBlockingPoolThreads = 3; + +scoped_ptr<base::Thread> CreateIOThread(const char* name) { + scoped_ptr<base::Thread> thread(new base::Thread(name)); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + thread->StartWithOptions(options); + return thread; +} + } // namespace -Context::Context() : main_entry_time_(base::Time::Now()) {} +Context::Context() + : io_thread_(CreateIOThread("io_thread")), + main_entry_time_(base::Time::Now()) {} Context::~Context() { DCHECK(!base::MessageLoop::current()); + blocking_pool_->Shutdown(); } // static @@ -107,11 +121,13 @@ } EnsureEmbedderIsInitialized(); - task_runners_.reset( - new TaskRunners(base::MessageLoop::current()->task_runner())); + + shell_runner_ = base::MessageLoop::current()->task_runner(); + blocking_pool_ = + new base::SequencedWorkerPool(kMaxBlockingPoolThreads, "blocking_pool"); // TODO(vtl): This should be MASTER, not NONE. - edk::InitIPCSupport(this, task_runners_->io_runner()); + edk::InitIPCSupport(this, io_thread_->task_runner().get()); scoped_ptr<NativeRunnerFactory> runner_factory; if (command_line.HasSwitch(switches::kSingleProcess)) { @@ -121,13 +137,13 @@ << " or don't pass --single-process."; #endif runner_factory.reset( - new InProcessNativeRunnerFactory(task_runners_->blocking_pool())); + new InProcessNativeRunnerFactory(blocking_pool_.get())); } else { - runner_factory.reset( - new OutOfProcessNativeRunnerFactory(task_runners_->blocking_pool())); + runner_factory.reset(new OutOfProcessNativeRunnerFactory( + blocking_pool_.get(), command_line_switches_)); } application_manager_.reset(new ApplicationManager( - std::move(runner_factory), task_runners_->blocking_pool(), true)); + std::move(runner_factory), blocking_pool_.get(), true)); shell::mojom::InterfaceProviderPtr tracing_remote_interfaces; shell::mojom::InterfaceProviderPtr tracing_local_interfaces; @@ -136,8 +152,8 @@ scoped_ptr<ConnectToApplicationParams> params(new ConnectToApplicationParams); params->set_source(Identity(GURL("mojo:shell"), std::string(), GetPermissiveCapabilityFilter())); - params->SetTarget(Identity(GURL("mojo:tracing"), std::string(), - GetPermissiveCapabilityFilter())); + params->set_target(Identity(GURL("mojo:tracing"), std::string(), + GetPermissiveCapabilityFilter())); params->set_remote_interfaces(GetProxy(&tracing_remote_interfaces)); params->set_local_interfaces(std::move(tracing_local_interfaces)); application_manager_->ConnectToApplication(std::move(params)); @@ -173,8 +189,7 @@ application_manager_.reset(); TRACE_EVENT0("mojo_shell", "Context::Shutdown"); - DCHECK_EQ(base::MessageLoop::current()->task_runner(), - task_runners_->shell_runner()); + DCHECK_EQ(base::MessageLoop::current()->task_runner(), shell_runner_); // Post a task in case OnShutdownComplete is called synchronously. base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(edk::ShutdownIPCSupport)); @@ -183,8 +198,7 @@ } void Context::OnShutdownComplete() { - DCHECK_EQ(base::MessageLoop::current()->task_runner(), - task_runners_->shell_runner()); + DCHECK_EQ(base::MessageLoop::current()->task_runner(), shell_runner_); base::MessageLoop::current()->QuitWhenIdle(); } @@ -196,7 +210,7 @@ app_urls_.insert(url); scoped_ptr<ConnectToApplicationParams> params(new ConnectToApplicationParams); - params->SetTarget( + params->set_target( Identity(url, std::string(), GetPermissiveCapabilityFilter())); params->set_remote_interfaces(GetProxy(&remote_interfaces)); params->set_local_interfaces(std::move(local_interfaces)); @@ -224,8 +238,7 @@ if (app_urls_.find(url) != app_urls_.end()) { app_urls_.erase(url); if (app_urls_.empty() && base::MessageLoop::current()->is_running()) { - DCHECK_EQ(base::MessageLoop::current()->task_runner(), - task_runners_->shell_runner()); + DCHECK_EQ(base::MessageLoop::current()->task_runner(), shell_runner_); if (app_complete_callback_.is_null()) { base::MessageLoop::current()->QuitWhenIdle(); } else {
diff --git a/mojo/shell/standalone/context.h b/mojo/shell/standalone/context.h index f87b82c8..a659009 100644 --- a/mojo/shell/standalone/context.h +++ b/mojo/shell/standalone/context.h
@@ -7,19 +7,27 @@ #include <set> #include <string> +#include <vector> #include "base/callback_forward.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread.h" #include "base/time/time.h" #include "mojo/edk/embedder/process_delegate.h" #include "mojo/shell/application_manager.h" -#include "mojo/shell/standalone/scoped_user_data_dir.h" -#include "mojo/shell/standalone/task_runners.h" +#include "mojo/shell/runner/host/command_line_switch.h" #include "mojo/shell/standalone/tracer.h" #include "url/gurl.h" +namespace base { +class SingleThreadTaskRunner; +} + namespace mojo { namespace shell { +struct CommandLineSwitch; class NativeApplicationLoader; // The "global" context for the shell's main process. @@ -30,6 +38,11 @@ static void EnsureEmbedderIsInitialized(); + void set_command_line_switches( + const std::vector<CommandLineSwitch>& command_line_switches) { + command_line_switches_ = command_line_switches; + } + // This must be called with a message loop set up for the current thread, // which must remain alive until after Shutdown() is called. void Init(const base::FilePath& shell_file_root); @@ -46,7 +59,6 @@ // |callback| is run if not null, otherwise the message loop is quit. void RunCommandLineApplication(const base::Closure& callback); - TaskRunners* task_runners() { return task_runners_.get(); } ApplicationManager* application_manager() { return application_manager_.get(); } @@ -59,15 +71,19 @@ void OnApplicationEnd(const GURL& url); - ScopedUserDataDir scoped_user_data_dir; std::set<GURL> app_urls_; - scoped_ptr<TaskRunners> task_runners_; + + scoped_refptr<base::SingleThreadTaskRunner> shell_runner_; + scoped_ptr<base::Thread> io_thread_; + scoped_refptr<base::SequencedWorkerPool> blocking_pool_; + // Ensure this is destructed before task_runners_ since it owns a message pipe // that needs the IO thread to destruct cleanly. Tracer tracer_; scoped_ptr<ApplicationManager> application_manager_; base::Closure app_complete_callback_; base::Time main_entry_time_; + std::vector<CommandLineSwitch> command_line_switches_; DISALLOW_COPY_AND_ASSIGN(Context); };
diff --git a/mojo/shell/standalone/desktop/launcher_process.cc b/mojo/shell/standalone/desktop/launcher_process.cc index 56de7a9..cdbb2f9b 100644 --- a/mojo/shell/standalone/desktop/launcher_process.cc +++ b/mojo/shell/standalone/desktop/launcher_process.cc
@@ -19,7 +19,6 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" #include "mojo/shell/standalone/context.h" -#include "mojo/shell/standalone/switches.h" #include "mojo/shell/switches.h" namespace mojo {
diff --git a/mojo/shell/standalone/scoped_user_data_dir.cc b/mojo/shell/standalone/scoped_user_data_dir.cc deleted file mode 100644 index dc92dfe..0000000 --- a/mojo/shell/standalone/scoped_user_data_dir.cc +++ /dev/null
@@ -1,39 +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. - -#include "mojo/shell/standalone/scoped_user_data_dir.h" - -#include "base/command_line.h" -#include "base/logging.h" -#include "mojo/shell/standalone/switches.h" - -namespace mojo { -namespace shell { - -ScopedUserDataDir::ScopedUserDataDir() { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (!command_line->HasSwitch(switches::kUseTemporaryUserDataDir)) - return; - - if (command_line->HasSwitch(switches::kUserDataDir)) { - // User should not specify a --user-data-dir manually when using - // --use-temporary-user-data-dir. The point of the flag is to let the - // mojo runner process manage the lifetime of the user data dir. - LOG(ERROR) << "Ignoring request to --use-temporary-user-data-dir because " - << "--user-data-dir was also specified."; - return; - } - - if (!temp_dir_.CreateUniqueTempDir()) { - LOG(ERROR) << "Failed to create a temporary user data dir."; - return; - } - - command_line->AppendSwitchPath(switches::kUserDataDir, temp_dir_.path()); -} - -ScopedUserDataDir::~ScopedUserDataDir() {} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/standalone/scoped_user_data_dir.h b/mojo/shell/standalone/scoped_user_data_dir.h deleted file mode 100644 index 364c4630..0000000 --- a/mojo/shell/standalone/scoped_user_data_dir.h +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_STANDALONE_SCOPED_USER_DATA_DIR_H_ -#define MOJO_SHELL_STANDALONE_SCOPED_USER_DATA_DIR_H_ - -#include "base/files/scoped_temp_dir.h" - -namespace mojo { -namespace shell { - -// A scoped class which owns a ScopedTempDir if --use-temporary-user-data-dir -// is set. If it is, also modifies the command line so that --user-data-dir -// points to the temporary dir. -class ScopedUserDataDir { - public: - ScopedUserDataDir(); - ~ScopedUserDataDir(); - - private: - base::ScopedTempDir temp_dir_; -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_SCOPED_USER_DATA_DIR_H_
diff --git a/mojo/shell/standalone/shell_apptest.cc b/mojo/shell/standalone/shell_apptest.cc deleted file mode 100644 index 7f2d2b0..0000000 --- a/mojo/shell/standalone/shell_apptest.cc +++ /dev/null
@@ -1,199 +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. - -#include <stdint.h> - -#include "base/base_paths.h" -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/path_service.h" -#include "base/run_loop.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "mojo/common/data_pipe_utils.h" -#include "mojo/public/cpp/system/macros.h" -#include "mojo/services/http_server/public/cpp/http_server_util.h" -#include "mojo/services/http_server/public/interfaces/http_server.mojom.h" -#include "mojo/services/http_server/public/interfaces/http_server_factory.mojom.h" -#include "mojo/services/network/public/interfaces/net_address.mojom.h" -#include "mojo/shell/public/cpp/application_test_base.h" -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/shell/standalone/kPingable.h" -#include "mojo/shell/standalone/test/pingable.mojom.h" - -namespace mojo { -namespace { - -std::string GetURL(uint16_t port, const std::string& path) { - return base::StringPrintf("http://127.0.0.1:%u/%s", - static_cast<unsigned>(port), path.c_str()); -} - -class GetHandler : public http_server::HttpHandler { - public: - GetHandler(InterfaceRequest<http_server::HttpHandler> request, uint16_t port) - : binding_(this, std::move(request)), port_(port) {} - ~GetHandler() override {} - - private: - // http_server::HttpHandler: - void HandleRequest( - http_server::HttpRequestPtr request, - const Callback<void(http_server::HttpResponsePtr)>& callback) override { - http_server::HttpResponsePtr response; - if (base::StartsWith(request->relative_url, "/app", - base::CompareCase::SENSITIVE)) { - response = http_server::CreateHttpResponse( - 200, std::string(kPingable.data, kPingable.size)); - response->content_type = "application/octet-stream"; - } else if (request->relative_url == "/redirect") { - response = http_server::HttpResponse::New(); - response->status_code = 302; - response->custom_headers.insert("Location", GetURL(port_, "app")); - } else { - NOTREACHED(); - } - - callback.Run(std::move(response)); - } - - Binding<http_server::HttpHandler> binding_; - uint16_t port_; - - MOJO_DISALLOW_COPY_AND_ASSIGN(GetHandler); -}; - -typedef test::ApplicationTestBase ShellAppTest; - -class ShellHTTPAppTest : public test::ApplicationTestBase { - public: - ShellHTTPAppTest() : ApplicationTestBase() {} - ~ShellHTTPAppTest() override {} - - protected: - // ApplicationTestBase: - void SetUp() override { - ApplicationTestBase::SetUp(); - - shell()->ConnectToInterface("mojo:http_server", &http_server_factory_); - - NetAddressPtr local_address(NetAddress::New()); - local_address->family = NET_ADDRESS_FAMILY_IPV4; - local_address->ipv4 = NetAddressIPv4::New(); - local_address->ipv4->addr.resize(4); - local_address->ipv4->addr[0] = 127; - local_address->ipv4->addr[1] = 0; - local_address->ipv4->addr[2] = 0; - local_address->ipv4->addr[3] = 1; - local_address->ipv4->port = 0; - http_server_factory_->CreateHttpServer(GetProxy(&http_server_), - std::move(local_address)); - - http_server_->GetPort([this](uint16_t p) { port_ = p; }); - EXPECT_TRUE(http_server_.WaitForIncomingResponse()); - - InterfacePtr<http_server::HttpHandler> http_handler; - handler_.reset(new GetHandler(GetProxy(&http_handler).Pass(), port_)); - http_server_->SetHandler(".*", std::move(http_handler), - [](bool result) { EXPECT_TRUE(result); }); - EXPECT_TRUE(http_server_.WaitForIncomingResponse()); - } - - std::string GetURL(const std::string& path) { - return ::mojo::GetURL(port_, path); - } - - http_server::HttpServerFactoryPtr http_server_factory_; - http_server::HttpServerPtr http_server_; - scoped_ptr<GetHandler> handler_; - uint16_t port_; - - private: - MOJO_DISALLOW_COPY_AND_ASSIGN(ShellHTTPAppTest); -}; - -// Test that we can load apps over http. -TEST_F(ShellHTTPAppTest, Http) { - InterfacePtr<Pingable> pingable; - shell()->ConnectToInterface(GetURL("app"), &pingable); - pingable->Ping("hello", - [this](const String& app_url, const String& connection_url, - const String& message) { - EXPECT_EQ(GetURL("app"), app_url); - EXPECT_EQ(GetURL("app"), connection_url); - EXPECT_EQ("hello", message); - base::MessageLoop::current()->QuitWhenIdle(); - }); - base::RunLoop().Run(); -} - -// Test that redirects work. -// TODO(aa): Test that apps receive the correct URL parameters. -TEST_F(ShellHTTPAppTest, Redirect) { - InterfacePtr<Pingable> pingable; - shell()->ConnectToInterface(GetURL("redirect"), &pingable); - pingable->Ping("hello", - [this](const String& app_url, const String& connection_url, - const String& message) { - EXPECT_EQ(GetURL("app"), app_url); - EXPECT_EQ(GetURL("app"), connection_url); - EXPECT_EQ("hello", message); - base::MessageLoop::current()->QuitWhenIdle(); - }); - base::RunLoop().Run(); -} - -// Test that querystring is not considered when resolving http applications. -// TODO(aa|qsr): Fix this test on Linux ASAN http://crbug.com/463662 -#if defined(ADDRESS_SANITIZER) -#define MAYBE_QueryHandling DISABLED_QueryHandling -#else -#define MAYBE_QueryHandling QueryHandling -#endif // ADDRESS_SANITIZER -TEST_F(ShellHTTPAppTest, MAYBE_QueryHandling) { - InterfacePtr<Pingable> pingable1; - InterfacePtr<Pingable> pingable2; - shell()->ConnectToInterface(GetURL("app?foo"), &pingable1); - shell()->ConnectToInterface(GetURL("app?bar"), &pingable2); - - int num_responses = 0; - auto callback = [this, &num_responses](const String& app_url, - const String& connection_url, - const String& message) { - EXPECT_EQ(GetURL("app"), app_url); - EXPECT_EQ("hello", message); - ++num_responses; - if (num_responses == 1) { - EXPECT_EQ(GetURL("app?foo"), connection_url); - } else if (num_responses == 2) { - EXPECT_EQ(GetURL("app?bar"), connection_url); - base::MessageLoop::current()->QuitWhenIdle(); - } else { - CHECK(false); - } - }; - pingable1->Ping("hello", callback); - pingable2->Ping("hello", callback); - base::RunLoop().Run(); -} - -// mojo: URLs can have querystrings too -TEST_F(ShellAppTest, MojoURLQueryHandling) { - InterfacePtr<Pingable> pingable; - shell()->ConnectToInterface("mojo:pingable_app?foo", &pingable); - auto callback = [this](const String& app_url, const String& connection_url, - const String& message) { - EXPECT_TRUE(base::EndsWith(app_url, "/pingable_app.mojo", - base::CompareCase::SENSITIVE)); - EXPECT_EQ(app_url.To<std::string>() + "?foo", connection_url); - EXPECT_EQ("hello", message); - base::MessageLoop::current()->QuitWhenIdle(); - }; - pingable->Ping("hello", callback); - base::RunLoop().Run(); -} - -} // namespace -} // namespace mojo
diff --git a/mojo/shell/standalone/shell_test_main.cc b/mojo/shell/standalone/shell_test_main.cc deleted file mode 100644 index 0697bd81..0000000 --- a/mojo/shell/standalone/shell_test_main.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/bind.h" -#include "base/test/launcher/unit_test_launcher.h" -#include "base/test/test_suite.h" -#include "testing/gtest/include/gtest/gtest.h" - -int main(int argc, char** argv) { - base::TestSuite test_suite(argc, argv); - return base::LaunchUnitTests( - argc, argv, - base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); -}
diff --git a/mojo/shell/standalone/switches.cc b/mojo/shell/standalone/switches.cc deleted file mode 100644 index 75a5d6b..0000000 --- a/mojo/shell/standalone/switches.cc +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 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. - -#include "mojo/shell/standalone/switches.h" - -namespace mojo { -namespace switches { - -// In multiprocess mode, force these apps to be loaded in the main process. -// This is a comma-separated list of URLs. Example: -// --force-in-process=mojo:native_viewport_service,mojo:network_service -const char kForceInProcess[] = "force-in-process"; - -// Print the usage message and exit. -const char kHelp[] = "help"; - -// Specify origin to map to base url. See url_resolver.cc for details. -// Can be used multiple times. -const char kMapOrigin[] = "map-origin"; - -// Specifies a set of mappings to apply when resolving URLs. The value is a set -// of comma-separated mappings, where each mapping consists of a pair of URLs -// giving the to/from URLs to map. For example, 'a=b,c=d' contains two mappings, -// the first maps 'a' to 'b' and the second 'c' to 'd'. -const char kURLMappings[] = "url-mappings"; - -// When this is set, we create a temporary user data dir for the process, and -// add a flag so kUserDataDir points to it. -const char kUseTemporaryUserDataDir[] = "use-temporary-user-data-dir"; - -// Specifies the user data directory. This is the one directory which stores -// all persistent data. -const char kUserDataDir[] = "user-data-dir"; - -} // namespace switches -} // namespace mojo
diff --git a/mojo/shell/standalone/switches.h b/mojo/shell/standalone/switches.h deleted file mode 100644 index 96a27ea8..0000000 --- a/mojo/shell/standalone/switches.h +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 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 MOJO_SHELL_STANDALONE_SWITCHES_H_ -#define MOJO_SHELL_STANDALONE_SWITCHES_H_ - -namespace mojo { -namespace switches { - -// All switches in alphabetical order. The switches should be documented -// alongside the definition of their values in the .cc file. -extern const char kApp[]; -extern const char kForceInProcess[]; -extern const char kHelp[]; -extern const char kMapOrigin[]; -extern const char kURLMappings[]; -extern const char kUseTemporaryUserDataDir[]; -extern const char kUserDataDir[]; - -} // namespace switches -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_SWITCHES_H_
diff --git a/mojo/shell/standalone/task_runners.cc b/mojo/shell/standalone/task_runners.cc deleted file mode 100644 index 48fac50..0000000 --- a/mojo/shell/standalone/task_runners.cc +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright 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. - -#include "mojo/shell/standalone/task_runners.h" - -#include <stddef.h> - -#include "base/threading/sequenced_worker_pool.h" - -namespace mojo { -namespace shell { - -namespace { - -const size_t kMaxBlockingPoolThreads = 3; - -scoped_ptr<base::Thread> CreateIOThread(const char* name) { - scoped_ptr<base::Thread> thread(new base::Thread(name)); - base::Thread::Options options; - options.message_loop_type = base::MessageLoop::TYPE_IO; - thread->StartWithOptions(options); - return thread; -} - -} // namespace - -TaskRunners::TaskRunners( - const scoped_refptr<base::SingleThreadTaskRunner>& shell_runner) - : shell_runner_(shell_runner), - io_thread_(CreateIOThread("io_thread")), - blocking_pool_(new base::SequencedWorkerPool(kMaxBlockingPoolThreads, - "blocking_pool")) {} - -TaskRunners::~TaskRunners() { - blocking_pool_->Shutdown(); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/standalone/task_runners.h b/mojo/shell/standalone/task_runners.h deleted file mode 100644 index c33e722..0000000 --- a/mojo/shell/standalone/task_runners.h +++ /dev/null
@@ -1,52 +0,0 @@ -// Copyright 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 MOJO_SHELL_STANDALONE_TASK_RUNNERS_H_ -#define MOJO_SHELL_STANDALONE_TASK_RUNNERS_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/threading/thread.h" - -namespace base { -class SequencedWorkerPool; -} - -namespace mojo { -namespace shell { - -// A context object that contains the common task runners for the shell's main -// process. -class TaskRunners { - public: - explicit TaskRunners( - const scoped_refptr<base::SingleThreadTaskRunner>& shell_runner); - ~TaskRunners(); - - base::SingleThreadTaskRunner* shell_runner() const { - return shell_runner_.get(); - } - - base::SingleThreadTaskRunner* io_runner() const { - return io_thread_->task_runner().get(); - } - - base::SequencedWorkerPool* blocking_pool() const { - return blocking_pool_.get(); - } - - private: - scoped_refptr<base::SingleThreadTaskRunner> shell_runner_; - scoped_ptr<base::Thread> io_thread_; - - scoped_refptr<base::SequencedWorkerPool> blocking_pool_; - - DISALLOW_COPY_AND_ASSIGN(TaskRunners); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STANDALONE_TASK_RUNNERS_H_
diff --git a/mojo/shell/standalone/test/BUILD.gn b/mojo/shell/standalone/test/BUILD.gn deleted file mode 100644 index 7d9dc14..0000000 --- a/mojo/shell/standalone/test/BUILD.gn +++ /dev/null
@@ -1,28 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//mojo/public/mojo_application.gni") -import("//mojo/public/tools/bindings/mojom.gni") - -mojom("bindings") { - sources = [ - "pingable.mojom", - ] -} - -mojo_native_application("pingable_app") { - output_name = "pingable_app" - - testonly = true - - sources = [ - "pingable_app.cc", - ] - - deps = [ - ":bindings", - "//mojo/public/cpp/bindings:callback", - "//mojo/shell/public/cpp", - ] -}
diff --git a/mojo/shell/standalone/test/pingable_app.cc b/mojo/shell/standalone/test/pingable_app.cc deleted file mode 100644 index 9b43df1..0000000 --- a/mojo/shell/standalone/test/pingable_app.cc +++ /dev/null
@@ -1,71 +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. - -#include "mojo/public/c/system/main.h" -#include "mojo/public/cpp/bindings/callback.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/public/cpp/bindings/strong_binding.h" -#include "mojo/shell/public/cpp/application_runner.h" -#include "mojo/shell/public/cpp/interface_factory.h" -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/standalone/test/pingable.mojom.h" - -namespace mojo { - -class PingableImpl : public Pingable { - public: - PingableImpl(InterfaceRequest<Pingable> request, - const std::string& app_url, - const std::string& connection_url) - : binding_(this, std::move(request)), - app_url_(app_url), - connection_url_(connection_url) {} - - ~PingableImpl() override {} - - private: - void Ping(const String& message, - const Callback<void(String, String, String)>& callback) override { - callback.Run(app_url_, connection_url_, message); - } - - StrongBinding<Pingable> binding_; - std::string app_url_; - std::string connection_url_; -}; - -class PingableApp : public mojo::ShellClient, - public mojo::InterfaceFactory<Pingable> { - public: - PingableApp() {} - ~PingableApp() override {} - - private: - // mojo::ShellClient: - void Initialize(Shell* shell, const std::string& url, uint32_t id) override { - app_url_ = url; - } - - bool AcceptConnection(mojo::Connection* connection) override { - connection->AddService(this); - return true; - } - - // InterfaceFactory<Pingable>: - void Create(mojo::Connection* connection, - mojo::InterfaceRequest<Pingable> request) override { - new PingableImpl(std::move(request), app_url_, - connection->GetConnectionURL()); - } - - std::string app_url_; -}; - -} // namespace mojo - -MojoResult MojoMain(MojoHandle shell_handle) { - mojo::ApplicationRunner runner(new mojo::PingableApp); - return runner.Run(shell_handle); -}
diff --git a/mojo/shell/static_application_loader.cc b/mojo/shell/static_application_loader.cc deleted file mode 100644 index a6920ff..0000000 --- a/mojo/shell/static_application_loader.cc +++ /dev/null
@@ -1,98 +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. - -#include "mojo/shell/static_application_loader.h" - -#include <utility> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/task_runner.h" -#include "base/thread_task_runner_handle.h" -#include "base/threading/simple_thread.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/shell/public/cpp/application_runner.h" -#include "mojo/shell/public/cpp/shell_client.h" -#include "mojo/shell/public/interfaces/shell_client.mojom.h" - -namespace mojo { -namespace shell { - -namespace { - -class RunnerThread : public base::SimpleThread { - public: - RunnerThread(const GURL& url, - InterfaceRequest<mojom::ShellClient> request, - scoped_refptr<base::TaskRunner> exit_task_runner, - const base::Closure& exit_callback, - const StaticApplicationLoader::ApplicationFactory& factory) - : base::SimpleThread("Mojo Application: " + url.spec()), - request_(std::move(request)), - exit_task_runner_(exit_task_runner), - exit_callback_(exit_callback), - factory_(factory) {} - - void Run() override { - scoped_ptr<ApplicationRunner> runner( - new ApplicationRunner(factory_.Run().release())); - runner->Run(request_.PassMessagePipe().release().value(), - false /* init_base */); - exit_task_runner_->PostTask(FROM_HERE, exit_callback_); - } - - private: - InterfaceRequest<mojom::ShellClient> request_; - scoped_refptr<base::TaskRunner> exit_task_runner_; - base::Closure exit_callback_; - StaticApplicationLoader::ApplicationFactory factory_; - - DISALLOW_COPY_AND_ASSIGN(RunnerThread); -}; - -} // namespace - -StaticApplicationLoader::StaticApplicationLoader( - const ApplicationFactory& factory) - : StaticApplicationLoader(factory, base::Closure()) { -} - -StaticApplicationLoader::StaticApplicationLoader( - const ApplicationFactory& factory, - const base::Closure& quit_callback) - : factory_(factory), quit_callback_(quit_callback), weak_factory_(this) { -} - -StaticApplicationLoader::~StaticApplicationLoader() { - if (thread_) - StopAppThread(); -} - -void StaticApplicationLoader::Load( - const GURL& url, - InterfaceRequest<mojom::ShellClient> request) { - if (thread_) - return; - - // If the application's thread quits on its own before this loader dies, we - // reset the Thread object, allowing future Load requests to be fulfilled - // with a new app instance. - auto exit_callback = base::Bind(&StaticApplicationLoader::StopAppThread, - weak_factory_.GetWeakPtr()); - thread_.reset(new RunnerThread(url, std::move(request), - base::ThreadTaskRunnerHandle::Get(), - exit_callback, factory_)); - thread_->Start(); -} - -void StaticApplicationLoader::StopAppThread() { - thread_->Join(); - thread_.reset(); - if (!quit_callback_.is_null()) - quit_callback_.Run(); -} - -} // namespace shell -} // namespace mojo
diff --git a/mojo/shell/static_application_loader.h b/mojo/shell/static_application_loader.h deleted file mode 100644 index b3d55ba6..0000000 --- a/mojo/shell/static_application_loader.h +++ /dev/null
@@ -1,69 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_STATIC_APPLICATION_LOADER_H_ -#define MOJO_SHELL_STATIC_APPLICATION_LOADER_H_ - -#include <list> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "mojo/shell/application_loader.h" - -namespace base { -class SimpleThread; -} - -namespace mojo { -class ShellClient; -} - -namespace mojo { -namespace shell { - -// An ApplicationLoader which loads a single type of app from a given -// mojo::ShellClient factory. A Load() request is fulfilled by creating an -// instance of the app on a new thread. Only one instance of the app will run at -// a time. Any Load requests received while the app is running will be dropped. -class StaticApplicationLoader : public mojo::shell::ApplicationLoader { - public: - using ApplicationFactory = - base::Callback<scoped_ptr<mojo::ShellClient>()>; - - // Constructs a static loader for |factory|. - explicit StaticApplicationLoader(const ApplicationFactory& factory); - - // Constructs a static loader for |factory| with a closure that will be called - // when the loaded application quits. - StaticApplicationLoader(const ApplicationFactory& factory, - const base::Closure& quit_callback); - - ~StaticApplicationLoader() override; - - // mojo::shell::ApplicationLoader: - void Load(const GURL& url, - InterfaceRequest<mojom::ShellClient> request) override; - - private: - void StopAppThread(); - - // The factory used t create new instances of the application delegate. - ApplicationFactory factory_; - - // If not null, this is run when the loaded application quits. - base::Closure quit_callback_; - - // Thread for the application if currently running. - scoped_ptr<base::SimpleThread> thread_; - - base::WeakPtrFactory<StaticApplicationLoader> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(StaticApplicationLoader); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_STATIC_APPLICATION_LOADER_H_
diff --git a/mojo/shell/switches.cc b/mojo/shell/switches.cc index 8c49a12d..ce52003d 100644 --- a/mojo/shell/switches.cc +++ b/mojo/shell/switches.cc
@@ -10,13 +10,6 @@ // Disables the sandbox for debugging. const char kNoSandbox[] = "no-sandbox"; -// If set apps downloaded are saved in with a predictable filename, to help -// remote debugging: when gdb is used through gdbserver, it needs to be able to -// find locally any loaded library. For this, gdb use the filename of the -// library. When using this flag, the application are named with the sha256 of -// their content. -const char kPredictableAppFilenames[] = "predictable-app-filenames"; - // Load apps in a single processes. const char kSingleProcess[] = "single-process";
diff --git a/mojo/shell/switches.h b/mojo/shell/switches.h index 1bbd21f..f0482e1 100644 --- a/mojo/shell/switches.h +++ b/mojo/shell/switches.h
@@ -14,7 +14,6 @@ // All switches in alphabetical order. The switches should be documented // alongside the definition of their values in the .cc file. extern const char kNoSandbox[]; -extern const char kPredictableAppFilenames[]; extern const char kSingleProcess[]; } // namespace switches
diff --git a/mojo/shell/tests/BUILD.gn b/mojo/shell/tests/BUILD.gn new file mode 100644 index 0000000..d50cf5ec --- /dev/null +++ b/mojo/shell/tests/BUILD.gn
@@ -0,0 +1,186 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//mojo/public/mojo_application.gni") +import("//mojo/public/mojo_application_manifest.gni") +import("//mojo/public/tools/bindings/mojom.gni") +import("//testing/test.gni") + +group("tests") { + testonly = true + deps = [ + ":apptests", + ":mojo_shell_unittests", + ] +} + +source_set("test_support") { + testonly = true + sources = [ + "capability_filter_test.cc", + "capability_filter_test.h", + ] + + deps = [ + ":test_bindings", + "//mojo/shell", + "//mojo/shell/public/cpp", + "//mojo/shell/public/interfaces", + "//testing/gtest", + ] +} + +# TODO(beng): this target should just be called "unittests" but I am having +# difficulty with the android _apk generator. +test("mojo_shell_unittests") { + sources = [ + "application_manager_unittest.cc", + "capability_filter_unittest.cc", + ] + + deps = [ + ":test_bindings", + ":test_support", + "//base", + "//mojo/edk/system:test_utils", + "//mojo/edk/test:run_all_unittests", + "//mojo/public/cpp/system", + "//mojo/shell", + "//mojo/shell/public/cpp", + "//mojo/util:filename_util", + "//testing/gtest", + "//url", + ] +} + +mojom("test_bindings") { + sources = [ + "application_manager_apptests.mojom", + "capability_filter_unittest.mojom", + "package_test.mojom", + "test.mojom", + ] +} + +mojo_native_application("apptests") { + output_name = "mojo_shell_apptests" + testonly = true + + sources = [ + "application_manager_apptest.cc", + "package_apptest.cc", + ] + + deps = [ + ":apptests_manifest", + ":test_bindings", + "//base", + "//base/test:test_config", + "//mojo/common:common_base", + "//mojo/converters/network", + "//mojo/shell/public/cpp:sources", + "//mojo/shell/public/cpp:test_support", + "//mojo/shell/public/interfaces", + ] + + data_deps = [ + ":application_manager_apptest_driver", + ":application_manager_apptest_target", + ":package_test_package", + ] +} + +mojo_application_manifest("apptests_manifest") { + application_name = "mojo_shell_apptests" + source = "application_manager_apptest_manifest.json" +} + +mojo_application_manifest("package_test_a_manifest") { + application_name = "package_test_a" + source = "package_test_app_a_manifest.json" +} + +mojo_application_manifest("package_test_b_manifest") { + application_name = "package_test_b" + source = "package_test_app_b_manifest.json" +} + +mojo_native_application("package_test_package") { + testonly = true + sources = [ + "package_test_package.cc", + ] + deps = [ + ":package_test_package_manifest", + ":test_bindings", + "//base", + "//mojo/common:common_base", + "//mojo/shell/public/cpp:sources", + "//mojo/shell/public/interfaces", + ] +} + +mojo_application_manifest("package_test_package_manifest") { + application_name = "package_test_package" + source = "package_test_package_manifest.json" + deps = [ + ":package_test_a_manifest", + ":package_test_b_manifest", + ] + packaged_applications = [ + "package_test_a", + "package_test_b", + ] +} + +executable("application_manager_apptest_driver") { + testonly = true + + sources = [ + "application_manager_apptest_driver.cc", + ] + + deps = [ + ":copy_application_manager_apptest_driver_manifest", + ":test_bindings", + "//base", + "//base:base_static", + "//build/config/sanitizers:deps", + "//mojo/common:common_base", + "//mojo/converters/network", + "//mojo/edk/system", + "//mojo/shell/public/cpp", + "//mojo/shell/public/interfaces", + "//mojo/shell/runner:init", + "//mojo/shell/runner/child:test_native_main", + "//mojo/shell/runner/common", + ] +} + +copy("copy_application_manager_apptest_driver_manifest") { + sources = [ + "application_manager_apptest_driver_manifest.json", + ] + outputs = [ + "${root_out_dir}/{{source_file_part}}", + ] +} + +executable("application_manager_apptest_target") { + testonly = true + + sources = [ + "application_manager_apptest_target.cc", + ] + + deps = [ + ":test_bindings", + "//base", + "//build/config/sanitizers:deps", + "//mojo/common:common_base", + "//mojo/converters/network", + "//mojo/shell/public/cpp", + "//mojo/shell/runner/child:test_native_main", + ] +}
diff --git a/mojo/shell/tests/application_manager_apptest.cc b/mojo/shell/tests/application_manager_apptest.cc new file mode 100644 index 0000000..80be147 --- /dev/null +++ b/mojo/shell/tests/application_manager_apptest.cc
@@ -0,0 +1,218 @@ +// 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. + +#include <stddef.h> +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/process/process_handle.h" +#include "mojo/converters/network/network_type_converters.h" +#include "mojo/public/cpp/bindings/weak_binding_set.h" +#include "mojo/shell/public/cpp/application_test_base.h" +#include "mojo/shell/public/cpp/interface_factory.h" +#include "mojo/shell/public/cpp/shell.h" +#include "mojo/shell/public/interfaces/application_manager.mojom.h" +#include "mojo/shell/tests/application_manager_apptests.mojom.h" + +using mojo::shell::test::mojom::CreateInstanceForHandleTest; + +namespace mojo { +namespace shell { +namespace { + +class ApplicationManagerAppTestDelegate + : public ShellClient, + public InterfaceFactory<CreateInstanceForHandleTest>, + public CreateInstanceForHandleTest { + public: + ApplicationManagerAppTestDelegate() + : target_id_(mojom::Shell::kInvalidApplicationID), + binding_(this) {} + ~ApplicationManagerAppTestDelegate() override {} + + uint32_t target_id() const { return target_id_; } + + private: + // mojo::ShellClient: + void Initialize(Shell* shell, const std::string& url, uint32_t id) override {} + bool AcceptConnection(Connection* connection) override { + connection->AddInterface<CreateInstanceForHandleTest>(this); + return true; + } + + // InterfaceFactory<CreateInstanceForHandleTest>: + void Create(Connection* connection, + InterfaceRequest<CreateInstanceForHandleTest> request) override { + binding_.Bind(std::move(request)); + } + + // CreateInstanceForHandleTest: + void SetTargetID(uint32_t target_id) override { + target_id_ = target_id; + base::MessageLoop::current()->QuitWhenIdle(); + } + + uint32_t target_id_; + + Binding<CreateInstanceForHandleTest> binding_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationManagerAppTestDelegate); +}; + +} // namespace + +class ApplicationManagerAppTest : public mojo::test::ApplicationTestBase, + public mojom::ApplicationManagerListener { + public: + ApplicationManagerAppTest() : delegate_(nullptr), binding_(this) {} + ~ApplicationManagerAppTest() override {} + + void OnDriverQuit() { + base::MessageLoop::current()->QuitNow(); + } + + protected: + struct ApplicationInfo { + ApplicationInfo(uint32_t id, + const std::string& url, + const std::string& name) + : id(id), url(url), pid(base::kNullProcessId), name(name) {} + + uint32_t id; + std::string url; + base::ProcessId pid; + std::string name; + }; + + void AddListenerAndWaitForApplications() { + mojom::ApplicationManagerPtr application_manager; + shell()->ConnectToInterface("mojo:shell", &application_manager); + + application_manager->AddListener(binding_.CreateInterfacePtrAndBind()); + binding_.WaitForIncomingMethodCall(); + } + + bool ContainsApplicationNamed(const std::string& name) const { + for (const auto& application : initial_applications_) { + if (application.name == name) + return true; + } + for (const auto& application : applications_) { + if (application.name == name) + return true; + } + return false; + } + + uint32_t target_id() const { + DCHECK(delegate_); + return delegate_->target_id(); + } + + const std::vector<ApplicationInfo>& applications() const { + return applications_; + } + + ApplicationManagerAppTestDelegate* delegate() { return delegate_; } + + private: + // test::ApplicationTestBase: + ShellClient* GetShellClient() override { + delegate_ = new ApplicationManagerAppTestDelegate; + return delegate_; + } + + // mojom::ApplicationManagerListener: + void SetRunningApplications( + Array<mojom::ApplicationInfoPtr> applications) override { + for (size_t i = 0; i < applications.size(); ++i) { + initial_applications_.push_back(ApplicationInfo(applications[i]->id, + applications[i]->url, + applications[i]->name)); + } + } + void ApplicationInstanceCreated( + mojom::ApplicationInfoPtr application) override { + applications_.push_back(ApplicationInfo(application->id, application->url, + application->name)); + } + void ApplicationInstanceDestroyed(uint32_t id) override { + for (auto it = applications_.begin(); it != applications_.end(); ++it) { + auto& application = *it; + if (application.id == id) { + applications_.erase(it); + break; + } + } + } + void ApplicationPIDAvailable(uint32_t id, uint32_t pid) override { + for (auto& application : applications_) { + if (application.id == id) { + application.pid = pid; + break; + } + } + } + + ApplicationManagerAppTestDelegate* delegate_; + Binding<mojom::ApplicationManagerListener> binding_; + std::vector<ApplicationInfo> applications_; + std::vector<ApplicationInfo> initial_applications_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationManagerAppTest); +}; + +TEST_F(ApplicationManagerAppTest, CreateInstanceForHandle) { + AddListenerAndWaitForApplications(); + + // 1. Launch a process. (Actually, have the runner launch a process that + // launches a process. #becauselinkerrors). + mojo::shell::test::mojom::DriverPtr driver; + scoped_ptr<Connection> connection = + shell()->Connect("exe:application_manager_apptest_driver"); + connection->GetInterface(&driver); + + // 2. Wait for the target to connect to us. (via + // mojo:application_manager_apptests) + base::MessageLoop::current()->Run(); + + uint32_t remote_id = mojom::Shell::kInvalidApplicationID; + EXPECT_TRUE(connection->GetRemoteApplicationID(&remote_id)); + EXPECT_NE(mojom::Shell::kInvalidApplicationID, remote_id); + + // 3. Validate that this test suite's pretty name was consumed from its + // manifest. + EXPECT_TRUE(ContainsApplicationNamed("Application Manager Apptests")); + + // 4. Validate that the right applications/processes were created. + // Note that the target process will be created even if the tests are + // run with --single-process. + EXPECT_EQ(2u, applications().size()); + { + auto& application = applications().front(); + EXPECT_EQ(remote_id, application.id); + EXPECT_EQ("exe://application_manager_apptest_driver/", application.url); + EXPECT_NE(base::kNullProcessId, application.pid); + } + { + auto& application = applications().back(); + // We learn about the target process id via a ping from it. + EXPECT_EQ(target_id(), application.id); + EXPECT_EQ("exe://application_manager_apptest_target/", application.url); + EXPECT_NE(base::kNullProcessId, application.pid); + } + + driver.set_connection_error_handler( + base::Bind(&ApplicationManagerAppTest::OnDriverQuit, + base::Unretained(this))); + driver->QuitDriver(); + base::MessageLoop::current()->Run(); +} + +} // namespace shell +} // namespace mojo
diff --git a/mojo/shell/tests/application_manager_apptest_driver.cc b/mojo/shell/tests/application_manager_apptest_driver.cc new file mode 100644 index 0000000..09e5999 --- /dev/null +++ b/mojo/shell/tests/application_manager_apptest_driver.cc
@@ -0,0 +1,156 @@ +// 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. + +#include <stdint.h> + +#include <utility> + +#include "base/at_exit.h" +#include "base/base_paths.h" +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "base/process/process.h" +#include "base/thread_task_runner_handle.h" +#include "mojo/converters/network/network_type_converters.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/platform_channel_pair.h" +#include "mojo/edk/embedder/scoped_platform_handle.h" +#include "mojo/public/cpp/bindings/weak_binding_set.h" +#include "mojo/shell/public/cpp/connection.h" +#include "mojo/shell/public/cpp/interface_factory.h" +#include "mojo/shell/public/cpp/shell.h" +#include "mojo/shell/public/cpp/shell_client.h" +#include "mojo/shell/public/interfaces/application_manager.mojom.h" +#include "mojo/shell/runner/child/test_native_main.h" +#include "mojo/shell/runner/common/switches.h" +#include "mojo/shell/runner/init.h" +#include "mojo/shell/tests/application_manager_apptests.mojom.h" + +using mojo::shell::test::mojom::CreateInstanceForHandleTestPtr; +using mojo::shell::test::mojom::Driver; + +namespace { + +class TargetApplicationDelegate : public mojo::ShellClient, + public mojo::InterfaceFactory<Driver>, + public Driver { + public: + TargetApplicationDelegate() : shell_(nullptr), weak_factory_(this) {} + ~TargetApplicationDelegate() override {} + + private: + // mojo::ShellClient: + void Initialize(mojo::Shell* shell, const std::string& url, + uint32_t id) override { + shell_ = shell; + + base::FilePath target_path; + CHECK(base::PathService::Get(base::DIR_EXE, &target_path)); + #if defined(OS_WIN) + target_path = target_path.Append( + FILE_PATH_LITERAL("application_manager_apptest_target.exe")); + #else + target_path = target_path.Append( + FILE_PATH_LITERAL("application_manager_apptest_target")); + #endif + + base::CommandLine child_command_line(target_path); + // Forward the wait-for-debugger flag but nothing else - we don't want to + // stamp on the platform-channel flag. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kWaitForDebugger)) { + child_command_line.AppendSwitch(switches::kWaitForDebugger); + } + + mojo::shell::mojom::PIDReceiverPtr receiver; + mojo::InterfaceRequest<mojo::shell::mojom::PIDReceiver> request = + GetProxy(&receiver); + + // Create the channel to be shared with the target process. Pass one end + // on the command line. + mojo::edk::PlatformChannelPair platform_channel_pair; + mojo::edk::HandlePassingInformation handle_passing_info; + platform_channel_pair.PrepareToPassClientHandleToChildProcess( + &child_command_line, &handle_passing_info); + + // Generate a token for the child to find and connect to a primordial pipe + // and pass that as well. + std::string primordial_pipe_token = mojo::edk::GenerateRandomToken(); + child_command_line.AppendSwitchASCII(switches::kPrimordialPipeToken, + primordial_pipe_token); + + // Allocate the pipe locally. + mojo::ScopedMessagePipeHandle pipe = + mojo::edk::CreateParentMessagePipe(primordial_pipe_token); + + mojo::shell::mojom::CapabilityFilterPtr filter( + mojo::shell::mojom::CapabilityFilter::New()); + mojo::Array<mojo::String> test_interfaces; + test_interfaces.push_back( + mojo::shell::test::mojom::CreateInstanceForHandleTest::Name_); + filter->filter.insert("mojo:mojo_shell_apptests", + std::move(test_interfaces)); + + mojo::shell::mojom::ApplicationManagerPtr application_manager; + shell_->ConnectToInterface("mojo:shell", &application_manager); + application_manager->CreateInstanceForHandle( + mojo::ScopedHandle(mojo::Handle(pipe.release().value())), + "exe:application_manager_apptest_target", std::move(filter), + std::move(request)); + + base::LaunchOptions options; + #if defined(OS_WIN) + options.handles_to_inherit = &handle_passing_info; + #elif defined(OS_POSIX) + options.fds_to_remap = &handle_passing_info; + #endif + target_ = base::LaunchProcess(child_command_line, options); + DCHECK(target_.IsValid()); + receiver->SetPID(target_.Pid()); + mojo::edk::ChildProcessLaunched(target_.Handle(), + platform_channel_pair.PassServerHandle()); + } + + bool AcceptConnection(mojo::Connection* connection) override { + connection->AddInterface<Driver>(this); + return true; + } + + // mojo::InterfaceFactory<Driver>: + void Create(mojo::Connection* connection, + mojo::InterfaceRequest<Driver> request) override { + bindings_.AddBinding(this, std::move(request)); + } + + // Driver: + void QuitDriver() override { + target_.Terminate(0, false); + shell_->Quit(); + } + + mojo::Shell* shell_; + base::Process target_; + mojo::WeakBindingSet<Driver> bindings_; + base::WeakPtrFactory<TargetApplicationDelegate> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(TargetApplicationDelegate); +}; + +} // namespace + +int main(int argc, char** argv) { + base::AtExitManager at_exit; + base::CommandLine::Init(argc, argv); + + mojo::shell::InitializeLogging(); + + TargetApplicationDelegate delegate; + return mojo::shell::TestNativeMain(&delegate); +}
diff --git a/mojo/shell/application_manager_apptest_driver_manifest.json b/mojo/shell/tests/application_manager_apptest_driver_manifest.json similarity index 100% rename from mojo/shell/application_manager_apptest_driver_manifest.json rename to mojo/shell/tests/application_manager_apptest_driver_manifest.json
diff --git a/mojo/shell/application_manager_apptest_manifest.json b/mojo/shell/tests/application_manager_apptest_manifest.json similarity index 100% rename from mojo/shell/application_manager_apptest_manifest.json rename to mojo/shell/tests/application_manager_apptest_manifest.json
diff --git a/mojo/shell/tests/application_manager_apptest_target.cc b/mojo/shell/tests/application_manager_apptest_target.cc new file mode 100644 index 0000000..5b5964f --- /dev/null +++ b/mojo/shell/tests/application_manager_apptest_target.cc
@@ -0,0 +1,46 @@ +// 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. + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/macros.h" +#include "mojo/shell/public/cpp/connection.h" +#include "mojo/shell/public/cpp/shell.h" +#include "mojo/shell/public/cpp/shell_client.h" +#include "mojo/shell/runner/child/test_native_main.h" +#include "mojo/shell/runner/init.h" +#include "mojo/shell/tests/application_manager_apptests.mojom.h" + +using mojo::shell::test::mojom::CreateInstanceForHandleTestPtr; + +namespace { + +class TargetApplicationDelegate : public mojo::ShellClient { + public: + TargetApplicationDelegate() {} + ~TargetApplicationDelegate() override {} + + private: + // mojo::ShellClient: + void Initialize(mojo::Shell* shell, const std::string& url, + uint32_t id) override { + CreateInstanceForHandleTestPtr service; + shell->ConnectToInterface("mojo:mojo_shell_apptests", &service); + service->SetTargetID(id); + } + + DISALLOW_COPY_AND_ASSIGN(TargetApplicationDelegate); +}; + +} // namespace + +int main(int argc, char** argv) { + base::AtExitManager at_exit; + base::CommandLine::Init(argc, argv); + + mojo::shell::InitializeLogging(); + + TargetApplicationDelegate delegate; + return mojo::shell::TestNativeMain(&delegate); +}
diff --git a/mojo/shell/application_manager_apptests.mojom b/mojo/shell/tests/application_manager_apptests.mojom similarity index 100% rename from mojo/shell/application_manager_apptests.mojom rename to mojo/shell/tests/application_manager_apptests.mojom
diff --git a/mojo/shell/tests/application_manager_unittest.cc b/mojo/shell/tests/application_manager_unittest.cc new file mode 100644 index 0000000..3ab66877 --- /dev/null +++ b/mojo/shell/tests/application_manager_unittest.cc
@@ -0,0 +1,627 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/shell/application_manager.h" + +#include <utility> + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/macros.h" +#include "base/memory/scoped_vector.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/shell/application_loader.h" +#include "mojo/shell/connect_util.h" +#include "mojo/shell/public/cpp/interface_factory.h" +#include "mojo/shell/public/cpp/shell_client.h" +#include "mojo/shell/public/cpp/shell_connection.h" +#include "mojo/shell/public/interfaces/interface_provider.mojom.h" +#include "mojo/shell/tests/test.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace shell { +namespace test { + +const char kTestURLString[] = "test:testService"; +const char kTestAURLString[] = "test:TestA"; +const char kTestBURLString[] = "test:TestB"; + +struct TestContext { + TestContext() : num_impls(0), num_loader_deletes(0) {} + std::string last_test_string; + int num_impls; + int num_loader_deletes; +}; + +void QuitClosure(bool* value) { + *value = true; + base::MessageLoop::current()->QuitWhenIdle(); +} + +class TestServiceImpl : public TestService { + public: + TestServiceImpl(TestContext* context, InterfaceRequest<TestService> request) + : context_(context), binding_(this, std::move(request)) { + ++context_->num_impls; + } + + ~TestServiceImpl() override { + --context_->num_impls; + if (!base::MessageLoop::current()->is_running()) + return; + base::MessageLoop::current()->QuitWhenIdle(); + } + + // TestService implementation: + void Test(const String& test_string, + const Callback<void()>& callback) override { + context_->last_test_string = test_string; + callback.Run(); + } + + private: + TestContext* context_; + StrongBinding<TestService> binding_; +}; + +class TestClient { + public: + explicit TestClient(TestServicePtr service) + : service_(std::move(service)), quit_after_ack_(false) {} + + void AckTest() { + if (quit_after_ack_) + base::MessageLoop::current()->QuitWhenIdle(); + } + + void Test(const std::string& test_string) { + quit_after_ack_ = true; + service_->Test(test_string, + base::Bind(&TestClient::AckTest, base::Unretained(this))); + } + + private: + TestServicePtr service_; + bool quit_after_ack_; + DISALLOW_COPY_AND_ASSIGN(TestClient); +}; + +class TestApplicationLoader : public ApplicationLoader, + public ShellClient, + public InterfaceFactory<TestService> { + public: + explicit TestApplicationLoader(TestContext* context) + : context_(context), num_loads_(0) {} + + ~TestApplicationLoader() override { + ++context_->num_loader_deletes; + shell_connection_.reset(); + } + + int num_loads() const { return num_loads_; } + const GURL& last_requestor_url() const { return last_requestor_url_; } + + private: + // ApplicationLoader implementation. + void Load(const GURL& url, + InterfaceRequest<mojom::ShellClient> request) override { + ++num_loads_; + shell_connection_.reset(new ShellConnection(this, std::move(request))); + } + + // mojo::ShellClient implementation. + bool AcceptConnection(Connection* connection) override { + connection->AddInterface<TestService>(this); + last_requestor_url_ = GURL(connection->GetRemoteApplicationURL()); + return true; + } + + // InterfaceFactory<TestService> implementation. + void Create(Connection* connection, + InterfaceRequest<TestService> request) override { + new TestServiceImpl(context_, std::move(request)); + } + + scoped_ptr<ShellConnection> shell_connection_; + TestContext* context_; + int num_loads_; + GURL last_requestor_url_; + + DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader); +}; + +class ClosingApplicationLoader : public ApplicationLoader { + private: + // ApplicationLoader implementation. + void Load(const GURL& url, + InterfaceRequest<mojom::ShellClient> request) override { + } +}; + +class TesterContext { + public: + explicit TesterContext(base::MessageLoop* loop) + : num_b_calls_(0), + num_c_calls_(0), + num_a_deletes_(0), + num_b_deletes_(0), + num_c_deletes_(0), + tester_called_quit_(false), + a_called_quit_(false), + loop_(loop) {} + + void IncrementNumBCalls() { + base::AutoLock lock(lock_); + num_b_calls_++; + } + + void IncrementNumCCalls() { + base::AutoLock lock(lock_); + num_c_calls_++; + } + + void IncrementNumADeletes() { + base::AutoLock lock(lock_); + num_a_deletes_++; + } + + void IncrementNumBDeletes() { + base::AutoLock lock(lock_); + num_b_deletes_++; + } + + void IncrementNumCDeletes() { + base::AutoLock lock(lock_); + num_c_deletes_++; + } + + void set_tester_called_quit() { + base::AutoLock lock(lock_); + tester_called_quit_ = true; + } + + void set_a_called_quit() { + base::AutoLock lock(lock_); + a_called_quit_ = true; + } + + int num_b_calls() { + base::AutoLock lock(lock_); + return num_b_calls_; + } + int num_c_calls() { + base::AutoLock lock(lock_); + return num_c_calls_; + } + int num_a_deletes() { + base::AutoLock lock(lock_); + return num_a_deletes_; + } + int num_b_deletes() { + base::AutoLock lock(lock_); + return num_b_deletes_; + } + int num_c_deletes() { + base::AutoLock lock(lock_); + return num_c_deletes_; + } + bool tester_called_quit() { + base::AutoLock lock(lock_); + return tester_called_quit_; + } + bool a_called_quit() { + base::AutoLock lock(lock_); + return a_called_quit_; + } + + void QuitSoon() { + loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + } + + private: + // lock_ protects all members except for loop_ which must be unchanged for the + // lifetime of this class. + base::Lock lock_; + int num_b_calls_; + int num_c_calls_; + int num_a_deletes_; + int num_b_deletes_; + int num_c_deletes_; + bool tester_called_quit_; + bool a_called_quit_; + + base::MessageLoop* loop_; +}; + +// Used to test that the requestor url will be correctly passed. +class TestAImpl : public TestA { + public: + TestAImpl(Shell* shell, + TesterContext* test_context, + InterfaceRequest<TestA> request, + InterfaceFactory<TestC>* factory) + : test_context_(test_context), binding_(this, std::move(request)) { + connection_ = shell->Connect(kTestBURLString); + connection_->AddInterface<TestC>(factory); + connection_->GetInterface(&b_); + } + + ~TestAImpl() override { + test_context_->IncrementNumADeletes(); + if (base::MessageLoop::current()->is_running()) + Quit(); + } + + private: + void CallB() override { + b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this))); + } + + void CallCFromB() override { + b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this))); + } + + void Quit() { + base::MessageLoop::current()->QuitWhenIdle(); + test_context_->set_a_called_quit(); + test_context_->QuitSoon(); + } + + scoped_ptr<Connection> connection_; + TesterContext* test_context_; + TestBPtr b_; + StrongBinding<TestA> binding_; +}; + +class TestBImpl : public TestB { + public: + TestBImpl(Connection* connection, + TesterContext* test_context, + InterfaceRequest<TestB> request) + : test_context_(test_context), binding_(this, std::move(request)) { + connection->GetInterface(&c_); + } + + ~TestBImpl() override { + test_context_->IncrementNumBDeletes(); + if (base::MessageLoop::current()->is_running()) + base::MessageLoop::current()->QuitWhenIdle(); + test_context_->QuitSoon(); + } + + private: + void B(const Callback<void()>& callback) override { + test_context_->IncrementNumBCalls(); + callback.Run(); + } + + void CallC(const Callback<void()>& callback) override { + test_context_->IncrementNumBCalls(); + c_->C(callback); + } + + TesterContext* test_context_; + TestCPtr c_; + StrongBinding<TestB> binding_; +}; + +class TestCImpl : public TestC { + public: + TestCImpl(Connection* connection, + TesterContext* test_context, + InterfaceRequest<TestC> request) + : test_context_(test_context), binding_(this, std::move(request)) {} + + ~TestCImpl() override { test_context_->IncrementNumCDeletes(); } + + private: + void C(const Callback<void()>& callback) override { + test_context_->IncrementNumCCalls(); + callback.Run(); + } + + TesterContext* test_context_; + StrongBinding<TestC> binding_; +}; + +class Tester : public ShellClient, + public ApplicationLoader, + public InterfaceFactory<TestA>, + public InterfaceFactory<TestB>, + public InterfaceFactory<TestC> { + public: + Tester(TesterContext* context, const std::string& requestor_url) + : context_(context), requestor_url_(requestor_url) {} + ~Tester() override {} + + private: + void Load(const GURL& url, + InterfaceRequest<mojom::ShellClient> request) override { + app_.reset(new ShellConnection(this, std::move(request))); + } + + bool AcceptConnection(Connection* connection) override { + if (!requestor_url_.empty() && + requestor_url_ != connection->GetRemoteApplicationURL()) { + context_->set_tester_called_quit(); + context_->QuitSoon(); + base::MessageLoop::current()->QuitWhenIdle(); + return false; + } + // If we're coming from A, then add B, otherwise A. + if (connection->GetRemoteApplicationURL() == kTestAURLString) + connection->AddInterface<TestB>(this); + else + connection->AddInterface<TestA>(this); + return true; + } + + void Create(Connection* connection, + InterfaceRequest<TestA> request) override { + a_bindings_.push_back( + new TestAImpl(app_.get(), context_, std::move(request), this)); + } + + void Create(Connection* connection, + InterfaceRequest<TestB> request) override { + new TestBImpl(connection, context_, std::move(request)); + } + + void Create(Connection* connection, + InterfaceRequest<TestC> request) override { + new TestCImpl(connection, context_, std::move(request)); + } + + TesterContext* context_; + scoped_ptr<ShellConnection> app_; + std::string requestor_url_; + ScopedVector<TestAImpl> a_bindings_; +}; + +void OnConnect(base::RunLoop* loop, uint32_t instance_id) { + loop->Quit(); +} + +class ApplicationManagerTest : public testing::Test { + public: + ApplicationManagerTest() : tester_context_(&loop_) {} + + ~ApplicationManagerTest() override {} + + void SetUp() override { + application_manager_.reset(new ApplicationManager(true)); + test_loader_ = new TestApplicationLoader(&context_); + application_manager_->set_default_loader( + scoped_ptr<ApplicationLoader>(test_loader_)); + + TestServicePtr service_proxy; + ConnectToInterface(GURL(kTestURLString), &service_proxy); + test_client_.reset(new TestClient(std::move(service_proxy))); + } + + void TearDown() override { + test_client_.reset(); + application_manager_.reset(); + } + + void AddLoaderForURL(const GURL& url, const std::string& requestor_url) { + application_manager_->SetLoaderForURL( + make_scoped_ptr(new Tester(&tester_context_, requestor_url)), url); + } + + bool HasRunningInstanceForURL(const GURL& url) { + ApplicationManager::TestAPI manager_test_api(application_manager_.get()); + return manager_test_api.HasRunningInstanceForURL(url); + } + + protected: + template <typename Interface> + void ConnectToInterface(const GURL& url, InterfacePtr<Interface>* ptr) { + base::RunLoop loop; + mojom::InterfaceProviderPtr remote_interfaces; + scoped_ptr<ConnectToApplicationParams> params( + new ConnectToApplicationParams); + params->set_source(CreateShellIdentity()); + params->set_target(Identity(url)); + params->set_remote_interfaces(GetProxy(&remote_interfaces)); + params->set_connect_callback( + base::Bind(&OnConnect, base::Unretained(&loop))); + application_manager_->ConnectToApplication(std::move(params)); + loop.Run(); + + mojo::GetInterface(remote_interfaces.get(), ptr); + } + + base::ShadowingAtExitManager at_exit_; + TestApplicationLoader* test_loader_; + TesterContext tester_context_; + TestContext context_; + base::MessageLoop loop_; + scoped_ptr<TestClient> test_client_; + scoped_ptr<ApplicationManager> application_manager_; + DISALLOW_COPY_AND_ASSIGN(ApplicationManagerTest); +}; + +TEST_F(ApplicationManagerTest, Basic) { + test_client_->Test("test"); + loop_.Run(); + EXPECT_EQ(std::string("test"), context_.last_test_string); +} + +TEST_F(ApplicationManagerTest, ClientError) { + test_client_->Test("test"); + EXPECT_TRUE(HasRunningInstanceForURL(GURL(kTestURLString))); + loop_.Run(); + EXPECT_EQ(1, context_.num_impls); + test_client_.reset(); + loop_.Run(); + EXPECT_EQ(0, context_.num_impls); + EXPECT_TRUE(HasRunningInstanceForURL(GURL(kTestURLString))); +} + +TEST_F(ApplicationManagerTest, Deletes) { + { + ApplicationManager am(true); + TestApplicationLoader* default_loader = + new TestApplicationLoader(&context_); + TestApplicationLoader* url_loader1 = new TestApplicationLoader(&context_); + TestApplicationLoader* url_loader2 = new TestApplicationLoader(&context_); + am.set_default_loader(scoped_ptr<ApplicationLoader>(default_loader)); + am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader1), + GURL("test:test1")); + am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader2), + GURL("test:test1")); + } + EXPECT_EQ(3, context_.num_loader_deletes); +} + +// Test for SetLoaderForURL() & set_default_loader(). +TEST_F(ApplicationManagerTest, SetLoaders) { + TestApplicationLoader* default_loader = new TestApplicationLoader(&context_); + TestApplicationLoader* url_loader = new TestApplicationLoader(&context_); + application_manager_->set_default_loader( + scoped_ptr<ApplicationLoader>(default_loader)); + application_manager_->SetLoaderForURL( + scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1")); + + // test::test1 should go to url_loader. + TestServicePtr test_service; + ConnectToInterface(GURL("test:test1"), &test_service); + EXPECT_EQ(1, url_loader->num_loads()); + EXPECT_EQ(0, default_loader->num_loads()); + + // http::test1 should go to default loader. + ConnectToInterface(GURL("http:test1"), &test_service); + EXPECT_EQ(1, url_loader->num_loads()); + EXPECT_EQ(1, default_loader->num_loads()); +} + +// Confirm that the url of a service is correctly passed to another service that +// it loads. +// TODO(beng): these tests are disabled due to the new async connect flow. +// they should be re-written as shell apptests. +TEST_F(ApplicationManagerTest, DISABLED_ACallB) { + // Any url can load a. + AddLoaderForURL(GURL(kTestAURLString), std::string()); + + // Only a can load b. + AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); + + TestAPtr a; + ConnectToInterface(GURL(kTestAURLString), &a); + a->CallB(); + loop_.Run(); + EXPECT_EQ(1, tester_context_.num_b_calls()); + EXPECT_TRUE(tester_context_.a_called_quit()); +} + +// A calls B which calls C. +TEST_F(ApplicationManagerTest, DISABLED_BCallC) { + // Any url can load a. + AddLoaderForURL(GURL(kTestAURLString), std::string()); + + // Only a can load b. + AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); + + TestAPtr a; + ConnectToInterface(GURL(kTestAURLString), &a); + a->CallCFromB(); + loop_.Run(); + + EXPECT_EQ(1, tester_context_.num_b_calls()); + EXPECT_EQ(1, tester_context_.num_c_calls()); + EXPECT_TRUE(tester_context_.a_called_quit()); +} + +// Confirm that a service impl will be deleted if the app that connected to +// it goes away. +TEST_F(ApplicationManagerTest, DISABLED_BDeleted) { + AddLoaderForURL(GURL(kTestAURLString), std::string()); + AddLoaderForURL(GURL(kTestBURLString), std::string()); + + TestAPtr a; + ConnectToInterface(GURL(kTestAURLString), &a); + + a->CallB(); + loop_.Run(); + + // Kills the a app. + application_manager_->SetLoaderForURL(scoped_ptr<ApplicationLoader>(), + GURL(kTestAURLString)); + loop_.Run(); + + EXPECT_EQ(1, tester_context_.num_b_deletes()); +} + +// Confirm that the url of a service is correctly passed to another service that +// it loads, and that it can be rejected. +TEST_F(ApplicationManagerTest, DISABLED_ANoLoadB) { + // Any url can load a. + AddLoaderForURL(GURL(kTestAURLString), std::string()); + + // Only c can load b, so this will fail. + AddLoaderForURL(GURL(kTestBURLString), "test:TestC"); + + TestAPtr a; + ConnectToInterface(GURL(kTestAURLString), &a); + a->CallB(); + loop_.Run(); + EXPECT_EQ(0, tester_context_.num_b_calls()); + + EXPECT_FALSE(tester_context_.a_called_quit()); + EXPECT_TRUE(tester_context_.tester_called_quit()); +} + +TEST_F(ApplicationManagerTest, NoServiceNoLoad) { + AddLoaderForURL(GURL(kTestAURLString), std::string()); + + // There is no TestC service implementation registered with + // ApplicationManager, so this cannot succeed (but also shouldn't crash). + TestCPtr c; + ConnectToInterface(GURL(kTestAURLString), &c); + c.set_connection_error_handler( + []() { base::MessageLoop::current()->QuitWhenIdle(); }); + + loop_.Run(); + EXPECT_TRUE(c.encountered_error()); +} + +TEST_F(ApplicationManagerTest, TestEndApplicationClosure) { + ClosingApplicationLoader* loader = new ClosingApplicationLoader(); + application_manager_->SetLoaderForURL( + scoped_ptr<ApplicationLoader>(loader), GURL("test:test")); + + bool called = false; + scoped_ptr<ConnectToApplicationParams> params(new ConnectToApplicationParams); + params->SetTargetURL(GURL("test:test")); + params->set_on_application_end( + base::Bind(&QuitClosure, base::Unretained(&called))); + application_manager_->ConnectToApplication(std::move(params)); + loop_.Run(); + EXPECT_TRUE(called); +} + +TEST_F(ApplicationManagerTest, SameIdentityShouldNotCauseDuplicateLoad) { + // 1 because ApplicationManagerTest connects once at startup. + EXPECT_EQ(1, test_loader_->num_loads()); + + TestServicePtr test_service; + ConnectToInterface(GURL("test:foo"), &test_service); + EXPECT_EQ(2, test_loader_->num_loads()); + + // Exactly the same URL as above. + ConnectToInterface(GURL("test:foo"), &test_service); + EXPECT_EQ(2, test_loader_->num_loads()); + + // A different identity because the domain is different. + ConnectToInterface(GURL("test:bar"), &test_service); + EXPECT_EQ(3, test_loader_->num_loads()); +} + +} // namespace test +} // namespace shell +} // namespace mojo
diff --git a/mojo/shell/tests/capability_filter_test.cc b/mojo/shell/tests/capability_filter_test.cc new file mode 100644 index 0000000..4ed390a3 --- /dev/null +++ b/mojo/shell/tests/capability_filter_test.cc
@@ -0,0 +1,351 @@ +// 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. + +#include "mojo/shell/tests/capability_filter_test.h" + +#include <utility> + +#include "base/macros.h" +#include "base/stl_util.h" +#include "base/strings/stringprintf.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/weak_binding_set.h" +#include "mojo/shell/application_loader.h" +#include "mojo/shell/public/cpp/connection.h" +#include "mojo/shell/public/cpp/interface_factory.h" +#include "mojo/shell/public/cpp/shell_connection.h" + +namespace mojo { +namespace shell { +namespace test { + +// Lives on the main thread of the test. +// Listens for interfaces exposed/blocked and for application connections being +// closed. Quits |loop| when all expectations are met. +class ConnectionValidator : public ApplicationLoader, + public ShellClient, + public InterfaceFactory<Validator>, + public Validator { + public: + ConnectionValidator(const std::set<std::string>& expectations, + base::MessageLoop* loop) + : app_(nullptr), + expectations_(expectations), + loop_(loop) {} + ~ConnectionValidator() override {} + + bool expectations_met() { + return unexpected_.empty() && expectations_.empty(); + } + + void PrintUnmetExpectations() { + for (auto expectation : expectations_) + ADD_FAILURE() << "Unmet: " << expectation; + for (auto unexpected : unexpected_) + ADD_FAILURE() << "Unexpected: " << unexpected; + } + + private: + // Overridden from ApplicationLoader: + void Load(const GURL& url, + InterfaceRequest<mojom::ShellClient> request) override { + app_.reset(new ShellConnection(this, std::move(request))); + } + + // Overridden from ShellClient: + bool AcceptConnection(Connection* connection) override { + connection->AddInterface<Validator>(this); + return true; + } + + // Overridden from InterfaceFactory<Validator>: + void Create(Connection* connection, + InterfaceRequest<Validator> request) override { + validator_bindings_.AddBinding(this, std::move(request)); + } + + // Overridden from Validator: + void AddInterfaceCalled(const String& app_url, + const String& service_url, + const String& name, + bool blocked) override { + Validate(base::StringPrintf("%s %s %s %s", + blocked ? "B" : "E", app_url.data(), service_url.data(), name.data())); + } + void ConnectionClosed(const String& app_url, + const String& service_url) override { + Validate(base::StringPrintf("C %s %s", app_url.data(), service_url.data())); + } + + void Validate(const std::string& result) { + DVLOG(1) << "Validate: " << result; + auto i = expectations_.find(result); + if (i != expectations_.end()) { + expectations_.erase(i); + if (expectations_.empty()) + loop_->QuitWhenIdle(); + } else { + // This is a test failure, and will result in PrintUnexpectedExpecations() + // being called. + unexpected_.insert(result); + loop_->QuitWhenIdle(); + } + } + + scoped_ptr<ShellConnection> app_; + std::set<std::string> expectations_; + std::set<std::string> unexpected_; + base::MessageLoop* loop_; + WeakBindingSet<Validator> validator_bindings_; + + DISALLOW_COPY_AND_ASSIGN(ConnectionValidator); +}; + +// This class models a system service that exposes two interfaces, Safe and +// Unsafe. The interface Unsafe is not to be exposed to untrusted applications. +class ServiceApplication : public ShellClient, + public InterfaceFactory<Safe>, + public InterfaceFactory<Unsafe>, + public Safe, + public Unsafe { + public: + ServiceApplication() : shell_(nullptr) {} + ~ServiceApplication() override {} + + private: + // Overridden from ShellClient: + void Initialize(Shell* shell, const std::string& url, uint32_t id) override { + shell_ = shell; + // ServiceApplications have no capability filter and can thus connect + // directly to the validator application. + shell_->ConnectToInterface("test:validator", &validator_); + } + bool AcceptConnection(Connection* connection) override { + AddInterface<Safe>(connection); + AddInterface<Unsafe>(connection); + return true; + } + + // Overridden from InterfaceFactory<Safe>: + void Create(Connection* connection, + InterfaceRequest<Safe> request) override { + safe_bindings_.AddBinding(this, std::move(request)); + } + + // Overridden from InterfaceFactory<Unsafe>: + void Create(Connection* connection, + InterfaceRequest<Unsafe> request) override { + unsafe_bindings_.AddBinding(this, std::move(request)); + } + + template <typename Interface> + void AddInterface(Connection* connection) { + validator_->AddInterfaceCalled(connection->GetRemoteApplicationURL(), + connection->GetConnectionURL(), + Interface::Name_, + !connection->AddInterface<Interface>(this)); + } + + Shell* shell_; + ValidatorPtr validator_; + WeakBindingSet<Safe> safe_bindings_; + WeakBindingSet<Unsafe> unsafe_bindings_; + + DISALLOW_COPY_AND_ASSIGN(ServiceApplication); +}; + +//////////////////////////////////////////////////////////////////////////////// +// TestApplication: + +TestApplication::TestApplication() : shell_(nullptr) {} +TestApplication::~TestApplication() {} + +void TestApplication::Initialize(Shell* shell, const std::string& url, + uint32_t id) { + shell_ = shell; + url_ = url; +} +bool TestApplication::AcceptConnection(Connection* connection) { + // TestApplications receive their Validator via the inbound connection. + connection->GetInterface(&validator_); + + connection1_ = shell_->Connect("test:service"); + connection1_->SetRemoteInterfaceProviderConnectionErrorHandler( + base::Bind(&TestApplication::ConnectionClosed, + base::Unretained(this), "test:service")); + + connection2_ = shell_->Connect("test:service2"); + connection2_->SetRemoteInterfaceProviderConnectionErrorHandler( + base::Bind(&TestApplication::ConnectionClosed, + base::Unretained(this), "test:service2")); + return true; +} + +void TestApplication::ConnectionClosed(const std::string& service_url) { + validator_->ConnectionClosed(url_, service_url); +} + +//////////////////////////////////////////////////////////////////////////////// +// TestLoader: + +TestLoader::TestLoader(ShellClient* delegate) : delegate_(delegate) {} +TestLoader::~TestLoader() {} + +void TestLoader::Load(const GURL& url, + InterfaceRequest<mojom::ShellClient> request) { + app_.reset(new ShellConnection(delegate_.get(), std::move(request))); +} + +//////////////////////////////////////////////////////////////////////////////// +// CapabilityFilterTest: + +CapabilityFilterTest::CapabilityFilterTest() : validator_(nullptr) {} +CapabilityFilterTest::~CapabilityFilterTest() {} + +void CapabilityFilterTest::RunBlockingTest() { + std::set<std::string> expectations; + expectations.insert("E test:trusted test:service mojo::shell::Safe"); + expectations.insert("E test:trusted test:service mojo::shell::Unsafe"); + expectations.insert("E test:trusted test:service2 mojo::shell::Safe"); + expectations.insert("E test:trusted test:service2 mojo::shell::Unsafe"); + expectations.insert("E test:untrusted test:service mojo::shell::Safe"); + expectations.insert("B test:untrusted test:service mojo::shell::Unsafe"); + expectations.insert("C test:untrusted test:service2"); + InitValidator(expectations); + + // This first application can only connect to test:service. Connections to + // test:service2 will be blocked. It also will only be able to see the + // "Safe" interface exposed by test:service. It will be blocked from seeing + // "Unsafe". + AllowedInterfaces interfaces; + interfaces.insert(Safe::Name_); + CapabilityFilter filter; + filter["test:service"] = interfaces; + RunApplication("test:untrusted", filter); + + // This second application can connect to both test:service and + // test:service2. It can connect to both "Safe" and "Unsafe" interfaces. + RunApplication("test:trusted", GetPermissiveCapabilityFilter()); + + RunTest(); +} + +void CapabilityFilterTest::RunWildcardTest() { + std::set<std::string> expectations; + expectations.insert("E test:wildcard test:service mojo::shell::Safe"); + expectations.insert("E test:wildcard test:service mojo::shell::Unsafe"); + expectations.insert("E test:wildcard test:service2 mojo::shell::Safe"); + expectations.insert("E test:wildcard test:service2 mojo::shell::Unsafe"); + expectations.insert("C test:blocked test:service"); + expectations.insert("C test:blocked test:service2"); + expectations.insert("B test:wildcard2 test:service mojo::shell::Safe"); + expectations.insert("B test:wildcard2 test:service mojo::shell::Unsafe"); + expectations.insert("B test:wildcard2 test:service2 mojo::shell::Safe"); + expectations.insert("B test:wildcard2 test:service2 mojo::shell::Unsafe"); + expectations.insert("E test:wildcard3 test:service mojo::shell::Safe"); + expectations.insert("E test:wildcard3 test:service mojo::shell::Unsafe"); + expectations.insert("E test:wildcard3 test:service2 mojo::shell::Safe"); + expectations.insert("B test:wildcard3 test:service2 mojo::shell::Unsafe"); + InitValidator(expectations); + + // This application is allowed to connect to any application because of a + // wildcard rule, and any interface exposed because of a wildcard rule in + // the interface array. + RunApplication("test:wildcard", GetPermissiveCapabilityFilter()); + + // This application is allowed to connect to no other applications because + // of an empty capability filter. + RunApplication("test:blocked", CapabilityFilter()); + + // This application is allowed to connect to any application because of a + // wildcard rule but may not connect to any interfaces because of an empty + // interface array. + CapabilityFilter filter1; + filter1["*"] = AllowedInterfaces(); + RunApplication("test:wildcard2", filter1); + + // This application is allowed to connect to both test:service and + // test:service2, and may see any interface exposed by test:service but only + // the Safe interface exposed by test:service2. + AllowedInterfaces interfaces2; + interfaces2.insert("*"); + CapabilityFilter filter2; + filter2["test:service"] = interfaces2; + AllowedInterfaces interfaces3; + interfaces3.insert(Safe::Name_); + filter2["test:service2"] = interfaces3; + RunApplication("test:wildcard3", filter2); +} + + +void CapabilityFilterTest::SetUp() { + application_manager_.reset(new ApplicationManager(true)); + CreateLoader<ServiceApplication>("test:service"); + CreateLoader<ServiceApplication>("test:service2"); +} + +void CapabilityFilterTest::TearDown() { + application_manager_.reset(); +} + +class InterfaceProviderImpl : public shell::mojom::InterfaceProvider { + public: + explicit InterfaceProviderImpl( + shell::mojom::InterfaceProviderRequest interfaces, + InterfaceFactory<Validator>* factory) + : binding_(this, std::move(interfaces)), + factory_(factory) {} + ~InterfaceProviderImpl() override {} + + private: + // shell::mojom::InterfaceProvider method. + void GetInterface(const mojo::String& interface_name, + ScopedMessagePipeHandle client_handle) override { + if (interface_name == Validator::Name_) { + factory_->Create(nullptr, + MakeRequest<Validator>(std::move(client_handle))); + } + } + + Binding<InterfaceProvider> binding_; + InterfaceFactory<Validator>* factory_; + + DISALLOW_COPY_AND_ASSIGN(InterfaceProviderImpl); +}; + +void CapabilityFilterTest::RunApplication(const std::string& url, + const CapabilityFilter& filter) { + shell::mojom::InterfaceProviderPtr remote_interfaces; + + // We expose Validator to the test application via ConnectToApplication + // because we don't allow the test application to connect to test:validator. + // Adding it to the CapabilityFilter would interfere with the test. + shell::mojom::InterfaceProviderPtr local_interfaces; + new InterfaceProviderImpl(GetProxy(&local_interfaces), validator_); + scoped_ptr<ConnectToApplicationParams> params( + new ConnectToApplicationParams); + params->set_target(Identity(GURL(url), std::string(), filter)); + params->set_remote_interfaces(GetProxy(&remote_interfaces)); + params->set_local_interfaces(std::move(local_interfaces)); + params->set_on_application_end(base::MessageLoop::QuitWhenIdleClosure()); + application_manager_->ConnectToApplication(std::move(params)); +} + +void CapabilityFilterTest::InitValidator( + const std::set<std::string>& expectations) { + validator_ = new ConnectionValidator(expectations, &loop_); + application_manager()->SetLoaderForURL(make_scoped_ptr(validator_), + GURL("test:validator")); +} + +void CapabilityFilterTest::RunTest() { + loop()->Run(); + EXPECT_TRUE(validator_->expectations_met()); + if (!validator_->expectations_met()) + validator_->PrintUnmetExpectations(); +} + +} // namespace test +} // namespace shell +} // namespace mojo
diff --git a/mojo/shell/tests/capability_filter_test.h b/mojo/shell/tests/capability_filter_test.h new file mode 100644 index 0000000..3e9baa80 --- /dev/null +++ b/mojo/shell/tests/capability_filter_test.h
@@ -0,0 +1,107 @@ +// 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. + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "mojo/shell/application_loader.h" +#include "mojo/shell/application_manager.h" +#include "mojo/shell/public/cpp/shell_client.h" +#include "mojo/shell/public/cpp/shell_connection.h" +#include "mojo/shell/tests/capability_filter_unittest.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace shell { +namespace test { + +class ConnectionValidator; + +// This class models an application who will use the shell to interact with a +// system service. The shell may limit this application's visibility of the full +// set of interfaces exposed by that service. +class TestApplication : public ShellClient { + public: + TestApplication(); + ~TestApplication() override; + + private: + // Overridden from ShellClient: + void Initialize(Shell* shell, const std::string& url, uint32_t id) override; + bool AcceptConnection(Connection* connection) override; + + void ConnectionClosed(const std::string& service_url); + + Shell* shell_; + std::string url_; + ValidatorPtr validator_; + scoped_ptr<Connection> connection1_; + scoped_ptr<Connection> connection2_; + + DISALLOW_COPY_AND_ASSIGN(TestApplication); +}; + +class TestLoader : public ApplicationLoader { + public: + explicit TestLoader(ShellClient* delegate); + ~TestLoader() override; + + private: + // Overridden from ApplicationLoader: + void Load(const GURL& url, + InterfaceRequest<mojom::ShellClient> request) override; + + scoped_ptr<ShellClient> delegate_; + scoped_ptr<ShellConnection> app_; + + DISALLOW_COPY_AND_ASSIGN(TestLoader); +}; + +class CapabilityFilterTest : public testing::Test { + public: + CapabilityFilterTest(); + ~CapabilityFilterTest() override; + + protected: + template <class T> + void CreateLoader(const std::string& url) { + application_manager_->SetLoaderForURL( + make_scoped_ptr(new TestLoader(new T)), GURL(url)); + } + + void RunBlockingTest(); + void RunWildcardTest(); + + // Overridden from testing::Test: + void SetUp() override; + void TearDown() override; + + base::MessageLoop* loop() { return &loop_; } + ApplicationManager* application_manager() { + return application_manager_.get(); + } + ConnectionValidator* validator() { return validator_; } + + private: + void RunApplication(const std::string& url, const CapabilityFilter& filter); + void InitValidator(const std::set<std::string>& expectations); + void RunTest(); + + template<class T> + scoped_ptr<ShellClient> CreateShellClient() { + return scoped_ptr<ShellClient>(new T); + } + + base::ShadowingAtExitManager at_exit_; + base::MessageLoop loop_; + scoped_ptr<ApplicationManager> application_manager_; + ConnectionValidator* validator_; + + DISALLOW_COPY_AND_ASSIGN(CapabilityFilterTest); +}; + +} // namespace test +} // namespace shell +} // namespace mojo
diff --git a/mojo/shell/tests/capability_filter_unittest.cc b/mojo/shell/tests/capability_filter_unittest.cc new file mode 100644 index 0000000..42748a5 --- /dev/null +++ b/mojo/shell/tests/capability_filter_unittest.cc
@@ -0,0 +1,38 @@ +// 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. + +#include "base/macros.h" +#include "mojo/shell/tests/capability_filter_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace shell { +namespace test { + +class CapabilityFilterApplicationTest : public CapabilityFilterTest { + public: + CapabilityFilterApplicationTest() {} + ~CapabilityFilterApplicationTest() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(CapabilityFilterApplicationTest); +}; + +TEST_F(CapabilityFilterApplicationTest, Blocking) { + CreateLoader<TestApplication>("test:trusted"); + CreateLoader<TestApplication>("test:untrusted"); + RunBlockingTest(); +} + +TEST_F(CapabilityFilterApplicationTest, Wildcards) { + CreateLoader<TestApplication>("test:wildcard"); + CreateLoader<TestApplication>("test:blocked"); + CreateLoader<TestApplication>("test:wildcard2"); + CreateLoader<TestApplication>("test:wildcard3"); + RunWildcardTest(); +} + +} // namespace test +} // namespace shell +} // namespace mojo
diff --git a/mojo/shell/capability_filter_unittest.mojom b/mojo/shell/tests/capability_filter_unittest.mojom similarity index 100% rename from mojo/shell/capability_filter_unittest.mojom rename to mojo/shell/tests/capability_filter_unittest.mojom
diff --git a/mojo/shell/tests/package_apptest.cc b/mojo/shell/tests/package_apptest.cc new file mode 100644 index 0000000..b29bc02 --- /dev/null +++ b/mojo/shell/tests/package_apptest.cc
@@ -0,0 +1,84 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "mojo/shell/public/cpp/application_test_base.h" +#include "mojo/shell/public/interfaces/application_manager.mojom.h" +#include "mojo/shell/tests/package_test.mojom.h" + +// Tests that multiple applications can be packaged in a single Mojo application +// implementing ShellClientFactory; that these applications can be specified by +// the package's manifest and are thus registered with the PackageManager. + +namespace mojo { +namespace shell { +namespace { +void ReceiveName(std::string* out_name, + base::RunLoop* loop, + const String& name) { + *out_name = name; + loop->Quit(); +} +} // namespace + +using PackageApptest = mojo::test::ApplicationTestBase; + +TEST_F(PackageApptest, Basic) { + std::set<uint32_t> ids; + { + // We need to do this to force the shell to read the test app's manifest and + // register aliases. + test::mojom::PackageTestServicePtr root_service; + scoped_ptr<Connection> connection = + shell()->Connect("mojo:package_test_package"); + connection->GetInterface(&root_service); + base::RunLoop run_loop; + std::string root_name; + root_service->GetName(base::Bind(&ReceiveName, &root_name, &run_loop)); + run_loop.Run(); + uint32_t id = mojom::Shell::kInvalidApplicationID; + EXPECT_TRUE(connection->GetRemoteApplicationID(&id)); + ids.insert(id); + } + + { + // Now subsequent connects to applications provided by the root app will be + // resolved correctly. + test::mojom::PackageTestServicePtr service_a; + scoped_ptr<Connection> connection = shell()->Connect("mojo:package_test_a"); + connection->GetInterface(&service_a); + base::RunLoop run_loop; + std::string a_name; + service_a->GetName(base::Bind(&ReceiveName, &a_name, &run_loop)); + run_loop.Run(); + EXPECT_EQ("A", a_name); + uint32_t id = mojom::Shell::kInvalidApplicationID; + EXPECT_TRUE(connection->GetRemoteApplicationID(&id)); + ids.insert(id); + } + + { + test::mojom::PackageTestServicePtr service_b; + scoped_ptr<Connection> connection = shell()->Connect("mojo:package_test_b"); + connection->GetInterface(&service_b); + base::RunLoop run_loop; + std::string b_name; + service_b->GetName(base::Bind(&ReceiveName, &b_name, &run_loop)); + run_loop.Run(); + EXPECT_EQ("B", b_name); + uint32_t id = mojom::Shell::kInvalidApplicationID; + EXPECT_TRUE(connection->GetRemoteApplicationID(&id)); + ids.insert(id); + } +} + +} // namespace shell +} // namespace mojo
diff --git a/mojo/shell/package_test.mojom b/mojo/shell/tests/package_test.mojom similarity index 100% rename from mojo/shell/package_test.mojom rename to mojo/shell/tests/package_test.mojom
diff --git a/mojo/shell/package_test_app_a_manifest.json b/mojo/shell/tests/package_test_app_a_manifest.json similarity index 100% rename from mojo/shell/package_test_app_a_manifest.json rename to mojo/shell/tests/package_test_app_a_manifest.json
diff --git a/mojo/shell/package_test_app_b_manifest.json b/mojo/shell/tests/package_test_app_b_manifest.json similarity index 100% rename from mojo/shell/package_test_app_b_manifest.json rename to mojo/shell/tests/package_test_app_b_manifest.json
diff --git a/mojo/shell/tests/package_test_package.cc b/mojo/shell/tests/package_test_package.cc new file mode 100644 index 0000000..4a37ebe --- /dev/null +++ b/mojo/shell/tests/package_test_package.cc
@@ -0,0 +1,166 @@ +// 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 <stddef.h> +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/threading/simple_thread.h" +#include "mojo/public/c/system/main.h" +#include "mojo/public/cpp/bindings/weak_binding_set.h" +#include "mojo/shell/public/cpp/application_runner.h" +#include "mojo/shell/public/cpp/interface_factory.h" +#include "mojo/shell/public/cpp/shell.h" +#include "mojo/shell/public/cpp/shell_client.h" +#include "mojo/shell/public/interfaces/shell_client_factory.mojom.h" +#include "mojo/shell/tests/package_test.mojom.h" + +// Tests that multiple applications can be packaged in a single Mojo application +// implementing ShellClientFactory; that these applications can be specified by +// the package's manifest and are thus registered with the PackageManager. + +namespace mojo { +namespace shell { + +using GetNameCallback = test::mojom::PackageTestService::GetNameCallback; + +class ProvidedShellClient + : public ShellClient, + public InterfaceFactory<test::mojom::PackageTestService>, + public test::mojom::PackageTestService, + public base::SimpleThread { + public: + ProvidedShellClient(const std::string& name, + mojom::ShellClientRequest request) + : base::SimpleThread(name), + name_(name), + request_(std::move(request)), + shell_(nullptr) { + Start(); + } + ~ProvidedShellClient() override { + Join(); + } + + private: + // mojo::ShellClient: + void Initialize(Shell* shell, const std::string& url, uint32_t id) override { + shell_ = shell; + bindings_.set_connection_error_handler( + base::Bind(&ProvidedShellClient::OnConnectionError, + base::Unretained(this))); + } + bool AcceptConnection(Connection* connection) override { + connection->AddInterface<test::mojom::PackageTestService>( + this); + return true; + } + + // InterfaceFactory<test::mojom::PackageTestService>: + void Create(Connection* connection, + test::mojom::PackageTestServiceRequest request) override { + bindings_.AddBinding(this, std::move(request)); + } + + // test::mojom::PackageTestService: + void GetName(const GetNameCallback& callback) override { + callback.Run(name_); + } + + // base::SimpleThread: + void Run() override { + ApplicationRunner(this).Run(request_.PassMessagePipe().release().value(), + false); + delete this; + } + + void OnConnectionError() { + if (bindings_.empty()) + shell_->Quit(); + } + + const std::string name_; + mojom::ShellClientRequest request_; + Shell* shell_; + WeakBindingSet<test::mojom::PackageTestService> bindings_; + + DISALLOW_COPY_AND_ASSIGN(ProvidedShellClient); +}; + +class PackageTestShellClient + : public ShellClient, + public InterfaceFactory<mojom::ShellClientFactory>, + public InterfaceFactory<test::mojom::PackageTestService>, + public mojom::ShellClientFactory, + public test::mojom::PackageTestService { + public: + PackageTestShellClient() : shell_(nullptr) {} + ~PackageTestShellClient() override {} + + private: + // mojo::ShellClient: + void Initialize(Shell* shell, const std::string& url, uint32_t id) override { + shell_ = shell; + bindings_.set_connection_error_handler( + base::Bind(&PackageTestShellClient::OnConnectionError, + base::Unretained(this))); + } + bool AcceptConnection(Connection* connection) override { + connection->AddInterface<ShellClientFactory>(this); + connection->AddInterface<test::mojom::PackageTestService>(this); + return true; + } + + // InterfaceFactory<mojom::ShellClientFactory>: + void Create(Connection* connection, + mojom::ShellClientFactoryRequest request) override { + shell_client_factory_bindings_.AddBinding(this, std::move(request)); + } + + // InterfaceFactory<test::mojom::PackageTestService>: + void Create(Connection* connection, + test::mojom::PackageTestServiceRequest request) override { + bindings_.AddBinding(this, std::move(request)); + } + + // mojom::ShellClientFactory: + void CreateShellClient(mojom::ShellClientRequest request, + const String& url) override { + if (url == "mojo://package_test_a/") + new ProvidedShellClient("A", std::move(request)); + else if (url == "mojo://package_test_b/") + new ProvidedShellClient("B", std::move(request)); + } + + // test::mojom::PackageTestService: + void GetName(const GetNameCallback& callback) override { + callback.Run("ROOT"); + } + + void OnConnectionError() { + if (bindings_.empty()) + shell_->Quit(); + } + + Shell* shell_; + std::vector<scoped_ptr<ShellClient>> delegates_; + WeakBindingSet<mojom::ShellClientFactory> shell_client_factory_bindings_; + WeakBindingSet<test::mojom::PackageTestService> bindings_; + + DISALLOW_COPY_AND_ASSIGN(PackageTestShellClient); +}; + +} // namespace shell +} // namespace mojo + + +MojoResult MojoMain(MojoHandle shell_handle) { + MojoResult rv = mojo::ApplicationRunner( + new mojo::shell::PackageTestShellClient).Run(shell_handle); + return rv; +}
diff --git a/mojo/shell/package_test_package_manifest.json b/mojo/shell/tests/package_test_package_manifest.json similarity index 100% rename from mojo/shell/package_test_package_manifest.json rename to mojo/shell/tests/package_test_package_manifest.json
diff --git a/mojo/shell/test.mojom b/mojo/shell/tests/test.mojom similarity index 100% rename from mojo/shell/test.mojom rename to mojo/shell/tests/test.mojom
diff --git a/mojo/tools/data/apptests b/mojo/tools/data/apptests index 0a7f9d6..a67ff167 100644 --- a/mojo/tools/data/apptests +++ b/mojo/tools/data/apptests
@@ -79,10 +79,6 @@ # }, # TODO(crbug.com/560626): Fix and enable mus_apptests on Android. mus_apptests, - { - 'test': 'mojo:sql_apptests', - 'type': 'gtest_isolated', - }, # TODO(sky): enable when works (http://crbug.com/577274 and likely # http://crbug.com/569367). # {
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py index 1ab95596..e32f9f7 100755 --- a/native_client_sdk/src/build_tools/build_sdk.py +++ b/native_client_sdk/src/build_tools/build_sdk.py
@@ -56,7 +56,7 @@ CYGTAR = os.path.join(BUILD_DIR, 'cygtar.py') PKGVER = os.path.join(BUILD_DIR, 'package_version', 'package_version.py') -GYPBUILD_DIR = 'gypbuild' +GNBUILD_DIR = 'gnbuild' options = None @@ -100,40 +100,22 @@ buildbot_common.ErrorExit('Unknown architecture: %s' % arch) -def GetConfigDir(arch): - if arch.endswith('x64') and getos.GetPlatform() == 'win': - return 'Release_x64' - else: - return 'Release' - - def GetNinjaOutDir(arch): - return os.path.join(OUT_DIR, GYPBUILD_DIR + '-' + arch, GetConfigDir(arch)) + return os.path.join(OUT_DIR, GNBUILD_DIR + '-' + arch) -def GetGypBuiltLib(tcname, arch): - if arch == 'ia32': - lib_suffix = '32' - elif arch == 'x64': - lib_suffix = '64' - elif arch == 'arm': - lib_suffix = 'arm' +def GetGnBuiltLib(tc, arch): + if 'glibc' in tc: + out_dir = 'glibc_%s' % arch + elif arch == 'pnacl': + out_dir = 'newlib_pnacl' else: - lib_suffix = '' - - tcdir = 'tc_' + GetToolchainLibc(tcname) - - if tcname == 'pnacl': - if arch is None: - lib_suffix = '' - tcdir = 'tc_pnacl_newlib' - arch = 'x64' - - return os.path.join(GetNinjaOutDir(arch), 'gen', tcdir, 'lib' + lib_suffix) + out_dir = 'clang_newlib_%s' % arch + return os.path.join(GetNinjaOutDir('x64'), out_dir) def GetToolchainNaClLib(tcname, tcpath, arch): - if arch == 'ia32': + if arch == 'x86': return os.path.join(tcpath, 'x86_64-nacl', 'lib32') elif arch == 'x64': return os.path.join(tcpath, 'x86_64-nacl', 'lib') @@ -143,15 +125,18 @@ return os.path.join(tcpath, 'le32-nacl', 'lib') - def GetOutputToolchainLib(pepperdir, tcname, arch): tcpath = os.path.join(pepperdir, 'toolchain', GetToolchainDirName(tcname)) return GetToolchainNaClLib(tcname, tcpath, arch) def GetPNaClTranslatorLib(tcpath, arch): - if arch not in ['arm', 'x86-32', 'x86-64']: + if arch not in ['arm', 'x86', 'x64']: buildbot_common.ErrorExit('Unknown architecture %s.' % arch) + if arch == 'x86': + arch = 'x86-32' + elif arch == 'x64': + arch = 'x86-64' return os.path.join(tcpath, 'translator', arch, 'lib') @@ -331,211 +316,142 @@ InstallFiles(SRC_DIR, tc_dst_inc, NACL_HEADER_MAP[GetToolchainLibc(tcname)]) -def MakeNinjaRelPath(path): - return os.path.join(os.path.relpath(OUT_DIR, SRC_DIR), path) - - -TOOLCHAIN_LIBS = { - 'newlib' : [ - 'libppapi.a', - 'libppapi_stub.a', - ], - 'glibc': [ - 'libppapi.a', - 'libppapi.so', - 'libppapi_stub.a', - ] -} - - -def GypNinjaInstall(pepperdir, toolchains): - tools_files_32 = [ +def GnNinjaInstall(pepperdir, toolchains): + tools_files_x86 = [ ['sel_ldr', 'sel_ldr_x86_32'], - ['irt_core_newlib_x32.nexe', 'irt_core_x86_32.nexe'], - ['irt_core_newlib_x64.nexe', 'irt_core_x86_64.nexe'], ] - arm_files = [ - ['elf_loader_newlib_arm.nexe', 'elf_loader_arm.nexe'], + tools_files_x64 = [ + ['sel_ldr', 'sel_ldr_x86_64'], + ['ncval_new', 'ncval'], + ['clang_newlib_arm/elf_loader.nexe', 'elf_loader_arm.nexe'], + ['irt_x86/irt_core.nexe', 'irt_core_x86_32.nexe'], + ['irt_x64/irt_core.nexe', 'irt_core_x86_64.nexe'], ] - - tools_files_64 = [] + tools_files_arm = [] platform = getos.GetPlatform() # TODO(binji): dump_syms doesn't currently build on Windows. See # http://crbug.com/245456 if platform != 'win': - tools_files_64 += [ + tools_files_x64 += [ ['dump_syms', 'dump_syms'], ['minidump_dump', 'minidump_dump'], ['minidump_stackwalk', 'minidump_stackwalk'] ] - tools_files_64.append(['sel_ldr', 'sel_ldr_x86_64']) - tools_files_64.append(['ncval_new', 'ncval']) if platform == 'linux': - tools_files_32.append(['nacl_helper_bootstrap', - 'nacl_helper_bootstrap_x86_32']) - tools_files_64.append(['nacl_helper_bootstrap', - 'nacl_helper_bootstrap_x86_64']) - tools_files_32.append(['nonsfi_loader_newlib_x32_nonsfi.nexe', - 'nonsfi_loader_x86_32']) + tools_files_x86 += [['nonsfi_loader', 'nonsfi_loader_x86_32'], + ['nacl_helper_bootstrap', + 'nacl_helper_bootstrap_x86_32']] + tools_files_x64 += [['nacl_helper_bootstrap', + 'nacl_helper_bootstrap_x86_64']] + + + # Add ARM trusted binaries (linux only) + if not options.no_arm_trusted: + tools_files_x64 += [ + ['irt_arm/irt_core.nexe', 'irt_core_arm.nexe'], + ] + tools_files_arm += [ + ['nacl_helper_bootstrap', 'nacl_helper_bootstrap_arm'], + ['nonsfi_loader', 'nonsfi_loader_arm'], + ['sel_ldr', 'sel_ldr_arm'] + ] tools_dir = os.path.join(pepperdir, 'tools') buildbot_common.MakeDir(tools_dir) # Add .exe extensions to all windows tools - for pair in tools_files_32 + tools_files_64: - if platform == 'win' and not pair[0].endswith('.nexe'): + for pair in tools_files_x86 + tools_files_x64: + if platform == 'win' and not os.path.splitext(pair[0])[1]: pair[0] += '.exe' pair[1] += '.exe' - # Add ARM binaries - if platform == 'linux' and not options.no_arm_trusted: - arm_files += [ - ['irt_core_newlib_arm.nexe', 'irt_core_arm.nexe'], - ['nacl_helper_bootstrap', 'nacl_helper_bootstrap_arm'], - ['nonsfi_loader_newlib_arm_nonsfi.nexe', 'nonsfi_loader_arm'], - ['sel_ldr', 'sel_ldr_arm'] - ] + InstallFiles(GetNinjaOutDir('x64'), tools_dir, tools_files_x64) + InstallFiles(GetNinjaOutDir('x86'), tools_dir, tools_files_x86) + if platform == 'linux': + InstallFiles(GetNinjaOutDir('arm'), tools_dir, tools_files_arm) - InstallFiles(GetNinjaOutDir('x64'), tools_dir, tools_files_64) - InstallFiles(GetNinjaOutDir('ia32'), tools_dir, tools_files_32) - InstallFiles(GetNinjaOutDir('arm'), tools_dir, arm_files) - + stub_dir = os.path.join(SRC_DIR, 'ppapi/native_client/src/untrusted/irt_stub') for tc in toolchains: if tc in ('host', 'clang-newlib'): continue elif tc == 'pnacl': - xarches = (None, 'ia32', 'x64', 'arm') + xarches = ('pnacl', 'x86', 'x64', 'arm') elif tc in ('x86_glibc'): - xarches = ('ia32', 'x64') + xarches = ('x86', 'x64') elif tc == 'arm_glibc': xarches = ('arm',) else: raise AssertionError('unexpected toolchain value: %s' % tc) for xarch in xarches: - src_dir = GetGypBuiltLib(tc, xarch) + src_dir = GetGnBuiltLib(tc, xarch) + src_dir = os.path.join(src_dir, 'obj', 'ppapi', 'native_client', 'src', + 'untrusted', 'irt_stub') dst_dir = GetOutputToolchainLib(pepperdir, tc, xarch) - libc = GetToolchainLibc(tc) - InstallFiles(src_dir, dst_dir, TOOLCHAIN_LIBS[libc]) + InstallFiles(src_dir, dst_dir, ['libppapi_stub.a']) + InstallFiles(stub_dir, dst_dir, ['libppapi.a']) + if 'glibc' in tc: + InstallFiles(stub_dir, dst_dir, ['libppapi.so']) -def GypNinjaBuild_NaCl(rel_out_dir): - gyp_py = os.path.join(NACL_DIR, 'build', 'gyp_nacl') - nacl_core_sdk_gyp = os.path.join(NACL_DIR, 'build', 'nacl_core_sdk.gyp') - all_gyp = os.path.join(NACL_DIR, 'build', 'all.gyp') +def GnNinjaBuildAll(rel_out_dir): + def MakeNinjaRelPath(suffix): + return os.path.join(os.path.relpath(OUT_DIR, SRC_DIR), rel_out_dir + suffix) - out_dir_32 = MakeNinjaRelPath(rel_out_dir + '-ia32') - out_dir_64 = MakeNinjaRelPath(rel_out_dir + '-x64') - out_dir_arm = MakeNinjaRelPath(rel_out_dir + '-arm') + GnNinjaBuild('x64', MakeNinjaRelPath('-x64'), + ['nacl_sdk_untrusted=true']) + GnNinjaBuild('x86', MakeNinjaRelPath('-x86')) - GypNinjaBuild('ia32', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_32) - GypNinjaBuild('x64', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_64) - GypNinjaBuild('arm', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_arm) - GypNinjaBuild('x64', gyp_py, all_gyp, 'ncval_new', out_dir_64) + platform = getos.GetPlatform() + if platform == 'linux': + GnNinjaBuild('arm', MakeNinjaRelPath('-arm')) -def GypNinjaBuild_Breakpad(rel_out_dir): - # TODO(binji): dump_syms doesn't currently build on Windows. See - # http://crbug.com/245456 - if getos.GetPlatform() == 'win': - return +def GnNinjaBuild(arch, out_dir, extra_gn_args=None): + gn_args = ['is_debug=false'] + if extra_gn_args is not None: + gn_args += extra_gn_args + platform = getos.GetPlatform() + if platform == 'mac': + if options.mac_sdk: + gn_args.append('mac_sdk_min="%s"' % options.mac_sdk) + # Without this the target_cpu='arm' build complains about missing code + # signing identity + gn_args.append('use_ios_simulator=true') - gyp_py = os.path.join(SRC_DIR, 'build', 'gyp_chromium') - out_dir = MakeNinjaRelPath(rel_out_dir) - gyp_file = os.path.join(SRC_DIR, 'breakpad', 'breakpad.gyp') - build_list = ['dump_syms', 'minidump_dump', 'minidump_stackwalk'] - GypNinjaBuild('x64', gyp_py, gyp_file, build_list, out_dir) - - -def GypNinjaBuild_PPAPI(arch, rel_out_dir): - gyp_py = os.path.join(SRC_DIR, 'build', 'gyp_chromium') - out_dir = MakeNinjaRelPath(rel_out_dir) - gyp_file = os.path.join(SRC_DIR, 'ppapi', 'native_client', - 'native_client.gyp') - GypNinjaBuild(arch, gyp_py, gyp_file, 'ppapi_lib', out_dir) - - -def GypNinjaBuild_Pnacl(arch, rel_out_dir): - # TODO(binji): This will build the pnacl_irt_shim twice; once as part of the - # Chromium build, and once here. When we move more of the SDK build process - # to gyp, we can remove this. - gyp_py = os.path.join(SRC_DIR, 'build', 'gyp_chromium') - - out_dir = MakeNinjaRelPath(rel_out_dir) - gyp_file = os.path.join(SRC_DIR, 'ppapi', 'native_client', 'src', - 'untrusted', 'pnacl_irt_shim', 'pnacl_irt_shim.gyp') - GypNinjaBuild(arch, gyp_py, gyp_file, 'aot', out_dir) - - -def GypNinjaBuild(arch, gyp_py_script, gyp_file, targets, out_dir): - gyp_env = dict(os.environ) - gyp_defines = [] - if options.mac_sdk: - gyp_defines.append('mac_sdk=%s' % options.mac_sdk) + gn_exe = 'gn' + if platform == 'win': + gn_exe += '.bat' if arch is not None: - gyp_defines.append('target_arch=%s' % arch) + gn_args.append('target_cpu="%s"' % arch) if arch == 'arm': - gyp_env['GYP_CROSSCOMPILE'] = '1' if options.no_arm_trusted: - gyp_defines.append('disable_cross_trusted=1') + gn_args.append('enable_cross_trusted=false') - gyp_env['GYP_DEFINES'] = ' '.join(gyp_defines) - # We can't use windows path separators in GYP_GENERATOR_FLAGS since - # gyp uses shlex to parse them and treats '\' as an escape char. - gyp_env['GYP_GENERATOR_FLAGS'] = 'output_dir=%s' % out_dir.replace('\\', '/') + gn_args = ' '.join(gn_args) + buildbot_common.Run([gn_exe, 'gen', '--args=%s' % gn_args, out_dir], + cwd=SRC_DIR) - # Print relevant environment variables - for key, value in gyp_env.iteritems(): - if key.startswith('GYP') or key in ('CC',): - print ' %s="%s"' % (key, value) - - buildbot_common.Run( - [sys.executable, gyp_py_script, gyp_file, '--depth=.'], - cwd=SRC_DIR, - env=gyp_env) - - NinjaBuild(targets, out_dir, arch) - - -def NinjaBuild(targets, out_dir, arch): - if type(targets) is not list: - targets = [targets] - out_config_dir = os.path.join(out_dir, GetConfigDir(arch)) - buildbot_common.Run(['ninja', '-C', out_config_dir] + targets, cwd=SRC_DIR) + buildbot_common.Run(['ninja', '-C', out_dir, 'nacl_core_sdk'], cwd=SRC_DIR) def BuildStepBuildToolchains(pepperdir, toolchains, build, clean): buildbot_common.BuildStep('SDK Items') if clean: - for dirname in glob.glob(os.path.join(OUT_DIR, GYPBUILD_DIR + '*')): + for dirname in glob.glob(os.path.join(OUT_DIR, GNBUILD_DIR + '*')): buildbot_common.RemoveDir(dirname) build = True if build: - GypNinjaBuild_NaCl(GYPBUILD_DIR) - GypNinjaBuild_Breakpad(GYPBUILD_DIR + '-x64') + GnNinjaBuildAll(GNBUILD_DIR) - if 'x86_glibc' in toolchains or 'pnacl' in toolchains: - GypNinjaBuild_PPAPI('ia32', GYPBUILD_DIR + '-ia32') - GypNinjaBuild_PPAPI('x64', GYPBUILD_DIR + '-x64') - - if 'arm_glibc' in toolchains or 'pnacl' in toolchains: - GypNinjaBuild_PPAPI('arm', GYPBUILD_DIR + '-arm') - - if 'pnacl' in toolchains: - # NOTE: For ia32, gyp builds both x86-32 and x86-64 by default. - for arch in ('ia32', 'arm'): - # Fill in the latest native pnacl shim library from the chrome build. - build_dir = GYPBUILD_DIR + '-pnacl-' + arch - GypNinjaBuild_Pnacl(arch, build_dir) - - GypNinjaInstall(pepperdir, toolchains) + GnNinjaInstall(pepperdir, toolchains) for toolchain in toolchains: if toolchain not in ('host', 'clang-newlib'): @@ -544,30 +460,21 @@ if 'pnacl' in toolchains: - # NOTE: For ia32, gyp builds both x86-32 and x86-64 by default. - for arch in ('ia32', 'arm'): - # Fill in the latest native pnacl shim library from the chrome build. - build_dir = GYPBUILD_DIR + '-pnacl-' + arch - if arch == 'ia32': - nacl_arches = ['x86-32', 'x86-64'] - elif arch == 'arm': - nacl_arches = ['arm'] - else: - buildbot_common.ErrorExit('Unknown architecture: %s' % arch) - for nacl_arch in nacl_arches: - release_build_dir = os.path.join(OUT_DIR, build_dir, 'Release', - 'gen', 'tc_pnacl_translate', - 'lib-' + nacl_arch) + # NOTE: gn build all untrusted code in the x86 build + build_dir = GetNinjaOutDir('x64') + nacl_arches = ['x86', 'x64', 'arm'] + for nacl_arch in nacl_arches: + shim_file = os.path.join(build_dir, 'clang_newlib_' + nacl_arch, 'obj', + 'ppapi', 'native_client', 'src', 'untrusted', + 'pnacl_irt_shim', 'libpnacl_irt_shim.a') - pnacldir = GetToolchainDir(pepperdir, 'pnacl') - pnacl_translator_lib_dir = GetPNaClTranslatorLib(pnacldir, nacl_arch) - if not os.path.isdir(pnacl_translator_lib_dir): - buildbot_common.ErrorExit('Expected %s directory to exist.' % - pnacl_translator_lib_dir) + pnacldir = GetToolchainDir(pepperdir, 'pnacl') + pnacl_translator_lib_dir = GetPNaClTranslatorLib(pnacldir, nacl_arch) + if not os.path.isdir(pnacl_translator_lib_dir): + buildbot_common.ErrorExit('Expected %s directory to exist.' % + pnacl_translator_lib_dir) - buildbot_common.CopyFile( - os.path.join(release_build_dir, 'libpnacl_irt_shim.a'), - pnacl_translator_lib_dir) + buildbot_common.CopyFile(shim_file, pnacl_translator_lib_dir) InstallNaClHeaders(GetToolchainNaClInclude(pepperdir, 'pnacl', 'x86'), 'pnacl') @@ -630,11 +537,11 @@ deps, config, args) -def BuildStepBuildLibraries(pepperdir, directory): - BuildStepMakeAll(pepperdir, directory, 'Build Libraries Debug', - clean=True, config='Debug') - BuildStepMakeAll(pepperdir, directory, 'Build Libraries Release', - clean=True, config='Release') +def BuildStepBuildLibraries(pepperdir, args=None): + BuildStepMakeAll(pepperdir, 'src', 'Build Libraries Debug', + clean=True, config='Debug', args=args) + BuildStepMakeAll(pepperdir, 'src', 'Build Libraries Release', + clean=True, config='Release', args=args) # Cleanup .pyc file generated while building libraries. Without # this we would end up shipping the pyc in the SDK tarball. @@ -813,7 +720,7 @@ parser.add_argument('--skip-toolchain', help='Skip toolchain untar', action='store_true') parser.add_argument('--no-clean', dest='clean', action='store_false', - help="Don't clean gypbuild directories") + help="Don't clean gn build directories") parser.add_argument('--mac-sdk', help='Set the mac-sdk (e.g. 10.6) to use when building with ninja.') parser.add_argument('--no-arm-trusted', action='store_true', @@ -923,7 +830,7 @@ BuildStepCopyTextFiles(pepperdir, pepper_ver, chrome_revision, nacl_revision) # Ship with libraries prebuilt, so run that first. - BuildStepBuildLibraries(pepperdir, 'src') + BuildStepBuildLibraries(pepperdir) GenerateNotice(pepperdir) # Verify the SDK contains what we expect.
diff --git a/native_client_sdk/src/build_tools/buildbot_run.py b/native_client_sdk/src/build_tools/buildbot_run.py index f6e4ee7..a0ab8c0 100755 --- a/native_client_sdk/src/build_tools/buildbot_run.py +++ b/native_client_sdk/src/build_tools/buildbot_run.py
@@ -71,20 +71,20 @@ subprocess.check_call(['subst', '/D', subst_drive]) -def StepTestSDK(): +def StepTestSDK(args): cmd = [] if getos.GetPlatform() == 'linux': # Run all of test_sdk.py under xvfb-run; it's startup time leaves something # to be desired, so only start it up once. # We also need to make sure that there are at least 24 bits per pixel. # https://code.google.com/p/chromium/issues/detail?id=316687 - cmd.extend([ + cmd += [ 'xvfb-run', '--auto-servernum', '--server-args', '-screen 0 1024x768x24' - ]) + ] - cmd.extend([sys.executable, 'test_sdk.py']) + cmd += [sys.executable, 'test_sdk.py'] + args Run(cmd, cwd=SCRIPT_DIR) @@ -115,7 +115,12 @@ StepRunUnittests() StepBuildSDK() if not options.build_only: - StepTestSDK() + # Run sanitizer tests on the asan bot, and on the trybots + args = [] + if getos.GetPlatform() == 'linux': + if 'asan' in os.getenv('BUILDBOT_BUILDERNAME', ''): + args = ['--sanitizer'] + StepTestSDK(args) return 0
diff --git a/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json b/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json index aab7909..cedee31 100644 --- a/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json +++ b/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json
@@ -35,16 +35,6 @@ }, { "archives": [], - "description": "Chrome 43 bundle, revision xxxxx", - "name": "pepper_43", - "recommended": "no", - "repath": "pepper_43", - "revision": 0, - "stability": "post_stable", - "version": 43 - }, - { - "archives": [], "description": "Chrome 44 bundle, revision xxxxx", "name": "pepper_44", "recommended": "no", @@ -95,6 +85,16 @@ }, { "archives": [], + "description": "Chrome 50 bundle, revision xxxxx", + "name": "pepper_50", + "recommended": "no", + "repath": "pepper_50", + "revision": 0, + "stability": "post_stable", + "version": 50 + }, + { + "archives": [], "description": "Chrome Canary", "name": "pepper_canary", "recommended": "no",
diff --git a/native_client_sdk/src/build_tools/test_sdk.py b/native_client_sdk/src/build_tools/test_sdk.py index b1cee6a..831cef5 100755 --- a/native_client_sdk/src/build_tools/test_sdk.py +++ b/native_client_sdk/src/build_tools/test_sdk.py
@@ -56,11 +56,31 @@ toolchains=toolchains) -def StepBuildTests(pepperdir): +def StepBuildLibraries(pepperdir, sanitizer): for config in ('Debug', 'Release'): - build_sdk.BuildStepMakeAll(pepperdir, 'tests', - 'Build Tests (%s)' % config, - deps=False, config=config) + title = 'Build Libs (%s)[sanitizer=%s]' % (config, sanitizer) + build_sdk.BuildStepMakeAll(pepperdir, 'src', title, config=config, + args=GetSanitizerArgs(sanitizer)) + + +def StepBuildTests(pepperdir, sanitizer): + for config in ('Debug', 'Release'): + title = 'Build Tests (%s)' % config + if sanitizer: + title += '[sanitizer=%s]' % sanitizer + + build_sdk.BuildStepMakeAll(pepperdir, 'tests', title, deps=False, + config=config, args=GetSanitizerArgs(sanitizer)) + + +def GetSanitizerArgs(sanitizer): + if sanitizer == 'valgrind': + return ['TOOLCHAIN=linux', 'RUN_UNDER=valgrind'] + elif sanitizer == 'address': + return ['TOOLCHAIN=linux', 'ASAN=1'] + elif sanitizer == 'thread': + return ['TOOLCHAIN=linux', 'TSAN=1'] + return [] def StepRunSelLdrTests(pepperdir, sanitizer): @@ -72,24 +92,12 @@ def RunTest(test, toolchain, config, arch=None): args = ['STANDALONE=1', 'TOOLCHAIN=%s' % toolchain] + args += GetSanitizerArgs(sanitizer) if arch is not None: args.append('NACL_ARCH=%s' % arch) - deps = False - - if sanitizer is not None: - # For sanitizer builds we pass extra argument for make, and do - # full clean build to make sure everything is rebuilt with the - # correct flags - deps = True - if sanitizer == 'valgrind': - args += ['RUN_UNDER=valgrind'] - elif sanitizer == 'address': - args += ['ASAN=1'] - elif sanitizer == 'thread': - args += ['TSAN=1'] build_projects.BuildProjectsBranch(pepperdir, test, clean=False, - deps=deps, config=config, + deps=False, config=config, args=args + ['run']) if getos.GetPlatform() == 'win': @@ -190,18 +198,31 @@ phases = [ ('build_examples', StepBuildExamples, pepperdir), ('copy_tests', StepCopyTests, pepperdir, toolchains, options.experimental), - ('build_tests', StepBuildTests, pepperdir), - ('sel_ldr_tests', StepRunSelLdrTests, pepperdir, None), - ('browser_tests', StepRunBrowserTests, toolchains, options.experimental), + ('build_tests', StepBuildTests, pepperdir, None), ] if options.sanitizer: if getos.GetPlatform() != 'linux': buildbot_common.ErrorExit('sanitizer tests only run on linux.') + clang_dir = os.path.join(SRC_DIR, 'third_party', 'llvm-build', + 'Release+Asserts', 'bin') + os.environ['PATH'] = clang_dir + os.pathsep + os.environ['PATH'] + phases += [ + ('build_libs_asan', StepBuildLibraries, pepperdir, 'address'), + ('build_libs_tsan', StepBuildLibraries, pepperdir, 'thread'), + ('build_tests_asan', StepBuildTests, pepperdir, 'address'), + ('build_tests_tsan', StepBuildTests, pepperdir, 'thread'), ('sel_ldr_tests_asan', StepRunSelLdrTests, pepperdir, 'address'), ('sel_ldr_tests_tsan', StepRunSelLdrTests, pepperdir, 'thread'), - ('sel_ldr_tests_valgrind', StepRunSelLdrTests, pepperdir, 'valgrind') + # TODO(sbc): get valgrind installed on the bots to enable this + # configuration + #('sel_ldr_tests_valgrind', StepRunSelLdrTests, pepperdir, 'valgrind') + ] + else: + phases += [ + ('sel_ldr_tests', StepRunSelLdrTests, pepperdir, None), + ('browser_tests', StepRunBrowserTests, toolchains, options.experimental), ] if options.phases:
diff --git a/native_client_sdk/src/libraries/nacl_io/event_emitter.cc b/native_client_sdk/src/libraries/nacl_io/event_emitter.cc index 2e620bc..c096b2ec 100644 --- a/native_client_sdk/src/libraries/nacl_io/event_emitter.cc +++ b/native_client_sdk/src/libraries/nacl_io/event_emitter.cc
@@ -22,12 +22,12 @@ } void EventEmitter::RegisterListener(EventListener* listener, uint32_t events) { - AUTO_LOCK(emitter_lock_); + AUTO_LOCK(GetLock()); RegisterListener_Locked(listener, events); } void EventEmitter::UnregisterListener(EventListener* listener) { - AUTO_LOCK(emitter_lock_); + AUTO_LOCK(GetLock()); UnregisterListener_Locked(listener); }
diff --git a/native_client_sdk/src/libraries/nacl_io/event_emitter.h b/native_client_sdk/src/libraries/nacl_io/event_emitter.h index ed404d5..a54fb103 100644 --- a/native_client_sdk/src/libraries/nacl_io/event_emitter.h +++ b/native_client_sdk/src/libraries/nacl_io/event_emitter.h
@@ -43,13 +43,13 @@ // This returns a snapshot, to ensure the status doesn't change from // fetch to use, hold the lock and call GetEventStatus_Locked. uint32_t GetEventStatus() { - AUTO_LOCK(emitter_lock_); + AUTO_LOCK(GetLock()); return GetEventStatus_Locked(); } uint32_t GetEventStatus_Locked() { return event_status_; } - sdk_util::SimpleLock& GetLock() { return emitter_lock_; } + virtual sdk_util::SimpleLock& GetLock() { return emitter_lock_; } // Updates the specified bits in the event status, and signals any // listeners waiting on those bits.
diff --git a/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.cc b/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.cc index b0a7bf3f..153a86d4 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.cc +++ b/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.cc
@@ -4,6 +4,7 @@ #include "nacl_io/socket/unix_event_emitter.h" +#include <poll.h> #include <stdlib.h> #include <sys/socket.h> @@ -22,7 +23,10 @@ class UnixMasterEventEmitter : public UnixEventEmitter { public: explicit UnixMasterEventEmitter(size_t size, int type) - : child_emitter_created_(false), child_emitter_(NULL) { + : in_shutdown_(false), + out_shutdown_(false), + child_emitter_created_(false), + child_emitter_(NULL) { if (type == SOCK_STREAM) { in_fifo_ = new FIFOChar(size); out_fifo_ = new FIFOChar(size); @@ -38,17 +42,25 @@ delete out_fifo_; } + virtual void Shutdown_Locked(bool read, bool write) { + in_shutdown_ |= read; + out_shutdown_ |= write; + } + + virtual bool IsShutdownRead() const { return in_shutdown_; } + virtual bool IsShutdownWrite() const { return out_shutdown_; } + virtual ScopedUnixEventEmitter GetPeerEmitter(); protected: virtual FIFOInterface* in_fifo() { return in_fifo_; } virtual FIFOInterface* out_fifo() { return out_fifo_; } - virtual const sdk_util::SimpleLock& GetFifoLock() { return fifo_lock_; } private: FIFOInterface* in_fifo_; FIFOInterface* out_fifo_; - sdk_util::SimpleLock fifo_lock_; + bool in_shutdown_; + bool out_shutdown_; bool child_emitter_created_; UnixChildEventEmitter* child_emitter_; @@ -62,15 +74,22 @@ UpdateStatus_Locked(); } virtual ScopedUnixEventEmitter GetPeerEmitter() { return parent_emitter_; } + virtual sdk_util::SimpleLock& GetLock() { return parent_emitter_->GetLock(); } + virtual void Shutdown_Locked(bool read, bool write) { + parent_emitter_->Shutdown_Locked(write, read); + } + virtual bool IsShutdownRead() const { + return parent_emitter_->IsShutdownWrite(); + } + virtual bool IsShutdownWrite() const { + return parent_emitter_->IsShutdownRead(); + } protected: virtual void Destroy() { parent_emitter_->child_emitter_ = NULL; } virtual FIFOInterface* in_fifo() { return parent_emitter_->out_fifo(); } virtual FIFOInterface* out_fifo() { return parent_emitter_->in_fifo(); } - virtual const sdk_util::SimpleLock& GetFifoLock() { - return parent_emitter_->GetFifoLock(); - } private: ScopedUnixMasterEventEmitter parent_emitter_; @@ -85,7 +104,6 @@ } uint32_t UnixEventEmitter::ReadIn_Locked(char* data, uint32_t len) { - AUTO_LOCK(GetFifoLock()); uint32_t count = in_fifo()->Read(data, len); ScopedUnixEventEmitter peer = GetPeerEmitter(); if (peer) { @@ -96,7 +114,6 @@ } uint32_t UnixEventEmitter::WriteOut_Locked(const char* data, uint32_t len) { - AUTO_LOCK(GetFifoLock()); uint32_t count = out_fifo()->Write(data, len); ScopedUnixEventEmitter peer = GetPeerEmitter(); if (peer) { @@ -106,6 +123,18 @@ return count; } +void UnixEventEmitter::UpdateStatus_Locked() { + uint32_t status = 0; + if (!in_fifo()->IsEmpty() || IsShutdownRead()) + status |= POLLIN; + + if (!out_fifo()->IsFull() && !IsShutdownWrite()) + status |= POLLOUT; + + ClearEvents_Locked(~status); + RaiseEvents_Locked(status); +} + ScopedUnixEventEmitter UnixEventEmitter::MakeUnixEventEmitter(size_t size, int type) { return ScopedUnixEventEmitter(new UnixMasterEventEmitter(size, type));
diff --git a/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.h b/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.h index f1dba0d..29af1b5 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.h +++ b/native_client_sdk/src/libraries/nacl_io/socket/unix_event_emitter.h
@@ -28,16 +28,18 @@ uint32_t SpaceInInputFIFO(); virtual ScopedUnixEventEmitter GetPeerEmitter() = 0; + virtual void Shutdown_Locked(bool read, bool write) = 0; + virtual bool IsShutdownRead() const = 0; + virtual bool IsShutdownWrite() const = 0; static ScopedUnixEventEmitter MakeUnixEventEmitter(size_t size, int type); protected: UnixEventEmitter() {} - // Probably only need the master's lock. - virtual const sdk_util::SimpleLock& GetFifoLock() = 0; virtual FIFOInterface* in_fifo() = 0; virtual FIFOInterface* out_fifo() = 0; + void UpdateStatus_Locked(); private: DISALLOW_COPY_AND_ASSIGN(UnixEventEmitter);
diff --git a/native_client_sdk/src/libraries/nacl_io/socket/unix_node.cc b/native_client_sdk/src/libraries/nacl_io/socket/unix_node.cc index 968cdd7..f84d92f4 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/unix_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/socket/unix_node.cc
@@ -6,6 +6,7 @@ #ifdef PROVIDES_SOCKET_API #include <assert.h> +#include <errno.h> #include <string.h> #include <algorithm> @@ -35,7 +36,7 @@ PP_Resource* out_addr, int* out_len) { assert(emitter_.get()); - *out_len = emitter_->ReadIn_Locked((char*)buffer, len); + *out_len = emitter_->ReadIn_Locked(static_cast<char*>(buffer), len); *out_addr = 0; return 0; } @@ -45,7 +46,10 @@ PP_Resource out_addr, int* out_len) { assert(emitter_.get()); - *out_len = emitter_->WriteOut_Locked((char*)buffer, len); + if (emitter_->IsShutdownWrite()) { + return EPIPE; + } + *out_len = emitter_->WriteOut_Locked(static_cast<const char*>(buffer), len); return 0; } @@ -90,6 +94,29 @@ return SendHelper(attr, buf, len, flags, addr, out_len); } +Error UnixNode::Shutdown(int how) { + bool read; + bool write; + switch (how) { + case SHUT_RDWR: + read = write = true; + break; + case SHUT_RD: + read = true; + write = false; + break; + case SHUT_WR: + read = false; + write = true; + break; + default: + return EINVAL; + } + AUTO_LOCK(emitter_->GetLock()); + emitter_->Shutdown_Locked(read, write); + return 0; +} + } // namespace nacl_io #endif // PROVIDES_SOCKET_API
diff --git a/native_client_sdk/src/libraries/nacl_io/socket/unix_node.h b/native_client_sdk/src/libraries/nacl_io/socket/unix_node.h index 881261a..7d644d3 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/unix_node.h +++ b/native_client_sdk/src/libraries/nacl_io/socket/unix_node.h
@@ -49,6 +49,7 @@ const struct sockaddr* dest_addr, socklen_t addrlen, int* out_len); + virtual Error Shutdown(int how); private: ScopedUnixEventEmitter emitter_;
diff --git a/native_client_sdk/src/tests/nacl_io_test/kernel_wrap_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_wrap_test.cc index 1455da7..1d9a38d 100644 --- a/native_client_sdk/src/tests/nacl_io_test/kernel_wrap_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_wrap_test.cc
@@ -157,13 +157,12 @@ .WillByDefault(Invoke(this, &KernelWrapTest::DefaultWrite)); EXPECT_CALL(mock, write(_, _, _)).Times(AnyNumber()); - // Ignore calls to munmap. These can be generated from within the standard - // library malloc implementation so can be expected at pretty much any time. - // Returning zero is fine since the real munmap see also run. - // See kernel_wrap_newlib.cc. +#ifndef _NEWLIB_VERSION + // Disable munmap mocking under newlib due to deadlock issues in dlmalloc ON_CALL(mock, munmap(_, _)) .WillByDefault(Return(0)); EXPECT_CALL(mock, munmap(_, _)).Times(AnyNumber()); +#endif ASSERT_EQ(0, ki_push_state_for_testing()); ASSERT_EQ(0, ki_init(&mock)); @@ -471,6 +470,8 @@ kDummyVoidPtr)); } +#ifndef _NEWLIB_VERSION +// Disable munmap mocking under newlib due to deadlock in dlmalloc TEST_F(KernelWrapTest, munmap) { // The way we wrap munmap, calls the "real" mmap as well as the intercepted // one. The result returned is from the "real" mmap. @@ -480,6 +481,7 @@ EXPECT_CALL(mock, munmap(kDummyVoidPtr, kDummySizeT)); munmap(kDummyVoidPtr, kDummySizeT); } +#endif TEST_F(KernelWrapTest, open) { // We pass O_RDONLY because we do not want an error in flags translation
diff --git a/native_client_sdk/src/tests/nacl_io_test/mock_kernel_proxy.h b/native_client_sdk/src/tests/nacl_io_test/mock_kernel_proxy.h index abd6c83..cc11b08 100644 --- a/native_client_sdk/src/tests/nacl_io_test/mock_kernel_proxy.h +++ b/native_client_sdk/src/tests/nacl_io_test/mock_kernel_proxy.h
@@ -48,7 +48,15 @@ MOCK_METHOD6(mmap, void*(void*, size_t, int, int, int, size_t)); MOCK_METHOD5(mount, int(const char*, const char*, const char*, unsigned long, const void*)); + +#ifndef _NEWLIB_VERSION + /* + * Disable mocking of munmap under newlib since it can lead to deadlocks + * in dlmalloc: free -> dlmalloc -> acquire lock -> munmap -> mock munmap -> + * std::string -> new -> dlmalloc -> acquire lock (already held). + */ MOCK_METHOD2(munmap, int(void*, size_t)); +#endif MOCK_METHOD3(open, int(const char*, int, mode_t)); MOCK_METHOD1(open_resource, int(const char*)); MOCK_METHOD1(pipe, int(int[2]));
diff --git a/native_client_sdk/src/tests/nacl_io_test/socket_test.cc b/native_client_sdk/src/tests/nacl_io_test/socket_test.cc index e739f74..60d9f309 100644 --- a/native_client_sdk/src/tests/nacl_io_test/socket_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/socket_test.cc
@@ -11,7 +11,9 @@ #include <sys/socket.h> #include <sys/stat.h> +#include <iterator> #include <map> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -422,6 +424,151 @@ EXPECT_NE(POLLIN, pollfd.revents & POLLIN); } +namespace { +using std::vector; +using std::min; +using std::advance; +using std::distance; + +typedef vector<uint8_t> Buffer; +typedef Buffer::iterator BufferIterator; +typedef Buffer::const_iterator BufferConstIterator; + +const size_t kReceiveBufferSize = 2 * 1024 * 1024; +const size_t kThreadSendSize = 512 * 1024; +const size_t kMainSendSize = 1024 * 1024; + +const uint8_t kThreadPattern[] = {0xAA, 0x12, 0x55, 0x34, 0xCC, 0x33}; + +// Exercises the implementation of an AF_UNIX socket. Will read from the socket +// into read_buf until EOF (or read_buf is full) whenever the socket is readable +// and write send_size number of bytes into the socket according to pattern. +// The test UnixSocketMultithreadedTest.SendRecv uses this function to quickly +// push about 1 Meg of data between two threads over a socketpair. +void ReadWriteSocket(int fd, + const uint8_t* pattern, + const size_t pattern_size, + const size_t send_size, + Buffer* read_vector) { + Buffer send; + while (send.size() != send_size) { + size_t s = min(pattern_size, send_size - send.size()); + send.insert(send.end(), &pattern[0], &pattern[s]); + } + + bool read_complete = false, write_complete = false; + + size_t received_count = 0; + read_vector->resize(kReceiveBufferSize); + + BufferConstIterator send_iterator(send.begin()); + BufferConstIterator send_end(send.end()); + struct timeval timeout; + timeout.tv_sec = 10; + timeout.tv_usec = 0; + while (!read_complete || !write_complete) { + fd_set rfd; + FD_ZERO(&rfd); + if (!read_complete) { + FD_SET(fd, &rfd); + } + fd_set wfd; + FD_ZERO(&wfd); + if (!write_complete) { + FD_SET(fd, &wfd); + } + struct timeval tv = timeout; + + // Should not timeout, but added to fail test and allow to proceed. + EXPECT_LT(0, select(fd + 1, &rfd, &wfd, NULL, &tv)); + if (!FD_ISSET(fd, &rfd) && !FD_ISSET(fd, &wfd)) { + FAIL() << "Select returned with neither readable nor writable fd."; + } + + if (!read_complete && FD_ISSET(fd, &rfd)) { + if (received_count == read_vector->size()) { + read_vector->resize(read_vector->size() + kReceiveBufferSize); + } + ssize_t len = + ki_recv(fd, read_vector->data() + received_count, + read_vector->size() - received_count, /* flags */ 0); + ASSERT_LE(0, len) << "Read should succeed"; + if (len == 0) { + read_complete = true; + read_vector->resize(received_count); + } + received_count += len; + } + if (!write_complete && FD_ISSET(fd, &wfd)) { + ssize_t len = ki_send(fd, &(*send_iterator), + distance(send_iterator, send_end), /* flags */ 0); + ASSERT_LE(0, len) << "Write should succeed"; + advance(send_iterator, len); + if (send_iterator == send_end) { + EXPECT_EQ(0, ki_shutdown(fd, SHUT_WR)); + write_complete = true; + } + } + } +} + +class UnixSocketMultithreadedTest : public UnixSocketTest { + public: + void SetUp() { + UnixSocketTest::SetUp(); + EXPECT_EQ(0, ki_socketpair(AF_UNIX, SOCK_STREAM, 0, sv_)); + } + + void TearDown() { UnixSocketTest::TearDown(); } + + pthread_t CreateThread() { + pthread_t id; + EXPECT_EQ(0, pthread_create(&id, NULL, ThreadThunk, this)); + return id; + } + + private: + static void* ThreadThunk(void* ptr) { + return static_cast<UnixSocketMultithreadedTest*>(ptr)->ThreadEntry(); + } + + void* ThreadEntry() { + int fd = sv_[1]; + + ReadWriteSocket(fd, kThreadPattern, sizeof(kThreadPattern), kThreadSendSize, + &thread_buffer_); + return NULL; + } + + protected: + Buffer thread_buffer_; +}; + +} // namespace + +TEST_F(UnixSocketMultithreadedTest, SendRecv) { + pthread_t thread = CreateThread(); + + uint8_t pattern[] = {0xA5, 0x00, 0xC3, 0xFF}; + size_t pattern_size = sizeof(pattern); + Buffer main_read_buf; + + ReadWriteSocket(sv_[0], pattern, pattern_size, kMainSendSize, &main_read_buf); + + pthread_join(thread, NULL); + + EXPECT_EQ(kMainSendSize, thread_buffer_.size()); + EXPECT_EQ(kThreadSendSize, main_read_buf.size()); + for (size_t i = 0; i != thread_buffer_.size(); ++i) { + EXPECT_EQ(pattern[i % pattern_size], thread_buffer_[i]) + << "Invalid result at position " << i << "in data received by thread"; + } + for (size_t i = 0; i != main_read_buf.size(); ++i) { + EXPECT_EQ(kThreadPattern[i % sizeof(kThreadPattern)], main_read_buf[i]) + << "Invalid result at position " << i << "in data received by main"; + } +} + TEST(SocketUtilityFunctions, Htonl) { uint32_t host_long = 0x44332211; uint32_t network_long = htonl(host_long);
diff --git a/net/BUILD.gn b/net/BUILD.gn index 485ae15..064ea28 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -605,6 +605,8 @@ "base/test_data_directory.h", "cert/mock_cert_verifier.cc", "cert/mock_cert_verifier.h", + "cert/mock_client_cert_verifier.cc", + "cert/mock_client_cert_verifier.h", "cookies/cookie_monster_store_test.cc", "cookies/cookie_monster_store_test.h", "cookies/cookie_store_test_callbacks.cc",
diff --git a/net/base/net_util.cc b/net/base/net_util.cc index e90c0e8..92db80c1 100644 --- a/net/base/net_util.cc +++ b/net/base/net_util.cc
@@ -4,72 +4,20 @@ #include "net/base/net_util.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include <netinet/in.h> +#elif defined(OS_WIN) +#include <ws2tcpip.h> +#endif + #include "base/logging.h" -#include "base/strings/string_util.h" -#include "net/base/address_list.h" #include "net/base/ip_address_number.h" +#include "net/base/url_util.h" namespace net { -namespace { - -bool IsNormalizedLocalhostTLD(const std::string& host) { - return base::EndsWith(host, ".localhost", base::CompareCase::SENSITIVE); -} - -// This function tests |host| to see if it is of any local hostname form. -// |host| is normalized before being tested and if |is_local6| is not NULL then -// it it will be set to true if the localhost name implies an IPv6 interface ( -// for instance localhost6.localdomain6). -bool IsLocalHostname(base::StringPiece host, bool* is_local6) { - std::string normalized_host = base::ToLowerASCII(host); - // Remove any trailing '.'. - if (!normalized_host.empty() && *normalized_host.rbegin() == '.') - normalized_host.resize(normalized_host.size() - 1); - - if (normalized_host == "localhost6" || - normalized_host == "localhost6.localdomain6") { - if (is_local6) - *is_local6 = true; - return true; - } - - if (is_local6) - *is_local6 = false; - return normalized_host == "localhost" || - normalized_host == "localhost.localdomain" || - IsNormalizedLocalhostTLD(normalized_host); -} - -} // namespace - -bool ResolveLocalHostname(base::StringPiece host, - uint16_t port, - AddressList* address_list) { - static const unsigned char kLocalhostIPv4[] = {127, 0, 0, 1}; - static const unsigned char kLocalhostIPv6[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; - - address_list->clear(); - - bool is_local6; - if (!IsLocalHostname(host, &is_local6)) - return false; - - address_list->push_back( - IPEndPoint(IPAddressNumber(kLocalhostIPv6, - kLocalhostIPv6 + arraysize(kLocalhostIPv6)), - port)); - if (!is_local6) { - address_list->push_back( - IPEndPoint(IPAddressNumber(kLocalhostIPv4, - kLocalhostIPv4 + arraysize(kLocalhostIPv4)), - port)); - } - - return true; -} - bool IsLocalhost(base::StringPiece host) { if (IsLocalHostname(host, nullptr)) return true;
diff --git a/net/base/net_util.h b/net/base/net_util.h index 73b337be..329a008 100644 --- a/net/base/net_util.h +++ b/net/base/net_util.h
@@ -5,24 +5,11 @@ #ifndef NET_BASE_NET_UTIL_H_ #define NET_BASE_NET_UTIL_H_ -#include <stdint.h> - #include "base/strings/string_piece.h" #include "net/base/net_export.h" namespace net { -class AddressList; - -// Resolves a local hostname (such as "localhost" or "localhost6") into -// IP endpoints with the given port. Returns true if |host| is a local -// hostname and false otherwise. Special IPv6 names (e.g. "localhost6") -// will resolve to an IPv6 address only, whereas other names will -// resolve to both IPv4 and IPv6. -NET_EXPORT_PRIVATE bool ResolveLocalHostname(base::StringPiece host, - uint16_t port, - AddressList* address_list); - // Returns true if |host| is one of the local hostnames // (e.g. "localhost") or IP addresses (IPv4 127.0.0.0/8 or IPv6 ::1). //
diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc index 76648f47..4e2d476 100644 --- a/net/base/net_util_unittest.cc +++ b/net/base/net_util_unittest.cc
@@ -4,59 +4,10 @@ #include "net/base/net_util.h" -#include "base/format_macros.h" -#include "net/base/address_list.h" -#include "net/base/ip_endpoint.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { -namespace { - -const unsigned char kLocalhostIPv4[] = {127, 0, 0, 1}; -const unsigned char kLocalhostIPv6[] = - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; -const uint16_t kLocalhostLookupPort = 80; - -bool HasEndpoint(const IPEndPoint& endpoint, const AddressList& addresses) { - for (const auto& address : addresses) { - if (endpoint == address) - return true; - } - return false; -} - -void TestBothLoopbackIPs(const std::string& host) { - IPEndPoint localhost_ipv4( - IPAddressNumber(kLocalhostIPv4, - kLocalhostIPv4 + arraysize(kLocalhostIPv4)), - kLocalhostLookupPort); - IPEndPoint localhost_ipv6( - IPAddressNumber(kLocalhostIPv6, - kLocalhostIPv6 + arraysize(kLocalhostIPv6)), - kLocalhostLookupPort); - - AddressList addresses; - EXPECT_TRUE(ResolveLocalHostname(host, kLocalhostLookupPort, &addresses)); - EXPECT_EQ(2u, addresses.size()); - EXPECT_TRUE(HasEndpoint(localhost_ipv4, addresses)); - EXPECT_TRUE(HasEndpoint(localhost_ipv6, addresses)); -} - -void TestIPv6LoopbackOnly(const std::string& host) { - IPEndPoint localhost_ipv6( - IPAddressNumber(kLocalhostIPv6, - kLocalhostIPv6 + arraysize(kLocalhostIPv6)), - kLocalhostLookupPort); - - AddressList addresses; - EXPECT_TRUE(ResolveLocalHostname(host, kLocalhostLookupPort, &addresses)); - EXPECT_EQ(1u, addresses.size()); - EXPECT_TRUE(HasEndpoint(localhost_ipv6, addresses)); -} - -} // anonymous namespace - TEST(NetUtilTest, IsLocalhost) { EXPECT_TRUE(IsLocalhost("localhost")); EXPECT_TRUE(IsLocalhost("localHosT")); @@ -101,62 +52,4 @@ EXPECT_FALSE(IsLocalhost("foo.localhos")); } -TEST(NetUtilTest, ResolveLocalHostname) { - AddressList addresses; - - TestBothLoopbackIPs("localhost"); - TestBothLoopbackIPs("localhoST"); - TestBothLoopbackIPs("localhost."); - TestBothLoopbackIPs("localhoST."); - TestBothLoopbackIPs("localhost.localdomain"); - TestBothLoopbackIPs("localhost.localdomAIn"); - TestBothLoopbackIPs("localhost.localdomain."); - TestBothLoopbackIPs("localhost.localdomAIn."); - TestBothLoopbackIPs("foo.localhost"); - TestBothLoopbackIPs("foo.localhOSt"); - TestBothLoopbackIPs("foo.localhost."); - TestBothLoopbackIPs("foo.localhOSt."); - - TestIPv6LoopbackOnly("localhost6"); - TestIPv6LoopbackOnly("localhoST6"); - TestIPv6LoopbackOnly("localhost6."); - TestIPv6LoopbackOnly("localhost6.localdomain6"); - TestIPv6LoopbackOnly("localhost6.localdomain6."); - - EXPECT_FALSE( - ResolveLocalHostname("127.0.0.1", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("::1", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("0:0:0:0:0:0:0:1", kLocalhostLookupPort, - &addresses)); - EXPECT_FALSE( - ResolveLocalHostname("localhostx", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE( - ResolveLocalHostname("localhost.x", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("foo.localdomain", kLocalhostLookupPort, - &addresses)); - EXPECT_FALSE(ResolveLocalHostname("foo.localdomain.x", kLocalhostLookupPort, - &addresses)); - EXPECT_FALSE( - ResolveLocalHostname("localhost6x", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("localhost.localdomain6", - kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("localhost6.localdomain", - kLocalhostLookupPort, &addresses)); - EXPECT_FALSE( - ResolveLocalHostname("127.0.0.1.1", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE( - ResolveLocalHostname(".127.0.0.255", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("::2", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("::1:1", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("0:0:0:0:1:0:0:1", kLocalhostLookupPort, - &addresses)); - EXPECT_FALSE(ResolveLocalHostname("::1:1", kLocalhostLookupPort, &addresses)); - EXPECT_FALSE(ResolveLocalHostname("0:0:0:0:0:0:0:0:1", kLocalhostLookupPort, - &addresses)); - EXPECT_FALSE(ResolveLocalHostname("foo.localhost.com", kLocalhostLookupPort, - &addresses)); - EXPECT_FALSE( - ResolveLocalHostname("foo.localhoste", kLocalhostLookupPort, &addresses)); -} - } // namespace net
diff --git a/net/base/url_util.cc b/net/base/url_util.cc index 879bed1..797d1c8 100644 --- a/net/base/url_util.cc +++ b/net/base/url_util.cc
@@ -24,6 +24,10 @@ return ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')); } +bool IsNormalizedLocalhostTLD(const std::string& host) { + return base::EndsWith(host, ".localhost", base::CompareCase::SENSITIVE); +} + } // namespace GURL AppendQueryParameter(const GURL& url, @@ -383,4 +387,24 @@ return false; } +bool IsLocalHostname(base::StringPiece host, bool* is_local6) { + std::string normalized_host = base::ToLowerASCII(host); + // Remove any trailing '.'. + if (!normalized_host.empty() && *normalized_host.rbegin() == '.') + normalized_host.resize(normalized_host.size() - 1); + + if (normalized_host == "localhost6" || + normalized_host == "localhost6.localdomain6") { + if (is_local6) + *is_local6 = true; + return true; + } + + if (is_local6) + *is_local6 = false; + return normalized_host == "localhost" || + normalized_host == "localhost.localdomain" || + IsNormalizedLocalhostTLD(normalized_host); +} + } // namespace net
diff --git a/net/base/url_util.h b/net/base/url_util.h index 80ff54e5..517aaa3 100644 --- a/net/base/url_util.h +++ b/net/base/url_util.h
@@ -157,6 +157,13 @@ // for histograms and shouldn't be used to affect behavior. NET_EXPORT_PRIVATE bool HasGoogleHost(const GURL& url); +// This function tests |host| to see if it is of any local hostname form. +// |host| is normalized before being tested and if |is_local6| is not NULL then +// it it will be set to true if the localhost name implies an IPv6 interface ( +// for instance localhost6.localdomain6). +NET_EXPORT_PRIVATE bool IsLocalHostname(base::StringPiece host, + bool* is_local6); + } // namespace net #endif // NET_BASE_URL_UTIL_H_
diff --git a/net/cert/cert_verify_result.h b/net/cert/cert_verify_result.h index 104fc6e..1f81da2 100644 --- a/net/cert/cert_verify_result.h +++ b/net/cert/cert_verify_result.h
@@ -37,7 +37,7 @@ // running within the sandbox. scoped_refptr<X509Certificate> verified_cert; - // Bitmask of CERT_STATUS_* from net/base/cert_status_flags.h. Note that + // Bitmask of CERT_STATUS_* from net/cert/cert_status_flags.h. Note that // these status flags apply to the certificate chain returned in // |verified_cert|, rather than the originally supplied certificate // chain.
diff --git a/net/cert/client_cert_verifier.h b/net/cert/client_cert_verifier.h new file mode 100644 index 0000000..f2044aa --- /dev/null +++ b/net/cert/client_cert_verifier.h
@@ -0,0 +1,41 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_CERT_CLIENT_CERT_VERIFIER_H_ +#define NET_CERT_CLIENT_CERT_VERIFIER_H_ + +#include "base/macros.h" +#include "net/base/completion_callback.h" +#include "net/base/net_export.h" + +namespace net { + +class X509Certificate; + +// ClientCertVerifier represents a service for verifying certificates. +class NET_EXPORT ClientCertVerifier { + public: + class Request { + public: + Request() {} + + // Destruction of the Request cancels it. + virtual ~Request() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Request); + }; + + virtual ~ClientCertVerifier() {} + + // Verifies the given certificate as a client certificate. + // Returns OK if successful or an error code upon failure. + virtual int Verify(X509Certificate* cert, + const CompletionCallback& callback, + scoped_ptr<Request>* out_req) = 0; +}; + +} // namespace net + +#endif // NET_CERT_CLIENT_CERT_VERIFIER_H_
diff --git a/net/cert/ct_known_logs_static.h b/net/cert/ct_known_logs_static.h index a1e30f1f..8452ece 100644 --- a/net/cert/ct_known_logs_static.h +++ b/net/cert/ct_known_logs_static.h
@@ -1,4 +1,4 @@ -// Copyright 2015 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. @@ -99,9 +99,18 @@ "\x02\x03\x01\x00\x01", 294, "Venafi log", - "https://ctlog.api.venafi.com/"}}; + "https://ctlog.api.venafi.com/"}, + {"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86" + "\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xea\x95\x9e\x02\xff\xee\xf1" + "\x33\x6d\x4b\x87\xbc\xcd\xfd\x19\x17\x62\xff\x94\xd3\xd0\x59\x07\x3f" + "\x02\x2d\x1c\x90\xfe\xc8\x47\x30\x3b\xf1\xdd\x0d\xb8\x11\x0c\x5d\x1d" + "\x86\xdd\xab\xd3\x2b\x46\x66\xfb\x6e\x65\xb7\x3b\xfd\x59\x68\xac\xdf" + "\xa6\xf8\xce\xd2\x18\x4d", + 91, + "Symantec 'Vega' log", + "https://vega.ws.symantec.com/"}}; -const size_t kNumKnownCTLogs = 8; +const size_t kNumKnownCTLogs = 9; // The list is sorted. const char kGoogleLogIDs[][33] = {
diff --git a/net/cert/ct_policy_enforcer.cc b/net/cert/ct_policy_enforcer.cc index 32181d45..aa8838b 100644 --- a/net/cert/ct_policy_enforcer.cc +++ b/net/cert/ct_policy_enforcer.cc
@@ -19,6 +19,7 @@ #include "base/version.h" #include "net/cert/ct_ev_whitelist.h" #include "net/cert/ct_known_logs.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/ct_verify_result.h" #include "net/cert/signed_certificate_timestamp.h" #include "net/cert/x509_certificate.h" @@ -78,11 +79,10 @@ } bool HasRequiredNumberOfSCTs(const X509Certificate& cert, - const ct::CTVerifyResult& ct_result) { - size_t num_valid_scts = ct_result.verified_scts.size(); + const ct::SCTList& verified_scts) { + size_t num_valid_scts = verified_scts.size(); size_t num_embedded_scts = base::checked_cast<size_t>( - std::count_if(ct_result.verified_scts.begin(), - ct_result.verified_scts.end(), IsEmbeddedSCT)); + std::count_if(verified_scts.begin(), verified_scts.end(), IsEmbeddedSCT)); size_t num_non_embedded_scts = num_valid_scts - num_embedded_scts; // If at least two valid SCTs were delivered by means other than embedding @@ -166,8 +166,8 @@ EV_WHITELIST_MAX, }; -void LogCTComplianceStatusToUMA(CTComplianceStatus status, - const ct::EVCertsWhitelist* ev_whitelist) { +void LogCTEVComplianceStatusToUMA(CTComplianceStatus status, + const ct::EVCertsWhitelist* ev_whitelist) { UMA_HISTOGRAM_ENUMERATION("Net.SSL_EVCertificateCTCompliance", status, CT_COMPLIANCE_MAX); if (status == CT_NOT_COMPLIANT) { @@ -185,18 +185,11 @@ } struct ComplianceDetails { - ComplianceDetails() - : ct_presence_required(false), - build_timely(false), - status(CT_NOT_COMPLIANT) {} + ComplianceDetails() : build_timely(false), status(CT_NOT_COMPLIANT) {} - // Whether enforcement of the policy was required or not. - bool ct_presence_required; - // Whether the build is not older than 10 weeks. The value is meaningful only - // if |ct_presence_required| is true. + // Whether the build is not older than 10 weeks. bool build_timely; - // Compliance status - meaningful only if |ct_presence_required| and - // |build_timely| are true. + // Compliance status - meaningful only if |build_timely| is true. CTComplianceStatus status; // EV whitelist version. base::Version whitelist_version; @@ -208,17 +201,14 @@ NetLogCaptureMode capture_mode) { scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); dict->Set("certificate", NetLogX509CertificateCallback(cert, capture_mode)); - dict->SetBoolean("policy_enforcement_required", - details->ct_presence_required); - if (details->ct_presence_required) { - dict->SetBoolean("build_timely", details->build_timely); - if (details->build_timely) { - dict->SetString("ct_compliance_status", - ComplianceStatusToString(details->status)); - if (details->whitelist_version.IsValid()) - dict->SetString("ev_whitelist_version", - details->whitelist_version.GetString()); - } + dict->SetBoolean("policy_enforcement_required", true); + dict->SetBoolean("build_timely", details->build_timely); + if (details->build_timely) { + dict->SetString("ct_compliance_status", + ComplianceStatusToString(details->status)); + if (details->whitelist_version.IsValid()) + dict->SetString("ev_whitelist_version", + details->whitelist_version.GetString()); } return std::move(dict); } @@ -259,10 +249,8 @@ void CheckCTEVPolicyCompliance(X509Certificate* cert, const ct::EVCertsWhitelist* ev_whitelist, - const ct::CTVerifyResult& ct_result, + const ct::SCTList& verified_scts, ComplianceDetails* result) { - result->ct_presence_required = true; - if (!IsBuildTimely()) return; result->build_timely = true; @@ -275,14 +263,13 @@ return; } - if (!HasRequiredNumberOfSCTs(*cert, ct_result)) { + if (!HasRequiredNumberOfSCTs(*cert, verified_scts)) { result->status = CT_NOT_COMPLIANT; return; } - if (AllSCTsPastDistinctSCTRequirementEnforcementDate( - ct_result.verified_scts) && - !HasEnoughDiverseSCTs(ct_result.verified_scts)) { + if (AllSCTsPastDistinctSCTRequirementEnforcementDate(verified_scts) && + !HasEnoughDiverseSCTs(verified_scts)) { result->status = CT_NOT_ENOUGH_DIVERSE_SCTS; return; } @@ -292,14 +279,14 @@ } // namespace -bool CTPolicyEnforcer::DoesConformToCTEVPolicy( +ct::EVPolicyCompliance CTPolicyEnforcer::DoesConformToCTEVPolicy( X509Certificate* cert, const ct::EVCertsWhitelist* ev_whitelist, - const ct::CTVerifyResult& ct_result, + const ct::SCTList& verified_scts, const BoundNetLog& net_log) { ComplianceDetails details; - CheckCTEVPolicyCompliance(cert, ev_whitelist, ct_result, &details); + CheckCTEVPolicyCompliance(cert, ev_whitelist, verified_scts, &details); NetLog::ParametersCallback net_log_callback = base::Bind(&NetLogComplianceCheckResultCallback, base::Unretained(cert), @@ -308,18 +295,25 @@ net_log.AddEvent(NetLog::TYPE_EV_CERT_CT_COMPLIANCE_CHECKED, net_log_callback); - if (!details.ct_presence_required) - return true; - if (!details.build_timely) - return false; + return ct::EVPolicyCompliance::EV_POLICY_BUILD_NOT_TIMELY; - LogCTComplianceStatusToUMA(details.status, ev_whitelist); + LogCTEVComplianceStatusToUMA(details.status, ev_whitelist); - if (details.status == CT_IN_WHITELIST || details.status == CT_ENOUGH_SCTS) - return true; + switch (details.status) { + case CT_NOT_COMPLIANT: + return ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS; + case CT_IN_WHITELIST: + return ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST; + case CT_ENOUGH_SCTS: + return ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS; + case CT_NOT_ENOUGH_DIVERSE_SCTS: + return ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS; + case CT_COMPLIANCE_MAX: + return ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; + } - return false; + return ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; } } // namespace net
diff --git a/net/cert/ct_policy_enforcer.h b/net/cert/ct_policy_enforcer.h index 8c29da5e..a2db8f0 100644 --- a/net/cert/ct_policy_enforcer.h +++ b/net/cert/ct_policy_enforcer.h
@@ -1,25 +1,30 @@ // Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + #ifndef NET_CERT_CT_POLICY_ENFORCER_H #define NET_CERT_CT_POLICY_ENFORCER_H #include <stddef.h> +#include <vector> #include "net/base/net_export.h" +#include "net/cert/signed_certificate_timestamp.h" #include "net/log/net_log.h" namespace net { namespace ct { -struct CTVerifyResult; class EVCertsWhitelist; +enum class EVPolicyCompliance; } // namespace ct class X509Certificate; +using SCTList = std::vector<scoped_refptr<ct::SignedCertificateTimestamp>>; + // Class for checking that a given certificate conforms to security-related // policies. class NET_EXPORT CTPolicyEnforcer { @@ -27,16 +32,17 @@ CTPolicyEnforcer() {} virtual ~CTPolicyEnforcer() {} - // Returns true if the collection of SCTs for the given certificate - // conforms with the CT/EV policy. Conformance details are logged to - // |net_log|. - // |cert| is the certificate for which the SCTs apply. - // |ct_result| must contain the result of verifying any SCTs associated with - // |cert| prior to invoking this method. - virtual bool DoesConformToCTEVPolicy(X509Certificate* cert, - const ct::EVCertsWhitelist* ev_whitelist, - const ct::CTVerifyResult& ct_result, - const BoundNetLog& net_log); + // Returns the CT/EV policy compliance status for a given certificate + // and collection of SCTs. + // |cert| is the certificate for which to check compliance, and + // |verified_scts| contains any/all SCTs associated with |cert| that + // have been verified (well-formed, issued by known logs, and applying to + // |cert|). + virtual ct::EVPolicyCompliance DoesConformToCTEVPolicy( + X509Certificate* cert, + const ct::EVCertsWhitelist* ev_whitelist, + const SCTList& verified_scts, + const BoundNetLog& net_log); }; } // namespace net
diff --git a/net/cert/ct_policy_enforcer_unittest.cc b/net/cert/ct_policy_enforcer_unittest.cc index 4355252..c6c61b3 100644 --- a/net/cert/ct_policy_enforcer_unittest.cc +++ b/net/cert/ct_policy_enforcer_unittest.cc
@@ -12,6 +12,7 @@ #include "crypto/sha2.h" #include "net/base/test_data_directory.h" #include "net/cert/ct_ev_whitelist.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/ct_verify_result.h" #include "net/cert/x509_certificate.h" #include "net/test/cert_test_util.h" @@ -65,6 +66,8 @@ non_google_log_id_.assign(crypto::kSHA256Length, 'A'); } + // TODO(eranm): Remove the use of CTVerifyResult in this file and just + // use lists of verified SCTs. https://crbug.com/587921 void FillResultWithSCTsOfOrigin( ct::SignedCertificateTimestamp::Origin desired_origin, size_t num_scts, @@ -123,15 +126,17 @@ for (size_t i = 0; i < required_scts - 1; ++i) { FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1, std::vector<std::string>(), false, &result); - EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy( - cert.get(), nullptr, result, BoundNetLog())) + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + cert.get(), nullptr, result.verified_scts, BoundNetLog())) << " for: " << (end - start).InDays() << " and " << required_scts << " scts=" << result.verified_scts.size() << " i=" << i; } FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1, std::vector<std::string>(), false, &result); - EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy( - cert.get(), nullptr, result, BoundNetLog())) + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + cert.get(), nullptr, result.verified_scts, BoundNetLog())) << " for: " << (end - start).InDays() << " and " << required_scts << " scts=" << result.verified_scts.size(); } @@ -148,8 +153,9 @@ ct::CTVerifyResult result; FillResultWithRepeatedLogID(google_log_id_, 2, true, &result); - EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy( - chain_.get(), nullptr, result, BoundNetLog())); + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), nullptr, result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, @@ -157,16 +163,18 @@ ct::CTVerifyResult result; FillResultWithRepeatedLogID(non_google_log_id_, 2, true, &result); - EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy( - chain_.get(), nullptr, result, BoundNetLog())); + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_NOT_DIVERSE_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), nullptr, result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, ConformsToCTEVPolicyIfSCTBeforeEnforcementDate) { ct::CTVerifyResult result; FillResultWithRepeatedLogID(non_google_log_id_, 2, false, &result); - EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, - result, BoundNetLog())); + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), nullptr, result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, ConformsToCTEVPolicyWithNonEmbeddedSCTs) { @@ -174,8 +182,9 @@ FillResultWithSCTsOfOrigin( ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, 2, &result); - EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, - result, BoundNetLog())); + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), nullptr, result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, ConformsToCTEVPolicyWithEmbeddedSCTs) { @@ -184,8 +193,9 @@ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 5, &result); - EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(chain_.get(), nullptr, - result, BoundNetLog())); + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), nullptr, result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, DoesNotConformToCTEVPolicyNotEnoughSCTs) { @@ -198,14 +208,18 @@ FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1, &result); - EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy( - chain_.get(), non_including_whitelist.get(), result, BoundNetLog())); + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), non_including_whitelist.get(), + result.verified_scts, BoundNetLog())); // ... but should be OK if whitelisted. scoped_refptr<ct::EVCertsWhitelist> whitelist( new DummyEVCertsWhitelist(true, true)); - EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy( - chain_.get(), whitelist.get(), result, BoundNetLog())); + EXPECT_EQ( + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), whitelist.get(), result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, DoesNotConformToPolicyInvalidDates) { @@ -214,13 +228,17 @@ ct::CTVerifyResult result; FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 5, &result); - EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy( - no_valid_dates_cert.get(), nullptr, result, BoundNetLog())); + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + no_valid_dates_cert.get(), nullptr, result.verified_scts, + BoundNetLog())); // ... but should be OK if whitelisted. scoped_refptr<ct::EVCertsWhitelist> whitelist( new DummyEVCertsWhitelist(true, true)); - EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy( - chain_.get(), whitelist.get(), result, BoundNetLog())); + EXPECT_EQ( + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), whitelist.get(), result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, @@ -274,8 +292,10 @@ ct::CTVerifyResult result; FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1, &result); - EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy( - chain_.get(), whitelist.get(), result, BoundNetLog())); + EXPECT_EQ( + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), whitelist.get(), result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, IgnoresInvalidEVWhitelist) { @@ -285,16 +305,19 @@ ct::CTVerifyResult result; FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1, &result); - EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy( - chain_.get(), whitelist.get(), result, BoundNetLog())); + EXPECT_EQ( + ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), whitelist.get(), result.verified_scts, BoundNetLog())); } TEST_F(CTPolicyEnforcerTest, IgnoresNullEVWhitelist) { ct::CTVerifyResult result; FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1, &result); - EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy( - chain_.get(), nullptr, result, BoundNetLog())); + EXPECT_EQ(ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS, + policy_enforcer_->DoesConformToCTEVPolicy( + chain_.get(), nullptr, result.verified_scts, BoundNetLog())); } } // namespace
diff --git a/net/cert/ct_policy_status.h b/net/cert/ct_policy_status.h new file mode 100644 index 0000000..e234cb7 --- /dev/null +++ b/net/cert/ct_policy_status.h
@@ -0,0 +1,39 @@ +// 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 NET_CERT_CT_POLICY_STATUS_H +#define NET_CERT_CT_POLICY_STATUS_H + +namespace net { + +namespace ct { + +// Information about a connection's compliance with the CT EV +// certificate policy. +enum class EVPolicyCompliance { + // The certificate was not EV, so the EV policy doesn't apply. + EV_POLICY_DOES_NOT_APPLY, + // The connection complied with the EV certificate policy by being + // included on the EV whitelist. + EV_POLICY_COMPLIES_VIA_WHITELIST, + // The connection complied with the EV certificate policy by + // including SCTs that satisfy the policy. + EV_POLICY_COMPLIES_VIA_SCTS, + // The connection did not have enough SCTs to retain its EV + // status. + EV_POLICY_NOT_ENOUGH_SCTS, + // The connection did not have diverse enough SCTs to retain its + // EV status. + EV_POLICY_NOT_DIVERSE_SCTS, + // The connection cannot be considered compliant because the build + // isn't timely and therefore log information might be out of date + // (for example a log might no longer be considered trustworthy). + EV_POLICY_BUILD_NOT_TIMELY, +}; + +} // namespace ct + +} // namespace net + +#endif // NET_CERT_CT_POLICY_STATUS_H
diff --git a/net/cert/ct_verify_result.cc b/net/cert/ct_verify_result.cc index c62a18a..5e89b8f5 100644 --- a/net/cert/ct_verify_result.cc +++ b/net/cert/ct_verify_result.cc
@@ -4,11 +4,15 @@ #include "net/cert/ct_verify_result.h" +#include "net/cert/ct_policy_status.h" + namespace net { namespace ct { -CTVerifyResult::CTVerifyResult() {} +CTVerifyResult::CTVerifyResult() + : ct_policies_applied(false), + ev_policy_compliance(ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY) {} CTVerifyResult::~CTVerifyResult() {}
diff --git a/net/cert/ct_verify_result.h b/net/cert/ct_verify_result.h index aa90164..e434fb8 100644 --- a/net/cert/ct_verify_result.h +++ b/net/cert/ct_verify_result.h
@@ -7,17 +7,20 @@ #include <vector> +#include "net/cert/ct_policy_enforcer.h" #include "net/cert/signed_certificate_timestamp.h" namespace net { namespace ct { +enum class EVPolicyCompliance; + typedef std::vector<scoped_refptr<SignedCertificateTimestamp> > SCTList; -// Holds Signed Certificate Timestamps, depending on their verification results. -// More information could be tracked here about SCTs, but for the current UI -// this categorization is enough. +// Holds Signed Certificate Timestamps, depending on their verification +// results, and information about CT policies that were applied on the +// connection. struct NET_EXPORT CTVerifyResult { CTVerifyResult(); ~CTVerifyResult(); @@ -28,6 +31,12 @@ SCTList invalid_scts; // SCTs from unknown logs and as such are unverifiable. SCTList unknown_logs_scts; + + // True if any CT policies were applied on this connection. + bool ct_policies_applied; + // The result of evaluating whether the connection complies with the + // EV CT policy. + EVPolicyCompliance ev_policy_compliance; }; } // namespace ct
diff --git a/net/cert/internal/name_constraints.cc b/net/cert/internal/name_constraints.cc index 2ad35dd..7bf4685 100644 --- a/net/cert/internal/name_constraints.cc +++ b/net/cert/internal/name_constraints.cc
@@ -144,124 +144,91 @@ der::Input value; if (!parser.ReadTagAndValue(&tag, &value)) return false; - if (!der::IsContextSpecific(tag)) - return false; GeneralNameTypes name_type = GENERAL_NAME_NONE; - // GeneralName ::= CHOICE { - switch (der::GetTagNumber(tag)) { + if (tag == der::ContextSpecificConstructed(0)) { // otherName [0] OtherName, - case 0: - if (!der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_OTHER_NAME; - break; + name_type = GENERAL_NAME_OTHER_NAME; + } else if (tag == der::ContextSpecificPrimitive(1)) { // rfc822Name [1] IA5String, - case 1: - if (der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_RFC822_NAME; - break; + name_type = GENERAL_NAME_RFC822_NAME; + } else if (tag == der::ContextSpecificPrimitive(2)) { // dNSName [2] IA5String, - case 2: { - if (der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_DNS_NAME; - const std::string s = value.AsString(); - if (!base::IsStringASCII(s)) - return false; - subtrees->dns_names.push_back(s); - break; - } - // x400Address [3] ORAddress, - case 3: - if (!der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_X400_ADDRESS; - break; - // directoryName [4] Name, - case 4: { - if (!der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_DIRECTORY_NAME; - // Name is a CHOICE { rdnSequence RDNSequence }, therefore the SEQUENCE - // tag is explicit. Remove it, since the name matching functions expect - // only the value portion. - der::Parser name_parser(value); - der::Input name_value; - if (!name_parser.ReadTag(der::kSequence, &name_value) || parser.HasMore()) - return false; - subtrees->directory_names.push_back( - std::vector<uint8_t>(name_value.UnsafeData(), - name_value.UnsafeData() + name_value.Length())); - break; - } - // ediPartyName [5] EDIPartyName, - case 5: - if (!der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_EDI_PARTY_NAME; - break; - // uniformResourceIdentifier [6] IA5String, - case 6: - if (der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_UNIFORM_RESOURCE_IDENTIFIER; - break; - // iPAddress [7] OCTET STRING, - case 7: - if (der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_IP_ADDRESS; - if (ip_address_type == IP_ADDRESS_ONLY) { - // RFC 5280 section 4.2.1.6: - // When the subjectAltName extension contains an iPAddress, the address - // MUST be stored in the octet string in "network byte order", as - // specified in [RFC791]. The least significant bit (LSB) of each octet - // is the LSB of the corresponding byte in the network address. For IP - // version 4, as specified in [RFC791], the octet string MUST contain - // exactly four octets. For IP version 6, as specified in [RFC2460], - // the octet string MUST contain exactly sixteen octets. - if ((value.Length() != kIPv4AddressSize && - value.Length() != kIPv6AddressSize)) { - return false; - } - subtrees->ip_addresses.push_back(std::vector<uint8_t>( - value.UnsafeData(), value.UnsafeData() + value.Length())); - } else { - DCHECK_EQ(ip_address_type, IP_ADDRESS_AND_NETMASK); - // RFC 5280 section 4.2.1.10: - // The syntax of iPAddress MUST be as described in Section 4.2.1.6 with - // the following additions specifically for name constraints. For IPv4 - // addresses, the iPAddress field of GeneralName MUST contain eight (8) - // octets, encoded in the style of RFC 4632 (CIDR) to represent an - // address range [RFC4632]. For IPv6 addresses, the iPAddress field - // MUST contain 32 octets similarly encoded. For example, a name - // constraint for "class C" subnet 192.0.2.0 is represented as the - // octets C0 00 02 00 FF FF FF 00, representing the CIDR notation - // 192.0.2.0/24 (mask 255.255.255.0). - if (value.Length() != kIPv4AddressSize * 2 && - value.Length() != kIPv6AddressSize * 2) { - return false; - } - const std::vector<uint8_t> mask(value.UnsafeData() + value.Length() / 2, - value.UnsafeData() + value.Length()); - const unsigned mask_prefix_length = MaskPrefixLength(mask); - if (!IsSuffixZero(mask, mask_prefix_length)) - return false; - subtrees->ip_address_ranges.push_back(std::make_pair( - std::vector<uint8_t>(value.UnsafeData(), - value.UnsafeData() + value.Length() / 2), - mask_prefix_length)); - } - break; - // registeredID [8] OBJECT IDENTIFIER } - case 8: - if (der::IsConstructed(tag)) - return false; - name_type = GENERAL_NAME_REGISTERED_ID; - break; - default: + name_type = GENERAL_NAME_DNS_NAME; + const std::string s = value.AsString(); + if (!base::IsStringASCII(s)) return false; + subtrees->dns_names.push_back(s); + } else if (tag == der::ContextSpecificConstructed(3)) { + // x400Address [3] ORAddress, + name_type = GENERAL_NAME_X400_ADDRESS; + } else if (tag == der::ContextSpecificConstructed(4)) { + // directoryName [4] Name, + name_type = GENERAL_NAME_DIRECTORY_NAME; + // Name is a CHOICE { rdnSequence RDNSequence }, therefore the SEQUENCE + // tag is explicit. Remove it, since the name matching functions expect + // only the value portion. + der::Parser name_parser(value); + der::Input name_value; + if (!name_parser.ReadTag(der::kSequence, &name_value) || parser.HasMore()) + return false; + subtrees->directory_names.push_back( + std::vector<uint8_t>(name_value.UnsafeData(), + name_value.UnsafeData() + name_value.Length())); + } else if (tag == der::ContextSpecificConstructed(5)) { + // ediPartyName [5] EDIPartyName, + name_type = GENERAL_NAME_EDI_PARTY_NAME; + } else if (tag == der::ContextSpecificPrimitive(6)) { + // uniformResourceIdentifier [6] IA5String, + name_type = GENERAL_NAME_UNIFORM_RESOURCE_IDENTIFIER; + } else if (tag == der::ContextSpecificPrimitive(7)) { + // iPAddress [7] OCTET STRING, + name_type = GENERAL_NAME_IP_ADDRESS; + if (ip_address_type == IP_ADDRESS_ONLY) { + // RFC 5280 section 4.2.1.6: + // When the subjectAltName extension contains an iPAddress, the address + // MUST be stored in the octet string in "network byte order", as + // specified in [RFC791]. The least significant bit (LSB) of each octet + // is the LSB of the corresponding byte in the network address. For IP + // version 4, as specified in [RFC791], the octet string MUST contain + // exactly four octets. For IP version 6, as specified in [RFC2460], + // the octet string MUST contain exactly sixteen octets. + if ((value.Length() != kIPv4AddressSize && + value.Length() != kIPv6AddressSize)) { + return false; + } + subtrees->ip_addresses.push_back(std::vector<uint8_t>( + value.UnsafeData(), value.UnsafeData() + value.Length())); + } else { + DCHECK_EQ(ip_address_type, IP_ADDRESS_AND_NETMASK); + // RFC 5280 section 4.2.1.10: + // The syntax of iPAddress MUST be as described in Section 4.2.1.6 with + // the following additions specifically for name constraints. For IPv4 + // addresses, the iPAddress field of GeneralName MUST contain eight (8) + // octets, encoded in the style of RFC 4632 (CIDR) to represent an + // address range [RFC4632]. For IPv6 addresses, the iPAddress field + // MUST contain 32 octets similarly encoded. For example, a name + // constraint for "class C" subnet 192.0.2.0 is represented as the + // octets C0 00 02 00 FF FF FF 00, representing the CIDR notation + // 192.0.2.0/24 (mask 255.255.255.0). + if (value.Length() != kIPv4AddressSize * 2 && + value.Length() != kIPv6AddressSize * 2) { + return false; + } + const std::vector<uint8_t> mask(value.UnsafeData() + value.Length() / 2, + value.UnsafeData() + value.Length()); + const unsigned mask_prefix_length = MaskPrefixLength(mask); + if (!IsSuffixZero(mask, mask_prefix_length)) + return false; + subtrees->ip_address_ranges.push_back(std::make_pair( + std::vector<uint8_t>(value.UnsafeData(), + value.UnsafeData() + value.Length() / 2), + mask_prefix_length)); + } + } else if (tag == der::ContextSpecificPrimitive(8)) { + // registeredID [8] OBJECT IDENTIFIER } + name_type = GENERAL_NAME_REGISTERED_ID; + } else { + return false; } DCHECK_NE(GENERAL_NAME_NONE, name_type); if ((name_type & kSupportedNameTypes) ||
diff --git a/net/cert/internal/parse_name.cc b/net/cert/internal/parse_name.cc new file mode 100644 index 0000000..2f783fe --- /dev/null +++ b/net/cert/internal/parse_name.cc
@@ -0,0 +1,214 @@ +// 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 "net/cert/internal/parse_name.h" + +#include "base/strings/string16.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversion_utils.h" +#include "base/strings/utf_string_conversions.h" +#include "base/sys_byteorder.h" +#include "base/third_party/icu/icu_utf.h" + +namespace net { + +namespace { + +// Converts a BMPString value in Input |in| to UTF-8. +// +// If the conversion is successful, returns true and stores the result in +// |out|. Otherwise it returns false and leaves |out| unmodified. +bool ConvertBmpStringValue(const der::Input& in, std::string* out) { + if (in.Length() % 2 != 0) + return false; + + base::string16 in_16bit; + if (in.Length()) { + memcpy(base::WriteInto(&in_16bit, in.Length() / 2 + 1), in.UnsafeData(), + in.Length()); + } + for (base::char16& c : in_16bit) { + // BMPString is UCS-2 in big-endian order. + c = base::NetToHost16(c); + + // BMPString only supports codepoints in the Basic Multilingual Plane; + // surrogates are not allowed. + if (CBU_IS_SURROGATE(c)) + return false; + } + return base::UTF16ToUTF8(in_16bit.data(), in_16bit.size(), out); +} + +// Converts a UniversalString value in Input |in| to UTF-8. +// +// If the conversion is successful, returns true and stores the result in +// |out|. Otherwise it returns false and leaves |out| unmodified. +bool ConvertUniversalStringValue(const der::Input& in, std::string* out) { + if (in.Length() % 4 != 0) + return false; + + std::vector<uint32_t> in_32bit(in.Length() / 4); + if (in.Length()) + memcpy(in_32bit.data(), in.UnsafeData(), in.Length()); + for (const uint32_t c : in_32bit) { + // UniversalString is UCS-4 in big-endian order. + uint32_t codepoint = base::NetToHost32(c); + if (!CBU_IS_UNICODE_CHAR(codepoint)) + return false; + + base::WriteUnicodeCharacter(codepoint, out); + } + return true; +} + +} // namespace + +der::Input TypeCommonNameOid() { + // id-at-commonName: 2.5.4.3 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x03}; + return der::Input(oid); +} + +der::Input TypeSurnameOid() { + // id-at-surname: 2.5.4.4 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x04}; + return der::Input(oid); +} + +der::Input TypeSerialNumberOid() { + // id-at-serialNumber: 2.5.4.5 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x05}; + return der::Input(oid); +} + +der::Input TypeCountryNameOid() { + // id-at-countryName: 2.5.4.6 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x06}; + return der::Input(oid); +} + +der::Input TypeLocalityNameOid() { + // id-at-localityName: 2.5.4.7 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x07}; + return der::Input(oid); +} + +der::Input TypeStateOrProvinceNameOid() { + // id-at-stateOrProvinceName: 2.5.4.8 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x08}; + return der::Input(oid); +} + +der::Input TypeOrganizationNameOid() { + // id-at-organizationName: 2.5.4.10 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x0a}; + return der::Input(oid); +} + +der::Input TypeOrganizationUnitNameOid() { + // id-at-organizationalUnitName: 2.5.4.11 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x0b}; + return der::Input(oid); +} + +der::Input TypeTitleOid() { + // id-at-title: 2.5.4.12 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x0c}; + return der::Input(oid); +} + +der::Input TypeNameOid() { + // id-at-name: 2.5.4.41 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x29}; + return der::Input(oid); +} + +der::Input TypeGivenNameOid() { + // id-at-givenName: 2.5.4.42 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x2a}; + return der::Input(oid); +} + +der::Input TypeInitialsOid() { + // id-at-initials: 2.5.4.43 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x2b}; + return der::Input(oid); +} + +der::Input TypeGenerationQualifierOid() { + // id-at-generationQualifier: 2.5.4.44 (RFC 5280) + static const uint8_t oid[] = {0x55, 0x04, 0x2c}; + return der::Input(oid); +} + +bool X509NameAttribute::ValueAsStringUnsafe(std::string* out) const { + switch (value_tag) { + case der::kIA5String: + case der::kPrintableString: + case der::kTeletexString: + case der::kUtf8String: + *out = value.AsString(); + return true; + case der::kUniversalString: + return ConvertUniversalStringValue(value, out); + case der::kBmpString: + return ConvertBmpStringValue(value, out); + default: + NOTREACHED(); + return false; + } +} + +bool ReadRdn(der::Parser* parser, std::vector<X509NameAttribute>* out) { + while (parser->HasMore()) { + der::Parser attr_type_and_value; + if (!parser->ReadSequence(&attr_type_and_value)) + return false; + // Read the attribute type, which must be an OBJECT IDENTIFIER. + der::Input type; + if (!attr_type_and_value.ReadTag(der::kOid, &type)) + return false; + + // Read the attribute value. + der::Tag tag; + der::Input value; + if (!attr_type_and_value.ReadTagAndValue(&tag, &value)) + return false; + + // There should be no more elements in the sequence after reading the + // attribute type and value. + if (attr_type_and_value.HasMore()) + return false; + + out->push_back(X509NameAttribute(type, tag, value)); + } + + // RFC 5280 section 4.1.2.4 + // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue + return out->size() != 0; +} + +bool ParseName(const der::Input& name_tlv, + std::vector<X509NameAttribute>* out) { + der::Parser name_parser(name_tlv); + der::Parser rdn_sequence_parser; + if (!name_parser.ReadSequence(&rdn_sequence_parser)) + return false; + + while (rdn_sequence_parser.HasMore()) { + der::Parser rdn_parser; + if (!rdn_sequence_parser.ReadConstructed(der::kSet, &rdn_parser)) + return false; + std::vector<X509NameAttribute> type_and_values; + if (!ReadRdn(&rdn_parser, &type_and_values)) + return false; + for (const auto& type_and_value : type_and_values) { + out->push_back(type_and_value); + } + } + + return true; +} + +} // namespace net
diff --git a/net/cert/internal/parse_name.h b/net/cert/internal/parse_name.h new file mode 100644 index 0000000..2cb1d4ca --- /dev/null +++ b/net/cert/internal/parse_name.h
@@ -0,0 +1,91 @@ +// 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 NET_CERT_INTERNAL_PARSE_NAME_H_ +#define NET_CERT_INTERNAL_PARSE_NAME_H_ + +#include <vector> + +#include "net/base/net_export.h" +#include "net/der/input.h" +#include "net/der/parser.h" +#include "net/der/tag.h" + +namespace net { + +NET_EXPORT der::Input TypeCommonNameOid(); +NET_EXPORT der::Input TypeSurnameOid(); +NET_EXPORT der::Input TypeSerialNumberOid(); +NET_EXPORT der::Input TypeCountryNameOid(); +NET_EXPORT der::Input TypeLocalityNameOid(); +NET_EXPORT der::Input TypeStateOrProvinceNameOid(); +NET_EXPORT der::Input TypeOrganizationNameOid(); +NET_EXPORT der::Input TypeOrganizationUnitNameOid(); +NET_EXPORT der::Input TypeTitleOid(); +NET_EXPORT der::Input TypeNameOid(); +NET_EXPORT der::Input TypeGivenNameOid(); +NET_EXPORT der::Input TypeInitialsOid(); +NET_EXPORT der::Input TypeGenerationQualifierOid(); + +// X509NameAttribute contains a representation of a DER-encoded RFC 2253 +// "AttributeTypeAndValue". +// +// AttributeTypeAndValue ::= SEQUENCE { +// type AttributeType, +// value AttributeValue +// } +struct NET_EXPORT X509NameAttribute { + X509NameAttribute(der::Input in_type, + der::Tag in_value_tag, + der::Input in_value) + : type(in_type), value_tag(in_value_tag), value(in_value) {} + + // Attempts to convert the value represented by this struct into a + // std::string and store it in |out|, returning whether the conversion was + // successful. Due to some encodings being incompatible, the caller must + // verify the attribute |type|. + // + // Note: The conversion doesn't verify that the value corresponds to the + // ASN.1 definition of the value type. + bool ValueAsStringUnsafe(std::string* out) const WARN_UNUSED_RESULT; + + der::Input type; + der::Tag value_tag; + der::Input value; +}; + +// Parses all the ASN.1 AttributeTypeAndValue elements in |parser| and stores +// each as an AttributeTypeAndValue object in |out|. +// +// AttributeTypeAndValue is defined in RFC 5280 section 4.1.2.4: +// +// AttributeTypeAndValue ::= SEQUENCE { +// type AttributeType, +// value AttributeValue } +// +// AttributeType ::= OBJECT IDENTIFIER +// +// AttributeValue ::= ANY -- DEFINED BY AttributeType +// +// DirectoryString ::= CHOICE { +// teletexString TeletexString (SIZE (1..MAX)), +// printableString PrintableString (SIZE (1..MAX)), +// universalString UniversalString (SIZE (1..MAX)), +// utf8String UTF8String (SIZE (1..MAX)), +// bmpString BMPString (SIZE (1..MAX)) } +// +// The type of the component AttributeValue is determined by the AttributeType; +// in general it will be a DirectoryString. +NET_EXPORT bool ReadRdn(der::Parser* parser, + std::vector<X509NameAttribute>* out) WARN_UNUSED_RESULT; + +// Parses a DER-encoded "Name" as specified by 5280. Returns true on success +// and sets the results in |out|. +NET_EXPORT bool ParseName(const der::Input& name_tlv, + std::vector<X509NameAttribute>* out) + WARN_UNUSED_RESULT; + +} // namespace net + +#endif // NET_CERT_INTERNAL_PARSE_NAME_H_
diff --git a/net/cert/internal/parse_name_unittest.cc b/net/cert/internal/parse_name_unittest.cc new file mode 100644 index 0000000..b013a7d5 --- /dev/null +++ b/net/cert/internal/parse_name_unittest.cc
@@ -0,0 +1,143 @@ +// 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 "net/cert/internal/parse_name.h" + +#include "net/cert/internal/test_helpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { +// Loads test data from file. The filename is constructed from the parameters: +// |prefix| describes the type of data being tested, e.g. "ascii", +// "unicode_bmp", "unicode_supplementary", and "invalid". +// |value_type| indicates what ASN.1 type is used to encode the data. +// |suffix| indicates any additional modifications, such as caseswapping, +// whitespace adding, etc. +::testing::AssertionResult LoadTestData(const std::string& prefix, + const std::string& value_type, + const std::string& suffix, + std::string* result) { + std::string path = "net/data/verify_name_match_unittest/names/" + prefix + + "-" + value_type + "-" + suffix + ".pem"; + + const PemBlockMapping mappings[] = { + {"NAME", result}, + }; + + return ReadTestDataFromPemFile(path, mappings); +} +} + +TEST(ParseNameTest, ConvertBmpString) { + const uint8_t der[] = { + 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x61, 0x00, 0x72, + }; + X509NameAttribute value(der::Input(), der::kBmpString, der::Input(der)); + std::string result; + ASSERT_TRUE(value.ValueAsStringUnsafe(&result)); + ASSERT_EQ("foobar", result); +} + +// BmpString must encode characters in pairs of 2 bytes. +TEST(ParseNameTest, ConvertInvalidBmpString) { + const uint8_t der[] = {0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x72}; + X509NameAttribute value(der::Input(), der::kBmpString, der::Input(der)); + std::string result; + ASSERT_FALSE(value.ValueAsStringUnsafe(&result)); +} + +TEST(ParseNameTest, ConvertUniversalString) { + const uint8_t der[] = {0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x6f, + 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x62, + 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x72}; + X509NameAttribute value(der::Input(), der::kUniversalString, der::Input(der)); + std::string result; + ASSERT_TRUE(value.ValueAsStringUnsafe(&result)); +} + +// UniversalString must encode characters in pairs of 4 bytes. +TEST(ParseNameTest, ConvertInvalidUniversalString) { + const uint8_t der[] = {0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72}; + X509NameAttribute value(der::Input(), der::kUniversalString, der::Input(der)); + std::string result; + ASSERT_FALSE(value.ValueAsStringUnsafe(&result)); +} + +TEST(ParseNameTest, EmptyName) { + const uint8_t der[] = {0x30, 0x00}; + der::Input rdn(der); + std::vector<X509NameAttribute> atv; + ASSERT_TRUE(ParseName(rdn, &atv)); + ASSERT_EQ(0u, atv.size()); +} + +TEST(ParseNameTest, ValidName) { + const uint8_t der[] = {0x30, 0x3c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x14, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, 0x47, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x0e, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41}; + der::Input rdn(der); + std::vector<X509NameAttribute> atv; + ASSERT_TRUE(ParseName(rdn, &atv)); + ASSERT_EQ(3u, atv.size()); + ASSERT_TRUE(atv[0].type == TypeCountryNameOid()); + ASSERT_EQ("US", atv[0].value.AsString()); + ASSERT_TRUE(atv[1].type == TypeOrganizationNameOid()); + ASSERT_EQ("Google Inc.", atv[1].value.AsString()); + ASSERT_TRUE(atv[2].type == TypeCommonNameOid()); + ASSERT_EQ("Google Test CA", atv[2].value.AsString()); +} + +TEST(ParseNameTest, InvalidNameExtraData) { + std::string invalid; + ASSERT_TRUE( + LoadTestData("invalid", "AttributeTypeAndValue", "extradata", &invalid)); + std::vector<X509NameAttribute> atv; + ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv)); +} + +TEST(ParseNameTest, InvalidNameEmpty) { + std::string invalid; + ASSERT_TRUE( + LoadTestData("invalid", "AttributeTypeAndValue", "empty", &invalid)); + std::vector<X509NameAttribute> atv; + ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv)); +} + +TEST(ParseNameTest, InvalidNameBadType) { + std::string invalid; + ASSERT_TRUE(LoadTestData("invalid", "AttributeTypeAndValue", + "badAttributeType", &invalid)); + std::vector<X509NameAttribute> atv; + ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv)); +} + +TEST(ParseNameTest, InvalidNameNotSequence) { + std::string invalid; + ASSERT_TRUE(LoadTestData("invalid", "AttributeTypeAndValue", "setNotSequence", + &invalid)); + std::vector<X509NameAttribute> atv; + ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv)); +} + +TEST(ParseNameTest, InvalidNameNotSet) { + std::string invalid; + ASSERT_TRUE(LoadTestData("invalid", "RDN", "sequenceInsteadOfSet", &invalid)); + std::vector<X509NameAttribute> atv; + ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv)); +} + +TEST(ParseNameTest, InvalidNameEmptyRdn) { + std::string invalid; + ASSERT_TRUE(LoadTestData("invalid", "RDN", "empty", &invalid)); + std::vector<X509NameAttribute> atv; + ASSERT_FALSE(ParseName(SequenceValueFromString(&invalid), &atv)); +} + +} // namespace net
diff --git a/net/cert/internal/verify_name_match.cc b/net/cert/internal/verify_name_match.cc index 16d8b78..15c6f83 100644 --- a/net/cert/internal/verify_name_match.cc +++ b/net/cert/internal/verify_name_match.cc
@@ -4,15 +4,8 @@ #include "net/cert/internal/verify_name_match.h" -#include <string.h> - -#include "base/strings/string16.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversion_utils.h" -#include "base/strings/utf_string_conversions.h" -#include "base/sys_byteorder.h" -#include "base/third_party/icu/icu_utf.h" #include "base/tuple.h" +#include "net/cert/internal/parse_name.h" #include "net/der/input.h" #include "net/der/parser.h" #include "net/der/tag.h" @@ -115,135 +108,28 @@ return true; } -// Normalizes the DER-encoded PrintableString value |in| according to -// RFC 2459, Section 4.1.2.4 +// Converts the value of X509NameAttribute |attribute| to UTF-8, normalizes it, +// and stores in |output|. The type of |attribute| must be one of the types for +// which IsNormalizableDirectoryString is true. // -// Briefly, normalization involves removing leading and trailing -// whitespace, folding multiple whitespace characters into a single -// whitespace character, and normalizing on case (this function -// normalizes to lowercase). -// -// During normalization, this function also validates that |in| -// is properly encoded - that is, that it restricts to the character -// set defined in X.680 (2008), Section 41.4, Table 10. X.680 defines -// the valid characters as -// a-z A-Z 0-9 (space) ' ( ) + , - . / : = ? -// -// However, due to an old OpenSSL encoding bug, a number of -// certificates have also included '*', which has historically been -// allowed by implementations, and so is also allowed here. -// -// If |in| can be normalized, returns true and sets |output| to the -// case folded, normalized value. If |in| is invalid, returns false. +// If the value of |attribute| can be normalized, returns true and sets +// |output| to the case folded, normalized value. If the value of |attribute| +// is invalid, returns false. // NOTE: |output| will be modified regardless of the return. -WARN_UNUSED_RESULT bool NormalizePrintableStringValue(const der::Input& in, - std::string* output) { - in.AsString().swap(*output); - return NormalizeDirectoryString(ENFORCE_PRINTABLE_STRING, output); -} - -// Normalized a UTF8String value. See the comment for NormalizeDirectoryString -// for details. -// -// If |in| can be normalized, returns true and sets |output| to the -// case folded, normalized value. If |in| is invalid, returns false. -// NOTE: |output| will be modified regardless of the return. -WARN_UNUSED_RESULT bool NormalizeUtf8StringValue(const der::Input& in, - std::string* output) { - in.AsString().swap(*output); - return NormalizeDirectoryString(NO_ENFORCEMENT, output); -} - -// IA5String is ISO/IEC Registrations 1 and 6 from the ISO -// "International Register of Coded Character Sets to be used -// with Escape Sequences", plus space and delete. That's just the -// polite way of saying 0x00 - 0x7F, aka ASCII (or, more formally, -// ISO/IEC 646) -// -// If |in| can be normalized, returns true and sets |output| to the case folded, -// normalized value. If |in| is invalid, returns false. -// NOTE: |output| will be modified regardless of the return. -WARN_UNUSED_RESULT bool NormalizeIA5StringValue(const der::Input& in, - std::string* output) { - in.AsString().swap(*output); - return NormalizeDirectoryString(ENFORCE_ASCII, output); -} - -// Converts BMPString value to UTF-8 and then normalizes it. See the comment for -// NormalizeDirectoryString for details. -// -// If |in| can be normalized, returns true and sets |output| to the case folded, -// normalized value. If |in| is invalid, returns false. -// NOTE: |output| will be modified regardless of the return. -WARN_UNUSED_RESULT bool NormalizeBmpStringValue(const der::Input& in, - std::string* output) { - if (in.Length() % 2 != 0) - return false; - - base::string16 in_16bit; - if (in.Length()) { - memcpy(base::WriteInto(&in_16bit, in.Length() / 2 + 1), in.UnsafeData(), - in.Length()); - } - for (base::char16& c : in_16bit) { - // BMPString is UCS-2 in big-endian order. - c = base::NetToHost16(c); - - // BMPString only supports codepoints in the Basic Multilingual Plane; - // surrogates are not allowed. - if (CBU_IS_SURROGATE(c)) - return false; - } - if (!base::UTF16ToUTF8(in_16bit.data(), in_16bit.size(), output)) - return false; - return NormalizeDirectoryString(NO_ENFORCEMENT, output); -} - -// Converts UniversalString value to UTF-8 and then normalizes it. See the -// comment for NormalizeDirectoryString for details. -// -// If |in| can be normalized, returns true and sets |output| to the case folded, -// normalized value. If |in| is invalid, returns false. -// NOTE: |output| will be modified regardless of the return. -WARN_UNUSED_RESULT bool NormalizeUniversalStringValue(const der::Input& in, - std::string* output) { - if (in.Length() % 4 != 0) - return false; - - std::vector<uint32_t> in_32bit(in.Length() / 4); - if (in.Length()) - memcpy(in_32bit.data(), in.UnsafeData(), in.Length()); - for (const uint32_t c : in_32bit) { - // UniversalString is UCS-4 in big-endian order. - uint32_t codepoint = base::NetToHost32(c); - if (!CBU_IS_UNICODE_CHAR(codepoint)) - return false; - - base::WriteUnicodeCharacter(codepoint, output); - } - return NormalizeDirectoryString(NO_ENFORCEMENT, output); -} - -// Converts the string |value| to UTF-8, normalizes it, and stores in |output|. -// |tag| must one of the types for which IsNormalizableDirectoryString is true. -// -// If |value| can be normalized, returns true and sets |output| to the case -// folded, normalized value. If |value| is invalid, returns false. -// NOTE: |output| will be modified regardless of the return. -WARN_UNUSED_RESULT bool NormalizeValue(const der::Tag tag, - const der::Input& value, +WARN_UNUSED_RESULT bool NormalizeValue(X509NameAttribute attribute, std::string* output) { - switch (tag) { + if (!attribute.ValueAsStringUnsafe(output)) + return false; + + switch (attribute.value_tag) { case der::kPrintableString: - return NormalizePrintableStringValue(value, output); - case der::kUtf8String: - return NormalizeUtf8StringValue(value, output); - case der::kIA5String: - return NormalizeIA5StringValue(value, output); - case der::kUniversalString: - return NormalizeUniversalStringValue(value, output); + return NormalizeDirectoryString(ENFORCE_PRINTABLE_STRING, output); case der::kBmpString: - return NormalizeBmpStringValue(value, output); + case der::kUniversalString: + case der::kUtf8String: + return NormalizeDirectoryString(NO_ENFORCEMENT, output); + case der::kIA5String: + return NormalizeDirectoryString(ENFORCE_ASCII, output); default: NOTREACHED(); return false; @@ -271,100 +157,31 @@ } } -// Returns true if the AttributeValue (|a_tag|, |a_value|) matches (|b_tag|, -// |b_value|). -bool VerifyValueMatch(const der::Tag a_tag, - const der::Input& a_value, - const der::Tag b_tag, - const der::Input& b_value) { - if (IsNormalizableDirectoryString(a_tag) && - IsNormalizableDirectoryString(b_tag)) { +// Returns true if the value of X509NameAttribute |a| matches |b|. +bool VerifyValueMatch(X509NameAttribute a, X509NameAttribute b) { + if (IsNormalizableDirectoryString(a.value_tag) && + IsNormalizableDirectoryString(b.value_tag)) { std::string a_normalized, b_normalized; - if (!NormalizeValue(a_tag, a_value, &a_normalized) || - !NormalizeValue(b_tag, b_value, &b_normalized)) + if (!NormalizeValue(a, &a_normalized) || !NormalizeValue(b, &b_normalized)) return false; return a_normalized == b_normalized; } // Attributes encoded with different types may be assumed to be unequal. - if (a_tag != b_tag) + if (a.value_tag != b.value_tag) return false; // All other types use binary comparison. - return a_value == b_value; -} - -struct AttributeTypeAndValue { - AttributeTypeAndValue(der::Input in_type, - der::Tag in_value_tag, - der::Input in_value) - : type(in_type), value_tag(in_value_tag), value(in_value) {} - der::Input type; - der::Tag value_tag; - der::Input value; -}; - -// Parses all the ASN.1 AttributeTypeAndValue elements in |parser| and stores -// each as an AttributeTypeAndValue object in |out|. -// -// AttributeTypeAndValue is defined in RFC 5280 section 4.1.2.4: -// -// AttributeTypeAndValue ::= SEQUENCE { -// type AttributeType, -// value AttributeValue } -// -// AttributeType ::= OBJECT IDENTIFIER -// -// AttributeValue ::= ANY -- DEFINED BY AttributeType -// -// DirectoryString ::= CHOICE { -// teletexString TeletexString (SIZE (1..MAX)), -// printableString PrintableString (SIZE (1..MAX)), -// universalString UniversalString (SIZE (1..MAX)), -// utf8String UTF8String (SIZE (1..MAX)), -// bmpString BMPString (SIZE (1..MAX)) } -// -// The type of the component AttributeValue is determined by the AttributeType; -// in general it will be a DirectoryString. -WARN_UNUSED_RESULT bool ReadRdn(der::Parser* parser, - std::vector<AttributeTypeAndValue>* out) { - while (parser->HasMore()) { - der::Parser attr_type_and_value; - if (!parser->ReadSequence(&attr_type_and_value)) - return false; - // Read the attribute type, which must be an OBJECT IDENTIFIER. - der::Input type; - if (!attr_type_and_value.ReadTag(der::kOid, &type)) - return false; - - // Read the attribute value. - der::Tag tag; - der::Input value; - if (!attr_type_and_value.ReadTagAndValue(&tag, &value)) - return false; - - // There should be no more elements in the sequence after reading the - // attribute type and value. - if (attr_type_and_value.HasMore()) - return false; - - out->push_back(AttributeTypeAndValue(type, tag, value)); - } - return true; + return a.value == b.value; } // Verifies that |a_parser| and |b_parser| are the same length and that every // AttributeTypeAndValue in |a_parser| has a matching AttributeTypeAndValue in // |b_parser|. bool VerifyRdnMatch(der::Parser* a_parser, der::Parser* b_parser) { - std::vector<AttributeTypeAndValue> a_type_and_values, b_type_and_values; + std::vector<X509NameAttribute> a_type_and_values, b_type_and_values; if (!ReadRdn(a_parser, &a_type_and_values) || !ReadRdn(b_parser, &b_type_and_values)) return false; - // RFC 5280 section 4.1.2.4 - // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue - if (a_type_and_values.empty() || b_type_and_values.empty()) - return false; - // RFC 5280 section 7.1: // Two relative distinguished names RDN1 and RDN2 match if they have the same // number of naming attributes and for each naming attribute in RDN1 there is @@ -377,12 +194,10 @@ // small, a naive linear search for each element should be fine. (Hostile // certificates already have ways to provoke pathological behavior.) for (const auto& a : a_type_and_values) { - std::vector<AttributeTypeAndValue>::iterator b_iter = - b_type_and_values.begin(); + std::vector<X509NameAttribute>::iterator b_iter = b_type_and_values.begin(); for (; b_iter != b_type_and_values.end(); ++b_iter) { const auto& b = *b_iter; - if (a.type == b.type && - VerifyValueMatch(a.value_tag, a.value, b.value_tag, b.value)) { + if (a.type == b.type && VerifyValueMatch(a, b)) { break; } } @@ -487,7 +302,7 @@ if (!rdn_sequence_parser.ReadConstructed(der::kSet, &rdn_parser)) return false; - std::vector<AttributeTypeAndValue> type_and_values; + std::vector<X509NameAttribute> type_and_values; if (!ReadRdn(&rdn_parser, &type_and_values)) return false;
diff --git a/net/cert/internal/verify_signed_data_unittest.cc b/net/cert/internal/verify_signed_data_unittest.cc index 7cb519db1..239aaae 100644 --- a/net/cert/internal/verify_signed_data_unittest.cc +++ b/net/cert/internal/verify_signed_data_unittest.cc
@@ -175,9 +175,7 @@ } TEST(VerifySignedDataTest, RsaPkcs1Sha1KeyParamsAbsent) { - // TODO(eroman): This should fail! (key algoritm parsing is too permissive) - // See https://crbug.com/522228 - RunTestCase(SUCCESS, "rsa-pkcs1-sha1-key-params-absent.pem"); + RunTestCase(FAILURE, "rsa-pkcs1-sha1-key-params-absent.pem"); } TEST(VerifySignedDataTest, RsaPssSha1Salt20UsingPssKeyNoParams) { @@ -215,9 +213,7 @@ } TEST(VerifySignedDataTest, RsaPkcs1Sha256SpkiNonNullParams) { - // TODO(eroman): This should fail! (shouldn't recognize bogus params in rsa - // SPKI). See https://crbug.com/522228 - RunTestCase(SUCCESS, "rsa-pkcs1-sha256-spki-non-null-params.pem"); + RunTestCase(FAILURE, "rsa-pkcs1-sha256-spki-non-null-params.pem"); } TEST(VerifySignedDataTest, EcdsaPrime256v1Sha512UnusedBitsSignature) {
diff --git a/net/cert/mock_client_cert_verifier.cc b/net/cert/mock_client_cert_verifier.cc new file mode 100644 index 0000000..96e3a0a --- /dev/null +++ b/net/cert/mock_client_cert_verifier.cc
@@ -0,0 +1,40 @@ +// Copyright (c) 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. + +#include "net/cert/mock_client_cert_verifier.h" + +#include "net/base/net_errors.h" +#include "net/cert/x509_certificate.h" + +namespace net { + +struct MockClientCertVerifier::Rule { + Rule(X509Certificate* cert, int rv) : cert(cert), rv(rv) { DCHECK(cert); } + + scoped_refptr<X509Certificate> cert; + int rv; +}; + +MockClientCertVerifier::MockClientCertVerifier() + : default_result_(ERR_CERT_INVALID) {} + +MockClientCertVerifier::~MockClientCertVerifier() {} + +int MockClientCertVerifier::Verify(X509Certificate* cert, + const CompletionCallback& callback, + scoped_ptr<Request>* out_req) { + for (const Rule& rule : rules_) { + // Check just the server cert. Intermediates will be ignored. + if (rule.cert->Equals(cert)) + return rule.rv; + } + return default_result_; +} + +void MockClientCertVerifier::AddResultForCert(X509Certificate* cert, int rv) { + Rule rule(cert, rv); + rules_.push_back(rule); +} + +} // namespace net
diff --git a/net/cert/mock_client_cert_verifier.h b/net/cert/mock_client_cert_verifier.h new file mode 100644 index 0000000..455df12 --- /dev/null +++ b/net/cert/mock_client_cert_verifier.h
@@ -0,0 +1,52 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_CERT_MOCK_CLIENT_CERT_VERIFIER_H_ +#define NET_CERT_MOCK_CLIENT_CERT_VERIFIER_H_ + +#include <list> + +#include "net/cert/client_cert_verifier.h" + +namespace net { + +class MockClientCertVerifier : public ClientCertVerifier { + public: + // Creates a new MockClientCertVerifier. By default, any call to Verify() will + // result in the cert status being flagged as CERT_STATUS_INVALID and return + // an ERR_CERT_INVALID network error code. This behaviour can be overridden + // by calling set_default_result() to change the default return value for + // Verify() or by calling one of the AddResult*() methods to specifically + // handle a certificate or certificate and host. + MockClientCertVerifier(); + + ~MockClientCertVerifier() override; + + // ClientCertVerifier implementation + int Verify(X509Certificate* cert, + const CompletionCallback& callback, + scoped_ptr<Request>* out_req) override; + + // Sets the default return value for Verify() for certificates/hosts that do + // not have explicit results added via the AddResult*() methods. + void set_default_result(int default_result) { + default_result_ = default_result; + } + + // Adds a rule that will cause any call to Verify() for |cert| to return rv. + // Note: Only the primary certificate of |cert| is checked. Any intermediate + // certificates will be ignored. + void AddResultForCert(X509Certificate* cert, int rv); + + private: + struct Rule; + typedef std::list<Rule> RuleList; + + int default_result_; + RuleList rules_; +}; + +} // namespace net + +#endif // NET_CERT_MOCK_CLIENT_CERT_VERIFIER_H_
diff --git a/net/cert/x509_util_openssl.cc b/net/cert/x509_util_openssl.cc index c81a3a49b..5b6779a 100644 --- a/net/cert/x509_util_openssl.cc +++ b/net/cert/x509_util_openssl.cc
@@ -35,7 +35,6 @@ using ScopedASN1_TIME = crypto::ScopedOpenSSL<ASN1_TIME, ASN1_TIME_free>; using ScopedX509_EXTENSION = crypto::ScopedOpenSSL<X509_EXTENSION, X509_EXTENSION_free>; -using ScopedX509_NAME = crypto::ScopedOpenSSL<X509_NAME, X509_NAME_free>; const EVP_MD* ToEVP(x509_util::DigestAlgorithm alg) { switch (alg) {
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc index 9c507ea..db0bd002 100644 --- a/net/cookies/cookie_monster.cc +++ b/net/cookies/cookie_monster.cc
@@ -111,11 +111,10 @@ const size_t CookieMonster::kMaxCookies = 3300; const size_t CookieMonster::kPurgeCookies = 300; -const size_t CookieMonster::kDomainCookiesQuotaLow = 30; -const size_t CookieMonster::kDomainCookiesQuotaMedium = 50; -const size_t CookieMonster::kDomainCookiesQuotaHigh = - kDomainMaxCookies - kDomainPurgeCookies - kDomainCookiesQuotaLow - - kDomainCookiesQuotaMedium; +const double CookieMonster::kDomainCookiesQuotaLow = 0.2; +const double CookieMonster::kDomainCookiesQuotaMedium = 0.333333333333333333333; +const double CookieMonster::kDomainCookiesQuotaHigh = + 1 - kDomainCookiesQuotaLow - kDomainCookiesQuotaMedium; const int CookieMonster::kSafeFromGlobalPurgeDays = 30; @@ -250,19 +249,6 @@ } } -// Predicate to support PartitionCookieByPriority(). -struct CookiePriorityEqualsTo - : std::unary_function<const CookieMonster::CookieMap::iterator, bool> { - explicit CookiePriorityEqualsTo(CookiePriority priority) - : priority_(priority) {} - - bool operator()(const CookieMonster::CookieMap::iterator it) const { - return it->second->Priority() == priority_; - } - - const CookiePriority priority_; -}; - // For a CookieItVector iterator range [|it_begin|, |it_end|), // moves all cookies with a given |priority| to the beginning of the list. // Returns: An iterator in [it_begin, it_end) to the first element with @@ -271,7 +257,11 @@ CookieMonster::CookieItVector::iterator it_begin, CookieMonster::CookieItVector::iterator it_end, CookiePriority priority) { - return std::partition(it_begin, it_end, CookiePriorityEqualsTo(priority)); + return std::partition( + it_begin, it_end, + [priority](const CookieMonster::CookieMap::iterator& it) { + return it->second->Priority() == priority; + }); } bool LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it, @@ -1940,77 +1930,113 @@ if (cookies_.count(key) > kDomainMaxCookies) { VLOG(kVlogGarbageCollection) << "GarbageCollect() key: " << key; - CookieItVector* cookie_its; + CookieItVector cookie_its; - CookieItVector non_expired_cookie_its; - cookie_its = &non_expired_cookie_its; + // First, we purge all expired cookies for this key (regardless of our + // target number of cookies, as we have no need to keep expired cookies + // around). num_deleted += - GarbageCollectExpired(current, cookies_.equal_range(key), cookie_its); + GarbageCollectExpired(current, cookies_.equal_range(key), &cookie_its); - CookieItVector secure_cookie_its; - if (enforce_strict_secure && cookie_its->size() > kDomainMaxCookies) { - VLOG(kVlogGarbageCollection) << "Garbage collecting non-Secure cookies."; - num_deleted += - GarbageCollectNonSecure(non_expired_cookie_its, &secure_cookie_its); - cookie_its = &secure_cookie_its; - } + if (cookie_its.size() > kDomainMaxCookies) { + // Then, if we're over the maximum allowed for this key, we'll remove + // cookies in the following order: + // + // 1. Low-priority non-secure cookies. + // 2. Medium-priority non-secure cookies. + // 3. High-priority non-secure cookies. + // 4. Low-priority secure cookies. + // 5. Medium-priority secure cookies. + // 6. High-priority secure cookies. + // + // Note that this implies that _all_ non-secure cookies will be removed + // before _any_ secure cookie is removed, in accordance with + // https://tools.ietf.org/html/draft-west-leave-secure-cookies-alone - if (cookie_its->size() > kDomainMaxCookies) { VLOG(kVlogGarbageCollection) << "Deep Garbage Collect domain."; size_t purge_goal = - cookie_its->size() - (kDomainMaxCookies - kDomainPurgeCookies); + cookie_its.size() - (kDomainMaxCookies - kDomainPurgeCookies); DCHECK(purge_goal > kDomainPurgeCookies); - // Boundary iterators into |cookie_its| for different priorities. - CookieItVector::iterator it_bdd[4]; - // Intialize |it_bdd| while sorting |cookie_its| by priorities. - // Schematic: [MLLHMHHLMM] => [LLL|MMMM|HHH], with 4 boundaries. - it_bdd[0] = cookie_its->begin(); - it_bdd[3] = cookie_its->end(); - it_bdd[1] = - PartitionCookieByPriority(it_bdd[0], it_bdd[3], COOKIE_PRIORITY_LOW); - it_bdd[2] = PartitionCookieByPriority(it_bdd[1], it_bdd[3], - COOKIE_PRIORITY_MEDIUM); - size_t quota[3] = {kDomainCookiesQuotaLow, - kDomainCookiesQuotaMedium, - kDomainCookiesQuotaHigh}; + // To execute the above removals, we'll divide |cookies_its| into 6 + // partitions, using 7 boundaries (note that the last boundary is off the + // end of the list): + // + // If both non-secure and secure cookies are present, the list will look + // like this: + // + // LLLLMMMMHHHHHLLLLMMMMHHHH + // ^ ^ ^ ^ ^ ^ ^ + // 0 1 2 3 4 5 6 + // + // If only secure cookies are present, the list will look like this: + // + // LLLLMMMMHHHHH + // ^ ^ ^ ^ + // 0 4 5 6 + // 1 + // 2 + // 3 + // + // If only non-secure cookies are present, the list will look like this: + // + // LLLLMMMMHHHHH + // ^ ^ ^ ^ + // 0 1 2 3 + // 4 + // 5 + // 6 + CookieItVector::iterator it_bdd[7]; + it_bdd[0] = cookie_its.begin(); + it_bdd[1] = it_bdd[0]; + it_bdd[2] = it_bdd[0]; + it_bdd[3] = cookie_its.end(); + it_bdd[4] = it_bdd[3]; + it_bdd[5] = it_bdd[3]; + it_bdd[6] = it_bdd[3]; - // Purge domain cookies in 3 rounds. - // Round 1: consider low-priority cookies only: evict least-recently - // accessed, while protecting quota[0] of these from deletion. - // Round 2: consider {low, medium}-priority cookies, evict least-recently - // accessed, while protecting quota[0] + quota[1]. - // Round 3: consider all cookies, evict least-recently accessed. - size_t accumulated_quota = 0; - CookieItVector::iterator it_purge_begin = it_bdd[0]; - for (int i = 0; i < 3 && purge_goal > 0; ++i) { - accumulated_quota += quota[i]; + size_t num_nonsecure = 0; - size_t num_considered = it_bdd[i + 1] - it_purge_begin; - if (num_considered <= accumulated_quota) - continue; + // Move all non-secure cookies to the front of the list, and set boundary + // #3 to the first secure cookie (or off the end of the list, in the case + // where no secure cookies are present). + it_bdd[3] = std::partition(it_bdd[0], it_bdd[6], + [](const CookieMap::iterator& it) { + return !it->second->IsSecure(); + }); - // Number of cookies that will be purged in this round. - size_t round_goal = - std::min(purge_goal, num_considered - accumulated_quota); - purge_goal -= round_goal; - - SortLeastRecentlyAccessed(it_purge_begin, it_bdd[i + 1], round_goal); - // Cookies accessed on or after |safe_date| would have been safe from - // global purge, and we want to keep track of this. - CookieItVector::iterator it_purge_end = it_purge_begin + round_goal; - CookieItVector::iterator it_purge_middle = - LowerBoundAccessDate(it_purge_begin, it_purge_end, safe_date); - // Delete cookies accessed before |safe_date|. - num_deleted += GarbageCollectDeleteRange( - current, DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, it_purge_begin, - it_purge_middle); - // Delete cookies accessed on or after |safe_date|. - num_deleted += GarbageCollectDeleteRange( - current, DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, it_purge_middle, - it_purge_end); - it_purge_begin = it_purge_end; + // If we have non-secure cookies, partition them into priorities: + if (it_bdd[3] > it_bdd[0]) { + it_bdd[1] = PartitionCookieByPriority(it_bdd[0], it_bdd[3], + COOKIE_PRIORITY_LOW); + it_bdd[2] = PartitionCookieByPriority(it_bdd[1], it_bdd[3], + COOKIE_PRIORITY_MEDIUM); + num_nonsecure = it_bdd[3] - it_bdd[0]; } + + // Likewise, if we have secure cookies, partition them into priorities: + if (it_bdd[3] < it_bdd[6]) { + it_bdd[4] = PartitionCookieByPriority(it_bdd[3], it_bdd[6], + COOKIE_PRIORITY_LOW); + it_bdd[5] = PartitionCookieByPriority(it_bdd[4], it_bdd[6], + COOKIE_PRIORITY_MEDIUM); + } + + // Start with the non-secure cookies. + if (purge_goal >= num_nonsecure) { + // If we need to purge more cookies than we have non-secure, remove + // them all, update |purge_goal| then purge the new |purge_goal| from + // the secure cookies. + num_deleted += GarbageCollectDeleteRange( + current, DELETE_COOKIE_NON_SECURE, it_bdd[0], it_bdd[3]); + num_deleted += GarbageCollectNumFromRangeWithQuota( + current, safe_date, purge_goal - num_deleted, it_bdd, GC_SECURE); + } else { + num_deleted += GarbageCollectNumFromRangeWithQuota( + current, safe_date, purge_goal, it_bdd, GC_NONSECURE); + } + purge_goal -= num_deleted; + DCHECK_EQ(0U, purge_goal); } } @@ -2058,6 +2084,70 @@ return num_deleted; } +size_t CookieMonster::GarbageCollectNumFromRangeWithQuota( + const Time& current, + const Time& safe_date, + size_t purge_goal, + CookieItVector::iterator* it_bdd, + GCType type) { + size_t num_deleted = 0; + size_t begin_partition = type == GC_SECURE ? 3 : 0; + size_t num_cookies = it_bdd[begin_partition + 3] - it_bdd[begin_partition]; + size_t num_to_keep = num_cookies - purge_goal; + size_t quota[3] = {std::floor(num_to_keep * kDomainCookiesQuotaLow), + std::floor(num_to_keep * kDomainCookiesQuotaMedium), + std::floor(num_to_keep * kDomainCookiesQuotaHigh)}; + + // The quota calculation will be up to 3 fewer than the number of + // cookies we can keep. Bump up the numbers to get the right answer: + if (quota[0] + quota[1] + quota[2] < num_to_keep) + quota[2] += 1; + if (quota[0] + quota[1] + quota[2] < num_to_keep) + quota[1] += 1; + if (quota[0] + quota[1] + quota[2] < num_to_keep) + quota[0] += 1; + DCHECK_EQ(num_to_keep, quota[0] + quota[1] + quota[2]); + + // Purge domain cookies in 3 rounds. + // Round 1: consider low-priority cookies only: evict least-recently + // accessed, while protecting quota[0] of these from deletion. + // Round 2: consider {low, medium}-priority cookies, evict least-recently + // accessed, while protecting quota[0] + quota[1]. + // Round 3: consider all cookies, evict least-recently accessed. + size_t accumulated_quota = 0; + CookieItVector::iterator it_purge_begin = it_bdd[begin_partition]; + for (size_t i = 0; i < arraysize(quota) && purge_goal > 0; ++i) { + accumulated_quota += quota[i]; + + size_t num_considered = it_bdd[i + begin_partition + 1] - it_purge_begin; + if (num_considered <= accumulated_quota) + continue; + + // Number of cookies that will be purged in this round. + size_t round_goal = + std::min(purge_goal, num_considered - accumulated_quota); + purge_goal -= round_goal; + + SortLeastRecentlyAccessed(it_purge_begin, it_bdd[i + begin_partition + 1], + round_goal); + // Cookies accessed on or after |safe_date| would have been safe from + // global purge, and we want to keep track of this. + CookieItVector::iterator it_purge_end = it_purge_begin + round_goal; + CookieItVector::iterator it_purge_middle = + LowerBoundAccessDate(it_purge_begin, it_purge_end, safe_date); + // Delete cookies accessed before |safe_date|. + num_deleted += GarbageCollectDeleteRange( + current, DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, it_purge_begin, + it_purge_middle); + // Delete cookies accessed on or after |safe_date|. + num_deleted += GarbageCollectDeleteRange( + current, DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, it_purge_middle, + it_purge_end); + it_purge_begin = it_purge_end; + } + return num_deleted; +} + size_t CookieMonster::GarbageCollectExpired(const Time& current, const CookieMapItPair& itpair, CookieItVector* cookie_its) {
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h index 0ad3c94..f482e5c 100644 --- a/net/cookies/cookie_monster.h +++ b/net/cookies/cookie_monster.h
@@ -129,9 +129,11 @@ static const size_t kPurgeCookies; // Quota for cookies with {low, medium, high} priorities within a domain. - static const size_t kDomainCookiesQuotaLow; - static const size_t kDomainCookiesQuotaMedium; - static const size_t kDomainCookiesQuotaHigh; + // The quota is specified as a percentage of the total number of cookies we + // intend to retain for a domain. + static const double kDomainCookiesQuotaLow; + static const double kDomainCookiesQuotaMedium; + static const double kDomainCookiesQuotaHigh; // The store passed in should not have had Init() called on it yet. This // class will take care of initializing it. The backing store is NOT owned by @@ -375,6 +377,8 @@ COOKIE_DELETE_EQUIVALENT_LAST_ENTRY }; + enum GCType { GC_NONSECURE, GC_SECURE }; + // The strategy for fetching cookies. Controlled by Finch experiment. enum FetchStrategy { // Fetches all cookies only when they're needed. @@ -591,6 +595,27 @@ size_t purge_goal, CookieItVector cookie_its); + // Helper for GarbageCollect(). Deletes |purge_goal| cookies of type |type| + // from the ranges specified in |it_bdd|. Returns the number of cookies + // deleted. + // + // |it_bdd| is a bit of a complicated beast: it must be a 7-element array + // of 'CookieItVector::Iterator' objects that demarcate the boundaries of + // a list of cookies sorted first by secure/non-secure, and then by priority, + // low to high. That is, it ought to look something like: + // + // LLLLMMMMHHHHHLLLLMMMMHHHH + // ^ ^ ^ ^ ^ ^ ^ + // 0 1 2 3 4 5 6 + // + // TODO(mkwst): This is super-complicated. We should determine whether we + // can simplify our implementation of "priority". + size_t GarbageCollectNumFromRangeWithQuota(const base::Time& current, + const base::Time& safe_date, + size_t purge_goal, + CookieItVector::iterator* it_bdd, + GCType type); + // Find the key (for lookup in cookies_) based on the given domain. // See comment on keys before the CookieMap typedef. std::string GetKey(const std::string& domain) const;
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc index 5a48599..ecba7ea 100644 --- a/net/cookies/cookie_monster_unittest.cc +++ b/net/cookies/cookie_monster_unittest.cc
@@ -353,11 +353,12 @@ // Instantiates a CookieMonster, adds multiple cookies (to http_www_google_) // with priorities specified by |coded_priority_str|, and tests priority-aware // domain cookie eviction. - // |coded_priority_str| specifies a run-length-encoded string of priorities. - // Example: "2M 3L M 4H" means "MMLLLMHHHH", and speicifies sequential (i.e., - // from least- to most-recently accessed) insertion of 2 medium-priority - // cookies, 3 low-priority cookies, 1 medium-priority cookie, and 4 - // high-priority cookies. + // + // Example: |coded_priority_string| of "2MN 3LS MN 4HN" specifies sequential + // (i.e., from least- to most-recently accessed) insertion of 2 + // medium-priority non-secure cookies, 3 low-priority secure cookies, 1 + // medium-priority non-secure cookie, and 4 high-priority non-secure cookies. + // // Within each priority, only the least-accessed cookies should be evicted. // Thus, to describe expected suriving cookies, it suffices to specify the // expected population of surviving cookies per priority, i.e., @@ -366,41 +367,55 @@ const std::string& coded_priority_str, size_t expected_low_count, size_t expected_medium_count, - size_t expected_high_count) { + size_t expected_high_count, + size_t expected_nonsecure, + size_t expected_secure) { + SCOPED_TRACE(coded_priority_str.c_str()); this->DeleteAll(cm); int next_cookie_id = 0; - std::vector<CookiePriority> priority_list; - std::vector<int> id_list[3]; // Indexed by CookiePriority. + // A list of cookie IDs, indexed by secure status, then by priority. + std::vector<int> id_list[2][3]; + // A list of all the cookies stored, along with their properties. + std::vector<std::pair<bool, CookiePriority>> cookie_data; // Parse |coded_priority_str| and add cookies. for (const std::string& token : base::SplitString(coded_priority_str, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { DCHECK(!token.empty()); - // Take last character as priority. - CookiePriority priority = CharToPriority(token.back()); - std::string priority_str = CookiePriorityToString(priority); + + // Take last character as security status, then discard it. + bool is_secure = token[token.size() - 1] == 'S'; + + // The second-to-last character is the priority. Grab and discard it. + CookiePriority priority = CharToPriority(token[token.size() - 2]); + // The rest of the string (possibly empty) specifies repetition. int rep = 1; if (!token.empty()) { bool result = base::StringToInt( - base::StringPiece(token.begin(), token.end() - 1), &rep); + base::StringPiece(token.begin(), token.end() - 2), &rep); DCHECK(result); } for (; rep > 0; --rep, ++next_cookie_id) { - std::string cookie = base::StringPrintf( - "a%d=b;priority=%s", next_cookie_id, priority_str.c_str()); - EXPECT_TRUE(SetCookie(cm, http_www_google_.url(), cookie)); - priority_list.push_back(priority); - id_list[priority].push_back(next_cookie_id); + std::string cookie = + base::StringPrintf("a%d=b;priority=%s;%s", next_cookie_id, + CookiePriorityToString(priority).c_str(), + is_secure ? "secure" : ""); + EXPECT_TRUE(SetCookie(cm, https_www_google_.url(), cookie)); + cookie_data.push_back(std::make_pair(is_secure, priority)); + id_list[is_secure][priority].push_back(next_cookie_id); } } - int num_cookies = static_cast<int>(priority_list.size()); - std::vector<int> surviving_id_list[3]; // Indexed by CookiePriority. + int num_cookies = static_cast<int>(cookie_data.size()); + // A list of cookie IDs, indexed by secure status, then by priority. + std::vector<int> surviving_id_list[2][3]; // Parse the list of cookies - std::string cookie_str = this->GetCookies(cm, http_www_google_.url()); + std::string cookie_str = this->GetCookies(cm, https_www_google_.url()); + size_t num_nonsecure = 0; + size_t num_secure = 0; for (const std::string& token : base::SplitString( cookie_str, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { // Assuming *it is "a#=b", so extract and parse "#" portion. @@ -410,22 +425,40 @@ DCHECK(result); DCHECK_GE(id, 0); DCHECK_LT(id, num_cookies); - surviving_id_list[priority_list[id]].push_back(id); + surviving_id_list[cookie_data[id].first][cookie_data[id].second] + .push_back(id); + if (cookie_data[id].first) + num_secure += 1; + else + num_nonsecure += 1; } + EXPECT_EQ(expected_nonsecure, num_nonsecure); + EXPECT_EQ(expected_secure, num_secure); + // Validate each priority. size_t expected_count[3] = { expected_low_count, expected_medium_count, expected_high_count}; for (int i = 0; i < 3; ++i) { - DCHECK_LE(surviving_id_list[i].size(), id_list[i].size()); - EXPECT_EQ(expected_count[i], surviving_id_list[i].size()); + size_t num_for_priority = + surviving_id_list[0][i].size() + surviving_id_list[1][i].size(); + EXPECT_EQ(expected_count[i], num_for_priority); // Verify that the remaining cookies are the most recent among those // with the same priorities. - if (expected_count[i] == surviving_id_list[i].size()) { - std::sort(surviving_id_list[i].begin(), surviving_id_list[i].end()); - EXPECT_TRUE(std::equal(surviving_id_list[i].begin(), - surviving_id_list[i].end(), - id_list[i].end() - expected_count[i])); + if (expected_count[i] == num_for_priority) { + // Non-secure: + std::sort(surviving_id_list[0][i].begin(), + surviving_id_list[0][i].end()); + EXPECT_TRUE(std::equal( + surviving_id_list[0][i].begin(), surviving_id_list[0][i].end(), + id_list[0][i].end() - surviving_id_list[0][i].size())); + + // Secure: + std::sort(surviving_id_list[1][i].begin(), + surviving_id_list[1][i].end()); + EXPECT_TRUE(std::equal( + surviving_id_list[1][i].begin(), surviving_id_list[1][i].end(), + id_list[1][i].end() - surviving_id_list[1][i].size())); } } } @@ -492,54 +525,158 @@ EXPECT_EQ(expected_non_secure_cookies, total_non_secure_cookies); } - void TestPriorityAwareGarbageCollectHelper() { + void TestPriorityAwareGarbageCollectHelperNonSecure() { // Hard-coding limits in the test, but use DCHECK_EQ to enforce constraint. DCHECK_EQ(180U, CookieMonster::kDomainMaxCookies); DCHECK_EQ(150U, CookieMonster::kDomainMaxCookies - CookieMonster::kDomainPurgeCookies); - DCHECK_EQ(30U, CookieMonster::kDomainCookiesQuotaLow); - DCHECK_EQ(50U, CookieMonster::kDomainCookiesQuotaMedium); - DCHECK_EQ(70U, CookieMonster::kDomainCookiesQuotaHigh); scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); // Each test case adds 181 cookies, so 31 cookies are evicted. // Cookie same priority, repeated for each priority. - TestPriorityCookieCase(cm.get(), "181L", 150U, 0U, 0U); - TestPriorityCookieCase(cm.get(), "181M", 0U, 150U, 0U); - TestPriorityCookieCase(cm.get(), "181H", 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "181LN", 150U, 0U, 0U, 150U, 0U); + TestPriorityCookieCase(cm.get(), "181MN", 0U, 150U, 0U, 150U, 0U); + TestPriorityCookieCase(cm.get(), "181HN", 0U, 0U, 150U, 150U, 0U); // Pairwise scenarios. // Round 1 => none; round2 => 31M; round 3 => none. - TestPriorityCookieCase(cm.get(), "10H 171M", 0U, 140U, 10U); + TestPriorityCookieCase(cm.get(), "10HN 171MN", 0U, 140U, 10U, 150U, 0U); // Round 1 => 10L; round2 => 21M; round 3 => none. - TestPriorityCookieCase(cm.get(), "141M 40L", 30U, 120U, 0U); + TestPriorityCookieCase(cm.get(), "141MN 40LN", 30U, 120U, 0U, 150U, 0U); // Round 1 => none; round2 => none; round 3 => 31H. - TestPriorityCookieCase(cm.get(), "101H 80M", 0U, 80U, 70U); + TestPriorityCookieCase(cm.get(), "101HN 80MN", 0U, 80U, 70U, 150U, 0U); // For {low, medium} priorities right on quota, different orders. // Round 1 => 1L; round 2 => none, round3 => 30L. - TestPriorityCookieCase(cm.get(), "31L 50M 100H", 0U, 50U, 100U); + TestPriorityCookieCase(cm.get(), "31LN 50MN 100HN", 0U, 50U, 100U, 150U, + 0U); // Round 1 => none; round 2 => 1M, round3 => 30M. - TestPriorityCookieCase(cm.get(), "51M 100H 30L", 30U, 20U, 100U); + TestPriorityCookieCase(cm.get(), "51MN 100HN 30LN", 30U, 20U, 100U, 150U, + 0U); // Round 1 => none; round 2 => none; round3 => 31H. - TestPriorityCookieCase(cm.get(), "101H 50M 30L", 30U, 50U, 70U); + TestPriorityCookieCase(cm.get(), "101HN 50MN 30LN", 30U, 50U, 70U, 150U, + 0U); // Round 1 => 10L; round 2 => 10M; round3 => 11H. - TestPriorityCookieCase(cm.get(), "81H 60M 40L", 30U, 50U, 70U); + TestPriorityCookieCase(cm.get(), "81HN 60MN 40LN", 30U, 50U, 70U, 150U, 0U); // More complex scenarios. // Round 1 => 10L; round 2 => 10M; round 3 => 11H. - TestPriorityCookieCase(cm.get(), "21H 60M 40L 60H", 30U, 50U, 70U); + TestPriorityCookieCase(cm.get(), "21HN 60MN 40LN 60HN", 30U, 50U, 70U, 150U, + 0U); // Round 1 => 10L; round 2 => 11M, 10L; round 3 => none. - TestPriorityCookieCase(cm.get(), "11H 10M 20L 110M 20L 10H", 20U, 109U, - 21U); + TestPriorityCookieCase(cm.get(), "11HN 10MN 20LN 110MN 20LN 10HN", 20U, + 109U, 21U, 150U, 0U); // Round 1 => none; round 2 => none; round 3 => 11L, 10M, 10H. - TestPriorityCookieCase(cm.get(), "11L 10M 140H 10M 10L", 10U, 10U, 130U); + TestPriorityCookieCase(cm.get(), "11LN 10MN 140HN 10MN 10LN", 10U, 10U, + 130U, 150U, 0U); // Round 1 => none; round 2 => 1M; round 3 => 10L, 10M, 10H. - TestPriorityCookieCase(cm.get(), "11M 10H 10L 60M 90H", 0U, 60U, 90U); + TestPriorityCookieCase(cm.get(), "11MN 10HN 10LN 60MN 90HN", 0U, 60U, 90U, + 150U, 0U); // Round 1 => none; round 2 => 10L, 21M; round 3 => none. - TestPriorityCookieCase(cm.get(), "11M 10H 10L 90M 60H", 0U, 80U, 70U); + TestPriorityCookieCase(cm.get(), "11MN 10HN 10LN 90MN 60HN", 0U, 80U, 70U, + 150U, 0U); + } + + void TestPriorityAwareGarbageCollectHelperSecure() { + // Hard-coding limits in the test, but use DCHECK_EQ to enforce constraint. + DCHECK_EQ(180U, CookieMonster::kDomainMaxCookies); + DCHECK_EQ(150U, CookieMonster::kDomainMaxCookies - + CookieMonster::kDomainPurgeCookies); + + scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + + // Each test case adds 181 cookies, so 31 cookies are evicted. + // Cookie same priority, repeated for each priority. + TestPriorityCookieCase(cm.get(), "181LS", 150U, 0U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "181MS", 0U, 150U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "181HS", 0U, 0U, 150U, 0U, 150U); + + // Pairwise scenarios. + // Round 1 => none; round2 => 31M; round 3 => none. + TestPriorityCookieCase(cm.get(), "10HS 171MS", 0U, 140U, 10U, 0U, 150U); + // Round 1 => 10L; round2 => 21M; round 3 => none. + TestPriorityCookieCase(cm.get(), "141MS 40LS", 30U, 120U, 0U, 0U, 150U); + // Round 1 => none; round2 => none; round 3 => 31H. + TestPriorityCookieCase(cm.get(), "101HS 80MS", 0U, 80U, 70U, 0U, 150U); + + // For {low, medium} priorities right on quota, different orders. + // Round 1 => 1L; round 2 => none, round3 => 30L. + TestPriorityCookieCase(cm.get(), "31LS 50MS 100HS", 0U, 50U, 100U, 0U, + 150U); + // Round 1 => none; round 2 => 1M, round3 => 30M. + TestPriorityCookieCase(cm.get(), "51MS 100HS 30LS", 30U, 20U, 100U, 0U, + 150U); + // Round 1 => none; round 2 => none; round3 => 31H. + TestPriorityCookieCase(cm.get(), "101HS 50MS 30LS", 30U, 50U, 70U, 0U, + 150U); + + // Round 1 => 10L; round 2 => 10M; round3 => 11H. + TestPriorityCookieCase(cm.get(), "81HS 60MS 40LS", 30U, 50U, 70U, 0U, 150U); + + // More complex scenarios. + // Round 1 => 10L; round 2 => 10M; round 3 => 11H. + TestPriorityCookieCase(cm.get(), "21HS 60MS 40LS 60HS", 30U, 50U, 70U, 0U, + 150U); + // Round 1 => 10L; round 2 => 11M, 10L; round 3 => none. + TestPriorityCookieCase(cm.get(), "11HS 10MS 20LS 110MS 20LS 10HS", 20U, + 109U, 21U, 0U, 150U); + // Round 1 => none; round 2 => none; round 3 => 11L, 10M, 10H. + TestPriorityCookieCase(cm.get(), "11LS 10MS 140HS 10MS 10LS", 10U, 10U, + 130U, 0U, 150U); + // Round 1 => none; round 2 => 1M; round 3 => 10L, 10M, 10H. + TestPriorityCookieCase(cm.get(), "11MS 10HS 10LS 60MS 90HS", 0U, 60U, 90U, + 0U, 150U); + // Round 1 => none; round 2 => 10L, 21M; round 3 => none. + TestPriorityCookieCase(cm.get(), "11MS 10HS 10LS 90MS 60HS", 0U, 80U, 70U, + 0U, 150U); + } + + void TestPriorityAwareGarbageCollectHelperMixed() { + // Hard-coding limits in the test, but use DCHECK_EQ to enforce constraint. + DCHECK_EQ(180U, CookieMonster::kDomainMaxCookies); + DCHECK_EQ(150U, CookieMonster::kDomainMaxCookies - + CookieMonster::kDomainPurgeCookies); + + scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL)); + + // Each test case adds 180 secure cookies, and some non-secure cookie. The + // secure cookies take priority, so the non-secure cookie is removed, along + // with 30 secure cookies. Repeated for each priority, and with the + // non-secure cookie as older and newer. + TestPriorityCookieCase(cm.get(), "1LN 180LS", 150U, 0U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "1MN 180MS", 0U, 150U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "1HN 180HS", 0U, 0U, 150U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "180LS 1LN", 150U, 0U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "180MS 1MN", 0U, 150U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "180HS 1HN", 0U, 0U, 150U, 0U, 150U); + + // Higher-priority non-secure cookies are removed before any secure cookie. + TestPriorityCookieCase(cm.get(), "180LS 1MN", 150U, 0U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "180LS 1HN", 150U, 0U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "180MS 1HN", 0U, 150U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "1MN 180LS", 150U, 0U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "1HN 180LS", 150U, 0U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "1HN 180MS", 0U, 150U, 0U, 0U, 150U); + + // Pairwise: + TestPriorityCookieCase(cm.get(), "1LS 180LN", 150U, 0U, 0U, 149U, 1U); + TestPriorityCookieCase(cm.get(), "100LS 81LN", 150U, 0U, 0U, 50U, 100U); + TestPriorityCookieCase(cm.get(), "150LS 31LN", 150U, 0U, 0U, 0U, 150U); + TestPriorityCookieCase(cm.get(), "1LS 180HN", 1U, 0U, 149U, 149U, 1U); + TestPriorityCookieCase(cm.get(), "100LS 81HN", 100U, 0U, 50U, 50U, 100U); + TestPriorityCookieCase(cm.get(), "150LS 31HN", 150U, 0U, 0U, 0U, 150U); + + // Quota calculations inside non-secure/secure blocks remain in place: + TestPriorityCookieCase(cm.get(), "50HN 50LS 81HS", 50U, 0U, 100U, 19U, + 131U); + TestPriorityCookieCase(cm.get(), "11MS 10HN 10LS 90MN 60HN", 10U, 79U, 61U, + 129U, 21U); + + // Multiple GC rounds end up with consistent behavior: + TestPriorityCookieCase(cm.get(), "100HS 100LN 100MN", 0, 76U, 100U, 76U, + 100U); } // Function for creating a CM with a number of cookies in it, @@ -1248,8 +1385,16 @@ TestHostGarbageCollectHelper(); } -TEST_F(CookieMonsterTest, TestPriorityAwareGarbageCollection) { - TestPriorityAwareGarbageCollectHelper(); +TEST_F(CookieMonsterTest, TestPriorityAwareGarbageCollectionNonSecure) { + TestPriorityAwareGarbageCollectHelperNonSecure(); +} + +TEST_F(CookieMonsterTest, TestPriorityAwareGarbageCollectionSecure) { + TestPriorityAwareGarbageCollectHelperSecure(); +} + +TEST_F(CookieMonsterTest, TestPriorityAwareGarbageCollectionMixed) { + TestPriorityAwareGarbageCollectHelperMixed(); } TEST_F(CookieMonsterTest, SetCookieableSchemes) { @@ -2962,13 +3107,13 @@ // non-secure cookie will not evict them (and, in fact, the non-secure cookie // will be removed right after creation). const CookiesEntry test1[] = {{180U, true}, {1U, false}}; - TestSecureCookieEviction(test1, arraysize(test1), 180U, 0U, nullptr); + TestSecureCookieEviction(test1, arraysize(test1), 150U, 0U, nullptr); // If non-secure cookies for one domain hit the per domain limit (180), the - // creation of secure cookies will evict all of the non-secure cookies, and - // the secure cookies will still be created. + // creation of secure cookies will evict the non-secure cookies first, making + // room for the secure cookies. const CookiesEntry test2[] = {{180U, false}, {20U, true}}; - TestSecureCookieEviction(test2, arraysize(test2), 20U, 0U, nullptr); + TestSecureCookieEviction(test2, arraysize(test2), 20U, 149U, nullptr); // If secure cookies for one domain go past the per domain limit (180), they // will be evicted as normal by the per domain purge amount (30) down to a @@ -2980,9 +3125,9 @@ // If a non-secure cookie is created, and a number of secure cookies exceeds // the per domain limit (18), the total cookies will be evicted down to a // lower amount (150), enforcing the eviction of the non-secure cookie, and - // the remaining secure cookies will be created (another 18 to 168). + // the remaining secure cookies will be created (another 19 to 169). const CookiesEntry test4[] = {{1U, false}, {199U, true}}; - TestSecureCookieEviction(test4, arraysize(test4), 168U, 0U, nullptr); + TestSecureCookieEviction(test4, arraysize(test4), 169U, 0U, nullptr); // If an even number of non-secure and secure cookies are created below the // per-domain limit (180), all will be created and none evicted. @@ -2991,38 +3136,40 @@ // If the same number of secure and non-secure cookies are created (50 each) // below the per domain limit (180), and then another set of secure cookies - // are created to bring the total above the per-domain limit, all of the - // non-secure cookies will be evicted but none of the secure ones will be - // evicted. + // are created to bring the total above the per-domain limit, all secure + // cookies will be retained, and the non-secure cookies will be culled down + // to the limit. const CookiesEntry test6[] = {{50U, true}, {50U, false}, {81U, true}}; - TestSecureCookieEviction(test6, arraysize(test6), 131U, 0U, nullptr); + TestSecureCookieEviction(test6, arraysize(test6), 131U, 19U, nullptr); // If the same number of non-secure and secure cookies are created (50 each) // below the per domain limit (180), and then another set of non-secure - // cookies are created to bring the total above the per-domain limit, all of - // the non-secure cookies will be evicted but none of the secure ones will be - // evicted. + // cookies are created to bring the total above the per-domain limit, all + // secure cookies will be retained, and the non-secure cookies will be culled + // down to the limit. const CookiesEntry test7[] = {{50U, false}, {50U, true}, {81U, false}}; - TestSecureCookieEviction(test7, arraysize(test7), 50U, 0U, nullptr); + TestSecureCookieEviction(test7, arraysize(test7), 50U, 100U, nullptr); // If the same number of non-secure and secure cookies are created (50 each) // below the per domain limit (180), and then another set of non-secure - // cookies are created to bring the total above the per-domain limit, all of - // the non-secure cookies will be evicted but none of the secure ones will be - // evicted, and then the remaining non-secure cookies will be created (9). + // cookies are created to bring the total above the per-domain limit, all + // secure cookies will be retained, and the non-secure cookies will be culled + // down to the limit, then the remaining non-secure cookies will be created + // (9). const CookiesEntry test8[] = {{50U, false}, {50U, true}, {90U, false}}; - TestSecureCookieEviction(test8, arraysize(test8), 50U, 9U, nullptr); + TestSecureCookieEviction(test8, arraysize(test8), 50U, 109U, nullptr); // If a number of non-secure cookies are created on other hosts (20) and are // past the global 'safe' date, and then the number of non-secure cookies for // a single domain are brought to the per-domain limit (180), followed by - // another set of secure cookies on that same domain (20), all of the - // non-secure cookies for that domain should be evicted, but the non-secure - // cookies for other domains should remain, as should the secure cookies for - // that domain. + // another set of secure cookies on that same domain (20), all the secure + // cookies for that domain should be retained, while the non-secure should be + // culled down to the per-domain limit. The non-secure cookies for other + // domains should remain untouched. const CookiesEntry test9[] = {{180U, false}, {20U, true}}; const AltHosts test9_alt_hosts(0, 20); - TestSecureCookieEviction(test9, arraysize(test9), 20U, 20U, &test9_alt_hosts); + TestSecureCookieEviction(test9, arraysize(test9), 20U, 169U, + &test9_alt_hosts); // If a number of secure cookies are created on other hosts and hit the global // cookie limit (3300) and are past the global 'safe' date, and then a single
diff --git a/net/der/tag.cc b/net/der/tag.cc index 99acf8c..a824024 100644 --- a/net/der/tag.cc +++ b/net/der/tag.cc
@@ -20,18 +20,10 @@ return (base & kTagNumberMask) | kTagPrimitive | kTagContextSpecific; } -bool IsContextSpecific(Tag tag) { - return (tag & kTagClassMask) == kTagContextSpecific; -} - bool IsConstructed(Tag tag) { return (tag & kTagConstructionMask) == kTagConstructed; } -uint8_t GetTagNumber(Tag tag) { - return tag & kTagNumberMask; -} - } // namespace der } // namespace net
diff --git a/net/der/tag.h b/net/der/tag.h index cb18ac6a..a168df66 100644 --- a/net/der/tag.h +++ b/net/der/tag.h
@@ -31,6 +31,7 @@ const Tag kEnumerated = 0x0A; const Tag kUtf8String = 0x0C; const Tag kPrintableString = 0x13; +const Tag kTeletexString = 0x14; const Tag kIA5String = 0x16; const Tag kUtcTime = 0x17; const Tag kGeneralizedTime = 0x18; @@ -67,12 +68,8 @@ NET_EXPORT Tag ContextSpecificPrimitive(uint8_t base); -NET_EXPORT bool IsContextSpecific(Tag tag); - NET_EXPORT bool IsConstructed(Tag tag); -NET_EXPORT uint8_t GetTagNumber(Tag tag); - } // namespace der } // namespace net
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc index 39a965f..93750ffc 100644 --- a/net/dns/host_resolver_impl.cc +++ b/net/dns/host_resolver_impl.cc
@@ -41,7 +41,7 @@ #include "net/base/host_port_pair.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" -#include "net/base/net_util.h" +#include "net/base/url_util.h" #include "net/dns/address_sorter.h" #include "net/dns/dns_client.h" #include "net/dns/dns_config_service.h" @@ -490,6 +490,33 @@ //----------------------------------------------------------------------------- +bool ResolveLocalHostname(base::StringPiece host, + uint16_t port, + AddressList* address_list) { + static const unsigned char kLocalhostIPv4[] = {127, 0, 0, 1}; + static const unsigned char kLocalhostIPv6[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + + address_list->clear(); + + bool is_local6; + if (!IsLocalHostname(host, &is_local6)) + return false; + + address_list->push_back( + IPEndPoint(IPAddressNumber(kLocalhostIPv6, + kLocalhostIPv6 + arraysize(kLocalhostIPv6)), + port)); + if (!is_local6) { + address_list->push_back( + IPEndPoint(IPAddressNumber(kLocalhostIPv4, + kLocalhostIPv4 + arraysize(kLocalhostIPv4)), + port)); + } + + return true; +} + const unsigned HostResolverImpl::kMaximumDnsFailures = 16; // Holds the data for a request that could not be completed synchronously.
diff --git a/net/dns/host_resolver_impl.h b/net/dns/host_resolver_impl.h index 7c45499..245bde2 100644 --- a/net/dns/host_resolver_impl.h +++ b/net/dns/host_resolver_impl.h
@@ -13,6 +13,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/strings/string_piece.h" #include "base/threading/non_thread_safe.h" #include "base/time/time.h" #include "net/base/ip_address_number.h" @@ -24,6 +25,7 @@ namespace net { +class AddressList; class BoundNetLog; class DnsClient; class NetLog; @@ -307,6 +309,18 @@ DISALLOW_COPY_AND_ASSIGN(HostResolverImpl); }; +// Resolves a local hostname (such as "localhost" or "localhost6") into +// IP endpoints with the given port. Returns true if |host| is a local +// hostname and false otherwise. Special IPv6 names (e.g. "localhost6") +// will resolve to an IPv6 address only, whereas other names will +// resolve to both IPv4 and IPv6. +// This function is only exposed so it can be unit-tested. +// TODO(tfarina): It would be better to change the tests so this function +// gets exercised indirectly through HostResolverImpl. +NET_EXPORT_PRIVATE bool ResolveLocalHostname(base::StringPiece host, + uint16_t port, + AddressList* address_list); + } // namespace net #endif // NET_DNS_HOST_RESOLVER_IMPL_H_
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc index 569d1ab..c4e863c 100644 --- a/net/dns/host_resolver_impl_unittest.cc +++ b/net/dns/host_resolver_impl_unittest.cc
@@ -447,6 +447,48 @@ } }; +const unsigned char kLocalhostIPv4[] = {127, 0, 0, 1}; +const unsigned char kLocalhostIPv6[] = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; +const uint16_t kLocalhostLookupPort = 80; + +bool HasEndpoint(const IPEndPoint& endpoint, const AddressList& addresses) { + for (const auto& address : addresses) { + if (endpoint == address) + return true; + } + return false; +} + +void TestBothLoopbackIPs(const std::string& host) { + IPEndPoint localhost_ipv4( + IPAddressNumber(kLocalhostIPv4, + kLocalhostIPv4 + arraysize(kLocalhostIPv4)), + kLocalhostLookupPort); + IPEndPoint localhost_ipv6( + IPAddressNumber(kLocalhostIPv6, + kLocalhostIPv6 + arraysize(kLocalhostIPv6)), + kLocalhostLookupPort); + + AddressList addresses; + EXPECT_TRUE(ResolveLocalHostname(host, kLocalhostLookupPort, &addresses)); + EXPECT_EQ(2u, addresses.size()); + EXPECT_TRUE(HasEndpoint(localhost_ipv4, addresses)); + EXPECT_TRUE(HasEndpoint(localhost_ipv6, addresses)); +} + +void TestIPv6LoopbackOnly(const std::string& host) { + IPEndPoint localhost_ipv6( + IPAddressNumber(kLocalhostIPv6, + kLocalhostIPv6 + arraysize(kLocalhostIPv6)), + kLocalhostLookupPort); + + AddressList addresses; + EXPECT_TRUE(ResolveLocalHostname(host, kLocalhostLookupPort, &addresses)); + EXPECT_EQ(1u, addresses.size()); + EXPECT_TRUE(HasEndpoint(localhost_ipv6, addresses)); +} + } // namespace class HostResolverImplTest : public testing::Test { @@ -2178,4 +2220,62 @@ EXPECT_TRUE(requests_[2]->HasOneAddress("192.168.0.3", 80)); } +TEST_F(HostResolverImplTest, ResolveLocalHostname) { + AddressList addresses; + + TestBothLoopbackIPs("localhost"); + TestBothLoopbackIPs("localhoST"); + TestBothLoopbackIPs("localhost."); + TestBothLoopbackIPs("localhoST."); + TestBothLoopbackIPs("localhost.localdomain"); + TestBothLoopbackIPs("localhost.localdomAIn"); + TestBothLoopbackIPs("localhost.localdomain."); + TestBothLoopbackIPs("localhost.localdomAIn."); + TestBothLoopbackIPs("foo.localhost"); + TestBothLoopbackIPs("foo.localhOSt"); + TestBothLoopbackIPs("foo.localhost."); + TestBothLoopbackIPs("foo.localhOSt."); + + TestIPv6LoopbackOnly("localhost6"); + TestIPv6LoopbackOnly("localhoST6"); + TestIPv6LoopbackOnly("localhost6."); + TestIPv6LoopbackOnly("localhost6.localdomain6"); + TestIPv6LoopbackOnly("localhost6.localdomain6."); + + EXPECT_FALSE( + ResolveLocalHostname("127.0.0.1", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("::1", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("0:0:0:0:0:0:0:1", kLocalhostLookupPort, + &addresses)); + EXPECT_FALSE( + ResolveLocalHostname("localhostx", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE( + ResolveLocalHostname("localhost.x", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("foo.localdomain", kLocalhostLookupPort, + &addresses)); + EXPECT_FALSE(ResolveLocalHostname("foo.localdomain.x", kLocalhostLookupPort, + &addresses)); + EXPECT_FALSE( + ResolveLocalHostname("localhost6x", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("localhost.localdomain6", + kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("localhost6.localdomain", + kLocalhostLookupPort, &addresses)); + EXPECT_FALSE( + ResolveLocalHostname("127.0.0.1.1", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE( + ResolveLocalHostname(".127.0.0.255", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("::2", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("::1:1", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("0:0:0:0:1:0:0:1", kLocalhostLookupPort, + &addresses)); + EXPECT_FALSE(ResolveLocalHostname("::1:1", kLocalhostLookupPort, &addresses)); + EXPECT_FALSE(ResolveLocalHostname("0:0:0:0:0:0:0:0:1", kLocalhostLookupPort, + &addresses)); + EXPECT_FALSE(ResolveLocalHostname("foo.localhost.com", kLocalhostLookupPort, + &addresses)); + EXPECT_FALSE( + ResolveLocalHostname("foo.localhoste", kLocalhostLookupPort, &addresses)); +} + } // namespace net
diff --git a/net/http/transport_security_state_static.h b/net/http/transport_security_state_static.h index 0db4913..fa8664d 100644 --- a/net/http/transport_security_state_static.h +++ b/net/http/transport_security_state_static.h
@@ -741,5736 +741,5799 @@ // Otherwise it's a pointer to the n'th element of the array. static const uint8_t kHSTSHuffmanTree[] = { 0xf5, 0xe7, 0xf9, 0xe6, 0x01, 0xe4, 0x00, 0x02, 0xf2, 0xe9, 0x03, 0x04, - 0xb2, 0xb0, 0xa0, 0xb5, 0xb7, 0x07, 0xb8, 0x08, 0x09, 0xf1, 0x06, 0x0a, + 0xb0, 0xb2, 0xa0, 0xb5, 0x07, 0xb7, 0xb8, 0x08, 0x09, 0xf1, 0x06, 0x0a, 0x0b, 0xae, 0xf7, 0x0c, 0xed, 0x0d, 0x0e, 0xef, 0x80, 0x0f, 0x05, 0x10, 0xb9, 0xb6, 0x12, 0xb3, 0xb1, 0xb4, 0x13, 0x14, 0x15, 0xf8, 0xad, 0x16, - 0xeb, 0x17, 0xe3, 0x18, 0x19, 0xe1, 0x1a, 0xff, 0xfa, 0xea, 0x1c, 0xf6, + 0xeb, 0x17, 0xe3, 0x18, 0xe1, 0x19, 0x1a, 0xff, 0xfa, 0xea, 0x1c, 0xf6, 0xe2, 0x1d, 0xec, 0x1e, 0xe5, 0x1f, 0xf3, 0xf4, 0xe8, 0xf0, 0xee, 0x22, 0x21, 0x23, 0x20, 0x24, 0x1b, 0x25, 0x11, 0x26, }; static const uint8_t kPreloadedHSTSData[] = { - 0xff, 0xff, 0xff, 0xcc, 0x78, 0x37, 0xd4, 0xf7, 0xd9, 0xe3, 0xef, 0x9e, - 0x0d, 0xf3, 0xcf, 0xf7, 0xfd, 0x29, 0x67, 0x16, 0x5f, 0xb4, 0x17, 0x70, - 0xaa, 0x22, 0x55, 0xef, 0x1e, 0x96, 0x59, 0xbe, 0x1e, 0x84, 0x46, 0x97, - 0x36, 0x45, 0x59, 0x77, 0xe0, 0x59, 0x7a, 0x05, 0xba, 0xb2, 0xef, 0xba, - 0xb2, 0xff, 0xfa, 0x7d, 0x30, 0x27, 0x17, 0xe8, 0x4e, 0x75, 0x65, 0xff, + 0xff, 0xff, 0xff, 0xcc, 0x79, 0x37, 0xd4, 0x77, 0xd9, 0xe3, 0xef, 0x9e, + 0x4d, 0xf3, 0xcf, 0xf7, 0xfd, 0x28, 0x67, 0x16, 0x5f, 0xb4, 0x17, 0x70, + 0xaa, 0x22, 0x55, 0xef, 0x1e, 0x96, 0x59, 0xbe, 0x1e, 0x84, 0xc6, 0x97, + 0x36, 0x05, 0x59, 0x77, 0xe0, 0x59, 0x7a, 0x45, 0xba, 0xb2, 0xef, 0xba, + 0xb2, 0xff, 0xfa, 0x3d, 0x12, 0x27, 0x17, 0xe9, 0x46, 0x75, 0x65, 0xff, 0x63, 0x3e, 0xe9, 0xfb, 0x37, 0x16, 0x5b, 0x8b, 0x28, 0x67, 0x9a, 0xe7, - 0x95, 0x04, 0x6a, 0x8c, 0x60, 0xe1, 0x39, 0x7f, 0xb1, 0xa2, 0x9e, 0xf8, - 0xd6, 0x5f, 0xff, 0x48, 0x1b, 0x7c, 0x0f, 0x8f, 0xd3, 0x9a, 0x82, 0xcb, - 0xfe, 0x78, 0x16, 0x6f, 0x2c, 0xe2, 0xcb, 0xff, 0x80, 0xcf, 0x16, 0x7d, - 0xd2, 0x7f, 0x2c, 0xbf, 0xff, 0x40, 0x9f, 0xae, 0xce, 0x48, 0xe7, 0xd3, - 0x05, 0x95, 0xc4, 0x4b, 0x88, 0x89, 0x76, 0x71, 0x65, 0xe7, 0x70, 0xaa, - 0x22, 0xd5, 0x40, 0xf8, 0xb0, 0x90, 0x22, 0xd7, 0xdd, 0x27, 0x15, 0x65, - 0xfa, 0x75, 0x9f, 0x75, 0x65, 0x8f, 0xc7, 0x95, 0xf9, 0x15, 0xf8, 0xc5, - 0xee, 0xa5, 0x65, 0xda, 0xe2, 0xca, 0x1a, 0xa1, 0x0d, 0x46, 0x63, 0xe7, + 0x95, 0x24, 0x6a, 0x8c, 0x60, 0xe1, 0x39, 0x7f, 0xb1, 0xa2, 0x8e, 0xf8, + 0xd6, 0x5f, 0xff, 0x40, 0x1b, 0x7c, 0x0f, 0x8f, 0xd1, 0x9a, 0x92, 0xcb, + 0xfe, 0x79, 0x16, 0x6f, 0x2c, 0xe2, 0xcb, 0xff, 0x80, 0xcf, 0x16, 0x7d, + 0xd2, 0x7f, 0x2c, 0xbf, 0xff, 0x48, 0x9f, 0xae, 0xce, 0x40, 0xe3, 0xd1, + 0x25, 0x95, 0xc4, 0x4b, 0x88, 0x89, 0x76, 0x71, 0x65, 0xe7, 0x70, 0xaa, + 0x22, 0xd5, 0x48, 0xf8, 0xb0, 0x90, 0x22, 0xd7, 0xdd, 0x27, 0x15, 0x65, + 0xfa, 0x35, 0x9f, 0x75, 0x65, 0x8f, 0xc7, 0x95, 0xf9, 0x15, 0xf8, 0xc5, + 0xee, 0xa1, 0x65, 0xda, 0xe2, 0xca, 0x1a, 0xa1, 0x0d, 0x46, 0x63, 0xe7, 0x82, 0x28, 0xe1, 0x45, 0xdd, 0xea, 0xcb, 0xff, 0xe7, 0xf6, 0x16, 0xb5, - 0x9f, 0x43, 0xa6, 0xc5, 0x95, 0x03, 0xe6, 0x00, 0xbd, 0xc1, 0x95, 0x97, - 0xfe, 0x03, 0x88, 0x59, 0x16, 0xa5, 0xa5, 0x97, 0x13, 0x4b, 0x2b, 0x0f, - 0xb7, 0xc2, 0xcd, 0x20, 0x5f, 0xfe, 0x84, 0xe1, 0x74, 0x0e, 0x3e, 0x48, - 0xab, 0x2e, 0x7d, 0xeb, 0x2f, 0xa4, 0x2c, 0x02, 0xcb, 0x0c, 0xcd, 0xd1, - 0x0c, 0x5f, 0xbe, 0x1e, 0x9e, 0x0b, 0x2f, 0xf0, 0x3f, 0x2c, 0xd8, 0x48, - 0x2c, 0xa9, 0x3d, 0xff, 0x14, 0xde, 0x21, 0xca, 0xcb, 0x86, 0x35, 0x97, - 0xf6, 0xa5, 0x98, 0x4c, 0x59, 0x7f, 0x88, 0xf8, 0x65, 0xf4, 0x16, 0x56, + 0x9f, 0x4b, 0xa6, 0xc5, 0x95, 0x23, 0xe6, 0x00, 0xbd, 0xc1, 0x85, 0x97, + 0xfe, 0x03, 0x88, 0x59, 0x36, 0xa1, 0xa5, 0x97, 0x13, 0x4b, 0x2b, 0x0f, + 0xb7, 0xc2, 0xcd, 0x20, 0x5f, 0xfe, 0x94, 0x61, 0x74, 0x0e, 0x3e, 0x40, + 0xab, 0x2e, 0x7d, 0xeb, 0x2f, 0xa0, 0x2c, 0x02, 0xcb, 0x0c, 0xcd, 0xd1, + 0x0c, 0x5f, 0xbe, 0x1e, 0x9e, 0x4b, 0x2f, 0xf0, 0x3f, 0x2c, 0xd8, 0x49, + 0x2c, 0xa8, 0x3d, 0xff, 0x14, 0xde, 0x21, 0xc2, 0xcb, 0x86, 0x35, 0x97, + 0xf6, 0xa1, 0x98, 0x4c, 0x59, 0x7f, 0x88, 0xf8, 0x65, 0xf4, 0x96, 0x56, 0x8f, 0x79, 0xa2, 0xcb, 0x8f, 0x65, 0x97, 0xfd, 0xd0, 0x69, 0xf8, 0xf9, - 0xe5, 0x97, 0x4e, 0xea, 0xca, 0x19, 0xf4, 0x18, 0xbb, 0x0e, 0x2b, 0x13, - 0x2e, 0x37, 0x57, 0x84, 0x5d, 0xff, 0xc1, 0xd4, 0x8b, 0x25, 0xd6, 0xd1, - 0xb4, 0x8e, 0x96, 0x5f, 0xf7, 0xa7, 0x59, 0xe3, 0xf8, 0x96, 0x5f, 0xde, - 0x20, 0x38, 0xa1, 0x59, 0x7f, 0x4e, 0xb0, 0x6f, 0x05, 0x94, 0x34, 0x49, - 0x19, 0xc0, 0x85, 0xd7, 0x17, 0x16, 0x54, 0xa6, 0x59, 0x90, 0xe6, 0x23, - 0x0b, 0xfe, 0x08, 0x9d, 0x38, 0x35, 0x9e, 0x59, 0x7f, 0xfb, 0xbc, 0xc8, - 0x8a, 0x59, 0xad, 0x4f, 0xcb, 0x2f, 0xf8, 0x0d, 0xb1, 0xe7, 0x79, 0x9e, - 0x59, 0x6e, 0xca, 0x22, 0x1d, 0x2e, 0xff, 0x83, 0x85, 0x9b, 0x6c, 0x02, - 0x59, 0x6d, 0xd5, 0x97, 0xda, 0xe6, 0x34, 0xb2, 0xfc, 0x45, 0x3f, 0x09, - 0x26, 0xdf, 0x05, 0x2f, 0xf0, 0x4f, 0x9a, 0xd4, 0xc1, 0x65, 0xff, 0xfb, - 0x3f, 0xe1, 0xfc, 0xd6, 0x06, 0x48, 0x0f, 0x05, 0x97, 0xff, 0x4e, 0x17, - 0x79, 0xdc, 0xc6, 0xb8, 0xb2, 0xfd, 0x9e, 0x6c, 0xc7, 0x18, 0xe0, 0xb2, - 0xa5, 0x54, 0xd6, 0x43, 0x0c, 0x24, 0xe6, 0xe1, 0xe3, 0xc2, 0x33, 0xea, + 0xe5, 0x97, 0x46, 0xea, 0xca, 0x19, 0xf4, 0x18, 0xbb, 0x0e, 0x2b, 0x13, + 0x2e, 0x37, 0x57, 0x84, 0x5d, 0xff, 0xc1, 0xd4, 0x0b, 0x05, 0xd6, 0xd1, + 0xb4, 0x9e, 0x96, 0x5f, 0xf7, 0xa3, 0x59, 0xe3, 0xf8, 0x96, 0x5f, 0xde, + 0x20, 0x38, 0xa1, 0x59, 0x7f, 0x46, 0xb0, 0x6f, 0x25, 0x94, 0x34, 0x49, + 0x19, 0xc0, 0x85, 0xd7, 0x17, 0x16, 0x54, 0x26, 0x59, 0x90, 0xe6, 0x23, + 0x0b, 0xfe, 0x08, 0x9d, 0x39, 0x35, 0x9e, 0x59, 0x7f, 0xfb, 0xbc, 0xc9, + 0x8a, 0x19, 0xad, 0x47, 0xcb, 0x2f, 0xf8, 0x0d, 0xb1, 0xe7, 0x79, 0x9e, + 0x59, 0x6e, 0xc2, 0x22, 0x1d, 0x2e, 0xff, 0x83, 0x85, 0x9b, 0x6c, 0x02, + 0x59, 0x6d, 0xd5, 0x97, 0xda, 0xe6, 0x34, 0xb2, 0xfc, 0x45, 0x1f, 0x09, + 0x06, 0xdf, 0x05, 0x2f, 0xf0, 0x4f, 0x9a, 0xd4, 0x49, 0x65, 0xff, 0xfb, + 0x3f, 0xe1, 0xfc, 0xd6, 0x06, 0x08, 0x0f, 0x25, 0x97, 0xff, 0x46, 0x17, + 0x79, 0xdc, 0xc6, 0xb8, 0xb2, 0xfd, 0x9e, 0x6c, 0x4f, 0x19, 0xe0, 0xb2, + 0xa1, 0x54, 0xd6, 0x43, 0x0c, 0x24, 0xe6, 0xe1, 0xe3, 0xc2, 0x33, 0xea, 0xb3, 0x48, 0x77, 0x85, 0xcd, 0xc5, 0x97, 0x6e, 0x6e, 0x2c, 0xbb, 0x3e, - 0x49, 0x69, 0x49, 0x6f, 0xf4, 0x69, 0xff, 0x17, 0xbf, 0xdd, 0xcd, 0x7e, - 0xc3, 0xe2, 0x46, 0xe6, 0xa6, 0xfe, 0x87, 0x8f, 0x7e, 0x44, 0xb2, 0xa4, - 0xfd, 0x9d, 0x1a, 0xfe, 0xf1, 0x86, 0x58, 0x6b, 0x2f, 0xf4, 0xff, 0xb3, - 0xc2, 0x77, 0x56, 0x58, 0xa4, 0xf8, 0xc8, 0xb2, 0xcd, 0xdb, 0x2c, 0xe2, - 0x38, 0xe8, 0x72, 0x38, 0x0c, 0x4c, 0x3d, 0x36, 0x34, 0x19, 0x96, 0x4a, - 0x63, 0x16, 0x16, 0xe1, 0x84, 0x7f, 0xc5, 0xe7, 0x08, 0x38, 0xa1, 0x09, + 0x49, 0x68, 0x49, 0x6f, 0xf4, 0x69, 0xff, 0x17, 0xbf, 0xdd, 0xcd, 0x7e, + 0xc3, 0xe2, 0x46, 0xe6, 0xa6, 0xfe, 0x97, 0x8f, 0x7e, 0x4c, 0xb2, 0xa0, + 0xfd, 0x9d, 0x1a, 0xfe, 0xf1, 0x86, 0x18, 0x6b, 0x2f, 0xf4, 0x7f, 0xb3, + 0xca, 0x37, 0x56, 0x58, 0xa0, 0xf8, 0xc8, 0xb2, 0xcd, 0xdb, 0x0c, 0xe2, + 0x39, 0xe8, 0x72, 0x78, 0x0c, 0x44, 0x3d, 0x36, 0x34, 0x19, 0x96, 0x4a, + 0x63, 0x16, 0x16, 0xe1, 0x84, 0x7f, 0xc5, 0xe7, 0x08, 0x39, 0xa1, 0x09, 0xa2, 0x1f, 0x46, 0x92, 0x51, 0xaf, 0xf2, 0x56, 0x68, 0x1d, 0x5a, 0x20, - 0x8f, 0x87, 0x18, 0x90, 0x83, 0xbf, 0xff, 0xf9, 0xd8, 0xdc, 0xf5, 0xa0, - 0x6b, 0x52, 0x58, 0x29, 0xfb, 0xd8, 0x22, 0xcb, 0xff, 0xcd, 0xa0, 0xad, - 0x6e, 0xec, 0x38, 0xee, 0x34, 0x6f, 0x47, 0xac, 0xbf, 0xd9, 0xff, 0x30, + 0x9f, 0x87, 0x18, 0x90, 0x83, 0xbf, 0xff, 0xf9, 0xd8, 0xdc, 0xf5, 0xa0, + 0x6b, 0x50, 0x58, 0x29, 0xfb, 0xd8, 0x22, 0xcb, 0xff, 0xcd, 0xa0, 0xad, + 0x6e, 0xec, 0x39, 0xee, 0x74, 0xef, 0x4f, 0xac, 0xbf, 0xd9, 0xff, 0x30, 0x2e, 0x35, 0x97, 0xe2, 0x76, 0xb9, 0xc5, 0x95, 0x87, 0xb7, 0xa3, 0x3b, - 0xe0, 0xbb, 0x85, 0x51, 0x51, 0x2f, 0x0b, 0x3c, 0x59, 0x7c, 0x50, 0x36, - 0x2c, 0xbf, 0x43, 0x08, 0xc6, 0xb2, 0xb0, 0xf9, 0x1c, 0x77, 0xa4, 0x35, - 0xa4, 0x5f, 0xb2, 0x12, 0x57, 0xde, 0xcd, 0x62, 0xcb, 0xc7, 0x16, 0xe2, - 0xca, 0x93, 0xc1, 0xf1, 0x0d, 0xf8, 0x2f, 0xe9, 0xf2, 0xcb, 0xff, 0xf8, - 0x04, 0xe2, 0xc6, 0xe8, 0xb2, 0x50, 0xcf, 0x1b, 0x16, 0x5c, 0x01, 0x16, - 0x5f, 0x0c, 0x72, 0xc5, 0x97, 0xff, 0xd0, 0xd9, 0xb4, 0x98, 0xe7, 0x60, - 0xea, 0x34, 0x6f, 0x47, 0xac, 0xa9, 0x44, 0xf9, 0x8c, 0x7e, 0x45, 0x7e, - 0xc1, 0xce, 0x12, 0xcb, 0xff, 0xf8, 0x04, 0xe2, 0xc6, 0xe8, 0xb2, 0x50, - 0xcf, 0x1b, 0x16, 0x5f, 0x6b, 0x59, 0x1e, 0xb2, 0x98, 0x88, 0x27, 0x5d, - 0xbf, 0xff, 0x3c, 0x0a, 0x79, 0x9f, 0x04, 0x9f, 0x3e, 0x0a, 0xcb, 0xf4, - 0xb5, 0x9e, 0x75, 0x96, 0x6f, 0x05, 0xcb, 0xf1, 0xc2, 0xff, 0x21, 0xc0, + 0xe0, 0xbb, 0x85, 0x51, 0x51, 0x2f, 0x0b, 0x1c, 0x59, 0x7c, 0x52, 0x36, + 0x2c, 0xbf, 0x4b, 0x08, 0xc6, 0xb2, 0xb0, 0xf9, 0x1c, 0x77, 0xa4, 0x35, + 0xa4, 0x5f, 0xb2, 0x12, 0x57, 0xde, 0xcd, 0x62, 0xcb, 0xc7, 0x36, 0xe2, + 0xca, 0x83, 0xc1, 0xf1, 0x0d, 0xf8, 0x2f, 0xe8, 0xf2, 0xcb, 0xff, 0xf8, + 0x04, 0xe2, 0xce, 0xe8, 0xb0, 0x52, 0xcf, 0x1b, 0x16, 0x5c, 0x01, 0x16, + 0x5f, 0x0c, 0x70, 0xc5, 0x97, 0xff, 0xd2, 0xd9, 0xb4, 0x89, 0xe7, 0x60, + 0xea, 0x74, 0xef, 0x4f, 0xac, 0xa8, 0x44, 0xf9, 0x8c, 0x7e, 0x45, 0x7e, + 0xc1, 0xc6, 0x12, 0xcb, 0xff, 0xf8, 0x04, 0xe2, 0xce, 0xe8, 0xb0, 0x52, + 0xcf, 0x1b, 0x16, 0x5f, 0x6b, 0x59, 0x3e, 0xb2, 0x98, 0x88, 0x27, 0x5d, + 0xbf, 0xff, 0x3c, 0x8a, 0x39, 0x9f, 0x04, 0x9f, 0x3e, 0x0a, 0xcb, 0xf4, + 0x35, 0x9e, 0x75, 0x96, 0x6f, 0x25, 0xcb, 0xf1, 0xc2, 0xff, 0x21, 0xc0, 0x16, 0x93, 0x21, 0x61, 0x3f, 0xa1, 0xf8, 0x45, 0xfc, 0x85, 0x3e, 0xf2, - 0x21, 0x15, 0x2f, 0xfb, 0x18, 0xde, 0x3f, 0x08, 0xfa, 0xb2, 0xff, 0xd2, - 0xdf, 0xc6, 0xf0, 0xe9, 0xec, 0xb2, 0x86, 0x7f, 0x9c, 0x3c, 0xbd, 0x8d, - 0x62, 0xcb, 0xc0, 0x9d, 0x96, 0x5f, 0xcf, 0x0f, 0xb9, 0x3b, 0x2c, 0xbf, - 0xe7, 0xf8, 0xb3, 0x7e, 0x8f, 0x8b, 0x2f, 0xc7, 0x30, 0x78, 0x2c, 0xa1, - 0xa2, 0x3d, 0x85, 0xfe, 0x3a, 0xbf, 0xb3, 0x61, 0xce, 0x0d, 0x65, 0xf0, - 0xfc, 0x71, 0x2c, 0xac, 0x3d, 0x07, 0x2d, 0xbf, 0x13, 0xee, 0x66, 0xcb, - 0x2f, 0x34, 0xfe, 0x59, 0x74, 0x8d, 0x65, 0x49, 0xb3, 0xf0, 0xed, 0xff, - 0x39, 0x77, 0x9a, 0xc9, 0x62, 0xcb, 0xf1, 0x91, 0x03, 0x75, 0x65, 0xff, - 0x73, 0x07, 0x9a, 0x84, 0xe9, 0x65, 0x49, 0xf0, 0xe8, 0xa6, 0xdb, 0x2c, - 0xa9, 0x36, 0x78, 0x43, 0x7c, 0x19, 0xd0, 0xab, 0x2f, 0xc5, 0x3f, 0xff, - 0x2b, 0x2e, 0x70, 0x2c, 0xb1, 0x2c, 0xbf, 0x6d, 0xa7, 0x7e, 0xac, 0xa3, - 0x37, 0x0e, 0x21, 0x7d, 0xe3, 0xf3, 0x76, 0xc2, 0xb8, 0x71, 0x8e, 0x64, - 0x2b, 0x05, 0x84, 0x00, 0x48, 0x22, 0x5d, 0x61, 0x07, 0xa1, 0x97, 0xc1, - 0xfe, 0x91, 0xb4, 0x51, 0x1e, 0x9d, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xaf, - 0x57, 0xe3, 0xdc, 0x08, 0x36, 0x59, 0x74, 0xc1, 0x65, 0xd3, 0xfa, 0xcb, - 0xfb, 0x61, 0x23, 0xdc, 0xbf, 0x59, 0x7f, 0xe7, 0x2f, 0xf3, 0xb2, 0x47, - 0xfa, 0xca, 0x93, 0xf0, 0x8f, 0x33, 0xb3, 0x7c, 0x4c, 0x87, 0x46, 0x8e, - 0x59, 0xf8, 0xb0, 0x90, 0x87, 0xbf, 0xf9, 0xbb, 0xc1, 0xbe, 0x68, 0x2e, - 0xe1, 0x54, 0x48, 0xcb, 0xff, 0xcd, 0xd8, 0xf0, 0x6f, 0x9a, 0x0b, 0xb8, - 0x55, 0x13, 0xc2, 0xff, 0xf3, 0x76, 0x3c, 0x1b, 0xe6, 0x82, 0xee, 0x15, - 0x44, 0xfe, 0xbf, 0xfc, 0xdd, 0x8f, 0x06, 0xf9, 0xa0, 0xbb, 0x85, 0x51, - 0x43, 0x2f, 0xfe, 0x63, 0xc1, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x51, 0x0b, - 0xff, 0x41, 0xb8, 0x39, 0x1a, 0x79, 0x30, 0x59, 0x4d, 0xd1, 0x2b, 0x2a, - 0x57, 0xfe, 0x88, 0xfd, 0xf6, 0x74, 0x78, 0xc5, 0x97, 0xff, 0x9d, 0xb8, - 0xdc, 0x9c, 0x31, 0x84, 0x76, 0x2c, 0xbf, 0x8f, 0xa7, 0xa7, 0xfd, 0x65, - 0xf6, 0x77, 0x3f, 0x59, 0x7e, 0x20, 0x3c, 0x38, 0xb2, 0xf1, 0x3b, 0x78, + 0x21, 0x15, 0x2f, 0xfb, 0x18, 0xde, 0x7f, 0x08, 0xfa, 0xb2, 0xff, 0xd0, + 0xdf, 0xc6, 0xf2, 0xe9, 0xec, 0xb2, 0x86, 0x7f, 0x9c, 0x3c, 0xbd, 0x8d, + 0x62, 0xcb, 0xc0, 0x8d, 0x96, 0x5f, 0xcf, 0x2f, 0xb9, 0x1b, 0x2c, 0xbf, + 0xe7, 0xf8, 0xb3, 0x7e, 0x8f, 0x8b, 0x2f, 0xc7, 0x12, 0x79, 0x2c, 0xa1, + 0xa2, 0x3d, 0x85, 0xfe, 0x3a, 0xbf, 0xb3, 0x61, 0xc6, 0x0d, 0x65, 0xf0, + 0xfc, 0x73, 0x2c, 0xac, 0x3d, 0x07, 0x2d, 0xbf, 0x13, 0xee, 0x66, 0xcb, + 0x2f, 0x34, 0xfe, 0x59, 0x74, 0x0d, 0x65, 0x41, 0xb3, 0xf0, 0xed, 0xff, + 0x39, 0x77, 0x9a, 0xc8, 0x62, 0xcb, 0xf1, 0x91, 0x03, 0x75, 0x65, 0xff, + 0x73, 0x07, 0x9a, 0x94, 0x69, 0x65, 0x41, 0xf0, 0xe8, 0xa6, 0xdb, 0x2c, + 0xa8, 0x36, 0x78, 0x43, 0x7c, 0x18, 0xd0, 0xab, 0x2f, 0xc5, 0x1f, 0xff, + 0x0b, 0x2e, 0x70, 0x2c, 0xb1, 0x2c, 0xbf, 0x6d, 0xa7, 0x7e, 0xac, 0xa3, + 0x37, 0x0e, 0x21, 0x7d, 0xe3, 0xf3, 0x76, 0xca, 0xb8, 0x71, 0x8e, 0x64, + 0x2b, 0x05, 0x84, 0x00, 0x48, 0x26, 0x5d, 0x61, 0x07, 0xa1, 0x97, 0xc1, + 0xfe, 0x91, 0xb4, 0x51, 0x3e, 0x9d, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xaf, + 0x57, 0xe3, 0xdc, 0x08, 0x36, 0x59, 0x74, 0x49, 0x65, 0xd1, 0xfa, 0xcb, + 0xfb, 0x61, 0x27, 0xdc, 0xbf, 0x59, 0x7f, 0xe7, 0x2f, 0xf3, 0xb0, 0x47, + 0xfa, 0xca, 0x83, 0xf0, 0x9f, 0x33, 0xb3, 0x7c, 0x4c, 0x87, 0x46, 0x8e, + 0x59, 0xf8, 0xb0, 0x90, 0x87, 0xbf, 0xf9, 0xbb, 0xc9, 0xbe, 0x68, 0x2e, + 0xe1, 0x54, 0x48, 0xcb, 0xff, 0xcd, 0xd8, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, + 0x55, 0x13, 0xc2, 0xff, 0xf3, 0x76, 0x3c, 0x9b, 0xe6, 0x82, 0xee, 0x15, + 0x44, 0xfe, 0xbf, 0xfc, 0xdd, 0x8f, 0x26, 0xf9, 0xa0, 0xbb, 0x85, 0x51, + 0x43, 0x2f, 0xfe, 0x63, 0xc9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x51, 0x0b, + 0xff, 0x49, 0xb8, 0x39, 0x3a, 0x39, 0x12, 0x59, 0x4d, 0xd1, 0x2b, 0x0a, + 0x57, 0xfe, 0x98, 0xfd, 0xf6, 0x74, 0x78, 0xc5, 0x97, 0xff, 0x9d, 0xb8, + 0xdc, 0x9c, 0x33, 0x84, 0x76, 0x2c, 0xbf, 0x8f, 0xa7, 0xa7, 0xfd, 0x65, + 0xf6, 0x77, 0x3f, 0x59, 0x7e, 0x20, 0x3c, 0xb8, 0xb2, 0xf1, 0x3b, 0x79, 0x1f, 0xb1, 0x16, 0xf4, 0x8a, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x48, 0xaf, - 0xfe, 0xf6, 0xf7, 0xd7, 0x1f, 0xdc, 0x9d, 0x96, 0x5f, 0xfc, 0xff, 0x73, - 0xcd, 0x4f, 0xf1, 0x84, 0x62, 0xcb, 0xfe, 0x98, 0x4e, 0xb6, 0x9d, 0x6c, - 0xb2, 0xf3, 0xc1, 0xbe, 0x26, 0x00, 0xe6, 0x9f, 0xa3, 0x92, 0x5d, 0xff, - 0xfd, 0xbd, 0xb8, 0x81, 0xce, 0xc9, 0x77, 0x8e, 0x40, 0xc5, 0x97, 0xff, - 0xa0, 0xdf, 0x4f, 0xfb, 0xf4, 0xf5, 0x8c, 0x59, 0x7f, 0xc6, 0x19, 0x67, - 0x3b, 0x30, 0x59, 0x77, 0xb1, 0x65, 0xd2, 0xd2, 0xcb, 0xf8, 0x1a, 0xfe, - 0x30, 0xe2, 0x59, 0x77, 0xed, 0xe0, 0x89, 0x81, 0x9c, 0x78, 0x5b, 0x82, - 0xf7, 0xf4, 0x33, 0xde, 0x78, 0x2c, 0xbf, 0xff, 0xb0, 0x7e, 0x91, 0x1b, - 0xf4, 0x9f, 0xfe, 0x72, 0x7f, 0x49, 0x66, 0xf2, 0xb9, 0x58, 0x34, 0x00, - 0xc3, 0x0d, 0x91, 0xa0, 0x79, 0x3d, 0xd7, 0xbf, 0x87, 0x27, 0x53, 0x23, - 0xcb, 0x6f, 0xff, 0xbd, 0x3e, 0x31, 0x79, 0xcc, 0x2f, 0xe3, 0xf1, 0x65, - 0xf0, 0x5d, 0xc2, 0xa8, 0x94, 0x17, 0xfe, 0x9f, 0xd8, 0xf0, 0xf4, 0xbe, - 0x96, 0x54, 0x11, 0x7f, 0xa5, 0x32, 0x2e, 0xbf, 0xfc, 0x4f, 0xde, 0x08, - 0x40, 0x81, 0x63, 0x16, 0x5f, 0x71, 0xc7, 0x8b, 0x2f, 0xff, 0x3e, 0xc7, - 0xad, 0x1f, 0xd0, 0xeb, 0xfc, 0xb2, 0xf9, 0xbb, 0xc1, 0xbe, 0x23, 0x95, - 0x85, 0xfc, 0x49, 0x8f, 0x21, 0xad, 0x9d, 0x2b, 0xbc, 0x29, 0xc9, 0xa3, + 0xfe, 0xf6, 0xf7, 0xd7, 0x1f, 0xdc, 0x8d, 0x96, 0x5f, 0xfc, 0xff, 0x73, + 0xcd, 0x47, 0xf3, 0x84, 0x62, 0xcb, 0xfe, 0x89, 0x46, 0xb6, 0x8d, 0x6c, + 0xb2, 0xf3, 0xc9, 0xbe, 0x26, 0x00, 0xe6, 0x9f, 0xa3, 0x92, 0x5d, 0xff, + 0xfd, 0xbd, 0xb8, 0x81, 0xce, 0xc1, 0x77, 0x8e, 0x40, 0xc5, 0x97, 0xff, + 0xa4, 0xdf, 0x4f, 0xfb, 0xf4, 0xf5, 0x8c, 0x59, 0x7f, 0xc6, 0x18, 0x67, + 0x3b, 0x12, 0x59, 0x77, 0xb1, 0x65, 0xd0, 0xd2, 0xcb, 0xf8, 0x1a, 0xfe, + 0x70, 0xe6, 0x59, 0x77, 0xed, 0xe4, 0x89, 0x81, 0x9c, 0x78, 0x5b, 0x82, + 0xf7, 0xf4, 0xb3, 0xde, 0x79, 0x2c, 0xbf, 0xff, 0xb0, 0x7e, 0x81, 0x1b, + 0xf4, 0x9f, 0xfe, 0x72, 0x3f, 0x49, 0x66, 0xf0, 0xb9, 0x58, 0x34, 0x00, + 0xc3, 0x0d, 0x91, 0xa0, 0x79, 0x3d, 0xd7, 0xbf, 0x87, 0x27, 0x53, 0x27, + 0xcb, 0x6f, 0xff, 0xbd, 0x1e, 0x31, 0x79, 0xcc, 0x2f, 0xe7, 0xf1, 0x65, + 0xf0, 0x5d, 0xc2, 0xa8, 0x94, 0x17, 0xfe, 0x8f, 0xd8, 0xf2, 0xf4, 0x3e, + 0x96, 0x54, 0x91, 0x7f, 0xa5, 0x32, 0x2e, 0xbf, 0xfc, 0x4f, 0xde, 0x08, + 0x40, 0x91, 0x63, 0x16, 0x5f, 0x71, 0xc7, 0x8b, 0x2f, 0xff, 0x3e, 0xc7, + 0xad, 0x1f, 0xd2, 0xeb, 0xfc, 0xb2, 0xf9, 0xbb, 0xc9, 0xbe, 0x23, 0x95, + 0x85, 0xfc, 0x49, 0x9f, 0x21, 0xad, 0x9d, 0x2b, 0xbc, 0xa9, 0xc9, 0xa3, 0x8d, 0xa8, 0x33, 0xa6, 0x67, 0x19, 0x9e, 0xec, 0xba, 0x9d, 0x47, 0xf2, 0xc5, 0x8f, 0x2a, 0x7e, 0xb4, 0xdb, 0x5a, 0xe4, 0x3b, 0xc1, 0x39, 0xa1, - 0xb9, 0x28, 0xfe, 0xff, 0xf3, 0x76, 0x3c, 0x1b, 0xe6, 0x82, 0xee, 0x15, - 0x44, 0xe6, 0xbf, 0xfc, 0xdd, 0x8f, 0x06, 0xf9, 0xa0, 0xbb, 0x85, 0x51, - 0x44, 0xaf, 0xf8, 0x0f, 0xa8, 0x47, 0x1d, 0xff, 0x9a, 0xcb, 0xc7, 0xf7, - 0x16, 0x5e, 0x29, 0x62, 0xcb, 0xfe, 0x27, 0xfb, 0xaf, 0xd8, 0x3a, 0xcb, + 0xb9, 0x28, 0xfe, 0xff, 0xf3, 0x76, 0x3c, 0x9b, 0xe6, 0x82, 0xee, 0x15, + 0x44, 0xe6, 0xbf, 0xfc, 0xdd, 0x8f, 0x26, 0xf9, 0xa0, 0xbb, 0x85, 0x51, + 0x44, 0xaf, 0xf8, 0x0f, 0xa9, 0x4f, 0x1d, 0xff, 0x9a, 0xcb, 0xc7, 0xf7, + 0x16, 0x5e, 0x28, 0x62, 0xcb, 0xfe, 0x27, 0xfb, 0xaf, 0xd9, 0x3a, 0xcb, 0xfb, 0xd9, 0x9d, 0xf6, 0x2c, 0xad, 0x91, 0x38, 0x63, 0xbf, 0x8d, 0xf4, - 0xe2, 0xfe, 0x3e, 0x45, 0x03, 0xfd, 0x65, 0xfd, 0x83, 0x10, 0x6e, 0xc5, - 0x97, 0xfa, 0x76, 0x12, 0x3d, 0xcb, 0xf5, 0x97, 0x41, 0xb8, 0xd3, 0xb1, - 0xc8, 0x6f, 0x78, 0xfb, 0x79, 0x78, 0x85, 0xd7, 0xf7, 0x0a, 0x33, 0x64, - 0x66, 0xb2, 0xff, 0xc5, 0x9f, 0xcf, 0xfa, 0xd4, 0xb4, 0xb2, 0xcd, 0xe4, + 0xe2, 0xfe, 0x3e, 0x4d, 0x23, 0xfd, 0x65, 0xfd, 0x83, 0x10, 0x6e, 0xc5, + 0x97, 0xfa, 0x36, 0x12, 0x7d, 0xcb, 0xf5, 0x97, 0x49, 0xb8, 0xd3, 0xb1, + 0xc8, 0x6f, 0x78, 0xfb, 0x79, 0x78, 0x85, 0xd7, 0xf7, 0x0a, 0x73, 0x60, + 0x66, 0xb2, 0xff, 0xc5, 0x9f, 0xc7, 0xfa, 0xd4, 0x34, 0xb2, 0xcd, 0xe0, 0xfc, 0xf8, 0x67, 0x5a, 0x47, 0xe7, 0xf0, 0xd3, 0xad, 0x2b, 0x8a, 0xe2, 0xd0, 0x25, 0x83, 0xdf, 0xb4, 0x17, 0x70, 0xaa, 0x21, 0xc5, 0xfc, 0x00, - 0xe8, 0xe4, 0x96, 0x5f, 0x8c, 0xbb, 0x2c, 0x59, 0x66, 0xf8, 0x88, 0x6f, - 0x1a, 0x47, 0x96, 0x5f, 0xf8, 0xc7, 0x8c, 0x2c, 0xfb, 0xae, 0xb2, 0xff, - 0xa7, 0x58, 0x1f, 0x4f, 0xd0, 0x59, 0x4b, 0x2f, 0xfe, 0x27, 0x14, 0x3e, - 0x3d, 0x6a, 0x5a, 0x59, 0x71, 0x12, 0xcb, 0xe0, 0xbb, 0x85, 0x51, 0x17, + 0xe8, 0xe0, 0x96, 0x5f, 0x8c, 0xbb, 0x0c, 0x59, 0x66, 0xf8, 0x88, 0x6f, + 0x1a, 0x4f, 0x96, 0x5f, 0xf8, 0xc7, 0x8c, 0x2c, 0xfb, 0xae, 0xb2, 0xff, + 0xa3, 0x58, 0x1f, 0x47, 0xd2, 0x59, 0x4b, 0x2f, 0xfe, 0x27, 0x14, 0x3e, + 0x3d, 0x6a, 0x1a, 0x59, 0x71, 0x12, 0xcb, 0xe0, 0xbb, 0x85, 0x51, 0x17, 0xaf, 0xf6, 0x7d, 0xff, 0x1c, 0xf6, 0x59, 0x7e, 0x27, 0xfd, 0xf8, 0xb2, - 0xfd, 0x91, 0x41, 0xf8, 0xb2, 0xa2, 0x46, 0x3e, 0x85, 0x48, 0xbb, 0xa6, + 0xfd, 0x93, 0x49, 0xf8, 0xb2, 0xa6, 0x46, 0x3e, 0x85, 0x48, 0xbb, 0xa6, 0xa2, 0x13, 0xdf, 0x33, 0xc7, 0xa5, 0x97, 0xf6, 0x0f, 0xd9, 0xfb, 0xac, - 0xbe, 0x90, 0x36, 0xf8, 0xb2, 0xb8, 0x7a, 0x6d, 0x16, 0xdc, 0x27, 0x16, - 0x5a, 0x0b, 0x2a, 0x4d, 0x4b, 0x8c, 0x5c, 0x5b, 0x2c, 0xbf, 0xee, 0x94, - 0xb0, 0x41, 0xfa, 0x56, 0x5f, 0x9e, 0x0c, 0x98, 0x2c, 0xa9, 0x3f, 0x73, - 0x17, 0xf1, 0xcd, 0xf4, 0x50, 0x78, 0x2c, 0xbb, 0x9f, 0x2c, 0xbe, 0xec, - 0x24, 0x54, 0x97, 0xd8, 0x3e, 0xf1, 0x65, 0x62, 0x20, 0x18, 0x46, 0xe3, - 0x04, 0x47, 0x7f, 0x8b, 0x3e, 0x87, 0x0c, 0x55, 0x97, 0xef, 0x60, 0x5e, - 0x0b, 0x2f, 0xcf, 0xf0, 0x1f, 0xab, 0x2e, 0x8f, 0x25, 0x97, 0xe7, 0xdb, - 0xb9, 0xe5, 0x96, 0x6f, 0x2b, 0x9b, 0x5b, 0x1e, 0xc0, 0xe4, 0x61, 0x79, - 0x18, 0x61, 0xa5, 0xc4, 0xea, 0xc4, 0xcf, 0x42, 0x58, 0xa1, 0x5b, 0xc3, - 0xae, 0x9a, 0x00, 0x9f, 0x79, 0x44, 0x78, 0xcd, 0xff, 0xff, 0xf1, 0x3b, - 0x7c, 0x03, 0x6c, 0xc5, 0x3e, 0xfb, 0x0e, 0x1c, 0x8d, 0x82, 0xcc, 0x16, - 0x5f, 0xb3, 0x8c, 0x1c, 0xac, 0xbf, 0xf3, 0xc1, 0xbe, 0x68, 0x2e, 0xe1, + 0xbe, 0x80, 0x36, 0xf8, 0xb2, 0xb8, 0x7a, 0x6d, 0x16, 0xdc, 0x27, 0x16, + 0x5a, 0x4b, 0x2a, 0x0d, 0x4b, 0x8c, 0x5c, 0x5b, 0x2c, 0xbf, 0xee, 0x94, + 0x30, 0x41, 0xfa, 0x16, 0x5f, 0x9e, 0x4c, 0x89, 0x2c, 0xa8, 0x3f, 0x73, + 0x17, 0xf1, 0xcd, 0xf4, 0xd2, 0x79, 0x2c, 0xbb, 0x9f, 0x2c, 0xbe, 0xec, + 0xa0, 0x54, 0x97, 0xd8, 0x3e, 0xf1, 0x65, 0x62, 0x20, 0x18, 0x46, 0xe3, + 0x04, 0x47, 0x7f, 0x8b, 0x3e, 0x97, 0x0c, 0x55, 0x97, 0xef, 0x60, 0x5e, + 0x4b, 0x2f, 0xcf, 0xf0, 0x1f, 0xab, 0x2e, 0x9f, 0x25, 0x97, 0xe7, 0xdb, + 0xb9, 0xe5, 0x96, 0x6f, 0x0b, 0x9b, 0x5b, 0x1e, 0xc8, 0xe4, 0x61, 0x79, + 0x18, 0x61, 0xa5, 0xcc, 0xea, 0xc4, 0xcf, 0x42, 0x58, 0xa1, 0x5b, 0xc3, + 0xae, 0x9a, 0x00, 0x9f, 0x79, 0x44, 0xf8, 0xcd, 0xff, 0xff, 0xf1, 0x3b, + 0x7c, 0x03, 0x6c, 0xc5, 0x3e, 0xfb, 0x0e, 0x5c, 0x9d, 0x82, 0xc4, 0x96, + 0x5f, 0xb3, 0x8c, 0x1c, 0x2c, 0xbf, 0xf3, 0xc9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4c, 0x8b, 0x37, 0x0a, 0x3d, 0xf5, 0x08, 0x46, 0x13, 0xde, 0xee, 0x31, 0x65, 0xde, 0x62, 0xcb, 0xf6, 0x82, 0xee, 0x15, 0x45, 0xbc, 0xb3, - 0x79, 0x3e, 0x71, 0x8e, 0x60, 0xbd, 0xfe, 0xfe, 0x46, 0x07, 0xd4, 0x16, - 0x5f, 0xf9, 0xe0, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x29, 0x35, 0x9b, 0x9a, - 0x20, 0x18, 0x69, 0x73, 0x66, 0x39, 0x59, 0x7f, 0xee, 0x01, 0xfe, 0xd6, - 0x81, 0x17, 0x16, 0x5c, 0xcf, 0x96, 0x5f, 0xd9, 0xd2, 0x99, 0x62, 0xcb, - 0xfb, 0xef, 0xc4, 0xd3, 0xfe, 0xb2, 0xef, 0x37, 0x6c, 0x23, 0x5f, 0x08, - 0x18, 0x85, 0xe1, 0x87, 0x2b, 0xa9, 0x64, 0xd4, 0xc2, 0x74, 0x53, 0x23, + 0x78, 0x3e, 0x71, 0x8e, 0x60, 0xbd, 0xfe, 0xfe, 0x06, 0x07, 0xd4, 0x96, + 0x5f, 0xf9, 0xe4, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x29, 0x35, 0x9b, 0x9a, + 0x20, 0x18, 0x69, 0x73, 0x62, 0x79, 0x59, 0x7f, 0xee, 0x01, 0xfe, 0xd6, + 0x81, 0x37, 0x16, 0x5c, 0xcf, 0x96, 0x5f, 0xd9, 0xd2, 0x88, 0x62, 0xcb, + 0xfb, 0xef, 0xc4, 0xd3, 0xfe, 0xb2, 0xef, 0x37, 0x6c, 0xa3, 0x5f, 0x08, + 0x18, 0x85, 0xe1, 0x87, 0x2b, 0xa8, 0x64, 0xd4, 0xca, 0x74, 0x53, 0x23, 0xe1, 0xe4, 0x29, 0x37, 0xc2, 0xd4, 0x48, 0xc0, 0x6f, 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x87, 0x57, 0xed, 0x05, 0xdc, 0x2a, 0x89, 0x61, 0x7d, - 0x9f, 0xb0, 0xd6, 0x5f, 0x9b, 0xb1, 0xe0, 0xdf, 0x0f, 0x5a, 0x3c, 0xd2, - 0xff, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x88, 0xc1, 0x79, 0xa9, 0x62, 0xcb, - 0xd0, 0x31, 0xac, 0xbf, 0x43, 0x38, 0xfc, 0x59, 0x66, 0xf8, 0x7c, 0x11, - 0x0e, 0x80, 0x72, 0xff, 0xfc, 0xff, 0xc7, 0xe3, 0x72, 0x7e, 0xc3, 0x3b, - 0x83, 0x59, 0x7f, 0xfe, 0x36, 0x1e, 0x9b, 0xf6, 0x3f, 0x3d, 0xd9, 0xf9, - 0xa5, 0x94, 0x48, 0xec, 0xe1, 0xbf, 0x56, 0x6f, 0x9b, 0xc3, 0x38, 0xb2, - 0xff, 0xff, 0xa1, 0xc6, 0xe1, 0xf3, 0xc4, 0x1f, 0x66, 0xa3, 0xaf, 0x44, - 0xce, 0x2c, 0xbf, 0x67, 0x7f, 0x90, 0xac, 0xbf, 0xfd, 0x3d, 0x3f, 0xe6, - 0x03, 0xf4, 0x90, 0x56, 0x5f, 0xb4, 0x17, 0x70, 0xaa, 0x23, 0xd5, 0xf7, - 0x1f, 0xed, 0xeb, 0x2e, 0x83, 0x7c, 0x3d, 0x9e, 0x9a, 0x5f, 0xff, 0xce, + 0x9f, 0xb0, 0xd6, 0x5f, 0x9b, 0xb1, 0xe4, 0xdf, 0x0f, 0x5a, 0x7c, 0xd2, + 0xff, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x88, 0xc1, 0x79, 0xa8, 0x62, 0xcb, + 0xd2, 0x31, 0xac, 0xbf, 0x4b, 0x38, 0xfc, 0x59, 0x66, 0xf8, 0x7c, 0x13, + 0x0e, 0x80, 0x72, 0xff, 0xfc, 0xff, 0xcf, 0xe3, 0x72, 0x7e, 0xcb, 0x3b, + 0x83, 0x59, 0x7f, 0xfe, 0x36, 0x1e, 0x9b, 0xf6, 0x7f, 0x3d, 0xd8, 0xf9, + 0xa5, 0x94, 0x48, 0xec, 0xe1, 0xbf, 0x56, 0x6f, 0x9b, 0xcb, 0x38, 0xb2, + 0xff, 0xff, 0xa5, 0xc6, 0xe1, 0xf3, 0xcc, 0x1f, 0x66, 0xa7, 0xaf, 0x4c, + 0xce, 0x2c, 0xbf, 0x67, 0x7f, 0x80, 0xac, 0xbf, 0xfd, 0x1d, 0x3f, 0xe2, + 0x43, 0xf4, 0x10, 0x56, 0x5f, 0xb4, 0x17, 0x70, 0xaa, 0x23, 0xd5, 0xf7, + 0x1f, 0xed, 0xeb, 0x2e, 0x93, 0x7c, 0x3d, 0x9e, 0x9a, 0x5f, 0xff, 0xce, 0xdc, 0x81, 0x9e, 0xc2, 0xff, 0x0b, 0xbe, 0xc5, 0x96, 0x6f, 0xf2, 0x78, - 0xb1, 0x39, 0x68, 0xa1, 0xe1, 0x44, 0x45, 0xf7, 0xed, 0x05, 0xdc, 0x2a, - 0x8b, 0x0d, 0x7f, 0xa0, 0x53, 0x9c, 0xc2, 0x59, 0x71, 0xc1, 0x65, 0x9b, + 0xb3, 0x39, 0x68, 0xa1, 0xe1, 0x44, 0x45, 0xf7, 0xed, 0x05, 0xdc, 0x2a, + 0x8b, 0x0d, 0x7f, 0xa4, 0x51, 0x9c, 0xc2, 0x59, 0x71, 0xc9, 0x65, 0x9b, 0xe1, 0xfd, 0x7c, 0x69, 0xf9, 0x85, 0xf3, 0x7f, 0xcb, 0x16, 0x5f, 0xe2, - 0x06, 0xc6, 0x40, 0x62, 0xcb, 0xd2, 0x5b, 0x2c, 0xbf, 0x14, 0xff, 0x06, - 0x2c, 0xa3, 0x3f, 0x7f, 0x19, 0xf4, 0x72, 0xff, 0xff, 0xa6, 0x78, 0xdf, - 0xa3, 0x36, 0x7d, 0x0e, 0x7d, 0x0c, 0xd6, 0xcb, 0x29, 0xba, 0x26, 0x88, - 0xba, 0xd3, 0xb2, 0x6b, 0x3a, 0x8c, 0xd6, 0xff, 0x37, 0xcd, 0x05, 0xdc, - 0x2a, 0x8b, 0x9d, 0x7f, 0xff, 0xff, 0xf4, 0xb6, 0xbc, 0x36, 0x0f, 0xa3, - 0x46, 0xf4, 0x7b, 0x7d, 0x80, 0x2c, 0x70, 0x96, 0xc7, 0xdb, 0x6c, 0x16, - 0x46, 0x8d, 0xe8, 0xf5, 0x95, 0x2c, 0x82, 0x1d, 0xa1, 0x2f, 0x04, 0x5c, + 0x06, 0xc6, 0x40, 0x62, 0xcb, 0xd0, 0x5b, 0x2c, 0xbf, 0x14, 0x7f, 0x26, + 0x2c, 0xa3, 0x3f, 0x7f, 0x19, 0xf4, 0x72, 0xff, 0xff, 0xa2, 0x38, 0xdf, + 0xa3, 0x36, 0x7d, 0x2e, 0x7d, 0x2c, 0xd6, 0xcb, 0x29, 0xba, 0x26, 0x88, + 0xba, 0xd1, 0xb2, 0x6b, 0x3a, 0x8c, 0xd6, 0xff, 0x37, 0xcd, 0x05, 0xdc, + 0x2a, 0x8b, 0x9d, 0x7f, 0xff, 0xff, 0xf4, 0x36, 0xac, 0xb6, 0x0f, 0xa7, + 0x4e, 0xf4, 0xfb, 0x7d, 0x80, 0x2c, 0xf0, 0x86, 0xcf, 0xdb, 0x6c, 0x16, + 0x4e, 0x9d, 0xe9, 0xf5, 0x95, 0x0c, 0x82, 0x1d, 0xa1, 0x2f, 0x24, 0x5c, 0x8e, 0x34, 0x25, 0xe7, 0x1f, 0x4b, 0x21, 0xa5, 0xe8, 0xe4, 0x7a, 0x61, 0xb8, 0x83, 0x7f, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0x3c, 0xbf, 0xfb, - 0x7e, 0x8f, 0x92, 0x37, 0x23, 0x62, 0xcb, 0xff, 0xff, 0xef, 0x66, 0xb5, - 0x3f, 0x40, 0x4e, 0x99, 0x02, 0x19, 0xc2, 0xcd, 0x84, 0x82, 0xcb, 0xe0, + 0x7e, 0x8f, 0x90, 0x37, 0x23, 0x62, 0xcb, 0xff, 0xff, 0xef, 0x66, 0xb5, + 0x1f, 0x48, 0x4e, 0x99, 0x02, 0x59, 0xc2, 0xcd, 0x84, 0x92, 0xcb, 0xe0, 0xbb, 0x85, 0x51, 0x13, 0x2f, 0xff, 0xf6, 0x8b, 0x36, 0xc1, 0xe3, 0x40, - 0xd6, 0x07, 0xd3, 0xbd, 0x65, 0xf0, 0x19, 0x83, 0x59, 0x52, 0x88, 0x5d, + 0xd6, 0x07, 0xd1, 0xbd, 0x65, 0xf0, 0x19, 0x83, 0x59, 0x50, 0x88, 0x5d, 0xc6, 0x3b, 0xf7, 0x79, 0xb6, 0x34, 0xb2, 0xb0, 0xf3, 0xdc, 0x96, 0xc0, - 0x59, 0x7f, 0xba, 0x07, 0xfb, 0xd3, 0xbd, 0x65, 0x05, 0x3e, 0x9d, 0x42, - 0x00, 0xa3, 0x02, 0xe1, 0x07, 0x44, 0x6f, 0xf9, 0xdc, 0xbf, 0xd1, 0xc8, - 0xab, 0x2f, 0x10, 0x36, 0x59, 0x7e, 0xff, 0x91, 0xb0, 0x6b, 0x2f, 0xf1, - 0xb5, 0x85, 0x9f, 0x75, 0x65, 0x19, 0xef, 0x68, 0xae, 0xff, 0xa7, 0xd3, - 0xbd, 0x92, 0x5b, 0x2c, 0xbd, 0xc7, 0x25, 0x97, 0xff, 0xe9, 0xd7, 0xfe, - 0x9d, 0xf1, 0x4c, 0xb3, 0x98, 0x35, 0x97, 0xfd, 0xef, 0x4e, 0xf8, 0x66, - 0xa5, 0x65, 0x79, 0x12, 0x4e, 0xb1, 0x51, 0x26, 0x13, 0xc3, 0xae, 0xc2, - 0xb6, 0xff, 0x8f, 0xa7, 0xdc, 0xfe, 0x74, 0xb2, 0xfb, 0x6f, 0x03, 0x65, - 0x95, 0x88, 0x94, 0x73, 0x70, 0x1c, 0x5f, 0x9f, 0x66, 0x4e, 0x96, 0x5c, - 0x3f, 0x2c, 0xbf, 0xfd, 0xd8, 0x66, 0xb4, 0xf0, 0xc6, 0x48, 0x56, 0x56, - 0x1f, 0x03, 0x8b, 0xdf, 0xf7, 0x41, 0xe9, 0x1f, 0x8f, 0xf5, 0x97, 0xfd, - 0x30, 0xc3, 0x2c, 0x91, 0x56, 0x5f, 0xff, 0xbd, 0x3a, 0xff, 0x98, 0x3e, - 0x81, 0xc3, 0x9a, 0x59, 0x5b, 0xa8, 0xc6, 0x88, 0xeb, 0xa6, 0xd7, 0x75, - 0x8b, 0x2f, 0xfc, 0x26, 0xdc, 0x93, 0xfb, 0xb2, 0xc5, 0x97, 0xff, 0x71, - 0xdc, 0x38, 0x19, 0x29, 0x0a, 0xcb, 0x67, 0x91, 0x05, 0xc4, 0x1b, 0xd3, - 0xae, 0x2c, 0xbf, 0xfd, 0x9b, 0xcb, 0x38, 0x1f, 0x1e, 0xe1, 0xf1, 0x65, - 0xff, 0x4c, 0x04, 0xcd, 0x73, 0x1a, 0x59, 0x7f, 0xa1, 0x22, 0x9f, 0x9f, - 0x65, 0x95, 0xb1, 0xf7, 0x19, 0xd5, 0x62, 0x7d, 0xdf, 0x1a, 0x7a, 0x13, - 0x3f, 0x94, 0x10, 0xe0, 0x21, 0x77, 0x7f, 0xc3, 0xcd, 0xfa, 0x7d, 0xfe, - 0x35, 0x97, 0xe6, 0x81, 0x3a, 0xd9, 0x65, 0x8e, 0x31, 0xf3, 0x34, 0x79, - 0x7d, 0x0f, 0x09, 0xc5, 0x97, 0x38, 0x8a, 0x88, 0x5d, 0x6e, 0xfc, 0x79, - 0x00, 0x24, 0xbf, 0x7b, 0x36, 0x01, 0x2c, 0xbe, 0x7d, 0x4e, 0xf5, 0x94, - 0x33, 0xcb, 0xfc, 0xa2, 0xfd, 0x14, 0x31, 0xc9, 0x65, 0xf1, 0x10, 0x05, - 0x59, 0x66, 0xf2, 0xca, 0x97, 0xc9, 0x45, 0xa2, 0xaa, 0x99, 0xc6, 0x9e, - 0xfd, 0x1d, 0xf3, 0x97, 0x14, 0x22, 0x39, 0x2b, 0x53, 0xb0, 0xc8, 0x03, - 0x96, 0xf7, 0x48, 0xf2, 0x31, 0x09, 0xef, 0xfd, 0xa0, 0x6d, 0x9e, 0x88, - 0xa4, 0x6b, 0x2f, 0xf7, 0x7d, 0x99, 0xdf, 0x62, 0xcb, 0xe1, 0x89, 0x24, - 0xb2, 0xff, 0xfe, 0x3e, 0x83, 0x4f, 0xcc, 0x2f, 0x66, 0x14, 0x38, 0xb2, - 0xa4, 0xfe, 0x9c, 0x8a, 0xfc, 0x0d, 0x7f, 0x1a, 0x56, 0x5d, 0xe7, 0x59, - 0x71, 0x8a, 0xb2, 0xc0, 0x59, 0x58, 0x6f, 0xa2, 0x16, 0xe0, 0xbd, 0xdd, - 0xea, 0xcb, 0xdf, 0xf1, 0xbc, 0xa7, 0x75, 0x88, 0x1e, 0x85, 0x67, 0x08, - 0x3a, 0xd2, 0x21, 0x75, 0xf8, 0x6d, 0xf0, 0xf7, 0x56, 0x5f, 0xfe, 0x38, - 0x71, 0xbe, 0x7d, 0x07, 0xd8, 0x40, 0xac, 0xa9, 0x3f, 0xbc, 0x2c, 0xa9, - 0x4c, 0x33, 0xd0, 0xec, 0xbf, 0xe6, 0xcb, 0x63, 0x59, 0xd3, 0xf0, 0x16, - 0x5f, 0x70, 0xf6, 0x6c, 0x2c, 0xbf, 0x00, 0x88, 0xe0, 0xb2, 0x9b, 0x28, - 0x8e, 0x8e, 0x90, 0x78, 0x4f, 0x7f, 0xe6, 0xcb, 0x60, 0xf6, 0x98, 0xc1, - 0xe6, 0x2c, 0xb9, 0xb4, 0x25, 0x97, 0xbd, 0x9a, 0x59, 0x4d, 0x94, 0x40, - 0x47, 0x49, 0x3e, 0x1b, 0xbf, 0x67, 0x4f, 0x58, 0xb2, 0xff, 0xb3, 0xa2, - 0x6c, 0xfa, 0x38, 0x96, 0x5f, 0xd0, 0x6d, 0x36, 0x6c, 0x47, 0x08, 0xe2, - 0xb2, 0xf8, 0x65, 0x9e, 0x59, 0x79, 0xb5, 0x36, 0xbc, 0x72, 0xb2, 0xff, - 0x67, 0x39, 0x86, 0x41, 0x59, 0x51, 0xd2, 0x64, 0xed, 0x82, 0x68, 0xe4, - 0xee, 0x3b, 0x45, 0x8e, 0x04, 0x24, 0x5f, 0x7f, 0x36, 0x44, 0xf7, 0xb3, - 0x65, 0x97, 0x47, 0xfc, 0xb2, 0xde, 0x59, 0x4d, 0x93, 0xe0, 0x6d, 0x73, - 0x40, 0x0d, 0xdf, 0x0f, 0x0a, 0x25, 0x97, 0x1f, 0x56, 0x5b, 0xa6, 0x6e, - 0x3c, 0x45, 0x7f, 0xbe, 0xef, 0xd0, 0x01, 0x0a, 0xb2, 0xff, 0xd3, 0x11, - 0x66, 0xf7, 0x63, 0x0d, 0x65, 0x39, 0xfb, 0x00, 0xde, 0xff, 0xff, 0xef, - 0x00, 0x67, 0x9a, 0x88, 0xb3, 0x7f, 0xb0, 0x73, 0xce, 0x48, 0xab, 0x2f, - 0xff, 0x1c, 0x50, 0xcf, 0xa1, 0x19, 0x8e, 0xe4, 0xb2, 0xe7, 0xdc, 0x59, - 0x7f, 0xff, 0xb9, 0x9d, 0x8d, 0xec, 0x0c, 0xb4, 0x58, 0x3f, 0x1c, 0x4b, - 0x2c, 0xd8, 0xc4, 0xfe, 0x45, 0x84, 0xe0, 0x48, 0x7c, 0xe8, 0xe9, 0x9d, - 0x19, 0xba, 0x3c, 0x55, 0x97, 0xff, 0xe1, 0xc6, 0xce, 0x16, 0xdb, 0x3e, - 0xec, 0x68, 0xde, 0x8f, 0x59, 0x7c, 0xf0, 0x9f, 0xd6, 0x5f, 0xbc, 0xfd, - 0x78, 0x2c, 0xbf, 0x8f, 0x45, 0x99, 0xa5, 0x97, 0x83, 0xb7, 0x96, 0x5f, - 0xa7, 0x8d, 0xa9, 0xb2, 0xd9, 0x59, 0x73, 0x18, 0xb2, 0xb8, 0x79, 0xa0, - 0x36, 0xbf, 0x4f, 0x01, 0xe3, 0x59, 0x51, 0x91, 0x69, 0x06, 0xbe, 0x11, - 0x5f, 0xf7, 0x0b, 0x3e, 0x81, 0x3e, 0xcb, 0x2f, 0xdc, 0x03, 0x97, 0xeb, - 0x2c, 0xc8, 0xc7, 0xc3, 0xd3, 0x9b, 0xdc, 0x3f, 0xd6, 0x56, 0x1e, 0x39, - 0x95, 0x5e, 0x69, 0xa6, 0x92, 0x5f, 0x9c, 0xbb, 0xec, 0x48, 0xdc, 0xd0, - 0x5f, 0xff, 0xfd, 0x3b, 0x46, 0xe0, 0x27, 0xee, 0xfb, 0x19, 0x9d, 0x29, - 0xff, 0xf9, 0x59, 0x7d, 0xd8, 0xb3, 0xab, 0x2c, 0x35, 0x94, 0x34, 0x55, - 0x3b, 0xb0, 0x08, 0xe8, 0x69, 0xa8, 0xfe, 0x80, 0x50, 0xe2, 0xbf, 0x78, - 0xa3, 0x8c, 0x7e, 0xe2, 0xcb, 0x9d, 0xd6, 0x5b, 0xc6, 0x79, 0x2e, 0x69, - 0x7f, 0xff, 0x7f, 0xa9, 0x8d, 0xa9, 0xef, 0xd0, 0x91, 0x74, 0xed, 0x2c, - 0xbf, 0xf1, 0xc5, 0x1b, 0xd2, 0x7a, 0x06, 0xea, 0xca, 0x94, 0x52, 0xfe, - 0xc3, 0x7f, 0x68, 0xf7, 0x35, 0xce, 0x2c, 0xbf, 0x88, 0x1d, 0x3d, 0xa5, - 0x65, 0xff, 0x8c, 0x59, 0x29, 0xe4, 0x9b, 0x16, 0x5f, 0xb7, 0x82, 0x19, - 0xc5, 0x97, 0xdf, 0x99, 0x01, 0x65, 0x61, 0xe6, 0x00, 0xaa, 0xfb, 0x07, - 0x9b, 0x2c, 0xbe, 0xdd, 0x8d, 0x31, 0xd2, 0xcb, 0xfd, 0xd7, 0xfb, 0x8f, - 0xf3, 0x4b, 0x2e, 0xd4, 0xac, 0xa8, 0xe9, 0x7b, 0x14, 0x6c, 0x98, 0x44, - 0x12, 0x7f, 0xa1, 0xbe, 0x72, 0x8a, 0xf7, 0x5f, 0xe2, 0x86, 0x5e, 0x88, - 0xd8, 0x63, 0xe2, 0xd2, 0x84, 0x47, 0x48, 0x77, 0x91, 0x08, 0x59, 0xb8, - 0x6d, 0x78, 0xb3, 0xab, 0x2f, 0xff, 0x7d, 0x0e, 0x72, 0x4f, 0xee, 0xf6, - 0x58, 0xb2, 0x88, 0xf9, 0xda, 0x1b, 0xbf, 0xf8, 0xba, 0x59, 0xbd, 0xcc, - 0x38, 0x4b, 0x2f, 0xa7, 0x06, 0x6b, 0x28, 0xcf, 0x89, 0xa4, 0x2b, 0xc0, - 0xef, 0x16, 0x5f, 0xf6, 0xeb, 0xf6, 0x28, 0x1e, 0xa2, 0x59, 0x7f, 0xd3, - 0x06, 0x9f, 0x5d, 0x9c, 0x59, 0x7f, 0xff, 0xff, 0xdc, 0x8d, 0xec, 0xda, - 0x5a, 0xfa, 0x1c, 0x8c, 0x00, 0xeb, 0x53, 0x1a, 0x21, 0x7e, 0x8d, 0x1b, - 0xd1, 0xeb, 0x2f, 0xef, 0xa3, 0x45, 0x98, 0x15, 0x95, 0x2a, 0x85, 0x72, - 0x10, 0x81, 0x22, 0x71, 0xd2, 0x3e, 0xe9, 0xc0, 0x21, 0x57, 0x66, 0x96, - 0x5f, 0xd0, 0x83, 0x22, 0x9d, 0xc5, 0x96, 0xfd, 0x65, 0x7e, 0x7a, 0xc4, - 0x24, 0x03, 0x2b, 0xa3, 0xe5, 0x65, 0xe8, 0xa7, 0xe5, 0x94, 0x46, 0xdf, - 0xa3, 0x37, 0x47, 0xb6, 0x56, 0x5f, 0xfe, 0xfe, 0x48, 0x31, 0xb0, 0x81, - 0x0c, 0xe2, 0xcb, 0xfc, 0xff, 0x7f, 0xa3, 0xfb, 0xf5, 0x97, 0xfe, 0x03, - 0x27, 0x38, 0x59, 0xbd, 0xd6, 0x5f, 0xef, 0xdc, 0xb3, 0x61, 0x20, 0xb2, - 0xbe, 0x47, 0xaf, 0x92, 0xf8, 0x6f, 0xd3, 0xeb, 0xd1, 0xa0, 0x15, 0x95, - 0x29, 0xfb, 0x63, 0x30, 0xa4, 0x07, 0x18, 0x9b, 0x0f, 0xaf, 0xff, 0xed, - 0x46, 0x2c, 0xdf, 0x9a, 0x84, 0x69, 0x1b, 0xc5, 0xc5, 0x97, 0xff, 0x63, - 0x5b, 0x8c, 0x01, 0xb3, 0x0b, 0xf5, 0x97, 0xff, 0xfe, 0xfa, 0x45, 0x2c, - 0x1f, 0x8e, 0x28, 0xdc, 0xcf, 0xa1, 0xac, 0x69, 0x65, 0x1a, 0x2e, 0x89, - 0x22, 0xb1, 0x33, 0xc2, 0x8c, 0x6e, 0xfb, 0xa0, 0x9d, 0x96, 0x5f, 0xd0, - 0x7d, 0x70, 0xc6, 0xb2, 0xfe, 0xcf, 0x63, 0x24, 0x2b, 0x2a, 0x4f, 0x66, - 0x05, 0xb7, 0xe3, 0xd1, 0xef, 0xc5, 0x94, 0x68, 0xc5, 0xeb, 0xe6, 0xe1, - 0x0d, 0xf4, 0x7e, 0x6a, 0x56, 0x5f, 0xff, 0xdf, 0x43, 0x8c, 0xc2, 0x8d, - 0xb8, 0x7e, 0xd8, 0xf3, 0x4b, 0x2d, 0xba, 0xb2, 0xb0, 0xfd, 0x42, 0xc3, - 0x7f, 0xff, 0xe6, 0xa3, 0x16, 0x07, 0xc6, 0x58, 0x0f, 0xbb, 0x19, 0xda, - 0xc1, 0x56, 0x5f, 0xe1, 0x46, 0x09, 0xf1, 0x4a, 0xcb, 0xfa, 0x1c, 0x14, - 0x13, 0xf2, 0xcb, 0xec, 0x1b, 0xc1, 0x65, 0x7c, 0x7a, 0x2d, 0x18, 0x5f, - 0x6a, 0x67, 0x65, 0x97, 0xd1, 0x4f, 0x5d, 0x65, 0xce, 0xd7, 0x0f, 0x10, - 0x04, 0x57, 0xb7, 0xf4, 0x45, 0x95, 0x29, 0xd6, 0x63, 0x91, 0xc2, 0x10, - 0x9a, 0x1a, 0x2e, 0xbf, 0xff, 0x16, 0x0f, 0xc0, 0x6b, 0x87, 0xdf, 0x1b, - 0xfe, 0xb2, 0xff, 0x16, 0x6f, 0x8d, 0x06, 0x41, 0x65, 0x69, 0x11, 0xbc, - 0x56, 0xbb, 0x9b, 0xd6, 0x54, 0x9b, 0xc0, 0x11, 0xdf, 0x7f, 0xd3, 0xd9, - 0x65, 0xfe, 0xef, 0x8c, 0xbf, 0x11, 0x8b, 0x2f, 0xff, 0xef, 0x1e, 0x16, - 0x18, 0xf2, 0x7f, 0x70, 0xbe, 0x96, 0x5f, 0xed, 0xa5, 0x85, 0x3f, 0x7e, - 0xb2, 0xa0, 0x8c, 0x1f, 0x8d, 0x5d, 0x5e, 0xfd, 0xe8, 0xcf, 0x9c, 0x59, - 0x52, 0x7b, 0x38, 0x61, 0x7f, 0x74, 0xa6, 0x19, 0xd5, 0x97, 0x9a, 0x7e, - 0x2c, 0xbc, 0xfa, 0x84, 0x63, 0xc9, 0xf1, 0x65, 0x4a, 0xf4, 0xbe, 0x43, - 0xb7, 0xe3, 0x23, 0x84, 0xce, 0xa3, 0xcd, 0xf4, 0x62, 0xce, 0x40, 0x51, - 0x9f, 0x01, 0xa6, 0xdb, 0xab, 0x2d, 0xd5, 0x97, 0x4f, 0xeb, 0x2f, 0xff, - 0x18, 0x1b, 0x73, 0x1b, 0x80, 0xfd, 0x99, 0xd5, 0x95, 0xf1, 0xf4, 0x68, - 0x5e, 0xff, 0xd2, 0x1e, 0x49, 0xfd, 0xd9, 0x62, 0xca, 0x59, 0x7b, 0x40, - 0xdd, 0x59, 0x50, 0x35, 0x7d, 0x0b, 0xa2, 0x44, 0x50, 0x1a, 0x6f, 0xbf, - 0xe6, 0xdd, 0x59, 0x7f, 0xe7, 0x16, 0x37, 0x5d, 0xca, 0x36, 0xcb, 0x2a, - 0x4f, 0xa0, 0x89, 0x6f, 0x3f, 0x78, 0xb2, 0xe0, 0x6c, 0xb2, 0xa0, 0x6d, - 0x34, 0x39, 0x7e, 0xc8, 0x79, 0xe0, 0xb2, 0xe2, 0xec, 0x47, 0x91, 0xe2, - 0x1b, 0xfc, 0xe4, 0x27, 0xbd, 0x23, 0x59, 0x7f, 0x00, 0x31, 0x87, 0xf3, - 0x4b, 0x2f, 0xf3, 0x5e, 0xc2, 0xfa, 0x45, 0x59, 0x7f, 0x83, 0xec, 0xee, - 0xb1, 0xa5, 0x95, 0x27, 0xd2, 0x66, 0xb7, 0x6a, 0x0b, 0x2f, 0xf0, 0xaf, - 0xcc, 0xe8, 0x9b, 0x2c, 0xbe, 0x8c, 0x16, 0xa5, 0x65, 0x40, 0xf6, 0xd8, - 0x6d, 0x52, 0x9d, 0x8f, 0xc5, 0xc6, 0x67, 0xa8, 0x4e, 0x30, 0x81, 0xdc, - 0xaf, 0xee, 0x0c, 0x9d, 0xfc, 0xb2, 0xff, 0x6a, 0x47, 0x3e, 0x98, 0x2c, - 0xbf, 0xff, 0xf1, 0xfd, 0xd7, 0xf1, 0x60, 0xfc, 0x71, 0x70, 0x38, 0x65, - 0x12, 0xcb, 0xff, 0xe9, 0xec, 0x62, 0x06, 0xe4, 0x6c, 0xec, 0x7e, 0x75, - 0x65, 0x1a, 0x3d, 0xf4, 0x64, 0x06, 0xcb, 0x9f, 0xab, 0x2f, 0xfe, 0xf4, - 0x8b, 0x1b, 0xa4, 0xfd, 0x9e, 0xac, 0xbf, 0xd1, 0xbe, 0x81, 0xf8, 0x0c, - 0x59, 0x5e, 0x44, 0x9f, 0xe2, 0xc4, 0x8d, 0x7e, 0x69, 0xfd, 0x13, 0x65, - 0x65, 0xff, 0x60, 0xe3, 0x74, 0x53, 0x6b, 0x8b, 0x2d, 0x9a, 0x3e, 0xbd, - 0xe5, 0xd7, 0xfe, 0xd4, 0x51, 0xb9, 0x27, 0xb3, 0xf5, 0x65, 0x4a, 0x60, - 0xc3, 0x84, 0xd8, 0x0a, 0x6e, 0x7f, 0xd6, 0x5f, 0xff, 0x46, 0x29, 0xe4, - 0xec, 0x58, 0x3f, 0x1c, 0x4b, 0x2f, 0xff, 0xfe, 0xe6, 0x8a, 0x62, 0x8c, - 0x27, 0x4e, 0x19, 0xf7, 0x73, 0x79, 0x67, 0x16, 0x56, 0x91, 0x90, 0x4a, - 0x35, 0xd4, 0xc0, 0x41, 0x0d, 0xfa, 0x59, 0x7b, 0xb1, 0x85, 0x59, 0x74, - 0x50, 0x59, 0x52, 0x78, 0x7f, 0x05, 0xe8, 0x82, 0xff, 0xe2, 0xef, 0x37, - 0x9f, 0x67, 0x0a, 0x25, 0x97, 0xed, 0x1f, 0x27, 0xe5, 0x95, 0x87, 0xda, - 0xe8, 0xb7, 0xf3, 0x62, 0x28, 0x1f, 0x78, 0xb2, 0xf7, 0x04, 0xe2, 0xca, - 0xd8, 0xf4, 0x58, 0x67, 0x74, 0xfe, 0xb2, 0xfc, 0x5d, 0xf6, 0x6e, 0x2c, - 0xbf, 0x45, 0xc7, 0xd0, 0xab, 0x2f, 0x3b, 0x5a, 0x93, 0xd4, 0xc2, 0xab, - 0xfe, 0x21, 0x66, 0x23, 0x29, 0x62, 0xcb, 0xf8, 0x57, 0x81, 0x4b, 0x16, - 0x54, 0x9f, 0x29, 0x9c, 0x54, 0x13, 0xb6, 0x37, 0x18, 0x88, 0xf4, 0xd7, - 0xe8, 0x4b, 0xdf, 0x18, 0x5f, 0x4b, 0x2f, 0x31, 0xda, 0x59, 0x6e, 0x40, - 0xdf, 0x39, 0x0d, 0xfe, 0x31, 0xe1, 0x43, 0xd8, 0xb2, 0xff, 0xbc, 0x7a, - 0x8b, 0xcf, 0x9a, 0x59, 0x7f, 0xff, 0xff, 0x7f, 0xc2, 0xc7, 0xfa, 0x30, - 0xfc, 0x08, 0xc5, 0x9f, 0x43, 0x01, 0xb1, 0x63, 0xfc, 0xb2, 0x82, 0x8c, - 0x83, 0x39, 0xbc, 0xee, 0x15, 0x45, 0x30, 0xa9, 0x4d, 0xa7, 0x62, 0x6c, - 0x87, 0xc8, 0x48, 0xaf, 0xfd, 0x85, 0xf4, 0x39, 0xd9, 0xd4, 0x4b, 0x2f, - 0xfc, 0xff, 0x61, 0xe8, 0xd9, 0x22, 0xac, 0xbd, 0x87, 0xfa, 0xcb, 0x62, - 0xcb, 0x67, 0xe6, 0xb3, 0x78, 0xe5, 0x2c, 0xb7, 0xc4, 0x6d, 0x38, 0x53, - 0x46, 0x98, 0x2e, 0x90, 0x1e, 0x13, 0xd7, 0xb0, 0x1a, 0x59, 0x76, 0xd1, - 0x2c, 0xb3, 0x4b, 0x2e, 0x31, 0xac, 0xad, 0xd3, 0x51, 0xc1, 0x2b, 0x87, - 0x12, 0xcb, 0xfe, 0xd6, 0x0f, 0xd2, 0xd3, 0x00, 0xb2, 0xe1, 0x76, 0x59, - 0x52, 0x8d, 0x0c, 0x41, 0x61, 0x27, 0x86, 0x1a, 0x3a, 0xbf, 0x63, 0x18, - 0xec, 0x59, 0x73, 0x3f, 0x59, 0x7b, 0x5e, 0x78, 0xc8, 0x88, 0xd2, 0x4f, - 0xe4, 0xf7, 0xfa, 0x37, 0xd0, 0x29, 0xce, 0x2c, 0xaf, 0x93, 0xed, 0x38, - 0xdd, 0x1d, 0x12, 0xff, 0xfd, 0xf9, 0xf2, 0x37, 0x9f, 0x9d, 0x98, 0xb9, - 0xec, 0x59, 0x4b, 0x2f, 0xe0, 0x18, 0xcc, 0x8d, 0x65, 0x85, 0x33, 0x6c, - 0xc0, 0xbb, 0xf3, 0xf3, 0xcf, 0x05, 0x97, 0xef, 0x3c, 0x0d, 0x8b, 0x2b, - 0x47, 0xeb, 0xe2, 0x76, 0x89, 0xef, 0xe3, 0xf3, 0x9e, 0xd2, 0xb2, 0xf4, - 0x27, 0xe5, 0x97, 0xff, 0xe2, 0xcf, 0xbb, 0x1b, 0xcf, 0xbe, 0x30, 0xfc, - 0x0d, 0x96, 0x5f, 0xbd, 0x3b, 0x63, 0x4b, 0x2c, 0xc9, 0x44, 0x38, 0xd7, - 0xaf, 0xbb, 0x0c, 0xea, 0xca, 0x94, 0xd2, 0xb0, 0xc3, 0xe2, 0xc7, 0x85, - 0x20, 0x0a, 0x2f, 0xdf, 0x04, 0xa7, 0x7a, 0xcb, 0xf9, 0xf5, 0x14, 0x0f, - 0xcb, 0x2f, 0xf0, 0x36, 0xf1, 0xef, 0xc8, 0x96, 0x5f, 0xf9, 0xfe, 0xe0, - 0x9e, 0xf4, 0x90, 0x56, 0x56, 0x23, 0x3d, 0xca, 0xb8, 0x5c, 0x03, 0x7b, - 0xb9, 0xc5, 0x96, 0xc5, 0x97, 0xfe, 0x88, 0xc5, 0x3e, 0x96, 0x6e, 0x46, - 0xd1, 0xa7, 0xde, 0x2f, 0x7f, 0xff, 0x8b, 0x03, 0xde, 0xcf, 0x79, 0x11, - 0x63, 0x51, 0xa6, 0x3a, 0x59, 0x7f, 0xe0, 0xcb, 0x3d, 0x9c, 0xc3, 0x25, - 0x95, 0x28, 0xa4, 0xf3, 0x45, 0xfb, 0x53, 0xbe, 0x74, 0xb2, 0xfe, 0x72, - 0x8c, 0x39, 0x25, 0x95, 0x29, 0xbc, 0x64, 0x60, 0x06, 0x44, 0x02, 0x9b, - 0xfe, 0x96, 0x82, 0x7b, 0x67, 0xdd, 0x59, 0x51, 0xcb, 0x6d, 0x42, 0xda, - 0x85, 0x25, 0xe7, 0x68, 0x56, 0xc2, 0x11, 0x43, 0x94, 0x9b, 0x8b, 0x62, - 0xc6, 0x46, 0x18, 0xf2, 0x3e, 0x8c, 0xd8, 0xdb, 0x22, 0x94, 0xa5, 0xa8, - 0xfd, 0xd9, 0x1a, 0x3f, 0xa5, 0x10, 0xfe, 0x6c, 0x52, 0x93, 0xb9, 0x18, - 0xef, 0x63, 0xda, 0xde, 0x83, 0x7f, 0xda, 0x76, 0xa3, 0x38, 0xdf, 0x65, - 0x97, 0xf3, 0xb5, 0x11, 0x18, 0xd6, 0x5e, 0x3d, 0x98, 0xb2, 0xff, 0x7d, - 0xec, 0xfd, 0x92, 0x15, 0x97, 0xff, 0xf6, 0x6c, 0xe5, 0xde, 0x48, 0x63, - 0x31, 0x86, 0x41, 0x59, 0x7f, 0x43, 0x91, 0x42, 0x62, 0x59, 0x43, 0x44, - 0x67, 0xcb, 0x97, 0xf0, 0xf8, 0xf3, 0xf4, 0x16, 0x54, 0x9e, 0x93, 0x92, - 0x5f, 0xe8, 0xbc, 0x72, 0xc7, 0x1a, 0xcb, 0xfd, 0x1b, 0x58, 0x7e, 0x91, - 0xac, 0xa9, 0x3e, 0x72, 0x33, 0xa1, 0x55, 0x20, 0xe8, 0xf3, 0xc5, 0xce, - 0x3a, 0x51, 0x89, 0x82, 0x10, 0xf7, 0xd8, 0x42, 0xee, 0xac, 0xbf, 0xff, - 0xff, 0x78, 0xfb, 0xec, 0xd4, 0xef, 0x8c, 0xc0, 0x6e, 0x46, 0xcf, 0xa1, - 0x80, 0x2f, 0xf8, 0xb2, 0xdd, 0x34, 0x59, 0xf0, 0x96, 0xff, 0xcc, 0x61, - 0xc5, 0xe7, 0x29, 0x95, 0x97, 0xf9, 0xe1, 0x17, 0x8f, 0xee, 0xac, 0xbf, - 0xf4, 0x86, 0x31, 0x3f, 0x19, 0x24, 0xb2, 0xff, 0xf7, 0x8c, 0x3e, 0xc8, - 0xdc, 0x07, 0xa7, 0x75, 0x65, 0xfc, 0x53, 0xf4, 0x0c, 0x96, 0x52, 0xcb, - 0xfd, 0x9e, 0x2c, 0xef, 0x8d, 0x65, 0x0c, 0xfa, 0x08, 0xb3, 0xa1, 0x77, - 0xfd, 0xd9, 0x87, 0x22, 0x84, 0xec, 0xb2, 0xd9, 0x89, 0x90, 0x7d, 0x0b, - 0x7e, 0x97, 0x5f, 0xff, 0x77, 0xd9, 0xfc, 0x66, 0x4e, 0x74, 0x78, 0x4b, - 0x2c, 0xd2, 0xcb, 0xf9, 0xc7, 0xad, 0x03, 0x65, 0x97, 0xe9, 0xc2, 0xec, - 0x7a, 0xca, 0x14, 0xfa, 0x82, 0x24, 0xc2, 0xfb, 0xee, 0x9e, 0xb1, 0x65, - 0xbe, 0x8c, 0x7a, 0x38, 0x61, 0x6e, 0x62, 0x66, 0xcf, 0x18, 0x65, 0x0d, - 0x59, 0x93, 0x0d, 0x4a, 0x35, 0xee, 0xc6, 0xe3, 0x7f, 0xff, 0xf4, 0x0f, - 0x85, 0x9b, 0xfd, 0x91, 0x7a, 0x4a, 0x28, 0xce, 0x13, 0xde, 0xb2, 0xff, - 0xf1, 0xe3, 0x45, 0x9c, 0xcf, 0x78, 0xf8, 0xb2, 0xd9, 0xf2, 0x2f, 0xfa, - 0xef, 0x7f, 0xff, 0xfd, 0xa9, 0x68, 0xb3, 0x7f, 0xb3, 0x5a, 0xce, 0xf3, - 0x73, 0xb3, 0xcf, 0x1e, 0x2c, 0xbf, 0xff, 0xec, 0x21, 0x63, 0x6a, 0x77, - 0x9f, 0x79, 0x82, 0x9e, 0x9f, 0xe5, 0x94, 0x49, 0x8a, 0xf4, 0xa4, 0x48, - 0x40, 0xd1, 0xaa, 0x54, 0x04, 0xa1, 0xeb, 0xff, 0xc6, 0x38, 0xcc, 0x38, - 0xd2, 0x37, 0x8b, 0x8b, 0x2f, 0xde, 0x3e, 0xe3, 0x16, 0x5a, 0x0b, 0x29, - 0xb0, 0x6e, 0x20, 0x4f, 0x6d, 0x4a, 0x2a, 0xf6, 0x84, 0x35, 0xfa, 0x13, - 0xf4, 0x38, 0xb2, 0xff, 0xff, 0xff, 0xd8, 0x42, 0xc6, 0x3f, 0x01, 0xbc, - 0x47, 0xd9, 0x61, 0x38, 0xbe, 0x36, 0xf9, 0xef, 0xbf, 0x91, 0xa4, 0xbf, - 0xff, 0xf8, 0x26, 0xd0, 0xc1, 0xa8, 0xc0, 0x9d, 0xb8, 0x1f, 0x03, 0xdf, - 0x3b, 0x4b, 0x2e, 0x06, 0xdf, 0x26, 0x8a, 0x65, 0x3c, 0x85, 0x15, 0xfc, - 0x29, 0x67, 0x7c, 0x6b, 0x2e, 0x1e, 0x2c, 0xa9, 0x64, 0x5d, 0x19, 0x47, - 0xa7, 0x70, 0xde, 0x1a, 0xc5, 0x1b, 0x98, 0x10, 0xe3, 0xcb, 0x6d, 0xb8, - 0xb2, 0xcc, 0x59, 0x7f, 0x9e, 0x18, 0xc7, 0x2f, 0xd6, 0x5f, 0x67, 0xa6, - 0x25, 0x97, 0x98, 0x18, 0xd1, 0xc9, 0xf2, 0xf8, 0x45, 0xcc, 0xaf, 0xff, - 0xff, 0x68, 0x25, 0x9b, 0xe3, 0x70, 0xf0, 0xbf, 0x8c, 0x59, 0xbc, 0xb3, - 0x9b, 0xd6, 0x5f, 0xfe, 0xf0, 0x30, 0x85, 0x8d, 0xdf, 0xa1, 0x84, 0xb2, - 0xff, 0x63, 0xb2, 0x34, 0x7c, 0xb1, 0x65, 0xff, 0xe8, 0x4f, 0x3c, 0x0d, - 0x67, 0x7a, 0x6c, 0x59, 0x43, 0x46, 0x70, 0x53, 0x7e, 0x37, 0xbf, 0xff, - 0x02, 0x63, 0x16, 0x0f, 0xc6, 0x28, 0xe4, 0x5f, 0xd6, 0x56, 0x27, 0xaf, - 0xa8, 0xd3, 0x48, 0xc6, 0xff, 0xf7, 0x7d, 0x9b, 0x9d, 0x8b, 0x37, 0x5c, - 0xbf, 0x59, 0x7f, 0xa5, 0x8e, 0x5f, 0x42, 0x56, 0x58, 0xc9, 0x10, 0x7d, - 0x51, 0xbe, 0x6a, 0x37, 0x22, 0x59, 0x6e, 0xac, 0xa9, 0x37, 0x0e, 0x4f, - 0x7f, 0xe2, 0xd8, 0xb3, 0x7f, 0x01, 0x3f, 0x2c, 0xbf, 0xef, 0x4f, 0xd1, - 0xa3, 0x8e, 0xff, 0xcd, 0x65, 0xfb, 0xbf, 0x40, 0xe3, 0xd6, 0x50, 0xcf, - 0xc7, 0x88, 0xb5, 0x28, 0xd4, 0xda, 0x16, 0x54, 0x6a, 0x8c, 0xb9, 0x0b, - 0x8e, 0xaf, 0x82, 0x1e, 0x77, 0xed, 0xfb, 0x9d, 0xcf, 0x2c, 0xbf, 0xf6, - 0x70, 0x12, 0x2f, 0x27, 0x51, 0x2c, 0xa3, 0x3e, 0xd6, 0x8b, 0x6f, 0xb5, - 0x07, 0x1a, 0xcb, 0x9c, 0x2b, 0x2f, 0xda, 0xc8, 0xf3, 0xfd, 0x65, 0xff, - 0x40, 0x3e, 0x3f, 0x10, 0x36, 0x59, 0x5b, 0x1f, 0x29, 0x15, 0xde, 0xee, - 0x36, 0xd6, 0x53, 0x61, 0x17, 0xb2, 0xf7, 0x84, 0x57, 0xfa, 0x73, 0xbf, - 0x88, 0x50, 0x59, 0x76, 0x79, 0x65, 0x11, 0xe5, 0x47, 0x9a, 0x5c, 0x2c, - 0xac, 0xbb, 0x06, 0xb2, 0xa3, 0xa5, 0xf4, 0xcd, 0x9e, 0x61, 0x38, 0xa1, - 0x90, 0xb5, 0x72, 0x2e, 0x43, 0x7f, 0xaf, 0x80, 0x24, 0xde, 0x2f, 0x7e, - 0x3e, 0xf3, 0x3f, 0x59, 0x7e, 0xf6, 0x6f, 0x3d, 0xeb, 0x2f, 0xff, 0xfc, - 0x4f, 0xdf, 0x4c, 0x51, 0x8b, 0x3c, 0x7d, 0xe0, 0x02, 0x09, 0x59, 0x76, - 0x7e, 0xb2, 0xff, 0xe2, 0xfc, 0xb0, 0xe2, 0xf4, 0x90, 0xab, 0x28, 0xcf, - 0x6d, 0xc5, 0xef, 0xe8, 0x73, 0xa7, 0x3f, 0x2c, 0xbf, 0x49, 0x79, 0xe0, - 0xb2, 0xf4, 0x8e, 0x56, 0x50, 0xcf, 0x05, 0x84, 0xd4, 0xb2, 0xf4, 0x8e, - 0x56, 0x5f, 0xb3, 0xbc, 0x78, 0xa3, 0x1e, 0x44, 0x91, 0x30, 0x2e, 0xfa, - 0x39, 0x6d, 0x51, 0xdc, 0x72, 0xb2, 0xf8, 0xdc, 0x86, 0xb2, 0xcd, 0x46, - 0x3d, 0x81, 0x9c, 0x5f, 0xdf, 0xe3, 0xfd, 0x0e, 0x2c, 0xa8, 0x23, 0xfc, - 0xe1, 0x4b, 0xc2, 0xbb, 0xff, 0xf6, 0x81, 0xad, 0x49, 0x60, 0xa7, 0xef, - 0x60, 0x8b, 0x2f, 0xbd, 0x20, 0x25, 0x97, 0xc0, 0x7f, 0xba, 0xb2, 0xa0, - 0xae, 0xfc, 0x65, 0x18, 0x56, 0x2c, 0x32, 0x3e, 0x20, 0x8a, 0x3b, 0x6d, - 0x1a, 0x31, 0x5b, 0xc4, 0x36, 0xdc, 0x59, 0x4b, 0x2f, 0xec, 0x60, 0xfd, - 0x9c, 0x59, 0x7f, 0x70, 0x21, 0x72, 0x15, 0x65, 0x36, 0x87, 0xcd, 0xb0, - 0x5f, 0x4b, 0x6f, 0xf1, 0x46, 0xdf, 0xf4, 0x39, 0x12, 0xcb, 0xb6, 0xf9, - 0x65, 0xfb, 0xcf, 0xbb, 0x3f, 0xac, 0xa1, 0x9f, 0xf3, 0x9d, 0x6e, 0x0c, - 0xdf, 0xd1, 0x16, 0x79, 0xfa, 0xb2, 0xfe, 0x14, 0xfe, 0xe0, 0x38, 0xb2, - 0xb4, 0x7b, 0xa4, 0x5b, 0x52, 0x9b, 0x16, 0x42, 0xcc, 0xe1, 0x1d, 0x7f, - 0xb5, 0x83, 0x8a, 0x13, 0xba, 0xb2, 0xfb, 0x43, 0xc2, 0x59, 0x7d, 0xff, - 0x37, 0x42, 0xb2, 0x8c, 0xf2, 0x00, 0x43, 0x7e, 0xff, 0x3a, 0x26, 0xcb, - 0x2d, 0xd5, 0x96, 0xe2, 0xcb, 0xf3, 0x5d, 0xcf, 0x46, 0x14, 0xd1, 0x7c, - 0x23, 0x52, 0x89, 0x07, 0x4d, 0xa8, 0xed, 0x5b, 0x2c, 0x23, 0xdc, 0x33, - 0x8f, 0x3f, 0x94, 0x2c, 0x6f, 0xef, 0x60, 0x61, 0x2c, 0x59, 0x7f, 0xbd, - 0x90, 0x72, 0xcf, 0x96, 0x56, 0x1e, 0xff, 0x4b, 0x6f, 0xff, 0xfd, 0x09, - 0xdb, 0xe8, 0x70, 0xc5, 0x8d, 0xcc, 0x09, 0xb0, 0x1a, 0xd9, 0x65, 0xff, - 0xfe, 0x7f, 0xf9, 0xb6, 0x19, 0x74, 0xb3, 0x7c, 0x6e, 0x34, 0x4b, 0x2b, - 0xe4, 0x6c, 0x13, 0x9d, 0xfd, 0xec, 0x2f, 0xa4, 0x55, 0x97, 0xf3, 0x97, - 0x41, 0xad, 0x96, 0x51, 0x26, 0xf1, 0xd8, 0x7c, 0xb4, 0x46, 0x21, 0x75, - 0xc3, 0xdc, 0x59, 0x7b, 0x0e, 0x25, 0x97, 0xfe, 0x3c, 0x8a, 0x37, 0x27, - 0xcf, 0xba, 0xb2, 0xff, 0xb1, 0xfe, 0xf6, 0x77, 0xf7, 0x59, 0x7f, 0xfd, - 0x0e, 0x03, 0x0a, 0x31, 0x67, 0xbc, 0xe3, 0x59, 0x7f, 0xdf, 0x64, 0xfd, - 0x11, 0x4b, 0x16, 0x5f, 0xdf, 0x73, 0xd8, 0xfa, 0x59, 0x7f, 0xff, 0xe3, - 0x66, 0x8f, 0x3e, 0x20, 0x76, 0x7d, 0x1b, 0x79, 0xf6, 0x29, 0x59, 0x4e, - 0x89, 0xee, 0x97, 0x56, 0xc9, 0xf1, 0x0c, 0x70, 0x54, 0x3f, 0x8e, 0x7c, - 0xa1, 0xd8, 0x6c, 0x5f, 0xff, 0x63, 0xed, 0xe9, 0x3e, 0xf2, 0x76, 0xce, - 0x2c, 0xbf, 0xec, 0xd6, 0xd2, 0x7a, 0xc2, 0x59, 0x66, 0x2c, 0x8c, 0x6d, - 0xef, 0x16, 0x0d, 0x65, 0x49, 0xbe, 0xf8, 0x8e, 0xfc, 0x53, 0x8d, 0x6c, - 0xb2, 0xf4, 0x78, 0x3a, 0xb2, 0xa3, 0xb3, 0xc8, 0x81, 0x45, 0xfd, 0xf7, - 0x33, 0x08, 0x55, 0x97, 0xcc, 0x12, 0x7f, 0x59, 0x7f, 0xfb, 0x61, 0xe9, - 0xc2, 0x59, 0xbf, 0x47, 0xc5, 0x95, 0x28, 0xc5, 0xc2, 0x73, 0x2e, 0x01, - 0x25, 0xdd, 0xe2, 0xcb, 0xfb, 0xbe, 0x78, 0x84, 0x62, 0xcb, 0x9f, 0xcb, - 0x2e, 0xce, 0x2c, 0xb9, 0xf4, 0xb2, 0xf7, 0x67, 0xf8, 0x1a, 0xcd, 0x0b, - 0x53, 0xa2, 0x97, 0xf3, 0x02, 0x42, 0xba, 0x46, 0xb2, 0xef, 0xbf, 0x59, - 0x5e, 0x35, 0xe0, 0x16, 0xbf, 0xb9, 0x3f, 0x7f, 0xa9, 0x59, 0x70, 0xcd, - 0x65, 0x4a, 0xf2, 0x66, 0xc8, 0x99, 0x1f, 0xe0, 0x58, 0x0e, 0x18, 0x9a, - 0x8c, 0xbb, 0xc7, 0x4f, 0x0b, 0xc2, 0x5e, 0xe1, 0x0e, 0xf2, 0xfb, 0xe3, - 0x64, 0xf9, 0x65, 0xf9, 0xfd, 0x18, 0x72, 0xb2, 0xbc, 0x79, 0x5d, 0x21, - 0xbf, 0xba, 0x53, 0x0c, 0xea, 0xcb, 0xec, 0x27, 0x82, 0xcb, 0xee, 0xf0, - 0x4e, 0x46, 0x3c, 0xc8, 0xec, 0xb2, 0xff, 0x74, 0xf6, 0xcf, 0x03, 0xab, - 0x2f, 0xbb, 0xc9, 0x15, 0x65, 0xa2, 0x59, 0x7f, 0x60, 0x33, 0xc1, 0xd9, - 0x65, 0xbe, 0x93, 0xc0, 0xe0, 0x95, 0xef, 0x48, 0xd6, 0x54, 0xa2, 0x9f, - 0x18, 0xe2, 0x28, 0xbf, 0xb3, 0x5f, 0x99, 0x75, 0x65, 0xdf, 0x7c, 0xb2, - 0xcc, 0x8c, 0x78, 0xcc, 0x2d, 0xbf, 0xa1, 0xd3, 0xd8, 0x9d, 0x65, 0xfd, - 0xe7, 0x83, 0x34, 0x6b, 0x2f, 0xfe, 0x14, 0x57, 0xef, 0x3a, 0x53, 0xf4, - 0x16, 0x5f, 0xdd, 0x29, 0x86, 0x75, 0x65, 0x49, 0xf9, 0xc1, 0x1e, 0xe3, - 0xe2, 0xcb, 0x31, 0x65, 0xed, 0x67, 0xcb, 0x2f, 0x61, 0xec, 0xb2, 0xfa, - 0x58, 0xc3, 0x59, 0x6c, 0xd8, 0xde, 0xf4, 0x72, 0x86, 0x8a, 0x8f, 0x0b, - 0x7e, 0x22, 0x4b, 0x57, 0xff, 0x9c, 0x5e, 0x8b, 0x25, 0x0c, 0xf1, 0xb1, - 0x65, 0xf8, 0xf9, 0xc9, 0x62, 0xca, 0x34, 0x57, 0x11, 0xee, 0xf4, 0xaa, - 0x95, 0xc9, 0xec, 0x6e, 0x15, 0x08, 0xe1, 0xb6, 0xc7, 0x57, 0x2a, 0xfc, - 0xb4, 0xa1, 0x40, 0x08, 0xdd, 0xef, 0xc3, 0x32, 0x71, 0xac, 0xbd, 0xc1, - 0xfc, 0xb2, 0xef, 0xc2, 0xb2, 0xff, 0xf6, 0x45, 0xe9, 0x21, 0x4b, 0x3b, - 0xe0, 0x2c, 0xa1, 0x4f, 0x8b, 0xc3, 0x17, 0xc1, 0x03, 0xc1, 0x65, 0x46, - 0x46, 0x2c, 0x21, 0x01, 0xf1, 0x1d, 0x1a, 0x64, 0x7a, 0x87, 0x5d, 0xfd, - 0xaf, 0x66, 0x81, 0x8b, 0x2f, 0xff, 0xec, 0xfa, 0x1c, 0xef, 0xa5, 0x92, - 0x63, 0x8d, 0x31, 0xd2, 0xca, 0xc4, 0x47, 0x99, 0x6d, 0xfe, 0x72, 0x8c, - 0x1e, 0x7e, 0x35, 0x97, 0xfe, 0x07, 0x23, 0x30, 0x4f, 0x3f, 0xa5, 0x65, - 0xfb, 0x3a, 0x0c, 0xf9, 0x65, 0xe1, 0x73, 0xe5, 0x97, 0x4c, 0x51, 0x8f, - 0x1b, 0xa5, 0x14, 0x48, 0xb8, 0x04, 0x22, 0x2f, 0xff, 0xff, 0xc0, 0x28, - 0xcd, 0x3f, 0xd0, 0xd3, 0xff, 0x1b, 0x80, 0x9f, 0xbd, 0x86, 0x58, 0x2a, - 0xcb, 0xff, 0xff, 0xfc, 0x1c, 0x09, 0x67, 0x7c, 0x0f, 0x3f, 0xd0, 0xe7, - 0x75, 0x8d, 0x61, 0x8f, 0xbc, 0x35, 0x97, 0xff, 0xfb, 0xa0, 0xc2, 0x8c, - 0x79, 0xd2, 0x9f, 0xe3, 0x61, 0x8d, 0x65, 0xff, 0x33, 0x19, 0x19, 0x9b, - 0xbb, 0xe0, 0xb2, 0xb4, 0x8a, 0x36, 0x31, 0x5f, 0xff, 0x6b, 0x59, 0xde, - 0x78, 0xf2, 0x37, 0xbf, 0x95, 0x95, 0x29, 0xd3, 0x9c, 0x66, 0x40, 0x23, - 0xbf, 0xff, 0xa7, 0xd8, 0x31, 0x41, 0x3e, 0xce, 0xcb, 0x1f, 0xab, 0x2b, - 0x15, 0x60, 0x7a, 0x51, 0x88, 0x0d, 0x6f, 0xff, 0xff, 0x8e, 0x2f, 0x67, - 0x35, 0x81, 0x8d, 0xe3, 0xc2, 0xcd, 0xe5, 0x83, 0x01, 0x2c, 0xbf, 0x61, - 0x04, 0x46, 0x2c, 0xbf, 0xfb, 0xd9, 0x25, 0x11, 0x67, 0x78, 0xeb, 0x2d, - 0x02, 0x47, 0x47, 0x5f, 0xb7, 0x0a, 0x2f, 0xff, 0xc3, 0xc6, 0x46, 0xdc, - 0xec, 0xf2, 0x28, 0x3e, 0xb6, 0x59, 0x7a, 0x23, 0xdd, 0x59, 0x7f, 0xff, - 0x74, 0xf6, 0xc1, 0xc6, 0x63, 0xc3, 0x47, 0xb3, 0xb1, 0x65, 0xfe, 0xd9, - 0xd8, 0x53, 0xa8, 0x96, 0x5f, 0xf7, 0xd8, 0x2f, 0xec, 0xc8, 0xda, 0xc4, - 0x49, 0x1a, 0xf5, 0xf7, 0xee, 0xf1, 0x2c, 0xb4, 0xac, 0xbd, 0x85, 0xb4, - 0x63, 0x66, 0x12, 0x3b, 0xff, 0xec, 0x06, 0xa3, 0x45, 0xe0, 0x0a, 0x78, - 0x42, 0xac, 0xbf, 0xfe, 0x7e, 0xce, 0x80, 0xc3, 0xe4, 0x6e, 0x43, 0x8b, - 0x2f, 0xfb, 0x5b, 0x4e, 0xdb, 0x03, 0x5b, 0x2c, 0xbf, 0xf4, 0x68, 0xbc, - 0x01, 0x4f, 0x08, 0x55, 0x97, 0xf8, 0xa3, 0x70, 0x11, 0xe0, 0xd2, 0xca, - 0xc3, 0xfc, 0x02, 0x1d, 0x4a, 0x36, 0xf9, 0x0b, 0xab, 0xf0, 0xf3, 0xe7, - 0x25, 0x97, 0xff, 0xb6, 0x8d, 0xcf, 0x02, 0x37, 0x79, 0xc9, 0xd2, 0xcb, - 0xff, 0x42, 0x7a, 0x00, 0xc6, 0xe7, 0x4d, 0x65, 0xff, 0xf7, 0xa7, 0x68, - 0xc5, 0x9b, 0xdf, 0xde, 0x9f, 0x96, 0x5f, 0xff, 0xf7, 0xdc, 0x3c, 0x6a, - 0x37, 0xb3, 0xbc, 0xcd, 0xa3, 0x61, 0x8d, 0x65, 0x0d, 0x18, 0x18, 0xa9, - 0x7f, 0xc5, 0x9a, 0xc0, 0xe1, 0x8d, 0x65, 0xfd, 0xb7, 0x62, 0x29, 0x62, - 0xcb, 0xfe, 0x98, 0xa3, 0x3c, 0xc5, 0x31, 0x2c, 0xbf, 0xff, 0xb3, 0x6c, - 0x31, 0x90, 0x3d, 0x1a, 0x46, 0xf1, 0x71, 0x65, 0xff, 0x63, 0x59, 0xe3, - 0xd7, 0x9d, 0x65, 0x4a, 0xad, 0x1c, 0x27, 0xf9, 0x44, 0xe3, 0x01, 0xf1, - 0x13, 0x9b, 0x11, 0x7f, 0x0f, 0x3a, 0xbb, 0x5b, 0x33, 0x04, 0xc6, 0x42, - 0x2c, 0x3b, 0x83, 0x2b, 0x9f, 0xe8, 0xc8, 0x0c, 0xe2, 0x25, 0xdd, 0x42, - 0xc9, 0x90, 0x87, 0xf1, 0xa1, 0x2a, 0x72, 0x32, 0xde, 0xca, 0xf8, 0xbf, - 0xdd, 0x2c, 0xef, 0xb3, 0x65, 0x97, 0xfe, 0x7d, 0x7b, 0x3b, 0x24, 0x7f, - 0xac, 0xbf, 0xfe, 0x2c, 0xda, 0x37, 0xe0, 0xee, 0x63, 0x3f, 0x69, 0x65, - 0xf4, 0x3a, 0x7b, 0x2c, 0xbf, 0xc1, 0x3e, 0x60, 0xe3, 0x0d, 0x65, 0xfd, - 0xf4, 0x39, 0x1b, 0xa6, 0xb2, 0xd9, 0xe3, 0xe6, 0x68, 0xd6, 0xff, 0xd0, - 0x9c, 0xff, 0xd9, 0x84, 0x15, 0x97, 0xff, 0xbc, 0x64, 0x38, 0xc2, 0x04, - 0xb3, 0xd8, 0xb2, 0xff, 0x9f, 0xbc, 0x8a, 0x00, 0x2e, 0xac, 0xac, 0x46, - 0x14, 0x47, 0xbf, 0xa5, 0xdf, 0xf7, 0x8d, 0x93, 0x85, 0xde, 0x2c, 0xbf, - 0xff, 0xc0, 0xee, 0x81, 0x14, 0x6e, 0xc5, 0xe0, 0x7f, 0xd3, 0xcd, 0xc5, - 0x97, 0xd2, 0x19, 0x64, 0x64, 0x69, 0x84, 0xc4, 0x8d, 0xea, 0x55, 0xa9, - 0x04, 0xcf, 0x47, 0xae, 0xaa, 0x50, 0x89, 0xdc, 0x8e, 0xf6, 0xff, 0xfe, - 0xd6, 0x3b, 0x23, 0x0a, 0xff, 0x7b, 0x3d, 0xe9, 0xea, 0xcb, 0xec, 0xd4, - 0x50, 0x59, 0x7f, 0xf1, 0xfd, 0x0e, 0x46, 0x19, 0xef, 0x91, 0xac, 0xbd, - 0xf4, 0xee, 0xac, 0xa1, 0x4f, 0xa5, 0xd2, 0x6f, 0xb3, 0xcf, 0xbd, 0x65, - 0xfd, 0x0c, 0xf6, 0x6f, 0xe2, 0xcb, 0x60, 0xcf, 0x4a, 0x22, 0x3b, 0xff, - 0xf0, 0xfc, 0x0e, 0x46, 0xf1, 0xf7, 0xd9, 0xa9, 0xde, 0xb2, 0xfe, 0xcf, - 0x8b, 0x33, 0xf5, 0x95, 0x88, 0x87, 0x25, 0xab, 0xa5, 0x98, 0x9e, 0x80, - 0xb0, 0x8c, 0xd3, 0xa1, 0x42, 0xbe, 0xff, 0xed, 0x4b, 0x5b, 0x85, 0x2d, - 0x7d, 0x0e, 0x2c, 0xb4, 0xac, 0xbe, 0x3d, 0x3f, 0xcb, 0x2d, 0xd3, 0x36, - 0x5c, 0x10, 0xa1, 0xa2, 0x88, 0xdf, 0x2f, 0xf9, 0xc8, 0x3e, 0x36, 0x14, - 0xac, 0xbf, 0xf8, 0x64, 0xf1, 0x45, 0x3d, 0xff, 0x38, 0xb2, 0xf4, 0xb0, - 0x45, 0x97, 0xff, 0x1e, 0xf3, 0x1c, 0x69, 0x88, 0x6e, 0xd2, 0xcb, 0xff, - 0xff, 0xc4, 0x62, 0xf9, 0xe2, 0x8c, 0x40, 0xdc, 0x8d, 0x9f, 0x43, 0x00, - 0x5f, 0xf1, 0x65, 0xfb, 0xc7, 0x25, 0xb2, 0xca, 0x62, 0x2a, 0x37, 0xc2, - 0x06, 0xf9, 0x86, 0x6d, 0x2c, 0xa9, 0x3c, 0xc7, 0x2a, 0xa2, 0x4d, 0xeb, - 0x83, 0xbd, 0x8c, 0xf6, 0xff, 0x6a, 0x76, 0xf1, 0xe0, 0xd6, 0x5f, 0x4c, - 0x5c, 0x35, 0x95, 0x05, 0x54, 0x63, 0x22, 0xc3, 0x63, 0x8f, 0x03, 0x46, - 0xc4, 0x67, 0x7f, 0xff, 0xd9, 0x14, 0x27, 0x42, 0xb0, 0x8f, 0xe8, 0x70, - 0x7a, 0x3d, 0x96, 0x5e, 0x98, 0xb8, 0xb2, 0xf4, 0x40, 0x1a, 0xca, 0x96, - 0xe0, 0x9f, 0x29, 0x99, 0x47, 0x38, 0x67, 0xa6, 0x4f, 0x4a, 0x2e, 0x79, - 0x72, 0xe4, 0xb9, 0xd6, 0xa0, 0x0e, 0xdf, 0xd3, 0x03, 0x1e, 0x12, 0xcb, - 0xe2, 0xe9, 0xef, 0x59, 0x7d, 0x00, 0x3f, 0x56, 0x5e, 0x0c, 0xee, 0xac, - 0xa9, 0x3e, 0x4c, 0x23, 0x32, 0x2b, 0xee, 0xf1, 0xff, 0x59, 0x7f, 0xf7, - 0x8e, 0x31, 0xe8, 0x1b, 0x3e, 0x82, 0xb2, 0xf8, 0xfc, 0xfb, 0x2c, 0xbe, - 0xdf, 0x3d, 0x95, 0x97, 0xee, 0x67, 0x4f, 0x8b, 0x2f, 0xfe, 0x3e, 0xf8, - 0xf3, 0x06, 0x58, 0xd2, 0xcb, 0xff, 0x72, 0x62, 0xcf, 0x1e, 0xbc, 0xeb, - 0x2e, 0xcf, 0x2c, 0xa3, 0x3d, 0x46, 0x1f, 0x5e, 0xd1, 0x8a, 0xb2, 0xff, - 0xf8, 0x18, 0x7e, 0xc0, 0xf8, 0xe3, 0x34, 0xc0, 0xac, 0xbd, 0xdc, 0x69, - 0x65, 0xec, 0x06, 0xc4, 0x7d, 0xfd, 0x52, 0xbf, 0x01, 0xb6, 0x4f, 0xd5, - 0x97, 0xed, 0x9c, 0xbb, 0xc5, 0x97, 0xf7, 0x9f, 0x68, 0xd0, 0xea, 0xcb, - 0xf9, 0xe2, 0x13, 0x93, 0x8b, 0x2f, 0xcf, 0xa2, 0xf8, 0xd6, 0x54, 0xa3, - 0x67, 0x0a, 0xcc, 0xa3, 0xc6, 0x3d, 0x2e, 0xbd, 0xe7, 0x25, 0x95, 0xb2, - 0xe2, 0x10, 0xe1, 0x1f, 0x85, 0x82, 0x91, 0x85, 0x1c, 0xc8, 0xa2, 0x23, - 0x61, 0x3f, 0xa1, 0x2c, 0xe4, 0x25, 0x08, 0xce, 0xc6, 0x18, 0xd2, 0x3d, - 0xf1, 0x76, 0x58, 0xb2, 0xfd, 0x9f, 0x0f, 0xe1, 0x56, 0x5f, 0xfd, 0xdf, - 0x49, 0x48, 0x75, 0x38, 0x4b, 0x2f, 0xec, 0xda, 0x45, 0x7e, 0x2c, 0xbb, - 0xee, 0x46, 0x3e, 0xf1, 0xa0, 0xdf, 0xef, 0x49, 0x93, 0xe8, 0x55, 0x97, - 0xf4, 0x99, 0x3e, 0x85, 0x59, 0x7f, 0x3f, 0xfa, 0xd1, 0xea, 0x31, 0xef, - 0x78, 0xca, 0xf1, 0x63, 0x16, 0x5b, 0xe5, 0x97, 0x3c, 0x20, 0x6b, 0x78, - 0x37, 0x7f, 0xff, 0xfa, 0x33, 0x27, 0x3b, 0x1b, 0x20, 0x7b, 0xcb, 0x39, - 0x1a, 0x46, 0xf1, 0x71, 0x65, 0xfc, 0x41, 0x8c, 0xd7, 0x3f, 0x59, 0x50, - 0x45, 0x83, 0xbf, 0x51, 0xaa, 0xbc, 0xd1, 0x0b, 0xc2, 0x6b, 0xf8, 0x47, - 0x93, 0xa0, 0x21, 0xb9, 0x7b, 0x9d, 0x35, 0x97, 0xa2, 0xff, 0xe5, 0x97, - 0xa4, 0x58, 0xc1, 0x37, 0x9f, 0x8e, 0x5f, 0x7f, 0xf8, 0x37, 0x56, 0x5b, - 0xf5, 0x97, 0xf4, 0xeb, 0x69, 0xd6, 0xcb, 0x2f, 0x43, 0x98, 0xb2, 0xfc, - 0xd8, 0x29, 0xcf, 0xd6, 0x56, 0x8f, 0x1f, 0xf1, 0xcb, 0xff, 0x1e, 0x35, - 0xc0, 0x44, 0x52, 0xc5, 0x97, 0x80, 0xfc, 0x59, 0x50, 0x47, 0xa6, 0x3b, - 0x30, 0x8c, 0x43, 0xfb, 0xfc, 0xd3, 0x24, 0xf4, 0x0d, 0xd5, 0x97, 0x10, - 0x16, 0x52, 0xcb, 0xa7, 0x51, 0x8d, 0x0f, 0x05, 0xab, 0x11, 0x0c, 0x05, - 0x8b, 0xff, 0xf8, 0xf5, 0x1b, 0x0f, 0x08, 0x5f, 0x1e, 0x33, 0x02, 0xb2, - 0xff, 0xb3, 0xe8, 0x73, 0xe8, 0x03, 0x4b, 0x2f, 0xff, 0xfc, 0xce, 0x07, - 0xd2, 0x1f, 0xa0, 0x40, 0x1c, 0x63, 0x15, 0xcb, 0xf5, 0x97, 0xff, 0xfb, - 0x6d, 0x4b, 0x38, 0x09, 0x8d, 0x3a, 0x91, 0xfa, 0x58, 0xb2, 0xb1, 0x1a, - 0x86, 0xe3, 0x58, 0x9c, 0x59, 0xad, 0x6a, 0x31, 0x0b, 0xf7, 0xfc, 0x3f, - 0x9a, 0x59, 0x7e, 0xfa, 0x59, 0xb4, 0xac, 0xbe, 0xfa, 0x07, 0xe5, 0x97, - 0xb5, 0x9f, 0x78, 0xf3, 0x3a, 0x53, 0x7f, 0x7c, 0xf0, 0x29, 0x62, 0xcb, - 0xff, 0x00, 0xa2, 0xe9, 0x66, 0xce, 0x4b, 0x2f, 0xff, 0xff, 0x6b, 0x47, - 0xb4, 0x6f, 0x67, 0x4a, 0x62, 0xf6, 0x6d, 0x3f, 0x74, 0x01, 0x59, 0x7f, - 0xff, 0xf4, 0x8b, 0xe9, 0x8c, 0x58, 0x01, 0x63, 0x7d, 0x0e, 0x19, 0x09, - 0x3b, 0x2c, 0xbf, 0x7d, 0xc3, 0xc2, 0x59, 0x58, 0x8a, 0x00, 0x3c, 0xd4, - 0xa7, 0x55, 0x85, 0xa6, 0x7d, 0xe8, 0xc7, 0x6f, 0xfd, 0xd6, 0x31, 0xe2, - 0x8c, 0x58, 0xd2, 0xcb, 0xff, 0xb4, 0x5f, 0x67, 0x01, 0x11, 0x4b, 0x16, - 0x5f, 0xf1, 0x8b, 0xe9, 0x3d, 0xa4, 0x55, 0x94, 0x67, 0xff, 0xc4, 0x5a, - 0x8c, 0x8e, 0x28, 0x43, 0x06, 0xff, 0x88, 0x11, 0x16, 0x79, 0xfa, 0xb2, - 0xff, 0xe8, 0x03, 0xfe, 0x16, 0x73, 0x92, 0xd2, 0xcb, 0xfd, 0xe9, 0x68, - 0x7e, 0x76, 0x2c, 0xbf, 0xe6, 0x67, 0x23, 0x44, 0x52, 0xc5, 0x97, 0xff, - 0xee, 0x6c, 0xf1, 0x87, 0xe9, 0xd6, 0xb3, 0x7c, 0xfc, 0xb2, 0x86, 0x8c, - 0xa6, 0x1a, 0x74, 0xee, 0xfb, 0x7e, 0x00, 0x2b, 0x2f, 0xff, 0xf9, 0xf5, - 0xec, 0xe9, 0xe1, 0x6d, 0xc9, 0xc2, 0x1f, 0xa5, 0x65, 0xf7, 0x63, 0x6d, - 0xa5, 0x95, 0x88, 0xad, 0xf8, 0x91, 0xd8, 0xef, 0xff, 0xfd, 0xf6, 0x6c, - 0x0d, 0xc8, 0xdf, 0x43, 0xb2, 0xd4, 0x6c, 0xd7, 0xff, 0xca, 0xcb, 0xfe, - 0xc8, 0xa3, 0xc1, 0xdc, 0xfb, 0xab, 0x2f, 0x66, 0x86, 0xb2, 0xe9, 0x82, - 0xca, 0xc3, 0xf0, 0x61, 0xf7, 0xe3, 0x97, 0xff, 0x6c, 0xe5, 0x9e, 0xce, - 0xeb, 0x1a, 0x59, 0x7f, 0xff, 0xfe, 0xc0, 0x0a, 0x40, 0xec, 0x59, 0xd3, - 0xfa, 0x05, 0x25, 0x9e, 0x3c, 0xd4, 0x4b, 0x2b, 0x11, 0x8f, 0xf2, 0x25, - 0xff, 0x63, 0x5e, 0x91, 0x73, 0xbc, 0x59, 0x7f, 0xf3, 0x96, 0xcc, 0x01, - 0x8c, 0xc8, 0xd6, 0x51, 0x9f, 0xe3, 0x47, 0x57, 0xf4, 0x5f, 0x40, 0x13, - 0xd5, 0x97, 0xfc, 0x0f, 0xbd, 0x9d, 0xd6, 0x34, 0xb2, 0xff, 0xfe, 0x84, - 0xeb, 0xe8, 0x72, 0x37, 0xa4, 0xc9, 0xf4, 0x2a, 0xcb, 0xf4, 0x3a, 0x59, - 0x05, 0x97, 0xfe, 0xf8, 0x7e, 0x03, 0xf0, 0xb0, 0x6b, 0x2b, 0x0f, 0x98, - 0x04, 0xf7, 0xfd, 0xe7, 0x61, 0x67, 0x9f, 0xab, 0x2e, 0xfd, 0xa5, 0x97, - 0xd1, 0xf9, 0xa9, 0x59, 0x6c, 0xd1, 0xbd, 0xde, 0x33, 0x52, 0x9f, 0x66, - 0x18, 0x7c, 0x77, 0xe8, 0x62, 0x70, 0x87, 0xae, 0x97, 0xff, 0x67, 0x79, - 0x1b, 0x58, 0x7e, 0x91, 0xac, 0xbf, 0xef, 0x7a, 0x7e, 0x8c, 0xd3, 0x02, - 0xb2, 0xfe, 0xce, 0xeb, 0x59, 0x05, 0x94, 0xb2, 0xfd, 0x9e, 0x2c, 0x0a, - 0xca, 0xfc, 0xd8, 0x90, 0x5d, 0x44, 0x7f, 0xfd, 0x5e, 0xbd, 0x9f, 0x75, - 0x65, 0xfe, 0xc1, 0x9e, 0xfe, 0x9e, 0x96, 0x5d, 0x9c, 0x8c, 0x7e, 0x9b, - 0xa4, 0x6e, 0x3b, 0x4e, 0x9b, 0x79, 0x46, 0x63, 0x7f, 0xd8, 0xcf, 0x49, - 0xe8, 0x1b, 0xab, 0x2f, 0xee, 0x9e, 0xc4, 0xec, 0x59, 0x5b, 0x33, 0xc1, - 0x47, 0x1d, 0x6e, 0x1a, 0x8a, 0xee, 0x18, 0xf7, 0xbe, 0x8c, 0x8c, 0xca, - 0xa2, 0x38, 0xd4, 0x62, 0x1e, 0x86, 0xeb, 0x97, 0xff, 0x0e, 0xa2, 0x87, - 0xb7, 0x21, 0x3f, 0xd9, 0x41, 0xc0, 0x5c, 0xdf, 0x1d, 0xa0, 0x85, 0x1b, - 0x87, 0x77, 0xfd, 0x2c, 0x2c, 0x1e, 0x8f, 0x65, 0x97, 0xff, 0xf9, 0xaf, - 0x02, 0x7e, 0x8d, 0xad, 0x4e, 0xc4, 0xef, 0xa1, 0x56, 0x5f, 0xe2, 0xc0, - 0x60, 0xb0, 0xfd, 0x65, 0x3a, 0x26, 0x9a, 0x66, 0xa8, 0x23, 0xd7, 0x21, - 0xa5, 0x78, 0x59, 0xd2, 0xcb, 0xff, 0xe9, 0x8d, 0xe7, 0xdf, 0x1b, 0x18, - 0xc9, 0xd0, 0xab, 0x2f, 0xdf, 0xb3, 0x33, 0xab, 0x2f, 0xfb, 0x23, 0x6b, - 0x3c, 0x59, 0x12, 0xca, 0xc3, 0xe2, 0x22, 0x8b, 0xff, 0xfd, 0xe7, 0xff, - 0x91, 0x8b, 0x37, 0x96, 0x73, 0x19, 0x21, 0x59, 0x51, 0x26, 0xb1, 0xe1, - 0xd2, 0x85, 0xc7, 0x08, 0x2f, 0xe0, 0x30, 0x59, 0x20, 0xac, 0xa6, 0xcb, - 0x6a, 0x3e, 0xda, 0x14, 0x4c, 0x60, 0xd9, 0x4f, 0xc8, 0xfa, 0x51, 0xcb, - 0xc6, 0xfe, 0x48, 0x77, 0xef, 0x08, 0x5f, 0x7e, 0xb2, 0xff, 0xed, 0x9f, - 0xba, 0xcd, 0x9c, 0xbb, 0xc5, 0x95, 0x87, 0xe0, 0x65, 0x77, 0xff, 0xfd, - 0xac, 0x87, 0xb0, 0xe1, 0xcc, 0x20, 0x74, 0xb3, 0xf9, 0x59, 0x7f, 0x4e, - 0x10, 0x44, 0x62, 0xcb, 0xfd, 0xd0, 0x4e, 0x6c, 0x23, 0x16, 0x5f, 0xfb, - 0xd2, 0x40, 0x8b, 0x39, 0xc9, 0x59, 0x6d, 0xde, 0xa2, 0x80, 0x42, 0xdd, - 0xc3, 0x5a, 0x94, 0xd0, 0x5e, 0x30, 0x4b, 0xfb, 0xd8, 0x5d, 0xfc, 0x0b, - 0x2f, 0xf8, 0x63, 0x9f, 0xe3, 0x67, 0xdd, 0x59, 0x7f, 0x3e, 0xe8, 0xcb, - 0x3c, 0xb2, 0xe6, 0x71, 0x65, 0xe7, 0xe1, 0xac, 0xa9, 0x36, 0x5f, 0x8b, - 0xdf, 0xf0, 0x9b, 0x61, 0x3b, 0x1f, 0xcb, 0x28, 0xd1, 0xf1, 0x11, 0xe9, - 0x30, 0xf4, 0x86, 0xff, 0x73, 0x92, 0x7b, 0x3f, 0x56, 0x5f, 0xd9, 0xdc, - 0x72, 0x89, 0x65, 0xf6, 0x72, 0x74, 0xb2, 0xc2, 0xf8, 0xf3, 0x48, 0xb2, - 0xd0, 0x94, 0x54, 0x02, 0x10, 0x16, 0x82, 0xcb, 0xe9, 0x63, 0x8d, 0x65, - 0x49, 0xb2, 0x21, 0x1b, 0xfd, 0x91, 0x7b, 0x3b, 0x23, 0x59, 0x7e, 0x39, - 0x67, 0x18, 0xb2, 0xfe, 0x2e, 0xbb, 0x0f, 0xab, 0x2f, 0x0c, 0xc6, 0xb2, - 0xf7, 0xef, 0xba, 0xb2, 0x86, 0x6f, 0x38, 0x39, 0x77, 0x1b, 0x52, 0xcb, - 0x41, 0x65, 0x4a, 0x2d, 0xf1, 0xa1, 0xc8, 0x7a, 0x3b, 0x78, 0x83, 0x2b, - 0x2f, 0xff, 0xff, 0xbd, 0x9d, 0xe3, 0xc5, 0x18, 0xb3, 0xe8, 0x60, 0x36, - 0x2c, 0x1f, 0x8e, 0x25, 0x97, 0xff, 0xc7, 0xc9, 0x87, 0x73, 0xc5, 0x9d, - 0xf1, 0xac, 0xb8, 0x02, 0xac, 0xa3, 0x4c, 0x07, 0x43, 0x7e, 0x84, 0x0f, - 0x53, 0x6f, 0x78, 0x5e, 0xac, 0xbf, 0x77, 0xbd, 0xc6, 0x96, 0x5b, 0xf9, - 0x3c, 0x73, 0x1e, 0xbc, 0xc9, 0x0a, 0xcb, 0xb5, 0x8b, 0x2f, 0x61, 0x8d, - 0x65, 0xb8, 0xb2, 0xe2, 0xc0, 0x9a, 0xcf, 0x86, 0xef, 0xfe, 0xcf, 0xe3, - 0x78, 0xfb, 0xc2, 0x38, 0x96, 0x56, 0xc7, 0xe7, 0xd2, 0xca, 0x8e, 0x51, - 0xe9, 0x23, 0x99, 0x0b, 0x5b, 0xff, 0x8b, 0xf8, 0xfc, 0xf1, 0xcb, 0x1c, - 0x6b, 0x2f, 0xf8, 0xbf, 0xd6, 0x40, 0xa5, 0x8b, 0x2f, 0xff, 0xff, 0xd0, - 0xe7, 0x01, 0x3d, 0x31, 0xe1, 0x45, 0x18, 0xb3, 0x52, 0x5d, 0xf6, 0x6e, - 0x2c, 0xbf, 0xfb, 0xe8, 0x73, 0x19, 0xf4, 0xfb, 0xd2, 0xb2, 0xb1, 0x30, - 0x4f, 0x8e, 0x35, 0x08, 0x6b, 0x71, 0x65, 0x62, 0x72, 0x20, 0x8d, 0x28, - 0x43, 0x6b, 0x6c, 0xb2, 0xfd, 0xb4, 0xf7, 0x3f, 0x59, 0x7e, 0xd6, 0x7d, - 0x0e, 0x2c, 0xbc, 0x1f, 0x62, 0xcb, 0x66, 0xc7, 0x8b, 0xa2, 0x9b, 0xfe, - 0xfa, 0x75, 0xb4, 0x6c, 0x31, 0xac, 0xba, 0x23, 0x59, 0x58, 0x8f, 0xb3, - 0x12, 0x26, 0xe0, 0x14, 0x6f, 0x3c, 0xbf, 0xb3, 0xe8, 0x4f, 0xf2, 0xb2, - 0xfc, 0x59, 0xe7, 0xea, 0xca, 0xf8, 0xf5, 0x18, 0x5d, 0x7c, 0x7b, 0xf3, - 0x4b, 0x2a, 0x4f, 0x1b, 0xc4, 0x77, 0xd1, 0x83, 0xb3, 0x4b, 0x2f, 0xdb, - 0x46, 0x04, 0xc4, 0xb2, 0xff, 0xff, 0x8a, 0x7f, 0xfa, 0x1c, 0x04, 0xf4, - 0xb0, 0x67, 0x9a, 0x89, 0x65, 0x3a, 0x24, 0xfa, 0x59, 0x52, 0xcc, 0xdc, - 0xda, 0x36, 0x11, 0x94, 0x64, 0x64, 0xdf, 0x43, 0x90, 0xd7, 0xe2, 0x1f, - 0xd1, 0x9f, 0xa1, 0xbc, 0xf1, 0x9d, 0xff, 0x08, 0x92, 0x8c, 0x6f, 0x91, - 0xe4, 0xf6, 0x33, 0x20, 0x43, 0x8f, 0x79, 0x08, 0x90, 0xba, 0xbf, 0x40, - 0xf5, 0x9f, 0x2c, 0xbd, 0xcf, 0x62, 0xcb, 0xf6, 0x73, 0x6c, 0x69, 0x65, - 0xff, 0xde, 0x00, 0xfc, 0x0e, 0xf3, 0xc0, 0x89, 0x65, 0x7c, 0x8b, 0x58, - 0x8a, 0x3c, 0x38, 0x45, 0x37, 0xff, 0xfb, 0xbc, 0xf6, 0x7d, 0x8f, 0xf4, - 0x61, 0xce, 0xd1, 0x85, 0x15, 0x65, 0xff, 0xf9, 0x9f, 0x43, 0x9a, 0xfa, - 0x7b, 0xc9, 0xf1, 0xf5, 0x65, 0xff, 0x60, 0xa5, 0x9d, 0xf1, 0xb4, 0xb2, - 0xff, 0xf4, 0xfd, 0xe3, 0xc2, 0x19, 0xef, 0x91, 0xac, 0xa9, 0x4d, 0x14, - 0xda, 0xb4, 0xb0, 0x47, 0x37, 0x6e, 0x8d, 0x65, 0xff, 0xf6, 0xd8, 0x11, - 0xf8, 0xf0, 0x58, 0xc2, 0x82, 0x56, 0x5f, 0xfd, 0x03, 0xef, 0xb3, 0x5a, - 0xce, 0xf1, 0x65, 0xfe, 0x13, 0xa7, 0xbe, 0x48, 0x2b, 0x2f, 0xd2, 0xc3, - 0xf4, 0xac, 0xbf, 0xd9, 0xdf, 0x1f, 0x70, 0x2b, 0x2a, 0x32, 0x23, 0x74, - 0x6a, 0x44, 0xd5, 0x1d, 0xa6, 0xe0, 0x63, 0x51, 0x2a, 0x7a, 0x1a, 0x37, - 0xfe, 0x87, 0x3a, 0xfb, 0x6e, 0x16, 0x7c, 0xb2, 0xfe, 0xf4, 0xc0, 0x0e, - 0x22, 0xcb, 0xf0, 0xfd, 0x85, 0xd5, 0x95, 0x03, 0xd5, 0x88, 0xba, 0xbe, - 0x45, 0xd9, 0x42, 0x62, 0xfc, 0x7f, 0x0f, 0xe1, 0x56, 0x54, 0x9e, 0x9e, - 0x8a, 0x2e, 0x8b, 0xf5, 0x97, 0xfd, 0xed, 0x8f, 0x0b, 0xe8, 0x71, 0x65, - 0xff, 0x7d, 0xec, 0x09, 0x60, 0x37, 0x56, 0x5f, 0xff, 0x35, 0x81, 0xe6, - 0x76, 0x2c, 0x14, 0xb3, 0xf5, 0x96, 0x84, 0xa3, 0x23, 0x0e, 0x8c, 0xf2, - 0xfe, 0x3f, 0xbf, 0xd3, 0x8d, 0x65, 0xec, 0xd7, 0xeb, 0x2f, 0xdc, 0x3c, - 0x2f, 0xd6, 0x5b, 0x0c, 0xf1, 0x38, 0x3b, 0x7e, 0x3f, 0xff, 0xcd, 0x2c, - 0xaf, 0x23, 0x08, 0x9c, 0xf8, 0x4d, 0x68, 0xe9, 0x65, 0xf9, 0xf0, 0x8c, - 0x55, 0x97, 0xbb, 0x9e, 0x59, 0x44, 0x78, 0x5b, 0x84, 0xd7, 0x1b, 0x16, - 0x5f, 0xff, 0xfb, 0xe8, 0x3f, 0xd1, 0x88, 0x1b, 0x91, 0xb3, 0xe8, 0x60, - 0x0b, 0xfe, 0x2c, 0xa6, 0x22, 0x37, 0x70, 0x5a, 0xf4, 0x79, 0xb6, 0xd6, - 0x54, 0x64, 0xe0, 0x63, 0xb2, 0xfd, 0x96, 0x32, 0x16, 0x31, 0xe4, 0xd7, - 0xff, 0xff, 0xdf, 0x70, 0x13, 0xf4, 0x6f, 0x60, 0xe3, 0x16, 0x00, 0x58, - 0xdc, 0xff, 0xf3, 0xea, 0xcb, 0xff, 0xff, 0x60, 0xd8, 0xc9, 0xef, 0x33, - 0xbc, 0xd1, 0xe0, 0x4b, 0x06, 0xb2, 0xff, 0xf7, 0x41, 0x39, 0xb4, 0x6e, - 0x19, 0x1e, 0x96, 0x59, 0xa2, 0x45, 0xa8, 0x8d, 0x55, 0x29, 0xb4, 0x64, - 0x67, 0xd7, 0xe9, 0x2f, 0x9f, 0x65, 0x97, 0xff, 0xff, 0xfe, 0xc0, 0xcf, - 0xa5, 0x9d, 0xce, 0x72, 0x75, 0xa9, 0x2c, 0xdb, 0x01, 0xc0, 0x44, 0x52, - 0xc5, 0x97, 0x81, 0xa1, 0x56, 0x53, 0x11, 0x6d, 0x1f, 0x09, 0x8b, 0xf3, - 0xfe, 0x23, 0xec, 0xb2, 0xb0, 0xf5, 0x4c, 0xae, 0xf1, 0xff, 0x2b, 0x2f, - 0xf4, 0x62, 0xcd, 0x1c, 0xb1, 0x65, 0xff, 0xb4, 0xfd, 0xd6, 0x1f, 0xa4, - 0x6b, 0x2f, 0xe1, 0x96, 0x79, 0xfa, 0xb2, 0xfd, 0xdf, 0xa0, 0x7a, 0x59, - 0x58, 0x7a, 0xbc, 0x2c, 0xad, 0x93, 0x02, 0x18, 0xe6, 0x8c, 0xff, 0x84, - 0x95, 0xed, 0x38, 0x8b, 0x2f, 0xd3, 0xd6, 0x1f, 0x56, 0x5b, 0x67, 0x3c, - 0x40, 0x0e, 0xdf, 0xfa, 0x45, 0xdc, 0x06, 0x14, 0x42, 0x31, 0x65, 0xff, - 0xff, 0xfe, 0x14, 0xb2, 0x31, 0xe1, 0x3f, 0x7d, 0x31, 0x46, 0x2c, 0xf1, - 0xf7, 0x80, 0x08, 0x25, 0x65, 0x62, 0x3f, 0x3e, 0x28, 0x02, 0x25, 0xff, - 0xb3, 0x3f, 0xe4, 0x6d, 0x70, 0x7c, 0x59, 0x7f, 0xb5, 0x81, 0xf1, 0xea, - 0x25, 0x96, 0xe8, 0xcf, 0xdb, 0x10, 0xaf, 0xf6, 0x04, 0xfb, 0xa0, 0x31, - 0x65, 0xf7, 0xc5, 0x91, 0xeb, 0x2b, 0x0f, 0xfc, 0xc9, 0xdc, 0xce, 0xfd, - 0x8c, 0x81, 0x1a, 0xcb, 0xc6, 0x5d, 0x59, 0x7f, 0x43, 0x8e, 0xc0, 0x71, - 0x65, 0x6c, 0x79, 0x2e, 0x37, 0x7e, 0xcf, 0x79, 0xe0, 0xb2, 0xfe, 0xe9, - 0xeb, 0x6c, 0x0a, 0xca, 0x96, 0x7f, 0xb8, 0xe3, 0x83, 0xc8, 0xea, 0xc5, - 0x8d, 0x58, 0x24, 0x3f, 0x43, 0x84, 0xe5, 0x3b, 0x6a, 0x3c, 0x26, 0x14, - 0x7a, 0x33, 0x37, 0x8c, 0x88, 0xa3, 0x85, 0xe4, 0x64, 0xbd, 0x2c, 0x03, - 0x7c, 0x79, 0x16, 0xe1, 0x3d, 0xe7, 0x0e, 0x2c, 0xa5, 0x97, 0xdb, 0x9e, - 0x3d, 0x2c, 0xbf, 0xe8, 0xdd, 0x2c, 0x6b, 0x0f, 0x8b, 0x29, 0xb2, 0x7d, - 0x41, 0x0b, 0x88, 0x96, 0xf7, 0xa7, 0xf5, 0x97, 0xf1, 0x9c, 0xb5, 0x31, - 0x2c, 0xa8, 0xed, 0x30, 0x39, 0x84, 0x2e, 0x1a, 0x44, 0x3b, 0x4b, 0x2f, - 0xec, 0x64, 0xf9, 0xfc, 0xb2, 0xe8, 0xd1, 0x2c, 0xbd, 0xe3, 0x15, 0x65, - 0x46, 0x3e, 0x31, 0x96, 0x0a, 0x35, 0x7f, 0xfd, 0xf4, 0x0a, 0x63, 0x30, - 0xa4, 0x5e, 0x9e, 0xcb, 0x2a, 0x51, 0xe7, 0x8d, 0xc6, 0x65, 0x7e, 0xd4, - 0x62, 0xde, 0xd2, 0xcb, 0xef, 0x3f, 0xf2, 0xb2, 0xf7, 0xb3, 0x4b, 0x2a, - 0x4f, 0xad, 0xcb, 0x77, 0x08, 0xaf, 0xed, 0xa2, 0x84, 0x77, 0xad, 0x96, - 0x5f, 0x98, 0x63, 0xc1, 0xac, 0xb4, 0x4b, 0x2e, 0x96, 0x2c, 0xb7, 0x56, - 0x56, 0x8d, 0x3b, 0x8b, 0x50, 0xcf, 0x67, 0xc7, 0x37, 0xd2, 0x37, 0x82, - 0xcb, 0xdd, 0xce, 0x2c, 0xbf, 0xf6, 0x3f, 0xdf, 0xc5, 0x09, 0xd6, 0xcb, - 0x2f, 0x6a, 0x62, 0x59, 0x7b, 0x8f, 0xf2, 0xca, 0xd9, 0x17, 0x22, 0x90, - 0xf8, 0x71, 0xd0, 0xba, 0x3b, 0x74, 0x4e, 0xb2, 0xe8, 0x6c, 0xb2, 0xff, - 0xfb, 0x24, 0xbf, 0x93, 0xef, 0xb1, 0x98, 0x35, 0x97, 0x48, 0xab, 0x2a, - 0x07, 0xc9, 0xd4, 0xdb, 0xf9, 0xda, 0x9e, 0xe6, 0xcb, 0x2f, 0xe3, 0xd7, - 0xd0, 0x29, 0x59, 0x7e, 0x9e, 0xc0, 0xfa, 0xb2, 0xfe, 0xf4, 0xfd, 0x0c, - 0xea, 0xca, 0x81, 0xea, 0x8c, 0x9e, 0xbc, 0x8a, 0x4e, 0xc2, 0x0a, 0xff, - 0xd1, 0xf2, 0x5d, 0xd1, 0xef, 0xc1, 0xac, 0xbb, 0x3a, 0xb2, 0xd1, 0x2c, - 0xaf, 0x1a, 0x82, 0x16, 0xa6, 0xca, 0xe0, 0xb4, 0x9b, 0xe4, 0x20, 0x0e, - 0x1b, 0x6c, 0x4b, 0xf0, 0xbb, 0xbe, 0x11, 0x10, 0x21, 0xa5, 0x1e, 0x50, - 0x23, 0x55, 0xfe, 0x6b, 0x20, 0x40, 0x7e, 0x2c, 0xbf, 0x7c, 0xd3, 0x97, - 0xeb, 0x2e, 0x21, 0x56, 0x5b, 0x86, 0x78, 0x20, 0x2a, 0xb9, 0xfc, 0xb2, - 0xfc, 0xe3, 0x9c, 0x25, 0x96, 0x15, 0x65, 0x6c, 0x79, 0xbe, 0x16, 0x22, - 0x5b, 0xff, 0xf3, 0x0b, 0x03, 0xf7, 0x49, 0xfc, 0xc9, 0xce, 0xac, 0xbf, - 0xfe, 0xcd, 0x9c, 0x83, 0x1b, 0x92, 0xc7, 0xfb, 0xab, 0x2a, 0x51, 0x4d, - 0xa5, 0x4b, 0xfd, 0xbb, 0xec, 0xdb, 0x5a, 0x95, 0x94, 0xb2, 0xff, 0xe7, - 0x86, 0x10, 0xca, 0x60, 0x3e, 0x2c, 0xb6, 0x61, 0xe6, 0xef, 0x0b, 0xbf, - 0xff, 0xf8, 0x0c, 0x63, 0xc2, 0x36, 0xe1, 0x63, 0x24, 0x31, 0x84, 0xdb, - 0x67, 0xf9, 0x65, 0xf8, 0x10, 0x13, 0x5c, 0x59, 0x63, 0xf2, 0x29, 0xc9, - 0xf6, 0xff, 0xfd, 0x3a, 0x90, 0xe3, 0x24, 0x30, 0x3e, 0x01, 0x8b, 0x2a, - 0x55, 0xd6, 0xc4, 0xe9, 0xa7, 0x4f, 0x43, 0x55, 0xc8, 0x8a, 0x10, 0x9d, - 0x86, 0x7c, 0x79, 0x3d, 0xfd, 0xac, 0x91, 0x64, 0x96, 0x5e, 0x27, 0x62, - 0xcb, 0xbf, 0x95, 0x97, 0xff, 0x79, 0x99, 0xf4, 0x38, 0xf3, 0xf4, 0x16, - 0x5c, 0x26, 0xcb, 0x28, 0xcf, 0x7c, 0x91, 0xaa, 0x08, 0xdd, 0x32, 0xc2, - 0x1b, 0xeb, 0xb5, 0x36, 0x61, 0x87, 0xcf, 0x1d, 0x42, 0xe5, 0xb1, 0x1b, - 0x04, 0x73, 0x0d, 0x08, 0xee, 0x53, 0x5b, 0x6b, 0xb3, 0x4c, 0xf7, 0x06, - 0xd2, 0xde, 0xe1, 0x2a, 0xe0, 0x74, 0x82, 0x2c, 0xad, 0xce, 0x85, 0x97, - 0x62, 0x1a, 0x4e, 0x87, 0xd3, 0xf8, 0xc7, 0x38, 0xb1, 0x14, 0xbf, 0x2d, - 0x52, 0x1f, 0x59, 0x3b, 0x2f, 0xea, 0xe7, 0x61, 0xe7, 0x37, 0xff, 0x97, - 0x0c, 0xdb, 0x85, 0x11, 0x56, 0xaa, 0xbc, 0xa6, 0xeb, 0xf6, 0xb1, 0xd5, - 0x04, 0xa2, 0xcd, 0xf1, 0x9d, 0x35, 0x09, 0x88, 0xf9, 0xc4, 0x61, 0x27, - 0x16, 0xb7, 0x23, 0x60, 0xbf, 0xff, 0x80, 0x16, 0xff, 0x77, 0x53, 0x9f, - 0x47, 0x63, 0x77, 0x15, 0x65, 0x37, 0x56, 0x20, 0x72, 0xaa, 0xef, 0xe7, - 0x3e, 0xf0, 0xc9, 0x65, 0xe3, 0xd6, 0x2c, 0xad, 0xd3, 0xc6, 0xe1, 0x5d, - 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x9d, 0x5d, 0x9f, 0x2c, 0xbf, 0xfc, 0x00, - 0x11, 0x94, 0xf8, 0xd9, 0x9c, 0x59, 0x76, 0x98, 0xb2, 0xcd, 0xe5, 0x19, - 0xd8, 0x4b, 0xf1, 0xa3, 0x05, 0xff, 0x47, 0xbf, 0xff, 0x41, 0xbe, 0x43, - 0xd8, 0x70, 0xe4, 0xf6, 0x46, 0xb2, 0xff, 0xda, 0x6f, 0x1e, 0xfb, 0x77, - 0x8f, 0xfa, 0xca, 0x94, 0x4e, 0x1a, 0xb5, 0xfb, 0x41, 0x77, 0x0a, 0xa2, - 0xa3, 0x5f, 0xf9, 0xe0, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x26, 0x85, 0xff, - 0xe2, 0xc8, 0xa0, 0xfa, 0x81, 0x66, 0xf7, 0x59, 0x7c, 0x53, 0x9f, 0x2c, - 0xac, 0x47, 0x5b, 0x0d, 0x38, 0x99, 0xd4, 0xab, 0xfc, 0x23, 0xef, 0x6f, - 0xd3, 0xd9, 0x65, 0x37, 0x3f, 0x59, 0x3c, 0xbf, 0x6c, 0x26, 0xd3, 0xb2, - 0xcb, 0xe0, 0xbb, 0x85, 0x51, 0x57, 0xab, 0x0f, 0x6b, 0x45, 0x97, 0xf6, - 0x6f, 0x92, 0xf0, 0x16, 0x5f, 0xc6, 0x31, 0xce, 0xa5, 0x65, 0xff, 0xfb, - 0xf2, 0x03, 0x04, 0x18, 0xe7, 0xf6, 0x4e, 0x75, 0x65, 0xfc, 0xfd, 0xe6, - 0x7d, 0xd5, 0x95, 0xf2, 0x21, 0x89, 0x62, 0xcd, 0xf1, 0x32, 0x4d, 0x10, - 0xf8, 0xb8, 0x48, 0x59, 0x5f, 0xb4, 0x17, 0x70, 0xaa, 0x2b, 0x65, 0xff, - 0x41, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x48, 0x2b, 0xec, 0x64, 0xee, 0xac, - 0xb3, 0x7c, 0x44, 0xfb, 0x9a, 0x71, 0x1e, 0xf9, 0x86, 0x52, 0xb2, 0xfd, - 0xa0, 0xbb, 0x85, 0x51, 0x21, 0xaf, 0xed, 0x03, 0x7c, 0xb4, 0xc5, 0x97, - 0xf1, 0x67, 0x3b, 0x09, 0x59, 0x7c, 0xdd, 0xe0, 0xde, 0x51, 0x63, 0x84, - 0x06, 0x69, 0xbc, 0xc6, 0xff, 0xdc, 0x6f, 0x9d, 0x31, 0xe1, 0x44, 0xb2, - 0xf3, 0xb8, 0x57, 0x18, 0x1a, 0xa4, 0xfa, 0x02, 0x81, 0x77, 0xb7, 0x56, - 0x5d, 0x1c, 0xec, 0xb2, 0xff, 0xbd, 0x3d, 0xe4, 0x75, 0xc8, 0xe4, 0x96, - 0x53, 0x64, 0xf8, 0x20, 0x3b, 0x7d, 0xde, 0xe6, 0xe2, 0xcb, 0x12, 0xcb, - 0x1a, 0xca, 0x8e, 0x8d, 0x04, 0x70, 0x10, 0xbf, 0xfd, 0x1c, 0x23, 0x84, - 0x75, 0xe9, 0x61, 0x3f, 0x8f, 0x4b, 0x2d, 0xf2, 0xcb, 0xdd, 0xcf, 0xd6, - 0x5c, 0x64, 0xb2, 0xe9, 0x89, 0x65, 0xfc, 0xcc, 0x09, 0x3e, 0xea, 0xcb, - 0x08, 0xb2, 0xb0, 0xf0, 0x40, 0x5f, 0x7f, 0x48, 0xe7, 0xae, 0x35, 0x97, + 0x59, 0x7f, 0xba, 0x07, 0xfb, 0xd1, 0xbd, 0x65, 0x05, 0x3e, 0x9d, 0x42, + 0x00, 0xa3, 0x02, 0xe1, 0x07, 0x44, 0x6f, 0xf9, 0xdc, 0xbf, 0xd1, 0xc0, + 0xab, 0x2f, 0x10, 0x36, 0x59, 0x7e, 0xff, 0x93, 0xb0, 0x6b, 0x2f, 0xf1, + 0xb5, 0x85, 0x9f, 0x75, 0x65, 0x19, 0xef, 0x68, 0xae, 0xff, 0xa3, 0xd1, + 0xbd, 0x90, 0x5b, 0x2c, 0xbd, 0xc7, 0x25, 0x97, 0xff, 0xe8, 0xd7, 0xfe, + 0x8d, 0xf3, 0x44, 0x33, 0x98, 0x35, 0x97, 0xfd, 0xef, 0x46, 0xf9, 0x66, + 0xa1, 0x65, 0x79, 0x12, 0x4e, 0xb1, 0x53, 0x26, 0x13, 0xc3, 0xae, 0xc2, + 0xb6, 0xff, 0x8f, 0xa7, 0xdc, 0xfe, 0x34, 0xb2, 0xfb, 0x6f, 0x03, 0x65, + 0x95, 0x88, 0x94, 0x73, 0x70, 0x1c, 0x5f, 0x9f, 0x66, 0x46, 0x96, 0x5f, + 0xf9, 0xfe, 0xe4, 0xdb, 0xa6, 0x5d, 0xe2, 0xcb, 0x87, 0xe5, 0x97, 0xff, + 0xbb, 0x2c, 0xd6, 0x9e, 0x58, 0xc8, 0x0a, 0xca, 0xc3, 0xe0, 0x71, 0x7b, + 0xfe, 0xe8, 0x3d, 0x03, 0xf1, 0xfe, 0xb2, 0xff, 0xa2, 0x58, 0x65, 0x90, + 0x2a, 0xcb, 0xff, 0xf7, 0xa3, 0x5f, 0xf3, 0x07, 0xd0, 0x38, 0x73, 0x4b, + 0x2b, 0x75, 0x18, 0xd3, 0x1d, 0x74, 0xda, 0xee, 0xb1, 0x65, 0xff, 0x84, + 0xdb, 0x90, 0x7f, 0x76, 0x18, 0xb2, 0xff, 0xee, 0x3b, 0x87, 0x03, 0x05, + 0x01, 0x59, 0x6c, 0xf2, 0x20, 0xb8, 0x83, 0x7a, 0x35, 0xc5, 0x97, 0xff, + 0xb3, 0x79, 0x67, 0x03, 0xe3, 0xdc, 0x3e, 0x2c, 0xbf, 0xe8, 0x90, 0x99, + 0xae, 0x63, 0x4b, 0x2f, 0xf4, 0xa0, 0x53, 0xf3, 0xec, 0xb2, 0xb6, 0x3e, + 0xe3, 0x3a, 0xac, 0x4f, 0xbb, 0xe3, 0x4f, 0x42, 0x67, 0xf2, 0x82, 0x1c, + 0x04, 0x2e, 0xef, 0xf8, 0x79, 0xbf, 0x4f, 0xbf, 0xc6, 0xb2, 0xfc, 0xd0, + 0x23, 0x5b, 0x2c, 0xb1, 0xce, 0x3e, 0x66, 0x8f, 0x2f, 0xa5, 0xe1, 0x38, + 0xb2, 0xe7, 0x11, 0x51, 0x0b, 0xad, 0xdf, 0x8f, 0x20, 0x04, 0x97, 0xef, + 0x66, 0xc0, 0x25, 0x97, 0xcf, 0xa8, 0xde, 0xb2, 0x86, 0x79, 0x7f, 0x94, + 0x5f, 0xa6, 0x96, 0x39, 0x2c, 0xbe, 0x22, 0x00, 0xab, 0x2c, 0xde, 0x19, + 0x5b, 0x59, 0x28, 0xb4, 0x55, 0x53, 0x38, 0xd3, 0xdf, 0xa3, 0xbe, 0x72, + 0xef, 0xca, 0x0a, 0x14, 0x7c, 0x95, 0xa9, 0xd8, 0x64, 0x01, 0xcb, 0x7b, + 0xa4, 0xf9, 0x18, 0x84, 0xf7, 0xfe, 0xd0, 0x36, 0xcf, 0x4c, 0x50, 0x35, + 0x97, 0xfb, 0xbe, 0xcc, 0xef, 0xb1, 0x65, 0xfe, 0xd4, 0x16, 0x0c, 0x70, + 0xb2, 0xf8, 0x62, 0x41, 0x2c, 0xbf, 0xff, 0x8f, 0xa0, 0xd3, 0xf3, 0x0b, + 0xd9, 0x85, 0x2e, 0x2c, 0xa8, 0x3f, 0xa7, 0x22, 0xbf, 0x03, 0x5f, 0xce, + 0x85, 0x97, 0x79, 0xd6, 0x5c, 0x62, 0xac, 0xb0, 0x16, 0x56, 0x1b, 0xe9, + 0x85, 0xb8, 0x2f, 0x77, 0x7a, 0xb2, 0xf7, 0xfc, 0x6f, 0x09, 0xf5, 0xe2, + 0x06, 0x8c, 0xbd, 0x0a, 0xbe, 0x10, 0x75, 0xa4, 0x42, 0xeb, 0xf0, 0xdb, + 0xe1, 0xee, 0xac, 0xbf, 0xfc, 0x72, 0xe3, 0x7c, 0xfa, 0x4f, 0xb0, 0x81, + 0x59, 0x50, 0x7f, 0x78, 0x59, 0x50, 0x98, 0xcf, 0xa1, 0xf3, 0x7f, 0xcd, + 0x86, 0xce, 0xb3, 0xa7, 0xe0, 0x2c, 0xbe, 0xe1, 0xec, 0xd9, 0x59, 0x7e, + 0x01, 0x11, 0xc9, 0x65, 0x36, 0x11, 0x1d, 0x3d, 0x20, 0xf0, 0x9e, 0xff, + 0xcd, 0x86, 0xc9, 0xed, 0x13, 0x83, 0xcc, 0x59, 0x73, 0x68, 0x4b, 0x2f, + 0x7b, 0x34, 0xb2, 0x9b, 0x08, 0x80, 0x9e, 0x92, 0x7c, 0x37, 0x7e, 0xce, + 0x9e, 0xb1, 0x65, 0xff, 0x67, 0x44, 0xd9, 0xf4, 0x73, 0x2c, 0xbf, 0xa4, + 0xda, 0x6c, 0xd9, 0x9e, 0x13, 0xc5, 0x65, 0xf0, 0xcb, 0x3c, 0xb2, 0xf3, + 0x6b, 0x6d, 0x59, 0xe5, 0x65, 0xfe, 0xce, 0x73, 0x0c, 0x82, 0xb2, 0xa7, + 0xa4, 0xc9, 0xdb, 0x24, 0xd3, 0xc9, 0xdc, 0xf6, 0x8b, 0x3c, 0x08, 0x48, + 0xbe, 0xfe, 0x6c, 0x09, 0xef, 0x66, 0xcb, 0x2e, 0x9f, 0xf9, 0x65, 0xbc, + 0xb2, 0x9b, 0x07, 0xc0, 0xda, 0xa6, 0x80, 0x1b, 0xbe, 0x1e, 0x14, 0xcb, + 0x2e, 0x3e, 0xac, 0xb7, 0x4c, 0xdc, 0x78, 0x8a, 0xff, 0x7d, 0xdf, 0xa4, + 0x02, 0x15, 0x65, 0xff, 0xa2, 0x62, 0xcd, 0xee, 0xc6, 0x1a, 0xca, 0x73, + 0xf6, 0x01, 0xbd, 0xff, 0xff, 0xde, 0x00, 0xcf, 0x35, 0x31, 0x66, 0xff, + 0x60, 0xe3, 0x9c, 0x81, 0x56, 0x5f, 0xfe, 0x39, 0xa5, 0x9f, 0x4a, 0x73, + 0x1d, 0xc9, 0x65, 0xcf, 0xb8, 0xb2, 0xff, 0xff, 0x73, 0x3b, 0x3b, 0xd8, + 0x18, 0x68, 0xb0, 0x7e, 0x39, 0x96, 0x59, 0xb3, 0x89, 0xfc, 0x8b, 0x09, + 0xc0, 0x90, 0xf9, 0xd1, 0xd3, 0x3a, 0x33, 0x74, 0xf8, 0xab, 0x2f, 0xff, + 0xc3, 0x9d, 0x9c, 0x2d, 0xb6, 0x7d, 0xd9, 0xd3, 0xbd, 0x3e, 0xb2, 0xf9, + 0xe5, 0x1f, 0xac, 0xbf, 0x79, 0xfa, 0xf2, 0x59, 0x7f, 0x1e, 0x8b, 0x33, + 0x4b, 0x2f, 0x07, 0x6f, 0x2c, 0xbf, 0x47, 0x1b, 0x5b, 0x61, 0xb0, 0xb2, + 0xe6, 0x31, 0x65, 0x70, 0xf3, 0x40, 0x6d, 0x7e, 0x8e, 0x03, 0xc6, 0xb2, + 0xa7, 0x22, 0xd2, 0x4d, 0x7c, 0x22, 0xbf, 0xee, 0x16, 0x7d, 0x22, 0x7d, + 0x96, 0x5f, 0xb8, 0x07, 0x2f, 0xd6, 0x59, 0x93, 0x8f, 0x87, 0xa7, 0x37, + 0xb8, 0x7f, 0xac, 0xac, 0x3c, 0x73, 0x2a, 0xbc, 0xd3, 0x4d, 0x24, 0xbf, + 0x39, 0x77, 0xd8, 0x91, 0xb9, 0xa0, 0xbf, 0xff, 0xfa, 0x36, 0x9d, 0xc0, + 0x47, 0xdd, 0xf6, 0x33, 0x3a, 0x51, 0xff, 0xf0, 0xb2, 0xfb, 0xb3, 0x67, + 0x56, 0x58, 0x6b, 0x28, 0x68, 0xaa, 0x77, 0x60, 0x11, 0xd0, 0xd3, 0x51, + 0xfd, 0x00, 0xa1, 0xc5, 0x7e, 0xf1, 0x4f, 0x19, 0xfd, 0xc5, 0x97, 0x3b, + 0xac, 0xb7, 0x8c, 0xf2, 0x5c, 0xd2, 0xff, 0xfe, 0xff, 0x51, 0x3b, 0x51, + 0xdf, 0xa5, 0x02, 0xe9, 0xda, 0x59, 0x7f, 0xe3, 0x9a, 0x77, 0xa0, 0xf4, + 0x0d, 0xd5, 0x95, 0x08, 0xa5, 0xfd, 0x86, 0xfe, 0xd1, 0xee, 0x6b, 0x9c, + 0x59, 0x7f, 0x10, 0x3a, 0x7b, 0x42, 0xcb, 0xff, 0x18, 0xb0, 0x51, 0xc8, + 0x36, 0x2c, 0xbf, 0x6f, 0x04, 0xb3, 0x8b, 0x2f, 0xbf, 0x32, 0x02, 0xca, + 0xc3, 0xcc, 0x01, 0x55, 0xf6, 0x0f, 0x36, 0x59, 0x7d, 0xbb, 0x3a, 0x27, + 0xa5, 0x97, 0xfb, 0xaf, 0xf7, 0x1f, 0xe6, 0x96, 0x5d, 0xa8, 0x59, 0x53, + 0xd2, 0xf6, 0x28, 0xd9, 0x30, 0x88, 0x24, 0xff, 0x43, 0x7c, 0xe5, 0x15, + 0xee, 0xbf, 0xcd, 0x0c, 0xbd, 0x11, 0xb0, 0xc7, 0xc5, 0xa5, 0x08, 0x8e, + 0x90, 0xef, 0x22, 0x10, 0xb3, 0x70, 0xda, 0xf1, 0x67, 0x56, 0x5f, 0xfe, + 0xfa, 0x5c, 0xe4, 0x1f, 0xdd, 0xec, 0x31, 0x65, 0x11, 0xf3, 0xb4, 0x37, + 0x7f, 0xf1, 0x74, 0xb3, 0x7b, 0x98, 0x70, 0x96, 0x5f, 0x46, 0x0c, 0xd6, + 0x51, 0x9f, 0x13, 0x48, 0x57, 0x81, 0xde, 0x2c, 0xbf, 0xed, 0xd7, 0xec, + 0xd2, 0x3d, 0x4c, 0xb2, 0xff, 0xa2, 0x4d, 0x3e, 0xbb, 0x18, 0xb2, 0xff, + 0xff, 0xff, 0xb9, 0x3b, 0xd9, 0xb4, 0x35, 0xf4, 0xb9, 0x38, 0x01, 0xd6, + 0xa2, 0x74, 0xc2, 0xfd, 0x3a, 0x77, 0xa7, 0xd6, 0x5f, 0xdf, 0x4e, 0x9b, + 0x30, 0x2b, 0x2a, 0x15, 0x0a, 0xe4, 0x21, 0x02, 0x44, 0xe3, 0xa4, 0x7d, + 0xd3, 0x80, 0x42, 0xae, 0xcd, 0x2c, 0xbf, 0xa5, 0x26, 0x4d, 0x1b, 0x8b, + 0x2d, 0xfa, 0xca, 0xfc, 0xf5, 0x88, 0x48, 0x06, 0x57, 0x4f, 0xc2, 0xcb, + 0xd3, 0x47, 0xcb, 0x28, 0x8d, 0xbf, 0x46, 0x6e, 0x9f, 0x6c, 0x2c, 0xbf, + 0xfd, 0xfc, 0x10, 0x67, 0x61, 0x02, 0x59, 0xc5, 0x97, 0xf9, 0xfe, 0xff, + 0x47, 0xf7, 0xeb, 0x2f, 0xfc, 0x06, 0x46, 0x70, 0xb3, 0x7b, 0xac, 0xbf, + 0xdf, 0xb9, 0x66, 0xc2, 0x49, 0x65, 0x7c, 0x8f, 0x5f, 0x25, 0xf0, 0xdf, + 0xa7, 0xd7, 0xa7, 0x48, 0x2b, 0x2a, 0x13, 0xf6, 0xc6, 0x61, 0x48, 0x0e, + 0x31, 0x36, 0x1f, 0x5f, 0xff, 0xda, 0x9c, 0x59, 0xbf, 0x35, 0x29, 0xd0, + 0x37, 0x9b, 0x8b, 0x2f, 0xfe, 0xc6, 0xb7, 0x18, 0x03, 0x66, 0x17, 0xeb, + 0x2f, 0xff, 0xfd, 0xf4, 0x0a, 0x58, 0x3f, 0x1c, 0xd3, 0xb9, 0x9f, 0x4b, + 0x58, 0xd2, 0xca, 0x34, 0x5d, 0x12, 0x45, 0x62, 0x67, 0x85, 0x18, 0xdd, + 0xf7, 0x41, 0x1b, 0x2c, 0xbf, 0xa4, 0xfa, 0xe1, 0x8d, 0x65, 0xfd, 0x9e, + 0xc6, 0x40, 0x56, 0x54, 0x1e, 0xcc, 0x8b, 0x6f, 0xc7, 0xa3, 0xdf, 0x8b, + 0x28, 0xd1, 0x8b, 0xd7, 0xcd, 0xc2, 0x1b, 0xe9, 0xfc, 0xd4, 0x2c, 0xbf, + 0xff, 0xbe, 0x97, 0x19, 0x85, 0x3b, 0x70, 0xfd, 0xb1, 0xe6, 0x96, 0x5b, + 0x75, 0x65, 0x61, 0xfa, 0x85, 0x86, 0xff, 0xff, 0xcd, 0x4e, 0x2c, 0x0f, + 0x8c, 0xb0, 0x1f, 0x76, 0x73, 0xb5, 0x82, 0xac, 0xbf, 0xc2, 0x8c, 0x11, + 0xe2, 0x85, 0x97, 0xf4, 0xb8, 0x28, 0x23, 0xe5, 0x97, 0xd8, 0x37, 0x92, + 0xca, 0xf8, 0xf4, 0x5a, 0x30, 0xbe, 0xd4, 0x46, 0xcb, 0x2f, 0xa6, 0x8e, + 0xba, 0xcb, 0x9d, 0xae, 0x1e, 0x20, 0x08, 0xaf, 0x6f, 0xe8, 0x8b, 0x2a, + 0x13, 0xac, 0xc7, 0x23, 0x84, 0x21, 0x34, 0x34, 0x5d, 0x7f, 0xfe, 0x2c, + 0x1f, 0x80, 0xd7, 0x0f, 0xbe, 0x37, 0xfd, 0x65, 0xfe, 0x2c, 0xdf, 0x3a, + 0x4c, 0x92, 0xca, 0xd2, 0x23, 0x78, 0xad, 0x77, 0x37, 0xac, 0xa8, 0x37, + 0x80, 0x23, 0xbe, 0xff, 0xa7, 0xb2, 0xcb, 0xfd, 0xdf, 0x19, 0x7e, 0x23, + 0x16, 0x5f, 0xff, 0xde, 0x3c, 0x2c, 0x31, 0xe4, 0x7e, 0xe1, 0x7d, 0x2c, + 0xbf, 0xdb, 0x43, 0x0a, 0x3e, 0xfd, 0x65, 0x49, 0x18, 0x3f, 0x1a, 0xba, + 0xbd, 0xfb, 0xd3, 0x9f, 0x38, 0xb2, 0xa0, 0xf6, 0x70, 0xc2, 0xfe, 0xe9, + 0x44, 0xb3, 0xab, 0x2f, 0x34, 0xfc, 0x59, 0x79, 0xf5, 0x29, 0xc7, 0x93, + 0xe2, 0xca, 0x85, 0xe9, 0x7c, 0x87, 0x6f, 0xc6, 0x47, 0x09, 0x9d, 0x47, + 0x9b, 0xe8, 0xc5, 0x9c, 0x80, 0xa3, 0x3e, 0x03, 0x4d, 0xb7, 0x56, 0x5b, + 0xab, 0x2e, 0x8f, 0xd6, 0x5f, 0xfe, 0x30, 0x36, 0xe2, 0x77, 0x01, 0xfb, + 0x33, 0xab, 0x2b, 0xe3, 0xe8, 0xd0, 0xbd, 0xff, 0xa0, 0x3c, 0x83, 0xfb, + 0xb0, 0xc5, 0x94, 0xb2, 0xf6, 0x81, 0xba, 0xb2, 0xa4, 0x6a, 0xfa, 0x17, + 0x44, 0x88, 0xa0, 0x34, 0xdf, 0x7f, 0xcd, 0xba, 0xb2, 0xff, 0xce, 0x2c, + 0xee, 0xbb, 0x94, 0xed, 0x96, 0x54, 0x1f, 0x41, 0x12, 0xde, 0x7e, 0xf1, + 0x65, 0xc0, 0xd9, 0x65, 0x48, 0xda, 0x68, 0x72, 0xfd, 0x92, 0xf3, 0xc9, + 0x65, 0xc5, 0xd9, 0x8f, 0x23, 0xc4, 0x37, 0xf9, 0xc8, 0x4f, 0x7a, 0x06, + 0xb2, 0xfe, 0x00, 0x67, 0x0f, 0xe6, 0x96, 0x5f, 0xe6, 0xbd, 0x85, 0xf4, + 0x0a, 0xb2, 0xff, 0x07, 0xd9, 0xdd, 0x63, 0x4b, 0x2a, 0x0f, 0xa4, 0xcd, + 0x6e, 0xd4, 0x96, 0x5f, 0xe1, 0x5f, 0x99, 0xd1, 0x36, 0x59, 0x7d, 0x38, + 0x2d, 0x42, 0xca, 0x91, 0xed, 0xb0, 0xda, 0xa1, 0x3b, 0x1f, 0x8b, 0x8c, + 0xcf, 0x50, 0x9c, 0x61, 0x03, 0xb9, 0x5f, 0xdc, 0x19, 0x3b, 0xf9, 0x65, + 0xfe, 0xd4, 0x0e, 0x3d, 0x12, 0x59, 0x7f, 0xff, 0xe3, 0xfb, 0xaf, 0xe2, + 0xc1, 0xf8, 0xe6, 0xe0, 0x70, 0xca, 0x65, 0x97, 0xff, 0xd1, 0xd9, 0xc4, + 0x0d, 0xc9, 0xd9, 0xd9, 0xfc, 0xea, 0xca, 0x34, 0x7b, 0xe8, 0xc8, 0x0d, + 0x97, 0x3f, 0x56, 0x5f, 0xfd, 0xe8, 0x16, 0x77, 0x49, 0xfb, 0x1d, 0x59, + 0x7f, 0xa7, 0x7d, 0x23, 0xf0, 0x18, 0xb2, 0xbc, 0x89, 0x3f, 0xc5, 0x89, + 0x1a, 0xfc, 0xd3, 0xfa, 0x66, 0xc2, 0xcb, 0xfe, 0xc1, 0xce, 0xe8, 0xa6, + 0xd7, 0x16, 0x5b, 0x34, 0x7d, 0x7b, 0xcb, 0xaf, 0xfd, 0xa9, 0xa7, 0x72, + 0x0f, 0x67, 0xea, 0xca, 0x84, 0xc1, 0x87, 0x09, 0xb0, 0x14, 0xdc, 0xff, + 0xac, 0xbf, 0xfe, 0x9c, 0x51, 0xc8, 0xd8, 0xb0, 0x7e, 0x39, 0x96, 0x5f, + 0xff, 0xfd, 0xcd, 0x14, 0x4d, 0x38, 0x4e, 0x9c, 0xb3, 0xee, 0xe6, 0xf2, + 0xce, 0x2c, 0xad, 0x23, 0x20, 0x94, 0x6b, 0xa9, 0x80, 0x82, 0x1b, 0xf4, + 0xb2, 0xf7, 0x67, 0x0a, 0xb2, 0xe9, 0xa4, 0xb2, 0xa0, 0xf0, 0xfe, 0x0b, + 0xd1, 0x05, 0xff, 0xc5, 0xde, 0x6f, 0x3e, 0xc6, 0x14, 0xcb, 0x2f, 0xda, + 0x3e, 0x47, 0xcb, 0x2b, 0x0f, 0xb5, 0xd1, 0x6f, 0xe6, 0xcc, 0xd2, 0x3e, + 0xf1, 0x65, 0xee, 0x09, 0xc5, 0x95, 0xb1, 0xe8, 0xb0, 0xce, 0xe8, 0xfd, + 0x65, 0xf8, 0xbb, 0xec, 0xdc, 0x59, 0x7e, 0x9b, 0x8f, 0xa1, 0x56, 0x5e, + 0x76, 0xb5, 0x07, 0xa9, 0x85, 0x57, 0xfc, 0x42, 0xc4, 0xc6, 0x50, 0xc5, + 0x97, 0xf0, 0xaf, 0x22, 0x86, 0x2c, 0xa8, 0x3e, 0x53, 0x38, 0xa9, 0x27, + 0x6c, 0x6e, 0x33, 0x11, 0xe9, 0xaf, 0xd0, 0x97, 0xbe, 0x30, 0xbe, 0x96, + 0x5e, 0x63, 0xb4, 0xb2, 0xdc, 0x91, 0xbe, 0x72, 0x1b, 0xfc, 0x63, 0xc2, + 0x97, 0xb1, 0x65, 0xff, 0x78, 0xf5, 0x37, 0x9f, 0x34, 0xb2, 0xff, 0xff, + 0xfe, 0xff, 0x85, 0x8f, 0xf4, 0xe1, 0xf8, 0x13, 0x8b, 0x3e, 0x96, 0x03, + 0x62, 0xc7, 0xf9, 0x65, 0x05, 0x19, 0x06, 0x73, 0x79, 0xdc, 0x2a, 0x8a, + 0x61, 0x50, 0x9b, 0x4e, 0xc4, 0xd9, 0x0f, 0x90, 0x91, 0x5f, 0xfb, 0x0b, + 0xe9, 0x73, 0xb1, 0xa9, 0x96, 0x5f, 0xf9, 0xfe, 0xc3, 0xd1, 0xb2, 0x05, + 0x59, 0x7b, 0x0f, 0xf5, 0x96, 0xc5, 0x96, 0xcf, 0xcd, 0x66, 0xf1, 0xca, + 0x59, 0x6f, 0x88, 0xda, 0x70, 0xa6, 0x8d, 0x30, 0x5d, 0x20, 0x3c, 0x27, + 0xaf, 0x60, 0x34, 0xb2, 0xed, 0xa6, 0x59, 0x66, 0x96, 0x5c, 0x63, 0x59, + 0x5b, 0xa6, 0xa3, 0x82, 0x57, 0x0e, 0x65, 0x97, 0xfd, 0xac, 0x1f, 0xa1, + 0xa6, 0x01, 0x65, 0xc2, 0xec, 0xb2, 0xa1, 0x1a, 0x18, 0x82, 0xc2, 0x4f, + 0x0c, 0x34, 0x75, 0x7e, 0xc6, 0x31, 0xd8, 0xb2, 0xe6, 0x7e, 0xb2, 0xf6, + 0xbc, 0xf3, 0x91, 0x11, 0xa4, 0x9f, 0xc9, 0xef, 0xf4, 0xef, 0xa4, 0x51, + 0x9c, 0x59, 0x5f, 0x27, 0xda, 0x71, 0xba, 0x3a, 0x25, 0xff, 0xfb, 0xf3, + 0xe4, 0xef, 0x3f, 0x3b, 0x13, 0x73, 0xd8, 0xb2, 0x96, 0x5f, 0xc0, 0x31, + 0x99, 0x1a, 0xcb, 0x0a, 0x66, 0xd9, 0x81, 0x77, 0xe7, 0xe7, 0x9e, 0x4b, + 0x2f, 0xde, 0x79, 0x1b, 0x16, 0x56, 0x8f, 0xd7, 0xc4, 0xed, 0x13, 0xdf, + 0xc7, 0xe7, 0x3d, 0xa1, 0x65, 0xe9, 0x47, 0xcb, 0x2f, 0xff, 0xc5, 0x9f, + 0x76, 0x77, 0x9f, 0x7c, 0xe1, 0xf8, 0x1b, 0x2c, 0xbf, 0x7a, 0x36, 0xc6, + 0x96, 0x59, 0x90, 0x88, 0x71, 0xaf, 0x5f, 0x76, 0x59, 0xd5, 0x95, 0x09, + 0xa5, 0x61, 0x87, 0xc5, 0x8f, 0x0a, 0x40, 0x14, 0x5f, 0xbe, 0x09, 0x46, + 0xf5, 0x97, 0xf3, 0xea, 0x69, 0x1f, 0x96, 0x5f, 0xe0, 0x6d, 0xe3, 0xdf, + 0x93, 0x2c, 0xbf, 0xf3, 0xfd, 0xc1, 0x3d, 0xe8, 0x20, 0xac, 0xac, 0x46, + 0x7b, 0x95, 0x70, 0xb8, 0x06, 0xf7, 0x73, 0x8b, 0x2d, 0x8b, 0x2f, 0xfd, + 0x31, 0x8a, 0x7d, 0x2c, 0xdc, 0x9d, 0xa3, 0x4f, 0xbc, 0x5e, 0xff, 0xff, + 0x16, 0x07, 0xbd, 0x8e, 0xf2, 0x62, 0xc6, 0xa7, 0x44, 0xf4, 0xb2, 0xff, + 0xc1, 0x86, 0x7b, 0x39, 0x86, 0x4b, 0x2a, 0x11, 0x49, 0xe6, 0x8b, 0xf6, + 0xa3, 0x7c, 0x69, 0x65, 0xfc, 0xe5, 0x38, 0x70, 0x4b, 0x2a, 0x13, 0x78, + 0xc8, 0xc0, 0x0c, 0x88, 0x05, 0x37, 0xfd, 0x0d, 0x04, 0xf6, 0xcf, 0xba, + 0xb2, 0xf4, 0x6a, 0x4b, 0x2a, 0x79, 0x6d, 0xaf, 0x5b, 0x58, 0xa4, 0x3c, + 0xed, 0x0a, 0xd9, 0x42, 0x28, 0x72, 0x93, 0x71, 0x6c, 0x58, 0xc8, 0xc3, + 0x1e, 0x47, 0xd1, 0x9b, 0x1b, 0x64, 0xd2, 0x94, 0xb5, 0x1f, 0xbb, 0x23, + 0x47, 0xf4, 0xa2, 0x1f, 0xcd, 0x8a, 0x52, 0x77, 0x23, 0x1d, 0xec, 0x7b, + 0x5b, 0xd0, 0x5a, 0x3b, 0xbf, 0xed, 0x3b, 0x53, 0x9c, 0x6f, 0xb2, 0xcb, + 0xf9, 0xda, 0x98, 0x8c, 0x6b, 0x2f, 0x1e, 0xcc, 0x59, 0x7f, 0xbe, 0xf6, + 0x7e, 0xc8, 0x0a, 0xcb, 0xff, 0xfb, 0x36, 0x72, 0xef, 0x20, 0x33, 0x98, + 0xc3, 0x20, 0xac, 0xbf, 0xa5, 0xc9, 0xa5, 0x13, 0x2c, 0xa1, 0xa2, 0x33, + 0xe5, 0xcb, 0xf8, 0x7c, 0x78, 0xfa, 0x4b, 0x2a, 0x0f, 0x49, 0xc9, 0x2f, + 0xf4, 0xde, 0x38, 0x63, 0x8d, 0x65, 0xfe, 0x9d, 0xac, 0x3f, 0x40, 0xd6, + 0x54, 0x1f, 0x39, 0x19, 0xd0, 0xaa, 0x90, 0x74, 0x79, 0xe2, 0xe7, 0x1d, + 0x28, 0xc4, 0xc1, 0x08, 0x7b, 0xec, 0x21, 0x77, 0x56, 0x5f, 0xff, 0xff, + 0xbc, 0x7d, 0xf6, 0x6a, 0x37, 0xce, 0x60, 0x37, 0x27, 0x67, 0xd2, 0xc0, + 0x17, 0xfc, 0x59, 0x6e, 0x9a, 0x2c, 0xf8, 0x4b, 0x7a, 0x63, 0x62, 0xcb, + 0xff, 0x31, 0x87, 0x37, 0x9c, 0xa2, 0x16, 0x5f, 0xe7, 0x94, 0xde, 0x3f, + 0xba, 0xb2, 0xff, 0xd0, 0x19, 0xc4, 0xfc, 0x64, 0x12, 0xcb, 0xf8, 0x1d, + 0x9d, 0xff, 0xdf, 0xac, 0xbf, 0xfd, 0xe3, 0x0f, 0xb2, 0x77, 0x01, 0xe8, + 0xdd, 0x59, 0x7f, 0x14, 0x7d, 0x23, 0x25, 0x94, 0xb2, 0xff, 0x67, 0x8b, + 0x3b, 0xe3, 0x59, 0x43, 0x3e, 0x82, 0x2c, 0xe8, 0x5d, 0xff, 0x76, 0x25, + 0xc9, 0xa5, 0x1b, 0x2c, 0xb6, 0x62, 0x64, 0x1f, 0x42, 0xdf, 0xa5, 0xd7, + 0xff, 0xdd, 0xf6, 0x7f, 0x39, 0x91, 0x9d, 0x1e, 0x12, 0xcb, 0x34, 0xb2, + 0xfe, 0x71, 0xeb, 0x40, 0xd9, 0x65, 0xfa, 0x30, 0xbb, 0x3e, 0xb2, 0xd3, + 0xca, 0xca, 0x15, 0x10, 0x01, 0x12, 0x61, 0x7f, 0x0a, 0x6f, 0xba, 0x7a, + 0xc5, 0x96, 0xfa, 0x71, 0xef, 0xe1, 0xf5, 0xb9, 0x89, 0xad, 0x3c, 0x66, + 0x14, 0x35, 0x71, 0x4c, 0x35, 0xf1, 0xf1, 0x46, 0xa9, 0xd8, 0xe5, 0x6f, + 0xff, 0xfe, 0x91, 0xf0, 0xb3, 0x7f, 0xb2, 0x6f, 0x41, 0x4d, 0x39, 0xc2, + 0x7b, 0xd6, 0x5f, 0xfe, 0x3c, 0x68, 0xb3, 0x99, 0xef, 0x1f, 0x16, 0x5b, + 0x3e, 0x45, 0xff, 0x5d, 0xef, 0xfd, 0x9a, 0xf4, 0x4e, 0x7e, 0x82, 0x16, + 0x5f, 0xff, 0xfe, 0x86, 0x8b, 0x37, 0xfb, 0x35, 0xac, 0xef, 0x37, 0x3b, + 0x1c, 0xf1, 0xe2, 0xca, 0x34, 0x5d, 0x68, 0xfe, 0xff, 0xff, 0xb0, 0x85, + 0x9d, 0xa8, 0xde, 0x7d, 0xe6, 0x0a, 0x7a, 0x7f, 0x96, 0x51, 0x26, 0xe1, + 0xd8, 0x71, 0x88, 0x45, 0x46, 0xaa, 0xf0, 0x12, 0xa1, 0xaf, 0xff, 0x18, + 0xe7, 0x30, 0xe7, 0x40, 0xde, 0x6e, 0x2c, 0xbf, 0x78, 0xfb, 0x8c, 0x59, + 0x69, 0x2c, 0xa6, 0xc9, 0xb8, 0x91, 0x3d, 0xb5, 0x08, 0xab, 0xda, 0x10, + 0xd7, 0xe9, 0x47, 0xd2, 0xe2, 0xcb, 0xff, 0xff, 0xff, 0x61, 0x0b, 0x38, + 0xfc, 0x06, 0xf3, 0x1f, 0x61, 0x84, 0xe2, 0xf8, 0xdb, 0xe7, 0xbe, 0xfe, + 0x06, 0x92, 0xff, 0xff, 0xe0, 0x9b, 0x43, 0x06, 0xa7, 0x02, 0x36, 0xe0, + 0x7c, 0x0f, 0x7c, 0xed, 0x2c, 0xb8, 0x1b, 0x7c, 0x9a, 0x29, 0x94, 0xf2, + 0x14, 0x57, 0xf0, 0xa5, 0x9d, 0xf1, 0xac, 0xb8, 0x78, 0xb2, 0xa1, 0x93, + 0x2f, 0x85, 0x06, 0x3b, 0xe9, 0xe8, 0x37, 0x86, 0xb1, 0x46, 0xe6, 0x04, + 0x39, 0xf2, 0xdb, 0x6e, 0x2c, 0xb3, 0x16, 0x5f, 0xe7, 0x96, 0x31, 0xcb, + 0xf5, 0x97, 0xd9, 0xe8, 0x99, 0x65, 0xe6, 0x06, 0x74, 0xf2, 0x7c, 0xbe, + 0x11, 0x73, 0x2b, 0xff, 0xff, 0xda, 0x09, 0x66, 0xf9, 0xdc, 0x3c, 0x2f, + 0xe7, 0x16, 0x6f, 0x2c, 0xe6, 0xf5, 0x97, 0xff, 0xbc, 0x0c, 0x21, 0x67, + 0x77, 0xe9, 0x61, 0x2c, 0xbf, 0xd8, 0xec, 0x9d, 0x3f, 0x0c, 0x59, 0x7f, + 0xfa, 0x51, 0xcf, 0x03, 0x59, 0xde, 0x9b, 0x16, 0x50, 0xd1, 0x9c, 0x14, + 0xdf, 0x8d, 0xef, 0xff, 0xc0, 0x89, 0xc5, 0x83, 0xf1, 0x8a, 0x38, 0x17, + 0xf5, 0x95, 0x89, 0xeb, 0xea, 0x34, 0xd2, 0x31, 0xbf, 0xfd, 0xdf, 0x66, + 0xe7, 0x66, 0xcd, 0xd7, 0x2f, 0xd6, 0x5f, 0xe8, 0x63, 0x97, 0xd2, 0x85, + 0x96, 0x32, 0x44, 0x1f, 0x54, 0x6f, 0x9a, 0x9d, 0xc9, 0x96, 0x5b, 0xab, + 0x2a, 0x0d, 0xc3, 0x93, 0xdf, 0xf8, 0xb6, 0x2c, 0xdf, 0xc0, 0x47, 0xcb, + 0x2f, 0xfb, 0xd1, 0xf4, 0xe9, 0xe3, 0xbf, 0xf3, 0x59, 0x7e, 0xef, 0xd2, + 0x39, 0xf5, 0x94, 0x33, 0xf1, 0xe2, 0x2d, 0x42, 0x35, 0x36, 0x85, 0x95, + 0x1a, 0xa3, 0x2e, 0x42, 0xe3, 0xab, 0xe0, 0x87, 0x9d, 0xfb, 0x7e, 0xe7, + 0x73, 0xcb, 0x2f, 0xfd, 0x9c, 0x04, 0x0b, 0xc8, 0xd4, 0xcb, 0x28, 0xcf, + 0xb5, 0xa2, 0xdb, 0xed, 0x49, 0xc6, 0xb2, 0xe7, 0x0a, 0xcb, 0xf6, 0xb2, + 0x7c, 0xff, 0x59, 0x7f, 0xd2, 0x0f, 0x8f, 0xc4, 0x0d, 0x96, 0x56, 0xc7, + 0xca, 0x45, 0x77, 0xbb, 0x8d, 0xb5, 0x94, 0xd9, 0x45, 0xec, 0x3d, 0xe1, + 0x15, 0xfe, 0x8c, 0xef, 0xe2, 0x14, 0x96, 0x5d, 0x9e, 0x59, 0x44, 0x79, + 0x53, 0xe6, 0x97, 0x0b, 0x0b, 0x2e, 0xc1, 0xac, 0xa9, 0xe9, 0x7d, 0x33, + 0x67, 0x99, 0x4e, 0x28, 0x64, 0x2d, 0x5c, 0x8b, 0x90, 0xdf, 0xeb, 0xe0, + 0x09, 0x37, 0x8b, 0xdf, 0x8f, 0xbc, 0xcf, 0xd6, 0x5f, 0xbd, 0x9b, 0xcf, + 0x7a, 0xcb, 0xff, 0xff, 0x13, 0xf7, 0xd1, 0x34, 0xe2, 0xcf, 0x1f, 0x78, + 0x00, 0x82, 0x16, 0x5d, 0x9f, 0xac, 0xbf, 0xf8, 0xbf, 0x2c, 0x39, 0xbd, + 0x04, 0x2a, 0xca, 0x33, 0xdb, 0x71, 0x7b, 0xfa, 0x5c, 0xe9, 0xc7, 0xcb, + 0x2f, 0xd0, 0x5e, 0x79, 0x2c, 0xbd, 0x03, 0x85, 0x94, 0x33, 0xc1, 0x61, + 0x35, 0x2c, 0xbd, 0x03, 0x85, 0x97, 0xec, 0xef, 0x1e, 0x69, 0xc7, 0x91, + 0x04, 0x4c, 0x0b, 0xbe, 0x9e, 0x5b, 0x5c, 0xf7, 0x3c, 0xac, 0xbe, 0x37, + 0x21, 0xac, 0xb3, 0x53, 0x8f, 0x60, 0x67, 0x17, 0xf7, 0xf8, 0xff, 0x4b, + 0x8b, 0x2a, 0x48, 0xff, 0x38, 0x52, 0xf0, 0xae, 0xff, 0xfd, 0xa0, 0x6b, + 0x50, 0x58, 0x29, 0xfb, 0xd8, 0x22, 0xcb, 0xef, 0x40, 0x09, 0x65, 0xf0, + 0x1f, 0xee, 0xac, 0xbf, 0xff, 0xfe, 0x73, 0x17, 0x35, 0xa3, 0x64, 0xe9, + 0xa4, 0x06, 0x7d, 0x2e, 0x72, 0x34, 0x15, 0x95, 0x25, 0xc4, 0x21, 0x94, + 0x61, 0x58, 0xb0, 0xc8, 0xf8, 0x82, 0x68, 0xed, 0xb4, 0x68, 0xc5, 0x6f, + 0x10, 0x91, 0x1d, 0xb7, 0x16, 0x52, 0xcb, 0xfb, 0x18, 0x3f, 0x67, 0x16, + 0x5f, 0xdc, 0x08, 0x5c, 0x85, 0x59, 0x4d, 0xa1, 0xf3, 0x6c, 0x17, 0xd2, + 0xdb, 0xfc, 0x53, 0xb7, 0xfd, 0x2e, 0x4c, 0xb2, 0xed, 0xbe, 0x59, 0x7e, + 0xf3, 0xee, 0xc7, 0xeb, 0x28, 0x67, 0xfc, 0xe7, 0x5b, 0x83, 0x37, 0xf4, + 0xc5, 0x9e, 0x7e, 0xac, 0xbf, 0x85, 0x3f, 0xb8, 0x0e, 0x2c, 0xad, 0x1e, + 0xe9, 0x16, 0xd4, 0x26, 0xc5, 0x90, 0xb3, 0x38, 0x47, 0x5f, 0xed, 0x60, + 0xe6, 0x94, 0x6e, 0xac, 0xbe, 0xd0, 0xf0, 0x96, 0x5f, 0x7f, 0xcd, 0xd0, + 0xac, 0xa3, 0x3c, 0x80, 0x10, 0xdf, 0xbf, 0xce, 0x89, 0xb2, 0xcb, 0x75, + 0x65, 0xb8, 0xb2, 0xfc, 0xd7, 0x73, 0xd3, 0x85, 0x34, 0x5f, 0x08, 0xd4, + 0x22, 0x41, 0xd3, 0x6a, 0x7b, 0x56, 0xcb, 0x28, 0xf7, 0x0c, 0xe3, 0xcf, + 0xe5, 0x0b, 0x1b, 0xfb, 0xd8, 0x19, 0x43, 0x16, 0x5f, 0xef, 0x64, 0x9c, + 0xb3, 0xe5, 0x95, 0x87, 0xbf, 0xd2, 0xdb, 0xff, 0xff, 0x4a, 0x36, 0xfa, + 0x5c, 0x31, 0x67, 0x73, 0x02, 0x6c, 0x06, 0xb6, 0x59, 0x7f, 0xff, 0x9f, + 0xfe, 0x6d, 0x86, 0x5d, 0x2c, 0xdf, 0x3b, 0x8d, 0x12, 0xca, 0xf9, 0x1b, + 0x04, 0xe7, 0x7f, 0x7b, 0x0b, 0xe8, 0x15, 0x65, 0xfc, 0xe5, 0xd0, 0x6b, + 0x65, 0x94, 0x49, 0xbc, 0x76, 0x1f, 0x2d, 0x11, 0x88, 0x5d, 0x70, 0xf7, + 0x16, 0x5e, 0xc3, 0x99, 0x65, 0xff, 0x8f, 0x26, 0x9d, 0xc8, 0xf3, 0xee, + 0xac, 0xbf, 0xec, 0x7f, 0xbd, 0x9d, 0xfd, 0xd6, 0x5f, 0xff, 0x4b, 0x80, + 0xc2, 0x9c, 0x59, 0xef, 0x38, 0xd6, 0x5f, 0xf7, 0xd9, 0x1f, 0x4c, 0x50, + 0xc5, 0x97, 0xf7, 0xdc, 0xf6, 0x3e, 0x96, 0x5f, 0xff, 0xf8, 0xd9, 0xa3, + 0xcf, 0x88, 0x1d, 0x8f, 0x4e, 0xde, 0x7d, 0x9a, 0x16, 0x53, 0xa2, 0x7b, + 0xa5, 0xd5, 0xb2, 0x7c, 0x43, 0x1c, 0x15, 0x0f, 0xe3, 0x9f, 0x28, 0x76, + 0x1b, 0x17, 0xff, 0xd8, 0xfb, 0x7a, 0x0f, 0xbc, 0x8d, 0xb3, 0x8b, 0x2f, + 0xfb, 0x35, 0xb4, 0x1e, 0xb0, 0x96, 0x59, 0x8b, 0x27, 0x1b, 0x7b, 0xc5, + 0x83, 0x59, 0x50, 0x6f, 0xbe, 0x23, 0xbf, 0x14, 0x63, 0x5b, 0x2c, 0xbd, + 0x3e, 0x0e, 0xac, 0xa9, 0xec, 0xf2, 0x24, 0x51, 0x7f, 0x7d, 0xcc, 0xc2, + 0x15, 0x65, 0xf3, 0x04, 0x8f, 0xd6, 0x5f, 0xfe, 0xd8, 0x7a, 0x70, 0x96, + 0x6f, 0xd1, 0xf1, 0x65, 0x42, 0x31, 0x70, 0x9c, 0xcb, 0x80, 0x49, 0x77, + 0x78, 0xb2, 0xfe, 0xef, 0x9e, 0x61, 0x18, 0xb2, 0xe7, 0xf2, 0xcb, 0xb3, + 0x8b, 0x2e, 0x7d, 0x2c, 0xbd, 0xd8, 0xfe, 0x46, 0xb3, 0x42, 0xd4, 0xe8, + 0xa5, 0xfc, 0xc0, 0x90, 0xae, 0x81, 0xac, 0xbb, 0xef, 0xd6, 0x57, 0x8d, + 0x78, 0x05, 0xaf, 0xee, 0x47, 0xdf, 0xea, 0x16, 0x5c, 0x33, 0x59, 0x50, + 0xbc, 0x99, 0xb2, 0x26, 0x47, 0xf8, 0x16, 0x03, 0x86, 0x26, 0xa3, 0x2e, + 0xf1, 0xd3, 0xc2, 0xf0, 0x97, 0xb8, 0x43, 0xbc, 0xbe, 0xf8, 0xd9, 0x1e, + 0x59, 0x7e, 0x7f, 0x4e, 0x1c, 0x2c, 0xaf, 0x1e, 0x57, 0x48, 0x6f, 0xee, + 0x94, 0x4b, 0x3a, 0xb2, 0xfb, 0x09, 0xe4, 0xb2, 0xfb, 0xbc, 0x13, 0x93, + 0x8f, 0x32, 0x7b, 0x2c, 0xbf, 0xdd, 0x3d, 0xb3, 0xc0, 0xea, 0xcb, 0xee, + 0xf2, 0x05, 0x59, 0x69, 0x96, 0x5f, 0xd8, 0x0c, 0xf0, 0x76, 0x59, 0x6f, + 0xa0, 0xf0, 0x38, 0x25, 0x7b, 0xd0, 0x35, 0x95, 0x08, 0xa7, 0xc6, 0x39, + 0x8a, 0x2f, 0xec, 0xd7, 0xe6, 0x5d, 0x59, 0x77, 0xdf, 0x2c, 0xb3, 0x27, + 0x1e, 0x33, 0x0b, 0x6f, 0xe9, 0x74, 0xf6, 0x27, 0x59, 0x7f, 0x79, 0xe4, + 0xcd, 0x1a, 0xcb, 0xff, 0x85, 0x15, 0xfb, 0xce, 0x94, 0x7d, 0x25, 0x97, + 0xf7, 0x4a, 0x25, 0x9d, 0x59, 0x50, 0x7e, 0x72, 0x47, 0xb8, 0xf8, 0xb2, + 0xcc, 0x59, 0x7b, 0x59, 0xf2, 0xcb, 0xd8, 0x7b, 0x2c, 0xbe, 0x86, 0x30, + 0xd6, 0x5b, 0x36, 0x37, 0xbd, 0x1c, 0xa1, 0xa2, 0xa3, 0xc2, 0xdf, 0x88, + 0x92, 0xd5, 0xff, 0xe7, 0x17, 0xa2, 0xc1, 0x4b, 0x3c, 0x6c, 0x59, 0x7e, + 0x3e, 0x72, 0x18, 0xb2, 0x8d, 0x15, 0xc4, 0x7b, 0xbd, 0x2a, 0xa1, 0x72, + 0x7b, 0x1b, 0x85, 0x42, 0x38, 0x6d, 0xb1, 0xd5, 0xca, 0xbf, 0x2d, 0x28, + 0x50, 0x02, 0x37, 0x7b, 0xf0, 0xcc, 0x9c, 0x6b, 0x2f, 0x70, 0x7f, 0x2c, + 0xbb, 0xf0, 0xac, 0xbf, 0xfd, 0x93, 0x7a, 0x08, 0x52, 0xce, 0xf8, 0x0b, + 0x28, 0x53, 0xe2, 0xf0, 0xc5, 0xf0, 0x40, 0xf2, 0x59, 0x53, 0x91, 0x8b, + 0x28, 0x40, 0x7c, 0x47, 0x46, 0x99, 0x1e, 0xa1, 0xd7, 0x7f, 0x6b, 0xd9, + 0xa0, 0x62, 0xcb, 0xff, 0xfb, 0x3e, 0x97, 0x3b, 0xe8, 0x64, 0x18, 0xe7, + 0x44, 0xf4, 0xb2, 0xb1, 0x11, 0xe6, 0x5b, 0x7f, 0x9c, 0xa7, 0x07, 0x9f, + 0x8d, 0x65, 0xff, 0x81, 0xc9, 0xcc, 0x13, 0xcf, 0xe8, 0x59, 0x7e, 0xce, + 0x83, 0x3e, 0x59, 0x78, 0x5c, 0xf9, 0x65, 0xd1, 0x34, 0xe3, 0xc6, 0xe9, + 0x45, 0x12, 0x2e, 0x01, 0x08, 0x8b, 0xff, 0xff, 0xf0, 0x0a, 0x73, 0x4f, + 0xf4, 0xb4, 0xff, 0xce, 0xe0, 0x23, 0xef, 0x61, 0x96, 0x0a, 0xb2, 0xff, + 0xff, 0xff, 0x07, 0x02, 0x59, 0xdf, 0x03, 0xcf, 0xf4, 0xb9, 0xdd, 0x63, + 0x58, 0x63, 0xef, 0x0d, 0x65, 0xff, 0xfe, 0xe8, 0x30, 0xa7, 0x1e, 0x74, + 0xa3, 0xf9, 0xd8, 0x63, 0x59, 0x7f, 0xcc, 0xc6, 0x4e, 0x66, 0xee, 0xf9, + 0x2c, 0xad, 0x22, 0x8d, 0x8c, 0x57, 0xff, 0xda, 0xd6, 0x77, 0x9e, 0x3c, + 0x9d, 0xef, 0xe1, 0x65, 0x42, 0x74, 0xe7, 0x19, 0x90, 0x08, 0xef, 0xff, + 0xe8, 0xf6, 0x0c, 0x50, 0x47, 0xb3, 0xb0, 0xc7, 0xea, 0xca, 0xc5, 0x58, + 0x1e, 0x94, 0x62, 0x03, 0x5b, 0xff, 0xff, 0xe3, 0x9b, 0xd9, 0xcd, 0x60, + 0x67, 0x78, 0xf0, 0xb3, 0x79, 0x60, 0xc0, 0x4b, 0x2f, 0xd8, 0x41, 0x11, + 0x8b, 0x2f, 0xfe, 0xf6, 0x41, 0x4c, 0x59, 0xde, 0x3a, 0xcb, 0x48, 0x91, + 0xd1, 0xd7, 0xed, 0xc2, 0x8b, 0xff, 0xf0, 0xf1, 0x93, 0xb7, 0x3b, 0x1c, + 0x9a, 0x4f, 0xad, 0x96, 0x5e, 0x98, 0xf7, 0x56, 0x5f, 0xff, 0xdd, 0x3d, + 0xb0, 0x73, 0x98, 0xf2, 0xd1, 0xec, 0xec, 0x59, 0x7f, 0xb6, 0x76, 0x14, + 0x6a, 0x65, 0x97, 0xfd, 0xf6, 0x0b, 0xfb, 0x32, 0x76, 0xb1, 0x12, 0x46, + 0xbd, 0x7d, 0xfb, 0xbc, 0xcb, 0x2d, 0x0b, 0x2f, 0x61, 0x6d, 0x38, 0xd9, + 0x84, 0x8e, 0xff, 0xfb, 0x01, 0xa9, 0xd3, 0x78, 0x02, 0x9e, 0x10, 0xab, + 0x2f, 0xff, 0x9f, 0xb1, 0xa0, 0x30, 0xf9, 0x3b, 0x92, 0xe2, 0xcb, 0xfe, + 0xd6, 0xd1, 0xb6, 0xc0, 0xd6, 0xcb, 0x2f, 0xfd, 0x3a, 0x6f, 0x00, 0x53, + 0xc2, 0x15, 0x65, 0xfe, 0x29, 0xdc, 0x04, 0xf8, 0x34, 0xb2, 0xb0, 0xff, + 0x00, 0x87, 0x50, 0x8d, 0xbe, 0x42, 0xea, 0xfc, 0x3c, 0xf9, 0xc9, 0x65, + 0xff, 0xed, 0xa7, 0x73, 0xc0, 0x9d, 0xde, 0x72, 0x34, 0xb2, 0xff, 0xd2, + 0x8e, 0x80, 0x33, 0xb9, 0xd3, 0x59, 0x7f, 0xfd, 0xe8, 0xda, 0x71, 0x66, + 0xf7, 0xf7, 0xa3, 0xe5, 0x97, 0xff, 0xfd, 0xf7, 0x0f, 0x1a, 0x9d, 0xec, + 0xef, 0x33, 0x69, 0xd8, 0x63, 0x59, 0x43, 0x46, 0x06, 0x2a, 0x5f, 0xfd, + 0x9d, 0x3f, 0xa4, 0x51, 0x3b, 0x70, 0x2b, 0x2f, 0xf8, 0xb3, 0x58, 0x1c, + 0x31, 0xac, 0xbf, 0xb6, 0xec, 0xc5, 0x0c, 0x59, 0x7f, 0xd1, 0x34, 0xe7, + 0x89, 0xa2, 0x65, 0x97, 0xff, 0xf6, 0x6d, 0x86, 0x32, 0x07, 0xa7, 0x40, + 0xde, 0x6e, 0x2c, 0xbf, 0xec, 0x6b, 0x3c, 0x7a, 0xf3, 0xac, 0xa8, 0x56, + 0x35, 0x84, 0xff, 0x28, 0x9c, 0x60, 0x33, 0x11, 0x79, 0x25, 0xcd, 0x88, + 0xbf, 0x87, 0x9d, 0x5d, 0xad, 0x99, 0x8d, 0x43, 0x21, 0x16, 0x1d, 0xc1, + 0x95, 0xcf, 0xf4, 0x64, 0x06, 0x71, 0x32, 0xee, 0xa1, 0x64, 0xc8, 0x43, + 0xf8, 0xd0, 0x95, 0x39, 0x19, 0x6f, 0x65, 0xa0, 0xdf, 0xee, 0x96, 0x77, + 0xd9, 0xb2, 0xcb, 0xff, 0x3e, 0xbd, 0x9d, 0x82, 0x3f, 0xd6, 0x5f, 0xff, + 0x16, 0x6d, 0x3b, 0xf0, 0x77, 0x31, 0x9f, 0xb4, 0xb2, 0xfa, 0x5d, 0x3d, + 0x96, 0x5f, 0xe0, 0x9f, 0x30, 0x73, 0x86, 0xb2, 0xfe, 0xfa, 0x5c, 0x9d, + 0xd3, 0x59, 0x6c, 0xf1, 0xf3, 0x34, 0x6b, 0x7f, 0xe9, 0x46, 0x7f, 0xec, + 0xc2, 0x0a, 0xcb, 0xff, 0xde, 0x32, 0x1c, 0xe1, 0x02, 0x59, 0xec, 0x59, + 0x7f, 0xcf, 0xde, 0x4d, 0x20, 0x17, 0x56, 0x56, 0x23, 0x0a, 0x63, 0xdf, + 0xd2, 0xef, 0xfb, 0xc6, 0xc8, 0xc2, 0xef, 0x16, 0x5f, 0xff, 0xe0, 0x77, + 0x40, 0x9a, 0x77, 0x66, 0xf0, 0x3f, 0xe9, 0xe6, 0xe2, 0xcb, 0xe8, 0x0c, + 0x32, 0x72, 0x34, 0xc2, 0x62, 0x46, 0xf5, 0x0a, 0xd4, 0x82, 0x67, 0xa3, + 0xd7, 0x55, 0x28, 0x44, 0xee, 0x47, 0x7b, 0x7f, 0xff, 0x6b, 0x1d, 0x93, + 0x85, 0x7f, 0xbd, 0x9e, 0xf4, 0x75, 0x65, 0xf6, 0x6a, 0x69, 0x2c, 0xbf, + 0xf8, 0xfe, 0x97, 0x27, 0x0c, 0xf7, 0xc0, 0xd6, 0x5e, 0xfa, 0x37, 0x56, + 0x50, 0xa7, 0xd2, 0xe9, 0x37, 0xd9, 0xe7, 0xde, 0xb2, 0xfe, 0x96, 0x7b, + 0x37, 0xf1, 0x65, 0xb0, 0x67, 0xa5, 0x31, 0x1d, 0xff, 0xf8, 0x7e, 0x07, + 0x27, 0x78, 0xfb, 0xec, 0xd4, 0x6f, 0x59, 0x7f, 0x67, 0xc5, 0x99, 0xfa, + 0xca, 0xc4, 0x43, 0x92, 0xd5, 0xd0, 0xcc, 0x4f, 0x40, 0x58, 0x46, 0x69, + 0xd0, 0xa1, 0x5f, 0x7f, 0xf6, 0xa1, 0xad, 0xc2, 0x86, 0xbe, 0x97, 0x16, + 0x5a, 0x16, 0x5f, 0x1e, 0x9f, 0xe5, 0x96, 0xe9, 0x9b, 0x2e, 0x08, 0x50, + 0xd1, 0x44, 0x6f, 0x97, 0xfc, 0xe4, 0x1f, 0x1b, 0x0a, 0x16, 0x5f, 0xfc, + 0x32, 0x79, 0xa6, 0x8e, 0xff, 0x9c, 0x59, 0x7a, 0x18, 0x22, 0xcb, 0xff, + 0x8f, 0x79, 0x8e, 0x74, 0x4c, 0x37, 0x69, 0x65, 0xff, 0xff, 0xe2, 0x31, + 0x7c, 0xf3, 0x4e, 0x20, 0x6e, 0x4e, 0xcf, 0xa5, 0x80, 0x2f, 0xf8, 0xb2, + 0xfd, 0xe3, 0x82, 0xd9, 0x65, 0x31, 0x15, 0x1b, 0xe1, 0x03, 0x7c, 0xc3, + 0x36, 0x96, 0x54, 0x1e, 0x63, 0x95, 0x51, 0x26, 0xf5, 0xc1, 0xde, 0xc6, + 0x7b, 0x7f, 0xb5, 0x1b, 0x78, 0xf0, 0x6b, 0x2f, 0xa2, 0x6e, 0x1a, 0xca, + 0x92, 0xaa, 0x31, 0x91, 0x61, 0xb1, 0xc7, 0x81, 0xa3, 0x62, 0x33, 0xbf, + 0xff, 0xec, 0x9a, 0x51, 0xa1, 0x58, 0x47, 0xf4, 0xb8, 0x3d, 0x1e, 0xcb, + 0x2f, 0x44, 0xdc, 0x59, 0x7a, 0x60, 0x0d, 0x65, 0x43, 0x70, 0xfd, 0x94, + 0xd2, 0x13, 0x9c, 0x33, 0xd3, 0x27, 0xa5, 0x17, 0x3c, 0xb9, 0x72, 0x5c, + 0xeb, 0x50, 0x07, 0x6f, 0xe8, 0x91, 0x8f, 0x09, 0x65, 0xf1, 0x74, 0xf7, + 0xac, 0xbe, 0x90, 0x1f, 0xab, 0x2f, 0x06, 0x37, 0x56, 0x54, 0x1f, 0x26, + 0x11, 0x99, 0x15, 0xf7, 0x78, 0xff, 0xac, 0xbf, 0xfb, 0xc7, 0x38, 0xf4, + 0x0d, 0x9f, 0x41, 0x59, 0x7c, 0x7e, 0x7d, 0x96, 0x5f, 0x6f, 0x8e, 0xc2, + 0xcb, 0xf7, 0x33, 0xa7, 0xc5, 0x97, 0xff, 0x1f, 0x7c, 0x79, 0x83, 0x2c, + 0x69, 0x65, 0xff, 0xb9, 0x13, 0x67, 0x8f, 0x5e, 0x75, 0x97, 0x67, 0x96, + 0x51, 0x9e, 0xa3, 0x0f, 0xaf, 0x68, 0xc5, 0x59, 0x7f, 0xfc, 0x0c, 0x3f, + 0x60, 0x7c, 0x73, 0x9a, 0x60, 0x56, 0x5e, 0xee, 0x34, 0xb2, 0xf6, 0x03, + 0x62, 0x3e, 0xfe, 0xa9, 0x5f, 0x80, 0xdb, 0x27, 0xea, 0xcb, 0xf6, 0xce, + 0x5d, 0xe2, 0xcb, 0xfb, 0xcf, 0xb4, 0xe9, 0x75, 0x65, 0xfc, 0xf3, 0x09, + 0xc8, 0xc5, 0x97, 0xe7, 0xd1, 0x7c, 0x6b, 0x2a, 0x11, 0xb3, 0x85, 0x66, + 0x51, 0xe3, 0x1e, 0x97, 0x5e, 0xf3, 0x92, 0xca, 0xd9, 0x71, 0x08, 0x70, + 0x8f, 0xc2, 0xc1, 0x48, 0xc2, 0x8e, 0x64, 0x53, 0x11, 0xb0, 0x9f, 0xd0, + 0x96, 0x72, 0x12, 0x84, 0x67, 0x63, 0x0c, 0x69, 0x1e, 0xf8, 0xbb, 0x0c, + 0x59, 0x7e, 0xcf, 0x87, 0xf0, 0xab, 0x2f, 0xfe, 0xef, 0xa0, 0xa0, 0x3a, + 0x8c, 0x25, 0x97, 0xf6, 0x6d, 0x02, 0xbf, 0x16, 0x5d, 0xf7, 0x27, 0x1f, + 0x78, 0xd0, 0x6f, 0xf7, 0xa0, 0xc9, 0xf4, 0x2a, 0xcb, 0xfa, 0x0c, 0x9f, + 0x42, 0xac, 0xbf, 0x9f, 0xfd, 0x68, 0xf5, 0x38, 0xf7, 0xbc, 0x65, 0x78, + 0xb1, 0x8b, 0x2d, 0xf2, 0xcb, 0x9e, 0x52, 0x35, 0xbc, 0x1b, 0xbf, 0xff, + 0xfd, 0x39, 0x91, 0x9d, 0x9d, 0x92, 0x3d, 0xe5, 0x9c, 0x9d, 0x03, 0x79, + 0xb8, 0xb2, 0xfe, 0x20, 0xce, 0x6b, 0x9f, 0xac, 0xa9, 0x22, 0xc1, 0xdf, + 0xa8, 0xd5, 0x5e, 0x68, 0x85, 0xe1, 0x35, 0xfc, 0x23, 0xc9, 0xd0, 0x10, + 0xdc, 0xbd, 0xce, 0x9a, 0xcb, 0xd3, 0x7f, 0xf2, 0xcb, 0xd0, 0x2c, 0xe0, + 0x9b, 0xcf, 0xc7, 0x2f, 0xbf, 0xfc, 0x1b, 0xab, 0x2d, 0xfa, 0xcb, 0xfa, + 0x35, 0xb4, 0x6b, 0x65, 0x97, 0xa5, 0xcc, 0x59, 0x7e, 0x6c, 0x94, 0x67, + 0xeb, 0x2b, 0x47, 0x8f, 0xf8, 0xe5, 0xff, 0x8f, 0x1a, 0xe0, 0x26, 0x28, + 0x62, 0xcb, 0xc0, 0x7e, 0x2c, 0xa9, 0x23, 0xd3, 0x1d, 0x98, 0x46, 0x21, + 0xfd, 0xfe, 0x69, 0x90, 0x7a, 0x06, 0xea, 0xcb, 0x88, 0x0b, 0x29, 0x65, + 0xd1, 0xa9, 0xc6, 0x87, 0x82, 0xd5, 0x88, 0x86, 0x02, 0xc5, 0xff, 0xfc, + 0x7a, 0x9d, 0x87, 0x84, 0x2f, 0x8f, 0x19, 0x81, 0x59, 0x7f, 0xd9, 0xf4, + 0xb9, 0xf4, 0x81, 0xa5, 0x97, 0xff, 0xfe, 0x67, 0x03, 0xe8, 0x0f, 0xd2, + 0x20, 0x0e, 0x71, 0x8a, 0xe5, 0xfa, 0xcb, 0xff, 0xfd, 0xb6, 0xa1, 0x9c, + 0x04, 0x4e, 0x8d, 0x40, 0xfd, 0x0c, 0x59, 0x58, 0x8d, 0x43, 0x71, 0xac, + 0x4e, 0x2c, 0xd6, 0xb5, 0x18, 0x85, 0xfb, 0xfe, 0x1f, 0xcd, 0x2c, 0xbf, + 0x7d, 0x0c, 0xda, 0x16, 0x5f, 0x7d, 0x23, 0xf2, 0xcb, 0xda, 0xcf, 0xbc, + 0x79, 0x9d, 0x29, 0xbf, 0xbe, 0x79, 0x14, 0x31, 0x65, 0xff, 0x80, 0x53, + 0x74, 0xb3, 0x67, 0x25, 0x97, 0xff, 0xff, 0xb5, 0xa3, 0xda, 0x77, 0xb3, + 0xa5, 0x13, 0x7b, 0x36, 0x8f, 0xba, 0x00, 0xac, 0xbf, 0xff, 0xfa, 0x05, + 0xf4, 0x4e, 0x2c, 0x00, 0xb3, 0xbe, 0x97, 0x0c, 0x84, 0x8d, 0x96, 0x5f, + 0xbe, 0xe1, 0xe1, 0x2c, 0xac, 0x45, 0x00, 0x1e, 0x6a, 0x13, 0xaa, 0xc2, + 0xd3, 0x3e, 0xf4, 0x63, 0xb7, 0xfe, 0xeb, 0x18, 0xf3, 0x4e, 0x2c, 0x69, + 0x65, 0xff, 0xda, 0x2f, 0xb3, 0x80, 0x98, 0xa1, 0x8b, 0x2f, 0xf8, 0xc5, + 0xf4, 0x1e, 0xd0, 0x2a, 0xca, 0x33, 0xff, 0xe2, 0x2d, 0x4e, 0x47, 0x14, + 0xa1, 0x83, 0x7f, 0xc4, 0x09, 0x8b, 0x3c, 0xfd, 0x59, 0x7f, 0xf4, 0x81, + 0xff, 0x0b, 0x39, 0xc8, 0x69, 0x65, 0xfe, 0xf4, 0x34, 0x3f, 0x3b, 0x16, + 0x5f, 0xf3, 0x33, 0x93, 0xa6, 0x28, 0x62, 0xcb, 0xff, 0xf7, 0x36, 0x79, + 0xc3, 0xf4, 0x6b, 0x59, 0xbe, 0x3e, 0x59, 0x43, 0x46, 0x53, 0x0d, 0x3a, + 0x77, 0x7f, 0xfe, 0x8c, 0xda, 0x70, 0xf4, 0x7b, 0x74, 0xf0, 0xb6, 0x59, + 0x7d, 0xbf, 0x00, 0x15, 0x97, 0xff, 0xfc, 0xfa, 0xf6, 0x74, 0xf0, 0xb6, + 0xe4, 0x61, 0x0f, 0xd0, 0xb2, 0xfb, 0xb3, 0xb6, 0xd2, 0xca, 0xc4, 0x56, + 0xfc, 0x48, 0xec, 0x77, 0xff, 0xfe, 0xfb, 0x36, 0x06, 0xe4, 0xef, 0xa5, + 0xd8, 0x6a, 0x76, 0x6b, 0xff, 0xe1, 0x65, 0xff, 0x64, 0xd3, 0xe0, 0xee, + 0x7d, 0xd5, 0x97, 0xb3, 0x43, 0x59, 0x74, 0x49, 0x65, 0x61, 0xf8, 0x30, + 0xfb, 0xf1, 0xcb, 0xff, 0xb6, 0x72, 0xcf, 0x67, 0x75, 0x8d, 0x2c, 0xbf, + 0xff, 0xff, 0x60, 0x05, 0x20, 0x76, 0x6c, 0xe9, 0xfd, 0x22, 0x82, 0xcf, + 0x1e, 0x6a, 0x65, 0x95, 0x88, 0xc7, 0xf9, 0x12, 0xff, 0xb1, 0xaf, 0x40, + 0xb9, 0xde, 0x2c, 0xbf, 0xf9, 0xcb, 0x66, 0x00, 0xc6, 0x64, 0x6b, 0x28, + 0xcf, 0xf1, 0xa3, 0xab, 0xfa, 0x6f, 0xa4, 0x08, 0xea, 0xcb, 0xfe, 0x07, + 0xde, 0xce, 0xeb, 0x1a, 0x59, 0x7f, 0xff, 0x4a, 0x35, 0xf4, 0xb9, 0x3b, + 0xd0, 0x64, 0xfa, 0x15, 0x65, 0xfa, 0x5d, 0x2c, 0x92, 0xcb, 0xff, 0x7c, + 0x3f, 0x01, 0xf8, 0x58, 0x35, 0x95, 0x87, 0xcc, 0x02, 0x7b, 0xfe, 0xf3, + 0xb0, 0xb3, 0xcf, 0xd5, 0x97, 0x7e, 0xd2, 0xcb, 0xe9, 0xfc, 0xd4, 0x2c, + 0xb6, 0x68, 0xde, 0xef, 0x19, 0xa8, 0x4f, 0xb3, 0x0c, 0x3e, 0x3b, 0xf4, + 0x31, 0x38, 0x43, 0xd7, 0x4b, 0xff, 0xb3, 0xbc, 0x9d, 0xac, 0x3f, 0x40, + 0xd6, 0x5f, 0xf7, 0xbd, 0x1f, 0x4e, 0x69, 0x81, 0x59, 0x7f, 0x67, 0x75, + 0xac, 0x92, 0xca, 0x59, 0x7e, 0xcf, 0x16, 0x05, 0x65, 0x7e, 0x6c, 0x48, + 0x2e, 0xa6, 0x3f, 0xfe, 0xaf, 0x5e, 0xcf, 0xba, 0xb2, 0xff, 0x60, 0xcf, + 0x7f, 0x4f, 0x4b, 0x2e, 0xce, 0x4e, 0x3f, 0x4d, 0xd2, 0x37, 0x1d, 0xa7, + 0x4d, 0xbc, 0xa3, 0x31, 0xbf, 0xec, 0x67, 0xa0, 0xf4, 0x0d, 0xd5, 0x97, + 0xf7, 0x4f, 0x62, 0x76, 0x2c, 0xad, 0x99, 0xeb, 0xa3, 0x8e, 0xb7, 0x0d, + 0x45, 0x77, 0x0c, 0x7b, 0xdf, 0x46, 0x46, 0x65, 0x53, 0x1c, 0x6a, 0x31, + 0x06, 0x18, 0x7a, 0x30, 0x57, 0x2f, 0xfe, 0x1d, 0x45, 0x0f, 0x6e, 0x42, + 0x7f, 0xb2, 0x83, 0x80, 0xb9, 0xbe, 0x3b, 0x41, 0x0a, 0x37, 0x0e, 0xef, + 0xfa, 0x18, 0x58, 0x3d, 0x1e, 0xcb, 0x2f, 0xff, 0xf3, 0x5e, 0x04, 0x7d, + 0x3b, 0x5a, 0x8d, 0x89, 0xdf, 0x42, 0xac, 0xbf, 0xc5, 0x80, 0xc1, 0x65, + 0xfa, 0xca, 0x74, 0x4d, 0x34, 0xcd, 0x52, 0x47, 0xae, 0x43, 0x4a, 0xf0, + 0xb1, 0xa5, 0x97, 0xff, 0xd1, 0x3b, 0xcf, 0xbe, 0x76, 0x31, 0x91, 0xa1, + 0x56, 0x5f, 0xbf, 0x66, 0x67, 0x56, 0x5f, 0xf6, 0x4e, 0xd6, 0x78, 0xb2, + 0x65, 0x95, 0x87, 0xc4, 0x45, 0x17, 0xff, 0xfb, 0xcf, 0xff, 0x27, 0x16, + 0x6f, 0x2c, 0xe6, 0x32, 0x02, 0xb2, 0xa6, 0x4d, 0x63, 0xc3, 0xa5, 0x0b, + 0x8e, 0x10, 0x5f, 0xc0, 0x60, 0xb0, 0x41, 0x59, 0x4d, 0x86, 0xd6, 0x15, + 0xb4, 0x28, 0x88, 0xc1, 0xb2, 0xb0, 0x20, 0xfa, 0x52, 0x3b, 0xc6, 0xfe, + 0x48, 0x77, 0xef, 0x08, 0x5f, 0x7e, 0xb2, 0xff, 0xed, 0x9f, 0xba, 0xcd, + 0x9c, 0xbb, 0xc5, 0x95, 0x87, 0xe0, 0x65, 0x77, 0xff, 0xfd, 0xac, 0x97, + 0xb0, 0xe5, 0xcc, 0x20, 0x74, 0xb3, 0xf8, 0x59, 0x7f, 0x46, 0x10, 0x44, + 0x62, 0xcb, 0xfd, 0xd0, 0x46, 0x6c, 0x23, 0x16, 0x5f, 0xfb, 0xd0, 0x40, + 0x9b, 0x39, 0xc8, 0x59, 0x6d, 0xde, 0xa2, 0x80, 0x42, 0xdd, 0xc3, 0x5a, + 0x84, 0xd0, 0x5e, 0x30, 0x4b, 0xfb, 0xd8, 0x5d, 0xfc, 0x0b, 0x2f, 0xf8, + 0x63, 0x8f, 0xe7, 0x67, 0xdd, 0x59, 0x7f, 0x3e, 0xe8, 0xcb, 0x3c, 0xb2, + 0xe6, 0x71, 0x65, 0xe7, 0xe1, 0xac, 0xa8, 0x36, 0x5f, 0x8b, 0xdf, 0xf0, + 0x9b, 0x61, 0x3b, 0x1f, 0xcb, 0x28, 0xd1, 0xf1, 0x31, 0xe9, 0x30, 0xf4, + 0x86, 0xff, 0x73, 0x90, 0x7b, 0x3f, 0x56, 0x5f, 0xd9, 0xdc, 0x72, 0x99, + 0x65, 0xf6, 0x72, 0x34, 0xb2, 0xc2, 0xf8, 0xf3, 0x48, 0xb2, 0xd2, 0x84, + 0x54, 0x02, 0x10, 0x16, 0x92, 0xcb, 0xe8, 0x63, 0x8d, 0x65, 0x41, 0xb2, + 0x21, 0x1b, 0xfd, 0x93, 0x7b, 0x3b, 0x03, 0x59, 0x7e, 0x38, 0x67, 0x18, + 0xb2, 0xfe, 0x2e, 0xbb, 0x0f, 0xab, 0x2f, 0x0c, 0xc6, 0xb2, 0xf7, 0xef, + 0xba, 0xb2, 0x86, 0x6f, 0x38, 0x39, 0x77, 0x1b, 0x5a, 0xcb, 0x49, 0x65, + 0x42, 0x2d, 0xf1, 0xa1, 0xc8, 0x7a, 0x3b, 0x78, 0x83, 0x0b, 0x2f, 0xff, + 0xff, 0xbd, 0x9d, 0xe3, 0xcd, 0x38, 0xb3, 0xe9, 0x60, 0x36, 0x2c, 0x1f, + 0x8e, 0x65, 0x97, 0xff, 0xc7, 0xc8, 0x97, 0x73, 0xc5, 0x9d, 0xf1, 0xac, + 0xb8, 0x02, 0xac, 0xa3, 0x4c, 0x07, 0x43, 0x7e, 0x84, 0x0f, 0x53, 0x6f, + 0x78, 0x5e, 0xac, 0xbf, 0x77, 0xbd, 0xc6, 0x96, 0x5b, 0xf8, 0x3c, 0x73, + 0x1e, 0xbc, 0xc8, 0x0a, 0xcb, 0xb5, 0x8b, 0x2f, 0x61, 0x8d, 0x65, 0xb8, + 0xb2, 0xe2, 0xc0, 0x9a, 0xcf, 0x86, 0xef, 0xfe, 0xcf, 0xe7, 0x78, 0xfb, + 0xc2, 0x39, 0x96, 0x56, 0xc7, 0xe7, 0xd2, 0xca, 0x9e, 0x51, 0xe9, 0x03, + 0x99, 0x0b, 0x5b, 0xff, 0x8b, 0xf9, 0xfc, 0xf1, 0xc3, 0x1c, 0x6b, 0x2f, + 0xf8, 0xbf, 0xd6, 0x48, 0xa1, 0x8b, 0x2f, 0xff, 0xff, 0xd2, 0xe7, 0x01, + 0x1d, 0x31, 0xe1, 0x4d, 0x38, 0xb3, 0x50, 0x5d, 0xf6, 0x6e, 0x2c, 0xbf, + 0xfb, 0xe9, 0x73, 0x19, 0xf4, 0x7b, 0xd0, 0xb2, 0xb1, 0x30, 0x4f, 0x8e, + 0x35, 0x08, 0x6b, 0x71, 0x65, 0x62, 0x72, 0x20, 0x8d, 0x28, 0x43, 0x6b, + 0x6c, 0xb2, 0xfd, 0xb4, 0x77, 0x3f, 0x59, 0x7e, 0xd6, 0x7d, 0x2e, 0x2c, + 0xbc, 0x1f, 0x62, 0xcb, 0x66, 0xc7, 0x8b, 0xa2, 0x9b, 0xfe, 0xfa, 0x35, + 0xb4, 0xec, 0x31, 0xac, 0xba, 0x63, 0x59, 0x58, 0x8f, 0xb3, 0x12, 0x26, + 0xe0, 0x14, 0x6f, 0x3c, 0xbf, 0xb3, 0xe9, 0x47, 0xf0, 0xb2, 0xfc, 0x59, + 0xe7, 0xea, 0xca, 0xf8, 0xf5, 0x18, 0x5d, 0x7c, 0x7b, 0xf3, 0x4b, 0x2a, + 0x0f, 0x1b, 0xc4, 0x77, 0xd3, 0x83, 0xb3, 0x4b, 0x2f, 0xdb, 0x4e, 0x04, + 0x4c, 0xb2, 0xff, 0xff, 0x8a, 0x3f, 0xfa, 0x5c, 0x04, 0x74, 0xb0, 0x67, + 0x9a, 0x99, 0x65, 0x3a, 0x24, 0xfa, 0x59, 0x50, 0xcc, 0xdc, 0xda, 0x36, + 0x11, 0x94, 0x64, 0x64, 0xdf, 0x43, 0x90, 0xd7, 0xe6, 0x1f, 0xd1, 0x9f, + 0xa1, 0xbc, 0xf1, 0x9d, 0xff, 0x08, 0x92, 0x8c, 0x6f, 0x91, 0xe4, 0xf6, + 0x33, 0x20, 0x43, 0x8f, 0x79, 0x08, 0x90, 0xba, 0xbf, 0x48, 0xf5, 0x9f, + 0x2c, 0xbd, 0xcf, 0x62, 0xcb, 0xf6, 0x73, 0x6c, 0x69, 0x65, 0xff, 0xde, + 0x00, 0xfc, 0x0e, 0xf3, 0xc0, 0x99, 0x65, 0x7c, 0x8b, 0x59, 0x8a, 0x3c, + 0x38, 0x45, 0x37, 0xff, 0xfb, 0xbc, 0xf6, 0x7d, 0x8f, 0xf4, 0xe1, 0xc6, + 0xd3, 0x85, 0x15, 0x65, 0xff, 0xf9, 0x9f, 0x4b, 0x9a, 0xfa, 0x3b, 0xc8, + 0xf1, 0xf5, 0x65, 0xff, 0x60, 0xa5, 0x9d, 0xf1, 0xb4, 0xb2, 0xff, 0xf4, + 0x7d, 0xe3, 0xc2, 0x19, 0xef, 0x81, 0xac, 0xa8, 0x4d, 0x14, 0xda, 0xb4, + 0xb0, 0x47, 0x37, 0x6e, 0x8d, 0x65, 0xff, 0xf6, 0xd8, 0x11, 0xf8, 0xf0, + 0x59, 0xc2, 0x82, 0x16, 0x5f, 0xfd, 0x23, 0xef, 0xb3, 0x5a, 0xce, 0xf1, + 0x65, 0xfe, 0x13, 0xa7, 0xbe, 0x08, 0x2b, 0x2f, 0xd0, 0xc3, 0xf4, 0x2c, + 0xbf, 0xd9, 0xdf, 0x1f, 0x70, 0x2b, 0x2a, 0x72, 0x23, 0x74, 0x6a, 0x44, + 0xd5, 0x3d, 0xa6, 0xe0, 0x63, 0x53, 0x2a, 0x7a, 0x1a, 0x37, 0xfe, 0x97, + 0x3a, 0xfb, 0x6e, 0x16, 0x7c, 0xb2, 0xfe, 0xf4, 0x48, 0x0e, 0x22, 0xcb, + 0xf0, 0xfd, 0x85, 0xd5, 0x95, 0x23, 0xd5, 0x98, 0xba, 0xbe, 0x45, 0xd9, + 0x42, 0x62, 0xfc, 0x7f, 0x0f, 0xe1, 0x56, 0x54, 0x1e, 0x9e, 0x8a, 0x2e, + 0x9b, 0xf5, 0x97, 0xfd, 0xed, 0x8f, 0x0b, 0xe9, 0x71, 0x65, 0xff, 0x7d, + 0xec, 0x09, 0x60, 0x37, 0x56, 0x5f, 0xff, 0x35, 0x81, 0xe6, 0x76, 0x6c, + 0x14, 0xb3, 0xf5, 0x96, 0x94, 0x23, 0x23, 0x0e, 0x8c, 0xf2, 0xfe, 0x3f, + 0xbf, 0xd3, 0x8d, 0x65, 0xec, 0xd7, 0xeb, 0x2f, 0xdc, 0x3c, 0x2f, 0xd6, + 0x5b, 0x0c, 0xf1, 0x38, 0x3b, 0x7e, 0x3f, 0xff, 0xcd, 0x2c, 0xaf, 0x23, + 0x08, 0x9c, 0xf8, 0x4d, 0x69, 0xe9, 0x65, 0xf9, 0xf0, 0x8c, 0x55, 0x97, + 0xbb, 0x9e, 0x59, 0x44, 0x78, 0x5b, 0x84, 0xd7, 0x1b, 0x16, 0x5f, 0xff, + 0xfb, 0xe9, 0x3f, 0xd3, 0x88, 0x1b, 0x93, 0xb3, 0xe9, 0x60, 0x0b, 0xfe, + 0x2c, 0xa6, 0x22, 0x37, 0x70, 0x5a, 0xf4, 0xf9, 0xb6, 0xd6, 0x54, 0xe4, + 0xe0, 0x67, 0xb2, 0xfd, 0x96, 0x32, 0x16, 0x33, 0xe4, 0xd7, 0xff, 0xff, + 0xdf, 0x70, 0x11, 0xf4, 0xef, 0x60, 0xe7, 0x16, 0x00, 0x59, 0xdc, 0xff, + 0xf3, 0xea, 0xcb, 0xff, 0xff, 0x60, 0xd8, 0xc8, 0xef, 0x33, 0xbc, 0xd1, + 0xe0, 0x4b, 0x06, 0xb2, 0xff, 0xf7, 0x41, 0x19, 0xb4, 0xee, 0x19, 0x1e, + 0x96, 0x59, 0xa2, 0x45, 0xa8, 0x8d, 0x55, 0x09, 0xb4, 0x64, 0x67, 0xd7, + 0xe8, 0x2f, 0x9f, 0x65, 0x97, 0xff, 0xff, 0xfe, 0xc0, 0xc7, 0xa1, 0x9d, + 0xce, 0x72, 0x35, 0xa8, 0x2c, 0xdb, 0x01, 0xc0, 0x4c, 0x50, 0xc5, 0x97, + 0x81, 0xa1, 0x56, 0x53, 0x11, 0x6d, 0x3f, 0x09, 0x8b, 0xf3, 0xfe, 0x23, + 0xec, 0xb2, 0xb0, 0xf5, 0x4c, 0xae, 0xf1, 0xff, 0x0b, 0x2f, 0xf4, 0xe2, + 0xcd, 0x1c, 0x31, 0x65, 0xff, 0xb4, 0xfd, 0xd6, 0x1f, 0xa0, 0x6b, 0x2f, + 0xe1, 0x96, 0x79, 0xfa, 0xb2, 0xfd, 0xdf, 0xa4, 0x7a, 0x59, 0x58, 0x7a, + 0xbc, 0x2c, 0xad, 0x93, 0x02, 0x18, 0xe6, 0x8c, 0xff, 0x84, 0x95, 0xed, + 0x38, 0x8b, 0x2f, 0xd1, 0xd6, 0x1f, 0x56, 0x5b, 0x67, 0x3c, 0x40, 0x0e, + 0xdf, 0xfa, 0x05, 0xdc, 0x06, 0x14, 0xc2, 0x31, 0x65, 0xff, 0xff, 0xfe, + 0x14, 0xb2, 0x71, 0xe1, 0x3f, 0x7d, 0x13, 0x4e, 0x2c, 0xf1, 0xf7, 0x80, + 0x08, 0x21, 0x65, 0x62, 0x3f, 0x3e, 0x28, 0x02, 0x25, 0xff, 0xb3, 0x3f, + 0xe4, 0xed, 0x70, 0x7c, 0x59, 0x7f, 0xb5, 0x81, 0xf1, 0xea, 0x65, 0x96, + 0xe8, 0xcf, 0xdb, 0x10, 0xaf, 0xf6, 0x04, 0xfb, 0xa0, 0x31, 0x65, 0xf7, + 0xc5, 0x93, 0xeb, 0x2b, 0x0f, 0xfc, 0xc9, 0xdc, 0xce, 0xfd, 0x8c, 0x91, + 0x1a, 0xcb, 0xc6, 0x5d, 0x59, 0x7f, 0x4b, 0x8e, 0xc0, 0x71, 0x65, 0x6c, + 0x79, 0x2e, 0x37, 0x7e, 0xcf, 0x79, 0xe4, 0xb2, 0xfe, 0xe9, 0xeb, 0x6c, + 0x0a, 0xca, 0x86, 0x7f, 0xb8, 0xe3, 0x83, 0xc8, 0xea, 0xc5, 0x8d, 0x58, + 0x24, 0x3f, 0x43, 0x84, 0xe5, 0x3b, 0x6a, 0x3c, 0x26, 0x14, 0x7a, 0x33, + 0x37, 0x8c, 0x88, 0xa3, 0x85, 0xe4, 0x64, 0xbd, 0x2c, 0x03, 0x7c, 0xf9, + 0x16, 0xe1, 0x3d, 0xe7, 0x0e, 0x2c, 0xa5, 0x97, 0xdb, 0x9e, 0x3d, 0x2c, + 0xbf, 0xe9, 0xdd, 0x2c, 0x6b, 0x0f, 0x8b, 0x29, 0xb0, 0x7d, 0x41, 0x0b, + 0x98, 0x96, 0xf7, 0xa3, 0xf5, 0x97, 0xf1, 0x9c, 0x35, 0x13, 0x2c, 0xa9, + 0xed, 0x30, 0x38, 0x84, 0x2e, 0x1a, 0x4c, 0x3b, 0x4b, 0x2f, 0xec, 0x64, + 0x79, 0xfc, 0xb2, 0xe9, 0xd3, 0x2c, 0xbd, 0xe3, 0x15, 0x65, 0x4e, 0x3e, + 0x31, 0x96, 0x0a, 0x35, 0x7f, 0xfd, 0xf4, 0x8a, 0x27, 0x30, 0xa0, 0x5e, + 0x9e, 0xcb, 0x2a, 0x11, 0xe7, 0x8d, 0xc6, 0x65, 0x7e, 0xd4, 0xe2, 0xde, + 0xd2, 0xcb, 0xef, 0x3f, 0xf0, 0xb2, 0xf7, 0xb3, 0x4b, 0x2a, 0x0f, 0xad, + 0xcb, 0x77, 0x08, 0xaf, 0xed, 0xa6, 0x94, 0xf7, 0xad, 0x96, 0x5f, 0x98, + 0x63, 0xc1, 0xac, 0xb4, 0xcb, 0x2e, 0x86, 0x2c, 0xb7, 0x56, 0x56, 0x8d, + 0x3b, 0x8b, 0x50, 0xcf, 0x67, 0xc7, 0x37, 0xd0, 0x37, 0x92, 0xcb, 0xdd, + 0xce, 0x2c, 0xbf, 0xf6, 0x3f, 0xdf, 0xcd, 0x28, 0xd6, 0xcb, 0x2f, 0x6a, + 0x26, 0x59, 0x7b, 0x8f, 0xf2, 0xca, 0xd9, 0x17, 0x22, 0x90, 0xf8, 0x71, + 0xd0, 0xba, 0x3b, 0x74, 0xce, 0xb2, 0xe9, 0x6c, 0xb2, 0xfa, 0x77, 0x62, + 0x4b, 0x2f, 0xff, 0xb2, 0x0b, 0xf8, 0x3e, 0xfb, 0x19, 0x83, 0x59, 0x74, + 0x0a, 0xb2, 0xb6, 0x44, 0x44, 0x89, 0x3a, 0x9b, 0x7f, 0x3b, 0x51, 0xdc, + 0xd9, 0x65, 0xfc, 0x7a, 0xfa, 0x45, 0x0b, 0x2f, 0xd1, 0xd9, 0x1f, 0x56, + 0x5f, 0xde, 0x8f, 0xa5, 0x9d, 0x59, 0x52, 0x3d, 0x51, 0x93, 0xd7, 0x91, + 0x49, 0xd8, 0x41, 0x5f, 0xfa, 0x7e, 0x0b, 0xba, 0x3d, 0xf8, 0x35, 0x97, + 0x67, 0x56, 0x5a, 0x65, 0x95, 0xe3, 0x50, 0x42, 0xd4, 0xd8, 0x5c, 0x49, + 0x83, 0x7c, 0x84, 0x01, 0xc3, 0x6d, 0x89, 0x7e, 0x17, 0x78, 0x50, 0x11, + 0x88, 0x21, 0xa5, 0x3e, 0x50, 0x23, 0x55, 0xfe, 0x6b, 0x24, 0x40, 0x7e, + 0x2c, 0xbf, 0x7c, 0xd3, 0x97, 0xeb, 0x2e, 0x21, 0x56, 0x5b, 0x86, 0x78, + 0x20, 0x2a, 0xb9, 0xfc, 0xb2, 0xfc, 0xe3, 0x8c, 0x25, 0x96, 0x15, 0x65, + 0x6c, 0x79, 0xbe, 0x16, 0x22, 0x5b, 0xff, 0xf3, 0x0b, 0x03, 0xf7, 0x49, + 0xfc, 0xc8, 0xce, 0xac, 0xbf, 0xfe, 0xcd, 0x9c, 0x83, 0x3b, 0x90, 0xc7, + 0xfb, 0xab, 0x2a, 0x11, 0x4d, 0xa5, 0x4b, 0xfd, 0xbb, 0xec, 0xdb, 0x5a, + 0x85, 0x94, 0xb2, 0xff, 0xe7, 0x96, 0x10, 0xca, 0x24, 0x3e, 0x2c, 0xb6, + 0x61, 0xe6, 0xef, 0x0b, 0xbf, 0xff, 0xf8, 0x0c, 0x63, 0xca, 0x76, 0xe1, + 0x63, 0x20, 0x33, 0x84, 0xdb, 0x67, 0xf9, 0x65, 0xf8, 0x12, 0x13, 0x5c, + 0x59, 0x63, 0xf2, 0x29, 0xc9, 0xf6, 0xff, 0xfd, 0x1a, 0x80, 0xe3, 0x20, + 0x32, 0x3e, 0x01, 0x8b, 0x2a, 0x15, 0xd6, 0xcc, 0xe9, 0xa7, 0x4f, 0x43, + 0x55, 0xc8, 0x8a, 0x10, 0x9d, 0x86, 0x7c, 0xf9, 0x3d, 0xfd, 0xac, 0x81, + 0x60, 0x96, 0x5e, 0x27, 0x62, 0xcb, 0xbf, 0x85, 0x97, 0xff, 0x79, 0x99, + 0xf4, 0xb8, 0xf1, 0xf4, 0x96, 0x5c, 0x26, 0xcb, 0x28, 0xcf, 0x7c, 0x91, + 0xaa, 0x48, 0xdd, 0x32, 0xc2, 0x1b, 0xeb, 0xb5, 0x36, 0x21, 0x8d, 0x01, + 0x3d, 0x42, 0xe5, 0xb3, 0x1b, 0x04, 0xf3, 0x0d, 0x09, 0xee, 0x53, 0x5b, + 0x6a, 0xb3, 0x44, 0xf7, 0x06, 0xd2, 0xde, 0xe5, 0x2a, 0xe0, 0x74, 0x82, + 0x2c, 0xad, 0xd5, 0x85, 0x97, 0x7e, 0x1a, 0x55, 0x6f, 0xd3, 0xfa, 0x47, + 0x39, 0xb7, 0x34, 0xbf, 0xfd, 0x52, 0x1f, 0x59, 0x3b, 0x2f, 0xea, 0xe8, + 0xbd, 0xe7, 0x37, 0xff, 0x97, 0x0c, 0xdb, 0x85, 0x11, 0x56, 0xb7, 0x7c, + 0xa6, 0xec, 0x76, 0xb1, 0xd5, 0x04, 0xa2, 0xcd, 0xf1, 0x9d, 0x35, 0x09, + 0x89, 0xf9, 0xc7, 0x91, 0x27, 0x17, 0xf7, 0x23, 0x60, 0xbf, 0xff, 0x80, + 0x16, 0xff, 0x77, 0x51, 0x9f, 0x4f, 0x63, 0x77, 0x15, 0x65, 0x37, 0x56, + 0x20, 0x72, 0xaa, 0xef, 0xe7, 0x3e, 0xf0, 0xc9, 0x65, 0xe3, 0xd6, 0x2c, + 0xad, 0xd3, 0xc6, 0xe1, 0x5d, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x9d, 0x5d, + 0x9f, 0x2c, 0xbf, 0xfc, 0x00, 0x11, 0x94, 0x78, 0xd9, 0x9c, 0x59, 0x76, + 0x98, 0xb2, 0xcd, 0xe1, 0x19, 0xd8, 0x4b, 0xf1, 0xa3, 0x05, 0xff, 0x47, + 0xbf, 0xff, 0x49, 0xbe, 0x4b, 0xd8, 0x72, 0xe4, 0x76, 0x06, 0xb2, 0xff, + 0xda, 0x6f, 0x3e, 0xfb, 0x77, 0x8f, 0xfa, 0xca, 0x84, 0x4e, 0x1a, 0xb5, + 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0xa3, 0x5f, 0xf9, 0xe4, 0xdf, 0x34, 0x17, + 0x70, 0xaa, 0x26, 0x85, 0xff, 0xe2, 0xc9, 0xa4, 0xfa, 0x91, 0x66, 0xf7, + 0x59, 0x7c, 0x51, 0x9f, 0x2c, 0xac, 0x47, 0x5b, 0x0d, 0x38, 0x99, 0xd4, + 0xab, 0xfc, 0x23, 0xef, 0x6f, 0xd3, 0xd9, 0x65, 0x37, 0x3f, 0x58, 0x3c, + 0xbf, 0x6c, 0x26, 0xd1, 0xb2, 0xcb, 0xe0, 0xbb, 0x85, 0x51, 0x57, 0xab, + 0x0f, 0x6b, 0x45, 0x97, 0xf6, 0x6f, 0x82, 0xf0, 0x16, 0x5f, 0xc6, 0x31, + 0xc6, 0xa1, 0x65, 0xfd, 0x1c, 0x27, 0x8e, 0x2c, 0xbf, 0xff, 0x7e, 0x40, + 0x60, 0x83, 0x1c, 0x7e, 0xc8, 0xce, 0xac, 0xbf, 0x9f, 0xbc, 0xcf, 0xba, + 0xb2, 0xbe, 0x44, 0x31, 0x2c, 0x59, 0xbe, 0x26, 0x9f, 0xa2, 0x1f, 0x17, + 0x74, 0xb0, 0x48, 0x58, 0xdf, 0xb4, 0x17, 0x70, 0xaa, 0x2b, 0x65, 0xff, + 0x49, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x48, 0x2b, 0xec, 0x64, 0x6e, 0xac, + 0xb3, 0x7c, 0x44, 0xfb, 0x9a, 0x71, 0x1e, 0xf9, 0x86, 0x50, 0xb2, 0xfd, + 0xa0, 0xbb, 0x85, 0x51, 0x21, 0xaf, 0xed, 0x03, 0x7c, 0x34, 0xc5, 0x97, + 0xf1, 0x67, 0x3b, 0x28, 0x59, 0x7c, 0xdd, 0xe4, 0xde, 0x11, 0x63, 0x84, + 0x06, 0x69, 0xbc, 0xc6, 0xff, 0xdc, 0x6f, 0x9d, 0x31, 0xe1, 0x4c, 0xb2, + 0xf3, 0xb8, 0x57, 0x18, 0x1a, 0xa0, 0xfa, 0x02, 0x81, 0x77, 0xb7, 0x56, + 0x5d, 0x3c, 0xec, 0xb2, 0xff, 0xbd, 0x1d, 0xe4, 0xf5, 0xc9, 0xe4, 0x96, + 0x53, 0x60, 0xf8, 0x24, 0x3b, 0x7d, 0xde, 0xe6, 0xe2, 0xcb, 0x12, 0xcb, + 0x1a, 0xca, 0x9e, 0x8d, 0x04, 0xf0, 0x10, 0xbf, 0xfd, 0x3c, 0x27, 0x84, + 0xf5, 0xe8, 0x61, 0x3f, 0x8f, 0x4b, 0x2d, 0xf2, 0xcb, 0xdd, 0xcf, 0xd6, + 0x5c, 0x64, 0xb2, 0xe8, 0x99, 0x65, 0xfc, 0xcc, 0x09, 0x3e, 0xea, 0xcb, + 0x08, 0xb2, 0xb0, 0xf0, 0x40, 0x5f, 0x7f, 0x40, 0xe3, 0xae, 0x35, 0x97, 0xbd, 0x9c, 0x59, 0x67, 0x59, 0x43, 0x35, 0x9d, 0x1c, 0xa1, 0xa6, 0x6d, - 0xba, 0x25, 0x10, 0xef, 0x85, 0x78, 0xb1, 0xd2, 0x11, 0x16, 0x2f, 0xbb, - 0xc3, 0xde, 0xb2, 0xfc, 0x39, 0x2d, 0xb7, 0x56, 0x5f, 0xe9, 0x14, 0x4f, - 0xff, 0x9f, 0x96, 0x54, 0x9f, 0x1e, 0xc5, 0x77, 0xe7, 0xf1, 0xb3, 0x16, + 0xba, 0x25, 0x30, 0xef, 0x85, 0x78, 0xb1, 0xd2, 0x11, 0x16, 0x2f, 0xbb, + 0xc3, 0xde, 0xb2, 0xfc, 0x38, 0x2d, 0xb7, 0x56, 0x5f, 0xe8, 0x14, 0x4f, + 0xff, 0x8f, 0x96, 0x54, 0x1f, 0x1e, 0xc5, 0x77, 0xe7, 0xf1, 0xb3, 0x16, 0x58, 0xc8, 0xf2, 0x37, 0x91, 0x5f, 0x61, 0x7f, 0xc5, 0x97, 0x82, 0x5b, - 0xab, 0x2f, 0xfc, 0xed, 0x78, 0xde, 0x1d, 0x3d, 0x96, 0x53, 0x9e, 0xf1, - 0x0f, 0xdf, 0x6d, 0xcf, 0x8d, 0x65, 0xff, 0xe9, 0xfa, 0x06, 0xc6, 0xb0, - 0x87, 0xe9, 0x59, 0x58, 0x98, 0x76, 0xa1, 0x01, 0xc2, 0x00, 0x12, 0x5f, - 0x9c, 0x62, 0x49, 0x2c, 0xbf, 0xc6, 0x0d, 0xdf, 0x4e, 0x6c, 0xb2, 0xff, - 0xda, 0xe8, 0x84, 0xe1, 0xc2, 0x0a, 0xcb, 0xff, 0x34, 0xf0, 0xd6, 0x1f, - 0xa4, 0x6b, 0x2f, 0x82, 0xee, 0x15, 0x45, 0x88, 0xbf, 0x6b, 0x4e, 0x5d, + 0xab, 0x2f, 0xfc, 0xed, 0x78, 0xde, 0x5d, 0x3d, 0x96, 0x53, 0x9e, 0xf1, + 0x0f, 0xdf, 0x6d, 0xcf, 0x8d, 0x65, 0xff, 0xe8, 0xfa, 0x46, 0xc6, 0xb0, + 0x87, 0xe8, 0x59, 0x58, 0x98, 0x76, 0xa1, 0x01, 0xc2, 0x00, 0x12, 0x5f, + 0x9c, 0x62, 0x41, 0x2c, 0xbf, 0xc6, 0x0d, 0xdf, 0x46, 0x6c, 0xb2, 0xff, + 0xda, 0xe8, 0x84, 0xe1, 0xc2, 0x0a, 0xcb, 0xff, 0x34, 0xf2, 0xd6, 0x1f, + 0xa0, 0x6b, 0x2f, 0x82, 0xee, 0x15, 0x45, 0x88, 0xbf, 0x6b, 0x4e, 0x5d, 0x59, 0x7c, 0xe3, 0xf0, 0x16, 0x59, 0xa5, 0x97, 0x70, 0xd6, 0x58, 0xcc, - 0xd4, 0x7e, 0x25, 0x7f, 0x4c, 0xfc, 0xc7, 0x1a, 0xcb, 0xc0, 0x28, 0x2c, - 0xbf, 0xc1, 0x9c, 0xd7, 0xff, 0xf5, 0x65, 0x6c, 0xa8, 0x72, 0x04, 0xf8, + 0xd4, 0x7e, 0x25, 0x7f, 0x44, 0x7c, 0xc7, 0x1a, 0xcb, 0xc0, 0x29, 0x2c, + 0xbf, 0xc1, 0x8c, 0xd7, 0xff, 0xf5, 0x65, 0x6c, 0xa8, 0x72, 0x44, 0xf8, 0x6c, 0x67, 0xda, 0x3e, 0x61, 0x77, 0x89, 0xc9, 0x2b, 0x84, 0xdd, 0x2d, - 0x00, 0xe5, 0xfb, 0x35, 0x24, 0x2a, 0xcb, 0x1a, 0xcb, 0x1a, 0xcb, 0x40, - 0x53, 0x41, 0xf8, 0x85, 0xf8, 0x48, 0xfc, 0xd4, 0xac, 0xbf, 0x77, 0xb2, - 0x5b, 0x2c, 0xbd, 0xc9, 0x25, 0x95, 0x27, 0xdd, 0x85, 0x66, 0x51, 0x7f, + 0x00, 0xe5, 0xfb, 0x35, 0x04, 0x2a, 0xcb, 0x1a, 0xcb, 0x1a, 0xcb, 0x48, + 0x53, 0x41, 0xf8, 0x85, 0xf8, 0x49, 0xfc, 0xd4, 0x2c, 0xbf, 0x77, 0xb0, + 0x5b, 0x2c, 0xbd, 0xc8, 0x25, 0x95, 0x07, 0xdd, 0x85, 0x66, 0x51, 0x7f, 0xc0, 0xd8, 0xb0, 0x7e, 0x00, 0xab, 0x28, 0x54, 0x7f, 0x7a, 0x12, 0xe4, - 0x59, 0x7d, 0xee, 0x02, 0x56, 0x5f, 0xfe, 0xcf, 0xbe, 0xfd, 0xfb, 0xc3, - 0x2f, 0xa0, 0xb2, 0xff, 0xe9, 0xce, 0x61, 0x0c, 0xff, 0x92, 0x59, 0x78, - 0xa7, 0x65, 0x94, 0x68, 0xaa, 0x24, 0xd0, 0x20, 0xdf, 0x4e, 0xa5, 0x8b, - 0x2a, 0x53, 0x1b, 0x84, 0x33, 0x38, 0x5d, 0x7c, 0x31, 0x23, 0xa8, 0x2c, + 0x59, 0x7d, 0xee, 0x02, 0x16, 0x5f, 0xfe, 0xcf, 0xbe, 0xfd, 0xfb, 0xc3, + 0x2f, 0xa4, 0xb2, 0xff, 0xe8, 0xce, 0x61, 0x0c, 0xff, 0x82, 0x59, 0x78, + 0xa3, 0x65, 0x94, 0x68, 0xaa, 0x24, 0xd0, 0x20, 0xdf, 0x46, 0xa1, 0x8b, + 0x2a, 0x13, 0x1b, 0x94, 0x33, 0x38, 0x5d, 0x7c, 0x31, 0x27, 0xa9, 0x2c, 0xbf, 0x7e, 0x1f, 0x1b, 0x6d, 0x65, 0xef, 0x67, 0xeb, 0x2f, 0xef, 0xb9, - 0xe9, 0x3e, 0xac, 0xb9, 0xfa, 0xb2, 0x8c, 0xf1, 0x18, 0x5d, 0x7b, 0xd3, - 0xfa, 0xcb, 0x81, 0x1e, 0xb2, 0xff, 0xfb, 0xb9, 0xf4, 0x08, 0x1d, 0x73, - 0x29, 0x1a, 0xcb, 0xf9, 0x85, 0x93, 0xbb, 0x12, 0xcb, 0x7e, 0xb2, 0xa4, - 0xf0, 0x8c, 0xc2, 0xf3, 0xcf, 0xcb, 0x2a, 0x39, 0x4f, 0x6f, 0x0a, 0x42, - 0x5b, 0xa6, 0x1f, 0x10, 0x90, 0xef, 0x46, 0xa3, 0xe1, 0x21, 0xb8, 0x41, - 0x7f, 0xfe, 0x7d, 0x41, 0x85, 0x3a, 0x91, 0xe4, 0x53, 0xe5, 0x97, 0xdb, - 0xc0, 0xfc, 0x59, 0x63, 0x59, 0x7e, 0x3d, 0x1b, 0x4d, 0xb5, 0x95, 0x04, - 0x5a, 0x1a, 0xa4, 0x44, 0x9e, 0x10, 0xbf, 0xf1, 0xfb, 0xe8, 0x67, 0x73, - 0xee, 0xac, 0xbf, 0xde, 0xc6, 0x63, 0xeb, 0x65, 0x95, 0x03, 0xf3, 0x1a, - 0x05, 0xfb, 0xbd, 0x78, 0x71, 0x65, 0xff, 0x81, 0x9f, 0x0f, 0x7f, 0xb1, - 0xe2, 0x59, 0x5f, 0x1f, 0x56, 0xf2, 0x8b, 0x41, 0x65, 0xef, 0xb8, 0xeb, - 0x2f, 0xb3, 0x1a, 0xe2, 0xca, 0x93, 0xd0, 0xc1, 0x1e, 0x8e, 0xdb, 0x7a, - 0xcb, 0x80, 0x4b, 0x2c, 0xdb, 0x59, 0x6d, 0xeb, 0x2a, 0x53, 0x7f, 0xc8, - 0x46, 0x69, 0xd7, 0xf2, 0xde, 0x09, 0xf4, 0x5b, 0x78, 0xad, 0xfc, 0x19, - 0xef, 0xb3, 0xf5, 0x97, 0xf0, 0x99, 0xac, 0xc8, 0x96, 0x51, 0x1e, 0xe7, - 0x0b, 0xaf, 0xdf, 0x72, 0x75, 0xc5, 0x97, 0xfc, 0xfd, 0xe0, 0xc0, 0xfa, - 0x82, 0xcb, 0xfd, 0xb7, 0xd0, 0xe1, 0x1c, 0x4b, 0x2f, 0xba, 0x40, 0xd9, - 0x65, 0x49, 0xec, 0x91, 0xb5, 0x4a, 0x3f, 0x30, 0x84, 0x8a, 0x41, 0x09, - 0x2b, 0xfb, 0x50, 0xff, 0x32, 0x25, 0x97, 0xcf, 0x27, 0x1e, 0xb2, 0xf6, - 0x84, 0xe2, 0xca, 0xc3, 0xf0, 0xd1, 0x7e, 0xf2, 0x3b, 0xe8, 0x09, 0xe9, - 0x59, 0x7b, 0x53, 0x05, 0x94, 0xe6, 0xff, 0x79, 0x1d, 0xf9, 0x98, 0x7a, - 0x89, 0x65, 0xb7, 0x56, 0x54, 0x9b, 0xc7, 0x28, 0xbe, 0x12, 0x77, 0x38, - 0xb2, 0xff, 0xc4, 0x4e, 0xce, 0x63, 0x27, 0x75, 0x65, 0xff, 0x8f, 0x67, - 0x2f, 0x3f, 0x27, 0x75, 0x65, 0xef, 0x4f, 0x96, 0x52, 0xcb, 0x60, 0xcd, + 0xe8, 0x3e, 0xac, 0xb9, 0xfa, 0xb2, 0x8c, 0xf1, 0x18, 0x5d, 0x7b, 0xd1, + 0xfa, 0xcb, 0x81, 0x3e, 0xb2, 0xff, 0xfb, 0xb9, 0xf4, 0x88, 0x1d, 0x73, + 0x28, 0x1a, 0xcb, 0xf9, 0x85, 0x91, 0xbb, 0x32, 0xcb, 0x7e, 0xb2, 0xa0, + 0xf0, 0x8c, 0xc2, 0xf3, 0xc7, 0xcb, 0x2a, 0x79, 0x4f, 0x6f, 0x0a, 0x42, + 0x5b, 0xa6, 0x1f, 0x10, 0x90, 0xef, 0x46, 0xa7, 0xe1, 0x21, 0xb8, 0x41, + 0x7f, 0xfe, 0x7d, 0x49, 0x85, 0x1a, 0x81, 0xe4, 0xd1, 0xe5, 0x97, 0xdb, + 0xc0, 0xfc, 0x59, 0x63, 0x59, 0x7e, 0x3d, 0x1b, 0x4d, 0xb5, 0x95, 0x24, + 0x5a, 0x1a, 0xa4, 0xc4, 0x9e, 0x10, 0xbf, 0xf1, 0xfb, 0xe9, 0x67, 0x73, + 0xee, 0xac, 0xbf, 0xde, 0xc6, 0x63, 0xeb, 0x65, 0x95, 0x23, 0xf3, 0x1a, + 0x05, 0xfb, 0xbd, 0x79, 0x71, 0x65, 0xff, 0x81, 0x9f, 0x0f, 0x7f, 0xb1, + 0xe6, 0x59, 0x5f, 0x1f, 0x56, 0xf2, 0x8b, 0x49, 0x65, 0xef, 0xb8, 0xeb, + 0x2f, 0xb3, 0x1a, 0xe2, 0xca, 0x83, 0xd0, 0xc1, 0x1e, 0x8e, 0xdb, 0x7a, + 0xcb, 0x80, 0x4b, 0x2c, 0xdb, 0x59, 0x6d, 0xeb, 0x2a, 0x13, 0x7f, 0xc8, + 0x46, 0x69, 0xd7, 0xf2, 0xde, 0x09, 0xf4, 0x5b, 0x78, 0xad, 0xfc, 0x18, + 0xef, 0xb3, 0xf5, 0x97, 0xf0, 0x99, 0xac, 0xc9, 0x96, 0x51, 0x1e, 0xe7, + 0x0b, 0xaf, 0xdf, 0x72, 0x35, 0xc5, 0x97, 0xfc, 0xfd, 0xe0, 0xc0, 0xfa, + 0x92, 0xcb, 0xfd, 0xb7, 0xd2, 0xe1, 0x1c, 0xcb, 0x2f, 0xba, 0x40, 0xd9, + 0x65, 0x41, 0xec, 0x91, 0xb5, 0x42, 0x3f, 0x30, 0x84, 0x8a, 0x41, 0x09, + 0x2b, 0xfb, 0x52, 0xff, 0x32, 0x65, 0x97, 0xcf, 0x07, 0x3e, 0xb2, 0xf6, + 0x84, 0xe2, 0xca, 0xc3, 0xf0, 0xd1, 0x7e, 0xf2, 0x3b, 0xe9, 0x09, 0xe8, + 0x59, 0x7b, 0x51, 0x25, 0x94, 0xe6, 0xff, 0x79, 0x1d, 0xf9, 0x98, 0x7a, + 0x99, 0x65, 0xb7, 0x56, 0x54, 0x1b, 0xc7, 0x28, 0xbe, 0x12, 0x37, 0x38, + 0xb2, 0xff, 0xc4, 0x4e, 0xce, 0x63, 0x23, 0x75, 0x65, 0xff, 0x8f, 0x67, + 0x2f, 0x3f, 0x23, 0x75, 0x65, 0xef, 0x47, 0x96, 0x52, 0xcb, 0x60, 0xcd, 0x3b, 0x07, 0x2f, 0xb4, 0x6d, 0x6e, 0xac, 0xa3, 0x47, 0x97, 0x8f, 0xc9, - 0x9f, 0x84, 0xd7, 0xff, 0xb4, 0x6c, 0x9f, 0xbd, 0x23, 0x27, 0xdd, 0x59, - 0x43, 0x4e, 0x7f, 0x91, 0x8c, 0x74, 0xee, 0xfd, 0xf0, 0x7d, 0x3a, 0x59, - 0x7f, 0xbf, 0x03, 0x89, 0xe9, 0x82, 0xca, 0x95, 0x64, 0x06, 0xdf, 0xe5, - 0xe7, 0x8e, 0xf8, 0x06, 0xed, 0x14, 0xdf, 0xdf, 0x07, 0xd3, 0xf6, 0xe2, - 0xcb, 0xf8, 0xc9, 0xd9, 0x3e, 0x59, 0x7f, 0xfa, 0x59, 0x30, 0x19, 0x4f, - 0x9f, 0x34, 0xb2, 0xe3, 0xf2, 0xca, 0x59, 0x50, 0x3e, 0x8e, 0xa3, 0xc7, - 0x8b, 0x5f, 0x8a, 0x7d, 0x3b, 0xab, 0x2f, 0x77, 0x3f, 0x59, 0x7f, 0x30, + 0x9f, 0x84, 0xd7, 0xff, 0xb4, 0x6c, 0x8f, 0xbd, 0x03, 0x27, 0xdd, 0x59, + 0x43, 0x4e, 0x7f, 0x91, 0x8c, 0x74, 0xee, 0xfd, 0xf0, 0x7d, 0x1a, 0x59, + 0x7f, 0xbf, 0x03, 0x89, 0xe8, 0x92, 0xca, 0x85, 0x64, 0x06, 0xdf, 0xe5, + 0xe7, 0x8e, 0xf8, 0x06, 0xed, 0x14, 0xdf, 0xdf, 0x07, 0xd1, 0xf6, 0xe2, + 0xcb, 0xf8, 0xc9, 0xd9, 0x1e, 0x59, 0x7f, 0xfa, 0x19, 0x12, 0x19, 0x47, + 0x9f, 0x34, 0xb2, 0xe3, 0xf2, 0xca, 0x59, 0x52, 0x3e, 0x8e, 0xa3, 0xcf, + 0x8b, 0x5f, 0x8a, 0x3d, 0x1b, 0xab, 0x2f, 0x77, 0x3f, 0x59, 0x7f, 0x30, 0xf9, 0xc3, 0x1a, 0xcb, 0xcf, 0xa1, 0x56, 0x50, 0x0f, 0x29, 0xa2, 0xdb, - 0xfc, 0x12, 0xcd, 0xe5, 0x9c, 0x59, 0x7f, 0xb7, 0x63, 0x09, 0x24, 0xe2, - 0xac, 0xbf, 0xd0, 0x7c, 0x2c, 0xef, 0x16, 0x56, 0x1f, 0x57, 0xc7, 0x37, - 0xfe, 0xcf, 0xa1, 0xe9, 0xe0, 0xbe, 0x95, 0x95, 0x2a, 0xaa, 0xa0, 0x67, - 0xf4, 0x24, 0xcc, 0xca, 0x22, 0x9d, 0x33, 0xf8, 0x8c, 0xa1, 0x3b, 0xb8, - 0x45, 0x7d, 0xd7, 0x7d, 0x96, 0x5a, 0x3d, 0x65, 0xd0, 0xe2, 0xcb, 0x3c, - 0x0d, 0x5f, 0xc2, 0x97, 0xdd, 0x32, 0x69, 0x25, 0x80, 0xb2, 0xd3, 0x86, + 0xfc, 0x12, 0xcd, 0xe5, 0x9c, 0x59, 0x7f, 0xb7, 0x67, 0x09, 0x04, 0xe2, + 0xac, 0xbf, 0xd2, 0x7c, 0x2c, 0xef, 0x16, 0x56, 0x1f, 0x57, 0xc7, 0x37, + 0xfe, 0xcf, 0xa5, 0xe8, 0xe0, 0xbe, 0x85, 0x95, 0x0a, 0xaa, 0xa4, 0x67, + 0xf4, 0x24, 0xcc, 0xca, 0x62, 0x9d, 0x33, 0xf8, 0x8c, 0xa1, 0x3b, 0xb8, + 0x45, 0x7d, 0xd7, 0x7d, 0x96, 0x5a, 0x7d, 0x65, 0xd2, 0xe2, 0xcb, 0x3c, + 0x8d, 0x5f, 0xc2, 0x97, 0xdd, 0x32, 0x69, 0x25, 0x80, 0xb2, 0xd1, 0x86, 0xcf, 0xc4, 0x57, 0x66, 0x96, 0x5f, 0xe2, 0xef, 0x3e, 0xe3, 0xee, 0xac, 0xbf, 0xb0, 0x7b, 0xb9, 0xf6, 0xea, 0xca, 0x73, 0xeb, 0x23, 0x7a, 0xdd, 0x4d, 0xb5, 0xd3, 0x7f, 0x5a, 0x22, 0x50, 0x42, 0x06, 0xff, 0xcc, 0x04, - 0x5f, 0x72, 0x62, 0x3d, 0xeb, 0x2f, 0xf7, 0xa5, 0x85, 0x3f, 0x41, 0x65, - 0xed, 0x6b, 0x16, 0x5e, 0xfa, 0x1c, 0x59, 0x77, 0x8f, 0x86, 0xe9, 0xa1, - 0xcb, 0xd2, 0xd4, 0x16, 0x5f, 0xf6, 0x77, 0x9a, 0xce, 0x4f, 0xcb, 0x28, - 0x27, 0xac, 0x63, 0xb7, 0xfe, 0x96, 0x84, 0xdb, 0x98, 0xc9, 0xdd, 0x59, - 0x73, 0xb4, 0xb2, 0x86, 0x9b, 0x60, 0x5b, 0x7d, 0x08, 0x3e, 0x90, 0xef, - 0x44, 0xbb, 0x9f, 0x2c, 0xbb, 0x38, 0xb2, 0xed, 0xcd, 0x2c, 0xa9, 0x54, - 0x95, 0x91, 0xd8, 0x1a, 0x90, 0x83, 0x1b, 0x82, 0xd7, 0xf1, 0xe7, 0xbe, - 0x87, 0x16, 0x5f, 0xf8, 0x01, 0x27, 0xe7, 0x01, 0x21, 0x59, 0x7f, 0xf7, - 0x9c, 0xfb, 0x0c, 0x14, 0xb3, 0xf5, 0x97, 0xbd, 0x8d, 0x2c, 0xbf, 0xcc, - 0x3e, 0x78, 0xe4, 0xd6, 0x5f, 0x8a, 0x29, 0xf4, 0xac, 0xa5, 0x97, 0x60, - 0xd6, 0x5d, 0xfb, 0xfc, 0x68, 0x77, 0x85, 0xdf, 0xb3, 0xdf, 0x3e, 0xea, - 0xcb, 0xef, 0xf9, 0x24, 0xb2, 0xff, 0x6b, 0xce, 0x7c, 0x3d, 0xeb, 0x2e, - 0x14, 0xd6, 0x54, 0x9f, 0x6e, 0x11, 0x39, 0xa5, 0xff, 0xde, 0xc2, 0x27, - 0xec, 0x68, 0x4b, 0x16, 0x5f, 0x9f, 0xa0, 0x71, 0x16, 0x50, 0xd5, 0x53, - 0x61, 0x71, 0x9f, 0x6e, 0xa2, 0x30, 0x75, 0xcc, 0x7f, 0x48, 0x23, 0x0e, - 0x42, 0x63, 0xa5, 0x80, 0x44, 0xbf, 0x9a, 0x9d, 0x69, 0xe2, 0x59, 0x7f, - 0xc3, 0x90, 0x89, 0xff, 0xf3, 0xf2, 0xcb, 0xfe, 0x7d, 0x63, 0x0e, 0x3e, - 0x58, 0xb2, 0x82, 0x7e, 0xe0, 0x3d, 0xbf, 0x7c, 0x31, 0x9f, 0x16, 0x5e, - 0xd6, 0x71, 0x65, 0x49, 0xe2, 0xe1, 0x4d, 0xf7, 0x67, 0xe8, 0x2c, 0xbf, - 0x8f, 0xf8, 0xf2, 0x3f, 0xd6, 0x5d, 0x9f, 0xac, 0xad, 0x8f, 0xaf, 0x44, - 0x64, 0x63, 0x7f, 0xf7, 0x9c, 0xfb, 0x9a, 0xfd, 0x8f, 0x05, 0x97, 0x87, - 0xe3, 0x59, 0x46, 0x7c, 0x00, 0x44, 0xbe, 0x22, 0x9d, 0x96, 0x5e, 0xee, - 0x08, 0xb2, 0xa5, 0x54, 0x36, 0x42, 0x9b, 0xcc, 0xef, 0x08, 0xbe, 0x42, - 0x3c, 0x04, 0x3b, 0x84, 0x37, 0xf1, 0x4c, 0x27, 0x7c, 0xac, 0xba, 0x7f, - 0x59, 0x7c, 0x03, 0xdc, 0x62, 0xcb, 0xfb, 0x7e, 0x9e, 0x4e, 0x3d, 0x65, - 0xc7, 0xb2, 0xcb, 0x62, 0xca, 0x34, 0x60, 0xc4, 0x5b, 0xa1, 0x76, 0x12, - 0xb9, 0x89, 0x0b, 0xdf, 0xb3, 0xdb, 0xcf, 0x4b, 0x2f, 0x66, 0xa5, 0x65, - 0xf4, 0x4d, 0x48, 0xab, 0x2f, 0xb6, 0xee, 0x05, 0x65, 0xfe, 0x71, 0x44, - 0xf7, 0xa6, 0x0b, 0x2a, 0x51, 0x31, 0x83, 0x6e, 0x4b, 0x1e, 0x47, 0x7f, - 0xef, 0x60, 0xc6, 0x7f, 0x67, 0xdd, 0x59, 0x7f, 0x46, 0x18, 0x8f, 0xad, - 0x2c, 0xad, 0x8f, 0xc4, 0x68, 0x17, 0xf8, 0x79, 0xdf, 0x49, 0x05, 0x65, - 0xf3, 0xc9, 0xc7, 0xac, 0xbf, 0xe1, 0xc6, 0xce, 0x4e, 0x6b, 0xf5, 0x97, - 0xbc, 0x27, 0x16, 0x56, 0x1e, 0xcb, 0x9d, 0xdf, 0xe3, 0x1f, 0x49, 0xfe, - 0xe2, 0xcb, 0xfd, 0xde, 0x1f, 0xd9, 0xf7, 0x56, 0x5d, 0xb4, 0xac, 0xa9, - 0x3f, 0xf7, 0x33, 0x01, 0xad, 0x4a, 0x73, 0xbb, 0x11, 0x8c, 0xcb, 0xcf, - 0x85, 0x09, 0xfa, 0x8e, 0x9d, 0x42, 0x6c, 0x72, 0xf5, 0x1d, 0x93, 0x36, - 0xba, 0x2b, 0x6a, 0x2c, 0x98, 0xe4, 0x36, 0x6a, 0x84, 0x33, 0xc7, 0x19, - 0xb6, 0x4a, 0x5e, 0x16, 0x13, 0x01, 0x8d, 0x17, 0xe8, 0xd4, 0x0e, 0x50, - 0x84, 0x51, 0xa0, 0xea, 0x51, 0x7b, 0x23, 0xc3, 0xf4, 0xbc, 0xf7, 0x96, - 0x0f, 0xfc, 0x7f, 0x25, 0x2a, 0x73, 0x92, 0xbe, 0x7b, 0x2c, 0x94, 0x11, - 0x9b, 0x6f, 0x55, 0x8f, 0x86, 0x08, 0x92, 0x92, 0xef, 0xdb, 0x8e, 0x19, - 0xd2, 0xcb, 0x9a, 0xc5, 0x97, 0xfe, 0x39, 0xef, 0x9c, 0x5c, 0x2f, 0xd6, - 0x5f, 0xfe, 0x7d, 0x46, 0x7e, 0xc6, 0xef, 0xc1, 0x32, 0x59, 0x46, 0x8a, - 0x3f, 0x0b, 0xef, 0x3e, 0xbe, 0xee, 0xb2, 0x0b, 0x2f, 0xec, 0xec, 0x9b, - 0x38, 0xb2, 0xff, 0x0e, 0x61, 0x9a, 0xce, 0x2c, 0xbf, 0xfe, 0xcd, 0x68, - 0x10, 0xd6, 0x4f, 0xd0, 0x36, 0x2c, 0xbd, 0xc1, 0x38, 0xb2, 0xff, 0xfb, - 0x3e, 0x87, 0x41, 0xb3, 0x7f, 0x3b, 0x0f, 0x8b, 0x2f, 0xfd, 0xd9, 0x67, - 0x24, 0xf6, 0x7e, 0xac, 0xbf, 0x9f, 0x75, 0xf3, 0xee, 0xac, 0xaf, 0x8f, - 0xbb, 0xc7, 0xd7, 0xf9, 0xf0, 0x67, 0xf1, 0x3a, 0xca, 0xc3, 0xd4, 0x01, - 0x1d, 0xf4, 0xf6, 0x0e, 0xb2, 0xfb, 0x5b, 0x92, 0x4b, 0x2c, 0x4b, 0x2a, - 0x4d, 0xa3, 0x44, 0x97, 0xba, 0x52, 0xb2, 0xef, 0x37, 0x95, 0x76, 0x99, - 0x0c, 0xd3, 0x31, 0x88, 0x89, 0x85, 0x9e, 0x32, 0x75, 0x02, 0x1e, 0xe4, - 0x62, 0xdb, 0xc8, 0x5a, 0x55, 0x10, 0x86, 0x9b, 0xaf, 0x41, 0x4c, 0xee, - 0x15, 0xfd, 0x9a, 0x0b, 0xb8, 0x55, 0x16, 0x6a, 0xff, 0x7f, 0x3c, 0x37, - 0xf9, 0xa5, 0x97, 0xe0, 0x45, 0xe3, 0x1a, 0xcb, 0xff, 0x60, 0xf3, 0x05, - 0x19, 0x1e, 0xcb, 0x2f, 0xb5, 0xe7, 0x6e, 0x68, 0x9c, 0x61, 0xa9, 0x14, - 0xdf, 0xee, 0xfb, 0x0f, 0x66, 0x4a, 0xcb, 0xff, 0x4e, 0xbf, 0xef, 0x3d, - 0x86, 0x35, 0x96, 0xd9, 0x65, 0xe8, 0x4f, 0xcb, 0x2d, 0x92, 0x6b, 0xe2, - 0x12, 0xbe, 0x32, 0x7d, 0x96, 0x56, 0x23, 0xa3, 0xc6, 0x64, 0xdd, 0xc2, - 0x6b, 0x79, 0x65, 0xfb, 0xd3, 0xcf, 0x1a, 0xcb, 0xff, 0x1f, 0x65, 0x84, - 0xe2, 0xf8, 0xd6, 0x5d, 0x3e, 0x59, 0x7f, 0x16, 0x6f, 0xf3, 0xc1, 0x65, - 0x9b, 0xec, 0x8b, 0x9c, 0x11, 0x88, 0x9f, 0xc7, 0xbd, 0x16, 0xa6, 0xe9, - 0xa3, 0xee, 0x43, 0xd2, 0xff, 0xf8, 0x0f, 0xb6, 0xb1, 0x8d, 0xfd, 0x3b, - 0x10, 0x16, 0x53, 0x75, 0x79, 0x87, 0x0d, 0x57, 0x94, 0xec, 0x45, 0xd7, - 0xc6, 0xc1, 0x34, 0xb2, 0xfb, 0x85, 0x2d, 0x2c, 0xbe, 0x90, 0xe7, 0x96, - 0x5f, 0x19, 0x68, 0x0b, 0x2f, 0x03, 0xee, 0x2c, 0xbf, 0x09, 0xd2, 0x9f, - 0xd6, 0x56, 0x8f, 0x1f, 0x78, 0xf5, 0xfb, 0x60, 0x40, 0x12, 0xb2, 0xa4, - 0xf3, 0x4c, 0x92, 0xf8, 0x42, 0xce, 0x2c, 0xba, 0x7e, 0x59, 0x71, 0xc7, - 0xac, 0xa8, 0x8d, 0x80, 0x82, 0xf7, 0x31, 0xbc, 0xa7, 0x61, 0xf1, 0x19, - 0x91, 0x44, 0x43, 0xe8, 0x5a, 0xb9, 0x00, 0x14, 0xae, 0xc3, 0x59, 0x76, - 0x0d, 0x65, 0xff, 0x9e, 0x0d, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x80, 0x5f, - 0xe3, 0x64, 0xee, 0xfb, 0x36, 0x59, 0x66, 0xf0, 0x45, 0x6e, 0x0a, 0xb0, - 0x5b, 0xc9, 0x97, 0x41, 0x8b, 0x2f, 0xe3, 0xe4, 0x45, 0x23, 0x59, 0x7b, - 0x5f, 0x41, 0x65, 0x0c, 0xf2, 0xdc, 0xb6, 0xfd, 0x3f, 0x75, 0xf4, 0xb2, - 0xfd, 0xde, 0x40, 0xff, 0x59, 0x7f, 0x1f, 0xa4, 0xa7, 0xab, 0x2f, 0x0d, - 0xcd, 0x65, 0x41, 0x31, 0xac, 0x62, 0x32, 0x17, 0x28, 0xe1, 0x48, 0x0a, - 0xef, 0xcd, 0xf2, 0x29, 0x82, 0xcb, 0xfe, 0x9e, 0xfb, 0x19, 0x9a, 0xc5, - 0x97, 0xd1, 0x66, 0x05, 0x65, 0xfb, 0xa2, 0xbf, 0x1b, 0xe8, 0xf6, 0x44, - 0x37, 0xa8, 0x23, 0x19, 0xa8, 0x41, 0xd3, 0x74, 0xd1, 0x26, 0x31, 0xbb, - 0xb9, 0xd5, 0x96, 0x95, 0x96, 0xf2, 0xca, 0x61, 0xa1, 0x00, 0x85, 0xf3, - 0x63, 0x7f, 0xdc, 0x59, 0x7f, 0xfe, 0xdf, 0x3e, 0x31, 0xfa, 0x61, 0xc7, - 0xf1, 0xf1, 0x65, 0xfd, 0xc9, 0x61, 0x4c, 0x4b, 0x2d, 0xe5, 0x97, 0x84, - 0x28, 0x96, 0x5b, 0xb8, 0x6b, 0xff, 0x11, 0xbb, 0x36, 0x59, 0x58, 0x6f, - 0x88, 0x9e, 0xf8, 0xd9, 0x31, 0x2c, 0xa3, 0x4c, 0xc7, 0xf5, 0x62, 0x84, - 0xd4, 0x78, 0xfd, 0xb8, 0xb2, 0xf8, 0x66, 0x5f, 0xac, 0xa8, 0xc6, 0xcd, - 0xc4, 0x6f, 0xfe, 0xcf, 0xf8, 0x4e, 0x2e, 0x6f, 0x3d, 0x2c, 0xbc, 0x01, - 0xe2, 0xcb, 0xfb, 0x5d, 0x29, 0x67, 0x16, 0x5f, 0xff, 0xb4, 0x37, 0x21, - 0x7d, 0x9b, 0xba, 0x36, 0x48, 0xab, 0x2b, 0x11, 0x0c, 0x02, 0xdb, 0x47, - 0xac, 0xac, 0x4c, 0x78, 0xd1, 0xbd, 0x0a, 0x2f, 0xc8, 0xaf, 0x82, 0xee, - 0x15, 0x45, 0xbe, 0xbd, 0x1e, 0xfd, 0x59, 0x7f, 0x8c, 0xa5, 0x9f, 0xbf, - 0x56, 0x5f, 0xc2, 0x75, 0xc2, 0xfa, 0x59, 0x5a, 0x45, 0x6b, 0x0b, 0xbc, - 0x3f, 0xf9, 0x95, 0xff, 0x1f, 0xa7, 0xe9, 0xdd, 0xcf, 0x96, 0x5f, 0xfc, - 0x7d, 0xd0, 0x39, 0xe7, 0x87, 0xe6, 0xb2, 0xff, 0x66, 0xd9, 0xde, 0x3f, - 0x56, 0x57, 0x8f, 0xe0, 0x91, 0x6c, 0xd2, 0xcb, 0xfe, 0x72, 0x0f, 0x27, - 0xcf, 0xba, 0xb2, 0x85, 0x3c, 0xce, 0x89, 0x5f, 0x61, 0x1c, 0x4b, 0x2f, - 0x8f, 0x35, 0xbd, 0x65, 0xf7, 0xd0, 0xda, 0x56, 0x5e, 0x96, 0xa3, 0xd6, - 0x56, 0x1e, 0x26, 0x89, 0x2f, 0x7a, 0x74, 0xb2, 0xfd, 0xe3, 0xd7, 0x9d, - 0x65, 0xff, 0xef, 0x4e, 0xdd, 0xcf, 0x16, 0x77, 0xc6, 0xb2, 0xf3, 0x30, - 0x6b, 0x2c, 0xc5, 0x97, 0xfa, 0x76, 0x1e, 0x35, 0xc6, 0xf2, 0x8b, 0x9c, - 0x1c, 0x88, 0x9d, 0xd2, 0x44, 0x1c, 0xa8, 0x26, 0x9f, 0xe8, 0x6d, 0x5f, - 0x0d, 0x99, 0xf2, 0xcb, 0xe8, 0x70, 0x4e, 0x2c, 0xa9, 0x3c, 0x7e, 0x91, - 0xdf, 0x67, 0x79, 0x8b, 0x2f, 0xe7, 0xf8, 0x66, 0x5b, 0x2c, 0xa9, 0x3c, - 0xfd, 0x10, 0xde, 0xe7, 0x86, 0xb2, 0xfc, 0x27, 0x4a, 0x7f, 0x59, 0x4c, - 0x3c, 0x6d, 0xe3, 0xb7, 0xff, 0x77, 0x87, 0xc9, 0x2c, 0xef, 0x8d, 0x65, - 0xff, 0x9f, 0x73, 0x46, 0x2e, 0x78, 0xe0, 0xb2, 0xf1, 0x67, 0x96, 0x53, - 0x11, 0x3d, 0xe4, 0x2d, 0xe8, 0x37, 0xe1, 0xce, 0xe8, 0x18, 0xb2, 0xff, - 0x9f, 0x93, 0x84, 0x3f, 0x4a, 0xcb, 0xfd, 0xe9, 0x19, 0xef, 0x91, 0xac, - 0xbf, 0xed, 0x67, 0xde, 0x27, 0xdd, 0xd9, 0x65, 0xb0, 0x67, 0xe2, 0xc3, - 0x4b, 0xb9, 0xcd, 0x23, 0x38, 0xa1, 0x4f, 0x52, 0xa8, 0x83, 0x21, 0x84, - 0x66, 0x4f, 0x0f, 0x9b, 0x80, 0x2a, 0xcb, 0xe6, 0x45, 0x22, 0x2c, 0xa6, - 0x1b, 0xc7, 0x18, 0xbc, 0x65, 0xd5, 0x97, 0x03, 0xe5, 0x97, 0xfc, 0xf0, - 0xef, 0x31, 0xaf, 0x1a, 0xcb, 0xdf, 0x3f, 0x96, 0x53, 0xa2, 0x27, 0xf1, - 0xb2, 0x18, 0x01, 0xcd, 0xfe, 0x9e, 0x67, 0xdd, 0x3d, 0x2c, 0xbe, 0xe7, - 0x65, 0x8b, 0x2b, 0x47, 0xab, 0xf9, 0x9d, 0xed, 0x4f, 0x56, 0x5f, 0xd1, - 0x38, 0xfc, 0x6c, 0x59, 0x7f, 0x74, 0xf0, 0x72, 0xc5, 0x95, 0xd3, 0xda, - 0x68, 0xba, 0xf9, 0xf6, 0x01, 0x2c, 0xbb, 0x84, 0xb2, 0xfe, 0xfb, 0x92, - 0x1c, 0x62, 0xcb, 0xe6, 0x07, 0x09, 0x65, 0x4a, 0x68, 0x7a, 0x23, 0x77, - 0x62, 0x23, 0xe1, 0x10, 0x05, 0x84, 0x2e, 0xbf, 0x45, 0x24, 0x52, 0xb2, - 0xe0, 0xe9, 0x65, 0xce, 0x4b, 0x28, 0xcf, 0x4b, 0xa4, 0xc0, 0x17, 0xbf, - 0x3e, 0xd3, 0xe7, 0x59, 0x78, 0xa4, 0x55, 0x97, 0xd9, 0xba, 0xfe, 0x59, - 0x78, 0x9f, 0xa1, 0x3c, 0x0e, 0x0e, 0x5c, 0x36, 0x2c, 0xbf, 0x64, 0x50, - 0x9f, 0x96, 0x50, 0xa7, 0x80, 0x42, 0xf4, 0x14, 0x4a, 0x79, 0xca, 0xfd, - 0x85, 0xd9, 0xe2, 0xcb, 0x41, 0x65, 0xfe, 0xff, 0xf0, 0x73, 0x77, 0x3e, - 0x59, 0x58, 0x79, 0x6e, 0x23, 0x7f, 0xe7, 0xfb, 0xee, 0x18, 0xf1, 0xae, - 0x2c, 0xa9, 0x4f, 0xc3, 0x21, 0xf7, 0xe2, 0x32, 0x71, 0xe1, 0x05, 0xff, - 0xf3, 0xfd, 0xac, 0xcf, 0x9a, 0xf0, 0x27, 0xee, 0x2c, 0xbf, 0xdd, 0x3e, - 0x38, 0x5f, 0x65, 0x97, 0xe7, 0x21, 0xfa, 0x56, 0x5f, 0x7e, 0x64, 0x2a, - 0xca, 0x73, 0xc9, 0xfc, 0x9a, 0x9b, 0x0d, 0x91, 0x54, 0x72, 0x70, 0xda, - 0xe4, 0x33, 0x1a, 0xdc, 0x1e, 0x07, 0x1a, 0x06, 0x43, 0xbc, 0x53, 0xe0, - 0xc2, 0xd3, 0xe6, 0xd3, 0x23, 0xdd, 0x21, 0x8a, 0x3b, 0x6d, 0x38, 0x31, - 0xc3, 0xd2, 0xa1, 0xde, 0x10, 0x3f, 0xc2, 0xa4, 0xa3, 0xd4, 0xe4, 0x2d, - 0x7b, 0x28, 0x78, 0x09, 0xa2, 0x29, 0xee, 0x3e, 0x5f, 0xcf, 0xf3, 0x76, - 0x3f, 0x56, 0x53, 0x74, 0xf2, 0xca, 0x39, 0xbb, 0xf8, 0x6d, 0xf9, 0xde, - 0x7c, 0xb2, 0xff, 0xf3, 0x04, 0x0c, 0x69, 0xd6, 0x8c, 0x9c, 0x2b, 0x2e, - 0x6c, 0xc7, 0x2b, 0x2f, 0x31, 0xfe, 0x59, 0x7b, 0xf7, 0xf2, 0xcb, 0xb7, - 0x9a, 0xcb, 0xed, 0x6b, 0x04, 0x59, 0x7f, 0xd3, 0x06, 0x9f, 0x5a, 0xc1, - 0x16, 0x5e, 0xc6, 0x9b, 0xc7, 0x48, 0xcf, 0x92, 0x0c, 0x1d, 0x88, 0x75, - 0xc6, 0x08, 0x8e, 0xdc, 0x6e, 0x9c, 0x46, 0xd1, 0x8d, 0xd4, 0xa7, 0xda, - 0x71, 0xdd, 0x5f, 0xff, 0xf3, 0x6c, 0xfa, 0x07, 0xd9, 0xbe, 0x43, 0xd3, - 0xac, 0xc2, 0x15, 0x65, 0xff, 0xff, 0xc1, 0x7d, 0x3f, 0xa1, 0x9d, 0xc1, - 0xf1, 0xd9, 0x9a, 0xda, 0x7e, 0x59, 0x7f, 0x60, 0xdd, 0x98, 0x4b, 0x28, - 0xd1, 0x3b, 0xa7, 0x2b, 0xf6, 0x82, 0xee, 0x15, 0x45, 0x28, 0xbf, 0xfe, - 0x7e, 0x96, 0x6d, 0xa9, 0xf1, 0xfb, 0x34, 0xb2, 0xee, 0x34, 0xb2, 0xff, - 0xde, 0x9f, 0xd8, 0xf0, 0xe7, 0x1a, 0x59, 0x79, 0xe0, 0xde, 0x51, 0xfb, - 0x84, 0x5f, 0x9a, 0x71, 0x38, 0x41, 0x8b, 0xfe, 0x83, 0x7c, 0xd0, 0x5d, - 0xc2, 0xa8, 0x93, 0x94, 0xc4, 0x52, 0x3a, 0xed, 0xfe, 0xd3, 0x7c, 0xf4, - 0xc4, 0xeb, 0x29, 0xb9, 0xeb, 0xb9, 0x15, 0x4c, 0x66, 0x08, 0xdb, 0x52, - 0xb6, 0x21, 0x2e, 0xc4, 0x78, 0xc7, 0xad, 0xc8, 0xd0, 0x45, 0x87, 0x70, - 0x63, 0x81, 0x38, 0xe8, 0x77, 0x61, 0xd5, 0x14, 0x39, 0xf5, 0x0b, 0xf6, - 0x5a, 0xef, 0x0f, 0x4b, 0xfb, 0x78, 0xfe, 0xbf, 0x8c, 0x90, 0xa5, 0x51, - 0x72, 0xb4, 0x20, 0xec, 0xa1, 0x80, 0x14, 0x6f, 0x96, 0x03, 0x7e, 0xd0, - 0x5d, 0xc2, 0xa8, 0x87, 0xd7, 0xfe, 0x78, 0x37, 0xcd, 0x05, 0xdc, 0x2a, - 0x89, 0x4d, 0x66, 0xf8, 0x88, 0x06, 0x1a, 0x5f, 0xe6, 0xf9, 0xa0, 0xbb, - 0x85, 0x51, 0x13, 0xaf, 0xc7, 0xde, 0x01, 0x8b, 0x2f, 0x7a, 0x62, 0x59, - 0x51, 0x1e, 0x2f, 0x8a, 0x2f, 0xf0, 0x3e, 0x10, 0x60, 0xef, 0x16, 0x5f, - 0xfe, 0x6d, 0x05, 0x6b, 0x77, 0x61, 0xc7, 0x71, 0xa3, 0x7a, 0x3d, 0x65, - 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x31, 0x5f, 0xb8, 0x7b, 0xe5, 0x8b, 0x2f, - 0x7d, 0x0d, 0x96, 0x5b, 0xd2, 0x79, 0x18, 0x53, 0x7f, 0x42, 0x7f, 0xf1, - 0xb4, 0xb2, 0xff, 0xfe, 0x01, 0x38, 0xb1, 0xba, 0x2c, 0x94, 0x33, 0xc6, - 0xc5, 0x97, 0xd2, 0xc0, 0xfe, 0xb2, 0xff, 0xfa, 0x1b, 0x36, 0x93, 0x1c, - 0xec, 0x1d, 0x46, 0x8d, 0xe8, 0xf5, 0x95, 0xe4, 0x44, 0x7e, 0x47, 0x7f, - 0x67, 0x67, 0x3e, 0xea, 0xcb, 0xff, 0x74, 0x59, 0x28, 0x67, 0x8d, 0x8b, - 0x2f, 0xf8, 0x59, 0x28, 0x67, 0x8d, 0x8b, 0x2f, 0x80, 0x4e, 0x2c, 0x63, - 0xf6, 0xe9, 0xf5, 0xfb, 0x5f, 0x9f, 0xa5, 0x65, 0x30, 0xf8, 0xc0, 0x77, - 0x7c, 0x7f, 0x42, 0x39, 0x59, 0x7e, 0x8d, 0x11, 0x48, 0xd6, 0x59, 0xbc, - 0xae, 0x09, 0x6c, 0x47, 0x03, 0x7c, 0x64, 0x0b, 0xe4, 0x44, 0xcc, 0x2f, - 0xf4, 0x39, 0x48, 0x93, 0x91, 0x83, 0x74, 0x8a, 0x3c, 0xa2, 0xff, 0xf3, - 0x76, 0x3c, 0x1b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0xf2, 0xbe, 0x9d, 0xf3, - 0xe5, 0x97, 0xd2, 0x50, 0xe2, 0xcb, 0xb3, 0x8b, 0x2f, 0x76, 0x58, 0xb2, - 0xfd, 0xe3, 0x2c, 0xde, 0xb2, 0xb4, 0x89, 0xef, 0xc8, 0xdb, 0x64, 0x3d, - 0x16, 0x10, 0x72, 0xfd, 0xc0, 0x0f, 0x18, 0xb2, 0xe8, 0x08, 0xb2, 0xf8, - 0x0f, 0xa8, 0x2c, 0xbc, 0xc0, 0x6e, 0xac, 0xbf, 0x38, 0xfd, 0x3c, 0x59, - 0x7f, 0xb3, 0xde, 0x9f, 0x9f, 0x75, 0x65, 0x9b, 0xca, 0x37, 0xe0, 0x50, - 0x31, 0x8f, 0xc8, 0x88, 0x83, 0x84, 0xf4, 0xdd, 0x38, 0xd6, 0xa3, 0x2f, - 0xa9, 0x64, 0x18, 0xec, 0x55, 0x09, 0xc9, 0x8f, 0x42, 0xf7, 0xb2, 0xa1, - 0x6f, 0xda, 0x0b, 0xb8, 0x55, 0x11, 0x02, 0xff, 0xcf, 0x06, 0xf9, 0xa0, - 0xbb, 0x85, 0x51, 0x2a, 0x2f, 0xed, 0x03, 0xd2, 0x41, 0x59, 0x7f, 0x04, - 0xf3, 0x73, 0xd2, 0xb2, 0xa4, 0xf7, 0x30, 0xb6, 0xcd, 0xf1, 0x1f, 0x8c, - 0x34, 0x28, 0x54, 0x5f, 0xb4, 0x17, 0x70, 0xaa, 0x22, 0x85, 0xfb, 0xc6, - 0xc0, 0x0d, 0x65, 0xe9, 0xff, 0x16, 0x59, 0xbe, 0x1f, 0xc3, 0x9a, 0x08, - 0x51, 0x7f, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0x64, 0xbf, 0x68, 0x2e, - 0xe1, 0x54, 0x4c, 0xab, 0xf4, 0x33, 0xae, 0x15, 0x97, 0xe6, 0xec, 0x78, - 0x37, 0xc3, 0xdb, 0xf8, 0xd2, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x52, 0x2c, - 0x4b, 0x2e, 0x83, 0x16, 0x58, 0x45, 0x96, 0x6f, 0x87, 0xdb, 0xba, 0x69, - 0xa1, 0x00, 0x0b, 0xdf, 0xe6, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x5d, 0x2f, - 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0xb1, 0x57, 0xfd, 0x3c, 0xe4, 0xb3, - 0xb8, 0x15, 0x97, 0xc1, 0x77, 0x0a, 0xa2, 0x7a, 0x5f, 0x98, 0xf0, 0x6f, - 0x81, 0x3e, 0x6d, 0x1c, 0x5e, 0x69, 0xc2, 0xb2, 0xfe, 0xf4, 0x9e, 0x8d, - 0xb6, 0xb2, 0xfd, 0x27, 0x1e, 0x7b, 0x8b, 0x2e, 0x69, 0xbe, 0x1f, 0xc8, - 0x47, 0x5c, 0xc2, 0xff, 0xff, 0xf0, 0xf1, 0xa6, 0xe5, 0x82, 0x8a, 0xfd, - 0xe1, 0x94, 0x52, 0x1d, 0x67, 0xcb, 0x2f, 0xff, 0xec, 0xcf, 0x18, 0x7d, - 0x8d, 0xcb, 0x3b, 0xe3, 0x69, 0x65, 0xff, 0xfa, 0x22, 0x96, 0x37, 0xce, - 0x1b, 0x43, 0x77, 0x15, 0x65, 0xdc, 0xea, 0xcb, 0xb3, 0x8b, 0x2f, 0xfd, - 0x9a, 0xfa, 0x59, 0xe7, 0xcd, 0x2c, 0xbb, 0x9f, 0xac, 0xbf, 0x13, 0xb0, - 0xa5, 0x65, 0xfc, 0x0e, 0x79, 0xe0, 0xde, 0x39, 0x45, 0x43, 0x8b, 0x90, - 0xb3, 0x47, 0xc2, 0x0c, 0x5e, 0xda, 0x77, 0x56, 0x5d, 0xae, 0xac, 0xbf, - 0x67, 0x78, 0x6c, 0x59, 0x7f, 0xfa, 0x7e, 0xee, 0x44, 0x7a, 0xef, 0x01, - 0xba, 0xb2, 0xf3, 0xfc, 0xde, 0x51, 0x5a, 0xe4, 0x04, 0x2f, 0xd2, 0x7a, - 0xc5, 0x4d, 0xfa, 0x8c, 0x03, 0x91, 0x90, 0xd0, 0xd5, 0xc1, 0x93, 0xe7, - 0x65, 0x65, 0x53, 0x75, 0xcb, 0x09, 0x85, 0xc9, 0xcb, 0xbf, 0xbf, 0xfc, - 0x36, 0xfd, 0xe8, 0x36, 0x12, 0x2e, 0x03, 0xe5, 0x97, 0xc7, 0x20, 0x6d, - 0xac, 0xbb, 0xb2, 0xb2, 0xa3, 0xa3, 0x77, 0xf1, 0x2d, 0xf8, 0xfc, 0x4f, - 0x12, 0xcb, 0xf4, 0x30, 0x71, 0xb1, 0x65, 0xf9, 0xcf, 0x6d, 0x4a, 0xcb, - 0xf7, 0x48, 0x0f, 0x1d, 0xac, 0xa6, 0xc2, 0x28, 0xe4, 0x9f, 0xc5, 0x22, - 0x13, 0xdc, 0xcf, 0xd6, 0x5f, 0xb3, 0xa6, 0x0e, 0x2c, 0xba, 0x3a, 0x6d, - 0x4b, 0x2a, 0x3a, 0x3e, 0x16, 0xd4, 0x30, 0xda, 0x13, 0xdf, 0xa3, 0x84, - 0x75, 0xcd, 0x75, 0x65, 0xfe, 0x10, 0xb3, 0x5a, 0x90, 0xac, 0xa8, 0xe8, - 0xf9, 0xe3, 0xb3, 0x4b, 0xed, 0x33, 0xbf, 0x2c, 0xbc, 0x13, 0xe2, 0xcb, - 0xf3, 0x65, 0xb1, 0x30, 0x62, 0xcb, 0xff, 0x36, 0x91, 0xcc, 0x76, 0xd8, - 0x8e, 0xe3, 0xb8, 0xe3, 0x1d, 0xc7, 0x4b, 0x2f, 0xfd, 0x1c, 0x23, 0xb8, - 0xe3, 0x1c, 0x23, 0x83, 0x61, 0xb0, 0xd9, 0x8e, 0x96, 0x5f, 0xfa, 0x38, - 0xb6, 0x91, 0xc2, 0x38, 0xc7, 0x06, 0xc3, 0x6a, 0x8e, 0x11, 0xd2, 0xcb, - 0xfe, 0xf0, 0xe0, 0xde, 0x1c, 0x26, 0xed, 0x84, 0xd4, 0x23, 0x96, 0xe6, - 0xd4, 0xdf, 0x51, 0xd2, 0x7e, 0x6d, 0xaa, 0x38, 0x0b, 0xf3, 0x61, 0x8e, - 0xe4, 0xb2, 0xfe, 0x93, 0xef, 0x65, 0x8b, 0x29, 0xb2, 0x7a, 0xd1, 0xc0, - 0xaa, 0xf0, 0x73, 0xc9, 0x2f, 0x32, 0x78, 0xb2, 0xff, 0xff, 0x74, 0x4d, - 0x4f, 0xa7, 0x02, 0x33, 0x2d, 0x99, 0x30, 0x59, 0x76, 0xa5, 0x65, 0x19, - 0xfb, 0xfe, 0xc3, 0x7a, 0x23, 0x69, 0x65, 0xd9, 0x05, 0x97, 0x67, 0xe6, - 0x6d, 0x1c, 0x7a, 0xfd, 0xdf, 0xc4, 0x28, 0x96, 0x5f, 0xf9, 0xf9, 0x1b, - 0x9c, 0xc8, 0x42, 0x56, 0x5b, 0x8b, 0x2f, 0xe9, 0x09, 0xec, 0xe4, 0xb2, - 0xff, 0xfc, 0x4e, 0x29, 0xea, 0x61, 0x0c, 0xe7, 0xff, 0xca, 0xcb, 0xfc, - 0xfd, 0x06, 0x61, 0x0a, 0xb2, 0x86, 0x9a, 0xde, 0x15, 0x8a, 0x57, 0x12, - 0x06, 0x84, 0x7c, 0x59, 0xfa, 0xb5, 0xff, 0xfd, 0xa3, 0xdc, 0x76, 0x37, - 0x9d, 0x68, 0xf7, 0x02, 0x0d, 0x96, 0x5f, 0x1f, 0x84, 0x62, 0xcb, 0xff, - 0xc6, 0x2c, 0x6c, 0x27, 0xfb, 0x82, 0x8a, 0xeb, 0x28, 0x27, 0xe2, 0x44, - 0x77, 0xff, 0x9f, 0x85, 0x9f, 0xb7, 0xf1, 0x85, 0xf4, 0xb2, 0xe2, 0x69, - 0x65, 0xfb, 0x84, 0x07, 0x82, 0xcb, 0xfb, 0xb1, 0x41, 0xf4, 0x2a, 0xcb, - 0xb0, 0x2b, 0x2f, 0x0f, 0x05, 0x73, 0xc6, 0xe9, 0x85, 0xf7, 0xa5, 0x81, - 0x59, 0x52, 0x8d, 0x9c, 0x17, 0x76, 0xde, 0x99, 0xdf, 0x72, 0x00, 0xfd, - 0x65, 0xe1, 0xe7, 0x96, 0x5e, 0x88, 0xda, 0x59, 0x61, 0xc0, 0xdd, 0x18, - 0xe5, 0xff, 0xd1, 0xa6, 0x4a, 0x59, 0x1a, 0x37, 0xa3, 0xd6, 0x54, 0x9f, - 0x91, 0x13, 0x5f, 0xfb, 0xd9, 0x17, 0x8f, 0xd3, 0xa9, 0x59, 0x73, 0xfc, - 0xb2, 0xff, 0x3f, 0x1f, 0xc0, 0x2d, 0x96, 0x51, 0x1e, 0x50, 0x05, 0xeb, - 0x65, 0xe9, 0x88, 0x42, 0x4c, 0xe3, 0xb1, 0xd3, 0x3b, 0x21, 0xab, 0xe2, - 0x12, 0x8c, 0x93, 0x87, 0x5d, 0x86, 0x80, 0x84, 0x1b, 0x90, 0x88, 0xba, - 0x74, 0xb2, 0xec, 0xf2, 0xcb, 0xe2, 0xcd, 0xf8, 0xb2, 0x82, 0x79, 0xd1, - 0xe2, 0xc2, 0x0b, 0x5f, 0x63, 0x0f, 0xab, 0x2b, 0x47, 0xa9, 0xd3, 0x3b, - 0xc1, 0x06, 0x96, 0x5e, 0xd3, 0xe2, 0xcb, 0xc4, 0xfd, 0x59, 0x78, 0x8f, - 0x8b, 0x2c, 0xd3, 0x60, 0xdb, 0x48, 0xdd, 0xd3, 0xe5, 0x97, 0xda, 0xde, - 0xec, 0x59, 0x7e, 0x2c, 0xec, 0xb4, 0xb2, 0xff, 0xf1, 0xc6, 0xf6, 0x44, - 0x7f, 0x73, 0xd8, 0x15, 0x97, 0xe6, 0x13, 0xf7, 0x8b, 0x2f, 0x4e, 0x69, - 0x65, 0xff, 0xe0, 0xbe, 0x9f, 0xa7, 0xc9, 0x29, 0xfd, 0x65, 0xa1, 0xe3, - 0xe3, 0x10, 0x6e, 0xbe, 0x45, 0x9e, 0xa1, 0x15, 0x52, 0xa9, 0x17, 0x62, - 0x21, 0x8e, 0xe2, 0x88, 0x4a, 0xcc, 0x5b, 0x84, 0x9d, 0x27, 0x04, 0x60, - 0x17, 0xc3, 0x63, 0x5c, 0x59, 0x7c, 0xe6, 0x23, 0x16, 0x5e, 0xe6, 0xe6, - 0xe2, 0xcb, 0xe8, 0xd0, 0xfd, 0x8b, 0x2f, 0x6b, 0x06, 0xb2, 0xff, 0x3f, - 0x66, 0x2e, 0x1b, 0x16, 0x50, 0xa7, 0x9f, 0xe1, 0xcb, 0xfd, 0x30, 0x2c, - 0xfb, 0xb8, 0xb2, 0xfc, 0x13, 0xfb, 0x62, 0x59, 0x7f, 0xf3, 0x86, 0x48, - 0x71, 0x42, 0x75, 0xb2, 0xcb, 0x1f, 0xc7, 0xdd, 0xe2, 0x9b, 0xf8, 0xb4, - 0xee, 0xfd, 0x59, 0x7f, 0xfd, 0xde, 0x0f, 0x58, 0x62, 0xf9, 0xa7, 0x2d, - 0x96, 0x5f, 0x10, 0xf0, 0x55, 0x97, 0xbc, 0x06, 0x2c, 0xbb, 0x78, 0xd6, - 0x53, 0x6b, 0xaa, 0x9e, 0x81, 0x26, 0x11, 0x7c, 0x46, 0x6e, 0xba, 0x23, - 0xf4, 0x28, 0x1c, 0xa3, 0x85, 0x7d, 0x52, 0x01, 0x13, 0x43, 0xb7, 0xec, - 0x22, 0x9d, 0x96, 0x5b, 0xe5, 0x9a, 0x34, 0xd7, 0xee, 0x45, 0x25, 0xb2, - 0xca, 0xc3, 0xcb, 0xe9, 0x15, 0xf8, 0x85, 0x3c, 0xe2, 0xcb, 0xfd, 0xa8, - 0x78, 0xf8, 0x63, 0x59, 0x7c, 0xfd, 0x70, 0xac, 0xa3, 0x3d, 0x46, 0x8c, - 0xef, 0x80, 0xdb, 0xe6, 0xcb, 0x2f, 0xfc, 0xd3, 0xff, 0x81, 0xf3, 0xff, - 0xc5, 0x95, 0x27, 0xd3, 0x85, 0x17, 0xfe, 0xe3, 0xfd, 0x24, 0xe3, 0xc1, - 0x56, 0x51, 0xa6, 0xc9, 0xe7, 0xb2, 0x84, 0x5f, 0x48, 0x2e, 0x9f, 0x2c, - 0xb0, 0xd6, 0x53, 0x0d, 0x39, 0x0b, 0x58, 0x55, 0x97, 0x8b, 0xf9, 0x59, - 0x71, 0x0e, 0x31, 0xae, 0xe0, 0x95, 0xfd, 0x9c, 0x69, 0xcb, 0x65, 0x95, - 0x88, 0xaa, 0x74, 0xe2, 0x2e, 0xbf, 0x68, 0x1c, 0x70, 0xac, 0xbd, 0xc1, - 0xca, 0xcb, 0xfe, 0xe7, 0x70, 0x20, 0xfd, 0xf1, 0x65, 0x0a, 0x7a, 0xae, - 0x39, 0x52, 0x89, 0xf0, 0x3e, 0xdb, 0x8b, 0x2d, 0x8b, 0x2c, 0xc3, 0x34, - 0x3b, 0x82, 0x37, 0xfd, 0x84, 0xf0, 0xe4, 0x1a, 0xd9, 0x65, 0xf4, 0x38, - 0x64, 0xb2, 0xff, 0xc1, 0x27, 0xf3, 0xfd, 0x9f, 0x75, 0x65, 0x41, 0x13, - 0x5f, 0x1d, 0x00, 0x86, 0xfb, 0x58, 0xec, 0x59, 0x52, 0x7a, 0x4e, 0x63, - 0x7f, 0x9f, 0x50, 0x90, 0x98, 0x56, 0x54, 0xae, 0x86, 0xe4, 0x70, 0xa7, - 0x18, 0x97, 0xa1, 0x98, 0xe8, 0x65, 0x18, 0xa8, 0x08, 0x2f, 0xff, 0x0a, - 0xf9, 0xf7, 0x7b, 0x39, 0xbc, 0xc2, 0xb2, 0xff, 0x16, 0x73, 0xae, 0xe4, - 0xb2, 0x85, 0x3f, 0xcd, 0xe9, 0x97, 0xff, 0xfe, 0x3d, 0x6a, 0x44, 0xe9, - 0x90, 0x21, 0x9c, 0x18, 0x1f, 0x50, 0x59, 0x7f, 0xff, 0x16, 0x0b, 0x85, - 0x1b, 0x9d, 0xf6, 0x30, 0x49, 0xfd, 0x65, 0x4a, 0x32, 0xb1, 0xb2, 0xff, - 0x01, 0xf8, 0x71, 0x1b, 0x4b, 0x28, 0xd3, 0x44, 0xf4, 0x3b, 0xc8, 0x86, - 0xf7, 0xa4, 0x96, 0x5e, 0xfb, 0xac, 0x59, 0x41, 0x37, 0x2e, 0x37, 0x7c, - 0x3d, 0x1b, 0x4b, 0x2f, 0x60, 0x06, 0xb2, 0xb6, 0x37, 0xfc, 0x23, 0xbc, - 0x65, 0xd5, 0x97, 0xf3, 0xef, 0x38, 0x8d, 0xa5, 0x94, 0xc3, 0xcb, 0x10, - 0x6e, 0xff, 0x03, 0x5b, 0x32, 0x73, 0xab, 0x2f, 0xfb, 0x92, 0xc1, 0xfa, - 0x48, 0x2b, 0x2e, 0x32, 0xc3, 0xec, 0x14, 0xd2, 0xa5, 0x17, 0x63, 0x84, - 0x7d, 0xe8, 0x03, 0x8b, 0x2f, 0xe1, 0xbe, 0xbf, 0x8f, 0xc5, 0x97, 0xbc, - 0x26, 0xf5, 0x97, 0xfd, 0x2c, 0x2c, 0x0e, 0x8f, 0xf5, 0x96, 0x1a, 0xcb, - 0xdc, 0xe6, 0x2c, 0xb0, 0xe4, 0xd6, 0xb8, 0x8d, 0x41, 0x15, 0x5c, 0x20, - 0x03, 0x2d, 0xfd, 0xe7, 0x30, 0xe1, 0x2c, 0xbf, 0xd0, 0xc3, 0xe7, 0x01, - 0x2b, 0x2f, 0xe1, 0xe1, 0x43, 0xd8, 0xb2, 0xa4, 0xf7, 0x8c, 0xca, 0xff, - 0xdd, 0x96, 0x7c, 0x13, 0xfb, 0x34, 0xb2, 0xfd, 0xec, 0xd1, 0xf1, 0x65, - 0xe2, 0x38, 0x7c, 0x7c, 0xfd, 0x40, 0xbf, 0xf3, 0xe7, 0xf2, 0x4e, 0x3c, - 0x15, 0x65, 0x4a, 0x70, 0x1b, 0x42, 0x26, 0x10, 0x87, 0xfc, 0xce, 0xff, - 0xf1, 0x76, 0x3f, 0x22, 0x83, 0xeb, 0x61, 0xca, 0xcb, 0xff, 0xf8, 0xa1, - 0x3d, 0x7f, 0x70, 0xdf, 0xbf, 0x88, 0x50, 0x59, 0x7d, 0xde, 0xe6, 0xea, - 0xcb, 0xfe, 0x9c, 0x87, 0xb0, 0xe1, 0xc5, 0x95, 0xc3, 0xdc, 0xe9, 0x2d, - 0xf8, 0x71, 0x78, 0x1b, 0xab, 0x2b, 0xe3, 0xcf, 0x22, 0x2a, 0xc4, 0xdd, - 0x8d, 0x39, 0xe3, 0x06, 0xbf, 0x68, 0x8f, 0x05, 0x59, 0x73, 0x20, 0xb2, - 0xff, 0x9f, 0x02, 0x3c, 0xd7, 0xe6, 0xb2, 0xda, 0x19, 0xe7, 0xf8, 0x5e, - 0xa5, 0x13, 0xc6, 0xed, 0x7f, 0xd3, 0x0e, 0x14, 0xfe, 0xec, 0x59, 0x7f, - 0x1e, 0xb6, 0x07, 0xf8, 0xb2, 0xfd, 0xd9, 0xc2, 0x0a, 0xcb, 0xef, 0xbb, - 0x2c, 0x59, 0x42, 0x9e, 0x4f, 0xc4, 0xd7, 0xfd, 0x01, 0x3a, 0x7e, 0x32, - 0xea, 0xcb, 0xff, 0xd3, 0x9f, 0x77, 0xd3, 0xd2, 0x9f, 0xf8, 0xb2, 0xe9, - 0xfd, 0x65, 0xde, 0x62, 0xca, 0x09, 0xae, 0xe0, 0xbd, 0x6c, 0x9b, 0x71, - 0xbc, 0x39, 0x27, 0x4e, 0x40, 0xf1, 0x7f, 0xf8, 0x80, 0xf0, 0x60, 0x3b, - 0xe9, 0x03, 0x6d, 0x65, 0xed, 0x8f, 0x4b, 0x2f, 0xfb, 0x87, 0xf7, 0x62, - 0x84, 0xfc, 0xb2, 0xfd, 0xce, 0xf3, 0x3a, 0xb2, 0xb8, 0x7c, 0x80, 0x3c, - 0xbf, 0x80, 0xdb, 0x38, 0x8d, 0xa5, 0x97, 0x67, 0x96, 0x5e, 0x69, 0xa6, - 0x92, 0x5f, 0xfe, 0xe9, 0xf7, 0x9e, 0x38, 0xf1, 0x35, 0x30, 0x48, 0xdc, - 0xd0, 0x50, 0xa9, 0xb6, 0x1c, 0x20, 0x5c, 0x8b, 0xa6, 0x60, 0x36, 0xba, - 0x23, 0x59, 0x7f, 0xff, 0x42, 0x4b, 0x3b, 0xe3, 0xce, 0xcb, 0x0f, 0x50, - 0x59, 0x63, 0xd1, 0xf7, 0xf4, 0x5e, 0xa0, 0xcc, 0x04, 0x1b, 0x6e, 0x2f, - 0x86, 0x32, 0x8f, 0x89, 0xcc, 0x76, 0x28, 0x6d, 0xea, 0x3b, 0x06, 0x47, - 0x7f, 0xe8, 0x69, 0x7e, 0x42, 0x51, 0xd3, 0x71, 0x2b, 0xb1, 0xdd, 0x6f, - 0x85, 0xf5, 0xff, 0x7d, 0x0e, 0x73, 0x06, 0xfd, 0x59, 0x7f, 0x6b, 0xb2, - 0x06, 0xdf, 0x16, 0x5b, 0x16, 0x5f, 0x68, 0x9f, 0x7a, 0xcb, 0xd8, 0xe4, - 0xb2, 0xe7, 0x69, 0x65, 0x4a, 0x34, 0x06, 0x74, 0x29, 0x91, 0x88, 0x68, - 0x8d, 0x83, 0x57, 0xbf, 0xe6, 0x2c, 0xbf, 0xcf, 0x08, 0xbc, 0x7f, 0x75, - 0x65, 0x9d, 0x65, 0xe9, 0xf7, 0x16, 0x57, 0xc6, 0xb0, 0xc4, 0x2f, 0xfd, - 0x21, 0x8d, 0x85, 0x20, 0x6d, 0xf1, 0x65, 0xff, 0xfc, 0xfd, 0x3f, 0x3b, - 0x23, 0x67, 0x27, 0x35, 0x84, 0xb2, 0xff, 0xee, 0x3f, 0xdc, 0xe6, 0x17, - 0xf1, 0xf8, 0xb2, 0xff, 0xf1, 0x87, 0x93, 0x1f, 0x9d, 0x84, 0x33, 0x8b, - 0x28, 0x69, 0xd9, 0x69, 0x85, 0x84, 0x24, 0x87, 0xd5, 0x9d, 0xe9, 0x17, - 0xda, 0xf6, 0x12, 0xca, 0x33, 0xfa, 0x75, 0x9b, 0xf8, 0x62, 0x40, 0xff, - 0xe2, 0xcb, 0xf8, 0xa3, 0x09, 0xc9, 0xc5, 0x97, 0xdd, 0x9f, 0x9a, 0x59, - 0x41, 0x55, 0xd9, 0xe9, 0x49, 0x2e, 0x41, 0xf9, 0x83, 0x45, 0xf7, 0xff, - 0xee, 0x80, 0x4e, 0x94, 0xfe, 0xfd, 0xfc, 0x42, 0x82, 0xcb, 0xf8, 0xfb, - 0xdc, 0x26, 0x2c, 0xb9, 0xaf, 0x2c, 0xbf, 0xfd, 0x24, 0x11, 0x34, 0xff, - 0x9f, 0x7d, 0x2b, 0x2f, 0x9f, 0x67, 0x25, 0x97, 0xee, 0xfb, 0x3f, 0x75, - 0x95, 0xf1, 0xe4, 0x91, 0x0d, 0xfc, 0xc8, 0xde, 0xcf, 0xdd, 0x65, 0xfc, - 0xd7, 0x33, 0x0b, 0x65, 0x95, 0x28, 0x80, 0xc2, 0x23, 0x30, 0xbe, 0x9e, - 0x4e, 0xf5, 0x97, 0xff, 0x49, 0x3f, 0x61, 0x22, 0xbb, 0x5d, 0x59, 0x5e, - 0x3e, 0x70, 0x11, 0xde, 0x8c, 0xe1, 0x59, 0x52, 0xaa, 0x12, 0x0b, 0x21, - 0x2c, 0xfc, 0x63, 0x91, 0x89, 0xf6, 0x12, 0x9b, 0x84, 0x57, 0xf4, 0x04, - 0x63, 0x0f, 0x75, 0x65, 0xfd, 0x0e, 0x74, 0xe7, 0xe5, 0x97, 0xcc, 0x12, - 0x49, 0x65, 0xf1, 0x8f, 0x18, 0xb2, 0xf9, 0xdc, 0xbf, 0x59, 0x66, 0xa4, - 0xf0, 0x85, 0x21, 0xb8, 0x0c, 0x59, 0x7f, 0x9a, 0x78, 0xb2, 0x13, 0xba, - 0xb2, 0xfd, 0xb8, 0x08, 0x8e, 0x25, 0x94, 0x68, 0xa4, 0xf1, 0x4f, 0x05, - 0xc0, 0x6f, 0x7f, 0xc6, 0xcf, 0x7c, 0xfb, 0xbd, 0xe2, 0xcb, 0xf8, 0x2f, - 0xef, 0x60, 0xd6, 0x54, 0x9f, 0x50, 0x0f, 0x6f, 0xf4, 0xbe, 0xbc, 0xd3, - 0x8d, 0x25, 0xcd, 0x34, 0x92, 0x86, 0x79, 0x8d, 0x19, 0xde, 0xe9, 0xee, - 0xa4, 0x6e, 0x68, 0xef, 0xef, 0x82, 0x7c, 0xf1, 0xac, 0xbe, 0x04, 0x01, - 0xd5, 0x95, 0x2a, 0xc8, 0x3e, 0x32, 0x32, 0xe8, 0xa3, 0x08, 0x64, 0x29, - 0xb9, 0x09, 0x0e, 0x98, 0xc7, 0x97, 0x5f, 0x10, 0xfc, 0xeb, 0x2f, 0x63, - 0xee, 0xac, 0xac, 0x37, 0xfa, 0x21, 0xbf, 0xb3, 0xf2, 0x9e, 0xf1, 0x65, - 0xcd, 0x71, 0x65, 0xb1, 0x65, 0xbe, 0xc3, 0x51, 0xd1, 0x8b, 0xef, 0xa0, - 0xd8, 0xf9, 0x65, 0xff, 0xdc, 0xe0, 0xca, 0x47, 0xe9, 0xef, 0x16, 0x54, - 0xa2, 0x53, 0x09, 0xc0, 0x53, 0x7f, 0xbc, 0x6f, 0x0e, 0x9e, 0xcb, 0x2f, - 0xfd, 0xd3, 0xd3, 0xff, 0xb9, 0xdc, 0xf2, 0xcb, 0xfe, 0x36, 0x8b, 0x3a, - 0x60, 0xfd, 0x65, 0xfc, 0x6c, 0x38, 0x8d, 0xa5, 0x97, 0xe3, 0xd0, 0xe7, - 0x65, 0x97, 0xf9, 0xf5, 0xde, 0x11, 0x8a, 0xb2, 0xff, 0x9d, 0xae, 0xe7, - 0x87, 0x2c, 0x59, 0x7f, 0xfb, 0x98, 0x2e, 0x03, 0x4e, 0x39, 0xf4, 0xac, - 0xbe, 0x10, 0x7e, 0x95, 0x95, 0x2a, 0xbc, 0xe1, 0x0e, 0x9f, 0x8b, 0x8c, - 0xcf, 0x75, 0x0b, 0x47, 0x3e, 0x2e, 0x72, 0x82, 0x33, 0xe1, 0xc8, 0x12, - 0x6f, 0x44, 0xfa, 0x59, 0x7f, 0xdd, 0xf6, 0x7e, 0x09, 0xf8, 0x45, 0x97, - 0xff, 0xfe, 0x73, 0xec, 0xf8, 0xb3, 0xbe, 0x31, 0x21, 0x3b, 0xcc, 0xbf, - 0x59, 0x7e, 0x0e, 0x32, 0x78, 0xb2, 0xf0, 0x08, 0x6b, 0x2a, 0x53, 0x08, - 0xf8, 0x77, 0xc7, 0x8e, 0xd9, 0xf9, 0x3d, 0xec, 0x3d, 0x2c, 0xbf, 0xd0, - 0x36, 0x7b, 0xe7, 0xdd, 0x59, 0x7c, 0xfa, 0x98, 0x2c, 0xbf, 0xe7, 0xfa, - 0x37, 0xdc, 0xc2, 0x15, 0x65, 0x6c, 0x8b, 0x0f, 0x86, 0xd8, 0x6f, 0xd2, - 0x1b, 0xef, 0x6e, 0x9b, 0x16, 0x5e, 0x29, 0xfd, 0x65, 0x46, 0x3c, 0x1e, - 0x12, 0xdf, 0xdd, 0x76, 0x32, 0x58, 0xb2, 0xe9, 0x66, 0x1e, 0x8f, 0xc4, - 0x97, 0xfe, 0x9f, 0xbd, 0x2c, 0x0f, 0x8e, 0x25, 0x97, 0xe7, 0xff, 0xfe, - 0xf9, 0x65, 0x4a, 0xa6, 0x3c, 0x87, 0xd9, 0xc3, 0x47, 0x45, 0xae, 0x81, - 0x7f, 0x10, 0xf9, 0xc3, 0x62, 0xcb, 0xf0, 0x5f, 0x6c, 0x69, 0x65, 0xfa, - 0x74, 0x3c, 0x25, 0x95, 0x87, 0xf8, 0x45, 0xbd, 0x29, 0xbf, 0x4c, 0x79, - 0xf8, 0x0b, 0x2f, 0x8b, 0x47, 0xe5, 0x97, 0xa3, 0xe4, 0x45, 0x97, 0xff, - 0x4f, 0xf3, 0xe6, 0x13, 0x8b, 0xe3, 0x59, 0x68, 0xed, 0x65, 0xfd, 0xe9, - 0x26, 0x9f, 0xe5, 0x97, 0xfe, 0x28, 0xa7, 0x0b, 0x6c, 0xfb, 0xab, 0x28, - 0x67, 0xda, 0xe5, 0xd7, 0xff, 0x18, 0xfd, 0x21, 0x9f, 0xdc, 0xbf, 0x59, - 0x7c, 0xd3, 0xcb, 0x16, 0x5f, 0x9f, 0x9b, 0x1e, 0x96, 0x5f, 0x68, 0xc1, - 0xb2, 0xe2, 0xf5, 0x5f, 0xfb, 0x0f, 0x7e, 0x16, 0x0d, 0xe0, 0xb8, 0xbd, - 0x57, 0xe7, 0x2f, 0xa0, 0xdc, 0x67, 0xf4, 0xd1, 0x85, 0x0d, 0x1c, 0x8c, - 0x85, 0x15, 0xf8, 0x5e, 0xbb, 0x92, 0xcb, 0xc4, 0x0d, 0x96, 0x53, 0x9e, - 0x27, 0x49, 0xe8, 0xd1, 0x1a, 0x4d, 0x95, 0x2a, 0xde, 0x06, 0x5b, 0x85, - 0x46, 0x43, 0xa2, 0x06, 0x22, 0x3c, 0x20, 0xf8, 0x43, 0xd4, 0x40, 0x47, - 0x27, 0x7f, 0x89, 0xd9, 0xe9, 0xc1, 0xac, 0xba, 0x3a, 0x8e, 0x56, 0x5e, - 0x39, 0xd2, 0xca, 0x8e, 0xcd, 0xe7, 0x88, 0x6f, 0xfe, 0xd1, 0xb2, 0x77, - 0x4b, 0x19, 0xf4, 0xac, 0xa8, 0x1f, 0x64, 0x44, 0xd7, 0x89, 0xc6, 0xb2, - 0xf7, 0x36, 0x75, 0x94, 0x66, 0xe0, 0x41, 0xbb, 0xfd, 0xd3, 0xd4, 0x87, - 0x37, 0xac, 0xb9, 0x86, 0xb2, 0xa4, 0xf2, 0xb6, 0x34, 0xbf, 0x63, 0x34, - 0xff, 0x2c, 0xa1, 0xa2, 0xd4, 0x9a, 0xf8, 0x45, 0x7e, 0x86, 0x6f, 0x98, - 0x2c, 0xbd, 0x9a, 0x95, 0x94, 0x15, 0x4b, 0x9f, 0x43, 0x64, 0xe3, 0x10, - 0xd1, 0x7f, 0x8a, 0x6f, 0x3e, 0xb6, 0x59, 0x4b, 0x2c, 0x41, 0x35, 0x11, - 0x0e, 0xdf, 0xde, 0x78, 0xb5, 0x3b, 0x2c, 0xbf, 0x8f, 0xee, 0x7b, 0x02, - 0xb2, 0xfe, 0x9d, 0xf9, 0xef, 0x4a, 0xcb, 0xf6, 0x77, 0x99, 0x12, 0xcf, - 0x1a, 0xeb, 0xff, 0xff, 0xf1, 0xe7, 0xdc, 0x3c, 0x6a, 0x36, 0x7d, 0x0e, - 0x1f, 0x78, 0x7f, 0x0c, 0x60, 0xd9, 0x65, 0xff, 0xf8, 0xc8, 0x71, 0x84, - 0xe9, 0xf1, 0xcc, 0x81, 0x05, 0x97, 0xcf, 0xb7, 0x8d, 0x65, 0x62, 0x7d, - 0x66, 0x4f, 0x11, 0x79, 0x2e, 0x74, 0xe3, 0x7c, 0x22, 0x63, 0xd5, 0xaf, - 0x03, 0x50, 0x59, 0x7f, 0xe6, 0x38, 0xf5, 0x3d, 0xf6, 0x0d, 0x65, 0xfc, - 0xfd, 0xfc, 0x42, 0x82, 0xcb, 0xfe, 0x1b, 0xbb, 0x5f, 0xbf, 0x78, 0xb2, - 0xff, 0xc3, 0x9e, 0xbc, 0x33, 0x7b, 0x8d, 0x65, 0xf8, 0xf6, 0xd8, 0xf4, - 0xb2, 0xfd, 0x24, 0x40, 0x62, 0xcb, 0x43, 0xa7, 0x9e, 0x21, 0x4d, 0x46, - 0x4d, 0xee, 0x47, 0x76, 0x3e, 0x14, 0xbd, 0x87, 0x4f, 0x08, 0xeb, 0xfd, - 0x30, 0x36, 0xed, 0x34, 0xd2, 0x4b, 0xb3, 0xcb, 0x2f, 0x0b, 0x83, 0x59, - 0x7f, 0x79, 0xf4, 0x19, 0xea, 0xcb, 0x9c, 0x6b, 0x2f, 0xed, 0x3f, 0x3c, - 0xf0, 0x59, 0x6f, 0x96, 0x5c, 0x6d, 0x2c, 0xb8, 0x4f, 0xd6, 0x54, 0x46, - 0xc3, 0xf1, 0x7b, 0xd0, 0x71, 0xac, 0xb9, 0xa6, 0x96, 0x54, 0x11, 0xbb, - 0x82, 0xc1, 0x2d, 0x34, 0x2e, 0x11, 0xb4, 0x39, 0x7b, 0xa2, 0x7e, 0x91, - 0xb9, 0xeb, 0x5f, 0xec, 0x61, 0x8f, 0x60, 0x71, 0x65, 0xff, 0xfd, 0xfc, - 0x7e, 0x6a, 0x7a, 0x61, 0x31, 0xed, 0x25, 0x2b, 0x2f, 0x67, 0xdd, 0x59, - 0x6c, 0x23, 0xfa, 0xe2, 0xed, 0x79, 0x1a, 0x9c, 0x85, 0x75, 0xff, 0x18, - 0xf1, 0x87, 0x11, 0xb4, 0xb2, 0xff, 0xff, 0xa1, 0xe9, 0x1c, 0x9f, 0xdf, - 0xeb, 0x07, 0x3a, 0x3f, 0xbf, 0x59, 0x7f, 0xf9, 0xc7, 0x98, 0x42, 0xf3, - 0x98, 0x5f, 0xac, 0xbe, 0xf0, 0x1c, 0x59, 0x47, 0x5e, 0x1c, 0xb1, 0xa6, - 0xf1, 0x4f, 0x96, 0x5f, 0xff, 0xd0, 0x7e, 0x72, 0x73, 0x60, 0x40, 0x7a, - 0x27, 0x69, 0x65, 0xa4, 0x68, 0xbd, 0x15, 0x18, 0xc6, 0xeb, 0x8a, 0x81, - 0x7b, 0x1e, 0x85, 0xf4, 0x3c, 0x7c, 0x59, 0x7f, 0x3e, 0xd9, 0x84, 0x2a, - 0xcb, 0xf8, 0xd8, 0x1c, 0x20, 0xac, 0xbe, 0x7d, 0xb9, 0xe5, 0x95, 0xa3, - 0xcf, 0xe1, 0x65, 0x46, 0x5d, 0x89, 0x80, 0xb0, 0xc7, 0x7e, 0x8d, 0x80, - 0xe5, 0x85, 0x91, 0x5f, 0x08, 0xba, 0xf9, 0x7e, 0xde, 0x27, 0x8f, 0xab, - 0x2f, 0xfb, 0x3b, 0xe9, 0xd7, 0x70, 0x2b, 0x2e, 0x15, 0xd6, 0x5f, 0xbe, - 0xc2, 0x04, 0x4b, 0x2e, 0xd4, 0x4b, 0x2e, 0xd4, 0xac, 0xa9, 0x35, 0xd8, - 0x31, 0x52, 0xcb, 0xe4, 0xc9, 0x4d, 0x47, 0x1f, 0x47, 0x97, 0xde, 0x77, - 0x07, 0xf8, 0x41, 0x11, 0x5f, 0x4e, 0x00, 0x2f, 0x1e, 0xaf, 0x7f, 0xde, - 0x07, 0x7d, 0x9a, 0x3e, 0x2c, 0xbd, 0xc7, 0x89, 0x65, 0xff, 0xb3, 0x7c, - 0x8c, 0xb3, 0xb9, 0xfa, 0xcb, 0xb0, 0x96, 0x5b, 0x3e, 0x3d, 0x4d, 0xe7, - 0xf7, 0xef, 0x76, 0x60, 0xc5, 0x97, 0x09, 0xfa, 0xca, 0xd1, 0xf5, 0xf8, - 0xab, 0xf2, 0x8b, 0xff, 0x8c, 0x6f, 0xd3, 0xe6, 0x43, 0x3c, 0xb2, 0xe7, - 0xdc, 0x59, 0x7b, 0x3e, 0xea, 0xcb, 0x67, 0x8f, 0xe3, 0xa8, 0x5b, 0x83, - 0x37, 0xff, 0x7e, 0x21, 0x40, 0xb3, 0xbd, 0xcd, 0x96, 0x5f, 0xb4, 0xfb, - 0xf7, 0x1d, 0x65, 0x0d, 0x53, 0x37, 0xc7, 0x3a, 0x86, 0xe1, 0x42, 0xa3, - 0xa6, 0xfb, 0xd1, 0xaf, 0xdb, 0xf0, 0x8c, 0x55, 0x97, 0x88, 0x3b, 0x8b, - 0x2f, 0xff, 0xef, 0xb8, 0x4f, 0xcf, 0x64, 0x33, 0xc7, 0xaf, 0x3a, 0xcb, - 0xec, 0xfb, 0x6d, 0xeb, 0x2f, 0xf8, 0x87, 0x84, 0x08, 0x67, 0x16, 0x5f, - 0x7d, 0xc0, 0x7e, 0xb2, 0xe2, 0x0a, 0xca, 0x93, 0x76, 0xe4, 0xb7, 0xf8, - 0x4e, 0x96, 0x6c, 0x7a, 0x59, 0x7c, 0xfb, 0x98, 0x4b, 0x2f, 0x1f, 0x0d, - 0x65, 0x70, 0xdf, 0x74, 0x8e, 0xa5, 0x3e, 0xcc, 0x29, 0x31, 0xfd, 0x2d, - 0x39, 0x37, 0xee, 0x44, 0x3e, 0x07, 0x2b, 0xff, 0xb6, 0x91, 0x3f, 0x04, - 0x20, 0xfe, 0x02, 0xcb, 0xfe, 0xf1, 0x8c, 0xdb, 0x72, 0x46, 0xb2, 0xff, - 0x3c, 0x30, 0x7a, 0xe7, 0x16, 0x5f, 0x4e, 0x08, 0x35, 0x97, 0xff, 0xf4, - 0x0a, 0x73, 0x98, 0x5d, 0xf6, 0x0c, 0x1d, 0xe2, 0xcb, 0xfc, 0x29, 0xf5, - 0xfc, 0xd6, 0xea, 0xcb, 0xfa, 0x76, 0x8f, 0xcd, 0x4a, 0xcb, 0xfd, 0x91, - 0x7b, 0x38, 0xe4, 0xb2, 0xff, 0xc3, 0xcf, 0xa1, 0xce, 0xf1, 0xff, 0x59, - 0x5a, 0x3f, 0x30, 0x19, 0x5f, 0x73, 0x98, 0xd2, 0xcb, 0xfc, 0xd0, 0xfd, - 0x9b, 0xf3, 0x4b, 0x2f, 0xfe, 0x36, 0x46, 0x0f, 0x8f, 0xc1, 0x3e, 0x2c, - 0xbf, 0xd9, 0x0c, 0x03, 0x6d, 0xc9, 0x65, 0xff, 0xd3, 0x0f, 0x4f, 0xdd, - 0x9f, 0x3e, 0xea, 0xca, 0x94, 0x7c, 0x68, 0xdb, 0xc8, 0xc4, 0x6b, 0x7f, - 0xff, 0x8c, 0xb3, 0xee, 0x8a, 0xfd, 0xf4, 0xfb, 0x37, 0xbc, 0x16, 0x50, - 0xd5, 0xb1, 0x84, 0xcf, 0xe2, 0x23, 0x59, 0x88, 0xe3, 0x50, 0xa3, 0xf1, - 0x17, 0x63, 0x1b, 0xde, 0x77, 0x71, 0x4a, 0xcb, 0xf8, 0x7c, 0xf4, 0xb5, - 0xc5, 0x97, 0xfc, 0x33, 0xd4, 0xef, 0xff, 0x90, 0x59, 0x74, 0xb1, 0x65, - 0x49, 0xe9, 0x78, 0xf2, 0xbc, 0x8b, 0x77, 0x15, 0xec, 0x20, 0x6f, 0xfb, - 0x8f, 0xe3, 0x18, 0x4f, 0x65, 0x97, 0xf9, 0xe1, 0x83, 0xe7, 0x25, 0x65, - 0xfd, 0xdf, 0x4b, 0x22, 0x25, 0x95, 0x28, 0x99, 0xc3, 0x9f, 0xcc, 0xaa, - 0x57, 0x77, 0xb2, 0x5c, 0xeb, 0xc6, 0x8a, 0x08, 0x66, 0xde, 0x92, 0xea, - 0xcb, 0xff, 0xe9, 0xd6, 0x6f, 0x3e, 0xfe, 0x02, 0xe1, 0x8d, 0x65, 0xf8, - 0xf5, 0x08, 0xa5, 0x65, 0xff, 0xde, 0xc2, 0x71, 0x7c, 0x7d, 0x81, 0xac, - 0xb1, 0xf8, 0xfa, 0xf8, 0x51, 0x7f, 0xfe, 0x30, 0x04, 0x4f, 0xbf, 0x7e, - 0x3e, 0x8f, 0xef, 0xd6, 0x5f, 0xc0, 0xef, 0x27, 0xfd, 0x96, 0x5f, 0x3e, - 0xa6, 0x0b, 0x2f, 0xff, 0xe3, 0x27, 0x32, 0xff, 0x53, 0xe7, 0x3e, 0xcf, - 0x96, 0x50, 0xd1, 0x50, 0x46, 0x1d, 0x21, 0xbf, 0x1f, 0x78, 0x0d, 0x96, - 0x5f, 0xfe, 0x23, 0xff, 0xbf, 0x04, 0xf4, 0x24, 0x8d, 0x65, 0xff, 0x78, - 0x41, 0x83, 0xbc, 0xfe, 0x56, 0x54, 0xa2, 0x21, 0xd3, 0x28, 0x6a, 0x80, - 0xce, 0x30, 0x26, 0x17, 0xf6, 0x16, 0x37, 0xf3, 0xfe, 0xe6, 0x33, 0x59, - 0x7a, 0x59, 0x2b, 0x2b, 0x47, 0x8f, 0xbc, 0xb2, 0xfd, 0xa0, 0x07, 0xd2, - 0xb2, 0xfe, 0xd4, 0x50, 0x3e, 0xf1, 0x65, 0x8a, 0x4f, 0x5f, 0x0a, 0x2f, - 0xb3, 0x53, 0xc5, 0x97, 0xfa, 0x27, 0x89, 0xfe, 0x87, 0x16, 0x5f, 0xcf, - 0xb1, 0xc4, 0x6d, 0x2c, 0xbf, 0xff, 0xce, 0xd7, 0x73, 0xd1, 0x87, 0x91, - 0x41, 0xf5, 0xb7, 0x80, 0xb2, 0xa5, 0x1d, 0x98, 0x42, 0xe6, 0xc4, 0x5f, - 0x7e, 0xc2, 0xdb, 0x1a, 0x59, 0x7e, 0x76, 0x78, 0xda, 0x59, 0x5b, 0x1e, - 0x80, 0xca, 0x2f, 0xfa, 0x76, 0x12, 0x1e, 0x03, 0xec, 0xb2, 0xff, 0x8c, - 0x39, 0x13, 0x96, 0xcd, 0x85, 0x95, 0x88, 0x9c, 0xd1, 0x19, 0x1e, 0x5a, - 0x3b, 0x59, 0x7e, 0x9d, 0x6b, 0x3c, 0xb2, 0xf3, 0xc3, 0xab, 0x2f, 0xff, - 0xa4, 0x56, 0xb0, 0xf7, 0xb4, 0xe3, 0x9c, 0x62, 0xcb, 0xf8, 0x9c, 0x59, - 0x64, 0xac, 0xa6, 0xc2, 0x35, 0xe0, 0x2c, 0xc2, 0x72, 0x1c, 0x02, 0x95, - 0xed, 0x85, 0x15, 0x65, 0xfd, 0x00, 0x0d, 0xb8, 0xbb, 0xab, 0x2f, 0xe6, - 0x3f, 0xd1, 0xd6, 0x79, 0x65, 0xe6, 0x9f, 0x8b, 0x2f, 0xe6, 0x4e, 0x17, - 0x78, 0xb2, 0xfd, 0x3b, 0x64, 0x0d, 0x65, 0xe1, 0x06, 0xdf, 0x11, 0x47, - 0xe3, 0x3e, 0x0e, 0xf4, 0xb2, 0xa2, 0x4c, 0xa7, 0xd0, 0xd8, 0xbf, 0xfc, - 0x7c, 0xc1, 0xc7, 0xb8, 0xe4, 0xa7, 0xf5, 0x95, 0x29, 0xdf, 0x0e, 0x34, - 0x90, 0x95, 0x5f, 0xff, 0x8c, 0x32, 0xce, 0x3e, 0xb1, 0x80, 0x0b, 0xc1, - 0x65, 0xc7, 0xbd, 0x65, 0xfe, 0x92, 0x7f, 0x00, 0xb6, 0x59, 0x7f, 0xe3, - 0x16, 0x46, 0x64, 0xfa, 0x15, 0x65, 0xff, 0xec, 0xfb, 0xbf, 0x74, 0xda, - 0xc1, 0xbc, 0x16, 0x56, 0x22, 0x2b, 0xf3, 0xeb, 0x79, 0x65, 0x41, 0x35, - 0xcd, 0xd5, 0x46, 0x0c, 0x14, 0x2f, 0x00, 0x47, 0x78, 0x72, 0xc5, 0x97, - 0xf7, 0xf9, 0x14, 0xfd, 0xc5, 0x97, 0xff, 0xf8, 0x9d, 0xaf, 0x63, 0x1d, - 0xae, 0xf3, 0x1a, 0x19, 0xb1, 0x65, 0xfe, 0xee, 0x6b, 0x22, 0x91, 0x56, - 0x56, 0x23, 0x1a, 0x23, 0x0e, 0xb1, 0x5f, 0xe1, 0x4b, 0x36, 0xd8, 0x04, - 0xb2, 0xa5, 0x34, 0x7c, 0x87, 0x10, 0x0c, 0x2f, 0xef, 0x3c, 0x27, 0x90, - 0x59, 0x7f, 0xd9, 0xe3, 0xfb, 0x9e, 0x07, 0x16, 0x5d, 0x84, 0x13, 0xe7, - 0xfc, 0xb6, 0xff, 0xdf, 0x42, 0x7b, 0xd9, 0x76, 0x6c, 0xb2, 0xff, 0xf6, - 0x79, 0xdd, 0x84, 0xfa, 0x39, 0xe2, 0xcb, 0x63, 0x11, 0x0d, 0xbd, 0x02, - 0xfc, 0xff, 0x8f, 0x09, 0x65, 0x78, 0xf4, 0x48, 0xaa, 0xfc, 0x71, 0xf9, - 0xa1, 0x56, 0x5c, 0xf2, 0xb2, 0xe1, 0x60, 0xb2, 0xfd, 0xe3, 0xde, 0x61, - 0x59, 0x76, 0x7a, 0x07, 0x82, 0x11, 0x8a, 0x62, 0x2b, 0xb8, 0x57, 0x1e, - 0xad, 0x79, 0xa6, 0x9a, 0x49, 0x7f, 0xed, 0x10, 0x1c, 0x4c, 0x8a, 0x58, - 0xb1, 0xb9, 0xa0, 0xbf, 0x78, 0xe2, 0x36, 0x96, 0x58, 0x2b, 0x2a, 0x51, - 0x17, 0x8a, 0x3a, 0x29, 0xbf, 0xd2, 0x3c, 0x84, 0x90, 0x56, 0x54, 0xb6, - 0x05, 0x1b, 0x0d, 0xc2, 0x17, 0x99, 0x28, 0xa8, 0xe1, 0x21, 0xba, 0xf7, - 0x14, 0x62, 0x3a, 0x8c, 0x95, 0x91, 0x82, 0xfa, 0x50, 0x5b, 0xc7, 0x24, - 0x51, 0xcc, 0xf2, 0x13, 0x7d, 0x8c, 0x10, 0x10, 0xcf, 0x6a, 0x1b, 0xc2, - 0x17, 0x5f, 0x98, 0x7a, 0x7f, 0xd6, 0x5f, 0x6b, 0x59, 0xe5, 0x97, 0xf7, - 0x8f, 0x73, 0x70, 0x02, 0xac, 0xa9, 0x3d, 0x70, 0x91, 0x59, 0xb6, 0xb2, - 0xc6, 0xb2, 0xff, 0xbb, 0x3a, 0xe7, 0xa4, 0xba, 0xb2, 0xcd, 0xb5, 0x97, - 0xfd, 0xd9, 0xd7, 0x3d, 0x25, 0xd5, 0x97, 0x72, 0x56, 0x5e, 0x60, 0xe5, - 0x65, 0xfe, 0x2e, 0xbc, 0x5c, 0x9e, 0xac, 0xa3, 0x3c, 0xf7, 0x1c, 0xbe, - 0x37, 0xc0, 0xac, 0xbf, 0xf6, 0x6f, 0x2c, 0xe4, 0x45, 0x23, 0x59, 0x7c, - 0x11, 0xce, 0xcb, 0x2e, 0xfe, 0x56, 0x5e, 0xe1, 0xe9, 0x65, 0x61, 0xeb, - 0x7e, 0x47, 0xd1, 0x7b, 0xfd, 0x0c, 0x23, 0x1e, 0x12, 0xcb, 0xdd, 0xe3, - 0x79, 0x4f, 0x9b, 0x05, 0x62, 0x38, 0xf3, 0x2f, 0xe4, 0x04, 0x41, 0xc8, - 0x4a, 0xf4, 0xbe, 0xee, 0x4a, 0xcb, 0xcc, 0x1c, 0xac, 0xbf, 0xc5, 0xd7, - 0x8b, 0x93, 0xd5, 0x94, 0x67, 0x9e, 0xe3, 0x97, 0xc6, 0xf8, 0x15, 0x97, - 0xfe, 0xcd, 0xe5, 0x9c, 0x88, 0xa4, 0x6b, 0x2f, 0x82, 0x39, 0xd9, 0x65, - 0xfd, 0x17, 0xe7, 0xba, 0xf1, 0x2c, 0xbb, 0xf9, 0x59, 0x7b, 0x87, 0xa5, - 0x95, 0x88, 0x89, 0x09, 0x1f, 0xe6, 0x7d, 0x17, 0xbf, 0xd0, 0xc2, 0x31, - 0xe1, 0x2c, 0xbf, 0x89, 0xc7, 0x82, 0xb7, 0x95, 0xca, 0x71, 0x8a, 0xe0, - 0x80, 0x65, 0x0f, 0x45, 0x08, 0xaf, 0x32, 0xfe, 0x40, 0x44, 0x1c, 0x86, - 0x47, 0x4f, 0x6f, 0xfe, 0xd4, 0xfd, 0xc2, 0x96, 0x67, 0xdd, 0x59, 0x7f, - 0xfe, 0x71, 0xe6, 0x10, 0xa2, 0x14, 0x96, 0xc7, 0xa5, 0x97, 0x9f, 0x5b, - 0x2e, 0x41, 0x25, 0xf4, 0x33, 0xe8, 0x2e, 0x41, 0x25, 0xee, 0x18, 0xd7, - 0x20, 0x92, 0xe6, 0x9a, 0x5c, 0x82, 0x4a, 0x0a, 0x2a, 0x62, 0x2a, 0xe9, - 0x83, 0x45, 0x37, 0x3f, 0x53, 0x20, 0x90, 0xdc, 0xdf, 0xdf, 0xff, 0xfc, - 0x32, 0x91, 0xfa, 0x7b, 0xc9, 0xec, 0x8e, 0x7b, 0x8c, 0xfd, 0xa5, 0x97, - 0xf6, 0x7b, 0x30, 0x85, 0x59, 0x79, 0xcb, 0xf8, 0xcb, 0xfb, 0xf3, 0x39, - 0x91, 0xa8, 0x61, 0x31, 0x14, 0xa3, 0x3c, 0xe1, 0xb7, 0x5c, 0xaf, 0xa3, - 0xf1, 0xfc, 0xb2, 0xd9, 0xba, 0x8a, 0xff, 0xe1, 0x17, 0x68, 0x2c, 0xbf, - 0xa0, 0x0d, 0xba, 0x7b, 0x24, 0xbe, 0x71, 0xfb, 0x16, 0x5e, 0x19, 0xb1, - 0x65, 0xff, 0xbd, 0x3d, 0x32, 0x96, 0x4f, 0x16, 0x50, 0xa7, 0xf2, 0xe4, - 0x3c, 0x1c, 0xa8, 0x91, 0xb1, 0xe8, 0x54, 0xdf, 0xf4, 0x94, 0x0a, 0x7f, - 0x76, 0x2c, 0xbf, 0x82, 0x7c, 0xee, 0x79, 0x65, 0xf4, 0xf8, 0x1a, 0x59, - 0x7b, 0xcf, 0xd5, 0x95, 0x11, 0xbe, 0xe1, 0x15, 0xa5, 0x65, 0xfe, 0x38, - 0x01, 0xf9, 0xe3, 0x59, 0x7f, 0x66, 0xf7, 0x66, 0x79, 0x65, 0xa0, 0x92, - 0xf3, 0xeb, 0x64, 0x94, 0x92, 0xa4, 0xdc, 0x84, 0x45, 0x83, 0xb7, 0xa7, - 0xf0, 0xa4, 0x6e, 0x6b, 0x69, 0x88, 0xc3, 0x28, 0x4a, 0x5e, 0x28, 0x79, - 0x65, 0x4a, 0x67, 0x19, 0x0f, 0x56, 0x13, 0xdf, 0xe7, 0xdb, 0x0b, 0x63, - 0xd2, 0xcb, 0xf6, 0xbf, 0x61, 0xf1, 0x65, 0xfc, 0x21, 0x43, 0x9e, 0x1a, - 0xcc, 0x35, 0x37, 0xf7, 0x79, 0xfb, 0xbe, 0x96, 0x5f, 0x48, 0x4f, 0x8b, - 0x2f, 0x80, 0xfa, 0x82, 0xcb, 0x31, 0x65, 0xa0, 0x33, 0x67, 0x11, 0x15, - 0x4a, 0x2a, 0xb6, 0x2e, 0xc5, 0x4b, 0xff, 0xb3, 0xee, 0xf0, 0xca, 0x7f, - 0x76, 0x2c, 0xbd, 0x09, 0xf9, 0x65, 0xf6, 0x44, 0x6c, 0x59, 0x5b, 0x22, - 0x0e, 0x24, 0x46, 0x87, 0x6f, 0xd3, 0x14, 0x27, 0xf4, 0x97, 0xb0, 0xbf, - 0x59, 0x7f, 0xfe, 0xf6, 0x1c, 0x39, 0x1b, 0xbe, 0xc1, 0x83, 0xbc, 0x59, - 0x41, 0x44, 0xee, 0x8a, 0x7a, 0x39, 0x7f, 0xdf, 0xb9, 0x76, 0x2c, 0xd6, - 0x2c, 0xb1, 0xac, 0xbf, 0xed, 0x81, 0x01, 0xf8, 0x0e, 0x4b, 0x2f, 0xe3, - 0x96, 0x6d, 0x8d, 0x2c, 0xbf, 0x66, 0xcc, 0xc6, 0x2c, 0xbd, 0xc3, 0xd2, - 0xca, 0x23, 0xc4, 0xe9, 0x45, 0xfc, 0x4f, 0xff, 0xe0, 0xe2, 0xcb, 0xf8, - 0xc5, 0x12, 0x58, 0xdc, 0x69, 0x86, 0x60, 0x86, 0x8e, 0xb8, 0xe5, 0xd2, - 0x1a, 0xc5, 0x56, 0xa7, 0x0d, 0x07, 0x30, 0x28, 0xd1, 0x6f, 0xef, 0x7b, - 0x36, 0xef, 0x16, 0x5e, 0xcd, 0x80, 0xb2, 0xb0, 0xf3, 0x08, 0xbe, 0xff, - 0xb3, 0xb1, 0x8b, 0x3f, 0x13, 0xe5, 0x95, 0x2d, 0x91, 0x54, 0x29, 0x21, - 0xa3, 0x31, 0xc8, 0x74, 0x8a, 0x53, 0xf1, 0xb9, 0xb5, 0xe8, 0x8b, 0xd1, - 0xa4, 0x39, 0xa7, 0xeb, 0x85, 0x0e, 0x8e, 0x4b, 0x67, 0xec, 0x24, 0xa3, - 0xc8, 0x2f, 0xff, 0xdb, 0x6b, 0x58, 0x37, 0x67, 0x9f, 0xa5, 0x31, 0x2c, - 0xbf, 0xff, 0xfc, 0x0d, 0x02, 0x22, 0x7f, 0xf9, 0xcc, 0x17, 0x01, 0xa7, - 0x1c, 0xfa, 0x56, 0x5f, 0xc0, 0x9d, 0x68, 0xf6, 0x59, 0x7f, 0x42, 0x7f, - 0xf1, 0xb4, 0xb2, 0xfe, 0x71, 0xfa, 0x48, 0x0b, 0x2f, 0x82, 0x2e, 0xbf, - 0x59, 0x66, 0xe3, 0x45, 0x74, 0x45, 0xcc, 0x2f, 0x22, 0xcb, 0xe9, 0xdd, - 0x1c, 0xac, 0xbe, 0xdc, 0xf0, 0x04, 0x59, 0x7f, 0xd3, 0x10, 0x04, 0xe1, - 0xe1, 0x2c, 0xbc, 0xd3, 0x4d, 0x24, 0xba, 0x12, 0x91, 0xb9, 0xa0, 0xbe, - 0xef, 0xa7, 0xf5, 0x97, 0x7b, 0x4b, 0x2f, 0x7a, 0x3b, 0x69, 0x65, 0x4a, - 0xae, 0xbd, 0x95, 0xb2, 0x32, 0x70, 0xa2, 0xfc, 0x49, 0x11, 0x3e, 0x96, - 0x5c, 0xa3, 0xa4, 0x7b, 0xc5, 0xef, 0x87, 0x99, 0xa5, 0x97, 0xff, 0xbb, - 0x16, 0x6b, 0x4f, 0x16, 0x6b, 0x3a, 0xb2, 0xb0, 0xfb, 0x0c, 0x86, 0xfe, - 0x16, 0x75, 0xac, 0xf2, 0xcb, 0xfb, 0x68, 0xa1, 0x1d, 0xeb, 0x65, 0x97, - 0xff, 0xbf, 0x63, 0xc2, 0x34, 0x8f, 0x66, 0x4e, 0x96, 0x5f, 0x1c, 0x97, - 0x56, 0x5e, 0xd9, 0xc9, 0x65, 0x69, 0x11, 0x5e, 0x4e, 0xe9, 0x05, 0xff, - 0x85, 0x07, 0x20, 0x7a, 0xd8, 0x1f, 0xac, 0xb8, 0xb1, 0x65, 0xe7, 0xf9, - 0xa5, 0x94, 0x29, 0xfb, 0x1a, 0x1f, 0x85, 0x6f, 0x1c, 0xb1, 0x65, 0xce, - 0x35, 0x96, 0xdb, 0x46, 0xc7, 0xa3, 0x77, 0xde, 0x1e, 0x12, 0xcb, 0xff, - 0xe9, 0x13, 0xa5, 0x3f, 0xbf, 0x7f, 0x10, 0xa0, 0xb2, 0x9b, 0x2a, 0x98, - 0xb2, 0x18, 0x27, 0x0a, 0x67, 0x60, 0xe1, 0x47, 0x48, 0x6f, 0x84, 0x1f, - 0x8d, 0x65, 0xff, 0x9f, 0xbc, 0x6b, 0x3d, 0xfb, 0xf5, 0x65, 0xfb, 0xfc, - 0x1b, 0xc1, 0x65, 0xff, 0xff, 0xb3, 0xb2, 0x12, 0xcc, 0x29, 0x87, 0x7c, - 0xec, 0x2c, 0xde, 0xb2, 0xb6, 0x47, 0xa0, 0xc8, 0xf4, 0x80, 0xd1, 0x45, - 0xe1, 0x07, 0x12, 0xcb, 0xd0, 0x32, 0x59, 0x7d, 0xb0, 0x9b, 0x8d, 0x2c, - 0xbf, 0xe9, 0xf3, 0x9f, 0x62, 0x36, 0x96, 0x5f, 0xfb, 0xfc, 0xd1, 0xff, - 0x9f, 0xe9, 0x8b, 0x2f, 0xff, 0x0b, 0x3c, 0xd4, 0xf8, 0xb3, 0xb2, 0xc5, - 0x97, 0x00, 0x45, 0x97, 0xf8, 0xb3, 0x79, 0xc4, 0x6d, 0x2c, 0xaf, 0x1e, - 0x6f, 0x06, 0x2f, 0xff, 0xe0, 0x3e, 0xc1, 0x06, 0xdd, 0xcf, 0x16, 0x77, - 0xc6, 0xb2, 0xfd, 0x31, 0x10, 0x38, 0xb2, 0xb1, 0x33, 0xc3, 0x84, 0xb7, - 0x88, 0xba, 0xb9, 0x71, 0x92, 0xcb, 0xf9, 0xcb, 0xfc, 0xe8, 0xab, 0x2a, - 0x55, 0x6e, 0x6c, 0x7f, 0x01, 0xf7, 0x1b, 0x22, 0x9e, 0x1c, 0xf6, 0x37, - 0xdd, 0xe8, 0x31, 0xe2, 0xb7, 0xc5, 0xd9, 0x8f, 0x59, 0x7e, 0xdf, 0x3a, - 0x7f, 0x2c, 0xbe, 0xf0, 0x19, 0x2b, 0x29, 0x65, 0x2c, 0xb3, 0xb0, 0xb6, - 0xe0, 0x5d, 0xc0, 0x6d, 0xac, 0xa6, 0xca, 0x31, 0x4c, 0x91, 0xca, 0x48, - 0xe0, 0x04, 0xb6, 0x6e, 0xd9, 0x84, 0x55, 0xab, 0x62, 0x19, 0x51, 0xcc, - 0x28, 0xa3, 0xb8, 0x53, 0x36, 0xb9, 0x64, 0x71, 0x24, 0x6d, 0x52, 0x83, - 0xdb, 0x48, 0x45, 0xc7, 0x02, 0xc9, 0x9e, 0xb4, 0xda, 0x5d, 0x2c, 0x25, - 0x91, 0x0e, 0x78, 0xc3, 0x29, 0xc7, 0xa2, 0xc7, 0x9e, 0x19, 0x68, 0x5f, - 0x4a, 0xd8, 0x39, 0x72, 0x9b, 0xb0, 0xc5, 0x8a, 0x58, 0x9e, 0xa7, 0x02, - 0x99, 0x38, 0x8d, 0xea, 0x76, 0xa3, 0xcb, 0x0e, 0xfe, 0x52, 0x8b, 0x6d, - 0xb8, 0xa7, 0x89, 0xb9, 0x58, 0x3b, 0xf6, 0xb3, 0x62, 0x04, 0xbe, 0x2d, - 0xf1, 0x80, 0xb4, 0x41, 0x1f, 0x2a, 0x40, 0x49, 0xc3, 0x4d, 0xc8, 0xd3, - 0xaf, 0xff, 0x6c, 0xc9, 0xf9, 0xbb, 0x65, 0xb4, 0xe9, 0x3f, 0xeb, 0x29, - 0xd5, 0xa3, 0x82, 0x57, 0x7d, 0xf9, 0x98, 0x6f, 0xfa, 0xcb, 0xe0, 0xbb, - 0x85, 0x51, 0x45, 0x2f, 0xff, 0xcf, 0xfc, 0x87, 0xc7, 0xcf, 0x1f, 0x7d, - 0x84, 0xb2, 0xb4, 0x88, 0x22, 0x2e, 0xbf, 0xf4, 0xf4, 0xff, 0x98, 0x77, - 0x91, 0xd2, 0xcb, 0xcf, 0x06, 0xf2, 0x98, 0x7e, 0x42, 0xa7, 0x44, 0x57, - 0xff, 0x4b, 0xe8, 0x80, 0xff, 0x67, 0x9d, 0x65, 0xff, 0xf8, 0x67, 0x11, - 0x4f, 0x79, 0xdf, 0x82, 0x65, 0xb2, 0xcb, 0x37, 0x62, 0x38, 0x3c, 0x8f, - 0xc4, 0x2b, 0xfc, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x2e, 0x85, 0xff, 0xcd, - 0xde, 0x0d, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x4b, 0x5f, 0xb4, 0x17, 0x70, - 0xaa, 0x2f, 0x25, 0xcf, 0xbd, 0x65, 0x9b, 0xe1, 0xe6, 0x47, 0x9a, 0x54, - 0xc2, 0x68, 0xef, 0x68, 0x59, 0xc0, 0xdb, 0x21, 0x38, 0x18, 0x58, 0x44, - 0x8c, 0xc4, 0x1f, 0x4e, 0xab, 0xb9, 0xf9, 0x62, 0x3a, 0x2b, 0x92, 0x86, - 0xba, 0x70, 0x04, 0x1d, 0xf0, 0xa0, 0xbf, 0xfc, 0xdd, 0x8f, 0x06, 0xf9, - 0xa0, 0xbb, 0x85, 0x51, 0x2c, 0xaf, 0x9b, 0x22, 0xc7, 0xb6, 0x56, 0x5a, - 0x3b, 0x59, 0x6d, 0xc5, 0x96, 0xf2, 0xca, 0x6c, 0x9b, 0xb8, 0xe4, 0x58, - 0x02, 0x97, 0xf8, 0xc7, 0xec, 0x32, 0x15, 0x65, 0xf7, 0x9f, 0xa0, 0x59, - 0x7d, 0xe9, 0xdd, 0x62, 0xcb, 0xff, 0x67, 0x8e, 0x05, 0x98, 0x06, 0xda, - 0xcb, 0xff, 0x61, 0xef, 0x32, 0x04, 0x33, 0x8b, 0x2f, 0x00, 0x2e, 0xb2, - 0xa0, 0x98, 0x99, 0x99, 0x6e, 0x91, 0x30, 0x94, 0x90, 0x04, 0x3e, 0xbf, - 0x87, 0xd0, 0x3c, 0x38, 0xb2, 0xf8, 0xf7, 0x67, 0x65, 0x97, 0xf3, 0x24, - 0x85, 0xcd, 0x96, 0x5c, 0xec, 0x59, 0x58, 0x78, 0x9d, 0x2e, 0xbf, 0x03, - 0xa7, 0xe9, 0x59, 0x7f, 0xef, 0x4f, 0x8c, 0x5e, 0xf1, 0xff, 0x59, 0x7f, - 0xcf, 0x0e, 0x03, 0xb3, 0xde, 0x2c, 0xbf, 0x8b, 0xba, 0x97, 0x0a, 0xcb, - 0x9f, 0x8b, 0x2f, 0xff, 0xce, 0xf0, 0x7f, 0x46, 0xcd, 0x81, 0xd3, 0xf4, - 0xac, 0xad, 0x8f, 0xb2, 0x21, 0x6b, 0xce, 0xe1, 0x54, 0x46, 0x6b, 0xff, - 0xff, 0xb3, 0xbc, 0xef, 0x4f, 0xdf, 0x43, 0x84, 0x0c, 0x14, 0x53, 0x78, - 0x2c, 0xa0, 0xa2, 0x73, 0x45, 0x17, 0xf1, 0xcb, 0x36, 0xc6, 0x96, 0x5c, - 0x50, 0x59, 0x66, 0x2c, 0xbf, 0x1e, 0xbc, 0xed, 0xf4, 0x7a, 0xac, 0x2e, - 0x10, 0x5a, 0xde, 0x59, 0x74, 0xe9, 0x65, 0x8c, 0x26, 0x9f, 0x78, 0x8d, - 0xf1, 0xe9, 0xf4, 0xb2, 0xee, 0x6e, 0xac, 0xb3, 0xc0, 0xdd, 0xf4, 0x86, - 0xff, 0xee, 0x18, 0x39, 0x84, 0x08, 0x67, 0x16, 0x5d, 0x3f, 0x2c, 0xbc, - 0x64, 0x15, 0x95, 0xc3, 0x65, 0xd1, 0x7b, 0xff, 0xe6, 0xd9, 0xf7, 0x87, - 0xd2, 0x90, 0x3e, 0x85, 0x59, 0x5b, 0x2e, 0x2b, 0xc0, 0x9c, 0x68, 0x18, - 0x72, 0x70, 0x98, 0xd4, 0x30, 0x18, 0xf1, 0xe7, 0x52, 0x63, 0xe1, 0x37, - 0x5d, 0x40, 0x43, 0x7f, 0xda, 0x76, 0xbc, 0x73, 0xa9, 0x59, 0x7f, 0xff, - 0xbc, 0x7d, 0xe9, 0xf0, 0x4d, 0xa7, 0x62, 0xcf, 0xc1, 0xc5, 0x97, 0xee, - 0x9b, 0x26, 0x25, 0x97, 0xf6, 0x4c, 0x69, 0xe3, 0x16, 0x5a, 0x18, 0x7a, - 0xdc, 0x28, 0xbf, 0x7b, 0x09, 0xd8, 0xb2, 0xfb, 0xef, 0x4f, 0x16, 0x5e, - 0x89, 0xc9, 0x65, 0xe9, 0xef, 0x16, 0x5f, 0xfb, 0x05, 0xe4, 0xe1, 0x0f, - 0xd2, 0xb2, 0xb8, 0x7b, 0x20, 0x1c, 0xa8, 0x23, 0x83, 0x09, 0xa2, 0x23, - 0xe3, 0xad, 0xff, 0xdd, 0x3d, 0x3f, 0xf1, 0xbe, 0x84, 0xb1, 0x65, 0xf8, - 0xa6, 0x1c, 0x75, 0x94, 0x67, 0xdd, 0xbd, 0x22, 0xde, 0x59, 0x7f, 0xff, - 0xf7, 0x4d, 0x9a, 0x3c, 0xf8, 0x81, 0xd9, 0xf4, 0x6d, 0xe7, 0xd8, 0xa5, - 0x65, 0x62, 0x21, 0xfa, 0x23, 0x7f, 0xd2, 0xcf, 0x3f, 0x5c, 0xbf, 0x59, - 0x58, 0x7b, 0x46, 0x45, 0x7f, 0x9e, 0x1c, 0xe3, 0x94, 0x16, 0x5c, 0xe3, - 0x59, 0x7e, 0x83, 0x90, 0x02, 0xb2, 0x82, 0x6f, 0x7e, 0x16, 0xb9, 0xc2, - 0xb2, 0xe9, 0xdd, 0x59, 0x5b, 0x1a, 0xf6, 0xd8, 0xb5, 0xff, 0xba, 0xe5, - 0xfc, 0x61, 0x33, 0xe6, 0x96, 0x5f, 0x77, 0xb2, 0xc5, 0x97, 0xd0, 0xe9, - 0xec, 0xb2, 0xb1, 0x11, 0x22, 0xa2, 0x39, 0x15, 0xe2, 0xc0, 0xac, 0xbf, - 0xfd, 0xd9, 0x09, 0xf4, 0xd9, 0x17, 0x0c, 0x96, 0x5f, 0xe6, 0x66, 0xbf, - 0x20, 0x7e, 0xb2, 0xd0, 0x59, 0x58, 0x8a, 0xa3, 0x1b, 0x74, 0x91, 0x0d, - 0x6f, 0xdf, 0xcf, 0x64, 0x6b, 0x2a, 0x55, 0x1e, 0x62, 0x89, 0xc2, 0xbd, - 0xe1, 0xb2, 0x03, 0xbb, 0xff, 0xe0, 0x77, 0x9f, 0x42, 0x4b, 0xfe, 0x46, - 0x01, 0x2c, 0xbd, 0x3d, 0x95, 0x94, 0x33, 0xef, 0xde, 0xa9, 0x7c, 0xc0, - 0x74, 0x0b, 0x2f, 0x7b, 0x09, 0x65, 0x19, 0xf1, 0x78, 0x90, 0x04, 0x77, - 0x7a, 0x25, 0x97, 0xd9, 0x16, 0x44, 0xb2, 0xfd, 0xa0, 0x77, 0x36, 0x59, - 0x7d, 0x9a, 0x1c, 0xac, 0xb7, 0x23, 0x1f, 0x6c, 0x91, 0x91, 0x4d, 0xbc, - 0xb2, 0xbe, 0x3c, 0x67, 0x35, 0xa3, 0x47, 0x6b, 0xc3, 0x2e, 0xff, 0xbb, - 0xcc, 0xd7, 0xe4, 0x0f, 0xd6, 0x5f, 0xfe, 0xd4, 0xf7, 0x9e, 0x93, 0xef, - 0x27, 0x65, 0x97, 0xdd, 0x7f, 0x4a, 0xcb, 0xee, 0xf8, 0x11, 0xeb, 0x2f, - 0xf6, 0x4e, 0x9e, 0x07, 0xe5, 0x97, 0xf8, 0xda, 0xe4, 0xf9, 0xf7, 0x56, - 0x56, 0x8f, 0x9f, 0xa6, 0x37, 0xee, 0xfb, 0xec, 0xdc, 0x59, 0x43, 0x4d, - 0x1b, 0xe4, 0xa3, 0x21, 0x64, 0x23, 0x7c, 0x45, 0x7e, 0xe3, 0x8b, 0xe3, - 0x59, 0x7f, 0xdb, 0xa3, 0xf0, 0x39, 0xf4, 0x1d, 0x65, 0xc7, 0xec, 0x3e, - 0x60, 0x14, 0x54, 0xaa, 0x63, 0xc8, 0xe0, 0x5e, 0x16, 0xd7, 0x84, 0xc8, - 0xed, 0x65, 0xff, 0xe0, 0x7e, 0xc3, 0x2c, 0xfc, 0xb1, 0xe2, 0x59, 0x7f, - 0x8f, 0xc7, 0x25, 0x10, 0x16, 0x5f, 0xe3, 0xd6, 0x61, 0x7d, 0xd5, 0x95, - 0x27, 0xc8, 0xe6, 0x57, 0xfe, 0x00, 0xf5, 0x80, 0xf4, 0xb3, 0x16, 0x5f, - 0xff, 0xf8, 0xb3, 0xf8, 0xfc, 0x8c, 0x59, 0xb0, 0x90, 0x8d, 0xce, 0x1b, - 0x5c, 0x59, 0x58, 0x9d, 0x26, 0x88, 0xbd, 0x0a, 0xee, 0x10, 0x74, 0xfa, - 0xff, 0xf4, 0x0f, 0x43, 0xf0, 0x35, 0x81, 0xf6, 0x2c, 0xbe, 0xe8, 0x1f, - 0xab, 0x2f, 0x6f, 0xcd, 0x2c, 0xa9, 0x3c, 0x0c, 0x22, 0xaf, 0x91, 0x54, - 0x50, 0x8a, 0xbc, 0x00, 0x0d, 0x65, 0xfd, 0x11, 0xfb, 0x58, 0xc5, 0x97, - 0x1b, 0x4b, 0x2e, 0xdc, 0xc5, 0x94, 0xe7, 0xbd, 0xc2, 0xee, 0x8b, 0xdf, - 0xdc, 0x9e, 0xf3, 0xc6, 0xb2, 0xfc, 0x58, 0xcd, 0x62, 0xcb, 0xed, 0x6f, - 0xc1, 0xac, 0xac, 0x3f, 0x76, 0x16, 0x80, 0x9a, 0xf0, 0xf3, 0xab, 0x2f, - 0xf6, 0x14, 0x6f, 0x4e, 0x6e, 0xac, 0xbf, 0xf1, 0xf7, 0x98, 0x38, 0x8a, - 0x46, 0xb2, 0xfb, 0xf3, 0xfa, 0x0b, 0x2f, 0xf4, 0x97, 0x4f, 0x62, 0x95, - 0x97, 0xff, 0x8f, 0x47, 0xb4, 0x96, 0x7b, 0x40, 0xde, 0xb2, 0x86, 0x99, - 0x83, 0x07, 0x3c, 0x6d, 0xf9, 0xf1, 0x12, 0x74, 0xc6, 0xff, 0xf4, 0x90, - 0x5f, 0xae, 0x28, 0x9b, 0x4e, 0xcb, 0x2f, 0xfc, 0x63, 0x07, 0x78, 0xfd, - 0x71, 0x56, 0x5f, 0xe3, 0xc6, 0xbe, 0x87, 0x23, 0x0a, 0x88, 0xb7, 0x4c, - 0xa6, 0xcb, 0x73, 0x6d, 0x1c, 0xb5, 0x4c, 0x69, 0x5b, 0x2d, 0x40, 0xb8, - 0x6d, 0x39, 0x38, 0xf0, 0x2c, 0x2c, 0x02, 0x71, 0xf4, 0x2e, 0x8e, 0x30, - 0x78, 0xa1, 0x57, 0xa8, 0xc1, 0x18, 0x41, 0xe9, 0x55, 0x6f, 0x19, 0x1f, - 0xf1, 0x8d, 0x94, 0xa8, 0x9e, 0x47, 0xbd, 0xd8, 0x7c, 0x80, 0xa3, 0x7b, - 0xdc, 0x7c, 0x29, 0x44, 0x8d, 0x7b, 0x72, 0x1c, 0x77, 0xfd, 0xd9, 0x1e, - 0xb5, 0x2c, 0xe2, 0xcb, 0x85, 0x0a, 0xcb, 0xf9, 0x90, 0x99, 0xe1, 0xac, - 0xb9, 0xff, 0x59, 0x52, 0x78, 0x6e, 0x59, 0x7c, 0x37, 0x7f, 0x96, 0x5f, - 0xc7, 0xf1, 0x86, 0x44, 0x59, 0x4e, 0x7a, 0x04, 0x45, 0x78, 0x2f, 0xbd, - 0x65, 0xdd, 0x6f, 0x29, 0xa1, 0x30, 0xe7, 0xcb, 0x6e, 0xe1, 0xd2, 0x0b, - 0xff, 0xff, 0x81, 0xf4, 0x1b, 0xb9, 0x77, 0x19, 0xc1, 0xfa, 0x74, 0x37, - 0x76, 0x96, 0x53, 0x75, 0x7a, 0x39, 0x2c, 0x14, 0x95, 0xef, 0xff, 0x37, - 0x63, 0xc1, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4d, 0x2b, 0xff, 0xcd, 0xd8, - 0xf0, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x13, 0x8a, 0xfd, 0x0c, 0xec, 0x20, - 0xb2, 0xff, 0x6f, 0x9f, 0x46, 0x29, 0x82, 0xcb, 0xff, 0x3c, 0x1b, 0xe6, - 0x82, 0xee, 0x15, 0x45, 0x02, 0xbf, 0xfc, 0x72, 0xc7, 0x1e, 0xa7, 0xbe, - 0xc1, 0xac, 0xbe, 0xe1, 0x7d, 0x05, 0x96, 0x6f, 0xf2, 0x62, 0xa6, 0x50, - 0xc3, 0x6f, 0x26, 0x75, 0x22, 0xf8, 0x2e, 0xe1, 0x54, 0x5c, 0x0b, 0xfc, - 0x3f, 0x4c, 0x7e, 0x6a, 0x56, 0x56, 0x8f, 0x8c, 0x8b, 0xaf, 0xe2, 0xce, - 0x84, 0xf8, 0xb2, 0xe9, 0x35, 0x97, 0xde, 0x03, 0xc1, 0x65, 0xfe, 0x28, - 0x49, 0x8f, 0x18, 0xb2, 0xb1, 0x16, 0xac, 0x21, 0xf1, 0x67, 0x45, 0x40, - 0x45, 0x7e, 0x83, 0x71, 0x5b, 0x02, 0xac, 0xa6, 0xe7, 0xf5, 0x29, 0x37, - 0x3f, 0xeb, 0x2f, 0xd1, 0x42, 0x4a, 0x0b, 0x29, 0x65, 0x61, 0xb3, 0x22, - 0x8b, 0xfa, 0x18, 0x29, 0x67, 0xeb, 0x2f, 0x7f, 0x8d, 0x2c, 0xbe, 0x29, - 0xcf, 0x2c, 0xa9, 0x37, 0xdb, 0x0f, 0x5c, 0x6d, 0xf4, 0x8f, 0x62, 0x4a, - 0xe8, 0xf8, 0x8d, 0x97, 0xfd, 0x06, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x24, - 0xac, 0xdf, 0xc8, 0x91, 0x74, 0xab, 0xf6, 0x35, 0xf8, 0x38, 0xb2, 0xe6, - 0xbf, 0x59, 0x7f, 0xe0, 0xfa, 0x75, 0x9d, 0x29, 0x62, 0xcb, 0xfc, 0xfd, - 0x2c, 0xd9, 0xc9, 0x65, 0xfa, 0x62, 0x29, 0x1a, 0xcb, 0x85, 0x69, 0x65, - 0xfd, 0x07, 0xd6, 0xc3, 0x95, 0x97, 0xf6, 0x13, 0xfd, 0x0e, 0x2c, 0xbf, - 0xff, 0x00, 0x9c, 0x5e, 0x8b, 0x25, 0x0c, 0xf1, 0xb1, 0x65, 0xf9, 0x9a, - 0xc1, 0xf5, 0x65, 0xfe, 0x9f, 0x84, 0xe9, 0x4f, 0xeb, 0x2a, 0x4f, 0x74, - 0x05, 0x17, 0xed, 0xcf, 0x49, 0x0a, 0xb2, 0xc3, 0x59, 0x7f, 0x6c, 0x59, - 0xbc, 0x12, 0xb2, 0xb6, 0x3c, 0x07, 0x11, 0xb9, 0xdb, 0xec, 0xaa, 0xb0, - 0x65, 0x58, 0x32, 0x29, 0xe8, 0x4c, 0x7e, 0x27, 0x88, 0x67, 0x45, 0xcc, - 0x2d, 0xe4, 0x2c, 0xba, 0x43, 0x1e, 0xd7, 0x5b, 0x3a, 0x70, 0x78, 0x57, - 0xbc, 0x21, 0x39, 0x89, 0x6b, 0xf8, 0xdb, 0xf9, 0x1f, 0x2f, 0x63, 0xaa, - 0xdf, 0x2b, 0x5a, 0xff, 0xc2, 0xbb, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0xb5, - 0x17, 0xfe, 0x78, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x89, 0x55, 0x71, 0xb4, - 0xb2, 0x96, 0x59, 0xb8, 0xa8, 0xbb, 0x62, 0x6f, 0x53, 0x37, 0x05, 0xef, - 0xec, 0xd0, 0x5d, 0xc2, 0xa8, 0x8a, 0x57, 0xfe, 0x77, 0x66, 0x6b, 0xf2, - 0x07, 0xeb, 0x2f, 0xfd, 0x3d, 0xe6, 0x6b, 0xf2, 0x07, 0xeb, 0x2f, 0x8f, - 0x92, 0xeb, 0x2f, 0xf1, 0x3f, 0xfc, 0xee, 0x05, 0x65, 0xcc, 0x6f, 0xe4, - 0x69, 0x11, 0xff, 0x10, 0x3a, 0x41, 0x4d, 0xd3, 0x55, 0x78, 0xc2, 0xef, - 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x8d, 0x17, 0xa3, 0x84, 0x7b, 0x65, - 0x65, 0xf4, 0x74, 0xda, 0xf1, 0xc2, 0x38, 0x2c, 0xb8, 0x1e, 0x59, 0x7f, - 0xe1, 0xe3, 0x0f, 0x59, 0xc8, 0xa5, 0x65, 0xff, 0xff, 0x38, 0x39, 0xec, - 0x18, 0x82, 0x70, 0xc4, 0xe9, 0xf2, 0x21, 0x16, 0x5f, 0xfe, 0x88, 0xcf, - 0xd1, 0x67, 0x67, 0xb2, 0x35, 0x97, 0x8d, 0xda, 0x49, 0x7f, 0xe9, 0x9d, - 0xf2, 0x53, 0x14, 0xc4, 0xb2, 0x8d, 0x34, 0xfd, 0xd3, 0xe8, 0x9b, 0x18, - 0x94, 0x43, 0x97, 0xc1, 0x77, 0x0a, 0xa2, 0x9f, 0x5d, 0xd9, 0x59, 0x5a, - 0x3c, 0x4e, 0x97, 0x5f, 0xe3, 0xe9, 0xf7, 0xc0, 0xea, 0xcb, 0xe9, 0x66, - 0x9d, 0x65, 0xff, 0x45, 0x3d, 0xf3, 0xc5, 0x1b, 0x16, 0x56, 0x91, 0x20, - 0x46, 0x7d, 0x21, 0xbf, 0xe7, 0x81, 0x8f, 0xd3, 0x0e, 0x2c, 0xbf, 0x03, - 0xbd, 0x9d, 0xeb, 0x2f, 0xfe, 0x98, 0xa2, 0x3f, 0xe6, 0x28, 0xb3, 0xf5, - 0x97, 0xbc, 0x73, 0xa3, 0xf3, 0x22, 0xab, 0xbc, 0x05, 0x95, 0x87, 0x90, - 0x03, 0x1b, 0xff, 0x98, 0x40, 0xf1, 0xeb, 0x39, 0x14, 0xac, 0xbe, 0x78, - 0x8f, 0xcb, 0x2f, 0xdb, 0xc1, 0xbb, 0x3f, 0xac, 0xa3, 0x44, 0xa3, 0x11, - 0x00, 0x45, 0x7d, 0xf7, 0x41, 0xa5, 0x97, 0xfe, 0xee, 0x81, 0x17, 0x02, - 0xf1, 0x71, 0x65, 0x61, 0xf3, 0x00, 0x92, 0xff, 0x39, 0x4f, 0x4a, 0x7a, - 0xb2, 0xff, 0xec, 0x88, 0x05, 0xb7, 0x02, 0xf1, 0x71, 0x65, 0xcf, 0xc5, - 0x97, 0xfd, 0xd9, 0xf9, 0xe2, 0xd4, 0xef, 0x59, 0x5b, 0xa7, 0xa5, 0xd1, - 0x6b, 0xe0, 0x78, 0xf4, 0xb2, 0xcd, 0xdb, 0x2b, 0xd5, 0x71, 0xd1, 0x54, - 0x72, 0x77, 0x31, 0xb3, 0x64, 0x24, 0x4e, 0x16, 0x4c, 0x2f, 0xf4, 0x3c, - 0xde, 0x16, 0xa5, 0x09, 0x5e, 0x10, 0xf4, 0xc0, 0x10, 0x99, 0x8f, 0x25, - 0xbf, 0xfc, 0xdd, 0x8f, 0x06, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x35, 0x2f, - 0xec, 0xd0, 0x5d, 0xc2, 0xa8, 0xae, 0xd7, 0xff, 0xb3, 0x70, 0xfa, 0x70, - 0x8c, 0x53, 0xde, 0x2c, 0xa5, 0x97, 0x34, 0xdf, 0xc7, 0xb0, 0xd2, 0x6d, - 0x37, 0x46, 0x19, 0xc2, 0x46, 0xef, 0xcd, 0x65, 0xee, 0x4b, 0x16, 0x5c, - 0xc2, 0x59, 0x7c, 0x17, 0x70, 0xaa, 0x2b, 0xe5, 0xe6, 0x9a, 0x69, 0x25, - 0x89, 0x23, 0x73, 0x41, 0x5a, 0x3f, 0x06, 0x27, 0x5f, 0xef, 0x60, 0xaf, - 0x3f, 0x6e, 0x2c, 0xbd, 0x09, 0xf9, 0x65, 0xf6, 0x77, 0xc6, 0xb2, 0xe0, - 0x71, 0x65, 0xd9, 0xe5, 0x94, 0x33, 0x5b, 0x10, 0xbd, 0xcf, 0xbd, 0x65, - 0xff, 0x79, 0xa7, 0xef, 0x27, 0x36, 0x59, 0x7c, 0x4f, 0xf7, 0x16, 0x56, - 0x1e, 0xe7, 0x4e, 0xaf, 0x35, 0x9a, 0x59, 0x6e, 0xac, 0xbf, 0xe3, 0xd6, - 0x6c, 0x70, 0xc1, 0xac, 0xa3, 0x3e, 0x6d, 0x0e, 0xb8, 0x8d, 0xf6, 0xe7, - 0x80, 0x22, 0xcb, 0xd1, 0x64, 0x4b, 0x2b, 0xe3, 0xc5, 0x88, 0x9e, 0xda, - 0x59, 0x4b, 0x28, 0xcb, 0xcd, 0x08, 0xdd, 0xb0, 0xab, 0x2b, 0xc6, 0xe1, - 0xc7, 0xef, 0xb0, 0xa6, 0x0b, 0x2c, 0xde, 0x38, 0xab, 0xc4, 0x91, 0x7d, - 0x87, 0x32, 0x10, 0xc1, 0x22, 0x88, 0xdf, 0x43, 0xae, 0x9d, 0xf9, 0x11, - 0x3a, 0x72, 0x10, 0xe0, 0x72, 0x8f, 0x78, 0x10, 0x82, 0xff, 0xf3, 0x76, - 0x3c, 0x1b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0xf6, 0xb3, 0x6d, 0x65, 0xff, - 0x0d, 0xc3, 0x21, 0x01, 0x0d, 0x65, 0xf4, 0xee, 0x3f, 0xcb, 0x2f, 0x19, - 0x7e, 0xb2, 0xb6, 0x3f, 0x9f, 0x1c, 0x6f, 0x25, 0xbf, 0x18, 0xe7, 0x5c, - 0x59, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xb8, 0x57, 0xcc, 0x07, 0x78, 0xb2, - 0xff, 0x78, 0xe0, 0x0f, 0xe6, 0x25, 0x96, 0x6e, 0xd8, 0x4d, 0xce, 0x61, - 0x1d, 0x03, 0x2c, 0x27, 0x14, 0xd0, 0xc8, 0xef, 0xfe, 0x6e, 0xf0, 0x6f, - 0x9a, 0x0b, 0xb8, 0x55, 0x12, 0x7a, 0xff, 0xc7, 0x13, 0x79, 0x04, 0xf6, - 0x7f, 0x59, 0x52, 0xd9, 0x2f, 0x6d, 0x1c, 0xf4, 0x0a, 0xc5, 0x9e, 0xec, - 0x0c, 0x3e, 0xa2, 0x8c, 0x77, 0x52, 0xfb, 0x7d, 0x0d, 0xce, 0x47, 0x9b, - 0xbd, 0x9d, 0xa5, 0x4b, 0xfc, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x22, 0xa5, - 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x99, 0x5f, 0xc7, 0xec, 0x7f, 0xbf, 0x59, - 0x66, 0xf8, 0x7c, 0x51, 0xe6, 0x97, 0xff, 0x9b, 0xb1, 0xe0, 0xdf, 0x34, - 0x17, 0x70, 0xaa, 0x26, 0x75, 0xff, 0xe6, 0xec, 0x78, 0x37, 0xcd, 0x05, - 0xdc, 0x2a, 0x8a, 0x35, 0x5b, 0x27, 0x06, 0x38, 0x51, 0x8a, 0x59, 0xd5, - 0xab, 0xff, 0x3c, 0x1b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0x74, 0xbf, 0xfb, - 0x5b, 0x37, 0xe1, 0x67, 0x79, 0x2c, 0x59, 0x7f, 0xff, 0xb5, 0x06, 0xf8, - 0x40, 0xe8, 0x05, 0xef, 0xc1, 0x3d, 0x71, 0x65, 0x37, 0x47, 0xcc, 0xa6, - 0x1a, 0x3d, 0xc0, 0xf2, 0xcb, 0xe0, 0x84, 0x01, 0x59, 0x7e, 0xc2, 0x1e, - 0x6c, 0xb2, 0xff, 0x8f, 0x5c, 0x2c, 0x1f, 0xa5, 0x65, 0xfe, 0x99, 0x0e, - 0x6b, 0x58, 0xb2, 0x8d, 0x11, 0xfe, 0x27, 0x23, 0x7b, 0xf6, 0x68, 0x20, - 0x15, 0x65, 0xff, 0x06, 0x59, 0x86, 0xc9, 0xdd, 0x59, 0x7f, 0xe2, 0xc1, - 0x5e, 0x2e, 0x7a, 0x46, 0xb2, 0xf4, 0x4f, 0xd5, 0x97, 0xc1, 0x77, 0x0a, - 0xa2, 0x9a, 0x5f, 0xb3, 0xe6, 0x9f, 0xcb, 0x2f, 0xfe, 0xe6, 0x10, 0xe6, - 0x34, 0x71, 0xdf, 0xf9, 0xac, 0xa1, 0xa6, 0xc9, 0x85, 0x26, 0x75, 0x11, - 0xfe, 0x87, 0x48, 0xbb, 0x85, 0x37, 0xda, 0x71, 0x9a, 0xcb, 0xff, 0xc1, - 0x7d, 0x73, 0xce, 0x7c, 0xe6, 0x71, 0x65, 0xff, 0xff, 0x61, 0x77, 0x86, - 0x1c, 0x2e, 0x07, 0xc7, 0xe0, 0x89, 0xb2, 0xcb, 0xfe, 0xe3, 0xfd, 0x91, - 0x4f, 0xb8, 0xb2, 0xef, 0x1f, 0x91, 0xd4, 0x49, 0x3d, 0x67, 0xbf, 0xfa, - 0x1e, 0x32, 0xdb, 0x80, 0x9d, 0x44, 0xb2, 0xf4, 0x19, 0x05, 0x97, 0xdd, - 0xe3, 0xfe, 0xb2, 0xf7, 0xfd, 0x35, 0x97, 0xfb, 0x81, 0x3d, 0x68, 0xf6, - 0x59, 0x52, 0x7e, 0x8e, 0x47, 0xc1, 0xdb, 0xff, 0xfb, 0x60, 0x6a, 0x2e, - 0x6d, 0x31, 0x73, 0xd8, 0x33, 0xde, 0xb2, 0xff, 0xb3, 0x4f, 0x26, 0x52, - 0xc5, 0x95, 0xb2, 0x25, 0xf8, 0xc5, 0x7f, 0xdb, 0xc3, 0xe3, 0xff, 0xc6, - 0xc5, 0x95, 0x87, 0xc0, 0x64, 0x97, 0x99, 0xa0, 0x2c, 0xbf, 0xc0, 0x72, - 0x0c, 0x6d, 0xb8, 0xb2, 0xfe, 0x92, 0xec, 0x7e, 0x75, 0x65, 0xfe, 0x7f, - 0xdf, 0xbd, 0xcf, 0x2c, 0xa8, 0x1f, 0x13, 0x45, 0xf7, 0xde, 0x3c, 0xd2, - 0xcb, 0xbc, 0x05, 0x96, 0x82, 0xca, 0xc3, 0xec, 0x32, 0x3f, 0xc8, 0x77, - 0x8b, 0xde, 0xf3, 0x5f, 0xac, 0xb7, 0x56, 0x51, 0x9b, 0x0f, 0x0f, 0xdc, - 0x39, 0x59, 0x7f, 0xfe, 0xef, 0x04, 0xe6, 0xa4, 0x79, 0xe0, 0xe1, 0x6c, - 0xb2, 0xa4, 0xfb, 0x70, 0x5a, 0xfe, 0x93, 0xfd, 0xfa, 0x05, 0x97, 0xfe, - 0xcf, 0xf3, 0x22, 0x88, 0xa4, 0x6b, 0x2b, 0x13, 0x19, 0x64, 0x23, 0x7c, - 0x40, 0x45, 0xb7, 0xbb, 0x30, 0x59, 0x7f, 0xc0, 0xef, 0xb2, 0x28, 0x4f, - 0xcb, 0x2b, 0xc7, 0xae, 0x43, 0x96, 0x89, 0x65, 0xcf, 0xe5, 0x97, 0xec, - 0xf3, 0x90, 0xd6, 0x56, 0xc7, 0x9d, 0x01, 0x2f, 0xc5, 0xae, 0x91, 0x56, - 0x5f, 0xcf, 0xff, 0x1b, 0x46, 0xd1, 0xb4, 0x59, 0x7f, 0x1c, 0xb3, 0x6c, - 0x69, 0x65, 0xff, 0xf6, 0x77, 0xd9, 0x0d, 0x39, 0x77, 0xcf, 0x05, 0x97, - 0x4f, 0xeb, 0x2f, 0xf3, 0xfd, 0x21, 0x30, 0xb7, 0xd2, 0x24, 0xc8, 0xb8, - 0x44, 0xea, 0xc4, 0xf8, 0x8d, 0xbf, 0xc6, 0x24, 0x2f, 0xc8, 0x68, 0xde, - 0x78, 0xb8, 0xb2, 0xe1, 0x34, 0xb2, 0xfd, 0xff, 0xa6, 0x1c, 0x59, 0x78, - 0x80, 0x2a, 0xcb, 0xcd, 0x3e, 0xcb, 0x2f, 0xfa, 0x7f, 0x7e, 0xfe, 0x21, - 0x41, 0x65, 0x78, 0xf6, 0x48, 0x7a, 0xa5, 0x18, 0x04, 0x53, 0xd7, 0x6b, - 0x37, 0x8e, 0x59, 0x95, 0xf2, 0x2e, 0x38, 0x59, 0x64, 0x72, 0x42, 0xb1, - 0x86, 0x30, 0xbf, 0x8e, 0x8d, 0x1b, 0x50, 0x91, 0xf4, 0x64, 0x8e, 0x41, - 0xf8, 0xe9, 0x46, 0x09, 0xc8, 0xe1, 0xbb, 0x29, 0xe8, 0x09, 0xb1, 0xe3, - 0xa2, 0x43, 0x62, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x55, 0x2f, 0xfc, 0xf0, - 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x13, 0x6a, 0xcd, 0xf1, 0x10, 0x0c, 0x34, - 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0xbf, 0x5f, 0xb4, 0x17, 0x70, - 0xaa, 0x2c, 0x65, 0xdb, 0x90, 0x59, 0x66, 0xf8, 0x79, 0xd1, 0x1a, 0x5f, - 0xb8, 0x1f, 0x48, 0xab, 0x2f, 0xf1, 0x02, 0x19, 0xc7, 0xea, 0xcb, 0xf9, - 0xb2, 0x38, 0xe6, 0x39, 0x21, 0x56, 0x5f, 0xf1, 0x03, 0x68, 0xa1, 0x3a, - 0xd9, 0x65, 0xda, 0xea, 0xcb, 0xe7, 0x86, 0x0d, 0x65, 0xfd, 0xdc, 0xd0, - 0xf0, 0x96, 0x5b, 0x06, 0x79, 0xbd, 0x21, 0xba, 0x18, 0xb2, 0xfe, 0x77, - 0xef, 0x73, 0xcb, 0x28, 0x53, 0xc2, 0xd0, 0xb5, 0xfd, 0x9a, 0xef, 0xb3, - 0x65, 0x97, 0xf7, 0x4d, 0x9f, 0x43, 0x8b, 0x2c, 0xdf, 0x13, 0xf8, 0x61, - 0x97, 0x8f, 0x5c, 0xf0, 0x99, 0x38, 0xd2, 0x21, 0x1e, 0xe1, 0x75, 0x8d, - 0xba, 0xa9, 0xcd, 0x4a, 0x21, 0xbf, 0x68, 0x2e, 0xe1, 0x54, 0x5a, 0xab, - 0xff, 0x3c, 0x1b, 0xe6, 0x82, 0xee, 0x15, 0x45, 0x04, 0xbf, 0x4b, 0x51, - 0xf9, 0xa5, 0x96, 0x6f, 0x88, 0xab, 0x61, 0xa0, 0x89, 0x97, 0xdd, 0xd3, - 0xf9, 0x65, 0xfd, 0xe3, 0xdb, 0x60, 0x71, 0x65, 0xfd, 0x83, 0xd4, 0xf7, - 0x8b, 0x2f, 0xe9, 0x63, 0xfd, 0xd8, 0x2c, 0xac, 0x45, 0x68, 0x48, 0x88, - 0xc3, 0x85, 0xb7, 0xf6, 0x79, 0xbb, 0xeb, 0xab, 0x2e, 0x76, 0x9b, 0x9f, - 0x57, 0x4e, 0xef, 0xf4, 0x97, 0xf3, 0xba, 0xec, 0x59, 0x7f, 0xfa, 0x29, - 0xde, 0xfa, 0xe7, 0x0f, 0x58, 0x2a, 0xcb, 0xfc, 0x71, 0x4e, 0xf7, 0xd7, - 0x16, 0x5b, 0x8b, 0x2a, 0x4f, 0x18, 0x8d, 0x6f, 0xfe, 0x9f, 0xc1, 0xde, - 0x14, 0xee, 0x4e, 0xcb, 0x2e, 0xd4, 0xac, 0xa8, 0x26, 0x8c, 0x66, 0xbe, - 0x84, 0x9e, 0xf2, 0x0d, 0xc4, 0x8b, 0xe0, 0xbb, 0x85, 0x51, 0x71, 0x2f, - 0xe0, 0x3f, 0xc3, 0xcd, 0x2c, 0xad, 0x1e, 0xe7, 0x0b, 0xaf, 0xff, 0xd0, - 0x13, 0xd0, 0x7e, 0xbf, 0xe0, 0x27, 0xde, 0x35, 0x97, 0xb6, 0x12, 0x25, - 0x97, 0xc7, 0x0c, 0x15, 0x65, 0xfb, 0x18, 0x7b, 0x83, 0x59, 0x74, 0xb1, - 0x65, 0x6e, 0x9e, 0x06, 0x8a, 0xaf, 0x9f, 0x70, 0xf6, 0x59, 0x7e, 0xf0, - 0x05, 0xc2, 0x59, 0x7e, 0x1e, 0x6e, 0xcf, 0x96, 0x5e, 0xdc, 0x30, 0xac, - 0xa1, 0x9f, 0x8e, 0x8a, 0x37, 0x0a, 0xaf, 0xf3, 0x1e, 0x1c, 0x31, 0xe2, - 0xcb, 0xc2, 0xcf, 0x16, 0x5f, 0xe8, 0x4c, 0x4f, 0xe9, 0xdd, 0x59, 0x52, - 0x7a, 0x78, 0x3b, 0x7e, 0x2e, 0x93, 0x8a, 0xb2, 0x96, 0x5b, 0x98, 0x6c, - 0xa3, 0xc9, 0xef, 0xe6, 0x3e, 0xf9, 0xd6, 0xe2, 0xcb, 0xff, 0x18, 0xf3, - 0x7b, 0x32, 0x1f, 0x41, 0x65, 0xff, 0x63, 0x33, 0x59, 0xb6, 0x34, 0xb2, - 0xa5, 0x15, 0x98, 0x65, 0xe4, 0x1b, 0xfb, 0x34, 0xfd, 0x04, 0xac, 0xbf, - 0xfe, 0x72, 0x93, 0xdc, 0xe1, 0xf3, 0x3d, 0x91, 0xeb, 0x28, 0x67, 0xfd, - 0xbc, 0xb2, 0xff, 0xe3, 0x20, 0x43, 0x39, 0xc3, 0x1e, 0x2c, 0xa8, 0x8f, - 0x9b, 0xa4, 0x96, 0x6f, 0x2b, 0xaf, 0x79, 0x0b, 0x0f, 0x88, 0x8d, 0x66, - 0x22, 0x0d, 0x31, 0x78, 0x95, 0xe1, 0x2d, 0xf9, 0x91, 0x42, 0x17, 0x8a, - 0x5d, 0x86, 0xa8, 0x91, 0x89, 0x5f, 0xd1, 0xd4, 0x75, 0xa7, 0x78, 0x2c, - 0xbf, 0xcd, 0x92, 0x8e, 0x7b, 0x1f, 0x1c, 0x8d, 0x65, 0xe2, 0x76, 0xc2, - 0xcb, 0xf4, 0x76, 0xda, 0xff, 0x8b, 0x2b, 0x2f, 0xfc, 0x7f, 0x36, 0x9c, - 0xd4, 0x68, 0xde, 0x8f, 0x59, 0x7d, 0xe9, 0xe8, 0x56, 0x53, 0x6a, 0x3e, - 0xf1, 0xa6, 0xdf, 0xfe, 0x6d, 0x05, 0x6b, 0x77, 0x61, 0xc7, 0x71, 0xa3, - 0x7a, 0x3d, 0x65, 0xfb, 0x9c, 0x7d, 0xf8, 0xb2, 0xf8, 0x2e, 0xe1, 0x54, - 0x5d, 0x2b, 0xf1, 0x03, 0xb3, 0xc5, 0x95, 0xa3, 0xd5, 0xf1, 0x75, 0xf1, - 0x38, 0xe5, 0x65, 0xfe, 0x9d, 0xee, 0x53, 0xbf, 0x8b, 0x2a, 0x4f, 0x57, - 0xc4, 0x17, 0xff, 0x36, 0x79, 0xc8, 0xa0, 0xdb, 0x8d, 0x1b, 0xd1, 0xeb, - 0x2f, 0xe8, 0x4f, 0xcf, 0xc8, 0x2c, 0xbf, 0xe2, 0x8b, 0x53, 0xf4, 0x0d, - 0x8b, 0x2f, 0xfd, 0xbe, 0x4b, 0xbc, 0x07, 0x73, 0x4b, 0x2a, 0x4f, 0xeb, - 0x0e, 0xaf, 0xa1, 0xb9, 0x9d, 0x59, 0x7d, 0xc9, 0xdc, 0xe2, 0xca, 0x93, - 0xca, 0xdc, 0x25, 0xbd, 0x3d, 0xdc, 0x59, 0x7f, 0x64, 0x0e, 0x61, 0xba, - 0xb2, 0xf0, 0xf0, 0x6b, 0x28, 0x67, 0x97, 0x85, 0xf7, 0xff, 0x6f, 0x32, - 0xd4, 0xf4, 0xc2, 0x63, 0x59, 0x7f, 0xb7, 0xcf, 0x78, 0x00, 0xb6, 0xd6, - 0x56, 0x1f, 0xf3, 0xa2, 0x5f, 0x73, 0x67, 0x11, 0x65, 0xfe, 0x9e, 0xbe, - 0xc0, 0x38, 0x2c, 0xb8, 0x4f, 0x2c, 0xa2, 0x3c, 0xb1, 0x0c, 0xef, 0xff, - 0xcf, 0x02, 0x9e, 0x67, 0xc1, 0x27, 0xcf, 0x82, 0xb2, 0xff, 0x8f, 0xd9, - 0xf7, 0xf0, 0x1e, 0x2c, 0xb3, 0x76, 0xca, 0xf5, 0xbc, 0x74, 0x6f, 0x1c, - 0xa2, 0xb6, 0x83, 0xf3, 0x0a, 0x58, 0x14, 0x0d, 0x93, 0x1f, 0x8d, 0xdf, - 0x74, 0x86, 0x25, 0x7d, 0x42, 0xa1, 0x8d, 0x5e, 0x24, 0x76, 0xa2, 0x84, - 0xff, 0x48, 0x00, 0xdf, 0xbc, 0x8a, 0x3d, 0x52, 0xff, 0xf1, 0x74, 0xd9, - 0xe9, 0x32, 0x7d, 0x0a, 0xb2, 0xfe, 0x7e, 0xcb, 0x24, 0x96, 0x56, 0x1f, - 0x9e, 0x92, 0x2f, 0xfc, 0xce, 0x03, 0xb0, 0x3d, 0xfe, 0x35, 0x97, 0xf6, - 0x67, 0x03, 0xd9, 0x59, 0x7f, 0x6d, 0x3d, 0x71, 0x40, 0xb2, 0x96, 0x5f, - 0xfd, 0x3a, 0xfe, 0x37, 0x3d, 0x2c, 0x72, 0x59, 0x7f, 0xa7, 0x42, 0x7b, - 0xd9, 0xfa, 0xca, 0x81, 0xfd, 0xf9, 0x1a, 0xfe, 0x83, 0xc1, 0xc8, 0x2b, - 0x2e, 0xef, 0x16, 0x5f, 0xfe, 0xce, 0xcb, 0x42, 0x6d, 0xcc, 0x64, 0xee, - 0xac, 0xb3, 0x16, 0x59, 0xbb, 0x65, 0x34, 0xbc, 0x84, 0x34, 0x44, 0x5e, - 0x2c, 0x21, 0x7e, 0xa7, 0xd6, 0x93, 0xe2, 0x64, 0x6f, 0x57, 0xd8, 0xd3, - 0x65, 0x8b, 0x2f, 0xe2, 0x9f, 0x98, 0xfd, 0x59, 0x7f, 0xf9, 0xa7, 0x1f, - 0xa7, 0xee, 0xcf, 0x9f, 0x75, 0x65, 0x8f, 0x47, 0xf7, 0xe2, 0xdb, 0xff, - 0xfe, 0x06, 0xa1, 0xcf, 0x39, 0xeb, 0x5a, 0x07, 0xc7, 0xa7, 0xe2, 0xcb, - 0xff, 0xb4, 0x19, 0xcf, 0x4f, 0x4a, 0x7a, 0xb2, 0xff, 0xc4, 0xed, 0x77, - 0x3c, 0xf1, 0x3a, 0xcb, 0xff, 0xf7, 0xff, 0xcc, 0x6f, 0x4f, 0x40, 0x1f, - 0x49, 0x3a, 0xcb, 0xff, 0xf1, 0x01, 0xf7, 0x72, 0x1e, 0xc1, 0x70, 0x71, - 0x01, 0x65, 0x62, 0x2c, 0x7c, 0xb1, 0x70, 0x71, 0x65, 0x49, 0xb8, 0x14, - 0x8a, 0xff, 0x3f, 0x79, 0x3e, 0x12, 0x25, 0x97, 0xa1, 0x2c, 0x59, 0x77, - 0x20, 0xb2, 0xff, 0xfd, 0xec, 0x18, 0x3b, 0xc0, 0xf8, 0xf8, 0x07, 0xf9, - 0x65, 0x0a, 0x7d, 0xdd, 0x17, 0xbf, 0xda, 0xd3, 0xc3, 0x6c, 0x69, 0x65, - 0xd1, 0x01, 0x65, 0xa1, 0x87, 0x9a, 0xd1, 0xad, 0xff, 0xd8, 0x39, 0x84, - 0xea, 0x44, 0x72, 0x59, 0x7f, 0xed, 0xf9, 0x06, 0x0c, 0xf7, 0xc8, 0xd6, - 0x53, 0x11, 0x08, 0xe8, 0x57, 0x3b, 0x7d, 0x95, 0xf7, 0xe3, 0x44, 0x48, - 0x5a, 0x8c, 0x9f, 0xc4, 0x2e, 0x6b, 0xc8, 0x45, 0x75, 0xc0, 0x48, 0x56, - 0x57, 0x97, 0x2d, 0x0a, 0x70, 0x5a, 0xff, 0x43, 0x3b, 0xe9, 0x10, 0x96, - 0x5f, 0xfb, 0xa6, 0x13, 0x1b, 0x90, 0xb2, 0xb2, 0xff, 0xa7, 0xef, 0x3f, - 0x3b, 0x2d, 0x2c, 0xa9, 0x3f, 0x7d, 0x1e, 0xdf, 0xe7, 0xe9, 0x03, 0x6f, - 0xb7, 0x56, 0x59, 0xb0, 0xb2, 0xe0, 0x79, 0x65, 0xf8, 0xb3, 0xcf, 0xd5, - 0x97, 0xf4, 0x1f, 0x37, 0xc8, 0xd6, 0x53, 0x60, 0xf5, 0x23, 0x89, 0x35, - 0xf1, 0x82, 0x12, 0xb2, 0xfb, 0x3e, 0x96, 0x2c, 0xbe, 0x32, 0x9f, 0xd6, - 0x5f, 0xde, 0x9d, 0xef, 0x9a, 0x59, 0x7e, 0x78, 0xa0, 0x7d, 0x59, 0x7f, - 0xe9, 0x1e, 0xa6, 0x07, 0xf7, 0x71, 0x65, 0x41, 0x1f, 0x02, 0x90, 0x84, - 0x8b, 0xc4, 0x3f, 0x97, 0x74, 0xa2, 0xff, 0xb5, 0xcf, 0x18, 0x42, 0x0d, - 0x96, 0x5f, 0xc0, 0x17, 0x0b, 0x73, 0x16, 0x5f, 0xff, 0xfd, 0xa8, 0x07, - 0xc6, 0xc6, 0x1b, 0x5c, 0xf8, 0x27, 0x9a, 0x1c, 0xfc, 0xb2, 0xa5, 0x1c, - 0x04, 0x76, 0x03, 0x0b, 0xff, 0xfe, 0x91, 0xfb, 0x3e, 0xe9, 0x3f, 0xbf, - 0x7e, 0x71, 0xf4, 0x15, 0x97, 0xfb, 0x33, 0x05, 0x15, 0xe0, 0xb2, 0x85, - 0x44, 0xd7, 0x59, 0xef, 0xd9, 0xc0, 0xf6, 0x56, 0x5f, 0xfe, 0x36, 0xb8, - 0x1f, 0x1f, 0xdd, 0xd6, 0xa5, 0x65, 0xff, 0xb5, 0xd7, 0x87, 0x37, 0x83, - 0x46, 0xb2, 0xff, 0xe7, 0xe1, 0x38, 0xa3, 0xf0, 0x1f, 0x8b, 0x2a, 0x51, - 0x0a, 0x04, 0x1b, 0xff, 0xfe, 0x76, 0x84, 0xcd, 0x13, 0xfd, 0x00, 0xcf, - 0x79, 0x9f, 0x75, 0x65, 0xf8, 0xb3, 0xf9, 0xfd, 0x65, 0xfe, 0x81, 0x94, - 0x50, 0x3f, 0x2c, 0xb4, 0xc0, 0xf6, 0xe2, 0x28, 0xac, 0x54, 0x0d, 0xa2, - 0x7f, 0x43, 0x45, 0xc8, 0x8a, 0x18, 0x97, 0xed, 0xe7, 0xac, 0x1a, 0xcb, - 0xff, 0xee, 0x78, 0xf9, 0xdf, 0x60, 0xcf, 0x98, 0x4b, 0x2f, 0x7a, 0x4b, - 0x0f, 0xdc, 0x05, 0x37, 0xe7, 0xf4, 0x08, 0x2b, 0x2f, 0xf4, 0xb3, 0x0d, - 0x93, 0xba, 0xb2, 0xff, 0x41, 0x9c, 0x32, 0xfa, 0x0b, 0x28, 0x27, 0xd1, - 0x11, 0xa5, 0xfe, 0x3d, 0x39, 0x7f, 0x84, 0xb2, 0xff, 0xf6, 0x6b, 0x4f, - 0x0e, 0x16, 0x30, 0x4f, 0x96, 0x5f, 0xf8, 0x8e, 0x05, 0x9e, 0x27, 0x82, - 0xcb, 0xce, 0xe1, 0x54, 0x49, 0x8b, 0xdb, 0x87, 0xc5, 0x95, 0x04, 0x41, - 0x04, 0xef, 0x70, 0xa2, 0xfc, 0x16, 0xd2, 0x3b, 0x6c, 0xb6, 0x16, 0x5f, - 0xfe, 0xfa, 0x1c, 0xcd, 0xee, 0xcc, 0x16, 0x78, 0xb2, 0xff, 0x67, 0xdd, - 0xeb, 0xc3, 0x8b, 0x2a, 0x51, 0x79, 0x87, 0x7a, 0x4d, 0xbf, 0xc4, 0xfd, - 0xe0, 0x40, 0x4b, 0x2f, 0xff, 0xbf, 0x3f, 0xda, 0xc3, 0xde, 0x59, 0xdf, - 0x1a, 0xcb, 0xfc, 0xff, 0x71, 0xde, 0x1c, 0x59, 0x6d, 0x62, 0x21, 0x7c, - 0xa5, 0x58, 0x8d, 0xf3, 0x85, 0xbd, 0xff, 0x9a, 0xc1, 0xbc, 0x3b, 0xc7, - 0xfd, 0x65, 0xff, 0xfc, 0xff, 0x61, 0x4b, 0x42, 0x74, 0xa7, 0xfc, 0xfb, - 0xab, 0x28, 0xd1, 0x3a, 0x04, 0x0b, 0xff, 0x8f, 0xe6, 0xbb, 0xe3, 0x92, - 0xc0, 0xac, 0xbf, 0xe3, 0x16, 0x47, 0xb3, 0x27, 0x4b, 0x2a, 0x0b, 0xa8, - 0x03, 0x31, 0xc8, 0x47, 0x8a, 0x46, 0x13, 0x1d, 0x43, 0x35, 0x90, 0xef, - 0xf4, 0x61, 0xbc, 0x86, 0x1f, 0x48, 0xb7, 0xa2, 0x5f, 0xc3, 0xfd, 0xb1, - 0xcf, 0xe3, 0xd6, 0x5e, 0xdc, 0xdc, 0x95, 0x97, 0xb8, 0x63, 0x59, 0x7f, - 0x14, 0xfc, 0xc7, 0xea, 0xcb, 0xfe, 0x90, 0xf4, 0x1e, 0xd4, 0xb4, 0xb2, - 0xff, 0x8f, 0x1a, 0x0f, 0xa4, 0xe3, 0xd6, 0x5f, 0xfe, 0x92, 0x86, 0x03, - 0x5b, 0x7f, 0x98, 0x35, 0x97, 0xf8, 0x27, 0xae, 0x45, 0x2e, 0xb2, 0xff, - 0xe0, 0x73, 0xcf, 0x0f, 0x3c, 0x24, 0x96, 0x50, 0xa8, 0xc0, 0xf9, 0x2b, - 0x46, 0x76, 0x38, 0x93, 0xb0, 0xd0, 0xe3, 0x0b, 0x5b, 0x67, 0x5d, 0x87, - 0xdd, 0xf8, 0x73, 0xce, 0x9a, 0xca, 0x95, 0x4a, 0x7e, 0x8f, 0x49, 0xd6, - 0xaf, 0xba, 0x6c, 0x75, 0x96, 0x15, 0x65, 0xfd, 0xee, 0x4f, 0x64, 0x6b, - 0x2b, 0x0d, 0xf9, 0x89, 0x56, 0x1f, 0xff, 0x58, 0x2f, 0xff, 0x01, 0xfc, - 0xe3, 0x83, 0x3d, 0x22, 0x12, 0xcb, 0xfe, 0x9f, 0x74, 0xf6, 0x64, 0x85, - 0x65, 0xff, 0xf9, 0xc3, 0xa9, 0xf1, 0x67, 0x71, 0xbb, 0x4d, 0x34, 0x92, - 0xa0, 0x8f, 0xd0, 0x90, 0xf1, 0x2b, 0xa7, 0x37, 0xe0, 0x80, 0x25, 0x2b, - 0x2f, 0xdc, 0x8b, 0xd3, 0xd5, 0x94, 0x13, 0xd0, 0x88, 0x9e, 0xff, 0xef, - 0xa0, 0x53, 0x0d, 0x4f, 0x5f, 0xab, 0x2f, 0xf8, 0x62, 0x4f, 0xd1, 0xf9, - 0xa9, 0x59, 0x74, 0xc1, 0x65, 0x62, 0x25, 0xdd, 0x10, 0x8f, 0x6f, 0x81, - 0xbb, 0x30, 0x59, 0x7f, 0xe7, 0xec, 0xeb, 0x9e, 0x92, 0xea, 0xcb, 0xf6, - 0x40, 0xdf, 0x7a, 0xca, 0xf2, 0x22, 0x88, 0x97, 0x87, 0xb5, 0x04, 0x73, - 0x02, 0x16, 0xf7, 0x98, 0xfe, 0x59, 0x7f, 0xf8, 0xa6, 0x1a, 0xd0, 0x3e, - 0x3d, 0x3f, 0x16, 0x5f, 0xff, 0x73, 0x05, 0x2c, 0xef, 0x32, 0x04, 0xed, - 0x2c, 0xb1, 0xf9, 0x13, 0x3d, 0x4a, 0xa3, 0x46, 0xee, 0xf8, 0x5a, 0xdf, - 0x6e, 0x4f, 0xb8, 0xb2, 0xff, 0x6d, 0xd2, 0x90, 0x77, 0x8b, 0x2f, 0xf3, - 0x3b, 0x91, 0x41, 0xc9, 0x65, 0x61, 0xf3, 0x99, 0xa5, 0x4a, 0x35, 0x70, - 0xa7, 0x50, 0x8c, 0xbc, 0x4e, 0xdb, 0x59, 0x7b, 0xff, 0xe5, 0x65, 0xf8, - 0x81, 0xb7, 0xdb, 0xab, 0x2b, 0x87, 0x97, 0xd1, 0xeb, 0xff, 0xd3, 0xb1, - 0x66, 0x6d, 0xd1, 0x64, 0xa0, 0xb2, 0xff, 0xce, 0x5b, 0x79, 0xd8, 0x7c, - 0x35, 0x94, 0xe8, 0xa9, 0x22, 0x26, 0x92, 0xaf, 0x34, 0xd3, 0x49, 0x2f, - 0xb6, 0x61, 0xf1, 0x23, 0x73, 0x41, 0x74, 0x8d, 0x65, 0xb4, 0xe7, 0x96, - 0x46, 0xb7, 0xe9, 0xc2, 0x76, 0x2c, 0xbf, 0xf3, 0xc3, 0xc7, 0xf7, 0xfa, - 0x71, 0xac, 0xb8, 0x6d, 0x2c, 0xbf, 0xff, 0x66, 0xf9, 0x2e, 0xb2, 0x70, - 0x87, 0x9f, 0x75, 0x65, 0xe7, 0x20, 0xec, 0x7d, 0xba, 0x18, 0xaf, 0xd3, - 0x05, 0x22, 0x50, 0x42, 0xb6, 0xff, 0x42, 0x75, 0xb4, 0xeb, 0x65, 0x97, - 0xff, 0xbd, 0x9c, 0xe6, 0x30, 0x67, 0xbe, 0x46, 0xb2, 0xfe, 0xce, 0x63, - 0x1f, 0xe5, 0x97, 0xee, 0x1e, 0x17, 0xeb, 0x2b, 0x47, 0xa9, 0xc2, 0xdb, - 0xda, 0xcd, 0xd5, 0x97, 0xfc, 0xd0, 0x9b, 0x73, 0x19, 0x3b, 0xab, 0x2f, - 0xff, 0x18, 0x70, 0x87, 0xa7, 0x0e, 0x7d, 0xd5, 0x95, 0x28, 0x88, 0xe1, - 0xfd, 0xff, 0x8c, 0x84, 0xc8, 0xb8, 0x7f, 0x34, 0xb2, 0xf9, 0xff, 0xeb, - 0x6d, 0x65, 0x05, 0x3f, 0x2d, 0x42, 0x7d, 0x84, 0x5d, 0x85, 0x3e, 0xf2, - 0x26, 0x90, 0x6f, 0xef, 0x67, 0xdd, 0xf4, 0xac, 0xa9, 0x55, 0x75, 0x92, - 0x89, 0xdd, 0x96, 0xff, 0xf8, 0xe2, 0x29, 0xef, 0x3b, 0xf0, 0x4c, 0xb6, - 0x59, 0x7f, 0xff, 0xf8, 0xcb, 0xf1, 0x0b, 0x35, 0xac, 0x6b, 0x37, 0x70, - 0xa7, 0xf6, 0x3c, 0x16, 0x5f, 0xff, 0xdd, 0xe0, 0x07, 0xe3, 0xc8, 0x9c, - 0xf7, 0x4f, 0xe8, 0x2c, 0xbf, 0xff, 0x8b, 0x3f, 0x72, 0xff, 0x5a, 0xc6, - 0xb3, 0xf7, 0xf9, 0x65, 0x12, 0x2f, 0x3a, 0xbf, 0x7e, 0x3e, 0xed, 0x8d, - 0x2c, 0xbf, 0xe9, 0x0f, 0x88, 0x1d, 0x9e, 0x2c, 0xbf, 0xf6, 0xe4, 0x86, - 0x59, 0x83, 0x76, 0x2c, 0xbf, 0x6e, 0x88, 0x52, 0x15, 0x94, 0x67, 0xd6, - 0xe8, 0x17, 0xff, 0x43, 0x99, 0x23, 0x32, 0x7d, 0x0a, 0xb2, 0xfd, 0xa9, - 0xc1, 0x9a, 0xcb, 0xfb, 0xc6, 0x31, 0xe3, 0x4b, 0x2f, 0xda, 0xcd, 0xfe, - 0xc8, 0xc7, 0xa8, 0x12, 0x6a, 0x94, 0x6c, 0xbc, 0x25, 0xef, 0xff, 0xdd, - 0xf6, 0x42, 0x30, 0x7c, 0x7d, 0xf8, 0x27, 0xa5, 0x95, 0x05, 0x73, 0x87, - 0x19, 0xb6, 0x88, 0x98, 0x53, 0xe8, 0x51, 0xff, 0x0f, 0x92, 0x26, 0xbf, - 0xfd, 0xd9, 0x60, 0x5c, 0x78, 0xd0, 0x9a, 0x82, 0xcb, 0xe2, 0xdb, 0x78, - 0xd6, 0x5f, 0xff, 0xef, 0x19, 0x0f, 0x58, 0x1f, 0x4f, 0xb3, 0x5a, 0x91, - 0x56, 0x5f, 0xf8, 0x21, 0x06, 0xef, 0x0d, 0xac, 0xd2, 0xcb, 0xff, 0xfe, - 0xe8, 0x34, 0xfc, 0x0f, 0x8e, 0x22, 0x76, 0xbb, 0x9e, 0x62, 0xcb, 0xff, - 0xdc, 0xcd, 0x46, 0x0f, 0x8f, 0x79, 0x90, 0xd6, 0x51, 0xa2, 0xd7, 0x8d, - 0x14, 0x69, 0x8d, 0x3c, 0x3e, 0x2f, 0xf9, 0x93, 0xad, 0xf9, 0xa9, 0x89, - 0x65, 0xff, 0xb4, 0x16, 0x4f, 0xdc, 0xcd, 0xf8, 0xb2, 0xe7, 0xdd, 0x59, - 0x61, 0x56, 0x5f, 0xff, 0xd1, 0x14, 0x8f, 0x3e, 0xee, 0x42, 0x4b, 0x66, - 0x62, 0xcb, 0xfb, 0xf8, 0xfc, 0x1e, 0x31, 0x65, 0x69, 0x13, 0xde, 0x12, - 0x25, 0xbb, 0xf8, 0xa3, 0x47, 0xe6, 0xa5, 0x65, 0x4a, 0x6b, 0x1b, 0x20, - 0xe4, 0x2d, 0xdc, 0xbe, 0xff, 0x3f, 0xf1, 0xb0, 0x6f, 0x05, 0x96, 0x15, - 0x65, 0xff, 0xd8, 0x1f, 0x4f, 0xb3, 0x5a, 0x91, 0x56, 0x5f, 0xb3, 0x5a, - 0x91, 0x56, 0x5f, 0x11, 0xff, 0xc9, 0x3f, 0xed, 0x09, 0x79, 0x12, 0xff, - 0xbc, 0xff, 0xb1, 0xe0, 0xfc, 0x59, 0x7f, 0x9e, 0x10, 0x98, 0x07, 0x8b, - 0x2b, 0x0f, 0xb0, 0x07, 0x35, 0x89, 0xc2, 0x9c, 0x2c, 0xfd, 0x0b, 0x1b, - 0xff, 0xcf, 0xb7, 0x8e, 0x4b, 0x22, 0x3d, 0x1a, 0xca, 0x1b, 0x29, 0x3b, - 0x25, 0xf6, 0xfc, 0xea, 0x69, 0x91, 0x12, 0xfa, 0x37, 0xaf, 0xc9, 0xf9, - 0x1c, 0x9f, 0x63, 0xc6, 0x68, 0xe6, 0xfc, 0x59, 0xe7, 0xea, 0xcb, 0xff, - 0x42, 0x4b, 0x61, 0x34, 0x40, 0x82, 0xcb, 0xf8, 0x9c, 0x7f, 0xfd, 0xfa, - 0xca, 0xdd, 0x44, 0xb4, 0x44, 0xdc, 0x40, 0xbf, 0xf9, 0xca, 0x1a, 0x7f, - 0xbd, 0x39, 0xc5, 0x97, 0xdd, 0xdb, 0x06, 0xb2, 0xff, 0x8c, 0x51, 0x35, - 0xfb, 0x0f, 0x8b, 0x2a, 0x23, 0xdf, 0x8f, 0x23, 0xbc, 0xd3, 0x4d, 0x2c, - 0xbf, 0xff, 0x63, 0x3a, 0x53, 0xfe, 0x0f, 0x98, 0xc9, 0x0a, 0x46, 0xe6, - 0x82, 0xfe, 0xd6, 0x6e, 0x79, 0xc6, 0xb2, 0xff, 0xd9, 0xf4, 0x62, 0xcd, - 0xfa, 0x3e, 0x2c, 0xbf, 0x83, 0xe3, 0x92, 0x0a, 0xca, 0x93, 0xee, 0xc4, - 0x2b, 0xfd, 0xa9, 0xe9, 0x84, 0xc6, 0xb2, 0xfd, 0x08, 0xb3, 0x02, 0xb2, - 0xda, 0x59, 0x51, 0xd1, 0xf5, 0xc9, 0x90, 0x85, 0x17, 0xe3, 0xff, 0x1f, - 0x75, 0x65, 0x62, 0xb0, 0x91, 0x4d, 0x0e, 0x14, 0x5e, 0x46, 0x76, 0x62, - 0x84, 0xf7, 0x21, 0x17, 0xd3, 0x4b, 0xe2, 0xda, 0x7c, 0xb2, 0xf7, 0xcd, - 0x47, 0x4b, 0x2f, 0x76, 0x1e, 0x59, 0x63, 0x59, 0x7f, 0xff, 0x9a, 0x92, - 0x91, 0x4a, 0x45, 0x12, 0x3e, 0x75, 0x21, 0x95, 0x97, 0xf7, 0xa6, 0x30, - 0x4f, 0x65, 0x97, 0xff, 0x79, 0xd9, 0x9a, 0x14, 0x50, 0x17, 0x56, 0x5f, - 0xf3, 0xf7, 0x9d, 0xeb, 0xc3, 0x8b, 0x2a, 0x23, 0xff, 0x24, 0x6b, 0xff, - 0x9f, 0x4f, 0x0c, 0xe3, 0xf7, 0x22, 0x59, 0x7f, 0x69, 0xf4, 0x53, 0x05, - 0x97, 0xff, 0xcf, 0xdf, 0x66, 0xff, 0x1c, 0x6e, 0x70, 0xd8, 0xb2, 0xff, - 0xc7, 0x03, 0xd7, 0xd2, 0x53, 0xc5, 0x94, 0xda, 0xea, 0x91, 0x24, 0x40, - 0x6c, 0x99, 0x0a, 0xf0, 0x91, 0x69, 0x0c, 0x8b, 0x3a, 0xa7, 0x66, 0xf1, - 0xd3, 0xad, 0x3e, 0x8e, 0x45, 0x63, 0xb6, 0xb9, 0x8c, 0x93, 0x68, 0xce, - 0x61, 0x0c, 0x81, 0xca, 0x2a, 0xc9, 0xe0, 0xa1, 0x61, 0x6e, 0x19, 0x51, - 0x7f, 0x47, 0x64, 0x70, 0x8d, 0x8a, 0x3a, 0x0d, 0x47, 0xb4, 0xc3, 0x3f, - 0x46, 0x26, 0xf0, 0x9a, 0xfe, 0x32, 0xb2, 0x95, 0xe1, 0xca, 0x54, 0x27, - 0x67, 0x12, 0x40, 0xfe, 0xd1, 0x14, 0x79, 0x20, 0x92, 0x98, 0x6f, 0xd0, - 0xfa, 0x48, 0x6b, 0x2a, 0x0a, 0x82, 0xca, 0x3e, 0x3b, 0xff, 0xda, 0xc6, - 0x87, 0xec, 0x88, 0xb3, 0x50, 0x59, 0x7b, 0x59, 0xba, 0xb2, 0xff, 0xd2, - 0xd0, 0x9b, 0x73, 0x19, 0x3b, 0xab, 0x2b, 0x48, 0xae, 0x62, 0x5f, 0x47, - 0xef, 0xff, 0x9d, 0x93, 0xa0, 0xe8, 0x02, 0xe8, 0x0f, 0xc5, 0x95, 0x88, - 0x81, 0x01, 0x85, 0xfe, 0xd0, 0x40, 0xe3, 0x64, 0xac, 0xbf, 0xfa, 0x74, - 0x27, 0x4f, 0xbe, 0xc1, 0x9a, 0xcb, 0xfc, 0x5f, 0x7f, 0x9c, 0x7d, 0xd5, - 0x97, 0xfc, 0x53, 0xb0, 0x9e, 0xf3, 0xee, 0xac, 0xbe, 0x9c, 0x1f, 0xcb, - 0x2b, 0x11, 0x2d, 0xe3, 0x7d, 0xe7, 0xb7, 0x6f, 0xdc, 0x59, 0x7f, 0xff, - 0xc6, 0x47, 0xff, 0x35, 0x81, 0xf4, 0xfb, 0x35, 0xa9, 0x15, 0x65, 0x4a, - 0x78, 0x1b, 0x19, 0xe4, 0x36, 0xbf, 0x31, 0xe8, 0xe5, 0xe8, 0xe3, 0x1d, - 0x41, 0x65, 0xe7, 0xd6, 0xcb, 0x2e, 0x69, 0xa5, 0x97, 0x9c, 0x2d, 0xc2, - 0x6d, 0x9a, 0x1d, 0xbf, 0x69, 0xe0, 0x7e, 0x59, 0x7f, 0xf6, 0xb9, 0xe3, - 0x6b, 0xc7, 0xb3, 0x92, 0xca, 0x93, 0xec, 0x72, 0x7b, 0xf6, 0x33, 0xce, - 0x35, 0x96, 0x6f, 0x2e, 0xf4, 0xea, 0x04, 0x23, 0x41, 0xc9, 0x41, 0xc2, - 0x95, 0x06, 0x75, 0x5a, 0x22, 0xdd, 0x42, 0xab, 0xc4, 0x2f, 0x6b, 0xe0, - 0x4a, 0x35, 0x9e, 0x47, 0xc5, 0xd5, 0x0d, 0xeb, 0xcd, 0x42, 0x87, 0x70, - 0x82, 0xa0, 0xfc, 0x59, 0x23, 0xa6, 0x6f, 0xfd, 0x1d, 0xd6, 0x8a, 0x99, - 0x08, 0xff, 0x4a, 0xc6, 0xfe, 0x1b, 0xc5, 0x1a, 0x7f, 0x27, 0xbc, 0x7b, - 0x3f, 0x1a, 0x0b, 0x7d, 0xd7, 0x7e, 0xc1, 0xfa, 0x5a, 0x59, 0x7f, 0x72, - 0x62, 0x81, 0xc4, 0xb2, 0xff, 0xbb, 0x24, 0x7f, 0xe7, 0xdd, 0x59, 0x7d, - 0x1e, 0xe5, 0xfa, 0xcb, 0xa7, 0x6c, 0x3d, 0xe1, 0x0e, 0x68, 0x91, 0xc7, - 0xc2, 0x81, 0x21, 0x1b, 0x7f, 0x9f, 0x41, 0x6f, 0x0c, 0xf9, 0x65, 0xff, - 0xe8, 0x3f, 0x1b, 0x94, 0xe7, 0x67, 0xe8, 0x2c, 0xba, 0x7f, 0x59, 0x7d, - 0xde, 0xcb, 0x16, 0x5f, 0x8c, 0x26, 0x36, 0xfe, 0x37, 0x22, 0x0b, 0xd3, - 0x74, 0xcb, 0xf6, 0x35, 0x33, 0x6e, 0xc2, 0x12, 0xff, 0xce, 0x16, 0xe0, - 0xfc, 0xe1, 0x90, 0x59, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0x8d, 0x57, 0xff, - 0xde, 0x36, 0x9c, 0x83, 0xa9, 0x38, 0x3f, 0x16, 0x5f, 0x8c, 0x6f, 0x9d, - 0x59, 0x78, 0x5f, 0xb8, 0xb2, 0xcd, 0xf6, 0x4c, 0x23, 0x10, 0x42, 0x68, - 0xe9, 0xdd, 0x26, 0xbf, 0xff, 0xdb, 0xe4, 0xba, 0xdd, 0xf7, 0xb8, 0xf0, - 0x6e, 0xc7, 0x1a, 0xcb, 0xf6, 0x82, 0xee, 0x15, 0x45, 0x4a, 0xb0, 0x55, - 0x10, 0xd2, 0x96, 0x53, 0x9a, 0x9f, 0xc7, 0xac, 0xdf, 0x0f, 0xf9, 0xd7, - 0x6f, 0xda, 0x0b, 0xb8, 0x55, 0x11, 0xf2, 0xff, 0xe0, 0x6b, 0xf0, 0xf8, - 0xe2, 0x29, 0xfd, 0x65, 0xfe, 0x86, 0x6e, 0xb9, 0x7f, 0x1d, 0xac, 0xbf, - 0xfe, 0x27, 0xec, 0x24, 0x58, 0xd3, 0xde, 0x4f, 0xeb, 0x2f, 0x3c, 0x1b, - 0xe2, 0x61, 0x1c, 0x34, 0xea, 0x38, 0x87, 0x77, 0xf3, 0x7e, 0x3c, 0xfd, - 0x05, 0x97, 0xee, 0xfa, 0x48, 0x55, 0x97, 0xfe, 0x7d, 0xa7, 0xc7, 0xa3, - 0x96, 0x2c, 0xbf, 0xd3, 0xac, 0x2e, 0xe7, 0x96, 0x5d, 0x8d, 0xe5, 0x12, - 0xd1, 0xe5, 0x02, 0x1e, 0xd0, 0xa9, 0x83, 0x32, 0x18, 0xd4, 0xdd, 0x38, - 0x19, 0x8d, 0x5e, 0xff, 0xff, 0xa0, 0x0d, 0x0a, 0xfc, 0xc1, 0xcf, 0xdc, - 0x13, 0xa2, 0x98, 0xab, 0x2f, 0x34, 0x28, 0x56, 0x5f, 0xbe, 0xfa, 0x19, - 0xd5, 0x95, 0xc3, 0xc9, 0x00, 0xfd, 0xff, 0xe6, 0xd0, 0x56, 0xb7, 0x76, - 0x1c, 0x77, 0x1a, 0x37, 0xa3, 0xd6, 0x5f, 0xf8, 0x0f, 0xa8, 0x16, 0x72, - 0x74, 0xb2, 0xf8, 0x2e, 0xe1, 0x54, 0x58, 0xeb, 0xf4, 0xf8, 0x9c, 0x45, - 0x95, 0xa3, 0xd5, 0x61, 0x75, 0xfe, 0xc3, 0x2d, 0xbb, 0x9e, 0x59, 0x73, - 0xf1, 0x65, 0xfd, 0xb0, 0x9a, 0xd6, 0x69, 0x65, 0x40, 0xf1, 0x9c, 0x5a, - 0xff, 0x89, 0xc5, 0xf1, 0xc9, 0x6c, 0xb2, 0xfe, 0x2e, 0xe8, 0x12, 0xc5, - 0x97, 0xff, 0xe2, 0x71, 0x63, 0x74, 0x59, 0x28, 0x67, 0x8d, 0x8b, 0x29, - 0xd1, 0x08, 0x02, 0xdb, 0xfb, 0xf7, 0xef, 0x24, 0x55, 0x97, 0xf8, 0xfb, - 0x3a, 0x17, 0xc6, 0xb2, 0xff, 0xfa, 0x1b, 0x36, 0x93, 0x1c, 0xec, 0x1d, - 0x46, 0x8d, 0xe8, 0xf5, 0x97, 0xfd, 0x9b, 0xe6, 0x1f, 0x43, 0x3a, 0xb2, - 0xb1, 0x30, 0x27, 0x2f, 0xfc, 0xcc, 0x99, 0x6e, 0x38, 0x2c, 0xbf, 0xdd, - 0x2c, 0xdf, 0xe7, 0x82, 0xca, 0x19, 0xe5, 0x60, 0xb5, 0xf8, 0xfb, 0xd3, - 0xea, 0xcb, 0xff, 0xf0, 0x09, 0xc5, 0xe8, 0xb2, 0x50, 0xcf, 0x1b, 0x16, - 0x5f, 0x6b, 0x59, 0x1e, 0xb2, 0xfd, 0xaf, 0xcf, 0xd2, 0xb2, 0xb1, 0x19, - 0x2c, 0x27, 0x75, 0x50, 0x12, 0xdf, 0xb3, 0x59, 0x91, 0x2c, 0xb3, 0x79, - 0x5e, 0x3a, 0xda, 0x16, 0xb0, 0x22, 0x1b, 0x26, 0x42, 0x48, 0x52, 0x23, - 0x78, 0x88, 0x85, 0x90, 0xbc, 0xf4, 0x64, 0x85, 0x08, 0x6e, 0x43, 0xa8, - 0x43, 0xbb, 0xe1, 0x4f, 0x51, 0x2c, 0xbf, 0xd1, 0x13, 0xb5, 0xdc, 0xf2, - 0xcb, 0x98, 0x2a, 0xca, 0x6c, 0x1e, 0x63, 0x68, 0x69, 0x7e, 0x2c, 0xf3, - 0xf5, 0x65, 0xe8, 0xe1, 0x1d, 0x7e, 0xb2, 0x9b, 0x07, 0x9e, 0xda, 0x13, - 0x5f, 0xfc, 0x58, 0xd7, 0x30, 0xf9, 0xdc, 0xdd, 0x59, 0x7f, 0xd3, 0x11, - 0xf7, 0x86, 0x51, 0x2c, 0xbf, 0xcf, 0xff, 0x1f, 0xbd, 0x75, 0x97, 0x30, - 0x2b, 0x2f, 0xf9, 0xc6, 0x7f, 0x43, 0xa7, 0xa5, 0x94, 0x34, 0xc0, 0xb4, - 0x8c, 0x47, 0x3d, 0x33, 0xde, 0x2f, 0x7f, 0xcd, 0x6b, 0x09, 0xfe, 0x87, - 0x16, 0x5c, 0x42, 0xac, 0xbb, 0x06, 0xb2, 0xb0, 0xf9, 0xdc, 0xeb, 0x78, - 0xbd, 0xfb, 0xce, 0xc9, 0x25, 0x97, 0xdc, 0xf1, 0xb4, 0xb2, 0xfe, 0x0b, - 0xea, 0x3d, 0xfc, 0xb2, 0xfd, 0x2d, 0x77, 0x3c, 0xb2, 0xf4, 0x89, 0x8b, - 0x2a, 0x32, 0x2f, 0x70, 0x98, 0xc8, 0xc8, 0xc3, 0x85, 0x17, 0xed, 0x73, - 0xf6, 0xcf, 0x96, 0x5f, 0xfe, 0xcd, 0xcc, 0x0f, 0x7e, 0x86, 0x17, 0xee, - 0xb2, 0xff, 0xff, 0xe3, 0x2d, 0xf8, 0x32, 0x77, 0xdf, 0x9b, 0xbd, 0xdd, - 0x3e, 0x94, 0xfe, 0xb2, 0xf1, 0x83, 0xf5, 0x96, 0xfb, 0x75, 0x12, 0xb1, - 0x3c, 0x5e, 0x61, 0xe9, 0x65, 0x61, 0xe5, 0x7e, 0x5d, 0x7b, 0x1c, 0x96, - 0x5e, 0xf4, 0xc4, 0xb2, 0xe3, 0xe9, 0x9b, 0x77, 0x1a, 0xbe, 0x86, 0xe9, - 0xb1, 0x65, 0xf7, 0x1d, 0xf7, 0x56, 0x5f, 0xec, 0xdf, 0x9e, 0xdd, 0x32, - 0x73, 0xc9, 0xe9, 0x2d, 0xff, 0xff, 0x4e, 0x8b, 0x07, 0x9e, 0xf1, 0x87, - 0xc7, 0xde, 0x3f, 0xcb, 0x2f, 0xff, 0xd9, 0x16, 0x6e, 0x16, 0x6f, 0x2c, - 0xfd, 0x93, 0x05, 0x97, 0xfc, 0xff, 0x16, 0x77, 0x98, 0xd2, 0xca, 0xc5, - 0x6d, 0x21, 0x2e, 0x38, 0xcb, 0x37, 0x56, 0x74, 0xe7, 0xe4, 0xc2, 0x65, - 0x02, 0xcd, 0xfb, 0xc7, 0xa0, 0x41, 0x65, 0xff, 0x6c, 0x7e, 0x89, 0xe0, - 0x7e, 0x59, 0x7f, 0x4e, 0xbf, 0x8f, 0x07, 0x56, 0x5f, 0xe2, 0xec, 0x76, - 0xfd, 0x04, 0xac, 0xbf, 0xa0, 0x59, 0xf7, 0x5d, 0x65, 0x49, 0xf2, 0x39, - 0xbd, 0xff, 0x6f, 0x7d, 0xbb, 0xc7, 0x7d, 0xd5, 0x97, 0xff, 0x7a, 0x58, - 0x1f, 0x1f, 0x73, 0x18, 0xb2, 0xff, 0x38, 0xb9, 0xad, 0xa4, 0x55, 0x95, - 0x27, 0xf0, 0x48, 0x77, 0xdf, 0xe1, 0xba, 0xcb, 0xdf, 0x98, 0x8b, 0x2f, - 0xff, 0xff, 0x49, 0x77, 0x87, 0xe3, 0x60, 0xe7, 0xd9, 0xbf, 0x74, 0xc8, - 0x79, 0xb2, 0xca, 0x74, 0x5c, 0xfe, 0x43, 0xd1, 0xeb, 0xfe, 0x7d, 0x6d, - 0xe3, 0xcd, 0x44, 0xb2, 0xff, 0xe7, 0x72, 0xff, 0x99, 0xbb, 0x13, 0xba, - 0xca, 0x0a, 0x20, 0x22, 0x3a, 0xbf, 0x43, 0x9b, 0x63, 0x4b, 0x2f, 0xbd, - 0x9a, 0xd9, 0x65, 0xe7, 0x70, 0xaa, 0x2d, 0x05, 0xff, 0x9d, 0xad, 0xc0, - 0xbe, 0xb6, 0xc6, 0x96, 0x5f, 0xdf, 0xc9, 0x4f, 0x78, 0xb2, 0xa5, 0x1c, - 0xc3, 0x2a, 0x09, 0x1e, 0x8a, 0x3f, 0x44, 0xbc, 0x00, 0x69, 0x65, 0xe2, - 0x9d, 0x2c, 0xbb, 0x51, 0x44, 0x6e, 0x38, 0x3b, 0x7f, 0xff, 0xfb, 0x5e, - 0x72, 0x39, 0xe9, 0xfa, 0x79, 0xec, 0xe6, 0xb0, 0x39, 0xd5, 0x95, 0x88, - 0xa3, 0x32, 0xfb, 0xf0, 0xe6, 0x23, 0x62, 0xcb, 0xf0, 0xf3, 0xf7, 0x95, - 0x97, 0xff, 0xfe, 0x7d, 0x43, 0x86, 0x3c, 0xd6, 0xc7, 0xcf, 0x3f, 0xfc, - 0xc2, 0x59, 0x60, 0x62, 0x24, 0xf8, 0x4f, 0x7e, 0xd9, 0x8e, 0xe4, 0xb2, - 0xff, 0x9f, 0xfc, 0x3e, 0x72, 0x7f, 0x59, 0x52, 0x7c, 0x1c, 0x27, 0xbf, - 0xf0, 0xe3, 0xf3, 0x83, 0xf1, 0xeb, 0x65, 0x97, 0xff, 0xd8, 0xd7, 0x59, - 0x39, 0xdf, 0x66, 0x10, 0x56, 0x54, 0xa2, 0x47, 0xc8, 0x77, 0xff, 0xe3, - 0xec, 0x33, 0x5a, 0x78, 0x08, 0x4f, 0xde, 0x2c, 0xbf, 0xd1, 0x7a, 0x7b, - 0xe9, 0x89, 0x65, 0x4b, 0x21, 0x23, 0x62, 0x88, 0x1d, 0x0e, 0x12, 0xd8, - 0x40, 0x18, 0x5b, 0x1c, 0x36, 0x77, 0x61, 0x61, 0xa8, 0xc2, 0xbd, 0x19, - 0x2b, 0x90, 0x94, 0x2e, 0xf9, 0x08, 0x8e, 0xc2, 0xcb, 0x79, 0x10, 0x8a, - 0xb7, 0xfc, 0x0e, 0xf0, 0xb3, 0xee, 0xba, 0xcb, 0x62, 0xcb, 0xff, 0xc3, - 0xf4, 0xc9, 0xf7, 0x86, 0x5f, 0x41, 0x65, 0x80, 0x33, 0xd9, 0x21, 0x0b, - 0xb1, 0x8b, 0x2f, 0xff, 0x85, 0x3d, 0x67, 0xdd, 0xf1, 0x89, 0x23, 0x95, - 0x97, 0xfe, 0xfb, 0x7f, 0xb7, 0x4f, 0xa5, 0x2c, 0x59, 0x58, 0x89, 0x57, - 0x51, 0xa1, 0x53, 0x85, 0x38, 0x49, 0x68, 0x9d, 0xe1, 0x57, 0x7f, 0x49, - 0x76, 0x33, 0x36, 0x59, 0x7f, 0xd1, 0x14, 0xb3, 0xd3, 0xbd, 0x8b, 0x2f, - 0xdd, 0x38, 0xfc, 0xd9, 0x65, 0xfe, 0xe6, 0x78, 0xf8, 0x0d, 0x96, 0x5f, - 0x1c, 0x7e, 0x6c, 0xb2, 0xe7, 0xfa, 0x32, 0x21, 0x38, 0x57, 0xd3, 0x4a, - 0xc4, 0x7e, 0xf2, 0x17, 0xf6, 0x31, 0xa6, 0xa9, 0xd8, 0xcb, 0x6f, 0xfb, - 0xc7, 0xde, 0x3f, 0xdf, 0xb4, 0xb2, 0xf0, 0xdf, 0x65, 0x97, 0x6b, 0x60, - 0x9e, 0xc8, 0x87, 0x97, 0xff, 0xc0, 0xdb, 0x47, 0xdd, 0x81, 0xa8, 0x73, - 0x74, 0xd6, 0x57, 0x93, 0x0e, 0x78, 0x44, 0x11, 0x85, 0xff, 0x01, 0xfa, - 0xf0, 0x7f, 0xa0, 0xb2, 0xfc, 0xe2, 0x67, 0xdd, 0x59, 0x41, 0x3e, 0x2e, - 0x1c, 0xdf, 0xe3, 0x1e, 0x32, 0x36, 0x0d, 0x65, 0xcf, 0xc5, 0x97, 0xe3, - 0x69, 0xb2, 0xd8, 0x6c, 0xac, 0xbf, 0xfe, 0x06, 0xb5, 0x25, 0x82, 0x9f, - 0xbd, 0x82, 0x2c, 0xb3, 0x6d, 0x65, 0x68, 0xf9, 0x40, 0xa1, 0x7d, 0x3a, - 0x98, 0x96, 0x5e, 0x29, 0x62, 0xcb, 0x4a, 0xca, 0x88, 0xd5, 0xb0, 0x6e, - 0xff, 0x30, 0x05, 0xde, 0x03, 0x4b, 0x2a, 0x09, 0xd4, 0x8c, 0xd0, 0x51, - 0x6d, 0x42, 0x69, 0x84, 0x44, 0x9b, 0x1e, 0x45, 0x7d, 0xb9, 0xac, 0xc5, - 0x97, 0xa4, 0x85, 0x59, 0x7f, 0x17, 0xd0, 0xe8, 0x06, 0xb2, 0xfd, 0xbb, - 0x84, 0xc3, 0x59, 0x52, 0x7b, 0x0e, 0x5f, 0x78, 0xb3, 0xf5, 0x97, 0xfb, - 0x3d, 0xc8, 0xb3, 0x36, 0x59, 0x66, 0xb4, 0x7a, 0x1d, 0x1c, 0xbf, 0x9a, - 0xe9, 0x4b, 0x38, 0xb2, 0x9c, 0xf5, 0xc0, 0x53, 0x7e, 0x8b, 0xd9, 0xfb, - 0xac, 0xa9, 0x4e, 0xa6, 0x04, 0x9e, 0x74, 0x28, 0x63, 0xef, 0x21, 0xbf, - 0xff, 0xfa, 0x47, 0xec, 0xef, 0x5f, 0x6c, 0xeb, 0x0c, 0x05, 0x11, 0x63, - 0x4b, 0x2f, 0xff, 0xdd, 0xe4, 0xc5, 0xe9, 0xde, 0x58, 0x3f, 0x4f, 0x16, - 0x5f, 0xfe, 0x61, 0xfd, 0x0e, 0x16, 0x07, 0xa0, 0x25, 0x97, 0xfb, 0x99, - 0xbb, 0xfb, 0xbc, 0x4b, 0x2b, 0x11, 0xb0, 0xc5, 0x7f, 0x25, 0x54, 0xa6, - 0xf3, 0x91, 0xa5, 0x5f, 0x88, 0x2f, 0xc3, 0x59, 0x7f, 0xff, 0x68, 0xa6, - 0x7e, 0x8d, 0xce, 0x94, 0x8f, 0xd3, 0xd5, 0x97, 0x1f, 0x96, 0x5f, 0x40, - 0x9c, 0x45, 0x97, 0xf8, 0x6f, 0xfe, 0xb4, 0xff, 0xac, 0xbe, 0xd0, 0x77, - 0x5a, 0x59, 0x7c, 0x2b, 0x61, 0xda, 0x59, 0x58, 0x79, 0xee, 0x4f, 0x46, - 0x8a, 0x42, 0x84, 0x25, 0x4a, 0x70, 0x98, 0x4d, 0xba, 0xb8, 0xe2, 0xc0, - 0x86, 0x35, 0xff, 0xda, 0x2c, 0xdf, 0xac, 0x3f, 0x48, 0xd6, 0x5e, 0x7d, - 0xd3, 0x59, 0x7f, 0xe1, 0x3a, 0x29, 0x8b, 0x38, 0x52, 0xb2, 0xdf, 0x2c, - 0xbf, 0xfc, 0x6e, 0xf1, 0x77, 0xd9, 0x0f, 0x1e, 0xf5, 0x95, 0x27, 0xbb, - 0xba, 0x25, 0x7e, 0x13, 0xa5, 0x3f, 0xac, 0xa9, 0x4d, 0x2f, 0xe4, 0x4d, - 0x0f, 0x32, 0x13, 0x9b, 0xc9, 0x2f, 0xd3, 0xdc, 0xf4, 0xac, 0xbf, 0x7f, - 0x3d, 0xc6, 0x2c, 0xbf, 0xff, 0xff, 0xcc, 0xdd, 0x3f, 0x7f, 0x8e, 0x41, - 0xcf, 0xa1, 0xc3, 0xef, 0x0f, 0xe8, 0x14, 0xfa, 0x56, 0x5f, 0xfb, 0x3e, - 0x87, 0x0f, 0xae, 0x58, 0xb2, 0xff, 0x63, 0x3d, 0x91, 0x3c, 0x4b, 0x2f, - 0xf8, 0xc7, 0x87, 0xb7, 0xa6, 0x63, 0x1f, 0x8f, 0x4f, 0x6b, 0x13, 0x52, - 0xf4, 0x60, 0x57, 0xff, 0xfe, 0x32, 0xce, 0xcb, 0x1a, 0x03, 0xed, 0x1b, - 0x3c, 0x7a, 0xf3, 0xac, 0xbf, 0xfe, 0xcd, 0x9c, 0x66, 0x00, 0xe8, 0xda, - 0x88, 0xd6, 0x54, 0xa2, 0xfb, 0x4d, 0xb7, 0xfe, 0xcf, 0xa1, 0xc8, 0xb4, - 0xfd, 0xe2, 0xcb, 0xf1, 0x48, 0x60, 0xc5, 0x97, 0xff, 0xb3, 0x77, 0xc0, - 0x8b, 0xd9, 0x14, 0x27, 0xe5, 0x97, 0xff, 0x73, 0x22, 0xf4, 0xef, 0xcf, - 0x7a, 0x56, 0x50, 0xd1, 0x7b, 0xa2, 0x7e, 0xa8, 0x5f, 0xe7, 0xff, 0x8c, - 0x7f, 0xba, 0xb2, 0xb0, 0xf9, 0x5c, 0xc2, 0xff, 0xe2, 0xcd, 0xb5, 0x33, - 0xd3, 0xf4, 0xac, 0xbf, 0xd3, 0x31, 0x66, 0xf3, 0xd2, 0xcb, 0xe3, 0xd6, - 0x12, 0xca, 0x23, 0xd4, 0xe1, 0xa5, 0xff, 0xf8, 0xc1, 0xf7, 0xb1, 0xa0, - 0x61, 0xfb, 0x93, 0xb2, 0xca, 0xd9, 0x74, 0xeb, 0x09, 0x83, 0x1b, 0xd6, - 0xa1, 0xcf, 0xf9, 0x11, 0x46, 0xc7, 0xc2, 0x0e, 0xc2, 0x5b, 0x70, 0x86, - 0xfa, 0x28, 0x1c, 0x4b, 0x2e, 0x2d, 0x96, 0x54, 0x63, 0x77, 0x1c, 0x92, - 0x5b, 0x71, 0x65, 0xde, 0xdc, 0x59, 0x7f, 0xfb, 0x37, 0xf9, 0xe1, 0xfb, - 0xfd, 0xf9, 0xfc, 0xb2, 0xdd, 0x93, 0xe9, 0xf0, 0xd5, 0xff, 0xf6, 0xb6, - 0x9d, 0xde, 0x16, 0x77, 0xa7, 0xa0, 0xac, 0xbf, 0xb3, 0x6e, 0x16, 0x6f, - 0x59, 0x50, 0x4c, 0x98, 0xdf, 0x5c, 0x9f, 0xaa, 0x75, 0x2b, 0xfd, 0x79, - 0x3b, 0x18, 0x70, 0xd5, 0x78, 0xd3, 0xaf, 0xfe, 0xf1, 0xc6, 0xc2, 0xc3, - 0xd3, 0xef, 0x59, 0x7f, 0xf1, 0x93, 0x8c, 0xb0, 0x7e, 0x9e, 0x2c, 0xbc, - 0x24, 0xe9, 0x65, 0xff, 0x6c, 0xc9, 0xd7, 0x4a, 0x76, 0x59, 0x7d, 0xbe, - 0x48, 0x2b, 0x2f, 0xff, 0xe9, 0x2c, 0xff, 0x98, 0x3f, 0x4e, 0xcc, 0x01, - 0x0d, 0x65, 0x19, 0xff, 0x78, 0x8e, 0xfc, 0x7d, 0xec, 0xc1, 0x65, 0x0a, - 0x9d, 0x18, 0x51, 0x74, 0x82, 0xe3, 0xbd, 0x85, 0xc6, 0xf2, 0x1b, 0xf8, - 0xbb, 0x3c, 0xe9, 0xac, 0xbf, 0xff, 0xa6, 0x2c, 0x1f, 0xa7, 0x7f, 0xb3, - 0xa5, 0x32, 0xc5, 0x97, 0xf7, 0xdc, 0x13, 0x60, 0x12, 0xcb, 0xfe, 0x90, - 0xf8, 0xe2, 0x77, 0xd9, 0x65, 0x4a, 0x3c, 0x58, 0x58, 0xeb, 0x7f, 0x98, - 0xdf, 0xe8, 0x4e, 0xb6, 0x9d, 0x6c, 0xb2, 0xff, 0xb7, 0xb9, 0x7f, 0xec, - 0xfd, 0xd6, 0x5f, 0xfe, 0xcc, 0xfd, 0x93, 0x17, 0x07, 0xe7, 0xd9, 0x65, - 0x1a, 0x21, 0xc8, 0xee, 0xf9, 0xfc, 0x64, 0xb2, 0xfb, 0x3f, 0x92, 0x59, - 0x62, 0xd8, 0xf0, 0x7c, 0x41, 0x7f, 0xc6, 0x02, 0x86, 0xa7, 0x06, 0xb2, - 0xff, 0xff, 0x64, 0x03, 0xe3, 0xf4, 0xe6, 0xa1, 0xd2, 0x96, 0x71, 0x65, - 0xff, 0xb4, 0xfb, 0x02, 0x70, 0x81, 0xb2, 0xca, 0xf2, 0x27, 0xb8, 0xbf, - 0x7f, 0xc0, 0xee, 0xef, 0x30, 0xc8, 0x6b, 0x2f, 0x81, 0x3f, 0x0d, 0x65, - 0x0d, 0x3e, 0x07, 0x62, 0xe1, 0x47, 0x61, 0xab, 0xbc, 0x8d, 0xa3, 0xbb, - 0xed, 0xc9, 0xd6, 0xcb, 0x2a, 0x55, 0x90, 0xe4, 0xa9, 0xc7, 0x5f, 0xbf, - 0xda, 0x0c, 0x58, 0x7e, 0x02, 0xcb, 0xff, 0xff, 0x79, 0xc5, 0xe0, 0xcf, - 0x3e, 0x09, 0x93, 0xfc, 0xc9, 0x9e, 0x2c, 0xbf, 0xf1, 0xef, 0x2c, 0xe7, - 0x45, 0x30, 0xac, 0xbf, 0xf1, 0x6f, 0xc1, 0xc5, 0x98, 0x42, 0xac, 0xbf, - 0xdd, 0xf6, 0xe9, 0xf9, 0xe2, 0x59, 0x7f, 0x48, 0x49, 0xfe, 0xea, 0xca, - 0x82, 0x39, 0x0d, 0x03, 0x75, 0x01, 0x86, 0xf7, 0xe9, 0xee, 0xd8, 0xd2, - 0xcb, 0xff, 0x7c, 0x12, 0x7d, 0x9e, 0x07, 0xe5, 0x95, 0xa3, 0xe8, 0x01, - 0x4d, 0xf8, 0x61, 0x06, 0x6e, 0xac, 0xbf, 0xf1, 0x4f, 0xde, 0x93, 0x27, - 0x1a, 0xca, 0x88, 0xf9, 0xd8, 0x57, 0x7d, 0xcc, 0x21, 0x56, 0x5f, 0x4e, - 0x41, 0xd6, 0x57, 0xc7, 0x85, 0xe2, 0x2a, 0x94, 0x44, 0x0d, 0x96, 0xff, - 0x0f, 0x7c, 0x97, 0x73, 0xf5, 0x97, 0xe9, 0xef, 0xa7, 0xab, 0x2e, 0x2f, - 0xd6, 0x56, 0xc7, 0xe7, 0xa3, 0x57, 0x27, 0xbf, 0xff, 0xfc, 0x0d, 0x7b, - 0x22, 0x89, 0xf5, 0x17, 0x7c, 0x0f, 0xf9, 0x87, 0xbc, 0x72, 0xb2, 0xff, - 0x8f, 0xdc, 0xd1, 0x94, 0xb1, 0x65, 0xfd, 0x3c, 0xdd, 0x3f, 0xba, 0xb2, - 0xfe, 0x9e, 0x44, 0x59, 0x12, 0xca, 0x34, 0xc5, 0x37, 0x5f, 0xb4, 0x6f, - 0xe3, 0x1b, 0xf7, 0x7c, 0xe4, 0x2a, 0xcb, 0xfd, 0xb8, 0x40, 0x00, 0x4c, - 0x55, 0x97, 0xe3, 0x69, 0x8f, 0xd5, 0x97, 0xf8, 0x1b, 0x0f, 0xd8, 0x52, - 0xb2, 0xa5, 0x12, 0x41, 0x37, 0x22, 0x8b, 0xff, 0xb0, 0x7e, 0x96, 0xbe, - 0x87, 0x40, 0x35, 0x97, 0x67, 0x16, 0x5c, 0xfd, 0x59, 0x7b, 0x80, 0x86, - 0x8d, 0x67, 0x85, 0xad, 0x83, 0x4f, 0x3b, 0x10, 0x03, 0x0b, 0xa2, 0x2e, - 0xeb, 0x9d, 0xff, 0xec, 0x92, 0xc0, 0x96, 0x74, 0x0e, 0xd2, 0xcb, 0xfe, - 0xcf, 0xe3, 0xf1, 0xc7, 0x9c, 0x59, 0x7f, 0xf1, 0xf7, 0xd8, 0x70, 0xe6, - 0x7d, 0xd5, 0x94, 0x34, 0x63, 0x12, 0x3f, 0x0e, 0xef, 0xfe, 0xfc, 0x3f, - 0x96, 0x77, 0x8e, 0xe4, 0xb2, 0xf3, 0x1f, 0xe5, 0x95, 0xa3, 0xe1, 0xfd, - 0x12, 0xa5, 0x91, 0x5c, 0x33, 0x4c, 0x8d, 0x40, 0xe1, 0x4f, 0xe8, 0xc4, - 0x5e, 0x12, 0xa5, 0x2c, 0x47, 0xb1, 0x8c, 0x82, 0x12, 0x17, 0xff, 0xf4, - 0xb3, 0xf7, 0xf6, 0x7f, 0xfb, 0xeb, 0x37, 0xfb, 0x16, 0x5f, 0xe3, 0xef, - 0xe6, 0x50, 0xe2, 0xcb, 0xfb, 0xd1, 0x1f, 0x8d, 0x8b, 0x2e, 0xff, 0x8b, - 0x2b, 0x0f, 0x18, 0xcb, 0xaf, 0xf4, 0x33, 0x5c, 0x29, 0xd9, 0x65, 0xf0, - 0xba, 0x96, 0x2c, 0xbb, 0x36, 0x59, 0x52, 0x6e, 0xa3, 0xc8, 0xef, 0xf1, - 0x97, 0x61, 0x9a, 0xc5, 0x97, 0xf4, 0x87, 0x47, 0x2c, 0x59, 0x50, 0x3d, - 0xd2, 0x31, 0xbf, 0xfc, 0xcf, 0xdf, 0x59, 0xbf, 0x0c, 0x7e, 0x35, 0x95, - 0x27, 0xd9, 0x84, 0x37, 0xb3, 0xce, 0xb2, 0xf0, 0x00, 0x4b, 0x2f, 0xc7, - 0xef, 0x60, 0xd6, 0x5a, 0x25, 0x83, 0x3c, 0x3e, 0x0e, 0x50, 0xd1, 0x1c, - 0xeb, 0xd7, 0xf6, 0xc5, 0x9f, 0x83, 0x8b, 0x2f, 0xf7, 0xf1, 0xf8, 0xe3, - 0xce, 0x2c, 0xbd, 0xba, 0x6c, 0x59, 0x7f, 0xfc, 0xff, 0xf2, 0x7f, 0xee, - 0x18, 0xf0, 0xa2, 0x59, 0xe2, 0xfe, 0xa5, 0x1c, 0x78, 0x5e, 0xea, 0xb7, - 0xf3, 0x58, 0x10, 0xe3, 0x4b, 0x2f, 0x48, 0x77, 0xac, 0xac, 0x3c, 0xde, - 0x97, 0xdf, 0xfe, 0xcd, 0xcf, 0xd9, 0x31, 0x74, 0xc1, 0xae, 0x2c, 0xbf, - 0xcc, 0xcc, 0x2e, 0xc7, 0xe2, 0xcb, 0xff, 0xb9, 0xd9, 0x9d, 0x7f, 0x11, - 0x63, 0x4b, 0x2b, 0xe3, 0xfa, 0x73, 0x4b, 0xc2, 0xc0, 0x0b, 0x2f, 0xde, - 0x9d, 0x6c, 0xc5, 0x97, 0xf8, 0x0e, 0x5e, 0xc7, 0x89, 0x65, 0xbe, 0x88, - 0xf6, 0xda, 0x29, 0xa3, 0x45, 0x23, 0xbc, 0xd6, 0x27, 0xa2, 0x64, 0x2f, - 0x0c, 0x92, 0x86, 0x35, 0x4a, 0xf4, 0x94, 0x17, 0xb1, 0xe7, 0xe2, 0x03, - 0x6d, 0xf4, 0x3b, 0x1e, 0x15, 0xc5, 0x18, 0x2f, 0x25, 0x25, 0xdc, 0x02, - 0x59, 0x7d, 0xf7, 0x27, 0xe5, 0x94, 0xdb, 0x37, 0x44, 0x2d, 0x78, 0xb3, - 0xf5, 0x96, 0x95, 0x95, 0xf9, 0xac, 0xe8, 0xe5, 0xcf, 0xd5, 0x95, 0xa3, - 0x71, 0xe2, 0x2b, 0xe6, 0x4e, 0xb8, 0xb2, 0xff, 0x4b, 0x0b, 0x3f, 0x07, - 0x16, 0x5f, 0xf6, 0xb3, 0xd9, 0xb4, 0x63, 0xe2, 0xca, 0xc4, 0x58, 0x99, - 0x09, 0x11, 0x00, 0xce, 0xff, 0x40, 0xf9, 0xc9, 0xd4, 0x16, 0x5f, 0x3b, - 0x24, 0xd6, 0x50, 0xcf, 0x4e, 0x23, 0x3b, 0xf8, 0xc8, 0x10, 0xce, 0x2c, - 0xbf, 0xff, 0x40, 0xfb, 0xc0, 0xf8, 0xfb, 0xf4, 0x0f, 0xbc, 0x59, 0x7f, - 0xdd, 0x9f, 0xbf, 0x7f, 0x78, 0xd6, 0x5f, 0x1c, 0x7e, 0x6c, 0xb2, 0xb0, - 0xf7, 0xfa, 0x75, 0x66, 0x2c, 0xbf, 0x8c, 0x81, 0x0c, 0xe4, 0x63, 0x65, - 0xbc, 0x86, 0xa5, 0x37, 0xd1, 0x48, 0xe2, 0x2c, 0xec, 0x3a, 0x2f, 0xfe, - 0xd0, 0x3e, 0x87, 0x3d, 0x9b, 0x00, 0x96, 0x59, 0xbc, 0x74, 0xf8, 0x45, - 0xb1, 0xcb, 0x84, 0x76, 0xf5, 0x31, 0x96, 0xed, 0x0b, 0x38, 0x43, 0xb0, - 0x72, 0xec, 0xb2, 0x91, 0x82, 0x2c, 0xa9, 0x30, 0xca, 0xa9, 0xfa, 0x12, - 0xc7, 0x1e, 0x5c, 0x52, 0x85, 0xb5, 0x1e, 0x4b, 0x23, 0xac, 0xf5, 0x26, - 0x19, 0xe3, 0xf2, 0xfe, 0x35, 0x02, 0x96, 0xbb, 0xca, 0x47, 0x27, 0x67, - 0xcd, 0xc1, 0x0c, 0xe8, 0xf8, 0xce, 0x44, 0x94, 0x15, 0xb8, 0x89, 0x7f, - 0x69, 0xb8, 0xf7, 0x9c, 0xac, 0xbf, 0xb7, 0x0c, 0x65, 0x31, 0x2c, 0xbd, - 0x2e, 0x4b, 0x2f, 0xb3, 0xcf, 0xd5, 0x96, 0x6c, 0x61, 0xf5, 0xf8, 0xc0, - 0x86, 0xaf, 0xff, 0x4e, 0x81, 0x14, 0x47, 0xe3, 0x63, 0x8d, 0x65, 0xfc, - 0x4f, 0xde, 0x4e, 0xea, 0xcb, 0xff, 0xff, 0x9c, 0xbb, 0xc9, 0x38, 0x66, - 0xe1, 0x76, 0x7d, 0x22, 0x82, 0x77, 0x16, 0x50, 0x51, 0xde, 0x69, 0x7e, - 0x2f, 0xbe, 0xce, 0x7e, 0x6b, 0x2e, 0x87, 0xcb, 0x2f, 0x82, 0xee, 0x15, - 0x45, 0xac, 0xa3, 0x3c, 0x6d, 0x0c, 0x5f, 0xf4, 0x00, 0x7e, 0xd9, 0xc8, - 0x6b, 0x2f, 0xf3, 0x0d, 0x8f, 0x16, 0x79, 0x65, 0xfb, 0xfc, 0xdc, 0x2e, - 0xac, 0xbf, 0xc0, 0x2c, 0xdc, 0x36, 0x8d, 0x65, 0xf7, 0x1c, 0x82, 0xb2, - 0xbc, 0x7a, 0xdb, 0x86, 0xb5, 0x28, 0xa6, 0xc8, 0x40, 0xde, 0xd4, 0xb1, - 0x65, 0xfd, 0xec, 0xe4, 0xed, 0xb8, 0xb2, 0xff, 0xa0, 0xdf, 0x34, 0x17, - 0x70, 0xaa, 0x28, 0x35, 0x61, 0xfc, 0x39, 0x8d, 0xf6, 0x45, 0x3d, 0x59, - 0x7f, 0xb9, 0x84, 0x17, 0x2f, 0xd6, 0x5f, 0xf4, 0xc2, 0x75, 0xb4, 0xeb, - 0x65, 0x97, 0xda, 0x31, 0x58, 0xb2, 0xf7, 0x73, 0xcb, 0x2b, 0x0d, 0xf8, - 0xa4, 0x77, 0xfb, 0xcd, 0xe7, 0xf0, 0x14, 0x4b, 0x2f, 0xcf, 0xc6, 0x4f, - 0xeb, 0x2f, 0xe9, 0x8b, 0xf7, 0x28, 0x96, 0x5f, 0x3f, 0xf9, 0xa5, 0x95, - 0x88, 0xb0, 0x14, 0xdc, 0xca, 0x3c, 0x5f, 0x7f, 0xa2, 0x3e, 0x82, 0x23, - 0xea, 0xcb, 0xff, 0xee, 0x1e, 0xcf, 0xc7, 0x27, 0xef, 0xa6, 0x25, 0x97, - 0xfe, 0x72, 0xff, 0x3b, 0x24, 0x7f, 0xac, 0xae, 0xa2, 0x32, 0x3d, 0x3e, - 0xcd, 0xe3, 0xb5, 0xe8, 0xb9, 0x8c, 0x9a, 0x05, 0xf8, 0xd1, 0xf1, 0x09, - 0x9c, 0xc5, 0x0d, 0x9d, 0x13, 0x32, 0x12, 0x9e, 0x20, 0x72, 0x22, 0x32, - 0xe3, 0xcf, 0x61, 0xa2, 0x03, 0xc1, 0x21, 0x83, 0x7f, 0x85, 0x6e, 0xfe, - 0xf6, 0x6c, 0xb2, 0xff, 0xf3, 0x76, 0x3c, 0x1b, 0xe6, 0x82, 0xee, 0x15, - 0x45, 0x16, 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0xea, 0x54, 0xbe, - 0xcc, 0xc4, 0x23, 0x4d, 0xc5, 0x20, 0xc3, 0x88, 0xe5, 0x58, 0xb2, 0x79, - 0x97, 0xd7, 0x86, 0x12, 0xf1, 0xff, 0x7f, 0x3e, 0x4e, 0x51, 0x80, 0xf0, - 0xdf, 0xab, 0x57, 0xed, 0x05, 0xdc, 0x2a, 0x88, 0x85, 0x7b, 0xc6, 0x4b, - 0x2e, 0xc1, 0xac, 0xb0, 0x56, 0x50, 0x4f, 0x0b, 0xc3, 0x64, 0x2d, 0x7d, - 0x9b, 0xcf, 0x4b, 0x2f, 0x8f, 0x59, 0xf2, 0xca, 0xc4, 0x74, 0xfc, 0xed, - 0xba, 0x5d, 0x1e, 0x47, 0x7f, 0xba, 0xde, 0x22, 0x9f, 0xf8, 0xb2, 0x9b, - 0x9f, 0xd6, 0x21, 0xda, 0x3d, 0x65, 0xf4, 0xfa, 0x58, 0xb2, 0xf8, 0x2e, - 0xe1, 0x54, 0x46, 0xca, 0x09, 0xe7, 0xe8, 0x86, 0xcd, 0xc6, 0x88, 0x5c, - 0x63, 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x9b, 0x5c, 0xf0, 0x59, - 0x7c, 0x17, 0x70, 0xaa, 0x2a, 0x05, 0x0c, 0xf1, 0x34, 0x2d, 0x7b, 0x3e, - 0x69, 0x65, 0x9b, 0xe1, 0xe0, 0x78, 0x8a, 0xff, 0x37, 0xcd, 0x05, 0xdc, - 0x2a, 0x8a, 0x99, 0x78, 0x07, 0xa5, 0x97, 0xed, 0x05, 0xdc, 0x2a, 0x8a, - 0xc1, 0x7e, 0x36, 0x86, 0x0d, 0x2c, 0xb6, 0xcb, 0x2e, 0x12, 0x0b, 0x2f, - 0x39, 0x05, 0x65, 0x49, 0xe2, 0xfe, 0x25, 0xd1, 0x8b, 0xb8, 0x4b, 0x2e, - 0x18, 0xab, 0x2e, 0x67, 0x16, 0x5f, 0x9e, 0x1d, 0x3d, 0x96, 0x5f, 0xb9, - 0xc2, 0xcf, 0xd6, 0x51, 0x9e, 0x83, 0x94, 0x5f, 0xc7, 0x1e, 0xf2, 0x71, - 0xeb, 0x2f, 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x8f, 0xd7, 0x03, 0x8b, - 0x2f, 0x14, 0x8d, 0x65, 0x4a, 0x29, 0x20, 0x62, 0x34, 0x13, 0x17, 0xbe, - 0xcc, 0x28, 0x2c, 0xb4, 0xac, 0xba, 0x76, 0x59, 0x50, 0x34, 0xf8, 0x21, - 0x7d, 0x31, 0x03, 0x4b, 0x2f, 0xef, 0xa0, 0xf0, 0x32, 0x59, 0x7a, 0x4a, - 0x25, 0x97, 0xd3, 0xc3, 0x89, 0x65, 0x61, 0xf5, 0x8a, 0x5b, 0xa1, 0xcb, - 0x9f, 0xab, 0x2e, 0x66, 0xe2, 0xcb, 0xbb, 0xd5, 0x96, 0x6e, 0x35, 0x7d, - 0x98, 0x38, 0x13, 0x4f, 0x9b, 0x8c, 0xbf, 0x74, 0x59, 0x83, 0x1e, 0x67, - 0x78, 0x62, 0x7e, 0x76, 0x48, 0xfc, 0x20, 0xec, 0x23, 0xa3, 0xcb, 0xc4, - 0x16, 0xdc, 0x1a, 0xbf, 0xfc, 0xdd, 0x8f, 0x06, 0xf9, 0xa0, 0xbb, 0x85, - 0x51, 0x37, 0xaf, 0xfe, 0x63, 0xc1, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4f, - 0xcb, 0xf4, 0x75, 0x1d, 0x47, 0x4d, 0x98, 0xe9, 0x65, 0xfc, 0xec, 0x96, - 0x48, 0x8b, 0x2e, 0x78, 0x2c, 0xa6, 0xca, 0x21, 0x23, 0xa4, 0x16, 0xd7, - 0x2d, 0xb8, 0x46, 0x2c, 0xbf, 0xb0, 0x53, 0xe9, 0x4a, 0xca, 0x09, 0xe2, - 0x47, 0x8c, 0x5f, 0xb5, 0x3e, 0x04, 0xac, 0xbf, 0x7b, 0x3b, 0x23, 0x59, - 0x4d, 0x83, 0xf3, 0x32, 0x57, 0x27, 0xbf, 0xd1, 0xc2, 0x3a, 0x38, 0xff, - 0x1b, 0x6d, 0x65, 0xc4, 0x15, 0x97, 0xdc, 0x89, 0x9c, 0x59, 0x7e, 0x2e, - 0xf8, 0xd8, 0xb2, 0xfa, 0x20, 0x38, 0x8b, 0x2a, 0x4f, 0x2d, 0xc9, 0xee, - 0x08, 0x56, 0x5f, 0xe0, 0xf8, 0xfb, 0x98, 0xc5, 0x97, 0x6f, 0x0a, 0xe3, - 0x02, 0x54, 0x70, 0x4d, 0x66, 0x51, 0x4c, 0x5a, 0x26, 0xff, 0xc8, 0x38, - 0x2f, 0xd3, 0x2b, 0xf3, 0x65, 0xb1, 0x1f, 0x9f, 0x2c, 0xbf, 0xa3, 0xa7, - 0xdb, 0xc7, 0xba, 0xb2, 0xff, 0xa3, 0x77, 0xd8, 0xcc, 0xd6, 0x2c, 0xbf, - 0x9b, 0x53, 0x65, 0xb2, 0xd8, 0xe4, 0xac, 0xbf, 0x47, 0x22, 0x8b, 0x9b, - 0xab, 0x2f, 0xee, 0x99, 0x35, 0x84, 0xb2, 0xed, 0xe1, 0x5c, 0x60, 0x2b, - 0x79, 0x65, 0xdf, 0xfe, 0xb2, 0xa3, 0xa4, 0xe4, 0x9b, 0x06, 0x91, 0xd9, - 0xb3, 0x6b, 0x9d, 0x47, 0x04, 0x0c, 0x32, 0xe9, 0x60, 0x09, 0x84, 0x11, - 0xbe, 0x83, 0x8c, 0xd6, 0x5e, 0xd3, 0xe2, 0xcb, 0xe7, 0x1e, 0x31, 0x65, - 0x36, 0x0f, 0x76, 0x39, 0x21, 0x8e, 0x23, 0x77, 0xd1, 0xde, 0xcc, 0xe2, - 0xcb, 0xfb, 0x22, 0x92, 0x3f, 0x96, 0x5f, 0xff, 0x89, 0xfb, 0x24, 0x13, - 0x60, 0xc0, 0xfa, 0x82, 0xcb, 0xf3, 0x5d, 0xec, 0xb1, 0x65, 0x47, 0x27, - 0xf9, 0xb2, 0x9d, 0xff, 0x3f, 0x8f, 0x7b, 0x76, 0x9a, 0x69, 0x25, 0xfe, - 0xf3, 0xf7, 0xae, 0xed, 0x2c, 0xbf, 0x60, 0x5f, 0x86, 0xb2, 0xa3, 0xb4, - 0xe4, 0xdb, 0x5c, 0xa6, 0x38, 0xc2, 0xa5, 0xb5, 0x14, 0x47, 0x04, 0x0f, - 0x19, 0xdf, 0xff, 0xcd, 0x8e, 0x6a, 0x4f, 0xbe, 0xc1, 0x9f, 0x30, 0xbf, - 0x59, 0x77, 0x25, 0x65, 0x36, 0x4f, 0xd2, 0x0b, 0xd7, 0xf6, 0xec, 0xe9, - 0xde, 0x0b, 0x2f, 0xde, 0x37, 0xfe, 0x38, 0x2c, 0xbd, 0x3f, 0x41, 0x65, - 0x47, 0x48, 0xa0, 0x6d, 0x09, 0xba, 0x5f, 0xb8, 0x5d, 0x7f, 0xe8, 0xe6, - 0x3a, 0x88, 0xa4, 0x79, 0xf7, 0x56, 0x5f, 0x60, 0xde, 0x0b, 0x2f, 0x68, - 0x1b, 0x8b, 0x2a, 0x39, 0x44, 0x5c, 0x71, 0x4a, 0x01, 0x0d, 0xf1, 0x61, - 0xfe, 0xb2, 0xf9, 0xb5, 0xff, 0x16, 0x56, 0x53, 0x60, 0xf3, 0x23, 0xb2, - 0x1b, 0xfe, 0x7e, 0xfd, 0x0e, 0xe6, 0x85, 0x59, 0x71, 0xfe, 0xb2, 0xa3, - 0x81, 0xe9, 0x11, 0xdd, 0xff, 0xa6, 0x66, 0x66, 0x67, 0xee, 0x2c, 0xbd, - 0x14, 0xf5, 0x65, 0xd3, 0x32, 0x7b, 0x11, 0x1d, 0xdf, 0xfe, 0xf4, 0xc5, - 0xf7, 0x33, 0xed, 0xd9, 0x92, 0x59, 0x78, 0xfe, 0xc5, 0x97, 0xed, 0x1e, - 0xce, 0xc4, 0x97, 0x34, 0xd2, 0x4a, 0xc3, 0xc1, 0x68, 0xa2, 0xc0, 0x48, - 0xdc, 0xd1, 0x5e, 0xf6, 0x05, 0x65, 0x4a, 0x67, 0x1e, 0x2c, 0xfd, 0x3b, - 0x8f, 0xe2, 0x11, 0xdf, 0x81, 0x1f, 0x20, 0xf9, 0x65, 0xf3, 0x94, 0xee, - 0x2c, 0xac, 0x3c, 0xf7, 0x2c, 0xbf, 0xf6, 0xe1, 0xee, 0xfa, 0x29, 0xdd, - 0x76, 0x2c, 0xbf, 0xf6, 0x32, 0x79, 0xcc, 0x84, 0x7e, 0x2c, 0xaf, 0x22, - 0x20, 0x92, 0x2f, 0xcc, 0x76, 0x60, 0xd6, 0x5f, 0xf7, 0x43, 0xe3, 0x22, - 0x9f, 0x96, 0x54, 0x9e, 0xfb, 0x93, 0xd1, 0xa2, 0x87, 0xd0, 0x81, 0xbf, - 0xd8, 0x33, 0xdf, 0xcf, 0xc2, 0xb2, 0xff, 0xff, 0xd9, 0xbc, 0xfb, 0x0c, - 0xff, 0x85, 0x9f, 0xea, 0x76, 0x7d, 0x05, 0x65, 0xd3, 0x07, 0x45, 0x2f, - 0x0d, 0xaf, 0xee, 0xf3, 0x33, 0x69, 0x59, 0x5e, 0x3d, 0xa2, 0x2d, 0xbe, - 0xe0, 0xfa, 0x05, 0x97, 0xff, 0xff, 0x6f, 0x9d, 0x60, 0xc9, 0xf4, 0x2b, - 0xf4, 0x1d, 0x3e, 0xf3, 0xc6, 0xb2, 0xff, 0x68, 0x04, 0x0e, 0x83, 0xab, - 0x2f, 0x7a, 0x49, 0x65, 0xa4, 0xcf, 0x3f, 0xa6, 0x97, 0xff, 0xe8, 0x49, - 0x14, 0xec, 0xd3, 0xed, 0xe3, 0x92, 0x59, 0x52, 0x9c, 0x00, 0x48, 0x4c, - 0x8f, 0xd0, 0xc2, 0x01, 0x35, 0xf1, 0xec, 0xec, 0x59, 0x78, 0x8e, 0x56, - 0x5f, 0xa4, 0x46, 0x78, 0xd6, 0x50, 0xcf, 0x93, 0x44, 0x44, 0x35, 0x7e, - 0x7d, 0xa5, 0xc2, 0xb2, 0xfe, 0x12, 0x34, 0xf2, 0x58, 0xb2, 0xbc, 0x7a, - 0xee, 0x4f, 0x7f, 0x43, 0x9d, 0xc2, 0x15, 0x65, 0xf7, 0x1e, 0x1b, 0xab, - 0x2f, 0xfd, 0x0e, 0x4c, 0x27, 0xfe, 0x66, 0xf5, 0x97, 0xff, 0x48, 0x73, - 0x59, 0xe3, 0xd7, 0x9d, 0x65, 0xe6, 0x9d, 0xa5, 0x97, 0xff, 0x4e, 0xa0, - 0x1f, 0x1c, 0x44, 0xed, 0x2c, 0xbf, 0xfb, 0x4f, 0xb0, 0xfd, 0x3c, 0xe8, - 0x3c, 0xb2, 0xb8, 0x88, 0xbe, 0xa2, 0xd9, 0x9a, 0x4c, 0x3b, 0xc8, 0x5c, - 0x85, 0x4d, 0xfe, 0x31, 0x25, 0xa1, 0xe3, 0x16, 0x54, 0xa7, 0x7f, 0x91, - 0xa7, 0xf8, 0xf2, 0xfe, 0x7f, 0xdc, 0x2f, 0xa5, 0x97, 0xff, 0xc0, 0x22, - 0x91, 0x59, 0xe9, 0xfb, 0xb3, 0xe5, 0x97, 0x86, 0xfe, 0x59, 0x7f, 0xd3, - 0xe9, 0xfa, 0x10, 0x9d, 0x96, 0x5f, 0x07, 0xc7, 0xa5, 0x97, 0xf6, 0xe9, - 0x66, 0xd8, 0xd2, 0xca, 0xc4, 0x54, 0xe8, 0x71, 0xce, 0x80, 0x45, 0x7f, - 0xfb, 0xc6, 0xc1, 0xcc, 0x68, 0xf0, 0x78, 0xf4, 0xb2, 0xfe, 0xec, 0xf8, - 0xa6, 0x0b, 0x2f, 0xf1, 0x38, 0xa7, 0xa9, 0x84, 0x63, 0xfb, 0x34, 0xfb, - 0x8f, 0xe5, 0x97, 0xfb, 0xf0, 0x67, 0xec, 0x78, 0x2c, 0xb3, 0xc0, 0xf3, - 0x3e, 0x17, 0xbf, 0xe3, 0xee, 0x6b, 0x40, 0xdb, 0x7a, 0xcb, 0xfe, 0xe6, - 0x74, 0x79, 0x9f, 0xf1, 0x65, 0x78, 0xfd, 0x7a, 0x79, 0x7f, 0xc7, 0xdc, - 0xd6, 0x81, 0xb6, 0xf5, 0x97, 0xb8, 0x02, 0x8c, 0x7b, 0xde, 0x22, 0xa8, - 0x2e, 0xdd, 0x8e, 0x3e, 0x4c, 0x35, 0x09, 0x6e, 0xa1, 0xf3, 0xe8, 0x5b, - 0x14, 0x25, 0xb9, 0x0f, 0xbb, 0xe6, 0x18, 0xe5, 0x65, 0xfc, 0x7e, 0xd6, - 0x8f, 0x65, 0x95, 0x27, 0x9f, 0xe2, 0x1b, 0x9a, 0xea, 0xcb, 0xff, 0x8c, - 0xfd, 0x16, 0x76, 0x7b, 0x23, 0x59, 0x69, 0x59, 0x7f, 0xfd, 0x3f, 0x41, - 0xcb, 0xf8, 0xdf, 0x9e, 0x8c, 0x55, 0x95, 0x28, 0xbf, 0x88, 0x62, 0x3d, - 0x0f, 0x70, 0x42, 0xff, 0x4f, 0xd1, 0x7a, 0x48, 0x55, 0x97, 0x14, 0x4b, - 0x2f, 0x73, 0xc6, 0xb2, 0x98, 0x6c, 0xfa, 0x2f, 0x7d, 0xb3, 0x0e, 0x0b, - 0x2f, 0xa7, 0xf1, 0x18, 0xb2, 0xf8, 0x80, 0xf0, 0x59, 0x41, 0x3c, 0x5e, - 0x12, 0x5f, 0xef, 0x4b, 0x1b, 0xff, 0xfc, 0xac, 0xa9, 0x46, 0x0e, 0x34, - 0x19, 0x15, 0xfd, 0x11, 0xfb, 0xb8, 0x15, 0x95, 0x29, 0xd2, 0xe3, 0x46, - 0xa1, 0xc4, 0xe5, 0xb7, 0xf8, 0x62, 0x4e, 0xef, 0x81, 0xc5, 0x97, 0xed, - 0xc3, 0x66, 0x75, 0x65, 0x7c, 0x7c, 0x4c, 0x38, 0xbd, 0xc9, 0x69, 0x65, - 0xcf, 0xe5, 0x97, 0x45, 0x2b, 0x2e, 0x9e, 0xac, 0xa9, 0x35, 0xa2, 0x8b, - 0xdf, 0x0f, 0x0a, 0x0b, 0x2f, 0xd8, 0x70, 0xf4, 0xac, 0xba, 0x2e, 0x99, - 0xe3, 0xf8, 0x86, 0xff, 0xfc, 0x3f, 0x4e, 0x8f, 0xf9, 0x12, 0x62, 0x29, - 0x62, 0xcb, 0xfc, 0x2c, 0x97, 0x78, 0xfe, 0x59, 0x52, 0x8b, 0x73, 0x2e, - 0x75, 0x6b, 0xf1, 0x63, 0x96, 0xcb, 0x2a, 0x53, 0xf5, 0xc2, 0x33, 0x1d, - 0x74, 0x32, 0x8c, 0x7f, 0x85, 0xb7, 0x9f, 0x34, 0xb2, 0xfe, 0x9d, 0xb5, - 0x38, 0x35, 0x97, 0xf7, 0xa7, 0xbd, 0x96, 0x2c, 0xbf, 0xff, 0xb5, 0x83, - 0xe1, 0x03, 0x20, 0x07, 0x1f, 0x33, 0x4b, 0x28, 0x68, 0x87, 0x32, 0xda, - 0xd2, 0x3c, 0x7c, 0x36, 0x24, 0x2d, 0x2f, 0xfb, 0x53, 0xa7, 0x98, 0x4c, - 0x16, 0x5f, 0xff, 0x16, 0x6f, 0x0b, 0xeb, 0x67, 0xeb, 0xfd, 0xfa, 0xca, - 0x94, 0x60, 0x19, 0xb3, 0x9b, 0xdf, 0xe2, 0x7d, 0xbe, 0x84, 0xee, 0x2c, - 0xbf, 0xdc, 0xf3, 0x89, 0xe9, 0x82, 0xca, 0xd8, 0xfa, 0xfe, 0x37, 0xbb, - 0x22, 0x59, 0x7f, 0xff, 0xec, 0xec, 0x9c, 0x1f, 0x9c, 0x9c, 0x29, 0x86, - 0x31, 0xfe, 0x59, 0x58, 0x7f, 0xfc, 0x17, 0xbf, 0xfe, 0xf4, 0xb0, 0xf4, - 0x7b, 0x92, 0x26, 0xd8, 0xd2, 0xcb, 0xfb, 0x00, 0x11, 0x33, 0xe5, 0x95, - 0x12, 0x20, 0xf7, 0xaa, 0x5f, 0xf8, 0xf5, 0x3c, 0x10, 0x8a, 0x74, 0xb2, - 0xff, 0xa6, 0x04, 0xfd, 0x81, 0xef, 0x59, 0x78, 0x49, 0xfd, 0x65, 0x78, - 0xf5, 0xf8, 0x73, 0x6d, 0xd5, 0x97, 0xbb, 0x0c, 0x59, 0x77, 0x31, 0x65, - 0xfa, 0x62, 0x39, 0xe2, 0xcb, 0xff, 0xb9, 0x3a, 0x0f, 0x4b, 0x36, 0x72, - 0x59, 0x52, 0x9c, 0x66, 0xc4, 0xd9, 0x09, 0x43, 0x22, 0x71, 0x4f, 0xc7, - 0x08, 0x5b, 0xa4, 0xf7, 0x75, 0x8b, 0x2f, 0xb9, 0xf1, 0x69, 0x65, 0xfa, - 0x20, 0x76, 0x7c, 0xb2, 0xa4, 0xf8, 0xfa, 0x2e, 0x02, 0x3b, 0xff, 0x4f, - 0x5f, 0x99, 0xef, 0x1b, 0x16, 0x5f, 0x8a, 0x76, 0x9d, 0x96, 0x50, 0xcf, - 0x96, 0x23, 0xdb, 0xf8, 0x72, 0xfa, 0xfc, 0x0b, 0x2e, 0xcf, 0x2c, 0xad, - 0x8f, 0x13, 0x79, 0x75, 0xff, 0xf6, 0x1b, 0x0f, 0xf7, 0xe9, 0x66, 0xda, - 0x95, 0x97, 0xf6, 0xee, 0x6f, 0x2c, 0x82, 0xcb, 0x8e, 0x25, 0x94, 0x67, - 0x91, 0xd3, 0x1b, 0xfc, 0x62, 0xbc, 0x38, 0x63, 0x59, 0x52, 0x9b, 0xb6, - 0x33, 0xf8, 0x97, 0x90, 0x96, 0xe9, 0x0d, 0xc0, 0x82, 0xcb, 0xfb, 0xc7, - 0xad, 0x67, 0x56, 0x56, 0xc7, 0x88, 0x11, 0x7b, 0xfc, 0x7c, 0xf3, 0xb2, - 0x77, 0x56, 0x5f, 0xfe, 0x8a, 0x07, 0xbe, 0x75, 0x87, 0x00, 0x69, 0x65, - 0x69, 0x10, 0x2e, 0x6b, 0x7e, 0x9d, 0xd3, 0x68, 0x2b, 0x2f, 0xfb, 0x19, - 0xc3, 0x8f, 0xcd, 0x4a, 0xca, 0x96, 0xf0, 0x5f, 0x68, 0x4d, 0xc2, 0x34, - 0x91, 0xca, 0xa6, 0xc8, 0x59, 0x06, 0x10, 0xbf, 0x10, 0x9c, 0xef, 0x4e, - 0xec, 0x36, 0xe2, 0x87, 0x06, 0xa3, 0xe8, 0x64, 0xa9, 0x7f, 0x4a, 0x0a, - 0x78, 0x4c, 0x7f, 0x0a, 0x32, 0x94, 0x61, 0xc8, 0x6b, 0xf6, 0x50, 0x48, - 0x21, 0x25, 0xbe, 0x14, 0x82, 0x11, 0x6e, 0x15, 0xdc, 0x2f, 0x56, 0x5f, - 0xff, 0xf4, 0x71, 0x8e, 0xe7, 0x1b, 0x56, 0xa3, 0xb6, 0xd3, 0x7e, 0xec, - 0x70, 0x04, 0x68, 0xde, 0x8f, 0x59, 0x71, 0xca, 0xcb, 0xd1, 0x08, 0xc5, - 0x97, 0xfd, 0x9d, 0xe6, 0x77, 0xd9, 0xfa, 0xca, 0x14, 0xfb, 0x5c, 0x54, - 0x87, 0xee, 0x72, 0x59, 0x5a, 0x3c, 0x36, 0x17, 0x5f, 0x4f, 0x71, 0x8b, - 0x2f, 0xdd, 0x92, 0x3f, 0xd6, 0x5d, 0xf7, 0xcb, 0x2f, 0xda, 0x06, 0xff, - 0xda, 0x59, 0x71, 0x71, 0x65, 0xfd, 0x8d, 0x3f, 0xec, 0xc5, 0x95, 0x19, - 0x1a, 0x58, 0x43, 0xf1, 0x3f, 0x86, 0x7f, 0x2d, 0x21, 0x6b, 0xff, 0x4b, - 0x0f, 0xee, 0x02, 0x20, 0x0a, 0xb2, 0xfc, 0x5d, 0xf6, 0x41, 0x65, 0xdc, - 0xc5, 0x94, 0x66, 0xf5, 0xc9, 0xef, 0xf1, 0x4b, 0x51, 0xf9, 0xa9, 0x59, - 0x66, 0xa3, 0x23, 0x4e, 0x0f, 0xe3, 0x1f, 0xbe, 0xda, 0x7b, 0xc5, 0x97, - 0xa7, 0xfe, 0x2c, 0xad, 0x1e, 0x01, 0x11, 0xdf, 0x3e, 0x9e, 0x0b, 0x2d, - 0xac, 0x3c, 0x22, 0x21, 0xbc, 0xd3, 0x4d, 0x2c, 0xbe, 0xd8, 0x8e, 0x52, - 0x37, 0x34, 0x17, 0xfd, 0xbb, 0xe7, 0x28, 0x0e, 0x76, 0x59, 0x7c, 0xfd, - 0x91, 0xac, 0xbb, 0xe8, 0x2c, 0xbf, 0xc2, 0xcf, 0x79, 0xe7, 0x02, 0xcb, - 0xf1, 0xed, 0x9f, 0x75, 0x65, 0xc6, 0x15, 0x95, 0x28, 0x8b, 0x14, 0x61, - 0x86, 0x9d, 0x29, 0xbf, 0xe8, 0x7d, 0xc3, 0xd0, 0x7d, 0x8b, 0x2f, 0xc2, - 0xbf, 0x37, 0xc1, 0x65, 0x7c, 0x8a, 0x2d, 0x1e, 0xf4, 0xea, 0xfc, 0xe5, - 0x03, 0x62, 0xcb, 0xd8, 0x08, 0xf5, 0x94, 0xd8, 0x5e, 0xb2, 0x6d, 0x06, - 0x26, 0x34, 0x61, 0xc3, 0xd3, 0x23, 0x2c, 0x38, 0x5d, 0xf9, 0x19, 0xcc, - 0xc8, 0xf0, 0x11, 0xa7, 0x6f, 0x31, 0x8f, 0x26, 0xbd, 0x1d, 0x36, 0xbb, - 0x65, 0x65, 0xff, 0x79, 0xe2, 0x29, 0xcd, 0x41, 0x65, 0xff, 0x9a, 0xce, - 0x11, 0xce, 0xb3, 0x65, 0x95, 0xba, 0x7e, 0xdb, 0xce, 0x2f, 0xff, 0xff, - 0xfd, 0xbb, 0x1b, 0xd9, 0xf4, 0x3c, 0xc8, 0xc1, 0xf0, 0x3b, 0xdc, 0x2f, - 0x67, 0x64, 0x71, 0xa3, 0x7a, 0x3d, 0x65, 0xff, 0x0f, 0x00, 0xdb, 0x3f, - 0x3b, 0x16, 0x5f, 0xa5, 0x85, 0x91, 0x2c, 0xc3, 0x79, 0x7b, 0x53, 0xbd, - 0x65, 0xed, 0xf8, 0x35, 0x97, 0xf7, 0x8e, 0x48, 0x12, 0xb2, 0xff, 0x4e, - 0xe8, 0xb9, 0xc7, 0xdd, 0x59, 0x42, 0x9f, 0x27, 0x8a, 0xef, 0xd8, 0x31, - 0x3f, 0xe2, 0xca, 0x8c, 0x8e, 0xdf, 0x0f, 0x3c, 0x20, 0x88, 0x8e, 0xfb, - 0xd2, 0x27, 0x96, 0x50, 0xd3, 0x88, 0xe4, 0x64, 0x7d, 0x42, 0xbf, 0xa6, - 0x1e, 0x9d, 0xe6, 0xb2, 0xfc, 0xc0, 0x14, 0xb1, 0x65, 0x86, 0xb2, 0xe7, - 0xe2, 0xcb, 0x69, 0x86, 0x9c, 0x02, 0x35, 0x27, 0xea, 0xe9, 0xd7, 0x43, - 0xab, 0x2f, 0xa2, 0x9e, 0xba, 0xcb, 0xe0, 0x7f, 0x84, 0x13, 0x72, 0x01, - 0x7b, 0x41, 0x65, 0x6c, 0x79, 0x3e, 0x38, 0xbf, 0xfa, 0x7c, 0x20, 0xfa, - 0x79, 0x14, 0xb1, 0x65, 0xff, 0xba, 0x6d, 0x07, 0xc7, 0xbc, 0xc6, 0xb2, - 0xff, 0x6a, 0x1e, 0xcd, 0xf2, 0xc5, 0x97, 0xc0, 0x6d, 0xf3, 0x16, 0x57, - 0xc8, 0x9c, 0x34, 0x17, 0x35, 0xbf, 0x3c, 0x38, 0x7b, 0xd6, 0x5f, 0xf6, - 0x6b, 0x23, 0x66, 0xa4, 0x6b, 0x2f, 0x30, 0x1b, 0xd6, 0x5f, 0xd9, 0x0f, - 0xa1, 0x9d, 0x59, 0x6e, 0xc1, 0x10, 0xd8, 0x73, 0xe1, 0xfb, 0xdd, 0x03, - 0x16, 0x5f, 0x7d, 0x0f, 0x1a, 0xca, 0x93, 0xc0, 0x71, 0xda, 0x54, 0x41, - 0xcb, 0xf9, 0xf6, 0xfd, 0xc8, 0x68, 0xac, 0x1a, 0xb7, 0x02, 0x7a, 0x91, - 0xe3, 0x14, 0xc4, 0x4f, 0xf5, 0xd2, 0xff, 0x8f, 0x3b, 0xcd, 0x60, 0xf1, - 0x65, 0xfd, 0xe7, 0x61, 0x4f, 0xcb, 0x2f, 0x99, 0xc7, 0x87, 0x8f, 0x90, - 0x43, 0x7b, 0xfb, 0xff, 0x16, 0x77, 0x8b, 0x2f, 0xe2, 0xcd, 0xe5, 0x9c, - 0x59, 0x69, 0xd1, 0xed, 0xf4, 0xba, 0xa5, 0x34, 0x3c, 0x84, 0x3f, 0x21, - 0x2b, 0x6e, 0x2c, 0xbf, 0x7a, 0x77, 0x41, 0x05, 0x95, 0x26, 0xf1, 0xc4, - 0x6a, 0x0b, 0xc2, 0x41, 0x85, 0x1e, 0xa1, 0x44, 0xc2, 0x3f, 0x43, 0x87, - 0xf2, 0xf2, 0x85, 0x7f, 0x65, 0x2b, 0xef, 0x75, 0xbf, 0xa0, 0x59, 0xf7, - 0x71, 0x65, 0xff, 0xf8, 0x4f, 0xff, 0x9f, 0xb3, 0x61, 0xbb, 0x85, 0xc6, - 0xb2, 0xa5, 0x10, 0xda, 0x2d, 0xbf, 0x8b, 0xb0, 0xcd, 0x62, 0xcb, 0xa3, - 0xdd, 0x65, 0x39, 0xe2, 0x88, 0x59, 0x7f, 0xb5, 0xb7, 0x1c, 0x3a, 0xc5, - 0x97, 0xfb, 0xd8, 0x17, 0xf4, 0xf9, 0x65, 0xff, 0xce, 0x7d, 0x92, 0x0e, - 0x77, 0xc6, 0xb2, 0xfe, 0x23, 0xff, 0x9e, 0x35, 0x97, 0x68, 0x6b, 0x2e, - 0x69, 0xa5, 0x95, 0x03, 0x60, 0xd0, 0xbd, 0xe9, 0x3d, 0xe9, 0x1b, 0x9a, - 0x2b, 0xfe, 0x32, 0x3f, 0xf9, 0x9e, 0x75, 0x95, 0x89, 0xac, 0x78, 0xc9, - 0xd0, 0x78, 0xff, 0xd2, 0xfb, 0xfa, 0x4b, 0xbe, 0xc8, 0x2c, 0xbb, 0xfe, - 0x2c, 0xbf, 0x8f, 0xc6, 0x28, 0xb2, 0xb2, 0xc2, 0x0c, 0xf1, 0xba, 0x31, - 0x5a, 0x44, 0xdf, 0x1c, 0xaa, 0x55, 0x49, 0x9c, 0x71, 0x82, 0x43, 0x92, - 0xfe, 0xdd, 0xc8, 0x19, 0x3a, 0xcb, 0xcd, 0x34, 0xd2, 0x4a, 0x48, 0xdc, - 0xd0, 0x5f, 0x1b, 0x47, 0x12, 0x4a, 0x09, 0xe0, 0x18, 0xed, 0xf6, 0xa7, - 0x7e, 0x2c, 0xbd, 0x16, 0x79, 0x65, 0x61, 0xe0, 0x39, 0x1d, 0xf6, 0x74, - 0x0e, 0xb2, 0xa3, 0x96, 0x79, 0x74, 0xc2, 0x8f, 0x62, 0x7c, 0x94, 0xde, - 0x73, 0xc9, 0x3a, 0x8c, 0x91, 0x8c, 0xfe, 0x95, 0x6a, 0xe7, 0x85, 0x08, - 0xee, 0x30, 0xf4, 0x82, 0xfb, 0x50, 0x06, 0x2c, 0xbf, 0xc5, 0xf7, 0x44, - 0xf3, 0xfe, 0xb2, 0xf1, 0x1f, 0x16, 0x5e, 0x3c, 0xd9, 0x65, 0xf0, 0xdc, - 0xbf, 0x59, 0x7f, 0xf4, 0x97, 0xe5, 0x3f, 0x45, 0x25, 0xfa, 0xca, 0xc4, - 0x6a, 0x19, 0xb7, 0x86, 0xff, 0x1c, 0xe1, 0x15, 0xf4, 0x3f, 0x81, 0x2c, - 0xbf, 0x45, 0x1f, 0xfc, 0x7e, 0x2c, 0xbe, 0x9e, 0xbf, 0x96, 0x58, 0xd6, - 0x5f, 0xf3, 0x8a, 0x1f, 0x18, 0xde, 0x0b, 0x2f, 0xb6, 0xd9, 0xfe, 0x59, - 0x77, 0xfc, 0x33, 0xee, 0x21, 0x01, 0x0e, 0x6f, 0xb3, 0x5e, 0x35, 0x97, - 0xff, 0x9e, 0x05, 0x9b, 0xde, 0x29, 0xce, 0xf1, 0x65, 0x49, 0xf5, 0xb9, - 0x0d, 0xff, 0xe6, 0xbf, 0xf6, 0x66, 0xc7, 0x9b, 0xe7, 0xe5, 0x97, 0xff, - 0xbf, 0x96, 0x6b, 0x53, 0xb7, 0x5d, 0xc5, 0x59, 0x79, 0x92, 0x15, 0x97, - 0xfc, 0xf0, 0xe7, 0xef, 0xbf, 0x06, 0xb2, 0xec, 0xf6, 0x1e, 0xbf, 0x87, - 0x2f, 0xda, 0x3d, 0x9d, 0x8b, 0x2e, 0x69, 0xa5, 0x95, 0x87, 0x82, 0xd1, - 0x45, 0xde, 0x02, 0x46, 0xe6, 0x8a, 0xf4, 0x04, 0x62, 0xcb, 0xe7, 0xf8, - 0x63, 0x59, 0x50, 0x47, 0xc9, 0xc2, 0x3d, 0xca, 0x78, 0x3d, 0x7b, 0x19, - 0xba, 0xb2, 0xff, 0xcf, 0xbf, 0x04, 0x29, 0xf1, 0xec, 0xb2, 0xff, 0x67, - 0x0b, 0x3b, 0xe3, 0x59, 0x43, 0x44, 0x86, 0x87, 0xc9, 0x02, 0xb1, 0x5d, - 0x7c, 0x50, 0xa6, 0xf1, 0x07, 0xe9, 0xdd, 0x8e, 0x73, 0x7c, 0x34, 0x2f, - 0xa1, 0xfe, 0x7e, 0xb2, 0xfe, 0xee, 0x44, 0x52, 0xc5, 0xc4, 0x04, 0xbe, - 0x21, 0xfa, 0x55, 0x10, 0x11, 0xb9, 0xba, 0xaf, 0x91, 0x3c, 0xeb, 0xd7, - 0xff, 0x1f, 0xdc, 0x1e, 0x14, 0x90, 0x25, 0x65, 0xfb, 0x46, 0x06, 0xdc, - 0xac, 0xbf, 0xda, 0xcf, 0xb9, 0xfb, 0xf5, 0x65, 0xc0, 0x0c, 0x9f, 0x00, - 0xa5, 0x77, 0xe9, 0x83, 0xfc, 0xd2, 0xcb, 0xfe, 0x8d, 0xad, 0x67, 0xbe, - 0x87, 0x16, 0x5f, 0xff, 0xec, 0xe0, 0x1f, 0xec, 0xc2, 0x17, 0x9c, 0xc2, - 0xef, 0x16, 0x50, 0x51, 0x3b, 0xf1, 0xed, 0xf9, 0xda, 0x18, 0xbb, 0xd6, - 0x56, 0x27, 0xcc, 0x70, 0xa2, 0xd1, 0x73, 0xc3, 0x18, 0x89, 0x2f, 0xcf, - 0xde, 0x02, 0x0b, 0x2d, 0xb2, 0xcb, 0xf4, 0x6c, 0x88, 0xd8, 0xb2, 0xff, - 0xf0, 0x26, 0x36, 0xe7, 0x4f, 0x9e, 0xf9, 0xda, 0x59, 0x5b, 0x1f, 0xdf, - 0x0a, 0xaf, 0xfd, 0x83, 0xf3, 0xb7, 0x9f, 0x4f, 0xcb, 0x29, 0xb0, 0x8f, - 0x0d, 0x42, 0x35, 0x84, 0x77, 0xfc, 0x42, 0xe8, 0x8c, 0x20, 0x25, 0x97, - 0xee, 0xce, 0x14, 0xac, 0xa3, 0x3d, 0xd2, 0x38, 0xbc, 0x21, 0x75, 0x65, - 0xe3, 0x63, 0xac, 0xad, 0x8d, 0xc0, 0xa3, 0xb7, 0xe0, 0xe1, 0xf1, 0x8b, - 0x2f, 0xfe, 0xe4, 0x97, 0x44, 0x8b, 0xc7, 0xde, 0x2c, 0xbe, 0xcd, 0x4e, - 0xf5, 0x96, 0xfb, 0xe3, 0xe9, 0xea, 0x35, 0x31, 0x17, 0x4f, 0x09, 0x0a, - 0x94, 0xfc, 0xb2, 0x12, 0xc6, 0xb0, 0x08, 0x6e, 0xde, 0xdc, 0x9f, 0x96, - 0x5e, 0x9c, 0x62, 0xcb, 0xfe, 0x17, 0xb9, 0xad, 0x1c, 0x5c, 0x59, 0x50, - 0x3f, 0x66, 0x10, 0x38, 0xdd, 0xff, 0xb1, 0x92, 0x13, 0xee, 0x80, 0xc5, - 0x97, 0xed, 0x81, 0x13, 0x8d, 0x65, 0xe3, 0xfb, 0x8b, 0x2f, 0xfe, 0xe4, - 0xed, 0x9a, 0x88, 0xa7, 0xfe, 0x2c, 0xbf, 0xd3, 0x91, 0x14, 0xea, 0x0b, - 0x2a, 0x08, 0xfd, 0x73, 0xef, 0xca, 0xba, 0x3a, 0xd2, 0x35, 0xf6, 0x9e, - 0x11, 0x2c, 0xbf, 0x73, 0x93, 0xfc, 0x74, 0xb2, 0x85, 0x3d, 0x16, 0x88, - 0xef, 0xbd, 0x9f, 0xba, 0xca, 0x94, 0x71, 0x3c, 0x27, 0x38, 0x49, 0x7f, - 0xb5, 0x17, 0x0c, 0xba, 0x6b, 0x2f, 0xfd, 0x03, 0x63, 0x58, 0x43, 0xf4, - 0xac, 0xbf, 0xc2, 0xe6, 0xbf, 0x61, 0xf1, 0x65, 0x7c, 0x7e, 0x64, 0x7d, - 0x7f, 0xbc, 0x7f, 0x7f, 0xa7, 0x1a, 0xcb, 0xef, 0xbf, 0xd4, 0xa4, 0xb9, - 0xa6, 0x92, 0x54, 0x9b, 0xd6, 0x89, 0x2e, 0x23, 0x48, 0xdc, 0xd0, 0xdf, - 0xff, 0xfc, 0x4d, 0xf3, 0x37, 0x26, 0x23, 0x64, 0xc5, 0x18, 0xbe, 0x87, - 0x81, 0xfa, 0xcb, 0x8c, 0x2b, 0x29, 0x65, 0xda, 0x84, 0x64, 0xd5, 0xa1, - 0x09, 0x3f, 0x89, 0xf4, 0xf7, 0xf8, 0xb5, 0x4a, 0x7b, 0x0f, 0x1b, 0xc5, - 0xf8, 0xa0, 0x10, 0x6c, 0xb2, 0xf4, 0xea, 0x25, 0x94, 0xe7, 0x8c, 0x02, - 0x9a, 0x8e, 0x5b, 0x24, 0x69, 0x87, 0xb6, 0xc9, 0x30, 0x23, 0x19, 0x86, - 0x4e, 0x1f, 0x1c, 0x61, 0x5a, 0x94, 0x12, 0xc5, 0x1f, 0x4b, 0x0a, 0x78, - 0x5d, 0x94, 0xa0, 0xde, 0x99, 0x82, 0x53, 0xf3, 0x4d, 0xd7, 0xba, 0x6d, - 0x2c, 0xbf, 0xb6, 0x8f, 0x07, 0x8f, 0x4b, 0x2f, 0x6c, 0xec, 0x59, 0x77, - 0xc4, 0xb2, 0xfd, 0xf4, 0x3c, 0x27, 0x16, 0x59, 0xf0, 0xf0, 0x88, 0x5e, - 0xe3, 0x1a, 0xcb, 0xdd, 0x36, 0x97, 0x18, 0x42, 0xf0, 0x1c, 0x45, 0x44, - 0x0e, 0xf8, 0xd5, 0x54, 0x11, 0x41, 0xe2, 0x5e, 0xa0, 0x54, 0xa6, 0xc2, - 0xe6, 0x40, 0x87, 0xfd, 0xe2, 0xd3, 0x6d, 0x65, 0xfd, 0x85, 0xfe, 0x60, - 0xd6, 0x5f, 0xff, 0xfa, 0x01, 0xf1, 0xeb, 0x66, 0x48, 0x7d, 0x26, 0x39, - 0xf4, 0xc1, 0x65, 0xef, 0x48, 0xab, 0x2b, 0xe4, 0x44, 0xef, 0x6c, 0xbf, - 0xd8, 0x19, 0xd8, 0xa4, 0x2b, 0x2d, 0xfa, 0xcb, 0x6e, 0xc6, 0x3c, 0x43, - 0x32, 0xba, 0x40, 0xb2, 0xfc, 0x02, 0xfc, 0x48, 0x2c, 0xbf, 0xfe, 0x2c, - 0xe4, 0x61, 0x3d, 0x3f, 0xb1, 0xe1, 0x1b, 0x0f, 0x03, 0x82, 0xd4, 0xe9, - 0xa0, 0x81, 0xab, 0x7b, 0x65, 0xff, 0xda, 0x3f, 0x84, 0xee, 0x1c, 0x1f, - 0x8b, 0x2f, 0xf8, 0x1b, 0x4c, 0x69, 0xec, 0x8d, 0x65, 0x69, 0x10, 0x1e, - 0x45, 0xbc, 0xfa, 0xd9, 0x65, 0xcd, 0x34, 0xb2, 0xff, 0x68, 0xf3, 0x78, - 0x90, 0x6e, 0x13, 0x6c, 0xd0, 0xed, 0xff, 0x8c, 0x58, 0xdc, 0x07, 0xec, - 0x98, 0x2c, 0xbf, 0xdc, 0xce, 0xbb, 0x30, 0x96, 0x50, 0xd3, 0x1c, 0xd2, - 0xcf, 0x93, 0xfa, 0x87, 0x7f, 0xee, 0x61, 0x67, 0xb0, 0x38, 0x4b, 0x2f, - 0xfa, 0x1c, 0xef, 0xb1, 0x99, 0x05, 0x97, 0xfe, 0xc1, 0x7c, 0xf0, 0x11, - 0xfe, 0x95, 0x95, 0x04, 0x5a, 0x19, 0xd9, 0x1c, 0xd4, 0xa6, 0x41, 0x90, - 0xf2, 0xbf, 0xdb, 0xda, 0xc2, 0x1f, 0xa5, 0x65, 0xf0, 0x5d, 0xc2, 0xa8, - 0x86, 0x17, 0xf8, 0xe7, 0x50, 0x04, 0xe2, 0xcb, 0xf3, 0x18, 0xf0, 0xcd, - 0x1e, 0xf9, 0x17, 0x5f, 0xff, 0xf1, 0x60, 0x7d, 0x3f, 0x42, 0x3f, 0x3c, - 0x13, 0xc2, 0x71, 0x56, 0x5c, 0x1f, 0xd6, 0x5c, 0xfc, 0xc4, 0x40, 0x31, - 0xa2, 0xb1, 0x1d, 0x40, 0x85, 0xed, 0xfd, 0xac, 0x8a, 0x48, 0x6b, 0x2a, - 0x57, 0xbf, 0x60, 0x40, 0x71, 0xfb, 0x7a, 0x57, 0x73, 0x93, 0x94, 0x6f, - 0xc0, 0x27, 0xbf, 0x14, 0xff, 0xf7, 0xeb, 0x2f, 0xd9, 0x09, 0xd7, 0xeb, - 0x2e, 0x91, 0x16, 0x5f, 0x9f, 0x62, 0x98, 0x24, 0xa8, 0x8d, 0xf7, 0x85, - 0xef, 0xfd, 0xdf, 0x39, 0xf6, 0x75, 0x3f, 0xac, 0xbf, 0x79, 0xc8, 0x4d, - 0x96, 0x54, 0x13, 0x19, 0x14, 0xa9, 0x8c, 0xc4, 0x44, 0xd1, 0xf5, 0xff, - 0x8c, 0x33, 0xc7, 0xd6, 0x8d, 0xa5, 0x97, 0xfd, 0xb4, 0x19, 0x3e, 0x73, - 0x15, 0x65, 0xff, 0xba, 0x65, 0xb6, 0x7d, 0xdf, 0x3a, 0xcb, 0xff, 0x8f, - 0x42, 0x87, 0xc7, 0xe2, 0x06, 0xcb, 0x2b, 0x11, 0x0b, 0xf9, 0xfd, 0x12, - 0x38, 0xb9, 0x0b, 0xfb, 0xe0, 0x67, 0x0d, 0x65, 0xdc, 0x02, 0xcb, 0xfa, - 0x1e, 0x77, 0x29, 0x59, 0x69, 0x09, 0xe1, 0x7c, 0x2f, 0x7f, 0xf6, 0x76, - 0x7c, 0x78, 0x28, 0xb3, 0x05, 0x97, 0xed, 0x05, 0xdc, 0x2a, 0x88, 0x29, - 0x7b, 0xd2, 0xc5, 0x97, 0xfe, 0x9d, 0x9f, 0xba, 0xc8, 0x47, 0xe2, 0xca, - 0x1a, 0x35, 0x71, 0x10, 0xcd, 0x08, 0x72, 0xfd, 0xe9, 0x60, 0x3e, 0x59, - 0x7d, 0xde, 0x4f, 0xe9, 0x2f, 0x3e, 0xb6, 0x49, 0x7c, 0x02, 0x71, 0x52, - 0x5f, 0xe3, 0xdb, 0x3b, 0xc9, 0xfd, 0x25, 0x24, 0xbf, 0xb0, 0x33, 0xe9, - 0x62, 0x4b, 0x9a, 0x69, 0x25, 0xfc, 0x52, 0x5b, 0x1e, 0x92, 0x56, 0x26, - 0x24, 0x12, 0x36, 0x0e, 0xf8, 0x8b, 0xf3, 0x42, 0x0b, 0x68, 0xb0, 0x41, - 0x9b, 0x8f, 0x49, 0x1b, 0x9f, 0x9d, 0x41, 0x50, 0x8c, 0x47, 0x5e, 0x8e, - 0xba, 0xff, 0xfd, 0x2d, 0x70, 0xb3, 0xee, 0xb9, 0x6d, 0x9f, 0x75, 0x65, - 0x4a, 0xec, 0xa6, 0x26, 0x1c, 0x64, 0x1a, 0x27, 0xf3, 0x1b, 0xcb, 0x0e, - 0x23, 0x9b, 0xcc, 0x71, 0xac, 0xbf, 0xff, 0xe2, 0xce, 0xf6, 0x7e, 0x84, - 0x6f, 0x4b, 0x01, 0xf7, 0x3d, 0x2b, 0x2f, 0xec, 0x2e, 0x19, 0x62, 0xcb, - 0xf9, 0x98, 0x3f, 0x66, 0xcb, 0x2f, 0xff, 0x13, 0x8b, 0xcf, 0xfb, 0xdf, - 0xbb, 0x81, 0x59, 0x52, 0x7f, 0x62, 0x97, 0x5e, 0xf0, 0x18, 0xb2, 0x85, - 0x4d, 0xab, 0x74, 0x73, 0xcd, 0x45, 0x0a, 0x5d, 0xc2, 0x2b, 0xf8, 0xb3, - 0x9a, 0x9e, 0x2c, 0xbc, 0x08, 0x62, 0xca, 0x73, 0xc8, 0xfc, 0xb2, 0xfe, - 0x1e, 0x13, 0xf7, 0x8b, 0x2a, 0x07, 0x9d, 0xc2, 0x2b, 0xe7, 0x8b, 0x3c, - 0xb2, 0xff, 0xa4, 0xc9, 0xfb, 0x11, 0xb4, 0xb2, 0xda, 0x8c, 0x7b, 0x5c, - 0x22, 0xa9, 0x45, 0x31, 0xbe, 0x5f, 0xb5, 0xf4, 0x33, 0xab, 0x2f, 0xff, - 0x9c, 0xff, 0xe6, 0x0a, 0xec, 0xd4, 0xe1, 0x2c, 0xbe, 0x90, 0x89, 0xfa, - 0xcb, 0xef, 0x4f, 0xdc, 0x59, 0x7c, 0x79, 0xf7, 0x56, 0x56, 0x1e, 0x2f, - 0x48, 0xef, 0xff, 0xfb, 0xc7, 0xac, 0x1b, 0xb3, 0x0b, 0xa5, 0x2d, 0x67, - 0x78, 0xb2, 0xd2, 0xb2, 0xfa, 0x77, 0x8e, 0x42, 0x7e, 0x9f, 0xb2, 0x5f, - 0xf9, 0xe1, 0xe7, 0xef, 0x30, 0xa0, 0xb2, 0xbc, 0x7f, 0x1d, 0x3b, 0xad, - 0x2a, 0x0f, 0xf1, 0x49, 0x27, 0x71, 0xa7, 0xb1, 0x86, 0xdf, 0xfe, 0xcd, - 0x7f, 0xdd, 0x66, 0xdd, 0x9c, 0xe2, 0xcb, 0xc1, 0xc6, 0x2c, 0xbf, 0x86, - 0xfd, 0xe0, 0x20, 0xb2, 0xe8, 0x47, 0xc9, 0xe5, 0x08, 0x39, 0x7f, 0x3e, - 0xa7, 0xa7, 0xbd, 0x65, 0x7c, 0x7b, 0xe4, 0x63, 0x7f, 0xff, 0xed, 0x6a, - 0x7e, 0x87, 0xdc, 0x3d, 0x07, 0xd8, 0x26, 0x8e, 0x46, 0xb2, 0xff, 0x9f, - 0x59, 0xf4, 0x27, 0xf9, 0x59, 0x52, 0x8c, 0xac, 0x22, 0x03, 0x7d, 0xff, - 0x7d, 0x0e, 0xe7, 0x3b, 0x9b, 0xab, 0x2f, 0xff, 0xc1, 0x15, 0xf9, 0x9a, - 0x81, 0xf9, 0xcb, 0xe8, 0x2c, 0xbf, 0xf0, 0x67, 0xbc, 0xce, 0x9f, 0xcd, - 0x2c, 0xac, 0x44, 0xb0, 0xaa, 0xd5, 0x28, 0xf6, 0x78, 0x69, 0xd4, 0xab, - 0x59, 0x1a, 0x8f, 0xa3, 0xbe, 0x28, 0xc9, 0x2f, 0xdc, 0x20, 0x6f, 0x82, - 0xcb, 0xdb, 0x08, 0x15, 0x97, 0xc4, 0x71, 0x6e, 0xac, 0xa9, 0x3e, 0xe7, - 0x2a, 0xe0, 0xfd, 0xcf, 0xfa, 0xca, 0x81, 0xe2, 0x74, 0xb6, 0xff, 0xfc, - 0xef, 0x07, 0xf4, 0x6c, 0xd8, 0x1d, 0x03, 0x88, 0xa8, 0xbe, 0xd7, 0xb7, - 0x83, 0xab, 0x2e, 0x91, 0xac, 0xa9, 0x36, 0xbf, 0x8f, 0xdf, 0xce, 0x19, - 0x29, 0xfd, 0x65, 0xff, 0xb8, 0x65, 0xf4, 0x34, 0x09, 0xe2, 0xcb, 0xf6, - 0x6b, 0x07, 0x2b, 0x2a, 0x24, 0xd3, 0x35, 0x09, 0xaf, 0x10, 0xf4, 0xb3, - 0x79, 0xfd, 0xfb, 0xd9, 0x14, 0xee, 0xac, 0xbf, 0x0f, 0x5a, 0xc6, 0x96, - 0x56, 0xc7, 0xa8, 0x45, 0x77, 0xf1, 0x96, 0x76, 0x62, 0x59, 0x7f, 0xf4, - 0x39, 0x2c, 0xc0, 0x93, 0xe0, 0x56, 0x5f, 0x49, 0x0a, 0x35, 0x97, 0x9f, - 0xf9, 0x59, 0x52, 0x8c, 0xe8, 0x11, 0x7c, 0x59, 0xe4, 0x2e, 0x11, 0x5d, - 0x0e, 0x2c, 0xbf, 0xb9, 0x03, 0x0b, 0xe9, 0x65, 0x82, 0xb2, 0xff, 0xff, - 0x7e, 0x7a, 0x17, 0xd2, 0xc8, 0xa0, 0x65, 0xe9, 0xd4, 0xac, 0xb7, 0x70, - 0xfc, 0xf8, 0x23, 0x78, 0xf5, 0x8b, 0x2f, 0x16, 0x41, 0x65, 0x04, 0xdb, - 0x74, 0x6e, 0xf1, 0x4b, 0x16, 0x54, 0xa2, 0x64, 0xd7, 0x78, 0x43, 0x7f, - 0xe2, 0x38, 0x77, 0xd9, 0xa3, 0xe2, 0xcb, 0xf4, 0x0b, 0x33, 0x65, 0x97, - 0x6b, 0x65, 0x97, 0xfd, 0x13, 0x63, 0xa6, 0x31, 0xe1, 0x2c, 0xa9, 0x3f, - 0xcc, 0x27, 0x31, 0x8b, 0xfc, 0xe3, 0x9f, 0xe5, 0xae, 0x2c, 0xaf, 0x1f, - 0x03, 0x96, 0x5f, 0xd1, 0xed, 0xaa, 0x34, 0x6f, 0x47, 0xae, 0x20, 0x15, - 0xfe, 0xef, 0xe2, 0x14, 0x3d, 0x8b, 0x88, 0x05, 0x79, 0xf5, 0x05, 0xc4, - 0x02, 0xac, 0x3e, 0xb0, 0x21, 0x5c, 0xf0, 0x5c, 0x40, 0x2b, 0xe7, 0x2f, - 0xa0, 0xb8, 0x80, 0x57, 0xf8, 0xc3, 0xec, 0xff, 0xf9, 0x5c, 0x40, 0x2b, - 0xc6, 0x43, 0x5c, 0x40, 0x2a, 0x1a, 0x2e, 0xd8, 0x47, 0xe2, 0xfd, 0xe8, - 0x36, 0x15, 0x71, 0x00, 0xaf, 0x6a, 0x7a, 0xb8, 0x80, 0x54, 0xb8, 0x80, - 0x57, 0xa3, 0xcf, 0xf5, 0xc4, 0x02, 0xba, 0x58, 0xb8, 0x80, 0x54, 0x33, - 0xe8, 0xc1, 0x83, 0x2d, 0xbe, 0x36, 0x0e, 0x57, 0x10, 0x0a, 0xf7, 0x5c, - 0x2b, 0x88, 0x05, 0x7f, 0xe2, 0x78, 0x37, 0xf1, 0x85, 0xf4, 0xb8, 0x80, - 0x57, 0xff, 0x3f, 0x41, 0xb1, 0x97, 0x7c, 0xf0, 0x5c, 0x40, 0x2b, 0x9c, - 0x6b, 0x88, 0x05, 0x7f, 0x89, 0xda, 0xe7, 0x27, 0xf5, 0xc4, 0x02, 0xbf, - 0x18, 0xae, 0x5f, 0xae, 0x20, 0x15, 0xc7, 0xc5, 0xc4, 0x02, 0xad, 0x1e, - 0xc7, 0x4d, 0x6f, 0xff, 0x78, 0xfb, 0xdc, 0xe4, 0x6d, 0x1c, 0xb1, 0x71, - 0x00, 0xaf, 0xdd, 0x29, 0xfa, 0x0a, 0x88, 0x05, 0x77, 0xf2, 0xb8, 0x80, - 0x4d, 0xcd, 0xad, 0xf8, 0xe5, 0x8e, 0x35, 0xc4, 0x02, 0xbe, 0x03, 0x90, - 0x57, 0x10, 0x0a, 0xf4, 0xe8, 0x2b, 0x88, 0x05, 0x7f, 0xec, 0xfb, 0x83, - 0x9c, 0x2f, 0xf8, 0xb8, 0x80, 0x57, 0xe0, 0x3c, 0x21, 0x2b, 0x88, 0x05, - 0x7e, 0x70, 0xfa, 0x7e, 0x5c, 0x40, 0x2a, 0xc4, 0x5a, 0xf9, 0x30, 0x06, - 0x97, 0x6b, 0xf5, 0xc4, 0x02, 0xa8, 0x2b, 0x34, 0x19, 0x1e, 0x42, 0x64, - 0x24, 0x47, 0x08, 0xdf, 0x1b, 0x7e, 0x67, 0xc2, 0xee, 0xc3, 0x78, 0x05, - 0xf7, 0xec, 0xef, 0x1f, 0xe5, 0xc4, 0x02, 0xbf, 0xc3, 0x90, 0xc5, 0xa9, - 0xea, 0xe2, 0x01, 0x0c, 0xda, 0xde, 0xd4, 0xb4, 0xb8, 0x80, 0x54, 0x13, - 0xfb, 0xd2, 0x8d, 0xfe, 0x03, 0xc2, 0x12, 0x23, 0x17, 0x10, 0x0a, 0xf8, - 0xca, 0x1c, 0x5c, 0x40, 0x2b, 0xf9, 0xe2, 0x86, 0x7d, 0x05, 0xc4, 0x02, - 0xac, 0x46, 0x6f, 0x88, 0xbf, 0x41, 0x01, 0x75, 0xff, 0x7a, 0x78, 0xdf, - 0x80, 0xd7, 0xeb, 0x88, 0x05, 0x63, 0x5c, 0x40, 0x2b, 0x9f, 0x61, 0x9f, - 0x3f, 0x92, 0xae, 0xd7, 0xeb, 0x88, 0x05, 0x7e, 0x7e, 0xf0, 0xfe, 0x5c, - 0x40, 0x2b, 0xf8, 0xc8, 0x10, 0xce, 0x2e, 0x20, 0x15, 0x4a, 0x24, 0xc8, - 0x97, 0xa6, 0xb5, 0x2c, 0xa8, 0x21, 0xc2, 0xdf, 0x21, 0xb8, 0x67, 0xf1, - 0x0f, 0xea, 0x12, 0x3e, 0x2d, 0x72, 0xef, 0xca, 0x49, 0x33, 0x92, 0xe1, - 0x7b, 0x28, 0x62, 0x3e, 0x14, 0x82, 0x43, 0x06, 0xf7, 0x4d, 0xa5, 0xc6, - 0x12, 0xbc, 0xee, 0x15, 0x44, 0x02, 0x6e, 0x98, 0xaf, 0xd0, 0xeb, 0xbc, - 0xda, 0xe3, 0x62, 0xcb, 0xfb, 0xa5, 0x9f, 0xc7, 0xe2, 0xca, 0x14, 0xf5, - 0x18, 0x49, 0x7f, 0xa0, 0x7d, 0x14, 0x5c, 0xdd, 0x59, 0x76, 0xfd, 0x96, - 0x5f, 0xdb, 0x4e, 0xbc, 0xfb, 0xab, 0x2d, 0xc5, 0x97, 0xc5, 0x23, 0xf2, - 0xca, 0xf1, 0xb2, 0xde, 0x23, 0x52, 0x8a, 0x0c, 0x1a, 0x36, 0x3b, 0xff, - 0x16, 0x07, 0xc7, 0xff, 0xe0, 0x89, 0x65, 0xff, 0x0f, 0x05, 0x2c, 0xec, - 0xb4, 0xb2, 0xa0, 0x7f, 0x5f, 0xa0, 0xdf, 0xbb, 0x83, 0x7f, 0x2c, 0xbb, - 0xf9, 0x49, 0x61, 0x52, 0x5e, 0xf4, 0xec, 0xb2, 0xe6, 0x9a, 0x49, 0x46, - 0x7b, 0xfb, 0xa2, 0xf1, 0x09, 0x34, 0x3b, 0x6e, 0x24, 0x6e, 0x78, 0x15, - 0x88, 0xea, 0x38, 0x63, 0x5f, 0xfd, 0x9f, 0xc0, 0xfb, 0x2c, 0x92, 0x1a, - 0xca, 0x61, 0xf4, 0xb9, 0x35, 0xfd, 0xde, 0x63, 0x5e, 0x35, 0x96, 0xea, - 0xca, 0xe9, 0xbf, 0x01, 0x75, 0xfa, 0x76, 0xe3, 0x20, 0xb2, 0x8c, 0xf2, - 0xba, 0x43, 0x7f, 0xfe, 0xfa, 0x1c, 0x2f, 0xa4, 0x5e, 0x4f, 0xd0, 0x36, - 0x2c, 0xbf, 0xfd, 0xde, 0x60, 0xa7, 0xa1, 0x3f, 0xfe, 0x7e, 0x59, 0x51, - 0xdb, 0x66, 0xfd, 0x22, 0xfb, 0x46, 0x73, 0x02, 0xe1, 0xc6, 0x32, 0x1a, - 0x5d, 0xc1, 0xc2, 0x92, 0x22, 0x3d, 0x43, 0x61, 0xe1, 0x4f, 0xfc, 0x6e, - 0xdd, 0x85, 0x50, 0x08, 0x44, 0x59, 0xbf, 0xec, 0x81, 0x8d, 0xc3, 0xc8, - 0x2c, 0xbf, 0xe6, 0x19, 0x04, 0xf4, 0x7f, 0xac, 0xa0, 0x9f, 0x87, 0x8e, - 0x2f, 0x07, 0xc4, 0xb2, 0xff, 0xee, 0xc7, 0xe4, 0x50, 0x7d, 0x6c, 0x39, - 0x59, 0x4c, 0x3e, 0x52, 0x1c, 0xbf, 0xa7, 0xe9, 0x27, 0xea, 0xcb, 0x8b, - 0xf5, 0x97, 0x4f, 0x16, 0x51, 0x9a, 0xdd, 0xe2, 0xf7, 0x74, 0x4f, 0x1f, - 0xde, 0xf5, 0x9b, 0xfd, 0x9f, 0x77, 0xaf, 0x0e, 0x2c, 0xbd, 0x06, 0xdc, - 0x16, 0x5d, 0xf4, 0x17, 0x18, 0x02, 0xff, 0xf6, 0x35, 0x3d, 0xc8, 0xa0, - 0x7d, 0xe3, 0xac, 0xbe, 0x8b, 0x53, 0xd5, 0x95, 0xf2, 0x2c, 0xc8, 0x87, - 0x84, 0xbd, 0x4a, 0xbf, 0xff, 0x4e, 0x7b, 0x84, 0xfd, 0x07, 0x47, 0xe3, - 0x15, 0x65, 0xff, 0xff, 0xff, 0xfe, 0xe0, 0x37, 0x49, 0xf3, 0xce, 0x21, - 0x66, 0xfc, 0x27, 0x16, 0x28, 0x03, 0x9c, 0x69, 0xc7, 0xe3, 0x6b, 0x98, - 0xe3, 0x59, 0x52, 0xae, 0x3f, 0x21, 0x0e, 0x70, 0x9c, 0xd1, 0x8b, 0xc3, - 0x9c, 0x8f, 0x7a, 0xab, 0x7f, 0xfe, 0x22, 0x07, 0xd9, 0xf7, 0x47, 0xa7, - 0xfb, 0x36, 0x59, 0x7e, 0xce, 0xea, 0x78, 0xb2, 0x8c, 0xff, 0xc9, 0x62, - 0xfd, 0x14, 0x27, 0x5b, 0x2c, 0xbe, 0x91, 0x65, 0x8b, 0x2a, 0x07, 0xd9, - 0xa2, 0x06, 0x15, 0x5f, 0xfa, 0x1c, 0x8c, 0x58, 0xc9, 0xd0, 0xab, 0x2f, - 0xd1, 0xa3, 0x67, 0x9d, 0x65, 0xfd, 0x09, 0xd7, 0xe2, 0x31, 0x65, 0xff, - 0xdb, 0xcb, 0x39, 0xdc, 0x28, 0x67, 0x16, 0x5f, 0x32, 0x41, 0xd5, 0x95, - 0x87, 0xcc, 0xe8, 0x97, 0xe7, 0xef, 0x5f, 0x4b, 0x2f, 0x75, 0xc9, 0x65, - 0x9f, 0x11, 0xf0, 0x50, 0x96, 0xe1, 0x06, 0xe1, 0x3d, 0x46, 0x4d, 0xaf, - 0xd1, 0x91, 0xdf, 0xf4, 0xee, 0x8f, 0xd3, 0xb3, 0x92, 0xcb, 0xfe, 0x90, - 0xcb, 0x0d, 0x9f, 0x9a, 0xca, 0xd2, 0x27, 0xfc, 0x59, 0xb8, 0x77, 0x7f, - 0x08, 0xd6, 0x8f, 0x3c, 0x92, 0xfd, 0xcf, 0x4e, 0x6c, 0xb2, 0xfa, 0x07, - 0xae, 0x2c, 0xbf, 0xfb, 0xf2, 0x3c, 0xfb, 0xb1, 0x41, 0xf4, 0xb2, 0xfa, - 0x2f, 0x3c, 0x4b, 0x2f, 0x82, 0xfa, 0xd9, 0x65, 0xfb, 0x22, 0x84, 0xfc, - 0xb2, 0xe3, 0x89, 0x65, 0xe3, 0x92, 0x59, 0x7e, 0xcd, 0x0f, 0xd8, 0xb2, - 0x86, 0x78, 0x3c, 0x1a, 0xaf, 0x1f, 0xd7, 0x16, 0x2f, 0x14, 0xc1, 0x25, - 0xff, 0x8b, 0xf3, 0xdc, 0xdc, 0x04, 0x27, 0x75, 0x65, 0x61, 0xf1, 0xb8, - 0xdd, 0x46, 0x54, 0x3f, 0x85, 0x1f, 0x11, 0x69, 0x1b, 0xc4, 0x84, 0x47, - 0xd8, 0x51, 0xee, 0x42, 0x16, 0xa5, 0x75, 0xf3, 0x0b, 0xce, 0x52, 0x5f, - 0x8c, 0xde, 0x52, 0x75, 0xfd, 0xf7, 0x35, 0xa9, 0xea, 0xcb, 0xfa, 0x1c, - 0x17, 0x4e, 0x15, 0x97, 0x8b, 0xbc, 0x59, 0x78, 0x8e, 0x0b, 0x2f, 0xda, - 0xf3, 0xbb, 0x16, 0x5f, 0x00, 0x80, 0x15, 0x94, 0xe7, 0x93, 0xc2, 0x7b, - 0xfb, 0xb1, 0x43, 0x3e, 0x82, 0xca, 0x94, 0xc0, 0x0c, 0xbd, 0xc7, 0x00, - 0xc8, 0x21, 0x0d, 0xff, 0xde, 0x6a, 0x75, 0xcc, 0x69, 0xf4, 0x2a, 0xcb, - 0xfc, 0xd6, 0x7d, 0xde, 0xcb, 0x16, 0x5f, 0xfb, 0xc1, 0x3c, 0x2c, 0xde, - 0xfc, 0x59, 0x4e, 0x7e, 0x7c, 0x35, 0xbe, 0x18, 0xf1, 0x8b, 0x2e, 0xfe, - 0x52, 0x5c, 0xd3, 0x49, 0x28, 0xcd, 0x83, 0x42, 0xf7, 0xe1, 0x81, 0xf5, - 0x04, 0x8d, 0xcd, 0x0d, 0x62, 0x2b, 0x45, 0x73, 0xbf, 0xfd, 0xdf, 0x82, - 0x7e, 0x8f, 0x9e, 0xf8, 0x4e, 0x2c, 0xbf, 0xd3, 0x03, 0xec, 0x27, 0xab, - 0x2e, 0x6d, 0xf1, 0x66, 0x8d, 0x95, 0xff, 0x79, 0xfb, 0xc0, 0x96, 0x6c, - 0xb2, 0xe0, 0x9a, 0xcb, 0xe2, 0x3f, 0xa0, 0xb2, 0xff, 0x8f, 0xef, 0x60, - 0xf4, 0xe1, 0x59, 0x52, 0x7b, 0x7a, 0x22, 0xbf, 0xdc, 0xc3, 0xe8, 0xa7, - 0xe5, 0x97, 0xfd, 0xe9, 0xd4, 0x84, 0xc8, 0x2b, 0x2f, 0x0a, 0xfc, 0x0a, - 0x3f, 0x34, 0xe3, 0xd2, 0x1d, 0xc3, 0x3a, 0xc4, 0xe1, 0xfb, 0x19, 0xad, - 0x41, 0x70, 0x23, 0xe8, 0x5d, 0x6a, 0x19, 0xcc, 0x23, 0x04, 0x20, 0x9a, - 0x8e, 0xca, 0xfe, 0x68, 0x57, 0xe9, 0x4a, 0xcb, 0xcc, 0x06, 0x96, 0x5e, - 0x09, 0xf1, 0x65, 0xf0, 0x19, 0x83, 0x59, 0x5f, 0x22, 0x24, 0xcb, 0xbc, - 0x3b, 0xb8, 0x39, 0x7f, 0xff, 0x6a, 0x00, 0x9c, 0xf0, 0x4f, 0x98, 0x3d, - 0xb1, 0xa5, 0x97, 0xfe, 0xf6, 0x6e, 0x39, 0x0b, 0x9f, 0x75, 0x65, 0xfd, - 0xe3, 0x1f, 0x80, 0x2a, 0xcb, 0xf1, 0x4c, 0x47, 0xf2, 0xcb, 0x60, 0x4f, - 0x5c, 0xcb, 0xef, 0x77, 0x02, 0xb2, 0x98, 0x78, 0x5c, 0x26, 0xbd, 0x06, - 0x7e, 0xb2, 0xa5, 0x37, 0x71, 0x57, 0x3f, 0x86, 0xdf, 0x48, 0xaf, 0xf8, - 0x57, 0x8d, 0x2c, 0x23, 0x15, 0x65, 0xff, 0x9f, 0x8c, 0x93, 0xd6, 0x8d, - 0xa5, 0x96, 0xe0, 0xa7, 0xf3, 0xe3, 0xbb, 0xe7, 0x83, 0xe9, 0x65, 0xff, - 0x67, 0x7a, 0x62, 0xf7, 0x02, 0xb2, 0xfe, 0x9f, 0xb9, 0x9f, 0x75, 0x65, - 0xda, 0x95, 0x94, 0x33, 0xc5, 0xe9, 0x7d, 0xfe, 0xe9, 0x84, 0xc7, 0x3d, - 0x59, 0x7e, 0xe4, 0x87, 0x18, 0xb2, 0xa4, 0xf6, 0x78, 0x65, 0x6e, 0x0d, - 0x31, 0xac, 0x7e, 0xd3, 0xdd, 0x7c, 0x9c, 0xd3, 0x0a, 0x7b, 0x19, 0xdd, - 0xff, 0x74, 0xf6, 0x9f, 0xbf, 0xd4, 0xac, 0xbd, 0xce, 0xe2, 0xcb, 0xb3, - 0x91, 0x8f, 0x5c, 0x43, 0xbb, 0xfd, 0xe0, 0x9f, 0x01, 0x03, 0x59, 0x7f, - 0xf6, 0x74, 0xfe, 0x81, 0x4c, 0x77, 0x1c, 0x63, 0xd6, 0x5f, 0xfe, 0x04, - 0x3c, 0xe7, 0xd3, 0x2e, 0x83, 0xcb, 0x2f, 0xf0, 0xbd, 0xf8, 0x27, 0xae, - 0x2c, 0xbf, 0x67, 0x7f, 0x7e, 0xac, 0xa9, 0x4c, 0xb2, 0x06, 0x7a, 0x52, - 0x02, 0x4c, 0x79, 0xb5, 0xff, 0xfa, 0x7c, 0xf8, 0x53, 0xa1, 0x46, 0x07, - 0xd4, 0x16, 0x54, 0x13, 0xfd, 0xec, 0x6b, 0x9b, 0x89, 0xb5, 0x2b, 0xf9, - 0xd9, 0x0f, 0xa3, 0x8e, 0xdd, 0xe5, 0x2b, 0x82, 0x54, 0x65, 0xf7, 0x8d, - 0xe0, 0xb2, 0xff, 0x61, 0x43, 0x38, 0x23, 0x16, 0x5f, 0x99, 0xcf, 0xdd, - 0xd6, 0x57, 0x4f, 0x6c, 0x43, 0x3b, 0xfd, 0x85, 0x1b, 0xd9, 0xf3, 0x4b, - 0x2f, 0xe9, 0xf1, 0xef, 0xc8, 0x96, 0x56, 0x8f, 0x97, 0xf3, 0x6b, 0xfe, - 0x81, 0x03, 0xf8, 0xc1, 0x67, 0xeb, 0x2b, 0x64, 0xd9, 0x60, 0xf9, 0x90, - 0x88, 0xf8, 0x8e, 0xff, 0xb3, 0xe8, 0x7b, 0x73, 0x72, 0x77, 0x56, 0x5f, - 0xfc, 0xfe, 0xe1, 0xc0, 0xfb, 0x3d, 0xe2, 0xcb, 0xfd, 0x9d, 0xf1, 0xc7, - 0xbe, 0xcb, 0x2f, 0xf8, 0xa7, 0x3e, 0xee, 0x6a, 0x56, 0x5f, 0xfa, 0x7c, - 0x27, 0x0b, 0x3b, 0xec, 0x59, 0x7e, 0xdf, 0xec, 0xfd, 0xd6, 0x5f, 0x7a, - 0x7f, 0xc5, 0x95, 0x87, 0x98, 0x02, 0xaa, 0x94, 0xf8, 0xb1, 0x10, 0xd0, - 0xfc, 0x86, 0xe6, 0xdf, 0x9b, 0x94, 0x22, 0xaf, 0xcf, 0xbc, 0x63, 0x95, - 0x97, 0x73, 0x8b, 0x2f, 0x34, 0xd3, 0x4b, 0x2e, 0xfd, 0xd2, 0x37, 0x34, - 0x15, 0xa3, 0xe0, 0xe1, 0xbd, 0xff, 0xb3, 0x84, 0xe2, 0xc4, 0x47, 0x12, - 0xcb, 0xfb, 0xf9, 0x87, 0x84, 0xe2, 0xcb, 0xfe, 0x3f, 0xc4, 0xcd, 0xe4, - 0x0e, 0x2c, 0xbf, 0x8c, 0xbf, 0x08, 0x4d, 0x65, 0xe7, 0xff, 0x12, 0x54, - 0x13, 0x9f, 0x64, 0x22, 0x7c, 0x45, 0xf9, 0xf9, 0x18, 0x6f, 0x3c, 0x8f, - 0x2d, 0xbe, 0x91, 0x98, 0xd6, 0x5f, 0xdc, 0xf1, 0x85, 0xf4, 0xb2, 0xfc, - 0x3d, 0x18, 0x36, 0x59, 0x7e, 0xce, 0xf1, 0xfe, 0x59, 0x7f, 0x9a, 0xc2, - 0xcd, 0xe7, 0xa5, 0x97, 0xf1, 0x67, 0xdf, 0x89, 0x05, 0x97, 0xff, 0xdc, - 0xee, 0x78, 0x46, 0x60, 0x67, 0xd2, 0xc5, 0x95, 0x89, 0x99, 0x44, 0x5b, - 0xa2, 0x92, 0x28, 0xe9, 0xa0, 0x0c, 0x2f, 0xd9, 0x9d, 0xf6, 0x2c, 0xb9, - 0xd8, 0xb2, 0xff, 0x81, 0xce, 0xcc, 0x40, 0xfb, 0x8b, 0x2f, 0x8f, 0x5a, - 0x95, 0x97, 0xfb, 0x37, 0xe3, 0x76, 0x9a, 0x69, 0x25, 0xfa, 0x29, 0xcf, - 0x71, 0x65, 0x46, 0x45, 0xff, 0x8f, 0x08, 0x84, 0x43, 0xab, 0xfc, 0xdb, - 0x90, 0x07, 0xc7, 0xa5, 0x97, 0x98, 0xfd, 0x59, 0x51, 0x26, 0xcf, 0xe8, - 0x6a, 0x80, 0xff, 0x79, 0xbd, 0x2c, 0xbe, 0xf8, 0x27, 0xa5, 0x96, 0xfd, - 0xb2, 0x6b, 0xba, 0x17, 0x7e, 0xde, 0xec, 0x36, 0x2c, 0xa9, 0x3d, 0x52, - 0x2c, 0xbf, 0x64, 0x50, 0x98, 0x2c, 0xbf, 0xb6, 0x1c, 0xe1, 0x0d, 0x65, - 0x4a, 0xe7, 0x24, 0x1b, 0xce, 0x35, 0x3f, 0x2b, 0xbc, 0x71, 0xbc, 0x86, - 0x4f, 0x48, 0x04, 0x28, 0xbc, 0xff, 0x9a, 0xcb, 0xfe, 0x19, 0x03, 0x99, - 0xb6, 0x6c, 0xb2, 0xf4, 0xe8, 0x96, 0x5f, 0xec, 0x19, 0x3f, 0x0c, 0x6b, - 0x2d, 0xe5, 0x97, 0x80, 0xfb, 0x11, 0xe1, 0x70, 0xc6, 0xb4, 0x89, 0x0e, - 0xae, 0xdf, 0x33, 0x98, 0xc5, 0x94, 0x67, 0x8a, 0xe4, 0x77, 0xf4, 0xeb, - 0xbd, 0xcd, 0xd5, 0x94, 0xc4, 0xde, 0xdc, 0x71, 0xb7, 0x18, 0x79, 0x10, - 0x5f, 0xc2, 0x0c, 0xa7, 0x36, 0x59, 0x62, 0x59, 0x7d, 0x3b, 0x4c, 0x16, - 0x50, 0xcf, 0x81, 0xcb, 0x88, 0x42, 0xfe, 0xf1, 0xf4, 0xa5, 0x8b, 0x2f, - 0xff, 0x60, 0xdd, 0x99, 0xf4, 0x33, 0xfc, 0x1a, 0xcb, 0xbc, 0xeb, 0x2e, - 0x8b, 0x8b, 0x2f, 0xd9, 0xbc, 0xc8, 0x78, 0x6b, 0x84, 0x16, 0xbf, 0xa3, - 0x77, 0x39, 0xc9, 0x59, 0x7d, 0x9f, 0x76, 0x56, 0x5f, 0xfc, 0xfa, 0xc1, - 0x4f, 0x59, 0xbd, 0xf4, 0xb2, 0xbc, 0x7c, 0xfb, 0xc8, 0xaf, 0xdb, 0x8f, - 0xb0, 0x90, 0x59, 0x51, 0x93, 0x9d, 0x6e, 0xf1, 0x27, 0xd9, 0x09, 0x60, - 0x12, 0x5f, 0x0f, 0x3c, 0xeb, 0x2f, 0xfd, 0x85, 0x0f, 0x61, 0x14, 0xb4, - 0xb2, 0xff, 0xd9, 0xad, 0x3c, 0x23, 0x6f, 0x9d, 0xd5, 0x95, 0x28, 0x81, - 0x81, 0xe5, 0x69, 0x31, 0x62, 0x52, 0xec, 0x28, 0xaf, 0xf6, 0xb9, 0xec, - 0x23, 0xe2, 0xca, 0x95, 0x6e, 0x78, 0x5a, 0x72, 0xb7, 0xdc, 0xd6, 0xff, - 0xb0, 0x78, 0x52, 0x40, 0x95, 0x96, 0x69, 0x65, 0xff, 0xf1, 0x7f, 0x9f, - 0x76, 0x7e, 0x87, 0x7f, 0x98, 0x2c, 0xa9, 0x3e, 0x53, 0x12, 0xa9, 0x7d, - 0xb8, 0x5d, 0xa3, 0x7e, 0x84, 0xf9, 0x88, 0xe7, 0xc8, 0xf2, 0x3d, 0x91, - 0x63, 0x92, 0x0c, 0xec, 0x4f, 0xd0, 0xfb, 0x38, 0xdb, 0xa2, 0x8d, 0xd7, - 0x55, 0x9e, 0x43, 0x27, 0x3e, 0x3d, 0x3d, 0xae, 0xea, 0xff, 0xc6, 0x5e, - 0x54, 0xc1, 0xfe, 0x4b, 0xa9, 0xec, 0xf7, 0x48, 0x23, 0xfe, 0xdf, 0x38, - 0x32, 0x22, 0x16, 0xe4, 0x26, 0xaf, 0xfb, 0x34, 0x53, 0xd8, 0x67, 0x96, - 0x5f, 0xd0, 0x2c, 0x09, 0xf5, 0x65, 0xff, 0xc4, 0x11, 0x5f, 0x99, 0x09, - 0x20, 0xac, 0xbf, 0x7f, 0x83, 0x78, 0x2c, 0xaf, 0x91, 0x2e, 0x65, 0x84, - 0x87, 0x7f, 0xdd, 0xe7, 0x8e, 0x58, 0xe3, 0x59, 0x7c, 0x3d, 0x3c, 0x16, - 0x5f, 0x17, 0xa4, 0x55, 0x97, 0xfe, 0xf1, 0x94, 0xfd, 0xc3, 0x64, 0xac, - 0xbd, 0xc9, 0xd2, 0xcb, 0xbb, 0xc0, 0x9e, 0xcf, 0xc7, 0xb5, 0x29, 0x82, - 0x0a, 0x70, 0x64, 0x40, 0x7b, 0xbf, 0xc1, 0x2c, 0x84, 0x1f, 0x8b, 0x2f, - 0xe7, 0x6b, 0x3f, 0x13, 0xab, 0x2e, 0x19, 0xac, 0xbe, 0xfb, 0x93, 0xf2, - 0xcb, 0x9e, 0x11, 0x8d, 0xd1, 0x8b, 0x5e, 0x17, 0xc6, 0xb2, 0xff, 0xe8, - 0xb5, 0x3b, 0xfd, 0x27, 0xa3, 0x89, 0x65, 0x40, 0xf9, 0x3e, 0x1d, 0xbf, - 0xce, 0x3f, 0x67, 0x78, 0xeb, 0x2f, 0xee, 0x60, 0xcf, 0x9b, 0x2c, 0xbd, - 0x09, 0xfd, 0x65, 0xf8, 0x48, 0xa1, 0x3b, 0xa9, 0x2a, 0x57, 0x0e, 0x32, - 0x19, 0xc2, 0xc6, 0x66, 0x67, 0xba, 0x32, 0xf3, 0x53, 0xc2, 0x44, 0x88, - 0xf8, 0x65, 0xd2, 0xe1, 0x07, 0x6c, 0x15, 0x97, 0xff, 0x0a, 0x40, 0x71, - 0x33, 0x87, 0x24, 0xb2, 0xf4, 0x79, 0x8d, 0x65, 0x68, 0xf8, 0x98, 0x87, - 0x7a, 0x5f, 0x4b, 0x2f, 0xe6, 0x77, 0xd3, 0xbe, 0x56, 0x5f, 0x61, 0x07, - 0xf5, 0x97, 0xfe, 0xe8, 0x19, 0xe9, 0x32, 0x71, 0xac, 0xbe, 0x84, 0x1f, - 0xab, 0x2b, 0xe3, 0xdf, 0x73, 0xdb, 0xe1, 0x74, 0xed, 0x2c, 0xb8, 0x3b, - 0xd6, 0x58, 0x45, 0x44, 0x0a, 0xb7, 0x55, 0x40, 0xa2, 0xb4, 0x7a, 0xdd, - 0x19, 0x68, 0x7e, 0xb1, 0x15, 0x46, 0xf5, 0x7f, 0x1b, 0x6c, 0x3e, 0x96, - 0x96, 0x5f, 0xef, 0x18, 0x8d, 0xff, 0xfe, 0x52, 0x56, 0x8f, 0xaa, 0x3c, - 0xca, 0xfd, 0x21, 0xf4, 0xe9, 0x65, 0xf1, 0x6e, 0x36, 0xf8, 0xb2, 0x8c, - 0xfd, 0x7a, 0x4a, 0x21, 0x3d, 0xfd, 0xec, 0x03, 0x6f, 0x38, 0xb2, 0xff, - 0x8b, 0xbc, 0x07, 0xdd, 0x36, 0x96, 0x5f, 0xff, 0xb1, 0xa9, 0x60, 0x1c, - 0x7d, 0xf8, 0x26, 0x4d, 0x2c, 0xbf, 0xfb, 0x3e, 0xe8, 0x7c, 0x63, 0xf6, - 0x6c, 0xb2, 0xfe, 0x7e, 0x66, 0x10, 0xab, 0x2b, 0x0f, 0xc8, 0x08, 0xd7, - 0x9d, 0xda, 0x59, 0x52, 0x9a, 0x08, 0xce, 0xfd, 0x0c, 0x92, 0x21, 0xbf, - 0xe9, 0xef, 0x23, 0x63, 0x0c, 0x6b, 0x2f, 0xf4, 0xbc, 0x38, 0x24, 0xee, - 0xac, 0xbf, 0xf4, 0x62, 0x71, 0x63, 0x73, 0xff, 0xe5, 0x65, 0xff, 0xed, - 0x81, 0xff, 0x20, 0x7c, 0xe4, 0xea, 0x0b, 0x2c, 0x62, 0xa2, 0x4b, 0x48, - 0xb7, 0xf9, 0xfd, 0xf7, 0x24, 0xba, 0xb2, 0xa0, 0x99, 0x27, 0x21, 0x91, - 0xd2, 0x9b, 0xff, 0xfc, 0x3f, 0x48, 0x7c, 0x71, 0x3b, 0xc0, 0x57, 0x11, - 0xf7, 0x56, 0x5f, 0x16, 0x6f, 0xc5, 0x95, 0xb2, 0x21, 0xda, 0x65, 0xbf, - 0xde, 0x38, 0xa1, 0x25, 0x05, 0x97, 0xff, 0xf4, 0xf7, 0x82, 0x17, 0x7c, - 0x07, 0x87, 0x79, 0x8d, 0x2c, 0xbf, 0xf4, 0xb5, 0xc6, 0xc3, 0x87, 0x3e, - 0xea, 0xca, 0x94, 0x7c, 0x84, 0x97, 0x46, 0x7d, 0x5d, 0xbe, 0x87, 0xb3, - 0x65, 0x97, 0xfd, 0x3f, 0x31, 0xfb, 0x9f, 0x75, 0x65, 0xcf, 0xb2, 0xca, - 0xf8, 0xfd, 0x08, 0x8d, 0xa3, 0xab, 0xfc, 0xc7, 0xe1, 0x4e, 0x0a, 0xb2, - 0xff, 0x80, 0x0f, 0x1f, 0x7c, 0xf0, 0x59, 0x7e, 0x07, 0xb5, 0x2d, 0x2c, - 0xba, 0x3d, 0xd6, 0x5d, 0x21, 0xc4, 0x56, 0xb0, 0xcb, 0xa7, 0x22, 0x14, - 0xdd, 0xa7, 0x59, 0x7f, 0xf6, 0xf3, 0xe7, 0x30, 0x82, 0xe5, 0xfa, 0xcb, - 0xfd, 0xe3, 0x18, 0xf1, 0xa8, 0x96, 0x5b, 0xfc, 0x3f, 0xa0, 0xa2, 0xdf, - 0x09, 0xe7, 0xea, 0xcb, 0x4e, 0x8f, 0x2d, 0x85, 0x15, 0xa4, 0xc0, 0xbd, - 0x0d, 0xbb, 0xff, 0x3f, 0xdc, 0xce, 0x9b, 0x1e, 0x0b, 0x2d, 0xd5, 0x95, - 0xf9, 0xe7, 0x88, 0x7d, 0x7f, 0xf6, 0x78, 0xc3, 0xec, 0xee, 0x0b, 0x05, - 0x97, 0xff, 0xfe, 0x7d, 0x0a, 0x3f, 0x4f, 0x3d, 0x9f, 0x43, 0xa7, 0x14, - 0xf0, 0x0b, 0x2b, 0x11, 0x66, 0xe8, 0x97, 0xc5, 0x18, 0x31, 0x2c, 0xbf, - 0xff, 0xd1, 0x14, 0xb3, 0x52, 0x1c, 0xd6, 0x78, 0xf5, 0xe7, 0x59, 0x7f, - 0xfc, 0xfa, 0xfe, 0x3f, 0x35, 0x30, 0x3f, 0xbb, 0x8b, 0x2f, 0xff, 0xfe, - 0x07, 0x3c, 0xf0, 0xe4, 0x0f, 0xa2, 0x6a, 0x7d, 0x3d, 0xe3, 0xfc, 0xb2, - 0xfd, 0x38, 0x5d, 0x8f, 0x59, 0x7f, 0x1f, 0xdf, 0xe9, 0xc6, 0xb2, 0xcc, - 0x59, 0x4c, 0x3e, 0x9f, 0x14, 0x91, 0x7d, 0xda, 0xdd, 0x59, 0x7a, 0x00, - 0x1a, 0xcb, 0x9f, 0xe8, 0xc7, 0xcb, 0x11, 0x73, 0x8d, 0x54, 0xaa, 0x56, - 0xc2, 0x53, 0x5f, 0xd2, 0x9f, 0x23, 0x42, 0xbf, 0x8f, 0x51, 0xdf, 0x3a, - 0x6b, 0x2f, 0xff, 0xec, 0x2d, 0xb9, 0xc9, 0xff, 0x3c, 0x7a, 0x76, 0xba, - 0xb2, 0xfe, 0x1e, 0x10, 0xba, 0x95, 0x95, 0x06, 0x63, 0x70, 0xe3, 0x72, - 0x0a, 0x0f, 0xd1, 0xaf, 0x45, 0x1d, 0xb6, 0xa1, 0x60, 0xc8, 0x6e, 0xfa, - 0x34, 0xaf, 0xde, 0x0a, 0x1a, 0xfd, 0x94, 0xef, 0xbd, 0x3d, 0xa3, 0x28, - 0xf5, 0xcb, 0xdf, 0x36, 0xf7, 0x16, 0x5f, 0xff, 0xe2, 0xc1, 0xfa, 0x5a, - 0x2c, 0x3d, 0xa3, 0xc1, 0xe3, 0xd2, 0xca, 0x96, 0x70, 0xf1, 0xd3, 0x27, - 0x5e, 0x1f, 0x20, 0x25, 0xbe, 0x90, 0x98, 0x56, 0x5f, 0x0f, 0x46, 0xd2, - 0xcb, 0x4a, 0xcb, 0xff, 0xe8, 0x9d, 0xe1, 0xc9, 0x60, 0xfd, 0x24, 0x15, - 0x95, 0x27, 0xbe, 0x62, 0x17, 0xfd, 0x91, 0x42, 0x7e, 0xcf, 0xba, 0xb2, - 0xdb, 0x61, 0xed, 0x91, 0x05, 0x41, 0x32, 0x51, 0x90, 0xea, 0x1b, 0xb7, - 0xc3, 0xf0, 0x36, 0x59, 0x7f, 0x3c, 0x33, 0x60, 0x12, 0xcb, 0xb0, 0x6b, - 0x28, 0x67, 0x86, 0x12, 0xdb, 0xff, 0x6b, 0x37, 0x62, 0xf3, 0xb0, 0xf8, - 0xb2, 0xdb, 0x4a, 0x32, 0xf1, 0x9f, 0x44, 0x57, 0x36, 0xf1, 0x65, 0x61, - 0xe7, 0xb9, 0xad, 0xff, 0xb7, 0x9f, 0x61, 0x31, 0xbb, 0x20, 0x59, 0x68, - 0x96, 0x5f, 0xa3, 0xc1, 0xec, 0x0a, 0xca, 0xc3, 0xff, 0x89, 0x0b, 0xa2, - 0x57, 0xf4, 0xf4, 0xff, 0x98, 0x2c, 0xba, 0x0e, 0xb2, 0xb4, 0x78, 0x6e, - 0x5b, 0x7f, 0x7d, 0xb3, 0xc2, 0x77, 0x56, 0x5c, 0x50, 0x59, 0x52, 0x79, - 0x1b, 0x19, 0x53, 0x61, 0xb5, 0xa7, 0x97, 0x38, 0x11, 0x0a, 0x36, 0x12, - 0xf3, 0x84, 0x0b, 0x21, 0x99, 0xea, 0x78, 0xf3, 0xc6, 0xe2, 0x51, 0xa8, - 0x72, 0x13, 0xfd, 0x72, 0x03, 0x4d, 0xff, 0x9b, 0x02, 0x4b, 0x03, 0xe7, - 0xfb, 0xf5, 0x97, 0xfa, 0x11, 0x61, 0x3f, 0xb1, 0x65, 0xf7, 0xd0, 0x96, - 0x2c, 0xbf, 0xe9, 0x2d, 0xb9, 0x8c, 0x9d, 0xd5, 0x97, 0x4f, 0x96, 0x5f, - 0xcf, 0x10, 0xe4, 0xb6, 0x59, 0x5b, 0x22, 0x88, 0x64, 0x64, 0x77, 0xbc, - 0x5a, 0xfa, 0x62, 0x3f, 0x2c, 0xbd, 0xb9, 0x2c, 0x59, 0x4e, 0x78, 0x3b, - 0x84, 0x57, 0xe2, 0x1f, 0xa6, 0x0b, 0x2f, 0xde, 0x98, 0x67, 0x16, 0x5f, - 0x6e, 0x49, 0x05, 0x65, 0xfd, 0xec, 0x15, 0xe7, 0x65, 0x97, 0xd3, 0x16, - 0xa5, 0x65, 0xcc, 0xc5, 0x94, 0xc3, 0x71, 0xe2, 0x2b, 0xe9, 0x03, 0x6c, - 0xd6, 0x5f, 0x70, 0xa5, 0xa5, 0x97, 0xe1, 0xfb, 0x0a, 0x0b, 0x2b, 0x0f, - 0x25, 0xc8, 0xaf, 0xcf, 0xce, 0x9c, 0x4b, 0x2f, 0xf1, 0xea, 0x76, 0x7d, - 0x05, 0x65, 0xbb, 0x19, 0x50, 0x34, 0x92, 0x0c, 0x9f, 0x09, 0xc2, 0x48, - 0x6c, 0xda, 0x21, 0xf3, 0x77, 0x08, 0x3a, 0x51, 0x7f, 0xfb, 0x41, 0x2c, - 0xde, 0x59, 0x3a, 0x9e, 0x2c, 0xbf, 0xcf, 0x17, 0xb1, 0x99, 0xc5, 0x95, - 0x87, 0xf9, 0xbd, 0x26, 0xf4, 0x7c, 0xf5, 0x65, 0xf4, 0xef, 0x3d, 0xeb, - 0x2e, 0xc6, 0x2c, 0xb0, 0xb1, 0x8d, 0xd9, 0x92, 0xd6, 0xc8, 0x83, 0x25, - 0xab, 0xf0, 0xbd, 0xcc, 0x62, 0xcb, 0xff, 0x42, 0x06, 0x19, 0x64, 0x5c, - 0x95, 0x95, 0x88, 0x8a, 0x32, 0x37, 0x28, 0xbf, 0xfd, 0x11, 0x60, 0xa2, - 0xbf, 0x78, 0x65, 0x12, 0xcb, 0xef, 0x66, 0xc4, 0xb2, 0xfd, 0x83, 0x3d, - 0x71, 0x65, 0xf8, 0x48, 0x1f, 0xfc, 0x59, 0x7e, 0xd0, 0x67, 0x36, 0x59, - 0x7b, 0xbd, 0xf9, 0x65, 0xff, 0xdf, 0x04, 0xfd, 0x1b, 0x73, 0x3c, 0x6c, - 0x59, 0x5f, 0x9f, 0x47, 0x47, 0xae, 0x06, 0x96, 0x56, 0x23, 0x2b, 0x90, - 0x90, 0x8f, 0x23, 0xb4, 0xec, 0x9a, 0x93, 0x09, 0xda, 0x87, 0xa5, 0x4a, - 0xf7, 0x76, 0x4a, 0x86, 0x38, 0xc3, 0xbd, 0x19, 0x83, 0x96, 0xf1, 0x33, - 0xb1, 0xba, 0x5f, 0xf8, 0x8c, 0x4f, 0xb3, 0x7f, 0xd2, 0x22, 0xcb, 0x41, - 0x65, 0xff, 0xe3, 0xcd, 0x0e, 0x5a, 0xcf, 0x4f, 0xdc, 0x59, 0x7c, 0x24, - 0xeb, 0xf5, 0x97, 0xfe, 0x9f, 0xbd, 0xe9, 0xda, 0x40, 0xc5, 0x97, 0xff, - 0x6b, 0x4f, 0xfe, 0x78, 0xf5, 0xe7, 0x59, 0x68, 0x2c, 0xae, 0x1e, 0xa8, - 0x88, 0x97, 0x3e, 0x96, 0x5f, 0xff, 0x85, 0xce, 0x9f, 0x3c, 0x0c, 0xef, - 0xee, 0x39, 0x59, 0x7f, 0x74, 0xf6, 0x64, 0x85, 0x65, 0x0d, 0x13, 0x5a, - 0x16, 0xf2, 0xb5, 0xff, 0xef, 0xe2, 0x81, 0xea, 0x11, 0x40, 0xf5, 0x05, - 0x97, 0xfb, 0xd9, 0xec, 0x0b, 0x85, 0x65, 0xc7, 0xb2, 0xca, 0x09, 0xe4, - 0xf4, 0xca, 0xbe, 0x45, 0xce, 0xa1, 0x2f, 0x7f, 0x83, 0x11, 0xb0, 0x0f, - 0xc5, 0x97, 0xd8, 0x0d, 0x6c, 0xb2, 0xbe, 0x3d, 0x76, 0x1a, 0x5e, 0x91, - 0x8d, 0x65, 0xd9, 0xb8, 0xb2, 0xfb, 0xf1, 0x0a, 0x0b, 0x2c, 0xe4, 0x6f, - 0x7a, 0x33, 0x7e, 0xe7, 0x30, 0xbf, 0x59, 0x5f, 0x9e, 0x69, 0x12, 0xd4, - 0xaa, 0x54, 0x38, 0x74, 0xbc, 0x20, 0x3a, 0x46, 0x08, 0x53, 0x5d, 0xf4, - 0x4b, 0x2f, 0xc2, 0x77, 0xf6, 0x62, 0xca, 0x61, 0xe1, 0xfe, 0x33, 0x74, - 0xf1, 0x65, 0xf4, 0xfa, 0x46, 0xb2, 0x82, 0x6d, 0xe2, 0x16, 0xbf, 0xe8, - 0x3e, 0xba, 0x52, 0xce, 0x2c, 0xbf, 0xd2, 0x39, 0xd0, 0x27, 0xf5, 0x97, - 0x8b, 0x3e, 0x59, 0x73, 0x39, 0x12, 0x21, 0xd8, 0x70, 0x46, 0x77, 0xff, - 0xd3, 0x9f, 0x77, 0xd8, 0x70, 0x31, 0x45, 0xc5, 0x97, 0xe2, 0x9d, 0xc2, - 0x95, 0x97, 0xff, 0xf3, 0xb4, 0x37, 0x7d, 0x6d, 0xe7, 0xef, 0x8f, 0x50, - 0x59, 0x7f, 0xce, 0x59, 0xde, 0x9b, 0x5d, 0x59, 0x7f, 0xff, 0xf4, 0x50, - 0x32, 0xec, 0xff, 0xb9, 0xd3, 0x17, 0x59, 0x3f, 0x40, 0xd8, 0xb2, 0xa5, - 0x15, 0xe4, 0x71, 0x52, 0xab, 0x8f, 0x16, 0x8e, 0x15, 0x3a, 0x3c, 0x75, - 0x02, 0x28, 0x04, 0x61, 0x75, 0x1d, 0xb6, 0x1a, 0xd2, 0x8d, 0x09, 0xfc, - 0x2c, 0x7c, 0x15, 0x0c, 0x22, 0x31, 0x25, 0x78, 0x93, 0xf8, 0x4a, 0xf2, - 0x58, 0x8f, 0x65, 0xbf, 0x5f, 0xfd, 0xdf, 0xa1, 0x9d, 0x3d, 0xf2, 0x41, - 0x59, 0x7f, 0x48, 0x3e, 0x3f, 0xb7, 0x16, 0x5f, 0xb1, 0xac, 0xfb, 0xab, - 0x2a, 0x31, 0xed, 0x99, 0x95, 0xfe, 0xfa, 0x18, 0x3f, 0x66, 0xcb, 0x2b, - 0xe4, 0x7c, 0xea, 0x14, 0x24, 0x47, 0x7e, 0x9d, 0x7d, 0x0e, 0x2c, 0xbf, - 0xff, 0xed, 0x44, 0xfe, 0x3e, 0x4c, 0x47, 0xde, 0xe7, 0xbf, 0x3d, 0xeb, - 0x2b, 0x64, 0x49, 0x80, 0xa6, 0xf6, 0xb3, 0x8b, 0x2f, 0xc6, 0x5b, 0xf2, - 0x0b, 0x2f, 0x61, 0xec, 0xb2, 0x85, 0x3e, 0x06, 0x0e, 0x78, 0xa2, 0xfe, - 0x9d, 0xb3, 0xb8, 0xc5, 0x97, 0xff, 0x61, 0x00, 0x3e, 0xc6, 0x44, 0xe1, - 0x59, 0x46, 0x8a, 0x1d, 0x18, 0x70, 0xb6, 0xf9, 0x87, 0x23, 0x59, 0x7f, - 0x88, 0x5f, 0x4b, 0x18, 0x4b, 0x2f, 0xed, 0xd3, 0x9d, 0xc0, 0x75, 0x65, - 0xfa, 0x62, 0xcc, 0x0a, 0xca, 0x94, 0x57, 0x61, 0x09, 0x99, 0xf8, 0xce, - 0xf8, 0x98, 0xfd, 0x59, 0x78, 0x8e, 0x0b, 0x2f, 0xfd, 0xec, 0x88, 0xe2, - 0x06, 0xe0, 0x38, 0xb2, 0xf1, 0xe8, 0x55, 0x97, 0xdd, 0x39, 0xf9, 0x65, - 0x1a, 0x20, 0xc9, 0x13, 0x83, 0xb7, 0xfd, 0x31, 0x4f, 0x7d, 0x9a, 0x0a, - 0xcb, 0xde, 0x98, 0x96, 0x5a, 0x7e, 0x3d, 0x6d, 0xc3, 0x9a, 0x82, 0x2b, - 0xf9, 0x08, 0x4b, 0xc4, 0x66, 0xb2, 0xf6, 0xcf, 0xb2, 0xcb, 0xff, 0x44, - 0x7e, 0x9f, 0xa1, 0x31, 0x4a, 0xca, 0x33, 0xde, 0x00, 0xf5, 0xf3, 0x27, - 0xac, 0x59, 0x7e, 0x1b, 0x94, 0xf9, 0x65, 0xfd, 0x09, 0xfb, 0xb9, 0xe5, - 0x97, 0xff, 0xbb, 0xc3, 0xfe, 0x61, 0xcc, 0xcf, 0xba, 0xb2, 0xa2, 0x3f, - 0x9e, 0x17, 0x57, 0x91, 0x84, 0xd4, 0x27, 0xef, 0xf7, 0x9e, 0x2c, 0x3d, - 0xc1, 0xac, 0xa9, 0x4d, 0x0c, 0xe1, 0xc9, 0xc2, 0x9b, 0xf3, 0x37, 0x08, - 0x1b, 0x2c, 0xbf, 0xdc, 0x3f, 0x1c, 0x47, 0xb2, 0xcb, 0xff, 0xe0, 0x6b, - 0x52, 0x58, 0x29, 0xfb, 0xd8, 0x22, 0xca, 0x94, 0x58, 0x61, 0x66, 0x8d, - 0x2f, 0x45, 0xa9, 0x59, 0x7e, 0x73, 0x1c, 0x92, 0xcb, 0xa4, 0x0b, 0x2a, - 0x4f, 0x60, 0x87, 0x78, 0x4b, 0x7d, 0xb4, 0xee, 0x4a, 0xcb, 0xef, 0x7e, - 0xfd, 0x59, 0x6f, 0x2c, 0xb6, 0xf7, 0x36, 0x80, 0x23, 0xbf, 0xf8, 0xd8, - 0x7c, 0xf4, 0x9e, 0x81, 0xba, 0xb2, 0xa5, 0x18, 0xe6, 0xb2, 0xe5, 0x17, - 0xf1, 0x70, 0xf7, 0xc8, 0xd6, 0x5f, 0xf7, 0xe6, 0x5d, 0x9e, 0x03, 0xcb, - 0x2f, 0x11, 0x8a, 0xb2, 0xfc, 0xfa, 0xd4, 0xec, 0xb2, 0xfe, 0xe9, 0xe8, - 0xfa, 0x05, 0x95, 0x19, 0x15, 0x4c, 0x39, 0xe8, 0xe0, 0x85, 0x17, 0xb8, - 0xff, 0xac, 0xa9, 0x3d, 0xde, 0xa0, 0x5f, 0xc0, 0x13, 0xd9, 0xf7, 0x16, - 0x5f, 0xff, 0xff, 0x4f, 0x49, 0xc2, 0x7c, 0x88, 0xf4, 0x19, 0xe7, 0x67, - 0xe9, 0x13, 0x38, 0xb2, 0xff, 0x73, 0x35, 0x3b, 0xe7, 0x75, 0x65, 0x69, - 0x16, 0x9d, 0x7d, 0xbf, 0xde, 0xcf, 0xa7, 0x46, 0xc5, 0x97, 0xff, 0xff, - 0xfd, 0xcc, 0xef, 0x9d, 0x9c, 0xd6, 0x8f, 0xee, 0xbb, 0x8b, 0x99, 0x10, - 0x3b, 0x2d, 0x7d, 0x2b, 0x2f, 0xc0, 0x8b, 0x23, 0xf1, 0x65, 0x71, 0x18, - 0x5d, 0x84, 0xbd, 0xff, 0x7d, 0xc2, 0x36, 0x77, 0x37, 0x56, 0x5d, 0x9a, - 0x59, 0x52, 0x7a, 0x5e, 0x3c, 0xbf, 0xba, 0x4f, 0xfe, 0x12, 0xcb, 0xf1, - 0x3f, 0xf8, 0x4b, 0x2a, 0x31, 0xe9, 0x74, 0xb2, 0xa5, 0x51, 0x48, 0xc8, - 0xde, 0x1e, 0xbd, 0x7a, 0x03, 0x9d, 0xff, 0xba, 0x7c, 0x27, 0xec, 0x4e, - 0x35, 0x97, 0xdf, 0xbf, 0xdc, 0x59, 0x73, 0x4d, 0x2c, 0xae, 0x1b, 0xd6, - 0x89, 0x2f, 0xbc, 0xf9, 0xe4, 0x8d, 0xcd, 0x15, 0x62, 0x34, 0x0e, 0x13, - 0xb7, 0xe1, 0x31, 0x87, 0xa5, 0x97, 0x31, 0xd6, 0x5f, 0xee, 0xc9, 0x06, - 0x39, 0xfd, 0x8b, 0x2b, 0xc7, 0x9e, 0x01, 0x6b, 0xde, 0x9f, 0xd6, 0x5e, - 0xdc, 0xdc, 0x95, 0x97, 0xfc, 0x3f, 0x1e, 0x8f, 0xe8, 0xa5, 0x65, 0x39, - 0xee, 0x00, 0x8a, 0xe6, 0xce, 0xf5, 0x95, 0x89, 0xa8, 0x1b, 0xbb, 0x91, - 0x13, 0xcf, 0x48, 0x6f, 0x7a, 0x36, 0xea, 0xcb, 0xc4, 0x13, 0x59, 0x7e, - 0xf4, 0x96, 0x6c, 0xb2, 0xa4, 0xf9, 0x0c, 0x89, 0xc6, 0xef, 0xf7, 0xa7, - 0xa0, 0x60, 0x3a, 0xb2, 0xa5, 0xb1, 0x04, 0xda, 0x19, 0x10, 0x8c, 0x94, - 0x70, 0xdd, 0xc3, 0xa1, 0x48, 0x7e, 0x8d, 0x34, 0xca, 0x37, 0x5e, 0x22, - 0x8d, 0x93, 0x50, 0xe4, 0x64, 0x21, 0xbd, 0x0f, 0x47, 0x2d, 0xfe, 0x33, - 0x56, 0xd9, 0x09, 0x4b, 0x0d, 0xe4, 0x63, 0xfd, 0x8d, 0xdb, 0x7c, 0x2a, - 0xf7, 0x0b, 0x2f, 0xf0, 0x7c, 0x7d, 0xe3, 0xfc, 0xb2, 0xf6, 0x6b, 0x7a, - 0xcb, 0xe3, 0xd0, 0x91, 0x2c, 0xbf, 0xf1, 0x7e, 0xc7, 0x81, 0x4f, 0x78, - 0xb2, 0xec, 0xc5, 0x97, 0xc1, 0xf1, 0x88, 0xb2, 0x85, 0x47, 0x61, 0x9a, - 0x78, 0x79, 0xc9, 0x1b, 0x67, 0xc2, 0x0a, 0xdf, 0xa2, 0x98, 0xbd, 0x2b, - 0x2f, 0xff, 0x17, 0x78, 0x27, 0x4c, 0x81, 0x0c, 0xe2, 0xcb, 0x84, 0x95, - 0x97, 0x9a, 0x69, 0xa4, 0x97, 0xd0, 0xee, 0x05, 0x23, 0x73, 0x41, 0x7e, - 0xc1, 0x9f, 0xfc, 0x5d, 0xdf, 0xca, 0xd1, 0xf4, 0x91, 0xa5, 0xe9, 0xe4, - 0x16, 0x54, 0xa6, 0xa1, 0x85, 0x3a, 0x4a, 0x78, 0x50, 0x7e, 0x43, 0x7c, - 0xc1, 0xe1, 0x2c, 0xbb, 0xbc, 0x59, 0x7f, 0xf8, 0x79, 0x82, 0xc6, 0xce, - 0x9f, 0x3c, 0x6b, 0x2f, 0xf7, 0x7d, 0x3d, 0xe7, 0x8d, 0x65, 0xcf, 0x05, - 0x97, 0xe8, 0xd9, 0x11, 0xec, 0xb2, 0xa3, 0x1f, 0xa3, 0x0c, 0xf8, 0x2d, - 0x6e, 0x2c, 0xbf, 0xdd, 0xfa, 0x1e, 0x92, 0xd9, 0x25, 0xff, 0xb3, 0xe8, - 0x1e, 0x11, 0x60, 0xd6, 0x5f, 0x30, 0x1f, 0x75, 0x65, 0xf6, 0x7a, 0x77, - 0xac, 0xad, 0x8f, 0x1f, 0x84, 0x95, 0x29, 0xf9, 0xec, 0x2f, 0xf4, 0x2f, - 0xf4, 0x62, 0x42, 0x3c, 0x35, 0xec, 0x20, 0xef, 0x1f, 0x0d, 0x65, 0x8d, - 0x65, 0xf3, 0x88, 0xe3, 0x59, 0x77, 0xd0, 0xf1, 0xe6, 0xb8, 0xd8, 0x04, - 0x2f, 0xf8, 0xa5, 0xae, 0x19, 0x7d, 0x05, 0x97, 0xff, 0xfe, 0xe7, 0xe7, - 0xa1, 0xe1, 0x78, 0xc3, 0xe3, 0xe8, 0xb2, 0x50, 0x59, 0x74, 0x85, 0x65, - 0x12, 0x2e, 0xfa, 0x73, 0xbd, 0xb2, 0xf0, 0x93, 0xba, 0xb2, 0xf0, 0xbe, - 0x35, 0x95, 0xb1, 0xbd, 0x32, 0x0b, 0xf1, 0x4b, 0x58, 0x4b, 0x2f, 0xfb, - 0xdf, 0x48, 0xb1, 0x42, 0x77, 0x56, 0x5b, 0x83, 0x3e, 0x5c, 0x26, 0xad, - 0x22, 0xb7, 0xb0, 0x86, 0xa5, 0x97, 0xe8, 0x68, 0xe5, 0x8b, 0x2b, 0x63, - 0x65, 0xf0, 0x5d, 0x4a, 0xb4, 0xac, 0x85, 0xe1, 0xc3, 0xd9, 0xe3, 0x1b, - 0x02, 0xd5, 0xfe, 0x87, 0xa7, 0xee, 0xcf, 0x96, 0x5f, 0xfe, 0x87, 0x41, - 0xed, 0x4b, 0x50, 0x3f, 0xf1, 0x65, 0x7c, 0x88, 0x02, 0x34, 0xbf, 0xff, - 0x75, 0xf5, 0x87, 0xff, 0x33, 0xc7, 0xaf, 0x3a, 0xcb, 0xd2, 0x43, 0x59, - 0x7e, 0xfb, 0x91, 0x67, 0x56, 0x56, 0x22, 0xf3, 0xe2, 0x3f, 0x2a, 0x10, - 0xdd, 0xfc, 0x7d, 0x27, 0xff, 0x8b, 0x2f, 0xb8, 0x72, 0x15, 0x97, 0xff, - 0x45, 0x9a, 0xc7, 0x64, 0xf8, 0xc6, 0xb2, 0xb0, 0xf9, 0x3a, 0x43, 0x7f, - 0x01, 0x99, 0xef, 0x9d, 0x65, 0x0a, 0x8e, 0x02, 0x84, 0x8f, 0x08, 0x6f, - 0xff, 0x18, 0xc9, 0xfe, 0x2c, 0xee, 0x1f, 0xeb, 0x2f, 0xb6, 0xd9, 0xf7, - 0x56, 0x5f, 0x04, 0x60, 0xd9, 0x65, 0xe8, 0x70, 0x0b, 0x2f, 0xe2, 0xe9, - 0xfa, 0x04, 0xb2, 0xdd, 0xc3, 0xed, 0xf1, 0x23, 0x8e, 0xdf, 0xff, 0x33, - 0x3e, 0xe9, 0x66, 0xcc, 0xc6, 0x61, 0x2c, 0xa9, 0x44, 0x11, 0x99, 0xdf, - 0xbc, 0xfa, 0xcd, 0x2c, 0xb8, 0x0e, 0xb2, 0x82, 0x6f, 0x48, 0x9e, 0xfa, - 0x1d, 0x00, 0xd6, 0x5f, 0xa4, 0xe0, 0xfc, 0x59, 0x5f, 0x1e, 0x4e, 0x88, - 0xef, 0xec, 0xef, 0x81, 0x3f, 0x2c, 0xbe, 0xe9, 0xeb, 0x16, 0x5f, 0x7d, - 0xc3, 0x14, 0x67, 0x9f, 0x85, 0xd5, 0xb2, 0x62, 0xa4, 0xd3, 0xd7, 0x2b, - 0xfd, 0x9c, 0x2c, 0xdf, 0xe3, 0x59, 0x7f, 0xe6, 0x4f, 0xc5, 0x9f, 0xf2, - 0x77, 0x56, 0x56, 0x8f, 0xd5, 0x86, 0x77, 0xf8, 0x65, 0x9b, 0xcf, 0x4e, - 0xb2, 0xa5, 0x1f, 0x67, 0x0a, 0x60, 0x11, 0x5f, 0xb6, 0xdd, 0x3c, 0xf9, - 0x65, 0xf8, 0xf9, 0x3b, 0x31, 0x76, 0x7e, 0xaf, 0xd2, 0xfa, 0xfc, 0x0b, - 0xb3, 0xf5, 0x73, 0xc1, 0x76, 0x7e, 0xaf, 0x81, 0x0c, 0xe2, 0xec, 0xfd, - 0x50, 0xcf, 0x48, 0x88, 0xef, 0xd3, 0x9c, 0xc2, 0x5d, 0x9f, 0xaa, 0x5d, - 0x9f, 0xab, 0x9f, 0xab, 0xb3, 0xf4, 0xc2, 0xe2, 0xd0, 0x23, 0xfc, 0x02, - 0x55, 0xf6, 0x47, 0x9f, 0xeb, 0xb3, 0xf5, 0x4b, 0xb3, 0xf5, 0x77, 0xf2, - 0xbb, 0x3f, 0x57, 0xfd, 0x9f, 0xbe, 0xb3, 0x7e, 0x0d, 0x76, 0x7e, 0xaf, - 0xec, 0xf1, 0xf0, 0x1b, 0x2e, 0xcf, 0xd5, 0x7e, 0x8a, 0x62, 0x24, 0xe2, - 0x3d, 0xf6, 0xb6, 0x9e, 0xae, 0xcf, 0xd5, 0x2e, 0xcf, 0xd6, 0x1b, 0x0b, - 0x9a, 0x69, 0x76, 0x7e, 0xaa, 0x0a, 0xc7, 0x46, 0x6b, 0x90, 0x87, 0xfa, - 0x13, 0xba, 0x27, 0x61, 0x97, 0x21, 0x7d, 0xd6, 0x06, 0x89, 0xee, 0x91, - 0x53, 0x67, 0xe8, 0xdd, 0x12, 0x17, 0xfd, 0xb4, 0xef, 0xc3, 0xd6, 0x6c, - 0xb2, 0xa0, 0x7e, 0x9a, 0x39, 0xbf, 0xa7, 0x04, 0x98, 0x41, 0x65, 0xfe, - 0xe6, 0x42, 0x13, 0xdf, 0x96, 0x56, 0x22, 0x0c, 0x88, 0xb8, 0x5b, 0x7f, - 0xec, 0x08, 0xf3, 0x4d, 0x3f, 0xa2, 0x59, 0x7f, 0xf7, 0x0b, 0x22, 0x9d, - 0xbf, 0x7f, 0xb8, 0xb2, 0xe8, 0x6c, 0x92, 0xfa, 0x27, 0x7d, 0x2c, 0xad, - 0x91, 0xdf, 0xf1, 0x77, 0x90, 0x09, 0x1f, 0x70, 0x62, 0xe3, 0xd9, 0x65, - 0x69, 0x78, 0x91, 0xe7, 0x6f, 0xfa, 0x9d, 0x7f, 0x70, 0xa7, 0xc0, 0xd2, - 0xcb, 0xfb, 0x85, 0x9d, 0xcf, 0xd6, 0x57, 0xc7, 0xb4, 0xc2, 0xdb, 0xfe, - 0x88, 0xb3, 0x7f, 0xbe, 0x7d, 0xd5, 0x97, 0xf7, 0x73, 0xd3, 0xaf, 0xd6, - 0x5f, 0xf6, 0x8d, 0x9c, 0x12, 0x72, 0x25, 0x97, 0xfc, 0x19, 0xfc, 0x60, - 0x7d, 0x41, 0x65, 0xff, 0x9d, 0xf6, 0xc6, 0x39, 0x7d, 0x05, 0x97, 0x9c, - 0x5e, 0x61, 0xfc, 0x44, 0x75, 0x66, 0xa3, 0x26, 0x29, 0xa2, 0xe2, 0x85, - 0x95, 0x4a, 0x76, 0x98, 0x46, 0xf1, 0xa9, 0xdf, 0x18, 0xa6, 0xd2, 0xcb, - 0xee, 0xfb, 0x36, 0x59, 0x4c, 0x3c, 0x62, 0x23, 0xb8, 0x6d, 0x2c, 0xbf, - 0xc3, 0x13, 0x7b, 0xb9, 0x05, 0x65, 0x49, 0xf7, 0x61, 0x09, 0x8c, 0x5e, - 0x67, 0xfa, 0x59, 0x7f, 0xfb, 0xee, 0xf8, 0xc4, 0x1e, 0x61, 0x70, 0xd6, - 0x5f, 0xff, 0xa1, 0xa9, 0xde, 0xf1, 0x48, 0xfd, 0x2c, 0x78, 0x96, 0x5f, - 0xed, 0xc7, 0xe1, 0xf2, 0x2c, 0x59, 0x51, 0x91, 0xed, 0x83, 0xdf, 0x25, - 0x69, 0x62, 0xff, 0xa7, 0xe9, 0xce, 0x71, 0x9f, 0x2c, 0xbc, 0x65, 0x05, - 0x95, 0x2d, 0xbc, 0xd6, 0xc9, 0xd0, 0x9d, 0x5e, 0xc8, 0xe5, 0x0e, 0x31, - 0xcd, 0xd3, 0x58, 0x92, 0x35, 0x18, 0xb7, 0xa5, 0x37, 0x3c, 0xf2, 0xb9, - 0x4a, 0x48, 0xe4, 0x3a, 0x01, 0x19, 0x13, 0x47, 0xfb, 0x87, 0x57, 0xf3, - 0xe8, 0x3f, 0xed, 0x8b, 0x2f, 0xff, 0xf8, 0xfc, 0xfe, 0x98, 0x37, 0xcf, - 0xe6, 0x1c, 0x1f, 0xa7, 0x4b, 0x2f, 0xfc, 0x2c, 0xfc, 0x53, 0xef, 0x00, - 0x2b, 0x2f, 0xfe, 0xd4, 0xe1, 0x64, 0x0f, 0xd9, 0xbd, 0x65, 0xff, 0xf1, - 0x38, 0xbc, 0xcd, 0xf3, 0xf7, 0x78, 0x06, 0x2c, 0xa1, 0xa2, 0x58, 0x90, - 0xef, 0xf1, 0x4f, 0x7e, 0x89, 0xd8, 0xb2, 0xed, 0xf8, 0xb2, 0x98, 0x79, - 0x84, 0x69, 0x7f, 0x13, 0x8b, 0xe7, 0x82, 0xcb, 0x9f, 0x4b, 0x2f, 0xc1, - 0xc6, 0x4f, 0x16, 0x5e, 0x69, 0xa6, 0x92, 0x5e, 0x12, 0x7f, 0x48, 0xdc, - 0xd0, 0x5f, 0xf3, 0xc0, 0x4d, 0x66, 0xf7, 0xd2, 0xcb, 0xff, 0xf7, 0xb2, - 0x02, 0x11, 0x66, 0xd1, 0x42, 0x75, 0xb2, 0xca, 0x24, 0x49, 0xf4, 0xee, - 0xff, 0x14, 0x0d, 0x99, 0xf7, 0x56, 0x5f, 0x7c, 0x13, 0x25, 0x97, 0xbf, - 0x98, 0x39, 0xea, 0xf4, 0xce, 0xfd, 0x1a, 0x43, 0x9a, 0x59, 0x7f, 0xe8, - 0xd2, 0x40, 0x64, 0xb5, 0x3c, 0x59, 0x6c, 0x19, 0xf4, 0x91, 0x4d, 0xff, - 0x75, 0xcb, 0x39, 0xa9, 0xe2, 0xcb, 0xe8, 0x4f, 0xff, 0x2c, 0xbf, 0xbf, - 0x92, 0xcf, 0xba, 0xb2, 0xe7, 0xfe, 0x31, 0xe9, 0x08, 0x49, 0x7f, 0xf8, - 0x42, 0x88, 0x57, 0x1f, 0xb0, 0x39, 0xa5, 0x94, 0x69, 0xe4, 0x7a, 0x13, - 0x8e, 0x4c, 0x50, 0x89, 0xe1, 0x8d, 0xed, 0xf3, 0xa5, 0x95, 0xb2, 0xef, - 0xc4, 0x0b, 0xc6, 0xd1, 0x90, 0xd6, 0x36, 0xe6, 0x10, 0xf8, 0xb5, 0xc5, - 0xbf, 0x47, 0x28, 0x61, 0xf6, 0x51, 0xfe, 0xf5, 0x2b, 0x8f, 0x65, 0x97, - 0xf8, 0x82, 0x33, 0xdf, 0x23, 0x59, 0x43, 0x3c, 0xb0, 0x0b, 0xdb, 0xf5, - 0x97, 0xfd, 0x93, 0xbb, 0x1b, 0xcf, 0x9a, 0x59, 0x68, 0x96, 0x59, 0x8b, - 0x2c, 0xc3, 0x34, 0x6e, 0x25, 0x7e, 0xcf, 0xf9, 0x30, 0x59, 0x7e, 0x36, - 0x0c, 0x78, 0xb2, 0xdf, 0x91, 0xe7, 0xc7, 0x94, 0x5f, 0xf7, 0x7d, 0x9c, - 0x8d, 0xd3, 0xd9, 0x65, 0x49, 0xf3, 0x99, 0x5d, 0xff, 0x11, 0x81, 0xb7, - 0xc8, 0xf9, 0xea, 0xcb, 0xfd, 0xdc, 0xff, 0x73, 0xa6, 0xc5, 0x97, 0x9f, - 0x5f, 0xac, 0xa8, 0x27, 0xff, 0x10, 0x93, 0x17, 0xfd, 0x0d, 0xb2, 0x20, - 0xe9, 0xfc, 0x79, 0xbd, 0xf6, 0x74, 0x4d, 0x96, 0x5e, 0x69, 0xa6, 0x93, - 0x10, 0x81, 0x7c, 0x17, 0x70, 0xa6, 0x21, 0x01, 0xb9, 0xae, 0xbe, 0x7d, - 0x63, 0x4b, 0x2b, 0x47, 0xc7, 0xbd, 0x06, 0xf3, 0x4d, 0x34, 0x98, 0x83, - 0xca, 0x4c, 0x41, 0xe3, 0x73, 0x5d, 0x7f, 0x75, 0xd8, 0xc9, 0x62, 0xcb, - 0xf3, 0xf5, 0xcb, 0xf5, 0x97, 0xf6, 0x77, 0xc0, 0x78, 0x2c, 0xbc, 0xd3, - 0x4d, 0x2c, 0xbd, 0xc9, 0x15, 0x23, 0x73, 0x41, 0x58, 0x9a, 0x68, 0x54, - 0xbe, 0x2b, 0xf1, 0x69, 0x13, 0xf5, 0x36, 0xb7, 0x55, 0x2c, 0xb2, 0x50, - 0x15, 0xe6, 0xa4, 0x96, 0x5f, 0xfd, 0xe9, 0x81, 0xf7, 0x3c, 0x19, 0x62, - 0xca, 0x94, 0x47, 0x39, 0x7e, 0xf1, 0xcb, 0xff, 0x78, 0xc7, 0x25, 0xdc, - 0xfb, 0xab, 0x2f, 0xf1, 0x4b, 0x39, 0xc0, 0x12, 0xca, 0x09, 0xf8, 0x70, - 0xfa, 0xff, 0xf6, 0x78, 0xf5, 0xe7, 0xce, 0x1b, 0xec, 0xb2, 0xff, 0xdb, - 0x33, 0x19, 0x85, 0xdc, 0x0a, 0xcb, 0xe9, 0xec, 0xb1, 0x65, 0x7c, 0x8c, - 0x96, 0x11, 0x12, 0x47, 0x4f, 0xaf, 0xfb, 0xd2, 0x41, 0xfb, 0x8f, 0xba, - 0xb2, 0xfd, 0x02, 0x06, 0xc4, 0xb2, 0xfe, 0x1f, 0x31, 0x93, 0xba, 0xb2, - 0xf0, 0x1c, 0x45, 0x97, 0x6d, 0xf2, 0xcb, 0x38, 0xcd, 0xb7, 0xe3, 0xb5, - 0x04, 0x78, 0xfc, 0x78, 0xe5, 0x0d, 0x33, 0x5f, 0x11, 0xc8, 0xab, 0x2f, - 0xfb, 0x0a, 0x7d, 0xe9, 0x68, 0xd6, 0x5e, 0x12, 0x7f, 0x59, 0x5b, 0x1f, - 0xb0, 0xa4, 0x2c, 0x37, 0xbf, 0x06, 0x7d, 0x2c, 0x49, 0x7e, 0x27, 0x22, - 0xc4, 0x97, 0x60, 0x52, 0x5c, 0xd3, 0x49, 0x2b, 0x0f, 0xeb, 0x84, 0xfd, - 0x24, 0x68, 0x5a, 0xff, 0x10, 0x93, 0xff, 0xb3, 0x71, 0x23, 0x73, 0x79, - 0x7f, 0xfb, 0x61, 0xe9, 0xc2, 0x59, 0xbf, 0x47, 0xc5, 0x95, 0x2a, 0xd7, - 0xb2, 0x32, 0xe3, 0x85, 0xa3, 0xc3, 0x9c, 0x09, 0x37, 0xff, 0x4f, 0xfc, - 0xcc, 0x1b, 0x93, 0x8a, 0xb2, 0xff, 0x8b, 0xff, 0x3f, 0x7a, 0x7f, 0xac, - 0xbf, 0xff, 0x78, 0xf7, 0x99, 0x0f, 0x93, 0xa3, 0xcf, 0xb8, 0xb2, 0xfe, - 0x29, 0xcd, 0x3f, 0xeb, 0x2f, 0xfd, 0x3a, 0xf4, 0x9f, 0xa4, 0x8d, 0x65, - 0xd0, 0xe6, 0x23, 0xdc, 0x27, 0x5d, 0x56, 0x68, 0xb2, 0xff, 0x49, 0x63, - 0x0f, 0xe8, 0x2c, 0xbf, 0xfe, 0xe1, 0x67, 0xdf, 0xbf, 0x1f, 0x47, 0xf7, - 0xeb, 0x2b, 0xe4, 0x66, 0xf5, 0x25, 0xa3, 0x2a, 0xf2, 0xa3, 0x97, 0x94, - 0x0f, 0x7b, 0x78, 0x8c, 0x59, 0x7d, 0xed, 0xb1, 0xa5, 0x97, 0xc3, 0x01, - 0x41, 0x65, 0xb8, 0xe7, 0x8c, 0x21, 0x25, 0xfc, 0x61, 0x1e, 0x3f, 0xeb, - 0x2f, 0xff, 0xdd, 0xe7, 0xd1, 0x87, 0x83, 0x97, 0xd6, 0x17, 0xeb, 0x2f, - 0xff, 0xfd, 0x1b, 0xb0, 0xcf, 0xe3, 0xf2, 0x30, 0xf0, 0x72, 0xfa, 0xc2, - 0xfd, 0x08, 0x5f, 0xfd, 0x9d, 0x8c, 0x1f, 0x1f, 0xbc, 0x7f, 0xaa, 0x10, - 0xb5, 0x41, 0x19, 0x7c, 0x77, 0xbd, 0x3b, 0x37, 0xe2, 0x68, 0xdd, 0x8c, - 0x8e, 0xfc, 0xce, 0x34, 0xff, 0x2c, 0xb7, 0x06, 0x7d, 0x02, 0x1f, 0x5f, - 0xd9, 0x14, 0x0f, 0x5c, 0x59, 0x7f, 0x4f, 0xc2, 0x72, 0x71, 0x65, 0xfc, - 0xfa, 0xd3, 0xff, 0x2b, 0x2a, 0x5b, 0x11, 0x71, 0xc6, 0x09, 0x92, 0x88, - 0x4e, 0x5a, 0xa6, 0xa7, 0x31, 0xde, 0x54, 0x8f, 0xe5, 0x6d, 0xb6, 0x62, - 0x28, 0xe4, 0x7b, 0x7d, 0x29, 0x01, 0x70, 0x85, 0xd7, 0x9a, 0x69, 0xa4, - 0x97, 0x9c, 0x86, 0x91, 0xb9, 0xa0, 0xbe, 0xcd, 0x67, 0x56, 0x57, 0xe7, - 0x9d, 0xc2, 0xdb, 0xef, 0x1b, 0xc1, 0x65, 0xff, 0xe1, 0x08, 0xb3, 0x69, - 0xfa, 0x10, 0x96, 0x2c, 0xbf, 0xb8, 0x7a, 0xd1, 0xb1, 0x65, 0x40, 0xfe, - 0x3a, 0x97, 0x79, 0x9b, 0x0d, 0x65, 0x11, 0xe0, 0x74, 0x8a, 0xf4, 0x61, - 0x63, 0xd6, 0x5e, 0xeb, 0x8d, 0x65, 0x68, 0xdf, 0xf0, 0x8e, 0xff, 0x0b, - 0xf7, 0x75, 0x39, 0xf2, 0xcb, 0xf8, 0x33, 0xf9, 0xe8, 0x54, 0x97, 0xf0, - 0x99, 0xac, 0xc8, 0x96, 0x5f, 0x9f, 0xd1, 0x87, 0x2b, 0x2a, 0x0a, 0x8e, - 0x46, 0x47, 0x90, 0xd9, 0x36, 0x07, 0x21, 0x23, 0x6e, 0x17, 0xf4, 0xba, - 0xfc, 0xfa, 0xd6, 0x7c, 0xb2, 0xff, 0xf7, 0x7c, 0x6f, 0xfe, 0x68, 0x72, - 0x50, 0x59, 0x7f, 0xfb, 0xe8, 0xdf, 0x7f, 0x24, 0x17, 0xd8, 0x9d, 0x65, - 0x4a, 0x2f, 0xf0, 0xa0, 0x92, 0x6f, 0xd3, 0xc1, 0x0a, 0x0b, 0x2f, 0xa3, - 0x04, 0xe0, 0xb2, 0xff, 0xc1, 0xcd, 0x61, 0xf3, 0xd3, 0xbd, 0x65, 0xff, - 0xff, 0xce, 0x7d, 0x3f, 0x04, 0xe3, 0x40, 0xfc, 0xe5, 0xd8, 0x66, 0xb1, - 0x65, 0xd9, 0xe5, 0x97, 0x66, 0xe2, 0xca, 0x88, 0xd7, 0xf8, 0x5a, 0xbc, - 0x8c, 0x17, 0x84, 0xfd, 0xff, 0x14, 0xfd, 0xc9, 0xec, 0x8d, 0x65, 0xee, - 0x09, 0xc8, 0xc7, 0xbf, 0x84, 0xf4, 0x34, 0xee, 0x7e, 0x24, 0xec, 0x6b, - 0xf7, 0xfd, 0xa9, 0x81, 0xf4, 0xca, 0x0b, 0x2f, 0xff, 0xff, 0xfa, 0x79, - 0x3d, 0xdb, 0x1a, 0xd6, 0x79, 0xc4, 0xe6, 0x0a, 0xe5, 0xfc, 0x94, 0xc5, - 0xe9, 0x59, 0x7e, 0xe7, 0x9c, 0x32, 0xb2, 0xfc, 0xfb, 0x34, 0xf0, 0x59, - 0x52, 0x98, 0xe1, 0x1b, 0xb5, 0x09, 0x51, 0x09, 0xef, 0xfb, 0x9e, 0x97, - 0xd6, 0xb3, 0xe5, 0x97, 0xff, 0xfe, 0x17, 0xe8, 0x77, 0x0b, 0xbc, 0x8d, - 0xce, 0x66, 0xb0, 0x9e, 0x0b, 0x2b, 0x11, 0x50, 0x67, 0x37, 0xff, 0x37, - 0x19, 0x48, 0x03, 0x82, 0x9e, 0x96, 0x5e, 0xd3, 0xf1, 0x65, 0xd8, 0xd2, - 0xcb, 0x64, 0x9f, 0xc8, 0xd1, 0xb8, 0x39, 0x7e, 0xf4, 0x93, 0xf9, 0x65, - 0xfd, 0xb7, 0x9f, 0xba, 0x95, 0x97, 0xf8, 0x7e, 0x98, 0x9d, 0xfe, 0x59, - 0x7b, 0xdf, 0xef, 0x59, 0x5a, 0x3d, 0x4d, 0xc3, 0x4b, 0xef, 0x7c, 0xfb, - 0xab, 0x2a, 0x09, 0x93, 0x0c, 0xcf, 0x44, 0xcf, 0x08, 0x22, 0x25, 0xbf, - 0xfb, 0x35, 0xfb, 0x0f, 0x82, 0x72, 0x71, 0x65, 0xe9, 0xfb, 0x75, 0x65, - 0xf7, 0x08, 0xc5, 0x59, 0x61, 0xe8, 0xf0, 0xba, 0x41, 0x7f, 0xf0, 0xad, - 0x4e, 0xbd, 0x26, 0xfd, 0xe2, 0xcb, 0xff, 0xff, 0x76, 0x75, 0xf8, 0x7c, - 0x70, 0x3d, 0x3f, 0xfd, 0x29, 0x67, 0x16, 0x5d, 0xbb, 0xc5, 0x97, 0xfd, - 0xc0, 0xf9, 0xc6, 0xd9, 0x6c, 0x47, 0x6b, 0x28, 0x54, 0x67, 0x3b, 0x6f, - 0x46, 0xaa, 0x09, 0xf9, 0x64, 0x21, 0xcc, 0xa1, 0xe3, 0x13, 0xbd, 0x1d, - 0x17, 0xeb, 0x2f, 0xe8, 0x6a, 0x60, 0xfa, 0x59, 0x76, 0x31, 0x65, 0xff, - 0xd8, 0x5f, 0xc6, 0x13, 0x5f, 0xb0, 0xf8, 0xb2, 0x86, 0x7b, 0xec, 0x16, - 0xbe, 0xff, 0xb9, 0xfa, 0xcb, 0xf4, 0xf3, 0x87, 0xbd, 0x65, 0xf3, 0x08, - 0xa5, 0x65, 0xff, 0x06, 0x77, 0xcf, 0x0f, 0xff, 0x96, 0x5c, 0xe2, 0xc9, - 0xee, 0x61, 0x05, 0x7c, 0x9c, 0x19, 0xc2, 0x33, 0x44, 0x5e, 0x24, 0x28, - 0x42, 0xde, 0x8f, 0xfa, 0x56, 0x5f, 0xf0, 0x7c, 0x53, 0xf1, 0xcf, 0x96, - 0x54, 0xb3, 0x05, 0xe1, 0x19, 0x98, 0xcb, 0x72, 0x3e, 0x13, 0x8d, 0x53, - 0x50, 0xeb, 0x78, 0xf0, 0x0a, 0x52, 0x07, 0x11, 0x81, 0x1d, 0x2c, 0x7a, - 0xa0, 0x84, 0x17, 0xfe, 0x73, 0x18, 0x3b, 0xcd, 0xb1, 0xa5, 0x97, 0x47, - 0x44, 0xb2, 0xfd, 0xcc, 0x16, 0x78, 0xb2, 0xfe, 0x9f, 0x98, 0xe5, 0xfa, - 0xcb, 0xfb, 0x77, 0x86, 0x5f, 0x41, 0x65, 0xff, 0x9f, 0x51, 0xe7, 0xec, - 0x7f, 0xbf, 0x59, 0x7f, 0x7e, 0x21, 0x7b, 0x3e, 0x59, 0x7f, 0xfe, 0x3e, - 0xcb, 0x33, 0xee, 0xcb, 0x04, 0xd3, 0xfe, 0xb2, 0xf8, 0xc9, 0xc6, 0xb2, - 0xff, 0xb9, 0x3a, 0xec, 0xb7, 0x19, 0xac, 0xa8, 0x22, 0xc4, 0x6a, 0xfc, - 0x20, 0xbf, 0xff, 0xb3, 0xd1, 0xb1, 0x87, 0xe9, 0x8d, 0xce, 0x61, 0x7e, - 0xb2, 0xff, 0xe3, 0xd4, 0xc3, 0xd3, 0x11, 0x00, 0x55, 0x95, 0xe4, 0x51, - 0xf1, 0x7a, 0xff, 0xdb, 0x4c, 0x45, 0x3c, 0xc9, 0x82, 0xcb, 0x83, 0x1e, - 0xb2, 0xfe, 0x17, 0xb3, 0xe9, 0xe2, 0xcb, 0xfe, 0xe1, 0x67, 0x79, 0x3a, - 0xd9, 0x65, 0xfd, 0xe3, 0xfb, 0x8f, 0xf2, 0xcb, 0xfd, 0x83, 0xcd, 0x42, - 0x74, 0xb2, 0xf7, 0x8f, 0xab, 0x2a, 0x53, 0x3c, 0x81, 0xf1, 0x8d, 0x44, - 0x5d, 0xf9, 0xcf, 0x0b, 0xc4, 0x32, 0xbf, 0x3f, 0xb3, 0xee, 0xac, 0xbf, - 0xfb, 0x1c, 0xb6, 0x6b, 0x08, 0x7e, 0x95, 0x97, 0xf8, 0x1f, 0x43, 0x82, - 0x4e, 0xea, 0xcb, 0xc1, 0x9d, 0xd5, 0x97, 0xfa, 0x2f, 0x3e, 0xb4, 0x63, - 0x59, 0x58, 0x7a, 0x6e, 0x3f, 0x7f, 0x9f, 0xe1, 0x34, 0x6c, 0xc5, 0x97, - 0xf3, 0x96, 0xcc, 0x7e, 0xac, 0xa2, 0x3e, 0x1e, 0x1a, 0x5f, 0x1f, 0x9d, - 0x8b, 0x2f, 0xf8, 0x8b, 0x36, 0xef, 0x65, 0x8b, 0x2a, 0x09, 0xf9, 0x44, - 0x51, 0xa4, 0x32, 0x84, 0x67, 0x21, 0x03, 0xd2, 0x11, 0x08, 0x6f, 0xda, - 0xce, 0x1b, 0x4b, 0x2f, 0x99, 0x3a, 0xe2, 0xcb, 0xff, 0xd2, 0x3f, 0x00, - 0x51, 0x24, 0x7e, 0x07, 0x16, 0x5d, 0x3b, 0xa3, 0x3e, 0xf1, 0x08, 0xaf, - 0x4c, 0x71, 0x75, 0x97, 0xf0, 0x92, 0x00, 0x81, 0x8b, 0x2e, 0x32, 0x59, - 0x7d, 0x09, 0x20, 0xac, 0xa1, 0x9b, 0x6c, 0x15, 0xb4, 0x4b, 0x2f, 0x9b, - 0x0f, 0x3f, 0x2c, 0xb4, 0x91, 0xb8, 0xe8, 0x95, 0x46, 0x3f, 0xd3, 0x58, - 0xbe, 0x9f, 0xf0, 0x96, 0x54, 0x19, 0x16, 0x63, 0x28, 0xc2, 0xe0, 0x98, - 0xfc, 0x84, 0x70, 0xe1, 0x8a, 0x1a, 0x7a, 0x8d, 0x7d, 0x92, 0x96, 0x1d, - 0xc3, 0xf8, 0x4b, 0xf0, 0xc4, 0x04, 0x2d, 0x43, 0x8a, 0x3c, 0x8e, 0xf9, - 0x8d, 0xa4, 0x71, 0x8e, 0x56, 0x5d, 0xde, 0x2c, 0xa3, 0x3c, 0xa8, 0x8c, - 0xef, 0x84, 0x96, 0xb8, 0xb2, 0xef, 0xfa, 0xb2, 0xef, 0xfc, 0xb2, 0xfb, - 0x5a, 0xce, 0x2c, 0xbb, 0x22, 0x59, 0x68, 0x46, 0x44, 0x6e, 0xc4, 0x8c, - 0x18, 0x71, 0x81, 0x08, 0xaf, 0xc7, 0xd7, 0x2c, 0x59, 0x7e, 0xd0, 0xf3, - 0x09, 0x65, 0xf0, 0xba, 0x76, 0x96, 0x5f, 0xf8, 0xf3, 0xd3, 0xbf, 0x9e, - 0x9f, 0x96, 0x56, 0x1f, 0x26, 0xe9, 0x25, 0xfd, 0xf6, 0x81, 0xf7, 0x60, - 0xb2, 0x9d, 0x1b, 0xfc, 0x84, 0x50, 0x09, 0x2f, 0xcd, 0x77, 0xb9, 0xb2, - 0xca, 0x59, 0x7f, 0x8b, 0x59, 0xcc, 0x3f, 0x2c, 0xa6, 0xc1, 0xbc, 0xc0, - 0xbb, 0xff, 0xe8, 0x74, 0xb1, 0xfa, 0x59, 0xde, 0xe3, 0x4b, 0x2f, 0x41, - 0xf7, 0x16, 0x5f, 0xff, 0x60, 0xcf, 0xb8, 0xcf, 0x4c, 0x85, 0x8e, 0xb2, - 0xec, 0xde, 0xb2, 0xff, 0x83, 0xec, 0x1b, 0x0f, 0x58, 0xb2, 0xa5, 0x13, - 0xbb, 0x26, 0xe0, 0xc5, 0xfd, 0x33, 0xaf, 0x4e, 0xf5, 0x97, 0xef, 0xcc, - 0xa2, 0xdd, 0x59, 0x7f, 0x4e, 0xfc, 0xf7, 0xa5, 0x65, 0xf7, 0x79, 0x91, - 0x2c, 0xf1, 0xae, 0xbf, 0xbc, 0x33, 0xdf, 0x23, 0x59, 0x58, 0x7c, 0x4e, - 0x67, 0x7d, 0xc3, 0xcf, 0xd6, 0x5f, 0xe3, 0x06, 0xbd, 0x9b, 0xf8, 0xb2, - 0xff, 0xff, 0x67, 0x4f, 0xe8, 0x14, 0xc6, 0xe9, 0xe6, 0xe0, 0xfd, 0x8b, - 0x2f, 0xb3, 0xa7, 0xc5, 0x97, 0xf4, 0x75, 0x11, 0x47, 0x3b, 0x01, 0x65, - 0x6e, 0xa3, 0xd2, 0x23, 0x5e, 0x32, 0x74, 0x86, 0xff, 0xc0, 0x76, 0x70, - 0xc9, 0xf4, 0x2a, 0xca, 0x82, 0xec, 0xd0, 0xe3, 0x04, 0xc3, 0x40, 0xb3, - 0x7c, 0x49, 0x12, 0x76, 0xa1, 0x79, 0xe2, 0xff, 0xcb, 0x8a, 0x17, 0x9c, - 0x20, 0xec, 0x62, 0xcd, 0x20, 0xdf, 0xd8, 0xd1, 0x6f, 0xd4, 0x16, 0x5f, - 0xff, 0xba, 0x7c, 0xdb, 0x1a, 0xe1, 0xec, 0x4f, 0xf7, 0x56, 0x5f, 0xf4, - 0x33, 0xb8, 0x33, 0x2f, 0xd6, 0x5e, 0x6b, 0x3c, 0xb2, 0xfe, 0xcd, 0x68, - 0x02, 0xe9, 0x65, 0xfc, 0x50, 0x69, 0xfd, 0xc5, 0x97, 0xe1, 0x7c, 0xe4, - 0x6b, 0x2d, 0xcc, 0x3d, 0x53, 0x2e, 0xbe, 0x19, 0x38, 0xab, 0x2f, 0xfe, - 0x38, 0x3e, 0xb3, 0x7f, 0xc1, 0x3f, 0x96, 0x56, 0xc7, 0xd3, 0xd2, 0x2b, - 0xe3, 0xe6, 0x0f, 0x65, 0x42, 0x78, 0x61, 0xf2, 0xc3, 0x9c, 0x10, 0xef, - 0x61, 0x08, 0x24, 0x23, 0xef, 0xfc, 0xfb, 0xbf, 0x83, 0x86, 0x5f, 0x41, - 0x65, 0xbf, 0x59, 0x7f, 0x87, 0x84, 0x6d, 0x78, 0x0b, 0x2f, 0xff, 0xbc, - 0x73, 0xd3, 0xfe, 0x60, 0xdd, 0xa6, 0x9a, 0x49, 0x7f, 0xf1, 0xcf, 0x7f, - 0x98, 0x37, 0x69, 0xa6, 0x92, 0x56, 0x22, 0x7f, 0xca, 0xb5, 0xb2, 0x3d, - 0x3b, 0x0d, 0x2b, 0xfd, 0xe0, 0x3b, 0x06, 0xec, 0x59, 0x44, 0x7b, 0x9d, - 0x29, 0xbe, 0x1e, 0x60, 0xa9, 0x2f, 0x9f, 0xf7, 0xea, 0xcb, 0xfe, 0xd3, - 0x86, 0x37, 0xa4, 0x42, 0x59, 0x78, 0x13, 0xa5, 0x97, 0x77, 0x83, 0x3d, - 0x78, 0x8e, 0xef, 0x85, 0x07, 0xfc, 0x59, 0x71, 0x01, 0x51, 0x0c, 0xaf, - 0xe9, 0x64, 0xf3, 0x58, 0xb2, 0xa0, 0x79, 0xf1, 0x11, 0xde, 0xf4, 0xec, - 0xb2, 0xfd, 0xd9, 0x80, 0xac, 0x59, 0x79, 0xa6, 0x9a, 0x49, 0x7b, 0x08, - 0x29, 0x1b, 0x9a, 0x0b, 0xff, 0x66, 0xd8, 0x37, 0x81, 0x4e, 0xcb, 0x2a, - 0x51, 0x69, 0xfa, 0x49, 0x16, 0xdf, 0xbb, 0xe7, 0x21, 0x56, 0x5f, 0xdf, - 0x43, 0x84, 0xe1, 0x59, 0x78, 0x9b, 0x54, 0x76, 0xb2, 0xb0, 0xf5, 0x5c, - 0xba, 0xe6, 0xd4, 0xda, 0x96, 0x5f, 0xb3, 0xbd, 0x06, 0xcb, 0x2f, 0xfc, - 0x7b, 0x78, 0xde, 0x1d, 0x3d, 0x96, 0x5a, 0x0d, 0xa9, 0x11, 0x64, 0x47, - 0xd2, 0x9b, 0xcf, 0xbb, 0x2b, 0x2f, 0xd8, 0x3f, 0x3e, 0xea, 0xcb, 0xf1, - 0x60, 0xe5, 0x8b, 0x2a, 0x39, 0x5d, 0xb5, 0x98, 0xd8, 0xb6, 0x21, 0x19, - 0x16, 0x3c, 0x8a, 0x5f, 0xf3, 0xa4, 0x44, 0x7a, 0x86, 0x8b, 0x0b, 0xfc, - 0xfa, 0xf0, 0xab, 0x01, 0xde, 0xf1, 0xe6, 0x8a, 0xaf, 0xe6, 0x61, 0xea, - 0x28, 0x2c, 0xbf, 0xfe, 0x14, 0x47, 0x2f, 0x3c, 0x39, 0x8c, 0x9d, 0xd5, - 0x95, 0x28, 0x81, 0x32, 0xeb, 0xdd, 0x39, 0x59, 0x76, 0x12, 0xca, 0x33, - 0x60, 0x01, 0xbb, 0xf1, 0x4f, 0xd3, 0xc5, 0x97, 0xfe, 0xc3, 0x27, 0xef, - 0x1a, 0x92, 0x59, 0x7b, 0xcf, 0xa9, 0x3e, 0x3e, 0x13, 0xdf, 0x1e, 0xa7, - 0xab, 0x2f, 0xb9, 0x93, 0x05, 0x94, 0x33, 0xc2, 0x22, 0x1b, 0xf4, 0xc3, - 0xd9, 0xf2, 0xcb, 0xe8, 0x1c, 0x85, 0x65, 0xdf, 0x7e, 0xb2, 0xfe, 0x64, - 0xf3, 0xf3, 0x0a, 0xcb, 0xdc, 0x91, 0x7f, 0x3c, 0x7e, 0x0c, 0xdf, 0xe0, - 0x66, 0xbf, 0xfc, 0xc9, 0x65, 0xb8, 0xb2, 0xe8, 0xe7, 0x65, 0x95, 0x03, - 0x5c, 0xe2, 0x37, 0xfe, 0xc6, 0x18, 0xf5, 0x83, 0x76, 0x2c, 0xbe, 0xc6, - 0x08, 0x35, 0x95, 0xe3, 0xe0, 0x23, 0xdb, 0xff, 0x78, 0x4e, 0x66, 0x82, - 0xee, 0x15, 0x44, 0x18, 0xbf, 0xfb, 0x69, 0x17, 0x35, 0xde, 0xce, 0x71, - 0x65, 0x8c, 0x68, 0x91, 0x24, 0xcb, 0xff, 0x78, 0x0e, 0x2e, 0x77, 0xb9, - 0xe5, 0x97, 0xfb, 0xd2, 0x5d, 0xfd, 0xfa, 0xb2, 0xff, 0xf4, 0x35, 0x30, - 0x0f, 0x8f, 0xc4, 0xec, 0x59, 0x5c, 0x3f, 0xc0, 0x19, 0xdf, 0xfb, 0x9f, - 0xbf, 0xdc, 0x13, 0x5e, 0x75, 0x97, 0xc0, 0xf6, 0x05, 0x65, 0x41, 0x36, - 0x81, 0x93, 0xea, 0x17, 0x7d, 0x22, 0x8f, 0x42, 0xbf, 0xfb, 0xbc, 0xda, - 0x44, 0x29, 0xf1, 0xec, 0xb2, 0xff, 0x72, 0x75, 0x00, 0x9e, 0xcb, 0x28, - 0x8f, 0xeb, 0x88, 0xd7, 0xf0, 0x05, 0x2c, 0xd8, 0x0b, 0x2a, 0x4f, 0x43, - 0xc4, 0x37, 0xbc, 0x0d, 0xd5, 0x97, 0xff, 0xb4, 0xfb, 0xf0, 0x83, 0xe3, - 0xfc, 0x4e, 0xac, 0xa9, 0x4e, 0xae, 0x11, 0x8e, 0xb6, 0xc8, 0x40, 0x41, - 0x7d, 0x98, 0x42, 0xac, 0xaf, 0x97, 0x51, 0x3c, 0x6a, 0x4c, 0x5c, 0x84, - 0x07, 0x65, 0xe2, 0xee, 0x23, 0xde, 0x29, 0x69, 0x65, 0xf1, 0x84, 0xc6, - 0xb2, 0xb0, 0xde, 0xf4, 0x72, 0xfa, 0x7a, 0x71, 0x2c, 0xbf, 0x80, 0xe2, - 0x01, 0xc4, 0x59, 0x51, 0x1e, 0x8e, 0x88, 0xae, 0xdf, 0x8b, 0x2b, 0x0d, - 0xdb, 0x91, 0xdd, 0xff, 0xeb, 0x2f, 0xf8, 0x3e, 0x3d, 0x11, 0xf7, 0x8b, - 0x29, 0x87, 0xa3, 0xf8, 0xcd, 0xfe, 0xfc, 0xfd, 0xec, 0xfd, 0xd6, 0x5f, - 0xee, 0x1e, 0x16, 0x7c, 0xd2, 0xcb, 0xff, 0xfd, 0x9d, 0xeb, 0xe8, 0xa7, - 0xe8, 0x09, 0xaf, 0xd8, 0x7c, 0x59, 0x5b, 0xa8, 0x96, 0x61, 0x9d, 0xf8, - 0x1f, 0x07, 0x18, 0xb2, 0xff, 0x98, 0x6d, 0x61, 0x0f, 0xd2, 0xb2, 0xef, - 0x01, 0x65, 0xff, 0xfa, 0x06, 0x43, 0xf6, 0x7c, 0x23, 0x97, 0x48, 0x6b, - 0x2f, 0xf6, 0x0f, 0xd9, 0xf6, 0x71, 0x65, 0xe3, 0x6b, 0x75, 0x65, 0xfe, - 0xcd, 0xfc, 0xef, 0x65, 0x8b, 0x2b, 0x47, 0xab, 0xe2, 0x0a, 0x94, 0xe7, - 0x46, 0x4d, 0x85, 0x26, 0x71, 0x10, 0xb9, 0x2a, 0xf2, 0x11, 0x17, 0x34, - 0x6b, 0x2f, 0xcc, 0x0e, 0x10, 0x56, 0x54, 0x9b, 0xe3, 0x17, 0xbf, 0xf9, - 0xf6, 0xe3, 0x8e, 0x76, 0x98, 0x4a, 0xcb, 0xfb, 0x39, 0x1f, 0x9a, 0x95, - 0x95, 0xa4, 0x49, 0x70, 0x7e, 0x3d, 0x12, 0xf8, 0xe4, 0xa2, 0x59, 0x50, - 0x66, 0x61, 0x8e, 0x10, 0x78, 0xe8, 0x12, 0x1f, 0x8a, 0x0e, 0x76, 0xb2, - 0x28, 0x53, 0x32, 0x15, 0x8e, 0xe1, 0xf9, 0x1f, 0x25, 0x33, 0xf6, 0x33, - 0x76, 0x8c, 0xef, 0xf7, 0x39, 0x85, 0xfc, 0x7e, 0x2c, 0xbe, 0x23, 0xdc, - 0x95, 0x97, 0xc2, 0x16, 0x71, 0x65, 0xa2, 0x59, 0x7f, 0x73, 0x0b, 0xf8, - 0xfc, 0x59, 0x51, 0x91, 0x7b, 0x86, 0xce, 0x47, 0xf9, 0x17, 0x04, 0xaf, - 0xba, 0xee, 0x4b, 0x2b, 0x79, 0xf5, 0xb4, 0x97, 0x7f, 0xfb, 0xe8, 0x1e, - 0x79, 0xfd, 0xe9, 0xfb, 0x8b, 0x2f, 0xf7, 0x7f, 0x03, 0x27, 0x3a, 0xb2, - 0x9c, 0xff, 0xc0, 0x97, 0x7f, 0xf8, 0xb6, 0x8d, 0x11, 0x96, 0x0f, 0x08, - 0x55, 0x97, 0xf1, 0xfd, 0xb9, 0xe9, 0xea, 0xcb, 0xf0, 0x38, 0x67, 0xf2, - 0xcb, 0xc2, 0x64, 0xac, 0xbd, 0xd9, 0x11, 0x65, 0xfe, 0xc1, 0xe9, 0xc3, - 0xdf, 0xd6, 0x5f, 0xb0, 0x87, 0xe9, 0x59, 0x58, 0x7e, 0xfe, 0x1d, 0x68, - 0xd2, 0xa5, 0x36, 0x7d, 0xd4, 0xc8, 0x8c, 0x5c, 0xa3, 0x90, 0x93, 0xbe, - 0x1e, 0x9e, 0x0b, 0x2f, 0xff, 0xf6, 0x13, 0xf7, 0x99, 0xaf, 0xff, 0x9c, - 0xfb, 0xbc, 0x35, 0x97, 0x64, 0x7a, 0xca, 0x19, 0xfd, 0x75, 0x86, 0xa5, - 0x17, 0xcf, 0x09, 0xcb, 0xd3, 0xa8, 0x96, 0x54, 0x67, 0x5c, 0x8c, 0xd9, - 0x40, 0x9a, 0x46, 0x76, 0xd1, 0x9f, 0x8e, 0x15, 0xf9, 0x3b, 0xda, 0x19, - 0x5c, 0x1f, 0x42, 0x18, 0xe7, 0x7e, 0x3d, 0x1b, 0x4b, 0xd3, 0x45, 0x3f, - 0x94, 0x06, 0x50, 0xa4, 0x04, 0x71, 0x0d, 0x46, 0x21, 0x1e, 0x4d, 0x7b, - 0xed, 0xb7, 0xac, 0xbf, 0x07, 0xd3, 0xf6, 0xe2, 0xcb, 0x80, 0xdb, 0x59, - 0x7e, 0xf3, 0xe9, 0x86, 0xb2, 0xf0, 0x93, 0xba, 0xb2, 0xfe, 0x10, 0x3e, - 0x9f, 0xb7, 0x16, 0x54, 0x64, 0x60, 0xc0, 0xb0, 0x23, 0x7c, 0x27, 0x01, - 0x05, 0xdf, 0xb4, 0xb2, 0xfe, 0xf4, 0x9e, 0xeb, 0x0d, 0x65, 0x46, 0x3c, - 0x80, 0x8c, 0xd7, 0xc8, 0xb8, 0xec, 0x25, 0x2f, 0xec, 0x6b, 0x30, 0x85, - 0x59, 0x79, 0xf3, 0x8b, 0x2f, 0xcf, 0xb1, 0x82, 0x0b, 0x2f, 0x7f, 0xfc, - 0xac, 0xa6, 0x1f, 0x0f, 0x86, 0xc8, 0xa2, 0x8d, 0x17, 0x8f, 0x08, 0xeb, - 0xff, 0xe0, 0xcf, 0x39, 0x2c, 0xce, 0x9f, 0x3c, 0xeb, 0x2f, 0xfe, 0x64, - 0xf7, 0x8f, 0x9a, 0xfd, 0x98, 0xb2, 0xfe, 0x29, 0x83, 0x24, 0xd6, 0x57, - 0xe7, 0xde, 0x48, 0x97, 0xec, 0xde, 0x59, 0xc5, 0x97, 0xfb, 0xa7, 0xbc, - 0xfb, 0x09, 0x49, 0x66, 0x2c, 0xbf, 0xb0, 0x85, 0xd4, 0xb7, 0x33, 0xc5, - 0x10, 0xd2, 0xb1, 0x39, 0x23, 0x85, 0xe9, 0x11, 0x71, 0xb6, 0xff, 0xf3, - 0xef, 0x9c, 0x1b, 0xc3, 0x3c, 0x6d, 0x2c, 0xbc, 0x0f, 0xa0, 0xb2, 0xfa, - 0x1a, 0x7d, 0xeb, 0x2a, 0x08, 0x8c, 0xd2, 0x51, 0x0f, 0x5f, 0xe2, 0x81, - 0x63, 0x24, 0x2b, 0x2f, 0xc7, 0xe2, 0x98, 0x2c, 0xbc, 0xfc, 0x35, 0x96, - 0xc5, 0x97, 0xb3, 0x05, 0x59, 0x66, 0x0c, 0xd6, 0x6e, 0x08, 0x54, 0xa3, - 0xdf, 0x0b, 0xf4, 0x63, 0xe2, 0x67, 0x47, 0xbf, 0xe3, 0xdf, 0x9a, 0x0b, - 0xb8, 0x55, 0x18, 0x6a, 0xf4, 0x78, 0x3a, 0xb2, 0xe3, 0xde, 0xb2, 0xf9, - 0xf4, 0xe2, 0x2c, 0xbb, 0x05, 0x59, 0x7f, 0xf4, 0xb0, 0xc7, 0x84, 0x2f, - 0xa5, 0x8b, 0x2b, 0xc7, 0xb5, 0xd1, 0x7a, 0x1a, 0x63, 0x81, 0x46, 0x61, - 0x07, 0x06, 0x3a, 0xf9, 0x78, 0x51, 0x3c, 0xb2, 0xff, 0xf8, 0xfb, 0xe9, - 0xe4, 0xfa, 0x4f, 0x69, 0x15, 0x65, 0xfe, 0xf4, 0xc5, 0x03, 0xd4, 0x16, - 0x5f, 0x8a, 0x22, 0x96, 0x2c, 0xbf, 0xdd, 0x36, 0xa2, 0x27, 0x69, 0x65, - 0xff, 0x66, 0xf9, 0x1f, 0xb0, 0xfa, 0xb2, 0xf7, 0x1f, 0xf5, 0x96, 0xe9, - 0x1e, 0xb7, 0x4e, 0x6f, 0xe0, 0x09, 0xff, 0xf3, 0xf2, 0xcb, 0xf4, 0xb3, - 0xb8, 0x15, 0x97, 0xe9, 0x87, 0xe5, 0x8b, 0x2e, 0x3d, 0x96, 0x54, 0xa7, - 0xe6, 0x34, 0xfc, 0x34, 0xf8, 0x9c, 0xe1, 0x17, 0x11, 0x3f, 0x8c, 0x88, - 0x9f, 0xa4, 0xf7, 0xe7, 0xee, 0xa7, 0x7a, 0xcb, 0xff, 0xf8, 0x82, 0x0f, - 0x0a, 0x65, 0x3e, 0x96, 0xa4, 0xb1, 0x65, 0xf1, 0x8f, 0x70, 0xd6, 0x54, - 0xa2, 0x99, 0xca, 0x5a, 0x59, 0xbf, 0xef, 0x4c, 0x0c, 0xb1, 0x92, 0xb2, - 0xff, 0xb3, 0xbe, 0xc6, 0x13, 0xf5, 0x65, 0xfd, 0xbc, 0xf5, 0xf4, 0xee, - 0xac, 0xb1, 0x0c, 0xfa, 0xb0, 0xde, 0xe2, 0xfd, 0x65, 0xf9, 0x92, 0x1d, - 0x86, 0xb2, 0xfa, 0x12, 0xc7, 0x59, 0x7e, 0xd7, 0xec, 0x3e, 0x2c, 0xbb, - 0xbc, 0x6c, 0x22, 0x2b, 0x05, 0xdc, 0xa4, 0x42, 0x1a, 0x94, 0x7e, 0x02, - 0x16, 0x16, 0xe2, 0xcb, 0xfa, 0x76, 0xc2, 0x31, 0x56, 0x56, 0x8d, 0xf7, - 0x04, 0x6f, 0xfe, 0x92, 0x7f, 0x4f, 0xed, 0xf5, 0xce, 0x24, 0xbd, 0x81, - 0x95, 0x95, 0xb2, 0xa8, 0x31, 0xc7, 0x39, 0x8c, 0xac, 0x21, 0xde, 0x8d, - 0x79, 0x92, 0x15, 0x97, 0xe7, 0x88, 0xf3, 0x8b, 0x2f, 0xb9, 0x3a, 0x82, - 0xcb, 0x0e, 0x31, 0xe4, 0xf0, 0x9e, 0xa5, 0x11, 0xee, 0xcb, 0x7f, 0xc4, - 0x3f, 0x4f, 0x7b, 0x2c, 0x59, 0x70, 0x36, 0x59, 0x7f, 0xbe, 0x87, 0xdc, - 0x3e, 0xca, 0xcb, 0xc7, 0xf7, 0x16, 0x56, 0x1e, 0x91, 0x9a, 0xd6, 0x23, - 0x08, 0xce, 0x3c, 0xd5, 0x76, 0x05, 0x65, 0xd9, 0xc5, 0x9d, 0x2d, 0xaf, - 0x77, 0x3c, 0xb2, 0xb4, 0x78, 0x20, 0x25, 0xbf, 0xfe, 0x1c, 0x67, 0x3c, - 0xe4, 0xed, 0x84, 0x62, 0xac, 0xbf, 0xda, 0x64, 0xfd, 0xd7, 0x0a, 0xcb, - 0xff, 0xf8, 0xb3, 0xbe, 0xc8, 0x60, 0x64, 0x3d, 0x27, 0x62, 0xca, 0xc4, - 0x6e, 0x1a, 0x87, 0x0d, 0x2f, 0xfd, 0xa3, 0x81, 0xf3, 0xe8, 0x19, 0x2c, - 0xbe, 0x36, 0x4c, 0x4b, 0x2f, 0xfe, 0xe1, 0xfc, 0x59, 0x16, 0xa7, 0xbc, - 0x59, 0x7d, 0x9b, 0x02, 0x0b, 0x28, 0xd1, 0x91, 0x11, 0xf7, 0xe4, 0x4d, - 0x22, 0xdf, 0xee, 0x4c, 0x1f, 0x67, 0x62, 0xcb, 0xe7, 0xd3, 0xfc, 0xb2, - 0xfe, 0xff, 0x67, 0x84, 0xee, 0xac, 0xbc, 0x31, 0xca, 0xca, 0x14, 0xfc, - 0x3e, 0x22, 0x01, 0x95, 0x4a, 0x62, 0x66, 0x7e, 0xf0, 0xa3, 0xbb, 0x1a, - 0x59, 0x49, 0xc4, 0x30, 0xbf, 0xb8, 0x24, 0xea, 0x58, 0x9c, 0x43, 0x0a, - 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, 0x93, 0x88, 0x61, 0x49, 0xc4, 0x30, - 0xa8, 0x22, 0xe4, 0xc6, 0x7f, 0x3c, 0x00, 0xcc, 0x78, 0xce, 0xe0, 0xcd, - 0xde, 0xc4, 0xe2, 0x18, 0x5f, 0xce, 0xfd, 0x8a, 0x58, 0x9c, 0x43, 0x08, - 0xc6, 0x92, 0xcd, 0xb4, 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x29, 0x38, 0x86, - 0x15, 0x03, 0x64, 0x63, 0x34, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x27, - 0x10, 0xc2, 0x93, 0x88, 0x61, 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x56, - 0xc8, 0x96, 0x18, 0xc9, 0x8c, 0xfe, 0x33, 0xc1, 0x9d, 0xe3, 0x34, 0x9c, - 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x40, 0xd9, 0x70, 0x66, 0x93, 0x88, 0x61, - 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x2a, 0x07, 0xc9, - 0xf8, 0xcf, 0x46, 0x77, 0x06, 0x69, 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, - 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, 0xb6, 0x3e, 0x41, 0x46, 0x74, 0x33, - 0xe1, 0x9b, 0x0a, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, - 0x93, 0x88, 0x61, 0x49, 0xc4, 0x30, 0xa1, 0x9f, 0x27, 0xc3, 0x3f, 0x8c, - 0x80, 0x66, 0x93, 0x88, 0x61, 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x5f, - 0xbd, 0x3f, 0xf3, 0x13, 0x88, 0x61, 0x49, 0xc4, 0x30, 0xa8, 0x22, 0x68, - 0x23, 0x3e, 0x19, 0x71, 0x9f, 0xcd, 0xac, 0x14, 0xe2, 0x18, 0x52, 0x71, - 0x0c, 0x29, 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x40, - 0xf9, 0x3e, 0x19, 0x31, 0x9d, 0xe3, 0x34, 0x9c, 0x43, 0x0a, 0x4e, 0x21, - 0x85, 0x27, 0x10, 0xc2, 0x93, 0x88, 0x61, 0x50, 0x3e, 0x41, 0x8c, 0xf8, - 0x64, 0x86, 0x6d, 0xe4, 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x29, 0x38, 0x86, - 0x16, 0x82, 0x71, 0x0c, 0x29, 0x38, 0x86, 0x1f, 0x17, 0xf4, 0x9c, 0x43, - 0x0a, 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, 0x93, 0x88, 0x61, 0x5b, 0x23, - 0x7a, 0x03, 0x22, 0x9c, 0x04, 0xa6, 0x21, 0x9f, 0xc6, 0x7a, 0x33, 0x6c, - 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, 0x93, 0x88, 0x61, 0x68, 0x27, 0x10, - 0xc2, 0x93, 0x88, 0x61, 0xf1, 0x7f, 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, - 0x54, 0xa2, 0xb2, 0x03, 0x26, 0x71, 0xa2, 0x96, 0x0c, 0xd2, 0x71, 0x0c, - 0x29, 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x27, 0x10, - 0xc2, 0xa4, 0xff, 0x7e, 0x19, 0xd0, 0xcb, 0x06, 0x48, 0x66, 0x93, 0x88, - 0x61, 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x56, 0x8f, 0x2f, 0x83, 0x3d, - 0x19, 0xa4, 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x29, 0x38, 0x86, 0x14, 0xc3, - 0xcb, 0x21, 0x9e, 0x8c, 0xd9, 0x89, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x52, - 0x71, 0x0c, 0x2b, 0xf3, 0x64, 0x01, 0x9a, 0x4e, 0x21, 0x85, 0x27, 0x10, - 0xc2, 0x93, 0x88, 0x61, 0x49, 0xc4, 0x30, 0xa9, 0x3e, 0x48, 0x86, 0x7c, - 0x32, 0x01, 0x9a, 0x96, 0x60, 0xa6, 0xd0, 0x81, 0x82, 0x68, 0xd5, 0x72, - 0x16, 0xc1, 0x84, 0x67, 0xd0, 0x85, 0x38, 0x52, 0x6e, 0x9f, 0x45, 0x08, - 0x4d, 0x43, 0x61, 0x89, 0x5e, 0x84, 0x1b, 0xc2, 0x8b, 0xf3, 0xb2, 0x87, - 0xef, 0x1a, 0x3b, 0x0c, 0xa0, 0x43, 0x17, 0x7c, 0x24, 0xda, 0x7a, 0x8f, - 0x2e, 0x11, 0x47, 0x72, 0x10, 0xd7, 0xe8, 0x14, 0xe7, 0x13, 0x88, 0x60, - 0xdd, 0x39, 0x7b, 0xc7, 0x2c, 0x4e, 0x21, 0x85, 0xf1, 0x85, 0xf4, 0xbc, - 0x43, 0x17, 0x8c, 0x78, 0xbc, 0x43, 0x16, 0x6f, 0xf2, 0x32, 0x34, 0x93, - 0xe2, 0x9e, 0x97, 0x5a, 0x77, 0xb3, 0xe2, 0x44, 0xa7, 0xa1, 0xde, 0x32, - 0xd9, 0x65, 0xfc, 0x6d, 0x0f, 0x53, 0xb2, 0xca, 0x81, 0xe5, 0xf8, 0x72, - 0xe7, 0xf9, 0x65, 0xf4, 0xc7, 0xcf, 0x56, 0x5e, 0x3d, 0xed, 0xb5, 0x95, - 0xf2, 0x60, 0x51, 0x42, 0x0f, 0xc4, 0x44, 0x2f, 0xd2, 0x4b, 0xf6, 0x64, - 0x4e, 0x22, 0xcb, 0xfc, 0xe4, 0x24, 0x50, 0x32, 0x59, 0x77, 0xf2, 0xb2, - 0xfe, 0xd3, 0x90, 0x93, 0xfa, 0xcb, 0x9f, 0x4b, 0x2c, 0x6b, 0x02, 0x5b, - 0x58, 0x2b, 0x2c, 0xc5, 0x96, 0x6e, 0x68, 0x9c, 0xd0, 0xbf, 0x91, 0x1c, - 0x7c, 0x41, 0x1b, 0xfb, 0xe8, 0x7a, 0x4b, 0x65, 0x94, 0xc4, 0xd3, 0xe3, - 0xe1, 0x9f, 0xb8, 0xa3, 0x7f, 0xde, 0x96, 0x64, 0x52, 0x5b, 0x2c, 0xbf, - 0x63, 0x3d, 0x3f, 0xac, 0xbf, 0xfa, 0x33, 0x84, 0x7e, 0x9f, 0xf0, 0xbf, - 0x59, 0x77, 0xf2, 0xb2, 0xb0, 0xf7, 0xbf, 0x47, 0xac, 0x4c, 0x77, 0xf3, - 0xce, 0x9c, 0x82, 0x10, 0xd7, 0xff, 0x11, 0x4e, 0xc5, 0x87, 0xbe, 0x63, - 0xd6, 0x5e, 0x9f, 0xb8, 0xb2, 0x9c, 0xf8, 0xf8, 0x8d, 0x79, 0xf5, 0x2b, - 0x2f, 0xc7, 0x1e, 0xe5, 0xfa, 0xcb, 0xf1, 0xfd, 0xc7, 0xf9, 0x65, 0x40, - 0xfb, 0x98, 0x37, 0xc2, 0xab, 0xed, 0xec, 0x93, 0x59, 0x7f, 0xc2, 0x38, - 0xf9, 0x8c, 0x9d, 0xd5, 0x97, 0xb3, 0xe6, 0x96, 0x5d, 0xfe, 0x68, 0xf6, - 0x88, 0xf2, 0xff, 0xf1, 0xee, 0x80, 0x53, 0xfb, 0x99, 0x14, 0xf5, 0x65, - 0x7c, 0x7f, 0x8c, 0x2d, 0xb8, 0x12, 0xb2, 0xf6, 0x6a, 0x0b, 0x28, 0x66, - 0xcb, 0x78, 0xb5, 0xe1, 0xb9, 0x2c, 0xa3, 0x37, 0xce, 0x47, 0x7e, 0x90, - 0xb9, 0x0a, 0xb2, 0xff, 0xe7, 0xd6, 0x9f, 0xf9, 0x6e, 0xd3, 0x4d, 0x2c, - 0xbf, 0xf0, 0xb9, 0xf7, 0x4f, 0xba, 0x03, 0x16, 0x5a, 0x63, 0x22, 0x2f, - 0xa9, 0x55, 0x2a, 0xad, 0xb0, 0xbd, 0xe1, 0xf3, 0xc8, 0x49, 0xf4, 0x7c, - 0x10, 0xb2, 0xbf, 0xf8, 0x02, 0x45, 0x1a, 0x44, 0x70, 0xcf, 0x96, 0x53, - 0x61, 0xd5, 0x91, 0x4c, 0x73, 0x5b, 0x43, 0x96, 0x11, 0xdc, 0x0e, 0x3c, - 0x3c, 0x8d, 0x8c, 0x54, 0xb0, 0xca, 0x12, 0xfa, 0x32, 0x83, 0x95, 0x65, - 0xbb, 0x0e, 0x38, 0xa1, 0xc7, 0xa8, 0x42, 0xb2, 0x1f, 0xde, 0x94, 0x12, - 0xf5, 0x85, 0xf7, 0xea, 0x25, 0x2a, 0x53, 0x90, 0xa6, 0xec, 0xb4, 0x51, - 0x19, 0x6f, 0x33, 0x81, 0x59, 0x78, 0x83, 0x2b, 0x2a, 0x31, 0xb8, 0x31, - 0xdb, 0xe8, 0x66, 0xa5, 0x65, 0xfc, 0xd3, 0xeb, 0x4f, 0xf2, 0xcb, 0xff, - 0xf7, 0x35, 0xa3, 0x8b, 0x9a, 0x9e, 0x98, 0x4c, 0x6b, 0x29, 0x88, 0xc8, - 0xfc, 0x88, 0x88, 0x5a, 0x2f, 0xbd, 0xd9, 0xe2, 0xcb, 0xdf, 0xc7, 0x92, - 0xca, 0x61, 0xbc, 0x21, 0xcb, 0xdb, 0x18, 0xd6, 0x58, 0x0b, 0x2d, 0xf9, - 0x9a, 0xe2, 0x1d, 0xb3, 0x61, 0x65, 0xf6, 0x8c, 0x85, 0x59, 0x7f, 0xbb, - 0x20, 0x0f, 0x8f, 0x4b, 0x2f, 0xec, 0x0f, 0xa7, 0xe8, 0x2c, 0xbf, 0x99, - 0xc3, 0x17, 0xad, 0x2c, 0xbf, 0x4f, 0xde, 0x98, 0x2c, 0xa8, 0xed, 0x1e, - 0x1a, 0x15, 0x22, 0x2e, 0x99, 0x88, 0x5d, 0xb8, 0x61, 0x7f, 0xd9, 0xf1, - 0x67, 0x7b, 0x9b, 0x2c, 0xbb, 0xee, 0xac, 0xbf, 0x61, 0x6c, 0x7a, 0x59, - 0x7e, 0xeb, 0x90, 0x31, 0x65, 0xfe, 0xe6, 0x30, 0xfb, 0xe7, 0x59, 0x69, - 0xc4, 0x4b, 0xe8, 0x60, 0x89, 0xfa, 0x4d, 0x7a, 0x33, 0x0d, 0x65, 0xc4, - 0xc5, 0x95, 0x03, 0x69, 0xc1, 0xeb, 0xdb, 0x9a, 0x95, 0x97, 0xfd, 0xe3, - 0xd7, 0x9d, 0xb0, 0xd3, 0x4b, 0x2f, 0xff, 0x13, 0xb5, 0xc2, 0xcf, 0xa1, - 0xd0, 0x6c, 0xb2, 0xff, 0x68, 0xe2, 0x2c, 0xde, 0xeb, 0x2d, 0xc8, 0xc8, - 0xff, 0x92, 0x1c, 0x1f, 0x88, 0xff, 0x49, 0x97, 0xfb, 0x45, 0x83, 0xf6, - 0x12, 0xcb, 0xe7, 0x20, 0x62, 0xcb, 0xf0, 0xf9, 0xc9, 0x62, 0xca, 0xf9, - 0x19, 0x4e, 0xa3, 0xf9, 0x89, 0x10, 0x5f, 0xf1, 0x4b, 0xed, 0xe9, 0xd7, - 0xeb, 0x2f, 0xf1, 0xe8, 0xbb, 0xdc, 0xd9, 0x65, 0xcf, 0xbd, 0x65, 0xfd, - 0x02, 0x38, 0x7b, 0x16, 0x5f, 0xb6, 0x3d, 0x4c, 0x16, 0x50, 0xa8, 0x9f, - 0x88, 0xcf, 0x43, 0x04, 0x59, 0x7e, 0x07, 0x63, 0xf3, 0xab, 0x2f, 0x0c, - 0xc5, 0x59, 0x52, 0x79, 0x18, 0x57, 0x7f, 0x3f, 0x84, 0xd3, 0xfe, 0xb2, - 0xf6, 0xbf, 0x11, 0x65, 0xff, 0xc7, 0xd9, 0xef, 0x3c, 0xe7, 0xe0, 0x2c, - 0xa9, 0x3e, 0x27, 0x1f, 0xbe, 0x22, 0x04, 0x16, 0x5f, 0xe2, 0x07, 0x31, - 0x92, 0x15, 0x97, 0xfe, 0xf4, 0xeb, 0xf2, 0xc6, 0x9f, 0xf5, 0x97, 0xb3, - 0x22, 0x59, 0x6c, 0x15, 0x11, 0xde, 0x32, 0xfd, 0x02, 0xf4, 0xfc, 0xd2, - 0xcb, 0xf6, 0x80, 0xc7, 0x25, 0x95, 0xd3, 0xc5, 0x00, 0xf5, 0xf3, 0x27, - 0x36, 0x59, 0x7e, 0xcd, 0xe5, 0x3f, 0x2c, 0xa3, 0x3c, 0xbe, 0x11, 0x5f, - 0x49, 0x74, 0x2b, 0x28, 0x6b, 0x81, 0x39, 0x0d, 0x8d, 0x42, 0x19, 0x84, - 0x1e, 0x84, 0x8b, 0x90, 0x14, 0x2b, 0xf8, 0xf9, 0xd6, 0xd8, 0xf2, 0x1b, - 0xf4, 0x7f, 0xb3, 0xf7, 0x59, 0x7f, 0xbf, 0x13, 0x08, 0x7e, 0x95, 0x95, - 0x29, 0x80, 0xb2, 0x14, 0x4e, 0x57, 0x7f, 0x73, 0x18, 0x7e, 0x95, 0x97, - 0xbe, 0x87, 0x16, 0x57, 0xc7, 0x95, 0xa2, 0xcb, 0xdc, 0xfd, 0xa5, 0x97, - 0xdf, 0x72, 0x7e, 0x59, 0x7f, 0x3c, 0x3b, 0xc9, 0x15, 0x65, 0xa0, 0x67, - 0xa4, 0xc2, 0x4a, 0x94, 0x5d, 0xe1, 0x1b, 0xb8, 0xdf, 0xf3, 0x45, 0x9a, - 0xe7, 0xb0, 0x2b, 0x2f, 0xfe, 0x2c, 0xe6, 0x0e, 0x28, 0x4e, 0xb6, 0x59, - 0x46, 0x7f, 0xdd, 0x39, 0xbe, 0xc1, 0x99, 0x2c, 0xbe, 0x03, 0xea, 0x0b, - 0x2f, 0xa4, 0x0d, 0xbe, 0x24, 0xbf, 0x41, 0xbb, 0x4d, 0x34, 0xb2, 0x88, - 0xf5, 0x00, 0x4d, 0x7f, 0xff, 0xe2, 0x08, 0x9d, 0x2c, 0xfd, 0xcb, 0xfe, - 0x93, 0xfe, 0xd6, 0x79, 0x65, 0xf3, 0xf5, 0x9d, 0x59, 0x7e, 0xe6, 0xa5, - 0xc2, 0xb2, 0xfd, 0x24, 0x19, 0xde, 0xb2, 0xb0, 0xf3, 0xfc, 0x4f, 0x51, - 0x93, 0xb7, 0x19, 0x06, 0x3b, 0x39, 0x0f, 0xed, 0xdd, 0x71, 0xbf, 0xdf, - 0x96, 0x6f, 0xd1, 0xf1, 0x65, 0xfe, 0x10, 0x89, 0xda, 0x7f, 0x2c, 0xa9, - 0x67, 0x87, 0x63, 0xd7, 0xc9, 0xc7, 0x18, 0xb6, 0x97, 0xd9, 0x0d, 0x07, - 0x94, 0x7b, 0xfc, 0xea, 0x39, 0x46, 0x73, 0xc8, 0x55, 0x82, 0x3d, 0x8d, - 0xeb, 0x2d, 0x1a, 0xde, 0x6c, 0xc7, 0x16, 0xc2, 0xcb, 0xd0, 0x27, 0x59, - 0x7f, 0xbb, 0x2c, 0xfa, 0x19, 0xd5, 0x96, 0xf9, 0xb2, 0x7f, 0x52, 0x57, - 0x83, 0x77, 0xed, 0xc9, 0x86, 0xa5, 0x65, 0xee, 0xbf, 0xeb, 0x2f, 0xe9, - 0xd6, 0xd3, 0xad, 0x96, 0x5f, 0xfa, 0x4b, 0xbe, 0x03, 0xc2, 0x12, 0xb2, - 0xe1, 0x76, 0x59, 0x7e, 0x07, 0xf8, 0x41, 0x59, 0x7b, 0x53, 0x12, 0xcb, - 0xc4, 0xf1, 0x2c, 0xbf, 0x71, 0xa7, 0xfb, 0x8b, 0x28, 0xcf, 0x19, 0xc7, - 0x2f, 0xb9, 0xd9, 0x62, 0xcb, 0xff, 0x14, 0x8b, 0xe7, 0xe8, 0x99, 0xfa, - 0xcb, 0xdc, 0xd6, 0x2c, 0xbc, 0x59, 0x05, 0x97, 0xed, 0x7e, 0x40, 0xfd, - 0x65, 0x49, 0xe2, 0xe0, 0xdd, 0x1a, 0x20, 0x78, 0xc3, 0x6d, 0xd5, 0x97, - 0xf6, 0x09, 0x31, 0x1f, 0x16, 0x51, 0x1e, 0x17, 0x45, 0x2a, 0x55, 0x6f, - 0xec, 0x55, 0x01, 0xd1, 0x97, 0xe1, 0xee, 0x86, 0x58, 0x51, 0xe6, 0x3f, - 0xc8, 0x08, 0x8b, 0xb0, 0xc1, 0xde, 0xc9, 0x7f, 0xd2, 0x7e, 0x96, 0x6d, - 0x8d, 0x2c, 0xbf, 0xf7, 0x24, 0xc6, 0x53, 0x03, 0xfd, 0x65, 0xfb, 0xc7, - 0xb3, 0x92, 0xcb, 0xf0, 0x05, 0x72, 0xfd, 0x65, 0xfd, 0xcd, 0x4e, 0x0c, - 0xd6, 0x5d, 0xe3, 0x59, 0x52, 0x89, 0x2f, 0x13, 0xef, 0x29, 0xdc, 0x2c, - 0xbf, 0xed, 0x8b, 0x21, 0xec, 0xff, 0x8b, 0x2f, 0xfd, 0x85, 0xf4, 0x39, - 0xa0, 0x4f, 0xeb, 0x2f, 0xd9, 0xde, 0x60, 0xab, 0x28, 0xcf, 0xa1, 0x88, - 0x17, 0xfe, 0xd7, 0x9a, 0x71, 0xef, 0xee, 0x05, 0x65, 0xfb, 0xb1, 0xbd, - 0x9b, 0x8b, 0x29, 0xcf, 0xbc, 0x90, 0xaf, 0x0e, 0x77, 0xac, 0xba, 0x62, - 0x59, 0x7f, 0xf1, 0x4b, 0x43, 0x29, 0xfa, 0x06, 0x4b, 0x2b, 0x47, 0xb4, - 0x42, 0xf7, 0xe6, 0x67, 0x9f, 0x71, 0x65, 0x61, 0xe5, 0x99, 0x0d, 0xfb, - 0xe0, 0x9f, 0x78, 0xb2, 0xff, 0xe8, 0xd1, 0x42, 0x75, 0xb4, 0x66, 0xb3, - 0xcb, 0x2a, 0x4f, 0xd5, 0xca, 0x6a, 0x53, 0x98, 0x19, 0x07, 0xa1, 0x86, - 0x50, 0x95, 0xbf, 0xe6, 0x7b, 0x3b, 0x80, 0xd6, 0xcb, 0x2f, 0xf1, 0x4c, - 0x22, 0xf4, 0xec, 0xb2, 0xff, 0xdc, 0x32, 0x8a, 0x43, 0xac, 0xf9, 0x65, - 0x0c, 0xfc, 0xfa, 0x69, 0x7e, 0x18, 0xa2, 0x83, 0xe5, 0x96, 0x15, 0xcf, - 0x38, 0x04, 0x57, 0xfd, 0x3d, 0xcd, 0x05, 0xdc, 0x2a, 0x8b, 0xe1, 0x7e, - 0xcd, 0x4f, 0xd0, 0x59, 0x74, 0xb0, 0x27, 0xd9, 0xd4, 0x4b, 0xfe, 0x71, - 0x78, 0x7c, 0x3f, 0xba, 0xb2, 0xbc, 0x7c, 0xe4, 0x5b, 0x7f, 0xfe, 0x20, - 0xc6, 0xf4, 0x9e, 0x8d, 0x87, 0x1f, 0x2c, 0x59, 0x79, 0xf5, 0xb2, 0xa2, - 0xff, 0x5f, 0xf8, 0xbb, 0xac, 0x9f, 0xa0, 0x6c, 0x59, 0x52, 0x8e, 0x1c, - 0x21, 0x0a, 0xc3, 0x95, 0x5e, 0xe9, 0xb4, 0xb2, 0xfe, 0x7d, 0x84, 0x1b, - 0xee, 0xac, 0xae, 0x9e, 0x78, 0x07, 0x6f, 0xf6, 0xb5, 0x91, 0x32, 0x78, - 0xb2, 0xff, 0x9f, 0xbc, 0x32, 0xfa, 0x0c, 0x59, 0x7f, 0xc7, 0xad, 0x81, - 0xff, 0xf3, 0xd5, 0x97, 0xfd, 0x39, 0xa8, 0xd0, 0x3f, 0xa0, 0xb2, 0xa0, - 0x98, 0x36, 0x11, 0x7e, 0x69, 0xc3, 0x9e, 0x9e, 0x5f, 0xff, 0xef, 0x3f, - 0x0e, 0x79, 0x1b, 0xbe, 0xc6, 0x60, 0x5f, 0x4b, 0x2e, 0x9d, 0xc5, 0x97, - 0xdc, 0xe3, 0xb1, 0x65, 0x79, 0x13, 0x9f, 0xb0, 0xf4, 0x66, 0xff, 0x7d, - 0xd1, 0x3d, 0xec, 0xdd, 0x59, 0x7e, 0xc3, 0xda, 0x45, 0x59, 0x6d, 0xb0, - 0xf8, 0x7c, 0x73, 0x7d, 0xf4, 0x3a, 0xeb, 0x2f, 0xff, 0x86, 0x1f, 0x19, - 0x66, 0xf7, 0x87, 0x0c, 0x6b, 0x2f, 0xee, 0x74, 0xa7, 0x3e, 0x59, 0x58, - 0x8a, 0x07, 0x23, 0x02, 0x85, 0xfb, 0xfe, 0x77, 0x3c, 0xb2, 0xfe, 0x6b, - 0xb9, 0xe8, 0x62, 0xca, 0x93, 0xd7, 0x72, 0x9a, 0x94, 0xdb, 0x61, 0x0c, - 0x82, 0x84, 0x2d, 0xfd, 0x18, 0x79, 0x9f, 0xf1, 0x65, 0xff, 0xfb, 0xb9, - 0xad, 0x8f, 0x87, 0xbf, 0xc7, 0x9f, 0x75, 0x65, 0xa5, 0x88, 0x88, 0x23, - 0x0b, 0xf7, 0x31, 0x8f, 0xf2, 0xcb, 0xcc, 0xd6, 0xcb, 0x2b, 0x0f, 0x18, - 0xca, 0x2f, 0xfd, 0xd9, 0xe7, 0x24, 0xf6, 0x7e, 0xac, 0xbf, 0xe3, 0xc2, - 0x18, 0xa7, 0x9a, 0x59, 0x46, 0x98, 0x16, 0x9b, 0x7a, 0x41, 0xb8, 0x7d, - 0x7f, 0xdc, 0xcd, 0xb8, 0x64, 0xfb, 0x2c, 0xbb, 0x3e, 0x59, 0x58, 0x7a, - 0x1d, 0x39, 0xbf, 0x9f, 0x5f, 0x43, 0x3a, 0xb2, 0xff, 0xdd, 0xe1, 0xeb, - 0xa4, 0x07, 0x82, 0xcb, 0xff, 0xf1, 0xf3, 0x07, 0xec, 0xe9, 0xe7, 0x36, - 0xc6, 0x96, 0x5f, 0xfe, 0x64, 0xc7, 0xe0, 0x0b, 0x33, 0x7c, 0xfc, 0xb2, - 0xff, 0xd3, 0x9a, 0xcd, 0x7e, 0xc3, 0xe2, 0xcb, 0xcf, 0xf6, 0xe2, 0xcb, - 0xf9, 0xfb, 0xc9, 0xcd, 0x96, 0x54, 0xa7, 0x33, 0x85, 0xc1, 0x3e, 0x35, - 0x52, 0x4d, 0xde, 0x7c, 0xd1, 0x05, 0xfb, 0xf7, 0xfa, 0x18, 0xb2, 0xff, - 0x4f, 0xdc, 0x32, 0xfa, 0x0b, 0x2a, 0x4f, 0x75, 0x85, 0x37, 0xfc, 0x09, - 0x67, 0x31, 0x93, 0xba, 0xb2, 0xff, 0xda, 0xdb, 0x07, 0x9e, 0xf6, 0x6e, - 0xac, 0xb0, 0xab, 0x2f, 0xf7, 0x78, 0x26, 0xf9, 0xcf, 0x96, 0x54, 0x9e, - 0x49, 0x09, 0x5f, 0xfb, 0x5c, 0x60, 0x99, 0xa3, 0x98, 0x96, 0x56, 0x1f, - 0x00, 0x08, 0x2b, 0x13, 0x0c, 0xe4, 0x3a, 0xef, 0xee, 0x3c, 0x0a, 0x58, - 0xb2, 0xf1, 0x4e, 0xea, 0xcb, 0xff, 0xe8, 0x4e, 0xb6, 0xe9, 0x8b, 0xce, - 0x39, 0x41, 0x65, 0xfd, 0xb7, 0x31, 0x93, 0xba, 0xb2, 0xa2, 0x44, 0x10, - 0x8a, 0x35, 0xa4, 0x78, 0xf0, 0xb1, 0xa8, 0x50, 0x5e, 0x3f, 0x1a, 0xcb, - 0x87, 0x05, 0x97, 0x8b, 0x20, 0xb2, 0xd0, 0xd1, 0xb2, 0xe8, 0xbd, 0xf3, - 0x3a, 0x7b, 0x2c, 0xac, 0x3c, 0xa3, 0x27, 0xbf, 0xfb, 0x7f, 0x45, 0x92, - 0x86, 0x78, 0xd8, 0xb2, 0xed, 0xba, 0xb2, 0xff, 0x6f, 0xf3, 0xc2, 0x70, - 0x96, 0x54, 0x9e, 0x5e, 0x0c, 0x5f, 0x41, 0xf5, 0x05, 0x97, 0xff, 0xa7, - 0x4f, 0xd0, 0x74, 0xfb, 0xcf, 0x1a, 0xcb, 0x42, 0x23, 0xeb, 0xf1, 0x15, - 0xfd, 0x8d, 0x09, 0xe7, 0xea, 0xcb, 0xfa, 0x0c, 0xe9, 0x02, 0x25, 0x97, - 0xff, 0xc2, 0x42, 0x77, 0x99, 0x7f, 0xa3, 0x96, 0x62, 0x4a, 0xd2, 0x20, - 0x3a, 0x5f, 0x7d, 0xe1, 0x3e, 0xea, 0xcb, 0xf6, 0x33, 0x0f, 0x7a, 0xcb, - 0xdb, 0x45, 0xba, 0xb2, 0xcd, 0x2c, 0xa9, 0x4d, 0xf3, 0x62, 0x9c, 0x85, - 0x78, 0x48, 0xcc, 0x94, 0x05, 0x11, 0xe4, 0x57, 0x77, 0x16, 0x5f, 0xb4, - 0x7b, 0x3b, 0x15, 0x30, 0x9a, 0xfb, 0xa0, 0x97, 0x54, 0xc2, 0x6b, 0xbf, - 0x95, 0x50, 0x26, 0xbf, 0xc4, 0xe2, 0xf6, 0x75, 0xfa, 0xa8, 0x13, 0x5f, - 0xee, 0x67, 0x8f, 0x80, 0xd9, 0x53, 0x09, 0xae, 0xc1, 0xaa, 0x61, 0x35, - 0xcd, 0x34, 0xb9, 0x84, 0xd5, 0x89, 0xa9, 0x7c, 0x6a, 0x65, 0xcc, 0x24, - 0xe2, 0x0e, 0xf4, 0x16, 0x88, 0xed, 0xd4, 0xcc, 0x26, 0x37, 0x3e, 0x7a, - 0xf9, 0x50, 0x0b, 0x51, 0xe7, 0xdf, 0x60, 0xc0, 0x4b, 0x28, 0xcf, 0x39, - 0xcb, 0x6f, 0xff, 0xdf, 0xb1, 0xe1, 0xcf, 0x4e, 0xfc, 0x10, 0x6f, 0x05, - 0x95, 0x2b, 0xd7, 0x06, 0x6d, 0xe8, 0x4b, 0xb9, 0x01, 0x42, 0x47, 0x92, - 0x81, 0xfb, 0x2a, 0xe8, 0x04, 0x17, 0xcf, 0xbf, 0x1a, 0x59, 0x7f, 0xfe, - 0xf3, 0xc3, 0xd3, 0x27, 0xde, 0x19, 0x7d, 0x05, 0x97, 0x8b, 0xf6, 0x2c, - 0xbf, 0xe9, 0x07, 0x48, 0xf7, 0x88, 0xc5, 0x97, 0x67, 0x30, 0xf6, 0x34, - 0x3b, 0x5a, 0x47, 0xe3, 0x92, 0x14, 0x2b, 0x2f, 0xb0, 0xf4, 0x2a, 0xcb, - 0xb6, 0xd2, 0xcb, 0xbf, 0x89, 0x65, 0xff, 0xcc, 0x8a, 0x0e, 0x5b, 0x0e, - 0x4b, 0x65, 0x95, 0x27, 0xee, 0x31, 0x8c, 0x19, 0xbe, 0x3f, 0x3b, 0x16, - 0x5f, 0x6a, 0x79, 0xb2, 0xca, 0xe9, 0xe2, 0x08, 0x43, 0x52, 0xdd, 0xa3, - 0xed, 0x0a, 0x21, 0xca, 0x28, 0xc4, 0x61, 0x61, 0xf6, 0x18, 0xf7, 0x3e, - 0x8e, 0x3c, 0xe1, 0xcf, 0x14, 0x7f, 0xfa, 0x8f, 0x0d, 0x90, 0x90, 0xf4, - 0x77, 0x5f, 0xc3, 0x04, 0x88, 0x79, 0x1a, 0x3f, 0x67, 0xf4, 0x77, 0xc7, - 0x09, 0x1e, 0x66, 0x24, 0x26, 0x77, 0x1c, 0x6f, 0xb6, 0x67, 0x78, 0xb2, - 0xf4, 0xe1, 0x2c, 0xbb, 0x38, 0xb2, 0xa0, 0x6c, 0x3f, 0x1a, 0xbf, 0xb2, - 0x04, 0x07, 0xe2, 0xcb, 0xef, 0xb8, 0x72, 0xb2, 0xff, 0x61, 0x6e, 0x74, - 0xc8, 0x55, 0x95, 0xa4, 0x41, 0xf8, 0xb3, 0xf2, 0x2b, 0xec, 0xd1, 0x8a, - 0xb2, 0xf3, 0x5b, 0x31, 0x65, 0xff, 0x7a, 0x58, 0x4f, 0x0c, 0xf2, 0xca, - 0x94, 0x54, 0x8c, 0xc7, 0x08, 0xb8, 0x3f, 0x7f, 0x7a, 0x44, 0x2f, 0xbf, - 0x59, 0x7a, 0x39, 0xcd, 0xc5, 0x97, 0xb3, 0x1a, 0x59, 0x71, 0x8a, 0xb2, - 0xb6, 0x36, 0x66, 0x39, 0x7e, 0xee, 0x44, 0x6c, 0x59, 0x7e, 0x9f, 0xbf, - 0xfe, 0x56, 0x54, 0x9e, 0x91, 0x14, 0x5f, 0xa7, 0x7c, 0x97, 0x56, 0x5f, - 0x8f, 0x7f, 0x4f, 0x65, 0x97, 0xee, 0xfa, 0x75, 0x05, 0x97, 0xfe, 0x61, - 0x18, 0xb3, 0xde, 0x1e, 0x96, 0x5f, 0x43, 0xb8, 0x15, 0x95, 0x87, 0xc0, - 0xe7, 0xd7, 0x14, 0xac, 0xbf, 0xef, 0x3c, 0x26, 0x22, 0x96, 0x2c, 0xbf, - 0x16, 0x09, 0x91, 0x2c, 0xbf, 0xfe, 0x20, 0x9b, 0x06, 0x07, 0xd4, 0x33, - 0xee, 0xac, 0xbf, 0xf3, 0x97, 0xff, 0x77, 0x53, 0x8d, 0x2c, 0xbf, 0xf7, - 0x3c, 0x71, 0xbe, 0x84, 0xe6, 0xcb, 0x2d, 0x9b, 0xa8, 0x82, 0xfd, 0x02, - 0xff, 0xbc, 0xec, 0xf1, 0xcf, 0xd0, 0x59, 0x7e, 0x2f, 0x3b, 0xb1, 0x65, - 0xf8, 0xa1, 0x1e, 0xfb, 0x2c, 0xa9, 0x3d, 0x11, 0x09, 0xaa, 0x53, 0xaa, - 0x84, 0x34, 0x8c, 0xb3, 0xf8, 0x45, 0x56, 0xca, 0xe0, 0x06, 0x40, 0x12, - 0x8f, 0x8a, 0xb5, 0x08, 0x86, 0x10, 0x78, 0x54, 0x8e, 0x3b, 0x1e, 0x95, - 0xfe, 0xf7, 0xd0, 0xe7, 0xb3, 0x65, 0x97, 0xfc, 0xfe, 0x88, 0xcb, 0xd9, - 0xf2, 0xca, 0x93, 0xef, 0xc3, 0x6b, 0xf4, 0x50, 0x9e, 0xf1, 0x65, 0xdc, - 0xfd, 0x65, 0x7e, 0x78, 0x31, 0xe5, 0x37, 0xff, 0xff, 0xde, 0xc2, 0xff, - 0x0b, 0xbe, 0xcf, 0x4e, 0x44, 0x7a, 0x8b, 0xc7, 0xf7, 0x56, 0x5f, 0xda, - 0x30, 0xfb, 0x22, 0x59, 0x7f, 0xfc, 0x59, 0x13, 0xff, 0x9d, 0x9d, 0xe6, - 0x5f, 0xac, 0xa6, 0x1f, 0xff, 0x4b, 0xab, 0x13, 0x1f, 0x78, 0x7e, 0xdf, - 0x14, 0xe3, 0x4b, 0x2f, 0xa7, 0xee, 0x3a, 0xca, 0x97, 0x50, 0x25, 0xb4, - 0x2b, 0x60, 0x72, 0x38, 0x67, 0xe5, 0x77, 0x10, 0x18, 0xdc, 0xfe, 0x54, - 0x38, 0xd3, 0xb7, 0x4e, 0xe2, 0x30, 0xd2, 0xbb, 0xcb, 0xf3, 0x28, 0x7d, - 0xf5, 0x84, 0x11, 0xa2, 0xef, 0x27, 0x10, 0x86, 0xff, 0xf8, 0xcb, 0x19, - 0x22, 0x10, 0x20, 0x58, 0xc5, 0x97, 0xfc, 0x33, 0xc2, 0x04, 0x33, 0x8b, - 0x2f, 0xfb, 0x5a, 0x90, 0xfb, 0xe7, 0xdd, 0x59, 0x78, 0xa7, 0x8b, 0x2f, - 0xb0, 0xb1, 0x8b, 0x29, 0x65, 0x49, 0xe3, 0x40, 0x6c, 0x04, 0x17, 0xf0, - 0x40, 0xe3, 0x64, 0xac, 0xbf, 0xef, 0x1c, 0x3c, 0xf0, 0x36, 0x2c, 0xbf, - 0xf7, 0x0c, 0xbe, 0x87, 0x4f, 0x86, 0xb2, 0xb0, 0xfd, 0xba, 0x71, 0x7f, - 0x3f, 0x7d, 0x20, 0x6d, 0xac, 0xbf, 0xf1, 0xb3, 0x07, 0xe3, 0xe1, 0x62, - 0xcb, 0xe6, 0x48, 0xe5, 0x65, 0x11, 0xee, 0xf0, 0xf2, 0xa0, 0xaa, 0xa3, - 0x13, 0x4c, 0xde, 0x28, 0x41, 0xe8, 0xb9, 0x90, 0xa3, 0x22, 0x1e, 0xc2, - 0x3a, 0xfb, 0xd9, 0xfb, 0xac, 0xbe, 0x29, 0xd4, 0x16, 0x5f, 0xf7, 0xa6, - 0x06, 0x44, 0x0f, 0x96, 0x5f, 0xf4, 0xc0, 0xfb, 0x09, 0x2f, 0xd6, 0x5f, - 0xf4, 0xe7, 0x44, 0xff, 0xf9, 0xf9, 0x65, 0xec, 0x21, 0x56, 0x5b, 0x52, - 0x8e, 0x9c, 0x21, 0xd1, 0xc3, 0x0e, 0x1b, 0x67, 0x97, 0xf1, 0x48, 0x75, - 0x30, 0x59, 0x7f, 0xf9, 0xcf, 0xb0, 0x01, 0x77, 0x9e, 0x78, 0x2c, 0x10, - 0xd6, 0x5d, 0x3e, 0x59, 0x5e, 0x3f, 0x0d, 0xeb, 0x57, 0xfe, 0xe0, 0x9d, - 0x32, 0x04, 0x33, 0x8b, 0x2e, 0x1f, 0x16, 0x56, 0xe9, 0xfd, 0x78, 0x90, - 0x8f, 0xef, 0xff, 0xec, 0xfb, 0x38, 0xe5, 0xe3, 0x9e, 0xf8, 0x0e, 0xc5, - 0x95, 0x19, 0x57, 0xec, 0xc6, 0x16, 0xf1, 0xb4, 0x11, 0x8d, 0xf8, 0x0c, - 0x3f, 0x4a, 0xcb, 0xf6, 0x68, 0x33, 0x8b, 0x28, 0x8f, 0x34, 0x42, 0x7b, - 0xfe, 0xc6, 0x66, 0x8f, 0x67, 0x62, 0xcb, 0xf7, 0x5d, 0xd9, 0xc5, 0x97, - 0x66, 0xcb, 0x2f, 0xfe, 0x76, 0x14, 0xfd, 0x1e, 0x2f, 0x73, 0xcb, 0x2f, - 0xff, 0xce, 0x5d, 0x77, 0xf1, 0x4f, 0x78, 0xd6, 0x79, 0x65, 0xfe, 0x13, - 0x59, 0xbe, 0x4a, 0x0b, 0x2f, 0xfe, 0x67, 0x1e, 0x1a, 0x9e, 0x49, 0x6c, - 0xb2, 0xff, 0xa7, 0xbe, 0xc6, 0x66, 0xb1, 0x65, 0xfb, 0xbe, 0x39, 0xe2, - 0xcb, 0xdb, 0x39, 0x68, 0xf7, 0xb8, 0x6f, 0x7f, 0xd3, 0xff, 0x06, 0x07, - 0xd4, 0x16, 0x5e, 0xd7, 0x38, 0xb8, 0x80, 0xd7, 0xc1, 0x77, 0x0a, 0xa2, - 0x03, 0x37, 0x35, 0x56, 0xc8, 0x22, 0x93, 0x4c, 0x97, 0xfd, 0x3e, 0x09, - 0xf1, 0xcf, 0x65, 0x96, 0x98, 0xc7, 0xc1, 0xa2, 0x8b, 0xcc, 0x78, 0x2c, - 0xaf, 0x1e, 0x2f, 0xe5, 0x37, 0x3f, 0xeb, 0x2d, 0xa9, 0x37, 0x5d, 0x23, - 0xac, 0x54, 0xfc, 0xf0, 0xa2, 0x12, 0x3b, 0x6a, 0x1a, 0xe0, 0x07, 0xc7, - 0x1a, 0x27, 0xf0, 0xbf, 0xe9, 0x04, 0xa7, 0xd9, 0x56, 0x77, 0xc3, 0xf4, - 0xc7, 0xac, 0xbf, 0xff, 0x8c, 0x65, 0x9c, 0x9d, 0x46, 0xe7, 0x9c, 0x72, - 0x35, 0x97, 0xb7, 0xcf, 0x96, 0x56, 0x2e, 0x97, 0x9c, 0xe1, 0x13, 0xb7, - 0xf0, 0x94, 0x45, 0xbb, 0x9f, 0xe5, 0x97, 0xbc, 0xfb, 0xab, 0x2f, 0xa7, - 0xf8, 0xfc, 0x59, 0x7c, 0xc6, 0x3f, 0x56, 0x53, 0x0f, 0xf7, 0xf1, 0x7e, - 0x8f, 0x88, 0x4b, 0x7f, 0x66, 0xe3, 0xbf, 0xb8, 0xb2, 0xf8, 0xbb, 0xf8, - 0x16, 0x5f, 0x98, 0xd9, 0x6c, 0xb6, 0x63, 0xa5, 0x97, 0xff, 0xf4, 0x33, - 0x87, 0xad, 0x1b, 0x04, 0xef, 0xb1, 0xae, 0xac, 0xac, 0x45, 0xd1, 0x91, - 0xb9, 0xd5, 0xfb, 0x6f, 0x3f, 0xa5, 0x65, 0xff, 0xd8, 0x5d, 0xc2, 0x86, - 0x70, 0x46, 0x2c, 0xa7, 0x3e, 0xb6, 0x8a, 0x2f, 0xff, 0x43, 0x3e, 0x80, - 0x93, 0xfb, 0x76, 0x9a, 0x69, 0x25, 0xf9, 0x8d, 0x61, 0xef, 0x59, 0x7a, - 0x18, 0x51, 0x1f, 0xe8, 0x8a, 0x75, 0x28, 0xd3, 0x78, 0x4f, 0xdf, 0xff, - 0x9f, 0x50, 0x1f, 0xa7, 0x85, 0x8d, 0x3e, 0x85, 0x59, 0x52, 0xac, 0x38, - 0x67, 0xde, 0x87, 0x49, 0x46, 0xd6, 0x02, 0x6b, 0xfe, 0x32, 0x87, 0x1b, - 0x54, 0x74, 0xd9, 0x8e, 0x96, 0x5f, 0x45, 0xcc, 0x89, 0x65, 0xfb, 0xf9, - 0xee, 0x6e, 0xac, 0xbf, 0xb9, 0xad, 0x1c, 0x5c, 0x59, 0x7f, 0xa7, 0x34, - 0x17, 0x70, 0xaa, 0x20, 0x95, 0xff, 0x4f, 0x73, 0x41, 0x77, 0x0a, 0xa2, - 0xf9, 0x5f, 0x9c, 0x7e, 0x91, 0x56, 0x5f, 0xff, 0xf6, 0x1e, 0xb0, 0x7e, - 0x9f, 0xa1, 0xa9, 0xef, 0x04, 0x28, 0x2c, 0xbf, 0xf1, 0x67, 0x7a, 0x7c, - 0xee, 0x7e, 0xb2, 0xf1, 0x67, 0x1b, 0x09, 0xbf, 0x0c, 0xbc, 0x27, 0xcc, - 0x44, 0x22, 0x8e, 0x33, 0xd1, 0x2a, 0x14, 0xdf, 0x1d, 0x4d, 0xff, 0xd3, - 0xe3, 0x17, 0x35, 0xb3, 0x01, 0xf2, 0xcb, 0xfe, 0x1e, 0xb1, 0x99, 0x07, - 0x1a, 0xcb, 0xff, 0xf0, 0x1f, 0x98, 0x41, 0xf1, 0x8f, 0xd9, 0xf6, 0xe2, - 0xca, 0xc5, 0x67, 0x67, 0x28, 0xe3, 0xc5, 0x8e, 0x8e, 0x47, 0x17, 0xe8, - 0x67, 0xe2, 0x75, 0x65, 0xfd, 0xe8, 0xff, 0x18, 0x7c, 0xb2, 0x9c, 0xf6, - 0xc0, 0x55, 0x7f, 0xff, 0x48, 0x03, 0xe9, 0xd6, 0x70, 0x80, 0xf0, 0x15, - 0xd6, 0x5f, 0xfb, 0xb2, 0xce, 0x49, 0xec, 0xfd, 0x59, 0x7f, 0xed, 0x9f, - 0xe8, 0xfc, 0xe9, 0x3e, 0xf5, 0x97, 0xff, 0xd9, 0xd9, 0x2e, 0xf3, 0x30, - 0x51, 0x5e, 0x0b, 0x2f, 0xe1, 0x73, 0xbd, 0xc1, 0xac, 0xbf, 0xf6, 0x0e, - 0x61, 0x3e, 0xcf, 0xdd, 0x65, 0xfc, 0xf0, 0x04, 0x1c, 0x6b, 0x2f, 0xff, - 0xfb, 0xc0, 0x7d, 0xb5, 0x3e, 0x73, 0xe7, 0x30, 0x57, 0x2f, 0xd6, 0x5f, - 0x8f, 0x6e, 0x71, 0xd6, 0x50, 0xd1, 0x1f, 0xe6, 0x7b, 0xcd, 0x34, 0xd2, - 0x4b, 0xf7, 0xb0, 0x2f, 0xa4, 0x8d, 0xcd, 0x05, 0xff, 0xfe, 0x8b, 0x53, - 0xbe, 0x30, 0xaf, 0x18, 0x48, 0xb9, 0x1b, 0x3e, 0xea, 0xca, 0x94, 0x75, - 0xf5, 0x0c, 0x43, 0x9b, 0xfe, 0xf1, 0xb5, 0xd8, 0xb5, 0x3d, 0x59, 0x7f, - 0xe3, 0xe8, 0x33, 0xae, 0xcc, 0x25, 0x96, 0x38, 0x1f, 0xbe, 0x8e, 0xeb, - 0x65, 0x6e, 0x61, 0x44, 0xd2, 0x7b, 0x0b, 0xbf, 0x3d, 0x28, 0xe9, 0xfb, - 0x0a, 0x7a, 0x59, 0x4b, 0x2e, 0x87, 0x1a, 0x2d, 0xb7, 0x02, 0xef, 0xfc, - 0xc9, 0xfb, 0x85, 0x9b, 0x09, 0x05, 0x97, 0xff, 0xf8, 0xfc, 0xe5, 0xf4, - 0x04, 0x84, 0x94, 0x1d, 0x98, 0x35, 0x97, 0xff, 0xb3, 0x3c, 0x3f, 0x4f, - 0x33, 0xc7, 0xfa, 0xcb, 0xf4, 0x50, 0x9d, 0x71, 0x65, 0xfe, 0xc2, 0x04, - 0x39, 0xb3, 0xac, 0xb0, 0xb1, 0x8f, 0x70, 0x65, 0x35, 0x29, 0xd0, 0xec, - 0x61, 0x04, 0x11, 0xb0, 0x1c, 0x29, 0x6f, 0xa3, 0x1f, 0x80, 0xb2, 0xfd, - 0x90, 0xf4, 0xe9, 0x65, 0x04, 0xf2, 0xf7, 0x49, 0x2f, 0xc7, 0xe1, 0x67, - 0xe5, 0x97, 0xec, 0x19, 0x4f, 0xcb, 0x2d, 0xd9, 0x3d, 0x02, 0x29, 0xbf, - 0xff, 0xff, 0x7d, 0xd2, 0x96, 0x70, 0x7e, 0x9e, 0x64, 0x0c, 0x51, 0x5f, - 0xa5, 0x2c, 0xe2, 0xcb, 0xff, 0xbb, 0xc7, 0x87, 0x30, 0x32, 0xc3, 0x59, - 0x7f, 0xfb, 0xe2, 0x8c, 0x1f, 0x1f, 0xff, 0xce, 0xa0, 0xb2, 0xa5, 0x3b, - 0x13, 0x75, 0x72, 0x60, 0x42, 0x10, 0x44, 0x3b, 0xff, 0x6d, 0xf4, 0x9b, - 0x30, 0xb3, 0x75, 0x65, 0xfb, 0x6f, 0x48, 0x31, 0x65, 0xfc, 0x7c, 0xc1, - 0x89, 0x8b, 0x2f, 0xdb, 0x86, 0x59, 0xbd, 0x65, 0xfd, 0x83, 0x30, 0x07, - 0xc1, 0x3d, 0x87, 0x2d, 0xbf, 0xb9, 0x03, 0x63, 0xc1, 0x65, 0x4a, 0x3c, - 0x35, 0x08, 0x0f, 0xd0, 0xef, 0xf7, 0x0b, 0x37, 0xf9, 0xe0, 0xb2, 0xf7, - 0x21, 0xbd, 0x65, 0x19, 0xe9, 0xee, 0x9a, 0x5f, 0xfe, 0x9d, 0x84, 0xf4, - 0xfd, 0xdc, 0xd8, 0x86, 0xb2, 0xb1, 0x51, 0x51, 0xc6, 0x62, 0xf0, 0x88, - 0x22, 0x4b, 0xff, 0x9e, 0x18, 0x32, 0x37, 0xf4, 0xb1, 0x65, 0xcf, 0xe5, - 0x95, 0xe3, 0xd7, 0xea, 0x0d, 0xfe, 0x9c, 0x2e, 0xb6, 0x1a, 0x0a, 0xcb, - 0xfd, 0xec, 0xdf, 0x1b, 0x90, 0x0a, 0xca, 0xe1, 0xf7, 0xf4, 0xde, 0xba, - 0x8b, 0x30, 0x42, 0x46, 0xff, 0xfe, 0x2f, 0xcb, 0x3b, 0xec, 0xf1, 0x3f, - 0xd0, 0x3c, 0x59, 0x7f, 0x14, 0x61, 0x35, 0xe7, 0x59, 0x7f, 0xe0, 0x3c, - 0x23, 0x49, 0x8f, 0x18, 0xb2, 0x8d, 0x19, 0x0e, 0xb2, 0x46, 0x17, 0xfe, - 0xd1, 0xff, 0xb7, 0xa7, 0x80, 0xe2, 0xcb, 0xf6, 0x69, 0xfe, 0x69, 0x65, - 0xa0, 0xb2, 0xff, 0xfc, 0xd1, 0x60, 0xf4, 0x60, 0xd8, 0x7e, 0x92, 0xd9, - 0x65, 0xb6, 0x93, 0xe8, 0xc1, 0x1a, 0xd9, 0x17, 0x1c, 0x84, 0x8d, 0xfe, - 0x88, 0x9d, 0xae, 0xe7, 0x96, 0x5e, 0xf6, 0x05, 0x65, 0xe6, 0x9a, 0x69, - 0x25, 0xfd, 0xfc, 0x96, 0x7d, 0xd4, 0x8d, 0xcd, 0x05, 0x46, 0x45, 0x84, - 0x79, 0xa0, 0x87, 0x97, 0xff, 0xf3, 0x6e, 0x30, 0x7c, 0x63, 0x1e, 0x35, - 0x1b, 0xbc, 0xcd, 0x96, 0x57, 0x91, 0x38, 0x03, 0x6a, 0x95, 0x52, 0x27, - 0x0f, 0x97, 0x8d, 0xd6, 0xff, 0x84, 0x9e, 0x63, 0x42, 0x17, 0x56, 0x56, - 0xcd, 0xa5, 0xec, 0x25, 0xc4, 0x0e, 0x15, 0x41, 0x21, 0xf9, 0x68, 0xe5, - 0xd8, 0xea, 0x51, 0x4b, 0x25, 0x25, 0xfa, 0x53, 0xc1, 0x46, 0xa1, 0xc8, - 0x78, 0x76, 0x55, 0xf0, 0x87, 0x57, 0xfe, 0xc3, 0x2d, 0x9c, 0xbb, 0x8c, - 0x59, 0x7f, 0xf0, 0xa2, 0x74, 0xf8, 0x59, 0xbc, 0xc6, 0xb2, 0xfe, 0x2c, - 0x1f, 0x9d, 0xa5, 0x95, 0xa3, 0xf4, 0x62, 0x3d, 0xe2, 0x76, 0x2c, 0xbf, - 0x87, 0xec, 0xec, 0x7e, 0x2c, 0xb1, 0xf8, 0xf2, 0xda, 0x1b, 0xa9, 0x44, - 0xaf, 0x9b, 0x6f, 0x89, 0xfe, 0xea, 0xcb, 0xd8, 0x5f, 0xac, 0xbe, 0xce, - 0xc5, 0x05, 0x97, 0xff, 0x6e, 0x14, 0xee, 0x71, 0xc8, 0x4f, 0xf8, 0xb2, - 0xbc, 0x7d, 0xce, 0x47, 0x7f, 0xec, 0xec, 0xeb, 0x9e, 0x92, 0xea, 0xe2, - 0x08, 0x5f, 0xd9, 0xa0, 0xbb, 0x85, 0x51, 0x04, 0x1b, 0x9e, 0x55, 0xe9, - 0x19, 0xac, 0xa9, 0x3e, 0x7e, 0x26, 0x5d, 0xbc, 0x6b, 0x2f, 0x6f, 0x96, - 0x2c, 0xbf, 0x6d, 0x9a, 0x98, 0x2c, 0xbe, 0xd6, 0x8d, 0xa5, 0x96, 0x76, - 0x1e, 0x5f, 0x8a, 0x2f, 0xed, 0xd1, 0xb4, 0xe5, 0x05, 0x95, 0xb2, 0x36, - 0xb0, 0x64, 0x9a, 0x40, 0x4f, 0x7f, 0xfa, 0x28, 0x19, 0x76, 0x7f, 0x88, - 0x8e, 0x25, 0x97, 0x14, 0x4b, 0x2d, 0x23, 0x3e, 0x3c, 0x4c, 0xad, 0x95, - 0x9f, 0x0a, 0x45, 0xe8, 0x45, 0x7f, 0x0d, 0x22, 0x87, 0xde, 0xf8, 0x4f, - 0xdf, 0xf6, 0x6b, 0xc7, 0xa2, 0xcd, 0x96, 0x5f, 0xde, 0xce, 0xf4, 0xd8, - 0xb2, 0xff, 0xee, 0x0c, 0x8f, 0x61, 0x34, 0x53, 0x8b, 0x2f, 0xee, 0x89, - 0x9e, 0x7f, 0x96, 0x57, 0xc8, 0xa0, 0xf1, 0x69, 0x22, 0x5f, 0xf1, 0x6d, - 0x9a, 0xfc, 0x81, 0xfa, 0xcb, 0xfb, 0x3e, 0xee, 0xb5, 0x2b, 0x2f, 0xfe, - 0xe1, 0x3f, 0x5d, 0x82, 0x4e, 0xd8, 0xb2, 0x86, 0x7e, 0x5c, 0x2e, 0xa9, - 0x47, 0xf9, 0x98, 0x02, 0x15, 0xb7, 0xf6, 0x04, 0x4d, 0xfe, 0xc5, 0x96, - 0x62, 0xcb, 0xfd, 0x3d, 0x1f, 0x80, 0xfc, 0x58, 0x21, 0x63, 0x7f, 0xff, - 0x19, 0x0f, 0x58, 0x1f, 0x4f, 0xb3, 0x5a, 0x91, 0x56, 0x5d, 0x22, 0xac, - 0xbf, 0xd9, 0xa3, 0xfd, 0x8f, 0x05, 0x97, 0x19, 0x2c, 0xb3, 0x4b, 0x28, - 0x66, 0x9c, 0x41, 0x5b, 0xc4, 0x0d, 0x96, 0x5e, 0xd8, 0xa5, 0x65, 0xfb, - 0xcf, 0x0c, 0x25, 0x97, 0xf1, 0xf4, 0xb3, 0xc6, 0xb2, 0xec, 0xf4, 0x63, - 0xd1, 0x92, 0x6b, 0x9d, 0xa8, 0xc9, 0xa5, 0x8c, 0x5f, 0x16, 0xbc, 0x44, - 0xe3, 0xbd, 0x6d, 0xbf, 0xa7, 0x6e, 0xf3, 0x3a, 0xb2, 0xa5, 0x73, 0x16, - 0x11, 0xeb, 0xe1, 0x9e, 0x90, 0x7c, 0x86, 0x51, 0xd2, 0xb4, 0xc9, 0x7f, - 0x70, 0xc6, 0x3c, 0x25, 0x97, 0xff, 0xf7, 0x7d, 0x9c, 0xe6, 0x60, 0x35, - 0xb6, 0x00, 0xba, 0xb2, 0xff, 0xbd, 0x3a, 0xe9, 0x4e, 0x7c, 0xb2, 0xff, - 0xff, 0x8f, 0x69, 0x87, 0x35, 0xa3, 0xfa, 0x28, 0x3e, 0x99, 0x9f, 0x2c, - 0xbf, 0xd2, 0xfb, 0x3e, 0x9c, 0x45, 0x97, 0xbd, 0x84, 0x34, 0x6a, 0xf8, - 0xe3, 0xad, 0x55, 0x89, 0xce, 0x19, 0x67, 0x63, 0x30, 0xbf, 0x4e, 0xa2, - 0xe6, 0xcb, 0x2f, 0xfb, 0xd3, 0x9a, 0x87, 0x00, 0xc5, 0x95, 0xb1, 0xf1, - 0x84, 0xaa, 0xe3, 0xe2, 0xcb, 0xfe, 0xcd, 0x9f, 0xbf, 0x88, 0x50, 0x59, - 0x7f, 0x3f, 0x63, 0x4c, 0x05, 0x59, 0x7f, 0x08, 0xce, 0x34, 0xff, 0x2c, - 0xa9, 0x4d, 0xb7, 0x21, 0x30, 0x29, 0x1e, 0x85, 0x9c, 0xef, 0xa6, 0x17, - 0xb5, 0x9e, 0x59, 0x7b, 0x86, 0xc5, 0x95, 0xa3, 0x71, 0xd1, 0xcb, 0x8f, - 0x65, 0x95, 0x26, 0xe3, 0x70, 0x86, 0xff, 0xef, 0x1c, 0x94, 0x67, 0x28, - 0x1b, 0x16, 0x5f, 0x1c, 0x46, 0xd2, 0xcb, 0xff, 0x98, 0xff, 0x09, 0xd3, - 0xd4, 0x81, 0xb6, 0xb2, 0xed, 0xfa, 0x59, 0x7f, 0xfe, 0x06, 0xc5, 0x39, - 0xe6, 0xf3, 0xdc, 0xf0, 0x34, 0xb2, 0xfb, 0xbc, 0x13, 0x91, 0x93, 0x20, - 0x1a, 0x1e, 0x11, 0x92, 0x50, 0x83, 0x37, 0x13, 0x4b, 0x2f, 0xdc, 0x66, - 0x77, 0x8b, 0x2f, 0xfe, 0x31, 0x73, 0xa7, 0xec, 0xec, 0xe9, 0x65, 0xf9, - 0xe0, 0x47, 0x2b, 0x2f, 0xce, 0x5d, 0xc2, 0x59, 0x7f, 0xe1, 0x49, 0xfb, - 0xcc, 0xdf, 0xe3, 0x59, 0x52, 0x7c, 0xae, 0x4b, 0x7f, 0x1e, 0xb5, 0x38, - 0x4b, 0x2f, 0xfa, 0x61, 0xcc, 0x09, 0x4e, 0x96, 0x50, 0xa7, 0xc5, 0xa2, - 0xbb, 0x1a, 0xcb, 0xc4, 0xe2, 0xe1, 0xb3, 0x09, 0x1d, 0x41, 0x5c, 0xd6, - 0x46, 0x86, 0x16, 0x53, 0x17, 0xd1, 0x43, 0x10, 0xca, 0x11, 0x40, 0x85, - 0xad, 0xf7, 0xc1, 0x31, 0xac, 0xbf, 0x78, 0xe3, 0xcc, 0x55, 0x97, 0xd9, - 0xf9, 0xef, 0x59, 0x7d, 0xff, 0xe0, 0xfd, 0x65, 0x04, 0xfd, 0x48, 0xac, - 0x42, 0x4b, 0xe0, 0x6b, 0xa6, 0xb2, 0xfc, 0x50, 0xe6, 0xdf, 0xac, 0xbf, - 0xfb, 0xf9, 0xe3, 0x24, 0xf5, 0xa3, 0x69, 0x65, 0xe7, 0x78, 0x2c, 0xa1, - 0xa2, 0xb3, 0x08, 0x8c, 0xab, 0xf4, 0x5b, 0xf3, 0x0f, 0xbd, 0x95, 0x97, - 0x8f, 0x1a, 0x59, 0x7c, 0x7c, 0xc1, 0x99, 0xe1, 0x91, 0x3d, 0xfc, 0x5c, - 0xc6, 0x4e, 0xea, 0xcb, 0xed, 0xdf, 0x66, 0xcb, 0x2f, 0xf4, 0x8f, 0x3a, - 0x7e, 0x02, 0xca, 0x93, 0xd8, 0x19, 0x35, 0xf0, 0xa7, 0xf7, 0x16, 0x54, - 0xa3, 0x31, 0x90, 0x87, 0xe9, 0x0d, 0xff, 0xba, 0x27, 0xf2, 0x24, 0xff, - 0x3f, 0xac, 0xbf, 0xe0, 0x0d, 0xcb, 0xf6, 0xdf, 0x9d, 0x65, 0xff, 0xff, - 0x98, 0xe5, 0xfc, 0x0f, 0x80, 0x67, 0x8e, 0x7b, 0xe0, 0x3b, 0x16, 0x5f, - 0xff, 0x10, 0x3b, 0x03, 0xff, 0xce, 0x7d, 0x76, 0x2c, 0xba, 0x36, 0xea, - 0xcb, 0xff, 0xf3, 0x9f, 0x4c, 0x78, 0x3c, 0xe9, 0xb0, 0xf4, 0xb2, 0xff, - 0xf4, 0x84, 0x78, 0x27, 0x4f, 0x52, 0x06, 0xda, 0xcb, 0xc0, 0xc1, 0xac, - 0xbd, 0xa7, 0xe6, 0x26, 0x4f, 0xa4, 0xff, 0x0e, 0x12, 0x94, 0x7a, 0x75, - 0xbb, 0x05, 0x45, 0xae, 0x7a, 0x08, 0xe4, 0xeb, 0xe5, 0x55, 0xa7, 0x29, - 0xc2, 0xe8, 0x4a, 0xcb, 0xff, 0xdb, 0x0f, 0xd3, 0xec, 0xfb, 0xfc, 0xef, - 0x16, 0x53, 0x9f, 0x18, 0x05, 0xaa, 0x57, 0xa1, 0x47, 0x09, 0x8c, 0x86, - 0xd8, 0x5f, 0xce, 0x30, 0x27, 0x95, 0xc2, 0x50, 0x8f, 0xbf, 0xb5, 0x3b, - 0x19, 0x75, 0x65, 0xe1, 0x88, 0xc5, 0x97, 0xe2, 0x91, 0xe4, 0x4b, 0x2f, - 0xb8, 0x0d, 0x0a, 0xb2, 0xfd, 0x3e, 0xec, 0xc1, 0x65, 0xf0, 0xc1, 0xde, - 0x49, 0xf9, 0x7c, 0x4f, 0xf9, 0x25, 0xfe, 0x92, 0x7f, 0x00, 0xb6, 0x59, - 0x7f, 0xfe, 0xf1, 0xeb, 0xcf, 0x24, 0x2b, 0xc4, 0xef, 0xb2, 0xcb, 0xec, - 0x3f, 0xf8, 0xb2, 0xf8, 0xf9, 0x30, 0xc3, 0xf7, 0xd2, 0xad, 0xed, 0x87, - 0x2b, 0x2f, 0xdf, 0xe3, 0x27, 0xcb, 0x2f, 0xfa, 0x79, 0x3d, 0xe6, 0x14, - 0x16, 0x5f, 0xff, 0xfe, 0xcd, 0xb9, 0x22, 0xbf, 0x3b, 0x0c, 0x14, 0x5c, - 0xfb, 0xba, 0x9c, 0xf9, 0x65, 0xfa, 0x63, 0x68, 0xa0, 0xb2, 0xbc, 0x8d, - 0xce, 0x1b, 0x81, 0xf2, 0xf3, 0x6f, 0xd8, 0xb2, 0x86, 0xad, 0x5b, 0x0b, - 0x4e, 0x12, 0x0c, 0x48, 0xf4, 0x2a, 0x5c, 0xdc, 0x87, 0x7b, 0x18, 0x20, - 0x0c, 0x2f, 0x68, 0x4e, 0x2c, 0xbf, 0x63, 0x5d, 0x11, 0x8b, 0x2f, 0xff, - 0xfb, 0x82, 0x4f, 0xe3, 0xf4, 0xeb, 0x08, 0xde, 0x1c, 0xce, 0xac, 0xbc, - 0x3e, 0x6c, 0xb2, 0xb4, 0x88, 0x4e, 0xb3, 0x5f, 0xfa, 0x61, 0xc0, 0x6b, - 0xfc, 0xf3, 0xac, 0xbf, 0xef, 0x4f, 0xdd, 0x9f, 0x3e, 0xea, 0xca, 0x59, - 0x51, 0x8f, 0x1d, 0xb5, 0xcf, 0x2d, 0xf2, 0xcb, 0xb1, 0xa5, 0x97, 0x77, - 0xa6, 0x6a, 0x7a, 0x25, 0x52, 0x88, 0x03, 0x54, 0xbf, 0xf3, 0x90, 0x73, - 0x41, 0x77, 0x0a, 0xa2, 0x11, 0x5d, 0x0d, 0x96, 0x54, 0xaa, 0x77, 0xc1, - 0xe3, 0x85, 0x73, 0x08, 0xff, 0x8c, 0x18, 0x04, 0x3b, 0xd2, 0x6f, 0xb6, - 0xe7, 0xee, 0xb2, 0xff, 0xb4, 0xe3, 0xc3, 0xde, 0xec, 0x59, 0x58, 0x7b, - 0xba, 0x24, 0xbf, 0xff, 0xfd, 0xec, 0x06, 0x85, 0xf1, 0xfd, 0x3d, 0x07, - 0x8b, 0x3e, 0xe8, 0x1f, 0x8b, 0x2f, 0xfe, 0xcf, 0x84, 0xe9, 0x90, 0x21, - 0x9c, 0x59, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xc2, 0xd8, 0x9c, 0x72, - 0x5b, 0x4e, 0x87, 0x85, 0x9d, 0xe1, 0x87, 0x08, 0x4d, 0xb3, 0x53, 0xc2, - 0x71, 0x79, 0x87, 0xff, 0xe0, 0xfc, 0x9c, 0x72, 0x5b, 0x4e, 0x96, 0x5f, - 0x9f, 0x73, 0x3e, 0xea, 0xcb, 0xfa, 0x05, 0x98, 0x06, 0xda, 0xca, 0x94, - 0xea, 0xb1, 0x17, 0xd1, 0x85, 0x39, 0x5d, 0xff, 0xdd, 0x72, 0xdb, 0xe8, - 0x14, 0xe7, 0x16, 0x5f, 0xff, 0xa7, 0xf8, 0xa1, 0x3b, 0x13, 0x8b, 0xc2, - 0x98, 0x2c, 0xbf, 0xf0, 0x99, 0xe7, 0x63, 0x76, 0x9a, 0x69, 0x65, 0xff, - 0xfb, 0x03, 0xe9, 0x21, 0x91, 0xed, 0x84, 0x62, 0xac, 0xb3, 0xec, 0x89, - 0x88, 0x22, 0xd4, 0x13, 0x18, 0x78, 0x77, 0xdf, 0xff, 0xd1, 0x14, 0xff, - 0xc0, 0xf8, 0xf5, 0x11, 0x4f, 0xfc, 0x59, 0x7f, 0xff, 0x77, 0x92, 0x2c, - 0x0f, 0xce, 0x5d, 0x86, 0x6b, 0x16, 0x5f, 0xf8, 0xf9, 0x83, 0x8c, 0xff, - 0xc7, 0xe2, 0xcb, 0xfd, 0x2c, 0xfb, 0xb3, 0x9f, 0x2c, 0xbf, 0xf7, 0x8f, - 0x7f, 0x9e, 0x1a, 0x91, 0x56, 0x56, 0x26, 0x24, 0x15, 0xaf, 0x21, 0xf4, - 0xd2, 0xfe, 0x07, 0x4c, 0x04, 0x2a, 0xcb, 0xfb, 0x1a, 0xc2, 0x07, 0xeb, - 0x2f, 0xd0, 0xce, 0xcf, 0xeb, 0x2e, 0x96, 0x6c, 0x7a, 0xdf, 0x17, 0x5f, - 0xff, 0xfd, 0xfb, 0x1e, 0x1e, 0x73, 0xe9, 0x8f, 0x07, 0x9d, 0x36, 0x1e, - 0x96, 0x57, 0x11, 0x3e, 0x02, 0xfb, 0xff, 0xf1, 0x67, 0x33, 0xcf, 0xb8, - 0x1f, 0x1b, 0x24, 0x2b, 0x2f, 0xdb, 0x32, 0x47, 0x2b, 0x2a, 0x0b, 0xeb, - 0x43, 0x94, 0x49, 0xf2, 0x0c, 0x51, 0xae, 0x68, 0xa3, 0xd1, 0xb8, 0x70, - 0xf7, 0xb1, 0x91, 0xef, 0x23, 0x11, 0x5a, 0xff, 0xd2, 0x28, 0xb2, 0x59, - 0xdf, 0x1a, 0xcb, 0xfd, 0x3c, 0xe4, 0xed, 0x8d, 0x2c, 0xbf, 0xfe, 0x1f, - 0x40, 0xff, 0x77, 0xfc, 0x1c, 0x6f, 0x1a, 0xcb, 0xfd, 0xec, 0x66, 0x78, - 0x04, 0xb2, 0x86, 0x8b, 0xa7, 0x34, 0xea, 0xa5, 0xf6, 0x68, 0x53, 0x59, - 0x52, 0xc9, 0x70, 0xc9, 0xf7, 0xb3, 0x85, 0x63, 0xc3, 0xe0, 0x05, 0xf7, - 0x9a, 0x69, 0xa4, 0x97, 0xfd, 0x9f, 0xbe, 0xb3, 0x7e, 0x0d, 0x23, 0x73, - 0x41, 0x73, 0x4d, 0x24, 0xbc, 0xd3, 0x4d, 0x24, 0xbf, 0x9f, 0x61, 0xfa, - 0x78, 0x91, 0xb9, 0xa0, 0xa2, 0x46, 0x2b, 0x49, 0x91, 0xe7, 0x37, 0xe0, - 0x98, 0xc4, 0x62, 0x46, 0xe6, 0xce, 0xf3, 0x4d, 0x34, 0x92, 0xf7, 0x27, - 0x49, 0x1b, 0x9a, 0x0b, 0xe7, 0x2f, 0xba, 0xb2, 0xdf, 0x9a, 0x2a, 0xfa, - 0xb6, 0xd1, 0x75, 0xfc, 0x32, 0x98, 0x38, 0x56, 0x5f, 0xb8, 0x0d, 0xa3, - 0xc9, 0x65, 0xf8, 0x67, 0xbe, 0x46, 0xb2, 0xfb, 0x0b, 0x37, 0x56, 0x56, - 0x8f, 0x31, 0x85, 0x34, 0x68, 0x9a, 0x11, 0xe2, 0xff, 0xef, 0x3c, 0x09, - 0xc5, 0x88, 0x8e, 0x25, 0x97, 0xfd, 0xb4, 0xfd, 0xc3, 0x2c, 0xfd, 0x65, - 0xf3, 0xc0, 0xff, 0x59, 0x5a, 0x3d, 0xcf, 0x1d, 0x5f, 0xf6, 0x06, 0x76, - 0x1f, 0xa7, 0x8b, 0x2b, 0x0f, 0x6e, 0x3c, 0x8a, 0xfe, 0xf6, 0x6f, 0xce, - 0xf1, 0x65, 0x05, 0x34, 0xff, 0xe1, 0xf9, 0xd2, 0x4a, 0xd2, 0xa6, 0xff, - 0x43, 0x18, 0xa3, 0x72, 0xbe, 0xdb, 0xf0, 0x7e, 0xb2, 0xed, 0xba, 0xb2, - 0xa5, 0x59, 0x06, 0x4a, 0xa6, 0x73, 0xc2, 0x26, 0xbf, 0x6d, 0x1f, 0xec, - 0x0a, 0xcb, 0x9b, 0x54, 0x74, 0xb2, 0xf7, 0x27, 0xab, 0x2e, 0xfd, 0xa5, - 0x96, 0xe4, 0x46, 0xd5, 0xc7, 0x2b, 0x11, 0x55, 0xf1, 0x67, 0x55, 0xef, - 0xc1, 0x9f, 0x4b, 0x16, 0x5c, 0xfd, 0x59, 0x7e, 0xcd, 0xd7, 0x2f, 0xf0, - 0xde, 0x91, 0x3d, 0xff, 0x49, 0x40, 0x87, 0xe9, 0xd9, 0x65, 0xfc, 0x3c, - 0xd4, 0x7b, 0x06, 0xb2, 0xf7, 0x8e, 0x24, 0x95, 0xa3, 0xce, 0xe1, 0x8d, - 0xff, 0x3f, 0xdd, 0xc8, 0x81, 0xa1, 0x56, 0x58, 0x26, 0x7b, 0xc0, 0x22, - 0xbf, 0xc5, 0x9f, 0x43, 0x86, 0x2a, 0xcb, 0xe7, 0x7d, 0x0a, 0xb2, 0xc2, - 0xac, 0xbf, 0xfb, 0x03, 0xe9, 0xf6, 0x6b, 0x52, 0x2a, 0xcb, 0xc7, 0xff, - 0x24, 0xf5, 0xb4, 0x25, 0x51, 0x91, 0xbf, 0xb1, 0xa1, 0x3c, 0x5f, 0xf6, - 0x60, 0x7f, 0x9c, 0xfa, 0x0b, 0x2f, 0xfe, 0xee, 0x81, 0xcf, 0x3c, 0x3f, - 0x31, 0xac, 0xbe, 0x97, 0x66, 0xf4, 0x97, 0xff, 0x78, 0xdf, 0xff, 0xe7, - 0xf9, 0xf7, 0x16, 0x5f, 0xde, 0xc6, 0x31, 0xe0, 0xb2, 0xf3, 0x4d, 0x34, - 0x92, 0xff, 0x17, 0x7c, 0x72, 0x5b, 0x24, 0x6e, 0x68, 0x2f, 0x4f, 0xf9, - 0x04, 0x48, 0xe2, 0x6d, 0x75, 0x30, 0xa1, 0x21, 0xaf, 0x7f, 0x64, 0x6e, - 0xbf, 0xfc, 0x59, 0x41, 0x4e, 0x1d, 0x91, 0x96, 0xef, 0x2a, 0xbf, 0xe7, - 0xfc, 0xb2, 0x28, 0x4f, 0xcb, 0x2a, 0x55, 0x20, 0x64, 0x79, 0xa6, 0x73, - 0x7f, 0x9c, 0x84, 0xd1, 0x02, 0x0b, 0x2f, 0xef, 0xe7, 0xf9, 0xf7, 0x16, - 0x5f, 0xf8, 0x49, 0xd7, 0xfd, 0xf3, 0x8c, 0xd6, 0x5f, 0xb5, 0xa3, 0xef, - 0x16, 0x50, 0xcf, 0xa0, 0x44, 0x0b, 0xff, 0x7f, 0x1f, 0x93, 0x03, 0xe9, - 0x4a, 0xca, 0x93, 0xe3, 0x72, 0x3b, 0xf4, 0xc0, 0xe1, 0xc5, 0x95, 0x2b, - 0xdd, 0x30, 0x6d, 0x14, 0xfb, 0xe8, 0x72, 0x1c, 0x60, 0xd1, 0x19, 0x7a, - 0x53, 0xcb, 0x9a, 0xfe, 0x66, 0x50, 0xfa, 0xe1, 0x05, 0xfb, 0xfc, 0x3f, - 0x0a, 0xb2, 0xff, 0xfb, 0x03, 0x24, 0xfd, 0xe0, 0xc0, 0xfa, 0x82, 0xca, - 0xf8, 0xfd, 0xd8, 0x53, 0x7f, 0x49, 0x05, 0xb3, 0x1c, 0x63, 0x95, 0x97, - 0xe9, 0x66, 0xa7, 0x7a, 0xcb, 0xff, 0xa4, 0x3d, 0x29, 0x6b, 0x3b, 0xce, - 0x2c, 0xbf, 0xfb, 0xcf, 0xed, 0x4f, 0x4c, 0x26, 0x35, 0x95, 0xa4, 0x43, - 0xfe, 0x89, 0x7f, 0xff, 0xf1, 0x3b, 0x27, 0x41, 0x1f, 0xa7, 0x80, 0x73, - 0x87, 0x3c, 0xf0, 0x59, 0x78, 0x1d, 0xe2, 0xcb, 0xf3, 0x4f, 0xe9, 0x62, - 0xcb, 0xc1, 0x9e, 0x2c, 0xbf, 0xf8, 0x48, 0x67, 0x9f, 0xc7, 0xad, 0x4a, - 0xca, 0x94, 0x44, 0x61, 0x47, 0xe3, 0x97, 0xf7, 0xb2, 0x0e, 0x5f, 0xac, - 0xbd, 0xcd, 0xf8, 0xb2, 0xe9, 0xd2, 0xca, 0xe9, 0xb4, 0x8f, 0x1e, 0xbf, - 0xb3, 0x6e, 0x6e, 0x98, 0x56, 0x54, 0x9e, 0xa9, 0x12, 0x5f, 0x81, 0xa1, - 0x7c, 0x6b, 0x2a, 0x55, 0xbb, 0x0c, 0x8c, 0x27, 0x9a, 0x85, 0x83, 0x92, - 0x7e, 0xe0, 0x50, 0xa5, 0xe1, 0x7f, 0x61, 0x6d, 0x1e, 0x41, 0x7f, 0x89, - 0xda, 0xc2, 0x71, 0x56, 0x5f, 0xfd, 0x9d, 0xe0, 0x9c, 0x61, 0x4e, 0xa5, - 0x65, 0xed, 0xf8, 0x43, 0x3f, 0x66, 0x19, 0x5f, 0xfc, 0xd4, 0xe8, 0x9f, - 0x69, 0xd4, 0xc1, 0x65, 0x19, 0xfc, 0x78, 0xd2, 0xff, 0xff, 0x78, 0xda, - 0xcd, 0xc3, 0x63, 0xf7, 0x80, 0x86, 0x7d, 0xd5, 0x97, 0xff, 0x68, 0xff, - 0x13, 0xd2, 0xc7, 0x7e, 0xac, 0xa1, 0xa2, 0xab, 0x4c, 0xb5, 0x88, 0xee, - 0x78, 0x65, 0xdf, 0xf4, 0x41, 0xf1, 0xb2, 0x74, 0x2a, 0xcb, 0xf6, 0xe6, - 0xe1, 0x90, 0x56, 0x5f, 0xb6, 0x8c, 0x09, 0x89, 0x65, 0xa0, 0xb2, 0xf0, - 0x85, 0x12, 0xca, 0x81, 0xaf, 0xfc, 0x46, 0xb1, 0x1c, 0xba, 0x3b, 0x72, - 0xde, 0x2d, 0xd4, 0xbf, 0x13, 0x5e, 0xd2, 0xdf, 0x61, 0x3b, 0x8c, 0x39, - 0x7b, 0xd9, 0x5a, 0x89, 0x0b, 0x0f, 0x20, 0xc6, 0x8a, 0x72, 0xd8, 0x22, - 0x9d, 0x36, 0xd4, 0xb7, 0xa6, 0x4e, 0x32, 0xfa, 0x7c, 0x15, 0xe5, 0xea, - 0x7f, 0x2c, 0x0c, 0xa9, 0x47, 0xdc, 0x9d, 0x3f, 0xec, 0xfe, 0xa8, 0x27, - 0x27, 0xda, 0x94, 0xf2, 0x24, 0x61, 0x77, 0xc3, 0xf6, 0x71, 0x65, 0xc7, - 0xe5, 0x97, 0xd9, 0x85, 0xd5, 0x97, 0x49, 0x2c, 0xaf, 0x1e, 0x5f, 0x05, - 0xb7, 0x90, 0x5f, 0xff, 0x61, 0x37, 0xe8, 0xa7, 0xf7, 0x3d, 0x9f, 0xba, - 0xca, 0x82, 0x21, 0x06, 0x63, 0x79, 0xdd, 0xa5, 0x97, 0x7f, 0x2b, 0x2f, - 0xc1, 0xce, 0xf8, 0xd6, 0x59, 0xc6, 0x7b, 0x46, 0x38, 0x42, 0xf7, 0xff, - 0xfd, 0x9e, 0x71, 0x39, 0x82, 0xb9, 0x7f, 0x25, 0x31, 0x7a, 0x56, 0x5a, - 0x56, 0x5f, 0xff, 0xa7, 0x5e, 0x96, 0x6e, 0x02, 0x4a, 0x62, 0xf4, 0xac, - 0xbf, 0x1e, 0xa2, 0x9f, 0x69, 0x19, 0xbf, 0xb3, 0x10, 0x85, 0x41, 0x55, - 0xd7, 0xd0, 0xf1, 0x63, 0xe8, 0x90, 0xff, 0xb3, 0x6b, 0xac, 0xbf, 0xf7, - 0x7c, 0xf0, 0xec, 0x45, 0x2c, 0x59, 0x7f, 0xe7, 0x2f, 0xe3, 0x0c, 0x0f, - 0xa8, 0x2c, 0xbe, 0x32, 0xda, 0x56, 0x5e, 0xee, 0xa5, 0x65, 0xff, 0x14, - 0xb0, 0xcb, 0x19, 0x2b, 0x2f, 0x78, 0xe0, 0xb2, 0xe3, 0x1e, 0x1f, 0x6c, - 0x43, 0x9d, 0x35, 0xad, 0xd4, 0xc2, 0x74, 0x82, 0x50, 0x8e, 0xbf, 0xf8, - 0xbb, 0xcf, 0x3c, 0x23, 0x67, 0xdd, 0x59, 0x51, 0xc5, 0x3d, 0xf9, 0x18, - 0xc8, 0xd2, 0x00, 0x71, 0x7f, 0xcf, 0xbb, 0x17, 0x27, 0xcf, 0xba, 0xb2, - 0xff, 0x84, 0x98, 0x84, 0x9d, 0x3c, 0x4b, 0x2f, 0x49, 0x6c, 0xb2, 0xf8, - 0xfc, 0x6c, 0x59, 0x6f, 0x19, 0xbd, 0x88, 0x72, 0xf7, 0x3a, 0x6b, 0x2e, - 0x88, 0x0b, 0x2e, 0x90, 0xc9, 0xb4, 0x21, 0xcb, 0xfe, 0x9e, 0xf9, 0xff, - 0x7f, 0xb8, 0xb2, 0x86, 0x7c, 0x84, 0x55, 0x7f, 0xff, 0x88, 0x0e, 0x1e, - 0x01, 0xce, 0x12, 0x41, 0xcf, 0xba, 0xb2, 0xff, 0xbf, 0x8d, 0xe3, 0x08, - 0x35, 0xfa, 0xcb, 0xdb, 0x93, 0xba, 0xb2, 0xb4, 0x7c, 0x1b, 0x87, 0xd7, - 0xa2, 0x06, 0x96, 0x5e, 0xc3, 0xea, 0xcb, 0xfe, 0x3d, 0x67, 0xcd, 0xda, - 0x69, 0xa5, 0x95, 0xe3, 0xda, 0x71, 0xba, 0x94, 0x5f, 0x39, 0x2f, 0x1e, - 0x2f, 0xff, 0x8a, 0x61, 0x18, 0x4e, 0x99, 0x02, 0x19, 0xc5, 0x97, 0xff, - 0xef, 0x03, 0x8c, 0x9f, 0x83, 0xe3, 0x61, 0x1e, 0xcb, 0x28, 0x6a, 0xeb, - 0x31, 0xe4, 0x30, 0xbf, 0x61, 0x0f, 0xa3, 0x7d, 0xfc, 0xbb, 0x8a, 0x37, - 0x70, 0x0b, 0x2f, 0x7e, 0xec, 0x59, 0x76, 0x7e, 0xb2, 0xa4, 0xda, 0x60, - 0xed, 0xe7, 0xc6, 0x2c, 0xb8, 0xa1, 0xb2, 0x21, 0x49, 0x2f, 0x70, 0x7e, - 0xfa, 0x7c, 0x0d, 0x2c, 0xbf, 0xb8, 0x58, 0x32, 0x75, 0x94, 0x47, 0x9a, - 0xd1, 0x15, 0xc1, 0x35, 0x97, 0xe2, 0x9d, 0xa7, 0xe5, 0x96, 0xe0, 0x4d, - 0xf1, 0x8b, 0x54, 0xa6, 0x22, 0x70, 0x8b, 0x76, 0x0b, 0xee, 0x14, 0xec, - 0xb2, 0xff, 0xb6, 0x7f, 0x38, 0xc9, 0xc2, 0xb2, 0xb4, 0x7b, 0x44, 0x45, - 0x7f, 0xfc, 0xd6, 0x07, 0x99, 0xd8, 0xb0, 0x52, 0xcf, 0xd6, 0x56, 0x1f, - 0x99, 0x90, 0xdf, 0xf6, 0x35, 0x9a, 0x0b, 0xb8, 0x55, 0x18, 0x72, 0xff, - 0xe0, 0x70, 0x7e, 0x76, 0x7d, 0x03, 0x25, 0x97, 0xee, 0xf9, 0xcb, 0xf5, - 0x95, 0xb2, 0x2c, 0xbc, 0x8a, 0xe8, 0xb7, 0xfe, 0x7e, 0xe4, 0x4f, 0x25, - 0x31, 0x2c, 0xbf, 0xf7, 0x8f, 0x04, 0xc8, 0x9a, 0x9f, 0x96, 0x58, 0xf4, - 0x8a, 0xef, 0x18, 0x70, 0xf6, 0xe0, 0x85, 0x51, 0x82, 0x2a, 0x09, 0xec, - 0x86, 0x38, 0x0f, 0xcd, 0x6f, 0xf4, 0x75, 0xc1, 0x96, 0x79, 0xb0, 0xb2, - 0xec, 0x25, 0x97, 0xff, 0xbb, 0x24, 0x1c, 0xef, 0x8f, 0x5f, 0x85, 0x65, - 0xf1, 0xf3, 0xc6, 0xb2, 0xf6, 0x7d, 0xd5, 0x94, 0x33, 0x7c, 0xc2, 0x1b, - 0xf8, 0xcb, 0x3b, 0xe3, 0x59, 0x69, 0xc3, 0xcc, 0xe1, 0x0d, 0x0d, 0x30, - 0xfc, 0x15, 0x04, 0x35, 0x2f, 0xfd, 0xe3, 0xdf, 0xec, 0xd6, 0x8f, 0x7a, - 0xcb, 0xfd, 0x2c, 0xe7, 0x1c, 0xb6, 0x59, 0x7f, 0xdf, 0xf2, 0x07, 0xe7, - 0x3d, 0x96, 0x5f, 0x73, 0x0b, 0xab, 0x2f, 0x6e, 0x4e, 0x96, 0x51, 0x9f, - 0xd3, 0x9d, 0x00, 0x86, 0xf7, 0x9f, 0xab, 0x2f, 0x75, 0xf7, 0x56, 0x5f, - 0xee, 0xf8, 0xdb, 0xfe, 0xe6, 0xb2, 0xff, 0x0b, 0xe9, 0x80, 0x1f, 0x4b, - 0x2c, 0x4b, 0x28, 0xcf, 0x10, 0x06, 0x97, 0x09, 0x8b, 0x2b, 0x65, 0x71, - 0x03, 0x8d, 0x2e, 0x23, 0x5f, 0x21, 0x14, 0x2b, 0x78, 0x5b, 0xd1, 0xc0, - 0x0f, 0xc7, 0xbc, 0x88, 0x43, 0x7e, 0x17, 0xf3, 0x8b, 0x8b, 0x2f, 0xbc, - 0x6f, 0x05, 0x97, 0x86, 0xf0, 0x59, 0x52, 0x6f, 0x70, 0x86, 0xc0, 0x94, - 0x43, 0xfe, 0xd1, 0x7f, 0xfa, 0x31, 0x3f, 0x67, 0x40, 0x86, 0xa6, 0x0b, - 0x2f, 0xd3, 0xdf, 0x4c, 0x4b, 0x2e, 0xef, 0x16, 0x5f, 0x9c, 0x56, 0xa7, - 0x4b, 0x28, 0xd1, 0x67, 0xe4, 0xb7, 0x28, 0x21, 0x7b, 0xfc, 0x65, 0xd8, - 0x66, 0xb1, 0x65, 0xff, 0xc6, 0x12, 0xc6, 0x4c, 0x43, 0x9d, 0x96, 0x5f, - 0xfd, 0x02, 0xc6, 0x39, 0x67, 0x9d, 0x8b, 0x2f, 0xf6, 0x76, 0x77, 0x99, - 0x7e, 0xb2, 0xff, 0xfb, 0xf6, 0x3c, 0x21, 0x9f, 0x89, 0xdd, 0xb8, 0x05, - 0x94, 0x14, 0x44, 0xe8, 0xd2, 0xff, 0x6a, 0x4c, 0x64, 0xf0, 0x59, 0x7f, - 0xd9, 0xbe, 0x73, 0xef, 0xba, 0xda, 0x96, 0x5f, 0xc0, 0x9e, 0xf3, 0x0a, - 0x31, 0xf9, 0xf8, 0xc6, 0xfc, 0x1f, 0x7c, 0xfb, 0xab, 0x2f, 0xf7, 0x26, - 0x02, 0x8b, 0x9b, 0xab, 0x2f, 0xfc, 0x32, 0x07, 0x79, 0x25, 0x3c, 0x59, - 0x52, 0x7e, 0xa6, 0x71, 0x50, 0x55, 0xb6, 0x13, 0x1f, 0x91, 0x0e, 0x19, - 0x45, 0x09, 0x60, 0x22, 0xef, 0x85, 0x0d, 0xff, 0x9f, 0x59, 0xbf, 0x07, - 0xa2, 0x69, 0x65, 0xff, 0xf8, 0x05, 0x0c, 0x2e, 0x9c, 0x7e, 0x74, 0x9e, - 0x0b, 0x2f, 0xf4, 0xff, 0x3e, 0xcf, 0xa0, 0xb2, 0x8d, 0x11, 0x24, 0xab, - 0x5f, 0xa3, 0xab, 0xb0, 0xc9, 0xbf, 0x46, 0x89, 0xe2, 0x75, 0x97, 0xfd, - 0xe7, 0x2c, 0x8a, 0x13, 0xf2, 0xcb, 0xff, 0xbc, 0x7a, 0xce, 0x16, 0x6f, - 0xf1, 0xac, 0xbf, 0x16, 0x70, 0x11, 0xb0, 0xff, 0x62, 0x39, 0xbf, 0x68, - 0x2e, 0xe1, 0x54, 0x40, 0xcb, 0xf9, 0xf6, 0xfd, 0xc8, 0x68, 0xac, 0x1e, - 0xbd, 0x9c, 0x06, 0x1f, 0x84, 0x79, 0xa5, 0xef, 0x3c, 0x23, 0x23, 0x94, - 0xa1, 0x61, 0x7f, 0xde, 0x3c, 0x2c, 0x08, 0xf1, 0x65, 0x31, 0x51, 0x31, - 0x14, 0xf6, 0x38, 0xdd, 0xe7, 0x57, 0xba, 0x09, 0x59, 0x7d, 0xfe, 0xb9, - 0xc5, 0x97, 0xfb, 0xfe, 0x9e, 0xcc, 0x90, 0xac, 0xaf, 0x8f, 0xd5, 0xc7, - 0x08, 0x92, 0xfc, 0x38, 0xe5, 0xc7, 0x1c, 0xac, 0xbf, 0x83, 0xf7, 0xbe, - 0x7d, 0xd5, 0x97, 0xfa, 0x74, 0xfa, 0xee, 0x05, 0x65, 0xff, 0xc6, 0x38, - 0xd9, 0x9f, 0x7e, 0xe5, 0xd5, 0x96, 0x84, 0x0f, 0xe3, 0x86, 0x55, 0x28, - 0xd3, 0x78, 0x55, 0xd4, 0xae, 0xcd, 0x64, 0xb8, 0x63, 0x85, 0xdb, 0x97, - 0x02, 0x30, 0x5b, 0x8e, 0x0b, 0x2f, 0xef, 0xba, 0x3c, 0xc1, 0x56, 0x50, - 0xcf, 0x17, 0x05, 0xad, 0x2b, 0x2f, 0xa4, 0x8f, 0xf5, 0x96, 0xcf, 0x1b, - 0x1e, 0x88, 0x5f, 0xed, 0x13, 0xb4, 0x27, 0xcd, 0x2c, 0xbb, 0xfd, 0xeb, - 0x28, 0x67, 0xa3, 0xe3, 0x7b, 0xff, 0xc7, 0xf8, 0x7c, 0x7e, 0x29, 0xcd, - 0x62, 0xcb, 0xfe, 0xf8, 0x73, 0xc3, 0x1f, 0xa5, 0x65, 0xe7, 0xd6, 0xca, - 0x8c, 0x15, 0x7f, 0xe7, 0xd6, 0x17, 0xfc, 0xef, 0x3e, 0x59, 0x7f, 0xff, - 0xff, 0x67, 0x7c, 0xe4, 0x2c, 0x6e, 0x69, 0xdf, 0xbb, 0xf0, 0x71, 0xb1, - 0x8e, 0x5f, 0x41, 0x71, 0x05, 0xaf, 0xfc, 0xee, 0x28, 0x5c, 0x71, 0x85, - 0x69, 0x71, 0x05, 0xaf, 0xfe, 0xf1, 0xf8, 0xe4, 0xbb, 0x18, 0x56, 0x97, - 0x10, 0x5a, 0xff, 0x49, 0x97, 0x63, 0x0a, 0xd2, 0xe2, 0x0b, 0x5f, 0xcc, - 0xc1, 0xc6, 0x15, 0xa5, 0xc4, 0x16, 0xbf, 0xff, 0xe7, 0x22, 0x36, 0x46, - 0xe0, 0x7c, 0x7a, 0x31, 0x76, 0xc6, 0x97, 0x10, 0x5a, 0xef, 0xa3, 0x0d, - 0x39, 0xc0, 0xa8, 0x69, 0x51, 0xd0, 0xc8, 0xfe, 0xa5, 0x56, 0x7f, 0x90, - 0x0a, 0x51, 0xbd, 0xfe, 0x39, 0x17, 0x9d, 0xe7, 0xcb, 0x2f, 0x9f, 0x5f, - 0xba, 0xcb, 0xff, 0xbc, 0x7e, 0x39, 0x2e, 0xc6, 0x15, 0xa5, 0xc4, 0x16, - 0xbf, 0xe8, 0xb8, 0x13, 0xda, 0x30, 0xad, 0x2e, 0x20, 0xb5, 0xfb, 0xbc, - 0x96, 0x46, 0x0a, 0x28, 0x44, 0x54, 0xbf, 0xfd, 0x18, 0x3e, 0x30, 0xcf, - 0x79, 0x18, 0x56, 0x97, 0x10, 0x5a, 0xff, 0xff, 0xe2, 0x23, 0x64, 0x60, - 0x64, 0x6e, 0x07, 0xc7, 0xa3, 0x17, 0x6c, 0x69, 0x71, 0x05, 0xab, 0x13, - 0x25, 0xf9, 0x0d, 0xd7, 0xaf, 0xfb, 0xc7, 0xa3, 0x17, 0x6c, 0x69, 0x71, - 0x05, 0xaf, 0xff, 0x9d, 0xfe, 0x87, 0x3c, 0x63, 0x1c, 0xea, 0x52, 0x5f, - 0xfb, 0x21, 0x00, 0x73, 0x45, 0xb4, 0x7a, 0xe2, 0x0b, 0x50, 0x51, 0xcf, - 0xa4, 0x8e, 0x27, 0xdf, 0xf8, 0x27, 0xae, 0xbf, 0xfc, 0x8c, 0xd2, 0xe2, - 0x0b, 0x5f, 0xde, 0x3f, 0x78, 0xff, 0x54, 0x01, 0x6b, 0xf6, 0x7f, 0x18, - 0x56, 0x97, 0x10, 0x5a, 0xec, 0xe8, 0x4f, 0xd7, 0xc7, 0x75, 0xf2, 0x3c, - 0xb9, 0x0c, 0x5b, 0xf9, 0x98, 0x38, 0xc2, 0xb4, 0xb8, 0x82, 0xd7, 0xfe, - 0x0f, 0x8f, 0x46, 0x2e, 0xd8, 0xd2, 0xe2, 0x0b, 0x5d, 0x91, 0x9d, 0x11, - 0xbc, 0x3f, 0xbf, 0xc0, 0x36, 0x39, 0x7d, 0x05, 0xc4, 0x16, 0xbf, 0xf6, - 0x1e, 0xfc, 0x2c, 0x1b, 0xc1, 0x71, 0x05, 0x98, 0x78, 0x14, 0x35, 0xe0, - 0xdd, 0x1b, 0xfe, 0x6a, 0x51, 0xf4, 0x72, 0x31, 0x7e, 0xc6, 0x44, 0x08, - 0x5b, 0x34, 0xdf, 0x77, 0xf2, 0xa8, 0x82, 0xcd, 0xd1, 0x19, 0x73, 0x85, - 0x65, 0xb8, 0x36, 0x50, 0xc8, 0x4e, 0x7f, 0xa4, 0xc9, 0x74, 0xe2, 0xe8, - 0x6e, 0x2c, 0xbd, 0x30, 0xdc, 0x59, 0x52, 0x6e, 0x48, 0x6a, 0xb6, 0x65, - 0xfa, 0x8d, 0x4b, 0xe7, 0x7d, 0x11, 0x7f, 0x4a, 0x61, 0x28, 0x45, 0x5f, - 0xfd, 0x90, 0x2c, 0xef, 0x8f, 0x3e, 0xea, 0xcb, 0xfd, 0x3e, 0x36, 0xbe, - 0x87, 0x16, 0x5f, 0xa7, 0xbc, 0xf1, 0xac, 0xbf, 0xf6, 0xb0, 0x5f, 0x63, - 0x01, 0xad, 0x96, 0x5f, 0xf7, 0xc5, 0x86, 0xcc, 0x2f, 0xd6, 0x5f, 0xff, - 0xff, 0x9e, 0x22, 0x76, 0xb3, 0x79, 0xf3, 0xd9, 0xfc, 0x7e, 0x77, 0x8e, - 0xcc, 0xfb, 0xab, 0x2f, 0xd0, 0xef, 0x30, 0x96, 0x5f, 0xfe, 0x70, 0xc9, - 0x0c, 0xb3, 0x7e, 0x8f, 0x8b, 0x2f, 0xfd, 0xc8, 0xa0, 0x01, 0xc5, 0x00, - 0x0d, 0x65, 0x6c, 0xa8, 0x46, 0x06, 0xa3, 0x27, 0xc4, 0x11, 0x4e, 0x3e, - 0x84, 0x4f, 0x89, 0xfa, 0x93, 0x7f, 0xd9, 0xd6, 0xb0, 0x87, 0xe9, 0x59, - 0x7f, 0xf4, 0xfd, 0x01, 0x19, 0xce, 0xcf, 0xcd, 0x2c, 0xbf, 0x43, 0x73, - 0x70, 0x1b, 0x2c, 0xb7, 0x8c, 0xfe, 0x5d, 0x26, 0xfb, 0xa5, 0x2c, 0x59, - 0x7e, 0xd4, 0xfd, 0x0e, 0x2c, 0xad, 0x93, 0x21, 0x84, 0x2c, 0x4c, 0x9b, - 0x44, 0x37, 0x98, 0xfd, 0x59, 0x7f, 0xe0, 0x72, 0x3f, 0x35, 0xfb, 0x0f, - 0x8b, 0x2f, 0xfe, 0xdf, 0xbe, 0x78, 0xfe, 0xfa, 0x19, 0xd5, 0x95, 0xfa, - 0x23, 0x04, 0x43, 0xbf, 0xdf, 0x83, 0xa5, 0x3d, 0xe2, 0xca, 0x82, 0x60, - 0x3f, 0xc2, 0xa3, 0x84, 0xb5, 0x2a, 0xc5, 0xb2, 0x39, 0xb7, 0x8d, 0x12, - 0xf3, 0x0f, 0x8b, 0x2f, 0xd0, 0x9d, 0xef, 0xe5, 0x95, 0xb1, 0xe2, 0xb8, - 0xe5, 0xff, 0xd9, 0xf7, 0x78, 0x65, 0x3f, 0xbb, 0x16, 0x5f, 0xff, 0xb3, - 0xbe, 0x38, 0xc2, 0xbc, 0x62, 0xc6, 0x9f, 0xf5, 0x97, 0xfb, 0x8f, 0xe3, - 0xe4, 0xc1, 0x65, 0xff, 0x9b, 0x78, 0x45, 0x82, 0x49, 0x7e, 0xb2, 0xff, - 0x67, 0x8f, 0x86, 0x08, 0x2c, 0xad, 0x93, 0x43, 0x24, 0x4e, 0xad, 0x00, - 0xcb, 0x71, 0x06, 0xfb, 0xee, 0x4f, 0xcb, 0x2f, 0xfd, 0x0c, 0xfa, 0x1a, - 0x9e, 0xbf, 0x56, 0x5e, 0x64, 0xc1, 0x65, 0xfb, 0x07, 0xe0, 0x34, 0xb2, - 0xa3, 0x22, 0xa2, 0x22, 0x47, 0x3f, 0x21, 0xcb, 0xfe, 0x0f, 0x8c, 0x7e, - 0x9e, 0xf1, 0x65, 0xe1, 0xbf, 0x56, 0x5f, 0xff, 0xe2, 0x9f, 0xbb, 0xdc, - 0x8a, 0x19, 0xb7, 0xd2, 0x53, 0xc5, 0x97, 0xfe, 0xe4, 0x94, 0xc3, 0xbe, - 0xc2, 0x59, 0x5a, 0x4c, 0xc5, 0x87, 0xce, 0x73, 0xf8, 0xe7, 0x18, 0xaf, - 0x1c, 0x7e, 0x2c, 0xbf, 0xb8, 0x63, 0xc6, 0xb8, 0xb2, 0xfe, 0x7d, 0x38, - 0xcf, 0x16, 0x5d, 0xaf, 0xd6, 0x5d, 0x8d, 0x2c, 0xa7, 0x35, 0xfd, 0x18, - 0xb7, 0xd2, 0x8c, 0x6f, 0x87, 0xbc, 0x5d, 0xd5, 0xab, 0xe6, 0xb3, 0xce, - 0xb2, 0xf8, 0x5d, 0x1b, 0x4b, 0x2f, 0xdb, 0x3f, 0xa7, 0x75, 0x65, 0x47, - 0x47, 0x9f, 0x84, 0x97, 0xf0, 0xbe, 0x9f, 0xbb, 0x8b, 0x2f, 0xf1, 0x60, - 0xfd, 0x2d, 0x12, 0xca, 0x93, 0xe1, 0xd1, 0x7d, 0x4a, 0xef, 0xd6, 0x46, - 0xca, 0x72, 0x89, 0xde, 0x31, 0x1f, 0xd1, 0x09, 0xc4, 0x10, 0x87, 0xbe, - 0x28, 0xb9, 0x8b, 0x2f, 0xd0, 0xcd, 0xbb, 0x2b, 0x2f, 0xfe, 0x29, 0xd9, - 0x9e, 0x3d, 0x69, 0xe0, 0xb2, 0x96, 0x51, 0xa2, 0x66, 0x22, 0x22, 0x28, - 0x8f, 0x44, 0xbf, 0xdb, 0x6d, 0x25, 0xf6, 0x75, 0x65, 0xf8, 0x79, 0x84, - 0x15, 0x97, 0x4c, 0x4b, 0x2b, 0x63, 0x7b, 0xf9, 0x35, 0x62, 0x35, 0x0c, - 0xfc, 0x0e, 0x57, 0xff, 0x68, 0x81, 0xf7, 0x4b, 0x36, 0x72, 0x59, 0x7f, - 0xff, 0x0f, 0xc0, 0xd8, 0xf3, 0x5a, 0xc9, 0xfa, 0x06, 0xc5, 0x97, 0xf9, - 0xe2, 0x20, 0x74, 0xa5, 0x65, 0xfe, 0x71, 0x89, 0xde, 0x9b, 0x4b, 0x2f, - 0x3e, 0xb6, 0x59, 0x77, 0xf2, 0xb2, 0xf8, 0xe5, 0x98, 0xb2, 0x96, 0x5f, - 0xce, 0x2f, 0x67, 0x5f, 0xac, 0xa1, 0x9b, 0xa2, 0x0b, 0xbf, 0xff, 0xd2, - 0x63, 0x29, 0x00, 0x7b, 0x81, 0x1c, 0x84, 0x46, 0x2c, 0xbb, 0xf9, 0x59, - 0x74, 0x8a, 0xb2, 0xff, 0xb3, 0xbc, 0x96, 0x09, 0x91, 0x2c, 0xbf, 0xdc, - 0xcf, 0x1f, 0x01, 0xb2, 0xcb, 0x9a, 0x69, 0x25, 0xff, 0x16, 0x6f, 0x78, - 0x70, 0xc6, 0xb2, 0x82, 0x9f, 0xe1, 0x8e, 0xe8, 0x5d, 0x8b, 0x5e, 0x20, - 0xfd, 0x81, 0xb6, 0x2e, 0x42, 0xfc, 0x3a, 0x68, 0xd0, 0x41, 0x9b, 0xcd, - 0x34, 0xd2, 0x4b, 0x31, 0x23, 0x73, 0x41, 0x7d, 0x13, 0xbe, 0x92, 0x37, - 0x47, 0x08, 0x90, 0xbe, 0xad, 0x97, 0x0b, 0x42, 0x89, 0xf2, 0xe7, 0x8c, - 0x9e, 0x58, 0x95, 0xfd, 0xd9, 0xcf, 0x66, 0xcb, 0x2e, 0xc6, 0x2c, 0xaf, - 0x8f, 0x0f, 0x85, 0xb5, 0x2e, 0xe6, 0x3a, 0x12, 0x8a, 0x86, 0x91, 0x92, - 0xfa, 0xc5, 0x94, 0x0f, 0xf4, 0xad, 0x93, 0x97, 0x81, 0xa8, 0xc9, 0x19, - 0x0d, 0xcf, 0x52, 0x2b, 0xde, 0x99, 0x2b, 0xfb, 0x59, 0x4e, 0xb5, 0x72, - 0x79, 0xeb, 0xb1, 0xf4, 0x02, 0x71, 0x6f, 0x7c, 0x26, 0x2e, 0x13, 0xab, - 0x2f, 0xec, 0xff, 0xf3, 0xd4, 0x16, 0x5c, 0x1c, 0x59, 0x43, 0x3c, 0x57, - 0x2f, 0xbb, 0x1a, 0x59, 0x74, 0xf5, 0x65, 0x6c, 0x6b, 0x42, 0x2f, 0x66, - 0x2c, 0xac, 0x36, 0x7d, 0x22, 0xbf, 0xd0, 0x32, 0xc6, 0x03, 0xcb, 0x2f, - 0xfd, 0x9d, 0xe7, 0x8d, 0x8f, 0x9a, 0x59, 0x52, 0x7d, 0xe4, 0x65, 0x7e, - 0xc8, 0xd9, 0xff, 0x56, 0x5f, 0x46, 0xec, 0xef, 0x59, 0x7e, 0xc0, 0xcb, - 0x0d, 0x65, 0xf6, 0x33, 0x3a, 0xb2, 0xff, 0xde, 0x3d, 0xfa, 0xc8, 0xb3, - 0xee, 0xac, 0xb9, 0xc5, 0x59, 0x5a, 0x3d, 0x9e, 0xa1, 0x5f, 0x3b, 0x1e, - 0x25, 0x97, 0xe3, 0x96, 0xa6, 0x25, 0x97, 0xcf, 0xdf, 0xb8, 0xb2, 0x8c, - 0xf2, 0xc8, 0xa2, 0xff, 0xff, 0xcf, 0x10, 0x20, 0x00, 0xc6, 0xf1, 0xf7, - 0xb3, 0xf4, 0xed, 0x3c, 0x59, 0x7e, 0x27, 0xdd, 0xda, 0x25, 0x97, 0xd1, - 0x72, 0x7a, 0xb2, 0x82, 0x8c, 0x08, 0x9c, 0x40, 0x59, 0x7f, 0xff, 0x89, - 0xdf, 0x7c, 0xc3, 0xd3, 0xad, 0xa7, 0xbc, 0x78, 0x2c, 0xbf, 0xa4, 0x3d, - 0xe3, 0xfc, 0xb2, 0xb7, 0x51, 0x1e, 0x4c, 0x37, 0x8f, 0xe8, 0x96, 0x5f, - 0x01, 0xb6, 0xc3, 0x59, 0x7e, 0xcc, 0xd8, 0x1c, 0x59, 0x7d, 0xa2, 0x3f, - 0xd6, 0x5f, 0x18, 0x5f, 0x4b, 0x2e, 0x9f, 0xd6, 0x5f, 0x03, 0x60, 0x37, - 0xf1, 0xb8, 0x10, 0x86, 0x8d, 0x12, 0xfe, 0x5a, 0xb7, 0xeb, 0x2e, 0xc0, - 0xac, 0xbf, 0x66, 0xb4, 0x7a, 0x59, 0x5b, 0xa7, 0x9f, 0xe1, 0x2f, 0xc5, - 0xaf, 0xcf, 0x3f, 0x60, 0x56, 0x5f, 0xf7, 0x9f, 0x9e, 0x3d, 0x9c, 0x96, - 0x5f, 0x3e, 0xc2, 0x41, 0x65, 0x36, 0x17, 0xbe, 0xe3, 0xb5, 0xd9, 0x84, - 0x0c, 0x21, 0x1e, 0x32, 0x0c, 0x2a, 0x14, 0x9b, 0xe2, 0x63, 0x7a, 0xdd, - 0x22, 0x89, 0xb7, 0x50, 0xe0, 0x78, 0x60, 0x7e, 0x4a, 0xdb, 0x1e, 0xe1, - 0x37, 0x61, 0x7e, 0x07, 0x16, 0x8c, 0xc4, 0x27, 0xdc, 0x38, 0xbf, 0x88, - 0x03, 0x63, 0xc1, 0x65, 0xe3, 0xd8, 0x0b, 0x2f, 0xd3, 0xf9, 0xfd, 0x8b, - 0x2e, 0x60, 0x16, 0x5c, 0xcd, 0xeb, 0x2c, 0x39, 0x3d, 0x83, 0x28, 0x21, - 0x7b, 0xff, 0xd0, 0x29, 0x8c, 0xc2, 0x91, 0x7a, 0x7b, 0x2c, 0xbf, 0x3b, - 0x33, 0xcc, 0x59, 0x5f, 0x1f, 0x9f, 0x53, 0x2f, 0x77, 0x52, 0xb2, 0xfc, - 0x59, 0xb0, 0x90, 0x59, 0x7f, 0x77, 0x7e, 0x16, 0x0d, 0x65, 0x44, 0x7a, - 0xfc, 0x29, 0xbf, 0xb3, 0x99, 0x03, 0xd2, 0xcb, 0x6f, 0x59, 0x7f, 0xda, - 0xf3, 0x88, 0xfe, 0x3f, 0xd6, 0x5b, 0xd2, 0x79, 0xa6, 0x27, 0x7b, 0x90, - 0xd9, 0x65, 0x61, 0xe2, 0x08, 0x4d, 0x52, 0xab, 0x7e, 0x05, 0xb8, 0xea, - 0x70, 0xa5, 0x61, 0x1b, 0xb9, 0x91, 0x1f, 0x61, 0x7b, 0x79, 0xe3, 0x08, - 0xb2, 0xf4, 0x47, 0xe5, 0x97, 0xf4, 0xe9, 0xe2, 0x3f, 0x2c, 0xbf, 0x70, - 0x72, 0x5b, 0x39, 0xe5, 0x70, 0x76, 0xfe, 0xcd, 0xa7, 0xde, 0x95, 0x97, - 0xec, 0x8b, 0xd8, 0x4b, 0x2f, 0xe9, 0xc1, 0x08, 0x1f, 0xac, 0xad, 0x22, - 0x0b, 0xc5, 0xa4, 0x4f, 0x79, 0x92, 0xc5, 0x95, 0xf2, 0x61, 0x7a, 0x86, - 0x03, 0x0b, 0xef, 0xff, 0x8a, 0x11, 0xb0, 0x67, 0xcf, 0xba, 0x07, 0xe2, - 0xcb, 0xf7, 0x80, 0x53, 0xbd, 0x65, 0xfc, 0x4f, 0xd8, 0x48, 0xab, 0x2a, - 0x4f, 0x58, 0x42, 0x9b, 0xec, 0xcf, 0xa0, 0xb2, 0xf7, 0x0e, 0x25, 0x96, - 0x14, 0x8d, 0xff, 0x08, 0xaf, 0xfb, 0x47, 0x3d, 0xcd, 0xee, 0x35, 0x97, - 0xfe, 0xd8, 0xa7, 0xe3, 0x19, 0x4e, 0xcb, 0x2f, 0xf9, 0xd9, 0x3e, 0x7e, - 0x4e, 0xea, 0xca, 0x73, 0xf9, 0x23, 0xfa, 0xd2, 0x3e, 0x7c, 0x4e, 0x50, - 0xb2, 0xbf, 0x46, 0xf4, 0xea, 0x0b, 0x2a, 0x32, 0xe0, 0x5c, 0xc6, 0xe9, - 0x86, 0xe1, 0x85, 0x43, 0xc6, 0xb4, 0xd1, 0xa5, 0xe6, 0x72, 0x56, 0x5f, - 0xdb, 0x45, 0x08, 0xef, 0x5b, 0x2c, 0xbe, 0x66, 0x3f, 0x96, 0x5b, 0x65, - 0x97, 0xee, 0xf8, 0xd9, 0xa5, 0x95, 0x03, 0x75, 0xa1, 0x2b, 0xde, 0x19, - 0xac, 0xbf, 0x7b, 0x0e, 0x1c, 0x59, 0x7f, 0xfd, 0xe9, 0x11, 0xbf, 0x49, - 0xff, 0xe7, 0x27, 0xf4, 0x97, 0xec, 0xeb, 0x81, 0xb6, 0xb2, 0xfd, 0xa0, - 0xbb, 0x85, 0x71, 0x02, 0x2f, 0x72, 0x7e, 0x59, 0x7c, 0x7c, 0xd4, 0x16, + 0xdf, 0x72, 0x26, 0x3d, 0xeb, 0x2f, 0x30, 0x1e, 0x59, 0x7f, 0xbd, 0x0c, + 0x28, 0xfa, 0x4b, 0x2f, 0x6b, 0x58, 0xb2, 0xf7, 0xd2, 0xe2, 0xcb, 0xbc, + 0x7c, 0x37, 0x4d, 0x0e, 0x5e, 0x86, 0xa4, 0xb2, 0xff, 0xb3, 0xbc, 0xd6, + 0x72, 0x3e, 0x59, 0x41, 0x3d, 0x63, 0x1d, 0xbf, 0xf4, 0x34, 0x26, 0xdc, + 0xc6, 0x46, 0xea, 0xcb, 0x9d, 0xa5, 0x95, 0xb2, 0x71, 0x23, 0x1d, 0x0b, + 0x6f, 0xa1, 0x07, 0xd2, 0x1d, 0xe8, 0x97, 0x73, 0xe5, 0x97, 0x67, 0x16, + 0x5d, 0xb9, 0xa5, 0x95, 0x0a, 0x9d, 0x72, 0x3c, 0xd3, 0x60, 0x10, 0x63, + 0x70, 0x5a, 0xfe, 0x3c, 0xf7, 0xd2, 0xe2, 0xcb, 0xff, 0x00, 0x24, 0xfc, + 0xe0, 0x20, 0x2b, 0x2f, 0xfe, 0xf3, 0x9f, 0x65, 0x82, 0x96, 0x7e, 0xb2, + 0xf7, 0xb1, 0xa5, 0x97, 0xf9, 0x87, 0xcf, 0x1c, 0x1a, 0xcb, 0xf1, 0x4d, + 0x1e, 0x85, 0x94, 0xb2, 0xec, 0x1a, 0xcb, 0xbf, 0x7f, 0x8d, 0x0e, 0xf0, + 0xbb, 0xf6, 0x7b, 0xe7, 0xdd, 0x59, 0x7d, 0xff, 0x20, 0x96, 0x5f, 0xed, + 0x79, 0xcf, 0x87, 0xbd, 0x65, 0xc2, 0x9a, 0xca, 0x83, 0xed, 0xc2, 0x27, + 0x34, 0xbf, 0xfb, 0xd8, 0x44, 0xfd, 0x9d, 0x28, 0x62, 0xcb, 0xf3, 0xf4, + 0x0e, 0x22, 0xca, 0x1a, 0xaa, 0x6c, 0x2e, 0x33, 0xed, 0xd4, 0x46, 0x0e, + 0xb9, 0x8f, 0xe9, 0x04, 0x61, 0xc8, 0x4c, 0x74, 0xb0, 0x08, 0x97, 0xf3, + 0x51, 0xad, 0x3c, 0xcb, 0x2f, 0xf8, 0x70, 0x11, 0x3f, 0xfe, 0x3e, 0x59, + 0x7f, 0xcf, 0xac, 0x61, 0xcf, 0xc3, 0x16, 0x50, 0x4f, 0xdc, 0x07, 0xb7, + 0xef, 0x86, 0x33, 0xe2, 0xcb, 0xda, 0xce, 0x2c, 0xa8, 0x3c, 0x5c, 0x29, + 0xbe, 0xec, 0x7d, 0x25, 0x97, 0xf1, 0xff, 0x3e, 0x47, 0xfa, 0xcb, 0xb3, + 0xf5, 0x95, 0xb1, 0xf5, 0xe8, 0x8c, 0x8c, 0x6f, 0xfe, 0xf3, 0x9f, 0x73, + 0x5f, 0xb1, 0xe4, 0xb2, 0xf0, 0xfc, 0x6b, 0x28, 0xcf, 0x80, 0x08, 0x97, + 0xc4, 0x51, 0xb2, 0xcb, 0xdd, 0xc1, 0x16, 0x54, 0x2a, 0x86, 0xc8, 0x53, + 0x79, 0x9d, 0xe1, 0x17, 0xc8, 0x47, 0x80, 0x87, 0x70, 0x86, 0xfe, 0x28, + 0x94, 0x6f, 0x85, 0x97, 0x47, 0xeb, 0x2f, 0x80, 0x7b, 0x8c, 0x59, 0x7f, + 0x6f, 0xd3, 0xc1, 0xcf, 0xac, 0xb8, 0xf6, 0x59, 0x6c, 0x59, 0x46, 0x8c, + 0x19, 0x8b, 0x74, 0x2e, 0xc2, 0x57, 0x31, 0x21, 0x7b, 0xf6, 0x7b, 0x79, + 0xe9, 0x65, 0xec, 0xd4, 0x2c, 0xbe, 0x99, 0xa8, 0x15, 0x65, 0xf6, 0xdd, + 0xc0, 0xac, 0xbf, 0xce, 0x28, 0x9e, 0xf4, 0x49, 0x65, 0x42, 0x26, 0x30, + 0x6d, 0xc9, 0x67, 0xc8, 0xef, 0xfd, 0xec, 0x18, 0xcf, 0xec, 0xfb, 0xab, + 0x2f, 0xe9, 0xc3, 0x11, 0xf5, 0xa5, 0x95, 0xb1, 0xf8, 0x8d, 0x02, 0xff, + 0x0f, 0x3b, 0xe8, 0x20, 0xac, 0xbe, 0x78, 0x39, 0xf5, 0x97, 0xfc, 0x39, + 0xd9, 0xc8, 0xcd, 0x7e, 0xb2, 0xf7, 0x84, 0xe2, 0xca, 0xc3, 0xd9, 0x73, + 0xbb, 0xfc, 0x63, 0xe9, 0x3f, 0xdc, 0x59, 0x7f, 0xbb, 0xc3, 0xfb, 0x3e, + 0xea, 0xcb, 0xb6, 0x85, 0x95, 0x07, 0xfe, 0xe6, 0x60, 0x35, 0xa8, 0x4e, + 0x77, 0x62, 0x31, 0x99, 0x79, 0xf0, 0xa1, 0x3f, 0x53, 0xd3, 0xa8, 0x78, + 0x9e, 0x5e, 0xa7, 0xb2, 0x66, 0xd5, 0x45, 0x6d, 0x65, 0x91, 0x1c, 0x86, + 0xcd, 0x52, 0x86, 0x78, 0xe3, 0x36, 0xc9, 0x4b, 0xc2, 0xc2, 0x60, 0x31, + 0xa2, 0xfd, 0x1a, 0x81, 0xca, 0x10, 0x9a, 0x34, 0x1d, 0x4a, 0x2f, 0x64, + 0x78, 0x7e, 0x97, 0x9e, 0xf2, 0xc1, 0xff, 0x8f, 0xe4, 0xa5, 0x63, 0xf2, + 0x57, 0xcf, 0x65, 0x92, 0x82, 0x33, 0x6d, 0xea, 0xb3, 0xf0, 0xc1, 0x12, + 0x52, 0x5d, 0xc1, 0x85, 0x97, 0xce, 0x18, 0xd2, 0xca, 0x68, 0xdb, 0xee, + 0x0b, 0x5c, 0xd6, 0x2c, 0xbf, 0xf1, 0xc7, 0x7c, 0xe2, 0xe1, 0x7e, 0xb2, + 0xff, 0xf3, 0xea, 0x73, 0xf6, 0x77, 0x7e, 0x09, 0x92, 0xca, 0x34, 0x51, + 0xf8, 0x5f, 0x79, 0xf5, 0xf7, 0x75, 0x92, 0x59, 0x7f, 0x67, 0x60, 0xd9, + 0xc5, 0x97, 0xf8, 0x71, 0x2c, 0xd6, 0x71, 0x65, 0xff, 0xf6, 0x6b, 0x40, + 0x96, 0xb2, 0x3e, 0x91, 0xb1, 0x65, 0xee, 0x09, 0xc5, 0x97, 0xff, 0xd9, + 0xf4, 0xba, 0x0d, 0x9b, 0xf9, 0xd8, 0x7c, 0x59, 0x7f, 0xee, 0xc3, 0x39, + 0x07, 0xb3, 0xf5, 0x65, 0xfc, 0xfb, 0xaf, 0x9f, 0x75, 0x65, 0x7c, 0x7d, + 0xde, 0x3e, 0xbf, 0xcf, 0x83, 0x3f, 0x89, 0xd6, 0x56, 0x1e, 0xa0, 0x08, + 0xef, 0xa3, 0xb2, 0x75, 0x97, 0xda, 0xdc, 0x82, 0x59, 0x62, 0x59, 0x50, + 0x6d, 0x1a, 0x24, 0xbd, 0xd2, 0x85, 0x97, 0x79, 0xbc, 0x2b, 0xaf, 0xc8, + 0x64, 0x19, 0x8c, 0xc4, 0x4c, 0x2c, 0xf1, 0x93, 0xa8, 0x10, 0xf7, 0x23, + 0x16, 0xde, 0x42, 0xd2, 0xa8, 0x84, 0x34, 0xdd, 0x7a, 0xd2, 0x27, 0x7d, + 0x2f, 0xec, 0xd0, 0x5d, 0xc2, 0xa8, 0xb3, 0x57, 0xfb, 0xf8, 0xe1, 0xbf, + 0xcd, 0x2c, 0xbf, 0x02, 0x6f, 0x18, 0xd6, 0x5f, 0xfb, 0x07, 0x98, 0x28, + 0xc8, 0xf6, 0x59, 0x7d, 0xaf, 0x3b, 0x73, 0x44, 0xe3, 0x0d, 0x48, 0xa6, + 0xff, 0x77, 0xd8, 0x7b, 0x32, 0x16, 0x5f, 0xfa, 0x35, 0xff, 0x79, 0xec, + 0x31, 0xac, 0xb6, 0xcb, 0x2f, 0x4a, 0x3e, 0x59, 0x6c, 0x83, 0x5f, 0x30, + 0x95, 0xf1, 0x93, 0xec, 0xb2, 0xb1, 0x1d, 0x1e, 0x33, 0x26, 0xee, 0x13, + 0x5b, 0xcb, 0x2f, 0xde, 0x8e, 0x78, 0xd6, 0x5f, 0xf8, 0xfb, 0x0c, 0x27, + 0x17, 0xc6, 0xb2, 0xe8, 0xf2, 0xcb, 0xf8, 0xb3, 0x7f, 0x9e, 0x4b, 0x2c, + 0xdf, 0x64, 0x5c, 0xe0, 0x8c, 0xc4, 0xfe, 0x3d, 0xe8, 0xb5, 0x37, 0x4d, + 0x1f, 0x72, 0x1e, 0x97, 0xff, 0xc0, 0x7d, 0xb5, 0x8c, 0x6f, 0xe8, 0xd8, + 0x80, 0xb2, 0x9b, 0xab, 0xcc, 0x38, 0x6a, 0xbc, 0xa7, 0x62, 0x2e, 0xbe, + 0x36, 0x09, 0xa5, 0x97, 0xdc, 0x28, 0x69, 0x65, 0xf4, 0x07, 0x3c, 0xb2, + 0xf8, 0xcb, 0x40, 0x59, 0x78, 0x1f, 0x71, 0x65, 0xf8, 0x4e, 0x94, 0x7e, + 0xb2, 0xb4, 0x78, 0xfb, 0xc7, 0xaf, 0xdb, 0x02, 0x40, 0x85, 0x95, 0x07, + 0x9a, 0x64, 0x97, 0xc2, 0x16, 0x71, 0x65, 0xf7, 0x8f, 0xee, 0x2c, 0xba, + 0x3e, 0x59, 0x71, 0xcf, 0xac, 0xad, 0xd3, 0xd3, 0x98, 0x8c, 0x41, 0x7b, + 0x98, 0xde, 0x13, 0xe4, 0xf8, 0x8c, 0xc8, 0xa6, 0x21, 0xf4, 0x2d, 0x5c, + 0x80, 0x0e, 0xb7, 0x61, 0xac, 0xbb, 0x06, 0xb2, 0xff, 0xcf, 0x26, 0xf9, + 0xa0, 0xbb, 0x85, 0x51, 0x40, 0x2f, 0xf1, 0xb2, 0x37, 0x7d, 0x9b, 0x2c, + 0xb3, 0x79, 0x22, 0xb7, 0x05, 0x58, 0x2d, 0xe4, 0xcb, 0xa4, 0xc5, 0x97, + 0xf1, 0xf2, 0x62, 0x81, 0xac, 0xbd, 0xaf, 0xa4, 0xb2, 0x86, 0x79, 0x6e, + 0x5b, 0x7e, 0x8f, 0xba, 0xfa, 0x59, 0x7e, 0xef, 0x24, 0x7f, 0xac, 0xbf, + 0x8f, 0xd0, 0x51, 0xd5, 0x97, 0x86, 0xe6, 0xb2, 0xa4, 0x98, 0xd6, 0x31, + 0x19, 0x0b, 0x94, 0x70, 0xa4, 0x05, 0x77, 0xe6, 0xf9, 0x34, 0x49, 0x65, + 0xff, 0x47, 0x7d, 0x8c, 0xcd, 0x62, 0xcb, 0xe9, 0xb3, 0x02, 0xb2, 0xfd, + 0xd1, 0x5f, 0x8d, 0xf4, 0x7b, 0x22, 0x1b, 0xd4, 0x91, 0x8c, 0xd4, 0x20, + 0xe9, 0xba, 0x68, 0x91, 0x18, 0xdd, 0xdc, 0xea, 0xcb, 0x42, 0xcb, 0x79, + 0x65, 0x30, 0xd0, 0x80, 0x42, 0xf9, 0xb3, 0xbf, 0xee, 0x2c, 0xbf, 0xff, + 0x6f, 0x8f, 0x18, 0xfd, 0x12, 0xe3, 0xf8, 0xf8, 0xb2, 0xfe, 0xe4, 0x30, + 0xa2, 0x65, 0x96, 0xf2, 0xcb, 0xc2, 0x14, 0xcb, 0x2d, 0xdc, 0x35, 0xff, + 0x88, 0xdd, 0x9b, 0x2c, 0xac, 0x37, 0xc4, 0x4f, 0x7c, 0x6c, 0x89, 0x96, + 0x51, 0xa6, 0x63, 0xfa, 0xb1, 0x42, 0x6a, 0x7c, 0x7e, 0xdc, 0x59, 0x7c, + 0x33, 0x2f, 0xd6, 0x54, 0xe3, 0x66, 0xe2, 0x37, 0xff, 0x67, 0xfc, 0x27, + 0x17, 0x37, 0x9e, 0x96, 0x5e, 0x00, 0xf1, 0x65, 0xfd, 0xae, 0x94, 0x33, + 0x8b, 0x2f, 0xff, 0xda, 0x1b, 0x90, 0xbe, 0xcd, 0xdd, 0x1b, 0x20, 0x55, + 0x95, 0x88, 0x86, 0x01, 0x6d, 0xa7, 0xd6, 0x56, 0x26, 0x3c, 0x68, 0xde, + 0x85, 0x17, 0xe4, 0x57, 0xc1, 0x77, 0x0a, 0xa2, 0xdf, 0x5e, 0x9f, 0x7e, + 0xac, 0xbf, 0xc6, 0x50, 0xcf, 0xdf, 0xab, 0x2f, 0x4d, 0x9f, 0xac, 0xbf, + 0x84, 0xeb, 0x85, 0xf4, 0xb2, 0xb4, 0x8c, 0xb6, 0x17, 0x78, 0x7d, 0xcc, + 0xbf, 0x1e, 0xbf, 0xe3, 0xf4, 0x7d, 0x1b, 0xb9, 0xf2, 0xcb, 0xff, 0x8f, + 0xba, 0x07, 0x3c, 0xf2, 0xfc, 0xd6, 0x5f, 0xec, 0xdb, 0x3b, 0xc7, 0xea, + 0xca, 0xf1, 0xfc, 0x12, 0x2d, 0x9a, 0x59, 0x7f, 0xce, 0x41, 0xe4, 0x79, + 0xf7, 0x56, 0x50, 0xa7, 0x99, 0xd1, 0x2b, 0xec, 0x23, 0x99, 0x65, 0xf1, + 0xe6, 0xb7, 0xac, 0xbe, 0xfa, 0x5b, 0x42, 0xcb, 0xd0, 0xd4, 0xfa, 0xca, + 0xc3, 0xc4, 0xd1, 0x25, 0xef, 0x46, 0x96, 0x5f, 0xbc, 0x7a, 0xf3, 0xac, + 0xbf, 0xfd, 0xe8, 0xdb, 0xb9, 0xe2, 0xce, 0xf8, 0xd6, 0x5e, 0x66, 0x0d, + 0x65, 0x98, 0xb2, 0xff, 0x46, 0xc3, 0xc6, 0xb8, 0xde, 0x11, 0x73, 0x83, + 0x93, 0x13, 0xba, 0x48, 0x83, 0x95, 0x24, 0xd3, 0xfd, 0x0d, 0xab, 0xe1, + 0xb3, 0x3e, 0x59, 0x7d, 0x2e, 0x09, 0xc5, 0x95, 0x07, 0x8f, 0xd2, 0x3b, + 0xec, 0xef, 0x31, 0x65, 0xfc, 0xff, 0x0c, 0xcb, 0x65, 0x95, 0x07, 0x9f, + 0xa2, 0x1b, 0xdc, 0xf0, 0xd6, 0x5f, 0x84, 0xe9, 0x47, 0xeb, 0x29, 0x87, + 0x8d, 0xbc, 0x76, 0xff, 0xee, 0xf0, 0xf9, 0x05, 0x9d, 0xf1, 0xac, 0xbf, + 0xf3, 0xee, 0x68, 0xc5, 0xcf, 0x1c, 0x96, 0x5e, 0x2c, 0xf2, 0xca, 0x62, + 0x27, 0xbc, 0x85, 0xbd, 0x06, 0xfc, 0x38, 0xdd, 0x03, 0x16, 0x5f, 0xf3, + 0xf2, 0x30, 0x87, 0xe8, 0x59, 0x7f, 0xbd, 0x03, 0x3d, 0xf0, 0x35, 0x97, + 0xfd, 0xac, 0xfb, 0xc4, 0xfb, 0xbb, 0x2c, 0xb6, 0x0c, 0xfc, 0x58, 0x69, + 0x77, 0x39, 0xa4, 0x67, 0x14, 0x29, 0xea, 0x15, 0x10, 0x64, 0x30, 0x8c, + 0xc9, 0xe1, 0xf3, 0x70, 0x05, 0x59, 0x7c, 0xc9, 0xa0, 0x45, 0x94, 0xc3, + 0x78, 0xe3, 0x17, 0x8c, 0xba, 0xb2, 0xe0, 0x7c, 0xb2, 0xff, 0x9e, 0x5d, + 0xe6, 0x35, 0xe3, 0x59, 0x7b, 0xe7, 0xf2, 0xca, 0x74, 0x44, 0xfe, 0x36, + 0x43, 0x00, 0x39, 0xbf, 0xd1, 0xcc, 0xfb, 0xa7, 0xa5, 0x97, 0xdc, 0xec, + 0x31, 0x65, 0x68, 0xf5, 0x7f, 0x33, 0xbd, 0xa8, 0xea, 0xcb, 0xfa, 0x67, + 0x1f, 0x8d, 0x8b, 0x2f, 0xee, 0x9e, 0x0e, 0x18, 0xb2, 0xba, 0x7b, 0x4d, + 0x17, 0x5f, 0x3e, 0xc0, 0x25, 0x97, 0x70, 0x96, 0x5f, 0xdf, 0x72, 0x03, + 0x8c, 0x59, 0x7c, 0xc0, 0xe1, 0x2c, 0xa8, 0x4d, 0x0f, 0x44, 0x6e, 0xec, + 0x44, 0x7c, 0x22, 0x00, 0xb0, 0x85, 0xd7, 0xe9, 0xa0, 0x8a, 0x16, 0x5c, + 0x1d, 0x2c, 0xb9, 0xc9, 0x65, 0x19, 0xe9, 0x74, 0x98, 0x02, 0xf7, 0xe7, + 0xda, 0x3c, 0xeb, 0x2f, 0x14, 0x0a, 0xb2, 0xfb, 0x37, 0x5f, 0xcb, 0x2f, + 0x13, 0xf4, 0x27, 0x81, 0xc1, 0xcb, 0x86, 0xc5, 0x97, 0xec, 0x9a, 0x51, + 0xf2, 0xca, 0x14, 0xf0, 0x08, 0x5e, 0x82, 0x89, 0x4f, 0x39, 0x5f, 0xb0, + 0xbb, 0x1c, 0x59, 0x69, 0x2c, 0xbf, 0xdf, 0xfe, 0x0e, 0x6e, 0xe7, 0xcb, + 0x2b, 0x0f, 0x2d, 0xc4, 0x6f, 0xfc, 0xff, 0x7d, 0xc3, 0x1e, 0x35, 0xc5, + 0x95, 0x09, 0xf8, 0x64, 0x3e, 0xfc, 0x46, 0x4e, 0x3c, 0x20, 0xbf, 0xfe, + 0x7f, 0xb5, 0x99, 0xf3, 0x5e, 0x04, 0x7d, 0xc5, 0x97, 0xfb, 0xa7, 0xc7, + 0x0b, 0xec, 0xb2, 0xfc, 0xe4, 0x3f, 0x42, 0xcb, 0xef, 0xcc, 0x85, 0x59, + 0x4e, 0x79, 0x3f, 0x93, 0x53, 0x65, 0xb2, 0x54, 0x9e, 0x4e, 0x1b, 0x54, + 0x86, 0x23, 0x5b, 0x93, 0xc0, 0xe3, 0x40, 0xc8, 0xc3, 0x45, 0x4a, 0x0c, + 0x2d, 0x3e, 0x6d, 0x32, 0x3d, 0xd2, 0x19, 0xa3, 0xb6, 0xd3, 0x83, 0x1c, + 0x3d, 0x2a, 0x1d, 0xe1, 0x03, 0xfc, 0x2a, 0x4a, 0x3d, 0x4e, 0x42, 0xd7, + 0xb2, 0x87, 0x80, 0x9a, 0x22, 0x9e, 0xe3, 0xe5, 0xfc, 0xff, 0x37, 0x63, + 0xf5, 0x65, 0x37, 0x4f, 0x30, 0xa3, 0x9c, 0xbf, 0x86, 0xdf, 0x9d, 0xe7, + 0xcb, 0x2f, 0x05, 0xc2, 0xb2, 0xff, 0xe1, 0x03, 0x3a, 0x35, 0xa3, 0x27, + 0x0a, 0xca, 0xc3, 0xe2, 0x60, 0xe5, 0xcd, 0x89, 0xe5, 0x65, 0xe6, 0x3f, + 0xcb, 0x2f, 0x7e, 0xfe, 0x59, 0x76, 0xf3, 0x59, 0x7d, 0xad, 0x60, 0x8b, + 0x2f, 0xfa, 0x24, 0xd3, 0xeb, 0x58, 0x22, 0xcb, 0xd8, 0xd3, 0x79, 0xe9, + 0x19, 0xf0, 0x41, 0x83, 0xb3, 0x0e, 0xb8, 0xc1, 0x11, 0xdb, 0x8d, 0xd3, + 0x5d, 0xda, 0x1e, 0x15, 0x0a, 0x88, 0xce, 0x3f, 0x1b, 0xff, 0xfe, 0x6d, + 0x9f, 0x40, 0xfb, 0x37, 0xc9, 0x7a, 0x35, 0x98, 0x42, 0xac, 0xbf, 0xff, + 0xf8, 0x2f, 0xa7, 0xf4, 0xb3, 0xb8, 0x3e, 0x3b, 0x33, 0x5b, 0x47, 0xcb, + 0x2f, 0xec, 0x1b, 0xb3, 0x09, 0x65, 0x1a, 0x27, 0x74, 0xe5, 0x7e, 0xd0, + 0x5d, 0xc2, 0xa8, 0xa5, 0x17, 0xff, 0xcf, 0xd2, 0xcd, 0xb5, 0x1e, 0x3f, + 0x66, 0x96, 0x5d, 0xc6, 0x96, 0x5f, 0xfb, 0xd1, 0xfb, 0x1e, 0x5c, 0xe3, + 0x4b, 0x2f, 0x3c, 0x9b, 0xc2, 0x3f, 0x70, 0x8b, 0xf3, 0x4e, 0x27, 0x08, + 0x31, 0x7f, 0xd2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x12, 0x72, 0x98, 0x8a, + 0x47, 0x5d, 0xbf, 0xda, 0x6f, 0x9e, 0x89, 0x9d, 0x65, 0x37, 0x3d, 0x77, + 0x22, 0xa8, 0x8c, 0xd2, 0x67, 0x6a, 0x58, 0xdc, 0xa5, 0xfe, 0x8f, 0x19, + 0x48, 0xd9, 0x1a, 0x08, 0xb0, 0xee, 0x0c, 0x70, 0x27, 0x1e, 0x66, 0xec, + 0x3d, 0xe6, 0x87, 0x3e, 0xa1, 0x7e, 0xcb, 0x5e, 0x9b, 0xe9, 0x7f, 0x6f, + 0x28, 0xbb, 0xf8, 0xc9, 0x0a, 0x55, 0x17, 0x2b, 0x44, 0xbe, 0xca, 0x4e, + 0x01, 0x4e, 0xf9, 0x60, 0x37, 0xed, 0x05, 0xdc, 0x2a, 0x88, 0x7d, 0x7f, + 0xe7, 0x93, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x94, 0xd6, 0x6f, 0x88, 0x80, + 0x61, 0xa5, 0xfe, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x11, 0x3a, 0xfc, 0x7d, + 0xe0, 0x18, 0xb2, 0xf7, 0xa2, 0x65, 0x95, 0x31, 0xe2, 0xf8, 0xa2, 0xff, + 0x03, 0xe1, 0x06, 0x0e, 0xf1, 0x65, 0xff, 0xe6, 0xd0, 0x56, 0xb7, 0x76, + 0x1c, 0xf7, 0x3a, 0x77, 0xa7, 0xd6, 0x5f, 0xb4, 0x17, 0x70, 0xaa, 0x23, + 0x15, 0xfb, 0x87, 0xbe, 0x18, 0xb2, 0xf7, 0xd2, 0xd9, 0x65, 0xbd, 0x07, + 0x91, 0x85, 0x37, 0xf4, 0xa3, 0xff, 0x1b, 0x4b, 0x2f, 0xff, 0xe0, 0x13, + 0x8b, 0x3b, 0xa2, 0xc1, 0x4b, 0x3c, 0x6c, 0x59, 0x7d, 0x0c, 0x0f, 0xeb, + 0x2f, 0xff, 0xa5, 0xb3, 0x69, 0x13, 0xce, 0xc1, 0xd4, 0xe9, 0xde, 0x9f, + 0x59, 0x5e, 0x44, 0x47, 0xe4, 0x77, 0xf6, 0x76, 0x33, 0xee, 0xac, 0xbf, + 0xf7, 0x45, 0x82, 0x96, 0x78, 0xd8, 0xb2, 0xff, 0x85, 0x82, 0x96, 0x78, + 0xd8, 0xb2, 0xf8, 0x04, 0xe2, 0xce, 0x3f, 0x6e, 0x9f, 0x5f, 0xb5, 0xf9, + 0xfa, 0x16, 0x53, 0x0f, 0x8c, 0x07, 0x77, 0xc7, 0xf4, 0xa7, 0x95, 0x97, + 0xe9, 0xd3, 0x14, 0x0d, 0x65, 0x9b, 0xc2, 0xe0, 0x96, 0xc4, 0x72, 0x37, + 0xc6, 0x40, 0xbe, 0x4c, 0x4c, 0xc2, 0xff, 0x43, 0x94, 0x89, 0x39, 0x18, + 0x37, 0x48, 0xa7, 0xca, 0x2f, 0xff, 0x37, 0x63, 0xc9, 0xbe, 0x68, 0x2e, + 0xe1, 0x54, 0x4f, 0x2b, 0xe8, 0xdf, 0x1e, 0x59, 0x7d, 0x05, 0x2e, 0x2c, + 0xbb, 0x38, 0xb2, 0xf7, 0x61, 0x8b, 0x2f, 0xde, 0x32, 0xcd, 0xeb, 0x2b, + 0x48, 0x9e, 0xfc, 0x8d, 0xb6, 0x43, 0xd1, 0x61, 0x07, 0x2f, 0xdc, 0x00, + 0xf1, 0x8b, 0x2e, 0x90, 0x8b, 0x2f, 0x80, 0xfa, 0x92, 0xcb, 0xcc, 0x06, + 0xea, 0xcb, 0xf3, 0x8f, 0xd1, 0xc5, 0x97, 0xfb, 0x3d, 0xe8, 0xf9, 0xf7, + 0x56, 0x59, 0xbc, 0x23, 0x7e, 0x45, 0x03, 0x18, 0xfc, 0x88, 0x88, 0x38, + 0x4f, 0x4d, 0xd3, 0x8d, 0x6a, 0x32, 0xfa, 0x86, 0x41, 0x8e, 0xc5, 0x52, + 0x9c, 0x98, 0xf4, 0x2f, 0x7b, 0x2a, 0x16, 0xfd, 0xa0, 0xbb, 0x85, 0x51, + 0x10, 0x2f, 0xfc, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x12, 0xa2, 0xfe, + 0xd0, 0x3d, 0x04, 0x15, 0x97, 0xf0, 0x4f, 0x37, 0x3d, 0x0b, 0x2a, 0x0f, + 0x73, 0x0b, 0x6c, 0xdf, 0x11, 0xf8, 0xc3, 0x42, 0x85, 0x45, 0xfb, 0x41, + 0x77, 0x0a, 0xa2, 0x28, 0x5f, 0xbc, 0x6c, 0x00, 0xd6, 0x5e, 0x8f, 0xf1, + 0x65, 0x9b, 0xe1, 0xfc, 0x39, 0xa0, 0x85, 0x17, 0xf9, 0xbe, 0x68, 0x2e, + 0xe1, 0x54, 0x46, 0x4b, 0xf6, 0x82, 0xee, 0x15, 0x44, 0xca, 0xbf, 0x4b, + 0x3a, 0xe1, 0x59, 0x7e, 0x6e, 0xc7, 0x93, 0x7c, 0x3d, 0xbf, 0x8d, 0x2f, + 0xda, 0x0b, 0xb8, 0x55, 0x15, 0x22, 0xc4, 0xb2, 0xe9, 0x31, 0x65, 0x84, + 0x59, 0x66, 0xf8, 0x7d, 0xbb, 0xa6, 0x9a, 0x10, 0x00, 0xbd, 0xfe, 0x6f, + 0x9a, 0x0b, 0xb8, 0x55, 0x15, 0xd2, 0xff, 0x37, 0xcd, 0x05, 0xdc, 0x2a, + 0x8b, 0x15, 0x7f, 0xd1, 0xce, 0x43, 0x3b, 0x81, 0x59, 0x7c, 0x17, 0x70, + 0xaa, 0x27, 0xa5, 0xf9, 0x8f, 0x26, 0xf8, 0x13, 0xe6, 0xd1, 0xc5, 0xe6, + 0x9c, 0x2b, 0x2f, 0xef, 0x41, 0xe8, 0xdb, 0x6b, 0x2f, 0xd0, 0x73, 0xe7, + 0xb8, 0xb2, 0xe6, 0x9b, 0xe1, 0xfc, 0x84, 0x75, 0xcc, 0x2f, 0xff, 0xff, + 0x0f, 0x1a, 0x6e, 0x58, 0x28, 0xaf, 0xde, 0x19, 0x4d, 0x01, 0xd6, 0x7c, + 0xb2, 0xff, 0xfe, 0xcc, 0xf1, 0x87, 0xd8, 0xdc, 0xb3, 0xbe, 0x36, 0x96, + 0x5f, 0xff, 0xa6, 0x28, 0x63, 0x7c, 0xe1, 0xb4, 0x37, 0x71, 0x56, 0x5d, + 0xce, 0xac, 0xbb, 0x38, 0xb2, 0xff, 0xd9, 0xaf, 0xa1, 0x9e, 0x7c, 0xd2, + 0xcb, 0xb9, 0xfa, 0xcb, 0xf1, 0x3b, 0x0a, 0x16, 0x5f, 0xc0, 0xe7, 0x9e, + 0x4d, 0xe7, 0x94, 0x54, 0x38, 0xb9, 0x0b, 0x34, 0x7c, 0x20, 0xc5, 0xed, + 0xa3, 0x75, 0x65, 0xda, 0xea, 0xcb, 0xf6, 0x77, 0x86, 0xc5, 0x97, 0xff, + 0xa3, 0xee, 0xe4, 0xc7, 0xae, 0xf0, 0x1b, 0xab, 0x2f, 0x3f, 0xcd, 0xe1, + 0x15, 0xae, 0x40, 0x42, 0xfd, 0x27, 0xac, 0x54, 0xdf, 0xa8, 0xc0, 0x39, + 0x19, 0x0d, 0x0d, 0x5c, 0x19, 0x3e, 0x76, 0x56, 0x55, 0x37, 0x5c, 0xb0, + 0x88, 0x5c, 0x9c, 0xbb, 0xfb, 0xff, 0xc3, 0x6f, 0xde, 0x83, 0x61, 0x26, + 0xe0, 0x3e, 0x59, 0x7c, 0x70, 0x06, 0xda, 0xcb, 0xbb, 0x0b, 0x2a, 0x7a, + 0x37, 0x7f, 0x12, 0xdf, 0x8f, 0xc4, 0xf3, 0x2c, 0xbf, 0x4b, 0x07, 0x3b, + 0x16, 0x5f, 0x9c, 0xf6, 0xd4, 0x2c, 0xbf, 0x74, 0x80, 0xf3, 0xda, 0xca, + 0x6c, 0xa2, 0x8e, 0x09, 0xfc, 0x52, 0x21, 0x3d, 0xcc, 0xfd, 0x65, 0xfb, + 0x3a, 0x60, 0xe2, 0xcb, 0xa7, 0xa6, 0xd6, 0xb2, 0xa7, 0xa3, 0xe1, 0x6d, + 0x63, 0x0d, 0xa1, 0x3d, 0xfa, 0x78, 0x4f, 0x5c, 0xd7, 0x56, 0x5f, 0xe1, + 0x0b, 0x35, 0xa8, 0x0a, 0xca, 0x9e, 0x8f, 0x9e, 0x7b, 0x34, 0xbe, 0xd3, + 0x3b, 0xf2, 0xcb, 0xc1, 0x3e, 0x2c, 0xbf, 0x36, 0x1b, 0x31, 0x26, 0x2c, + 0xbf, 0xf3, 0x69, 0x3c, 0xcf, 0x6d, 0x99, 0xee, 0x7b, 0x9e, 0x33, 0xdc, + 0xf4, 0xb2, 0xff, 0xd3, 0xc2, 0x7b, 0x9e, 0x33, 0xc2, 0x78, 0x36, 0x5b, + 0x2d, 0x89, 0xe9, 0x65, 0xff, 0xa7, 0x8b, 0x69, 0x3c, 0x27, 0x8c, 0xf0, + 0x6c, 0xb6, 0xb9, 0xe1, 0x3d, 0x2c, 0xbf, 0xef, 0x0e, 0x4d, 0xe5, 0xc2, + 0x6e, 0xd9, 0x4d, 0x42, 0x79, 0x6e, 0x6d, 0x6d, 0xf5, 0x3d, 0x27, 0xe6, + 0xda, 0xe3, 0x80, 0xbf, 0x36, 0x58, 0xee, 0x4b, 0x2f, 0xe8, 0x3e, 0xf6, + 0x18, 0xb2, 0x9b, 0x07, 0xad, 0x3c, 0x0a, 0xaf, 0x07, 0x3c, 0x92, 0xf3, + 0x23, 0x8b, 0x2f, 0xff, 0xf7, 0x44, 0xd4, 0x7a, 0x30, 0x23, 0x32, 0xd9, + 0x91, 0x25, 0x97, 0x6a, 0x16, 0x51, 0x9f, 0xbf, 0xec, 0x37, 0xa6, 0x36, + 0x96, 0x5d, 0x92, 0x59, 0x76, 0x7e, 0x66, 0xd1, 0xc7, 0xaf, 0xdd, 0xfc, + 0x42, 0x99, 0x65, 0xff, 0x9f, 0x93, 0xb9, 0xcc, 0x94, 0xa1, 0x65, 0xb8, + 0xb2, 0xfe, 0x80, 0x9e, 0xce, 0x4b, 0x2f, 0xff, 0xc4, 0xe2, 0x9e, 0xa2, + 0x52, 0xce, 0x7f, 0xfc, 0x2c, 0xbf, 0xcf, 0xd0, 0x66, 0x10, 0xab, 0x28, + 0x69, 0xad, 0xe1, 0x58, 0xa5, 0x73, 0x20, 0x68, 0x47, 0xc5, 0x9f, 0xab, + 0x5f, 0xff, 0xda, 0x3d, 0xc7, 0x63, 0x78, 0xd6, 0x8f, 0x70, 0x20, 0xd9, + 0x65, 0xf1, 0xf8, 0x46, 0x2c, 0xbf, 0xfc, 0x62, 0xce, 0xc2, 0x7f, 0xb8, + 0x28, 0xae, 0xb2, 0x82, 0x7e, 0x24, 0x47, 0x7f, 0xf9, 0xf8, 0x59, 0xfb, + 0x7f, 0x18, 0x5f, 0x4b, 0x2e, 0x26, 0x96, 0x5f, 0xb8, 0x40, 0x79, 0x2c, + 0xbf, 0xbb, 0x34, 0x9f, 0x42, 0xac, 0xbb, 0x02, 0xb2, 0xf0, 0xf0, 0x57, + 0x3c, 0x6e, 0x98, 0x5f, 0x7a, 0x18, 0x15, 0x95, 0x08, 0xd9, 0xc1, 0x77, + 0x6d, 0xe9, 0x9d, 0xf7, 0x24, 0x0f, 0xd6, 0x5e, 0x1e, 0x79, 0x65, 0xe9, + 0x8d, 0xa5, 0x96, 0x1c, 0x8d, 0xd1, 0x8e, 0x5f, 0xfd, 0x3a, 0x20, 0xa1, + 0x93, 0xa7, 0x7a, 0x7d, 0x65, 0x41, 0xf9, 0x11, 0x35, 0xff, 0xbd, 0x93, + 0x78, 0xfd, 0x1a, 0x85, 0x97, 0x3f, 0xcb, 0x2f, 0xf3, 0xf1, 0xfc, 0x02, + 0xd9, 0x65, 0x11, 0xe5, 0x00, 0x5e, 0xb6, 0x5e, 0x98, 0x94, 0x24, 0xce, + 0x3b, 0x1d, 0x33, 0xb2, 0x1a, 0xbe, 0x21, 0x28, 0xc9, 0x38, 0x75, 0xd8, + 0x68, 0x08, 0x41, 0xb9, 0x08, 0x8b, 0xa3, 0x4b, 0x2e, 0xcf, 0x2c, 0xbe, + 0x2c, 0xdf, 0x8b, 0x28, 0x27, 0x9d, 0x3e, 0x2c, 0x20, 0xb5, 0xf6, 0x30, + 0xfa, 0xb2, 0xb4, 0x7a, 0x9d, 0x33, 0xbc, 0x10, 0x69, 0x65, 0xed, 0x3e, + 0x2c, 0xbc, 0x4f, 0xd5, 0x97, 0x88, 0xf8, 0xb2, 0xcd, 0x36, 0x4d, 0xb4, + 0x0d, 0xdd, 0x1e, 0x59, 0x7d, 0xad, 0xee, 0xc5, 0x97, 0xe2, 0xce, 0xc3, + 0x4b, 0x2f, 0xff, 0x1c, 0xef, 0x64, 0xc7, 0xf7, 0x3d, 0x81, 0x59, 0x7e, + 0x61, 0x3f, 0x78, 0xb2, 0xf4, 0x66, 0x96, 0x5f, 0xfe, 0x0b, 0xe9, 0xfa, + 0x7c, 0x82, 0x8f, 0xd6, 0x5a, 0x5e, 0x3e, 0x31, 0x06, 0xeb, 0xe4, 0x59, + 0xea, 0x11, 0x55, 0x0a, 0x91, 0x76, 0x22, 0x18, 0xee, 0x28, 0x84, 0xac, + 0xc5, 0xb8, 0x49, 0xd2, 0x70, 0x46, 0x01, 0x7c, 0x36, 0x35, 0xc5, 0x97, + 0xce, 0x62, 0x31, 0x65, 0xee, 0x6e, 0x6e, 0x2c, 0xbe, 0x9d, 0x2f, 0xd8, + 0xb2, 0xf6, 0xb0, 0x6b, 0x2f, 0xf3, 0xf6, 0x26, 0xe1, 0xb1, 0x65, 0x0a, + 0x79, 0xfe, 0x1c, 0xbf, 0xd1, 0x22, 0xcf, 0xbb, 0x8b, 0x2f, 0xc1, 0x3f, + 0xb6, 0x25, 0x97, 0xff, 0x38, 0x60, 0x87, 0x34, 0xa3, 0x5b, 0x2c, 0xb1, + 0xfc, 0x7d, 0xde, 0x29, 0xbf, 0x8b, 0x4e, 0xef, 0xd5, 0x97, 0xff, 0xdd, + 0xe0, 0xf5, 0x86, 0x2f, 0x9a, 0x72, 0xd9, 0x65, 0xf1, 0x0f, 0x05, 0x59, + 0x7b, 0xc0, 0x62, 0xcb, 0xb7, 0x8d, 0x65, 0x36, 0xaa, 0xa9, 0xe9, 0x12, + 0x61, 0x17, 0xc4, 0x66, 0xeb, 0xa2, 0x3f, 0x42, 0x81, 0xca, 0x38, 0x57, + 0xd5, 0x20, 0x11, 0x34, 0x3b, 0x7e, 0xc2, 0x28, 0xd9, 0x65, 0xbe, 0x59, + 0xa3, 0x4d, 0x7e, 0xe4, 0xd0, 0x5b, 0x2c, 0xac, 0x3c, 0xbe, 0x91, 0x5f, + 0x88, 0x53, 0xce, 0x2c, 0xbf, 0xda, 0x97, 0x8f, 0x86, 0x35, 0x97, 0xcf, + 0xd7, 0x0a, 0xca, 0x33, 0xd4, 0x68, 0xce, 0xf8, 0x0d, 0xbe, 0x6c, 0xb2, + 0xff, 0xcd, 0x3f, 0xf8, 0x1f, 0x3f, 0xfc, 0x59, 0x50, 0x7d, 0x38, 0x51, + 0x7f, 0xee, 0x3f, 0xd0, 0x4e, 0x3c, 0x15, 0x65, 0x1a, 0x6c, 0x9e, 0x7b, + 0x28, 0x45, 0xf4, 0x82, 0xe8, 0xf2, 0xcb, 0x0d, 0x65, 0x30, 0xd3, 0x90, + 0xb5, 0x85, 0x59, 0x78, 0xbf, 0x85, 0x97, 0x10, 0xe7, 0x1a, 0xee, 0x09, + 0x5f, 0xd9, 0xc6, 0x9c, 0xb6, 0x59, 0x58, 0x8a, 0xa7, 0x4e, 0x22, 0xeb, + 0xf6, 0x81, 0xc7, 0x0a, 0xcb, 0xdc, 0x1c, 0x2c, 0xbf, 0xee, 0x77, 0x02, + 0x0f, 0xdf, 0x16, 0x50, 0xa7, 0xaa, 0xe3, 0x95, 0x08, 0x9f, 0x03, 0xed, + 0xb8, 0xb2, 0xd8, 0xb2, 0xcc, 0x33, 0x43, 0xb8, 0x23, 0x7f, 0xd8, 0x4f, + 0x2e, 0x49, 0xad, 0x96, 0x5f, 0x4b, 0x86, 0x4b, 0x2f, 0xfc, 0x12, 0x7f, + 0x3f, 0xd9, 0xf7, 0x56, 0x54, 0x91, 0x35, 0xf1, 0xd0, 0x08, 0x6f, 0xb5, + 0x8e, 0xc5, 0x95, 0x07, 0xa4, 0xe6, 0x37, 0xf9, 0xf5, 0x28, 0x09, 0x85, + 0x65, 0x42, 0xe8, 0x6e, 0x47, 0x0a, 0x71, 0x89, 0x7a, 0x19, 0x8e, 0x86, + 0x51, 0x8a, 0x80, 0x82, 0xff, 0xf0, 0xaf, 0x9f, 0x77, 0xb1, 0x9b, 0xcc, + 0x2b, 0x2f, 0xf1, 0x67, 0x3a, 0xee, 0x4b, 0x28, 0x53, 0xfc, 0xde, 0x99, + 0x7f, 0xff, 0xe3, 0xd6, 0xa0, 0x4e, 0x99, 0x02, 0x59, 0xc1, 0x81, 0xf5, + 0x25, 0x97, 0xff, 0xf1, 0x60, 0xb8, 0x53, 0xb9, 0xdf, 0x63, 0x04, 0x8f, + 0xd6, 0x54, 0x23, 0x2b, 0x1b, 0x2f, 0xf0, 0x1f, 0x87, 0x31, 0xb4, 0xb2, + 0x8d, 0x34, 0x4f, 0x43, 0xbc, 0x88, 0x6f, 0x7a, 0x09, 0x65, 0xef, 0xba, + 0xc5, 0x94, 0x13, 0x72, 0xe3, 0x77, 0xc3, 0xd1, 0xb4, 0xb2, 0xf6, 0x00, + 0x6b, 0x2b, 0x63, 0x7f, 0xc2, 0x3b, 0xc6, 0x5d, 0x59, 0x7f, 0x3e, 0xf3, + 0x98, 0xda, 0x59, 0x4c, 0x3c, 0xb1, 0x06, 0xef, 0xf0, 0x35, 0xb3, 0x23, + 0x3a, 0xb2, 0xff, 0xb9, 0x0c, 0x1f, 0xa0, 0x82, 0xb2, 0xe3, 0x2c, 0x3e, + 0xc1, 0x4d, 0x2a, 0x11, 0x76, 0x38, 0x47, 0xde, 0x90, 0x38, 0xb2, 0xfe, + 0x1b, 0xeb, 0xf9, 0xfc, 0x59, 0x7b, 0xc2, 0x6f, 0x59, 0x7f, 0xd0, 0xc2, + 0xc0, 0xe8, 0xff, 0x59, 0x61, 0xac, 0xbd, 0xce, 0x62, 0xcb, 0x0e, 0x0d, + 0x6b, 0x88, 0xd4, 0x91, 0x55, 0xc2, 0x00, 0x32, 0xdf, 0xde, 0x73, 0x0e, + 0x12, 0xcb, 0xfd, 0x2c, 0x3e, 0x70, 0x10, 0xb2, 0xfe, 0x1e, 0x14, 0xbd, + 0x8b, 0x2a, 0x0f, 0x78, 0xcc, 0xaf, 0xfd, 0xd8, 0x67, 0xc1, 0x3f, 0xb3, + 0x4b, 0x2f, 0xde, 0xcd, 0x1f, 0x16, 0x5e, 0x23, 0x97, 0xc7, 0xcf, 0xd4, + 0x0b, 0xff, 0x3e, 0x7f, 0x04, 0xe3, 0xc1, 0x56, 0x54, 0x27, 0x01, 0xb4, + 0x22, 0x65, 0x08, 0x7f, 0xcc, 0xef, 0xff, 0x17, 0x67, 0xf2, 0x69, 0x3e, + 0xb6, 0x1c, 0x2c, 0xbf, 0xff, 0x8a, 0x51, 0xd7, 0xf7, 0x0d, 0xfb, 0xf8, + 0x85, 0x25, 0x97, 0xdd, 0xee, 0x6e, 0xac, 0xbf, 0xe8, 0xc9, 0x7b, 0x0e, + 0x5c, 0x59, 0x5c, 0x3d, 0xce, 0x92, 0xdf, 0x87, 0x37, 0x81, 0xba, 0xb2, + 0xbe, 0x3c, 0xf2, 0x22, 0xac, 0x4d, 0xd8, 0xd3, 0x9e, 0x30, 0x6b, 0xf6, + 0x88, 0xf0, 0x55, 0x97, 0x32, 0x4b, 0x2f, 0xf9, 0xf0, 0x23, 0xcd, 0x7e, + 0x6b, 0x2d, 0xa1, 0x9e, 0x7f, 0x85, 0xea, 0x11, 0x3c, 0x6e, 0xd7, 0xfd, + 0x12, 0xe1, 0x47, 0xee, 0xc5, 0x97, 0xf1, 0xeb, 0x60, 0x7f, 0x8b, 0x2f, + 0xdd, 0x8c, 0x20, 0xac, 0xbe, 0xfb, 0xb0, 0xc5, 0x94, 0x29, 0xe4, 0xfc, + 0x4d, 0x7f, 0xd2, 0x13, 0xa7, 0xe3, 0x2e, 0xac, 0xbf, 0xfd, 0x19, 0xf7, + 0x7d, 0x1d, 0x28, 0xff, 0x8b, 0x2e, 0x8f, 0xd6, 0x5d, 0xe6, 0x2c, 0xa0, + 0x9a, 0xee, 0x0b, 0xd6, 0xc9, 0xb7, 0x1b, 0xc3, 0x92, 0x74, 0xe4, 0x0f, + 0x17, 0xff, 0x88, 0x0f, 0x26, 0x03, 0xbe, 0x80, 0x36, 0xd6, 0x5e, 0xd8, + 0xf4, 0xb2, 0xff, 0xb8, 0x7f, 0x76, 0x69, 0x47, 0xcb, 0x2f, 0xdc, 0xef, + 0x33, 0xab, 0x2b, 0x87, 0xc8, 0x03, 0xcb, 0xf8, 0x0d, 0xb3, 0x98, 0xda, + 0x59, 0x76, 0x79, 0x65, 0xe6, 0x9a, 0x69, 0x25, 0xff, 0xee, 0x9f, 0x79, + 0xe3, 0x9f, 0x13, 0x51, 0x24, 0x8d, 0xcd, 0x05, 0x0a, 0x9b, 0x61, 0xc2, + 0x05, 0xc8, 0xba, 0x66, 0x03, 0x6b, 0xa6, 0x35, 0x97, 0xff, 0xf4, 0xa0, + 0xb3, 0xbe, 0x3c, 0xec, 0x30, 0xf5, 0x25, 0x96, 0x3d, 0x1f, 0x7f, 0x45, + 0xea, 0x4c, 0xc0, 0x41, 0xb6, 0xe2, 0xf8, 0x63, 0x28, 0xf8, 0x9c, 0xc7, + 0x66, 0x86, 0xde, 0xa3, 0xb0, 0x64, 0x77, 0xfe, 0x86, 0x97, 0xe4, 0x25, + 0x1d, 0x37, 0x12, 0xbb, 0x1d, 0xd6, 0xf8, 0x5f, 0x5f, 0xf7, 0xd2, 0xe7, + 0x30, 0x6f, 0xd5, 0x97, 0xf6, 0xbb, 0x00, 0x6d, 0xf1, 0x65, 0xb1, 0x65, + 0xf6, 0x89, 0xf7, 0xac, 0xbd, 0x8e, 0x4b, 0x2e, 0x76, 0x96, 0x54, 0x23, + 0x40, 0x67, 0x42, 0x99, 0x18, 0x86, 0x88, 0xd8, 0x35, 0x7b, 0xfe, 0x62, + 0xcb, 0xfc, 0xf2, 0x9b, 0xc7, 0xf7, 0x56, 0x59, 0xd6, 0x5e, 0x8f, 0x71, + 0x65, 0x7c, 0x6b, 0x0c, 0x42, 0xff, 0xd0, 0x19, 0xd8, 0x50, 0x06, 0xdf, + 0x16, 0x5f, 0xff, 0xcf, 0xd3, 0xf3, 0xb2, 0x76, 0x72, 0x33, 0x58, 0x4b, + 0x2f, 0xfe, 0xe3, 0xfd, 0xce, 0x61, 0x7f, 0x3f, 0x8b, 0x2f, 0xff, 0x18, + 0x79, 0x13, 0xf9, 0xd9, 0x4b, 0x38, 0xb2, 0x86, 0x9d, 0x96, 0x98, 0x58, + 0x42, 0x48, 0x7d, 0x59, 0xde, 0x91, 0x7d, 0xaf, 0x61, 0x2c, 0xa3, 0x3f, + 0xa7, 0x59, 0xbf, 0x86, 0x24, 0x8f, 0xfe, 0x2c, 0xbf, 0x8a, 0x70, 0x9c, + 0x8c, 0x59, 0x7d, 0xd8, 0xf9, 0xa5, 0x94, 0x15, 0x5d, 0x9e, 0x94, 0x92, + 0xe4, 0x1f, 0x98, 0x34, 0x5f, 0x7f, 0xfe, 0xe8, 0x04, 0xe9, 0x47, 0xef, + 0xdf, 0xc4, 0x29, 0x2c, 0xbf, 0x8f, 0xbd, 0xc2, 0x62, 0xcb, 0x9a, 0xf2, + 0xcb, 0xff, 0xd0, 0x41, 0x13, 0x4f, 0xf9, 0xf7, 0xd0, 0xb2, 0xf9, 0xf6, + 0x72, 0x59, 0x7e, 0xef, 0xb3, 0xf7, 0x59, 0x5f, 0x1e, 0x49, 0x10, 0xdf, + 0xcc, 0x9d, 0xec, 0xfd, 0xd6, 0x5f, 0xcd, 0x73, 0x30, 0xb6, 0x59, 0x50, + 0x88, 0x0c, 0x22, 0x33, 0x0b, 0xe8, 0xe4, 0x6f, 0x59, 0x7f, 0xf4, 0x13, + 0xf6, 0x50, 0x2b, 0xb5, 0xd5, 0x95, 0xe3, 0xe7, 0x01, 0x1d, 0xe9, 0xce, + 0x15, 0x95, 0x0a, 0xa1, 0x24, 0xb2, 0x12, 0xcf, 0xc6, 0x39, 0x18, 0x9f, + 0x61, 0x29, 0xb8, 0x45, 0x7f, 0x48, 0x46, 0x30, 0xf7, 0x56, 0x5f, 0xd2, + 0xe7, 0x4e, 0x3e, 0x59, 0x7c, 0xc1, 0x20, 0x96, 0x5f, 0x18, 0xf1, 0x8b, + 0x2f, 0x9d, 0xcb, 0xf5, 0x96, 0x6a, 0x0f, 0x08, 0x52, 0x1b, 0x80, 0xc5, + 0x97, 0xf9, 0xa7, 0x9b, 0x25, 0x1b, 0xab, 0x2f, 0xdb, 0x80, 0x98, 0xe6, + 0x59, 0x46, 0x8a, 0x4f, 0x14, 0xf0, 0x5c, 0x06, 0xf7, 0xfc, 0x6c, 0xf7, + 0xcf, 0xbb, 0xde, 0x2c, 0xbf, 0x82, 0xfe, 0xf6, 0x0d, 0x65, 0x41, 0xf5, + 0x00, 0xf6, 0xff, 0x43, 0xeb, 0xcd, 0x38, 0xd2, 0x5c, 0xd3, 0x49, 0x28, + 0x67, 0x98, 0xd1, 0x9d, 0xee, 0x9e, 0xea, 0x46, 0xe6, 0x8e, 0xfe, 0xf8, + 0x27, 0xcf, 0x1a, 0xcb, 0xe0, 0x48, 0x1d, 0x59, 0x50, 0xac, 0x83, 0xe3, + 0x23, 0x2e, 0x9a, 0x30, 0x86, 0x42, 0x9b, 0x90, 0x90, 0xe9, 0x8c, 0xf9, + 0x75, 0xf1, 0x0f, 0xce, 0xb2, 0xf6, 0x3e, 0xea, 0xca, 0xc3, 0x7f, 0xa2, + 0x1b, 0xfb, 0x3f, 0x28, 0xef, 0x16, 0x5c, 0xd7, 0x16, 0x5b, 0x16, 0x5b, + 0xec, 0x35, 0x1d, 0x18, 0xbe, 0xfa, 0x4d, 0x9f, 0x96, 0x5f, 0xfd, 0xce, + 0x0c, 0xa0, 0x7e, 0x8e, 0xf1, 0x65, 0x42, 0x25, 0x30, 0x9c, 0x05, 0x37, + 0xfb, 0xc6, 0xf2, 0xe9, 0xec, 0xb2, 0xff, 0xdd, 0x3d, 0x3f, 0xfb, 0x9d, + 0xcf, 0x2c, 0xbf, 0xe3, 0x68, 0xb3, 0xa6, 0x0f, 0xd6, 0x5f, 0xc6, 0xc3, + 0x98, 0xda, 0x59, 0x7e, 0x3d, 0x0e, 0x36, 0x59, 0x7f, 0x9f, 0x5d, 0xe1, + 0x18, 0xab, 0x2f, 0xf9, 0xda, 0xee, 0x78, 0x70, 0xc5, 0x97, 0xff, 0xb9, + 0x82, 0xe0, 0x34, 0xe3, 0x8f, 0x42, 0xcb, 0xe1, 0x07, 0xe8, 0x59, 0x50, + 0xab, 0xce, 0x50, 0xe9, 0xf8, 0xb8, 0xcc, 0xf7, 0x50, 0xb4, 0x73, 0xe2, + 0xe7, 0x28, 0x23, 0x3e, 0x1c, 0x81, 0x26, 0xf4, 0xcf, 0xa5, 0x97, 0xfd, + 0xdf, 0x67, 0xe0, 0x8f, 0x84, 0x59, 0x7f, 0xff, 0xe7, 0x3e, 0xc7, 0x8b, + 0x3b, 0xe3, 0x12, 0x51, 0xbc, 0xcb, 0xf5, 0x97, 0xe0, 0xe3, 0x23, 0x8b, + 0x2f, 0x00, 0x86, 0xb2, 0xa1, 0x30, 0x8f, 0x87, 0x7c, 0x78, 0xed, 0x9f, + 0x93, 0xde, 0xc3, 0xd2, 0xcb, 0xfd, 0x23, 0x67, 0xbe, 0x7d, 0xd5, 0x97, + 0xcf, 0xa8, 0x92, 0xcb, 0xfe, 0x7f, 0xa7, 0x7d, 0xcc, 0x21, 0x56, 0x56, + 0xc8, 0xb0, 0xf8, 0x6d, 0x86, 0xfd, 0x21, 0xbe, 0xf6, 0xe9, 0xb1, 0x65, + 0xe2, 0x8f, 0xd6, 0x54, 0xe3, 0xc1, 0xe1, 0x2d, 0xfd, 0xd7, 0x63, 0x21, + 0x8b, 0x2e, 0x86, 0x61, 0xe8, 0xfc, 0x49, 0x7f, 0xe8, 0xfb, 0xd0, 0xc0, + 0xf8, 0xe6, 0x59, 0x7e, 0x7f, 0xff, 0xef, 0x96, 0x54, 0x2a, 0x63, 0xc8, + 0x7d, 0x9c, 0x34, 0x74, 0x5a, 0xe8, 0x17, 0xf1, 0x0f, 0x9c, 0x36, 0x2c, + 0xbf, 0x05, 0xf6, 0xc6, 0x96, 0x5f, 0xa3, 0x43, 0xc2, 0x59, 0x58, 0x7f, + 0x84, 0x5b, 0xd2, 0x9b, 0xf4, 0x4f, 0x9f, 0x80, 0xb2, 0xf8, 0xb4, 0x7e, + 0x59, 0x7a, 0x7e, 0x04, 0x59, 0x7f, 0xf4, 0x7f, 0x1e, 0x61, 0x38, 0xbe, + 0x35, 0x96, 0x9e, 0xd6, 0x5f, 0xde, 0x82, 0x69, 0xfe, 0x59, 0x7f, 0xe2, + 0x9a, 0x30, 0xb6, 0xcf, 0xba, 0xb2, 0x86, 0x7d, 0xae, 0x5d, 0x7f, 0xf1, + 0x8f, 0xd0, 0x18, 0xfd, 0xcb, 0xf5, 0x97, 0xcd, 0x3c, 0x31, 0x65, 0xf9, + 0xf9, 0xb1, 0xe9, 0x65, 0xf6, 0x8c, 0x1b, 0x2e, 0x2f, 0x55, 0xe0, 0x83, + 0x65, 0xc5, 0xea, 0xbf, 0xf6, 0x1e, 0xfc, 0x2c, 0x1b, 0xc9, 0x71, 0x7a, + 0xaf, 0xce, 0x5f, 0x49, 0xb8, 0xd1, 0x4b, 0xc3, 0x06, 0x8a, 0xe8, 0x69, + 0x8e, 0xb2, 0x19, 0xb7, 0xe1, 0x7a, 0xee, 0x4b, 0x2f, 0x10, 0x36, 0x59, + 0x4e, 0x78, 0x9d, 0x27, 0xa3, 0x44, 0x71, 0x36, 0xd4, 0x2b, 0xb2, 0x19, + 0x6e, 0x15, 0x19, 0x0e, 0x88, 0x18, 0x88, 0xf0, 0x83, 0xe1, 0x0f, 0x51, + 0x01, 0x1e, 0x6d, 0xfe, 0x27, 0x67, 0xa3, 0x06, 0xb2, 0xe9, 0xea, 0x79, + 0x59, 0x78, 0xe3, 0x4b, 0x2a, 0x7b, 0x37, 0x9e, 0x21, 0xbf, 0xfb, 0x46, + 0xc8, 0xdd, 0x2c, 0x67, 0xd0, 0xb2, 0xa4, 0x7d, 0x93, 0x13, 0x5e, 0x27, + 0x1a, 0xcb, 0xdc, 0xd9, 0xd6, 0x51, 0x9b, 0x81, 0x06, 0xef, 0xf7, 0x4f, + 0x50, 0x1c, 0xde, 0xb2, 0xe6, 0x1a, 0xca, 0x83, 0xca, 0xd8, 0xd2, 0xfd, + 0x8c, 0xd3, 0xfc, 0xb2, 0x86, 0x8b, 0x52, 0x6b, 0xe1, 0x15, 0xfa, 0x59, + 0xbe, 0x24, 0xb2, 0xf6, 0x6a, 0x16, 0x50, 0x55, 0x2e, 0x7d, 0x0d, 0x93, + 0x8c, 0x43, 0x45, 0xfe, 0x29, 0xbf, 0xf1, 0xcc, 0x6d, 0x41, 0x8f, 0x18, + 0xb2, 0xf3, 0xeb, 0x65, 0x94, 0xb2, 0xc4, 0x13, 0x51, 0x30, 0xed, 0xfd, + 0xe7, 0x9b, 0x51, 0xb2, 0xcb, 0xf8, 0xfe, 0xe7, 0xb0, 0x2b, 0x2f, 0xe8, + 0xdf, 0x9e, 0xf4, 0x2c, 0xbf, 0x67, 0x79, 0x93, 0x2c, 0xf1, 0xae, 0xbf, + 0xff, 0xff, 0x1e, 0x7d, 0xc3, 0xc6, 0xa7, 0x67, 0xd2, 0xe1, 0xf7, 0x87, + 0xf0, 0xc6, 0x0d, 0x96, 0x5f, 0xff, 0x8c, 0x87, 0x38, 0x4e, 0x9f, 0x1c, + 0xc8, 0x12, 0x59, 0x7c, 0xfb, 0x78, 0xd6, 0x56, 0x27, 0xd6, 0x64, 0xf3, + 0x17, 0x92, 0xe7, 0x4e, 0x37, 0xc2, 0x26, 0x7d, 0x5a, 0xf0, 0x35, 0x25, + 0x97, 0xfe, 0x63, 0x8f, 0x51, 0xdf, 0x60, 0xd6, 0x5f, 0xcf, 0xdf, 0xc4, + 0x29, 0x2c, 0xbf, 0xe1, 0xbb, 0xb5, 0xfb, 0xf7, 0x8b, 0x2f, 0xfc, 0x38, + 0xeb, 0xcb, 0x37, 0xb8, 0xd6, 0x5f, 0x8f, 0x6d, 0x8f, 0x4b, 0x2f, 0xd0, + 0x44, 0x06, 0x2c, 0xb4, 0xba, 0x79, 0xe2, 0x14, 0xd4, 0xe4, 0xde, 0xe0, + 0x77, 0x63, 0xe1, 0x4b, 0xd8, 0x74, 0xf0, 0x8e, 0xbf, 0xd1, 0x23, 0x6e, + 0xd3, 0x4d, 0x24, 0xbb, 0x3c, 0xb2, 0xf0, 0xb8, 0x35, 0x97, 0xf7, 0x9f, + 0x41, 0x8e, 0xac, 0xb9, 0xc6, 0xb2, 0xfe, 0xd3, 0xf3, 0xcf, 0x25, 0x96, + 0xf9, 0x65, 0xc6, 0xd2, 0xcb, 0x84, 0xfd, 0x65, 0x4c, 0x6c, 0x3f, 0x17, + 0xbd, 0x27, 0x1a, 0xcb, 0x9a, 0x69, 0x65, 0x49, 0x1b, 0xb8, 0x2c, 0x12, + 0xd3, 0x42, 0xe1, 0x1b, 0x43, 0x97, 0xba, 0x27, 0xe9, 0x1b, 0x9e, 0xb5, + 0xfe, 0xc6, 0x18, 0xf6, 0x07, 0x16, 0x5f, 0xff, 0xdf, 0xcf, 0xe6, 0xa3, + 0xa6, 0x13, 0x1e, 0xd0, 0x50, 0xb2, 0xf6, 0x7d, 0xd5, 0x96, 0xc2, 0x3f, + 0xae, 0x2e, 0xd7, 0x91, 0xa9, 0xc8, 0x57, 0x5f, 0xf1, 0x8f, 0x18, 0x73, + 0x1b, 0x4b, 0x2f, 0xff, 0xfa, 0x5e, 0x81, 0xc1, 0xfd, 0xfe, 0xb0, 0x71, + 0xa3, 0xfb, 0xf5, 0x97, 0xff, 0x9c, 0x79, 0x84, 0x2f, 0x39, 0x85, 0xfa, + 0xcb, 0xef, 0x01, 0xc5, 0x84, 0x75, 0xe1, 0xcb, 0x1a, 0x6f, 0x14, 0x79, + 0x65, 0xff, 0xfd, 0x27, 0xe7, 0x23, 0x36, 0x04, 0x87, 0xa2, 0x76, 0x96, + 0x5a, 0x06, 0x8b, 0xd1, 0x51, 0x8c, 0x6e, 0xb8, 0xa8, 0x17, 0xb1, 0xe8, + 0x5f, 0x4b, 0xc7, 0xc5, 0x97, 0xf3, 0xed, 0x98, 0x42, 0xac, 0xbf, 0x8d, + 0x81, 0xc2, 0x0a, 0xcb, 0xe7, 0xdb, 0x9e, 0x59, 0x5a, 0x3c, 0xfe, 0x16, + 0x54, 0xe5, 0xd8, 0x99, 0x0b, 0x0c, 0x77, 0xe8, 0xd8, 0x0e, 0x58, 0x59, + 0x15, 0xf0, 0x8b, 0xaf, 0x97, 0xed, 0xe2, 0x78, 0xfa, 0xb2, 0xff, 0xb3, + 0xbe, 0x8d, 0x77, 0x02, 0xb2, 0xe1, 0x5d, 0x65, 0xfb, 0xec, 0x20, 0x4c, + 0xb2, 0xed, 0x4c, 0xb2, 0xed, 0x42, 0xca, 0x83, 0x5d, 0x83, 0x15, 0x0c, + 0xc2, 0x29, 0x2b, 0xe4, 0xa5, 0x13, 0x8f, 0xa3, 0xcb, 0xef, 0x3b, 0x83, + 0xfc, 0x20, 0x88, 0xaf, 0xa7, 0x00, 0x17, 0x9f, 0x57, 0xbf, 0xef, 0x03, + 0xbe, 0xcd, 0x1f, 0x16, 0x5e, 0xe3, 0xcc, 0xb2, 0xff, 0xd9, 0xbe, 0x06, + 0x59, 0xdc, 0xfd, 0x65, 0xd8, 0x4b, 0x2d, 0x9f, 0x1e, 0xa6, 0xf3, 0xfb, + 0xf7, 0xbb, 0x12, 0x62, 0xcb, 0x84, 0xfd, 0x65, 0x68, 0xfa, 0xfc, 0x55, + 0xf9, 0x45, 0xff, 0xc6, 0x37, 0xe9, 0xf3, 0x25, 0x9e, 0x59, 0x73, 0xee, + 0x2c, 0xbd, 0x9f, 0x75, 0x65, 0xb3, 0xc7, 0xf1, 0xd4, 0x2d, 0xc1, 0x9b, + 0xff, 0xbf, 0x10, 0xa4, 0x59, 0xde, 0xe6, 0xcb, 0x2f, 0xda, 0x7d, 0xfb, + 0x8e, 0xb2, 0x86, 0xa9, 0x9b, 0xe3, 0x9d, 0x43, 0x70, 0xa1, 0x51, 0xd3, + 0x7d, 0xe8, 0xd7, 0xed, 0xf8, 0x46, 0x2a, 0xcb, 0xc4, 0x1d, 0xc5, 0x97, + 0xff, 0xf7, 0xdc, 0x27, 0xe7, 0xb2, 0x59, 0xe3, 0xd7, 0x9d, 0x65, 0xf6, + 0x7d, 0xb6, 0xf5, 0x97, 0xfc, 0x43, 0xc2, 0x04, 0xb3, 0x8b, 0x2f, 0xbe, + 0xe0, 0x3f, 0x59, 0x71, 0x05, 0x65, 0x41, 0xbb, 0x72, 0x5b, 0xfc, 0x27, + 0x4b, 0x36, 0x3d, 0x2c, 0xbe, 0x7d, 0xcc, 0x25, 0x97, 0x8f, 0x86, 0xb2, + 0xb8, 0x6f, 0xba, 0x47, 0x50, 0x9f, 0x66, 0x14, 0x98, 0xfe, 0x96, 0x9c, + 0x9b, 0xf7, 0x22, 0x1f, 0x03, 0x95, 0xff, 0xdb, 0x40, 0x9f, 0x82, 0x52, + 0x7f, 0x01, 0x65, 0xff, 0x78, 0xc6, 0x6d, 0xb8, 0x23, 0x59, 0x7f, 0x9e, + 0x58, 0x3d, 0x73, 0x8b, 0x2f, 0xa3, 0x04, 0x1a, 0xcb, 0xff, 0xfa, 0x45, + 0x19, 0xcc, 0x2e, 0xfb, 0x06, 0x0e, 0xf1, 0x65, 0xfe, 0x14, 0xfa, 0xfe, + 0x6b, 0x75, 0x65, 0xfd, 0x1b, 0x4f, 0xe6, 0xa1, 0x65, 0xfe, 0xc9, 0xbd, + 0x9c, 0x72, 0x59, 0x7f, 0xe1, 0xe7, 0xd2, 0xe7, 0x78, 0xff, 0xac, 0xad, + 0x1f, 0x98, 0x0c, 0xaf, 0xb9, 0xcc, 0x69, 0x65, 0xfe, 0x68, 0x7e, 0xcd, + 0xf9, 0xa5, 0x97, 0xff, 0x1b, 0x27, 0x07, 0xc7, 0xe0, 0x9f, 0x16, 0x5f, + 0xec, 0x96, 0x01, 0xb6, 0xe4, 0xb2, 0xff, 0xe8, 0x97, 0xa3, 0xee, 0xc7, + 0x9f, 0x75, 0x65, 0x42, 0x3e, 0x34, 0x6d, 0xe4, 0x62, 0x35, 0xbf, 0xff, + 0xc6, 0x59, 0xf7, 0x45, 0x7e, 0xfa, 0x3d, 0x9b, 0xde, 0x4b, 0x28, 0x6a, + 0xd8, 0xc2, 0x67, 0xf1, 0x11, 0xac, 0xcc, 0x71, 0xa8, 0x51, 0xf8, 0x8b, + 0xb1, 0x8d, 0xef, 0x3b, 0xb8, 0xa1, 0x65, 0xfc, 0x3e, 0x7a, 0x1a, 0xe2, + 0xcb, 0xfe, 0x19, 0xea, 0x37, 0xff, 0xc9, 0x2c, 0xba, 0x18, 0xb2, 0xa0, + 0xf4, 0xbc, 0x79, 0x5e, 0x45, 0xbb, 0x8a, 0xf6, 0x10, 0x37, 0xfd, 0xc7, + 0xf1, 0x8c, 0x27, 0xb2, 0xcb, 0xfc, 0xf2, 0xc1, 0xf3, 0x90, 0xb2, 0xfe, + 0xef, 0xa1, 0x93, 0x12, 0xca, 0x84, 0x4c, 0xe1, 0xcf, 0xe6, 0x55, 0x0b, + 0xbb, 0xd9, 0x2e, 0x75, 0xe3, 0x45, 0x04, 0x33, 0x6f, 0x41, 0x75, 0x65, + 0xff, 0xf4, 0x6b, 0x37, 0x9f, 0x7f, 0x01, 0x70, 0xc6, 0xb2, 0xfc, 0x7a, + 0x94, 0xd0, 0xb2, 0xff, 0xef, 0x61, 0x38, 0xbe, 0x3e, 0xc8, 0xd6, 0x58, + 0xfc, 0x7d, 0x7c, 0x28, 0xbf, 0xff, 0x18, 0x02, 0x27, 0xdf, 0xbf, 0x1f, + 0x47, 0xf7, 0xeb, 0x2f, 0xe0, 0x77, 0x91, 0xfe, 0xcb, 0x2f, 0x9f, 0x51, + 0x25, 0x97, 0xff, 0xf1, 0x93, 0x99, 0x7f, 0xa8, 0xf3, 0x9f, 0x63, 0xcb, + 0x28, 0x68, 0xa8, 0x23, 0x0e, 0x90, 0xdf, 0x8f, 0xbc, 0x06, 0xcb, 0x2f, + 0xff, 0x11, 0xff, 0xdf, 0x82, 0x7a, 0x12, 0x06, 0xb2, 0xff, 0xbc, 0x20, + 0xc1, 0xde, 0x7f, 0x0b, 0x2a, 0x11, 0x10, 0xe9, 0x94, 0x35, 0x40, 0x67, + 0x18, 0x13, 0x0b, 0xfb, 0x0b, 0x1b, 0xf9, 0xff, 0x73, 0x19, 0xac, 0xbd, + 0x0c, 0x85, 0x95, 0xa3, 0xc7, 0xde, 0x59, 0x7e, 0xd0, 0x03, 0xe8, 0x59, + 0x7f, 0x6a, 0x69, 0x1f, 0x78, 0xb2, 0xc5, 0x07, 0xaf, 0x85, 0x17, 0xd9, + 0xa8, 0xe2, 0xcb, 0xfd, 0x33, 0xcc, 0xff, 0x4b, 0x8b, 0x2f, 0xe7, 0xd8, + 0xe6, 0x36, 0x96, 0x5f, 0xff, 0xe7, 0x6b, 0xb9, 0xe9, 0xc3, 0xc9, 0xa4, + 0xfa, 0xdb, 0xc0, 0x59, 0x50, 0x8e, 0xcc, 0x21, 0x73, 0x62, 0x2f, 0xbf, + 0x61, 0x6d, 0x8d, 0x2c, 0xbf, 0x3b, 0x3c, 0x6d, 0x2c, 0xad, 0x8f, 0x40, + 0x65, 0x17, 0xfd, 0x1b, 0x09, 0x2f, 0x01, 0xf6, 0x59, 0x7f, 0xc6, 0x1c, + 0x99, 0xcb, 0x66, 0xca, 0xca, 0xc4, 0x4e, 0x68, 0x8c, 0x8f, 0x2d, 0x3d, + 0xac, 0xbf, 0x46, 0xb5, 0x9e, 0x59, 0x79, 0xe5, 0xd5, 0x97, 0xff, 0xd0, + 0x2b, 0x58, 0x7b, 0xda, 0x71, 0xc6, 0x31, 0x65, 0xfc, 0x4e, 0x2c, 0x32, + 0x16, 0x53, 0x65, 0x1a, 0xf2, 0x16, 0x61, 0x39, 0x0e, 0x01, 0x4a, 0xf6, + 0xc2, 0x8a, 0xb2, 0xfe, 0x90, 0x06, 0xdc, 0x5d, 0xd5, 0x97, 0xf3, 0x1f, + 0xe9, 0xeb, 0x3c, 0xb2, 0xf3, 0x4f, 0xc5, 0x97, 0xf3, 0x23, 0x0b, 0xbc, + 0x59, 0x7e, 0x8d, 0xb2, 0x46, 0xb2, 0xf0, 0x83, 0x6f, 0x88, 0xa3, 0xf1, + 0x9f, 0x07, 0x7a, 0x59, 0x53, 0x26, 0x53, 0xe8, 0x6c, 0x5f, 0xfe, 0x3e, + 0x60, 0xe7, 0xdc, 0x70, 0x51, 0xfa, 0xca, 0x84, 0xef, 0x87, 0x1a, 0x48, + 0x4a, 0xaf, 0xff, 0xc6, 0x18, 0x67, 0x1f, 0x58, 0xc0, 0x05, 0xe4, 0xb2, + 0xe3, 0xde, 0xb2, 0xff, 0x41, 0x3f, 0x80, 0x5b, 0x2c, 0xbf, 0xf1, 0x8b, + 0x03, 0x32, 0x7d, 0x0a, 0xb2, 0xff, 0xf6, 0x7d, 0xdf, 0xba, 0x6d, 0x60, + 0xde, 0x4b, 0x2b, 0x11, 0x15, 0xf9, 0xf5, 0xbc, 0xb2, 0xa4, 0x9a, 0xe6, + 0xea, 0xa3, 0x06, 0x0a, 0x17, 0x80, 0x23, 0xbc, 0x38, 0x62, 0xcb, 0xfb, + 0xfc, 0x9a, 0x3e, 0xe2, 0xcb, 0xff, 0xfc, 0x4e, 0xd7, 0xb1, 0x8e, 0xd7, + 0x79, 0x8d, 0x0c, 0xd8, 0xb2, 0xff, 0x77, 0x35, 0x93, 0x40, 0xab, 0x2b, + 0x11, 0x8d, 0x31, 0x87, 0x58, 0xaf, 0xf0, 0xa5, 0x9b, 0x6c, 0x02, 0x59, + 0x50, 0x9a, 0x3e, 0x43, 0x88, 0x06, 0x17, 0xf7, 0x9e, 0x51, 0xc9, 0x2c, + 0xbf, 0xec, 0xf1, 0xfd, 0xcf, 0x03, 0x8b, 0x2e, 0xc2, 0x09, 0xf3, 0xfe, + 0x5b, 0x7f, 0xef, 0xa5, 0x1d, 0xec, 0x3b, 0x36, 0x59, 0x7f, 0xfb, 0x3c, + 0xee, 0xc2, 0x7d, 0x1c, 0x71, 0x65, 0xb1, 0x88, 0x86, 0xde, 0x81, 0x7e, + 0x7f, 0xc7, 0x84, 0xb2, 0xbc, 0x7a, 0x24, 0x55, 0x7e, 0x39, 0xfc, 0xd0, + 0xab, 0x2e, 0x78, 0x59, 0x70, 0xb2, 0x59, 0x7e, 0xf1, 0xef, 0x30, 0xac, + 0xbb, 0x3d, 0x23, 0xc1, 0x08, 0xc5, 0x31, 0x15, 0xdc, 0x2b, 0x9f, 0x56, + 0xbc, 0xd3, 0x4d, 0x24, 0xbf, 0xf6, 0x88, 0x0e, 0x26, 0x4d, 0x0c, 0x58, + 0xdc, 0xd0, 0x5f, 0xbc, 0x73, 0x1b, 0x4b, 0x2c, 0x15, 0x95, 0x08, 0x8b, + 0xc5, 0x1d, 0x14, 0xdf, 0xe8, 0x1e, 0x4a, 0x08, 0x2b, 0x2a, 0x1b, 0x02, + 0x8d, 0x86, 0xe5, 0x0b, 0xcc, 0x94, 0x54, 0x70, 0x90, 0xdd, 0x7b, 0x9a, + 0x31, 0x1d, 0x46, 0x4a, 0xc8, 0xc1, 0x7d, 0x28, 0x2d, 0xe3, 0x92, 0x28, + 0xe6, 0x79, 0x09, 0xbe, 0xc6, 0x08, 0x08, 0x67, 0xb5, 0x0d, 0xe1, 0x0b, + 0xaf, 0xcc, 0x3d, 0x3f, 0xeb, 0x2f, 0xb5, 0xac, 0xf2, 0xcb, 0xfb, 0xc7, + 0xb9, 0xb8, 0x01, 0x56, 0x54, 0x1e, 0xb8, 0x48, 0xac, 0xdb, 0x59, 0x63, + 0x59, 0x7f, 0xdd, 0x8d, 0x73, 0xd0, 0x5d, 0x59, 0x66, 0xda, 0xcb, 0xfe, + 0xec, 0x6b, 0x9e, 0x82, 0xea, 0xcb, 0xb9, 0x0b, 0x2f, 0x30, 0x70, 0xb2, + 0xff, 0x17, 0x5e, 0x6e, 0x47, 0x56, 0x51, 0x9e, 0x7b, 0x8e, 0x5f, 0x1b, + 0xe0, 0x56, 0x5f, 0xfb, 0x37, 0x96, 0x72, 0x62, 0x81, 0xac, 0xbe, 0x08, + 0xe3, 0x65, 0x97, 0x7f, 0x0b, 0x2f, 0x70, 0xf4, 0xb2, 0xb0, 0xf5, 0xbf, + 0x23, 0xe8, 0xbd, 0xfe, 0x96, 0x11, 0x8f, 0x09, 0x65, 0xee, 0xf1, 0xbc, + 0x27, 0xcd, 0x82, 0xb3, 0x1c, 0x79, 0x97, 0xf2, 0x02, 0x20, 0xe4, 0x25, + 0x7a, 0x5f, 0x77, 0x21, 0x65, 0xe6, 0x0e, 0x16, 0x5f, 0xe2, 0xeb, 0xcd, + 0xc8, 0xea, 0xca, 0x33, 0xcf, 0x71, 0xcb, 0xe3, 0x7c, 0x0a, 0xcb, 0xff, + 0x66, 0xf2, 0xce, 0x4c, 0x50, 0x35, 0x97, 0xc1, 0x1c, 0x6c, 0xb2, 0xfe, + 0x9b, 0xf3, 0xdd, 0x79, 0x96, 0x5d, 0xfc, 0x2c, 0xbd, 0xc3, 0xd2, 0xca, + 0xc4, 0x44, 0x84, 0x8f, 0xf3, 0x3e, 0x8b, 0xdf, 0xe9, 0x61, 0x18, 0xf0, + 0x96, 0x5f, 0xc4, 0xe3, 0xc1, 0x5b, 0xc2, 0xe5, 0x38, 0xc5, 0x70, 0x40, + 0x32, 0x87, 0xa6, 0x84, 0x57, 0x99, 0x7f, 0x20, 0x22, 0x0e, 0x43, 0x23, + 0xa7, 0xb7, 0xff, 0x6a, 0x3e, 0xe1, 0x43, 0x33, 0xee, 0xac, 0xbf, 0xff, + 0x38, 0xf3, 0x08, 0x51, 0x0a, 0x0b, 0x63, 0xd2, 0xcb, 0xcf, 0xad, 0x97, + 0x20, 0x92, 0xfa, 0x59, 0xf4, 0x97, 0x20, 0x92, 0xf7, 0x0c, 0x6b, 0x90, + 0x49, 0x73, 0x4d, 0x2e, 0x41, 0x25, 0x05, 0x15, 0x33, 0x15, 0x74, 0xc1, + 0xa2, 0x9b, 0x9f, 0xa9, 0x90, 0x48, 0x6e, 0x6f, 0xef, 0xff, 0xfe, 0x19, + 0x40, 0xfd, 0x1d, 0xe4, 0x76, 0x07, 0x1d, 0xc6, 0x7e, 0xd2, 0xcb, 0xfb, + 0x3d, 0x98, 0x42, 0xac, 0xbc, 0xe5, 0xfc, 0xe5, 0xfd, 0xf8, 0x9c, 0xc8, + 0xd4, 0x30, 0x98, 0x8a, 0x51, 0x9e, 0x70, 0xdb, 0xae, 0x57, 0xd3, 0xf8, + 0xfe, 0x59, 0x6c, 0xdd, 0x45, 0x7f, 0xf0, 0x8b, 0xb4, 0x96, 0x5f, 0xd2, + 0x06, 0xdd, 0x3d, 0x92, 0x5f, 0x38, 0xfd, 0x8b, 0x2f, 0x0c, 0xd8, 0xb2, + 0xff, 0xde, 0x8e, 0x99, 0x43, 0x23, 0x8b, 0x28, 0x53, 0xf9, 0x72, 0x1e, + 0x0e, 0x54, 0xc8, 0xd8, 0xf4, 0x2a, 0x6f, 0xfa, 0x0a, 0x45, 0x1f, 0xbb, + 0x16, 0x5f, 0xc1, 0x3e, 0x77, 0x3c, 0xb2, 0xfa, 0x3c, 0x0d, 0x2c, 0xbd, + 0xe7, 0xea, 0xca, 0x98, 0xdf, 0x70, 0x8a, 0xd0, 0xb2, 0xff, 0x1c, 0x80, + 0xfc, 0xf1, 0xac, 0xbf, 0xb3, 0x7b, 0xb3, 0x3c, 0xb2, 0xd2, 0x49, 0x79, + 0xf5, 0xb2, 0x4a, 0x49, 0x50, 0x6e, 0x42, 0x22, 0xc1, 0xdb, 0xd1, 0xf8, + 0x52, 0x37, 0x35, 0xb4, 0xc4, 0x61, 0x94, 0x25, 0x2f, 0x14, 0xbc, 0xb2, + 0xa1, 0x33, 0x8c, 0x87, 0xab, 0x09, 0xef, 0xf3, 0xed, 0x85, 0xb1, 0xe9, + 0x65, 0xfb, 0x5f, 0xb0, 0xf8, 0xb2, 0xfe, 0x10, 0xa5, 0xcf, 0x0d, 0x66, + 0x1a, 0x9b, 0xfb, 0xbc, 0xfd, 0xdf, 0x4b, 0x2f, 0xa0, 0x27, 0xc5, 0x97, + 0xc0, 0x7d, 0x49, 0x65, 0x98, 0xb2, 0xd2, 0x19, 0xb3, 0x98, 0x8a, 0xa1, + 0x15, 0x5b, 0x17, 0x62, 0xa5, 0xff, 0xd9, 0xf7, 0x78, 0x65, 0x1f, 0xbb, + 0x16, 0x5e, 0x94, 0x7c, 0xb2, 0xfb, 0x26, 0x36, 0x2c, 0xad, 0x91, 0x07, + 0x32, 0x23, 0x43, 0xb7, 0xe8, 0x9a, 0x51, 0xfa, 0x4b, 0xd8, 0x5f, 0xac, + 0xbf, 0xff, 0x7b, 0x0e, 0x5c, 0x9d, 0xdf, 0x60, 0xc1, 0xde, 0x2c, 0xa0, + 0xa2, 0x77, 0x45, 0x3d, 0x1c, 0xbf, 0xef, 0xdc, 0xbb, 0x36, 0x6b, 0x16, + 0x58, 0xd6, 0x5f, 0xf6, 0xc0, 0x90, 0xfc, 0x07, 0x25, 0x97, 0xf1, 0xc3, + 0x36, 0xc6, 0x96, 0x5f, 0xb3, 0x66, 0x63, 0x16, 0x5e, 0xe1, 0xe9, 0x65, + 0x11, 0xe2, 0x74, 0xa2, 0xfe, 0x27, 0xff, 0xf0, 0x71, 0x65, 0xfc, 0x62, + 0x89, 0x0c, 0x6e, 0x34, 0xc3, 0x30, 0x43, 0x47, 0x5c, 0x72, 0xe9, 0x0d, + 0x62, 0xab, 0x53, 0x86, 0x83, 0x98, 0x14, 0x68, 0xb7, 0xf7, 0xbd, 0x9b, + 0x77, 0x8b, 0x2f, 0x66, 0xc0, 0x59, 0x58, 0x79, 0x84, 0x5f, 0x7f, 0xd9, + 0xd9, 0xc5, 0x9f, 0x89, 0xf2, 0xca, 0x86, 0xc8, 0xaa, 0x54, 0x90, 0xd1, + 0x98, 0xe4, 0x3a, 0x45, 0x29, 0xf8, 0xdc, 0xda, 0xf4, 0x45, 0xe8, 0xd2, + 0x1c, 0xd3, 0xf5, 0xc2, 0x87, 0x47, 0x25, 0xb3, 0xf6, 0x12, 0x53, 0xe4, + 0x17, 0xff, 0xed, 0xb5, 0xac, 0x1b, 0xb3, 0xcf, 0xd2, 0x89, 0x96, 0x5f, + 0xff, 0xfe, 0x06, 0x81, 0x31, 0x3f, 0xfc, 0xe6, 0x0b, 0x80, 0xd3, 0x8e, + 0x3d, 0x0b, 0x2f, 0xe0, 0x46, 0xb4, 0x7b, 0x2c, 0xbf, 0xa5, 0x1f, 0xf8, + 0xda, 0x59, 0x7f, 0x38, 0xfd, 0x04, 0x05, 0x97, 0xc1, 0x17, 0x5f, 0xac, + 0xb3, 0x71, 0xa2, 0xba, 0x62, 0xe6, 0x17, 0x91, 0x65, 0xf4, 0x6e, 0x8e, + 0x16, 0x5f, 0x6e, 0x78, 0x02, 0x2c, 0xbf, 0xe8, 0x98, 0x02, 0x70, 0xf0, + 0x96, 0x5e, 0x69, 0xa6, 0x92, 0x5d, 0x28, 0x48, 0xdc, 0xd0, 0x5f, 0x77, + 0xd1, 0xfa, 0xcb, 0xbd, 0xa5, 0x97, 0xbd, 0x3d, 0xb4, 0xb2, 0xfa, 0x6c, + 0xc0, 0xac, 0xa8, 0x56, 0x1b, 0xb2, 0xb6, 0x46, 0x4e, 0x14, 0x5f, 0x89, + 0x26, 0x27, 0xd2, 0xcb, 0x94, 0x74, 0x8f, 0x78, 0xbb, 0x44, 0x77, 0xc3, + 0xcc, 0xd2, 0xcb, 0xff, 0xdd, 0x9b, 0x35, 0xa7, 0x9b, 0x35, 0x9d, 0x59, + 0x58, 0x7d, 0x86, 0x43, 0x7f, 0x0b, 0x1a, 0xd6, 0x79, 0x65, 0xfd, 0xb4, + 0xd2, 0x9e, 0xf5, 0xb2, 0xcb, 0xff, 0xdf, 0xb1, 0xe5, 0x3a, 0x07, 0xb3, + 0x23, 0x4b, 0x2f, 0x8e, 0x0b, 0xab, 0x2f, 0x6c, 0xe4, 0xb2, 0xb4, 0x88, + 0xaf, 0x27, 0x74, 0x82, 0xff, 0xc2, 0x83, 0x92, 0x3d, 0x6c, 0x0f, 0xd6, + 0x5c, 0x58, 0xb2, 0xf3, 0xfc, 0xd2, 0xca, 0x14, 0xfd, 0x8d, 0x0f, 0xc2, + 0xb7, 0x8e, 0x18, 0xb2, 0xe7, 0x1a, 0xcb, 0x6d, 0xa3, 0x63, 0xd1, 0xbb, + 0xef, 0x0f, 0x09, 0x65, 0xff, 0xf4, 0x09, 0xd2, 0x8f, 0xdf, 0xbf, 0x88, + 0x52, 0x59, 0x4d, 0x85, 0x4c, 0x59, 0x0c, 0x13, 0x85, 0x33, 0xb0, 0x70, + 0xa3, 0xa4, 0x37, 0xc2, 0x0f, 0xc6, 0xb2, 0xff, 0xcf, 0xde, 0x35, 0x9e, + 0xfd, 0xfa, 0xb2, 0xfd, 0xfe, 0x0d, 0xe4, 0xb2, 0xff, 0xff, 0xd9, 0xd8, + 0x09, 0x66, 0x14, 0x4b, 0xbe, 0x76, 0x16, 0x6f, 0x59, 0x5b, 0x23, 0xd0, + 0x64, 0x7a, 0x40, 0x68, 0xa2, 0xf0, 0x83, 0x99, 0x65, 0xe9, 0x19, 0x2c, + 0xbe, 0xd8, 0x4d, 0xc6, 0x96, 0x5f, 0xf4, 0x79, 0xcf, 0xb3, 0x1b, 0x4b, + 0x2f, 0xfd, 0xfe, 0x68, 0xff, 0xcf, 0xf4, 0xc5, 0x97, 0xff, 0x85, 0x8e, + 0x6a, 0x3c, 0x59, 0xd8, 0x62, 0xcb, 0x80, 0x22, 0xcb, 0xfc, 0x59, 0xbc, + 0xe6, 0x36, 0x96, 0x57, 0x8f, 0x37, 0x83, 0x17, 0xff, 0xf0, 0x1f, 0x60, + 0x83, 0x6e, 0xe7, 0x8b, 0x3b, 0xe3, 0x59, 0x7e, 0x89, 0x88, 0x1c, 0x59, + 0x58, 0x99, 0xe1, 0xc2, 0x5b, 0xc4, 0x5d, 0x5c, 0xb8, 0xc9, 0x65, 0xfc, + 0xe5, 0xfe, 0x74, 0x55, 0x95, 0x0a, 0xb7, 0x36, 0x3f, 0x90, 0xfb, 0x8d, + 0x91, 0x4f, 0x0e, 0x7b, 0x1b, 0xee, 0xf4, 0x19, 0xf1, 0x5b, 0xe2, 0xec, + 0x4f, 0xac, 0xbf, 0x6f, 0x8d, 0x3f, 0x96, 0x5f, 0x78, 0x0c, 0x85, 0x94, + 0xb2, 0x96, 0x59, 0xd8, 0x5b, 0x70, 0x2e, 0xe0, 0x36, 0xd6, 0x53, 0x61, + 0x18, 0xa6, 0x48, 0xe5, 0x24, 0x70, 0x02, 0x5b, 0x37, 0x6c, 0x42, 0x2d, + 0xa9, 0xb3, 0x0c, 0xa9, 0xe6, 0x14, 0x53, 0xdc, 0x29, 0x9b, 0x54, 0xb2, + 0x78, 0x92, 0x36, 0xb9, 0x41, 0xed, 0xa4, 0x22, 0xe7, 0x81, 0x64, 0x4f, + 0x5a, 0x6d, 0x2e, 0x96, 0x52, 0xc8, 0x87, 0x3c, 0x61, 0x94, 0xe3, 0xd1, + 0x63, 0xcf, 0x0c, 0xb4, 0x2f, 0xa5, 0x6c, 0x1c, 0xb9, 0x4d, 0xd8, 0x62, + 0xcd, 0x2c, 0x4f, 0x53, 0x81, 0x4c, 0x9c, 0x80, 0xf5, 0x3c, 0x49, 0xe5, + 0x8d, 0xff, 0x29, 0x45, 0xb6, 0xdc, 0x53, 0xc4, 0xdc, 0xac, 0x1d, 0xfb, + 0x59, 0xb1, 0x02, 0x70, 0x13, 0x7c, 0x61, 0xed, 0x10, 0x4f, 0xca, 0x90, + 0x12, 0x70, 0xd3, 0x72, 0x34, 0xeb, 0xff, 0xdb, 0x32, 0x3e, 0x6e, 0xd8, + 0x6d, 0x3a, 0x4f, 0xfa, 0xca, 0x75, 0x69, 0x00, 0x95, 0xe1, 0x7e, 0x66, + 0x1b, 0xfe, 0xb2, 0xf8, 0x2e, 0xe1, 0x54, 0x51, 0x4b, 0xff, 0xf3, 0xff, + 0x01, 0xf1, 0xf3, 0xc7, 0xdf, 0x61, 0x2c, 0xad, 0x22, 0x08, 0x8b, 0xaf, + 0xfd, 0x1d, 0x3f, 0xe2, 0x5d, 0xe4, 0xf4, 0xb2, 0xf3, 0xc9, 0xbc, 0x26, + 0x1f, 0x90, 0xa9, 0xd1, 0x15, 0xff, 0xd0, 0xfa, 0x20, 0x3f, 0xd9, 0xe7, + 0x59, 0x7f, 0xfe, 0x19, 0xcc, 0x51, 0xde, 0x77, 0xe0, 0x99, 0x6c, 0xb2, + 0xcd, 0xd8, 0x8e, 0x0f, 0x23, 0xf1, 0x0a, 0xff, 0x37, 0xcd, 0x05, 0xdc, + 0x2a, 0x8b, 0xa1, 0x7f, 0xf3, 0x77, 0x93, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, + 0x92, 0xd7, 0xed, 0x05, 0xdc, 0x2a, 0x8b, 0xc9, 0x73, 0xef, 0x59, 0x66, + 0xf8, 0x79, 0x93, 0xe6, 0x95, 0x10, 0x9a, 0xf1, 0xda, 0x16, 0x72, 0x36, + 0xc8, 0x4e, 0x06, 0x16, 0x13, 0x23, 0x31, 0x07, 0xd3, 0xaa, 0xee, 0x7e, + 0x58, 0x8f, 0x40, 0xe4, 0xa1, 0xae, 0x9c, 0x01, 0x07, 0x7c, 0x28, 0x2f, + 0xff, 0x37, 0x63, 0xc9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4b, 0x2b, 0xe6, + 0xc0, 0xb3, 0xed, 0x85, 0x96, 0x9e, 0xd6, 0x5b, 0x71, 0x65, 0xbc, 0xb2, + 0x9b, 0x06, 0xee, 0x79, 0x16, 0x00, 0xa5, 0xfe, 0x31, 0xfb, 0x0c, 0x85, + 0x59, 0x7d, 0xe7, 0xe8, 0x16, 0x5f, 0x7a, 0x37, 0x58, 0xb2, 0xff, 0xd9, + 0xe3, 0x91, 0x66, 0x01, 0xb6, 0xb2, 0xff, 0xd8, 0x7b, 0xcc, 0x81, 0x2c, + 0xe2, 0xcb, 0xc0, 0x0b, 0xac, 0xa9, 0x26, 0x26, 0x66, 0x5b, 0xa4, 0x4c, + 0x25, 0x24, 0x01, 0x0f, 0xaf, 0xe1, 0xf4, 0x0f, 0x2e, 0x2c, 0xbe, 0x3d, + 0xd8, 0xd9, 0x65, 0xfc, 0xc8, 0x21, 0x73, 0x65, 0x97, 0x3b, 0x16, 0x56, + 0x1e, 0x27, 0x4b, 0xaf, 0xc0, 0xe9, 0xfa, 0x16, 0x5f, 0xfb, 0xd1, 0xe3, + 0x17, 0xbc, 0x7f, 0xd6, 0x5f, 0xf3, 0xcb, 0x80, 0xec, 0x77, 0x8b, 0x2f, + 0xe2, 0xee, 0xa1, 0xc2, 0xb2, 0xe7, 0xe2, 0xcb, 0xff, 0xf3, 0xbc, 0x9f, + 0xd3, 0xb3, 0x60, 0x74, 0xfd, 0x0b, 0x2b, 0x63, 0xec, 0x98, 0x5a, 0xf3, + 0xb8, 0x55, 0x11, 0x9a, 0xff, 0xff, 0xec, 0xef, 0x3b, 0xd3, 0xf7, 0xd2, + 0xe1, 0x03, 0x05, 0x14, 0xde, 0x4b, 0x28, 0x28, 0x9c, 0xd1, 0x45, 0xfc, + 0x70, 0xcd, 0xb1, 0xa5, 0x97, 0x14, 0x96, 0x59, 0x8b, 0x2f, 0xc7, 0xaf, + 0x3b, 0x7d, 0x1e, 0xab, 0x0b, 0x84, 0x16, 0xb7, 0x96, 0x5d, 0x1a, 0x59, + 0x63, 0x09, 0xa7, 0xde, 0x23, 0x7c, 0x7a, 0x7d, 0x2c, 0xbb, 0x9b, 0xab, + 0x2c, 0xf2, 0x37, 0x7d, 0x21, 0xbf, 0xfb, 0x86, 0x0e, 0x61, 0x02, 0x59, + 0xc5, 0x97, 0x47, 0xcb, 0x2f, 0x19, 0x05, 0x65, 0x70, 0xd9, 0x74, 0x5e, + 0xff, 0xf9, 0xb6, 0x7d, 0xe1, 0xf4, 0xa0, 0x0f, 0xa1, 0x56, 0x56, 0xcb, + 0x8a, 0xf2, 0x27, 0x1a, 0x06, 0x1c, 0x9c, 0x26, 0x35, 0x0c, 0x06, 0x3c, + 0x79, 0xd4, 0x98, 0xf8, 0x4d, 0xd7, 0x50, 0x10, 0xdf, 0xf6, 0x9d, 0xaf, + 0x1c, 0x6a, 0x16, 0x5f, 0xff, 0xef, 0x1f, 0x7a, 0x7c, 0x13, 0x68, 0xd8, + 0xb3, 0xf0, 0x71, 0x65, 0xfb, 0xa6, 0xc8, 0x99, 0x65, 0xfd, 0x91, 0x3a, + 0x38, 0xc5, 0x96, 0x96, 0x1e, 0xb7, 0x0a, 0x2f, 0xde, 0xc2, 0x76, 0x2c, + 0xbe, 0xfb, 0xd1, 0xc5, 0x97, 0xa6, 0x72, 0x59, 0x7a, 0x3b, 0xc5, 0x97, + 0xfe, 0xc1, 0x79, 0x18, 0x43, 0xf4, 0x2c, 0xae, 0x1e, 0xc8, 0x07, 0x2a, + 0x48, 0xe0, 0xc2, 0x69, 0x88, 0xf8, 0xeb, 0x7f, 0xf7, 0x4f, 0x4f, 0xfc, + 0xef, 0xa5, 0x0c, 0x59, 0x7e, 0x28, 0x97, 0x1d, 0x65, 0x19, 0xf7, 0x6f, + 0x48, 0xb7, 0x96, 0x5f, 0xff, 0xfd, 0xd3, 0x66, 0x8f, 0x3e, 0x20, 0x76, + 0x3d, 0x3b, 0x79, 0xf6, 0x68, 0x59, 0x58, 0x88, 0x7e, 0x88, 0xdf, 0xf4, + 0x33, 0xcf, 0xd7, 0x2f, 0xd6, 0x56, 0x1e, 0xd1, 0x91, 0x5f, 0xd2, 0xe7, + 0x1c, 0xa4, 0xb2, 0xf6, 0xdc, 0xe2, 0xca, 0x73, 0xcb, 0x22, 0xdb, 0x9c, + 0x6b, 0x2f, 0xd2, 0x72, 0x00, 0x56, 0x50, 0x4d, 0xef, 0xc2, 0xd7, 0x38, + 0x56, 0x5d, 0x1b, 0xab, 0x2b, 0x63, 0x5e, 0xdb, 0x16, 0xbf, 0xf7, 0x5c, + 0xbf, 0x9c, 0x26, 0x7c, 0xd2, 0xcb, 0xee, 0xf6, 0x18, 0xb2, 0xfa, 0x5d, + 0x3d, 0x96, 0x56, 0x22, 0x24, 0x54, 0x47, 0x22, 0xbc, 0x58, 0x15, 0x97, + 0xff, 0xbb, 0x01, 0x3e, 0x9b, 0x26, 0xe1, 0x92, 0xcb, 0xfc, 0xcc, 0xd7, + 0xe4, 0x0f, 0xd6, 0x5a, 0x4b, 0x2b, 0x11, 0x54, 0x63, 0x6e, 0x92, 0x21, + 0xad, 0xfb, 0xf8, 0xec, 0x0d, 0x65, 0x42, 0xa3, 0xcc, 0x51, 0x38, 0x57, + 0xbc, 0x36, 0x40, 0x77, 0x7f, 0xfc, 0x0e, 0xf3, 0xe9, 0x41, 0x7f, 0xc9, + 0xc0, 0x25, 0x97, 0xa3, 0xb0, 0xb2, 0x86, 0x7d, 0xfb, 0xd5, 0x2f, 0x98, + 0x0e, 0x81, 0x65, 0xef, 0x61, 0x2c, 0xa3, 0x3e, 0x2f, 0x12, 0x00, 0x8e, + 0xef, 0x4c, 0xb2, 0xfb, 0x26, 0xc9, 0x96, 0x5f, 0xb4, 0x0e, 0xe6, 0xcb, + 0x2f, 0xb3, 0x43, 0x85, 0x96, 0xe4, 0xe3, 0xed, 0x82, 0x32, 0x29, 0xb7, + 0x96, 0x57, 0xc7, 0x8c, 0xe6, 0xb4, 0x68, 0xed, 0x78, 0x65, 0xdf, 0xf7, + 0x79, 0x9a, 0xfc, 0x81, 0xfa, 0xcb, 0xff, 0xda, 0x8e, 0xf3, 0xd0, 0x7d, + 0xe4, 0x6c, 0xb2, 0xfb, 0xaf, 0xe8, 0x59, 0x7d, 0xdf, 0x02, 0x7d, 0x65, + 0xfe, 0xc8, 0xd3, 0xc8, 0xfc, 0xb2, 0xff, 0x1b, 0x5c, 0x8f, 0x3e, 0xea, + 0xca, 0xd1, 0xf3, 0xf4, 0xc6, 0xfd, 0xdf, 0x7d, 0x9b, 0x8b, 0x28, 0x69, + 0xa3, 0x7c, 0x94, 0x64, 0x2c, 0x84, 0x6f, 0x88, 0xaf, 0xdc, 0x71, 0x7c, + 0x6b, 0x2f, 0xfb, 0x74, 0x7e, 0x07, 0x3e, 0x93, 0xac, 0xb8, 0xfd, 0x87, + 0xcc, 0x02, 0x8a, 0x85, 0x4c, 0x79, 0x1c, 0x0b, 0xc2, 0xda, 0xff, 0x1c, + 0xa2, 0x50, 0xc9, 0x2c, 0xbc, 0x26, 0x4f, 0x6b, 0x2f, 0xff, 0x03, 0xf6, + 0x19, 0x67, 0xe5, 0x8f, 0x32, 0xcb, 0xfc, 0x7e, 0x38, 0x29, 0x80, 0xb2, + 0xff, 0x1e, 0xb3, 0x0b, 0xee, 0xac, 0xa8, 0x3e, 0x47, 0x32, 0xbf, 0xf0, + 0x07, 0xac, 0x07, 0xa1, 0x98, 0xb2, 0xff, 0xff, 0xc5, 0x9f, 0xcf, 0xe4, + 0xe2, 0xcd, 0x84, 0x94, 0xee, 0x70, 0xda, 0xe2, 0xca, 0x84, 0xf5, 0xb0, + 0xcb, 0x44, 0x5e, 0x85, 0x77, 0x08, 0x3a, 0x7d, 0x7f, 0xfa, 0x47, 0xa1, + 0xf8, 0x1a, 0xc0, 0xfb, 0x16, 0x5f, 0x74, 0x0f, 0xd5, 0x97, 0xb7, 0xe6, + 0x96, 0x54, 0x1e, 0x06, 0x11, 0x57, 0xc8, 0xaa, 0x28, 0x45, 0x5e, 0x00, + 0x06, 0xb2, 0xfe, 0x98, 0xfd, 0xac, 0x62, 0xcb, 0x8d, 0xa5, 0x97, 0x6e, + 0x62, 0xca, 0x73, 0xde, 0xe1, 0x77, 0x45, 0xef, 0xee, 0x47, 0x79, 0xe3, + 0x59, 0x7e, 0x2c, 0x66, 0xb1, 0x65, 0xf6, 0xb7, 0xe0, 0xd6, 0x56, 0x1f, + 0xbb, 0x0b, 0x40, 0x4d, 0x7e, 0xf4, 0x69, 0x92, 0x59, 0x78, 0x79, 0xd5, + 0x97, 0xfb, 0x0a, 0x77, 0xa3, 0x37, 0x56, 0x5f, 0xf8, 0xfb, 0xcc, 0x1c, + 0xc5, 0x03, 0x59, 0x7d, 0xf9, 0xfd, 0x25, 0x97, 0xfa, 0x0b, 0xa7, 0xb1, + 0x42, 0xcb, 0xff, 0xc7, 0xa3, 0xda, 0x0b, 0x3d, 0xa0, 0x6f, 0x59, 0x52, + 0x4d, 0x60, 0x65, 0x0c, 0x1c, 0xf1, 0xb7, 0xe7, 0xc4, 0x49, 0xd3, 0x1b, + 0xff, 0xd0, 0x41, 0x7e, 0xb8, 0xa2, 0x6d, 0x1b, 0x2c, 0xbf, 0xf1, 0x8c, + 0x1d, 0xe3, 0xf5, 0xc5, 0x59, 0x7f, 0x8f, 0x1a, 0xfa, 0x5c, 0x9c, 0x2a, + 0x22, 0xdd, 0x32, 0x9b, 0x0d, 0xd3, 0x14, 0xf2, 0xd5, 0x11, 0xa5, 0x6c, + 0xb5, 0x22, 0xe1, 0xb4, 0xe4, 0xe3, 0xc0, 0xb0, 0xb0, 0x09, 0xc7, 0xd0, + 0xba, 0x38, 0xc1, 0xe6, 0x85, 0x5e, 0xa3, 0x04, 0x63, 0x5f, 0xa5, 0x4c, + 0xbc, 0x64, 0x7f, 0xc6, 0x36, 0x52, 0xa2, 0x79, 0x28, 0x43, 0xb1, 0x82, + 0x80, 0xa3, 0x7b, 0xdc, 0xfc, 0x29, 0x44, 0x8e, 0x1b, 0x72, 0x1e, 0x17, + 0xfd, 0xd8, 0x1e, 0xb5, 0x0c, 0xe2, 0xcb, 0x85, 0x0a, 0xcb, 0xf9, 0x92, + 0x88, 0xe1, 0xac, 0xb9, 0xff, 0x59, 0x50, 0x78, 0x6e, 0x59, 0x7c, 0x37, + 0x7f, 0x96, 0x5f, 0xc7, 0xf1, 0x86, 0x04, 0x59, 0x4e, 0x7a, 0x04, 0x45, + 0x78, 0x2f, 0xbd, 0x65, 0xdd, 0x6f, 0x09, 0xa1, 0x30, 0xe7, 0xcb, 0x6e, + 0xe1, 0xd2, 0x0b, 0xff, 0xff, 0x81, 0xf4, 0x9b, 0xb9, 0x77, 0x19, 0xc1, + 0xfa, 0x34, 0x37, 0x76, 0x96, 0x53, 0x75, 0x7a, 0x79, 0x2c, 0x1c, 0x95, + 0xef, 0xff, 0x37, 0x63, 0xc9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4d, 0x2b, + 0xff, 0xcd, 0xd8, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x13, 0x8a, 0xfd, + 0x2c, 0xec, 0xa4, 0xb2, 0xff, 0x6f, 0x8f, 0x4e, 0x28, 0x92, 0xcb, 0xff, + 0x3c, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x45, 0x02, 0xbf, 0xfc, 0x70, 0xc7, + 0x1e, 0xa3, 0xbe, 0xc1, 0xac, 0xbe, 0xe1, 0x7d, 0x25, 0x96, 0x6f, 0xf2, + 0x62, 0xa6, 0x50, 0xc3, 0x6f, 0x26, 0x75, 0x22, 0xf8, 0x2e, 0xe1, 0x54, + 0x5c, 0x0b, 0xfc, 0x3f, 0x44, 0xfe, 0x6a, 0x16, 0x56, 0x8f, 0x8c, 0x8b, + 0xaf, 0xe2, 0xce, 0x84, 0xf8, 0xb2, 0xe8, 0x35, 0x97, 0xde, 0x03, 0xc9, + 0x65, 0xfe, 0x29, 0x41, 0x8f, 0x18, 0xb2, 0xb1, 0x16, 0xac, 0x21, 0xf1, + 0x67, 0x45, 0x40, 0x45, 0x7e, 0x93, 0x71, 0x5b, 0x22, 0xac, 0xa6, 0xe7, + 0xf5, 0x09, 0x37, 0x3f, 0xeb, 0x2f, 0xd3, 0x4a, 0x0a, 0x4b, 0x29, 0x65, + 0x61, 0xb3, 0x22, 0x8b, 0xfa, 0x58, 0x29, 0x67, 0xeb, 0x2f, 0x7f, 0x8d, + 0x2c, 0xbe, 0x28, 0xcf, 0x2c, 0xa8, 0x37, 0xdb, 0x0f, 0x5c, 0x6d, 0xf4, + 0x8f, 0x62, 0x4a, 0xe8, 0xf8, 0x8d, 0x97, 0xfd, 0x26, 0xf9, 0xa0, 0xbb, + 0x85, 0x51, 0x24, 0xac, 0xdf, 0xc8, 0x91, 0x74, 0xab, 0xf6, 0x35, 0xf8, + 0x38, 0xb2, 0xe6, 0xbf, 0x59, 0x7f, 0xe0, 0xfa, 0x35, 0x9d, 0x28, 0x62, + 0xcb, 0xfc, 0xfd, 0x2c, 0xd9, 0xc9, 0x65, 0xfa, 0x26, 0x28, 0x1a, 0xcb, + 0x85, 0x69, 0x65, 0xfd, 0x27, 0xd6, 0xc3, 0x85, 0x97, 0xf6, 0x13, 0xfd, + 0x2e, 0x2c, 0xbf, 0xff, 0x00, 0x9c, 0x5e, 0x8b, 0x05, 0x2c, 0xf1, 0xb1, + 0x65, 0xf9, 0x9a, 0xc1, 0xf5, 0x65, 0xfe, 0x8f, 0x84, 0xe9, 0x47, 0xeb, + 0x2a, 0x0f, 0x74, 0x05, 0x17, 0xed, 0xcf, 0x41, 0x0a, 0xb2, 0xc3, 0x59, + 0x7f, 0x6c, 0x59, 0xbc, 0x10, 0xb2, 0xb6, 0x3c, 0x07, 0x11, 0xb9, 0xdb, + 0xec, 0xaa, 0xb0, 0x65, 0x58, 0x32, 0x29, 0xe8, 0x4c, 0x7e, 0x27, 0x98, + 0x67, 0x45, 0xcc, 0x2d, 0xe4, 0x2c, 0xba, 0x43, 0x3e, 0xd7, 0x5b, 0x3a, + 0x7b, 0x59, 0x57, 0xd1, 0xe1, 0x39, 0x99, 0x6b, 0xf8, 0xdb, 0xf9, 0x1f, + 0x2f, 0x63, 0xaa, 0xdf, 0x2b, 0x5a, 0xff, 0xc2, 0xbb, 0x7c, 0xd0, 0x5d, + 0xc2, 0xa8, 0xb5, 0x17, 0xfe, 0x79, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x89, + 0x55, 0x71, 0xb4, 0xb2, 0x96, 0x59, 0xb8, 0xa8, 0xbb, 0x62, 0x6f, 0x53, + 0x37, 0x05, 0xef, 0xec, 0xd0, 0x5d, 0xc2, 0xa8, 0x8a, 0x57, 0xfe, 0x77, + 0x66, 0x6b, 0xf2, 0x07, 0xeb, 0x2f, 0xfd, 0x1d, 0xe6, 0x6b, 0xf2, 0x07, + 0xeb, 0x2f, 0x8f, 0x90, 0xeb, 0x2f, 0xf1, 0x3f, 0xfc, 0xee, 0x05, 0x65, + 0xcc, 0x6f, 0xe4, 0x69, 0x11, 0xff, 0x10, 0x3a, 0x41, 0x4d, 0xd3, 0x55, + 0x78, 0xc2, 0xef, 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x8d, 0x17, 0xa7, + 0x84, 0xfb, 0x61, 0x65, 0xf4, 0xf4, 0xda, 0xb3, 0xc2, 0x78, 0x2c, 0xb8, + 0x1e, 0x59, 0x7f, 0xe1, 0xe3, 0x0f, 0x59, 0xc9, 0xa1, 0x65, 0xff, 0xff, + 0x38, 0x39, 0xec, 0x18, 0x82, 0x70, 0xc4, 0xe9, 0xf2, 0x61, 0x16, 0x5f, + 0xfe, 0x98, 0xcf, 0xd3, 0x67, 0x63, 0xb0, 0x35, 0x97, 0x8d, 0xda, 0x49, + 0x7f, 0xe8, 0x8d, 0xf0, 0x51, 0x34, 0x4c, 0xb2, 0x8d, 0x34, 0xfd, 0xd3, + 0xe9, 0x9b, 0x18, 0x94, 0x43, 0x97, 0xc1, 0x77, 0x0a, 0xa2, 0x9f, 0x5d, + 0xd8, 0x59, 0x5a, 0x3c, 0x4e, 0x97, 0x5f, 0xe3, 0xe9, 0xf7, 0xc0, 0xea, + 0xcb, 0xe8, 0x66, 0x9d, 0x65, 0xff, 0x4d, 0x1d, 0xf3, 0xcd, 0x3b, 0x16, + 0x56, 0x91, 0x20, 0x46, 0x7d, 0x21, 0xbf, 0xe7, 0x91, 0x8f, 0xd1, 0x2e, + 0x2c, 0xbf, 0x03, 0xbd, 0x8d, 0xeb, 0x2f, 0xfe, 0x89, 0xa6, 0x3f, 0xe2, + 0x69, 0xb3, 0xf5, 0x97, 0xbc, 0x71, 0xa3, 0xf3, 0x22, 0xab, 0xbc, 0x05, + 0x95, 0x87, 0x90, 0x03, 0x1b, 0xff, 0x98, 0x40, 0xf1, 0xeb, 0x39, 0x34, + 0x2c, 0xbe, 0x79, 0x8f, 0xcb, 0x2f, 0xdb, 0xc1, 0xbb, 0x1f, 0xac, 0xa3, + 0x44, 0xa3, 0x11, 0x00, 0x45, 0x7d, 0xf7, 0x41, 0xa5, 0x97, 0xfe, 0xee, + 0x81, 0x37, 0x02, 0xf3, 0x71, 0x65, 0x61, 0xf3, 0x00, 0x92, 0xff, 0x39, + 0x47, 0x4a, 0x3a, 0xb2, 0xff, 0xec, 0x98, 0x05, 0xb7, 0x02, 0xf3, 0x71, + 0x65, 0xcf, 0xc5, 0x97, 0xfd, 0xd8, 0xf9, 0xe6, 0xd4, 0x6f, 0x59, 0x5b, + 0xa7, 0xa5, 0xd1, 0x6b, 0xe0, 0x78, 0xf4, 0xb2, 0xcd, 0xdb, 0x0b, 0xd5, + 0x73, 0xd1, 0x54, 0xf2, 0x77, 0x11, 0xb3, 0x64, 0x24, 0x4e, 0x16, 0x4c, + 0x2f, 0xf4, 0x3c, 0xde, 0x16, 0xa5, 0x09, 0x5e, 0x10, 0xf4, 0xc0, 0x10, + 0x99, 0x9f, 0x25, 0xbf, 0xfc, 0xdd, 0x8f, 0x26, 0xf9, 0xa0, 0xbb, 0x85, + 0x51, 0x35, 0x2f, 0xec, 0xd0, 0x5d, 0xc2, 0xa8, 0xae, 0xd7, 0xff, 0xb3, + 0x70, 0xfa, 0x72, 0x9c, 0x51, 0xde, 0x2c, 0xa5, 0x97, 0x34, 0xdf, 0xc7, + 0xb0, 0xd2, 0x6d, 0x37, 0x46, 0x19, 0xc2, 0x46, 0xef, 0xcd, 0x65, 0xee, + 0x43, 0x16, 0x5c, 0xc2, 0x59, 0x7c, 0x17, 0x70, 0xaa, 0x2b, 0xe5, 0xe6, + 0x9a, 0x69, 0x25, 0x89, 0x23, 0x73, 0x41, 0x5a, 0x3f, 0x06, 0x27, 0x5f, + 0xef, 0x60, 0xaf, 0x1f, 0x6e, 0x2c, 0xbd, 0x28, 0xf9, 0x65, 0xf6, 0x77, + 0xc6, 0xb2, 0xe0, 0x71, 0x65, 0xd9, 0xe5, 0x94, 0x33, 0x5b, 0x30, 0xbd, + 0xcf, 0xbd, 0x65, 0xff, 0x79, 0xa7, 0xef, 0x23, 0x36, 0x59, 0x7c, 0x4f, + 0xf7, 0x16, 0x56, 0x1e, 0xe7, 0x4e, 0xaf, 0x35, 0x9a, 0x59, 0x6e, 0xac, + 0xbf, 0xe3, 0xd6, 0x6c, 0x72, 0xc1, 0xac, 0xa3, 0x3e, 0x6d, 0x0e, 0xb8, + 0x8d, 0xf6, 0xe7, 0x80, 0x22, 0xcb, 0xd3, 0x64, 0xcb, 0x2b, 0xe3, 0xc5, + 0x98, 0x9e, 0xda, 0x59, 0x4b, 0x28, 0xcb, 0xcd, 0x08, 0xdd, 0xb0, 0xab, + 0x2b, 0xc6, 0xe1, 0xc7, 0xef, 0xb0, 0xa2, 0x4b, 0x2c, 0xde, 0x78, 0xab, + 0xc4, 0x81, 0x7d, 0x87, 0x32, 0x10, 0xc1, 0x22, 0x98, 0xdf, 0x43, 0xae, + 0x9d, 0xf9, 0x11, 0x3a, 0x72, 0x10, 0xe0, 0x72, 0x9f, 0x78, 0x10, 0x82, + 0xff, 0xf3, 0x76, 0x3c, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0xf6, 0xb3, + 0x6d, 0x65, 0xff, 0x0d, 0xc3, 0x01, 0x01, 0x0d, 0x65, 0xf4, 0x6e, 0x3f, + 0xcb, 0x2f, 0x19, 0x7e, 0xb2, 0xb6, 0x3f, 0x9f, 0x1c, 0x6f, 0x25, 0xbf, + 0x18, 0xe3, 0x5c, 0x59, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xb8, 0x57, 0xcc, + 0x07, 0x78, 0xb2, 0xff, 0x78, 0xe4, 0x0f, 0xe2, 0x65, 0x96, 0x6e, 0xd9, + 0x4d, 0xce, 0x21, 0x1d, 0x23, 0x2c, 0x27, 0x14, 0xd0, 0xc8, 0xef, 0xfe, + 0x6e, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x12, 0x7a, 0xff, 0xc7, 0x33, + 0x78, 0x04, 0x76, 0x3f, 0x59, 0x50, 0xd9, 0x2f, 0x6d, 0x1c, 0xf4, 0x8a, + 0xc5, 0x9e, 0xec, 0x0c, 0x3e, 0xa6, 0x8c, 0x77, 0x52, 0xfb, 0x7d, 0x0d, + 0xce, 0x47, 0x9b, 0xbd, 0x9d, 0xa5, 0x4b, 0xfc, 0xdf, 0x34, 0x17, 0x70, + 0xaa, 0x22, 0xa5, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x99, 0x5f, 0xc7, 0xec, + 0x7f, 0xbf, 0x59, 0x66, 0xf8, 0x7c, 0x53, 0xe6, 0x97, 0xff, 0x9b, 0xb1, + 0xe4, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x26, 0x75, 0xff, 0xe6, 0xec, 0x79, + 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x8a, 0x35, 0x5b, 0x27, 0x06, 0x38, 0x51, + 0x8a, 0x59, 0xd5, 0xab, 0xff, 0x3c, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x44, + 0x74, 0xbf, 0xfb, 0x5b, 0x37, 0xe1, 0x67, 0x79, 0x0c, 0x59, 0x7f, 0xff, + 0xb5, 0x26, 0xf8, 0x40, 0xe8, 0x05, 0xef, 0xc1, 0x3d, 0x71, 0x65, 0x37, + 0x47, 0xcc, 0x26, 0x1a, 0x3d, 0xc0, 0xf2, 0xcb, 0xe0, 0x84, 0x01, 0x59, + 0x7e, 0xc2, 0x1e, 0x6c, 0xb2, 0xff, 0x8f, 0x5c, 0x2c, 0x1f, 0xa1, 0x65, + 0xfe, 0x88, 0x0e, 0x6b, 0x58, 0xb2, 0x8d, 0x11, 0xfe, 0x27, 0x23, 0x7b, + 0xf6, 0x68, 0x20, 0x15, 0x65, 0xff, 0x06, 0x19, 0x86, 0xc8, 0xdd, 0x59, + 0x7f, 0xe2, 0xc1, 0x5e, 0x6e, 0x7a, 0x06, 0xb2, 0xf4, 0xcf, 0xd5, 0x97, + 0xc1, 0x77, 0x0a, 0xa2, 0x9a, 0x5f, 0xb3, 0xe6, 0x9f, 0xcb, 0x2f, 0xfe, + 0xe6, 0x10, 0xe2, 0x74, 0xf1, 0xdf, 0xf9, 0xac, 0xa1, 0xa6, 0xc9, 0x85, + 0x26, 0x75, 0x31, 0xfe, 0x87, 0x48, 0xbb, 0x85, 0x37, 0xda, 0x71, 0x9a, + 0xcb, 0xff, 0xc1, 0x7d, 0x73, 0xce, 0x7c, 0xe6, 0x71, 0x65, 0xff, 0xff, + 0x61, 0x77, 0x86, 0x1c, 0x2e, 0x07, 0xc7, 0xe0, 0x89, 0xb2, 0xcb, 0xfe, + 0xe3, 0xfd, 0x93, 0x47, 0xb8, 0xb2, 0xef, 0x1f, 0x91, 0xd4, 0x49, 0x3d, + 0x67, 0xbf, 0xfa, 0x5e, 0x32, 0xdb, 0x80, 0x8d, 0x4c, 0xb2, 0xf4, 0x99, + 0x25, 0x97, 0xdd, 0xe3, 0xfe, 0xb2, 0xf7, 0xfd, 0x35, 0x97, 0xfb, 0x81, + 0x3d, 0x68, 0xf6, 0x59, 0x50, 0x7e, 0x8e, 0x47, 0xc1, 0xdb, 0xff, 0xfb, + 0x60, 0x6a, 0x6e, 0x6d, 0x13, 0x73, 0xd8, 0x33, 0xde, 0xb2, 0xff, 0xb3, + 0x4f, 0x06, 0x50, 0xc5, 0x95, 0xb2, 0x25, 0xf8, 0xc5, 0x7f, 0xdb, 0xc3, + 0xe3, 0xff, 0xc6, 0xc5, 0x95, 0x87, 0xc0, 0x64, 0x97, 0x99, 0xa0, 0x2c, + 0xbf, 0xc0, 0x72, 0x0c, 0xed, 0xb8, 0xb2, 0xfe, 0x82, 0xec, 0xfe, 0x75, + 0x65, 0xfe, 0x7f, 0xdf, 0xbd, 0xcf, 0x2c, 0xa9, 0x1f, 0x13, 0x45, 0xf7, + 0xde, 0x3c, 0xd2, 0xcb, 0xbc, 0x05, 0x96, 0x92, 0xca, 0xc3, 0xec, 0x32, + 0x3f, 0xc8, 0x77, 0x8b, 0xde, 0xf3, 0x5f, 0xac, 0xb7, 0x56, 0x51, 0x9b, + 0x0f, 0x0f, 0xdc, 0x38, 0x59, 0x7f, 0xfe, 0xef, 0x04, 0xe6, 0xa0, 0x79, + 0xe0, 0xe1, 0x6c, 0xb2, 0xa0, 0xfb, 0x70, 0x5a, 0xfe, 0x83, 0xfd, 0xfa, + 0x05, 0x97, 0xfe, 0xcf, 0xf3, 0x26, 0x98, 0xa0, 0x6b, 0x2b, 0x13, 0x19, + 0x64, 0x23, 0x7c, 0x40, 0x45, 0xb7, 0xbb, 0x12, 0x59, 0x7f, 0xc0, 0xef, + 0xb2, 0x69, 0x47, 0xcb, 0x2b, 0xc7, 0xae, 0x43, 0x96, 0x99, 0x65, 0xcf, + 0xe5, 0x97, 0xec, 0xf3, 0x90, 0xd6, 0x56, 0xc7, 0x9d, 0x21, 0x2f, 0xc5, + 0xae, 0x81, 0x56, 0x5f, 0xcf, 0xff, 0x1b, 0x46, 0xd1, 0xb4, 0x59, 0x7f, + 0x1c, 0x33, 0x6c, 0x69, 0x65, 0xff, 0xf6, 0x77, 0xd9, 0x2d, 0x39, 0x77, + 0xcf, 0x25, 0x97, 0x47, 0xeb, 0x2f, 0xf3, 0xfd, 0x01, 0x30, 0xb7, 0xd2, + 0x24, 0xc8, 0xb8, 0x44, 0xea, 0xc4, 0xf8, 0x8d, 0xbf, 0xc6, 0x24, 0x2f, + 0xc8, 0x68, 0xde, 0x79, 0xb8, 0xb2, 0xe1, 0x34, 0xb2, 0xfd, 0xff, 0xa2, + 0x5c, 0x59, 0x78, 0x80, 0x2a, 0xcb, 0xcd, 0x3e, 0xcb, 0x2f, 0xfa, 0x3f, + 0x7e, 0xfe, 0x21, 0x49, 0x65, 0x78, 0xf6, 0x48, 0x7a, 0xa1, 0x18, 0x04, + 0x53, 0xd7, 0x6b, 0x37, 0x9e, 0x59, 0x95, 0xf0, 0x2e, 0x38, 0x59, 0x64, + 0x72, 0x42, 0xb1, 0x86, 0x30, 0xbf, 0x8e, 0x8d, 0x1b, 0x50, 0x91, 0xf4, + 0x64, 0x8e, 0x41, 0xf8, 0xe9, 0x46, 0x09, 0xc8, 0xe1, 0xbb, 0x29, 0xe8, + 0x09, 0xb3, 0xe3, 0xa2, 0x43, 0x62, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x55, + 0x2f, 0xfc, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x13, 0x6a, 0xcd, 0xf1, + 0x10, 0x0c, 0x34, 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0xbf, 0x5f, + 0xb4, 0x17, 0x70, 0xaa, 0x2c, 0x65, 0xdb, 0x92, 0x59, 0x66, 0xf8, 0x79, + 0xd3, 0x1a, 0x5f, 0xb8, 0x1f, 0x40, 0xab, 0x2f, 0xf1, 0x02, 0x59, 0xc7, + 0xea, 0xcb, 0xf9, 0xb0, 0x39, 0xe6, 0x79, 0x21, 0x56, 0x5f, 0xf1, 0x03, + 0x69, 0xa5, 0x1a, 0xd9, 0x65, 0xda, 0xea, 0xcb, 0xe7, 0x96, 0x0d, 0x65, + 0xfd, 0xdc, 0xd0, 0xf0, 0x96, 0x5b, 0x06, 0x79, 0xbd, 0x21, 0xba, 0x58, + 0xb2, 0xfe, 0x77, 0xef, 0x73, 0xcb, 0x28, 0x53, 0xc2, 0xd0, 0xb5, 0xfd, + 0x9a, 0xef, 0xb3, 0x65, 0x97, 0xf7, 0x4d, 0x9f, 0x4b, 0x8b, 0x2c, 0xdf, + 0x13, 0xf8, 0x61, 0x97, 0x8f, 0x5c, 0xf0, 0x99, 0x38, 0xd2, 0x21, 0x1e, + 0xe1, 0x75, 0x8d, 0xba, 0xa9, 0xcd, 0x4a, 0x21, 0xbf, 0x68, 0x2e, 0xe1, + 0x54, 0x5a, 0xab, 0xff, 0x3c, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x45, 0x04, + 0xbf, 0x43, 0x53, 0xf9, 0xa5, 0x96, 0x6f, 0x88, 0xab, 0x61, 0xa0, 0x89, + 0x97, 0xdd, 0xd3, 0xf9, 0x65, 0xfd, 0xe3, 0xdb, 0x60, 0x71, 0x65, 0xfd, + 0x83, 0xd4, 0x77, 0x8b, 0x2f, 0xe8, 0x63, 0xfd, 0xd9, 0x2c, 0xac, 0x45, + 0x68, 0x48, 0x88, 0xc3, 0x85, 0xb7, 0xf6, 0x79, 0xbb, 0xeb, 0xab, 0x2e, + 0x76, 0x9b, 0x9f, 0x57, 0x4e, 0xef, 0xf4, 0x17, 0xf1, 0xba, 0xec, 0x59, + 0x7f, 0xfa, 0x68, 0xde, 0xfa, 0xe7, 0x0f, 0x58, 0x2a, 0xcb, 0xfc, 0x73, + 0x46, 0xf7, 0xd7, 0x16, 0x5b, 0x8b, 0x2a, 0x0f, 0x18, 0x8d, 0x6f, 0xfe, + 0x8f, 0xc1, 0xde, 0x14, 0x6e, 0x46, 0xcb, 0x2e, 0xd4, 0x2c, 0xa9, 0x26, + 0x8c, 0x66, 0xbe, 0x84, 0x9e, 0xf2, 0x0d, 0xc4, 0x8b, 0xe0, 0xbb, 0x85, + 0x51, 0x71, 0x2f, 0xe0, 0x3f, 0xc3, 0xcd, 0x2c, 0xad, 0x1e, 0xe7, 0x0b, + 0xaf, 0xff, 0xd2, 0x13, 0xd2, 0x7e, 0xbf, 0xe0, 0x27, 0xde, 0x35, 0x97, + 0xb6, 0x12, 0x65, 0x97, 0xc7, 0x2c, 0x15, 0x65, 0xfb, 0x18, 0x7b, 0x83, + 0x59, 0x74, 0x31, 0x65, 0x6e, 0x9e, 0x06, 0x8a, 0xaf, 0x9f, 0x70, 0xf6, + 0x59, 0x7e, 0xf0, 0x05, 0xc2, 0x59, 0x7e, 0x1e, 0x6e, 0xc7, 0x96, 0x5e, + 0xdc, 0x30, 0xac, 0xa1, 0x9f, 0x8e, 0x8a, 0x37, 0x0a, 0xaf, 0xf3, 0x1e, + 0x5c, 0x31, 0xe2, 0xcb, 0xc2, 0xc7, 0x16, 0x5f, 0xe9, 0x44, 0xcf, 0xe8, + 0xdd, 0x59, 0x50, 0x7a, 0x78, 0x3b, 0x7e, 0x2e, 0x93, 0x8a, 0xb2, 0x96, + 0x5b, 0x98, 0x6c, 0xa7, 0xc9, 0xef, 0xe6, 0x3e, 0xf8, 0xd6, 0xe2, 0xcb, + 0xff, 0x18, 0xf3, 0x7b, 0x32, 0x5f, 0x49, 0x65, 0xfe, 0x91, 0xef, 0x93, + 0x22, 0x65, 0x97, 0xfd, 0x8c, 0xcd, 0x66, 0xd8, 0xd2, 0xca, 0x84, 0x71, + 0x61, 0x91, 0xa0, 0xf8, 0xda, 0xfe, 0xcd, 0x3f, 0x41, 0x0b, 0x2f, 0xff, + 0x9c, 0xa0, 0xf7, 0x38, 0x7c, 0xcf, 0x64, 0xfa, 0xca, 0x19, 0xff, 0x6f, + 0x2c, 0xbf, 0xf8, 0xc8, 0x12, 0xce, 0x70, 0xc7, 0x8b, 0x2a, 0x63, 0xe6, + 0xe9, 0x25, 0x9b, 0xc2, 0xed, 0xee, 0x42, 0xc3, 0xe2, 0x23, 0x59, 0x98, + 0x83, 0x4c, 0x5e, 0x25, 0x78, 0x4b, 0x7e, 0x64, 0x50, 0x85, 0xe2, 0x97, + 0x63, 0x0b, 0x12, 0x31, 0xeb, 0xfa, 0x7a, 0x9e, 0xb4, 0xef, 0x25, 0x97, + 0xf9, 0xb0, 0x53, 0xcf, 0x67, 0xe7, 0x91, 0xac, 0xbc, 0x4e, 0xd9, 0x59, + 0x7e, 0x9e, 0xdb, 0x57, 0xf1, 0x61, 0x65, 0xff, 0x8f, 0xe6, 0xd3, 0x9a, + 0x9d, 0x3b, 0xd3, 0xeb, 0x2f, 0xbd, 0x1d, 0x0a, 0xca, 0x6d, 0x67, 0xde, + 0x34, 0xdb, 0xff, 0xcd, 0xa0, 0xad, 0x6e, 0xec, 0x39, 0xee, 0x74, 0xef, + 0x4f, 0xac, 0xbf, 0x73, 0x8f, 0xbf, 0x16, 0x5f, 0x05, 0xdc, 0x2a, 0x8b, + 0xa5, 0x7e, 0x20, 0x76, 0x38, 0xb2, 0xb4, 0x7a, 0xbe, 0x2e, 0xbe, 0x27, + 0x1c, 0x2c, 0xbf, 0xd1, 0xbd, 0xca, 0x37, 0xf1, 0x65, 0x41, 0xea, 0xf8, + 0x82, 0xff, 0xe6, 0xc7, 0x39, 0x34, 0x9b, 0x73, 0xa7, 0x7a, 0x7d, 0x65, + 0xfd, 0x28, 0xf9, 0xf9, 0x25, 0x97, 0xfc, 0x53, 0x6a, 0x3e, 0x91, 0xb1, + 0x65, 0xff, 0xb7, 0xc1, 0x77, 0x80, 0xee, 0x69, 0x65, 0x41, 0xfd, 0x61, + 0xd5, 0xf4, 0xb7, 0x33, 0xab, 0x2f, 0xb9, 0x1b, 0x9c, 0x59, 0x50, 0x79, + 0x5b, 0x84, 0xb7, 0xa3, 0xbb, 0x8b, 0x2f, 0xec, 0x91, 0xc4, 0xb7, 0x56, + 0x5e, 0x1e, 0x0d, 0x65, 0x0c, 0xf2, 0xf0, 0xbe, 0xff, 0xed, 0xe6, 0x5a, + 0x8e, 0x98, 0x4c, 0x6b, 0x2f, 0xf6, 0xf8, 0xef, 0x00, 0x16, 0xda, 0xca, + 0xc3, 0xfe, 0x74, 0x4b, 0xee, 0x6c, 0xe2, 0x2c, 0xbf, 0xd1, 0xd7, 0xd8, + 0x07, 0x25, 0x97, 0x09, 0xe5, 0x94, 0x47, 0x96, 0x21, 0x9d, 0xff, 0xf9, + 0xe4, 0x51, 0xcc, 0xf8, 0x24, 0xf9, 0xf0, 0x56, 0x5f, 0xf1, 0xfb, 0x3e, + 0xfe, 0x43, 0xc5, 0x96, 0x6e, 0xd8, 0x5e, 0xb7, 0x9e, 0x8d, 0xe7, 0x94, + 0x56, 0xd0, 0x7e, 0x21, 0x4b, 0x22, 0x81, 0xb2, 0x63, 0xf1, 0xbb, 0xee, + 0x90, 0xcc, 0xaf, 0xa8, 0x54, 0x31, 0xab, 0xc4, 0x8e, 0xd4, 0x50, 0x9f, + 0xe9, 0x00, 0x1b, 0xf7, 0x91, 0x4f, 0xaa, 0x5f, 0xfe, 0x2e, 0x9b, 0x3d, + 0x06, 0x4f, 0xa1, 0x56, 0x5f, 0xcf, 0xd8, 0x64, 0x12, 0xca, 0xc3, 0xf3, + 0xd2, 0x45, 0xff, 0x99, 0xc0, 0x76, 0x47, 0xbf, 0xc6, 0xb2, 0xfe, 0xcc, + 0xe0, 0x7b, 0x0b, 0x2f, 0xed, 0xa3, 0xae, 0x28, 0x16, 0x52, 0xcb, 0xff, + 0xa3, 0x5f, 0xce, 0xe7, 0xa1, 0x8e, 0x4b, 0x2f, 0xf4, 0x68, 0x4f, 0x7b, + 0x3f, 0x59, 0x52, 0x3f, 0xbf, 0x23, 0x5f, 0xd2, 0x79, 0x39, 0x05, 0x65, + 0xdd, 0xe2, 0xcb, 0xff, 0xd9, 0xd8, 0x68, 0x4d, 0xb9, 0x8c, 0x8d, 0xd5, + 0x96, 0x62, 0xcb, 0x37, 0x6c, 0x26, 0x97, 0x90, 0x86, 0x98, 0x8b, 0xc5, + 0x84, 0x2f, 0xd4, 0xfa, 0xd2, 0x7c, 0x4c, 0x8d, 0xea, 0xfb, 0x1a, 0x6c, + 0x31, 0x65, 0xfc, 0x51, 0xf3, 0x1f, 0xab, 0x2f, 0xff, 0x34, 0xe3, 0xf4, + 0x7d, 0xd8, 0xf3, 0xee, 0xac, 0xb1, 0xe8, 0xfe, 0xfc, 0x5b, 0x7f, 0xff, + 0xc0, 0xd4, 0xb9, 0xe7, 0x3d, 0x6b, 0x40, 0xf8, 0xf4, 0xfc, 0x59, 0x7f, + 0xf6, 0x83, 0x19, 0xe8, 0xe9, 0x47, 0x56, 0x5f, 0xf8, 0x9d, 0xae, 0xe7, + 0x9e, 0x67, 0x59, 0x7f, 0xfe, 0xff, 0xf8, 0x9d, 0xe8, 0xe8, 0x03, 0xe8, + 0x27, 0x59, 0x7f, 0xfe, 0x20, 0x3e, 0xee, 0x4b, 0xd8, 0x2e, 0x0e, 0x60, + 0x2c, 0xac, 0x45, 0x8f, 0x96, 0x2e, 0x0e, 0x2c, 0xa8, 0x37, 0x02, 0x91, + 0x5f, 0xe7, 0xef, 0x23, 0xc2, 0x4c, 0xb2, 0xf4, 0xa1, 0x8b, 0x2e, 0xe4, + 0x96, 0x5f, 0xff, 0xbd, 0x83, 0x07, 0x78, 0x1f, 0x1f, 0x00, 0xff, 0x2c, + 0xa1, 0x4f, 0xbb, 0xa2, 0xf7, 0xfb, 0x5a, 0x79, 0x6d, 0x8d, 0x2c, 0xba, + 0x60, 0x2c, 0xb4, 0xb0, 0xf3, 0x5a, 0x35, 0xbf, 0xfb, 0x07, 0x12, 0x8d, + 0x40, 0x8e, 0x4b, 0x2f, 0xfd, 0xbf, 0x24, 0xc1, 0x9e, 0xf8, 0x1a, 0xca, + 0x62, 0x21, 0x1d, 0x0a, 0xe7, 0x6f, 0xb2, 0xbe, 0xfc, 0x68, 0x99, 0x0b, + 0x51, 0x93, 0xf8, 0x85, 0xcd, 0x79, 0x08, 0xae, 0xb8, 0x09, 0x0a, 0xca, + 0xf2, 0xe5, 0xa1, 0x4e, 0x0b, 0x5f, 0xe9, 0x67, 0x7d, 0x02, 0x12, 0xcb, + 0xff, 0x74, 0xc2, 0x63, 0x72, 0x16, 0x16, 0x5f, 0xf4, 0x7d, 0xe7, 0xe7, + 0x61, 0xa5, 0x95, 0x07, 0xef, 0xa3, 0xdb, 0xfc, 0xfd, 0x20, 0x6d, 0xf6, + 0xea, 0xcb, 0x36, 0x56, 0x5c, 0x0f, 0x2c, 0xbf, 0x16, 0x79, 0xfa, 0xb2, + 0xfe, 0x93, 0xe6, 0xf8, 0x1a, 0xca, 0x6c, 0x9e, 0xa4, 0xf1, 0x26, 0xbe, + 0x30, 0x4a, 0x16, 0x5f, 0x67, 0xd0, 0xc5, 0x97, 0xc6, 0x51, 0xfa, 0xcb, + 0xfb, 0xd1, 0xbd, 0xf3, 0x4b, 0x2f, 0xcf, 0x34, 0x8f, 0xab, 0x2f, 0xfd, + 0x03, 0xd4, 0x48, 0xfe, 0xee, 0x2c, 0xa9, 0x23, 0xe0, 0x52, 0x10, 0x91, + 0x78, 0x87, 0xf2, 0xee, 0x94, 0x5f, 0xf6, 0xb9, 0xe3, 0x08, 0x41, 0xb2, + 0xcb, 0xf9, 0xac, 0xde, 0x40, 0x92, 0xcb, 0xf8, 0x02, 0xe1, 0x6e, 0x62, + 0xcb, 0xff, 0xff, 0xb5, 0x20, 0xf8, 0xd8, 0xc3, 0x6b, 0x9f, 0x04, 0xf3, + 0x43, 0x8f, 0x96, 0x54, 0x26, 0x21, 0x87, 0x64, 0x60, 0x03, 0x0b, 0xff, + 0xfe, 0x81, 0xfb, 0x3e, 0xe9, 0x3f, 0xbf, 0x7e, 0x71, 0xf4, 0x15, 0x97, + 0xfb, 0x33, 0x05, 0x15, 0xe4, 0xb2, 0x85, 0x44, 0xd7, 0x59, 0xef, 0xd9, + 0xc0, 0xf6, 0x16, 0x5f, 0xfe, 0x36, 0xb8, 0x1f, 0x1f, 0xdd, 0xd6, 0xa1, + 0x65, 0xff, 0xb5, 0xd7, 0x97, 0x37, 0x83, 0x46, 0xb2, 0xff, 0xe7, 0xe1, + 0x38, 0xa3, 0xf0, 0x1f, 0x8b, 0x2a, 0x11, 0x0a, 0x04, 0x1b, 0xff, 0xfe, + 0x76, 0x84, 0xcd, 0x13, 0xfd, 0x20, 0xc7, 0x79, 0x9f, 0x75, 0x65, 0xf8, + 0xb3, 0xf8, 0xfd, 0x65, 0xfe, 0x91, 0x94, 0xd2, 0x3f, 0x2c, 0xb4, 0x48, + 0xf6, 0xe6, 0x28, 0xac, 0x54, 0x0d, 0xa2, 0x7f, 0x43, 0x45, 0xc8, 0x8a, + 0x18, 0x97, 0xed, 0xe7, 0xac, 0x1a, 0xcb, 0xff, 0xee, 0x78, 0xf9, 0xdf, + 0x60, 0xcf, 0x98, 0x4b, 0x2f, 0x7a, 0x0b, 0x0f, 0xdc, 0x05, 0x37, 0xe7, + 0xf4, 0x88, 0x2b, 0x2f, 0xf4, 0x33, 0x0d, 0x91, 0xba, 0xb2, 0xff, 0x49, + 0x9c, 0x32, 0xfa, 0x4b, 0x28, 0x27, 0xd1, 0x31, 0xa5, 0xfe, 0x3d, 0x39, + 0x7f, 0x84, 0xb2, 0xff, 0xf6, 0x6b, 0x4f, 0x2e, 0x16, 0x30, 0x4f, 0x96, + 0x5f, 0xf8, 0x8e, 0x45, 0x9e, 0x27, 0x92, 0xcb, 0xce, 0xe1, 0x54, 0x49, + 0x8b, 0xdb, 0x87, 0xc5, 0x95, 0x24, 0x41, 0x04, 0xef, 0x70, 0xa2, 0xfc, + 0x16, 0xd2, 0x7b, 0x6c, 0x36, 0x56, 0x5f, 0xfe, 0xfa, 0x5c, 0xcd, 0xee, + 0xcc, 0x16, 0x38, 0xb2, 0xff, 0x67, 0xdd, 0xeb, 0xcb, 0x8b, 0x2a, 0x11, + 0x79, 0x87, 0x7a, 0x4d, 0xbf, 0xc4, 0xfd, 0xe0, 0x40, 0x4b, 0x2f, 0xff, + 0xbf, 0x3f, 0xda, 0xc3, 0xde, 0x59, 0xdf, 0x1a, 0xcb, 0xfc, 0xff, 0x71, + 0xde, 0x5c, 0x59, 0x6d, 0x62, 0x21, 0x7c, 0xa5, 0x58, 0x8d, 0xf3, 0x85, + 0xbd, 0xff, 0x9a, 0xc1, 0xbc, 0xbb, 0xc7, 0xfd, 0x65, 0xff, 0xfc, 0xff, + 0x61, 0x43, 0x42, 0x74, 0xa3, 0xfc, 0xfb, 0xab, 0x28, 0xd1, 0x3a, 0x04, + 0x0b, 0xff, 0x8f, 0xe6, 0xbb, 0xe3, 0x82, 0xc0, 0xac, 0xbf, 0xe3, 0x16, + 0x07, 0xb3, 0x23, 0x4b, 0x2a, 0x4b, 0xa8, 0x03, 0x31, 0xc8, 0x47, 0x8a, + 0x46, 0x13, 0x1d, 0x43, 0x35, 0x90, 0xef, 0xf4, 0x61, 0xbc, 0x86, 0x1f, + 0x48, 0xb7, 0xa2, 0x5f, 0xc3, 0xfd, 0xb3, 0xcf, 0xe7, 0xd6, 0x5e, 0xdc, + 0xdc, 0x85, 0x97, 0xb8, 0x63, 0x59, 0x7f, 0x14, 0x7c, 0xc7, 0xea, 0xcb, + 0xfe, 0x80, 0xf4, 0x1e, 0xd4, 0x34, 0xb2, 0xff, 0x8f, 0x1a, 0x0f, 0xa0, + 0xe7, 0xd6, 0x5f, 0xfe, 0x82, 0x96, 0x03, 0x5b, 0x7f, 0x98, 0x35, 0x97, + 0xf8, 0x27, 0xae, 0x4d, 0x0e, 0xb2, 0xff, 0xe0, 0x73, 0xcf, 0x2f, 0x3c, + 0xa0, 0x96, 0x50, 0xa8, 0xc0, 0xf9, 0x2b, 0x46, 0x76, 0x39, 0x93, 0xb0, + 0xd0, 0xe3, 0x0b, 0x5b, 0x67, 0x5d, 0x87, 0xdd, 0xf8, 0x71, 0xce, 0x9a, + 0xca, 0x85, 0x4a, 0x7e, 0x8f, 0x49, 0xd6, 0xaf, 0xba, 0x6c, 0x75, 0x96, + 0x15, 0x65, 0xfd, 0xee, 0x47, 0x60, 0x6b, 0x2b, 0x0d, 0xf9, 0x89, 0x56, + 0x1f, 0xff, 0x58, 0x2f, 0xff, 0x01, 0xfc, 0xe3, 0x93, 0x3d, 0x02, 0x12, + 0xcb, 0xfe, 0x8f, 0x74, 0xf6, 0x64, 0x05, 0x65, 0xff, 0xf9, 0xc3, 0xa8, + 0xf1, 0x67, 0x71, 0xbb, 0x4d, 0x34, 0x92, 0xa4, 0x8f, 0xd0, 0x90, 0xf1, + 0x2b, 0xa7, 0x37, 0xe0, 0x80, 0x25, 0x0b, 0x2f, 0xdc, 0x9b, 0xd1, 0xd5, + 0x94, 0x13, 0xd0, 0x98, 0x9e, 0xff, 0xef, 0xa4, 0x51, 0x2d, 0x47, 0x5f, + 0xab, 0x2f, 0xf8, 0x62, 0x47, 0xd3, 0xf9, 0xa8, 0x59, 0x74, 0x49, 0x65, + 0x62, 0x25, 0xdd, 0x10, 0x8f, 0x6f, 0x81, 0xbb, 0x12, 0x59, 0x7f, 0xe7, + 0xec, 0x6b, 0x9e, 0x82, 0xea, 0xcb, 0xf6, 0x48, 0xdf, 0x7a, 0xca, 0xf2, + 0x22, 0x88, 0x97, 0x87, 0xb5, 0x24, 0x73, 0x02, 0x16, 0xf7, 0x98, 0xfe, + 0x59, 0x7f, 0xf8, 0xa2, 0x5a, 0xd0, 0x3e, 0x3d, 0x3f, 0x16, 0x5f, 0xff, + 0x73, 0x05, 0x2c, 0xef, 0x32, 0x44, 0xed, 0x2c, 0xb1, 0xf9, 0x13, 0x3d, + 0x4a, 0xa3, 0x46, 0xee, 0xf8, 0x5a, 0xdf, 0x6e, 0x47, 0xb8, 0xb2, 0xff, + 0x6d, 0xd2, 0x80, 0x77, 0x8b, 0x2f, 0xf3, 0x3b, 0x93, 0x49, 0xc9, 0x65, + 0x61, 0xf3, 0x99, 0xa5, 0x42, 0x35, 0x70, 0xa7, 0x50, 0x8c, 0xbc, 0x4e, + 0xdb, 0x59, 0x7b, 0xff, 0xe1, 0x65, 0xf8, 0x81, 0xb7, 0xdb, 0xab, 0x2b, + 0x87, 0x97, 0xd1, 0xeb, 0xff, 0xd1, 0xb1, 0x66, 0x6d, 0xd1, 0x60, 0xa4, + 0xb2, 0xff, 0xce, 0x5b, 0x79, 0xd8, 0x7c, 0x35, 0x94, 0xe8, 0xa9, 0x22, + 0x26, 0x92, 0xaf, 0x34, 0xd3, 0x49, 0x2f, 0xb6, 0x61, 0xf1, 0x23, 0x73, + 0x41, 0x74, 0x0d, 0x65, 0xb4, 0xe7, 0x96, 0x46, 0xb7, 0xe8, 0xc2, 0x76, + 0x2c, 0xbf, 0xf3, 0xcb, 0xc7, 0xf7, 0xfa, 0x71, 0xac, 0xb8, 0x6d, 0x2c, + 0xbf, 0xff, 0x66, 0xf8, 0x2e, 0xb2, 0x30, 0x87, 0x9f, 0x75, 0x65, 0xe7, + 0x20, 0xec, 0x7d, 0xba, 0x18, 0xaf, 0xd3, 0x05, 0x22, 0x50, 0x42, 0xb6, + 0xff, 0x4a, 0x35, 0xb4, 0x6b, 0x65, 0x97, 0xff, 0xbd, 0x9c, 0xe6, 0x30, + 0x67, 0xbe, 0x06, 0xb2, 0xfe, 0xce, 0x63, 0x1f, 0xe5, 0x97, 0xee, 0x1e, + 0x17, 0xeb, 0x2b, 0x47, 0xa9, 0xc2, 0xdb, 0xda, 0xcd, 0xd5, 0x97, 0xfc, + 0xd0, 0x9b, 0x73, 0x19, 0x1b, 0xab, 0x2f, 0xff, 0x18, 0x70, 0x87, 0xa7, + 0x0e, 0x7d, 0xd5, 0x95, 0x08, 0x88, 0xe1, 0xfd, 0xff, 0x8c, 0x84, 0xc9, + 0xb8, 0x7f, 0x34, 0xb2, 0xf9, 0xff, 0xeb, 0x6d, 0x65, 0x05, 0x3f, 0x2d, + 0x42, 0x7d, 0x84, 0x5d, 0x85, 0x3e, 0xf2, 0x26, 0x90, 0x6f, 0xef, 0x67, + 0xdd, 0xf4, 0x2c, 0xa8, 0x55, 0x75, 0x92, 0x89, 0xdd, 0x96, 0xff, 0xf8, + 0xe6, 0x28, 0xef, 0x3b, 0xf0, 0x4c, 0xb6, 0x59, 0x7f, 0xff, 0xf8, 0xcb, + 0xf1, 0x0b, 0x35, 0xac, 0x6b, 0x37, 0x70, 0xa3, 0xf6, 0x3c, 0x96, 0x5f, + 0xff, 0xdd, 0xe0, 0x07, 0xe3, 0xc9, 0x9c, 0xf7, 0x4f, 0xe9, 0x2c, 0xbf, + 0xff, 0x8b, 0x3f, 0x72, 0xff, 0x5a, 0xc6, 0xb3, 0xf7, 0xf9, 0x65, 0x12, + 0x2f, 0x3a, 0xbf, 0x7e, 0x3e, 0xed, 0x8d, 0x2c, 0xbf, 0xe8, 0x0f, 0x88, + 0x1d, 0x8e, 0x2c, 0xbf, 0xf6, 0xe4, 0x06, 0x19, 0x83, 0x76, 0x2c, 0xbf, + 0x6e, 0x88, 0x50, 0x15, 0x94, 0x67, 0xd6, 0xe8, 0x17, 0xff, 0x4b, 0x99, + 0x03, 0x32, 0x7d, 0x0a, 0xb2, 0xfd, 0xa8, 0xc1, 0x9a, 0xcb, 0xfb, 0xc6, + 0x31, 0xe3, 0x4b, 0x2f, 0xda, 0xcd, 0xfe, 0xc9, 0xc7, 0xa8, 0x12, 0x6a, + 0x84, 0x6c, 0xbc, 0x25, 0xef, 0xff, 0xdd, 0xf6, 0x4a, 0x70, 0x7c, 0x7d, + 0xf8, 0x27, 0xa5, 0x95, 0x25, 0x73, 0x87, 0x19, 0xb6, 0x88, 0x98, 0x53, + 0xe8, 0x51, 0xff, 0x0f, 0x92, 0x26, 0xbf, 0xfd, 0xd8, 0x60, 0x5c, 0x78, + 0xd0, 0x9a, 0x92, 0xcb, 0xe2, 0xdb, 0x78, 0xd6, 0x5f, 0xff, 0xef, 0x19, + 0x0f, 0x58, 0x1f, 0x47, 0xb3, 0x5a, 0x81, 0x56, 0x5f, 0xf8, 0x21, 0x06, + 0xef, 0x0d, 0xac, 0xd2, 0xcb, 0xff, 0xfe, 0xe8, 0x34, 0xfc, 0x0f, 0x8e, + 0x62, 0x76, 0xbb, 0x9e, 0x62, 0xcb, 0xff, 0xdc, 0xcd, 0x4e, 0x0f, 0x8f, + 0x79, 0x90, 0xd6, 0x51, 0xa2, 0xd7, 0x8d, 0x14, 0x69, 0x8d, 0x3c, 0x3e, + 0x2f, 0xf9, 0x91, 0xad, 0xf9, 0xa8, 0x99, 0x65, 0xff, 0xb4, 0x16, 0x47, + 0xdc, 0xcd, 0xf8, 0xb2, 0xe7, 0xdd, 0x59, 0x61, 0x56, 0x5f, 0xff, 0xd3, + 0x14, 0x0f, 0x3e, 0xee, 0x4a, 0x0b, 0x66, 0x62, 0xcb, 0xfb, 0xf9, 0xfc, + 0x1e, 0x31, 0x65, 0x69, 0x13, 0xde, 0x12, 0x25, 0xbb, 0xf8, 0xa7, 0x4f, + 0xe6, 0xa1, 0x65, 0x42, 0x6b, 0x1b, 0x20, 0xe4, 0x2d, 0xdc, 0xbe, 0xff, + 0x3f, 0xf3, 0xb0, 0x6f, 0x25, 0x96, 0x15, 0x65, 0xff, 0xd8, 0x1f, 0x47, + 0xb3, 0x5a, 0x81, 0x56, 0x5f, 0xb3, 0x5a, 0x81, 0x56, 0x5f, 0x11, 0xff, + 0xc8, 0x3f, 0xed, 0x09, 0x79, 0x12, 0xff, 0xbc, 0xff, 0xb1, 0xe4, 0xfc, + 0x59, 0x7f, 0x9e, 0x52, 0x89, 0x07, 0x8b, 0x2b, 0x0f, 0xb0, 0x07, 0x35, + 0x89, 0xc2, 0x9c, 0x2c, 0xfd, 0x0b, 0x1b, 0xff, 0xcf, 0xb7, 0x8e, 0x0b, + 0x26, 0x3d, 0x1a, 0xca, 0x1b, 0x29, 0x3b, 0x25, 0xf6, 0xfc, 0xea, 0x69, + 0x93, 0x12, 0xfa, 0x37, 0xaf, 0xc9, 0xf9, 0x1c, 0x9f, 0x63, 0xc6, 0x68, + 0xe6, 0xfc, 0x59, 0xe7, 0xea, 0xcb, 0xff, 0x4a, 0x0b, 0x61, 0x34, 0x40, + 0x92, 0xcb, 0xf8, 0x9c, 0x7f, 0xfd, 0xfa, 0xca, 0xdd, 0x44, 0xb4, 0xc4, + 0xdc, 0x40, 0xbf, 0xf9, 0xca, 0x5a, 0x7f, 0xbd, 0x19, 0xc5, 0x97, 0xdd, + 0xdb, 0x06, 0xb2, 0xff, 0x8c, 0x51, 0x35, 0xfb, 0x0f, 0x8b, 0x2a, 0x63, + 0xdf, 0x9f, 0x23, 0xbc, 0xd3, 0x4d, 0x2c, 0xbf, 0xff, 0x63, 0x3a, 0x51, + 0xfe, 0x0f, 0x98, 0xc8, 0x0a, 0x46, 0xe6, 0x82, 0xfe, 0xd6, 0x6e, 0x79, + 0xc6, 0xb2, 0xff, 0xd9, 0xf4, 0xe2, 0xcd, 0xfa, 0x3e, 0x2c, 0xbf, 0x83, + 0xe3, 0x82, 0x0a, 0xca, 0x83, 0xee, 0xc4, 0x2b, 0xfd, 0xa8, 0xe9, 0x84, + 0xc6, 0xb2, 0xfd, 0x29, 0xb3, 0x02, 0xb2, 0xda, 0x59, 0x53, 0xd1, 0xf5, + 0xc1, 0x90, 0x85, 0x17, 0xe3, 0xff, 0x1f, 0x75, 0x65, 0x62, 0xb0, 0x91, + 0x4d, 0x0e, 0x14, 0x5e, 0x46, 0x76, 0x62, 0x84, 0xf7, 0x21, 0x17, 0xd3, + 0x4b, 0xe2, 0xda, 0x3c, 0xb2, 0xf7, 0xcd, 0x4f, 0x4b, 0x2f, 0x76, 0x5e, + 0x59, 0x63, 0x59, 0x7f, 0xff, 0x9a, 0x82, 0x81, 0x4a, 0x05, 0x12, 0x7e, + 0x35, 0x01, 0x85, 0x97, 0xf7, 0xa2, 0x70, 0x4f, 0x65, 0x97, 0xff, 0x79, + 0xd9, 0x9a, 0x14, 0x50, 0x17, 0x56, 0x5f, 0xf3, 0xf7, 0x9d, 0xeb, 0xcb, + 0x8b, 0x2a, 0x63, 0xff, 0x24, 0x6b, 0xff, 0x9f, 0x4f, 0x2c, 0xe3, 0xf7, + 0x26, 0x59, 0x7f, 0x69, 0xf4, 0x51, 0x25, 0x97, 0xff, 0xcf, 0xdf, 0x66, + 0xff, 0x1c, 0xee, 0x70, 0xd8, 0xb2, 0xff, 0xc7, 0x23, 0xd7, 0xd0, 0x51, + 0xc5, 0x94, 0xda, 0xaa, 0x91, 0x20, 0x40, 0x6c, 0x99, 0x0a, 0xf0, 0x91, + 0x69, 0x0c, 0x8b, 0x3a, 0xa7, 0x66, 0xf3, 0xd3, 0xad, 0x7b, 0x9e, 0x45, + 0x67, 0xb6, 0xb8, 0x8c, 0x93, 0x68, 0xda, 0x65, 0x0d, 0x51, 0xca, 0x2a, + 0xc9, 0xe0, 0xa1, 0x61, 0x6e, 0x19, 0x51, 0x7f, 0x47, 0x64, 0x70, 0x8d, + 0x9a, 0x3a, 0x0d, 0x47, 0xb4, 0xc3, 0x3f, 0x46, 0x26, 0xf0, 0x9a, 0xfe, + 0x32, 0xb2, 0x95, 0xe1, 0xca, 0x54, 0x27, 0x67, 0x12, 0x40, 0xfe, 0xd1, + 0x14, 0xf9, 0x20, 0x92, 0x98, 0x6f, 0xd2, 0xfa, 0x08, 0x6b, 0x2a, 0x4a, + 0x82, 0xca, 0x3e, 0x3b, 0xff, 0xda, 0xc6, 0x87, 0xec, 0x98, 0xb3, 0x52, + 0x59, 0x7b, 0x59, 0xba, 0xb2, 0xff, 0xd0, 0xd0, 0x9b, 0x73, 0x19, 0x1b, + 0xab, 0x2b, 0x48, 0xae, 0x62, 0x5f, 0x47, 0xef, 0xff, 0x9d, 0x91, 0xa0, + 0xe8, 0x02, 0xe8, 0x0f, 0xc5, 0x95, 0x88, 0x81, 0x01, 0x85, 0xfe, 0xd0, + 0x40, 0xe3, 0x64, 0x2c, 0xbf, 0xfa, 0x34, 0x27, 0x4f, 0xbe, 0xc1, 0x9a, + 0xcb, 0xfc, 0x5f, 0x7f, 0x9c, 0x7d, 0xd5, 0x97, 0xfc, 0x51, 0xb0, 0x9e, + 0xf3, 0xee, 0xac, 0xbe, 0x8c, 0x1f, 0xcb, 0x2b, 0x11, 0x2d, 0xe3, 0x7d, + 0xe7, 0xb7, 0x6f, 0xdc, 0x59, 0x7f, 0xff, 0xc6, 0x47, 0xff, 0x35, 0x81, + 0xf4, 0x7b, 0x35, 0xa8, 0x15, 0x65, 0x42, 0x78, 0x1b, 0x19, 0xe4, 0x36, + 0xbf, 0x31, 0xe8, 0xe5, 0xe9, 0xe3, 0x3d, 0x49, 0x65, 0xe7, 0xd6, 0xcb, + 0x2e, 0x69, 0xa5, 0x97, 0x9c, 0x2d, 0xc2, 0x6d, 0x9a, 0x1d, 0xbf, 0x69, + 0xe4, 0x7e, 0x59, 0x7f, 0xf6, 0xb9, 0xe3, 0x6b, 0xc7, 0xb3, 0x92, 0xca, + 0x83, 0xec, 0x72, 0x7b, 0xf6, 0x33, 0xce, 0x35, 0x96, 0x6f, 0x0e, 0xf5, + 0xde, 0x44, 0x23, 0x41, 0xc9, 0x41, 0xc2, 0x95, 0x06, 0x75, 0x5a, 0x62, + 0xdd, 0x42, 0xab, 0xc4, 0x2f, 0x6b, 0xef, 0x8a, 0x35, 0x9e, 0x47, 0xc5, + 0xd5, 0x0d, 0xeb, 0xcd, 0x42, 0x87, 0x70, 0x82, 0xa4, 0xfc, 0x69, 0x23, + 0xa6, 0x6f, 0xfd, 0x1d, 0xd6, 0x8a, 0x99, 0x08, 0xff, 0x4a, 0xc6, 0xfe, + 0x1b, 0xc5, 0x1a, 0x7f, 0x27, 0xcc, 0xfb, 0x3f, 0x1c, 0x0b, 0x7e, 0x51, + 0x7e, 0xc1, 0xfa, 0x1a, 0x59, 0x7f, 0x72, 0x26, 0x91, 0xcc, 0xb2, 0xff, + 0xbb, 0x04, 0x7f, 0xe7, 0xdd, 0x59, 0x7d, 0x3e, 0xe5, 0xfa, 0xcb, 0xa3, + 0x6c, 0x3d, 0xe1, 0x0e, 0x68, 0x91, 0xc7, 0xc2, 0x81, 0x21, 0x1b, 0x7f, + 0x9f, 0x41, 0x6f, 0x2c, 0xf9, 0x65, 0xff, 0xe9, 0x3f, 0x1b, 0x94, 0x67, + 0x63, 0xe9, 0x2c, 0xba, 0x3f, 0x59, 0x7d, 0xde, 0xc3, 0x16, 0x5f, 0x8c, + 0x26, 0x36, 0xfe, 0x37, 0x22, 0x0b, 0xd3, 0x74, 0xcb, 0xf6, 0x35, 0x33, + 0x6e, 0xc2, 0x12, 0xff, 0xce, 0x16, 0xe0, 0xfc, 0xe5, 0x92, 0x59, 0x7e, + 0xd0, 0x5d, 0xc2, 0xa8, 0x8d, 0x57, 0xff, 0xde, 0x36, 0x9c, 0x83, 0xa8, + 0x39, 0x3f, 0x16, 0x5f, 0x8c, 0x6f, 0x9d, 0x59, 0x78, 0x5f, 0xb8, 0xb2, + 0xcd, 0xf6, 0x4c, 0x23, 0x10, 0x42, 0x68, 0xe9, 0xdd, 0x26, 0xbf, 0xff, + 0xdb, 0xe0, 0xba, 0xdd, 0xf7, 0xb8, 0xf0, 0x6e, 0xc7, 0x1a, 0xcb, 0xf6, + 0x82, 0xee, 0x15, 0x45, 0x4a, 0xb0, 0x55, 0x10, 0xd2, 0x96, 0x53, 0x9a, + 0x9f, 0xc7, 0xac, 0xdf, 0x0f, 0xf9, 0xd7, 0x6f, 0xce, 0xcd, 0x9f, 0x75, + 0x65, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x3e, 0x5f, 0xfc, 0x0d, 0x7e, 0x1f, + 0x1c, 0xc5, 0x1f, 0xac, 0xbf, 0xd2, 0xcd, 0xd7, 0x2f, 0xe7, 0xb5, 0x97, + 0xff, 0xc4, 0xfd, 0x94, 0x0b, 0x3a, 0x3b, 0xc8, 0xfd, 0x65, 0xe7, 0x93, + 0x7d, 0x93, 0x33, 0xc2, 0xbe, 0x1a, 0x75, 0x1c, 0x43, 0xbb, 0xf9, 0xbf, + 0x1e, 0x3e, 0x92, 0xcb, 0xf7, 0x7d, 0x04, 0x2a, 0xcb, 0xff, 0x3e, 0xd1, + 0xe3, 0xd1, 0xc3, 0x16, 0x5f, 0xe8, 0xd6, 0x17, 0x73, 0xcb, 0x2e, 0xc6, + 0xf0, 0x89, 0x69, 0xf2, 0x81, 0x0f, 0x68, 0x54, 0xc1, 0x99, 0x0c, 0x6a, + 0x6e, 0x9c, 0x7c, 0x46, 0xcb, 0x7f, 0xff, 0xd2, 0x06, 0x85, 0x7e, 0x60, + 0xe3, 0xee, 0x09, 0xd1, 0x4c, 0x55, 0x97, 0x9a, 0x14, 0x2b, 0x2f, 0xdf, + 0x7d, 0x2c, 0xea, 0xca, 0xe1, 0xe4, 0x80, 0x7e, 0xff, 0xf3, 0x68, 0x2b, + 0x5b, 0xbb, 0x0e, 0x7b, 0x9d, 0x3b, 0xd3, 0xeb, 0x2f, 0xfc, 0x07, 0xd4, + 0x8b, 0x39, 0x1a, 0x59, 0x7c, 0x17, 0x70, 0xaa, 0x2c, 0x75, 0xfa, 0x3c, + 0x4e, 0x22, 0xca, 0xd1, 0xea, 0xb0, 0xba, 0xff, 0x61, 0x96, 0xdd, 0xcf, + 0x2c, 0xb9, 0xf8, 0xb2, 0xfe, 0xd8, 0x4d, 0x6b, 0x34, 0xb2, 0xa4, 0x78, + 0xce, 0x2d, 0x7f, 0xc4, 0xe2, 0xf8, 0xe0, 0xb6, 0x59, 0x7f, 0x17, 0x74, + 0x08, 0x62, 0xcb, 0xff, 0xf1, 0x38, 0xb3, 0xba, 0x2c, 0x14, 0xb3, 0xc6, + 0xc5, 0x94, 0xe8, 0x84, 0x01, 0x6d, 0xfd, 0xfb, 0xf7, 0x90, 0x2a, 0xcb, + 0xfc, 0x7d, 0x8d, 0x0b, 0xe3, 0x59, 0x7f, 0xfd, 0x2d, 0x9b, 0x48, 0x9e, + 0x76, 0x0e, 0xa7, 0x4e, 0xf4, 0xfa, 0xcb, 0xfe, 0xcd, 0xf1, 0x2f, 0xa5, + 0x9d, 0x59, 0x58, 0x98, 0x13, 0x97, 0xfe, 0x66, 0x4c, 0xb7, 0x1c, 0x96, + 0x5f, 0xee, 0x96, 0x6f, 0xf3, 0xc9, 0x65, 0x0c, 0xf2, 0xb0, 0x5a, 0xfc, + 0x7d, 0xe9, 0xf5, 0x65, 0xff, 0xf8, 0x04, 0xe2, 0xf4, 0x58, 0x29, 0x67, + 0x8d, 0x8b, 0x2f, 0xb5, 0xac, 0x9f, 0x59, 0x7e, 0xd7, 0xe7, 0xe8, 0x59, + 0x58, 0x8c, 0x96, 0x13, 0xba, 0xa8, 0x09, 0x6f, 0xd9, 0xac, 0xc9, 0x96, + 0x59, 0xbc, 0x2f, 0x1d, 0x6d, 0x0b, 0x59, 0x11, 0x0d, 0x93, 0x21, 0x24, + 0x29, 0x11, 0xbc, 0x4c, 0x42, 0xc8, 0x5e, 0x7a, 0x32, 0x42, 0x84, 0x37, + 0x21, 0xd4, 0x21, 0xdd, 0xf0, 0xa7, 0xa9, 0x96, 0x5f, 0xe9, 0x89, 0xda, + 0xee, 0x79, 0x65, 0xcc, 0x15, 0x65, 0x36, 0x4f, 0x31, 0xb4, 0x34, 0xbf, + 0x16, 0x79, 0xfa, 0xb2, 0xf4, 0xf0, 0x9e, 0xbf, 0x59, 0x4d, 0x93, 0xcf, + 0x6d, 0x09, 0xaf, 0xfe, 0x2c, 0x6b, 0x98, 0x7c, 0xee, 0x6e, 0xac, 0xbf, + 0xe8, 0x98, 0xfb, 0xc3, 0x29, 0x96, 0x5f, 0xe7, 0xff, 0x8f, 0xde, 0xba, + 0xcb, 0x98, 0x15, 0x97, 0xfc, 0xe3, 0x3f, 0xa5, 0xd3, 0xd2, 0xca, 0x1a, + 0x60, 0x5a, 0x46, 0x23, 0x9e, 0x99, 0xef, 0x17, 0xbf, 0xe6, 0xb5, 0x84, + 0xff, 0x4b, 0x8b, 0x2e, 0x21, 0x56, 0x5d, 0x83, 0x59, 0x58, 0x7c, 0xee, + 0x75, 0xbc, 0x5e, 0xfd, 0xe7, 0x64, 0x12, 0xcb, 0xee, 0x78, 0xda, 0x59, + 0x7f, 0x05, 0xf5, 0x3e, 0xfe, 0x59, 0x7e, 0x86, 0xbb, 0x9e, 0x59, 0x7a, + 0x04, 0xc5, 0x95, 0x39, 0x17, 0xb8, 0x4c, 0x64, 0x64, 0x61, 0xc2, 0x8b, + 0xee, 0x7e, 0xd8, 0xf2, 0xcb, 0xe0, 0x73, 0xd8, 0xb2, 0xb4, 0x79, 0x44, + 0x51, 0x7f, 0xfb, 0x37, 0x30, 0x3d, 0xfa, 0x58, 0x5f, 0xba, 0xcb, 0xff, + 0xff, 0x8c, 0xb7, 0xe0, 0xc9, 0xdf, 0x7e, 0x6e, 0xf7, 0x74, 0xfa, 0x51, + 0xfa, 0xcb, 0xc6, 0x0f, 0xd6, 0x5b, 0xed, 0xd4, 0x4a, 0xcc, 0xf1, 0x79, + 0x87, 0xa5, 0x95, 0x87, 0x95, 0xf9, 0x75, 0xec, 0x72, 0x59, 0x7b, 0xd1, + 0x32, 0xcb, 0x8f, 0xa6, 0x6d, 0xdc, 0x6a, 0xfa, 0x5b, 0xa6, 0xc5, 0x97, + 0xdc, 0x77, 0xdd, 0x59, 0x7f, 0xb3, 0x7e, 0x7b, 0x74, 0xc9, 0xcf, 0x27, + 0xa4, 0xb7, 0xff, 0xfd, 0x1a, 0x2c, 0x1e, 0x7b, 0xc6, 0x1f, 0x1f, 0x78, + 0xff, 0x2c, 0xbf, 0xff, 0x64, 0xd9, 0xb8, 0x59, 0xbc, 0xb3, 0xf6, 0x44, + 0x96, 0x5f, 0xf3, 0xfc, 0x59, 0xde, 0x63, 0x4b, 0x2b, 0x15, 0xae, 0x04, + 0x84, 0xe3, 0x2c, 0xdd, 0x59, 0xd3, 0x9f, 0x93, 0x09, 0x94, 0x0b, 0x37, + 0xef, 0x1e, 0x81, 0x25, 0x97, 0xfd, 0xb1, 0xfa, 0x67, 0x91, 0xf9, 0x65, + 0xfd, 0x1a, 0xfe, 0x7c, 0x1d, 0x59, 0x7f, 0x8b, 0xb3, 0xdb, 0xf4, 0x10, + 0xb2, 0xfe, 0x91, 0x67, 0xdd, 0x75, 0x95, 0x07, 0xc8, 0xe6, 0xf7, 0xfd, + 0xbd, 0xf6, 0xef, 0x1d, 0xf7, 0x56, 0x5f, 0xfd, 0xe8, 0x60, 0x7c, 0x7d, + 0xcc, 0x62, 0xcb, 0xfc, 0xe2, 0xe6, 0xb6, 0x81, 0x56, 0x54, 0x1f, 0xc1, + 0x21, 0xdf, 0x7f, 0x86, 0xeb, 0x2f, 0x7e, 0x62, 0x2c, 0xbf, 0xff, 0xfd, + 0x05, 0xde, 0x1f, 0x8d, 0x83, 0x8f, 0x66, 0xfd, 0xd3, 0x21, 0xe6, 0xcb, + 0x29, 0xd1, 0x73, 0xf9, 0x0f, 0x47, 0xaf, 0xf9, 0xf5, 0xb7, 0x8f, 0x35, + 0x32, 0xcb, 0xff, 0x9d, 0xcb, 0xfe, 0x66, 0xec, 0xce, 0xeb, 0x28, 0x28, + 0x80, 0x98, 0xea, 0xfd, 0x2e, 0x6d, 0x8d, 0x2c, 0xbe, 0xf6, 0x6b, 0x65, + 0x97, 0x9d, 0xc2, 0xa8, 0xb4, 0x17, 0xfe, 0x76, 0xb7, 0x02, 0xfa, 0xdb, + 0x1a, 0x59, 0x7f, 0x7f, 0x05, 0x1d, 0xe2, 0xca, 0x84, 0x73, 0x0c, 0xa8, + 0x24, 0x7a, 0x28, 0xfd, 0x12, 0xf0, 0x01, 0xa5, 0x97, 0x8a, 0x34, 0xb2, + 0xed, 0x4d, 0x31, 0xb8, 0xe0, 0xed, 0xff, 0xff, 0xed, 0x79, 0xc8, 0xe3, + 0xa7, 0xe8, 0xe7, 0xb3, 0x9a, 0xc0, 0xe7, 0x56, 0x56, 0x22, 0x8c, 0xcb, + 0xef, 0xc3, 0x89, 0x8d, 0x8b, 0x2f, 0xc3, 0xcf, 0xde, 0x16, 0x5f, 0xff, + 0xf9, 0xf5, 0x2e, 0x18, 0xf3, 0x5b, 0x1f, 0x3c, 0xff, 0xf3, 0x09, 0x65, + 0x81, 0x88, 0x93, 0xe1, 0x3d, 0xfb, 0x66, 0x3b, 0x92, 0xcb, 0xfe, 0x7f, + 0xf0, 0xf9, 0xc8, 0xfd, 0x65, 0x41, 0xf0, 0x70, 0x9e, 0xff, 0xc3, 0x9f, + 0xce, 0x0f, 0xc7, 0xad, 0x96, 0x5f, 0xff, 0x63, 0x5d, 0x64, 0x67, 0x7d, + 0x98, 0x41, 0x59, 0x50, 0x89, 0x1f, 0x21, 0xdf, 0xff, 0x8f, 0xb2, 0xcd, + 0x69, 0xe4, 0x21, 0x3f, 0x78, 0xb2, 0xff, 0x4d, 0xe8, 0xef, 0xa2, 0x65, + 0x95, 0x0c, 0x84, 0x8d, 0x8a, 0x24, 0x74, 0x38, 0x4b, 0x61, 0x00, 0x61, + 0x6c, 0x70, 0xd9, 0xdd, 0x85, 0x86, 0xa3, 0x0a, 0xf4, 0x64, 0xae, 0x42, + 0x50, 0xbb, 0xe4, 0x22, 0x3b, 0x0b, 0x2d, 0xe4, 0x42, 0x2a, 0xdf, 0xf0, + 0x3b, 0xc2, 0xcf, 0xba, 0xeb, 0x2d, 0x8b, 0x2f, 0xff, 0x0f, 0xd1, 0x07, + 0xde, 0x19, 0x7d, 0x25, 0x96, 0x00, 0xcf, 0x64, 0x84, 0x2e, 0xc6, 0x2c, + 0xbf, 0xfe, 0x14, 0xf5, 0x9f, 0x77, 0xc6, 0x24, 0x0e, 0x16, 0x5f, 0xfb, + 0xed, 0xfe, 0xdd, 0x3e, 0x94, 0x31, 0x65, 0x62, 0x25, 0x5d, 0x46, 0x85, + 0x4e, 0x14, 0xe1, 0x25, 0xa2, 0x77, 0x85, 0x5d, 0xfd, 0x05, 0xd9, 0xcc, + 0xd9, 0x65, 0xff, 0x4c, 0x50, 0xcf, 0x46, 0xf6, 0x2c, 0xbf, 0x74, 0xe7, + 0xf3, 0x65, 0x97, 0xfb, 0x99, 0xe3, 0xe0, 0x36, 0x59, 0x7c, 0x73, 0xf9, + 0xb2, 0xcb, 0x9f, 0xe9, 0xc8, 0x84, 0xe1, 0x5f, 0x4d, 0x2b, 0x11, 0xfb, + 0xc8, 0x5f, 0xd8, 0xc6, 0x9a, 0xa7, 0x63, 0x2d, 0xbf, 0xef, 0x1f, 0x78, + 0xff, 0x7e, 0xd2, 0xcb, 0xc3, 0x7d, 0x96, 0x5d, 0xad, 0x82, 0x7b, 0x22, + 0x1e, 0x5f, 0xff, 0x03, 0x6d, 0x1f, 0x76, 0x06, 0xa5, 0xcd, 0xd3, 0x59, + 0x5e, 0x4c, 0x39, 0xe1, 0x10, 0x46, 0x17, 0xfc, 0x07, 0xeb, 0xc9, 0xfe, + 0x92, 0xcb, 0xf3, 0x89, 0x9f, 0x75, 0x65, 0x04, 0xf8, 0xb8, 0x73, 0x7f, + 0x8c, 0x78, 0xc9, 0xd8, 0x35, 0x97, 0x3f, 0x16, 0x5f, 0x8d, 0xa6, 0xc3, + 0x65, 0xb0, 0xb2, 0xff, 0xf8, 0x1a, 0xd4, 0x16, 0x0a, 0x7e, 0xf6, 0x08, + 0xb2, 0xcd, 0xb5, 0x95, 0xa3, 0xe5, 0x02, 0x85, 0xf4, 0x6a, 0x26, 0x59, + 0x78, 0xa1, 0x8b, 0x2d, 0x0b, 0x2a, 0x63, 0x56, 0xc1, 0xbb, 0xfc, 0xc0, + 0x17, 0x78, 0x0d, 0x2c, 0xa9, 0x27, 0x52, 0x33, 0x41, 0x45, 0xb5, 0x09, + 0xa6, 0x11, 0x12, 0x6c, 0xf9, 0x15, 0xf6, 0xe6, 0xb3, 0x16, 0x5c, 0x42, + 0xac, 0xbf, 0x7d, 0x2d, 0x73, 0x8b, 0x2a, 0x0f, 0x07, 0x05, 0xef, 0xe2, + 0xfa, 0x5d, 0x00, 0xd6, 0x5f, 0xb7, 0x70, 0x98, 0x6b, 0x2a, 0x0f, 0x61, + 0xcb, 0xef, 0x16, 0x7e, 0xb2, 0xff, 0x67, 0xb9, 0x36, 0x66, 0xcb, 0x2c, + 0xd6, 0x8f, 0x43, 0xa3, 0x97, 0xf3, 0x5d, 0x28, 0x67, 0x16, 0x53, 0x9e, + 0xb8, 0x0a, 0x6f, 0xd3, 0x7b, 0x3f, 0x75, 0x95, 0x09, 0xf7, 0x49, 0x97, + 0xcf, 0x05, 0x0c, 0x7d, 0xe4, 0x37, 0xff, 0xff, 0x40, 0xfd, 0x9d, 0xeb, + 0xed, 0x9d, 0x61, 0x80, 0xa6, 0x2c, 0x69, 0x65, 0xff, 0xfb, 0xbc, 0x89, + 0xbd, 0x1b, 0xcb, 0x07, 0xe8, 0xe2, 0xcb, 0xff, 0xcc, 0x3f, 0xa5, 0xc2, + 0xc0, 0xf4, 0x04, 0xb2, 0xff, 0x73, 0x37, 0x7f, 0x77, 0x99, 0x65, 0x62, + 0x36, 0x18, 0xaf, 0xe4, 0xaa, 0x84, 0xde, 0x72, 0x34, 0xab, 0xf1, 0x05, + 0xf8, 0x6b, 0x2f, 0xff, 0xed, 0x14, 0x47, 0xd3, 0xb9, 0xd2, 0x81, 0xfa, + 0x3a, 0xb2, 0xe3, 0xf2, 0xcb, 0xe9, 0x13, 0x88, 0xb2, 0xff, 0x0d, 0xff, + 0xd6, 0x9f, 0xf5, 0x97, 0xda, 0x0e, 0xeb, 0x4b, 0x2f, 0x85, 0x6c, 0xbb, + 0x4b, 0x2b, 0x0f, 0x3d, 0xc9, 0xe8, 0xd1, 0x48, 0x50, 0x84, 0xa8, 0x4e, + 0x13, 0x09, 0xb7, 0x57, 0x1c, 0x58, 0x10, 0xc6, 0xbf, 0xfb, 0x45, 0x9b, + 0xf5, 0x87, 0xe8, 0x1a, 0xcb, 0xcf, 0xba, 0x6b, 0x2f, 0xfc, 0x27, 0x45, + 0x31, 0x63, 0x0a, 0x16, 0x5b, 0xe5, 0x97, 0xff, 0x8d, 0xde, 0x6e, 0xfb, + 0x25, 0xe3, 0xde, 0xb2, 0xa0, 0xf7, 0x77, 0x44, 0xaf, 0xc2, 0x74, 0xa3, + 0xf5, 0x95, 0x09, 0xa5, 0xfc, 0x89, 0xa1, 0xe6, 0x42, 0x73, 0x79, 0x25, + 0xfa, 0x3b, 0x9e, 0x85, 0x97, 0xef, 0xe3, 0xb8, 0xc5, 0x97, 0xff, 0xff, + 0xf9, 0x9b, 0xa7, 0xef, 0xf1, 0xc8, 0x39, 0xf4, 0xb8, 0x7d, 0xe1, 0xfd, + 0x22, 0x8f, 0x42, 0xcb, 0xff, 0x67, 0xd2, 0xe1, 0xf5, 0xcb, 0x16, 0x5f, + 0xec, 0x67, 0xb2, 0x67, 0x99, 0x65, 0xfd, 0x87, 0xb7, 0xa2, 0x27, 0x1f, + 0x8f, 0x4f, 0x6f, 0xff, 0xfb, 0x26, 0xce, 0x8f, 0xd1, 0xf6, 0x6a, 0x3a, + 0x61, 0x31, 0xac, 0xb1, 0x8d, 0x15, 0x7e, 0x45, 0xac, 0x4f, 0x57, 0xd1, + 0xd6, 0xdf, 0xff, 0xf8, 0xcb, 0x3b, 0x0c, 0x68, 0x0f, 0xb4, 0xec, 0xf1, + 0xeb, 0xce, 0xb2, 0xff, 0xfb, 0x36, 0x71, 0x98, 0x03, 0xa3, 0x6a, 0x63, + 0x59, 0x50, 0x8b, 0xed, 0x36, 0xdf, 0xfb, 0x3e, 0x97, 0x26, 0xd3, 0xf7, + 0x8b, 0x2f, 0xc5, 0x01, 0x93, 0x16, 0x5f, 0xfe, 0xcd, 0xdf, 0x02, 0x6f, + 0x64, 0xd2, 0x8f, 0x96, 0x5f, 0xfd, 0xcc, 0x9b, 0xd1, 0xbf, 0x3d, 0xe8, + 0x59, 0x43, 0x45, 0xee, 0x89, 0xfa, 0xa1, 0x7f, 0x9f, 0xfe, 0x31, 0xfe, + 0xea, 0xca, 0xc3, 0xe5, 0x73, 0x0b, 0xff, 0x8b, 0x36, 0xd4, 0x47, 0x4f, + 0xd0, 0xb2, 0xff, 0xf4, 0x8c, 0x53, 0xe9, 0x64, 0xb5, 0xce, 0x2c, 0xbf, + 0xd1, 0x13, 0x66, 0xf3, 0xd2, 0xcb, 0xe3, 0xd6, 0x12, 0xca, 0x84, 0x4f, + 0x12, 0x57, 0x0d, 0x2f, 0xff, 0xc6, 0x0f, 0xbd, 0x8d, 0x03, 0x0f, 0xdc, + 0x8d, 0x96, 0x56, 0xcb, 0xc2, 0x98, 0x4c, 0x18, 0xff, 0x75, 0x0e, 0x7f, + 0xc8, 0x8a, 0x36, 0x3e, 0x10, 0x76, 0x1c, 0x1b, 0x85, 0xd7, 0xd3, 0x48, + 0xe6, 0x59, 0x71, 0x6c, 0xb2, 0xfb, 0xfd, 0x3c, 0xfa, 0xca, 0x9c, 0x7c, + 0x33, 0xc9, 0x27, 0xc2, 0xf6, 0xdc, 0x59, 0x77, 0xb7, 0x16, 0x5f, 0xfe, + 0xcd, 0xfe, 0x79, 0x7e, 0xff, 0x7e, 0x7f, 0x2c, 0xb7, 0x60, 0xfa, 0x7c, + 0x35, 0x7f, 0xfd, 0xad, 0xa3, 0x77, 0x85, 0x9d, 0xe9, 0xe8, 0x2b, 0x2f, + 0xec, 0xdb, 0x85, 0x9b, 0xd6, 0x54, 0x93, 0x26, 0x37, 0xd7, 0x27, 0xea, + 0x9d, 0x43, 0x23, 0xeb, 0x27, 0x99, 0x8e, 0x1f, 0x6f, 0x1a, 0xed, 0xf3, + 0xb1, 0xe4, 0xb2, 0xff, 0xef, 0x1c, 0xec, 0x2c, 0x3d, 0x3e, 0xf5, 0x97, + 0xff, 0x19, 0x38, 0xcb, 0x07, 0xe8, 0xe2, 0xcb, 0xc2, 0x46, 0x96, 0x5f, + 0xf6, 0xcc, 0x8d, 0x74, 0xa3, 0x65, 0x97, 0xdb, 0xe0, 0x82, 0xb2, 0xff, + 0xfe, 0x82, 0xcf, 0xf9, 0x83, 0xf4, 0x6c, 0xc0, 0x10, 0xd6, 0x51, 0x9f, + 0xf7, 0x88, 0xef, 0xc7, 0xde, 0xc4, 0x96, 0x50, 0xd3, 0xc4, 0x14, 0x84, + 0x28, 0xba, 0x41, 0x71, 0xde, 0xc2, 0xe3, 0x79, 0x0d, 0xfc, 0x5d, 0x8e, + 0x74, 0xd6, 0x5f, 0xff, 0xd1, 0x36, 0x0f, 0xd1, 0xbf, 0xd9, 0xd2, 0x88, + 0x62, 0xcb, 0xfb, 0xee, 0x09, 0xb0, 0x09, 0x65, 0xff, 0x40, 0x7c, 0x73, + 0x3b, 0xec, 0xb2, 0xa1, 0x1e, 0x2c, 0x2c, 0x75, 0xbf, 0xcc, 0x6f, 0xf4, + 0xa3, 0x5b, 0x46, 0xb6, 0x59, 0x7f, 0xdb, 0xdc, 0xbf, 0xf6, 0x7e, 0xeb, + 0x2f, 0xff, 0x66, 0x7e, 0xc8, 0x9b, 0x83, 0xf3, 0xec, 0xb2, 0x8d, 0x10, + 0xe4, 0x77, 0x7c, 0xfe, 0x32, 0x59, 0x7d, 0x9f, 0xc1, 0x2c, 0xb1, 0x6c, + 0x78, 0x3e, 0x20, 0xbf, 0xe3, 0x01, 0x4b, 0x51, 0x83, 0x59, 0x7f, 0xff, + 0xb2, 0x41, 0xf1, 0xfa, 0x33, 0x52, 0xe9, 0x43, 0x38, 0xb2, 0xff, 0xda, + 0x7d, 0x81, 0x18, 0x40, 0xd9, 0x65, 0x79, 0x13, 0xdc, 0x5f, 0xbf, 0xe0, + 0x77, 0x77, 0x98, 0x64, 0x35, 0x97, 0xc0, 0x8f, 0x86, 0xb2, 0x86, 0x9f, + 0x03, 0xb1, 0x70, 0xa3, 0xb0, 0xd5, 0xde, 0x46, 0xd1, 0xdd, 0xf6, 0xe4, + 0x6b, 0x65, 0x95, 0x0a, 0xc8, 0x72, 0x54, 0xe3, 0xaf, 0xdf, 0xed, 0x06, + 0x6c, 0x3f, 0x01, 0x65, 0xff, 0xff, 0xbc, 0xe2, 0xf0, 0x67, 0x9f, 0x04, + 0xc9, 0xfe, 0x64, 0x47, 0x16, 0x5f, 0xf8, 0xf7, 0x96, 0x73, 0xa2, 0x98, + 0x56, 0x5f, 0xf8, 0xb7, 0xe0, 0xe6, 0xcc, 0x21, 0x56, 0x5f, 0xee, 0xfb, + 0x74, 0xfc, 0xf3, 0x2c, 0xbf, 0xa0, 0x24, 0xff, 0x75, 0x65, 0x49, 0x1c, + 0x86, 0x81, 0xba, 0x80, 0xc3, 0x7b, 0xf4, 0x77, 0x6c, 0x69, 0x65, 0xff, + 0xbe, 0x09, 0x3e, 0xcf, 0x23, 0xf2, 0xca, 0xd1, 0xf4, 0x00, 0xa6, 0xfc, + 0x30, 0x83, 0x37, 0x56, 0x5f, 0xf8, 0xa3, 0xef, 0x41, 0x93, 0x8d, 0x65, + 0x4c, 0x7c, 0xec, 0x2b, 0xbe, 0xe6, 0x10, 0xab, 0x2f, 0xa3, 0x24, 0xeb, + 0x2b, 0xe3, 0xc2, 0xf1, 0x15, 0x42, 0x22, 0x06, 0xcb, 0x7f, 0x87, 0xbe, + 0x0b, 0xb9, 0xfa, 0xcb, 0xf4, 0x77, 0xd1, 0xd5, 0x97, 0x17, 0xeb, 0x2b, + 0x63, 0xf3, 0xd1, 0xab, 0x93, 0xdf, 0xff, 0xfe, 0x06, 0xbd, 0x93, 0x4c, + 0xfa, 0x9b, 0xbe, 0x07, 0xfc, 0xc3, 0xde, 0x38, 0x59, 0x7f, 0xc7, 0xee, + 0x68, 0xca, 0x18, 0xb2, 0xfe, 0x8e, 0x6e, 0x9f, 0xdd, 0x59, 0x7f, 0x47, + 0x26, 0x2c, 0x99, 0x65, 0x1a, 0x62, 0x9b, 0xaf, 0xda, 0x37, 0xf1, 0x8d, + 0xfb, 0xbe, 0x72, 0x15, 0x65, 0xfe, 0xdc, 0x20, 0x00, 0x26, 0x2a, 0xcb, + 0xf1, 0xb4, 0xc7, 0xea, 0xcb, 0xfc, 0x0d, 0x87, 0xec, 0x28, 0x59, 0x50, + 0x89, 0x20, 0x9b, 0x91, 0x45, 0xff, 0xd8, 0x3f, 0x43, 0x5f, 0x4b, 0xa0, + 0x1a, 0xcb, 0xb3, 0x8b, 0x2e, 0x7e, 0xac, 0xbd, 0xc0, 0x4b, 0x46, 0xb3, + 0xc2, 0xd6, 0xc1, 0xa7, 0x9d, 0x88, 0x01, 0x85, 0xd1, 0x17, 0x75, 0xce, + 0xff, 0xf6, 0x41, 0x60, 0x4b, 0x3a, 0x07, 0x69, 0x65, 0xff, 0x67, 0xf3, + 0xf8, 0xe3, 0xce, 0x2c, 0xbf, 0xf8, 0xfb, 0xec, 0x39, 0x73, 0x3e, 0xea, + 0xca, 0x1a, 0x31, 0x89, 0x1f, 0x87, 0x77, 0xff, 0x7e, 0x1f, 0xcb, 0x3b, + 0xc7, 0x72, 0x59, 0x79, 0x8f, 0xf2, 0xca, 0xd1, 0xf0, 0xfe, 0x89, 0x50, + 0xc8, 0xae, 0x19, 0xa6, 0x46, 0xa0, 0x70, 0xa7, 0xf4, 0x62, 0x2f, 0x09, + 0x52, 0x96, 0x23, 0xd8, 0xc6, 0x41, 0x09, 0x0b, 0xff, 0xfa, 0x19, 0xfb, + 0xfb, 0x3f, 0xfd, 0xf5, 0x9b, 0xfd, 0x8b, 0x2f, 0xf1, 0xf7, 0xf3, 0x29, + 0x71, 0x65, 0xfd, 0xe9, 0x8f, 0xc6, 0xc5, 0x97, 0x7f, 0xc5, 0x95, 0x87, + 0x8c, 0x65, 0xd7, 0xfa, 0x59, 0xae, 0x14, 0x6c, 0xb2, 0xf8, 0x5d, 0x43, + 0x16, 0x5d, 0x9b, 0x2c, 0xa8, 0x37, 0x53, 0xe4, 0x77, 0xf8, 0xcb, 0xb2, + 0xcd, 0x62, 0xcb, 0xfa, 0x03, 0xa3, 0x86, 0x2c, 0xa9, 0x1e, 0xe9, 0x18, + 0xdf, 0xfe, 0x67, 0xef, 0xac, 0xdf, 0x86, 0x3f, 0x1a, 0xca, 0x83, 0xec, + 0xc2, 0x1b, 0xd9, 0xe7, 0x59, 0x78, 0x00, 0x25, 0x97, 0xe3, 0xf7, 0xb0, + 0x6b, 0x2d, 0x32, 0xc1, 0x9e, 0x1f, 0x07, 0x28, 0x68, 0x8e, 0x75, 0xeb, + 0xfb, 0x62, 0xcf, 0xc1, 0xc5, 0x97, 0xfb, 0xf9, 0xfc, 0x71, 0xe7, 0x16, + 0x5e, 0xdd, 0x36, 0x2c, 0xbf, 0xfe, 0x7f, 0xf9, 0x1f, 0xf7, 0x0c, 0x78, + 0x53, 0x2c, 0xf1, 0x7f, 0x50, 0x8e, 0x3c, 0x2f, 0x75, 0x5b, 0xf9, 0xac, + 0x08, 0x71, 0xa5, 0x97, 0xa0, 0x3b, 0xd6, 0x56, 0x1e, 0x6f, 0x4b, 0xef, + 0xff, 0x66, 0xe7, 0xec, 0x89, 0xba, 0x60, 0xd7, 0x16, 0x5f, 0xe6, 0x66, + 0x17, 0x67, 0xf1, 0x65, 0xff, 0xdc, 0xec, 0x46, 0xbf, 0x98, 0xb1, 0xa5, + 0x95, 0xf1, 0xfd, 0x39, 0xa5, 0xe1, 0x64, 0x05, 0x97, 0xef, 0x46, 0xb6, + 0x62, 0xcb, 0xfc, 0x07, 0x2f, 0x63, 0xcc, 0xb2, 0xdf, 0x4c, 0x7b, 0x6d, + 0x14, 0xd1, 0xa2, 0x91, 0xde, 0x6b, 0x13, 0xd1, 0x32, 0x17, 0x86, 0x49, + 0x43, 0x1a, 0xa1, 0x7a, 0x4a, 0x4b, 0xd8, 0xf3, 0xf1, 0x01, 0xb6, 0xfa, + 0x1d, 0x8f, 0x0a, 0xe2, 0x8c, 0x17, 0x92, 0x92, 0xee, 0x01, 0x2c, 0xbe, + 0xfb, 0x91, 0xf2, 0xca, 0x6d, 0x9b, 0xa2, 0x16, 0xbc, 0x59, 0xfa, 0xcb, + 0x42, 0xca, 0xfc, 0xd6, 0x74, 0x72, 0xe7, 0xea, 0xca, 0xd1, 0xb8, 0xf1, + 0x15, 0xf3, 0x23, 0x5c, 0x59, 0x7f, 0xa1, 0x85, 0x9f, 0x83, 0x8b, 0x2f, + 0xfb, 0x59, 0xec, 0xda, 0x71, 0xf1, 0x65, 0x62, 0x2c, 0x4c, 0x84, 0x88, + 0x80, 0x67, 0x7f, 0xa4, 0x7c, 0xe4, 0x6a, 0x4b, 0x2f, 0x9d, 0x90, 0x6b, + 0x28, 0x67, 0xa7, 0x31, 0x9d, 0xfc, 0x64, 0x09, 0x67, 0x16, 0x5f, 0xff, + 0xa4, 0x7d, 0xe0, 0x7c, 0x7d, 0xfa, 0x47, 0xde, 0x2c, 0xbf, 0xee, 0xc7, + 0xdf, 0xbf, 0xbc, 0x6b, 0x2f, 0x8e, 0x7f, 0x36, 0x59, 0x58, 0x7b, 0xfd, + 0x3a, 0xb3, 0x16, 0x5f, 0xc6, 0x40, 0x96, 0x72, 0x71, 0xb2, 0xde, 0x43, + 0x50, 0x9b, 0xe8, 0xa4, 0x73, 0x16, 0x76, 0x1d, 0x17, 0xff, 0x68, 0x1f, + 0x4b, 0x9e, 0xcd, 0x80, 0x4b, 0x2c, 0xde, 0x7a, 0x7c, 0x3e, 0xd9, 0xe5, + 0xc2, 0x7b, 0x7a, 0x88, 0xcb, 0x76, 0x85, 0x9c, 0xa1, 0xd8, 0x39, 0x7d, + 0xf9, 0x48, 0xc1, 0x16, 0x54, 0x98, 0x65, 0x54, 0xfd, 0x09, 0x63, 0x8f, + 0x2e, 0x69, 0x4b, 0x7a, 0x8f, 0x25, 0x91, 0xd6, 0x7a, 0x95, 0x2a, 0xf2, + 0x85, 0x3f, 0x8d, 0x74, 0xa5, 0xae, 0xf2, 0x91, 0xc9, 0xd9, 0xf3, 0x70, + 0x43, 0x3a, 0x7e, 0x33, 0x91, 0x25, 0x05, 0x6e, 0x22, 0x5f, 0xda, 0x6e, + 0x3d, 0xe7, 0x0b, 0x2f, 0xed, 0xc3, 0x19, 0x44, 0xcb, 0x2f, 0x43, 0x92, + 0xcb, 0xec, 0xf3, 0xf5, 0x65, 0x9b, 0x38, 0x7d, 0x7e, 0x30, 0x21, 0xab, + 0xff, 0xd1, 0xa0, 0x4d, 0x31, 0xf8, 0xd8, 0xe3, 0x59, 0x7f, 0x13, 0xf7, + 0x91, 0xba, 0xb2, 0xff, 0xff, 0xe7, 0x2e, 0xf2, 0x0e, 0x59, 0xb8, 0x5d, + 0x8f, 0x40, 0xa0, 0x8d, 0xc5, 0x94, 0x14, 0x77, 0x9a, 0x5f, 0x8b, 0xef, + 0xb3, 0x9f, 0x9a, 0xcb, 0xa5, 0xf2, 0xcb, 0xe0, 0xbb, 0x85, 0x51, 0x6b, + 0x28, 0xcf, 0x1b, 0x43, 0x17, 0xfd, 0x20, 0x1f, 0xb6, 0x72, 0x1a, 0xcb, + 0xfc, 0xc3, 0x63, 0xcd, 0x9e, 0x59, 0x7e, 0xff, 0x37, 0x0b, 0xab, 0x2f, + 0xf0, 0x0b, 0x37, 0x0d, 0xa3, 0x59, 0x7d, 0xc7, 0x20, 0xac, 0xaf, 0x1e, + 0xb6, 0xe1, 0xad, 0x42, 0x29, 0xb2, 0x10, 0x37, 0xb5, 0x0c, 0x59, 0x7f, + 0x7b, 0x39, 0x1b, 0x6e, 0x2c, 0xbf, 0xe9, 0x37, 0xcd, 0x05, 0xdc, 0x2a, + 0x8a, 0x0d, 0x58, 0x7f, 0x0e, 0x63, 0x7d, 0x93, 0x47, 0x56, 0x5f, 0xee, + 0x61, 0x05, 0xcb, 0xf5, 0x97, 0xfd, 0x12, 0x8d, 0x6d, 0x1a, 0xd9, 0x65, + 0xf6, 0x8c, 0x56, 0x2c, 0xbd, 0xdc, 0xf2, 0xca, 0xc3, 0x7e, 0x29, 0x1d, + 0xfe, 0xf3, 0x78, 0xfc, 0x05, 0x32, 0xcb, 0xf3, 0xf1, 0x91, 0xfa, 0xcb, + 0xfa, 0x26, 0xfd, 0xca, 0x65, 0x97, 0xcf, 0xfe, 0x69, 0x65, 0x62, 0x2c, + 0x05, 0x37, 0x32, 0x8f, 0x17, 0xdf, 0xe9, 0x8f, 0xa0, 0x98, 0xfa, 0xb2, + 0xff, 0xfb, 0x87, 0xb3, 0xf1, 0xc9, 0xfb, 0xe8, 0x99, 0x65, 0xff, 0x9c, + 0xbf, 0xce, 0xc1, 0x1f, 0xeb, 0x2b, 0xa8, 0x8c, 0x9f, 0x4f, 0xb3, 0x79, + 0xed, 0x7a, 0x2e, 0x23, 0x26, 0x91, 0x7e, 0x34, 0x7c, 0x42, 0x67, 0x33, + 0x43, 0x67, 0x44, 0xcc, 0x84, 0xa7, 0x88, 0x1c, 0x88, 0x8c, 0xb8, 0xf3, + 0xd8, 0x68, 0x80, 0xf0, 0x48, 0x60, 0xdf, 0xe1, 0x5b, 0xbf, 0xbd, 0x9b, + 0x2c, 0xbf, 0xfc, 0xdd, 0x8f, 0x26, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x45, + 0xaf, 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0xba, 0x95, 0x0f, 0xb7, 0x25, + 0x28, 0xd3, 0x71, 0x48, 0x30, 0xe2, 0x39, 0x5d, 0x4c, 0x9e, 0x65, 0xf5, + 0xe2, 0x64, 0xbc, 0x7f, 0xff, 0xcf, 0x93, 0x94, 0x60, 0x3c, 0x37, 0xea, + 0xd5, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x21, 0x5e, 0xf1, 0x92, 0xcb, 0xb0, + 0x6b, 0x2c, 0x15, 0x94, 0x13, 0xc2, 0xf0, 0xd9, 0x0b, 0x5f, 0x66, 0xf3, + 0xd2, 0xcb, 0xe3, 0xd6, 0x7c, 0xb2, 0xb1, 0x1d, 0x3f, 0x3b, 0x6e, 0x97, + 0x4f, 0x91, 0xdf, 0xee, 0xb7, 0x98, 0xa3, 0xfe, 0x2c, 0xa6, 0xe7, 0xf5, + 0x88, 0x76, 0x9f, 0x59, 0x7d, 0x1e, 0x86, 0x2c, 0xbe, 0x0b, 0xb8, 0x55, + 0x11, 0xb2, 0x82, 0x79, 0xfa, 0x21, 0xb3, 0x71, 0xa2, 0x17, 0x18, 0xef, + 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0xa6, 0xd7, 0x3c, 0x96, 0x5f, 0x05, + 0xdc, 0x2a, 0x8a, 0x81, 0x43, 0x3c, 0x4d, 0x0b, 0x5e, 0xcf, 0x9a, 0x59, + 0x66, 0xf8, 0x78, 0x1e, 0x22, 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, + 0xa6, 0x5e, 0x01, 0xe9, 0x65, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0xb0, 0x5f, + 0x8d, 0xa1, 0x83, 0x4b, 0x2d, 0xb2, 0xcb, 0x84, 0x92, 0xcb, 0xce, 0x41, + 0x59, 0x50, 0x78, 0xbf, 0x89, 0x74, 0x62, 0xee, 0x12, 0xcb, 0x86, 0x2a, + 0xcb, 0x99, 0xc5, 0x97, 0xe7, 0x97, 0x4f, 0x65, 0x97, 0xee, 0x70, 0xb3, + 0xf5, 0x94, 0x67, 0xa0, 0xe5, 0x17, 0xf1, 0xcf, 0xbc, 0x1c, 0xfa, 0xcb, + 0xfc, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x23, 0xf5, 0xc0, 0xe2, 0xcb, 0xc5, + 0x03, 0x59, 0x50, 0x8a, 0x49, 0x18, 0x8d, 0x04, 0xc5, 0xef, 0xb3, 0x0a, + 0x4b, 0x2d, 0x0b, 0x2e, 0x8d, 0x96, 0x54, 0x8d, 0x3e, 0x08, 0x5f, 0x44, + 0xc0, 0xd2, 0xcb, 0xfb, 0xe9, 0x3c, 0x8c, 0x96, 0x5e, 0x82, 0x99, 0x65, + 0xf4, 0x70, 0xe6, 0x59, 0x58, 0x7d, 0x62, 0x96, 0xe8, 0x72, 0xe7, 0xea, + 0xcb, 0x99, 0xb8, 0xb2, 0xee, 0xf5, 0x65, 0x9b, 0x8d, 0x5f, 0x66, 0x0e, + 0x04, 0xd3, 0xe6, 0xe3, 0x2f, 0xdd, 0x16, 0x60, 0xc7, 0x99, 0xde, 0x18, + 0x9f, 0x9d, 0x92, 0x3f, 0x08, 0x3b, 0x08, 0xe9, 0xf2, 0xf1, 0x05, 0xb7, + 0x06, 0xaf, 0xff, 0x37, 0x63, 0xc9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4d, + 0xeb, 0xff, 0x98, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x13, 0xf2, 0xfd, + 0x3d, 0x4f, 0x53, 0xd3, 0x62, 0x7a, 0x59, 0x7f, 0x3b, 0x21, 0x90, 0x22, + 0xcb, 0x9e, 0x4b, 0x29, 0xb0, 0x88, 0x49, 0xe9, 0x05, 0xb5, 0x4b, 0x6e, + 0x11, 0x8b, 0x2f, 0xec, 0x14, 0xfa, 0x50, 0xb2, 0x82, 0x78, 0x93, 0xe3, + 0x17, 0xed, 0x47, 0x81, 0x0b, 0x2f, 0xde, 0xce, 0xc0, 0xd6, 0x53, 0x64, + 0xfc, 0xcc, 0x95, 0xc9, 0xef, 0xf4, 0xf0, 0x9e, 0x8e, 0x7f, 0xc6, 0xdb, + 0x59, 0x71, 0x05, 0x65, 0xf7, 0x26, 0x67, 0x16, 0x5f, 0x8b, 0xbe, 0x36, + 0x2c, 0xbe, 0x98, 0x0e, 0x22, 0xca, 0x83, 0xcb, 0x72, 0x7b, 0x82, 0x15, + 0x97, 0xf8, 0x3e, 0x3e, 0xe6, 0x31, 0x65, 0xdb, 0xc2, 0xb8, 0xc0, 0x95, + 0x3c, 0x13, 0x59, 0x84, 0x53, 0x16, 0x99, 0xbf, 0xf2, 0x0e, 0x0b, 0xf4, + 0xca, 0xfc, 0xd8, 0x6c, 0xcf, 0xe7, 0xcb, 0x2f, 0xe9, 0xe9, 0xf6, 0xf1, + 0xee, 0xac, 0xbf, 0xe9, 0xdd, 0xf6, 0x33, 0x35, 0x8b, 0x2f, 0xe6, 0xd6, + 0xd8, 0x6c, 0x36, 0x79, 0x0b, 0x2f, 0xd3, 0xc8, 0xa2, 0xe6, 0xea, 0xcb, + 0xfb, 0xa6, 0x4d, 0x61, 0x2c, 0xbb, 0x78, 0x57, 0x18, 0x0a, 0xde, 0x59, + 0x77, 0xff, 0xac, 0xa9, 0xe9, 0x39, 0x26, 0xc9, 0xa4, 0xf6, 0x6c, 0xda, + 0xa7, 0x53, 0xc1, 0x03, 0x0c, 0xba, 0x58, 0x02, 0x61, 0x04, 0x6f, 0xa4, + 0xe3, 0x35, 0x97, 0xb4, 0xf8, 0xb2, 0xf9, 0xc7, 0x8c, 0x59, 0x4d, 0x93, + 0xdd, 0x9e, 0x48, 0x67, 0x88, 0xdd, 0xf4, 0xf7, 0xb3, 0x38, 0xb2, 0xfe, + 0xc9, 0xa0, 0x8f, 0xe5, 0x97, 0xff, 0xe2, 0x7e, 0xc1, 0x04, 0xd8, 0x30, + 0x3e, 0xa4, 0xb2, 0xfc, 0xd7, 0x7b, 0x0c, 0x59, 0x53, 0xc9, 0xfe, 0x6c, + 0xa7, 0x7f, 0xcf, 0xe3, 0xde, 0xdd, 0xa6, 0x9a, 0x49, 0x7f, 0xbc, 0xfd, + 0xeb, 0xbb, 0x4b, 0x2f, 0xd8, 0x17, 0xe1, 0xac, 0xa9, 0xed, 0x39, 0x36, + 0xd5, 0x29, 0x9e, 0x30, 0xa9, 0x6d, 0x65, 0x13, 0xc1, 0x03, 0xc6, 0x77, + 0xff, 0xf3, 0x67, 0x9a, 0x83, 0xef, 0xb0, 0x67, 0xcc, 0x2f, 0xd6, 0x5d, + 0xc8, 0x59, 0x4d, 0x83, 0xf4, 0x92, 0xf5, 0xfd, 0xbb, 0x1a, 0x77, 0x92, + 0xcb, 0xf7, 0x8d, 0xff, 0x9e, 0x0b, 0x2f, 0x47, 0xd2, 0x59, 0x53, 0xd2, + 0x28, 0x1b, 0x42, 0x6e, 0x97, 0xee, 0x17, 0x5f, 0xfa, 0x79, 0x9e, 0xa6, + 0x28, 0x1e, 0x7d, 0xd5, 0x97, 0xd8, 0x37, 0x92, 0xcb, 0xda, 0x06, 0xe2, + 0xca, 0x9e, 0x51, 0x17, 0x3c, 0x52, 0x80, 0x43, 0x7c, 0x58, 0x7f, 0xac, + 0xbe, 0x6d, 0x5f, 0xc5, 0x85, 0x94, 0xd9, 0x3c, 0xc9, 0xec, 0x86, 0xff, + 0x9f, 0xbf, 0x4b, 0xb9, 0xa1, 0x56, 0x5c, 0x7f, 0xac, 0xa9, 0xe0, 0x7a, + 0x44, 0x77, 0x7f, 0xe8, 0x88, 0x88, 0x88, 0xfb, 0x8b, 0x2f, 0x4d, 0x1d, + 0x59, 0x74, 0x44, 0x1e, 0xc4, 0xc7, 0x77, 0xff, 0xbd, 0x13, 0x7d, 0xcc, + 0xfb, 0x76, 0x20, 0x96, 0x5e, 0x3f, 0xb1, 0x65, 0xfb, 0x47, 0xb3, 0xb1, + 0x25, 0xcd, 0x34, 0x92, 0xb0, 0xf0, 0x5a, 0x28, 0xb0, 0x12, 0x37, 0x34, + 0x57, 0xbd, 0x81, 0x59, 0x50, 0x99, 0xc7, 0x8b, 0x3f, 0x4e, 0xe3, 0xf8, + 0x84, 0x77, 0xe0, 0x4f, 0xc0, 0x3e, 0x59, 0x7c, 0xe5, 0x1b, 0x8b, 0x2b, + 0x0f, 0x3d, 0xcb, 0x2f, 0xfd, 0xb8, 0x7b, 0xbe, 0x9a, 0x37, 0x5d, 0x8b, + 0x2f, 0xfd, 0x8c, 0x8e, 0x73, 0x25, 0x3f, 0x8b, 0x2b, 0xc8, 0x88, 0x24, + 0x8b, 0xf3, 0x1d, 0x98, 0x35, 0x97, 0xfd, 0xd0, 0xf8, 0xc8, 0xa3, 0xe5, + 0x95, 0x07, 0xbe, 0xe4, 0xf4, 0x68, 0xa1, 0xf4, 0x20, 0x6f, 0xf6, 0x0c, + 0xf7, 0xf3, 0xf0, 0xac, 0xbf, 0xff, 0xf6, 0x6f, 0x3e, 0xcb, 0x3f, 0xe1, + 0x67, 0xfa, 0x8d, 0x9f, 0x41, 0x59, 0x74, 0x49, 0xd1, 0x4b, 0xc3, 0x6b, + 0xfb, 0xbc, 0xcc, 0xda, 0x16, 0x57, 0x8f, 0x68, 0x8b, 0x6f, 0xb8, 0x3e, + 0x81, 0x65, 0xff, 0xff, 0xdb, 0xe3, 0x58, 0x32, 0x7d, 0x0a, 0xfd, 0x07, + 0x4f, 0xbc, 0xf1, 0xac, 0xbf, 0xda, 0x01, 0x03, 0xa0, 0xea, 0xcb, 0xde, + 0x82, 0x59, 0x68, 0x33, 0xcf, 0xe9, 0xa5, 0xff, 0xfa, 0x50, 0x45, 0x1b, + 0x34, 0xfb, 0x78, 0xe0, 0x96, 0x54, 0x27, 0x00, 0x12, 0x13, 0x23, 0xf4, + 0x30, 0x80, 0x4d, 0x7c, 0x7b, 0x3b, 0x16, 0x5e, 0x23, 0x85, 0x97, 0xe8, + 0x11, 0x9e, 0x35, 0x94, 0x33, 0xe4, 0xd1, 0x11, 0x0d, 0x5f, 0x9f, 0x68, + 0x70, 0xac, 0xbf, 0x84, 0x9d, 0x1c, 0x86, 0x2c, 0xaf, 0x1e, 0xbb, 0x93, + 0xdf, 0xd2, 0xe7, 0x70, 0x85, 0x59, 0x7d, 0xc7, 0x96, 0xea, 0xcb, 0xff, + 0x4b, 0x91, 0x28, 0xff, 0x99, 0xbd, 0x65, 0xff, 0xd0, 0x1c, 0xd6, 0x78, + 0xf5, 0xe7, 0x59, 0x79, 0xa7, 0x69, 0x65, 0xff, 0xd1, 0xa9, 0x07, 0xc7, + 0x31, 0x3b, 0x4b, 0x2f, 0xfe, 0xd3, 0xec, 0x3f, 0x47, 0x3a, 0x0f, 0x2c, + 0xae, 0x22, 0x2f, 0xa8, 0xb6, 0x66, 0x93, 0x0e, 0xf2, 0x17, 0x21, 0x53, + 0x7f, 0x8c, 0x48, 0x68, 0x78, 0xc5, 0x95, 0x09, 0xdf, 0xe4, 0x69, 0xfe, + 0x3c, 0xbf, 0x9f, 0xf7, 0x0b, 0xe9, 0x65, 0xff, 0xf0, 0x08, 0xa0, 0x56, + 0x7a, 0x3e, 0xec, 0x79, 0x65, 0xe1, 0xbf, 0x96, 0x5f, 0xf4, 0x7a, 0x3e, + 0x94, 0xa3, 0x65, 0x97, 0xc1, 0xf1, 0xe9, 0x65, 0xfd, 0xba, 0x59, 0xb6, + 0x34, 0xb2, 0xb1, 0x15, 0x3a, 0x1c, 0x73, 0xa0, 0x11, 0x5f, 0xfe, 0xf1, + 0xb0, 0x71, 0x3a, 0x7c, 0x1e, 0x3d, 0x2c, 0xbf, 0xbb, 0x1e, 0x28, 0x92, + 0xcb, 0xfc, 0x4e, 0x29, 0xea, 0x25, 0x38, 0xfe, 0xcd, 0x3e, 0xe3, 0xf9, + 0x65, 0xfe, 0xfc, 0x19, 0xfb, 0x1e, 0x4b, 0x2c, 0xf2, 0x3c, 0xcf, 0x85, + 0xef, 0xf8, 0xfb, 0x9a, 0xd0, 0x36, 0xde, 0xb2, 0xff, 0xb9, 0x9d, 0x1e, + 0x67, 0xfc, 0x59, 0x5e, 0x3f, 0x5e, 0x9e, 0x5f, 0xf1, 0xf7, 0x35, 0xa0, + 0x6d, 0xbd, 0x65, 0xee, 0x00, 0xa7, 0x1e, 0xf7, 0x88, 0xaa, 0x4b, 0xb7, + 0x63, 0x8f, 0x93, 0x0d, 0x42, 0x5b, 0xa8, 0x7c, 0xfa, 0x16, 0xc5, 0x09, + 0x6e, 0x43, 0xee, 0xf9, 0x86, 0x38, 0x59, 0x7f, 0x1f, 0xb5, 0xa3, 0xd9, + 0x65, 0x41, 0xe7, 0xf8, 0x86, 0xe6, 0xba, 0xb2, 0xff, 0xe3, 0x3f, 0x4d, + 0x9d, 0x8e, 0xc0, 0xd6, 0x5a, 0x16, 0x5f, 0xff, 0x47, 0xd2, 0x72, 0xfe, + 0x77, 0xe7, 0xa3, 0x15, 0x65, 0x42, 0x2f, 0xe6, 0x18, 0x9f, 0x43, 0xdc, + 0x10, 0xbf, 0xd1, 0xf4, 0xde, 0x82, 0x15, 0x65, 0xc5, 0x32, 0xcb, 0xdc, + 0xf1, 0xac, 0xa6, 0x1b, 0x3e, 0x8b, 0xdf, 0x6c, 0xc3, 0x92, 0xcb, 0xe8, + 0xfc, 0x46, 0x2c, 0xbe, 0x20, 0x3c, 0x96, 0x50, 0x4f, 0x17, 0x84, 0x97, + 0xfb, 0xd0, 0xc6, 0xff, 0xff, 0x0b, 0x2a, 0x11, 0x83, 0x8d, 0x06, 0x45, + 0x7f, 0x4c, 0x7e, 0xee, 0x05, 0x65, 0x42, 0x74, 0xb8, 0xd1, 0xa8, 0x71, + 0x39, 0x6d, 0xfe, 0x18, 0x91, 0xbb, 0xe0, 0x71, 0x65, 0xfb, 0x70, 0xd9, + 0x9d, 0x59, 0x5f, 0x1f, 0x13, 0x0e, 0x2f, 0x72, 0x1a, 0x59, 0x73, 0xf9, + 0x65, 0xd3, 0x42, 0xcb, 0xa3, 0xab, 0x2a, 0x0d, 0x68, 0xa2, 0xf7, 0xc3, + 0xc2, 0x92, 0xcb, 0xf6, 0x1c, 0xbd, 0x0b, 0x2e, 0x9b, 0xa6, 0x78, 0xfe, + 0x21, 0xbf, 0xff, 0x0f, 0xd1, 0xa3, 0xfe, 0x04, 0x89, 0x8a, 0x18, 0xb2, + 0xff, 0x0b, 0x05, 0xde, 0x3f, 0x96, 0x54, 0x22, 0xdc, 0xcb, 0x9d, 0x5a, + 0xfc, 0x58, 0xe5, 0xb2, 0xca, 0x84, 0xfd, 0x70, 0x8c, 0xc7, 0x5d, 0x0c, + 0xa3, 0x1f, 0xe1, 0x6d, 0xe7, 0xcd, 0x2c, 0xbf, 0xa3, 0x6d, 0x46, 0x0d, + 0x65, 0xfd, 0xe8, 0xef, 0x61, 0x8b, 0x2f, 0xff, 0xed, 0x60, 0xf8, 0x40, + 0xc9, 0x01, 0xc7, 0xcc, 0xd2, 0xca, 0x1a, 0x21, 0xcc, 0xb6, 0xb4, 0x8f, + 0x1f, 0x0d, 0x89, 0x0b, 0x4b, 0xfe, 0xd4, 0x69, 0xe2, 0x51, 0x25, 0x97, + 0xff, 0xc5, 0x9b, 0xc2, 0xfa, 0xd9, 0xfa, 0xff, 0x7e, 0xb2, 0xa1, 0x18, + 0x06, 0x6c, 0xe6, 0xf7, 0xf8, 0x9f, 0x6f, 0xa5, 0x1b, 0x8b, 0x2f, 0xf7, + 0x3c, 0xe2, 0x7a, 0x24, 0xb2, 0xb6, 0x3e, 0xbf, 0x8d, 0xee, 0xc9, 0x96, + 0x5f, 0xff, 0xfb, 0x3b, 0x07, 0x27, 0xe7, 0x23, 0x0a, 0x25, 0x8c, 0x7f, + 0x96, 0x56, 0x1f, 0xff, 0x05, 0xef, 0xff, 0xbd, 0x0c, 0x3d, 0x1e, 0xe4, + 0x09, 0xb6, 0x34, 0xb2, 0xfe, 0xc0, 0x04, 0x4c, 0xf9, 0x65, 0x4c, 0x88, + 0x3d, 0xea, 0x97, 0xfe, 0x3d, 0x47, 0x04, 0x22, 0x8d, 0x2c, 0xbf, 0xe8, + 0x91, 0x3f, 0x64, 0x7b, 0xd6, 0x5e, 0x12, 0x3f, 0x59, 0x5e, 0x3d, 0x7e, + 0x1c, 0xdb, 0x75, 0x65, 0xee, 0xcb, 0x16, 0x5d, 0xcc, 0x59, 0x7e, 0x89, + 0x8e, 0x38, 0xb2, 0xff, 0xee, 0x46, 0x83, 0xd2, 0xcd, 0x9c, 0x96, 0x54, + 0x27, 0x19, 0xb1, 0x36, 0x42, 0x50, 0xc8, 0x9c, 0x53, 0xf1, 0xc2, 0x16, + 0xe9, 0x3d, 0xdd, 0x62, 0xcb, 0xee, 0x7c, 0x5a, 0x59, 0x7e, 0x98, 0x1d, + 0x8f, 0x2c, 0xa8, 0x3e, 0x3e, 0x8b, 0x80, 0x8e, 0xff, 0xd1, 0xd7, 0xe6, + 0x7b, 0xc6, 0xc5, 0x97, 0xe2, 0x8d, 0xa3, 0x65, 0x94, 0x33, 0xe5, 0x98, + 0xf6, 0xfe, 0x1c, 0x3e, 0xbf, 0x02, 0xcb, 0xb3, 0xcb, 0x2b, 0x63, 0xc4, + 0xde, 0x5d, 0x7f, 0xfd, 0x86, 0xc3, 0xfd, 0xfa, 0x59, 0xb6, 0xa1, 0x65, + 0xfd, 0xbb, 0x9b, 0xcb, 0x24, 0xb2, 0xe3, 0x99, 0x65, 0x19, 0xe4, 0x74, + 0xc6, 0xff, 0x18, 0xaf, 0x2e, 0x18, 0xd6, 0x54, 0x26, 0xed, 0x8c, 0xfe, + 0x25, 0xe4, 0x25, 0xba, 0x43, 0x70, 0x24, 0xb2, 0xfe, 0xf1, 0xeb, 0x59, + 0xd5, 0x95, 0xb1, 0xe2, 0x04, 0x5e, 0xff, 0x1f, 0x3c, 0xec, 0x8d, 0xd5, + 0x97, 0xff, 0xa6, 0x91, 0xef, 0x8d, 0x61, 0xc8, 0x1a, 0x59, 0x5a, 0x44, + 0x0b, 0x9a, 0xdf, 0xa3, 0x74, 0xda, 0x0a, 0xcb, 0xfe, 0xc6, 0x70, 0xe7, + 0xf3, 0x50, 0xb2, 0xa1, 0xbc, 0x17, 0xda, 0x13, 0x72, 0x8d, 0x24, 0x72, + 0xa9, 0xb2, 0x16, 0x41, 0x84, 0x2f, 0xc4, 0x27, 0x3b, 0xd3, 0xbb, 0x0d, + 0xb9, 0xa1, 0xc1, 0xa8, 0xfa, 0x19, 0x2a, 0x5f, 0xd2, 0x82, 0x9e, 0x13, + 0x1f, 0xc2, 0x8c, 0xa5, 0x18, 0x72, 0x1a, 0xfd, 0x94, 0x12, 0x08, 0x49, + 0x6f, 0x85, 0x20, 0x84, 0x5b, 0x85, 0x77, 0x0b, 0xd5, 0x97, 0xff, 0xfd, + 0x3c, 0x67, 0xb8, 0xc6, 0xd7, 0xa9, 0xed, 0xb4, 0xdf, 0xbb, 0x3c, 0x01, + 0x3a, 0x77, 0xa7, 0xd6, 0x5c, 0x70, 0xb2, 0xf4, 0xc2, 0x31, 0x65, 0xff, + 0x67, 0x79, 0x9d, 0xf6, 0x7e, 0xb2, 0x85, 0x3e, 0xd7, 0x15, 0x21, 0xfb, + 0x9c, 0x96, 0x56, 0x8f, 0x0d, 0x85, 0xd7, 0xd1, 0xdc, 0x62, 0xcb, 0xf7, + 0x60, 0x8f, 0xf5, 0x97, 0x7d, 0xf2, 0xcb, 0xf6, 0x81, 0xbf, 0xf6, 0x96, + 0x5c, 0x5c, 0x59, 0x7f, 0x63, 0x4f, 0xfb, 0x31, 0x65, 0x4e, 0x46, 0x96, + 0x10, 0xfc, 0x4f, 0xe1, 0x9f, 0xcb, 0x48, 0x5a, 0xff, 0xd0, 0xc3, 0xfb, + 0x80, 0x98, 0x02, 0xac, 0xbf, 0x17, 0x7d, 0x92, 0x59, 0x77, 0x31, 0x65, + 0x19, 0xbd, 0x72, 0x7b, 0xfc, 0x50, 0xd4, 0xfe, 0x6a, 0x16, 0x59, 0xa9, + 0xc8, 0xd3, 0x93, 0xf8, 0xc7, 0xef, 0xb6, 0x8e, 0xf1, 0x65, 0xe8, 0xff, + 0x8b, 0x2b, 0x47, 0x80, 0x44, 0x77, 0xcf, 0xa7, 0x92, 0xcb, 0x6b, 0x0f, + 0x08, 0x88, 0x6f, 0x34, 0xd3, 0x4b, 0x2f, 0xb6, 0x23, 0x84, 0x8d, 0xcd, + 0x05, 0xff, 0x6e, 0xf9, 0xca, 0x43, 0x8d, 0x96, 0x5f, 0x3f, 0x60, 0x6b, + 0x2e, 0xfa, 0x4b, 0x2f, 0xf0, 0xb1, 0xde, 0x79, 0xc0, 0xb2, 0xfc, 0x7b, + 0x67, 0xdd, 0x59, 0x71, 0x85, 0x65, 0x42, 0x22, 0xc5, 0x18, 0x61, 0xa7, + 0x4a, 0x6f, 0xfa, 0x5f, 0x70, 0xf4, 0x1f, 0x62, 0xcb, 0xf0, 0xaf, 0xcd, + 0xf2, 0x59, 0x5f, 0x22, 0x8b, 0x47, 0xbd, 0x3a, 0xbf, 0x39, 0x48, 0xd8, + 0xb2, 0xf6, 0x02, 0x7d, 0x65, 0x36, 0x57, 0xac, 0x9b, 0x41, 0x88, 0x8d, + 0x18, 0x70, 0xf4, 0xc8, 0xcb, 0x0e, 0x17, 0x7e, 0x46, 0x73, 0x32, 0x3c, + 0x04, 0x69, 0xdb, 0xcc, 0x67, 0xc9, 0xaf, 0x4f, 0x4d, 0xaa, 0xd8, 0x59, + 0x7f, 0xde, 0x79, 0x8a, 0x33, 0x52, 0x59, 0x7f, 0xe7, 0x21, 0x26, 0xe1, + 0x97, 0xd2, 0x59, 0x7f, 0xe6, 0xb3, 0x84, 0x71, 0xac, 0xd9, 0x65, 0x6e, + 0xa2, 0xd8, 0x07, 0x1b, 0xd0, 0x2f, 0xff, 0xff, 0xfd, 0xbb, 0x3b, 0xd9, + 0xf4, 0xbc, 0xc9, 0xc1, 0xf0, 0x3b, 0xdc, 0x2f, 0x67, 0x60, 0x73, 0xa7, + 0x7a, 0x7d, 0x65, 0xff, 0x0f, 0x00, 0xdb, 0x3f, 0x3b, 0x16, 0x5f, 0xa1, + 0x85, 0x93, 0x2c, 0xc3, 0x79, 0x7b, 0x51, 0xbd, 0x65, 0xed, 0xf8, 0x35, + 0x97, 0xf7, 0x8e, 0x08, 0x10, 0xb2, 0xff, 0x46, 0xe8, 0xb9, 0xc7, 0xdd, + 0x59, 0x42, 0x9f, 0x27, 0x8a, 0xef, 0xd8, 0x31, 0x3f, 0xe2, 0xca, 0x9c, + 0x8e, 0xdf, 0x0f, 0x3c, 0x20, 0x88, 0x8e, 0xfb, 0xd0, 0x27, 0x96, 0x50, + 0xd3, 0x88, 0xe4, 0x64, 0x7d, 0x42, 0xbf, 0xa2, 0x5e, 0x8d, 0xe6, 0xb2, + 0xfc, 0xc0, 0x14, 0x31, 0x65, 0x86, 0xb2, 0xe7, 0xe2, 0xcb, 0x69, 0x86, + 0x9c, 0x02, 0x35, 0x07, 0xea, 0xe9, 0xd7, 0x4b, 0xab, 0x2f, 0xa6, 0x8e, + 0xba, 0xcb, 0xe0, 0x7f, 0x84, 0x13, 0x72, 0x01, 0x7b, 0x49, 0x65, 0x6c, + 0x79, 0x3e, 0x38, 0xbf, 0xfa, 0x3c, 0x20, 0xfa, 0x79, 0x34, 0x31, 0x65, + 0xff, 0xba, 0x6d, 0x07, 0xc7, 0xbc, 0xc6, 0xb2, 0xff, 0x6a, 0x5e, 0xcd, + 0xf0, 0xc5, 0x97, 0xc0, 0x6d, 0xf3, 0x16, 0x57, 0xc8, 0x9c, 0x34, 0x17, + 0x35, 0xbf, 0x3c, 0xb8, 0x7b, 0xd6, 0x5f, 0xf6, 0x6b, 0x27, 0x66, 0xa0, + 0x6b, 0x2f, 0x30, 0x1b, 0xd6, 0x5f, 0xd9, 0x2f, 0xa5, 0x9d, 0x59, 0x6e, + 0xc9, 0x10, 0xd8, 0x73, 0xe1, 0xfb, 0xdd, 0x03, 0x16, 0x5f, 0x7d, 0x2f, + 0x1a, 0xca, 0x83, 0xc0, 0x71, 0xda, 0x54, 0x41, 0xcb, 0xf9, 0xf6, 0xfd, + 0xc8, 0x68, 0xac, 0x1a, 0xb7, 0x02, 0x7a, 0x93, 0xe3, 0x14, 0xc4, 0x4f, + 0xf5, 0xd2, 0xff, 0x8f, 0x3b, 0xcd, 0x60, 0xf1, 0x65, 0xfd, 0xe7, 0x61, + 0x47, 0xcb, 0x2f, 0x99, 0xc7, 0x97, 0x8f, 0x90, 0x43, 0x7b, 0xfb, 0xff, + 0x16, 0x77, 0x8b, 0x2f, 0xe2, 0xcd, 0xe5, 0x9c, 0x59, 0x68, 0xd1, 0xed, + 0xf4, 0xba, 0xa1, 0x34, 0x3c, 0x84, 0x3f, 0x21, 0x2b, 0x6e, 0x2c, 0xbf, + 0x7a, 0x37, 0x41, 0x25, 0x95, 0x06, 0xf1, 0xc4, 0x6a, 0x4b, 0xc2, 0x41, + 0x85, 0x1e, 0xa1, 0x44, 0xc2, 0x3f, 0x43, 0x87, 0xf2, 0xf2, 0x85, 0x7f, + 0x65, 0x2b, 0xef, 0x75, 0xbf, 0xa4, 0x59, 0xf7, 0x71, 0x65, 0xff, 0xf8, + 0x4f, 0xff, 0x8f, 0xb3, 0x61, 0xbb, 0x85, 0xc6, 0xb2, 0xa1, 0x10, 0xda, + 0x2d, 0xbf, 0x8b, 0xb2, 0xcd, 0x62, 0xcb, 0xa7, 0xdd, 0x65, 0x39, 0xe2, + 0x88, 0x59, 0x7f, 0xb5, 0xb7, 0x1c, 0x3a, 0xc5, 0x97, 0xfb, 0xd8, 0x17, + 0xf4, 0x79, 0x65, 0xff, 0xce, 0x7d, 0x82, 0x0e, 0x77, 0xc6, 0xb2, 0xfe, + 0x23, 0xff, 0x9e, 0x35, 0x97, 0x68, 0x6b, 0x2e, 0x69, 0xa5, 0x95, 0x23, + 0x60, 0xd0, 0xbd, 0xe8, 0x3d, 0xe9, 0x1b, 0x9a, 0x2b, 0xfe, 0x32, 0x3f, + 0xf9, 0x9e, 0x75, 0x95, 0x89, 0xac, 0x78, 0xc9, 0xd0, 0x78, 0xff, 0xd2, + 0xfb, 0xfa, 0x0b, 0xbe, 0xc9, 0x2c, 0xbb, 0xfe, 0x2c, 0xbf, 0x8f, 0xc6, + 0x28, 0xb0, 0xb2, 0xc2, 0x0c, 0xf1, 0xba, 0x31, 0x5a, 0x44, 0xdf, 0x1c, + 0xaa, 0x15, 0x49, 0x9c, 0x71, 0x82, 0x43, 0x92, 0xfe, 0xdd, 0xc9, 0x19, + 0x3a, 0xcb, 0xcd, 0x34, 0xd2, 0x4a, 0x48, 0xdc, 0xd0, 0x5f, 0x1b, 0x47, + 0x32, 0x4a, 0x09, 0xe0, 0x18, 0xed, 0xf6, 0xa3, 0x7e, 0x2c, 0xbd, 0x36, + 0x79, 0x65, 0x61, 0xe0, 0x39, 0x1d, 0xf6, 0x74, 0x0e, 0xb2, 0xa7, 0x96, + 0x7b, 0xe4, 0x43, 0x8f, 0x63, 0x5c, 0x94, 0xde, 0x73, 0xc9, 0x3a, 0x8c, + 0x91, 0x8c, 0xfe, 0x95, 0x6a, 0xe7, 0x85, 0x08, 0xee, 0x30, 0xf4, 0x82, + 0xfb, 0x52, 0x06, 0x2c, 0xbf, 0xc5, 0xf7, 0x44, 0xf3, 0xfe, 0xb2, 0xf1, + 0x1f, 0x16, 0x5e, 0x3c, 0xd9, 0x65, 0xf0, 0xdc, 0xbf, 0x59, 0x7f, 0xf4, + 0x17, 0xe5, 0x1f, 0x4d, 0x05, 0xfa, 0xca, 0xc4, 0x6a, 0x19, 0xb7, 0x86, + 0xff, 0x1c, 0xe1, 0x15, 0xf4, 0xbf, 0x91, 0x2c, 0xbf, 0x4d, 0x3f, 0xfc, + 0xfe, 0x2c, 0xbe, 0x8e, 0xbf, 0x96, 0x58, 0xd6, 0x5f, 0xf3, 0x8a, 0x1f, + 0x18, 0xde, 0x4b, 0x2f, 0xb6, 0xd9, 0xfe, 0x59, 0x77, 0xfc, 0x33, 0xee, + 0x21, 0x01, 0x0e, 0x6f, 0xb3, 0x5e, 0x35, 0x97, 0xff, 0x9e, 0x45, 0x9b, + 0xde, 0x68, 0xce, 0xf1, 0x65, 0x41, 0xf5, 0xb9, 0x0d, 0xff, 0xe6, 0xbf, + 0xf6, 0x66, 0xc7, 0x9b, 0xe3, 0xe5, 0x97, 0xff, 0xbf, 0x86, 0x6b, 0x51, + 0xb7, 0x5d, 0xc5, 0x59, 0x7c, 0x0e, 0x7b, 0x16, 0x5e, 0x64, 0x05, 0x65, + 0xff, 0x3c, 0xb9, 0xfb, 0xef, 0xc1, 0xac, 0xbb, 0x3d, 0x87, 0xaf, 0xe1, + 0xcb, 0xf6, 0x8f, 0x67, 0x62, 0xcb, 0x9a, 0x69, 0x65, 0x61, 0xe0, 0xb4, + 0x51, 0x77, 0x80, 0x91, 0xb9, 0xa2, 0xbd, 0x21, 0x18, 0xb2, 0xf9, 0xfe, + 0x18, 0xd6, 0x54, 0x91, 0xf2, 0x70, 0x8f, 0x72, 0x9e, 0x0f, 0x5e, 0xc6, + 0x6e, 0xac, 0xbf, 0xf3, 0xef, 0xc1, 0x0a, 0x3c, 0x7b, 0x2c, 0xbf, 0xd9, + 0xc2, 0xce, 0xf8, 0xd6, 0x50, 0xd1, 0x21, 0xa1, 0xf2, 0x40, 0xac, 0x57, + 0xa3, 0x34, 0x29, 0xbc, 0x41, 0xfa, 0x71, 0x26, 0x76, 0x37, 0x2d, 0xf0, + 0xd0, 0xbe, 0x97, 0xf9, 0xfa, 0xcb, 0xfb, 0xb9, 0x31, 0x43, 0x17, 0x10, + 0x12, 0xf8, 0x87, 0xe8, 0x54, 0x40, 0x46, 0xe6, 0xea, 0xbe, 0x44, 0xf3, + 0xaf, 0x5f, 0xfb, 0xe9, 0x11, 0xed, 0xd7, 0x97, 0x16, 0x5f, 0xf7, 0x07, + 0x85, 0x04, 0x08, 0x59, 0x63, 0x14, 0xfd, 0xbe, 0x40, 0xbf, 0x68, 0xc0, + 0xdb, 0x85, 0x97, 0xfb, 0x59, 0xf7, 0x3f, 0x7e, 0xac, 0xb8, 0x01, 0x83, + 0xe0, 0x14, 0xae, 0xfd, 0x12, 0x7f, 0x9a, 0x59, 0x7f, 0xd3, 0xb5, 0xac, + 0xf7, 0xd2, 0xe2, 0xcb, 0xff, 0xfd, 0x9c, 0x03, 0xfd, 0x98, 0x42, 0xf3, + 0x98, 0x5d, 0xe2, 0xca, 0x0a, 0x27, 0x7e, 0x3d, 0xbf, 0x3b, 0x43, 0x17, + 0x7a, 0xca, 0xc4, 0xf3, 0x4e, 0x11, 0x3a, 0x2e, 0x78, 0x63, 0x11, 0x25, + 0xf9, 0xfb, 0xc0, 0x49, 0x65, 0xb6, 0x59, 0x7e, 0x9d, 0x93, 0x1b, 0x16, + 0x5f, 0xfe, 0x04, 0x4e, 0xdc, 0xe9, 0xf3, 0xdf, 0x3b, 0x4b, 0x2b, 0x63, + 0xfb, 0xe1, 0x55, 0xff, 0xb0, 0x7e, 0x76, 0xf1, 0xe8, 0xf9, 0x65, 0x36, + 0x51, 0xe1, 0xa8, 0x46, 0xb0, 0x8e, 0xff, 0x88, 0x5d, 0x11, 0x84, 0x04, + 0xb2, 0xfd, 0xd8, 0xc2, 0x85, 0x94, 0x67, 0xba, 0x47, 0x17, 0x84, 0x2e, + 0xac, 0xbc, 0x6c, 0x75, 0x95, 0xb1, 0xb8, 0x14, 0x76, 0xfc, 0x1c, 0x3e, + 0x31, 0x65, 0xff, 0xdc, 0x82, 0xe8, 0x93, 0x78, 0xfb, 0xc5, 0x97, 0xd9, + 0xa8, 0xde, 0xb2, 0xdf, 0x7c, 0x7d, 0x3d, 0x46, 0xa6, 0x22, 0xe9, 0xe1, + 0x21, 0x50, 0x9f, 0x96, 0x42, 0x58, 0xd6, 0x01, 0x0d, 0xdb, 0xdb, 0x91, + 0xf2, 0xcb, 0xd1, 0x8c, 0x59, 0x7f, 0xc2, 0xf7, 0x35, 0xa3, 0x9b, 0x8b, + 0x2a, 0x47, 0xec, 0xc2, 0x07, 0x1b, 0xbf, 0xf6, 0x32, 0x02, 0x7d, 0xd0, + 0x18, 0xb2, 0xfd, 0xb0, 0x26, 0x71, 0xac, 0xbc, 0x7f, 0x71, 0x65, 0xff, + 0xdc, 0x8d, 0xb3, 0x53, 0x14, 0x7f, 0xc5, 0x97, 0xfa, 0x32, 0x62, 0x8d, + 0x49, 0x65, 0x49, 0x1f, 0xae, 0x7d, 0xf9, 0x57, 0x47, 0x5a, 0x46, 0xbe, + 0xd3, 0xca, 0x65, 0x97, 0xee, 0x72, 0x3f, 0x9e, 0x96, 0x50, 0xa7, 0xa2, + 0xd1, 0x1d, 0xf7, 0xb3, 0xf7, 0x59, 0x50, 0x8e, 0x27, 0x84, 0xe7, 0x09, + 0x2f, 0xf6, 0xa6, 0xe1, 0x97, 0x4d, 0x65, 0xff, 0xa4, 0x6c, 0x6b, 0x08, + 0x7e, 0x85, 0x97, 0xf8, 0x5c, 0xd7, 0xec, 0x3e, 0x2c, 0xaf, 0x8f, 0xcc, + 0x8f, 0xaf, 0xf7, 0x8f, 0xef, 0xf4, 0xe3, 0x59, 0x7d, 0xf7, 0xfa, 0x84, + 0x97, 0x34, 0xd2, 0x4a, 0x83, 0x7a, 0xd1, 0x25, 0xc4, 0x69, 0x1b, 0x9a, + 0x1b, 0xff, 0xff, 0x89, 0xbe, 0x66, 0xe4, 0x4c, 0x6c, 0x89, 0xa7, 0x17, + 0xd2, 0xf0, 0x3f, 0x59, 0x71, 0x85, 0x65, 0x2c, 0xbb, 0x52, 0x9c, 0x9a, + 0xb4, 0xa1, 0x27, 0xf1, 0x3e, 0x9e, 0xff, 0x16, 0xa8, 0x4f, 0x61, 0xe3, + 0x78, 0xbf, 0x14, 0x82, 0x0d, 0x96, 0x5e, 0x8d, 0x4c, 0xb2, 0x9c, 0xf1, + 0x80, 0x53, 0x53, 0xcb, 0x65, 0xa3, 0x10, 0xf6, 0xd9, 0x26, 0x44, 0x63, + 0x30, 0xc9, 0xc6, 0x03, 0x8c, 0x5b, 0x52, 0x99, 0x98, 0xa3, 0xe9, 0x61, + 0x4f, 0x0b, 0xb2, 0x94, 0x1b, 0xd3, 0x30, 0x4a, 0x7e, 0x69, 0xba, 0xf7, + 0x4d, 0xa5, 0x97, 0xf6, 0xd3, 0xe0, 0xf1, 0xe9, 0x65, 0xfe, 0x29, 0x70, + 0x7e, 0x36, 0x96, 0x5e, 0xd9, 0xd8, 0xb2, 0xef, 0x89, 0x65, 0xfb, 0xe9, + 0x78, 0x4e, 0x2c, 0xb3, 0xe1, 0xe1, 0x10, 0xbd, 0xc6, 0x35, 0x97, 0xba, + 0x6d, 0x2e, 0x30, 0x85, 0xe0, 0x38, 0x8a, 0x88, 0x1d, 0xf1, 0xaa, 0xa9, + 0x22, 0x83, 0xc4, 0xbd, 0x40, 0xa8, 0x4e, 0x54, 0xcc, 0x9c, 0xd0, 0x10, + 0xff, 0xbc, 0x5a, 0x6d, 0xac, 0xbf, 0xb0, 0xbf, 0xcc, 0x1a, 0xcb, 0xff, + 0xff, 0x48, 0x3e, 0x3d, 0x6c, 0xc8, 0x0f, 0xa0, 0xc7, 0x1e, 0x89, 0x2c, + 0xbd, 0xe8, 0x15, 0x65, 0x7c, 0x88, 0x9d, 0xed, 0x97, 0xfb, 0x03, 0x1b, + 0x14, 0x05, 0x65, 0xbf, 0x59, 0x6d, 0xd9, 0xc7, 0x88, 0x66, 0x57, 0x40, + 0x16, 0x5f, 0x80, 0x5f, 0x89, 0x25, 0x97, 0xff, 0xc5, 0x9c, 0x9c, 0x27, + 0xa3, 0xf6, 0x3c, 0xa7, 0x61, 0xe0, 0x70, 0x5a, 0x9d, 0x34, 0x10, 0x35, + 0x6f, 0x6c, 0xbf, 0xfb, 0x47, 0xf0, 0x9d, 0xc3, 0x93, 0xf1, 0x65, 0xff, + 0x03, 0x68, 0x9d, 0x1d, 0x81, 0xac, 0xad, 0x22, 0x03, 0xc8, 0xb7, 0x9f, + 0x5b, 0x2c, 0xb9, 0xa6, 0x96, 0x5f, 0xed, 0x1e, 0x6f, 0x12, 0x4d, 0xc2, + 0x6d, 0x9a, 0x1d, 0xbf, 0xf1, 0x8b, 0x3b, 0x80, 0xfd, 0x91, 0x25, 0x97, + 0xfb, 0x99, 0xd7, 0x66, 0x12, 0xca, 0x1a, 0x63, 0x9a, 0x59, 0xf2, 0x7f, + 0x50, 0xef, 0xfd, 0xcc, 0x2c, 0xf6, 0x07, 0x09, 0x65, 0xff, 0x4b, 0x9d, + 0xf6, 0x33, 0x24, 0xb2, 0xff, 0xd8, 0x2f, 0x9e, 0x42, 0x3f, 0xd0, 0xb2, + 0xa4, 0x8b, 0x43, 0x3b, 0x23, 0x9a, 0x84, 0xc8, 0x32, 0x1e, 0x57, 0xfb, + 0x7b, 0x58, 0x43, 0xf4, 0x2c, 0xbe, 0x0b, 0xb8, 0x55, 0x10, 0xc2, 0xff, + 0x1c, 0x6a, 0x40, 0x8c, 0x59, 0x7e, 0x63, 0x1e, 0x59, 0xa3, 0xdf, 0x22, + 0xeb, 0xff, 0xfe, 0x2c, 0x0f, 0xa3, 0xe9, 0x4f, 0xe7, 0x82, 0x78, 0x4e, + 0x2a, 0xcb, 0x83, 0xfa, 0xcb, 0x9f, 0x98, 0x88, 0x06, 0x34, 0x56, 0x23, + 0xa8, 0x10, 0xbd, 0xbf, 0xb5, 0x93, 0x41, 0x0d, 0x65, 0x42, 0xf7, 0xec, + 0x88, 0x0e, 0x3f, 0x6f, 0x4a, 0xee, 0x72, 0x72, 0x8d, 0xf8, 0x04, 0xf7, + 0xe2, 0x8f, 0xfe, 0xfd, 0x65, 0xfb, 0x25, 0x1a, 0xfd, 0x65, 0xd0, 0x22, + 0xcb, 0xf3, 0xec, 0x51, 0x24, 0x95, 0x31, 0xbe, 0xf0, 0xbd, 0xff, 0xbb, + 0xe7, 0x3e, 0xc6, 0xa3, 0xf5, 0x97, 0xef, 0x39, 0x09, 0xb2, 0xca, 0x92, + 0x63, 0x22, 0x95, 0x31, 0x98, 0x88, 0x9a, 0x3e, 0xbf, 0xf1, 0x86, 0x38, + 0xfa, 0xd1, 0xb4, 0xb2, 0xff, 0xb6, 0x93, 0x23, 0xce, 0x62, 0xac, 0xbf, + 0xf7, 0x4c, 0xb6, 0xcf, 0xbb, 0xe7, 0x59, 0x7f, 0xf1, 0xe8, 0x50, 0xf8, + 0xfc, 0x40, 0xd9, 0x65, 0x62, 0x21, 0x7f, 0x3f, 0xa2, 0x47, 0x17, 0x21, + 0x7f, 0x7c, 0x0c, 0xe1, 0xac, 0xbb, 0x80, 0x59, 0x7f, 0x4b, 0xce, 0xe5, + 0x0b, 0x2d, 0x01, 0x3c, 0x2f, 0x85, 0xef, 0xfe, 0xce, 0xc7, 0x8f, 0x05, + 0x16, 0x24, 0xb2, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x05, 0x2f, 0x7a, 0x18, + 0xb2, 0xff, 0xd1, 0xb3, 0xf7, 0x59, 0x29, 0xfc, 0x59, 0x43, 0x46, 0xae, + 0x22, 0x19, 0xa1, 0x0e, 0x5f, 0xbd, 0x0c, 0x07, 0xcb, 0x2f, 0xbb, 0xc8, + 0xfd, 0x25, 0xe7, 0xd6, 0xc9, 0x2f, 0x80, 0x4e, 0x2a, 0x4b, 0xfc, 0x7b, + 0x67, 0x79, 0x1f, 0xa4, 0xa4, 0x97, 0xf6, 0x06, 0x3d, 0x0c, 0x49, 0x73, + 0x4d, 0x24, 0xbf, 0x8a, 0x0b, 0x63, 0xd2, 0x4a, 0xc4, 0xc4, 0x82, 0x46, + 0xc1, 0xdf, 0x11, 0x7e, 0x68, 0x41, 0x6d, 0x16, 0x08, 0x33, 0x71, 0xe9, + 0x23, 0x73, 0xf3, 0xa9, 0x2a, 0x11, 0x98, 0xeb, 0xd1, 0xd7, 0x5f, 0xff, + 0xa1, 0xae, 0x16, 0x7d, 0xd7, 0x2d, 0xb3, 0xee, 0xac, 0xa8, 0x5d, 0x94, + 0xc4, 0xc3, 0x8c, 0x83, 0x44, 0xfe, 0x63, 0x79, 0x61, 0xc4, 0x73, 0x79, + 0x8e, 0x35, 0x97, 0xff, 0xfc, 0x59, 0xde, 0xc7, 0xd2, 0x9d, 0xe8, 0x60, + 0x3e, 0xe7, 0xa1, 0x65, 0xfd, 0x85, 0xc3, 0x2c, 0x59, 0x7f, 0x33, 0x07, + 0xec, 0xd9, 0x65, 0xff, 0xe2, 0x71, 0x79, 0xff, 0x7b, 0xf7, 0x70, 0x2b, + 0x2a, 0x0f, 0xec, 0x52, 0xeb, 0xde, 0x03, 0x16, 0x50, 0xa9, 0xb5, 0x6e, + 0x8e, 0x79, 0xa8, 0xa1, 0x4b, 0xb8, 0x45, 0x7f, 0x16, 0x73, 0x51, 0xc5, + 0x97, 0x81, 0x2c, 0x59, 0x4e, 0x79, 0x1f, 0x96, 0x5f, 0xc3, 0xc2, 0x7e, + 0xf1, 0x65, 0x48, 0xf3, 0xb8, 0x45, 0x7c, 0xf3, 0x67, 0x96, 0x5f, 0xf4, + 0x19, 0x3f, 0x66, 0x36, 0x96, 0x5b, 0x53, 0x8f, 0x6b, 0x84, 0x55, 0x08, + 0xa6, 0x37, 0xcb, 0xf6, 0xbe, 0x96, 0x75, 0x65, 0xff, 0xf3, 0x9f, 0xfc, + 0xc1, 0x5d, 0x9a, 0x8c, 0x25, 0x97, 0xd0, 0x11, 0x3f, 0x59, 0x7d, 0xe8, + 0xfb, 0x8b, 0x2f, 0x8f, 0x3e, 0xea, 0xca, 0xc3, 0xc5, 0xe9, 0x1d, 0xff, + 0xff, 0x78, 0xf5, 0x83, 0x76, 0x61, 0x74, 0xa1, 0xac, 0xef, 0x16, 0x5a, + 0x16, 0x5f, 0x46, 0xf1, 0xc0, 0x4f, 0xd3, 0xf6, 0x4b, 0xff, 0x3c, 0xbc, + 0xfd, 0xe6, 0x14, 0x96, 0x57, 0x8f, 0xe3, 0xa7, 0x75, 0xa5, 0x41, 0xfe, + 0x29, 0x24, 0xee, 0x34, 0xf6, 0x30, 0xdb, 0xff, 0xd9, 0xaf, 0xfb, 0xac, + 0xdb, 0xb1, 0x9c, 0x59, 0x78, 0x38, 0xc5, 0x97, 0xf0, 0xdf, 0xbc, 0x04, + 0x96, 0x5d, 0x29, 0xf8, 0x3c, 0xa1, 0x07, 0x2f, 0xe7, 0xd4, 0x74, 0xf7, + 0xac, 0xaf, 0x8f, 0x7c, 0x8c, 0x6f, 0xff, 0xfd, 0xad, 0x47, 0xd2, 0xfb, + 0x87, 0xa0, 0xfb, 0x04, 0xd1, 0xc0, 0xd6, 0x5f, 0xf3, 0xeb, 0x3e, 0x94, + 0x7f, 0x0b, 0x2a, 0x11, 0x95, 0x84, 0x40, 0x6f, 0xbf, 0xef, 0xa5, 0xdc, + 0xe7, 0x73, 0x75, 0x65, 0xff, 0xf8, 0x22, 0xbf, 0x33, 0x52, 0x3f, 0x39, + 0x7d, 0x25, 0x97, 0xfe, 0x0c, 0x77, 0x99, 0xd3, 0xf9, 0xa5, 0x95, 0x88, + 0x96, 0x15, 0x5a, 0xa1, 0x1e, 0xcf, 0x0d, 0x3a, 0x85, 0x6b, 0x23, 0x51, + 0xf4, 0x77, 0xc5, 0x19, 0x25, 0xfb, 0x84, 0x0d, 0xf2, 0x59, 0x7b, 0x61, + 0x02, 0xb2, 0xf8, 0x8e, 0x6d, 0xd5, 0x95, 0x07, 0xdc, 0xe5, 0x5c, 0x1f, + 0xb9, 0xff, 0x59, 0x52, 0x3c, 0x4e, 0x96, 0xdf, 0xff, 0x9d, 0xe4, 0xfe, + 0x9d, 0x9b, 0x03, 0xa0, 0x71, 0x15, 0x17, 0xda, 0xf6, 0xf0, 0x75, 0x65, + 0xd0, 0x35, 0x95, 0x06, 0xd7, 0xf1, 0xfb, 0xf9, 0xc3, 0x05, 0x1f, 0xac, + 0xbf, 0xf7, 0x0c, 0xbe, 0x96, 0x81, 0x1c, 0x59, 0x7e, 0xcd, 0x60, 0xe1, + 0x65, 0x4c, 0x9a, 0x66, 0xa1, 0x35, 0xe2, 0x1e, 0x96, 0x6f, 0x3f, 0xbf, + 0x7b, 0x26, 0x8d, 0xd5, 0x97, 0xe1, 0xeb, 0x58, 0xd2, 0xca, 0xd8, 0xf5, + 0x08, 0xae, 0xfe, 0x32, 0xce, 0xc4, 0xcb, 0x2f, 0xfe, 0x97, 0x21, 0x98, + 0x12, 0x7c, 0x0a, 0xcb, 0xe8, 0x21, 0x46, 0xb2, 0xf3, 0xff, 0x0b, 0x2a, + 0x11, 0x9d, 0x22, 0x2f, 0x8b, 0x3c, 0x85, 0xc2, 0x2b, 0xa5, 0xc5, 0x97, + 0xf7, 0x24, 0x61, 0x7d, 0x2c, 0xb0, 0x56, 0x5f, 0xff, 0xef, 0xcf, 0x42, + 0xfa, 0x19, 0x34, 0x8c, 0xbd, 0x1a, 0x85, 0x96, 0xee, 0x1f, 0x9f, 0x04, + 0x6f, 0x1e, 0xb1, 0x65, 0xe2, 0xc9, 0x2c, 0xa0, 0x9b, 0x6e, 0x8d, 0xde, + 0x28, 0x62, 0xca, 0x84, 0x4c, 0x9a, 0xef, 0x08, 0x6f, 0xfc, 0x47, 0x2e, + 0xfb, 0x34, 0x7c, 0x59, 0x7e, 0x91, 0x66, 0x6c, 0xb2, 0xed, 0x6c, 0xb2, + 0xff, 0xa6, 0x6c, 0xf4, 0xc6, 0x3c, 0x25, 0x95, 0x07, 0xf9, 0x84, 0xe6, + 0x31, 0x7f, 0x9c, 0x71, 0xfc, 0x35, 0xc5, 0x95, 0xe3, 0xe0, 0x72, 0xcb, + 0xfa, 0x7d, 0xb5, 0xce, 0x9d, 0xe9, 0xf5, 0xc4, 0x02, 0xbf, 0xdd, 0xfc, + 0x42, 0x97, 0xb1, 0x71, 0x00, 0xaf, 0x3e, 0xa4, 0xb8, 0x80, 0x55, 0x87, + 0xd6, 0x04, 0x2b, 0x9e, 0x4b, 0x88, 0x05, 0x7c, 0xe5, 0xf4, 0x97, 0x10, + 0x0a, 0xff, 0x18, 0x7d, 0x9f, 0xff, 0x0b, 0x88, 0x05, 0x78, 0xc8, 0x6b, + 0x88, 0x05, 0x43, 0x45, 0xdb, 0x08, 0xfc, 0x5f, 0xbd, 0x06, 0xc2, 0xae, + 0x20, 0x15, 0xed, 0x47, 0x57, 0x10, 0x0a, 0x97, 0x10, 0x0a, 0xf4, 0xf9, + 0xfe, 0xb8, 0x80, 0x57, 0x43, 0x17, 0x10, 0x0a, 0x86, 0x7d, 0x18, 0x30, + 0x65, 0xb7, 0xc6, 0xc1, 0xc2, 0xe2, 0x01, 0x5e, 0xeb, 0x85, 0x71, 0x00, + 0xaf, 0xfc, 0x4f, 0x26, 0xfe, 0x30, 0xbe, 0x97, 0x10, 0x0a, 0xff, 0xe7, + 0xe8, 0x36, 0x32, 0xef, 0x9e, 0x4b, 0x88, 0x05, 0x73, 0x8d, 0x71, 0x00, + 0xaf, 0xf1, 0x3b, 0x5c, 0xe4, 0x7e, 0xb8, 0x80, 0x57, 0xe3, 0x15, 0xcb, + 0xf5, 0xc4, 0x02, 0xb8, 0xf8, 0xb8, 0x80, 0x55, 0xa3, 0xd8, 0xe9, 0xad, + 0xff, 0xef, 0x1f, 0x7b, 0x9c, 0x9d, 0xa3, 0x86, 0x2e, 0x20, 0x15, 0xfb, + 0xa5, 0x1f, 0x49, 0x51, 0x00, 0xae, 0xfe, 0x17, 0x10, 0x09, 0xb9, 0xb5, + 0xbf, 0x1c, 0x31, 0xc6, 0xb8, 0x80, 0x57, 0xc0, 0x72, 0x0a, 0xe2, 0x01, + 0x5e, 0x8d, 0x05, 0x71, 0x00, 0xaf, 0xfd, 0x9f, 0x70, 0x71, 0x85, 0xff, + 0x17, 0x10, 0x0a, 0xfc, 0x07, 0x94, 0xa1, 0x71, 0x00, 0xaf, 0xce, 0x1f, + 0x47, 0xcb, 0x88, 0x05, 0x58, 0x8b, 0x5f, 0x26, 0x00, 0xd2, 0xed, 0x7e, + 0xb8, 0x80, 0x55, 0x25, 0x66, 0x83, 0x23, 0xc8, 0x4c, 0x84, 0x88, 0xe1, + 0x1b, 0xe3, 0x6f, 0xcc, 0xf8, 0x5d, 0xd8, 0x6f, 0x00, 0xbe, 0xfd, 0x9d, + 0xe3, 0xfc, 0xb8, 0x80, 0x57, 0xf8, 0x70, 0x19, 0xb5, 0x1d, 0x5c, 0x40, + 0x21, 0x9b, 0x5b, 0xda, 0x86, 0x97, 0x10, 0x0a, 0x82, 0x7f, 0x7a, 0x51, + 0xbf, 0xc0, 0x79, 0x4a, 0x04, 0x62, 0xe2, 0x01, 0x5f, 0x19, 0x4b, 0x8b, + 0x88, 0x05, 0x7f, 0x3c, 0xd2, 0xcf, 0xa4, 0xb8, 0x80, 0x55, 0x88, 0xcd, + 0xf1, 0x17, 0xe8, 0x20, 0x2e, 0xbf, 0xef, 0x47, 0x1b, 0xf0, 0x1a, 0xfd, + 0x71, 0x00, 0xac, 0x6b, 0x88, 0x05, 0x73, 0xec, 0x33, 0xe7, 0xf2, 0x55, + 0xda, 0xfd, 0x71, 0x00, 0xaf, 0xcf, 0xde, 0x1f, 0xcb, 0x88, 0x05, 0x7f, + 0x19, 0x02, 0x59, 0xc5, 0xc4, 0x02, 0xa8, 0x44, 0x99, 0x12, 0xf4, 0xd6, + 0xa1, 0x95, 0x04, 0x38, 0x5b, 0xe4, 0x37, 0x0c, 0xfe, 0x61, 0xfd, 0x42, + 0x47, 0xc5, 0xae, 0x5d, 0xf9, 0x49, 0x26, 0x72, 0x5c, 0x2f, 0x65, 0x0c, + 0x4f, 0xc2, 0x90, 0x48, 0x60, 0xde, 0xe9, 0xb4, 0xb8, 0xc2, 0x57, 0x9d, + 0xc2, 0xa8, 0x80, 0x4d, 0xd3, 0x15, 0xfa, 0x1d, 0x77, 0x9b, 0x54, 0x6c, + 0x59, 0x7f, 0x74, 0xb3, 0xf9, 0xfc, 0x59, 0x42, 0x9e, 0xa3, 0x09, 0x2f, + 0xf4, 0x8f, 0xa2, 0x8b, 0x9b, 0xab, 0x2e, 0xdf, 0xb2, 0xcb, 0xfb, 0x68, + 0xd7, 0x9f, 0x75, 0x65, 0xb8, 0xb2, 0xf8, 0xa0, 0x7e, 0x59, 0x5e, 0x36, + 0x5b, 0xc4, 0x6a, 0x11, 0x41, 0x83, 0x46, 0xc7, 0x7f, 0xe2, 0xc0, 0xf8, + 0xff, 0xfc, 0x13, 0x2c, 0xbf, 0xe1, 0xe0, 0xa5, 0x9d, 0x86, 0x96, 0x54, + 0x8f, 0xeb, 0xf4, 0x1b, 0xf7, 0x70, 0x6f, 0xe5, 0x97, 0x7f, 0x09, 0x2c, + 0x2a, 0x4b, 0xde, 0x8d, 0x96, 0x5c, 0xd3, 0x49, 0x28, 0xcf, 0x7f, 0x74, + 0x5e, 0x61, 0x26, 0x87, 0x6d, 0xc4, 0x8d, 0xcf, 0x02, 0xb1, 0x1d, 0x47, + 0x0c, 0x6b, 0xff, 0xb3, 0xf9, 0x1f, 0x61, 0x90, 0x43, 0x59, 0x4c, 0x3e, + 0x97, 0x26, 0xbf, 0xbb, 0xcc, 0x6b, 0xc6, 0xb2, 0xdd, 0x59, 0x5d, 0x37, + 0xe0, 0x2e, 0xbf, 0x46, 0xdc, 0x64, 0x96, 0x51, 0x9e, 0x57, 0x48, 0x6f, + 0xff, 0xdf, 0x4b, 0x85, 0xf4, 0x0b, 0xc8, 0xfa, 0x46, 0xc5, 0x97, 0xff, + 0xbb, 0xcc, 0x14, 0xf4, 0x27, 0xff, 0xc7, 0xcb, 0x2a, 0x7b, 0x6c, 0xdf, + 0xa0, 0x5f, 0x68, 0xce, 0x64, 0x5c, 0x38, 0xc6, 0x43, 0x4b, 0xb8, 0x38, + 0x52, 0x4c, 0x47, 0xa8, 0x6c, 0x3c, 0x29, 0xff, 0x8d, 0xdb, 0xb0, 0xaa, + 0x01, 0x08, 0x8b, 0x37, 0xfd, 0x92, 0x31, 0xb8, 0x79, 0x25, 0x97, 0xfc, + 0xc3, 0x20, 0x9e, 0x8f, 0xf5, 0x94, 0x13, 0xf0, 0xf1, 0xc5, 0xe0, 0xf8, + 0x96, 0x5f, 0xfd, 0xd9, 0xfc, 0x9a, 0x4f, 0xad, 0x87, 0x0b, 0x29, 0x87, + 0xca, 0x43, 0x97, 0xf4, 0x7d, 0x04, 0xfd, 0x59, 0x71, 0x7e, 0xb2, 0xe8, + 0xe2, 0xca, 0x33, 0x5b, 0xbc, 0x5e, 0xee, 0x89, 0xe3, 0xfb, 0xde, 0xb3, + 0x7f, 0xb3, 0xee, 0xf5, 0xe5, 0xc5, 0x97, 0xa4, 0xdb, 0x92, 0xcb, 0xbe, + 0x92, 0xe3, 0x00, 0x5f, 0xfe, 0xc6, 0xa3, 0xb9, 0x34, 0x8f, 0xbc, 0x75, + 0x97, 0xd3, 0x6a, 0x3a, 0xb2, 0xbe, 0x45, 0x99, 0x10, 0xf0, 0x97, 0xa9, + 0x57, 0xff, 0xe8, 0xcf, 0x70, 0x9f, 0xa0, 0xe8, 0xfc, 0x62, 0xac, 0xbf, + 0xff, 0xff, 0xff, 0xdc, 0x06, 0xe9, 0x3e, 0x79, 0xc4, 0x2c, 0xdf, 0x84, + 0xe2, 0xcd, 0x20, 0x73, 0x8d, 0x38, 0xfc, 0x6d, 0x73, 0x1c, 0x6b, 0x2a, + 0x15, 0xc7, 0xe4, 0x21, 0xce, 0x13, 0x9a, 0x31, 0x78, 0x73, 0x91, 0xef, + 0x55, 0x6f, 0xff, 0xc4, 0x40, 0xfb, 0x3e, 0xe8, 0xf4, 0xff, 0x66, 0xcb, + 0x2f, 0xd9, 0xdd, 0x47, 0x16, 0x51, 0x9f, 0xf9, 0x2c, 0x5f, 0xa6, 0x94, + 0x6b, 0x65, 0x97, 0xd0, 0x2c, 0x31, 0x65, 0x48, 0xfb, 0x34, 0x40, 0xc2, + 0xab, 0xff, 0x4b, 0x93, 0x8b, 0x19, 0x1a, 0x15, 0x65, 0xfa, 0x74, 0xec, + 0xf3, 0xac, 0xbf, 0xa5, 0x1a, 0xfc, 0x46, 0x2c, 0xbf, 0xfb, 0x79, 0x67, + 0x3b, 0x85, 0x2c, 0xe2, 0xcb, 0xe6, 0x40, 0x3a, 0xb2, 0xb0, 0xf9, 0x9d, + 0x12, 0xfc, 0xfd, 0xeb, 0xe9, 0x65, 0xee, 0xb9, 0x2c, 0xb3, 0xe2, 0x3e, + 0x0a, 0x12, 0xdc, 0x20, 0xdc, 0x27, 0xa9, 0xc9, 0xb5, 0xfa, 0x32, 0x3b, + 0xfe, 0x8d, 0xd1, 0xfa, 0x36, 0x72, 0x59, 0x7f, 0xd0, 0x18, 0x61, 0xb3, + 0xf3, 0x59, 0x5a, 0x44, 0xff, 0x8b, 0x37, 0x0e, 0xef, 0xe1, 0x1a, 0xd1, + 0xe7, 0x92, 0x5f, 0xb9, 0xe8, 0xcd, 0x96, 0x5f, 0x48, 0xf5, 0xc5, 0x97, + 0xff, 0x7e, 0x47, 0x9f, 0x76, 0x69, 0x3e, 0x96, 0x5f, 0x4d, 0xe7, 0x99, + 0x65, 0xf0, 0x5f, 0x5b, 0x2c, 0xbf, 0x64, 0xd2, 0x8f, 0x96, 0x5c, 0x73, + 0x2c, 0xbc, 0x70, 0x4b, 0x2f, 0xd9, 0xa1, 0xfb, 0x16, 0x50, 0xcf, 0x07, + 0x83, 0x55, 0xe3, 0xfa, 0xe2, 0xc5, 0xe2, 0x89, 0x24, 0xbf, 0xf1, 0x7e, + 0x7b, 0x9b, 0x80, 0x94, 0x6e, 0xac, 0xac, 0x3e, 0x37, 0x1b, 0xa9, 0xca, + 0x87, 0xf0, 0xa3, 0xe2, 0x2d, 0x23, 0x78, 0x90, 0x88, 0xfb, 0x0a, 0x3d, + 0xc8, 0x42, 0xd4, 0x2e, 0xbe, 0x61, 0x79, 0xca, 0x4b, 0xf1, 0x9b, 0xca, + 0x4e, 0xbf, 0xbe, 0xe6, 0xb5, 0x1d, 0x59, 0x7f, 0x4b, 0x82, 0xe9, 0xc2, + 0xb2, 0xf1, 0x77, 0x8b, 0x2f, 0x11, 0xc9, 0x65, 0xfb, 0x5e, 0x77, 0x62, + 0xcb, 0xe0, 0x10, 0x02, 0xb2, 0x9c, 0xf2, 0x78, 0x4f, 0x7f, 0x76, 0x69, + 0x67, 0xd2, 0x59, 0x50, 0x98, 0x01, 0x97, 0xb8, 0xe0, 0x19, 0x04, 0x21, + 0xbf, 0xfb, 0xcd, 0x46, 0xb9, 0x8d, 0x3e, 0x85, 0x59, 0x7f, 0x9a, 0xcf, + 0xbb, 0xd8, 0x62, 0xcb, 0xff, 0x78, 0x27, 0x85, 0x9b, 0xdf, 0x8b, 0x29, + 0xcf, 0xcf, 0x86, 0xb7, 0xc3, 0x1e, 0x31, 0x65, 0xdf, 0xc2, 0x4b, 0x9a, + 0x69, 0x25, 0x19, 0xb0, 0x68, 0x5e, 0xfc, 0x30, 0x3e, 0xa4, 0x91, 0xb9, + 0xa1, 0xac, 0x45, 0x68, 0xae, 0x77, 0xff, 0xbb, 0xf0, 0x4f, 0xd3, 0xf1, + 0xdf, 0x09, 0xc5, 0x97, 0xfa, 0x24, 0x7d, 0x94, 0x75, 0x65, 0xcd, 0xbe, + 0x2c, 0xd1, 0xb2, 0xbf, 0xef, 0x3f, 0x78, 0x12, 0xcd, 0x96, 0x5c, 0x13, + 0x59, 0x7c, 0x47, 0xf4, 0x96, 0x5f, 0xf1, 0xfd, 0xec, 0x1e, 0x9c, 0x2b, + 0x2a, 0x0f, 0x6f, 0x44, 0x57, 0xfb, 0x98, 0x7d, 0x14, 0xfc, 0xb2, 0xff, + 0xbd, 0x1a, 0x80, 0x99, 0x05, 0x65, 0xe1, 0x5f, 0x81, 0x47, 0xe6, 0x9c, + 0x7a, 0x43, 0xb8, 0x67, 0x58, 0x9c, 0x3f, 0x63, 0x35, 0xa9, 0x2e, 0x04, + 0x7d, 0x0b, 0xad, 0x43, 0x39, 0x84, 0x60, 0x84, 0x13, 0x51, 0xd9, 0x5f, + 0xcd, 0x0a, 0xfd, 0x28, 0x59, 0x79, 0x80, 0xd2, 0xcb, 0xc1, 0x3e, 0x2c, + 0xbe, 0x03, 0x30, 0x6b, 0x2b, 0xe4, 0x44, 0x99, 0x77, 0x87, 0x77, 0x07, + 0x2f, 0xff, 0xed, 0x48, 0x11, 0x9e, 0x09, 0xf3, 0x07, 0xb6, 0x34, 0xb2, + 0xff, 0xde, 0xcd, 0xc7, 0x21, 0x73, 0xee, 0xac, 0xbf, 0xbc, 0x63, 0xf0, + 0x05, 0x59, 0x7e, 0x28, 0x98, 0xfe, 0x59, 0x6c, 0x09, 0xeb, 0x99, 0x7d, + 0xee, 0xe0, 0x56, 0x53, 0x0f, 0x0b, 0x84, 0xd7, 0xa4, 0xcf, 0xd6, 0x54, + 0x26, 0xee, 0x2a, 0xe7, 0xf0, 0xdb, 0xe9, 0x15, 0xff, 0x0a, 0xf3, 0xa1, + 0x84, 0x62, 0xac, 0xbf, 0xf3, 0xf1, 0x90, 0x7a, 0xd1, 0xb4, 0xb2, 0xdc, + 0x14, 0xfe, 0x7c, 0x77, 0x7c, 0xf2, 0x7d, 0x2c, 0xbf, 0xec, 0xef, 0x4c, + 0x5e, 0xe0, 0x56, 0x5f, 0xd1, 0xf7, 0x33, 0xee, 0xac, 0xbb, 0x50, 0xb2, + 0x86, 0x78, 0xbd, 0x2f, 0xbf, 0xdd, 0x30, 0x98, 0xe3, 0xab, 0x2f, 0xdc, + 0x80, 0xe3, 0x16, 0x54, 0x1e, 0xcf, 0x0c, 0xad, 0xc1, 0xa6, 0x35, 0x8f, + 0xda, 0x7b, 0xaf, 0x93, 0x9a, 0x61, 0x4f, 0x63, 0x3b, 0xbf, 0xee, 0x9e, + 0xd1, 0xf7, 0xfa, 0x85, 0x97, 0xb9, 0xdc, 0x59, 0x76, 0x72, 0x71, 0xeb, + 0x88, 0x77, 0x7f, 0xbc, 0x13, 0xe0, 0x24, 0x6b, 0x2f, 0xfe, 0xce, 0x9f, + 0xd2, 0x28, 0x9e, 0xe7, 0x8c, 0xfa, 0xcb, 0xff, 0xc0, 0x97, 0x9c, 0xfa, + 0x65, 0xd0, 0x79, 0x65, 0xfe, 0x17, 0xbf, 0x04, 0xf5, 0xc5, 0x97, 0xec, + 0xef, 0xef, 0xd5, 0x95, 0x09, 0x96, 0x48, 0xcf, 0x4a, 0x40, 0x49, 0x9f, + 0x36, 0xbf, 0xff, 0x47, 0x9f, 0x0a, 0x34, 0x28, 0xc0, 0xfa, 0x92, 0xca, + 0x92, 0x7f, 0xbd, 0x8d, 0x73, 0x71, 0x36, 0xa1, 0x7f, 0x3b, 0x21, 0xf4, + 0x71, 0xdb, 0xbc, 0xa5, 0x70, 0x4a, 0x8c, 0xbe, 0xf1, 0xbc, 0x96, 0x5f, + 0xec, 0x29, 0x67, 0x04, 0x62, 0xcb, 0xf3, 0x39, 0xfb, 0xba, 0xca, 0xe9, + 0xed, 0x88, 0x67, 0x7f, 0xb0, 0xa7, 0x7b, 0x3e, 0x69, 0x65, 0xfd, 0x1e, + 0x3d, 0xf9, 0x32, 0xca, 0xd1, 0xf2, 0xfe, 0x6d, 0x7f, 0xd2, 0x20, 0x7f, + 0x38, 0x2c, 0xfd, 0x65, 0x6c, 0x9b, 0x2c, 0x9f, 0x32, 0x11, 0x1f, 0x11, + 0xdf, 0xf6, 0x7d, 0x2f, 0x6e, 0x6e, 0x46, 0xea, 0xcb, 0xff, 0x9f, 0xdc, + 0x39, 0x1f, 0x63, 0xbc, 0x59, 0x7f, 0xb3, 0xbe, 0x39, 0xf7, 0xd9, 0x65, + 0xff, 0x14, 0x67, 0xdd, 0xcd, 0x42, 0xcb, 0xff, 0x47, 0x84, 0xe1, 0x67, + 0x7d, 0x8b, 0x2f, 0xdb, 0xfd, 0x9f, 0xba, 0xcb, 0xef, 0x47, 0xf8, 0xb2, + 0xb0, 0xf3, 0x00, 0x55, 0x50, 0x9f, 0x16, 0x22, 0x1a, 0x1f, 0x90, 0xdc, + 0xdb, 0xf3, 0x72, 0x84, 0x55, 0xf9, 0xf7, 0x8c, 0x70, 0xb2, 0xee, 0x71, + 0x65, 0xe6, 0x9a, 0x69, 0x65, 0xdf, 0xba, 0x46, 0xe6, 0x82, 0xb4, 0x7c, + 0x1c, 0x37, 0xbf, 0xf6, 0x70, 0x9c, 0x59, 0x88, 0xe6, 0x59, 0x7f, 0x7f, + 0x12, 0xf0, 0x9c, 0x59, 0x7f, 0xc7, 0xf8, 0x99, 0xbc, 0x81, 0xc5, 0x97, + 0xf1, 0x97, 0xe1, 0x09, 0xac, 0xbc, 0xff, 0xe2, 0x4a, 0x92, 0x73, 0xec, + 0x84, 0x4f, 0x88, 0xbf, 0x3f, 0x23, 0x0d, 0xe7, 0x93, 0xe5, 0xb7, 0xd0, + 0x33, 0x1a, 0xcb, 0xfb, 0x9e, 0x30, 0xbe, 0x96, 0x5f, 0x87, 0xa3, 0x06, + 0xcb, 0x2f, 0xd9, 0xde, 0x3f, 0xcb, 0x2f, 0xf3, 0x58, 0x59, 0xbc, 0xf4, + 0xb2, 0xfe, 0x2c, 0xfb, 0xf1, 0x24, 0xb2, 0xff, 0xfb, 0x9d, 0xcf, 0x08, + 0xcc, 0x0c, 0x7a, 0x18, 0xb2, 0xb1, 0x33, 0x29, 0x8b, 0x74, 0x52, 0x45, + 0x1d, 0x34, 0x01, 0x85, 0xfb, 0x33, 0xbe, 0xc5, 0x97, 0x3b, 0x16, 0x5f, + 0xf0, 0x39, 0xd8, 0x98, 0x1f, 0x71, 0x65, 0xf1, 0xeb, 0x50, 0xb2, 0xff, + 0x66, 0xfc, 0x6e, 0xd3, 0x4d, 0x24, 0xbf, 0x4d, 0x19, 0xee, 0x2c, 0xa9, + 0xc8, 0xbf, 0xf1, 0xe1, 0x10, 0x88, 0x75, 0x7f, 0x9b, 0x70, 0x00, 0xf8, + 0xf4, 0xb2, 0xf3, 0x1f, 0xab, 0x2a, 0x64, 0xd9, 0xfd, 0x0d, 0x50, 0x1f, + 0xef, 0x37, 0xa5, 0x97, 0xdf, 0x04, 0xf4, 0xb2, 0xdf, 0xb6, 0x0d, 0x77, + 0x42, 0xef, 0xdb, 0xdd, 0x86, 0xc5, 0x95, 0x07, 0xaa, 0x45, 0x97, 0xec, + 0x9a, 0x51, 0x25, 0x97, 0xf6, 0xc3, 0x8c, 0x21, 0xac, 0xa8, 0x5c, 0xe4, + 0x93, 0x79, 0xc6, 0xa7, 0xe5, 0x77, 0x8e, 0x37, 0x90, 0xc9, 0xe9, 0x00, + 0x85, 0x17, 0x9f, 0xf3, 0x59, 0x7f, 0xc3, 0x20, 0x73, 0x36, 0xcd, 0x96, + 0x5e, 0x8d, 0x12, 0xcb, 0xfd, 0x83, 0x27, 0xe1, 0x8d, 0x65, 0xbc, 0xb2, + 0xf0, 0x1f, 0x62, 0x3c, 0x2e, 0x18, 0xd6, 0x91, 0x21, 0xd5, 0xdb, 0xe6, + 0x73, 0x18, 0xb2, 0x8c, 0xf1, 0x5c, 0x8e, 0xfe, 0x8d, 0x77, 0xb9, 0xba, + 0xb2, 0x98, 0x9b, 0xdb, 0x8e, 0x36, 0xe3, 0x0f, 0x22, 0x0b, 0xf8, 0x41, + 0x94, 0x66, 0xcb, 0x2c, 0x4b, 0x2f, 0xa3, 0x68, 0x92, 0xca, 0x19, 0xf0, + 0x39, 0x71, 0x08, 0x5f, 0xde, 0x3e, 0x94, 0x31, 0x65, 0xff, 0xec, 0x1b, + 0xb3, 0x3e, 0x96, 0x7f, 0x83, 0x59, 0x77, 0x9d, 0x65, 0xd3, 0x71, 0x65, + 0xfb, 0x37, 0x99, 0x0f, 0x0d, 0x70, 0x82, 0xd7, 0xf4, 0xee, 0xe7, 0x39, + 0x0b, 0x2f, 0xb3, 0xee, 0xc2, 0xcb, 0xff, 0x9f, 0x58, 0x29, 0xeb, 0x37, + 0xbe, 0x96, 0x57, 0x8f, 0x9f, 0x79, 0x15, 0xfb, 0x71, 0xf6, 0x12, 0x4b, + 0x2a, 0x72, 0x73, 0xad, 0xde, 0x20, 0xfb, 0x21, 0x2c, 0x02, 0x4b, 0xe1, + 0xe7, 0x9d, 0x65, 0xff, 0x14, 0xbd, 0x84, 0x50, 0xd2, 0xcb, 0xff, 0xcf, + 0xb4, 0x6b, 0x4f, 0x29, 0xdf, 0x8f, 0x16, 0x56, 0x22, 0x10, 0x8d, 0xef, + 0xfd, 0x9a, 0xd3, 0xca, 0x76, 0xf8, 0xdd, 0x59, 0x50, 0x7c, 0xd2, 0x21, + 0xad, 0x26, 0xfc, 0x4a, 0x5d, 0x8c, 0x2e, 0xff, 0x6b, 0x9e, 0xc2, 0x3e, + 0x2c, 0xa8, 0x57, 0xb9, 0x85, 0xa7, 0x2d, 0x1d, 0xcd, 0xaf, 0xfb, 0x07, + 0x85, 0x04, 0x08, 0x59, 0x66, 0x96, 0x5f, 0xff, 0x17, 0xf9, 0xf7, 0x63, + 0xe9, 0x77, 0xf8, 0x92, 0xca, 0x83, 0xe5, 0x31, 0x2a, 0x87, 0xdc, 0x2e, + 0xda, 0x3b, 0x29, 0x4f, 0x9e, 0x8e, 0x7c, 0x8f, 0x23, 0xd9, 0x16, 0x39, + 0x20, 0xce, 0xc4, 0xfd, 0x0f, 0xb3, 0x8d, 0xba, 0x68, 0xdd, 0x75, 0x59, + 0xe4, 0x32, 0x73, 0xe3, 0xd3, 0xda, 0xee, 0xaf, 0xfc, 0x65, 0xe5, 0x4c, + 0x1f, 0xe4, 0xba, 0x9e, 0xcf, 0x74, 0x82, 0x3f, 0xed, 0xf3, 0x90, 0x42, + 0x21, 0x6e, 0x42, 0x6a, 0xff, 0xb3, 0x45, 0x1d, 0x96, 0x79, 0x65, 0xfd, + 0x22, 0xc0, 0x9f, 0x56, 0x5f, 0xfc, 0x41, 0x15, 0xf9, 0x92, 0x82, 0x0a, + 0xcb, 0xf7, 0xf8, 0x37, 0x92, 0xca, 0xf9, 0x12, 0xe6, 0x58, 0x48, 0x77, + 0xfd, 0xde, 0x78, 0xe1, 0x8e, 0x35, 0x97, 0xc3, 0xd3, 0xc9, 0x65, 0xf1, + 0x7a, 0x05, 0x59, 0x7f, 0xef, 0x19, 0x47, 0xdc, 0x36, 0x42, 0xcb, 0xdc, + 0x8d, 0x2c, 0xbb, 0xbc, 0x09, 0xec, 0xfc, 0x7b, 0x50, 0x98, 0x20, 0xa7, + 0x06, 0x44, 0x07, 0xbb, 0xfc, 0x12, 0xc9, 0x49, 0xf8, 0xb2, 0xfe, 0x76, + 0xb3, 0xf1, 0x3a, 0xb2, 0xe1, 0x9a, 0xcb, 0xef, 0xb9, 0x1f, 0x2c, 0xb9, + 0xe5, 0x38, 0xdd, 0x18, 0xb5, 0xe1, 0x7c, 0x6b, 0x2f, 0xfe, 0x9b, 0x51, + 0xbf, 0xd0, 0x7a, 0x39, 0x96, 0x54, 0x8f, 0x93, 0xe1, 0xdb, 0xfc, 0xe3, + 0xf6, 0x77, 0x8e, 0xb2, 0xfe, 0xe6, 0x0c, 0xf9, 0xb2, 0xcb, 0xd2, 0x8f, + 0xd6, 0x5f, 0x84, 0x9a, 0x51, 0xba, 0x92, 0xa1, 0x70, 0xe3, 0x21, 0x9c, + 0x2c, 0x66, 0x66, 0x7b, 0xa3, 0x2f, 0x35, 0x3c, 0x24, 0x48, 0x8f, 0x86, + 0x5d, 0x2e, 0x10, 0x76, 0xc1, 0x59, 0x7f, 0xf0, 0xa4, 0x07, 0x13, 0x38, + 0x70, 0x4b, 0x2f, 0x4f, 0x98, 0xd6, 0x56, 0x8f, 0x89, 0x88, 0x77, 0xa1, + 0xf4, 0xb2, 0xfe, 0x67, 0x7d, 0x1b, 0xe1, 0x65, 0xf6, 0x10, 0x7f, 0x59, + 0x7f, 0xee, 0x81, 0x9e, 0x83, 0x27, 0x1a, 0xcb, 0xe9, 0x49, 0xfa, 0xb2, + 0xbe, 0x3d, 0xf7, 0x3d, 0xbe, 0x17, 0x4e, 0xd2, 0xcb, 0x83, 0xbd, 0x65, + 0x84, 0x54, 0x40, 0xab, 0x75, 0x54, 0x0a, 0x2b, 0x47, 0xad, 0xd1, 0x96, + 0x87, 0xeb, 0x11, 0x54, 0x6f, 0x57, 0xf0, 0x4f, 0x6e, 0xb8, 0xd6, 0x5f, + 0xc6, 0xdb, 0x0f, 0xa1, 0xa5, 0x97, 0xfb, 0xc6, 0x23, 0x7f, 0xff, 0x84, + 0x95, 0xa3, 0xea, 0x9f, 0x32, 0xbf, 0x40, 0x7d, 0x1a, 0x59, 0x7c, 0x5b, + 0x8d, 0xbe, 0x2c, 0xad, 0x93, 0x0d, 0x38, 0x4c, 0x74, 0x94, 0x42, 0x7b, + 0xfb, 0xd8, 0x06, 0xde, 0x71, 0x65, 0xff, 0x17, 0x78, 0x0f, 0xba, 0x6d, + 0x2c, 0xbf, 0xff, 0x63, 0x50, 0xc0, 0x38, 0xfb, 0xf0, 0x4c, 0x9a, 0x59, + 0x7f, 0xf6, 0x7d, 0xd0, 0xf8, 0xc7, 0xec, 0xd9, 0x65, 0xfc, 0xfc, 0xcc, + 0x21, 0x56, 0x56, 0x1f, 0x90, 0x11, 0xaf, 0x3b, 0xb4, 0xb2, 0xa1, 0x34, + 0x11, 0x9d, 0xfa, 0x19, 0x24, 0x43, 0x7f, 0xd1, 0xde, 0x4e, 0xc6, 0x18, + 0xd6, 0x5f, 0xe8, 0x79, 0x70, 0x48, 0xdd, 0x59, 0x7f, 0xe9, 0xc4, 0xe2, + 0xce, 0xe7, 0xff, 0xc2, 0xcb, 0xff, 0xdb, 0x03, 0xfe, 0x48, 0xf9, 0xc8, + 0xd4, 0x96, 0x58, 0xc5, 0x44, 0x96, 0x91, 0x6f, 0xf3, 0xfb, 0xee, 0x41, + 0x75, 0x65, 0x49, 0x32, 0x4e, 0x43, 0x23, 0xa5, 0x37, 0xff, 0xf8, 0x7e, + 0x80, 0xf8, 0xe6, 0x77, 0x90, 0xae, 0x23, 0xee, 0xac, 0xbe, 0x2c, 0xdf, + 0x8b, 0x2b, 0x64, 0x43, 0xb4, 0xcb, 0x7f, 0xbc, 0x73, 0x4a, 0x0a, 0x4b, + 0x2f, 0xff, 0xe8, 0xef, 0x04, 0x2e, 0xf8, 0x0f, 0x2e, 0xf3, 0x1a, 0x59, + 0x7f, 0xe8, 0x6b, 0x8d, 0x97, 0x0e, 0x7d, 0xd5, 0x95, 0x08, 0xf9, 0x09, + 0x2e, 0x8c, 0xfa, 0xbb, 0x7d, 0x2f, 0x66, 0xcb, 0x2f, 0xfa, 0x3e, 0x63, + 0xf7, 0x3e, 0xea, 0xcb, 0x9f, 0x65, 0x95, 0xf1, 0xfa, 0x11, 0x1b, 0x47, + 0x57, 0xf9, 0x8f, 0xc2, 0x8c, 0x15, 0x65, 0xff, 0x00, 0x1e, 0x3e, 0xf9, + 0xe4, 0xb2, 0xfc, 0x0f, 0x6a, 0x1a, 0x59, 0x74, 0xfb, 0xac, 0xba, 0x03, + 0x88, 0xad, 0x61, 0x97, 0x4e, 0x44, 0x29, 0xbb, 0x4e, 0xb2, 0xff, 0xed, + 0xe7, 0xce, 0x61, 0x05, 0xcb, 0xf5, 0x97, 0xfb, 0xc6, 0x31, 0xe3, 0x53, + 0x2c, 0xbe, 0x14, 0xfe, 0xe2, 0xcb, 0x7f, 0x88, 0x9e, 0x0a, 0x2f, 0x4d, + 0x6f, 0x84, 0xf3, 0xf5, 0x65, 0xa3, 0x47, 0xb2, 0xc3, 0x6a, 0xd2, 0x66, + 0x9e, 0x8c, 0x36, 0xff, 0xcf, 0xf7, 0x33, 0xa6, 0xc7, 0x92, 0xcb, 0x75, + 0x65, 0x7e, 0x79, 0xe2, 0x1f, 0x5f, 0xfd, 0x9e, 0x30, 0xfb, 0x3b, 0x82, + 0xc9, 0x65, 0xff, 0xff, 0x9f, 0x42, 0x8f, 0xd1, 0xcf, 0x67, 0xd2, 0xe9, + 0xcd, 0x1c, 0x02, 0xca, 0xc4, 0x59, 0xba, 0x25, 0xf1, 0x4e, 0x0c, 0xcb, + 0x2f, 0xff, 0xf4, 0xc5, 0x0c, 0xd4, 0x07, 0x35, 0x9e, 0x3d, 0x79, 0xd6, + 0x5f, 0xff, 0x3e, 0xbf, 0x9f, 0xcd, 0x44, 0x8f, 0xee, 0xe2, 0xcb, 0xff, + 0xff, 0x81, 0xcf, 0x3c, 0xb9, 0x23, 0xe8, 0x9a, 0x8f, 0x47, 0x78, 0xff, + 0x2c, 0xbf, 0x46, 0x17, 0x67, 0xd6, 0x5f, 0xc7, 0xf7, 0xfa, 0x71, 0xac, + 0xb3, 0x16, 0x53, 0x0f, 0xa7, 0xc5, 0x24, 0x5f, 0x76, 0xb7, 0x56, 0x5e, + 0x90, 0x06, 0xb2, 0xe7, 0xfa, 0x71, 0xf2, 0xcc, 0x5c, 0xe3, 0x55, 0x0a, + 0x95, 0xb0, 0x94, 0xd7, 0xf4, 0xa7, 0xc8, 0xd0, 0xaf, 0xe3, 0xd4, 0xf7, + 0xce, 0x9a, 0xcb, 0xff, 0xfb, 0x0b, 0x6e, 0x72, 0x3f, 0xcf, 0x1e, 0x9d, + 0xae, 0xac, 0xbf, 0x87, 0x84, 0x2e, 0xa1, 0x65, 0x49, 0x99, 0x42, 0x38, + 0xdc, 0x82, 0x83, 0xf4, 0x6b, 0xd3, 0x47, 0x6d, 0xa8, 0x58, 0x32, 0x1b, + 0xbe, 0x8d, 0xf3, 0xf7, 0x92, 0x86, 0xbf, 0x65, 0x3b, 0xef, 0x4f, 0x68, + 0xca, 0x7d, 0x72, 0xe6, 0xde, 0xe2, 0xcb, 0xc6, 0xcc, 0x59, 0x5f, 0x1b, + 0x9d, 0x0f, 0x5f, 0xff, 0xe2, 0xc1, 0xfa, 0x1a, 0x2c, 0x3d, 0xa7, 0xc1, + 0xe3, 0xd2, 0xca, 0x86, 0x74, 0xe1, 0xd3, 0x39, 0x5e, 0x33, 0x20, 0x11, + 0x5f, 0x40, 0x4c, 0x2b, 0x2f, 0x87, 0xa3, 0x69, 0x65, 0xa1, 0x65, 0xff, + 0xf4, 0xce, 0xf2, 0xe4, 0x30, 0x7e, 0x82, 0x0a, 0xca, 0x83, 0xdf, 0x31, + 0x0b, 0xfe, 0xc9, 0xa5, 0x1f, 0x67, 0xdd, 0x59, 0x6d, 0xb0, 0xf6, 0xc8, + 0x82, 0xa4, 0x99, 0x28, 0xc8, 0x75, 0x0d, 0xdb, 0xe1, 0xf8, 0x1b, 0x2c, + 0xbf, 0x9e, 0x59, 0xb0, 0x09, 0x65, 0xd8, 0x35, 0x94, 0x33, 0xc3, 0x09, + 0x6d, 0xff, 0xb5, 0x9b, 0xb3, 0x79, 0xd8, 0x7c, 0x59, 0x6d, 0xa1, 0x19, + 0x78, 0xcf, 0xa2, 0x2b, 0x9b, 0x78, 0xb2, 0xb0, 0xf3, 0xdc, 0xd6, 0xff, + 0xdb, 0xcf, 0xb2, 0x89, 0xdd, 0x80, 0x2c, 0xb4, 0xcb, 0x2f, 0xd3, 0xe0, + 0xf6, 0x05, 0x65, 0x61, 0xff, 0xcc, 0x85, 0xd1, 0x2b, 0xfa, 0x3a, 0x7f, + 0xc4, 0x96, 0x5d, 0x27, 0x59, 0x5a, 0x3c, 0x37, 0x2d, 0xbf, 0xbe, 0xd9, + 0xe5, 0x1b, 0xab, 0x2e, 0x29, 0x2c, 0xa8, 0x3c, 0x8d, 0x8c, 0xa9, 0xb2, + 0xdb, 0x2a, 0x43, 0x9c, 0x88, 0x85, 0x1b, 0x09, 0x79, 0xc2, 0x05, 0x90, + 0xcc, 0xf5, 0x3f, 0x29, 0xe3, 0x72, 0x28, 0xd4, 0x39, 0x09, 0xfe, 0xb9, + 0x01, 0xa6, 0xff, 0xcd, 0x91, 0x21, 0x81, 0xf3, 0xfd, 0xfa, 0xcb, 0xfd, + 0x29, 0xb0, 0x9f, 0xd8, 0xb2, 0xfb, 0xe9, 0x43, 0x16, 0x5f, 0xf4, 0x16, + 0xdc, 0xc6, 0x46, 0xea, 0xcb, 0xa3, 0xcb, 0x2f, 0xe7, 0x98, 0x70, 0x5b, + 0x2c, 0xad, 0x91, 0x44, 0x32, 0x32, 0x3b, 0xde, 0x2d, 0x7d, 0x13, 0x1f, + 0x96, 0x5e, 0xdc, 0x86, 0x2c, 0xa7, 0x3c, 0x1d, 0xc2, 0x2b, 0xf1, 0x0f, + 0xd1, 0x25, 0x97, 0xef, 0x44, 0xb3, 0x8b, 0x2f, 0xb7, 0x20, 0x82, 0xb2, + 0xfe, 0xf6, 0x0a, 0xf1, 0xb2, 0xcb, 0xe8, 0x9b, 0x50, 0xb2, 0xe6, 0x62, + 0xca, 0x61, 0xb8, 0xf1, 0x15, 0xf4, 0x01, 0xb6, 0x6b, 0x2f, 0xb8, 0x50, + 0xd2, 0xcb, 0xf0, 0xfd, 0x85, 0x25, 0x95, 0x87, 0x92, 0xe4, 0x57, 0xe7, + 0xe7, 0x4e, 0x65, 0x97, 0xf8, 0xf5, 0x1b, 0x3e, 0x82, 0xb2, 0xdd, 0x9c, + 0xa8, 0x1a, 0x09, 0x06, 0x4f, 0x84, 0xe1, 0x24, 0x36, 0x6d, 0x10, 0xf9, + 0xbb, 0x84, 0x1d, 0x28, 0xbf, 0xfd, 0xa0, 0x96, 0x6f, 0x2c, 0x8d, 0x47, + 0x16, 0x5f, 0xe7, 0x9b, 0xd8, 0xcc, 0xe2, 0xca, 0xc3, 0xfc, 0xde, 0x93, + 0x7a, 0x7e, 0x3a, 0xb2, 0xfa, 0x37, 0x9e, 0xf5, 0x97, 0x63, 0x16, 0x58, + 0x59, 0xc6, 0xec, 0xc9, 0x6b, 0x64, 0x41, 0x92, 0xd5, 0xf8, 0x5e, 0xe6, + 0x31, 0x65, 0xff, 0xa5, 0x23, 0x0c, 0x32, 0x6e, 0x42, 0xca, 0xc4, 0x45, + 0x19, 0x1b, 0x94, 0x5f, 0xfe, 0x98, 0xb0, 0x51, 0x5f, 0xbc, 0x32, 0x99, + 0x65, 0xf7, 0xb3, 0x62, 0x59, 0x7e, 0xc1, 0x9e, 0xb8, 0xb2, 0xfc, 0x24, + 0x8f, 0xfe, 0x2c, 0xbf, 0x68, 0x31, 0x9b, 0x2c, 0xbd, 0xde, 0xfc, 0xb2, + 0xff, 0xef, 0x82, 0x7e, 0x9d, 0xb9, 0x9e, 0x36, 0x2c, 0xaf, 0xcf, 0xa3, + 0xa3, 0xd7, 0x03, 0x4b, 0x2b, 0x11, 0x95, 0xc8, 0x48, 0x4f, 0x91, 0xda, + 0x36, 0x4d, 0x49, 0x84, 0xed, 0x43, 0xd2, 0xa1, 0x7b, 0xbb, 0x25, 0x43, + 0x1c, 0x61, 0xde, 0x8c, 0xc1, 0xcb, 0x78, 0x99, 0xd8, 0xdd, 0x2f, 0xfc, + 0x46, 0x27, 0xd9, 0xbf, 0xe8, 0x11, 0x65, 0xa4, 0xb2, 0xff, 0xf1, 0xe6, + 0x87, 0x0d, 0x67, 0xa3, 0xee, 0x2c, 0xbe, 0x12, 0x35, 0xfa, 0xcb, 0xff, + 0x47, 0xde, 0xf4, 0x6d, 0x00, 0x62, 0xcb, 0xff, 0xb5, 0xa7, 0xff, 0x3c, + 0x7a, 0xf3, 0xac, 0xb4, 0x96, 0x57, 0x0f, 0x54, 0x44, 0x4b, 0x9f, 0x4b, + 0x2f, 0xff, 0xc2, 0xe7, 0x4f, 0x9e, 0x06, 0x77, 0xf7, 0x1c, 0x2c, 0xbf, + 0xba, 0x7b, 0x32, 0x02, 0xb2, 0x86, 0x89, 0xad, 0x0b, 0x79, 0x5a, 0xfa, + 0x47, 0xa9, 0x2c, 0xbf, 0xbf, 0x9a, 0x47, 0xa9, 0x2c, 0x98, 0xd1, 0x5f, + 0xef, 0x67, 0xb0, 0x2e, 0x15, 0x97, 0x1e, 0xcb, 0x28, 0x27, 0x93, 0xd3, + 0x2a, 0xf9, 0x15, 0x9a, 0x84, 0x55, 0xfe, 0x0c, 0xc6, 0xc0, 0x3f, 0x16, + 0x5f, 0x60, 0x35, 0xb2, 0xca, 0xf8, 0xf5, 0xd8, 0x69, 0x7a, 0x06, 0x35, + 0x97, 0x66, 0xe2, 0xcb, 0xef, 0xc4, 0x29, 0x2c, 0xb3, 0x91, 0xbd, 0xe8, + 0xcd, 0xfb, 0x9c, 0xc2, 0xfd, 0x65, 0x7e, 0x79, 0xa4, 0x4b, 0x50, 0xa9, + 0xa4, 0xe1, 0xfc, 0xf0, 0x80, 0xe9, 0x18, 0x21, 0x4d, 0x77, 0xd3, 0x2c, + 0xbf, 0x09, 0xdf, 0xd9, 0x8b, 0x29, 0x87, 0x87, 0xf8, 0xcd, 0xd1, 0xc5, + 0x97, 0xd1, 0xe8, 0x1a, 0xca, 0x09, 0xb7, 0x98, 0x5a, 0xff, 0xa4, 0xfa, + 0xe9, 0x43, 0x38, 0xb2, 0xff, 0x40, 0xe3, 0x40, 0x8f, 0xd6, 0x5e, 0x2c, + 0xf9, 0x65, 0xcc, 0xe4, 0xc8, 0x87, 0x61, 0xc1, 0x19, 0xdf, 0xff, 0x46, + 0x7d, 0xdf, 0x61, 0xc8, 0xc5, 0x17, 0x16, 0x5f, 0x8a, 0x37, 0x0a, 0x16, + 0x5f, 0xff, 0xce, 0xd0, 0xdd, 0xf5, 0xb7, 0x9f, 0xbe, 0x3d, 0x49, 0x65, + 0xff, 0x39, 0x67, 0x7a, 0x6d, 0x75, 0x65, 0xff, 0xff, 0xd3, 0x48, 0xcb, + 0xb1, 0xfe, 0xe7, 0x4c, 0x5d, 0x64, 0x7d, 0x23, 0x62, 0xca, 0x84, 0x57, + 0x91, 0xc5, 0x42, 0xae, 0x3c, 0x5a, 0x38, 0x54, 0xe8, 0xf1, 0xd4, 0x08, + 0xa0, 0x11, 0x85, 0xd4, 0xf6, 0xd8, 0x75, 0xc2, 0x34, 0xa7, 0xf0, 0xb1, + 0xf0, 0x54, 0x30, 0x88, 0xcc, 0x95, 0xe2, 0x4f, 0xe1, 0x2b, 0xc9, 0x64, + 0xdd, 0x96, 0xfd, 0x7f, 0xf7, 0x7e, 0x96, 0x74, 0xf7, 0xc1, 0x05, 0x65, + 0xfd, 0x00, 0xf8, 0xfe, 0xdc, 0x59, 0x7e, 0xc6, 0xb3, 0xee, 0xac, 0xa9, + 0xc7, 0xb6, 0x66, 0x57, 0xfb, 0xe9, 0x60, 0xfd, 0x9b, 0x2c, 0xaf, 0x91, + 0xf3, 0xa8, 0x50, 0x91, 0x1d, 0xfa, 0x35, 0xf4, 0xb8, 0xb2, 0xff, 0xff, + 0xb5, 0x33, 0xf8, 0xf9, 0x13, 0x1f, 0x7b, 0x9e, 0xfc, 0xf7, 0xac, 0xad, + 0x91, 0x26, 0x02, 0x9b, 0xda, 0xce, 0x2c, 0xbf, 0x19, 0x6f, 0xc9, 0x2c, + 0xbd, 0x87, 0xb2, 0xca, 0x14, 0xf8, 0x18, 0x39, 0xe2, 0x8b, 0xfa, 0x36, + 0xce, 0xe3, 0x16, 0x5f, 0xfd, 0x84, 0x00, 0xfb, 0x19, 0x33, 0x85, 0x65, + 0x1a, 0x28, 0x74, 0x61, 0xc2, 0xdb, 0xe6, 0x1c, 0x0d, 0x65, 0xfe, 0x21, + 0x7d, 0x0c, 0x61, 0x2c, 0xbf, 0xb7, 0x4e, 0x37, 0x01, 0xd5, 0x97, 0xe8, + 0x9b, 0x30, 0x2b, 0x2a, 0x11, 0x5d, 0x84, 0x26, 0x67, 0xe3, 0x3b, 0xe2, + 0x63, 0xf5, 0x65, 0xe2, 0x39, 0x2c, 0xbf, 0xf7, 0xb2, 0x63, 0x98, 0x1b, + 0x80, 0xe2, 0xcb, 0xc7, 0xa1, 0x56, 0x5f, 0x74, 0xe3, 0xe5, 0x94, 0x68, + 0x83, 0x24, 0x4e, 0x0e, 0xdf, 0xf4, 0x4d, 0x1d, 0xf6, 0x68, 0x2b, 0x2f, + 0x7a, 0x26, 0x59, 0x68, 0xf8, 0xf5, 0xb7, 0x0e, 0x6a, 0x48, 0xaf, 0xe4, + 0x21, 0x2f, 0x11, 0x9a, 0xcb, 0xdb, 0x3e, 0xcb, 0x2f, 0xfd, 0x31, 0xfa, + 0x3e, 0x94, 0x4d, 0x0b, 0x28, 0xcf, 0x78, 0x03, 0xd7, 0xcc, 0x8e, 0xb1, + 0x65, 0xf8, 0x6e, 0x51, 0xe5, 0x97, 0xf4, 0xa3, 0xee, 0xe7, 0x96, 0x5f, + 0xfe, 0xef, 0x0f, 0xf8, 0x97, 0x33, 0x3e, 0xea, 0xca, 0x98, 0xfe, 0x78, + 0x5d, 0x5e, 0x46, 0x13, 0x50, 0x9f, 0xbf, 0xde, 0x79, 0xb0, 0xf7, 0x06, + 0xb2, 0xa1, 0x34, 0x33, 0x87, 0x27, 0x0a, 0x6f, 0xcc, 0xdc, 0x20, 0x6c, + 0xb2, 0xff, 0x70, 0xfc, 0x73, 0x1e, 0xcb, 0x2f, 0xff, 0x81, 0xad, 0x41, + 0x60, 0xa7, 0xef, 0x60, 0x8b, 0x2a, 0x11, 0x61, 0x85, 0x9a, 0x34, 0xbd, + 0x36, 0xa1, 0x65, 0xf9, 0xcc, 0x70, 0x4b, 0x2e, 0x80, 0x2c, 0xa8, 0x3d, + 0x82, 0x1d, 0xe1, 0x2d, 0xf6, 0xd1, 0xb9, 0x0b, 0x2f, 0xbd, 0xfb, 0xf5, + 0x65, 0xbc, 0xb2, 0xdb, 0xdc, 0xda, 0x00, 0x8e, 0xff, 0xe3, 0x61, 0xf3, + 0xd0, 0x7a, 0x06, 0xea, 0xca, 0x84, 0x63, 0x9a, 0xcb, 0x94, 0x5f, 0xc5, + 0xc3, 0xdf, 0x03, 0x59, 0x7f, 0xdf, 0x99, 0x76, 0x38, 0x0f, 0x2c, 0xbc, + 0x46, 0x2a, 0xcb, 0xf3, 0xeb, 0x51, 0xb2, 0xcb, 0xfb, 0xa7, 0xa3, 0xe8, + 0x16, 0x54, 0xe4, 0x55, 0x30, 0xe7, 0xa3, 0x82, 0x14, 0x5e, 0xe3, 0xfe, + 0xb2, 0xa0, 0xf7, 0x7a, 0x81, 0x7f, 0x00, 0x4f, 0x67, 0xdc, 0x59, 0x7f, + 0xff, 0xfd, 0x1d, 0x27, 0x09, 0xf2, 0x63, 0xd0, 0x63, 0x9d, 0x8f, 0xa0, + 0x4c, 0xe2, 0xcb, 0xfd, 0xcc, 0xd4, 0x6f, 0x8d, 0xd5, 0x95, 0xa4, 0x5a, + 0x75, 0xf6, 0xff, 0x7b, 0x3e, 0x8d, 0x1b, 0x16, 0x5f, 0xff, 0xff, 0xf7, + 0x33, 0xbe, 0x76, 0x73, 0x5a, 0x3f, 0xba, 0xee, 0x2e, 0x64, 0xc0, 0xec, + 0x35, 0xf4, 0x2c, 0xbf, 0x02, 0x6c, 0x9f, 0xc5, 0x95, 0xc4, 0x61, 0x76, + 0x12, 0xf7, 0xfd, 0xf7, 0x08, 0xd9, 0xdc, 0xdd, 0x59, 0x76, 0x69, 0x65, + 0x41, 0xe9, 0x78, 0xf2, 0xfe, 0xe9, 0x3f, 0xf8, 0x4b, 0x2f, 0xc4, 0xff, + 0xe1, 0x2c, 0xa9, 0xc7, 0xa5, 0xd2, 0xca, 0x85, 0x45, 0x23, 0x23, 0x78, + 0x7a, 0xf5, 0xe8, 0x0e, 0x77, 0xfe, 0xe9, 0xf0, 0x9f, 0xb3, 0x38, 0xd6, + 0x5f, 0x7e, 0xff, 0x71, 0x65, 0xcd, 0x34, 0xb2, 0xb8, 0x6f, 0x5a, 0x24, + 0xbe, 0xf3, 0xe7, 0x92, 0x37, 0x34, 0x55, 0x88, 0xd0, 0x38, 0x4e, 0xdf, + 0x84, 0xc6, 0x1e, 0x96, 0x5c, 0xc7, 0x59, 0x7f, 0xbb, 0x04, 0x19, 0xe7, + 0xf6, 0x2c, 0xaf, 0x1e, 0x78, 0x05, 0xaf, 0x7a, 0x3f, 0x59, 0x7b, 0x73, + 0x72, 0x16, 0x5f, 0xf0, 0xfc, 0x7a, 0x3f, 0xa6, 0x85, 0x94, 0xe7, 0xb8, + 0x02, 0x2b, 0x9b, 0x1b, 0xd6, 0x56, 0x26, 0xa0, 0x6e, 0xee, 0x44, 0x4f, + 0x3d, 0x21, 0xbd, 0xe9, 0xdb, 0xab, 0x2f, 0x10, 0x4d, 0x65, 0xfb, 0xd0, + 0x59, 0xb2, 0xca, 0x83, 0xe4, 0x32, 0x27, 0x1b, 0xbf, 0xde, 0x8e, 0x81, + 0x80, 0xea, 0xca, 0x86, 0xc4, 0x13, 0x68, 0x64, 0x4a, 0x32, 0x51, 0xc3, + 0x77, 0x0e, 0x85, 0x21, 0xfa, 0x34, 0xd3, 0x28, 0xdd, 0x78, 0x9a, 0x36, + 0x4d, 0x43, 0x91, 0x90, 0x86, 0xf4, 0x3d, 0x1c, 0xb7, 0xf8, 0xcd, 0x5b, + 0x64, 0x25, 0x2c, 0x37, 0x91, 0x8f, 0xf6, 0x37, 0x6d, 0xf0, 0xab, 0xdc, + 0x2c, 0xbf, 0xc1, 0xf1, 0xf7, 0x8f, 0xf2, 0xcb, 0xd9, 0xad, 0xeb, 0x2f, + 0x8f, 0x42, 0x4c, 0xb2, 0xff, 0xc5, 0xfb, 0x1e, 0x45, 0x1d, 0xe2, 0xcb, + 0xb3, 0x16, 0x5f, 0x07, 0xc6, 0x22, 0xca, 0x15, 0x1d, 0x86, 0x69, 0xe1, + 0xe7, 0x24, 0x6d, 0x9f, 0x08, 0x2b, 0x7e, 0x9a, 0x26, 0xf4, 0x2c, 0xbf, + 0xfc, 0x5d, 0xe0, 0x9d, 0x32, 0x04, 0xb3, 0x8b, 0x2e, 0x12, 0x16, 0x5e, + 0x69, 0xa6, 0x92, 0x5f, 0x4b, 0xb8, 0x14, 0x8d, 0xcd, 0x05, 0xfb, 0x06, + 0x7f, 0xf1, 0x77, 0x7f, 0x2b, 0x47, 0xd2, 0x46, 0x97, 0xa3, 0x92, 0x59, + 0x50, 0x9a, 0x86, 0x14, 0xe9, 0x29, 0xe1, 0x41, 0xf9, 0x0d, 0xf3, 0x07, + 0x84, 0xb2, 0xee, 0xf1, 0x65, 0xff, 0xe1, 0xe6, 0x0b, 0x3b, 0x3a, 0x7c, + 0xf1, 0xac, 0xbf, 0xdd, 0xf4, 0x77, 0x9e, 0x35, 0x97, 0x3c, 0x96, 0x5f, + 0xa7, 0x64, 0xc7, 0xb2, 0xca, 0x9c, 0x7e, 0x8c, 0x33, 0xe0, 0xb5, 0xb8, + 0xb2, 0xff, 0x77, 0xe9, 0x7a, 0x0b, 0x64, 0x97, 0xfe, 0xcf, 0xa4, 0x78, + 0x45, 0x83, 0x59, 0x7c, 0xc0, 0x7d, 0xd5, 0x97, 0xd9, 0xe8, 0xde, 0xb2, + 0xb6, 0x3c, 0x7e, 0x12, 0x54, 0x27, 0xe7, 0xb0, 0xbf, 0xd0, 0xbf, 0xd1, + 0x89, 0x08, 0xf0, 0xd7, 0xb0, 0x83, 0xbc, 0x7c, 0x35, 0x96, 0x35, 0x97, + 0xce, 0x23, 0x8d, 0x65, 0xdf, 0x4b, 0xc7, 0x9a, 0xe3, 0x60, 0x10, 0xbf, + 0xe2, 0x86, 0xb8, 0x65, 0xf4, 0x96, 0x5f, 0xff, 0xfb, 0x9f, 0x9e, 0x87, + 0x85, 0xe3, 0x0f, 0x8f, 0xa2, 0xc1, 0x49, 0x65, 0xd0, 0x15, 0x94, 0x48, + 0xbb, 0xe9, 0xce, 0xf6, 0xcb, 0xc2, 0x46, 0xea, 0xcb, 0xc2, 0xf8, 0xd6, + 0x56, 0xc6, 0xf4, 0xc8, 0x2f, 0xc5, 0x0d, 0x61, 0x2c, 0xbf, 0xef, 0x7d, + 0x02, 0xcd, 0x28, 0xdd, 0x59, 0x6e, 0x0c, 0xf9, 0x70, 0x9a, 0xb4, 0x8a, + 0xde, 0xc2, 0x1a, 0x96, 0x5f, 0xa5, 0xa3, 0x86, 0x2c, 0xad, 0x8d, 0x97, + 0xc1, 0x75, 0x0a, 0xd2, 0xb2, 0x17, 0x87, 0x0f, 0x67, 0x8c, 0x6c, 0x0b, + 0x57, 0xfa, 0x5e, 0x8f, 0xbb, 0x1e, 0x59, 0x7f, 0xfa, 0x5d, 0x07, 0xb5, + 0x0d, 0x48, 0xff, 0xc5, 0x95, 0xf2, 0x20, 0x08, 0xd2, 0xff, 0xfd, 0xd7, + 0xd6, 0x1f, 0xfc, 0xcf, 0x1e, 0xbc, 0xeb, 0x2f, 0x41, 0x0d, 0x65, 0xfb, + 0xee, 0x4d, 0x9d, 0x59, 0x58, 0x8b, 0xcf, 0x88, 0xfc, 0xa8, 0x43, 0x77, + 0xf1, 0xf4, 0x9f, 0xfe, 0x2c, 0xbe, 0xe1, 0xc0, 0x56, 0x5f, 0xfd, 0x36, + 0x6b, 0x1d, 0x91, 0xe3, 0x1a, 0xca, 0xc3, 0xe4, 0xe9, 0x0d, 0xfc, 0x06, + 0x67, 0xbe, 0x75, 0x94, 0x2a, 0x38, 0x0a, 0x12, 0x3c, 0x21, 0xbf, 0xfc, + 0x63, 0x27, 0xf8, 0xb3, 0xb8, 0x7f, 0xac, 0xbe, 0xdb, 0x67, 0xdd, 0x59, + 0x7c, 0x11, 0x83, 0x65, 0x97, 0xa5, 0xc0, 0x2c, 0xbf, 0x8b, 0xa7, 0xe9, + 0x12, 0xcb, 0xff, 0xff, 0xf7, 0xa3, 0x36, 0x91, 0xf7, 0x91, 0xac, 0x6e, + 0x7f, 0xc4, 0x6f, 0x88, 0x20, 0xac, 0xb7, 0x71, 0x1e, 0x3e, 0x24, 0x71, + 0xde, 0x16, 0x5f, 0xff, 0x33, 0x3e, 0xe9, 0x66, 0xcc, 0xc6, 0x61, 0x2c, + 0xa8, 0x44, 0x81, 0xa0, 0xdf, 0xbc, 0xfa, 0xcd, 0x2c, 0xb8, 0x0e, 0xb2, + 0x82, 0x6f, 0x48, 0x9e, 0xfa, 0x5d, 0x00, 0xd6, 0x5f, 0xa0, 0xe4, 0xfc, + 0x59, 0x5f, 0x1e, 0x4e, 0x88, 0xef, 0xec, 0xef, 0x81, 0x1f, 0x2c, 0xbe, + 0xe9, 0xeb, 0x16, 0x5f, 0x7d, 0xc3, 0x14, 0x67, 0x9f, 0x85, 0xd5, 0xb2, + 0x62, 0xa4, 0xd3, 0xd7, 0x2b, 0xfd, 0x9c, 0x2c, 0xdf, 0xe3, 0x59, 0x7f, + 0xe6, 0x47, 0xc5, 0x9f, 0xf2, 0x37, 0x56, 0x56, 0x8f, 0xd5, 0x86, 0x77, + 0xf8, 0x65, 0x9b, 0xcf, 0x4e, 0xb2, 0xa1, 0x1f, 0x67, 0x0a, 0x60, 0x11, + 0x5f, 0xb6, 0xdd, 0x3c, 0xf9, 0x65, 0xf8, 0xf9, 0x1b, 0x31, 0x76, 0x7e, + 0xaf, 0xd0, 0xfa, 0xfc, 0x0b, 0xb3, 0xf5, 0x73, 0xc9, 0x76, 0x7e, 0xaf, + 0x81, 0x2c, 0xe2, 0xec, 0xfd, 0x50, 0xcf, 0x48, 0x88, 0xef, 0xd1, 0x9c, + 0xc2, 0x5d, 0x9f, 0xaa, 0x5d, 0x9f, 0xab, 0x9f, 0xab, 0xb3, 0xf4, 0xc2, + 0xe2, 0xd2, 0x23, 0xfc, 0x02, 0x55, 0xf6, 0x4f, 0x9f, 0xeb, 0xb3, 0xf5, + 0x4b, 0xb3, 0xf5, 0x77, 0xf0, 0xbb, 0x3f, 0x57, 0xfd, 0x9f, 0xbe, 0xb3, + 0x7e, 0x0d, 0x76, 0x7e, 0xaf, 0xec, 0xf1, 0xf0, 0x1b, 0x2e, 0xcf, 0xd5, + 0x7e, 0x8a, 0x62, 0x24, 0xe2, 0x3d, 0xf6, 0xb6, 0x8e, 0xae, 0xcf, 0xd5, + 0x2e, 0xcf, 0xd6, 0x1b, 0x0b, 0x9a, 0x69, 0x76, 0x7e, 0xaa, 0x4a, 0xc7, + 0x46, 0x6b, 0x90, 0x87, 0xfa, 0x13, 0xba, 0x27, 0x61, 0x97, 0x21, 0x7d, + 0xd6, 0x06, 0x89, 0xee, 0x81, 0x53, 0x67, 0xe8, 0xdd, 0x12, 0x17, 0xfd, + 0xb4, 0x6f, 0xc3, 0xd6, 0x6c, 0xb2, 0xa4, 0x7e, 0x9a, 0x39, 0xbf, 0xa3, + 0x04, 0x89, 0x49, 0x65, 0xfe, 0xe6, 0x4a, 0x51, 0xdf, 0x96, 0x56, 0x22, + 0x0c, 0x88, 0xb8, 0x5b, 0x7f, 0xec, 0x08, 0xf3, 0x4d, 0x3f, 0xa6, 0x59, + 0x7f, 0xf7, 0x0b, 0x26, 0x8d, 0xbf, 0x7f, 0xb8, 0xb2, 0xe9, 0x6c, 0x92, + 0xfa, 0x67, 0x7d, 0x2c, 0xad, 0x91, 0xdf, 0xf1, 0x77, 0x90, 0x09, 0x1f, + 0x70, 0x62, 0xe3, 0xd9, 0x65, 0x69, 0x78, 0x91, 0xe7, 0x6f, 0xfa, 0x9d, + 0x7f, 0x70, 0xa3, 0xc0, 0xd2, 0xcb, 0xfb, 0x85, 0x9d, 0xcf, 0xd6, 0x57, + 0xc7, 0xb4, 0xc2, 0xdb, 0xfe, 0x98, 0xb3, 0x7f, 0xbe, 0x7d, 0xd5, 0x97, + 0xf7, 0x73, 0xd1, 0xaf, 0xd6, 0x5f, 0xf6, 0x8d, 0x9c, 0x12, 0x32, 0x65, + 0x97, 0xfc, 0x18, 0xfc, 0x60, 0x7d, 0x49, 0x65, 0xff, 0x9d, 0xf6, 0xc6, + 0x39, 0x7d, 0x25, 0x97, 0x9c, 0x5e, 0x61, 0xfc, 0x4c, 0x75, 0x66, 0xa7, + 0x26, 0x29, 0xa2, 0xe2, 0x85, 0x95, 0x42, 0x76, 0x98, 0x46, 0xf1, 0xa9, + 0xdf, 0x18, 0xa6, 0xd2, 0xcb, 0xee, 0xfb, 0x36, 0x59, 0x4c, 0x3c, 0x62, + 0x23, 0xb8, 0x6d, 0x2c, 0xbf, 0xc3, 0x13, 0x7b, 0xb9, 0x05, 0x65, 0x41, + 0xf7, 0x61, 0x09, 0x8c, 0x5e, 0x67, 0xfa, 0x59, 0x7f, 0xfb, 0xee, 0xf8, + 0xc4, 0x1e, 0x61, 0x70, 0xd6, 0x5f, 0xff, 0xa5, 0xa8, 0xde, 0xf3, 0x40, + 0xfd, 0x0c, 0x79, 0x96, 0x5f, 0xed, 0xc7, 0xe1, 0xf2, 0x6c, 0x59, 0x53, + 0x91, 0xed, 0x83, 0xdf, 0x25, 0x69, 0x62, 0xff, 0xa3, 0xe8, 0xce, 0x71, + 0x9f, 0x2c, 0xbc, 0x65, 0x25, 0x95, 0x0d, 0xc1, 0x66, 0xc9, 0xd2, 0x9d, + 0x5e, 0xc8, 0xe5, 0x0e, 0x31, 0xcd, 0xd3, 0x59, 0x92, 0x35, 0x1d, 0x47, + 0xa5, 0x37, 0x3c, 0xf2, 0xb9, 0x4a, 0x48, 0xe4, 0x3a, 0x01, 0x19, 0x13, + 0x47, 0xfb, 0x87, 0x57, 0xf3, 0xe8, 0x3f, 0xed, 0x8b, 0x2f, 0xff, 0xf8, + 0xfc, 0xfe, 0x89, 0x37, 0xcf, 0xe2, 0x5c, 0x1f, 0xa3, 0x4b, 0x2f, 0xfc, + 0x2c, 0x7c, 0x51, 0xef, 0x00, 0x2b, 0x2f, 0xfe, 0xd4, 0x61, 0x64, 0x8f, + 0xd9, 0xbd, 0x65, 0xff, 0xf1, 0x38, 0xbc, 0xcd, 0xf1, 0xf7, 0x78, 0x06, + 0x2c, 0xa1, 0xa2, 0x58, 0x90, 0xef, 0xf1, 0x47, 0x7e, 0x99, 0xd8, 0xb2, + 0xed, 0xf8, 0xb2, 0x98, 0x79, 0x84, 0x69, 0x7f, 0x13, 0x8b, 0xe7, 0x92, + 0xcb, 0x9f, 0x4b, 0x2f, 0xc1, 0xc6, 0x47, 0x16, 0x5e, 0x69, 0xa6, 0x92, + 0x5e, 0x12, 0x3f, 0x48, 0xdc, 0xd0, 0x5f, 0xf3, 0xc8, 0x4d, 0x66, 0xf7, + 0xd2, 0xcb, 0xff, 0xf7, 0xb2, 0x42, 0x11, 0x66, 0xd3, 0x4a, 0x35, 0xb2, + 0xca, 0x24, 0x49, 0xf4, 0xee, 0xff, 0x14, 0x8d, 0x99, 0xf7, 0x56, 0x5f, + 0x7c, 0x13, 0x25, 0x94, 0xe7, 0xab, 0xd3, 0x3a, 0x59, 0x77, 0xf1, 0x23, + 0x58, 0xdb, 0x21, 0xbf, 0x4e, 0x80, 0xe6, 0x96, 0x5f, 0xfa, 0x74, 0x10, + 0x19, 0x0d, 0x47, 0x16, 0x5b, 0x06, 0x7d, 0x24, 0x53, 0x7f, 0xdd, 0x72, + 0xce, 0x6a, 0x38, 0xb2, 0xfa, 0x51, 0xff, 0xcb, 0x2f, 0xef, 0xe0, 0xb3, + 0xee, 0xac, 0xb9, 0xff, 0x9c, 0x7a, 0x42, 0x12, 0x5f, 0xfe, 0x10, 0xa6, + 0x15, 0xc7, 0xec, 0x0e, 0x69, 0x65, 0x1a, 0x78, 0xde, 0x84, 0xd3, 0x93, + 0x14, 0x22, 0x78, 0x63, 0x7b, 0x7c, 0x69, 0x65, 0x6c, 0xbc, 0x45, 0x22, + 0xf1, 0xb4, 0x64, 0x35, 0x8d, 0xb9, 0x84, 0x3e, 0x2d, 0x71, 0x6f, 0xd1, + 0xca, 0x18, 0x7d, 0x94, 0xd3, 0xbd, 0x4a, 0xe3, 0xd9, 0x65, 0xfe, 0x20, + 0x8c, 0xf7, 0xc0, 0xd6, 0x50, 0xcf, 0x2c, 0x02, 0xf6, 0xfd, 0x65, 0xff, + 0x64, 0x6e, 0xce, 0xf3, 0xe6, 0x96, 0x5a, 0x65, 0x96, 0x62, 0xcb, 0x30, + 0xcd, 0x1b, 0x89, 0x5f, 0xb3, 0xfe, 0x44, 0x96, 0x5f, 0x8d, 0x83, 0x1e, + 0x2c, 0xb7, 0xe4, 0x79, 0xf3, 0xe5, 0x17, 0xfd, 0xdf, 0x67, 0x27, 0x74, + 0xf6, 0x59, 0x50, 0x7c, 0xe6, 0x57, 0x7f, 0xc4, 0x60, 0x6d, 0xf2, 0x7e, + 0x3a, 0xb2, 0xff, 0x77, 0x3f, 0xdc, 0xe9, 0xb1, 0x65, 0xe7, 0xd7, 0xeb, + 0x2a, 0x49, 0xff, 0xcc, 0x24, 0xc5, 0xff, 0x43, 0x6c, 0x88, 0x3a, 0x7f, + 0x3e, 0x6f, 0x7d, 0x9d, 0x13, 0x65, 0x97, 0x9a, 0x69, 0xa4, 0xc4, 0x20, + 0x5f, 0x05, 0xdc, 0x29, 0x88, 0x40, 0x6e, 0x6b, 0xaf, 0x9f, 0x58, 0xd2, + 0xca, 0xd1, 0xf1, 0xef, 0x41, 0xbc, 0xd3, 0x4d, 0x26, 0x20, 0xf2, 0x93, + 0x10, 0x78, 0xdc, 0xd7, 0x5f, 0xdd, 0x76, 0x32, 0x18, 0xb2, 0xfc, 0xfd, + 0x72, 0xfd, 0x65, 0xfd, 0x9d, 0xf0, 0x1e, 0x4b, 0x2f, 0x34, 0xd3, 0x4b, + 0x2f, 0x72, 0x05, 0x48, 0xdc, 0xd0, 0x56, 0x26, 0x9a, 0x15, 0x2f, 0x8a, + 0xfc, 0x5a, 0x44, 0xfd, 0x4d, 0xad, 0xd5, 0x4b, 0x2c, 0x94, 0x05, 0x79, + 0xa8, 0x25, 0x97, 0xff, 0x7a, 0x24, 0x7d, 0xcf, 0x06, 0x18, 0xb2, 0xa1, + 0x11, 0xce, 0x5f, 0xbc, 0x72, 0xff, 0xde, 0x31, 0xc1, 0x77, 0x3e, 0xea, + 0xcb, 0xfc, 0x50, 0xce, 0x70, 0x04, 0xb2, 0x82, 0x7e, 0x1c, 0x3e, 0xbf, + 0xfd, 0x9e, 0x3d, 0x79, 0xf3, 0x86, 0xfb, 0x2c, 0xbf, 0xf6, 0xcc, 0xc6, + 0x61, 0x77, 0x02, 0xb2, 0xfa, 0x3b, 0x0c, 0x59, 0x5f, 0x23, 0x25, 0x84, + 0x44, 0x91, 0xd3, 0xeb, 0xfe, 0xf4, 0x10, 0x7e, 0xe3, 0xee, 0xac, 0xbf, + 0x48, 0x81, 0xb1, 0x2c, 0xbf, 0x87, 0xcc, 0x64, 0x6e, 0xac, 0xbc, 0x07, + 0x11, 0x65, 0xdb, 0x7c, 0xb2, 0xce, 0x33, 0x6d, 0xf8, 0xed, 0x49, 0x1e, + 0x3f, 0x1e, 0x39, 0x43, 0x4c, 0xd7, 0xc4, 0x70, 0x2a, 0xcb, 0xfe, 0xc2, + 0x8f, 0x7a, 0x1a, 0x35, 0x97, 0x84, 0x8f, 0xd6, 0x56, 0xc7, 0xec, 0x29, + 0x0b, 0x0d, 0xef, 0xc1, 0x8f, 0x43, 0x12, 0x5f, 0x89, 0xc8, 0xb1, 0x25, + 0xd8, 0x14, 0x97, 0x34, 0xd2, 0x4a, 0xc3, 0xfa, 0xe1, 0x3f, 0x49, 0x1a, + 0x16, 0xbf, 0xc4, 0x24, 0x7f, 0xec, 0xdc, 0x48, 0xdc, 0xde, 0x5f, 0xfe, + 0xd8, 0x7a, 0x70, 0x96, 0x6f, 0xd1, 0xf1, 0x65, 0x42, 0xb5, 0xec, 0x8c, + 0xb8, 0xe1, 0x68, 0xf0, 0xe7, 0x02, 0x4d, 0xff, 0xd1, 0xff, 0x33, 0x06, + 0xe4, 0xe2, 0xac, 0xbf, 0xe2, 0xff, 0xcf, 0xde, 0x9f, 0xeb, 0x2f, 0xff, + 0xde, 0x3d, 0xe6, 0x43, 0xe4, 0x68, 0xf3, 0xee, 0x2c, 0xbf, 0x8a, 0x33, + 0x4f, 0xfa, 0xcb, 0xff, 0x46, 0xbd, 0x07, 0xe8, 0x23, 0x59, 0x74, 0xb9, + 0x88, 0xf7, 0x09, 0xd7, 0x55, 0x9a, 0x2c, 0xbf, 0xd0, 0x58, 0xc3, 0xfa, + 0x4b, 0x2f, 0xff, 0xb8, 0x59, 0xf7, 0xef, 0xc7, 0xd1, 0xfd, 0xfa, 0xca, + 0xf9, 0x19, 0xbd, 0x49, 0x68, 0xca, 0xbc, 0xa8, 0xe5, 0xe5, 0x03, 0xde, + 0xde, 0x23, 0x16, 0x5f, 0x7b, 0x6c, 0x69, 0x65, 0xf0, 0xc0, 0x52, 0x59, + 0x6e, 0x39, 0xe3, 0x08, 0x49, 0x7f, 0x18, 0x47, 0x8f, 0xfa, 0xcb, 0xff, + 0xf7, 0x79, 0xf4, 0xe1, 0xe0, 0xe1, 0xf5, 0x85, 0xfa, 0xcb, 0xff, 0xff, + 0x4e, 0xec, 0xb3, 0xf9, 0xfc, 0x9c, 0x3c, 0x1c, 0x3e, 0xb0, 0xbf, 0x42, + 0x17, 0xff, 0x67, 0x67, 0x07, 0xc7, 0xef, 0x1f, 0xea, 0x84, 0x2d, 0x52, + 0x46, 0x5f, 0x1d, 0xef, 0x46, 0xcd, 0xf8, 0x9a, 0x37, 0x63, 0x23, 0xbf, + 0x33, 0x8d, 0x3f, 0xcb, 0x2d, 0xc1, 0x9f, 0x40, 0x87, 0xd7, 0xf6, 0x4d, + 0x23, 0xd7, 0x16, 0x5f, 0xd1, 0xf0, 0x9c, 0x8c, 0x59, 0x7f, 0x3e, 0xb4, + 0xff, 0xc2, 0xca, 0x86, 0xc4, 0x5c, 0x71, 0x82, 0x64, 0xa2, 0x13, 0x96, + 0xa9, 0xa9, 0xcc, 0x77, 0x95, 0x23, 0xf9, 0x5b, 0x6d, 0x98, 0x8a, 0x39, + 0x1e, 0xdf, 0x4a, 0x40, 0x5c, 0x21, 0x75, 0xe6, 0x9a, 0x69, 0x25, 0xe7, + 0x21, 0xa4, 0x6e, 0x68, 0x2f, 0xb3, 0x59, 0xd5, 0x95, 0xf9, 0xe7, 0x70, + 0xb6, 0xfb, 0xc6, 0xf2, 0x59, 0x7f, 0xf8, 0x42, 0x2c, 0xda, 0x3e, 0x94, + 0xa1, 0x8b, 0x2f, 0xee, 0x1e, 0xb4, 0x6c, 0x59, 0x52, 0x3f, 0x8e, 0xa5, + 0xde, 0x66, 0xc3, 0x59, 0x44, 0x78, 0x1d, 0x22, 0xbd, 0x38, 0x59, 0xf5, + 0x97, 0xba, 0xe3, 0x59, 0x5a, 0x37, 0xfc, 0x23, 0xbf, 0xc2, 0xfd, 0xdd, + 0x46, 0x7c, 0xb2, 0xfe, 0x0c, 0x7e, 0x7a, 0x15, 0x25, 0xfc, 0x26, 0x6b, + 0x32, 0x65, 0x97, 0xe7, 0xf4, 0xe1, 0xc2, 0xca, 0x92, 0xa3, 0x91, 0x91, + 0xe4, 0x36, 0x4d, 0x81, 0xc8, 0x48, 0xdb, 0x85, 0xfd, 0x2e, 0xbf, 0x3e, + 0xb5, 0x9f, 0x2c, 0xbf, 0xfd, 0xdf, 0x1b, 0xff, 0x9a, 0x1c, 0x14, 0x96, + 0x5f, 0xfe, 0xfa, 0x77, 0xdf, 0xc1, 0x05, 0xf6, 0x27, 0x59, 0x50, 0x8b, + 0xfc, 0x28, 0x24, 0x9b, 0xf4, 0x70, 0x42, 0x92, 0xcb, 0xe9, 0xc1, 0x39, + 0x2c, 0xbf, 0xf0, 0x73, 0x58, 0x7c, 0xf4, 0x6f, 0x59, 0x7f, 0xff, 0xf3, + 0x9f, 0x4f, 0xc1, 0x39, 0xd2, 0x3f, 0x39, 0x76, 0x59, 0xac, 0x59, 0x76, + 0x79, 0x65, 0xd9, 0xb8, 0xb2, 0xa6, 0x35, 0xfe, 0x16, 0xaf, 0x23, 0x05, + 0xe1, 0x3f, 0x7f, 0xc5, 0x1f, 0x72, 0x3b, 0x03, 0x59, 0x7b, 0x82, 0x72, + 0x71, 0xef, 0xe1, 0x3d, 0x0d, 0x3b, 0x9f, 0x89, 0x3b, 0x1a, 0xfd, 0xff, + 0x6a, 0x24, 0x7d, 0x32, 0x92, 0xcb, 0xff, 0xff, 0xfe, 0x8e, 0x47, 0x76, + 0xc6, 0xb5, 0x9e, 0x71, 0x39, 0x82, 0xb9, 0x7f, 0x05, 0x13, 0x7a, 0x16, + 0x5f, 0xb9, 0xe7, 0x0c, 0x2c, 0xbf, 0x3e, 0xcd, 0x3c, 0x96, 0x54, 0x26, + 0x38, 0x46, 0xed, 0x42, 0x54, 0x42, 0x7b, 0xfe, 0xe7, 0xa1, 0xf5, 0xac, + 0xf9, 0x65, 0xff, 0xff, 0x85, 0xfa, 0x5d, 0xc2, 0xef, 0x27, 0x73, 0x99, + 0xac, 0x27, 0x92, 0xca, 0xc4, 0x54, 0x19, 0xcd, 0xff, 0xcd, 0xc6, 0x50, + 0x00, 0xe0, 0xa7, 0xa5, 0x97, 0xb4, 0xfc, 0x59, 0x76, 0x34, 0xb2, 0xa0, + 0xfe, 0x46, 0x8d, 0xc1, 0xcb, 0xdd, 0xf4, 0x2c, 0xac, 0x3c, 0xa7, 0x2e, + 0xbf, 0x7a, 0x09, 0xfc, 0xb2, 0xfe, 0xdb, 0xcf, 0xdd, 0x42, 0xcb, 0xfc, + 0x3f, 0x44, 0xce, 0xff, 0x2c, 0xbd, 0xef, 0xf7, 0xac, 0xad, 0x1e, 0xa6, + 0xe1, 0xa5, 0xf7, 0xbe, 0x7d, 0xd5, 0x95, 0x24, 0xc4, 0xc6, 0x41, 0xa2, + 0x67, 0x84, 0x11, 0x12, 0xdf, 0xfd, 0x9a, 0xfd, 0x87, 0xc1, 0x39, 0x18, + 0xb2, 0xf4, 0x7d, 0xba, 0xb2, 0xfb, 0x84, 0x62, 0xac, 0xb0, 0xf4, 0x78, + 0x5d, 0x20, 0xbf, 0xf8, 0x56, 0xa3, 0x5e, 0x83, 0x7e, 0xf1, 0x65, 0xff, + 0xff, 0xbb, 0x1a, 0xfc, 0x3e, 0x39, 0x1e, 0x9f, 0xfe, 0x94, 0x33, 0x8b, + 0x2e, 0xdd, 0xe2, 0xcb, 0xfe, 0xe0, 0x7c, 0xe3, 0x6c, 0x36, 0x67, 0xb5, + 0x94, 0x2a, 0x33, 0x9d, 0xb7, 0xa3, 0x55, 0x24, 0xfc, 0xb2, 0x10, 0xe6, + 0x50, 0xf1, 0x89, 0xde, 0x9e, 0x8b, 0xf5, 0x97, 0xf4, 0xb5, 0x12, 0x7d, + 0x2c, 0xbb, 0x18, 0xb2, 0xff, 0xec, 0x2f, 0xe7, 0x09, 0xaf, 0xd8, 0x7c, + 0x59, 0x43, 0x3d, 0xf6, 0x0b, 0x5f, 0x7f, 0xdc, 0xfd, 0x65, 0xfa, 0x39, + 0xc3, 0xde, 0xb2, 0xf9, 0x84, 0x50, 0xb2, 0xff, 0x83, 0x1b, 0xe3, 0x87, + 0xff, 0xcb, 0x2e, 0x71, 0x60, 0xf7, 0x30, 0x82, 0xbe, 0x4e, 0x0c, 0xe1, + 0x19, 0xa2, 0x2f, 0x12, 0x14, 0x21, 0x6f, 0x4f, 0xfd, 0x0b, 0x2f, 0xf8, + 0x3e, 0x28, 0xf8, 0xe3, 0xcb, 0x2a, 0x19, 0x89, 0xd2, 0x8c, 0xcc, 0x65, + 0xb9, 0x1f, 0x09, 0xc6, 0xa9, 0xa8, 0x75, 0xbc, 0x7e, 0xe5, 0x29, 0x03, + 0x88, 0xc0, 0x8e, 0x96, 0x7d, 0x50, 0x42, 0x0b, 0xff, 0x39, 0x8c, 0x1d, + 0xe6, 0xd8, 0xd2, 0xcb, 0xa7, 0xa2, 0x59, 0x7e, 0xe6, 0x0b, 0x1c, 0x59, + 0x7f, 0x47, 0xcc, 0x72, 0xfd, 0x65, 0xfd, 0xbb, 0xc3, 0x2f, 0xa4, 0xb2, + 0xff, 0xcf, 0xa9, 0xf3, 0xf6, 0x3f, 0xdf, 0xac, 0xbf, 0xbf, 0x10, 0xbd, + 0x9f, 0x2c, 0xbf, 0xff, 0x1f, 0x61, 0x99, 0xf7, 0x61, 0x82, 0x69, 0xff, + 0x59, 0x7c, 0x64, 0xe3, 0x59, 0x7f, 0xdc, 0x8d, 0x76, 0x1b, 0x8c, 0xd6, + 0x54, 0x91, 0x62, 0x35, 0x7e, 0x10, 0x5f, 0xff, 0xd9, 0xe9, 0xd8, 0xc3, + 0xf4, 0x4e, 0xe7, 0x30, 0xbf, 0x59, 0x7f, 0xf1, 0xea, 0x25, 0xe8, 0x98, + 0x80, 0x2a, 0xca, 0xf2, 0x28, 0xf8, 0xbd, 0x7f, 0xed, 0xa2, 0x62, 0x8e, + 0x64, 0x49, 0x65, 0xc1, 0x9f, 0x59, 0x7f, 0x0b, 0xd8, 0xf4, 0x71, 0x65, + 0xff, 0x70, 0xb3, 0xbc, 0x8d, 0x6c, 0xb2, 0xfe, 0xf1, 0xfd, 0xc7, 0xf9, + 0x65, 0xfe, 0xc1, 0xe6, 0xa5, 0x1a, 0x59, 0x7b, 0xc7, 0xd5, 0x95, 0x09, + 0x9e, 0x48, 0xf8, 0xc6, 0xa6, 0x2e, 0xfc, 0xe7, 0x85, 0xe2, 0x19, 0x5f, + 0x9f, 0xd9, 0xf7, 0x56, 0x5f, 0xfd, 0x8e, 0x5b, 0x35, 0x84, 0x3f, 0x42, + 0xcb, 0xfc, 0x0f, 0xa5, 0xc1, 0x23, 0x75, 0x65, 0xe0, 0xc6, 0xea, 0xcb, + 0xfd, 0x37, 0x9f, 0x5a, 0x31, 0xac, 0xac, 0x3d, 0x37, 0x1f, 0xbf, 0xcf, + 0xf0, 0x9a, 0x36, 0x62, 0xcb, 0xf9, 0xcb, 0x66, 0x3f, 0x56, 0x51, 0x1f, + 0x0f, 0x0d, 0x2f, 0x8f, 0xce, 0xc5, 0x97, 0xfc, 0x45, 0x9b, 0x77, 0xb0, + 0xc5, 0x95, 0x24, 0xfc, 0xa6, 0x28, 0xd2, 0x19, 0x42, 0x33, 0x90, 0x81, + 0xe9, 0x08, 0x84, 0x37, 0xed, 0x67, 0x0d, 0xa5, 0x97, 0xcc, 0x8d, 0x71, + 0x65, 0xff, 0xe8, 0x1f, 0x80, 0x28, 0x90, 0x3f, 0x03, 0x8b, 0x2e, 0x8d, + 0xd1, 0x9f, 0x78, 0x84, 0x57, 0xa2, 0x78, 0xba, 0xcb, 0xf8, 0x48, 0x00, + 0x40, 0xc5, 0x97, 0x19, 0x2c, 0xbe, 0x94, 0x10, 0x56, 0x50, 0xcd, 0xb6, + 0x0a, 0xda, 0x65, 0x97, 0xcd, 0x97, 0x8f, 0x96, 0x5a, 0x08, 0xdc, 0x74, + 0x4a, 0xa7, 0x1f, 0xe9, 0xac, 0x5f, 0x47, 0xf8, 0x4b, 0x2a, 0x4c, 0x8b, + 0x31, 0x94, 0x61, 0x70, 0x4c, 0x7e, 0x42, 0x38, 0x70, 0xcd, 0x0d, 0x3d, + 0x46, 0xbe, 0xc9, 0x4b, 0x0e, 0xe1, 0xfc, 0x25, 0xf8, 0x62, 0x02, 0x16, + 0xa1, 0xc5, 0x3e, 0x47, 0x7c, 0xc6, 0xd2, 0x78, 0xcf, 0x2b, 0x2e, 0xef, + 0x16, 0x51, 0x9e, 0x54, 0xc6, 0x77, 0xc2, 0x43, 0x5c, 0x59, 0x77, 0xfd, + 0x59, 0x77, 0xfe, 0x59, 0x7d, 0xad, 0x67, 0x16, 0x5d, 0x93, 0x2c, 0xb4, + 0xa7, 0x22, 0x37, 0x62, 0x46, 0x0c, 0x38, 0xc0, 0x84, 0x57, 0xe3, 0xeb, + 0x96, 0x2c, 0xbf, 0x68, 0x79, 0x84, 0xb2, 0xf8, 0x5d, 0x3b, 0x4b, 0x2f, + 0xfc, 0x79, 0xe8, 0xdf, 0xcf, 0x47, 0xcb, 0x2b, 0x0f, 0x93, 0x74, 0x92, + 0xfe, 0xfb, 0x40, 0xfb, 0xb2, 0x59, 0x4e, 0x8d, 0xfe, 0x42, 0x28, 0x04, + 0x97, 0xe6, 0xbb, 0xdc, 0xd9, 0x65, 0x2c, 0xbf, 0xc5, 0xac, 0xe6, 0x1f, + 0x96, 0x53, 0x64, 0xde, 0x60, 0x5d, 0xff, 0xf4, 0xba, 0x58, 0xfd, 0x2c, + 0xef, 0x71, 0xa5, 0x97, 0xa4, 0xfb, 0x8b, 0x2f, 0xff, 0xb0, 0x67, 0xdc, + 0x67, 0xa2, 0x02, 0xc7, 0x59, 0x76, 0x6f, 0x59, 0x7f, 0xc1, 0xf6, 0x0d, + 0x87, 0xac, 0x59, 0x50, 0x89, 0xdd, 0x93, 0x70, 0x62, 0xfe, 0x88, 0xd7, + 0xa3, 0x7a, 0xcb, 0xf7, 0xe6, 0x53, 0x6e, 0xac, 0xbf, 0xa3, 0x7e, 0x7b, + 0xd0, 0xb2, 0xfb, 0xbc, 0xc9, 0x96, 0x78, 0xd7, 0x5f, 0xde, 0x19, 0xef, + 0x81, 0xac, 0xac, 0x3e, 0x27, 0x33, 0xbe, 0xe1, 0xe7, 0xeb, 0x2f, 0xf1, + 0x83, 0x5e, 0xcd, 0xfc, 0x59, 0x7f, 0xff, 0xb3, 0xa7, 0xf4, 0x8a, 0x27, + 0x74, 0xf3, 0x70, 0x7e, 0xc5, 0x97, 0xd9, 0xd3, 0xe2, 0xcb, 0xfa, 0x7a, + 0x98, 0xa7, 0x9d, 0x80, 0xb2, 0xb7, 0x51, 0xe9, 0x31, 0xaf, 0x19, 0x3a, + 0x43, 0x7f, 0xe0, 0x3b, 0x38, 0x64, 0xfa, 0x15, 0x65, 0x49, 0x76, 0x68, + 0x71, 0x82, 0x61, 0xa0, 0x59, 0xbe, 0x24, 0x99, 0x3b, 0x50, 0xbc, 0xf1, + 0x7f, 0xe5, 0xc5, 0x0b, 0xce, 0x10, 0x76, 0x31, 0x66, 0x90, 0x6f, 0xec, + 0x68, 0xb7, 0xea, 0x4b, 0x2f, 0xff, 0xdd, 0x3e, 0x6d, 0x8d, 0x70, 0xf6, + 0x27, 0xfb, 0xab, 0x2f, 0xfa, 0x59, 0xdc, 0x19, 0x97, 0xeb, 0x2f, 0x35, + 0x9e, 0x59, 0x7f, 0x66, 0xb4, 0x01, 0x74, 0xb2, 0xfe, 0x29, 0x34, 0xfe, + 0xe2, 0xcb, 0xf0, 0xbe, 0x72, 0x35, 0x96, 0xe6, 0x1e, 0xa9, 0x97, 0x5f, + 0x0c, 0x9c, 0x55, 0x97, 0xff, 0x1c, 0x9f, 0x59, 0xbf, 0xe0, 0x9f, 0xcb, + 0x2b, 0x63, 0xe9, 0xe9, 0x15, 0xf1, 0xf3, 0x07, 0xb2, 0xa1, 0x3c, 0x30, + 0xf9, 0x61, 0xce, 0x08, 0x77, 0xb0, 0x84, 0x12, 0x11, 0xf7, 0xfe, 0x7d, + 0xdf, 0xc1, 0xc3, 0x2f, 0xa4, 0xb2, 0xdf, 0xac, 0xbf, 0xc3, 0xc2, 0x36, + 0xbc, 0x05, 0x97, 0xff, 0xde, 0x38, 0xe9, 0xff, 0x12, 0x6e, 0xd3, 0x4d, + 0x24, 0xbf, 0xf8, 0xe3, 0xbf, 0xc4, 0x9b, 0xb4, 0xd3, 0x49, 0x2b, 0x11, + 0x3f, 0xe5, 0x5a, 0xd9, 0x1e, 0x9d, 0x86, 0x95, 0xfe, 0xf0, 0x1d, 0x83, + 0x76, 0x2c, 0xa2, 0x3d, 0xce, 0x94, 0xdf, 0x0f, 0x30, 0x54, 0x97, 0xcf, + 0xfb, 0xf5, 0x65, 0xff, 0x69, 0xc3, 0x3b, 0xd0, 0x21, 0x2c, 0xbc, 0x08, + 0xd2, 0xcb, 0xbb, 0xc1, 0x9e, 0xbc, 0xc7, 0x77, 0xc2, 0x83, 0xfe, 0x2c, + 0xb8, 0x80, 0xa8, 0x86, 0x57, 0xf4, 0x32, 0x39, 0xac, 0x59, 0x52, 0x3c, + 0xf9, 0x88, 0xef, 0x7a, 0x36, 0x59, 0x7e, 0xec, 0x48, 0x56, 0x2c, 0xbc, + 0xd3, 0x4d, 0x24, 0xbd, 0x84, 0x14, 0x8d, 0xcd, 0x05, 0xff, 0xb3, 0x6c, + 0x1b, 0xc8, 0xa3, 0x65, 0x95, 0x08, 0xb4, 0xfd, 0x24, 0x8b, 0x6f, 0xdd, + 0xf3, 0x90, 0xab, 0x2f, 0xef, 0xa5, 0xc2, 0x70, 0xac, 0xbc, 0x4d, 0xae, + 0x7b, 0x59, 0x58, 0x7a, 0xae, 0x5d, 0x73, 0x6b, 0x6d, 0x6b, 0x2f, 0xd9, + 0xde, 0x83, 0x65, 0x97, 0xfe, 0x3d, 0xbc, 0x6f, 0x2e, 0x9e, 0xcb, 0x2d, + 0x26, 0xd6, 0x88, 0xb2, 0x23, 0xe9, 0x4d, 0xe7, 0xdd, 0x85, 0x97, 0xec, + 0x1f, 0x9f, 0x75, 0x65, 0xf8, 0xb0, 0x70, 0xc5, 0x95, 0x3c, 0xae, 0xda, + 0xc4, 0x6c, 0x5b, 0x10, 0x8c, 0x8b, 0x1e, 0x45, 0x2f, 0xf9, 0xd2, 0x62, + 0x3d, 0x43, 0x45, 0x85, 0xfe, 0x7d, 0x78, 0x55, 0x80, 0xef, 0x78, 0xf3, + 0x45, 0x57, 0xf3, 0x30, 0xf5, 0x34, 0x96, 0x5f, 0xff, 0x0a, 0x23, 0x97, + 0x9e, 0x5c, 0xc6, 0x46, 0xea, 0xca, 0x84, 0x40, 0x99, 0x75, 0xee, 0x9c, + 0x2c, 0xbb, 0x09, 0x65, 0x19, 0xb0, 0x00, 0xdd, 0xf8, 0xa3, 0xe8, 0xe2, + 0xcb, 0xff, 0x61, 0x93, 0xf7, 0x8d, 0x41, 0x2c, 0xbd, 0xe7, 0xd4, 0x1f, + 0x1f, 0x09, 0xef, 0x8f, 0x51, 0xd5, 0x97, 0xdc, 0xc8, 0x92, 0xca, 0x19, + 0xe1, 0x11, 0x0d, 0xfa, 0x25, 0xec, 0xf9, 0x65, 0xf4, 0x8e, 0x02, 0xb2, + 0xef, 0xbf, 0x59, 0x7f, 0x32, 0x39, 0xf9, 0x85, 0x65, 0xee, 0x40, 0xbf, + 0x9e, 0x3f, 0x06, 0x6f, 0xf0, 0x33, 0x5f, 0xfe, 0x64, 0xb2, 0xdc, 0x59, + 0x74, 0xf3, 0xb2, 0xca, 0x91, 0xae, 0x71, 0x1b, 0xff, 0x63, 0x0c, 0x7a, + 0xc1, 0xbb, 0x16, 0x5f, 0x63, 0x04, 0x1a, 0xca, 0xf1, 0xf0, 0x11, 0xed, + 0xff, 0xbc, 0x27, 0x33, 0x41, 0x77, 0x0a, 0xa2, 0x0c, 0x5f, 0xfd, 0xb4, + 0x0b, 0x9a, 0xef, 0x63, 0x38, 0xb2, 0xc6, 0x34, 0x48, 0x92, 0x65, 0xff, + 0xbc, 0x07, 0x17, 0x3b, 0xdc, 0xf2, 0xcb, 0xfd, 0xe8, 0x2e, 0xfe, 0xfd, + 0x59, 0x7f, 0xfa, 0x5a, 0x89, 0x07, 0xc7, 0xe2, 0x76, 0x2c, 0xae, 0x1f, + 0xe0, 0x0c, 0xef, 0xfd, 0xcf, 0xdf, 0xee, 0x09, 0xaf, 0x3a, 0xcb, 0xe0, + 0x7b, 0x02, 0xb2, 0xa4, 0x9b, 0x40, 0xc9, 0xf5, 0x0b, 0xbe, 0x91, 0x4f, + 0xa1, 0x5f, 0xfd, 0xde, 0x6d, 0x02, 0x14, 0x78, 0xf6, 0x59, 0x7f, 0xb9, + 0x1a, 0x90, 0x4f, 0x65, 0x94, 0x47, 0xf5, 0xc4, 0x6b, 0xf8, 0x02, 0x96, + 0x6c, 0x05, 0x95, 0x07, 0xa1, 0xe2, 0x1b, 0xde, 0x06, 0xea, 0xcb, 0xff, + 0xda, 0x7d, 0xf8, 0x41, 0xf1, 0xfe, 0x27, 0x56, 0x54, 0x27, 0x57, 0x28, + 0xc7, 0x5b, 0x64, 0x20, 0x20, 0xbe, 0xcc, 0x21, 0x56, 0x57, 0xcb, 0xa8, + 0x9e, 0x35, 0x26, 0x2e, 0x42, 0x03, 0xb2, 0xf1, 0x77, 0x11, 0xef, 0x14, + 0x34, 0xb2, 0xf8, 0xc2, 0x63, 0x59, 0x58, 0x6f, 0x7a, 0x39, 0x7d, 0x1d, + 0x39, 0x96, 0x5f, 0xc0, 0x71, 0x00, 0xe2, 0x2c, 0xa9, 0x8f, 0x47, 0x44, + 0x57, 0x6f, 0xc5, 0x95, 0x86, 0xed, 0xc8, 0xee, 0xff, 0xf5, 0x97, 0xfc, + 0x1f, 0x1e, 0x88, 0xfb, 0xc5, 0x94, 0xc3, 0xd1, 0xfc, 0x66, 0xff, 0x7e, + 0x7e, 0xf6, 0x7e, 0xeb, 0x2f, 0xf7, 0x0f, 0x0b, 0x3e, 0x69, 0x65, 0xff, + 0xfe, 0xce, 0xf5, 0xf4, 0x51, 0xf4, 0x84, 0xd7, 0xec, 0x3e, 0x2c, 0xad, + 0xd4, 0x4b, 0x30, 0xce, 0xfc, 0x0f, 0x83, 0x8c, 0x59, 0x7f, 0xcc, 0x36, + 0xb0, 0x87, 0xe8, 0x59, 0x77, 0x80, 0xb2, 0xff, 0xfd, 0x23, 0x21, 0xfb, + 0x3e, 0x11, 0xcb, 0xa4, 0x35, 0x97, 0xfb, 0x07, 0xec, 0xfb, 0x38, 0xb2, + 0xf1, 0xb5, 0xba, 0xb2, 0xff, 0x66, 0xfe, 0x77, 0xb0, 0xc5, 0x95, 0xa3, + 0xd5, 0xf1, 0x05, 0x42, 0x73, 0xa3, 0x26, 0xc2, 0x93, 0x38, 0x98, 0x5c, + 0x95, 0x79, 0x08, 0x8b, 0x9a, 0x35, 0x97, 0xe6, 0x07, 0x08, 0x2b, 0x2a, + 0x0d, 0xf1, 0x8b, 0xdf, 0xfc, 0xfb, 0x71, 0xc7, 0x1b, 0x44, 0xa1, 0x65, + 0xfd, 0x9c, 0x9f, 0xcd, 0x42, 0xca, 0xd2, 0x24, 0xb8, 0x3f, 0x3e, 0x89, + 0x7c, 0x70, 0x53, 0x2c, 0xa9, 0x33, 0x30, 0xc7, 0x08, 0x3c, 0x74, 0x09, + 0x0f, 0xc5, 0x07, 0x3b, 0x59, 0x34, 0x29, 0x99, 0x0a, 0xc7, 0x70, 0xfc, + 0x8f, 0x92, 0x99, 0xfb, 0x19, 0xbb, 0x46, 0x77, 0xfb, 0x9c, 0xc2, 0xfe, + 0x7f, 0x16, 0x5f, 0x11, 0xee, 0x42, 0xcb, 0xe1, 0x0b, 0x38, 0xb2, 0xd3, + 0x2c, 0xbf, 0xb9, 0x85, 0xfc, 0xfe, 0x2c, 0xa9, 0xc8, 0xbd, 0xc3, 0x67, + 0x23, 0xfc, 0x8b, 0x82, 0x57, 0xdd, 0x77, 0x25, 0x95, 0xbc, 0xfa, 0xda, + 0x4b, 0xbf, 0xfd, 0xf4, 0x8f, 0x3c, 0xfe, 0xf4, 0x7d, 0xc5, 0x97, 0xfb, + 0xbf, 0x81, 0x91, 0x9d, 0x59, 0x4e, 0x7f, 0xe0, 0x4b, 0xbf, 0xfc, 0x5b, + 0x4e, 0x98, 0xcb, 0x07, 0x84, 0x2a, 0xcb, 0xf8, 0xfe, 0xdc, 0xf4, 0x75, + 0x65, 0xf8, 0x1c, 0x33, 0xf9, 0x65, 0xf8, 0xb3, 0xbe, 0x35, 0x97, 0x84, + 0xc8, 0x59, 0x7b, 0xb0, 0x22, 0xcb, 0xfd, 0x83, 0xd3, 0x87, 0xbf, 0xac, + 0xbf, 0x61, 0x0f, 0xd0, 0xb2, 0xb0, 0xfd, 0xfc, 0x3a, 0xd1, 0xa5, 0x42, + 0x71, 0xbb, 0xa9, 0x93, 0x18, 0xe8, 0xa1, 0xc9, 0xb9, 0x09, 0x3b, 0xe1, + 0xe9, 0xe4, 0xb2, 0xff, 0xff, 0x61, 0x3f, 0x79, 0x9a, 0xff, 0xf8, 0xcf, + 0xbb, 0xc3, 0x59, 0x76, 0x4f, 0xac, 0xa1, 0x9f, 0xd7, 0x58, 0x6a, 0x11, + 0x7c, 0xf0, 0x9c, 0xbd, 0x1a, 0x99, 0x65, 0x4e, 0x75, 0xcf, 0x2d, 0x84, + 0x08, 0xa4, 0x67, 0x6d, 0x19, 0xf8, 0xe1, 0x5f, 0x93, 0xbd, 0xa1, 0x95, + 0xc1, 0xf4, 0x21, 0x8e, 0x77, 0xe3, 0xd1, 0xb4, 0xbd, 0x34, 0x53, 0xf9, + 0x40, 0x65, 0x0a, 0x40, 0x47, 0x5c, 0xd4, 0x63, 0xb3, 0xe4, 0xd7, 0xbe, + 0xdb, 0x7a, 0xcb, 0xf0, 0x7d, 0x1f, 0x6e, 0x2c, 0xb8, 0x0d, 0xb5, 0x97, + 0xef, 0x3e, 0x98, 0x6b, 0x2f, 0x09, 0x1b, 0xab, 0x2f, 0xe1, 0x03, 0xe8, + 0xfb, 0x71, 0x65, 0x4e, 0x46, 0x0c, 0x8b, 0x02, 0x37, 0xc2, 0x70, 0x10, + 0x5d, 0xfb, 0x4b, 0x2f, 0xef, 0x41, 0xee, 0xb0, 0xd6, 0x54, 0xe3, 0xc8, + 0x08, 0xcd, 0x7c, 0x8b, 0x8e, 0xc2, 0x52, 0xfe, 0xc6, 0xb3, 0x08, 0x55, + 0x97, 0x9f, 0x38, 0xb2, 0xfc, 0xfb, 0x18, 0x24, 0xb2, 0xf7, 0xff, 0xc2, + 0xca, 0x61, 0xf0, 0xf8, 0x6c, 0x8a, 0x28, 0xd1, 0x78, 0xf0, 0x8e, 0xbf, + 0xfe, 0x0c, 0x73, 0x90, 0xcc, 0xe9, 0xf3, 0xce, 0xb2, 0xff, 0xe6, 0x47, + 0x78, 0xf9, 0xaf, 0xd9, 0x8b, 0x2f, 0xe2, 0x89, 0x32, 0x0d, 0x65, 0x7e, + 0x7d, 0xe4, 0x89, 0x7e, 0xcd, 0xe5, 0x9c, 0x59, 0x7f, 0xba, 0x7b, 0xcf, + 0xb2, 0x84, 0x96, 0x62, 0xcb, 0xfb, 0x08, 0x5d, 0x43, 0x73, 0x3c, 0x51, + 0x0d, 0x2b, 0x13, 0x92, 0x38, 0x5e, 0x91, 0x17, 0x1b, 0x6f, 0xff, 0x3e, + 0xf8, 0xc1, 0xbc, 0xb3, 0xc6, 0xd2, 0xcb, 0xc0, 0xfa, 0x4b, 0x2f, 0xa5, + 0xa7, 0xde, 0xb2, 0xa4, 0x88, 0xcd, 0x25, 0x10, 0xf5, 0xfe, 0x29, 0x16, + 0x32, 0x02, 0xb2, 0xfc, 0x7e, 0x28, 0x92, 0xcb, 0xcf, 0xc3, 0x59, 0x6c, + 0x59, 0x7b, 0x30, 0x55, 0x96, 0x60, 0xcd, 0x66, 0xe0, 0x85, 0x42, 0x3d, + 0xf0, 0xbf, 0x46, 0x3e, 0x26, 0x74, 0x7b, 0xfe, 0x3d, 0xf9, 0xa0, 0xbb, + 0x85, 0x51, 0x86, 0xaf, 0x4f, 0x83, 0xab, 0x2e, 0x3d, 0xeb, 0x2f, 0x9f, + 0x4e, 0x22, 0xcb, 0xb0, 0x55, 0x97, 0xff, 0x43, 0x0c, 0x78, 0x42, 0xfa, + 0x18, 0xb2, 0xbc, 0x7b, 0x5d, 0x17, 0xa1, 0xa6, 0x38, 0x14, 0x66, 0x10, + 0x70, 0x63, 0xaf, 0x97, 0x85, 0x13, 0xcb, 0x2f, 0xff, 0x8f, 0xbe, 0x8e, + 0x47, 0xa0, 0xf6, 0x81, 0x56, 0x5f, 0xef, 0x44, 0xd2, 0x3d, 0x49, 0x65, + 0xf8, 0xa6, 0x28, 0x62, 0xcb, 0xfd, 0xd3, 0x6a, 0x62, 0x76, 0x96, 0x5f, + 0xf6, 0x6f, 0x81, 0xfb, 0x0f, 0xab, 0x2f, 0x71, 0xff, 0x59, 0x6e, 0x91, + 0xeb, 0x74, 0xe6, 0xfe, 0x00, 0x9f, 0xff, 0x1f, 0x2c, 0xbf, 0x43, 0x3b, + 0x81, 0x59, 0x7e, 0x89, 0x7e, 0x58, 0xb2, 0xe3, 0xd9, 0x65, 0x42, 0x7e, + 0x63, 0x4f, 0xc3, 0x4f, 0x89, 0xce, 0x11, 0x73, 0x13, 0xf8, 0xc8, 0x89, + 0xfa, 0x4f, 0x7e, 0x7e, 0xea, 0x37, 0xac, 0xbf, 0xff, 0x88, 0x20, 0xf0, + 0xa6, 0x51, 0xe8, 0x6a, 0x0b, 0x16, 0x5f, 0x18, 0xf7, 0x0d, 0x65, 0x42, + 0x29, 0x9c, 0xa5, 0xa5, 0x9b, 0xfe, 0xf4, 0x48, 0xcb, 0x19, 0x0b, 0x2f, + 0xfb, 0x3b, 0xec, 0x61, 0x3f, 0x56, 0x5f, 0xdb, 0xcf, 0x5f, 0x46, 0xea, + 0xcb, 0x10, 0xcf, 0xab, 0x0d, 0xee, 0x2f, 0xd6, 0x5f, 0x99, 0x01, 0xd8, + 0x6b, 0x2f, 0xa5, 0x0c, 0x75, 0x97, 0xed, 0x7e, 0xc3, 0xe2, 0xcb, 0xbb, + 0xc6, 0xca, 0x22, 0xb0, 0x5d, 0xca, 0x44, 0x21, 0xa8, 0x47, 0xe0, 0x21, + 0x61, 0x6e, 0x2c, 0xbf, 0xa3, 0x6c, 0x23, 0x15, 0x65, 0x68, 0xdf, 0x70, + 0x46, 0xff, 0xe8, 0x27, 0xf4, 0x7e, 0xdf, 0x5c, 0xe2, 0x4b, 0xd8, 0x18, + 0x59, 0x5b, 0x2a, 0x83, 0x1c, 0x73, 0x98, 0xca, 0xc2, 0x1d, 0xe8, 0xd7, + 0x99, 0x01, 0x59, 0x7e, 0x79, 0x8f, 0x38, 0xb2, 0xfb, 0x91, 0xa9, 0x2c, + 0xb0, 0xe7, 0x1e, 0x4f, 0x09, 0xea, 0x11, 0x1e, 0xec, 0xb7, 0xfc, 0x43, + 0xf4, 0x77, 0xb0, 0xc5, 0x97, 0x03, 0x65, 0x97, 0xfb, 0xe9, 0x7d, 0xc3, + 0xec, 0x2c, 0xbc, 0x7f, 0x71, 0x65, 0x61, 0xe9, 0x19, 0xad, 0x62, 0x30, + 0x8c, 0xe3, 0xcd, 0x57, 0x60, 0x56, 0x5d, 0x9c, 0x59, 0xd2, 0xda, 0xf7, + 0x73, 0xcb, 0x2b, 0x47, 0x82, 0x02, 0x5b, 0xff, 0xe1, 0xce, 0x73, 0xce, + 0x46, 0xd8, 0x46, 0x2a, 0xcb, 0xfd, 0xa6, 0x47, 0xdd, 0x70, 0xac, 0xbf, + 0xff, 0x8b, 0x3b, 0xec, 0x96, 0x06, 0x03, 0xd2, 0x76, 0x2c, 0xac, 0x46, + 0xe1, 0xa8, 0x70, 0xd2, 0xff, 0xda, 0x39, 0x1f, 0x3e, 0x91, 0x92, 0xcb, + 0xe3, 0x64, 0x4c, 0xb2, 0xff, 0xee, 0x1f, 0xc5, 0x93, 0x6a, 0x3b, 0xc5, + 0x97, 0xd9, 0xb0, 0x24, 0xb2, 0x8d, 0x19, 0x13, 0x1f, 0x7e, 0x44, 0xd2, + 0x2d, 0xfe, 0xe4, 0x49, 0xf6, 0x76, 0x2c, 0xbe, 0x7d, 0x3f, 0xcb, 0x2f, + 0xef, 0xf6, 0x79, 0x46, 0xea, 0xcb, 0xc3, 0x1c, 0x2c, 0xa1, 0x4f, 0xc3, + 0xe2, 0x20, 0x19, 0x54, 0x26, 0x26, 0x67, 0xef, 0x0a, 0x3b, 0xb1, 0xa5, + 0x94, 0x9c, 0x43, 0x0b, 0xfb, 0x82, 0x46, 0xa1, 0x89, 0xc4, 0x30, 0xa4, + 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x29, 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, + 0x92, 0x2e, 0x4c, 0x67, 0xf3, 0xc0, 0x0c, 0xcf, 0x8c, 0xee, 0x0c, 0xdd, + 0xec, 0x4e, 0x21, 0x85, 0xfc, 0xef, 0xd9, 0xa1, 0x89, 0xc4, 0x30, 0x9c, + 0x69, 0x2c, 0xdb, 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, 0x93, 0x88, 0x61, + 0x52, 0x36, 0x46, 0x33, 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x52, 0x71, + 0x0c, 0x29, 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x6c, + 0x89, 0x61, 0x8c, 0x98, 0xcf, 0xe3, 0x3c, 0x19, 0xde, 0x33, 0x49, 0xc4, + 0x30, 0xa4, 0xe2, 0x18, 0x54, 0x8d, 0x97, 0x06, 0x69, 0x38, 0x86, 0x14, + 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, 0xa4, 0x7c, 0x9f, + 0x8c, 0xf4, 0x67, 0x70, 0x66, 0x93, 0x88, 0x61, 0x49, 0xc4, 0x30, 0xa4, + 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x2b, 0x63, 0xe4, 0x14, 0x67, 0x43, 0x3e, + 0x19, 0xb0, 0xa9, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x29, + 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, 0x19, 0xf2, 0x7c, 0x33, 0xf8, 0xc8, + 0x06, 0x69, 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0xfb, + 0xd1, 0xff, 0x31, 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, 0x92, 0x26, 0x82, + 0x33, 0xe1, 0x97, 0x19, 0xfc, 0xda, 0xc1, 0x4e, 0x21, 0x85, 0x27, 0x10, + 0xc2, 0x93, 0x88, 0x61, 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x54, 0x8f, + 0x93, 0xe1, 0x93, 0x19, 0xde, 0x33, 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, + 0x52, 0x71, 0x0c, 0x29, 0x38, 0x86, 0x15, 0x23, 0xe4, 0x18, 0xcf, 0x86, + 0x48, 0x66, 0xde, 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, 0x93, 0x88, 0x61, + 0x69, 0x27, 0x10, 0xc2, 0x93, 0x88, 0x61, 0xf1, 0x7f, 0x49, 0xc4, 0x30, + 0xa4, 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x29, 0x38, 0x86, 0x15, 0xb2, 0x37, + 0xa4, 0x32, 0x29, 0xc0, 0x4a, 0x66, 0x19, 0xfc, 0x67, 0xa3, 0x36, 0xc4, + 0xe2, 0x18, 0x52, 0x71, 0x0c, 0x29, 0x38, 0x86, 0x16, 0x92, 0x71, 0x0c, + 0x29, 0x38, 0x86, 0x1f, 0x17, 0xf4, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, + 0x42, 0x2b, 0x24, 0x32, 0x67, 0x1a, 0x29, 0x60, 0xcd, 0x27, 0x10, 0xc2, + 0x93, 0x88, 0x61, 0x49, 0xc4, 0x30, 0xa4, 0xe2, 0x18, 0x52, 0x71, 0x0c, + 0x2a, 0x0f, 0xf7, 0xe1, 0x9d, 0x0c, 0xb0, 0x64, 0x86, 0x69, 0x38, 0x86, + 0x14, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x68, 0xf2, 0xf8, 0x33, 0xd1, + 0x9a, 0x4e, 0x21, 0x85, 0x27, 0x10, 0xc2, 0x93, 0x88, 0x61, 0x4c, 0x3c, + 0xb2, 0x19, 0xe8, 0xcd, 0x98, 0x9c, 0x43, 0x0a, 0x4e, 0x21, 0x85, 0x27, + 0x10, 0xc2, 0xbf, 0x36, 0x40, 0x19, 0xa4, 0xe2, 0x18, 0x52, 0x71, 0x0c, + 0x29, 0x38, 0x86, 0x14, 0x9c, 0x43, 0x0a, 0x83, 0xe4, 0x98, 0x67, 0xc3, + 0x20, 0x19, 0xa8, 0x66, 0x0a, 0x6d, 0x08, 0x19, 0x26, 0x8d, 0x57, 0x21, + 0x6c, 0x18, 0x46, 0x7d, 0x08, 0x53, 0x85, 0x26, 0xe9, 0xf4, 0xd0, 0x84, + 0xd4, 0x36, 0x18, 0x95, 0xe8, 0x41, 0xbc, 0x28, 0xbf, 0x3b, 0x28, 0x7e, + 0xf1, 0xa3, 0xb0, 0xca, 0x04, 0x31, 0x77, 0xc2, 0x4d, 0xa7, 0xa9, 0xf2, + 0xe1, 0x14, 0x77, 0x21, 0x0d, 0x7e, 0x91, 0x46, 0x71, 0x38, 0x86, 0x0d, + 0xd3, 0x97, 0xbc, 0x70, 0xc4, 0xe2, 0x18, 0x5f, 0x18, 0x5f, 0x4b, 0xc4, + 0x31, 0x78, 0xc7, 0x8b, 0xc4, 0x31, 0x66, 0xff, 0x23, 0x23, 0x49, 0x3e, + 0x29, 0xe9, 0x75, 0xa3, 0x7b, 0x3e, 0x24, 0x4a, 0x7a, 0x1d, 0xe3, 0x2d, + 0x96, 0x5f, 0xc6, 0xd0, 0xf5, 0x1b, 0x2c, 0xa9, 0x1e, 0x5f, 0x87, 0x2e, + 0x7f, 0x96, 0x5f, 0x44, 0xfc, 0x75, 0x65, 0xe3, 0xde, 0xdb, 0x59, 0x5f, + 0x26, 0x05, 0x34, 0x20, 0xfc, 0x44, 0x42, 0xfd, 0x24, 0xbf, 0x66, 0x4c, + 0xe2, 0x2c, 0xbf, 0xce, 0x42, 0x4d, 0x23, 0x25, 0x97, 0x7f, 0x0b, 0x2f, + 0xed, 0x39, 0x09, 0x1f, 0xac, 0xb9, 0xf4, 0xb2, 0xc6, 0xb0, 0x25, 0xb5, + 0x82, 0xb2, 0xcc, 0x59, 0x66, 0xe6, 0x89, 0xcd, 0x0b, 0xf9, 0x11, 0xc7, + 0xc4, 0x11, 0xbf, 0xbe, 0x97, 0xa0, 0xb6, 0x59, 0x4c, 0x4d, 0x3e, 0x7e, + 0x19, 0xfb, 0x8a, 0x37, 0xfd, 0xe8, 0x66, 0x4d, 0x05, 0xb2, 0xcb, 0xf6, + 0x33, 0xd1, 0xfa, 0xcb, 0xff, 0xa7, 0x38, 0x47, 0xe8, 0xff, 0x0b, 0xf5, + 0x97, 0x7f, 0x0b, 0x2b, 0x0f, 0x7b, 0xf4, 0x7a, 0xc4, 0xc7, 0x7f, 0x3c, + 0xe9, 0xc8, 0x21, 0x0d, 0x7f, 0xf1, 0x14, 0x6c, 0x58, 0x7b, 0xe2, 0x7d, + 0x65, 0xe8, 0xfb, 0x8b, 0x29, 0xcf, 0x8f, 0x88, 0xd7, 0x9f, 0x50, 0xb2, + 0xfc, 0x73, 0xee, 0x5f, 0xac, 0xbf, 0x1f, 0xdc, 0x7f, 0x96, 0x54, 0x8f, + 0xb9, 0x83, 0x7c, 0x2a, 0xbe, 0xde, 0xc8, 0x35, 0x97, 0xfc, 0x23, 0x8f, + 0x98, 0xc8, 0xdd, 0x59, 0x7b, 0x3e, 0x69, 0x65, 0xdf, 0xe6, 0x8f, 0x68, + 0x8f, 0x2f, 0xff, 0x1e, 0xe8, 0x05, 0x3f, 0xb9, 0x93, 0x47, 0x56, 0x57, + 0xc7, 0xf8, 0xc2, 0xdb, 0x81, 0x0b, 0x2f, 0x66, 0xa4, 0xb2, 0x86, 0x6c, + 0xb7, 0x8b, 0x5e, 0x1b, 0x92, 0xca, 0x33, 0x7c, 0xe4, 0x77, 0xe8, 0x0b, + 0x90, 0xab, 0x2f, 0xfe, 0x7d, 0x69, 0xff, 0x86, 0xed, 0x34, 0xd2, 0xcb, + 0xff, 0x0b, 0x9f, 0x74, 0xfb, 0xa0, 0x31, 0x65, 0xa2, 0x72, 0x22, 0xfa, + 0x95, 0x50, 0xaa, 0xdb, 0x0b, 0xde, 0x1f, 0x3c, 0x84, 0x9f, 0x47, 0xc1, + 0x0b, 0x2b, 0xff, 0x80, 0x24, 0xd3, 0xa0, 0x47, 0x0c, 0x79, 0x65, 0xff, + 0xdb, 0x91, 0xa3, 0x67, 0x31, 0x91, 0xba, 0xb2, 0x9b, 0x2e, 0xad, 0xae, + 0x23, 0x9a, 0xda, 0x1c, 0xb2, 0x8e, 0xe0, 0x71, 0xe1, 0xe4, 0x6c, 0x62, + 0xa5, 0x86, 0x50, 0x97, 0xd1, 0x94, 0x1c, 0xab, 0x2d, 0xd8, 0x71, 0xcd, + 0x0e, 0x3d, 0x42, 0x15, 0x90, 0xfe, 0xf4, 0xa0, 0x97, 0xac, 0x2f, 0xbf, + 0x51, 0x29, 0x52, 0x9c, 0x85, 0x37, 0x65, 0xa2, 0x88, 0xcb, 0xb8, 0x93, + 0x79, 0x9c, 0x0a, 0xcb, 0xc4, 0x18, 0x59, 0x53, 0x8d, 0xc1, 0x8e, 0xdf, + 0x4b, 0x35, 0x0b, 0x2f, 0xe6, 0x9f, 0x5a, 0x7f, 0x96, 0x5f, 0xff, 0xb9, + 0xad, 0x1c, 0xdc, 0xd4, 0x74, 0xc2, 0x63, 0x59, 0x4c, 0x46, 0x47, 0xe4, + 0x44, 0x42, 0xd1, 0x7d, 0xee, 0xc7, 0x16, 0x5e, 0xfe, 0x7c, 0x96, 0x53, + 0x0d, 0xe1, 0x0e, 0x5e, 0xd8, 0xc6, 0xb2, 0xc0, 0x59, 0x6f, 0xcc, 0xd7, + 0x10, 0xed, 0x9b, 0x2b, 0x2f, 0xb4, 0x64, 0x2a, 0xcb, 0xfd, 0xd8, 0x00, + 0x7c, 0x7a, 0x59, 0x7f, 0x60, 0x7d, 0x1f, 0x49, 0x65, 0xfc, 0xce, 0x18, + 0xbd, 0x69, 0x65, 0xfa, 0x3e, 0xf4, 0x49, 0x65, 0x4f, 0x68, 0xf0, 0xd0, + 0xa9, 0x11, 0x74, 0xcc, 0x42, 0xed, 0xc3, 0x0b, 0xfe, 0xcf, 0x8b, 0x3b, + 0xdc, 0xd9, 0x65, 0xdf, 0x75, 0x65, 0xfb, 0x0b, 0x63, 0xd2, 0xcb, 0xf7, + 0x5c, 0x81, 0x8b, 0x2f, 0xf7, 0x31, 0x87, 0xdf, 0x3a, 0xcb, 0x46, 0x22, + 0x5f, 0x43, 0x04, 0x4f, 0xd2, 0x6b, 0xd3, 0x98, 0x6b, 0x2e, 0x26, 0x2c, + 0xa9, 0x1b, 0x4e, 0x0f, 0x5e, 0xdc, 0xd4, 0x2c, 0xbf, 0xef, 0x1e, 0xbc, + 0xed, 0x96, 0x9a, 0x59, 0x7f, 0xf8, 0x9d, 0xae, 0x16, 0x7d, 0x2e, 0x83, + 0x65, 0x97, 0xfb, 0x47, 0x31, 0x66, 0xf7, 0x59, 0x6e, 0x4e, 0x47, 0xfc, + 0x10, 0xe0, 0xfc, 0xc7, 0xfa, 0x4c, 0xbf, 0xda, 0x2c, 0x1f, 0xb0, 0x96, + 0x5f, 0x39, 0x03, 0x16, 0x5f, 0x87, 0xce, 0x43, 0x16, 0x57, 0xc8, 0xca, + 0x75, 0x1f, 0xcc, 0x48, 0x82, 0xff, 0x8a, 0x1f, 0x6f, 0x46, 0xbf, 0x59, + 0x7f, 0x8f, 0x45, 0xde, 0xe6, 0xcb, 0x2e, 0x7d, 0xeb, 0x2f, 0xe9, 0x11, + 0xcb, 0xd8, 0xb2, 0xfd, 0xb1, 0xea, 0x24, 0xb2, 0x85, 0x44, 0xfc, 0xc6, + 0x7a, 0x18, 0x22, 0xcb, 0xf0, 0x3b, 0x3f, 0x9d, 0x59, 0x78, 0x66, 0x2a, + 0xca, 0x83, 0xc8, 0xc2, 0xbb, 0xf9, 0xfc, 0x26, 0x9f, 0xf5, 0x97, 0xb5, + 0xf8, 0x8b, 0x2f, 0xfe, 0x3e, 0xc7, 0x79, 0xe7, 0x3f, 0x01, 0x65, 0x41, + 0xf1, 0x38, 0xfd, 0xf1, 0x10, 0x24, 0xb2, 0xff, 0x10, 0x39, 0x8c, 0x80, + 0xac, 0xbf, 0xf7, 0xa3, 0x5f, 0x96, 0x34, 0xff, 0xac, 0xbd, 0x99, 0x32, + 0xcb, 0x60, 0xa8, 0x8e, 0xf1, 0x97, 0xe8, 0x17, 0xa3, 0xe6, 0x96, 0x5f, + 0xb4, 0x06, 0x39, 0x2c, 0xae, 0x9e, 0x28, 0x07, 0xaf, 0x99, 0x19, 0xb2, + 0xcb, 0xf6, 0x6f, 0x28, 0xf9, 0x65, 0x19, 0xe5, 0xf0, 0x8a, 0xfa, 0x0b, + 0xa1, 0x59, 0x43, 0x5c, 0x09, 0xc8, 0x6c, 0x6a, 0x10, 0xcc, 0x20, 0xf4, + 0x24, 0x5c, 0x80, 0xa1, 0x5f, 0xc7, 0xce, 0xb6, 0xcf, 0x90, 0xdf, 0xa7, + 0xfd, 0x9f, 0xba, 0xcb, 0xfd, 0xf8, 0x98, 0x43, 0xf4, 0x2c, 0xa8, 0x4c, + 0x05, 0x90, 0xa2, 0x72, 0xbb, 0xfb, 0x98, 0xc3, 0xf4, 0x2c, 0xbd, 0xf4, + 0xb8, 0xb2, 0xbe, 0x3c, 0xad, 0x16, 0x5e, 0xe7, 0xed, 0x2c, 0xbe, 0xfb, + 0x91, 0xf2, 0xcb, 0xf9, 0xe5, 0xde, 0x40, 0xab, 0x2d, 0x23, 0x3d, 0x26, + 0x12, 0x54, 0x22, 0xef, 0x08, 0xdd, 0xc6, 0xff, 0x9a, 0x2c, 0xd7, 0x3d, + 0x81, 0x59, 0x7f, 0xf1, 0x67, 0x30, 0x73, 0x4a, 0x35, 0xb2, 0xca, 0x33, + 0xfe, 0xe9, 0xcd, 0xf6, 0x0c, 0xc9, 0x65, 0xf0, 0x1f, 0x52, 0x59, 0x7d, + 0x00, 0x6d, 0xf1, 0x25, 0xfa, 0x4d, 0xda, 0x69, 0xa5, 0x94, 0x47, 0xa8, + 0x02, 0x6b, 0xff, 0xff, 0x10, 0x44, 0xe9, 0x67, 0xee, 0x5f, 0xf4, 0x9f, + 0xf6, 0xb3, 0xcb, 0x2f, 0x9f, 0xac, 0xea, 0xcb, 0xf7, 0x35, 0x0e, 0x15, + 0x97, 0xe8, 0x20, 0xc6, 0xf5, 0x95, 0x87, 0x9f, 0xe2, 0x7a, 0x9c, 0x9d, + 0xb8, 0xc8, 0x31, 0xd9, 0xc8, 0x7f, 0x6e, 0xeb, 0x8d, 0xfe, 0xfc, 0xb3, + 0x7e, 0x8f, 0x8b, 0x2f, 0xf0, 0x84, 0x4e, 0xd3, 0xf9, 0x65, 0x43, 0x3c, + 0x3b, 0x1e, 0xbe, 0x4e, 0x38, 0xc5, 0xb4, 0xbe, 0xc8, 0x68, 0x3c, 0xa3, + 0xdf, 0xe7, 0x51, 0xca, 0x33, 0x9e, 0x42, 0xac, 0x11, 0xec, 0x6f, 0x59, + 0x68, 0xd6, 0xf3, 0x62, 0x78, 0xb6, 0x56, 0x5e, 0x91, 0x3a, 0xcb, 0xfd, + 0xd8, 0x67, 0xd2, 0xce, 0xac, 0xb7, 0xcd, 0x83, 0xfa, 0x82, 0xbc, 0x1b, + 0xbf, 0x6e, 0x44, 0xb5, 0x0b, 0x2f, 0x75, 0xff, 0x59, 0x7e, 0xd6, 0xd1, + 0xad, 0x96, 0x5f, 0xd9, 0xde, 0x00, 0xba, 0xb2, 0xa0, 0xf5, 0xf0, 0xaa, + 0xff, 0xd0, 0x5d, 0xf0, 0x1e, 0x52, 0x85, 0x97, 0x0b, 0xb2, 0xcb, 0xf0, + 0x3f, 0xc2, 0x0a, 0xcb, 0xda, 0x89, 0x96, 0x5e, 0x27, 0x99, 0x65, 0xfb, + 0x8d, 0x3f, 0xdc, 0x59, 0x46, 0x78, 0xce, 0x39, 0x7d, 0xce, 0xc3, 0x16, + 0x5f, 0xf8, 0xa0, 0x5f, 0x3f, 0x44, 0xcf, 0xd6, 0x5e, 0xe6, 0xb1, 0x65, + 0xe2, 0xc9, 0x2c, 0xbf, 0x6b, 0xf2, 0x07, 0xeb, 0x2a, 0x0f, 0x17, 0x06, + 0xe8, 0xd1, 0x03, 0xc6, 0x1b, 0x6e, 0xac, 0xbf, 0xb0, 0x48, 0x98, 0xf8, + 0xb2, 0x88, 0xf0, 0xba, 0x29, 0x50, 0xac, 0x9f, 0x62, 0xa9, 0x3a, 0x0c, + 0x83, 0x0f, 0x74, 0x32, 0xc2, 0x8f, 0x31, 0xfe, 0x40, 0x44, 0x5d, 0x86, + 0x0e, 0xf6, 0x4b, 0xfe, 0x83, 0xf4, 0x33, 0x6c, 0x69, 0x65, 0xff, 0xb9, + 0x06, 0x32, 0x89, 0x1f, 0xeb, 0x2f, 0xde, 0x3d, 0x9c, 0x96, 0x5f, 0x80, + 0x2b, 0x97, 0xeb, 0x2f, 0xee, 0x6a, 0x30, 0x66, 0xb2, 0xef, 0x1a, 0xca, + 0x84, 0x49, 0x78, 0x9f, 0x79, 0x4e, 0xe1, 0x65, 0xff, 0x6c, 0x59, 0x2f, + 0x67, 0xfc, 0x59, 0x7f, 0xec, 0x2f, 0xa5, 0xcd, 0x02, 0x3f, 0x59, 0x7e, + 0xce, 0xf3, 0x05, 0x59, 0x46, 0x7d, 0x0c, 0x40, 0xbf, 0xf6, 0xbc, 0xd3, + 0x8f, 0x7f, 0x70, 0x2b, 0x2f, 0xdd, 0x9d, 0xec, 0xdc, 0x59, 0x4e, 0x7d, + 0xe4, 0x85, 0x78, 0x71, 0xbd, 0x65, 0xd1, 0x32, 0xcb, 0xff, 0x8a, 0x1a, + 0x19, 0x47, 0xd2, 0x32, 0x59, 0x5a, 0x3d, 0xa2, 0x17, 0xbf, 0x33, 0x3c, + 0xfb, 0x8b, 0x2b, 0x0f, 0x2c, 0xc8, 0x6f, 0xdf, 0x04, 0xfb, 0xc5, 0x97, + 0xff, 0x4e, 0x9a, 0x51, 0xad, 0xa7, 0x35, 0x9e, 0x59, 0x50, 0x7e, 0xae, + 0x53, 0x50, 0x9c, 0xc0, 0xc8, 0x3d, 0x0c, 0x32, 0x84, 0xad, 0xff, 0x33, + 0xd9, 0xdc, 0x06, 0xb6, 0x59, 0x7f, 0x8a, 0x25, 0x37, 0xa3, 0x65, 0x97, + 0xfe, 0xe1, 0x94, 0xd0, 0x1d, 0x67, 0xcb, 0x28, 0x67, 0xe7, 0xd3, 0x4b, + 0xf0, 0xc5, 0x14, 0x1f, 0x2c, 0xb0, 0xae, 0x79, 0xc0, 0x22, 0xbf, 0xe8, + 0xee, 0x68, 0x2e, 0xe1, 0x54, 0x5f, 0x0b, 0xf6, 0x6a, 0x3e, 0x92, 0xcb, + 0xa1, 0x81, 0x3e, 0xce, 0xa2, 0x5f, 0xf3, 0x8b, 0xc3, 0xe1, 0xfd, 0xd5, + 0x95, 0xe3, 0xe7, 0x22, 0xdb, 0xff, 0xf1, 0x06, 0x77, 0xa0, 0xf4, 0x6c, + 0x39, 0xf8, 0x62, 0xcb, 0xcf, 0xad, 0x95, 0x17, 0xfa, 0xff, 0xc5, 0xdd, + 0x64, 0x7d, 0x23, 0x62, 0xca, 0x84, 0x70, 0xe1, 0x08, 0x56, 0x1c, 0xaa, + 0xf7, 0x4d, 0xa5, 0x97, 0xf3, 0xec, 0x20, 0xdf, 0x75, 0x65, 0x74, 0xf3, + 0xc0, 0x3b, 0x7f, 0xb5, 0xac, 0x99, 0x91, 0xc5, 0x97, 0xfc, 0xfd, 0xe1, + 0x97, 0xd2, 0x62, 0xcb, 0xfe, 0x3d, 0x6c, 0x0f, 0xff, 0x8e, 0xac, 0xbf, + 0xe8, 0xcd, 0x4e, 0x91, 0xfd, 0x25, 0x95, 0x24, 0xc1, 0xb0, 0x8b, 0xf3, + 0x4e, 0x1c, 0xf4, 0xf2, 0xff, 0xff, 0x79, 0xf8, 0x71, 0xc9, 0xdd, 0xf6, + 0x33, 0x02, 0xfa, 0x59, 0x74, 0x6e, 0x2c, 0xbe, 0xe7, 0x1d, 0x8b, 0x2b, + 0xc8, 0x9c, 0xfd, 0x87, 0xa3, 0x37, 0xfb, 0xee, 0x89, 0xef, 0x66, 0xea, + 0xcb, 0xf6, 0x1e, 0xd0, 0x2a, 0xcb, 0x6d, 0x87, 0xc3, 0xe3, 0x9b, 0xef, + 0xa5, 0xd7, 0x59, 0x7f, 0xfc, 0x30, 0xf8, 0xcb, 0x37, 0xbc, 0xb8, 0x63, + 0x59, 0x7f, 0x73, 0xa5, 0x19, 0xf2, 0xca, 0xc4, 0x50, 0x39, 0x18, 0x14, + 0x2f, 0xdf, 0xf3, 0xb9, 0xe5, 0x97, 0xf3, 0x5d, 0xcf, 0x4b, 0x16, 0x54, + 0x1e, 0xbb, 0x94, 0xd4, 0x26, 0xdb, 0x28, 0x64, 0x14, 0x21, 0x6f, 0xe9, + 0xc3, 0xcc, 0xff, 0x8b, 0x2f, 0xff, 0xdd, 0xcd, 0x6c, 0x7c, 0x3d, 0xfe, + 0x3c, 0xfb, 0xab, 0x2d, 0x0c, 0x44, 0x41, 0x18, 0x5f, 0xb9, 0x8c, 0x7f, + 0x96, 0x5e, 0x66, 0xb6, 0x59, 0x58, 0x78, 0xc6, 0x51, 0x7f, 0xee, 0xc7, + 0x39, 0x07, 0xb3, 0xf5, 0x65, 0xff, 0x1e, 0x10, 0xc5, 0x3c, 0xd2, 0xca, + 0x34, 0xc0, 0xb4, 0xdb, 0xd2, 0x0d, 0xc3, 0xeb, 0xfe, 0xe6, 0x6d, 0xc3, + 0x27, 0xd9, 0x65, 0xd9, 0xf2, 0xca, 0xc3, 0xd0, 0xe9, 0xcd, 0xfc, 0xfa, + 0xfa, 0x59, 0xd5, 0x97, 0xfe, 0xef, 0x0f, 0x5d, 0x20, 0x3c, 0x96, 0x5f, + 0xff, 0x8f, 0x98, 0x3f, 0x67, 0x4f, 0x39, 0xb6, 0x34, 0xb2, 0xff, 0xf3, + 0x22, 0x7f, 0x00, 0x59, 0x9b, 0xe3, 0xe5, 0x97, 0xfe, 0x8c, 0xd6, 0x6b, + 0xf6, 0x1f, 0x16, 0x5e, 0x7f, 0xb7, 0x16, 0x5f, 0xcf, 0xde, 0x46, 0x6c, + 0xb2, 0xa1, 0x39, 0x9c, 0x2e, 0x09, 0xf1, 0xaa, 0x92, 0x6e, 0xf3, 0xe6, + 0x88, 0x2f, 0xdf, 0xbf, 0xd2, 0xc5, 0x97, 0xfa, 0x3e, 0xe1, 0x97, 0xd2, + 0x59, 0x50, 0x7b, 0xac, 0x29, 0xbf, 0xe0, 0x43, 0x39, 0x8c, 0x8d, 0xd5, + 0x97, 0xfe, 0xd6, 0xd8, 0x3c, 0xf7, 0xb3, 0x75, 0x65, 0x85, 0x59, 0x7f, + 0xbb, 0xc1, 0x37, 0xc6, 0x7c, 0xb2, 0xa0, 0xf2, 0x48, 0x4a, 0xff, 0xda, + 0xe3, 0x04, 0xcd, 0x1c, 0x4c, 0xb2, 0xb0, 0xf8, 0x00, 0x41, 0x58, 0x98, + 0x67, 0x21, 0xd7, 0x7f, 0x71, 0xe4, 0x50, 0xc5, 0x97, 0x8a, 0x37, 0x56, + 0x5f, 0xff, 0x4a, 0x35, 0xb7, 0x4c, 0x5e, 0x71, 0xca, 0x4b, 0x2f, 0xed, + 0xb9, 0x8c, 0x8d, 0xd5, 0x95, 0x32, 0x20, 0x84, 0x51, 0xad, 0x23, 0xc7, + 0x85, 0x8d, 0x42, 0x82, 0xf1, 0xf8, 0xd6, 0x5c, 0x39, 0x2c, 0xbc, 0x59, + 0x25, 0x96, 0x96, 0x8d, 0x97, 0x45, 0xef, 0x99, 0xd3, 0xd9, 0x65, 0x61, + 0xe5, 0x19, 0x3d, 0xff, 0xdb, 0xfa, 0x2c, 0x14, 0xb3, 0xc6, 0xc5, 0x97, + 0x6d, 0xd5, 0x97, 0xfb, 0x7f, 0x9e, 0x51, 0x84, 0xb2, 0xa0, 0xf2, 0xf0, + 0x62, 0xfa, 0x4f, 0xa9, 0x2c, 0xbf, 0xfd, 0x1a, 0x7e, 0x83, 0xa7, 0xde, + 0x78, 0xd6, 0x5a, 0x53, 0x1f, 0x5f, 0x88, 0xaf, 0xec, 0x68, 0x4f, 0x3f, + 0x56, 0x5f, 0xd2, 0x67, 0x48, 0x13, 0x2c, 0xbf, 0xfe, 0x12, 0x51, 0xbc, + 0xcb, 0xfd, 0x1c, 0x33, 0x12, 0x56, 0x91, 0x01, 0xd2, 0xfb, 0xef, 0x09, + 0xf7, 0x56, 0x5f, 0xb1, 0x98, 0x7b, 0xd6, 0x5e, 0xda, 0x6d, 0xd5, 0x96, + 0x69, 0x65, 0x42, 0x6f, 0x9b, 0x14, 0xe4, 0x2b, 0xc2, 0x46, 0x64, 0xa0, + 0x28, 0x9f, 0x22, 0xbb, 0xb8, 0xb2, 0xfd, 0xa3, 0xd9, 0xd8, 0xa9, 0x84, + 0xd7, 0xdd, 0x04, 0x3a, 0xa6, 0x13, 0x5d, 0xfc, 0x2a, 0x81, 0x35, 0xfe, + 0x27, 0x17, 0xb1, 0xaf, 0xd5, 0x40, 0x9a, 0xff, 0x73, 0x3c, 0x7c, 0x06, + 0xca, 0x98, 0x4d, 0x76, 0x0d, 0x53, 0x09, 0xae, 0x69, 0xa5, 0xcc, 0x26, + 0xac, 0x4d, 0x4b, 0xe3, 0x53, 0x2e, 0x61, 0x27, 0x10, 0x77, 0xa0, 0xb4, + 0x47, 0x6e, 0xa6, 0x61, 0x31, 0xb9, 0xf3, 0xd7, 0xca, 0x80, 0x5a, 0x8f, + 0x3e, 0xfb, 0x06, 0x02, 0x59, 0x46, 0x79, 0xce, 0x5b, 0x7f, 0xfe, 0xfd, + 0x8f, 0x2e, 0x7a, 0x37, 0xe0, 0x83, 0x79, 0x2c, 0xa8, 0x5e, 0xb8, 0x33, + 0x6f, 0x42, 0x5d, 0xc8, 0x0a, 0x12, 0x3c, 0x94, 0x0f, 0xd9, 0x57, 0x40, + 0x20, 0xbf, 0xfb, 0x52, 0x27, 0x16, 0x69, 0x1e, 0xa4, 0xb2, 0xf9, 0xf7, + 0xe3, 0x4b, 0x2f, 0xff, 0xde, 0x79, 0x7a, 0x20, 0xfb, 0xc3, 0x2f, 0xa4, + 0xb2, 0xf1, 0x7e, 0xc5, 0x97, 0xfd, 0x00, 0xe9, 0x1e, 0xf1, 0x18, 0xb2, + 0xec, 0xe6, 0x1e, 0xc6, 0x87, 0x6b, 0x13, 0x42, 0xd2, 0x33, 0x92, 0x14, + 0x2b, 0x2f, 0xb0, 0xf4, 0x2a, 0xcb, 0xb6, 0xd2, 0xcb, 0xbf, 0x99, 0x65, + 0xff, 0xcc, 0x9a, 0x4e, 0x5b, 0x0e, 0x0b, 0x65, 0x95, 0x07, 0xee, 0x31, + 0x8c, 0x19, 0xbe, 0x3f, 0x3b, 0x16, 0x5f, 0x6a, 0x39, 0xb2, 0xca, 0xe9, + 0xe2, 0x08, 0x43, 0x50, 0xdd, 0xcd, 0x6d, 0x0a, 0x21, 0xca, 0x28, 0xc4, + 0x61, 0x61, 0xf6, 0x18, 0xf7, 0x3e, 0x8e, 0x3c, 0xe1, 0xcf, 0x34, 0x7f, + 0xfa, 0x8f, 0x0d, 0x90, 0x90, 0xf4, 0x77, 0x5f, 0xc3, 0x04, 0x88, 0x79, + 0x1a, 0x3f, 0x67, 0xf4, 0x77, 0xc7, 0x95, 0x3e, 0x80, 0x24, 0x26, 0x77, + 0x1c, 0x6f, 0xb6, 0x67, 0x78, 0xb2, 0xf4, 0x61, 0x2c, 0xbb, 0x38, 0xb2, + 0xa4, 0x6c, 0x3f, 0x1a, 0xbf, 0xb2, 0x44, 0x07, 0xe2, 0xcb, 0xef, 0xb8, + 0x70, 0xb2, 0xff, 0x61, 0x6e, 0x74, 0xc8, 0x55, 0x95, 0xa4, 0x41, 0xf8, + 0xb3, 0xf2, 0x2b, 0xec, 0xd1, 0x8a, 0xb2, 0xf3, 0x5b, 0x31, 0x65, 0xff, + 0x7a, 0x18, 0x4f, 0x2c, 0xf2, 0xca, 0x84, 0x54, 0x8c, 0xc7, 0x08, 0xb8, + 0x3f, 0x7f, 0x7a, 0x04, 0x2f, 0xbf, 0x59, 0x76, 0x6e, 0x2c, 0xbe, 0x8f, + 0x09, 0xc5, 0x95, 0x3c, 0x9b, 0xc9, 0x0c, 0xde, 0xcc, 0x69, 0x65, 0xc6, + 0x2a, 0xca, 0xd8, 0xd9, 0x98, 0xe5, 0xfb, 0xb9, 0x31, 0xb1, 0x65, 0xfa, + 0x3e, 0xff, 0xf8, 0x59, 0x50, 0x7a, 0x44, 0x51, 0x7e, 0x8d, 0xf0, 0x5d, + 0x59, 0x7e, 0x3d, 0xfd, 0x3d, 0x96, 0x5f, 0xbb, 0xe8, 0xd4, 0x96, 0x5f, + 0xf9, 0x84, 0x62, 0xc7, 0x78, 0x7a, 0x59, 0x7d, 0x2e, 0xe0, 0x56, 0x56, + 0x1f, 0x03, 0x9f, 0x5c, 0x50, 0xb2, 0xff, 0xbc, 0xf2, 0x89, 0x8a, 0x18, + 0xb2, 0xfc, 0x58, 0x26, 0x4c, 0xb2, 0xff, 0xf8, 0x82, 0x6c, 0x18, 0x1f, + 0x52, 0xcf, 0xba, 0xb2, 0xff, 0xce, 0x5f, 0xfd, 0xdd, 0x46, 0x34, 0xb2, + 0xff, 0xdc, 0xf1, 0xce, 0xfa, 0x51, 0x9b, 0x2c, 0xb6, 0x6e, 0xa2, 0x0b, + 0xf4, 0x0b, 0xfe, 0xf3, 0xb3, 0xc7, 0x1f, 0x49, 0x65, 0xf7, 0x9d, 0xd8, + 0xb2, 0xf9, 0xfd, 0xe3, 0x59, 0x44, 0x78, 0x5b, 0xc8, 0x6f, 0xc5, 0x29, + 0xf7, 0xd9, 0x65, 0x41, 0xe7, 0x08, 0x47, 0x50, 0x9f, 0x4c, 0xa1, 0xa4, + 0x65, 0x9f, 0xc3, 0x26, 0xe1, 0x4d, 0x65, 0x6c, 0xaf, 0x58, 0x64, 0x01, + 0x28, 0xf8, 0xab, 0x50, 0x88, 0x61, 0x07, 0x85, 0x48, 0xe3, 0xb2, 0x84, + 0x67, 0xd1, 0xef, 0xf7, 0xbe, 0x97, 0x3d, 0x9b, 0x2c, 0xbf, 0xe7, 0xf4, + 0xc6, 0x5e, 0xcf, 0x96, 0x54, 0x1f, 0x7e, 0x1b, 0x5f, 0xa6, 0x94, 0x77, + 0x8b, 0x2e, 0xe7, 0xeb, 0x2b, 0xf3, 0xc1, 0x9f, 0x29, 0xbf, 0xff, 0xfe, + 0xf6, 0x17, 0xf8, 0x5d, 0xf6, 0x7a, 0x32, 0x63, 0xd4, 0xde, 0x3f, 0xba, + 0xb2, 0xfe, 0xd1, 0x87, 0xd9, 0x32, 0xcb, 0xff, 0xe2, 0xc9, 0x9f, 0xfc, + 0xec, 0x6f, 0x32, 0xfd, 0x65, 0x30, 0xff, 0xfa, 0x5d, 0x58, 0x98, 0xfb, + 0xc3, 0xf6, 0xf8, 0xa3, 0x1a, 0x59, 0x7d, 0x1f, 0x71, 0xd6, 0x54, 0x3a, + 0x90, 0xad, 0xa1, 0x5b, 0x23, 0x91, 0xc3, 0x3f, 0x2b, 0xbd, 0xb0, 0xc6, + 0xe7, 0xf2, 0xa1, 0xc6, 0x9d, 0xba, 0x77, 0x33, 0x66, 0x96, 0x5e, 0x71, + 0x24, 0xa3, 0x0c, 0xeb, 0x08, 0x23, 0x45, 0xde, 0x4e, 0x21, 0x0d, 0xff, + 0xf1, 0x96, 0x32, 0x04, 0x20, 0x48, 0xb1, 0x8b, 0x2f, 0xf8, 0x67, 0x84, + 0x09, 0x67, 0x16, 0x5f, 0xf6, 0xb5, 0x01, 0xf7, 0xcf, 0xba, 0xb2, 0xf1, + 0x47, 0x16, 0x5f, 0x61, 0x63, 0x16, 0x52, 0xca, 0x83, 0xc6, 0x90, 0xd8, + 0x08, 0x2f, 0xe0, 0x81, 0xc6, 0xc8, 0x59, 0x7f, 0xde, 0x39, 0x79, 0xe4, + 0x6c, 0x59, 0x7f, 0xee, 0x19, 0x7d, 0x2e, 0x9f, 0x0d, 0x65, 0x61, 0xfb, + 0x74, 0xe2, 0xfe, 0x7e, 0xfa, 0x00, 0xdb, 0x59, 0x7f, 0xe3, 0x66, 0x0f, + 0xc7, 0xc2, 0xc5, 0x97, 0xcc, 0x81, 0xc2, 0xca, 0x23, 0xdd, 0xe1, 0xe5, + 0x49, 0x55, 0x46, 0x26, 0x99, 0xbc, 0xd0, 0x83, 0xd1, 0x73, 0x21, 0x46, + 0x44, 0x3d, 0x84, 0x75, 0xf7, 0xb3, 0xf7, 0x59, 0x7c, 0x51, 0xa9, 0x2c, + 0xbf, 0xef, 0x44, 0x8c, 0x88, 0x1f, 0x2c, 0xbf, 0xe8, 0x91, 0xf6, 0x50, + 0x5f, 0xac, 0xbf, 0xe8, 0xce, 0x89, 0xff, 0xf1, 0xf2, 0xcb, 0xd8, 0x42, + 0xac, 0xb6, 0xa1, 0x1d, 0x38, 0x43, 0xa3, 0x86, 0x1c, 0x36, 0xcf, 0x2f, + 0xe2, 0x80, 0xea, 0x24, 0xb2, 0xff, 0xf3, 0x9f, 0x64, 0x02, 0xef, 0x3c, + 0xf2, 0x58, 0x21, 0xac, 0xba, 0x3c, 0xb2, 0xbc, 0x7e, 0x1b, 0xd6, 0xaf, + 0xfd, 0xc1, 0x3a, 0x64, 0x09, 0x67, 0x16, 0x5c, 0x3e, 0x2c, 0xad, 0xd3, + 0xfa, 0xf1, 0x21, 0x1f, 0xdf, 0xff, 0xd9, 0xf6, 0x71, 0xcb, 0xc7, 0x1d, + 0xf0, 0x1d, 0x8b, 0x2a, 0x72, 0xaf, 0xd8, 0x8c, 0x2d, 0xe3, 0x68, 0x23, + 0x1b, 0xf0, 0x18, 0x7e, 0x85, 0x97, 0xec, 0xd0, 0x63, 0x16, 0x51, 0x1e, + 0x68, 0x84, 0xf7, 0xfd, 0x8c, 0xcd, 0x1e, 0xce, 0xc5, 0x97, 0xee, 0xbb, + 0xb3, 0x8b, 0x2e, 0xcd, 0x96, 0x5f, 0xfc, 0xec, 0x28, 0xfa, 0x7c, 0x5e, + 0xe7, 0x96, 0x5f, 0xff, 0x9c, 0xba, 0xef, 0xe2, 0x8e, 0xf1, 0xac, 0xf2, + 0xcb, 0xfc, 0x26, 0xb3, 0x7c, 0x14, 0x96, 0x5f, 0xfc, 0xce, 0x3c, 0xb5, + 0x1c, 0x82, 0xd9, 0x65, 0xff, 0x47, 0x7d, 0x8c, 0xcd, 0x62, 0xcb, 0xf7, + 0x7c, 0x71, 0xc5, 0x97, 0xb6, 0x72, 0xd1, 0xef, 0x70, 0xde, 0xff, 0xa3, + 0xfe, 0x0c, 0x0f, 0xa9, 0x2c, 0xbd, 0xae, 0x71, 0x71, 0x01, 0xaf, 0x82, + 0xee, 0x15, 0x44, 0x06, 0x6e, 0x6a, 0xad, 0x92, 0x45, 0x26, 0x99, 0x2f, + 0xfa, 0x3c, 0x13, 0xe3, 0x9e, 0xcb, 0x2d, 0x13, 0x8f, 0x83, 0x45, 0x17, + 0x98, 0xf2, 0x59, 0x5e, 0x3c, 0x5f, 0xca, 0x6e, 0x7f, 0xd6, 0x5b, 0x50, + 0x6e, 0xba, 0x47, 0x58, 0xa9, 0xf9, 0xe1, 0x44, 0x24, 0x76, 0xd4, 0x35, + 0xc0, 0x0f, 0x8e, 0x34, 0x4f, 0xe1, 0x7f, 0xd2, 0x09, 0x4f, 0xb2, 0xac, + 0xef, 0x87, 0xe8, 0x9f, 0x59, 0x7f, 0xff, 0x18, 0xcb, 0x39, 0x1a, 0x9d, + 0xcf, 0x38, 0xe0, 0x6b, 0x2f, 0x6f, 0x8f, 0x2c, 0xac, 0x5d, 0x2f, 0x39, + 0xc2, 0x27, 0x6f, 0xe1, 0x28, 0x8b, 0x77, 0x3f, 0xcb, 0x2f, 0x79, 0xf7, + 0x56, 0x5f, 0x47, 0xf3, 0xf8, 0xb2, 0xf9, 0x8c, 0x7e, 0xac, 0xa6, 0x1f, + 0xef, 0xe2, 0xfd, 0x1f, 0x10, 0x96, 0xfe, 0xcd, 0xc7, 0x7f, 0x71, 0x65, + 0xf1, 0x77, 0xf0, 0x2c, 0xbf, 0x31, 0xb0, 0xd8, 0x6c, 0x4f, 0x4b, 0x2f, + 0xff, 0xe9, 0x67, 0x0f, 0x5a, 0x36, 0x09, 0xdf, 0x63, 0x5d, 0x59, 0x58, + 0x8b, 0xa3, 0x23, 0x73, 0xab, 0xf6, 0xde, 0x7f, 0x42, 0xcb, 0xff, 0xb0, + 0xbb, 0x85, 0x2c, 0xe0, 0x8c, 0x59, 0x4e, 0x7d, 0x6d, 0x14, 0x5f, 0xfe, + 0x96, 0x7d, 0x21, 0x23, 0xf6, 0xed, 0x34, 0xd2, 0x4b, 0xf3, 0x1a, 0xc3, + 0xde, 0xb2, 0xf4, 0xb0, 0xa6, 0x3f, 0xd1, 0x14, 0xea, 0x11, 0xa6, 0xf0, + 0x9f, 0xbf, 0xff, 0x3e, 0xa4, 0x3f, 0x47, 0x0b, 0x1a, 0x7d, 0x0a, 0xb2, + 0xa1, 0x58, 0x70, 0xcf, 0xbd, 0x0e, 0x92, 0x8d, 0xac, 0x04, 0xd7, 0xfc, + 0x65, 0x2e, 0x36, 0xb9, 0xe9, 0xb1, 0x3d, 0x2c, 0xbe, 0x9b, 0x99, 0x32, + 0xcb, 0xf7, 0xf1, 0xdc, 0xdd, 0x59, 0x7f, 0xc3, 0x9f, 0x31, 0x45, 0x82, + 0xea, 0xcb, 0xfb, 0x9a, 0xd1, 0xcd, 0xc5, 0x97, 0xfa, 0x33, 0x41, 0x77, + 0x0a, 0xa2, 0x09, 0x5f, 0xf4, 0x77, 0x34, 0x17, 0x70, 0xaa, 0x2f, 0x95, + 0xf9, 0xc7, 0xe8, 0x15, 0x65, 0xff, 0xff, 0x61, 0xeb, 0x07, 0xe8, 0xfa, + 0x5a, 0x8e, 0xf0, 0x42, 0x92, 0xcb, 0xff, 0x16, 0x77, 0xa7, 0xce, 0xe7, + 0xeb, 0x2e, 0xce, 0x36, 0x53, 0x7e, 0x19, 0x78, 0x4f, 0x98, 0x88, 0x45, + 0x1c, 0x67, 0xac, 0x54, 0x42, 0x51, 0xdd, 0xd1, 0x2a, 0x75, 0xdf, 0x29, + 0x32, 0xff, 0xe8, 0xf1, 0x8b, 0x9a, 0xd9, 0x80, 0xf9, 0x65, 0xff, 0x0f, + 0x58, 0xcc, 0x93, 0x8d, 0x65, 0xff, 0xf8, 0x0f, 0xcc, 0x20, 0xf8, 0xc7, + 0xec, 0xfb, 0x71, 0x65, 0x62, 0xb9, 0xd3, 0x95, 0x33, 0xe2, 0xd7, 0x47, + 0x23, 0x8b, 0xf4, 0xb3, 0xf1, 0x3a, 0xb2, 0xfe, 0xf4, 0xff, 0x8c, 0x3e, + 0x59, 0x4e, 0x7b, 0x60, 0x2a, 0xbf, 0xff, 0xa0, 0x01, 0xf4, 0x6b, 0x38, + 0x40, 0x79, 0x0a, 0xeb, 0x2f, 0xfd, 0xd8, 0x67, 0x20, 0xf6, 0x7e, 0xac, + 0xbf, 0xf6, 0xcf, 0xf4, 0xfe, 0x74, 0x9f, 0x7a, 0xcb, 0xff, 0xec, 0xec, + 0x17, 0x79, 0x98, 0x28, 0xaf, 0x25, 0x97, 0xf0, 0xb9, 0xde, 0xe0, 0xd6, + 0x5f, 0xfb, 0x07, 0x12, 0x8f, 0x67, 0xee, 0xb2, 0xfe, 0x79, 0x02, 0x4e, + 0x35, 0x97, 0xff, 0xfd, 0xe0, 0x3e, 0xda, 0x8f, 0x39, 0xf3, 0x98, 0x2b, + 0x97, 0xeb, 0x2f, 0xc7, 0xb7, 0x38, 0xeb, 0x28, 0x68, 0x8f, 0xf3, 0x3d, + 0xe6, 0x9a, 0x69, 0x25, 0xfb, 0xd8, 0x17, 0xd2, 0x46, 0xe6, 0x82, 0xff, + 0xff, 0x4d, 0xa8, 0xdf, 0x38, 0x57, 0x9c, 0x24, 0xdc, 0x9d, 0x9f, 0x75, + 0x65, 0x42, 0x3a, 0xfa, 0x86, 0x21, 0xcd, 0xff, 0x78, 0xda, 0xec, 0xda, + 0x8e, 0xac, 0xbf, 0xf1, 0xf4, 0x19, 0xd7, 0x66, 0x12, 0xcb, 0x1c, 0x8f, + 0xdf, 0x47, 0x75, 0xb2, 0xb7, 0x30, 0xa2, 0x69, 0x3d, 0x85, 0xdf, 0x9e, + 0x94, 0x74, 0xfd, 0x85, 0x3d, 0x2c, 0xa5, 0x97, 0x4b, 0x8d, 0x16, 0xdb, + 0x81, 0x77, 0xfe, 0x64, 0x7d, 0xc2, 0xcd, 0x84, 0x92, 0xcb, 0xff, 0xfc, + 0x7e, 0x72, 0xfa, 0x42, 0x4a, 0x0a, 0x4e, 0xcc, 0x1a, 0xcb, 0xff, 0xd9, + 0x9e, 0x1f, 0xa3, 0x99, 0xe3, 0xfd, 0x65, 0xfa, 0x69, 0x46, 0xb8, 0xb2, + 0xff, 0x61, 0x02, 0x5c, 0xd9, 0xd6, 0x58, 0x59, 0xc7, 0xb8, 0x32, 0x9a, + 0x84, 0xe8, 0x76, 0x30, 0x92, 0x08, 0xd8, 0x0e, 0x14, 0xb7, 0xd3, 0x8f, + 0xc0, 0x59, 0x7e, 0xc9, 0x7a, 0x34, 0xb2, 0x82, 0x79, 0x7b, 0xa4, 0x97, + 0xe3, 0xf0, 0xb1, 0xf2, 0xcb, 0xf6, 0x0c, 0xa3, 0xe5, 0x96, 0xec, 0x1e, + 0x81, 0x14, 0xdf, 0xff, 0xff, 0xbe, 0xe9, 0x43, 0x38, 0x3f, 0x47, 0x32, + 0x46, 0x28, 0xaf, 0xd2, 0x86, 0x71, 0x65, 0xff, 0xdd, 0xe3, 0xcb, 0x98, + 0x18, 0x61, 0xac, 0xbf, 0xfd, 0xf1, 0x4e, 0x0f, 0x8f, 0xff, 0xe3, 0x52, + 0x59, 0x50, 0x9d, 0x89, 0xba, 0xb9, 0x30, 0x21, 0x08, 0x22, 0x1d, 0xff, + 0xb6, 0xfa, 0x0d, 0x98, 0x59, 0xba, 0xb2, 0xfd, 0xb7, 0xa0, 0x18, 0xb2, + 0xfe, 0x3e, 0x60, 0xc4, 0xc5, 0x97, 0xed, 0xc3, 0x2c, 0xde, 0xb2, 0xfe, + 0xc1, 0x98, 0x03, 0xe0, 0x9e, 0xc3, 0x96, 0xdf, 0xdc, 0x91, 0xb1, 0xe4, + 0xb2, 0xa1, 0x1e, 0x1a, 0x84, 0x07, 0xe8, 0x77, 0xfb, 0x85, 0x9b, 0xfc, + 0xf2, 0x59, 0x7b, 0x92, 0xde, 0xb2, 0x8c, 0xf4, 0xf7, 0x4d, 0x2f, 0xff, + 0x46, 0xc2, 0x7a, 0x3e, 0xee, 0x6c, 0x43, 0x59, 0x58, 0xa8, 0xa8, 0xe3, + 0x31, 0x78, 0x44, 0x11, 0x25, 0xff, 0xcf, 0x2c, 0x19, 0x1b, 0xfa, 0x18, + 0xb2, 0xe7, 0xf2, 0xca, 0xf1, 0xeb, 0xf5, 0x06, 0xff, 0x46, 0x17, 0x5b, + 0x2d, 0x05, 0x65, 0xfe, 0xf6, 0x6f, 0x9d, 0xc9, 0x05, 0x65, 0x70, 0xfb, + 0xfa, 0x6f, 0x5d, 0x45, 0x98, 0x21, 0x23, 0x7f, 0xff, 0x17, 0xe5, 0x9d, + 0xf6, 0x78, 0x9f, 0xe9, 0x1e, 0x2c, 0xbf, 0x8a, 0x70, 0x9a, 0xf3, 0xac, + 0xbf, 0xf0, 0x1e, 0x53, 0xa0, 0xc7, 0x8c, 0x59, 0x46, 0x8c, 0x87, 0x59, + 0x23, 0x0b, 0xff, 0x68, 0xff, 0xdb, 0xd1, 0xc0, 0x71, 0x65, 0xfb, 0x34, + 0xff, 0x34, 0xb2, 0xd2, 0x59, 0x7f, 0xfe, 0x68, 0xb0, 0x7a, 0x30, 0x6c, + 0x3f, 0x41, 0x6c, 0xb2, 0xdb, 0x41, 0xf4, 0x60, 0x8d, 0x6c, 0x8b, 0x8e, + 0x42, 0x46, 0xff, 0x4c, 0x4e, 0xd7, 0x73, 0xcb, 0x2f, 0x7b, 0x02, 0xb2, + 0xf3, 0x4d, 0x34, 0x92, 0xfe, 0xfe, 0x0b, 0x3e, 0xea, 0x46, 0xe6, 0x82, + 0xa7, 0x22, 0xc2, 0x7c, 0xd0, 0x43, 0xcb, 0xff, 0xf9, 0xb7, 0x38, 0x3e, + 0x31, 0x8f, 0x1a, 0x9d, 0xde, 0x66, 0xcb, 0x2b, 0xc8, 0x9c, 0x01, 0xb5, + 0x42, 0xa9, 0x13, 0x87, 0xcb, 0xc6, 0xeb, 0x7f, 0xc2, 0x47, 0x31, 0xa1, + 0x0b, 0xab, 0x2b, 0x66, 0xd4, 0x7e, 0x52, 0xfa, 0x87, 0x0a, 0xa0, 0x90, + 0xfc, 0xb4, 0x72, 0xec, 0x75, 0x28, 0xa5, 0x92, 0x92, 0xfd, 0x29, 0xe0, + 0xa3, 0x50, 0xe4, 0x3c, 0x3b, 0x2a, 0xf8, 0x43, 0xab, 0xff, 0x61, 0x96, + 0xce, 0x5d, 0xc6, 0x2c, 0xbf, 0xf8, 0x51, 0x3a, 0x7c, 0x2c, 0xde, 0x63, + 0x59, 0x7f, 0x16, 0x0f, 0xce, 0xd2, 0xca, 0xd1, 0xfa, 0x31, 0x1e, 0xf1, + 0x3b, 0x16, 0x5f, 0xc3, 0xf6, 0x76, 0x7f, 0x16, 0x58, 0xfc, 0x79, 0x6d, + 0x0d, 0xd4, 0x22, 0x57, 0xcd, 0xb7, 0xc4, 0xff, 0x75, 0x65, 0xec, 0x2f, + 0xd6, 0x5f, 0x67, 0x66, 0x92, 0xcb, 0xff, 0xb7, 0x0a, 0x37, 0x38, 0xe4, + 0x27, 0xfc, 0x59, 0x5e, 0x3e, 0xe7, 0x23, 0xbf, 0xf6, 0x76, 0x35, 0xcf, + 0x41, 0x75, 0x71, 0x04, 0x2f, 0xec, 0xd0, 0x5d, 0xc2, 0xa8, 0x82, 0x0d, + 0xcf, 0x2a, 0xf4, 0x0c, 0xd6, 0x54, 0x1f, 0x3f, 0x13, 0x2e, 0xde, 0x35, + 0x97, 0xb7, 0xc3, 0x16, 0x5f, 0xb6, 0xcd, 0x44, 0x96, 0x5f, 0x6b, 0x46, + 0xd2, 0xcb, 0x3b, 0x0f, 0x2f, 0xc5, 0x17, 0xf6, 0xe8, 0xda, 0x72, 0x92, + 0xca, 0xd9, 0x1b, 0x58, 0x32, 0x4d, 0x20, 0x27, 0xbf, 0xfd, 0x34, 0x8c, + 0xbb, 0x1f, 0xcc, 0x47, 0x32, 0xcb, 0x8a, 0x65, 0x96, 0x81, 0x9f, 0x1e, + 0x26, 0x56, 0xca, 0xcf, 0x85, 0x22, 0xf4, 0x22, 0xbf, 0x86, 0x91, 0x43, + 0xef, 0x7c, 0x27, 0xef, 0xfb, 0x35, 0xe3, 0xd1, 0x66, 0xcb, 0x2f, 0xef, + 0x67, 0x7a, 0x6c, 0x59, 0x7f, 0xf7, 0x06, 0x47, 0xb0, 0x9a, 0x28, 0xc5, + 0x97, 0xf7, 0x44, 0xcf, 0x3f, 0xcb, 0x2b, 0xe4, 0x50, 0x78, 0xb4, 0x91, + 0x2f, 0xf8, 0xb6, 0xcd, 0x7e, 0x40, 0xfd, 0x65, 0xfd, 0x9f, 0x77, 0x5a, + 0x85, 0x97, 0xff, 0x70, 0x9f, 0xae, 0xc1, 0x23, 0x6c, 0x59, 0x43, 0x3f, + 0x2e, 0x17, 0x54, 0x23, 0xfc, 0xcc, 0x01, 0x0a, 0xdb, 0xfb, 0x02, 0x26, + 0xff, 0x62, 0xcb, 0x31, 0x65, 0xfe, 0x8e, 0x8f, 0xc0, 0x7e, 0x2c, 0x10, + 0xb1, 0xbf, 0xff, 0x8c, 0x87, 0xac, 0x0f, 0xa3, 0xd9, 0xad, 0x40, 0xab, + 0x2e, 0x81, 0x56, 0x5f, 0xec, 0xd1, 0xfe, 0xc7, 0x92, 0xcb, 0x8c, 0x96, + 0x59, 0xa5, 0x94, 0x33, 0x4e, 0x20, 0xad, 0xe2, 0x06, 0xcb, 0x2f, 0x6c, + 0x50, 0xb2, 0xfd, 0xe7, 0x96, 0x12, 0xcb, 0xf8, 0xfa, 0x59, 0xe3, 0x59, + 0x76, 0x7a, 0x71, 0xe8, 0xc1, 0x35, 0xce, 0xd4, 0xe4, 0xd2, 0xc6, 0x2f, + 0x8b, 0x5e, 0x22, 0x71, 0xde, 0xb6, 0xdf, 0xd1, 0xb7, 0x79, 0x9d, 0x59, + 0x50, 0xb9, 0x8b, 0x28, 0xf5, 0xf0, 0xcf, 0x48, 0x3e, 0x43, 0x28, 0xe9, + 0x5a, 0x64, 0xbf, 0xb8, 0x63, 0x1e, 0x12, 0xcb, 0xff, 0xfb, 0xbe, 0xce, + 0x73, 0x30, 0x1a, 0xdb, 0x00, 0x5d, 0x59, 0x7f, 0xde, 0x8d, 0x74, 0xa3, + 0x3e, 0x59, 0x7f, 0xff, 0xc7, 0xb4, 0x4b, 0x9a, 0xd1, 0xfd, 0x34, 0x9f, + 0x4c, 0xcf, 0x96, 0x5f, 0xe8, 0x7d, 0x9f, 0x4e, 0x22, 0xcb, 0xde, 0xc2, + 0x1a, 0x35, 0x7c, 0x71, 0xd6, 0xaa, 0xc4, 0xe7, 0x0c, 0xb3, 0xb1, 0x98, + 0x5f, 0xa3, 0x53, 0x73, 0x65, 0x97, 0xfd, 0xe8, 0xcd, 0x4b, 0x80, 0x62, + 0xca, 0xd8, 0xf8, 0xc2, 0x55, 0x71, 0xf1, 0x65, 0xff, 0x66, 0xcf, 0xdf, + 0xc4, 0x29, 0x2c, 0xbf, 0x9f, 0xb3, 0xa2, 0x42, 0xac, 0xbf, 0x84, 0x67, + 0x1a, 0x7f, 0x96, 0x54, 0x26, 0xdb, 0x90, 0x98, 0x14, 0x8f, 0x42, 0xce, + 0x77, 0xd3, 0x0b, 0xda, 0xcf, 0x2c, 0xbd, 0xc3, 0x62, 0xca, 0xd1, 0xb8, + 0xe8, 0xe5, 0xc7, 0xb2, 0xca, 0x83, 0x71, 0xb8, 0x43, 0x7f, 0xf7, 0x8e, + 0x0a, 0x73, 0x94, 0x8d, 0x8b, 0x2f, 0x8e, 0x63, 0x69, 0x65, 0xff, 0xcc, + 0x7f, 0x84, 0xe9, 0xea, 0x00, 0xdb, 0x59, 0x76, 0xfd, 0x2c, 0xbf, 0xff, + 0x03, 0x62, 0x8c, 0xf3, 0x78, 0xee, 0x78, 0x1a, 0x59, 0x7d, 0xde, 0x09, + 0xc9, 0xc9, 0x90, 0x0d, 0x0f, 0x08, 0xc9, 0x28, 0x41, 0x9b, 0x89, 0xa5, + 0x97, 0xee, 0x33, 0x3b, 0xc5, 0x97, 0xff, 0x18, 0xb9, 0xd3, 0xf6, 0x76, + 0x34, 0xb2, 0xfc, 0xf2, 0x23, 0x85, 0x97, 0xe7, 0x2e, 0xe1, 0x2c, 0xbf, + 0xf0, 0xa4, 0xfd, 0xe6, 0x6f, 0xf1, 0xac, 0xa8, 0x3e, 0x57, 0x25, 0xbf, + 0x8f, 0x5a, 0x8c, 0x25, 0x97, 0xfd, 0x12, 0xe6, 0x04, 0xa3, 0x4b, 0x28, + 0x53, 0xe2, 0xd1, 0x5d, 0x8d, 0x65, 0xe2, 0x71, 0x70, 0xd9, 0x84, 0x8e, + 0xa4, 0xae, 0x6b, 0x23, 0x43, 0x0b, 0x29, 0x8b, 0xe8, 0xa1, 0x88, 0x65, + 0x08, 0xa0, 0x42, 0xd6, 0xfb, 0xe0, 0x98, 0xd6, 0x5f, 0xbc, 0x73, 0xe6, + 0x2a, 0xcb, 0xec, 0xfc, 0xf7, 0xac, 0xbe, 0xff, 0xf0, 0x7e, 0xb2, 0x82, + 0x7e, 0xa4, 0x56, 0x21, 0x25, 0xf0, 0x35, 0xd3, 0x59, 0x7e, 0x29, 0x73, + 0x6f, 0xd6, 0x5f, 0xfd, 0xfc, 0x71, 0x90, 0x7a, 0xd1, 0xb4, 0xb2, 0xf3, + 0xbc, 0x96, 0x50, 0xd1, 0x59, 0x84, 0x46, 0x55, 0xfa, 0x2d, 0xf9, 0x87, + 0xde, 0xc2, 0xcb, 0xc7, 0x8d, 0x2c, 0xbe, 0x3e, 0x60, 0xcc, 0xf0, 0xc8, + 0x9e, 0xfe, 0x2e, 0x63, 0x23, 0x75, 0x65, 0xf6, 0xef, 0xb3, 0x65, 0x97, + 0xfa, 0x07, 0x9d, 0x3f, 0x01, 0x65, 0x41, 0xec, 0x0c, 0x9a, 0xf8, 0x53, + 0xfb, 0x8b, 0x2a, 0x11, 0x98, 0xc8, 0x43, 0xf4, 0x86, 0xff, 0xdd, 0x13, + 0xf8, 0x12, 0x3f, 0x8f, 0xd6, 0x5f, 0xf0, 0x06, 0xe5, 0xfb, 0x6f, 0xce, + 0xb2, 0xff, 0xff, 0xcc, 0x72, 0xfe, 0x47, 0xc0, 0x33, 0xc7, 0x1d, 0xf0, + 0x1d, 0x8b, 0x2f, 0xff, 0x88, 0x1d, 0x91, 0xff, 0xe7, 0x3e, 0xbb, 0x16, + 0x5d, 0x3b, 0x75, 0x65, 0xff, 0xf9, 0xcf, 0xa6, 0x3c, 0x1e, 0x74, 0xd8, + 0x7a, 0x59, 0x7f, 0xfa, 0x02, 0x3c, 0x13, 0xa7, 0xa8, 0x03, 0x6d, 0x65, + 0xe0, 0x60, 0xd6, 0x5e, 0xd3, 0xf3, 0x13, 0x27, 0xd2, 0x7f, 0x87, 0x09, + 0x4a, 0x7d, 0x3a, 0xdd, 0x92, 0xa2, 0xd7, 0x3d, 0x04, 0x72, 0x75, 0xf2, + 0xaa, 0xd3, 0x94, 0xe1, 0x74, 0xa1, 0x65, 0xff, 0xed, 0x87, 0xe8, 0xf6, + 0x7d, 0xfe, 0x77, 0x8b, 0x29, 0xcf, 0x8c, 0x02, 0xd5, 0x0b, 0xd0, 0xa3, + 0x84, 0xc6, 0x43, 0x6c, 0x2f, 0xe7, 0x18, 0x13, 0xca, 0xe1, 0x28, 0x47, + 0xdf, 0xda, 0x8d, 0x8c, 0xba, 0xb2, 0xf0, 0xc4, 0x62, 0xcb, 0xf1, 0x40, + 0xf2, 0x65, 0x97, 0xdc, 0x06, 0x85, 0x59, 0x7e, 0x8f, 0x76, 0x24, 0xb2, + 0xf8, 0x60, 0xef, 0x20, 0xfc, 0xbe, 0x27, 0xfc, 0x92, 0xff, 0x41, 0x3f, + 0x80, 0x5b, 0x2c, 0xbf, 0xff, 0x78, 0xf5, 0xe7, 0x82, 0x15, 0xe6, 0x77, + 0xd9, 0x65, 0xf6, 0x1f, 0xfc, 0x59, 0x7c, 0x7c, 0x89, 0x61, 0xfb, 0xe9, + 0x56, 0xf6, 0xc3, 0x85, 0x97, 0xef, 0xf1, 0x91, 0xe5, 0x97, 0xfd, 0x1c, + 0x8e, 0xf3, 0x0a, 0x4b, 0x2f, 0xff, 0xff, 0x66, 0xdc, 0x81, 0x5f, 0x9d, + 0x96, 0x0a, 0x2e, 0x7d, 0xdd, 0x46, 0x7c, 0xb2, 0xfd, 0x13, 0xb4, 0x52, + 0x59, 0x5e, 0x46, 0xe7, 0x0d, 0xc0, 0xf9, 0x79, 0xb7, 0xec, 0x59, 0x43, + 0x56, 0xad, 0x85, 0xa7, 0x09, 0x06, 0x24, 0x7a, 0x15, 0x2e, 0x6e, 0x43, + 0xbd, 0x8c, 0x10, 0x06, 0x17, 0xb4, 0x27, 0x16, 0x5f, 0xb1, 0xae, 0x88, + 0xc5, 0x97, 0xff, 0xfd, 0xc1, 0x23, 0xf1, 0xfa, 0x35, 0x84, 0x6f, 0x2e, + 0x67, 0x56, 0x5e, 0x1f, 0x36, 0x59, 0x5a, 0x44, 0x27, 0x59, 0xaf, 0xfd, + 0x12, 0xe0, 0x35, 0xfe, 0x79, 0xd6, 0x5f, 0xf7, 0xa3, 0xee, 0xc7, 0x9f, + 0x75, 0x65, 0x2c, 0xa9, 0xc7, 0x8e, 0xda, 0xa7, 0x96, 0xf9, 0x65, 0xd8, + 0xd2, 0xcb, 0xbb, 0xd3, 0x35, 0x3d, 0x12, 0xa8, 0x44, 0x01, 0xaa, 0x5f, + 0xf9, 0xc8, 0x39, 0xa0, 0xbb, 0x85, 0x51, 0x08, 0xae, 0x96, 0xcb, 0x2a, + 0x15, 0x3b, 0xe0, 0xf1, 0xc2, 0xb9, 0x84, 0x7f, 0xc6, 0x0c, 0x02, 0x1d, + 0xe9, 0x37, 0xdb, 0x73, 0xf7, 0x59, 0x7f, 0xda, 0x71, 0xe1, 0xef, 0x76, + 0x2c, 0xac, 0x3d, 0xdd, 0x12, 0x5f, 0xff, 0xfe, 0xf6, 0x03, 0x42, 0xf8, + 0xfe, 0x8e, 0x83, 0xc5, 0x9f, 0x74, 0x0f, 0xc5, 0x97, 0xff, 0x67, 0xc2, + 0x74, 0xc8, 0x12, 0xce, 0x2c, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0x61, + 0x6c, 0x4e, 0x38, 0x2d, 0xa3, 0x43, 0xc2, 0xce, 0xf0, 0xc3, 0x84, 0x26, + 0xd9, 0xa8, 0xe1, 0x38, 0xbc, 0xc3, 0xff, 0xf0, 0x7e, 0x4e, 0x38, 0x2d, + 0xa3, 0x4b, 0x2f, 0xcf, 0xb9, 0x9f, 0x75, 0x65, 0xfd, 0x22, 0xcc, 0x03, + 0x6d, 0x65, 0x42, 0x75, 0x58, 0x8b, 0xe8, 0xc2, 0x9c, 0xae, 0xff, 0xee, + 0xb9, 0x6d, 0xf4, 0x8a, 0x33, 0x8b, 0x2f, 0xff, 0xd1, 0xfc, 0xd2, 0x8d, + 0x89, 0xc5, 0xe1, 0x44, 0x96, 0x5f, 0xf8, 0x4c, 0xf3, 0xb1, 0xbb, 0x4d, + 0x34, 0xb2, 0xff, 0xfd, 0x81, 0xf4, 0x10, 0xc8, 0xf6, 0xc2, 0x31, 0x56, + 0x59, 0xf6, 0x44, 0xc4, 0x91, 0x6a, 0x49, 0x8c, 0x3c, 0x3b, 0xef, 0xff, + 0xe9, 0x8a, 0x3f, 0xe0, 0x7c, 0x7a, 0x98, 0xa3, 0xfe, 0x2c, 0xbf, 0xff, + 0xbb, 0xc8, 0x16, 0x47, 0xe7, 0x2e, 0xcb, 0x35, 0x8b, 0x2f, 0xfc, 0x7c, + 0xc1, 0xce, 0x7f, 0xe7, 0xf1, 0x65, 0xfe, 0x86, 0x7d, 0xd8, 0xcf, 0x96, + 0x5f, 0xfb, 0xc7, 0xbf, 0xcf, 0x2d, 0x40, 0xab, 0x2b, 0x13, 0x12, 0x0a, + 0xd7, 0x90, 0xfa, 0x69, 0x7f, 0x03, 0xa6, 0x02, 0x15, 0x65, 0xfd, 0x8d, + 0x61, 0x03, 0xf5, 0x97, 0xe9, 0x67, 0x63, 0xf5, 0x97, 0x43, 0x36, 0x3d, + 0x6f, 0x8b, 0xaf, 0xff, 0xfe, 0xfd, 0x8f, 0x2f, 0x39, 0xf4, 0xc7, 0x83, + 0xce, 0x9b, 0x0f, 0x4b, 0x2b, 0x88, 0x9f, 0x01, 0x7d, 0xff, 0xf8, 0xb3, + 0x99, 0xe7, 0xdc, 0x0f, 0x8d, 0x90, 0x15, 0x97, 0xed, 0x99, 0x03, 0x85, + 0x95, 0x25, 0xf5, 0xa1, 0xca, 0x24, 0xf9, 0x06, 0x68, 0xd7, 0x34, 0x51, + 0xe8, 0xdc, 0x38, 0x7b, 0xd8, 0xc8, 0xf7, 0x91, 0x88, 0xad, 0x7f, 0xe8, + 0x14, 0x58, 0x2c, 0xef, 0x8d, 0x65, 0xfe, 0x8e, 0x72, 0x36, 0xc6, 0x96, + 0x5f, 0xff, 0x0f, 0xa0, 0x7f, 0xbb, 0xfe, 0x0e, 0x77, 0x8d, 0x65, 0xfe, + 0xf6, 0x33, 0x3c, 0x02, 0x59, 0x43, 0x45, 0xd3, 0x9a, 0x75, 0x52, 0xfb, + 0x34, 0x29, 0xac, 0xa8, 0x64, 0xb8, 0x64, 0xfb, 0xd9, 0xc2, 0xb1, 0xe1, + 0xf0, 0x02, 0xfb, 0xcd, 0x34, 0xd2, 0x4b, 0xfe, 0xcf, 0xdf, 0x59, 0xbf, + 0x06, 0x91, 0xb9, 0xa0, 0xb9, 0xa6, 0x92, 0x5e, 0x69, 0xa6, 0x92, 0x5f, + 0xcf, 0xb0, 0xfd, 0x1c, 0x48, 0xdc, 0xd0, 0x51, 0x23, 0x15, 0xa4, 0xc9, + 0xf3, 0x9b, 0xf0, 0x4c, 0x62, 0x31, 0x23, 0x73, 0x67, 0x79, 0xa6, 0x9a, + 0x49, 0x7b, 0x91, 0xa4, 0x8d, 0xcd, 0x05, 0xf3, 0x97, 0xdd, 0x59, 0x6f, + 0xcd, 0x15, 0x7d, 0x5b, 0x68, 0xba, 0xfe, 0x19, 0x44, 0x9c, 0x2b, 0x2f, + 0xdc, 0x06, 0xd3, 0xe4, 0xb2, 0xfc, 0x33, 0xdf, 0x03, 0x59, 0x7d, 0x85, + 0x9b, 0xab, 0x2b, 0x47, 0x98, 0xc2, 0x9a, 0x34, 0x4d, 0x08, 0xf1, 0x7f, + 0xf7, 0x9e, 0x44, 0xe2, 0xcc, 0x47, 0x32, 0xcb, 0xfe, 0xda, 0x3e, 0xe1, + 0x96, 0x7e, 0xb2, 0xf9, 0xe4, 0x7f, 0xac, 0xad, 0x1e, 0xe7, 0x8e, 0xaf, + 0xfb, 0x03, 0x1b, 0x0f, 0xd1, 0xc5, 0x95, 0x87, 0xb7, 0x3e, 0x45, 0x7f, + 0x7b, 0x37, 0xe7, 0x78, 0xb2, 0x82, 0x9a, 0x7f, 0xf0, 0xfc, 0xe9, 0x25, + 0x69, 0x53, 0x7f, 0xa1, 0x8c, 0x51, 0xb9, 0x5f, 0x6d, 0xf8, 0x3f, 0x59, + 0x76, 0xdd, 0x59, 0x50, 0xac, 0x83, 0x25, 0x53, 0x39, 0xe1, 0x13, 0x5f, + 0xb6, 0x9f, 0xf6, 0x05, 0x65, 0xcd, 0xae, 0x7a, 0x59, 0x7b, 0x91, 0xd5, + 0x97, 0x7e, 0xd2, 0xcb, 0x72, 0x63, 0x6a, 0xe3, 0x95, 0x88, 0xaa, 0xf8, + 0xb3, 0xaa, 0xf7, 0xe0, 0xc7, 0xa1, 0x8b, 0x2e, 0x7e, 0xac, 0xbf, 0x66, + 0xeb, 0x97, 0xf8, 0x6f, 0x48, 0x9e, 0xff, 0xa0, 0xa4, 0x43, 0xf4, 0x6c, + 0xb2, 0xfe, 0x1e, 0x6a, 0x7d, 0x83, 0x59, 0x7b, 0xc7, 0x32, 0x4a, 0xd1, + 0xe7, 0x70, 0xc6, 0xff, 0x9f, 0xee, 0xe4, 0xc0, 0xd0, 0xab, 0x2c, 0x13, + 0x3d, 0xe0, 0x11, 0x5f, 0xe2, 0xcf, 0xa5, 0xc3, 0x15, 0x65, 0xed, 0x03, + 0x65, 0x97, 0x9f, 0x42, 0xac, 0xad, 0x1b, 0xb7, 0x1e, 0xb0, 0xab, 0x2f, + 0xfe, 0xc0, 0xfa, 0x3d, 0x9a, 0xd4, 0x0a, 0xb2, 0xf1, 0xff, 0xc8, 0x3d, + 0x6d, 0x09, 0x54, 0xe4, 0xc6, 0x36, 0x6f, 0x27, 0x6b, 0xfe, 0xcc, 0x0f, + 0xf1, 0x9f, 0x49, 0x65, 0xff, 0xdd, 0xd0, 0x39, 0xe7, 0x97, 0xe6, 0x35, + 0x97, 0xd0, 0xec, 0xde, 0x92, 0xff, 0xef, 0x1b, 0xff, 0xfc, 0x7f, 0x1e, + 0xe2, 0xcb, 0xfb, 0xd8, 0xc6, 0x3c, 0x96, 0x5e, 0x69, 0xa6, 0x92, 0x5f, + 0xe2, 0xef, 0x8e, 0x0b, 0x64, 0x8d, 0xcd, 0x05, 0xe8, 0xff, 0x24, 0x89, + 0x1c, 0x4d, 0xae, 0xa6, 0x14, 0x24, 0x35, 0xef, 0xec, 0x9d, 0xd7, 0xff, + 0x8b, 0x28, 0x29, 0xc3, 0xb2, 0x32, 0xdd, 0xe5, 0x57, 0xfc, 0xff, 0x96, + 0x4d, 0x28, 0xf9, 0x65, 0x42, 0xa4, 0x0c, 0x8f, 0x34, 0xce, 0x6f, 0xf3, + 0x90, 0x9a, 0x20, 0x49, 0x65, 0xfd, 0xfc, 0x7f, 0x1e, 0xe2, 0xcb, 0xff, + 0x09, 0x1a, 0xff, 0xbe, 0x71, 0x9a, 0xcb, 0xf6, 0xb4, 0x7d, 0xe2, 0xca, + 0x19, 0xf4, 0x08, 0x81, 0x7f, 0xef, 0xe7, 0xf2, 0x24, 0x7d, 0x28, 0x59, + 0x50, 0x7c, 0x6e, 0x47, 0x7e, 0x89, 0x1c, 0xb8, 0xb2, 0xa1, 0x7c, 0x8e, + 0x4d, 0xa2, 0x9f, 0x7d, 0x0e, 0x43, 0x8c, 0xfe, 0x63, 0x3f, 0x4a, 0x79, + 0x73, 0x5f, 0xcc, 0xca, 0x1f, 0x5c, 0x20, 0xbf, 0x7f, 0x87, 0xe1, 0x56, + 0x5f, 0xff, 0x60, 0x60, 0x9f, 0xbc, 0x18, 0x1f, 0x52, 0x59, 0x5f, 0x1f, + 0xbb, 0x0a, 0x6f, 0xe8, 0x20, 0xb6, 0x27, 0x8c, 0xf2, 0xb2, 0xfd, 0x0c, + 0xd4, 0x6f, 0x59, 0x7f, 0xf4, 0x07, 0xa5, 0x0d, 0x67, 0x79, 0xc5, 0x97, + 0xff, 0x79, 0xfd, 0xa8, 0xe9, 0x84, 0xc6, 0xb2, 0xb4, 0x88, 0x7f, 0xd1, + 0x2f, 0xff, 0xfe, 0x27, 0x64, 0x68, 0x23, 0xf4, 0x70, 0x0e, 0x72, 0xe7, + 0x9e, 0x4b, 0x2f, 0x03, 0xbc, 0x59, 0x7e, 0x69, 0xfd, 0x0c, 0x59, 0x78, + 0x31, 0xc5, 0x97, 0xff, 0x09, 0x2c, 0xf3, 0xf8, 0xf5, 0xa8, 0x59, 0x50, + 0x88, 0x8c, 0x28, 0xfc, 0x72, 0xfe, 0xf6, 0x49, 0xcb, 0xf5, 0x97, 0xb9, + 0xbf, 0x16, 0x5d, 0x1a, 0x59, 0x5d, 0x36, 0x93, 0xe3, 0xd7, 0xf6, 0x6d, + 0xcd, 0xd3, 0x0a, 0xca, 0x83, 0xd5, 0x22, 0x4b, 0xf0, 0x34, 0x2f, 0x8d, + 0x65, 0x42, 0xb7, 0x61, 0x91, 0x84, 0xf3, 0x50, 0xb0, 0x72, 0x4f, 0xdc, + 0x0a, 0x14, 0xbc, 0x2f, 0xec, 0x2d, 0xa7, 0xc8, 0x2e, 0xd3, 0x4b, 0x2f, + 0xf1, 0x3b, 0x58, 0x4e, 0x2a, 0xcb, 0xff, 0xb3, 0xbc, 0x13, 0x8c, 0x28, + 0xd4, 0x2c, 0xbd, 0xbf, 0x08, 0x67, 0xec, 0xc3, 0x2b, 0xff, 0x9a, 0x8d, + 0x13, 0xed, 0x1a, 0x89, 0x2c, 0xa3, 0x3f, 0x8f, 0x1a, 0x5f, 0xff, 0xef, + 0x1b, 0x59, 0xb8, 0x6c, 0x7e, 0xf0, 0x12, 0xcf, 0xba, 0xb2, 0xff, 0xed, + 0x1f, 0xe2, 0x7a, 0x18, 0xef, 0xd5, 0x94, 0x34, 0x55, 0x69, 0x96, 0xa1, + 0x3f, 0x4c, 0x8c, 0x19, 0xe1, 0x97, 0x7f, 0xd3, 0x07, 0xc6, 0xc8, 0xd0, + 0xab, 0x2f, 0xdb, 0x9b, 0x86, 0x41, 0x59, 0x7e, 0xda, 0x70, 0x22, 0x65, + 0x96, 0x92, 0xcb, 0xc2, 0x14, 0xcb, 0x2a, 0x46, 0xbf, 0xf1, 0x1a, 0xc4, + 0x72, 0xe8, 0xed, 0xcb, 0x78, 0xb7, 0x50, 0xfc, 0x66, 0x5b, 0x4b, 0x7d, + 0x94, 0xee, 0x30, 0xe5, 0xef, 0x65, 0x6a, 0xe8, 0x2c, 0x3c, 0x83, 0x1a, + 0x29, 0xcb, 0x60, 0x9a, 0x74, 0xdb, 0x52, 0xde, 0x99, 0x38, 0xcb, 0xe9, + 0xf0, 0x57, 0x97, 0xa9, 0xfc, 0xb0, 0x32, 0xa5, 0x1f, 0x72, 0x74, 0xff, + 0xb4, 0x80, 0xf0, 0x4e, 0x4f, 0xb5, 0x2a, 0x44, 0x48, 0xc7, 0xaf, 0x87, + 0xec, 0xe2, 0xcb, 0x8f, 0xcb, 0x2f, 0xb3, 0x0b, 0xab, 0x2e, 0x82, 0x59, + 0x5e, 0x3c, 0xbe, 0x0b, 0x6f, 0x20, 0xbf, 0xfe, 0xc2, 0x6f, 0xd1, 0x4f, + 0xee, 0x7b, 0x3f, 0x75, 0x95, 0x24, 0x42, 0x0c, 0xc6, 0xf3, 0xbb, 0x4b, + 0x2e, 0xfe, 0x16, 0x5f, 0x83, 0x9d, 0xf1, 0xac, 0xb3, 0x8c, 0xf6, 0x8c, + 0x70, 0x85, 0xef, 0xff, 0xfb, 0x3c, 0xe2, 0x73, 0x05, 0x72, 0xfe, 0x0a, + 0x26, 0xf4, 0x2c, 0xb4, 0x2c, 0xbf, 0xff, 0x46, 0xbd, 0x0c, 0xdc, 0x04, + 0x14, 0x4d, 0xe8, 0x59, 0x7e, 0x3d, 0x4d, 0x1e, 0xd2, 0x33, 0x7f, 0x66, + 0x21, 0x0a, 0x92, 0xab, 0xaf, 0xa1, 0xe2, 0xc7, 0xd1, 0x21, 0xff, 0x66, + 0xd5, 0x59, 0x7f, 0xee, 0xf9, 0xe5, 0xd9, 0x8a, 0x18, 0xb2, 0xff, 0xce, + 0x5f, 0xce, 0x18, 0x1f, 0x52, 0x59, 0x7c, 0x65, 0xb4, 0x2c, 0xbd, 0xdd, + 0x42, 0xcb, 0xfe, 0x28, 0x61, 0x96, 0x32, 0x16, 0x5e, 0xf1, 0xc9, 0x65, + 0xc6, 0x3c, 0x3e, 0xd9, 0x87, 0x3a, 0x6b, 0x5b, 0xa9, 0x84, 0xe9, 0x04, + 0xa1, 0x1d, 0x7f, 0xf1, 0x77, 0x9e, 0x79, 0x4e, 0xcf, 0xba, 0xb2, 0xa7, + 0x8a, 0x7b, 0xf0, 0x31, 0x91, 0xa4, 0x00, 0xe2, 0xff, 0x9f, 0x76, 0x6e, + 0x47, 0x9f, 0x75, 0x65, 0xff, 0x09, 0x13, 0x09, 0x1a, 0x79, 0x96, 0x5e, + 0x82, 0xd9, 0x65, 0xf1, 0xf8, 0xd8, 0xb2, 0xde, 0x33, 0x7b, 0x30, 0xe5, + 0xee, 0x74, 0xd6, 0x5d, 0x30, 0x16, 0x5d, 0x01, 0x83, 0x68, 0x43, 0x97, + 0xfd, 0x1d, 0xf3, 0xfe, 0xff, 0x71, 0x65, 0x0c, 0xf9, 0x08, 0xaa, 0xff, + 0xff, 0x10, 0x1c, 0x3c, 0x03, 0x9c, 0xa0, 0x83, 0x9f, 0x75, 0x65, 0xff, + 0x7f, 0x3b, 0xc6, 0x10, 0x6b, 0xf5, 0x97, 0xb7, 0x23, 0x75, 0x65, 0x68, + 0xf8, 0x37, 0x0f, 0xaf, 0x4c, 0x0d, 0x2c, 0xbd, 0x87, 0xd5, 0x97, 0xfc, + 0x7a, 0xcf, 0x9b, 0xb4, 0xd3, 0x4b, 0x2b, 0xc7, 0xb4, 0xe3, 0x75, 0x08, + 0xbe, 0x72, 0x5e, 0x3c, 0x5f, 0xff, 0x14, 0x4a, 0x70, 0x9d, 0x32, 0x04, + 0xb3, 0x8b, 0x2f, 0xff, 0xde, 0x07, 0x19, 0x1f, 0x07, 0xc6, 0xc2, 0x3d, + 0x96, 0x50, 0xd5, 0xd6, 0x63, 0xc8, 0x61, 0x7e, 0xc2, 0x1f, 0x46, 0xfb, + 0xf9, 0x77, 0x14, 0x6e, 0xe0, 0x16, 0x5e, 0xfd, 0xd8, 0xb2, 0xec, 0xfd, + 0x65, 0x41, 0xb4, 0xc1, 0xdb, 0xcf, 0x8c, 0x59, 0x71, 0x4b, 0x64, 0x42, + 0x92, 0x5e, 0xe0, 0xfd, 0xf4, 0x78, 0x1a, 0x59, 0x7f, 0x70, 0xb0, 0x64, + 0xeb, 0x28, 0x8f, 0x35, 0xa2, 0x2b, 0x82, 0x6b, 0x2f, 0xc5, 0x1b, 0x47, + 0xcb, 0x2d, 0xc0, 0x9b, 0xe3, 0x16, 0xa8, 0x4c, 0x44, 0xe1, 0x16, 0xec, + 0x17, 0xdc, 0x28, 0xd9, 0x65, 0xff, 0x6c, 0xfe, 0x71, 0x93, 0x85, 0x65, + 0x68, 0xf6, 0x88, 0x8a, 0xff, 0xf9, 0xac, 0x0f, 0x33, 0xb3, 0x60, 0xa5, + 0x9f, 0xac, 0xac, 0x3f, 0x33, 0x21, 0xbf, 0xec, 0x6b, 0x34, 0x17, 0x70, + 0xaa, 0x30, 0xe5, 0xff, 0xc0, 0xe0, 0xfc, 0xec, 0xfa, 0x46, 0x4b, 0x2f, + 0xdd, 0xf3, 0x97, 0xeb, 0x2b, 0x64, 0x59, 0x79, 0x15, 0xd1, 0x6f, 0xfc, + 0xfd, 0xc9, 0x9e, 0x0a, 0x26, 0x59, 0x7f, 0xef, 0x1e, 0x09, 0x93, 0x35, + 0x1f, 0x2c, 0xb1, 0xe9, 0x15, 0xde, 0x30, 0xe1, 0xed, 0xc1, 0x0a, 0xa3, + 0x04, 0x54, 0x93, 0xd9, 0x0c, 0x70, 0x1f, 0x9a, 0xdf, 0xe9, 0xeb, 0x83, + 0x2c, 0xf3, 0x65, 0x65, 0xd8, 0x4b, 0x2f, 0xff, 0x76, 0x08, 0x39, 0xdf, + 0x1e, 0xbf, 0x0a, 0xcb, 0xe3, 0xe7, 0x8d, 0x65, 0xec, 0xfb, 0xab, 0x28, + 0x66, 0xf9, 0x84, 0x37, 0xf1, 0x96, 0x77, 0xc6, 0xb2, 0xd1, 0x87, 0x99, + 0xc2, 0x1a, 0x1a, 0x61, 0xf8, 0x2a, 0x08, 0x6a, 0x5f, 0xfb, 0xc7, 0xbf, + 0xd9, 0xad, 0x1e, 0xf5, 0x97, 0xfa, 0x19, 0xce, 0x39, 0x6c, 0xb2, 0xff, + 0xbf, 0xe4, 0x8f, 0xce, 0x7b, 0x2c, 0xbe, 0xe6, 0x17, 0x56, 0x5e, 0xdc, + 0x8d, 0x2c, 0xa3, 0x3f, 0xa7, 0x3a, 0x01, 0x0d, 0xef, 0x3f, 0x56, 0x5e, + 0xeb, 0xee, 0xac, 0xbf, 0xdd, 0xf1, 0xb7, 0xfd, 0xcd, 0x65, 0xfe, 0x17, + 0xd1, 0x20, 0x3e, 0x96, 0x58, 0x96, 0x51, 0x9e, 0x20, 0x0d, 0x2e, 0x13, + 0x16, 0x56, 0xca, 0xe2, 0x07, 0x1a, 0x5c, 0xc6, 0xbe, 0x42, 0x28, 0x56, + 0xf0, 0xb7, 0xa3, 0x80, 0x1f, 0x9f, 0x79, 0x10, 0x86, 0xfc, 0x2f, 0xe7, + 0x37, 0x16, 0x5f, 0x78, 0xde, 0x4b, 0x2f, 0x0d, 0xe4, 0xb2, 0xa0, 0xde, + 0xe1, 0x0d, 0x81, 0x08, 0x87, 0xfd, 0xa2, 0xff, 0xf4, 0xe2, 0x7e, 0xc6, + 0x81, 0x2d, 0x44, 0x96, 0x5f, 0xa3, 0xbe, 0x89, 0x96, 0x5d, 0xde, 0x2c, + 0xbf, 0x38, 0xad, 0x46, 0x96, 0x51, 0xa2, 0xcf, 0xc9, 0x6e, 0x50, 0x42, + 0xf7, 0xf8, 0xcb, 0xb2, 0xcd, 0x62, 0xcb, 0xff, 0x8c, 0x25, 0x8c, 0x89, + 0x87, 0x1b, 0x2c, 0xbf, 0xfa, 0x45, 0x8c, 0x72, 0xcf, 0x3b, 0x16, 0x5f, + 0xec, 0xec, 0x6f, 0x32, 0xfd, 0x65, 0xff, 0xf7, 0xec, 0x79, 0x4b, 0x3f, + 0x13, 0xbb, 0x70, 0x0b, 0x28, 0x28, 0x89, 0xd1, 0xa5, 0xfe, 0xd4, 0x18, + 0xc9, 0xe4, 0xb2, 0xff, 0xb3, 0x7c, 0x67, 0xdf, 0x75, 0xb5, 0xac, 0xbf, + 0x81, 0x1d, 0xe6, 0x14, 0xe3, 0xf3, 0xf1, 0x8d, 0xf8, 0x3e, 0xf9, 0xf7, + 0x56, 0x5f, 0xee, 0x44, 0x85, 0x17, 0x37, 0x56, 0x5f, 0xf8, 0x64, 0x0e, + 0xf2, 0x0a, 0x38, 0xb2, 0xa0, 0xfd, 0x4c, 0xe2, 0xa4, 0xab, 0x6c, 0x26, + 0x3f, 0x22, 0x1c, 0x32, 0x8a, 0x12, 0xc0, 0x45, 0xdf, 0x0a, 0x1b, 0xff, + 0x3e, 0xb3, 0x7e, 0x0f, 0x44, 0xd2, 0xcb, 0xff, 0xf0, 0x0a, 0x58, 0x5d, + 0x39, 0xfc, 0xe9, 0x3c, 0x96, 0x5f, 0xe8, 0xfe, 0x3d, 0x9f, 0x49, 0x65, + 0x1a, 0x22, 0x49, 0x56, 0xbf, 0x47, 0x57, 0x61, 0x93, 0x7e, 0x9d, 0x33, + 0xcc, 0xeb, 0x2f, 0xfb, 0xce, 0x59, 0x34, 0xa3, 0xe5, 0x97, 0xff, 0x78, + 0xf5, 0x9c, 0x2c, 0xdf, 0xe3, 0x59, 0x7e, 0x2c, 0xe0, 0x27, 0x61, 0xfe, + 0xcc, 0x73, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0x81, 0x97, 0xf3, 0xed, 0xfb, + 0x90, 0xd1, 0x58, 0x3d, 0x7b, 0x38, 0x0c, 0x3f, 0x09, 0xf3, 0x4b, 0xde, + 0x79, 0x4e, 0x47, 0x29, 0x42, 0xc2, 0xff, 0xbc, 0x78, 0x58, 0x11, 0xe2, + 0xca, 0x62, 0xa2, 0x62, 0x29, 0xec, 0x71, 0xbb, 0xce, 0xaf, 0x74, 0x10, + 0xb2, 0xfb, 0xfd, 0x73, 0x8b, 0x2f, 0xf7, 0xfd, 0x3d, 0x99, 0x01, 0x59, + 0x5f, 0x1f, 0xab, 0x8e, 0x11, 0x25, 0xf8, 0x73, 0xcb, 0x8e, 0x79, 0x59, + 0x7f, 0x07, 0xef, 0x7c, 0xfb, 0xab, 0x2f, 0xf4, 0x69, 0xf5, 0xdc, 0x0a, + 0xcb, 0xff, 0x8c, 0x73, 0xb3, 0x3e, 0xfd, 0xcb, 0xab, 0x2d, 0x29, 0x1f, + 0xc7, 0x0c, 0xaa, 0x11, 0xa6, 0xf0, 0xab, 0xa8, 0x5d, 0x9a, 0xc9, 0x70, + 0xc7, 0x0b, 0xb7, 0x2e, 0x04, 0x60, 0xb7, 0x1c, 0x96, 0x5f, 0xdf, 0x74, + 0x79, 0x82, 0xac, 0xa1, 0x9e, 0x2e, 0x0b, 0x5a, 0x16, 0x5f, 0x41, 0x1f, + 0xeb, 0x2d, 0x9e, 0x36, 0x3d, 0x10, 0xbf, 0xda, 0x27, 0x68, 0x4f, 0x9a, + 0x59, 0x77, 0xfb, 0xd6, 0x50, 0xcf, 0x47, 0xc6, 0xf7, 0xff, 0x8f, 0xf0, + 0xf8, 0xfc, 0x51, 0x9a, 0xc5, 0x97, 0xfd, 0xf0, 0xe3, 0x86, 0x3f, 0x42, + 0xcb, 0xcf, 0xad, 0x95, 0x18, 0x2a, 0xff, 0xcf, 0xac, 0x2f, 0xf9, 0xde, + 0x7c, 0xb2, 0xff, 0xff, 0xfe, 0xce, 0xf9, 0xc8, 0x59, 0xdc, 0xd3, 0xbf, + 0x77, 0xe0, 0xe7, 0x63, 0x1c, 0xbe, 0x92, 0xe2, 0x0b, 0x5f, 0xf9, 0xdc, + 0x50, 0xb8, 0xe7, 0x0a, 0xd2, 0xe2, 0x0b, 0x5f, 0xfd, 0xe3, 0xf1, 0xc1, + 0x76, 0x70, 0xad, 0x2e, 0x20, 0xb5, 0xfe, 0x83, 0x2e, 0xce, 0x15, 0xa5, + 0xc4, 0x16, 0xbf, 0x99, 0x83, 0x9c, 0x2b, 0x4b, 0x88, 0x2d, 0x7f, 0xff, + 0xce, 0x44, 0x6c, 0x9d, 0xc0, 0xf8, 0xf4, 0x62, 0xed, 0x8d, 0x2e, 0x20, + 0xb5, 0xdf, 0x4e, 0x1a, 0x73, 0x81, 0x50, 0xd2, 0xa3, 0xa1, 0x91, 0xfd, + 0x42, 0xac, 0xff, 0x20, 0x14, 0xa3, 0x7b, 0xfc, 0x70, 0x2f, 0x3b, 0xcf, + 0x96, 0x5f, 0x3e, 0xbf, 0x75, 0x97, 0xff, 0x78, 0xfc, 0x70, 0x5d, 0x9c, + 0x2b, 0x4b, 0x88, 0x2d, 0x7f, 0xd3, 0x70, 0x27, 0xb4, 0xe1, 0x5a, 0x5c, + 0x41, 0x6b, 0xf7, 0x79, 0x0c, 0x9c, 0x14, 0x50, 0x88, 0xa9, 0x7f, 0xfa, + 0x70, 0x7c, 0x61, 0x8e, 0xf2, 0x70, 0xad, 0x2e, 0x20, 0xb5, 0xff, 0xff, + 0xc4, 0x46, 0xc9, 0xc0, 0xc9, 0xdc, 0x0f, 0x8f, 0x46, 0x2e, 0xd8, 0xd2, + 0xe2, 0x0b, 0x56, 0x26, 0x4b, 0xf2, 0x1b, 0xaf, 0x5f, 0xf7, 0x8f, 0x46, + 0x2e, 0xd8, 0xd2, 0xe2, 0x0b, 0x5f, 0xff, 0x3b, 0xfd, 0x2e, 0x78, 0xc6, + 0x38, 0xd4, 0x24, 0xbf, 0xf6, 0x4a, 0x40, 0xe6, 0x8b, 0x69, 0xf5, 0xc4, + 0x16, 0xa0, 0xa3, 0x9f, 0x49, 0x1c, 0x4f, 0xbf, 0xf0, 0x4f, 0x5d, 0x7f, + 0xf9, 0x39, 0xa5, 0xc4, 0x16, 0xbf, 0xbc, 0x7e, 0xf1, 0xfe, 0xa8, 0x02, + 0xd7, 0xec, 0xfe, 0x70, 0xad, 0x2e, 0x20, 0xb5, 0xd9, 0xd0, 0x9f, 0xaf, + 0x8e, 0xeb, 0xe4, 0x79, 0x72, 0x18, 0xb7, 0xf3, 0x30, 0x73, 0x85, 0x69, + 0x71, 0x05, 0xaf, 0xfc, 0x1f, 0x1e, 0x8c, 0x5d, 0xb1, 0xa5, 0xc4, 0x16, + 0xbb, 0x27, 0x3a, 0x23, 0x78, 0x7f, 0x7f, 0x80, 0x6c, 0x72, 0xfa, 0x4b, + 0x88, 0x2d, 0x7f, 0xec, 0x3d, 0xf8, 0x58, 0x37, 0x92, 0xe2, 0x0b, 0x30, + 0xf0, 0x28, 0x6b, 0xc1, 0xba, 0x37, 0xfc, 0xd4, 0xa3, 0xe8, 0xe4, 0x62, + 0xfd, 0x8c, 0x88, 0x10, 0xb6, 0x69, 0xbe, 0xef, 0xe1, 0x51, 0x05, 0x9b, + 0xa2, 0x32, 0xe7, 0x0a, 0xcb, 0x70, 0x6c, 0xa1, 0x90, 0x9c, 0xff, 0x49, + 0x92, 0xe9, 0xc5, 0xd2, 0xdc, 0x59, 0x7a, 0x25, 0xb8, 0xb2, 0xa0, 0xdc, + 0x90, 0xd5, 0x6c, 0xcb, 0xf5, 0x1a, 0x97, 0xce, 0xfa, 0x22, 0xfe, 0x94, + 0xc2, 0x50, 0x8a, 0xbf, 0xfb, 0x24, 0x59, 0xdf, 0x1e, 0x7d, 0xd5, 0x97, + 0xfa, 0x3c, 0x6d, 0x7d, 0x2e, 0x2c, 0xbf, 0x47, 0x79, 0xe3, 0x59, 0x7f, + 0xed, 0x60, 0xbe, 0xc6, 0x03, 0x5b, 0x2c, 0xbf, 0xef, 0x8b, 0x0d, 0x98, + 0x5f, 0xac, 0xbf, 0xff, 0xff, 0x3c, 0xc4, 0xed, 0x66, 0xf3, 0xe7, 0xb3, + 0xf9, 0xfc, 0xef, 0x1d, 0x99, 0xf7, 0x56, 0x5f, 0xa5, 0xde, 0x61, 0x2c, + 0xbf, 0xfc, 0xe1, 0x82, 0x19, 0x66, 0xfd, 0x1f, 0x16, 0x5f, 0xfb, 0x93, + 0x48, 0x03, 0x9a, 0x40, 0x1a, 0xca, 0xd9, 0x50, 0x8c, 0x8d, 0x46, 0x4f, + 0x88, 0x22, 0x9c, 0x7d, 0x08, 0x9f, 0x13, 0xf5, 0x26, 0xff, 0xb3, 0xad, + 0x61, 0x0f, 0xd0, 0xb2, 0xff, 0xe8, 0xfa, 0x42, 0x33, 0x9d, 0x8f, 0x9a, + 0x59, 0x7e, 0x96, 0xe6, 0xe0, 0x36, 0x59, 0x6f, 0x19, 0xfc, 0xba, 0x4d, + 0xf7, 0x4a, 0x18, 0xb2, 0xfd, 0xa8, 0xfa, 0x5c, 0x59, 0x5b, 0x26, 0x43, + 0x28, 0x58, 0x99, 0x36, 0x88, 0x6f, 0x31, 0xfa, 0xb2, 0xff, 0xfc, 0x42, + 0x74, 0xc8, 0x12, 0xce, 0x7f, 0xde, 0xfc, 0xb2, 0xff, 0xc0, 0xe4, 0xfe, + 0x6b, 0xf6, 0x1f, 0x16, 0x5f, 0xfd, 0xbf, 0x7c, 0x71, 0xfd, 0xf4, 0xb3, + 0xab, 0x2b, 0x11, 0xc9, 0xfa, 0xc0, 0x88, 0x77, 0xfb, 0xf0, 0x74, 0xa3, + 0xbc, 0x59, 0x52, 0x4d, 0x97, 0xf8, 0xc0, 0x38, 0x63, 0x50, 0xad, 0xef, + 0x23, 0x9b, 0x78, 0xea, 0x2f, 0x30, 0xf8, 0xb2, 0xfd, 0x28, 0xde, 0xfe, + 0x59, 0x5b, 0x1e, 0x2b, 0x8e, 0x5f, 0xfd, 0x9f, 0x77, 0x86, 0x51, 0xfb, + 0xb1, 0x65, 0xff, 0xfb, 0x3b, 0xe3, 0x9c, 0x2b, 0xce, 0x2c, 0x69, 0xff, + 0x59, 0x7f, 0xb8, 0xfe, 0x3e, 0x44, 0x96, 0x5f, 0xf9, 0xb7, 0x84, 0x58, + 0x24, 0x17, 0xeb, 0x2f, 0xf6, 0x78, 0xf8, 0x60, 0x92, 0xca, 0xd9, 0x34, + 0x32, 0x44, 0xea, 0xd0, 0x0c, 0xb7, 0x10, 0x6f, 0xbe, 0xe4, 0x7c, 0xb2, + 0xff, 0xd2, 0xcf, 0xa5, 0xa8, 0xeb, 0xf5, 0x65, 0xe6, 0x44, 0x96, 0x5f, + 0xb0, 0x7e, 0x03, 0x4b, 0x2a, 0x72, 0x2a, 0x26, 0x24, 0x73, 0xf2, 0x1c, + 0xbf, 0xe0, 0xf8, 0xc7, 0xe8, 0xef, 0x16, 0x5e, 0x1b, 0xf5, 0x65, 0xff, + 0xfe, 0x28, 0xfb, 0xbd, 0xc9, 0xa5, 0x9b, 0x7d, 0x05, 0x1c, 0x59, 0x7f, + 0xee, 0x41, 0x44, 0xbb, 0xec, 0x25, 0x95, 0xa4, 0xcc, 0x58, 0x7c, 0xe7, + 0x3f, 0x8e, 0x71, 0x8a, 0xf1, 0xcf, 0xe2, 0xcb, 0xfb, 0x86, 0x3c, 0x6b, + 0x8b, 0x2f, 0xe7, 0xd3, 0x8c, 0xf1, 0x65, 0xda, 0xfd, 0x65, 0xd8, 0xd2, + 0xca, 0x73, 0x5f, 0xd1, 0x8b, 0x7d, 0x08, 0xc6, 0xf8, 0x7b, 0xc5, 0xdd, + 0x5a, 0xbe, 0x6b, 0x3c, 0xeb, 0x2f, 0x85, 0xd1, 0xb4, 0xb2, 0xfd, 0xb3, + 0xfa, 0x37, 0x56, 0x54, 0xf4, 0x79, 0xf8, 0x49, 0x7f, 0x0b, 0xe8, 0xfb, + 0xb8, 0xb2, 0xff, 0x16, 0x0f, 0xd0, 0xd1, 0x2c, 0xa8, 0x3e, 0x1d, 0x17, + 0xd4, 0x2e, 0xfd, 0x64, 0x6c, 0xa7, 0x28, 0x9d, 0xe3, 0x11, 0xfd, 0x10, + 0x9c, 0x41, 0x08, 0x7b, 0xe2, 0x9b, 0x98, 0xb2, 0xfd, 0x2c, 0xdb, 0xb0, + 0xb2, 0xff, 0xe2, 0x8d, 0x99, 0xe3, 0xd6, 0x9e, 0x4b, 0x29, 0x65, 0x1a, + 0x26, 0x66, 0x22, 0x22, 0x89, 0xf4, 0x4b, 0xfd, 0xb6, 0xd0, 0x5f, 0x67, + 0x56, 0x5f, 0x87, 0x98, 0x41, 0x59, 0x74, 0x4c, 0xb2, 0xb6, 0x37, 0xbf, + 0x93, 0x56, 0x23, 0x50, 0xcf, 0xc0, 0xe5, 0x7f, 0xf6, 0x88, 0x1f, 0x74, + 0xb3, 0x67, 0x25, 0x97, 0xff, 0xf0, 0xfc, 0x0d, 0x8f, 0x35, 0xac, 0x8f, + 0xa4, 0x6c, 0x59, 0x7f, 0x9e, 0x62, 0x07, 0x4a, 0x16, 0x5f, 0xe7, 0x18, + 0x9d, 0xe9, 0xb4, 0xb2, 0xf3, 0xeb, 0x65, 0x97, 0x7f, 0x0b, 0x2f, 0x8e, + 0x19, 0x8b, 0x29, 0x65, 0xfc, 0xe2, 0xf6, 0x35, 0xfa, 0xca, 0x19, 0xba, + 0x20, 0xbb, 0xff, 0xfd, 0x06, 0x32, 0x80, 0x07, 0xb8, 0x11, 0xc0, 0x44, + 0x62, 0xcb, 0xbf, 0x85, 0x97, 0x40, 0xab, 0x2f, 0xfb, 0x3b, 0xc8, 0x60, + 0x99, 0x32, 0xcb, 0xfd, 0xcc, 0xf1, 0xf0, 0x1b, 0x2c, 0xb9, 0xa6, 0x92, + 0x5f, 0xf1, 0x66, 0xf7, 0x97, 0x0c, 0x6b, 0x28, 0x29, 0xfe, 0x18, 0xee, + 0x85, 0xd8, 0xb5, 0xe2, 0x0f, 0xd8, 0x1b, 0x62, 0xe4, 0x2f, 0xc3, 0xa6, + 0x8d, 0x04, 0x19, 0xbc, 0xd3, 0x4d, 0x24, 0xb3, 0x12, 0x37, 0x34, 0x17, + 0xd3, 0x3b, 0xe9, 0x23, 0x74, 0x70, 0x89, 0x0b, 0xea, 0xd9, 0x70, 0xb4, + 0x28, 0x9f, 0x2e, 0x78, 0xc9, 0xe5, 0x89, 0x5f, 0xdd, 0x8c, 0xf6, 0x6c, + 0xb2, 0xec, 0x62, 0xca, 0xf8, 0xf0, 0xf8, 0x5b, 0x50, 0xee, 0x7c, 0xe5, + 0x28, 0xa8, 0x69, 0x19, 0x2f, 0xac, 0x59, 0x40, 0xff, 0x4a, 0xd9, 0x39, + 0x78, 0x1a, 0x8c, 0x91, 0x90, 0xdc, 0xf5, 0x22, 0xbd, 0xe9, 0x92, 0xbf, + 0xb5, 0x94, 0xee, 0x7f, 0x27, 0x9e, 0xbb, 0x1f, 0x40, 0x27, 0x16, 0xf7, + 0xc2, 0x62, 0xe1, 0x3a, 0xb2, 0xfe, 0xcf, 0xff, 0x3d, 0x49, 0x65, 0xc1, + 0xc5, 0x94, 0x33, 0xc5, 0x72, 0xfb, 0xb1, 0xa5, 0x97, 0x47, 0x56, 0x56, + 0xc6, 0xb4, 0x22, 0xf6, 0x62, 0xca, 0xc3, 0x67, 0xd2, 0x2b, 0xfd, 0x23, + 0x2c, 0x60, 0x3c, 0xb2, 0xff, 0xd9, 0xde, 0x78, 0xd8, 0xf9, 0xa5, 0x95, + 0x07, 0xde, 0x46, 0x57, 0xec, 0x9d, 0x9f, 0xf5, 0x65, 0xf4, 0xee, 0xc6, + 0xf5, 0x97, 0xec, 0x0c, 0x30, 0xd6, 0x5f, 0x63, 0x33, 0xab, 0x2f, 0xfd, + 0xe3, 0xdf, 0xac, 0x9b, 0x3e, 0xea, 0xcb, 0x9c, 0x55, 0x95, 0xa3, 0xd9, + 0xea, 0x15, 0xf3, 0xb1, 0xe6, 0x59, 0x7e, 0x38, 0x6a, 0x26, 0x59, 0x7c, + 0xfd, 0xfb, 0x8b, 0x28, 0xcf, 0x2c, 0x8a, 0x2f, 0xff, 0xfc, 0xf3, 0x02, + 0x40, 0x0c, 0xef, 0x1f, 0x7b, 0x1f, 0x46, 0xd1, 0xc5, 0x97, 0xe2, 0x7d, + 0xdd, 0xa6, 0x59, 0x7d, 0x37, 0x23, 0xab, 0x28, 0x28, 0xc0, 0x99, 0xc4, + 0x05, 0x97, 0xff, 0xf8, 0x9d, 0xf7, 0xc4, 0xbd, 0x1a, 0xda, 0x3b, 0xc7, + 0x92, 0xcb, 0xfa, 0x03, 0xde, 0x3f, 0xcb, 0x2b, 0x75, 0x11, 0xe4, 0xc3, + 0x78, 0xfe, 0x99, 0x65, 0xf0, 0x1b, 0x6c, 0x35, 0x97, 0xec, 0xcd, 0x81, + 0xc5, 0x97, 0xda, 0x23, 0xfd, 0x65, 0xf1, 0x85, 0xf4, 0xb2, 0xe8, 0xfd, + 0x65, 0xf0, 0x36, 0x03, 0x7f, 0x1b, 0x81, 0x08, 0x68, 0xd1, 0x2f, 0xe5, + 0xab, 0x7e, 0xb2, 0xec, 0x0a, 0xcb, 0xf6, 0x6b, 0x47, 0xa5, 0x95, 0xba, + 0x79, 0xfe, 0x12, 0xfc, 0x5a, 0xfc, 0xf1, 0xf6, 0x05, 0x65, 0xff, 0x79, + 0xf9, 0xe3, 0xd9, 0xc9, 0x65, 0xf3, 0xec, 0x24, 0x96, 0x53, 0x65, 0x7b, + 0xee, 0x7b, 0x5d, 0x88, 0x40, 0xca, 0x11, 0xe3, 0x20, 0xc2, 0xa1, 0x49, + 0xbe, 0x26, 0x37, 0xad, 0xd2, 0x29, 0x9b, 0x75, 0x0e, 0x07, 0x86, 0x07, + 0xe4, 0xad, 0xb1, 0xee, 0x13, 0x76, 0x17, 0xe0, 0x71, 0x68, 0xcc, 0x42, + 0x7d, 0xc3, 0x8b, 0xf8, 0x80, 0x36, 0x3c, 0x96, 0x5e, 0x3d, 0x80, 0xb2, + 0xfd, 0x1f, 0x9f, 0xd8, 0xb2, 0xe6, 0x01, 0x65, 0xcc, 0xde, 0xb2, 0xc3, + 0x83, 0xd8, 0x32, 0x82, 0x17, 0xbf, 0xfd, 0x22, 0x89, 0xcc, 0x28, 0x17, + 0xa7, 0xb2, 0xcb, 0xf3, 0xb3, 0x3c, 0xc5, 0x95, 0xf1, 0xf9, 0xf5, 0x32, + 0xf7, 0x75, 0x0b, 0x2f, 0xb3, 0x61, 0x24, 0xb2, 0xf0, 0x85, 0xfa, 0xca, + 0x23, 0xc2, 0xe9, 0x25, 0xfd, 0xdd, 0xf8, 0x58, 0x35, 0x95, 0x31, 0xe7, + 0xf0, 0x86, 0xfe, 0xce, 0x64, 0x8f, 0x4b, 0x2d, 0xbd, 0x65, 0xff, 0x6b, + 0xce, 0x23, 0xf8, 0xff, 0x59, 0x6f, 0x41, 0xe6, 0x98, 0x9d, 0xee, 0x4b, + 0x65, 0x95, 0x87, 0x88, 0x21, 0x35, 0x42, 0xb1, 0x99, 0x16, 0xe3, 0xa9, + 0xc2, 0x95, 0x84, 0x6f, 0x0a, 0xd2, 0x23, 0xec, 0x2f, 0x6f, 0x3c, 0xe1, + 0x16, 0x5e, 0x98, 0xfc, 0xb2, 0xfe, 0x8d, 0x3c, 0xc7, 0xe5, 0x97, 0xee, + 0x0e, 0x0b, 0x67, 0x3c, 0xae, 0x0e, 0xdf, 0xd9, 0xb4, 0x7b, 0xd0, 0xb2, + 0xfd, 0x93, 0x7b, 0x09, 0x65, 0xfd, 0x18, 0x21, 0x03, 0xf5, 0x95, 0xa4, + 0x41, 0x78, 0xb4, 0x89, 0xef, 0x32, 0x18, 0xb2, 0xbe, 0x4c, 0x2f, 0x50, + 0xc0, 0x61, 0x7d, 0xff, 0xf1, 0x4a, 0x76, 0x0c, 0xf9, 0xf7, 0x40, 0xfc, + 0x59, 0x7e, 0xf0, 0x0a, 0x37, 0xac, 0xbf, 0x89, 0xfb, 0x28, 0x15, 0x65, + 0x41, 0xeb, 0x08, 0x53, 0x7f, 0xfb, 0x91, 0xa0, 0xf7, 0x87, 0xdf, 0x60, + 0xd6, 0x5f, 0x66, 0x7d, 0x25, 0x97, 0xb8, 0x73, 0x2c, 0xb0, 0xa4, 0x6f, + 0xf8, 0x45, 0x7f, 0xda, 0x38, 0xee, 0x6f, 0x71, 0xac, 0xbf, 0xf6, 0xc5, + 0x1f, 0x18, 0xca, 0x36, 0x59, 0x7f, 0xce, 0xc8, 0xf3, 0xf2, 0x37, 0x56, + 0x53, 0x9f, 0xc9, 0x1f, 0xd0, 0x53, 0x82, 0xd4, 0x22, 0x7c, 0x4e, 0x50, + 0xb2, 0xbf, 0x4e, 0xf4, 0x6a, 0x4b, 0x2a, 0x72, 0xe3, 0x44, 0x46, 0xe9, + 0x86, 0xe1, 0x85, 0x43, 0xc7, 0x48, 0xd2, 0x3d, 0xe6, 0x72, 0x16, 0x5f, + 0xdb, 0x4d, 0x29, 0xef, 0x5b, 0x2c, 0xbe, 0x66, 0x3f, 0x96, 0x5b, 0x65, + 0x97, 0xee, 0xf8, 0xd9, 0xa5, 0x95, 0x23, 0x75, 0xa1, 0x2b, 0xde, 0x19, + 0xac, 0xbf, 0x7b, 0x0e, 0x5c, 0x59, 0x7f, 0xfd, 0xe8, 0x11, 0xbf, 0x49, + 0xff, 0xe7, 0x23, 0xf4, 0x97, 0xec, 0xeb, 0x81, 0xb6, 0xb2, 0xfd, 0xa0, + 0xbb, 0x85, 0x71, 0x02, 0x2f, 0x72, 0x3e, 0x59, 0x7c, 0x7c, 0xd4, 0x96, 0x5b, 0xfc, 0x3f, 0x3f, 0x8d, 0x18, 0x3b, 0x7c, 0x58, 0xd6, 0xe2, 0xcb, 0x8f, 0x65, 0x94, 0x66, 0xf3, 0xa4, 0xb7, 0xdb, 0x13, 0xb1, 0x65, 0xce, - 0x4b, 0x2a, 0x4d, 0xbe, 0xc4, 0x55, 0x05, 0x40, 0x63, 0x27, 0xf9, 0x53, + 0x4b, 0x2a, 0x0d, 0xbe, 0xc4, 0x55, 0x25, 0x40, 0x63, 0x27, 0xf9, 0x53, 0x50, 0x9e, 0x63, 0x97, 0x55, 0xef, 0xe7, 0xd6, 0x6f, 0xc1, 0xac, 0xbf, - 0x05, 0xc5, 0xf1, 0xac, 0xaf, 0xcf, 0x58, 0x8b, 0xaf, 0x0d, 0xe0, 0xb2, - 0xd0, 0x59, 0x52, 0x6b, 0x77, 0x07, 0x2f, 0xbb, 0xb3, 0x92, 0xcb, 0xcc, - 0x90, 0xac, 0xb9, 0xc5, 0x59, 0x58, 0x6c, 0xc8, 0x72, 0xff, 0x0b, 0x9b, - 0x03, 0xbb, 0xfa, 0xb2, 0xff, 0x9e, 0x1a, 0x9d, 0x9f, 0x41, 0x59, 0x63, - 0x59, 0x78, 0x53, 0xea, 0xcb, 0xdc, 0x7e, 0xac, 0xa9, 0x3c, 0x91, 0x44, - 0x3e, 0x1d, 0xba, 0x37, 0x96, 0x5f, 0xcc, 0xe9, 0x8c, 0x12, 0xb2, 0xf3, - 0x3a, 0x15, 0x96, 0xec, 0x9e, 0x56, 0x16, 0xdf, 0xd2, 0xcc, 0x16, 0x78, + 0x05, 0xc5, 0xf1, 0xac, 0xaf, 0xcf, 0x58, 0x8b, 0xaf, 0x0d, 0xe4, 0xb2, + 0xd2, 0x59, 0x50, 0x6b, 0x77, 0x07, 0x2f, 0xbb, 0xb3, 0x92, 0xcb, 0xcc, + 0x80, 0xac, 0xb9, 0xc5, 0x59, 0x58, 0x6c, 0xc8, 0x72, 0xff, 0x0b, 0x9b, + 0x03, 0xbb, 0xfa, 0xb2, 0xff, 0x9e, 0x5a, 0x8d, 0x9f, 0x41, 0x59, 0x63, + 0x59, 0x78, 0x53, 0xea, 0xcb, 0xdc, 0x7e, 0xac, 0xa8, 0x3c, 0x91, 0x44, + 0x3e, 0x1d, 0xba, 0x77, 0x96, 0x5f, 0xcc, 0xe9, 0x8c, 0x10, 0xb2, 0xf3, + 0x3a, 0x15, 0x96, 0xec, 0x1e, 0x56, 0x16, 0xdf, 0xd0, 0xcc, 0x16, 0x38, 0xb2, 0xb6, 0x54, 0x6f, 0x84, 0x61, 0x55, 0xd0, 0xfb, 0x0e, 0x3d, 0x08, 0x2f, 0xcc, 0x09, 0x8f, 0xa4, 0xd7, 0xcc, 0xcd, 0x62, 0xcb, 0xfe, 0xff, - 0xd9, 0xf4, 0x3a, 0x71, 0x2c, 0xac, 0x3d, 0xd0, 0x10, 0xdd, 0xa0, 0x2c, - 0xbf, 0xc4, 0xed, 0x73, 0x93, 0xfa, 0xcb, 0xd2, 0xfa, 0x59, 0x7f, 0xda, - 0xd1, 0xfd, 0x0e, 0xbf, 0xcb, 0x2f, 0x47, 0xb8, 0xab, 0x2f, 0xfa, 0x59, - 0x0c, 0xdb, 0x00, 0xdb, 0x59, 0x7f, 0xd3, 0xf9, 0xc3, 0xc6, 0x5f, 0xac, + 0xd9, 0xf4, 0xba, 0x73, 0x2c, 0xac, 0x3d, 0xd0, 0x10, 0xdd, 0xa0, 0x2c, + 0xbf, 0xc4, 0xed, 0x73, 0x91, 0xfa, 0xcb, 0xd0, 0xfa, 0x59, 0x7f, 0xda, + 0xd1, 0xfd, 0x2e, 0xbf, 0xcb, 0x2f, 0x4f, 0xb8, 0xab, 0x2f, 0xfa, 0x19, + 0x2c, 0xdb, 0x00, 0xdb, 0x59, 0x7f, 0xd1, 0xf9, 0xcb, 0xc6, 0x5f, 0xac, 0xb6, 0xcb, 0x2e, 0x7e, 0xe8, 0xf2, 0xdb, 0x67, 0x35, 0x88, 0xd9, 0x32, - 0x07, 0x84, 0x45, 0xe6, 0xa4, 0x45, 0x97, 0x9c, 0x86, 0xba, 0xbe, 0x97, - 0xd0, 0x04, 0x8a, 0xb2, 0xdf, 0xac, 0xac, 0x36, 0xae, 0x47, 0x52, 0xa9, + 0x07, 0x84, 0x45, 0xe6, 0xa0, 0x45, 0x97, 0x9c, 0x86, 0xba, 0xbe, 0x97, + 0xd2, 0x04, 0x0a, 0xb2, 0xdf, 0xac, 0xac, 0x36, 0xae, 0x47, 0x50, 0xa9, 0x18, 0x62, 0xe2, 0x9a, 0x18, 0xde, 0xa3, 0x14, 0xf1, 0x97, 0xe3, 0xdd, - 0x5d, 0xbf, 0xdd, 0xf6, 0x43, 0xc7, 0xbd, 0x65, 0xf3, 0x34, 0x0d, 0x96, - 0x5f, 0xff, 0x11, 0x8a, 0xdf, 0x9d, 0xf0, 0x1e, 0x10, 0x95, 0x95, 0x27, - 0xed, 0x84, 0x97, 0xef, 0x67, 0x79, 0x2b, 0x2f, 0xe9, 0x2e, 0x83, 0x3c, - 0xb2, 0xb6, 0x4e, 0x5e, 0x0f, 0x22, 0xc2, 0xac, 0x24, 0x1a, 0x27, 0xbe, - 0xc1, 0xbc, 0x16, 0x5f, 0x9c, 0x88, 0xd8, 0xb2, 0xf6, 0x6f, 0x11, 0x65, - 0xc7, 0xe5, 0x97, 0x86, 0xf0, 0x59, 0x7c, 0xec, 0xc6, 0x2c, 0xbe, 0x36, - 0x4e, 0xea, 0xcb, 0x8f, 0x51, 0x8f, 0x17, 0x08, 0x68, 0x68, 0xa5, 0xc1, - 0x6f, 0x30, 0xdf, 0x8d, 0x8e, 0x58, 0xb2, 0xa3, 0x33, 0x2e, 0x5b, 0x23, - 0x92, 0x6f, 0x05, 0xb1, 0x90, 0xe4, 0xa1, 0xf1, 0x61, 0x6a, 0x72, 0xa1, + 0x5d, 0xbf, 0xdd, 0xf6, 0x4b, 0xc7, 0xbd, 0x65, 0xf3, 0x34, 0x0d, 0x96, + 0x5f, 0xff, 0x11, 0x8a, 0xdf, 0x9d, 0xf0, 0x1e, 0x52, 0x85, 0x95, 0x07, + 0xed, 0x84, 0x97, 0xef, 0x67, 0x79, 0x0b, 0x2f, 0xe8, 0x2e, 0x83, 0x3c, + 0xb2, 0xb6, 0x4e, 0x5e, 0x4f, 0x22, 0xc2, 0xac, 0x24, 0x1a, 0x27, 0xbe, + 0xc1, 0xbc, 0x96, 0x5f, 0x9c, 0x88, 0xd8, 0xb2, 0xf6, 0x6f, 0x11, 0x65, + 0xc7, 0xe5, 0x97, 0x86, 0xf2, 0x59, 0x7c, 0xec, 0xc6, 0x2c, 0xbe, 0x36, + 0x46, 0xea, 0xcb, 0x8f, 0x53, 0x8f, 0x17, 0x08, 0x68, 0x68, 0xa5, 0xc1, + 0x6f, 0x30, 0xdf, 0x8d, 0x8e, 0x58, 0xb2, 0xa7, 0x33, 0x2e, 0x5b, 0x03, + 0x90, 0x6f, 0x25, 0xb1, 0x90, 0xe4, 0xa1, 0xf1, 0x61, 0x6a, 0x72, 0xa1, 0xbd, 0x0e, 0x27, 0x97, 0xfd, 0xfa, 0x91, 0x10, 0xf0, 0x94, 0x10, 0xc4, - 0x8f, 0x2f, 0xbf, 0x80, 0x39, 0xf4, 0x88, 0xb2, 0xfe, 0xcf, 0x60, 0x41, - 0x8b, 0x2e, 0xd3, 0x16, 0x5f, 0x1f, 0x66, 0x0b, 0x2b, 0x0d, 0xc7, 0x85, - 0xef, 0xc7, 0xd9, 0xef, 0x12, 0x5f, 0x06, 0x7b, 0xc4, 0x97, 0x34, 0xd2, - 0x4a, 0x81, 0xf1, 0x61, 0x3b, 0x44, 0x56, 0x69, 0x23, 0x73, 0x5f, 0x79, - 0xcb, 0xf5, 0x95, 0x87, 0x80, 0x29, 0x25, 0xf4, 0x44, 0xed, 0x2c, 0xbd, - 0x17, 0x8d, 0x65, 0xff, 0xf7, 0x64, 0x39, 0x85, 0xdf, 0x1c, 0x96, 0xcb, - 0x29, 0x65, 0x61, 0xec, 0x71, 0x36, 0x86, 0xa9, 0x7a, 0x22, 0xe6, 0x32, - 0x7f, 0x0f, 0x12, 0x22, 0xe1, 0x1b, 0x4e, 0xb7, 0xff, 0x8a, 0x7e, 0xff, - 0x0a, 0x61, 0xad, 0x4a, 0xcb, 0xff, 0x77, 0x98, 0x1f, 0x39, 0x77, 0x8b, - 0x2f, 0x19, 0x7e, 0xb2, 0x31, 0xbe, 0xa6, 0x22, 0xd7, 0x7c, 0x21, 0xef, - 0xff, 0x48, 0xac, 0x3c, 0x67, 0x78, 0x02, 0xea, 0xca, 0x93, 0xf4, 0xf1, - 0x55, 0xfd, 0x3e, 0x9f, 0x4e, 0xcb, 0x2f, 0xff, 0x64, 0x0d, 0xfe, 0x87, - 0x78, 0x46, 0x2a, 0xca, 0xd8, 0xfd, 0xe0, 0x5b, 0x73, 0xf1, 0x65, 0xbd, - 0x26, 0xe5, 0xc8, 0xef, 0xd0, 0xf0, 0x27, 0x4b, 0x2f, 0xf4, 0xef, 0x3d, - 0xe4, 0x0e, 0x2c, 0xbf, 0x8f, 0x8f, 0xf4, 0x31, 0x65, 0xce, 0xcd, 0x1f, - 0x1c, 0x79, 0xb5, 0xfe, 0x39, 0xe9, 0x4b, 0x38, 0xb2, 0xbe, 0x47, 0x67, - 0xa1, 0x16, 0x46, 0x17, 0xec, 0x03, 0x27, 0x8b, 0x29, 0x65, 0xd9, 0x16, - 0x8d, 0x97, 0x09, 0xef, 0xf8, 0xc0, 0xdb, 0xf4, 0x88, 0x5b, 0x2c, 0xbf, - 0xe9, 0xda, 0x7e, 0xe9, 0x66, 0xcb, 0x2b, 0x11, 0x4a, 0x12, 0xd3, 0x3e, - 0xbf, 0xf8, 0x4e, 0x1f, 0x22, 0x31, 0xe1, 0x7e, 0xb2, 0xf9, 0xfb, 0x9b, - 0x2c, 0xba, 0x7e, 0x59, 0x71, 0x4a, 0xcb, 0x33, 0xc7, 0x9c, 0x44, 0x5d, - 0x17, 0xbf, 0xff, 0x01, 0xf7, 0x44, 0x29, 0xf3, 0x9f, 0x67, 0xbc, 0x59, - 0x43, 0x4c, 0x7d, 0xe1, 0x28, 0x46, 0x97, 0xc1, 0x3d, 0x62, 0xca, 0x89, + 0x9f, 0x2f, 0xbf, 0x80, 0x38, 0xf4, 0x08, 0xb2, 0xfe, 0xcf, 0x60, 0x41, + 0x8b, 0x2e, 0xd3, 0x16, 0x5f, 0x1f, 0x62, 0x4b, 0x2b, 0x0d, 0xc7, 0x85, + 0xef, 0xc7, 0xd8, 0xef, 0x12, 0x5f, 0x06, 0x3b, 0xc4, 0x97, 0x34, 0xd2, + 0x4a, 0x91, 0xf1, 0x61, 0x3b, 0x44, 0x56, 0x69, 0x23, 0x73, 0x5f, 0x79, + 0xcb, 0xf5, 0x95, 0x87, 0x80, 0x29, 0x25, 0xf4, 0xc4, 0xed, 0x2c, 0xbd, + 0x37, 0x8d, 0x65, 0xff, 0xf7, 0x60, 0x39, 0x85, 0xdf, 0x1c, 0x16, 0xcb, + 0x29, 0x65, 0x61, 0xec, 0x71, 0x36, 0x86, 0xa9, 0x7a, 0x62, 0xe6, 0x32, + 0x7f, 0x0f, 0x12, 0x22, 0xe1, 0x1b, 0x4e, 0xb7, 0xff, 0x8a, 0x3e, 0xff, + 0x0a, 0x25, 0xad, 0x42, 0xcb, 0xff, 0x77, 0x98, 0x1f, 0x39, 0x77, 0x8b, + 0x2f, 0x19, 0x7e, 0xb2, 0x71, 0xbe, 0xa6, 0x22, 0xd7, 0x7c, 0x21, 0xef, + 0xff, 0x40, 0xac, 0x3c, 0x67, 0x78, 0x02, 0xea, 0xca, 0x83, 0xf4, 0xf1, + 0x55, 0xfd, 0x1e, 0x8f, 0x46, 0xcb, 0x2f, 0xff, 0x64, 0x8d, 0xfe, 0x97, + 0x78, 0x46, 0x2a, 0xca, 0xd8, 0xfd, 0xe4, 0x5b, 0x73, 0xf1, 0x65, 0xbd, + 0x06, 0xe5, 0xc8, 0xef, 0xd2, 0xf0, 0x23, 0x4b, 0x2f, 0xf4, 0x6f, 0x3d, + 0xe4, 0x0e, 0x2c, 0xbf, 0x8f, 0x8f, 0xf4, 0xb1, 0x65, 0xce, 0xcd, 0x1f, + 0x1c, 0xf9, 0xb5, 0xfe, 0x38, 0xe9, 0x43, 0x38, 0xb2, 0xbe, 0x47, 0x67, + 0xa1, 0x16, 0x46, 0x17, 0xec, 0x03, 0x23, 0x8b, 0x29, 0x65, 0xd9, 0x36, + 0x8d, 0x97, 0x09, 0xef, 0xf8, 0xc0, 0xdb, 0xf4, 0x08, 0x5b, 0x2c, 0xbf, + 0xe8, 0xda, 0x3e, 0xe9, 0x66, 0xcb, 0x2b, 0x11, 0x4a, 0x12, 0xd3, 0x3e, + 0xbf, 0xf8, 0x4e, 0x1f, 0x26, 0x31, 0xe1, 0x7e, 0xb2, 0xf9, 0xfb, 0x9b, + 0x2c, 0xba, 0x3e, 0x59, 0x71, 0x42, 0xcb, 0x33, 0xc7, 0x9c, 0x44, 0x5d, + 0x17, 0xbf, 0xff, 0x01, 0xf7, 0x44, 0x28, 0xf3, 0x9f, 0x63, 0xbc, 0x59, + 0x43, 0x4c, 0x7d, 0xe1, 0x28, 0x46, 0x97, 0xc1, 0x3d, 0x62, 0xca, 0x99, 0x55, 0x76, 0xa3, 0x0c, 0xf4, 0x69, 0xa4, 0x69, 0x7d, 0x9e, 0xcd, 0x96, - 0x5e, 0x83, 0xe9, 0x65, 0xfd, 0xfb, 0x1e, 0x18, 0x35, 0x97, 0xfb, 0x37, - 0xf3, 0xd2, 0x5d, 0x59, 0x7d, 0x9d, 0xc1, 0xa4, 0xbf, 0xe7, 0x21, 0x70, - 0x9f, 0xbc, 0x59, 0x7c, 0xe2, 0xfe, 0xd2, 0xca, 0x81, 0xfe, 0xf8, 0x84, - 0x8d, 0xef, 0xa4, 0x8a, 0x56, 0x54, 0x49, 0xa4, 0x68, 0x70, 0x8b, 0xbb, + 0x5e, 0x93, 0xe9, 0x65, 0xfd, 0xfb, 0x1e, 0x58, 0x35, 0x97, 0xfb, 0x37, + 0xf3, 0xd0, 0x5d, 0x59, 0x7d, 0x9d, 0xc1, 0xa4, 0xbf, 0xe7, 0x21, 0x70, + 0x9f, 0xbc, 0x59, 0x7c, 0xe2, 0xfe, 0xd2, 0xca, 0x91, 0xfe, 0xf8, 0x84, + 0x8d, 0xef, 0xa0, 0x8a, 0x16, 0x54, 0xc9, 0xa4, 0x68, 0x70, 0x8b, 0xbb, 0x0b, 0x11, 0x0b, 0xaf, 0xe7, 0x69, 0xfa, 0xe2, 0xac, 0xbe, 0xff, 0x8c, - 0xd2, 0xca, 0x15, 0x50, 0x69, 0xc6, 0xd5, 0xa5, 0x1e, 0x97, 0x5f, 0xfb, - 0x4e, 0x27, 0x4f, 0x52, 0x06, 0xda, 0xcb, 0xfd, 0x9e, 0xc8, 0xde, 0xcd, - 0x96, 0x53, 0x9f, 0xc1, 0x21, 0xdf, 0xff, 0x16, 0x74, 0xf0, 0xbd, 0x9d, - 0xf1, 0x85, 0x65, 0xff, 0x74, 0xd9, 0xcf, 0x48, 0x84, 0xb2, 0xff, 0xdf, - 0xc9, 0x05, 0xfa, 0xff, 0x7f, 0x19, 0x10, 0xbf, 0x26, 0x5f, 0xc0, 0x6d, - 0x9e, 0xb5, 0x8b, 0x2f, 0xf7, 0xb0, 0x57, 0x93, 0x8f, 0x59, 0x7f, 0xb3, - 0x99, 0xbb, 0xe9, 0x1a, 0xcb, 0xa7, 0x65, 0x97, 0xbd, 0x31, 0x2c, 0xbf, - 0xd2, 0x65, 0x9b, 0x09, 0x05, 0x96, 0xf8, 0x67, 0xcf, 0x82, 0xfe, 0x1d, - 0xb7, 0x02, 0x98, 0x37, 0x8d, 0x41, 0x09, 0xcb, 0xee, 0x8b, 0x9e, 0x59, - 0x76, 0xa0, 0xb2, 0xff, 0xc5, 0x9d, 0xe8, 0x36, 0xee, 0x05, 0x65, 0xf8, - 0x60, 0x7d, 0x41, 0x65, 0xb8, 0xb2, 0xcd, 0x2c, 0xb8, 0xff, 0x59, 0x77, - 0x8d, 0x65, 0xb9, 0x18, 0xd6, 0xc4, 0x2f, 0x46, 0x7d, 0x20, 0x3f, 0xa7, - 0x44, 0xdf, 0xef, 0x57, 0x7f, 0x2b, 0x2e, 0xfe, 0x56, 0x5f, 0x81, 0x0c, - 0x26, 0xe6, 0x6b, 0xbf, 0x17, 0xa9, 0x4f, 0x30, 0x64, 0x62, 0x8b, 0x99, - 0xff, 0xf0, 0xc3, 0x25, 0x1b, 0xff, 0xec, 0x27, 0xfb, 0x82, 0x8a, 0xf1, - 0x82, 0xcf, 0xd6, 0x5f, 0xf6, 0x1f, 0xdd, 0xd4, 0xe7, 0xcb, 0x2f, 0xfe, - 0xef, 0xb1, 0x82, 0x4f, 0xf9, 0xf7, 0x56, 0x5f, 0xef, 0x82, 0x7a, 0x12, - 0x46, 0xb2, 0xfb, 0xf6, 0x3c, 0x23, 0x26, 0x14, 0x4a, 0xbc, 0x39, 0xea, - 0x3d, 0xc0, 0xea, 0xcb, 0xfb, 0xf6, 0x09, 0xc1, 0x36, 0x59, 0x7f, 0x70, - 0xc8, 0xa7, 0xe5, 0x97, 0x67, 0xcb, 0x2b, 0xe3, 0xf6, 0xe9, 0x9c, 0x79, - 0x65, 0xff, 0xe1, 0x25, 0xa9, 0x8a, 0x0e, 0x59, 0xf7, 0x56, 0x5f, 0x7f, - 0xa3, 0xd2, 0xca, 0x94, 0xe1, 0xa0, 0xae, 0xf0, 0x8d, 0xe1, 0x9b, 0x49, - 0x97, 0xf7, 0x39, 0x30, 0x9d, 0x2c, 0xbf, 0xed, 0x4b, 0x0e, 0x29, 0x7d, - 0xd5, 0x95, 0x87, 0xd0, 0x12, 0xdb, 0xc0, 0xfb, 0xab, 0x2c, 0xeb, 0x2e, - 0x8b, 0x8b, 0x28, 0x8f, 0x9f, 0x84, 0x3d, 0x1e, 0x10, 0x42, 0xec, 0x69, - 0x65, 0xff, 0x4f, 0xd9, 0xfc, 0xef, 0xcd, 0x96, 0x5f, 0xc5, 0x9d, 0xe3, - 0xfe, 0xb2, 0xb4, 0x88, 0x0f, 0xc5, 0xf8, 0x79, 0x7f, 0x8b, 0x63, 0xd7, - 0xde, 0xd9, 0x65, 0xff, 0x4e, 0xdc, 0xd6, 0x06, 0x76, 0x59, 0x7f, 0xb0, - 0xb6, 0xc1, 0xbf, 0x96, 0x54, 0x47, 0xd8, 0xd1, 0xd5, 0xfd, 0x8c, 0xc1, - 0x9b, 0x4b, 0x2e, 0x64, 0xac, 0xa6, 0x1e, 0x1b, 0x96, 0xdf, 0xc5, 0x91, - 0x78, 0xfa, 0xb2, 0xfb, 0x33, 0xdc, 0x59, 0x52, 0x9b, 0xc6, 0x42, 0x89, - 0xd9, 0xfa, 0x43, 0xb8, 0x5b, 0x79, 0xa6, 0x9a, 0x49, 0x7e, 0xc1, 0x48, - 0x1c, 0x48, 0xdc, 0xd0, 0x5f, 0x37, 0x69, 0xa6, 0x96, 0x5d, 0x9f, 0xac, - 0xac, 0x37, 0xfd, 0x28, 0xa9, 0x44, 0xcf, 0x5e, 0x6f, 0xff, 0x8e, 0x0f, - 0xc6, 0x31, 0xe0, 0xe2, 0xf8, 0xd6, 0x5f, 0x1e, 0xbf, 0xe2, 0xcb, 0xff, - 0xa4, 0x0d, 0xbc, 0x8f, 0x2c, 0x69, 0xff, 0x59, 0x7f, 0xdc, 0xd4, 0x83, - 0xa4, 0x7b, 0xd6, 0x5f, 0xf8, 0xb3, 0x81, 0xf1, 0x86, 0x49, 0x65, 0xff, - 0xfe, 0x15, 0xa7, 0xd0, 0xad, 0xf9, 0x90, 0x20, 0x3f, 0x30, 0x96, 0x54, - 0xa3, 0x6b, 0x0e, 0x8c, 0xf2, 0xfe, 0xd0, 0x03, 0xe3, 0xe2, 0xcb, 0xe6, - 0x9c, 0xa0, 0xb2, 0xff, 0xbb, 0x3f, 0x7e, 0xfe, 0xf1, 0xac, 0xb7, 0x96, - 0x56, 0x1e, 0x58, 0x4e, 0xaf, 0xb8, 0x6c, 0x95, 0x97, 0xe2, 0xce, 0xce, - 0x96, 0x5e, 0x69, 0xa6, 0x92, 0x5f, 0x9c, 0x5f, 0x1f, 0x52, 0x37, 0x34, - 0x15, 0x28, 0x80, 0x34, 0x6b, 0xdb, 0xe5, 0x8b, 0x2f, 0x74, 0xda, 0x59, - 0x7b, 0x8f, 0xa5, 0x95, 0xf1, 0xb9, 0xe8, 0xed, 0xef, 0x48, 0xd6, 0x56, - 0x22, 0x54, 0xd5, 0xdc, 0x8a, 0xfb, 0xfc, 0xc8, 0x96, 0x5f, 0x73, 0x0b, - 0xab, 0x2b, 0x0f, 0x11, 0xc8, 0xed, 0xc8, 0xca, 0x82, 0x7e, 0x21, 0xd4, - 0x28, 0x7d, 0x0b, 0x62, 0x71, 0xbd, 0x1f, 0x9f, 0x2c, 0xb4, 0x06, 0xab, - 0xd3, 0x25, 0x3b, 0x1a, 0xf5, 0x47, 0x6a, 0xd7, 0xc1, 0x2c, 0x8a, 0xfc, - 0xdb, 0xf4, 0xec, 0x05, 0x97, 0xff, 0x1f, 0x67, 0x58, 0x5d, 0x77, 0x25, - 0x95, 0x12, 0xeb, 0x96, 0x88, 0xfd, 0x19, 0x29, 0x4b, 0x8c, 0x01, 0x8e, - 0xf2, 0xdb, 0xff, 0xa7, 0x0b, 0x06, 0xf0, 0x29, 0xd9, 0x65, 0xf0, 0x75, - 0x30, 0x59, 0x76, 0xf9, 0x59, 0x7f, 0xe3, 0xd0, 0x7c, 0x61, 0xf1, 0x92, - 0xcb, 0xff, 0xff, 0xce, 0x5f, 0xf7, 0xd8, 0xce, 0xf3, 0x37, 0xf8, 0xc3, - 0xe3, 0x10, 0x1b, 0x2c, 0xad, 0x91, 0x73, 0xf9, 0xed, 0x4a, 0x3d, 0xb2, - 0x1a, 0x35, 0x8b, 0xf5, 0x27, 0x3b, 0x83, 0xa6, 0x97, 0x41, 0x28, 0xc6, - 0x2f, 0x14, 0xef, 0x59, 0x7f, 0xf6, 0xa4, 0x3e, 0x27, 0xe9, 0x4b, 0x4b, - 0x2e, 0x6d, 0x9a, 0xcb, 0x8f, 0xcb, 0x2b, 0x63, 0x61, 0xa1, 0xaa, 0x94, - 0x4a, 0xbb, 0x9d, 0xe8, 0x9f, 0x65, 0x97, 0xb8, 0x0d, 0xd5, 0x95, 0x86, - 0xfb, 0xa3, 0xd7, 0xfa, 0x4a, 0x04, 0x73, 0xf2, 0xca, 0x96, 0xf9, 0xda, - 0x11, 0xcb, 0x0e, 0x1a, 0x39, 0x2e, 0xfc, 0x59, 0x4b, 0x01, 0x85, 0xef, - 0xd0, 0xa5, 0x35, 0xb8, 0xa3, 0x37, 0xd4, 0xa0, 0x8f, 0x4a, 0xec, 0x78, - 0xc7, 0xbf, 0x85, 0xe1, 0x47, 0x53, 0xc8, 0xc2, 0x7b, 0x3f, 0x97, 0xbd, - 0x5e, 0x3e, 0x16, 0x42, 0x2f, 0xee, 0x10, 0x5f, 0x8e, 0x28, 0x19, 0x2c, - 0xba, 0x3a, 0x35, 0x97, 0xfd, 0x17, 0x77, 0x39, 0x31, 0x03, 0x4b, 0x2f, - 0xf0, 0xe4, 0x99, 0x23, 0x95, 0x95, 0x27, 0xe0, 0xe7, 0xf7, 0xfd, 0xe9, - 0xdb, 0x53, 0x07, 0xd2, 0xcb, 0xf1, 0x0a, 0x24, 0xfe, 0xb2, 0xff, 0xc4, - 0xff, 0x77, 0xd2, 0x1c, 0x89, 0x65, 0x61, 0xf4, 0x91, 0x55, 0xfd, 0xb4, - 0x91, 0xb3, 0x16, 0x5f, 0xf8, 0xfd, 0x23, 0xf0, 0x27, 0xee, 0x2c, 0xbc, - 0x45, 0x8b, 0x2b, 0x0f, 0x65, 0xcf, 0xef, 0x0c, 0x5f, 0xd6, 0x5f, 0x99, - 0xad, 0x3f, 0x56, 0x54, 0x63, 0xc7, 0x91, 0xfb, 0xff, 0xa2, 0x70, 0xfa, - 0x61, 0xd2, 0xc6, 0x2c, 0xbf, 0xb5, 0x84, 0x4f, 0xb2, 0xca, 0x09, 0xf8, - 0x1a, 0x25, 0xff, 0xd2, 0xd7, 0xd0, 0xe4, 0x6c, 0xf6, 0x12, 0xcb, 0xff, - 0xcc, 0xfa, 0x1c, 0x8d, 0x84, 0x58, 0x7f, 0xac, 0xbf, 0x9f, 0x40, 0x9f, - 0xa0, 0xb2, 0x8c, 0xfe, 0x9d, 0x32, 0xfe, 0x63, 0xe0, 0xcf, 0x7a, 0xcb, - 0xc7, 0xe0, 0x2c, 0xa8, 0xcb, 0x8b, 0xed, 0x92, 0x78, 0x42, 0x2c, 0x64, - 0x19, 0x0a, 0x23, 0x20, 0xd4, 0x21, 0x7c, 0xce, 0xf0, 0x93, 0x22, 0x1e, - 0xc3, 0x04, 0x04, 0x1b, 0xcb, 0xac, 0xdd, 0xb3, 0x2a, 0xe3, 0x0a, 0x3a, - 0x87, 0xbb, 0x62, 0x38, 0xa8, 0xe6, 0x3f, 0xd8, 0xee, 0x1e, 0xed, 0xaf, - 0x1e, 0x8c, 0x71, 0x8e, 0x3d, 0xb5, 0x43, 0x55, 0xb4, 0x84, 0x3c, 0x70, - 0x7d, 0x9a, 0xf7, 0x3b, 0x6a, 0x42, 0x7c, 0x29, 0xff, 0x63, 0xac, 0xc5, - 0xb2, 0xf7, 0x23, 0x05, 0x9d, 0xf2, 0x0d, 0x6c, 0x9f, 0xf5, 0x63, 0x64, - 0x75, 0x95, 0xde, 0xec, 0xa6, 0x08, 0xab, 0x94, 0x5d, 0x57, 0x03, 0xec, - 0xa7, 0xcf, 0xfa, 0xd7, 0x8c, 0x3d, 0xac, 0x60, 0xfe, 0xb1, 0x9f, 0x6d, - 0xc6, 0xb6, 0x56, 0xac, 0xa7, 0x97, 0xcc, 0x6b, 0xdb, 0x7b, 0xd4, 0x0a, - 0x42, 0x56, 0xf9, 0xc2, 0xa6, 0xa7, 0x20, 0xe3, 0xe9, 0xa4, 0x82, 0x5a, - 0x1c, 0xed, 0xc9, 0xdd, 0x4a, 0x6f, 0x2a, 0xf3, 0xca, 0x1a, 0xae, 0x66, - 0x2f, 0x33, 0x3a, 0xb2, 0xff, 0x3b, 0x27, 0xa7, 0xc3, 0x59, 0x7e, 0xcd, - 0x09, 0xde, 0x2c, 0xb3, 0x7f, 0xcf, 0xd8, 0x87, 0x38, 0x63, 0x7f, 0x9b, - 0xe6, 0x82, 0xee, 0x15, 0x45, 0xc6, 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, - 0xa2, 0xeb, 0x5f, 0xfc, 0xdd, 0xe0, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x25, - 0x15, 0x4c, 0xac, 0x5e, 0xf8, 0x42, 0xa0, 0x65, 0x42, 0xc3, 0x14, 0x25, - 0x27, 0x38, 0x5b, 0xbb, 0x0f, 0x57, 0xd5, 0x7a, 0x99, 0xfc, 0x2d, 0x38, - 0x6f, 0xd4, 0x1d, 0xc4, 0x1b, 0xff, 0xcd, 0xd8, 0xf0, 0x6f, 0x9a, 0x0b, - 0xb8, 0x55, 0x12, 0xd2, 0xff, 0xa3, 0x87, 0x8f, 0x75, 0xe7, 0xed, 0xc5, - 0x97, 0xff, 0xff, 0x35, 0xb9, 0xd8, 0xec, 0x6c, 0x8e, 0xc8, 0xdb, 0x57, - 0x5b, 0x7a, 0x8e, 0x63, 0x46, 0xf4, 0x7a, 0xcb, 0xce, 0xe1, 0x54, 0x46, - 0xeb, 0xf7, 0xd1, 0x14, 0x8d, 0x65, 0xb4, 0x13, 0xd1, 0x72, 0x8b, 0xfd, - 0xa3, 0x6d, 0x87, 0xd2, 0xd2, 0xcb, 0xc0, 0x8f, 0x69, 0x65, 0xf6, 0x81, - 0x3f, 0xac, 0xa3, 0x3f, 0xc8, 0x8e, 0x04, 0x21, 0xbf, 0xff, 0xfe, 0xde, - 0x59, 0xce, 0x0e, 0x73, 0x41, 0x77, 0x0b, 0x78, 0x1f, 0x67, 0xbc, 0x54, - 0x61, 0xeb, 0x7a, 0x51, 0x78, 0x66, 0x17, 0xf7, 0x1b, 0xb5, 0xcd, 0xd1, - 0x16, 0x5f, 0xb7, 0x1b, 0xe9, 0xb6, 0xd2, 0xcb, 0xe1, 0x96, 0x7e, 0xb2, - 0xfa, 0x5a, 0x60, 0xd6, 0x56, 0x1e, 0x2e, 0x88, 0xaf, 0xfc, 0xf0, 0x6f, - 0x9a, 0x0b, 0xb8, 0x55, 0x12, 0xfa, 0xd8, 0xb2, 0xf7, 0x9f, 0xab, 0x2d, - 0xde, 0x1a, 0xc1, 0x04, 0x2f, 0xe1, 0x0f, 0xed, 0xcd, 0x34, 0xb2, 0xa4, - 0xf7, 0x0c, 0xa2, 0xfe, 0x93, 0xdd, 0xcf, 0xa0, 0xb2, 0xfd, 0xa1, 0x7c, - 0x0e, 0x2c, 0xaf, 0x1e, 0xd0, 0x86, 0x17, 0xa7, 0x52, 0xb2, 0xb0, 0xdf, - 0x19, 0x1d, 0xe8, 0x3f, 0x96, 0x5e, 0xfe, 0x5a, 0x59, 0x7f, 0xb0, 0xf5, - 0x0f, 0x1b, 0x4b, 0x2b, 0x74, 0xf4, 0x7c, 0x3d, 0x7c, 0x0f, 0x1e, 0x96, - 0x5f, 0x73, 0xd3, 0xe5, 0x97, 0xfa, 0x76, 0x92, 0xdb, 0x37, 0x16, 0x5d, - 0xcd, 0x2c, 0xa9, 0x3e, 0xfd, 0x88, 0xa2, 0x36, 0xb3, 0x76, 0xd4, 0xbd, - 0x05, 0x28, 0xb9, 0x0e, 0xa0, 0xc6, 0xbf, 0xf1, 0x3e, 0xe9, 0xb6, 0x9e, - 0x18, 0x43, 0xe8, 0x6a, 0x14, 0x2e, 0x38, 0x3f, 0xd6, 0xd8, 0xf2, 0x51, - 0x21, 0x23, 0x7f, 0xfe, 0x29, 0x0b, 0x7e, 0xfc, 0x13, 0xe1, 0x66, 0xf7, - 0x59, 0x7f, 0xf4, 0x3a, 0x71, 0x37, 0x19, 0xef, 0x91, 0xac, 0xbf, 0xcd, - 0xff, 0x7f, 0xbf, 0x3f, 0x96, 0x50, 0xd1, 0xb5, 0xf2, 0xc1, 0x24, 0xdf, - 0xe6, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x55, 0xae, 0x8f, 0x6c, 0xac, 0xb8, - 0xf7, 0x16, 0x5d, 0x9a, 0x59, 0x7b, 0x3e, 0xea, 0xcb, 0xe3, 0x63, 0xef, - 0x59, 0x42, 0x9f, 0x7b, 0x8c, 0xfe, 0x2d, 0xd1, 0xdb, 0xc5, 0xb8, 0xd2, - 0xcb, 0xfb, 0xd3, 0xbc, 0x03, 0xc5, 0x97, 0xf3, 0xfa, 0x22, 0x91, 0xac, - 0xbf, 0xfb, 0xbe, 0x9e, 0x13, 0x81, 0xb6, 0x7a, 0x59, 0x7a, 0x61, 0x8b, - 0x2f, 0xe3, 0x00, 0x4a, 0x62, 0x59, 0x50, 0x47, 0x58, 0xcb, 0xfe, 0x2d, - 0x89, 0x1f, 0xc3, 0x77, 0xfc, 0x2e, 0x16, 0x7d, 0xd7, 0x62, 0xcb, 0xdb, - 0x93, 0xd5, 0x97, 0xf4, 0xb0, 0x49, 0x20, 0xac, 0xba, 0x74, 0xb2, 0xb4, - 0x78, 0x7c, 0x2d, 0xbd, 0xf3, 0xfc, 0xb2, 0xf7, 0x5f, 0xe5, 0x97, 0xff, - 0xd0, 0xf3, 0xc0, 0x57, 0xe6, 0x03, 0x9c, 0x35, 0x94, 0xb2, 0xb0, 0xf6, - 0xf7, 0x14, 0x29, 0xd1, 0x38, 0x4f, 0x17, 0x0c, 0xd6, 0x5f, 0xd9, 0xa0, - 0xbb, 0x85, 0x51, 0x20, 0x2a, 0x31, 0xe7, 0xb7, 0x16, 0xbf, 0x71, 0xc9, - 0xfe, 0x59, 0x76, 0x12, 0xcb, 0x81, 0x2b, 0x2a, 0x07, 0xf1, 0xf1, 0x36, - 0xe9, 0x3e, 0x85, 0x6f, 0xe9, 0xd6, 0xd3, 0xad, 0x96, 0x5f, 0x1b, 0x27, - 0x8b, 0x2a, 0x07, 0xa0, 0x29, 0x7d, 0xfe, 0xd6, 0xcc, 0x92, 0x87, 0x16, - 0x54, 0x9e, 0xbe, 0x11, 0xde, 0xd9, 0xc6, 0xb2, 0xff, 0x04, 0xf1, 0xa0, - 0x17, 0x56, 0x56, 0x1e, 0x87, 0xc3, 0xb7, 0xb9, 0xc8, 0x2c, 0xb9, 0xb1, - 0xe5, 0x97, 0xff, 0xb2, 0x2f, 0x3b, 0x3b, 0xe9, 0xe8, 0x18, 0xb2, 0xf6, - 0xe4, 0xf5, 0x65, 0x4a, 0xf4, 0xde, 0xd0, 0x8d, 0x19, 0xee, 0x46, 0x16, - 0x2a, 0x66, 0xe9, 0xcc, 0x4c, 0x4c, 0x22, 0xf4, 0x2e, 0x9e, 0x1d, 0x05, - 0x0e, 0x7e, 0xb8, 0x00, 0x8a, 0x3c, 0x74, 0x41, 0xbd, 0xc4, 0xbb, 0xff, - 0x42, 0x73, 0xff, 0x66, 0x10, 0x56, 0x5f, 0xa4, 0x8b, 0x3a, 0xb2, 0xfb, - 0x5a, 0x30, 0xac, 0xbe, 0x2c, 0xd9, 0xbe, 0x22, 0x12, 0x3c, 0xf4, 0x42, - 0x5b, 0xfc, 0xdf, 0xce, 0xcc, 0x91, 0xac, 0xa6, 0xea, 0x80, 0xf5, 0x1c, - 0x1c, 0x7a, 0x65, 0xfe, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x16, 0x42, 0xfe, - 0x3d, 0xed, 0xf9, 0x3f, 0x2c, 0xbf, 0xff, 0xf4, 0xc0, 0x03, 0xc6, 0xe2, - 0x67, 0x88, 0x0f, 0xbb, 0x82, 0xcf, 0x16, 0x58, 0xf8, 0x8a, 0x3e, 0x99, - 0x5f, 0xe6, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x6b, 0xaf, 0xff, 0x60, 0x93, - 0xb3, 0x7c, 0x8b, 0xa3, 0x92, 0x59, 0x7f, 0x9b, 0xe6, 0x82, 0xee, 0x15, - 0x45, 0xc8, 0xbf, 0x68, 0x2e, 0xe1, 0x54, 0x5d, 0x8b, 0xff, 0x3c, 0x1b, - 0xe6, 0x82, 0xee, 0x15, 0x45, 0x1c, 0xb3, 0x7c, 0x44, 0x03, 0x0d, 0x2f, - 0x8a, 0x60, 0xc5, 0x97, 0xfe, 0x8e, 0xdb, 0x0c, 0x9d, 0x43, 0x0f, 0x7a, - 0xcb, 0x41, 0x65, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x95, 0x5f, 0xf0, 0x7c, - 0x71, 0x7a, 0x76, 0xc5, 0x97, 0xff, 0xbd, 0x3b, 0x4f, 0x8f, 0x7b, 0x8f, - 0xc6, 0xb1, 0xb9, 0xba, 0xba, 0x0d, 0xf1, 0x19, 0x71, 0x37, 0x57, 0xc9, - 0x86, 0xbc, 0x37, 0x2f, 0xfc, 0xe7, 0xd8, 0x00, 0x6e, 0x5f, 0xac, 0xbb, - 0xc0, 0x59, 0x66, 0xe3, 0x4f, 0xd7, 0xe2, 0x16, 0x46, 0xad, 0xe2, 0x9e, - 0x1f, 0x5f, 0xff, 0xfc, 0xfd, 0xcf, 0xe6, 0x0d, 0xc4, 0x66, 0x0e, 0x61, - 0x23, 0x77, 0x15, 0x65, 0x6c, 0xdb, 0x9c, 0xc2, 0x7b, 0x73, 0x23, 0xc9, - 0xf8, 0xc0, 0xe9, 0x1b, 0xec, 0x3d, 0x78, 0x72, 0x7e, 0x50, 0x48, 0x3c, - 0x4d, 0xec, 0x30, 0x77, 0xca, 0x1f, 0x69, 0x6a, 0xff, 0xa0, 0xdf, 0x34, - 0x17, 0x70, 0xaa, 0x23, 0x85, 0xff, 0x1b, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, - 0xad, 0x56, 0x6e, 0xe8, 0x8d, 0xea, 0x3d, 0xff, 0xe6, 0xec, 0x78, 0x37, - 0xcd, 0x05, 0xdc, 0x2a, 0x89, 0x6d, 0x78, 0x1a, 0xfd, 0x65, 0xd9, 0xf2, - 0xcb, 0xfc, 0x3c, 0x63, 0x24, 0xf8, 0xb2, 0xfd, 0xd9, 0xf0, 0x9c, 0x59, - 0x7b, 0x78, 0x20, 0xb2, 0x82, 0x8a, 0xff, 0x87, 0x8c, 0x5c, 0x8c, 0xb7, - 0x94, 0xde, 0xf0, 0xe0, 0xb2, 0xfc, 0x7b, 0x83, 0x9f, 0x96, 0x5f, 0xf6, - 0x7c, 0x59, 0xff, 0xef, 0x05, 0x97, 0x30, 0x2b, 0x2f, 0xe3, 0xfb, 0x83, - 0xc2, 0x59, 0x7f, 0x7d, 0x90, 0x9d, 0x7e, 0xb2, 0xb0, 0xf7, 0x1c, 0xb6, - 0xff, 0xfa, 0x4b, 0x6e, 0x01, 0xce, 0x1c, 0xf3, 0xc1, 0x65, 0x4a, 0x63, - 0x90, 0x3a, 0xd3, 0x9f, 0x88, 0x2f, 0x8a, 0x7e, 0x82, 0xcb, 0xff, 0x19, - 0x77, 0x83, 0x03, 0xea, 0x0b, 0x2f, 0xed, 0xa4, 0x2f, 0xaf, 0xd6, 0x5f, - 0xff, 0x9f, 0xef, 0xc7, 0xe7, 0x86, 0x70, 0x80, 0xe2, 0x2c, 0xbe, 0xff, - 0x35, 0x12, 0xcb, 0xf1, 0xfa, 0x44, 0x89, 0x65, 0xee, 0xcb, 0x16, 0x50, - 0x53, 0x4f, 0x88, 0x8b, 0x47, 0xde, 0x2f, 0xfd, 0x5f, 0xa4, 0x9b, 0x85, - 0x37, 0xff, 0x0b, 0x25, 0xdc, 0xd1, 0xec, 0xec, 0x59, 0x7c, 0xf0, 0x14, - 0xd6, 0x58, 0x55, 0x9e, 0x34, 0x57, 0xf1, 0xe8, 0x8f, 0xbc, 0x59, 0x7e, - 0xd3, 0x96, 0x4a, 0xca, 0x54, 0x43, 0x63, 0x3f, 0x3f, 0x11, 0x39, 0x65, - 0xd2, 0x4b, 0x2f, 0x61, 0x8d, 0x65, 0xe1, 0xe3, 0x16, 0x5c, 0x64, 0x33, - 0x6e, 0x63, 0x75, 0x27, 0xed, 0x04, 0xcb, 0xf8, 0x3e, 0x3d, 0x48, 0xd6, - 0x5f, 0x64, 0x50, 0xc5, 0x97, 0xfe, 0x78, 0x37, 0xcd, 0x05, 0xdc, 0x2a, - 0x89, 0x81, 0x7e, 0x03, 0xf4, 0xfa, 0xb2, 0xee, 0x4a, 0xca, 0x93, 0x7a, - 0x02, 0x7b, 0xcd, 0x1f, 0xeb, 0x2f, 0xb8, 0xe5, 0xfa, 0xca, 0x88, 0xf0, - 0x3f, 0x1e, 0xbf, 0xc0, 0xfb, 0x47, 0x2c, 0xc5, 0x97, 0xef, 0x1c, 0x96, - 0xcb, 0x2f, 0xfa, 0x76, 0xe9, 0x48, 0x3b, 0xc5, 0x95, 0xe4, 0x49, 0xf0, - 0xce, 0x3c, 0x9e, 0xf8, 0xb3, 0x92, 0xb2, 0xba, 0x7a, 0x7b, 0xcc, 0xef, - 0x40, 0xff, 0x59, 0x7d, 0xf0, 0x4c, 0x55, 0x97, 0xe7, 0x17, 0x3c, 0xeb, - 0x2b, 0x11, 0x19, 0x11, 0x27, 0x47, 0x40, 0x49, 0x7f, 0xfa, 0x1e, 0xc2, - 0x68, 0x4d, 0x78, 0xe5, 0x8b, 0x2f, 0xe3, 0xee, 0x32, 0x7c, 0xb2, 0xff, - 0x8f, 0xfd, 0xbd, 0x3c, 0x07, 0x16, 0x5f, 0xe8, 0x3f, 0x78, 0x65, 0x2b, - 0x2d, 0x3a, 0x3e, 0xc2, 0x3b, 0xaf, 0x93, 0x09, 0xd2, 0x5f, 0x61, 0x31, - 0x7f, 0xec, 0x66, 0x6b, 0x3e, 0x86, 0x75, 0x65, 0xfc, 0x7e, 0x36, 0x60, - 0xab, 0x2b, 0xc7, 0xd7, 0xc3, 0xeb, 0xff, 0xcf, 0xb7, 0x8e, 0x4b, 0x22, - 0x3d, 0x1a, 0xca, 0xc4, 0xc3, 0x3b, 0x0a, 0x26, 0x88, 0xac, 0x4b, 0x2d, - 0x1e, 0xb2, 0xf8, 0x27, 0x83, 0x59, 0x70, 0x71, 0x65, 0xc0, 0xfd, 0x25, - 0x36, 0x4f, 0xcf, 0x61, 0x0f, 0x85, 0x0c, 0x87, 0x42, 0xd7, 0xe1, 0x66, - 0x18, 0x35, 0x97, 0xfb, 0x06, 0x59, 0xdf, 0x62, 0xcb, 0xa3, 0xb0, 0xae, - 0x30, 0xa5, 0xdd, 0xfd, 0x65, 0xdb, 0x90, 0x59, 0x5b, 0x1b, 0x2d, 0x0c, - 0x5f, 0x84, 0x8f, 0x72, 0xfd, 0x65, 0xfd, 0x9e, 0x71, 0x0b, 0xab, 0x2b, - 0x63, 0xd9, 0x19, 0x65, 0xe7, 0x70, 0xac, 0xbf, 0xb3, 0xbd, 0x9d, 0x71, - 0x65, 0xfc, 0xe5, 0xfe, 0xdf, 0xf5, 0x65, 0x4a, 0x26, 0xf6, 0x23, 0x21, - 0xb8, 0xf2, 0xdb, 0x37, 0x96, 0x6b, 0xb6, 0xc9, 0x70, 0x1d, 0x1c, 0x68, - 0xf9, 0x1c, 0x60, 0xad, 0x41, 0x86, 0xcf, 0xd0, 0xb7, 0x88, 0x87, 0x45, - 0xac, 0x22, 0xf4, 0x22, 0x5d, 0x80, 0xa3, 0x96, 0xe4, 0xa5, 0x2e, 0xc2, - 0xa4, 0x09, 0xdb, 0xca, 0x1a, 0x31, 0x8f, 0x5b, 0x12, 0x31, 0x6b, 0xcc, - 0x06, 0x92, 0x5f, 0xf9, 0xe0, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x26, 0x35, - 0xd3, 0xb8, 0xb2, 0xed, 0x62, 0xcb, 0x4e, 0x8d, 0x7f, 0x86, 0x6c, 0xdc, - 0xd1, 0x7c, 0xc1, 0xdf, 0x3d, 0xdf, 0xf0, 0xcf, 0x7b, 0x7f, 0xbf, 0x12, - 0x0b, 0x2f, 0xe6, 0xc3, 0x66, 0x38, 0xc2, 0x1e, 0x59, 0x79, 0xb5, 0xe0, - 0x35, 0x94, 0xd9, 0x3e, 0x16, 0xd0, 0xf6, 0xfe, 0xd6, 0x80, 0x59, 0xe5, - 0x97, 0xd3, 0xb4, 0xe9, 0x65, 0xee, 0xcc, 0x4b, 0x2f, 0xd9, 0x31, 0x1b, - 0x16, 0x5f, 0x6c, 0x01, 0xee, 0xac, 0xbf, 0xed, 0xc3, 0xfe, 0x36, 0x45, - 0x2c, 0x59, 0x46, 0x8c, 0xc8, 0x88, 0xb4, 0x3a, 0x21, 0x3e, 0xe1, 0x3d, - 0xa0, 0xb2, 0xfc, 0xc9, 0xce, 0xf1, 0x65, 0x47, 0x26, 0xe0, 0x62, 0x37, - 0xfb, 0xd3, 0xad, 0x84, 0x07, 0xeb, 0x2f, 0x81, 0xb3, 0x92, 0xcb, 0x9f, - 0x8b, 0x2f, 0xa3, 0x77, 0x3c, 0xb2, 0xd3, 0xe3, 0x75, 0xc1, 0x6b, 0xd2, - 0x5b, 0x2c, 0xbf, 0xff, 0xfe, 0x87, 0x3d, 0x80, 0x6b, 0x9e, 0xc0, 0xe7, - 0x23, 0x67, 0xd0, 0xe1, 0x91, 0xac, 0xbc, 0xee, 0x15, 0x45, 0x62, 0xbe, - 0x71, 0x24, 0x55, 0x94, 0x13, 0xcb, 0xd1, 0x45, 0xfb, 0xc6, 0xc9, 0xd2, - 0xcb, 0xff, 0xc0, 0x7d, 0xb5, 0x8c, 0xf4, 0xec, 0x40, 0x59, 0x46, 0x7e, - 0x44, 0x4f, 0x7f, 0xe8, 0xfc, 0xe9, 0x60, 0x24, 0xba, 0xb2, 0xff, 0x16, - 0x73, 0xfc, 0xc1, 0xac, 0xa1, 0x55, 0x19, 0x04, 0x9b, 0xe1, 0xcd, 0x43, - 0x2f, 0xd0, 0x94, 0xe9, 0x06, 0xf3, 0xfb, 0xff, 0x85, 0x07, 0x78, 0x59, - 0xbc, 0xb3, 0x8b, 0x2f, 0xfe, 0x2e, 0x8a, 0x09, 0x2c, 0x0c, 0xe9, 0x65, - 0xfe, 0xd6, 0xdd, 0xe3, 0xfc, 0x15, 0x95, 0x27, 0xf2, 0xe8, 0x77, 0xfd, - 0x0c, 0xee, 0x14, 0xb5, 0x1e, 0xb2, 0xff, 0xde, 0x9d, 0xef, 0xa0, 0xf9, - 0xf7, 0x16, 0x5f, 0xe9, 0x2e, 0xfb, 0x3f, 0x75, 0x97, 0xd2, 0xc7, 0xea, - 0xca, 0x81, 0xe9, 0xf0, 0xca, 0xfd, 0xb9, 0xdc, 0xfa, 0x25, 0x97, 0xdc, - 0x88, 0xc5, 0x59, 0x7f, 0xff, 0xff, 0x9f, 0xbd, 0xcf, 0x71, 0xe0, 0x65, - 0xd8, 0x66, 0xb3, 0x99, 0xe7, 0xd8, 0xa6, 0x0b, 0x2f, 0x76, 0x62, 0x59, - 0x52, 0x8f, 0x9c, 0x2d, 0xe1, 0x20, 0x90, 0x8b, 0xbf, 0x81, 0x1e, 0x1f, - 0x1b, 0x6d, 0x65, 0xf7, 0xd0, 0x9f, 0x2c, 0xbf, 0xdc, 0x1f, 0xb0, 0x20, - 0xdd, 0x59, 0x6c, 0xe1, 0xed, 0x00, 0x8e, 0xe9, 0xde, 0xb2, 0xbc, 0x6f, - 0xc8, 0x9e, 0xfe, 0xf1, 0x8c, 0xb0, 0x0b, 0x2f, 0xf4, 0x4f, 0x87, 0x01, - 0x18, 0xb2, 0x86, 0x88, 0x00, 0x90, 0x19, 0x65, 0xfe, 0xfa, 0x11, 0x41, - 0xf5, 0xb2, 0xcb, 0xe8, 0x74, 0xf6, 0x59, 0x52, 0x7b, 0x4e, 0x6f, 0x52, - 0x9f, 0xbe, 0x46, 0x9c, 0x70, 0x89, 0xba, 0x1d, 0x59, 0x7f, 0x66, 0xd8, - 0x59, 0x05, 0x95, 0x27, 0x87, 0xa1, 0x7b, 0xfe, 0x31, 0x19, 0xd0, 0x3e, - 0xd2, 0xb2, 0xff, 0x68, 0xc1, 0xb7, 0xfc, 0x82, 0xcb, 0xc7, 0x3b, 0x2c, - 0xa1, 0x9e, 0x94, 0x46, 0xd7, 0xe0, 0xfa, 0x48, 0x55, 0x95, 0x87, 0x96, - 0xe4, 0x77, 0xcc, 0x7f, 0xba, 0xb2, 0xff, 0xed, 0xa7, 0xb1, 0x84, 0x08, - 0x9b, 0x82, 0x71, 0x65, 0xff, 0xcf, 0xbb, 0x84, 0xe3, 0x7c, 0xfb, 0xab, - 0x2f, 0xfb, 0x53, 0xc7, 0xd1, 0x4c, 0x16, 0x56, 0x91, 0x97, 0xe4, 0xfd, - 0xe8, 0xb7, 0xa0, 0xfb, 0x2c, 0xbd, 0x1b, 0x66, 0x2c, 0xbf, 0x8a, 0x76, - 0x29, 0xd9, 0x65, 0xba, 0xb2, 0xff, 0xc2, 0xf3, 0x99, 0xe3, 0xe0, 0x36, - 0x59, 0x43, 0x3d, 0x20, 0x08, 0xd4, 0x76, 0x9f, 0xf6, 0x43, 0xbb, 0xe3, - 0x23, 0x1d, 0x72, 0x02, 0x7f, 0xbf, 0xf1, 0x38, 0xfd, 0x3b, 0xfc, 0xe4, - 0xb2, 0xe9, 0xdd, 0x59, 0x7f, 0xff, 0xe0, 0x6d, 0x81, 0x8c, 0x40, 0xdc, - 0x8d, 0x9f, 0x43, 0x00, 0x5f, 0xf1, 0x65, 0xff, 0xed, 0xb3, 0xee, 0xf0, - 0xca, 0x7f, 0x76, 0x2c, 0xbf, 0xdf, 0x16, 0x0f, 0xce, 0xd2, 0xca, 0xc3, - 0xff, 0x74, 0xba, 0x1a, 0x6d, 0x8e, 0x7c, 0x43, 0x3c, 0x87, 0xad, 0xf7, - 0x61, 0x26, 0xb2, 0xff, 0xb0, 0x7b, 0x63, 0x59, 0xe7, 0x59, 0x7f, 0x6d, - 0x14, 0x23, 0xbd, 0x6c, 0xb2, 0xf8, 0x8b, 0x3a, 0xb2, 0xfc, 0x7b, 0xba, - 0xd1, 0xac, 0xa6, 0xca, 0x20, 0x64, 0xdf, 0xc4, 0x17, 0xf6, 0xc2, 0x47, - 0xb9, 0x7e, 0xb2, 0xfb, 0x76, 0x78, 0x4b, 0x2f, 0xfd, 0xe7, 0x67, 0x7d, - 0x3d, 0x03, 0x16, 0x56, 0x1f, 0x1c, 0x44, 0x97, 0xff, 0x66, 0x0a, 0xc9, - 0x3d, 0x68, 0xda, 0x59, 0x7f, 0xc7, 0x03, 0xf0, 0x18, 0xc3, 0x59, 0x76, - 0x75, 0x65, 0x4a, 0x69, 0x59, 0x09, 0x6d, 0x11, 0x75, 0x12, 0x3c, 0xe2, - 0xf1, 0x3e, 0xe2, 0xcb, 0xde, 0x36, 0xed, 0xae, 0xd9, 0x38, 0xb6, 0xa2, - 0xb9, 0x87, 0xce, 0xd0, 0x86, 0x81, 0x30, 0xcd, 0xf2, 0x54, 0x70, 0xae, - 0x81, 0x85, 0xff, 0xc4, 0x06, 0x77, 0x14, 0x25, 0xb5, 0x18, 0xdf, 0xa5, - 0x1c, 0x3b, 0xe7, 0xe4, 0x25, 0x0d, 0x7e, 0x4a, 0x1e, 0xec, 0x79, 0x60, - 0x41, 0xde, 0x43, 0x1f, 0x0b, 0xa1, 0x23, 0x7d, 0xdc, 0x51, 0xbd, 0x1c, - 0x23, 0xdb, 0x2b, 0x2e, 0x81, 0x2c, 0xbf, 0xb5, 0xa9, 0x1e, 0x31, 0x65, - 0xb7, 0x56, 0x5c, 0x7b, 0x8b, 0x2f, 0xb6, 0xd4, 0xec, 0xb2, 0xe1, 0x3f, - 0x59, 0x77, 0xfc, 0x59, 0x7b, 0xc6, 0xc5, 0x96, 0x89, 0x65, 0x19, 0xe3, - 0x38, 0xc0, 0x07, 0x2f, 0xce, 0x5b, 0x03, 0x8b, 0x2f, 0xd8, 0x3d, 0x38, - 0x56, 0x5f, 0x3e, 0xd3, 0xa5, 0x97, 0x48, 0xfc, 0x79, 0x24, 0x4f, 0x46, - 0x9d, 0x3c, 0x45, 0xac, 0x14, 0x71, 0x9f, 0xc9, 0x09, 0x7f, 0x85, 0xbd, - 0x71, 0xb7, 0x96, 0x5e, 0xe0, 0x9c, 0x59, 0x7d, 0xf6, 0xd2, 0x2a, 0xca, - 0xd8, 0xf5, 0x06, 0x22, 0x01, 0xeb, 0xbb, 0x2b, 0x2f, 0xf7, 0xda, 0x93, - 0x83, 0xf1, 0x65, 0xf6, 0x14, 0xc1, 0x65, 0x61, 0xe9, 0xb0, 0xce, 0xd1, - 0xeb, 0x2f, 0xf6, 0x7f, 0xcc, 0xe9, 0xe9, 0x65, 0xf0, 0x3b, 0x9c, 0x59, - 0x71, 0x6e, 0xac, 0xbd, 0xa7, 0x25, 0x94, 0xb2, 0xff, 0x34, 0x59, 0xbf, - 0x47, 0xc5, 0x96, 0xf9, 0x65, 0x49, 0xf6, 0xe0, 0xd9, 0x85, 0x80, 0xd6, - 0xfd, 0x9a, 0x84, 0xb1, 0x65, 0xfc, 0x7b, 0x61, 0x18, 0xab, 0x2f, 0xf3, - 0xea, 0x1f, 0x9f, 0xdc, 0x59, 0x7d, 0xce, 0x4f, 0xeb, 0x2a, 0x53, 0xf5, - 0x19, 0x0e, 0x0a, 0x19, 0x9b, 0x08, 0xbd, 0x08, 0x67, 0x39, 0x22, 0x70, - 0x16, 0xb4, 0x6b, 0x7f, 0xec, 0x2c, 0x14, 0xb3, 0xbe, 0x35, 0x97, 0xf3, - 0x8b, 0x83, 0x78, 0x2c, 0xaf, 0x1f, 0x49, 0x1e, 0x5f, 0xc6, 0x45, 0x9b, - 0xdd, 0x65, 0xe8, 0x09, 0xd5, 0x97, 0xb5, 0xa7, 0x59, 0x7d, 0x0f, 0xc1, - 0x05, 0x97, 0xda, 0x12, 0x7f, 0x59, 0x52, 0x79, 0x06, 0x49, 0x7f, 0x8f, - 0x67, 0x67, 0xff, 0xca, 0xcb, 0x8c, 0x6b, 0x2e, 0x7f, 0x96, 0x5f, 0xe0, - 0xfb, 0x22, 0x29, 0x1a, 0xca, 0x19, 0xe5, 0x18, 0xbd, 0xee, 0x9f, 0xeb, - 0x2f, 0xbf, 0x92, 0x0a, 0xca, 0x14, 0xdf, 0xfc, 0x3b, 0x7d, 0x0e, 0xe0, - 0x8b, 0x2f, 0x06, 0x7f, 0x59, 0x7b, 0x52, 0xc5, 0x94, 0x67, 0xfe, 0xe4, - 0x60, 0x23, 0x68, 0x76, 0xfb, 0x61, 0x32, 0x25, 0x97, 0xa7, 0x5b, 0x2c, - 0xa9, 0x3c, 0x23, 0x25, 0xa5, 0x97, 0x83, 0x3f, 0xac, 0xba, 0x4a, 0x06, - 0xa3, 0x02, 0xef, 0xba, 0x47, 0xb8, 0xb2, 0xe0, 0x71, 0x65, 0xf8, 0x10, - 0xe7, 0x86, 0xb2, 0xa4, 0xf0, 0x08, 0x5e, 0xa0, 0xaf, 0x58, 0x64, 0x3f, - 0x16, 0x18, 0xf4, 0x4c, 0x9a, 0x20, 0x61, 0xaf, 0x98, 0x0a, 0x1b, 0xfc, - 0x7d, 0xea, 0x40, 0x0a, 0xb7, 0xb2, 0x5f, 0xcf, 0x0d, 0xcd, 0xc0, 0x6c, - 0xb2, 0xc4, 0xb2, 0xe8, 0x6e, 0xac, 0xaf, 0x1e, 0xff, 0x0d, 0x7a, 0x21, - 0x68, 0xf5, 0x97, 0xf6, 0x6c, 0x3f, 0x1e, 0x96, 0x57, 0x8f, 0x08, 0x85, - 0x2f, 0xfd, 0x39, 0xc8, 0xd2, 0x63, 0x12, 0x0b, 0x2f, 0xe3, 0x6b, 0x9b, - 0x63, 0x4b, 0x28, 0x8f, 0xc3, 0xa8, 0x17, 0x14, 0xac, 0xbf, 0x48, 0xfb, - 0x81, 0x59, 0x7a, 0x48, 0x2b, 0x2f, 0xff, 0xe7, 0x68, 0x6e, 0xfa, 0xdb, - 0xcf, 0xdf, 0x1e, 0xa0, 0xb2, 0xbc, 0x7e, 0xe4, 0x37, 0x50, 0x47, 0xa8, - 0x48, 0x78, 0x2b, 0xd8, 0x4f, 0x5f, 0x39, 0x3c, 0x4b, 0x2f, 0xc7, 0xd2, - 0xc8, 0x2c, 0xbe, 0x96, 0xa6, 0x25, 0x97, 0xfe, 0xe6, 0xf7, 0xf4, 0x8b, - 0x9d, 0xe2, 0xcb, 0x84, 0x62, 0xcb, 0xf1, 0x66, 0x81, 0x8b, 0x2f, 0x3f, - 0xc4, 0xb2, 0xfe, 0xe9, 0x3f, 0xf8, 0x4b, 0x2f, 0xc4, 0xff, 0xe1, 0x2c, - 0xa8, 0xc7, 0xa5, 0xd2, 0xca, 0x1a, 0x24, 0x80, 0xdd, 0x5b, 0x27, 0x3c, - 0x29, 0x09, 0x93, 0x44, 0x47, 0xe4, 0x1f, 0xc6, 0x0a, 0x17, 0x17, 0xdc, - 0xc2, 0x0a, 0xcb, 0xfa, 0x4b, 0xa1, 0x06, 0xcb, 0x2f, 0xf4, 0xb4, 0x30, - 0x3e, 0xa0, 0xb2, 0xf7, 0x26, 0x0b, 0x2f, 0xde, 0xcd, 0x1f, 0x16, 0x5c, - 0x7a, 0xf8, 0xf0, 0xfa, 0x39, 0x6d, 0xeb, 0x2a, 0x53, 0x07, 0x81, 0x0e, - 0x8b, 0xbc, 0xef, 0xb8, 0x5d, 0x78, 0x8c, 0x55, 0x97, 0xd8, 0x33, 0xde, - 0xb2, 0xe9, 0x66, 0x1b, 0xf7, 0x1c, 0xbf, 0x43, 0xa6, 0xce, 0xac, 0xbf, - 0x9f, 0xef, 0x48, 0x84, 0xb2, 0xd9, 0xf1, 0xeb, 0x34, 0x51, 0x7f, 0x3c, - 0x1c, 0xb3, 0x71, 0x65, 0xf7, 0x3c, 0xf0, 0x59, 0x46, 0x8f, 0x9d, 0x42, - 0x01, 0xca, 0x40, 0x5d, 0x7b, 0x0a, 0x25, 0x96, 0xf9, 0x65, 0xdf, 0xca, - 0x4b, 0x9a, 0x69, 0x25, 0x19, 0xb0, 0x68, 0x5e, 0xfc, 0x59, 0xdf, 0x1a, - 0x46, 0xe6, 0x86, 0xf8, 0x52, 0xce, 0x2c, 0xa8, 0x1e, 0xd0, 0xce, 0x2f, - 0xf3, 0x8b, 0x24, 0x59, 0xd5, 0x95, 0x29, 0x96, 0x0c, 0x71, 0xe1, 0x7a, - 0x44, 0x57, 0xbd, 0x2d, 0x2c, 0xbc, 0xff, 0x71, 0x65, 0xb8, 0xb2, 0xf8, - 0x4f, 0x3f, 0x56, 0x56, 0xc7, 0xdc, 0x31, 0xdf, 0x87, 0x7a, 0x23, 0x7a, - 0x4a, 0x25, 0x97, 0x14, 0xac, 0xa5, 0x94, 0xb2, 0xa2, 0x2d, 0x98, 0x17, - 0x77, 0xa2, 0x59, 0x71, 0xfe, 0xb2, 0xa4, 0xd7, 0xfe, 0x31, 0x51, 0x22, - 0xe3, 0x43, 0x8e, 0x57, 0xfa, 0x85, 0xff, 0xc2, 0x99, 0x07, 0x92, 0xc7, - 0xfb, 0xab, 0x2f, 0x7f, 0x9b, 0x8b, 0x2f, 0xff, 0x8b, 0x3f, 0xc6, 0x02, - 0x75, 0xa3, 0x00, 0x56, 0x5f, 0x14, 0xb2, 0x25, 0x94, 0x2a, 0x35, 0xb4, - 0x8a, 0x44, 0x1d, 0x4f, 0xbe, 0x67, 0x00, 0xc5, 0x96, 0x15, 0x65, 0xa3, - 0xd6, 0x5d, 0xa1, 0x56, 0x54, 0x9f, 0x09, 0x91, 0x80, 0x48, 0x41, 0x4b, - 0xff, 0xef, 0x64, 0x39, 0x3d, 0x86, 0x6e, 0xb9, 0x7e, 0xb2, 0xff, 0x8f, - 0xbe, 0xcf, 0xba, 0x09, 0x59, 0x7c, 0x24, 0xeb, 0xf5, 0x95, 0x03, 0xdd, - 0xf1, 0xcd, 0xd9, 0xc5, 0x95, 0x26, 0xe2, 0x04, 0x55, 0xc4, 0xc1, 0xfb, - 0x0e, 0x7b, 0x7e, 0xb2, 0xfc, 0x72, 0xc9, 0x62, 0xcb, 0x62, 0xca, 0x33, - 0xf1, 0x72, 0x9e, 0x09, 0x47, 0x93, 0x58, 0x55, 0x95, 0x2b, 0x95, 0x59, - 0x0f, 0x63, 0x8c, 0x53, 0xd0, 0x9e, 0x78, 0xec, 0xf7, 0x0f, 0x6f, 0xa5, - 0xa0, 0x44, 0xb2, 0xf7, 0x26, 0x25, 0x97, 0xf6, 0x0f, 0x37, 0x9e, 0x96, - 0x52, 0xcb, 0x31, 0x65, 0x74, 0xbc, 0xde, 0x17, 0x7c, 0x37, 0x7f, 0x96, - 0x5f, 0xbf, 0x3d, 0x18, 0xab, 0x2e, 0x71, 0x56, 0x5d, 0x9f, 0x2c, 0xa0, - 0xa6, 0x4d, 0x11, 0x23, 0x07, 0x7c, 0x94, 0xe4, 0x5f, 0x91, 0x11, 0x4c, - 0x78, 0xbd, 0xd9, 0xe5, 0x97, 0x66, 0x96, 0x52, 0xca, 0x59, 0x69, 0x59, - 0x4d, 0xb3, 0x4c, 0x41, 0x7d, 0x0b, 0xbd, 0x9b, 0xdd, 0x65, 0xf7, 0xbd, - 0x9f, 0xac, 0xb4, 0xf8, 0xf0, 0x04, 0x1d, 0xa9, 0x47, 0x5b, 0x05, 0x9c, - 0xf3, 0xf7, 0x1b, 0xf8, 0xfb, 0x9e, 0x71, 0x56, 0x5f, 0xdd, 0x19, 0xff, - 0x24, 0xb2, 0xf6, 0xde, 0x35, 0x97, 0x78, 0x1f, 0x1e, 0x53, 0x96, 0xde, - 0x69, 0xa6, 0x92, 0x5f, 0xe9, 0xd8, 0x4c, 0xf3, 0xfc, 0x91, 0xb9, 0xa0, - 0xbf, 0xc6, 0x37, 0x2f, 0xa1, 0xc5, 0x94, 0x33, 0xfa, 0xf2, 0x3d, 0xd9, - 0xb2, 0xcb, 0xf3, 0x04, 0x9d, 0x7e, 0xb2, 0x96, 0x52, 0xcb, 0x4b, 0x0b, - 0x6f, 0xc2, 0xea, 0x4f, 0x9a, 0x08, 0x17, 0xfe, 0xe4, 0xc3, 0xd9, 0x85, - 0x0e, 0x2c, 0xa1, 0xa7, 0x8a, 0x18, 0x64, 0x19, 0x14, 0x4f, 0xbd, 0x21, - 0xbd, 0x2c, 0x35, 0x97, 0xa1, 0x81, 0x59, 0x58, 0x88, 0x70, 0xa8, 0x7c, - 0x37, 0x7c, 0x7a, 0xdc, 0x1a, 0xcb, 0xf7, 0xa7, 0xe7, 0xdd, 0x59, 0x7f, - 0xfd, 0x3d, 0xe0, 0xf3, 0xa7, 0xe0, 0x67, 0xdd, 0x59, 0x5e, 0x3f, 0xbe, - 0x95, 0xde, 0x2c, 0xf2, 0xcb, 0x69, 0x65, 0xfe, 0xcc, 0x08, 0x9b, 0xfd, - 0x8b, 0x2f, 0xfa, 0x4b, 0xa5, 0x9d, 0x96, 0x96, 0x5f, 0xec, 0x6b, 0x86, - 0x5f, 0x41, 0x65, 0x2c, 0xa3, 0x3f, 0x6d, 0x1b, 0xb9, 0xa5, 0xee, 0xe8, - 0x0b, 0x2d, 0xc5, 0x94, 0x46, 0xb8, 0x03, 0xb7, 0xf9, 0xf7, 0xe0, 0xf6, - 0xc6, 0x96, 0x5f, 0xff, 0xb0, 0x73, 0x9f, 0x09, 0xd3, 0x20, 0x43, 0x38, - 0xb2, 0xe6, 0x7e, 0xb2, 0xe7, 0x25, 0x95, 0xe3, 0x5a, 0xe3, 0x17, 0xe6, - 0x1f, 0x7f, 0x75, 0x95, 0xb2, 0xb1, 0x4c, 0x85, 0x01, 0x91, 0x6e, 0x8d, - 0xc4, 0x23, 0xa8, 0x55, 0xba, 0xdf, 0xe4, 0x04, 0x6d, 0xd8, 0x41, 0x00, - 0x82, 0xed, 0x0a, 0xb2, 0xfa, 0x58, 0xfe, 0x59, 0x7b, 0xbd, 0x75, 0x97, - 0xda, 0x04, 0xef, 0x59, 0x7e, 0xcd, 0xd7, 0x2f, 0xc6, 0x7c, 0x2e, 0x43, - 0xd1, 0xcb, 0xef, 0x74, 0xf6, 0x59, 0x7f, 0x82, 0x7c, 0x2c, 0xde, 0xeb, - 0x2f, 0xa7, 0x67, 0x82, 0xcb, 0xee, 0xf3, 0xc6, 0xb2, 0xfe, 0x27, 0xee, - 0xd8, 0xd2, 0xca, 0x59, 0x44, 0x6e, 0x7a, 0x5d, 0x52, 0x7f, 0x78, 0xb7, - 0x7e, 0xcd, 0x8b, 0x3e, 0x59, 0x7d, 0xd3, 0x2e, 0x2c, 0xbf, 0x06, 0x36, - 0x7d, 0xd5, 0x97, 0xef, 0xb3, 0xa7, 0xc5, 0x95, 0x87, 0xe8, 0x64, 0x2e, - 0x57, 0x52, 0xaa, 0x86, 0x0f, 0x38, 0x9b, 0xf1, 0x1e, 0x8c, 0xfd, 0x09, - 0xd2, 0x20, 0xe4, 0x27, 0x2f, 0x08, 0x01, 0x16, 0x5e, 0x23, 0xdd, 0x59, - 0x7c, 0x38, 0xf7, 0x15, 0x65, 0x49, 0xe2, 0x60, 0xf5, 0xa3, 0xd6, 0x5d, - 0x23, 0x59, 0x4d, 0x83, 0x55, 0xe1, 0x4b, 0x71, 0x65, 0x98, 0xb2, 0xda, - 0x59, 0x4e, 0x68, 0x80, 0x23, 0x5a, 0x3d, 0x3f, 0x19, 0xd2, 0xc3, 0x34, - 0x57, 0xfa, 0x4a, 0x0e, 0xcc, 0x1a, 0xcb, 0xee, 0xb1, 0x9a, 0x59, 0x7f, - 0xf7, 0xf2, 0x41, 0x7e, 0xfe, 0x21, 0x41, 0x65, 0xe8, 0x1f, 0x96, 0x5f, - 0x74, 0xb2, 0x0b, 0x2f, 0x80, 0xdb, 0xe3, 0xac, 0xbf, 0x6c, 0x37, 0x76, - 0x96, 0x56, 0x1e, 0x78, 0x09, 0xaf, 0xff, 0x1f, 0x3d, 0x83, 0x2c, 0xde, - 0x59, 0xc5, 0x95, 0x04, 0xe4, 0x05, 0x31, 0xf8, 0x8e, 0x24, 0x6d, 0x0e, - 0x13, 0x87, 0x48, 0x69, 0xb2, 0xea, 0x42, 0xa3, 0xb2, 0xa9, 0x8e, 0xf7, - 0x68, 0x62, 0xc0, 0xc0, 0x72, 0x97, 0xb2, 0x73, 0x30, 0x58, 0xc6, 0x82, - 0xe3, 0xf4, 0x70, 0xc7, 0x1f, 0x04, 0x51, 0xb9, 0xea, 0x38, 0x56, 0x46, - 0xd9, 0xe9, 0xd5, 0x87, 0x8e, 0x57, 0xf8, 0xd8, 0x1b, 0x67, 0x85, 0x2b, - 0x0f, 0x92, 0xd1, 0x3b, 0x2d, 0x30, 0x0f, 0x7b, 0xd8, 0xda, 0x4a, 0x8f, - 0x84, 0xd0, 0x91, 0xd5, 0x5f, 0xf3, 0x7e, 0x9e, 0x6e, 0x70, 0x12, 0xb2, - 0xff, 0xff, 0x81, 0x2d, 0xfb, 0xc3, 0xf3, 0x9f, 0x73, 0x5f, 0xb1, 0xe0, - 0xb2, 0x9b, 0xaa, 0x8c, 0xda, 0x3e, 0x71, 0x9e, 0xdf, 0xb4, 0x17, 0x70, - 0xaa, 0x2b, 0x75, 0xff, 0x9e, 0x0d, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x70, - 0x59, 0xbe, 0x22, 0x01, 0x86, 0x97, 0x47, 0x63, 0x59, 0x6f, 0x2c, 0xb7, - 0x16, 0x50, 0x0d, 0x14, 0x78, 0x8d, 0xfb, 0x1a, 0xe1, 0xb4, 0xb2, 0xf4, - 0x39, 0xc5, 0x97, 0xc1, 0x77, 0x0a, 0xa2, 0xd1, 0x5f, 0xfe, 0xc1, 0xf9, - 0xc5, 0x8a, 0x23, 0xe0, 0x18, 0xb2, 0xb4, 0x7f, 0x7e, 0x2e, 0xbe, 0x0b, - 0x97, 0xeb, 0x2f, 0xbf, 0x92, 0x0a, 0xcb, 0xfe, 0x8b, 0x37, 0x79, 0xad, - 0x3f, 0x56, 0x5d, 0xee, 0x2c, 0xbf, 0xa5, 0xc8, 0x03, 0xc5, 0x97, 0xed, - 0x0c, 0x1d, 0xe2, 0xcb, 0xfe, 0x9d, 0xbd, 0x9c, 0xf6, 0x6e, 0xac, 0xbf, - 0xfa, 0x43, 0xcc, 0x09, 0x4f, 0xfc, 0x95, 0x97, 0xfd, 0xbf, 0x99, 0xec, - 0x21, 0x06, 0xb2, 0xa4, 0xff, 0xb1, 0x0e, 0xbc, 0x8d, 0xa2, 0x85, 0xd5, - 0xfe, 0xcd, 0x7f, 0x91, 0x09, 0xc5, 0x96, 0x15, 0x65, 0xff, 0x64, 0xed, - 0x91, 0x68, 0xf8, 0xb2, 0x8c, 0xf2, 0xe2, 0x12, 0xbf, 0xff, 0x6d, 0x3d, - 0x91, 0xea, 0x7c, 0xfc, 0x2c, 0xfd, 0x65, 0xff, 0xa7, 0xe7, 0xdd, 0xf1, - 0x3e, 0xee, 0xcb, 0x2f, 0xfd, 0x3a, 0xff, 0x0f, 0x35, 0xd0, 0x2c, 0xa9, - 0x46, 0xc7, 0x95, 0x78, 0x8b, 0x43, 0x4f, 0x44, 0xc9, 0xe2, 0x84, 0x0f, - 0x21, 0xf5, 0x52, 0xac, 0x73, 0x25, 0x6a, 0x52, 0xcb, 0xda, 0x91, 0x56, - 0x5f, 0x8f, 0xa5, 0x2c, 0x59, 0x4d, 0x93, 0xce, 0x88, 0x2f, 0xc3, 0xb7, - 0xfa, 0x59, 0xad, 0x1f, 0xfc, 0x59, 0x78, 0x38, 0xd2, 0xcb, 0x64, 0x47, - 0xa3, 0xe3, 0x4b, 0x34, 0xb2, 0xff, 0x6a, 0x7b, 0xf4, 0x33, 0xab, 0x2e, - 0xc1, 0xac, 0xbd, 0xb3, 0xb1, 0x65, 0xe9, 0xff, 0x52, 0x6c, 0xcc, 0x5a, - 0xff, 0xef, 0xb9, 0xd2, 0xcd, 0xfd, 0xe9, 0x8d, 0x65, 0x1a, 0x3c, 0x62, - 0x12, 0xe3, 0x38, 0x0c, 0x6e, 0x38, 0x2c, 0xbf, 0xb3, 0x7f, 0x41, 0x21, - 0x59, 0x7f, 0xbd, 0xe0, 0x0a, 0xc9, 0xe2, 0xcb, 0xff, 0xf1, 0xf0, 0xb3, - 0x7b, 0xf4, 0xb3, 0x9e, 0x78, 0x96, 0x5f, 0xa7, 0xbc, 0x13, 0xcb, 0x2a, - 0x51, 0xf0, 0x31, 0x6c, 0x2f, 0x88, 0xd4, 0x95, 0x6f, 0x88, 0xfe, 0x82, - 0xcb, 0xdf, 0xbf, 0x96, 0x5f, 0xed, 0xb0, 0xc6, 0xee, 0x2a, 0xca, 0xc3, - 0xd0, 0x71, 0xdb, 0xfb, 0x52, 0x39, 0x28, 0x96, 0x5b, 0x8b, 0x2b, 0x63, - 0x7d, 0xe2, 0xdb, 0x37, 0x6c, 0x32, 0x6a, 0x63, 0x93, 0xed, 0x89, 0x06, - 0x53, 0x90, 0x98, 0x09, 0x17, 0xc4, 0x51, 0x11, 0x68, 0xf5, 0x82, 0xfe, - 0x96, 0x8f, 0xfb, 0xc1, 0x42, 0x17, 0x91, 0x8c, 0xf6, 0x33, 0x50, 0x24, - 0x47, 0xb9, 0x08, 0xbd, 0x7b, 0x8c, 0xe2, 0xcb, 0xff, 0xe3, 0xe9, 0xe9, - 0xff, 0x9f, 0x18, 0x67, 0xf5, 0x97, 0x42, 0x0b, 0x2f, 0xfc, 0xfe, 0x8d, - 0x39, 0xdf, 0x1b, 0x16, 0x5f, 0xff, 0xe3, 0xec, 0xf8, 0xb3, 0xba, 0x9f, - 0x16, 0x6f, 0x00, 0x56, 0x5f, 0xff, 0x4e, 0x77, 0xcf, 0x0d, 0x00, 0x72, - 0x52, 0xb2, 0xa0, 0x9a, 0x96, 0xea, 0x78, 0x05, 0xf7, 0x9f, 0xee, 0x30, - 0x5f, 0xfa, 0x4a, 0x61, 0x9c, 0xc3, 0x95, 0x97, 0xff, 0xfd, 0x3f, 0xb1, - 0xe1, 0x1b, 0xbe, 0xc6, 0x60, 0x67, 0xd2, 0xc5, 0x97, 0xff, 0xc6, 0x5d, - 0x07, 0x8f, 0x91, 0xef, 0xdc, 0x1a, 0xca, 0xf2, 0x2e, 0x7a, 0xd1, 0x7f, - 0x1e, 0xd1, 0x9c, 0xbf, 0x59, 0x7c, 0x17, 0x70, 0xaa, 0x2d, 0xa5, 0xff, - 0xfd, 0x2f, 0xd9, 0xf4, 0xf0, 0xce, 0x07, 0xac, 0x1a, 0xcb, 0xfb, 0xd2, - 0xc9, 0x31, 0xac, 0xa3, 0x4c, 0x12, 0x22, 0x3d, 0x18, 0x70, 0xbb, 0xaa, - 0xf7, 0xf3, 0xc0, 0x7e, 0x36, 0x2c, 0xbf, 0xd9, 0x11, 0x4b, 0x23, 0x05, - 0x65, 0xde, 0x31, 0x9f, 0x17, 0x4b, 0x6f, 0xfb, 0x30, 0x31, 0xe0, 0xf1, - 0xe9, 0x65, 0xff, 0xde, 0xcd, 0xb8, 0x7a, 0x9d, 0xc7, 0x25, 0x97, 0x61, - 0xac, 0xb4, 0x84, 0xf6, 0xf7, 0xa3, 0x5a, 0x3a, 0x59, 0x7f, 0xf6, 0x76, - 0x58, 0x4e, 0x29, 0x67, 0xeb, 0x2d, 0xfa, 0xca, 0x88, 0xfa, 0xc8, 0x5b, - 0xa8, 0x77, 0xfe, 0x32, 0x1c, 0xcb, 0x37, 0x1e, 0x0b, 0x2f, 0xfa, 0x7f, - 0xf4, 0xec, 0x59, 0xc5, 0x95, 0x27, 0xf0, 0x03, 0xfb, 0xff, 0xf1, 0x8c, - 0x1d, 0xe0, 0xf2, 0x1e, 0x9d, 0xe3, 0x95, 0x97, 0xfd, 0x30, 0x9d, 0x6d, - 0x3a, 0xd9, 0x65, 0xff, 0xe9, 0x87, 0xa4, 0x85, 0x2c, 0xd8, 0x48, 0x2c, - 0xbf, 0xff, 0xd2, 0x64, 0xfd, 0x07, 0x70, 0x72, 0xc1, 0xfa, 0x7e, 0x59, - 0x7f, 0x8d, 0x83, 0x9e, 0x09, 0xc5, 0x97, 0xbb, 0x81, 0x59, 0x7f, 0xfc, - 0x47, 0xbc, 0xfb, 0xe9, 0xef, 0x27, 0x7c, 0xac, 0xa3, 0x4d, 0x2f, 0x49, - 0x7e, 0x5f, 0xe9, 0xa0, 0x07, 0x2e, 0x71, 0xac, 0xbf, 0xfb, 0xae, 0x79, - 0x3a, 0x78, 0x1f, 0x96, 0x5f, 0x9c, 0x78, 0x5f, 0xac, 0xa8, 0x1f, 0x41, - 0xa0, 0xdf, 0xf4, 0xea, 0x37, 0x27, 0xce, 0x6b, 0x2f, 0xf7, 0x8d, 0xe1, - 0xd3, 0xd9, 0x65, 0xf8, 0x73, 0x84, 0x6b, 0x2a, 0x3a, 0x64, 0xed, 0xcc, - 0x6d, 0xe3, 0x53, 0xc8, 0xfd, 0xc3, 0x0a, 0xd8, 0x8b, 0x75, 0x09, 0x66, - 0x42, 0x53, 0xd0, 0xa7, 0x72, 0x12, 0x57, 0xe4, 0x70, 0xbd, 0x4a, 0x03, - 0xf3, 0x44, 0x31, 0xe7, 0x22, 0x19, 0xdf, 0xd2, 0xdf, 0x59, 0x82, 0xac, - 0xbf, 0xff, 0xb5, 0x3d, 0x30, 0x98, 0xe4, 0xf4, 0x70, 0x3e, 0x2c, 0xbf, - 0x85, 0x7d, 0x0c, 0x04, 0xb2, 0xff, 0xa7, 0xa6, 0x13, 0x1f, 0xc4, 0xb2, - 0xff, 0xff, 0xff, 0xc3, 0x29, 0x0f, 0xc1, 0x3f, 0x98, 0xe4, 0x2e, 0x13, - 0xfc, 0xd0, 0x7c, 0x71, 0x11, 0xb3, 0x8b, 0x2f, 0xfc, 0x7d, 0x9e, 0x9e, - 0xf9, 0x20, 0xac, 0xbb, 0xe8, 0x2c, 0xac, 0x47, 0x93, 0xc2, 0x70, 0x07, - 0xd7, 0xf1, 0x47, 0x9e, 0xb0, 0x55, 0x97, 0x77, 0xcb, 0x2f, 0xfb, 0x3b, - 0xc1, 0x39, 0xdc, 0xf2, 0xca, 0xd8, 0xf4, 0x58, 0x2f, 0x7e, 0x3d, 0x99, - 0x21, 0x59, 0x7e, 0x7f, 0xa0, 0x27, 0x16, 0x5f, 0xf8, 0xe2, 0xe6, 0xf7, - 0x2d, 0x80, 0x35, 0x96, 0x6f, 0x8a, 0xc6, 0xc5, 0x58, 0xd1, 0x77, 0xa3, - 0x2a, 0x23, 0x3e, 0x42, 0x07, 0xa4, 0x7b, 0xca, 0x04, 0x2a, 0xa6, 0xeb, - 0x9e, 0xd9, 0x18, 0xbf, 0xf2, 0xd2, 0xae, 0x67, 0x96, 0x5f, 0xb4, 0x17, - 0x70, 0xaa, 0x2e, 0x55, 0xfc, 0xe3, 0xf4, 0x96, 0xcb, 0x2c, 0xdc, 0x67, - 0xf1, 0x82, 0xfe, 0x34, 0xbf, 0x68, 0x2e, 0xe1, 0x54, 0x5d, 0xab, 0xdb, - 0x63, 0x4b, 0x2f, 0xf8, 0xe1, 0x03, 0x2f, 0xe6, 0x0b, 0x2e, 0x64, 0x7a, - 0xcb, 0xe3, 0xd3, 0xfe, 0xb2, 0xf3, 0xed, 0x2b, 0x2c, 0xdf, 0x11, 0xc4, - 0xc3, 0x47, 0x1e, 0x23, 0x8e, 0x0d, 0x00, 0x8a, 0xa5, 0xfb, 0x84, 0xf6, - 0x2a, 0x85, 0x39, 0xfc, 0x72, 0x87, 0xb0, 0xdc, 0x5a, 0xcc, 0x4c, 0xed, - 0x6a, 0x56, 0xec, 0x2e, 0xfd, 0x49, 0xd3, 0x2a, 0x7f, 0xe7, 0x21, 0x75, - 0xd8, 0xc6, 0x2f, 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0xa9, 0xd7, 0xe2, - 0x6f, 0xae, 0x71, 0x65, 0xdb, 0x62, 0xcb, 0xee, 0x17, 0xba, 0xb2, 0xff, - 0x7a, 0x79, 0x2c, 0x04, 0x4b, 0x2a, 0x07, 0xac, 0x32, 0x2b, 0xf8, 0x1f, - 0x31, 0xdc, 0x96, 0x5b, 0xab, 0x2f, 0xbe, 0xe4, 0x8d, 0x65, 0x80, 0xb2, - 0xf4, 0xbe, 0xd1, 0x8d, 0xa7, 0xe4, 0x74, 0x67, 0xf4, 0x48, 0xb7, 0xc1, - 0x77, 0x0a, 0xa2, 0xb9, 0x5f, 0xe6, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x67, - 0x2e, 0x78, 0x2c, 0xbf, 0xa1, 0xdf, 0x82, 0x7c, 0x59, 0x4e, 0x78, 0xa0, - 0x16, 0xbf, 0x8f, 0x98, 0x4e, 0xd2, 0xca, 0xd2, 0x60, 0x3e, 0x2e, 0x28, - 0x43, 0x74, 0x86, 0xff, 0xf7, 0x67, 0x91, 0x81, 0xce, 0xcc, 0x51, 0x1a, - 0xcb, 0xf8, 0xd9, 0xb0, 0x35, 0x05, 0x97, 0xe6, 0x64, 0x4f, 0xfa, 0xcb, - 0xed, 0xbc, 0xfd, 0x59, 0x4e, 0x79, 0x80, 0x29, 0xbf, 0xd3, 0xe8, 0xcc, - 0x8e, 0x5b, 0x1d, 0x59, 0x58, 0x99, 0x4f, 0x93, 0x89, 0xef, 0x79, 0x0d, - 0xff, 0x14, 0xb3, 0x80, 0x83, 0x8a, 0xb2, 0xfd, 0xac, 0xe9, 0xb1, 0x65, - 0xcc, 0xde, 0xb2, 0xf1, 0x48, 0xd6, 0x56, 0x1b, 0x47, 0x19, 0xbf, 0x45, - 0x2c, 0x91, 0x16, 0x5d, 0x06, 0x2c, 0xac, 0x3c, 0x02, 0x29, 0xbf, 0xfd, - 0xe3, 0x8d, 0xe7, 0x3e, 0xce, 0x0c, 0xd6, 0x5f, 0xf8, 0xf5, 0x1a, 0x58, - 0x7b, 0xaf, 0x12, 0xcb, 0xe0, 0xbb, 0x85, 0x51, 0x21, 0x28, 0x53, 0xf1, - 0xd2, 0x1d, 0xff, 0x67, 0x78, 0x08, 0x4e, 0xd2, 0xb2, 0xff, 0xfc, 0x7a, - 0x0f, 0xb2, 0x36, 0xb5, 0x8d, 0x47, 0xcb, 0x16, 0x54, 0x11, 0x29, 0xc3, - 0x9b, 0xff, 0x63, 0x31, 0xf5, 0xb4, 0x6f, 0x98, 0xb2, 0xff, 0x7d, 0xce, - 0x94, 0xb3, 0x8b, 0x2f, 0xff, 0x39, 0x06, 0x30, 0xc9, 0xe1, 0xe7, 0x82, - 0xca, 0x33, 0xfe, 0x01, 0xa5, 0xfd, 0x84, 0x7f, 0xf2, 0x56, 0x59, 0xbc, - 0x15, 0x50, 0xe4, 0x2e, 0xbe, 0x85, 0xbf, 0x88, 0xff, 0x86, 0x07, 0x08, - 0x6e, 0x3f, 0x2c, 0xbf, 0xf3, 0x89, 0x11, 0x1f, 0x4a, 0x7f, 0x59, 0x50, - 0x46, 0x37, 0xce, 0x3d, 0x16, 0xbf, 0xfb, 0xc6, 0xcc, 0xe4, 0x6f, 0xa1, - 0x9d, 0x59, 0x7d, 0x0f, 0x09, 0xc5, 0x97, 0x47, 0x18, 0xe5, 0x65, 0xf4, - 0x98, 0x38, 0xb2, 0xcd, 0xdb, 0x5c, 0xf0, 0xfc, 0x45, 0x52, 0x8d, 0xfc, - 0x47, 0x76, 0x7b, 0xe8, 0xe7, 0xd2, 0xc5, 0x97, 0xf7, 0xb9, 0xcc, 0xd6, - 0xcb, 0x2e, 0x96, 0x96, 0x5f, 0xf7, 0xf8, 0x32, 0x7f, 0xe7, 0x7a, 0xca, - 0xc4, 0x40, 0x99, 0x7b, 0x8b, 0xde, 0x66, 0xf8, 0x2c, 0xbf, 0xfd, 0xe9, - 0xfd, 0x8f, 0x0e, 0x77, 0x31, 0x8b, 0x2f, 0x44, 0xc8, 0x96, 0x59, 0xbb, - 0x6a, 0x65, 0x8a, 0x4b, 0x6e, 0xc4, 0x43, 0x85, 0x3e, 0x46, 0x42, 0x71, - 0xb5, 0xc4, 0x7d, 0xa3, 0x96, 0x2d, 0xf9, 0x7d, 0xe5, 0xc3, 0x94, 0x62, - 0x1c, 0x2d, 0xec, 0x2a, 0x00, 0x5a, 0x20, 0xfe, 0xe2, 0x55, 0xff, 0xec, - 0xf9, 0xb8, 0xc0, 0xfa, 0x87, 0x9a, 0x75, 0x97, 0xff, 0x37, 0xf3, 0xf0, - 0xb3, 0xbd, 0x9f, 0xd6, 0x5f, 0xfe, 0x6e, 0xc7, 0x83, 0x7c, 0xd0, 0x5d, - 0xc2, 0xa8, 0x9f, 0x17, 0xc0, 0xd4, 0xf1, 0x65, 0xf8, 0x57, 0xee, 0x34, - 0xb2, 0xee, 0xb7, 0x19, 0xe5, 0x80, 0x8a, 0x82, 0xce, 0x04, 0x34, 0x1d, - 0xda, 0x59, 0xf6, 0xa3, 0x1c, 0x62, 0x77, 0x91, 0x9e, 0x14, 0xb7, 0xff, - 0xf0, 0x09, 0xc5, 0x8d, 0xd1, 0x64, 0xa1, 0x9e, 0x36, 0x2c, 0xb8, 0xfe, - 0x59, 0x7f, 0xfd, 0x0d, 0x9b, 0x49, 0x8e, 0x76, 0x0e, 0xa3, 0x46, 0xf4, - 0x7a, 0xca, 0x81, 0xfe, 0x7e, 0x2f, 0x7f, 0xf8, 0xb6, 0xf4, 0x9f, 0x78, - 0x65, 0xf4, 0x16, 0x5f, 0xfb, 0xa2, 0xc9, 0x43, 0x3c, 0x6c, 0x59, 0x7f, - 0xc2, 0xc9, 0x43, 0x3c, 0x6c, 0x59, 0x7c, 0x02, 0x71, 0x63, 0x1f, 0xb7, - 0x4f, 0xaf, 0xda, 0xfc, 0xfd, 0x2b, 0x29, 0x87, 0xc6, 0x03, 0xbb, 0xf6, - 0x6b, 0x32, 0x25, 0x96, 0x6e, 0xc5, 0x4d, 0x1e, 0x86, 0xcb, 0x91, 0xf2, - 0x32, 0x31, 0x08, 0xaf, 0xda, 0x0b, 0xb8, 0x55, 0x15, 0x92, 0xff, 0xcf, - 0x06, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x37, 0x2c, 0xdf, 0x11, 0x00, 0xc3, - 0x4a, 0x0a, 0x61, 0x27, 0x0d, 0xfb, 0xe0, 0xbb, 0x85, 0x51, 0x2b, 0x2f, - 0xff, 0xb5, 0xb1, 0x4e, 0x9c, 0x64, 0xfa, 0x9d, 0xeb, 0x2b, 0x47, 0xfc, - 0x02, 0xeb, 0xf1, 0x48, 0xdf, 0xcb, 0x2f, 0xf4, 0x99, 0x4c, 0x27, 0x71, - 0x65, 0xf3, 0x1e, 0x0d, 0xf0, 0xfe, 0xcc, 0x8b, 0x84, 0xd7, 0xe6, 0xef, - 0x17, 0x31, 0x65, 0xf9, 0xbe, 0x42, 0x7f, 0x59, 0x4d, 0xd1, 0x35, 0xb2, - 0x34, 0x0a, 0xaf, 0xfd, 0xdc, 0x6f, 0x9a, 0x3d, 0x9d, 0x8b, 0x2f, 0xff, - 0x47, 0x4d, 0x96, 0xc7, 0x31, 0xe0, 0x79, 0xac, 0x59, 0x7f, 0xe3, 0xe0, - 0x18, 0x47, 0xbc, 0x0e, 0xb2, 0xff, 0xfb, 0xd3, 0x03, 0xff, 0x59, 0xe7, - 0x27, 0x82, 0xcb, 0xfa, 0x49, 0xc2, 0xfb, 0xab, 0x2f, 0xfa, 0x43, 0x3a, - 0x3e, 0xc8, 0x56, 0x54, 0x11, 0xe5, 0xf1, 0xfb, 0x13, 0xba, 0x5d, 0x70, - 0xf4, 0xb2, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x2e, 0x2f, 0xf8, 0xde, 0x1d, - 0x29, 0x67, 0x16, 0x5f, 0xa1, 0x1e, 0x7e, 0x75, 0x97, 0xa6, 0x3e, 0x56, - 0x5f, 0xff, 0xd8, 0x00, 0xfa, 0x4a, 0x60, 0xe3, 0x9f, 0xb4, 0x6b, 0x2f, - 0xfd, 0xc3, 0x20, 0x76, 0x3c, 0xf7, 0xb1, 0x65, 0xe3, 0x29, 0x59, 0x50, - 0x47, 0xf0, 0xca, 0xb7, 0x47, 0x98, 0xb3, 0xbd, 0x0e, 0xff, 0xe0, 0x40, - 0x8e, 0x13, 0xd2, 0x04, 0x16, 0x5f, 0xa7, 0x9f, 0xb3, 0xab, 0x2d, 0xd3, - 0x3e, 0xcf, 0x21, 0xd8, 0x2b, 0x29, 0xcd, 0xc7, 0xe4, 0xf7, 0xf4, 0x3d, - 0x85, 0x81, 0x59, 0x7a, 0x1e, 0xea, 0xcb, 0xbd, 0x26, 0x79, 0x3e, 0x2c, - 0xbf, 0xf8, 0x8f, 0xfe, 0x6a, 0x4e, 0x0f, 0xc5, 0x95, 0x87, 0xde, 0x65, - 0x97, 0xf6, 0x8d, 0x84, 0xf0, 0x59, 0x7f, 0xef, 0xa1, 0x9d, 0xcd, 0x06, - 0x71, 0x65, 0xe7, 0x83, 0x78, 0xe2, 0xba, 0xb7, 0x31, 0xa3, 0xc0, 0xfb, - 0x05, 0xfc, 0x68, 0xf1, 0x9a, 0xf2, 0x1e, 0x7d, 0x86, 0xc8, 0x08, 0x37, - 0x0b, 0x2f, 0xfc, 0x06, 0x37, 0xe4, 0x97, 0x73, 0x4b, 0x2f, 0xf8, 0x78, - 0xde, 0x74, 0x7e, 0x02, 0xcb, 0xff, 0x87, 0x2d, 0xf7, 0x33, 0xe8, 0x7a, - 0x78, 0xb2, 0xcd, 0xcd, 0x7c, 0x71, 0x93, 0xb3, 0x7c, 0x85, 0xe8, 0x10, - 0x37, 0x9d, 0x5f, 0xfc, 0xdd, 0xe0, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x23, - 0xb5, 0xff, 0xf0, 0x27, 0xe8, 0x1b, 0x1a, 0xc2, 0x1f, 0xa5, 0x65, 0xf9, - 0x9f, 0x74, 0xda, 0x59, 0x76, 0xcd, 0xc6, 0x7f, 0x42, 0x28, 0x5f, 0xef, - 0x64, 0x1b, 0xb9, 0x71, 0x65, 0x61, 0xf3, 0x74, 0xca, 0xff, 0x9b, 0x11, - 0xf8, 0x29, 0xf4, 0xa5, 0x65, 0xfd, 0x1d, 0x94, 0x87, 0x09, 0x65, 0xc0, - 0xf2, 0xcb, 0xf9, 0xb0, 0x59, 0xe7, 0xea, 0xcb, 0xec, 0x66, 0xa5, 0x65, - 0xfb, 0x08, 0xff, 0x11, 0x65, 0x11, 0xe4, 0xf0, 0x86, 0xf6, 0x6a, 0x56, - 0x5f, 0xff, 0xa0, 0x63, 0xf1, 0xef, 0x8d, 0x83, 0xd1, 0x80, 0x2b, 0x2f, - 0xa7, 0x53, 0xa5, 0x94, 0x68, 0xb6, 0xd1, 0x0f, 0x46, 0xc4, 0x59, 0xbf, - 0xd8, 0xd7, 0x24, 0xbb, 0xc5, 0x97, 0xff, 0x67, 0x3b, 0x9e, 0x92, 0xef, - 0x8d, 0x65, 0xfd, 0xf9, 0x67, 0xdd, 0xc5, 0x97, 0x6d, 0xba, 0xb2, 0xfc, - 0xe2, 0xfb, 0x3f, 0x59, 0x7f, 0x17, 0xef, 0xa7, 0x11, 0x65, 0x4a, 0x28, - 0x36, 0x2e, 0xd0, 0xdb, 0x94, 0xdf, 0xff, 0xff, 0xbd, 0x3e, 0xf3, 0x8c, - 0x9e, 0x37, 0x3d, 0x3c, 0x8c, 0x07, 0x8d, 0x0c, 0xdf, 0x2b, 0x2f, 0xda, - 0xd4, 0xef, 0xe2, 0xcb, 0xff, 0x1e, 0xa3, 0x70, 0xb1, 0x92, 0x15, 0x97, - 0xf8, 0x1d, 0x37, 0xde, 0xf0, 0x59, 0x7f, 0x3e, 0xf1, 0xe1, 0x0a, 0xb2, - 0xd2, 0x47, 0xc6, 0x21, 0xa5, 0xff, 0xff, 0xdf, 0xc9, 0x05, 0xfa, 0xff, - 0x7f, 0x1b, 0x08, 0xe4, 0x1e, 0xf4, 0xac, 0xbf, 0xf1, 0x02, 0x11, 0xb0, - 0xca, 0x58, 0xb2, 0xfe, 0xe4, 0x0c, 0x2f, 0xa5, 0x97, 0x9d, 0xc2, 0xa8, - 0xa8, 0x55, 0x27, 0xaa, 0x12, 0xdb, 0xff, 0x1f, 0xfc, 0x8d, 0xcf, 0xdf, - 0x22, 0x59, 0x7f, 0x00, 0x26, 0x2b, 0x85, 0x65, 0xff, 0x67, 0xb0, 0x9f, - 0x5a, 0x35, 0x95, 0x05, 0x69, 0x59, 0x08, 0x91, 0x4a, 0x83, 0x0a, 0x2f, - 0x89, 0xcd, 0xcf, 0x50, 0x93, 0xf1, 0x09, 0x21, 0xf0, 0xba, 0xf6, 0xeb, - 0xca, 0xcb, 0x62, 0xcb, 0xff, 0xe3, 0x64, 0x86, 0x30, 0x7c, 0x7b, 0x92, - 0xc9, 0x59, 0x50, 0x3e, 0x3f, 0x08, 0x5f, 0xf4, 0x3a, 0x7a, 0x70, 0x99, - 0x2c, 0xbd, 0xff, 0x31, 0x65, 0xfe, 0xe0, 0x22, 0x71, 0xbc, 0x4b, 0x2a, - 0x07, 0xa5, 0xf0, 0xed, 0xfa, 0x05, 0x2c, 0x15, 0x65, 0xff, 0xcc, 0xcd, - 0x16, 0x1e, 0xf3, 0xd3, 0xac, 0xaf, 0x8f, 0xad, 0xca, 0x2f, 0xfc, 0x59, - 0x17, 0x7d, 0x9a, 0x3e, 0x2c, 0xbf, 0xf9, 0xd9, 0x19, 0x8f, 0xd8, 0xde, - 0xee, 0x96, 0x57, 0x91, 0x0e, 0x47, 0xd7, 0xef, 0x88, 0xd8, 0x6b, 0x2f, - 0xd3, 0x1d, 0xed, 0x8d, 0x2c, 0xbe, 0x8f, 0x06, 0xe4, 0xac, 0xa9, 0x4d, - 0x23, 0x21, 0x52, 0x64, 0x5a, 0x27, 0x01, 0x75, 0xfd, 0xd7, 0xf4, 0x61, - 0xca, 0xcb, 0xfe, 0xef, 0x04, 0xe3, 0xfd, 0x0c, 0x59, 0x7f, 0x9e, 0x11, - 0xb8, 0x0f, 0xdd, 0x65, 0xf4, 0xfa, 0x76, 0x59, 0x63, 0x19, 0xec, 0x78, - 0xda, 0xa5, 0x1d, 0x38, 0x5e, 0x08, 0x49, 0xdf, 0xb8, 0x7e, 0xc1, 0xac, - 0xbf, 0x42, 0x7b, 0x9b, 0x2c, 0xad, 0xd3, 0xcf, 0xd1, 0x3d, 0xef, 0xbb, - 0x8b, 0x2f, 0xee, 0x19, 0x07, 0xd2, 0xb2, 0xb0, 0xfb, 0xb4, 0x4b, 0xd1, - 0xdb, 0xff, 0x67, 0x79, 0xac, 0x8b, 0xc6, 0x2a, 0xcb, 0xff, 0xf8, 0xf9, - 0xfe, 0x40, 0xa7, 0xf7, 0xf7, 0xd0, 0xce, 0xac, 0xbf, 0xec, 0x17, 0x18, - 0x71, 0xf2, 0xc5, 0x95, 0xa4, 0x4b, 0x92, 0xe5, 0xff, 0x46, 0x72, 0xcd, - 0xc8, 0xde, 0x02, 0xcb, 0xf3, 0x47, 0xac, 0x15, 0x65, 0x4a, 0x71, 0xe6, - 0x5c, 0xf0, 0xd7, 0x01, 0x10, 0x87, 0xf7, 0xfa, 0x13, 0xad, 0xa7, 0x5b, - 0x2c, 0xbf, 0xf7, 0x30, 0xbb, 0xcc, 0x81, 0xf9, 0x65, 0xfd, 0xcc, 0xde, - 0x53, 0xf2, 0xcb, 0x81, 0xe5, 0x95, 0xc4, 0x41, 0xf4, 0xf6, 0x3c, 0xbe, - 0xff, 0xfa, 0x7f, 0xc2, 0x8c, 0xfd, 0x8d, 0xec, 0xfd, 0xd6, 0x5c, 0x5b, - 0x2c, 0xa9, 0x4d, 0x57, 0x21, 0x70, 0x66, 0x64, 0xa7, 0x7f, 0xfb, 0xd2, - 0xc2, 0x71, 0x4b, 0x3f, 0xfb, 0xf5, 0x97, 0xf6, 0xf7, 0x67, 0x9a, 0x75, - 0x97, 0xff, 0xf8, 0xe7, 0xaf, 0xe6, 0xfc, 0xf6, 0x17, 0x63, 0x7f, 0x0e, - 0x2c, 0xa8, 0x23, 0x84, 0xd3, 0x08, 0xc2, 0xf7, 0x01, 0xa5, 0x97, 0x8f, - 0xc6, 0xb2, 0xfe, 0xd3, 0x97, 0xff, 0xca, 0xca, 0xc4, 0x4a, 0x19, 0x73, - 0x8e, 0x88, 0x37, 0x7f, 0xd0, 0x2c, 0xef, 0x3f, 0x3f, 0x96, 0x5f, 0xa5, - 0x8f, 0xf7, 0x56, 0x5d, 0x9b, 0x2c, 0xbf, 0xf8, 0x7c, 0xcd, 0x16, 0x7f, - 0xcc, 0xd2, 0xca, 0xe2, 0x20, 0xfa, 0x50, 0x01, 0x7b, 0xf6, 0x04, 0x0f, - 0xb2, 0xca, 0xf1, 0xec, 0x11, 0x85, 0xff, 0xf6, 0x88, 0x1f, 0x16, 0x1e, - 0x83, 0xe9, 0xe2, 0xcb, 0xff, 0xf8, 0x2f, 0xec, 0x84, 0xea, 0x7f, 0xd4, - 0x88, 0xe4, 0xb2, 0xa5, 0x15, 0x38, 0x9f, 0x7e, 0xcd, 0x9c, 0xb7, 0xac, - 0xbf, 0xff, 0xff, 0xd3, 0x08, 0xdd, 0xf1, 0xcf, 0x23, 0x60, 0x03, 0x3b, - 0xe3, 0x66, 0xb5, 0x9b, 0xc0, 0xfe, 0x59, 0x7d, 0xe2, 0x7d, 0xc5, 0x95, - 0x2a, 0xb9, 0x32, 0x31, 0x5f, 0x43, 0x28, 0x88, 0x78, 0x53, 0xd8, 0x4b, - 0xdf, 0xe7, 0xfb, 0x86, 0xc9, 0x0a, 0xcb, 0xed, 0x9e, 0x74, 0xb2, 0xff, - 0xff, 0x3e, 0xe9, 0xed, 0xe7, 0x3e, 0xcf, 0x8f, 0xbc, 0xc1, 0xac, 0xa7, - 0x44, 0x20, 0x08, 0xaf, 0xfd, 0xbd, 0xe1, 0x84, 0x32, 0x98, 0x2c, 0xbb, - 0x69, 0x59, 0x62, 0xc3, 0xd5, 0x01, 0xf5, 0xff, 0x9d, 0xfc, 0x7c, 0xe0, - 0x1f, 0xf5, 0x96, 0x6f, 0x1d, 0x36, 0xc9, 0xad, 0x83, 0xf8, 0xe4, 0xc2, - 0x3b, 0x17, 0x98, 0xcb, 0xf6, 0x3d, 0x81, 0x98, 0xe1, 0xc5, 0x92, 0xe1, - 0x85, 0x84, 0xd8, 0x61, 0x03, 0xf1, 0x11, 0xc2, 0x27, 0x51, 0xeb, 0x32, - 0x33, 0x6f, 0x4a, 0x7f, 0x28, 0xe8, 0xf9, 0x1d, 0xa7, 0x65, 0x8f, 0x81, - 0x9a, 0x3e, 0x16, 0xe2, 0x3b, 0xee, 0x13, 0x5f, 0x05, 0xdc, 0x2a, 0x8a, - 0xa1, 0x7f, 0xb5, 0x3b, 0xfe, 0x86, 0x75, 0x65, 0x68, 0xf9, 0x00, 0x5d, - 0x7f, 0x85, 0x9e, 0xf3, 0x7e, 0x0d, 0x65, 0xff, 0x9e, 0x0d, 0xf3, 0x41, - 0x77, 0x0a, 0xa2, 0x6b, 0x5f, 0xf7, 0xa5, 0x9c, 0xe3, 0x90, 0x56, 0x5f, - 0x4e, 0xa7, 0xab, 0x2e, 0x7e, 0xac, 0xa1, 0x9b, 0x76, 0x10, 0xd9, 0xbe, - 0x26, 0x68, 0x29, 0x13, 0x0d, 0xbc, 0x98, 0xed, 0xf7, 0xed, 0x05, 0xdc, - 0x2a, 0x8a, 0xb1, 0x7b, 0x52, 0xc5, 0x97, 0xfd, 0x30, 0x9d, 0x6d, 0x3a, - 0xd9, 0x65, 0x9b, 0xe2, 0x22, 0xb4, 0x68, 0x43, 0x97, 0xfa, 0x7e, 0xec, - 0xf9, 0xf7, 0x56, 0x5f, 0xda, 0x04, 0x5c, 0x9e, 0xac, 0xaf, 0x1f, 0x28, - 0x0d, 0xaf, 0xfe, 0xff, 0x09, 0xbc, 0x3f, 0xe6, 0xce, 0x4b, 0x29, 0xb9, - 0xf5, 0x49, 0x15, 0xfc, 0x6c, 0x3e, 0xfa, 0x56, 0x5f, 0x4c, 0x53, 0xf2, - 0xca, 0xf1, 0xe7, 0xf0, 0xb2, 0xff, 0xa0, 0xdf, 0x34, 0x17, 0x70, 0xaa, - 0x24, 0x45, 0xfd, 0xb4, 0xeb, 0xce, 0x6b, 0x2c, 0xdf, 0x11, 0x2c, 0xe4, - 0x5c, 0x47, 0xbf, 0x68, 0x2e, 0xe1, 0x54, 0x5a, 0x4b, 0xff, 0x3c, 0x1b, - 0xe6, 0x82, 0xee, 0x15, 0x44, 0xfa, 0xb3, 0x7c, 0x44, 0x03, 0x0d, 0x2f, - 0xff, 0x37, 0x63, 0xc1, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x50, 0x8b, 0xff, - 0xb1, 0xa6, 0xfd, 0x15, 0xf8, 0x7d, 0x95, 0x97, 0xee, 0x80, 0x2f, 0xc5, - 0x97, 0xc7, 0xb4, 0xe9, 0x65, 0x30, 0xf2, 0xbc, 0x51, 0x7e, 0xd0, 0x5d, - 0xc2, 0xa8, 0xa3, 0xd7, 0xfd, 0x30, 0x9d, 0x6d, 0x3a, 0xd9, 0x65, 0xff, - 0xff, 0xfe, 0x10, 0xf5, 0x14, 0xfb, 0x59, 0xe7, 0x13, 0x98, 0x2b, 0x97, - 0xf2, 0x53, 0x17, 0xa5, 0x65, 0xf9, 0xf8, 0x7d, 0x95, 0x97, 0xfd, 0x31, - 0x49, 0x4c, 0x5e, 0x95, 0x95, 0x28, 0xee, 0x16, 0x12, 0x04, 0x4d, 0x7f, - 0xff, 0x60, 0xfd, 0x22, 0x37, 0xe9, 0x3f, 0xfc, 0xe4, 0xfe, 0x92, 0xf3, - 0xc1, 0xbc, 0xaa, 0x11, 0xc2, 0x22, 0x34, 0xec, 0x65, 0x11, 0xe6, 0x97, - 0xff, 0x67, 0x9b, 0xf4, 0x57, 0xe1, 0xf6, 0x56, 0x59, 0xbe, 0xca, 0xd3, - 0x59, 0x2a, 0x8b, 0xac, 0xb5, 0x2e, 0xa9, 0x13, 0x69, 0xf4, 0xf8, 0x31, - 0xe4, 0x66, 0x82, 0xd6, 0xcd, 0xc1, 0x96, 0x7d, 0xf4, 0x37, 0x0e, 0x30, - 0x58, 0xa3, 0x08, 0xf4, 0x2d, 0xbf, 0x2a, 0xec, 0xba, 0x7b, 0xff, 0xe6, - 0x83, 0x1d, 0x36, 0x3f, 0x6d, 0xea, 0x39, 0x8d, 0x1b, 0xd1, 0xeb, 0x2f, - 0xff, 0xfd, 0xb9, 0xd8, 0xec, 0x6c, 0x8e, 0xc8, 0xdb, 0x57, 0x5b, 0x7a, - 0x8e, 0x63, 0x46, 0xf4, 0x7a, 0xca, 0xf2, 0x60, 0x0d, 0x37, 0x5f, 0xfd, - 0xe9, 0x3d, 0x1b, 0x6c, 0x3e, 0x96, 0x96, 0x5f, 0xfb, 0x8d, 0xb3, 0x16, - 0x34, 0x6f, 0x47, 0xb7, 0x93, 0xed, 0x09, 0x2d, 0xfb, 0x41, 0x77, 0x0a, - 0xa2, 0x22, 0x5f, 0xee, 0x02, 0x62, 0xe9, 0xef, 0x59, 0x66, 0xf8, 0x7d, - 0x51, 0x1a, 0x5f, 0xd9, 0xa0, 0xbb, 0x85, 0x51, 0x15, 0xaf, 0xfb, 0x75, - 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x57, 0x0a, 0x6e, 0x88, 0x37, 0x39, 0xbe, - 0xf3, 0xe8, 0xd6, 0x5f, 0xbb, 0xf8, 0x85, 0x05, 0x97, 0xef, 0xe4, 0x82, - 0xde, 0x4f, 0x2d, 0xc8, 0x6f, 0x9b, 0xed, 0x16, 0xea, 0xcb, 0x1f, 0xc7, - 0xd3, 0xf9, 0xfd, 0xfe, 0x6c, 0x47, 0x45, 0x21, 0x7f, 0x96, 0x5f, 0x67, - 0x9f, 0xab, 0x2f, 0xf6, 0x1e, 0xbf, 0xfc, 0x1c, 0x59, 0x66, 0xc1, 0x1e, - 0xb7, 0x08, 0x6f, 0xed, 0x4f, 0x5c, 0xb1, 0x65, 0xff, 0xfb, 0x87, 0xe7, - 0x38, 0x19, 0x74, 0x1e, 0xd6, 0x2c, 0xa3, 0x3f, 0xee, 0x95, 0xdf, 0xd3, - 0xff, 0x3a, 0x52, 0xb2, 0xf8, 0x2e, 0xe1, 0x54, 0x53, 0xcb, 0xff, 0x14, - 0xef, 0x3e, 0xc2, 0x70, 0x96, 0x56, 0x8f, 0xaf, 0xa5, 0xd7, 0xf8, 0x7f, - 0xc6, 0x9d, 0xd9, 0x82, 0xcb, 0xfc, 0xc3, 0x18, 0x1f, 0x50, 0x59, 0x73, - 0x0d, 0x65, 0x49, 0xe4, 0x84, 0xce, 0xff, 0xfe, 0x9e, 0xf0, 0x0c, 0x09, - 0xff, 0x39, 0xe3, 0x00, 0x56, 0x5f, 0xd2, 0xce, 0x49, 0xec, 0xb2, 0xa0, - 0x88, 0x8e, 0xad, 0xdf, 0xff, 0xcf, 0xa7, 0xf7, 0xd0, 0xce, 0xe1, 0x03, - 0xa0, 0x15, 0x65, 0xf4, 0x27, 0x36, 0x59, 0x7f, 0xfe, 0xd0, 0x35, 0xa9, - 0x2c, 0x14, 0xfd, 0xec, 0x11, 0x65, 0x05, 0x1a, 0x9f, 0x2e, 0x68, 0x8a, - 0xf1, 0x97, 0x56, 0x5f, 0xfc, 0xf0, 0x6e, 0xe3, 0xc8, 0x7a, 0x42, 0xb2, - 0xff, 0xc7, 0xa7, 0xd8, 0x60, 0x7d, 0x41, 0x65, 0xff, 0xa1, 0x85, 0xfc, - 0xb0, 0xf5, 0x05, 0x97, 0xff, 0xcc, 0xc3, 0xe8, 0x3b, 0x30, 0x31, 0xe0, - 0xd6, 0x51, 0xa2, 0x33, 0xf3, 0xeb, 0xfa, 0x75, 0xb4, 0xeb, 0x65, 0x97, - 0xf4, 0xec, 0xe3, 0x7e, 0xac, 0xa8, 0x1e, 0xe6, 0xe1, 0x7d, 0xf4, 0x62, - 0x78, 0x2c, 0xbf, 0x3e, 0x78, 0xd8, 0xb2, 0xb8, 0x79, 0x3b, 0xc8, 0xea, - 0x51, 0x25, 0x8d, 0xd7, 0xfd, 0xbd, 0xd9, 0x18, 0x7e, 0x36, 0x2c, 0xbf, - 0xe2, 0x96, 0x4c, 0x0b, 0x18, 0xb2, 0xff, 0xf3, 0x27, 0x5b, 0x4f, 0xd8, - 0x06, 0xde, 0x0d, 0x65, 0x62, 0x30, 0x8c, 0xf5, 0xcd, 0xaf, 0xf1, 0x07, - 0x37, 0xe7, 0x78, 0xb2, 0xe7, 0x15, 0x65, 0x49, 0xe5, 0xe8, 0xd2, 0xfc, - 0xfb, 0x4e, 0x80, 0xb2, 0xa3, 0x96, 0x42, 0x4c, 0x77, 0x09, 0x29, 0x85, - 0x4c, 0x08, 0x72, 0x13, 0x02, 0x91, 0x06, 0x10, 0x5f, 0x42, 0xa4, 0xe1, - 0xef, 0x11, 0x8b, 0x06, 0xfc, 0x8a, 0xf0, 0xcb, 0x28, 0xc4, 0x79, 0x0e, - 0x3d, 0xef, 0x11, 0xe4, 0x37, 0xff, 0x9e, 0x0d, 0xc6, 0x4e, 0xd7, 0x39, - 0x3f, 0xac, 0xbf, 0xfd, 0xff, 0xe0, 0xe3, 0x71, 0x09, 0xd8, 0x52, 0xb2, - 0xff, 0xe2, 0xcf, 0xdc, 0xbf, 0x6e, 0xc3, 0x62, 0xca, 0xd2, 0x25, 0x3a, - 0x9d, 0x7e, 0x7f, 0x78, 0xe2, 0x59, 0x7f, 0xf6, 0x70, 0xcb, 0xf2, 0xce, - 0xfb, 0x16, 0x5f, 0xf8, 0xcb, 0xf2, 0xce, 0xfb, 0x1b, 0xf8, 0xfa, 0x84, - 0x28, 0xa6, 0xea, 0xba, 0x87, 0x19, 0x29, 0xc3, 0x87, 0x90, 0x8d, 0xb6, - 0x2c, 0xbf, 0x14, 0x81, 0xb7, 0xc5, 0x97, 0xf0, 0xba, 0x71, 0xb9, 0x2c, - 0xa1, 0x9f, 0x46, 0x08, 0x39, 0x5d, 0xff, 0xc6, 0x3d, 0x3f, 0xcd, 0xf5, - 0xa9, 0xd9, 0x65, 0x37, 0x3f, 0x5f, 0x16, 0xdc, 0x39, 0x59, 0x7f, 0xfe, - 0xf4, 0xeb, 0x20, 0x6f, 0xdc, 0x19, 0x80, 0x29, 0x2f, 0xd3, 0x0f, 0xc1, - 0x05, 0x97, 0xc1, 0x77, 0x0a, 0xa2, 0xb3, 0x54, 0x47, 0xaf, 0xa2, 0x9b, - 0xee, 0x9b, 0x38, 0xb2, 0xee, 0x0a, 0xb2, 0xf7, 0x00, 0x15, 0x94, 0x29, - 0xb5, 0xe0, 0xc5, 0xf7, 0x35, 0x3b, 0xd6, 0x5f, 0xdb, 0xc8, 0xa7, 0x76, - 0x25, 0x97, 0xe6, 0x49, 0x43, 0x8b, 0x2a, 0x4f, 0x67, 0x63, 0x2b, 0xee, - 0xf0, 0x4e, 0x2c, 0xbe, 0xe7, 0x25, 0xa5, 0x96, 0x75, 0x95, 0x87, 0xae, - 0x64, 0x9c, 0x23, 0xbb, 0x63, 0x59, 0x7f, 0xb9, 0x11, 0x97, 0xb3, 0xe5, - 0x95, 0x27, 0x97, 0x82, 0xf7, 0x3f, 0x56, 0x5f, 0x7e, 0x21, 0x41, 0x65, - 0x0c, 0xdd, 0x74, 0x5a, 0xff, 0xfa, 0x59, 0xdc, 0xc2, 0xef, 0x3d, 0x9f, - 0xba, 0xcb, 0xfc, 0xc9, 0xfb, 0x9a, 0x9d, 0xeb, 0x2e, 0x7e, 0x2c, 0xa9, - 0x3c, 0xc0, 0x1b, 0x5f, 0xe8, 0x18, 0x37, 0x63, 0x9f, 0xd8, 0xb2, 0xfc, - 0x53, 0xe3, 0xc5, 0x95, 0x87, 0xc4, 0xe7, 0xb6, 0x35, 0x97, 0xf7, 0xb0, - 0x89, 0xfa, 0xb3, 0x85, 0x85, 0xf8, 0xbb, 0xe9, 0xde, 0xb2, 0xf3, 0xbb, - 0x16, 0x5f, 0xb2, 0x2d, 0x81, 0xfa, 0xcb, 0xfb, 0xb3, 0xaf, 0xe6, 0x25, - 0x96, 0x6f, 0x2b, 0xad, 0x43, 0x16, 0xc8, 0x55, 0x7c, 0x46, 0x6a, 0xf1, - 0x11, 0x69, 0xf5, 0x8f, 0x3e, 0x78, 0x75, 0xaf, 0xc8, 0x4a, 0x13, 0x5c, - 0x84, 0x1f, 0x4c, 0x00, 0x75, 0xbc, 0xa4, 0x41, 0xbd, 0xc2, 0xbb, 0xfe, - 0x3e, 0xbe, 0xb6, 0x93, 0x1a, 0xcb, 0xfa, 0x59, 0xb0, 0x35, 0x05, 0x95, - 0xe3, 0xe7, 0xe9, 0xc5, 0xfe, 0xdb, 0xd8, 0x1f, 0x4e, 0x96, 0x5f, 0xf4, - 0x94, 0x38, 0xc7, 0x21, 0x56, 0x54, 0x9f, 0x67, 0x4d, 0x2e, 0x91, 0xac, - 0xbf, 0xe0, 0x33, 0x03, 0xaf, 0x9d, 0xa5, 0x95, 0x03, 0xf2, 0xe1, 0x0f, - 0x45, 0xaf, 0xf6, 0xa4, 0x5c, 0x23, 0x15, 0x65, 0xff, 0xfb, 0x07, 0xe9, - 0xe4, 0x6c, 0x3d, 0x1b, 0x24, 0x55, 0x97, 0xff, 0xa5, 0xa1, 0x18, 0x59, - 0xf7, 0x67, 0x1a, 0x59, 0x7e, 0xe9, 0x96, 0x31, 0x65, 0xf4, 0x1c, 0x9b, - 0xe2, 0xa2, 0xaf, 0xa3, 0x00, 0xd1, 0x79, 0x19, 0x88, 0xad, 0xb8, 0x99, - 0x7f, 0xe7, 0x83, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x91, 0x57, 0xff, 0xee, - 0xfb, 0x09, 0xbf, 0x5d, 0xfe, 0xe0, 0xa2, 0xba, 0xcb, 0xff, 0x3f, 0x5b, - 0xc9, 0xe8, 0xfa, 0x05, 0x94, 0xdd, 0x1c, 0x92, 0x98, 0xeb, 0x37, 0xfd, - 0xad, 0x1b, 0x59, 0x11, 0xb1, 0x65, 0xf0, 0x67, 0xd2, 0xb2, 0x8c, 0xf6, - 0xc8, 0xea, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x64, 0xaa, 0x4f, 0x53, 0x08, - 0x2f, 0xdb, 0x19, 0x43, 0x8b, 0x2f, 0xf0, 0x36, 0xf4, 0xf7, 0x86, 0xb2, - 0xfe, 0x00, 0xcf, 0x4f, 0xc5, 0x97, 0x1f, 0x56, 0x5f, 0xbb, 0xc1, 0x38, - 0xdf, 0x11, 0x3f, 0xa2, 0x8e, 0x1a, 0x74, 0xb6, 0x9b, 0xa6, 0x39, 0x90, - 0xbf, 0xbe, 0xc6, 0xb9, 0xe5, 0x97, 0xfb, 0x70, 0xd9, 0x3e, 0x10, 0x6b, - 0x2f, 0xef, 0x1e, 0xa6, 0x1c, 0x59, 0x7f, 0x33, 0xa7, 0xe7, 0x62, 0xcb, - 0xff, 0xc7, 0xdf, 0x67, 0x70, 0xa7, 0xb2, 0x35, 0x95, 0x27, 0xe8, 0xe5, - 0xb7, 0xfc, 0xe7, 0xd0, 0x69, 0xf8, 0xdf, 0x64, 0xc6, 0xf0, 0x8c, 0x26, - 0xfe, 0x85, 0x0d, 0xff, 0xfe, 0xeb, 0xfd, 0xfb, 0x71, 0x94, 0xfd, 0x0e, - 0x99, 0x7f, 0xc5, 0x97, 0xf6, 0x68, 0x2e, 0xe1, 0x54, 0x5b, 0x0b, 0xff, - 0xf7, 0xa3, 0x16, 0x6b, 0xbc, 0xf4, 0x51, 0xa3, 0x7a, 0x3d, 0x65, 0xfb, - 0xcf, 0xa3, 0x62, 0xca, 0x6c, 0x22, 0x20, 0x2c, 0x37, 0xb4, 0x0f, 0x2c, - 0xbb, 0xad, 0xe4, 0xf1, 0x34, 0x4f, 0x4d, 0xd3, 0x34, 0x38, 0xc1, 0x6f, - 0x67, 0x31, 0x65, 0xf0, 0x5d, 0xc2, 0xa8, 0xb6, 0xd7, 0x3e, 0x96, 0x5a, - 0x0b, 0x2d, 0xcd, 0x8d, 0x38, 0x05, 0xab, 0x47, 0xfb, 0xd5, 0x5b, 0xf8, - 0xfd, 0xd3, 0xfc, 0x0b, 0x2f, 0xfa, 0x61, 0x3a, 0xda, 0x75, 0xb2, 0xcb, - 0xe9, 0x8b, 0x86, 0xb2, 0xa0, 0x9a, 0x26, 0x42, 0x62, 0x22, 0x22, 0x2e, - 0xdc, 0x3a, 0xbf, 0xf0, 0xf4, 0x60, 0xd9, 0xbf, 0x35, 0xd5, 0x94, 0xdd, - 0x12, 0xb8, 0xa5, 0x7b, 0xd9, 0xb2, 0xcb, 0xf9, 0xc7, 0x98, 0x42, 0xac, - 0xbe, 0xc2, 0xfd, 0xbc, 0x47, 0x92, 0xc1, 0xdb, 0xa3, 0xb2, 0x59, 0x7a, - 0x3b, 0xf0, 0xd6, 0x5e, 0xdb, 0xc6, 0xb2, 0xff, 0xd1, 0xdc, 0x76, 0xda, - 0xfd, 0xf6, 0x14, 0xfe, 0xb2, 0xf4, 0x71, 0x8e, 0x11, 0xc5, 0x65, 0xfb, - 0xb3, 0xe7, 0x89, 0x65, 0xfd, 0x25, 0x01, 0x81, 0x8b, 0x2f, 0xf7, 0x8c, - 0x48, 0xb8, 0x0f, 0x96, 0x5f, 0xf6, 0x6b, 0x53, 0x07, 0xf9, 0xa5, 0x97, - 0xc1, 0x77, 0x0a, 0xa2, 0xf0, 0x5f, 0xbb, 0xf0, 0x4f, 0x4b, 0x2f, 0xfc, - 0x7f, 0xf2, 0x37, 0x3f, 0x7c, 0x89, 0x65, 0xff, 0x9f, 0xb3, 0xbf, 0x5a, - 0xcf, 0xb8, 0xb2, 0xb4, 0x8d, 0xb6, 0x17, 0x78, 0xa4, 0x90, 0xef, 0xda, - 0xeb, 0x6a, 0xc8, 0x96, 0x5c, 0xfc, 0x59, 0x7a, 0x3f, 0x3c, 0xb2, 0xbf, - 0x36, 0xbd, 0x16, 0xbf, 0x32, 0x4a, 0x0e, 0xb2, 0xfe, 0x92, 0xee, 0xf7, - 0x0a, 0xcb, 0xfa, 0x13, 0xbe, 0x75, 0xf2, 0xcb, 0xff, 0xf9, 0xf6, 0x8a, - 0x13, 0xad, 0xbb, 0x0c, 0xdd, 0x72, 0xfd, 0x65, 0x41, 0x17, 0xf8, 0x5c, - 0xe6, 0x17, 0xf7, 0x5f, 0xd1, 0x87, 0x2b, 0x2f, 0xf9, 0xfb, 0xc9, 0x31, - 0xe3, 0x16, 0x54, 0x9f, 0x31, 0x17, 0xdf, 0x16, 0x18, 0xab, 0x2f, 0xf9, - 0x9f, 0x43, 0xb8, 0x5d, 0xe2, 0xca, 0x61, 0xed, 0x88, 0x43, 0x76, 0x0a, - 0xb2, 0xfc, 0xf0, 0xe9, 0xec, 0xb2, 0xa4, 0xf8, 0xb0, 0x8c, 0xc5, 0xee, - 0xc6, 0x2c, 0xbf, 0x1f, 0x86, 0x7c, 0x59, 0x63, 0xd8, 0xde, 0xc0, 0x5a, - 0xfd, 0x85, 0xfe, 0xe4, 0xac, 0xbf, 0x48, 0x80, 0x7e, 0x2c, 0xba, 0x21, - 0xac, 0xbf, 0xf9, 0x92, 0x18, 0xd8, 0x40, 0x86, 0x71, 0x65, 0xfe, 0xf1, - 0xb4, 0x2e, 0x9d, 0xa5, 0x97, 0xff, 0xfe, 0x73, 0xeb, 0xb1, 0xcb, 0xf0, - 0xf8, 0xf5, 0x01, 0x21, 0x84, 0xb2, 0xfe, 0x9f, 0xbe, 0x83, 0xfc, 0xb2, - 0x86, 0x99, 0x76, 0x0c, 0x05, 0x17, 0xc6, 0xc0, 0x6b, 0xbf, 0xce, 0x5f, - 0xf6, 0x3f, 0x3c, 0xb2, 0xff, 0xbc, 0xf0, 0x61, 0xce, 0xa0, 0xb2, 0xd9, - 0xba, 0x7d, 0xde, 0x36, 0xb9, 0x9c, 0x59, 0x7d, 0x25, 0x0d, 0xd5, 0x97, - 0xff, 0x1b, 0xff, 0xd3, 0xe7, 0xa4, 0xba, 0xb2, 0xfb, 0x3a, 0xd0, 0x56, - 0x5d, 0x3f, 0xac, 0xa8, 0x23, 0x8b, 0x0a, 0x85, 0x17, 0xf1, 0x27, 0x10, - 0xfa, 0x47, 0x7f, 0x9f, 0xce, 0x23, 0x33, 0x8b, 0x2e, 0x08, 0x56, 0x53, - 0x65, 0x93, 0xba, 0xd8, 0x1b, 0x8e, 0xc8, 0xa3, 0x88, 0xec, 0x70, 0x50, - 0x92, 0xfd, 0x8a, 0x20, 0x5a, 0x33, 0x7c, 0x8c, 0x54, 0x53, 0xcf, 0x99, - 0x0c, 0x8a, 0x28, 0x72, 0xb2, 0x12, 0x9e, 0x86, 0xeb, 0xb4, 0xfe, 0x4e, - 0x45, 0x5c, 0x8d, 0x5b, 0xb1, 0xcd, 0x6f, 0x57, 0x8f, 0x33, 0xbd, 0xd9, - 0x1a, 0xcb, 0xef, 0xdf, 0x5c, 0x59, 0x77, 0xcd, 0xe4, 0xdf, 0xe0, 0xe5, - 0xfe, 0xfd, 0xbc, 0x50, 0x92, 0xea, 0xca, 0x6e, 0xa8, 0x6e, 0x63, 0xb6, - 0xd1, 0x75, 0xf1, 0x0c, 0x4f, 0x96, 0x5d, 0x9f, 0x2c, 0xbe, 0x7e, 0x03, - 0x65, 0x94, 0x66, 0xeb, 0x42, 0xf7, 0xc1, 0x77, 0x0a, 0xa2, 0xf4, 0x5f, - 0xf8, 0xfb, 0xe2, 0x78, 0x7e, 0xff, 0xac, 0xad, 0x1f, 0x6b, 0x0b, 0xaf, - 0xd9, 0xd2, 0x9d, 0x96, 0x5f, 0xf9, 0xcf, 0xa0, 0xd3, 0xef, 0xc1, 0xac, - 0xbf, 0x4b, 0x1f, 0xd2, 0xb2, 0xf3, 0x0f, 0xab, 0x2f, 0xf9, 0xfb, 0x09, - 0x17, 0xa7, 0xb2, 0xca, 0xd1, 0xfe, 0x30, 0x98, 0x87, 0x2f, 0xd8, 0xcf, - 0x38, 0xd6, 0x54, 0xaa, 0x44, 0x1a, 0xf6, 0x42, 0x37, 0x44, 0x5e, 0x27, - 0x12, 0x17, 0x7b, 0x85, 0xd7, 0xff, 0x60, 0xfd, 0x8c, 0x63, 0xc3, 0x09, - 0x65, 0xe0, 0x4f, 0xeb, 0x2f, 0xb7, 0xe1, 0x37, 0x19, 0xef, 0xe9, 0x0a, - 0xff, 0x37, 0xee, 0x45, 0x07, 0x1a, 0xca, 0x33, 0xf2, 0xf1, 0xe5, 0x37, - 0x4d, 0x93, 0x23, 0x43, 0xa6, 0xd1, 0xd8, 0xb3, 0xcc, 0x33, 0xb6, 0x85, - 0x6c, 0x21, 0xb1, 0x94, 0xb5, 0xc0, 0xc6, 0x5a, 0x73, 0xb1, 0xd1, 0x4b, - 0xa2, 0xd4, 0x6b, 0xcc, 0x8f, 0xef, 0xd1, 0xa2, 0xbb, 0x97, 0xf1, 0xcd, - 0x14, 0xa0, 0x4e, 0x36, 0x82, 0x98, 0x68, 0xd4, 0xbe, 0x1b, 0x81, 0xe5, - 0x97, 0xff, 0x02, 0x62, 0x2c, 0xde, 0xec, 0x61, 0xac, 0xbe, 0xcf, 0x3f, - 0x56, 0x5f, 0xec, 0x3d, 0x7f, 0xf8, 0x38, 0xb2, 0xcd, 0x81, 0x51, 0x36, - 0x48, 0xbc, 0x21, 0xbf, 0xfd, 0xde, 0x02, 0x79, 0x9d, 0x36, 0x3c, 0x16, - 0x5e, 0x9f, 0xb6, 0x59, 0x44, 0x7c, 0xfd, 0x49, 0xbf, 0xee, 0xf3, 0x3e, - 0x87, 0x80, 0x2a, 0xcb, 0xdc, 0x98, 0x96, 0x51, 0x9f, 0xd1, 0x10, 0xf0, - 0xf2, 0xf1, 0x3f, 0x96, 0x5e, 0xdf, 0x30, 0x59, 0x7b, 0xd9, 0xc5, 0x97, - 0xfc, 0x46, 0x2f, 0x7e, 0x09, 0xf9, 0x65, 0x49, 0xfa, 0x0c, 0x7b, 0x07, - 0x2f, 0xe7, 0xd4, 0x08, 0x0e, 0xb2, 0xe3, 0x62, 0xca, 0x01, 0xe1, 0x6e, - 0x16, 0x5e, 0xde, 0xff, 0x2c, 0xbc, 0xee, 0x15, 0x44, 0x46, 0xbe, 0x14, - 0x2f, 0xa5, 0x94, 0x13, 0xcb, 0x32, 0x8b, 0xf4, 0x77, 0xde, 0x3f, 0xeb, - 0x2f, 0xc6, 0xec, 0xcd, 0x2c, 0xbc, 0x11, 0x18, 0xb2, 0xff, 0xff, 0xa6, - 0x2e, 0xcf, 0x35, 0x3d, 0x30, 0x98, 0xe3, 0x0a, 0x4e, 0xb2, 0xa5, 0x10, - 0xc4, 0x3d, 0x7f, 0xf1, 0xf3, 0xd8, 0x28, 0xaf, 0x18, 0x7f, 0x2c, 0xa3, - 0x4f, 0x3f, 0x4d, 0x4c, 0x21, 0xf1, 0x77, 0x61, 0x59, 0xbc, 0x86, 0xff, - 0xfb, 0xc0, 0xe7, 0x31, 0x85, 0x9b, 0xe3, 0x4c, 0x74, 0xb2, 0xff, 0x6d, - 0xac, 0xef, 0x4f, 0xab, 0x29, 0x65, 0xff, 0xde, 0xce, 0x94, 0xc5, 0x18, - 0x52, 0x75, 0x97, 0x02, 0x62, 0x3d, 0x1e, 0x85, 0xd4, 0xa2, 0xcf, 0x90, - 0x85, 0xbc, 0x53, 0xf2, 0xca, 0x81, 0xe1, 0x8c, 0x9e, 0xff, 0xb3, 0x59, - 0xcc, 0x72, 0xd9, 0x65, 0xc3, 0x62, 0xcb, 0xff, 0xb1, 0xa0, 0x4e, 0xdd, - 0x1c, 0xf7, 0x8b, 0x2b, 0x0f, 0x74, 0x85, 0xef, 0xff, 0xf7, 0x9c, 0xfb, - 0x3e, 0x2c, 0xef, 0x8f, 0xd8, 0x6d, 0x2c, 0xbf, 0xff, 0xfd, 0xde, 0x9b, - 0x34, 0x79, 0xf1, 0x03, 0xb3, 0xe8, 0xdb, 0xcf, 0xb1, 0x4a, 0xcb, 0xfd, - 0x86, 0x47, 0xad, 0x62, 0xcb, 0xfe, 0xc7, 0x2f, 0xf4, 0x09, 0xfd, 0x65, - 0x4a, 0x68, 0x78, 0xbd, 0xf3, 0xe6, 0x8c, 0x2f, 0xff, 0xcc, 0xdd, 0x37, - 0x78, 0xbb, 0xec, 0x87, 0x8f, 0x7a, 0xcb, 0xfb, 0xe8, 0x68, 0x13, 0xfa, - 0xcb, 0xf7, 0x67, 0x53, 0xfa, 0xcb, 0xf9, 0x99, 0xa8, 0x4b, 0x16, 0x54, - 0xa3, 0x7f, 0x16, 0x8c, 0xc1, 0xca, 0x2e, 0xd9, 0x89, 0x2e, 0x69, 0xa4, - 0x97, 0xfe, 0x6e, 0x1f, 0x1c, 0x5e, 0x9d, 0x9b, 0xfe, 0x6c, 0x5a, 0x18, - 0xbe, 0xdf, 0x3d, 0x95, 0x97, 0xee, 0x67, 0x8b, 0x16, 0x54, 0x11, 0x4c, - 0x6b, 0xbd, 0x23, 0xbf, 0x3b, 0x33, 0xee, 0xac, 0xbd, 0xc7, 0xdd, 0x59, - 0x58, 0x78, 0xe6, 0x51, 0x7f, 0xfd, 0xe3, 0x17, 0x87, 0x85, 0xf8, 0xf4, - 0xe1, 0x59, 0x7f, 0xa7, 0xd9, 0x14, 0x1f, 0x65, 0x97, 0xff, 0x16, 0x7d, - 0x0e, 0x0c, 0xf7, 0xc8, 0xd6, 0x50, 0xd1, 0x94, 0x05, 0x0d, 0xc3, 0x4b, - 0xf7, 0x0f, 0x4f, 0xfa, 0xcb, 0xfd, 0xfc, 0xef, 0x7f, 0x78, 0xd6, 0x53, - 0x73, 0xdd, 0xe9, 0x45, 0xff, 0x70, 0x1a, 0xd1, 0xc9, 0xe9, 0x65, 0x62, - 0x3b, 0x1e, 0x12, 0x1d, 0x24, 0xb6, 0xea, 0xcb, 0xe2, 0xfa, 0x29, 0x59, - 0x7f, 0xd3, 0x9f, 0x4b, 0xc1, 0xe0, 0xb2, 0xfe, 0xcf, 0xe3, 0x16, 0x7e, - 0xb2, 0xe6, 0x05, 0x65, 0x0c, 0xf1, 0xdc, 0xc2, 0xff, 0xd8, 0xd4, 0x67, - 0x19, 0xc4, 0x72, 0xb2, 0xf9, 0x9d, 0xfd, 0xd6, 0x5f, 0x16, 0x3e, 0xea, - 0xcb, 0xf4, 0xb4, 0x27, 0xa5, 0x65, 0x36, 0x53, 0x85, 0x91, 0x41, 0x91, - 0xe3, 0xf6, 0x88, 0x5d, 0x03, 0x84, 0x62, 0x11, 0xdf, 0x8b, 0x3c, 0xfd, - 0x59, 0x7a, 0x37, 0x02, 0xb2, 0xb7, 0x4f, 0x17, 0xc4, 0xd7, 0xf8, 0xdf, - 0xfd, 0x68, 0x1e, 0x59, 0x7f, 0x48, 0xb8, 0x46, 0x2a, 0xcb, 0xfb, 0x3b, - 0xb9, 0xdc, 0xf2, 0xca, 0xc4, 0x5b, 0x78, 0x94, 0x8d, 0x38, 0x5b, 0x7f, - 0xff, 0x05, 0xbe, 0x9c, 0x8f, 0xad, 0xf5, 0x3d, 0x27, 0xfd, 0x65, 0xff, - 0xdd, 0x29, 0xfd, 0xfb, 0xf8, 0x85, 0x05, 0x97, 0xff, 0x78, 0xf5, 0x19, - 0xc6, 0x71, 0x1c, 0xac, 0xa8, 0xe5, 0xb1, 0x74, 0x8e, 0xe1, 0x65, 0x31, - 0x8d, 0xec, 0x5b, 0x08, 0x47, 0x8d, 0xbf, 0x23, 0xf4, 0x0a, 0xf7, 0xd1, - 0xa7, 0x19, 0x14, 0x50, 0x94, 0xd4, 0x6c, 0x7e, 0x8c, 0x71, 0xe3, 0x10, - 0xfd, 0xd8, 0xa3, 0xd6, 0xe4, 0x77, 0x1d, 0x8d, 0xbf, 0x79, 0xd8, 0x8b, - 0xdb, 0x88, 0xd7, 0x84, 0xf7, 0x96, 0x5d, 0xb9, 0xe5, 0x97, 0xed, 0x05, - 0xdc, 0x2a, 0x88, 0xb9, 0x7e, 0xef, 0xe2, 0x14, 0x12, 0x5f, 0xb9, 0xdf, - 0x4f, 0xeb, 0x2f, 0xcf, 0xb4, 0xe8, 0x0b, 0x2c, 0xde, 0x51, 0xcf, 0xb0, - 0xf6, 0x0d, 0x39, 0xa0, 0x0a, 0xa3, 0xca, 0x69, 0xba, 0xab, 0xa8, 0x4a, - 0x5a, 0xbf, 0xf9, 0x8f, 0x06, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x33, 0x2f, - 0xfe, 0x1b, 0x72, 0x03, 0x88, 0x3f, 0x18, 0xab, 0x2f, 0x78, 0xf8, 0xb2, - 0xf9, 0xb1, 0xc9, 0xea, 0xcb, 0xfa, 0x78, 0x53, 0xde, 0x2c, 0xa6, 0xd7, - 0x3d, 0x26, 0x13, 0x54, 0x72, 0x89, 0x76, 0xd1, 0xba, 0xfb, 0x01, 0xad, - 0x96, 0x5d, 0x1e, 0xd9, 0x59, 0x51, 0xd9, 0xe0, 0xb6, 0xb9, 0x1d, 0xfc, - 0xda, 0x9b, 0x2d, 0x96, 0xc7, 0x25, 0x65, 0xd3, 0xc5, 0x94, 0xda, 0xe7, - 0xad, 0xf2, 0x05, 0xff, 0xe6, 0xc7, 0x61, 0x9e, 0xf3, 0xc0, 0xcc, 0x96, - 0x5e, 0x09, 0xb1, 0x65, 0xff, 0x36, 0x7f, 0x60, 0x9a, 0x3d, 0xa5, 0x65, - 0xf4, 0x77, 0xd7, 0x25, 0x97, 0xe6, 0xd5, 0x1d, 0x47, 0x0e, 0x4a, 0xcb, - 0xa3, 0xdb, 0x2b, 0x2e, 0xc0, 0x2c, 0xbf, 0xfa, 0x58, 0xfd, 0xcf, 0xbf, - 0x60, 0x9e, 0x59, 0x7f, 0xf8, 0x3a, 0x06, 0xdf, 0x70, 0xf4, 0x1f, 0x62, - 0xcb, 0xe3, 0xde, 0xff, 0xac, 0xa9, 0x3f, 0x1e, 0xa6, 0xd7, 0x91, 0xb2, - 0x50, 0xb8, 0xbe, 0x3d, 0x80, 0x4b, 0x2f, 0xd9, 0xb6, 0x7f, 0x12, 0xca, - 0x8c, 0x79, 0x9d, 0x22, 0xbf, 0xd1, 0xb0, 0x9f, 0x5f, 0xc7, 0xac, 0xbf, - 0xff, 0x31, 0xfd, 0x9f, 0x7f, 0xe6, 0x9f, 0xd1, 0x78, 0x0b, 0x2f, 0xec, - 0xe4, 0x88, 0x52, 0xb2, 0x86, 0x8b, 0xec, 0x38, 0xf2, 0xd5, 0xb1, 0x65, - 0xce, 0xc5, 0x95, 0xc3, 0x4d, 0xb8, 0x21, 0x76, 0xe7, 0x56, 0x59, 0xb6, - 0xb2, 0xdf, 0xc9, 0xaf, 0x00, 0xd5, 0xff, 0xff, 0x14, 0x84, 0xa5, 0x91, - 0xa7, 0x5a, 0x3d, 0xfa, 0x77, 0xfd, 0x65, 0xfc, 0x6d, 0x1f, 0x30, 0x2b, - 0x28, 0xd1, 0x2b, 0xc6, 0x8b, 0xe3, 0xec, 0xc1, 0x65, 0xf7, 0x78, 0xf1, - 0x2c, 0xa6, 0xca, 0xe1, 0x60, 0x47, 0x8e, 0x1d, 0xf1, 0x39, 0xfa, 0x1c, - 0x4e, 0xab, 0xfa, 0x9f, 0x61, 0x79, 0xbc, 0x88, 0x42, 0x1b, 0xce, 0x41, - 0x59, 0x7b, 0xa5, 0x2b, 0x2f, 0x88, 0xb3, 0xab, 0x29, 0xb0, 0x6e, 0xe4, - 0x6e, 0xb8, 0x7f, 0x4d, 0x2b, 0x5f, 0x3f, 0x8e, 0x25, 0x97, 0xc5, 0x14, - 0x8d, 0x65, 0xdf, 0x75, 0x65, 0xf7, 0xa2, 0x91, 0xac, 0xac, 0x45, 0x10, - 0x48, 0xcc, 0x8a, 0x22, 0x27, 0x18, 0xbf, 0x4b, 0x0b, 0x22, 0x59, 0x7f, - 0x1e, 0x10, 0xfd, 0x2b, 0x2c, 0xc5, 0x94, 0x33, 0xe3, 0x09, 0x3e, 0xe9, - 0x5d, 0x2c, 0xbf, 0xb5, 0x24, 0x0c, 0xf2, 0xcb, 0xed, 0x4e, 0x12, 0xcb, - 0xe7, 0xec, 0x3f, 0x59, 0x7f, 0xdc, 0x30, 0xbe, 0x6b, 0x3a, 0xb2, 0xff, - 0x3e, 0x8e, 0x77, 0x4d, 0x8b, 0x28, 0x28, 0xda, 0x61, 0x67, 0x88, 0x3a, - 0x47, 0xbc, 0xde, 0xff, 0xb5, 0x39, 0x09, 0xf4, 0xfe, 0xb2, 0xff, 0xf4, - 0x6f, 0x39, 0xf7, 0xf0, 0x11, 0x3c, 0x16, 0x5f, 0xf0, 0x8c, 0xce, 0x3b, - 0xfd, 0x05, 0x97, 0xa3, 0x8b, 0x65, 0xb0, 0xb2, 0xb1, 0x52, 0x58, 0x4c, - 0x8e, 0x1c, 0xba, 0x4c, 0x61, 0xc7, 0xe9, 0x64, 0x75, 0x7f, 0xff, 0xff, - 0xff, 0xfa, 0x38, 0xb6, 0x32, 0x38, 0xc7, 0x6d, 0xae, 0xda, 0xe1, 0x6d, - 0xcc, 0x70, 0x8e, 0xf5, 0xb6, 0xe6, 0x00, 0x6d, 0xaa, 0x63, 0xfe, 0x13, - 0xd1, 0xdc, 0xcc, 0x77, 0xba, 0xda, 0x76, 0x34, 0x6f, 0x47, 0xac, 0xbf, - 0xff, 0x7f, 0xf4, 0x3c, 0x09, 0xd3, 0x7f, 0x18, 0x5f, 0x4b, 0x2f, 0xfd, - 0xec, 0x2c, 0x17, 0x0b, 0x06, 0xb2, 0xff, 0xef, 0x9a, 0x9e, 0x8c, 0xf7, - 0x79, 0x2c, 0x59, 0x7d, 0xcc, 0xfb, 0xab, 0x2f, 0xc3, 0xf4, 0x96, 0xcb, - 0x2f, 0x8c, 0xbb, 0xc5, 0x97, 0xff, 0xfc, 0xc3, 0xd7, 0x81, 0xc8, 0x4e, - 0xfd, 0x49, 0x77, 0xd9, 0x05, 0x95, 0x04, 0x44, 0x99, 0x0d, 0xff, 0x8f, - 0xa6, 0x13, 0x1f, 0x70, 0x2b, 0x2a, 0x09, 0xdc, 0x61, 0xe6, 0x92, 0x48, - 0x8f, 0xb0, 0xb1, 0xdc, 0x22, 0xbf, 0xc1, 0x2c, 0xf7, 0xb3, 0xf5, 0x97, - 0xce, 0xd3, 0xee, 0xac, 0xa7, 0x3d, 0x82, 0x33, 0xbe, 0xec, 0x7e, 0x79, - 0x65, 0xff, 0xfe, 0x72, 0x09, 0x60, 0xfc, 0x08, 0xde, 0x98, 0x6a, 0x76, - 0x59, 0x58, 0x88, 0x70, 0x12, 0xdf, 0xc2, 0x14, 0xfa, 0x60, 0xb2, 0xff, - 0xfe, 0x14, 0xb3, 0xf0, 0x72, 0x34, 0x0f, 0xd2, 0x12, 0x75, 0x94, 0xc4, - 0x45, 0x39, 0x6d, 0xf3, 0x52, 0xce, 0xac, 0xbf, 0xd8, 0xd4, 0xf4, 0x9f, - 0x8b, 0x2f, 0x1f, 0x8d, 0x65, 0x49, 0xf8, 0x19, 0x1f, 0x4c, 0xaf, 0xf9, - 0xf5, 0x08, 0xd8, 0x11, 0xe2, 0xca, 0x8e, 0xd7, 0x91, 0x66, 0x33, 0x1c, - 0x94, 0x25, 0xa8, 0x5b, 0x7a, 0x14, 0xef, 0x0a, 0xc2, 0x84, 0x90, 0x0b, - 0x6f, 0x3e, 0xfd, 0xd5, 0x94, 0xb2, 0xff, 0xfb, 0x87, 0x1f, 0x81, 0xf1, - 0x91, 0x03, 0x5b, 0x2c, 0xad, 0x1e, 0xef, 0x42, 0xef, 0xee, 0xe3, 0x3e, - 0xc6, 0x2c, 0xac, 0x3d, 0x03, 0x22, 0xbf, 0xec, 0x63, 0x1e, 0x1c, 0x38, - 0x2c, 0xa3, 0x4c, 0x9b, 0xd0, 0xce, 0x22, 0x0b, 0xff, 0xec, 0xdf, 0x83, - 0xf4, 0xef, 0x64, 0x93, 0x88, 0xb2, 0xfe, 0x1b, 0xc3, 0x87, 0x05, 0x95, - 0xa3, 0xff, 0x02, 0x95, 0xf8, 0x20, 0x2e, 0xf1, 0x65, 0xe8, 0x47, 0xb4, - 0xb2, 0xfb, 0x51, 0xbf, 0x35, 0x97, 0xe7, 0x68, 0xb0, 0x6b, 0x2a, 0x4f, - 0xb4, 0xc8, 0x74, 0x4b, 0x7f, 0xe2, 0x90, 0x8a, 0xf9, 0xa3, 0xfd, 0x65, - 0xfd, 0xcc, 0x3d, 0x69, 0xd6, 0x56, 0xc7, 0xd5, 0xf9, 0xf5, 0xd3, 0xa5, - 0x97, 0xff, 0x6d, 0x9c, 0x37, 0xf3, 0x76, 0x9a, 0x69, 0x25, 0x8f, 0xf3, - 0xe0, 0x21, 0x6b, 0xfe, 0x03, 0xea, 0x3c, 0x1e, 0x3d, 0x2c, 0xa9, 0x47, - 0x38, 0xe1, 0x09, 0xe2, 0x7b, 0xfb, 0xec, 0xf0, 0x3e, 0xfd, 0x65, 0xfb, - 0xd3, 0xb7, 0x80, 0xb2, 0xfc, 0x60, 0x09, 0xec, 0xb2, 0xb1, 0x10, 0x5d, - 0x31, 0x8f, 0x29, 0xbf, 0xe2, 0xc9, 0x15, 0xfa, 0xe2, 0xac, 0xbf, 0xcc, - 0xc0, 0x93, 0xe0, 0x56, 0x5c, 0x08, 0x96, 0x5d, 0x0e, 0x49, 0xe4, 0xfe, - 0x63, 0x78, 0x20, 0x0a, 0xca, 0xf8, 0xf2, 0xc0, 0x5d, 0x7f, 0xdb, 0x67, - 0xe3, 0x03, 0xea, 0x0b, 0x2f, 0xe7, 0x71, 0xef, 0xc1, 0xac, 0xbc, 0xee, - 0x15, 0x45, 0x9e, 0xbf, 0xd2, 0x42, 0xe8, 0x12, 0x4b, 0x2f, 0xef, 0xdc, - 0xa0, 0x6c, 0x59, 0x5b, 0x23, 0xc0, 0x67, 0x61, 0x2e, 0x32, 0x8f, 0xcc, - 0xaf, 0xf7, 0xbb, 0xc7, 0xf8, 0x46, 0x2c, 0xbf, 0xd3, 0x03, 0xec, 0xf7, - 0x8b, 0x2f, 0xbf, 0x13, 0xd2, 0xb2, 0xbc, 0x7a, 0xc4, 0x65, 0x52, 0x8a, - 0xd3, 0x84, 0x65, 0xff, 0x0a, 0xe5, 0x9b, 0xcb, 0x38, 0xb2, 0xff, 0xcf, - 0x1b, 0x37, 0x96, 0x73, 0x09, 0x65, 0xb4, 0x29, 0xfc, 0x78, 0xe6, 0xff, - 0x18, 0xf1, 0x8d, 0x67, 0x96, 0x5f, 0xb8, 0x33, 0x92, 0x59, 0x52, 0x7b, - 0x3d, 0x33, 0xbf, 0xfb, 0x9d, 0x38, 0x9a, 0x71, 0xcf, 0xdc, 0x59, 0x46, - 0x8e, 0xa7, 0x84, 0x10, 0x08, 0x6f, 0x34, 0xd3, 0x49, 0x2f, 0xfc, 0xf0, - 0xe1, 0x60, 0x47, 0x9a, 0x48, 0xdc, 0xd0, 0x5f, 0xb7, 0x00, 0xcc, 0x1a, - 0xcb, 0xfd, 0xdf, 0x66, 0xf8, 0xd0, 0x62, 0xca, 0xc3, 0xe3, 0x01, 0x5d, - 0xff, 0xbe, 0x81, 0x03, 0xf8, 0xc1, 0x67, 0xeb, 0x2e, 0x91, 0x56, 0x5f, - 0xf0, 0xe4, 0xa7, 0xf8, 0x78, 0xd6, 0x54, 0xa2, 0x53, 0x64, 0x53, 0x17, - 0xbf, 0xde, 0x31, 0x93, 0xbc, 0x4b, 0x2f, 0xf0, 0xfc, 0x07, 0xfe, 0x3f, - 0x16, 0x54, 0x9f, 0x49, 0x99, 0x5f, 0xe9, 0x8d, 0xdc, 0xe7, 0x25, 0x65, - 0xfc, 0x31, 0x06, 0x0e, 0xf1, 0x65, 0x68, 0xf9, 0x08, 0xd2, 0xfe, 0xce, - 0x43, 0xc2, 0x71, 0x65, 0x19, 0xe8, 0x11, 0x0d, 0xe9, 0x21, 0x56, 0x54, - 0x19, 0x72, 0x23, 0x85, 0x9e, 0x11, 0x0b, 0x09, 0x50, 0xc6, 0xfd, 0xf4, - 0x2d, 0x8c, 0xca, 0x28, 0x66, 0x6a, 0x31, 0x36, 0x43, 0xe3, 0xd1, 0xc4, - 0x7e, 0xa6, 0x50, 0xb2, 0xe4, 0x2d, 0x7b, 0x09, 0x8d, 0xf0, 0xd4, 0x10, - 0x82, 0xef, 0xf8, 0xb2, 0xfc, 0x5d, 0xcf, 0xd8, 0xb2, 0xfb, 0x47, 0x3d, - 0x59, 0x7f, 0x70, 0xb2, 0x29, 0x62, 0xca, 0xfc, 0xf3, 0xba, 0x43, 0x4e, - 0x89, 0x90, 0x3b, 0x5d, 0x3d, 0x59, 0x7d, 0xfb, 0xb5, 0xd5, 0x97, 0xf9, - 0xda, 0xf1, 0xce, 0xa5, 0x65, 0xf8, 0xf7, 0xc9, 0x75, 0x65, 0x0d, 0x36, - 0x71, 0x61, 0x63, 0xf1, 0x11, 0x8b, 0x68, 0x93, 0x86, 0x57, 0xfe, 0xd1, - 0xf2, 0x02, 0x7a, 0x4a, 0x56, 0x5f, 0x68, 0xe5, 0x8b, 0x2f, 0xff, 0xdd, - 0x14, 0x13, 0x83, 0xf6, 0x73, 0xe8, 0x4f, 0x96, 0x56, 0x91, 0x6c, 0xc3, - 0xe2, 0x21, 0xbe, 0xfc, 0x1a, 0xd9, 0x65, 0xcf, 0x05, 0x97, 0xff, 0xff, - 0xf1, 0x3b, 0x5d, 0xcf, 0x16, 0x74, 0x1f, 0xb1, 0xe0, 0xe3, 0x92, 0x7d, - 0x3c, 0x16, 0x5f, 0xec, 0xf7, 0x81, 0xd7, 0x25, 0x97, 0x18, 0xd6, 0x5f, - 0xde, 0x0b, 0xe9, 0xe0, 0xb2, 0xc1, 0x81, 0xe1, 0xe0, 0xb5, 0xe3, 0x8b, - 0x8b, 0x2a, 0x4f, 0x11, 0x84, 0xf7, 0xbd, 0x3c, 0x59, 0x63, 0x1a, 0x72, - 0x51, 0x0b, 0x6a, 0x10, 0xcc, 0x85, 0xcf, 0x48, 0x6f, 0xc7, 0xe7, 0x61, - 0xac, 0xbc, 0xe5, 0xfa, 0xca, 0x8c, 0x78, 0x41, 0x26, 0xbd, 0xbf, 0x06, - 0xb2, 0xda, 0xd8, 0xf0, 0xa2, 0x24, 0xbd, 0xa7, 0xf2, 0xcb, 0xfd, 0xb9, - 0xac, 0xdc, 0x20, 0x34, 0xb2, 0xb6, 0x3d, 0x66, 0x87, 0x2f, 0x6e, 0x18, - 0xd6, 0x56, 0x1e, 0x1b, 0x92, 0x54, 0xab, 0xa8, 0x32, 0xff, 0x47, 0x6e, - 0xf0, 0xdf, 0x28, 0x61, 0xdf, 0xff, 0xff, 0x7f, 0xcc, 0xc2, 0x14, 0x4d, - 0x14, 0xe7, 0x9b, 0xf0, 0xb3, 0x66, 0x63, 0x16, 0x5d, 0xac, 0x59, 0x77, - 0x0d, 0x65, 0x6c, 0x6b, 0x34, 0x2d, 0x7d, 0x24, 0x23, 0x16, 0x56, 0x1e, - 0x20, 0x84, 0x55, 0x89, 0x89, 0x76, 0x1e, 0x17, 0x7f, 0xc5, 0x97, 0xcf, - 0xe9, 0x11, 0x65, 0x61, 0xba, 0xde, 0x31, 0x7d, 0x2d, 0x60, 0xd6, 0x5f, - 0xff, 0x03, 0xe8, 0x70, 0x7e, 0x9d, 0x0d, 0xdd, 0xa5, 0x95, 0x88, 0xf8, - 0x36, 0x2d, 0x11, 0x11, 0x15, 0xe2, 0x61, 0xac, 0xbd, 0xd1, 0x31, 0x65, - 0xd3, 0xb2, 0xca, 0x73, 0x69, 0xc1, 0xdb, 0xc3, 0x98, 0x2c, 0xbf, 0xe9, - 0xc0, 0xf8, 0xf7, 0xbe, 0x96, 0x5d, 0x9e, 0x59, 0x4e, 0x7d, 0x64, 0x39, - 0x1e, 0x73, 0x76, 0x0d, 0x65, 0xdd, 0x6d, 0xac, 0xa8, 0x26, 0xeb, 0x87, - 0x5c, 0x4f, 0xec, 0x22, 0xf7, 0x98, 0x47, 0x8b, 0x5f, 0xd9, 0xaf, 0xd8, - 0x7c, 0x59, 0x7b, 0xc0, 0x69, 0x65, 0xfb, 0x9d, 0x39, 0xf9, 0x65, 0xc0, - 0x0a, 0xca, 0x81, 0xbf, 0xfc, 0xa2, 0xf7, 0x70, 0x45, 0x97, 0xf3, 0x01, - 0xce, 0xe7, 0x56, 0x5f, 0x9f, 0xdf, 0x43, 0x8b, 0x2a, 0x4f, 0x5c, 0x05, - 0xd7, 0xec, 0xe7, 0xf3, 0x12, 0xcb, 0xfa, 0x7e, 0xe4, 0x6d, 0x4a, 0xcb, - 0xc3, 0x00, 0x56, 0x5f, 0x0c, 0xa6, 0x0b, 0x28, 0xcd, 0xf3, 0x8e, 0xdf, - 0xfe, 0x12, 0x37, 0xdd, 0x29, 0xc8, 0xd9, 0xf7, 0x56, 0x54, 0xaa, 0x48, - 0x19, 0x77, 0xcb, 0xa6, 0x45, 0x13, 0xa6, 0x88, 0x58, 0x53, 0xe6, 0xe0, - 0x0f, 0xdf, 0xe3, 0x89, 0xc8, 0xfe, 0xe2, 0xcb, 0xb0, 0x2b, 0x2f, 0xf1, - 0x77, 0x85, 0x3d, 0xe2, 0xcb, 0xfd, 0xe2, 0xc3, 0xfe, 0x7f, 0x59, 0x7e, - 0x3d, 0xf8, 0x5c, 0x59, 0x58, 0x88, 0xd3, 0x32, 0xd1, 0x9d, 0xfd, 0xf7, - 0x73, 0xc0, 0xdd, 0x59, 0x7f, 0x6b, 0x3d, 0xe0, 0x75, 0x65, 0xfb, 0xc5, - 0x39, 0xa5, 0x17, 0xf6, 0x6b, 0x60, 0x38, 0xd5, 0x10, 0x69, 0xb9, 0xa6, - 0xb6, 0xb6, 0x45, 0x0c, 0x15, 0x2f, 0x8b, 0xd9, 0xf2, 0xca, 0x94, 0xc7, - 0xce, 0x1a, 0x6e, 0x55, 0x7e, 0x70, 0x9b, 0x25, 0x65, 0xfd, 0x0f, 0x13, - 0xfd, 0xd5, 0x94, 0x13, 0xd4, 0x22, 0x6b, 0xe1, 0x09, 0xf6, 0x59, 0x58, - 0x78, 0x9c, 0x21, 0xbf, 0x4f, 0x63, 0x31, 0xa5, 0x97, 0x99, 0x21, 0x59, - 0x7c, 0x5f, 0x83, 0x8b, 0x2c, 0x78, 0x6f, 0xf4, 0x39, 0x7f, 0xc3, 0xe7, - 0x33, 0x43, 0xf6, 0x2c, 0xbf, 0xd0, 0x36, 0x4f, 0x84, 0x1a, 0xcb, 0xff, - 0xd3, 0x85, 0xf7, 0x73, 0x47, 0xb3, 0xb1, 0x65, 0xd9, 0xee, 0x1f, 0xef, - 0x4d, 0x2e, 0xce, 0x2c, 0xb3, 0x5d, 0x3c, 0x38, 0xf2, 0xda, 0x94, 0xc9, - 0x5e, 0x1f, 0x35, 0x19, 0x73, 0xaa, 0x39, 0x33, 0x98, 0x51, 0xc2, 0x34, - 0xdc, 0x86, 0x5e, 0x88, 0x7c, 0xd4, 0x51, 0xa5, 0x5e, 0xe6, 0x41, 0x65, - 0xd8, 0x22, 0xcb, 0xef, 0xfd, 0x23, 0x59, 0x7f, 0x78, 0xda, 0x29, 0x1a, - 0xcb, 0xfe, 0x9f, 0xd9, 0x38, 0x5d, 0xe2, 0xcb, 0xfe, 0xe6, 0x35, 0xe7, - 0x61, 0xf1, 0x65, 0xfb, 0x35, 0xb0, 0x38, 0xb2, 0x8d, 0x1d, 0x51, 0x11, - 0xf8, 0xb7, 0x87, 0x1b, 0xce, 0x6f, 0xc7, 0xf4, 0x0c, 0x96, 0x5e, 0x3f, - 0x62, 0xcb, 0xfb, 0xae, 0xc6, 0x4b, 0x16, 0x5d, 0x2c, 0xc3, 0xef, 0x09, - 0x3f, 0xc3, 0x77, 0xb8, 0x02, 0x59, 0x7f, 0xe0, 0x0a, 0xc0, 0x77, 0xd2, - 0x06, 0xda, 0xca, 0xf8, 0xf8, 0x48, 0x72, 0xf9, 0x93, 0xa1, 0x56, 0x5f, - 0x67, 0xff, 0xba, 0xca, 0xc3, 0xc6, 0x22, 0x3a, 0xd9, 0x11, 0x0d, 0x34, - 0x5f, 0x0f, 0x9e, 0x95, 0x97, 0xe2, 0xd3, 0x4f, 0xb2, 0xca, 0x93, 0xf0, - 0xc2, 0x57, 0x22, 0xbf, 0x67, 0x78, 0x6c, 0x59, 0x70, 0x09, 0x65, 0x4a, - 0xbd, 0x48, 0x0e, 0x64, 0x60, 0x27, 0x0b, 0xbd, 0x47, 0x26, 0x45, 0x9c, - 0x27, 0xbb, 0xd0, 0x59, 0x7b, 0xd8, 0x4b, 0x2f, 0xf6, 0x16, 0x7d, 0xd8, - 0x31, 0x65, 0xdd, 0x95, 0x95, 0x27, 0x92, 0xe6, 0x77, 0xec, 0x22, 0x91, - 0xac, 0xbf, 0xa7, 0x66, 0x3c, 0x52, 0xb2, 0xda, 0x59, 0x51, 0x1b, 0xf0, - 0x17, 0x5f, 0xff, 0x38, 0xbd, 0x9d, 0x7f, 0xa9, 0xe9, 0x3f, 0xeb, 0x2f, - 0x82, 0x27, 0x78, 0xb2, 0xfe, 0x2c, 0xff, 0x0a, 0x0b, 0x2f, 0xff, 0x85, - 0x8c, 0x21, 0x3f, 0x78, 0x7f, 0x78, 0xda, 0x59, 0x58, 0x88, 0x17, 0x2c, - 0xa3, 0x46, 0x31, 0x42, 0x96, 0xa5, 0x52, 0xf6, 0x0b, 0xe9, 0x93, 0xc4, - 0x0e, 0xc8, 0x44, 0x40, 0x8c, 0x5e, 0xee, 0x6f, 0x59, 0x71, 0xf5, 0x65, - 0xf1, 0x93, 0x8d, 0x65, 0xff, 0x99, 0x9d, 0xe0, 0x9c, 0x78, 0x62, 0xcb, - 0xf7, 0x27, 0x5d, 0x95, 0x8d, 0xcd, 0xfd, 0x0d, 0x13, 0x1c, 0x5f, 0xb8, - 0xdb, 0x6b, 0x2f, 0x61, 0x8a, 0xb2, 0xfa, 0x27, 0x7d, 0x2c, 0xa2, 0x37, - 0xc2, 0x0e, 0x5f, 0x7b, 0x0f, 0x7a, 0xcb, 0xfb, 0x87, 0xac, 0x06, 0x96, - 0x5f, 0xff, 0xd1, 0x46, 0x30, 0x83, 0x5c, 0x04, 0x66, 0x1f, 0x7c, 0x6b, - 0x2e, 0x91, 0x56, 0x54, 0x9f, 0xc1, 0xb0, 0xd6, 0x2a, 0x4b, 0x14, 0x64, - 0xe1, 0x5d, 0x11, 0x1b, 0x16, 0x3c, 0x43, 0xd2, 0x3d, 0xc8, 0x52, 0xdc, - 0xc6, 0xc2, 0xa2, 0xfc, 0x5f, 0xb6, 0xfa, 0x12, 0x35, 0x97, 0xec, 0x23, - 0xfa, 0x0b, 0x2f, 0x6b, 0x31, 0x65, 0x36, 0x4f, 0xb0, 0xca, 0xb8, 0x4f, - 0x7f, 0xf4, 0x3a, 0x08, 0x41, 0x9d, 0xec, 0xb1, 0x65, 0xfe, 0xef, 0x27, - 0x6c, 0xe0, 0xd6, 0x5d, 0xec, 0x59, 0x7f, 0xfd, 0xe9, 0x1e, 0x06, 0x36, - 0x16, 0x0f, 0xd2, 0xb2, 0xff, 0xb5, 0x3e, 0x3d, 0xfa, 0x98, 0x2c, 0xbf, - 0xf8, 0xf0, 0xb3, 0x39, 0x1a, 0x1c, 0x62, 0xcb, 0xff, 0x3c, 0xfd, 0x08, - 0xdc, 0xf8, 0x72, 0xb2, 0x86, 0x9e, 0x8f, 0xc6, 0x46, 0x8d, 0xba, 0x69, - 0x10, 0xb6, 0x93, 0xf8, 0x75, 0xd4, 0x4b, 0xc2, 0xce, 0x96, 0x5f, 0xde, - 0x36, 0x3b, 0xee, 0x2c, 0xbf, 0x87, 0x3f, 0xeb, 0x3e, 0x59, 0x68, 0xf5, - 0x94, 0xdd, 0x11, 0xe1, 0x1d, 0x61, 0x7b, 0x6c, 0xbe, 0xff, 0x8c, 0x59, - 0xd1, 0x61, 0x8a, 0xb2, 0xe0, 0xec, 0xb2, 0xff, 0x4b, 0x30, 0xf5, 0x14, - 0x16, 0x5c, 0x71, 0x2c, 0xbf, 0xda, 0x91, 0x46, 0x53, 0xf2, 0xcb, 0xe9, - 0x2c, 0x15, 0x65, 0xff, 0xc0, 0x1e, 0x6a, 0x60, 0x63, 0x92, 0x59, 0x7f, - 0xc4, 0xfb, 0x4e, 0xb4, 0xf0, 0x59, 0x77, 0x66, 0x07, 0xf5, 0xe4, 0x2b, - 0xdd, 0x3d, 0x96, 0x5f, 0xbf, 0x7f, 0xa6, 0x25, 0x97, 0x8a, 0x42, 0xb2, - 0xff, 0xe1, 0x9c, 0x5e, 0x9d, 0x7d, 0x09, 0xd2, 0xcb, 0xff, 0xc6, 0x4f, - 0xf7, 0x39, 0x9a, 0x1f, 0xb1, 0x65, 0x46, 0x54, 0x09, 0x01, 0x70, 0x9a, - 0x1c, 0x26, 0xd8, 0x5a, 0xe3, 0xa4, 0x55, 0xc1, 0xbd, 0xc4, 0x6b, 0x4a, - 0xcb, 0xd9, 0x9f, 0xac, 0xbd, 0xe3, 0xea, 0xcb, 0x6f, 0xc3, 0xc8, 0x21, - 0x01, 0x07, 0x2f, 0xf0, 0xb9, 0xad, 0x80, 0xe3, 0x59, 0x7e, 0xd6, 0xb3, - 0x76, 0x25, 0x97, 0x35, 0xc5, 0x95, 0x2c, 0x83, 0x21, 0xc2, 0x03, 0x25, - 0x32, 0x9c, 0x60, 0xbb, 0xa8, 0x6c, 0x38, 0xf0, 0xc3, 0xca, 0x37, 0xfe, - 0x19, 0xe0, 0x36, 0xde, 0x6a, 0x21, 0x65, 0xe6, 0x38, 0x8b, 0x2f, 0xfb, - 0x0a, 0x05, 0x8c, 0x90, 0xac, 0xbf, 0x78, 0xf7, 0xe0, 0xd6, 0x5f, 0xf8, - 0x72, 0x58, 0x40, 0x86, 0x71, 0x65, 0xd1, 0xbe, 0x59, 0x7e, 0x76, 0x1e, - 0xe6, 0x2c, 0xbf, 0xb3, 0xcf, 0xd7, 0x89, 0x65, 0xd3, 0xf2, 0xca, 0xf8, - 0xf1, 0x3a, 0x5b, 0x58, 0x89, 0x07, 0x6d, 0xbf, 0xec, 0x01, 0x67, 0x49, - 0xd8, 0xb2, 0xe3, 0x89, 0x65, 0xd8, 0x2a, 0xca, 0x93, 0x5d, 0xe1, 0x7b, - 0xb5, 0x8b, 0x2f, 0x13, 0x67, 0x71, 0x65, 0xf1, 0xea, 0x60, 0xb2, 0xfb, - 0x66, 0x4f, 0x16, 0x56, 0xc7, 0x89, 0x84, 0x37, 0xff, 0x7a, 0x7a, 0xe0, - 0xeb, 0xfd, 0x9d, 0x59, 0x7e, 0x0b, 0xc9, 0xc7, 0xac, 0xbc, 0x59, 0xc5, - 0x97, 0xff, 0xfe, 0x9f, 0x1b, 0x1b, 0xc5, 0x07, 0xd0, 0x9e, 0x3f, 0xb8, - 0x13, 0xd9, 0x65, 0x6c, 0xae, 0x80, 0x63, 0xa2, 0x9b, 0x19, 0x4e, 0xe9, - 0xe4, 0x50, 0xaa, 0xd1, 0x0f, 0x99, 0x3f, 0x20, 0x21, 0x6e, 0x33, 0xf4, - 0x88, 0x08, 0x82, 0x14, 0xee, 0x0d, 0xdb, 0x8b, 0x2f, 0xf0, 0x7f, 0x7f, - 0xcc, 0x82, 0xb2, 0xc7, 0xe3, 0xc5, 0x21, 0x1b, 0xe8, 0xcf, 0x08, 0xe9, - 0x65, 0xf9, 0xe1, 0xc1, 0xb1, 0x65, 0xff, 0xd9, 0xbf, 0x07, 0x84, 0x08, - 0x67, 0x16, 0x5f, 0xb5, 0x3e, 0x10, 0x6b, 0x2b, 0x47, 0xda, 0xe8, 0x97, - 0xd2, 0x7b, 0x4a, 0xcb, 0xe6, 0x9f, 0x38, 0xb2, 0xfe, 0x1c, 0xfe, 0x59, - 0xf2, 0xcb, 0xfe, 0x83, 0x1e, 0x1d, 0xe4, 0x8a, 0xb2, 0xa5, 0x11, 0x58, - 0x44, 0xe5, 0xd7, 0xb8, 0x64, 0xb2, 0xfc, 0x41, 0x8d, 0xcf, 0x96, 0x5f, - 0xe1, 0x21, 0x3b, 0xcc, 0xbf, 0x59, 0x7c, 0xfb, 0x98, 0x4b, 0x2b, 0x11, - 0x3e, 0xe3, 0x64, 0x57, 0xc3, 0x6a, 0xc5, 0x56, 0xdb, 0xa4, 0xcc, 0x29, - 0x78, 0x4a, 0xfe, 0x42, 0x50, 0xab, 0x04, 0x33, 0x2e, 0xda, 0x0b, 0x2f, - 0x1f, 0x8d, 0x65, 0x7c, 0x6c, 0xfa, 0x31, 0x7d, 0x9f, 0x42, 0x56, 0x5e, - 0x03, 0xf1, 0x65, 0x78, 0xdf, 0x08, 0x45, 0x7f, 0xc6, 0x2c, 0x8e, 0x77, - 0xe7, 0x96, 0x5f, 0xe3, 0x6b, 0xe8, 0x73, 0x02, 0xb2, 0xfe, 0x80, 0x9f, - 0xff, 0x3f, 0x2c, 0xaf, 0x1f, 0x3b, 0x9a, 0xdf, 0xc0, 0xe4, 0xc4, 0x0d, - 0x2c, 0xbc, 0x0c, 0xf9, 0x65, 0x78, 0xf3, 0x3a, 0x5f, 0x7f, 0x63, 0x35, - 0xa9, 0x1a, 0xcb, 0xf8, 0x05, 0xb7, 0x0f, 0xe5, 0x97, 0xff, 0x4e, 0xcd, - 0x3f, 0xdc, 0xc1, 0x9f, 0x16, 0x54, 0x0f, 0xd4, 0x8b, 0xef, 0xbf, 0xff, - 0x3e, 0x59, 0x7f, 0xfe, 0x9d, 0x7f, 0x9c, 0x20, 0x3c, 0x3c, 0x07, 0xd9, - 0x65, 0xfb, 0xb8, 0xe4, 0x15, 0x95, 0xe3, 0xfc, 0xdc, 0x56, 0xbf, 0xfc, - 0x3f, 0x4f, 0xd0, 0x2c, 0x66, 0x7d, 0xd5, 0x97, 0xe6, 0x67, 0xb0, 0x96, - 0x51, 0x9f, 0x8b, 0xa5, 0xdf, 0xf0, 0x1e, 0x03, 0xcc, 0xff, 0x8b, 0x2f, - 0xcc, 0xcd, 0x39, 0x2c, 0xbf, 0xf7, 0xd0, 0xf4, 0xb1, 0xfe, 0x86, 0x2c, - 0xbf, 0x8f, 0xef, 0xa1, 0x9d, 0x59, 0x52, 0x89, 0x5c, 0x26, 0xfd, 0x02, - 0xbc, 0x8f, 0xde, 0x43, 0x3a, 0xfd, 0xf7, 0x18, 0x7b, 0xab, 0x2f, 0xfe, - 0xf3, 0x9f, 0x7f, 0x01, 0x13, 0xc1, 0x65, 0xf4, 0x90, 0x1b, 0x6b, 0x2e, - 0xce, 0x46, 0x3e, 0x8f, 0x21, 0xd4, 0xa3, 0x0c, 0xa1, 0x29, 0x7f, 0x3e, - 0xdf, 0xb9, 0x0d, 0x65, 0x6c, 0xbb, 0x0c, 0x38, 0x50, 0x85, 0xb4, 0xc8, - 0xa2, 0x85, 0x16, 0x88, 0x7d, 0x0a, 0x1e, 0x42, 0x5b, 0xb1, 0x8e, 0xef, - 0x87, 0x5c, 0x79, 0x35, 0xfd, 0x3f, 0xe4, 0x46, 0xc5, 0x97, 0xbb, 0x84, - 0xb2, 0xf7, 0x9f, 0x75, 0x65, 0xfe, 0x12, 0x43, 0x1f, 0x9a, 0x95, 0x97, - 0xfc, 0x3c, 0x06, 0xb6, 0x09, 0xec, 0xb2, 0xfd, 0xe3, 0xd4, 0xb1, 0x65, - 0xff, 0xf0, 0x7c, 0x02, 0xcf, 0xa0, 0x7c, 0x2c, 0xde, 0xb2, 0xfd, 0xe9, - 0xf1, 0x8a, 0xb2, 0xe7, 0x8b, 0x87, 0xf3, 0xb8, 0xa3, 0x52, 0x8d, 0x1f, - 0x42, 0x82, 0xa0, 0x9d, 0x18, 0xa3, 0x7e, 0x1f, 0x73, 0x6e, 0xc6, 0x19, - 0x52, 0xc8, 0x8a, 0xc9, 0xda, 0x83, 0x86, 0x03, 0xc7, 0xa3, 0x74, 0x40, - 0x59, 0x66, 0x2c, 0xaf, 0x8d, 0x4b, 0x6c, 0x62, 0xff, 0x77, 0xf0, 0x17, - 0x0c, 0x6b, 0x2f, 0xd0, 0xff, 0x30, 0x6b, 0x2f, 0xa7, 0x59, 0xbc, 0xcf, - 0x71, 0xcd, 0x2f, 0xff, 0x44, 0x53, 0xde, 0x77, 0xe0, 0x99, 0x6c, 0xb2, - 0xfc, 0x6c, 0x12, 0x2e, 0x2c, 0xbf, 0x9f, 0xe8, 0x14, 0x8d, 0x65, 0xff, - 0x4f, 0xd0, 0x32, 0x63, 0xf5, 0x65, 0x1a, 0x36, 0xfc, 0x98, 0xe5, 0x44, - 0x5b, 0x7e, 0x78, 0x66, 0xd2, 0xb2, 0xff, 0xe9, 0xe7, 0x31, 0x9d, 0x29, - 0x67, 0x16, 0x5f, 0xc2, 0xc6, 0xc6, 0x48, 0x56, 0x5f, 0xfd, 0x3f, 0x43, - 0xa0, 0x9d, 0x8f, 0xee, 0xac, 0xbf, 0xc7, 0xe7, 0x0c, 0x90, 0xd6, 0x51, - 0xa2, 0xa0, 0x8c, 0x38, 0x8f, 0x7f, 0x48, 0x7f, 0x60, 0x3f, 0x59, 0x7e, - 0xcd, 0xe6, 0x43, 0x59, 0x7f, 0x87, 0xc3, 0xf7, 0xb0, 0x6b, 0x2f, 0xfe, - 0x39, 0xe1, 0x3c, 0x23, 0x73, 0x34, 0xb2, 0xf1, 0x3e, 0x96, 0x5f, 0x3b, - 0xbe, 0xe2, 0xcb, 0xff, 0xfb, 0x07, 0x18, 0xff, 0x98, 0xdd, 0xf6, 0x77, - 0xcf, 0x05, 0x97, 0xff, 0x8f, 0x9b, 0x63, 0x58, 0x40, 0xdc, 0x96, 0xf8, - 0x89, 0xd6, 0x0d, 0xef, 0x23, 0xbf, 0xdf, 0x43, 0x91, 0x42, 0x76, 0x59, - 0x43, 0x56, 0xb0, 0x12, 0x7d, 0x43, 0xad, 0x85, 0xde, 0x30, 0x72, 0x82, - 0x33, 0xec, 0x38, 0x40, 0xeb, 0x7f, 0xff, 0x77, 0x3e, 0xee, 0xb5, 0x3d, - 0xfc, 0x42, 0x87, 0xb1, 0x65, 0xc7, 0xbd, 0x65, 0xf4, 0x94, 0x38, 0xb2, - 0xc7, 0xf2, 0x25, 0xe2, 0x5e, 0xfc, 0x62, 0xff, 0x6b, 0x62, 0x90, 0x9c, - 0x16, 0x57, 0x8f, 0xad, 0xce, 0x2f, 0xde, 0x9d, 0xe3, 0x95, 0x97, 0x86, - 0xe4, 0xb2, 0xdb, 0xd6, 0x5f, 0xbe, 0xe9, 0x4f, 0x96, 0x5b, 0x52, 0x6e, - 0xdc, 0x4e, 0xe0, 0x69, 0x65, 0x41, 0x17, 0x63, 0x29, 0xd2, 0xb7, 0x09, - 0x6f, 0xf1, 0x77, 0x47, 0xbf, 0x06, 0xb2, 0xfd, 0xa8, 0x3f, 0xcd, 0x2c, - 0xa9, 0x3d, 0xe3, 0x34, 0xbf, 0xc0, 0xfa, 0x1d, 0xe0, 0x37, 0x56, 0x5f, - 0xce, 0xd7, 0xf3, 0xee, 0x2c, 0xa8, 0x22, 0x23, 0xc4, 0x04, 0x73, 0x7f, - 0xbe, 0xd4, 0x9c, 0x1f, 0x8b, 0x2f, 0xfb, 0xbc, 0x04, 0x04, 0x19, 0xf5, - 0x65, 0x49, 0xf7, 0x39, 0x9d, 0xfb, 0xd3, 0x90, 0x75, 0x97, 0xb0, 0xf8, - 0xb2, 0xfd, 0xde, 0xe0, 0x36, 0x59, 0x7f, 0xfd, 0xb3, 0xfb, 0x07, 0x1b, - 0x08, 0xb0, 0xff, 0x59, 0x5b, 0x22, 0x50, 0x23, 0x7a, 0x29, 0xbf, 0xef, - 0x04, 0xde, 0x37, 0x1f, 0xcb, 0x28, 0x69, 0x92, 0xe4, 0x2e, 0x9c, 0xc6, - 0xf8, 0xa6, 0x06, 0xb2, 0xfc, 0xe5, 0xf4, 0x0d, 0x65, 0xe0, 0x49, 0x2c, - 0xad, 0x1f, 0x17, 0x88, 0x08, 0x9e, 0xff, 0xfd, 0xc2, 0x8d, 0xe7, 0x3e, - 0xfe, 0x02, 0x27, 0x82, 0xcb, 0xff, 0xff, 0xa7, 0x59, 0x14, 0xb3, 0x02, - 0x63, 0xf4, 0xf7, 0x82, 0xeb, 0x37, 0x16, 0x5f, 0xef, 0x18, 0xb1, 0xe0, - 0xd0, 0xab, 0x2f, 0xf9, 0x92, 0x41, 0x72, 0xfd, 0xb0, 0xb2, 0xff, 0xa7, - 0xfd, 0x6a, 0x5a, 0x1f, 0xcb, 0x2f, 0xf3, 0x4c, 0xf4, 0x99, 0x6c, 0xb2, - 0xff, 0xf3, 0xc0, 0xf5, 0x9f, 0x45, 0x09, 0xd6, 0xcb, 0x2a, 0x51, 0x01, - 0xf9, 0xa5, 0xff, 0xf1, 0x67, 0x31, 0x92, 0x1c, 0xe9, 0xeb, 0x16, 0x50, - 0x53, 0xa0, 0x61, 0xcf, 0xe7, 0x9d, 0x85, 0xe6, 0xf2, 0x3b, 0xff, 0x45, - 0xce, 0x83, 0xec, 0x1b, 0x92, 0xca, 0x95, 0x55, 0x19, 0x28, 0x55, 0xd4, - 0xef, 0xed, 0xb3, 0x46, 0x19, 0x59, 0x7c, 0x43, 0x96, 0x2c, 0xbf, 0xfe, - 0x01, 0x77, 0x9e, 0x38, 0x8a, 0x58, 0xf0, 0x59, 0x7d, 0x84, 0x07, 0x59, - 0x4c, 0x3e, 0xfd, 0xe9, 0xf7, 0xf3, 0xff, 0x83, 0x78, 0x2c, 0xa9, 0x47, - 0x0e, 0x42, 0x40, 0xc9, 0x2f, 0xbf, 0xe3, 0x68, 0xd8, 0x59, 0x46, 0x9b, - 0x6f, 0xa3, 0x0e, 0x73, 0x2b, 0xf1, 0xf8, 0x22, 0x41, 0x65, 0x4b, 0x60, - 0xab, 0xb4, 0x20, 0x87, 0x18, 0x9e, 0x4b, 0x92, 0x0c, 0x71, 0x47, 0x0d, - 0xc8, 0xa3, 0x23, 0x64, 0x27, 0x7d, 0x1a, 0x2b, 0xc2, 0x5b, 0xf2, 0xe2, - 0x95, 0xfd, 0xd8, 0xe8, 0xc0, 0x6b, 0x7f, 0xf6, 0x17, 0xf9, 0x9a, 0xe7, - 0x67, 0x16, 0x5f, 0xf7, 0xdd, 0xcd, 0x1e, 0xce, 0xc5, 0x97, 0xb3, 0x7e, - 0x2c, 0xb6, 0x7e, 0x89, 0x92, 0x42, 0xe1, 0xd5, 0xe6, 0xdc, 0x0d, 0x65, - 0xf4, 0x93, 0xc1, 0x65, 0xfb, 0x37, 0x5c, 0xbf, 0x8c, 0x78, 0x1a, 0x20, - 0xbf, 0xb7, 0xf8, 0xe7, 0x5c, 0x59, 0x7f, 0xfe, 0x87, 0x0b, 0x3b, 0xc3, - 0xc6, 0x77, 0x58, 0xd2, 0xcb, 0xff, 0x78, 0xd8, 0x3c, 0x8a, 0x73, 0x4b, - 0x2f, 0x42, 0x7e, 0x59, 0x7e, 0x01, 0x02, 0x7e, 0x59, 0x7d, 0x3f, 0xbb, - 0x16, 0x5a, 0x18, 0x8a, 0x58, 0x8f, 0x98, 0x3a, 0x45, 0x15, 0xf2, 0x67, - 0x85, 0x0f, 0xab, 0xee, 0x1f, 0x65, 0x65, 0x49, 0xe5, 0x39, 0x4d, 0xf8, - 0xb3, 0xf0, 0x71, 0x65, 0xfe, 0x8c, 0x4f, 0xb1, 0x4f, 0xcb, 0x2e, 0x09, - 0xe1, 0xee, 0xe8, 0xa2, 0xf8, 0x3e, 0x38, 0x96, 0x5f, 0xff, 0x8b, 0x05, - 0x14, 0xc7, 0xe3, 0x8b, 0xc7, 0xf7, 0x56, 0x54, 0x9f, 0xde, 0x11, 0xdc, - 0x0d, 0x2c, 0xbc, 0x17, 0xd2, 0xcb, 0xff, 0x6d, 0x84, 0xcc, 0xef, 0x04, - 0xe2, 0xcb, 0xf7, 0x03, 0x3a, 0x15, 0x65, 0xfc, 0x7f, 0xfd, 0x0c, 0xea, - 0xca, 0xd9, 0x19, 0x7e, 0x17, 0x71, 0xcf, 0xd0, 0x37, 0x94, 0xdf, 0x43, - 0xb9, 0xfa, 0xcb, 0xdc, 0x38, 0x96, 0x57, 0xc7, 0x81, 0xc2, 0x3b, 0xee, - 0xf8, 0x01, 0x59, 0x7e, 0xc3, 0x1f, 0x8d, 0x65, 0x6c, 0x79, 0x30, 0x23, - 0xbc, 0xfa, 0x15, 0x65, 0x4a, 0x2b, 0x31, 0xb1, 0xc8, 0xef, 0xfe, 0xcf, - 0xbb, 0xc3, 0x29, 0xfd, 0xd8, 0xb2, 0xf4, 0x19, 0xe5, 0x95, 0xb1, 0xf0, - 0xf5, 0x12, 0xff, 0xff, 0xd1, 0xf9, 0xdf, 0x38, 0xdb, 0x96, 0x6f, 0x2c, - 0xe7, 0x0f, 0xee, 0xac, 0xbf, 0xbe, 0xe6, 0x32, 0x77, 0x56, 0x5e, 0x14, - 0xda, 0x59, 0x7e, 0x68, 0xf0, 0x85, 0x59, 0x46, 0x78, 0xc0, 0x1e, 0xac, - 0x4c, 0xb4, 0xc8, 0xdd, 0xc8, 0x9d, 0xef, 0xe7, 0x0f, 0x9f, 0x73, 0x8b, - 0x2e, 0x8d, 0x8b, 0x2f, 0xb8, 0x52, 0xc5, 0x95, 0x26, 0xe7, 0xe1, 0x8b, - 0xde, 0x83, 0x16, 0x5d, 0xe3, 0x59, 0x58, 0x6c, 0xfa, 0x3b, 0x52, 0xc8, - 0x87, 0x83, 0xae, 0x21, 0x8b, 0x1d, 0xaf, 0xcf, 0xbe, 0x85, 0x3b, 0xc3, - 0xac, 0xa3, 0x39, 0xe4, 0x77, 0x7d, 0x3a, 0x03, 0x4c, 0x7a, 0xa5, 0xfe, - 0x98, 0x8f, 0xee, 0x01, 0x8b, 0x2e, 0x60, 0x56, 0x56, 0xc7, 0x98, 0x33, - 0x5b, 0xf8, 0x62, 0x0c, 0xb3, 0xcb, 0x2f, 0xd9, 0xc0, 0x6b, 0x65, 0x95, - 0x87, 0xae, 0x45, 0xd7, 0x43, 0xab, 0x2f, 0xe0, 0x8c, 0xf7, 0xc8, 0xd6, - 0x5b, 0xcb, 0x2d, 0xfa, 0xcb, 0xb9, 0x05, 0x94, 0x34, 0x40, 0x38, 0xbf, - 0xe5, 0xfc, 0x11, 0x00, 0x95, 0xfd, 0xb4, 0x50, 0x8e, 0xf5, 0xb2, 0xcb, - 0xcf, 0x9a, 0x59, 0x78, 0xb3, 0xab, 0x2f, 0x7b, 0x18, 0xb2, 0xbc, 0x7a, - 0x44, 0x37, 0xd1, 0xbb, 0xba, 0x4b, 0x2f, 0x4f, 0xdc, 0x59, 0x7c, 0x65, - 0x0c, 0x59, 0x7b, 0x67, 0x25, 0x94, 0x33, 0xd9, 0x60, 0xef, 0x48, 0x2f, - 0xa4, 0x6f, 0x05, 0x97, 0xec, 0xef, 0x8f, 0xab, 0x2f, 0xcf, 0xf6, 0x7d, - 0xd5, 0x95, 0xe3, 0xd0, 0xe9, 0x3d, 0xe0, 0x16, 0xcb, 0x2b, 0x64, 0x58, - 0x0a, 0xe3, 0xe2, 0x2b, 0x6c, 0xb2, 0xda, 0x59, 0x5b, 0x1a, 0x33, 0x12, - 0xbf, 0xb6, 0x8d, 0xb3, 0xbf, 0x96, 0x5d, 0xf7, 0x56, 0x5f, 0xff, 0xef, - 0x1e, 0xb1, 0x8f, 0xf3, 0x71, 0x81, 0xf5, 0x07, 0xfd, 0x65, 0xfb, 0x08, - 0x7e, 0x95, 0x94, 0xd9, 0x56, 0x9d, 0x30, 0x89, 0x81, 0x76, 0x37, 0x9c, - 0x37, 0xfc, 0xaa, 0xe4, 0x24, 0x62, 0x01, 0x86, 0x99, 0x2f, 0xfb, 0x06, - 0x33, 0xfb, 0x3e, 0xea, 0xcb, 0xe0, 0x94, 0xc1, 0x65, 0x78, 0xf6, 0xdc, - 0xea, 0xff, 0x4e, 0x76, 0x7c, 0xe3, 0x59, 0x7a, 0x7e, 0x82, 0xca, 0xd8, - 0xfb, 0xc6, 0x43, 0xb8, 0x63, 0x7d, 0x98, 0x5d, 0x59, 0x7f, 0xfb, 0x79, - 0x97, 0xfe, 0x37, 0x87, 0x4f, 0x65, 0x97, 0xc0, 0x7d, 0x41, 0x65, 0x49, - 0xf7, 0x0d, 0x32, 0xff, 0xe1, 0xc8, 0xf5, 0x3d, 0x30, 0x98, 0xd6, 0x5f, - 0x45, 0x3b, 0x90, 0x59, 0x7f, 0x74, 0x23, 0x1b, 0x34, 0xb2, 0xfd, 0xd3, - 0x29, 0xfd, 0x65, 0xff, 0x45, 0xcd, 0xee, 0x5b, 0x00, 0x6b, 0x2f, 0xe7, - 0x6b, 0xff, 0xd9, 0x1e, 0xb2, 0xfb, 0x3b, 0x8c, 0x59, 0x71, 0x0a, 0xb2, - 0x9c, 0xdc, 0x00, 0x86, 0xa0, 0x88, 0xce, 0x36, 0x5e, 0xfc, 0xff, 0x59, - 0x7d, 0xb4, 0x5e, 0x35, 0x96, 0xf6, 0x1e, 0x10, 0x07, 0xaf, 0xfc, 0x0d, - 0xdf, 0x1c, 0x61, 0x75, 0x9c, 0x59, 0x7f, 0xe2, 0x71, 0x7c, 0xf0, 0xe1, - 0x8d, 0x65, 0xfb, 0xc6, 0x4f, 0xb2, 0xcb, 0x05, 0x65, 0xf0, 0x21, 0x9c, - 0x09, 0xb8, 0x22, 0x7a, 0x62, 0x2b, 0x49, 0xda, 0xf1, 0x67, 0x56, 0x5c, - 0x23, 0x16, 0x51, 0x9e, 0xa9, 0x11, 0x74, 0x6e, 0xef, 0x86, 0xb2, 0xa5, - 0x72, 0x0b, 0x63, 0x38, 0x42, 0x38, 0x64, 0x38, 0x86, 0x29, 0x37, 0xc5, - 0xe6, 0x4f, 0x14, 0x32, 0x7c, 0xca, 0x44, 0xfd, 0x8d, 0x1c, 0x05, 0xb7, - 0xf4, 0x68, 0xa7, 0xa5, 0xbd, 0x65, 0xff, 0xd3, 0xbf, 0x53, 0xac, 0x3f, - 0x48, 0xd6, 0x5d, 0x38, 0xb2, 0x88, 0xf6, 0x78, 0x89, 0x73, 0xb1, 0x65, - 0xd1, 0x47, 0xac, 0xa2, 0x36, 0x3b, 0xc5, 0xaa, 0x08, 0xff, 0xc8, 0x45, - 0x9a, 0x95, 0x9b, 0xb6, 0x5f, 0xfa, 0xd1, 0xb0, 0xdd, 0x1c, 0xc2, 0x12, - 0x3b, 0x2a, 0x6d, 0x74, 0xc8, 0xe2, 0x38, 0xda, 0x90, 0x1b, 0x42, 0x69, - 0x9c, 0x65, 0xda, 0x3c, 0x08, 0x4a, 0xf6, 0x1c, 0xf5, 0x76, 0x53, 0x53, - 0x45, 0x95, 0x28, 0x19, 0xcb, 0xcf, 0xa5, 0xeb, 0x9c, 0xaa, 0x98, 0xa7, - 0x5a, 0xb5, 0x38, 0xc4, 0xc9, 0x5a, 0x3e, 0xa5, 0x72, 0xbc, 0xe3, 0x57, - 0xf2, 0xf5, 0xdb, 0x70, 0xa9, 0x2a, 0x47, 0x57, 0x2b, 0x0c, 0xee, 0xd2, - 0x88, 0x81, 0x18, 0x96, 0xf7, 0xa6, 0xa1, 0x41, 0x1f, 0x2e, 0x60, 0x49, - 0xde, 0x3d, 0xc8, 0xf1, 0x29, 0xbc, 0x20, 0xa6, 0x8d, 0x4b, 0xd7, 0xff, - 0x27, 0x7f, 0xf9, 0xbb, 0x1e, 0x0d, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x6c, - 0x5f, 0xb4, 0x17, 0x70, 0xaa, 0x2a, 0xd5, 0xdc, 0x8f, 0x59, 0x66, 0xf8, - 0x79, 0xdc, 0x34, 0xbb, 0x7e, 0x96, 0x5c, 0xf2, 0xb2, 0xff, 0x42, 0x00, - 0xe9, 0x4c, 0x16, 0x5f, 0x67, 0x9f, 0xab, 0x2c, 0xd8, 0xc3, 0xd4, 0x23, - 0x3b, 0xff, 0xff, 0x42, 0x77, 0xc9, 0xe8, 0x26, 0xc7, 0x81, 0x96, 0x0f, - 0x05, 0x59, 0x7f, 0xfb, 0x47, 0x0c, 0x86, 0x3c, 0x0a, 0x58, 0xb2, 0xff, - 0x6b, 0x03, 0x3f, 0x88, 0xc5, 0x97, 0xfb, 0xd3, 0xb6, 0xa7, 0x06, 0xb2, - 0xff, 0xfd, 0x3b, 0x4e, 0xa5, 0x93, 0x3d, 0xf1, 0xfe, 0xc5, 0x95, 0x28, - 0x88, 0xf1, 0x9d, 0xe9, 0x1c, 0x4b, 0x2a, 0x09, 0xd7, 0x1b, 0x5e, 0x91, - 0xfd, 0x0b, 0xde, 0x91, 0x5f, 0x69, 0x9e, 0x1a, 0xcb, 0xff, 0xb5, 0x02, - 0xce, 0xf2, 0x59, 0xd0, 0xac, 0xa9, 0x3e, 0x70, 0x11, 0xde, 0x1e, 0x12, - 0xcb, 0xfb, 0x90, 0x30, 0xbe, 0x96, 0x5e, 0x77, 0x0a, 0xa2, 0xb4, 0x5f, - 0x85, 0xfb, 0x80, 0xf9, 0x65, 0x4a, 0x20, 0x42, 0x5a, 0x65, 0x17, 0xff, - 0x8f, 0xb0, 0x90, 0xf8, 0xfb, 0xc7, 0xf9, 0x65, 0x68, 0xfe, 0x37, 0x97, - 0x5f, 0xdf, 0xc6, 0xcd, 0x18, 0xab, 0x2f, 0xfb, 0x06, 0x7c, 0x88, 0xa4, - 0x6b, 0x28, 0x67, 0xd2, 0xe6, 0x37, 0xfd, 0x21, 0xc6, 0x61, 0x76, 0x3d, - 0x65, 0xfe, 0xf4, 0xeb, 0xfe, 0xe6, 0xcb, 0x2e, 0xf1, 0xb0, 0xfb, 0xfc, - 0x75, 0x7a, 0x1f, 0x41, 0x65, 0xe0, 0x73, 0xab, 0x2f, 0xff, 0xe7, 0x3d, - 0xc9, 0x20, 0x40, 0xf9, 0xc9, 0x8c, 0xc0, 0xac, 0xbf, 0xfe, 0x0c, 0xeb, - 0x3f, 0x76, 0x3c, 0x24, 0xc2, 0xb2, 0xff, 0xfa, 0x1c, 0xc2, 0x96, 0x3e, - 0x77, 0xb2, 0xc5, 0x96, 0xec, 0xa2, 0x6d, 0xd3, 0xa9, 0x89, 0x9e, 0xf8, - 0x77, 0xb0, 0xf9, 0xbf, 0xff, 0xfb, 0x79, 0xf7, 0x98, 0x28, 0x7c, 0x7d, - 0xc8, 0x8a, 0x59, 0xb6, 0x34, 0xb2, 0xff, 0xfc, 0xfd, 0x7c, 0xe0, 0xf2, - 0x1e, 0x9d, 0xe3, 0x95, 0x95, 0x28, 0xcb, 0x77, 0x6b, 0xfd, 0x3d, 0x7f, - 0x46, 0x1c, 0xac, 0xbe, 0x87, 0x4f, 0x65, 0x97, 0xee, 0x71, 0xcb, 0x65, - 0x97, 0xe1, 0x3a, 0x53, 0xfa, 0xcb, 0x79, 0x65, 0x7c, 0x88, 0x56, 0x11, - 0xef, 0x28, 0x10, 0xa6, 0xff, 0xfb, 0xf7, 0xcf, 0x39, 0xee, 0x4b, 0x09, - 0xc5, 0x59, 0x7f, 0xe3, 0xe6, 0x0f, 0xee, 0x4e, 0xb8, 0xb2, 0xff, 0x3c, - 0x3a, 0x7b, 0x78, 0xd6, 0x54, 0xa6, 0x09, 0x88, 0x21, 0x51, 0x33, 0xfb, - 0xf6, 0x78, 0xf1, 0x8b, 0x2f, 0xc7, 0xfb, 0x0f, 0x4b, 0x2f, 0xff, 0xfc, - 0x4f, 0xd9, 0x27, 0xda, 0x75, 0x27, 0x09, 0x61, 0x48, 0xab, 0x2f, 0xef, - 0x48, 0x49, 0xd8, 0xb2, 0xff, 0xce, 0x61, 0xf3, 0x83, 0x6d, 0x1a, 0xca, - 0x33, 0xea, 0x22, 0xcb, 0xff, 0x63, 0xed, 0x38, 0x43, 0xf4, 0xac, 0xad, - 0x93, 0x7c, 0x19, 0x31, 0x94, 0x7a, 0x1a, 0x44, 0x41, 0x7f, 0xa1, 0x3a, - 0xda, 0x75, 0xb2, 0xcb, 0xe8, 0x6b, 0x18, 0xb2, 0xf6, 0x6b, 0xf5, 0x97, - 0xe8, 0xb0, 0x6c, 0xdd, 0x59, 0x7f, 0x14, 0xb5, 0xf4, 0x38, 0xb2, 0xbe, - 0x44, 0xd1, 0x91, 0x44, 0x3b, 0xb8, 0x59, 0x78, 0x50, 0x05, 0x65, 0x4a, - 0x65, 0xd9, 0x0c, 0xf1, 0x50, 0x6f, 0xff, 0xff, 0xec, 0x30, 0xe7, 0x64, - 0xbb, 0xc8, 0xd2, 0x63, 0xc6, 0x46, 0xd4, 0x9c, 0x1f, 0x8b, 0x2f, 0xec, - 0xeb, 0x97, 0xe0, 0x59, 0x7f, 0x7a, 0x31, 0xec, 0xe4, 0xb2, 0xfe, 0x67, - 0x06, 0x4e, 0xd2, 0xcb, 0xf7, 0xfc, 0x6d, 0x1b, 0x46, 0xd1, 0x65, 0x49, - 0xf3, 0xb9, 0x75, 0xf3, 0xfe, 0xe3, 0x59, 0x50, 0x4c, 0x17, 0x0b, 0x4a, - 0x12, 0xdc, 0x20, 0xbf, 0xf6, 0x6a, 0x22, 0xc1, 0xfa, 0x77, 0xac, 0xb3, - 0x76, 0xcb, 0x3d, 0x01, 0xb0, 0x33, 0x1d, 0xb4, 0xcc, 0x7b, 0x1b, 0x42, - 0xd8, 0x64, 0x39, 0x18, 0xa0, 0xb0, 0x8d, 0x0c, 0x24, 0x3e, 0x2d, 0x38, - 0xd6, 0xf5, 0x18, 0x0b, 0x08, 0x7d, 0x1d, 0xeb, 0xc7, 0x6e, 0x51, 0xbe, - 0xf0, 0xd7, 0xb1, 0xc6, 0x88, 0x83, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xb0, - 0x17, 0xf9, 0x87, 0xc0, 0x4e, 0xa2, 0x59, 0x7c, 0x11, 0x3d, 0x12, 0xca, - 0x93, 0xd9, 0x73, 0x4b, 0xdf, 0x89, 0xfa, 0xcb, 0xf0, 0xf3, 0x3f, 0xe2, - 0xcb, 0x37, 0xc4, 0x7a, 0xf2, 0x11, 0xbd, 0x20, 0x10, 0x82, 0xfd, 0xa0, - 0xbb, 0x85, 0x51, 0x3a, 0xaf, 0xff, 0xf7, 0xa7, 0x61, 0xe3, 0x1b, 0xf3, - 0x99, 0xe3, 0xe0, 0x36, 0x59, 0x7e, 0x6e, 0xc7, 0x83, 0x7c, 0x44, 0xcc, - 0x46, 0x97, 0xff, 0xf1, 0x02, 0x7b, 0xcc, 0x26, 0xff, 0xff, 0x3e, 0x7d, - 0x96, 0x5f, 0xc4, 0xde, 0x27, 0x89, 0xd6, 0x54, 0xa2, 0x3b, 0x17, 0xaf, - 0x0b, 0x2c, 0x59, 0x7e, 0xcd, 0xb6, 0x90, 0xac, 0xbe, 0x70, 0x88, 0xc5, - 0x97, 0xa1, 0xc6, 0xe2, 0x9f, 0x49, 0x0e, 0xef, 0x29, 0xbf, 0x68, 0x2e, - 0xe1, 0x54, 0x5b, 0x2b, 0xfb, 0xaf, 0xe8, 0xc3, 0x95, 0x97, 0xff, 0xf4, - 0x1b, 0xf2, 0x4f, 0x06, 0x79, 0xcc, 0xe9, 0x4a, 0xca, 0x94, 0x44, 0x39, - 0x7d, 0xfa, 0x12, 0x7b, 0xe5, 0x65, 0xff, 0x4f, 0x1b, 0xf8, 0xc2, 0xfa, - 0x59, 0x52, 0x7c, 0x5f, 0x94, 0x5f, 0xf4, 0xc2, 0x75, 0xb4, 0xeb, 0x65, - 0x96, 0x6f, 0x89, 0xcd, 0xb2, 0x17, 0x8f, 0x08, 0x32, 0x22, 0xbf, 0xf1, - 0x37, 0x60, 0x0f, 0xa4, 0xfb, 0x8b, 0x2f, 0xfe, 0x07, 0x1b, 0xe6, 0x9f, - 0x9e, 0x78, 0x2c, 0xb9, 0xff, 0xc4, 0x44, 0x7e, 0x85, 0x77, 0xc1, 0x59, - 0x7c, 0x17, 0x70, 0xaa, 0x2e, 0x65, 0x98, 0xb2, 0xb4, 0x6f, 0xda, 0x2e, - 0xbf, 0x8b, 0x3e, 0xfc, 0xe0, 0xb2, 0xcd, 0xb5, 0x95, 0xa3, 0xc3, 0xde, - 0x5d, 0x74, 0x4c, 0x59, 0x4b, 0x2c, 0xde, 0x53, 0x03, 0xc5, 0x73, 0x65, - 0x68, 0x92, 0x3c, 0x62, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x77, 0xaf, 0xf4, - 0x1b, 0xf3, 0x5a, 0x36, 0x96, 0x59, 0xbe, 0x1f, 0x5b, 0x9a, 0x5f, 0xf1, - 0x4b, 0xed, 0xf7, 0x24, 0x6b, 0x2e, 0x1f, 0xeb, 0x2f, 0x16, 0x6c, 0xb2, - 0xcd, 0xc6, 0x7d, 0xa2, 0x9c, 0x90, 0xc5, 0x4c, 0x27, 0x46, 0xf3, 0x10, - 0x59, 0x01, 0x45, 0xfa, 0x15, 0x07, 0x4f, 0x69, 0xd4, 0xa4, 0xc6, 0x43, - 0x8f, 0xd0, 0xd2, 0x78, 0x45, 0x7f, 0x1f, 0x01, 0x43, 0x57, 0x91, 0x99, - 0x76, 0x17, 0xe2, 0x42, 0x56, 0xff, 0xcd, 0x39, 0x6d, 0xc3, 0x2f, 0xa0, - 0xb2, 0xff, 0xec, 0xdb, 0x1a, 0xf6, 0x73, 0x0c, 0x96, 0x5f, 0xf8, 0xc8, - 0xb0, 0x50, 0x81, 0xc6, 0xb2, 0xfd, 0x9e, 0x1b, 0x92, 0xcb, 0x8d, 0xbf, - 0x91, 0xcc, 0x48, 0x1c, 0x43, 0x10, 0xf6, 0xfd, 0xce, 0x31, 0xe0, 0xb2, - 0xff, 0xff, 0xff, 0xde, 0x3f, 0x1c, 0x97, 0x49, 0xc5, 0xce, 0x83, 0xa7, - 0xde, 0x78, 0xf5, 0x3b, 0x3e, 0x82, 0xb2, 0xee, 0x4a, 0xcb, 0xff, 0xbe, - 0x08, 0x1f, 0xee, 0xe6, 0x10, 0xab, 0x2f, 0x13, 0xb7, 0x94, 0xc7, 0x02, - 0x51, 0xc8, 0x4c, 0x74, 0x5a, 0xfe, 0x6c, 0xc7, 0x33, 0xf6, 0xb6, 0x59, - 0x78, 0x0e, 0xc5, 0x97, 0xec, 0xdc, 0xec, 0x8a, 0xb2, 0xdb, 0x8b, 0x2b, - 0x63, 0x7f, 0x85, 0x75, 0xa3, 0xfc, 0x25, 0xbb, 0xff, 0x71, 0xc5, 0xf1, - 0xc9, 0x02, 0x56, 0x5f, 0xfb, 0xcf, 0xd7, 0xf7, 0xd0, 0xce, 0xac, 0xac, - 0x3f, 0xc3, 0x3d, 0xb9, 0xff, 0x59, 0x7f, 0xff, 0xf8, 0x9d, 0xa2, 0xce, - 0xf7, 0x36, 0x92, 0x71, 0x78, 0xc9, 0xc2, 0x1a, 0xcb, 0xd8, 0x27, 0x16, - 0x56, 0x22, 0xa7, 0x42, 0xfb, 0x8e, 0xb7, 0xfb, 0xd8, 0x33, 0xdf, 0xac, - 0x59, 0x7f, 0x9e, 0x1a, 0xc8, 0xf3, 0xfd, 0x65, 0xe7, 0x70, 0xaa, 0x25, - 0x75, 0xfe, 0x15, 0xcb, 0xfe, 0xe7, 0x96, 0x50, 0x4f, 0x70, 0xca, 0x2f, - 0xe7, 0xfb, 0x3d, 0x81, 0x59, 0x7f, 0xbe, 0x27, 0x15, 0xfb, 0x05, 0x97, - 0xff, 0x7b, 0x20, 0x1f, 0x18, 0xe4, 0xba, 0xb2, 0x8c, 0xfd, 0xfa, 0x69, - 0x50, 0x4e, 0xb0, 0x66, 0x9a, 0x84, 0x93, 0x08, 0x4a, 0x14, 0x97, 0xfa, - 0x74, 0xf0, 0x7f, 0xa0, 0xb2, 0xff, 0xff, 0xfb, 0x4e, 0x42, 0x4f, 0xf9, - 0x0f, 0x4e, 0xf1, 0xcb, 0x7c, 0x20, 0x43, 0x38, 0xb2, 0xdb, 0x2c, 0xb6, - 0xcb, 0x2a, 0x4d, 0x24, 0x04, 0xaf, 0xd8, 0x17, 0x86, 0x2c, 0xbf, 0xff, - 0xcf, 0xf7, 0x33, 0x5d, 0xeb, 0xec, 0x23, 0x07, 0xe3, 0x15, 0x65, 0xff, - 0x61, 0xef, 0x29, 0xcd, 0x41, 0x65, 0xff, 0xfb, 0xb0, 0xcd, 0xd7, 0x2f, - 0xfe, 0xee, 0xa7, 0x1a, 0x58, 0xd1, 0xb8, 0xbf, 0x7d, 0xdf, 0x4f, 0xeb, - 0x29, 0xd1, 0x2a, 0x06, 0xbb, 0xff, 0xd1, 0x14, 0xf7, 0x9d, 0xf8, 0x26, - 0x5b, 0x2c, 0xbf, 0xe7, 0x68, 0x7e, 0x9d, 0xb1, 0xa5, 0x94, 0x68, 0x87, - 0x24, 0xcb, 0xff, 0xf6, 0x1f, 0xf8, 0xc3, 0xef, 0x25, 0x8f, 0xf7, 0x56, - 0x5f, 0xff, 0xe0, 0xbe, 0x9f, 0xdf, 0x43, 0x3b, 0x9d, 0x90, 0x36, 0xf1, - 0x65, 0xff, 0xcf, 0xf4, 0x0f, 0xbe, 0x9d, 0x76, 0x56, 0x5f, 0xf4, 0xb3, - 0xb9, 0x14, 0x0f, 0xab, 0x2a, 0x09, 0x9b, 0x1a, 0xab, 0x19, 0x3a, 0x89, - 0x7f, 0xff, 0xbc, 0x63, 0x1e, 0x35, 0xf7, 0x40, 0xfc, 0xf3, 0x4e, 0x35, - 0x97, 0xc0, 0x7d, 0x41, 0x65, 0xfc, 0x53, 0xb7, 0xef, 0xd5, 0x97, 0xf9, - 0xc6, 0x26, 0xe4, 0x94, 0x16, 0x5a, 0x42, 0x7c, 0x8c, 0x2e, 0xbf, 0xff, - 0xc7, 0xd7, 0x63, 0x97, 0xfe, 0x69, 0xcb, 0x60, 0x9e, 0xcb, 0x28, 0x69, - 0x87, 0x9c, 0x21, 0x9c, 0x9e, 0xff, 0x81, 0xfb, 0x1e, 0x1c, 0xd0, 0xd6, - 0x5f, 0xff, 0xec, 0x32, 0xee, 0x14, 0x62, 0x71, 0xc9, 0x6d, 0x3a, 0x59, - 0x6c, 0xea, 0x26, 0x77, 0x9d, 0xdf, 0xff, 0xfe, 0xf6, 0x33, 0xf7, 0xd6, - 0x6f, 0xc1, 0xe7, 0x00, 0xff, 0x79, 0xa7, 0xea, 0xcb, 0xfe, 0x88, 0x3e, - 0x36, 0x4e, 0x85, 0x59, 0x7f, 0xbf, 0xcd, 0x66, 0x77, 0x8b, 0x2b, 0x0f, - 0xbb, 0xe3, 0xba, 0x1a, 0xe8, 0x4e, 0x46, 0xae, 0x13, 0xdf, 0x46, 0xe6, - 0x50, 0xcd, 0xe9, 0x50, 0x90, 0xe9, 0xbf, 0xf6, 0x7b, 0xcf, 0x0f, 0xf5, - 0x9f, 0x2c, 0xbf, 0xec, 0xfb, 0x83, 0x97, 0x2d, 0x96, 0x5f, 0xf3, 0xc3, - 0x9e, 0x9d, 0x08, 0x35, 0x95, 0x04, 0x59, 0xe2, 0x07, 0x4e, 0x6f, 0xff, - 0xf7, 0xb0, 0x25, 0x8c, 0xc2, 0xce, 0x83, 0xf6, 0x3c, 0x16, 0x5f, 0xff, - 0xff, 0x7f, 0x24, 0x17, 0xeb, 0xfd, 0xfe, 0x6f, 0x3e, 0xc3, 0xc7, 0xde, - 0x18, 0xd6, 0x5f, 0xf8, 0xf7, 0x9f, 0x62, 0x84, 0x97, 0x56, 0x54, 0x13, - 0x1f, 0xf9, 0x7f, 0xb0, 0x80, 0xbf, 0xfc, 0xf9, 0x0f, 0x61, 0x96, 0x7f, - 0x1f, 0x8b, 0x2f, 0x3c, 0x1b, 0xc7, 0x2c, 0xf4, 0xc9, 0x85, 0xac, 0x21, - 0x3a, 0x38, 0x64, 0xe4, 0x7b, 0xdf, 0x2a, 0x68, 0xcd, 0x90, 0xa4, 0xf1, - 0x07, 0xe4, 0xc5, 0x19, 0x97, 0x27, 0x6c, 0xbb, 0x19, 0x78, 0x91, 0xa7, - 0x6e, 0x1b, 0xdc, 0xdb, 0xc5, 0x97, 0xee, 0xc8, 0x1b, 0x78, 0xb2, 0xfd, - 0x81, 0x78, 0x62, 0xcb, 0xfd, 0x83, 0x32, 0xee, 0x79, 0x65, 0xec, 0xf3, - 0x76, 0xd1, 0x13, 0x98, 0x35, 0xe2, 0xb2, 0x26, 0xbd, 0xd9, 0x1a, 0x4b, - 0xff, 0xde, 0x73, 0xec, 0xc0, 0xf5, 0xb0, 0x3f, 0x49, 0x7f, 0x9c, 0x2d, - 0xc7, 0xa6, 0xdb, 0x79, 0x3e, 0x7e, 0x0e, 0x59, 0xb8, 0xdb, 0x3c, 0xd0, - 0xc6, 0xcc, 0xca, 0x7d, 0x07, 0x63, 0xbb, 0xdf, 0x0b, 0xab, 0xff, 0xcd, - 0xd8, 0xf0, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x13, 0x0a, 0xff, 0x06, 0x7e, - 0xe9, 0x4c, 0x16, 0x5f, 0xff, 0xd9, 0xbd, 0xc7, 0xe9, 0x6f, 0xdf, 0x60, - 0xc1, 0xde, 0x2c, 0xbf, 0xfd, 0xa7, 0xfd, 0xb8, 0xbd, 0x83, 0x53, 0xc8, - 0x2c, 0xbb, 0x1b, 0xe2, 0x38, 0x88, 0xcc, 0x45, 0xfb, 0x99, 0xbd, 0x65, - 0xf8, 0xfd, 0xec, 0xd9, 0x65, 0xe8, 0xef, 0xc3, 0x59, 0x7f, 0xf3, 0x63, - 0x3e, 0x80, 0x1f, 0xa5, 0x30, 0x59, 0x7c, 0xda, 0xf9, 0x06, 0x2c, 0xbf, - 0x47, 0x31, 0xcc, 0x75, 0x1e, 0xd9, 0x59, 0x7e, 0xc6, 0x46, 0xfb, 0x75, - 0x65, 0xfd, 0x2c, 0xcf, 0xe7, 0x7a, 0xca, 0x19, 0xee, 0xf8, 0xba, 0xff, - 0x6b, 0x03, 0x3f, 0x88, 0xc5, 0x97, 0xee, 0xbe, 0xcf, 0xc5, 0x97, 0xd2, - 0xc3, 0xd2, 0xcb, 0x4c, 0x47, 0x93, 0xa2, 0x8b, 0xe9, 0x23, 0x89, 0x65, - 0xff, 0xfb, 0x36, 0x8d, 0x98, 0x5d, 0xf3, 0x9f, 0x0f, 0x7a, 0xcb, 0xf7, - 0xa4, 0x66, 0x2a, 0xca, 0x34, 0xda, 0x34, 0x44, 0xc7, 0xe2, 0x28, 0xe9, - 0x0e, 0xe2, 0xbd, 0xcd, 0x9e, 0xac, 0xbd, 0xbd, 0xf8, 0xb2, 0xff, 0xdc, - 0x1e, 0x89, 0xda, 0xcf, 0xba, 0xb2, 0xff, 0x34, 0x59, 0xf7, 0x73, 0xcb, - 0x2b, 0x0f, 0xd0, 0xd0, 0x2f, 0xf4, 0xbe, 0xdd, 0x15, 0xf4, 0xb2, 0xfc, - 0xfa, 0x83, 0xb1, 0x65, 0x0d, 0x32, 0x8c, 0x1b, 0xf4, 0x24, 0x88, 0x80, - 0x06, 0x97, 0xfb, 0xbe, 0x73, 0xe1, 0xef, 0x59, 0x66, 0xca, 0xca, 0x23, - 0xc9, 0x8f, 0x34, 0xbf, 0xdc, 0x9c, 0x21, 0xfa, 0x56, 0x5f, 0xd3, 0x84, - 0x3f, 0x4a, 0xcb, 0xc0, 0xd4, 0x51, 0x8f, 0x73, 0x86, 0x37, 0xff, 0xb4, - 0x60, 0xc1, 0x73, 0xa0, 0x9f, 0x86, 0xb2, 0xff, 0xfc, 0x7a, 0xf3, 0xc6, - 0x1e, 0x43, 0xd3, 0xbc, 0x72, 0xb2, 0xff, 0x9a, 0x13, 0xd2, 0x1f, 0x1c, - 0x4b, 0x2f, 0x80, 0xcc, 0x1a, 0xcb, 0x67, 0xc7, 0xbd, 0xb8, 0x79, 0x7e, - 0xd4, 0xc3, 0x98, 0xb2, 0xf8, 0xfb, 0x9b, 0xd6, 0x5f, 0xec, 0x6b, 0xa4, - 0xff, 0xf1, 0x65, 0x49, 0xff, 0x78, 0x9f, 0xa4, 0x75, 0x8a, 0xa3, 0xc3, - 0x08, 0x0d, 0x1b, 0xf9, 0x2c, 0xa1, 0x6d, 0xd8, 0x54, 0xdf, 0xff, 0x49, - 0x77, 0x52, 0x70, 0x2c, 0x63, 0xc1, 0x65, 0xff, 0xe9, 0x0f, 0x01, 0xd3, - 0xf1, 0x8b, 0xe3, 0x59, 0x7f, 0x32, 0x70, 0xbb, 0x1e, 0xb2, 0xdc, 0x59, - 0x7e, 0x9c, 0x2e, 0xc7, 0xac, 0xbd, 0xc7, 0xfa, 0x31, 0xf4, 0xc4, 0x5e, - 0xc1, 0x1a, 0x1a, 0x68, 0x6c, 0x4c, 0xec, 0x2c, 0xef, 0xfb, 0x77, 0x07, - 0xe9, 0xdd, 0x29, 0x59, 0x46, 0x7e, 0x6e, 0x6d, 0x7f, 0xda, 0xd8, 0x3e, - 0x3f, 0x03, 0xab, 0x2b, 0xc7, 0xb6, 0xe4, 0x17, 0xf8, 0xc5, 0xd6, 0x6d, - 0x3b, 0x2c, 0xbf, 0x4b, 0x37, 0xff, 0x05, 0x95, 0xc3, 0xe0, 0xe9, 0xad, - 0xdd, 0x35, 0x97, 0x03, 0x16, 0x54, 0x0f, 0x38, 0x52, 0x2f, 0xc5, 0xaf, - 0xfb, 0xbe, 0xc3, 0x2e, 0x83, 0xcb, 0x2f, 0xed, 0x61, 0xfa, 0x46, 0xb2, - 0xf6, 0x81, 0xc5, 0x97, 0xff, 0xde, 0x92, 0xce, 0xfb, 0x3b, 0xe9, 0x03, - 0x6d, 0x65, 0xef, 0x67, 0xcb, 0x2f, 0xff, 0xf3, 0x5b, 0x9d, 0xcf, 0x46, - 0x2c, 0xd8, 0xf0, 0xbe, 0x87, 0x16, 0x5f, 0x9a, 0xee, 0x78, 0x2b, 0x2b, - 0x11, 0x20, 0xec, 0xf4, 0x34, 0xda, 0x70, 0xb0, 0xc7, 0x5d, 0x48, 0xa1, - 0x5b, 0x7f, 0xfb, 0xa4, 0xff, 0xfb, 0x0c, 0x60, 0xef, 0x16, 0x5f, 0x6f, - 0xc1, 0xca, 0xcb, 0xfe, 0x9d, 0x84, 0x87, 0x80, 0xfb, 0x2c, 0xa9, 0x45, - 0x4e, 0x25, 0x68, 0x8e, 0xff, 0xdf, 0x42, 0x37, 0x9c, 0x32, 0x43, 0x59, - 0x7c, 0x0d, 0x7e, 0xc5, 0x95, 0x87, 0xcb, 0xc4, 0x1b, 0xe2, 0xc3, 0x15, - 0x65, 0xf1, 0xcf, 0xdc, 0x59, 0x7e, 0x13, 0xa5, 0x3f, 0xac, 0xa6, 0x1f, - 0x67, 0x48, 0x77, 0x91, 0x5f, 0x77, 0xb8, 0xd2, 0xcb, 0xff, 0xd3, 0xdf, - 0xe6, 0x19, 0xf7, 0x76, 0x03, 0x4b, 0x2f, 0xd2, 0xcd, 0x3c, 0x4b, 0x2f, - 0x71, 0xff, 0x59, 0x52, 0x8e, 0x7c, 0x31, 0x32, 0x3f, 0x27, 0x74, 0xa2, - 0xff, 0x7a, 0x75, 0x2c, 0x38, 0x2c, 0xb9, 0xe0, 0xb2, 0xff, 0xf1, 0xf2, - 0x61, 0x1b, 0x3c, 0x7a, 0xf3, 0xac, 0xa1, 0xa2, 0x4b, 0x74, 0xcb, 0xc2, - 0xd7, 0xe8, 0xb8, 0x30, 0x6c, 0xb2, 0xfe, 0x7f, 0xf5, 0xa3, 0xd2, 0xcb, - 0xdd, 0x8d, 0x8b, 0x2a, 0x4f, 0xdf, 0xf2, 0xb1, 0x0b, 0xaf, 0xbc, 0x0f, - 0xba, 0xb2, 0xff, 0xef, 0x64, 0xe8, 0xb1, 0x8c, 0x04, 0x4b, 0x2f, 0xee, - 0x89, 0xff, 0xf3, 0xf2, 0xcb, 0xfe, 0x3e, 0x9e, 0x30, 0xb3, 0x65, 0x94, - 0x67, 0xd3, 0xe3, 0x2b, 0xfb, 0x86, 0x2b, 0x1f, 0xab, 0x2f, 0xfd, 0xbe, - 0x45, 0x81, 0x94, 0xea, 0x56, 0x5f, 0xff, 0xfb, 0x59, 0xbe, 0x4b, 0xb1, - 0x84, 0x29, 0xf3, 0x9f, 0x67, 0xbc, 0x59, 0x70, 0x63, 0xb5, 0x97, 0xb6, - 0x04, 0x16, 0x5e, 0x2c, 0xe4, 0x63, 0x75, 0x83, 0x94, 0x35, 0x42, 0xa1, - 0x85, 0x6f, 0xc4, 0x26, 0x5d, 0xa3, 0xfd, 0xf0, 0xa8, 0xbe, 0x8d, 0xbf, - 0x73, 0x65, 0x94, 0x35, 0x59, 0x39, 0x28, 0xe4, 0x56, 0x5b, 0x6e, 0xac, - 0xb6, 0xea, 0xcb, 0xd8, 0x3e, 0xac, 0xb3, 0x0c, 0xd8, 0x68, 0x52, 0xee, - 0xe2, 0xcb, 0xfd, 0x25, 0x3f, 0xbb, 0xee, 0x2c, 0xbd, 0xa7, 0x82, 0xcb, - 0xfc, 0x3f, 0x60, 0x7d, 0x3a, 0x59, 0x43, 0x44, 0x1e, 0x8d, 0x08, 0x72, - 0xfd, 0xcc, 0xee, 0xa5, 0x65, 0xff, 0xe6, 0xd9, 0x4b, 0x1c, 0xfc, 0xfd, - 0xf4, 0xac, 0xa9, 0x4c, 0xe8, 0xe1, 0x33, 0xc2, 0xf0, 0x13, 0xdc, 0xe1, - 0x59, 0x7f, 0x1c, 0x4e, 0x1e, 0x41, 0x65, 0x04, 0xf1, 0x3c, 0x2d, 0x7f, - 0xff, 0xbc, 0x64, 0x3d, 0x60, 0x7d, 0x3e, 0xcd, 0x6a, 0x45, 0x59, 0x7e, - 0x06, 0xe1, 0xe3, 0x16, 0x5f, 0xba, 0xec, 0x3e, 0xac, 0xa1, 0x4f, 0x44, - 0x8a, 0xaf, 0xde, 0x9c, 0x83, 0xac, 0xbf, 0xf1, 0xf4, 0x1a, 0x7e, 0x46, - 0x67, 0x56, 0x54, 0xa2, 0x24, 0x64, 0x4e, 0x4d, 0x7f, 0xd8, 0x78, 0xc2, - 0xcd, 0x9d, 0x65, 0xfa, 0x7b, 0xcf, 0xc0, 0xb2, 0xfe, 0xf1, 0xf4, 0xa5, - 0x8b, 0x2f, 0xa7, 0x79, 0x4a, 0xca, 0x88, 0xf3, 0x98, 0x59, 0x4d, 0x95, - 0xc8, 0x39, 0x44, 0xc8, 0xd8, 0x03, 0x08, 0x58, 0x88, 0xbd, 0x19, 0xd1, - 0x17, 0x74, 0xd8, 0x0e, 0xf7, 0xff, 0xce, 0xc1, 0xfa, 0x7d, 0x2c, 0x07, - 0xd0, 0xe2, 0xcb, 0xff, 0xd9, 0xdf, 0x1e, 0x7d, 0xdc, 0xdf, 0x3c, 0x59, - 0x6f, 0x79, 0x13, 0x84, 0xa3, 0x7c, 0xfc, 0xee, 0xcb, 0x2f, 0xfe, 0x72, - 0xf4, 0x98, 0xcf, 0x99, 0x05, 0x97, 0xa7, 0xf1, 0x16, 0x5f, 0xd9, 0xbf, - 0xc7, 0xfc, 0x4b, 0x2b, 0x11, 0xb6, 0x29, 0x43, 0x91, 0x92, 0x0f, 0x07, - 0xae, 0x29, 0x59, 0x7d, 0xe9, 0xe4, 0xac, 0xbc, 0x20, 0xf1, 0x65, 0xe6, - 0xdf, 0x23, 0xb5, 0x95, 0xb1, 0xfc, 0x8c, 0x55, 0xc8, 0x78, 0x3b, 0x7b, - 0x87, 0xc5, 0x97, 0xd1, 0x99, 0x18, 0x2b, 0x2f, 0x60, 0x46, 0xb2, 0xfb, - 0xb8, 0x5f, 0xac, 0xbf, 0x9d, 0xa9, 0xee, 0x6c, 0xb2, 0xb0, 0xfb, 0x0c, - 0x70, 0x88, 0xaf, 0xb6, 0x10, 0x1b, 0x2c, 0xbe, 0x8a, 0x13, 0xb2, 0xca, - 0x93, 0xca, 0xc2, 0x6b, 0xf8, 0xfb, 0xf8, 0x85, 0x05, 0x97, 0xcd, 0x45, - 0xc9, 0x59, 0x7e, 0x91, 0x73, 0xbc, 0x59, 0x7f, 0xe7, 0xef, 0x07, 0xa7, - 0xef, 0x80, 0xb2, 0xff, 0xf8, 0xc8, 0x10, 0xce, 0x46, 0xe9, 0xc7, 0xe6, - 0xcb, 0x2b, 0x64, 0xcb, 0x85, 0x20, 0xf8, 0xbf, 0xc4, 0x84, 0x51, 0xd3, - 0xfb, 0x37, 0x6c, 0xb7, 0xd0, 0x11, 0xd0, 0xcb, 0x60, 0xa2, 0x3b, 0x22, - 0x6d, 0x74, 0x86, 0xd4, 0x53, 0x32, 0x81, 0x07, 0x1c, 0x0e, 0x4b, 0x26, - 0x0c, 0xa7, 0x7f, 0xa1, 0x9e, 0x66, 0x31, 0x47, 0x43, 0xa8, 0x7a, 0xb2, - 0x12, 0x7e, 0x8d, 0xd1, 0xe1, 0xb3, 0xfc, 0x2a, 0x8a, 0x57, 0xcf, 0x27, - 0x2f, 0x3b, 0x28, 0x2c, 0x10, 0xc1, 0xde, 0x78, 0xd0, 0xe4, 0x7c, 0x24, - 0xc4, 0x8e, 0x92, 0xff, 0xfb, 0x47, 0xf4, 0x38, 0x7d, 0xf4, 0xeb, 0xb2, - 0xb2, 0xff, 0x4e, 0x9e, 0x27, 0xe4, 0x16, 0x5e, 0x77, 0x0a, 0xa2, 0xae, - 0x5f, 0xce, 0x27, 0xbc, 0xec, 0x59, 0x41, 0x3d, 0x6f, 0x14, 0x5f, 0xc7, - 0xaf, 0x38, 0x8c, 0x59, 0x43, 0x46, 0xce, 0xa1, 0x13, 0xe2, 0x2b, 0xe2, - 0xdc, 0xe3, 0xac, 0xbd, 0xa9, 0x62, 0xcb, 0xfd, 0x3a, 0xec, 0x94, 0xfe, - 0xb2, 0xff, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x88, 0xf1, 0x7e, 0xf1, 0xeb, - 0xce, 0xb2, 0xf1, 0x75, 0xd6, 0x5d, 0x83, 0xc3, 0xc2, 0xe1, 0x3d, 0x41, - 0x15, 0xec, 0x84, 0x1d, 0xfe, 0x84, 0xeb, 0x69, 0xd6, 0xcb, 0x2f, 0xf4, - 0x7b, 0xfa, 0x4a, 0x7f, 0x59, 0x52, 0x7d, 0x58, 0x6b, 0x7f, 0xdd, 0x30, - 0x36, 0xf2, 0x1b, 0x44, 0xb2, 0xfe, 0x2c, 0xef, 0x31, 0xa5, 0x95, 0x27, - 0xd8, 0xe7, 0xf7, 0xf8, 0x27, 0xcf, 0x1f, 0x4d, 0x65, 0xfb, 0x36, 0x72, - 0xf9, 0x65, 0x7c, 0x7b, 0x44, 0x65, 0x7e, 0xef, 0x70, 0xbf, 0x59, 0x7f, - 0xb7, 0x1c, 0x73, 0x83, 0x35, 0x96, 0x6e, 0x35, 0xc9, 0x5c, 0x8c, 0xa4, - 0xcd, 0x74, 0x49, 0xe1, 0xc7, 0x87, 0x09, 0x42, 0x4b, 0x90, 0x8c, 0xeb, - 0xd0, 0x84, 0x5b, 0x85, 0x17, 0xed, 0x05, 0xdc, 0x2a, 0x8b, 0x05, 0x78, - 0x59, 0xd9, 0x65, 0xff, 0xfc, 0x40, 0x7d, 0x99, 0x91, 0xe0, 0xd6, 0x6a, - 0x02, 0x41, 0x65, 0xfb, 0x0b, 0x3b, 0xc5, 0x97, 0xfb, 0x4e, 0xc1, 0x34, - 0xff, 0x2c, 0xb3, 0x7c, 0x4c, 0x34, 0x53, 0x40, 0x8f, 0x3b, 0x00, 0x84, - 0xd7, 0xf9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x59, 0x4b, 0xff, 0x67, 0x61, - 0x9f, 0xc7, 0xe7, 0x80, 0xb2, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x69, 0xaf, - 0xf9, 0xc7, 0xe7, 0x8b, 0xc6, 0xc5, 0x97, 0xfa, 0x59, 0x83, 0x29, 0xf9, - 0x65, 0xf8, 0x19, 0xa7, 0xe2, 0xcb, 0x37, 0x1a, 0x61, 0x18, 0x84, 0xc3, - 0x4f, 0x1c, 0xf0, 0xca, 0xfd, 0x1d, 0x36, 0x63, 0x8c, 0x71, 0x8e, 0x2b, - 0x2f, 0xa3, 0x8b, 0x65, 0xb3, 0x1c, 0xac, 0xa8, 0xe8, 0xfe, 0x9b, 0x5d, - 0x12, 0xff, 0xa3, 0xbf, 0x1b, 0x43, 0x77, 0x0a, 0xcb, 0xf3, 0x6b, 0xb6, - 0x39, 0x92, 0xb2, 0xff, 0x08, 0xcc, 0xef, 0x0f, 0x8b, 0x2c, 0x6b, 0x2a, - 0x3b, 0x3c, 0x48, 0xf3, 0x4b, 0xfd, 0x3b, 0x69, 0xc7, 0xb3, 0xac, 0xbf, - 0xff, 0x79, 0xcb, 0xb0, 0xcd, 0x64, 0x91, 0x67, 0x56, 0x54, 0x11, 0x0a, - 0x66, 0x97, 0xed, 0xee, 0x3c, 0x62, 0xcb, 0xe6, 0x4f, 0xdc, 0x59, 0x7f, - 0x8d, 0x87, 0xb9, 0x24, 0x6b, 0x2f, 0xd2, 0x41, 0xf4, 0xac, 0xbf, 0xfb, - 0x3b, 0xe3, 0x66, 0x77, 0x82, 0x71, 0x65, 0xff, 0xb1, 0xff, 0x98, 0x6a, - 0x70, 0x96, 0x50, 0x51, 0x02, 0x68, 0xb7, 0xc7, 0xbf, 0xdb, 0xd6, 0x5f, - 0x9f, 0x92, 0x51, 0x2c, 0xbf, 0xdb, 0x87, 0xfc, 0x67, 0xf0, 0x16, 0x54, - 0x13, 0xf0, 0xc2, 0x28, 0x8a, 0x7c, 0x47, 0xf9, 0x99, 0x42, 0x9f, 0x84, - 0x5d, 0x26, 0xdc, 0x27, 0xbf, 0xcc, 0x11, 0x9f, 0xb0, 0xff, 0x59, 0x7e, - 0x8d, 0xad, 0x03, 0xcb, 0x2f, 0xc6, 0x2c, 0x90, 0x56, 0x57, 0x0f, 0x4b, - 0xa5, 0x75, 0x28, 0xab, 0xc8, 0x44, 0xde, 0x3c, 0xd9, 0x65, 0xff, 0xe0, - 0xf8, 0xfc, 0x7a, 0x98, 0x9a, 0xcf, 0x2c, 0xaf, 0x1f, 0x39, 0x0e, 0x5e, - 0xfc, 0xb1, 0x65, 0xc5, 0x8b, 0x2a, 0x31, 0xb1, 0xfc, 0x72, 0xff, 0xdc, - 0x38, 0xde, 0x7d, 0x4e, 0xf9, 0x59, 0x7f, 0xcc, 0x78, 0x47, 0x83, 0xc7, - 0xa5, 0x95, 0xb2, 0x28, 0x74, 0x48, 0xc4, 0x1b, 0xf3, 0xf3, 0xb2, 0x35, - 0x95, 0x27, 0xb0, 0x03, 0x1b, 0xfc, 0xfa, 0x83, 0xf5, 0xc2, 0xb2, 0xa5, - 0x39, 0xef, 0x46, 0x96, 0x02, 0x1b, 0xe9, 0x12, 0x7c, 0xb2, 0xe3, 0xd9, - 0x65, 0xe7, 0x70, 0xaa, 0x2d, 0xc5, 0x6c, 0x6f, 0xc2, 0x2f, 0x7f, 0xe3, - 0xd7, 0x9f, 0x35, 0xb1, 0xec, 0xb2, 0xfa, 0x29, 0x1e, 0x2c, 0xbf, 0xf6, - 0x6b, 0x0a, 0x37, 0xb3, 0xe6, 0x96, 0x5f, 0x8b, 0xf7, 0xfb, 0x8b, 0x2a, - 0x4f, 0xa9, 0xd0, 0x6b, 0xe4, 0xdb, 0xb4, 0xc3, 0xe2, 0x27, 0x3f, 0xe4, - 0x23, 0x2f, 0xfc, 0xe3, 0x27, 0xdf, 0xe9, 0x8b, 0x8b, 0x2f, 0xb7, 0xc8, - 0x49, 0x65, 0xfc, 0x5f, 0xc4, 0x52, 0xc5, 0x94, 0x2a, 0x24, 0x58, 0x80, - 0xe4, 0x77, 0x98, 0x46, 0xb2, 0xc4, 0xb2, 0xff, 0xbc, 0x07, 0xe6, 0x61, - 0x0a, 0xb2, 0xfd, 0xc7, 0xf9, 0xc6, 0xb2, 0xc6, 0x33, 0xe0, 0xe9, 0xc5, - 0x4a, 0x2c, 0xb0, 0x6f, 0xce, 0x17, 0xfd, 0x2e, 0x5e, 0xcc, 0x20, 0xac, - 0xbf, 0xdd, 0xe9, 0xec, 0xc9, 0x25, 0x95, 0x03, 0xe8, 0xc3, 0x5b, 0xfe, - 0x92, 0x03, 0x27, 0x9d, 0x35, 0x97, 0xf8, 0x03, 0x2c, 0xde, 0xf0, 0x59, - 0x7f, 0xd0, 0x3d, 0xf9, 0xe7, 0x2d, 0x96, 0x5e, 0x9c, 0xd2, 0xcb, 0xfd, - 0xe9, 0x81, 0xc7, 0xe3, 0x16, 0x50, 0xcf, 0x43, 0x83, 0x77, 0xe8, 0xdf, - 0x70, 0xc5, 0x59, 0x5b, 0x1e, 0x77, 0xc4, 0x55, 0xf2, 0x3f, 0x1e, 0x1b, - 0x17, 0xfe, 0xcf, 0x1e, 0x87, 0xe9, 0x20, 0xac, 0xbf, 0xf1, 0x67, 0x79, - 0x31, 0xbb, 0x81, 0x59, 0x50, 0x3f, 0xc3, 0x3d, 0xbf, 0xdf, 0x42, 0x4b, - 0xb9, 0xfa, 0xcb, 0xee, 0x7e, 0x7d, 0x59, 0x50, 0x54, 0x98, 0xf1, 0x98, - 0x72, 0x14, 0x80, 0x21, 0x10, 0xd2, 0xfc, 0xfa, 0xee, 0x79, 0x65, 0xff, - 0x8f, 0xf9, 0x80, 0xfd, 0x24, 0x15, 0x95, 0x03, 0xe5, 0xe9, 0x3d, 0xfd, - 0xd8, 0x66, 0xb3, 0x8b, 0x29, 0x65, 0xe0, 0x3f, 0x56, 0x5c, 0x09, 0x59, - 0x50, 0x36, 0x5d, 0x1c, 0xa5, 0x97, 0xdf, 0x04, 0xf4, 0xb2, 0xe6, 0x37, - 0xc4, 0x46, 0x7c, 0x7a, 0xc2, 0x1e, 0x85, 0xdf, 0x60, 0xc8, 0x6b, 0x2a, - 0x4f, 0xb3, 0x7a, 0x5d, 0xff, 0x17, 0x7c, 0xf0, 0x03, 0x88, 0xb2, 0xa5, - 0x3a, 0x3c, 0x22, 0x38, 0xc9, 0x5c, 0x8e, 0xf1, 0x76, 0x3d, 0x65, 0xe8, - 0xd1, 0xcc, 0x72, 0xb2, 0xff, 0xa0, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x28, - 0x75, 0x1a, 0x23, 0xe2, 0x1f, 0x72, 0x9b, 0xf7, 0xf2, 0x1c, 0x62, 0xcb, - 0xe9, 0x8b, 0x06, 0xb2, 0xb4, 0x79, 0x5c, 0x28, 0xbf, 0xb9, 0xc7, 0xf6, - 0x05, 0x65, 0xff, 0x64, 0x8f, 0xd3, 0xb9, 0x31, 0x2c, 0xbf, 0xdf, 0x9f, - 0xbd, 0x9f, 0xba, 0xca, 0x61, 0xf7, 0xfe, 0x77, 0x7f, 0xe2, 0xdc, 0x72, - 0x76, 0x0f, 0x06, 0xb2, 0xa5, 0x32, 0x0c, 0x22, 0x78, 0x4e, 0x6e, 0x11, - 0xdf, 0xf9, 0xc7, 0x23, 0xf1, 0x91, 0xb1, 0x65, 0xff, 0xb7, 0xbe, 0xb8, - 0xfe, 0xe4, 0xec, 0xb2, 0xff, 0x70, 0x01, 0xce, 0x94, 0xac, 0xa6, 0x22, - 0xbf, 0xc7, 0x9d, 0x41, 0xbd, 0xdc, 0x0a, 0xcb, 0xb0, 0x2b, 0x2d, 0x91, - 0x8d, 0x97, 0x47, 0x2f, 0xfd, 0x0f, 0x3e, 0xbe, 0xec, 0x9e, 0xcb, 0x28, - 0x8f, 0xa0, 0x05, 0x17, 0xfa, 0x13, 0xad, 0xa7, 0x5b, 0x2c, 0xbf, 0x3f, - 0x37, 0xe7, 0x16, 0x54, 0x9e, 0xf7, 0xe6, 0xb7, 0xe8, 0xde, 0xcf, 0xdd, - 0x65, 0xfd, 0x07, 0xd6, 0x7d, 0xd5, 0x97, 0xf0, 0xdd, 0x9e, 0x36, 0x96, - 0x54, 0x9e, 0xe6, 0x17, 0x5f, 0xf6, 0x77, 0x82, 0x73, 0x4f, 0xc5, 0x97, - 0xe3, 0xef, 0x1c, 0xd6, 0x5f, 0xf6, 0xbf, 0xd3, 0x97, 0x73, 0xf5, 0x95, - 0xb2, 0x68, 0x1a, 0x84, 0x33, 0x08, 0x3f, 0x3a, 0x01, 0x35, 0xe1, 0x35, - 0x05, 0x97, 0xf0, 0x9d, 0x04, 0xe6, 0xcb, 0x2f, 0xef, 0xfc, 0xe7, 0xe0, - 0x2c, 0xa8, 0x1f, 0xdc, 0x43, 0xda, 0x2f, 0xbf, 0xf8, 0x25, 0x3d, 0x2c, - 0xdf, 0x9a, 0x82, 0xcb, 0xf3, 0xf3, 0x9b, 0x71, 0x65, 0xee, 0x31, 0xd6, - 0x5f, 0xe2, 0x14, 0x4f, 0x78, 0x1c, 0x59, 0x51, 0x1e, 0x90, 0x07, 0x2f, - 0xfe, 0x07, 0x3c, 0xf0, 0x92, 0x1e, 0x79, 0x65, 0xff, 0x09, 0x3f, 0xc5, - 0x09, 0xd6, 0xcb, 0x2f, 0xe2, 0x7e, 0x89, 0x21, 0x59, 0x4c, 0x3e, 0xa2, - 0x3d, 0xbe, 0x2f, 0xf6, 0xfd, 0x65, 0xff, 0xfe, 0x8b, 0x86, 0x38, 0xd1, - 0x19, 0x6f, 0x78, 0x9f, 0xa0, 0x95, 0x95, 0x88, 0xac, 0x32, 0x17, 0x25, - 0xbf, 0xdc, 0x8d, 0xd8, 0xc3, 0xc2, 0x59, 0x6f, 0xd6, 0x58, 0xfe, 0x3c, - 0x9d, 0x1b, 0xdf, 0x9f, 0xee, 0x74, 0xd6, 0x54, 0xab, 0x31, 0xc3, 0x01, - 0x51, 0x0d, 0xef, 0x44, 0x7e, 0x8d, 0x64, 0x9d, 0xb8, 0x51, 0x7e, 0x70, - 0x0b, 0x22, 0x2c, 0xbf, 0x47, 0x79, 0xa9, 0xe2, 0xca, 0x88, 0xf5, 0x48, - 0xa6, 0xfe, 0xce, 0x98, 0xf1, 0x8b, 0x2f, 0x8a, 0x06, 0xc5, 0x96, 0x8d, - 0x11, 0xe6, 0xb9, 0x65, 0xfe, 0xe4, 0xc5, 0xf4, 0x33, 0x7a, 0xca, 0x19, - 0xf0, 0xb9, 0x55, 0xff, 0xf8, 0x7e, 0x91, 0x1b, 0xf4, 0x9f, 0xfe, 0x72, - 0x7f, 0x49, 0x70, 0xf1, 0x65, 0xfb, 0x69, 0xd9, 0xf8, 0xb2, 0xb1, 0x13, - 0x26, 0xb4, 0xe2, 0xd7, 0x7e, 0x05, 0x97, 0xfe, 0xe6, 0xf7, 0x8b, 0x67, - 0x27, 0x89, 0x65, 0xfc, 0xff, 0xc4, 0x52, 0x35, 0x97, 0xfc, 0x59, 0xb0, - 0xfd, 0x85, 0xd5, 0x95, 0x28, 0xc0, 0x88, 0x63, 0xc8, 0x44, 0x5d, 0x51, - 0xd3, 0x7a, 0xbf, 0x1c, 0x97, 0x47, 0x13, 0xe6, 0xd1, 0xda, 0x65, 0x4f, - 0xc2, 0x33, 0x21, 0xca, 0x2b, 0xc8, 0xe9, 0x05, 0x87, 0x08, 0x61, 0xc5, - 0xf4, 0x25, 0x8c, 0x86, 0x29, 0x50, 0x7a, 0x94, 0xbe, 0xc8, 0x67, 0xfa, - 0x39, 0x97, 0x87, 0x97, 0xf0, 0xca, 0x27, 0xfe, 0x46, 0xc5, 0xd9, 0x7f, - 0xc0, 0x86, 0x5e, 0xf8, 0x68, 0x47, 0xc2, 0xe8, 0x48, 0x76, 0xdf, 0xff, - 0x82, 0xdf, 0xfd, 0x67, 0xc1, 0xf1, 0xf7, 0x8f, 0xf2, 0xca, 0x6e, 0xa8, - 0xd4, 0x11, 0xed, 0x5f, 0x4e, 0xa0, 0xc5, 0x97, 0xdf, 0x71, 0xf7, 0x56, - 0x5f, 0xb0, 0x60, 0xef, 0x16, 0x5e, 0x7d, 0x0a, 0xb2, 0xcd, 0xf6, 0x44, - 0x69, 0x91, 0x11, 0x33, 0x45, 0x17, 0xf9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, - 0x5e, 0x6a, 0x97, 0xcb, 0xf3, 0x83, 0x98, 0xe3, 0x3c, 0xca, 0xfa, 0xf3, - 0xe9, 0xe8, 0x7d, 0x47, 0x8a, 0xc5, 0x3f, 0x46, 0x92, 0x56, 0x81, 0x83, - 0x90, 0xba, 0xde, 0x8b, 0x7f, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x45, 0x2c, - 0xb9, 0xb2, 0xda, 0x2c, 0xbf, 0x1b, 0x7e, 0x61, 0xac, 0xbf, 0x68, 0x2e, - 0xe1, 0x54, 0x58, 0x4b, 0xff, 0xec, 0x21, 0xcf, 0xa7, 0xee, 0xe1, 0x3b, - 0x16, 0x5e, 0x20, 0x05, 0x65, 0x49, 0xf4, 0xba, 0x6d, 0xb8, 0xb2, 0xfe, - 0x06, 0x8f, 0x67, 0x62, 0xcb, 0x37, 0x6d, 0x13, 0x44, 0x19, 0x06, 0x14, - 0x32, 0x13, 0xbc, 0x20, 0x8f, 0x11, 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, - 0xa2, 0xcb, 0x5a, 0x0b, 0x2e, 0xd0, 0xab, 0x2f, 0x9b, 0x8a, 0xda, 0x47, - 0x4b, 0x29, 0x25, 0xe6, 0xe2, 0x31, 0x65, 0x04, 0xf6, 0xf8, 0x60, 0x00, - 0xba, 0x15, 0x16, 0x64, 0x23, 0xc7, 0x3b, 0xa7, 0xcb, 0x2d, 0xfa, 0xcb, - 0x85, 0xd9, 0x65, 0xb5, 0x03, 0x57, 0x82, 0x54, 0x13, 0xe7, 0x74, 0x1b, - 0x8f, 0xf5, 0x97, 0xfc, 0xff, 0x16, 0x7d, 0xd9, 0x15, 0x65, 0xfe, 0xdb, - 0xee, 0x48, 0xfa, 0x6b, 0x2f, 0xd3, 0x9b, 0xe7, 0x8b, 0x2d, 0x2e, 0x7b, - 0xad, 0x1a, 0xd3, 0xa2, 0xf8, 0xa1, 0x2f, 0x7c, 0xf0, 0x80, 0xd6, 0x5f, - 0xcf, 0xa9, 0xeb, 0xf9, 0x65, 0xe6, 0x9a, 0x69, 0x25, 0xff, 0x4c, 0x3e, - 0xee, 0xa7, 0x3e, 0x48, 0xdc, 0xd0, 0x5f, 0xf0, 0x1f, 0xec, 0xe9, 0xfc, - 0xd2, 0xcb, 0xa7, 0x8b, 0x28, 0x69, 0x85, 0xf8, 0x88, 0x93, 0x38, 0x99, - 0xbc, 0xee, 0xe6, 0xd2, 0x3a, 0x59, 0x7f, 0x1b, 0xf3, 0xfe, 0x01, 0x65, - 0xff, 0xc7, 0x07, 0xe3, 0x90, 0x03, 0x9c, 0x49, 0x7f, 0xff, 0x78, 0xfb, - 0xec, 0xe1, 0x63, 0x01, 0xcf, 0x3c, 0x16, 0x5f, 0xe9, 0x87, 0x5f, 0xce, - 0x35, 0x97, 0xcf, 0xd1, 0x78, 0xb2, 0xa5, 0x1e, 0x38, 0x85, 0xa5, 0xa7, - 0x32, 0xb4, 0x16, 0x5a, 0x0b, 0x2d, 0x05, 0x97, 0x9a, 0x69, 0xa5, 0x96, - 0x0a, 0x46, 0xe6, 0x82, 0xa4, 0xfd, 0x85, 0x11, 0x31, 0x1f, 0xcc, 0x6f, - 0xe0, 0x73, 0xcf, 0x0c, 0x59, 0x7f, 0xfd, 0xc3, 0x0e, 0x16, 0xa4, 0xb0, - 0x78, 0x2a, 0xca, 0x61, 0xfd, 0x74, 0xb6, 0xdf, 0x2c, 0xb0, 0xab, 0x2c, - 0x05, 0x94, 0x46, 0x8f, 0x82, 0x55, 0x87, 0xef, 0xc2, 0x2e, 0x9a, 0xdd, - 0xc1, 0x56, 0x5b, 0x16, 0x5c, 0x0f, 0xe4, 0xd4, 0x0c, 0x62, 0xe3, 0x95, - 0x97, 0xf1, 0x4b, 0x0a, 0x71, 0x65, 0xff, 0xd2, 0xfa, 0xef, 0xb0, 0x60, - 0xef, 0x16, 0x5d, 0x09, 0x59, 0x7b, 0x83, 0x15, 0x65, 0x6c, 0x8e, 0x41, - 0x96, 0xc4, 0x2b, 0xe2, 0xa0, 0x22, 0x08, 0x2d, 0x7f, 0xff, 0xfd, 0xe9, - 0xef, 0x0f, 0x9c, 0x9e, 0xf8, 0xe7, 0x5f, 0xb1, 0xe1, 0x0e, 0x8a, 0xb2, - 0xe3, 0xf9, 0x65, 0xd2, 0xc5, 0x97, 0xff, 0xe9, 0x20, 0x42, 0x13, 0xdf, - 0x63, 0x04, 0x9f, 0xd6, 0x5f, 0xff, 0x19, 0x02, 0x19, 0xcd, 0x49, 0xc1, - 0xf8, 0x92, 0xa0, 0x8a, 0x3e, 0xab, 0x5c, 0x16, 0xf2, 0xc9, 0x2d, 0xda, - 0x12, 0xf0, 0x21, 0x1c, 0x35, 0x72, 0x33, 0x11, 0x53, 0x02, 0x43, 0xa8, - 0xc7, 0xde, 0x16, 0x05, 0x19, 0x6f, 0x23, 0x3a, 0xea, 0xd6, 0xf7, 0xf8, - 0xf1, 0x71, 0x21, 0x73, 0x7f, 0xf9, 0xbb, 0x1e, 0x0d, 0xf3, 0x41, 0x77, - 0x0a, 0xa2, 0x8c, 0x5f, 0xcf, 0xaf, 0xd9, 0x3f, 0xac, 0xbe, 0x9d, 0x4f, - 0xeb, 0x2b, 0xc7, 0xa3, 0xbc, 0xbe, 0xfc, 0x4f, 0xfc, 0x88, 0xb2, 0xff, - 0xc7, 0xf4, 0x38, 0x59, 0xdf, 0x01, 0x65, 0xff, 0x1e, 0xbd, 0x98, 0x50, - 0xe2, 0xca, 0x81, 0xfb, 0x04, 0xfe, 0xf9, 0xf7, 0xe1, 0x2c, 0xaf, 0x8f, - 0x13, 0x44, 0x57, 0xfe, 0x78, 0x07, 0xc6, 0xc9, 0xd0, 0xab, 0x2c, 0xde, - 0x53, 0x68, 0xf1, 0x27, 0x61, 0xc4, 0xd1, 0x1d, 0x41, 0x9e, 0xfd, 0xa8, - 0xe1, 0x58, 0xd8, 0xf4, 0xa2, 0x1e, 0x46, 0x1d, 0xd9, 0x42, 0x77, 0xff, - 0x37, 0x78, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x88, 0xe5, 0x7f, 0xf9, 0xbb, - 0x1e, 0x0d, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x72, 0x5f, 0xe6, 0xf9, 0xa0, - 0xbb, 0x85, 0x51, 0x66, 0x2e, 0xef, 0xeb, 0x2f, 0xd9, 0xe2, 0xcd, 0x2c, - 0xbd, 0xb4, 0xe9, 0x65, 0xfa, 0x1c, 0x76, 0x37, 0xf1, 0xed, 0x90, 0xc7, - 0x09, 0xaf, 0xa3, 0x84, 0x75, 0x1d, 0x47, 0x05, 0x97, 0xf8, 0x78, 0xc9, - 0xfa, 0x58, 0xb2, 0xa3, 0xa3, 0xec, 0x8e, 0x07, 0x37, 0xfb, 0x53, 0xdf, - 0x3f, 0xd0, 0x59, 0x7f, 0xff, 0xfd, 0x31, 0x7a, 0x7b, 0x22, 0x1e, 0xa2, - 0x9f, 0x66, 0xfc, 0xf6, 0x0f, 0x7e, 0x2c, 0xaf, 0xd1, 0x6a, 0x46, 0x97, - 0xfd, 0x9c, 0x7e, 0xfe, 0x21, 0x41, 0x65, 0xbf, 0x59, 0x7f, 0x73, 0x5a, - 0xcf, 0xb8, 0xb2, 0xa3, 0xa3, 0xc1, 0xc1, 0x2b, 0xda, 0xc6, 0x2c, 0xbe, - 0x32, 0xce, 0x2c, 0xb6, 0x74, 0xde, 0x6e, 0x0e, 0x5f, 0xff, 0xfb, 0xd2, - 0x40, 0xe7, 0x24, 0x4e, 0x99, 0x02, 0x19, 0xce, 0x9a, 0xcb, 0xee, 0xf4, - 0xf4, 0xb2, 0xa0, 0x88, 0xdd, 0x35, 0xde, 0x72, 0x0a, 0xcb, 0xdc, 0x9e, - 0x2c, 0xbd, 0xb4, 0x7f, 0x16, 0x5d, 0x1d, 0xf9, 0x65, 0x8d, 0x65, 0xfe, - 0x9e, 0x4f, 0x76, 0xc6, 0x96, 0x58, 0xd8, 0x78, 0x84, 0x21, 0x7f, 0xe2, - 0x1f, 0xa7, 0x9c, 0xcd, 0x4a, 0xcb, 0xbf, 0x75, 0x97, 0xec, 0xfb, 0xb9, - 0x2b, 0x2e, 0x8c, 0x22, 0xcb, 0xf0, 0x7c, 0xe6, 0xdb, 0x59, 0x7e, 0x07, - 0x27, 0x92, 0xb2, 0xb4, 0x7a, 0x4e, 0x57, 0x7f, 0x17, 0xfe, 0xcf, 0xdd, - 0x65, 0xf1, 0x4e, 0xf0, 0x2c, 0xa3, 0x3d, 0x17, 0x2e, 0xbb, 0xe6, 0x2c, - 0xbf, 0xfa, 0x3d, 0xcb, 0xfc, 0xec, 0x91, 0xfe, 0xb2, 0xcd, 0xe3, 0xa5, - 0xd7, 0x09, 0x87, 0x16, 0xc4, 0x70, 0x7b, 0x1b, 0x16, 0x42, 0xd8, 0x24, - 0x7f, 0x0d, 0xc4, 0x39, 0xa2, 0x16, 0x30, 0x39, 0x33, 0x6c, 0xf4, 0x85, - 0xf8, 0x4e, 0x06, 0xcd, 0xee, 0x51, 0xe4, 0x02, 0x0c, 0x5f, 0xd9, 0xa0, - 0xbb, 0x85, 0x51, 0x6e, 0xaf, 0xcd, 0xc7, 0xe9, 0xd2, 0xca, 0x6e, 0x7c, - 0x7e, 0x39, 0xbf, 0x89, 0xdb, 0xeb, 0x6f, 0x2c, 0xbc, 0x7f, 0x7e, 0xb2, - 0xff, 0xed, 0x38, 0x7f, 0x07, 0x3b, 0x3f, 0x34, 0xb2, 0xe2, 0x15, 0x65, - 0xf0, 0x5d, 0xc2, 0xa8, 0xa4, 0x15, 0x87, 0x8b, 0xa1, 0x7b, 0xfd, 0x0e, - 0x9b, 0x5c, 0xfc, 0xd6, 0x5f, 0xfb, 0x3c, 0x7b, 0xfd, 0x98, 0x41, 0x59, - 0x7f, 0x67, 0xb3, 0xc6, 0x2a, 0xcb, 0xcf, 0x06, 0xe3, 0x4d, 0x3b, 0x21, - 0x1b, 0xf1, 0x0f, 0x0d, 0x7a, 0x7d, 0x7f, 0x8c, 0x60, 0xef, 0x18, 0x4b, - 0x2f, 0x9a, 0x64, 0xf1, 0x65, 0xe7, 0x28, 0x2c, 0xb3, 0x70, 0xaa, 0x70, - 0xd1, 0x83, 0x23, 0x6c, 0x76, 0x30, 0x19, 0xb4, 0x47, 0x7f, 0xf3, 0x77, - 0x83, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x94, 0x95, 0xb3, 0x31, 0x22, 0x25, - 0x46, 0x2d, 0x3c, 0x27, 0x3f, 0x9e, 0x29, 0x28, 0xe1, 0xbb, 0x2a, 0x53, - 0x71, 0x9e, 0xf3, 0x77, 0x0a, 0xcb, 0xf8, 0xcb, 0x3d, 0x81, 0x59, 0x7f, - 0x19, 0x77, 0x0c, 0x6b, 0x2e, 0x87, 0x56, 0x5e, 0x29, 0x69, 0x65, 0xd2, - 0xdf, 0x46, 0xcc, 0x85, 0xea, 0x08, 0x8b, 0xea, 0xf5, 0xee, 0x01, 0x8b, - 0x2c, 0xd9, 0x59, 0x66, 0xf0, 0x4c, 0x2f, 0xe8, 0x5b, 0x19, 0x1c, 0x78, - 0xed, 0xe2, 0x78, 0x2c, 0xbf, 0x68, 0x2e, 0xe1, 0x54, 0x4e, 0xcb, 0xff, - 0xb3, 0xc4, 0xf0, 0x07, 0xec, 0x78, 0x2c, 0xbc, 0xf0, 0x6f, 0x87, 0xf0, - 0x46, 0x97, 0x6d, 0xba, 0xb2, 0xff, 0xfc, 0x63, 0x03, 0x27, 0x71, 0xc6, - 0x52, 0x58, 0x15, 0x97, 0xfa, 0x13, 0xad, 0xa7, 0x5b, 0x2c, 0xbf, 0xf7, - 0x89, 0xe0, 0x0f, 0xd8, 0xf0, 0x59, 0x52, 0x7e, 0x98, 0x6b, 0x7f, 0xfe, - 0xcf, 0xa1, 0xe9, 0xe3, 0xea, 0x77, 0x3c, 0xe1, 0x59, 0x66, 0xf8, 0xa8, - 0x89, 0x90, 0x93, 0xf1, 0x9b, 0x8d, 0x94, 0x32, 0x77, 0x08, 0x2f, 0x8f, - 0x53, 0xbd, 0x65, 0xd9, 0xe5, 0x97, 0xff, 0x47, 0xb9, 0x7f, 0x9d, 0x92, - 0x3f, 0xd6, 0x59, 0xbf, 0x0f, 0xfe, 0x3c, 0x8c, 0x41, 0x6b, 0xff, 0x9d, - 0x9e, 0xc0, 0xbf, 0xa4, 0x48, 0x2c, 0xbc, 0x63, 0x75, 0x97, 0xff, 0x16, - 0x79, 0xf6, 0xce, 0x13, 0xfc, 0xb2, 0xe8, 0xf6, 0xe3, 0x45, 0x17, 0x91, - 0x38, 0x37, 0x52, 0xbb, 0x43, 0x08, 0xca, 0x59, 0x29, 0xc3, 0x90, 0xea, - 0x8f, 0x86, 0xed, 0xff, 0xb9, 0x25, 0xf7, 0xfc, 0xe4, 0xfe, 0xb2, 0xff, - 0xff, 0xff, 0xbc, 0x7d, 0x77, 0xfb, 0x9c, 0xe4, 0xb2, 0x7d, 0x99, 0xb6, - 0x16, 0x7d, 0xde, 0x4f, 0xcb, 0x2f, 0xf4, 0xc0, 0xfa, 0x65, 0x05, 0x97, - 0xff, 0xfe, 0x08, 0x36, 0xcd, 0xf3, 0xf6, 0x0d, 0xe0, 0x23, 0x39, 0x8e, - 0x35, 0x97, 0xc3, 0x7e, 0xb7, 0xc4, 0xdd, 0x82, 0x83, 0xa8, 0x4a, 0x70, - 0xc6, 0xff, 0xe8, 0xbe, 0xe1, 0xb2, 0x30, 0x9a, 0xcd, 0x96, 0x5f, 0x05, - 0xdc, 0x2a, 0x88, 0xb1, 0x7f, 0x1e, 0x9c, 0x83, 0x2b, 0x2b, 0x47, 0xb3, - 0xe2, 0xeb, 0xff, 0x3c, 0x1b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0xba, 0xbf, - 0xff, 0xb0, 0x7e, 0x91, 0x1b, 0xf4, 0x9f, 0xfe, 0x72, 0x7f, 0x49, 0x66, - 0xf2, 0x9b, 0x8e, 0x42, 0x91, 0x84, 0x51, 0xe9, 0x97, 0xff, 0x31, 0xe0, - 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x26, 0x25, 0xfd, 0xe9, 0x2f, 0xa2, 0x75, - 0x97, 0xf9, 0xce, 0x3c, 0xff, 0xc1, 0xac, 0xbe, 0xc0, 0xcb, 0x79, 0x3e, - 0x3f, 0x17, 0x53, 0x74, 0x77, 0x7a, 0x16, 0xb7, 0xed, 0x05, 0xdc, 0x2a, - 0x8a, 0xa5, 0x6c, 0x59, 0x58, 0x78, 0x82, 0x9a, 0x5f, 0xf8, 0x0f, 0xa8, - 0x16, 0x34, 0xff, 0xac, 0xbf, 0xf9, 0xf9, 0xa3, 0x6b, 0xbe, 0x3d, 0x41, - 0x65, 0xff, 0x69, 0xfd, 0xf4, 0x33, 0xad, 0xc6, 0x88, 0x4f, 0x1f, 0xd3, - 0x74, 0x7d, 0x3c, 0x29, 0xaf, 0xfc, 0xf0, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, - 0x13, 0xa2, 0xfb, 0x87, 0xf3, 0x4b, 0x29, 0x87, 0xe1, 0xfa, 0x65, 0xff, - 0x99, 0x22, 0xb7, 0x06, 0xc5, 0x3b, 0x2c, 0xa6, 0xe7, 0xcc, 0x64, 0x77, - 0xf8, 0xdf, 0x53, 0x07, 0xde, 0xb2, 0xfd, 0x14, 0xc5, 0x3c, 0x59, 0x7f, - 0x35, 0x87, 0xbf, 0x09, 0x65, 0x19, 0xeb, 0xf0, 0xa6, 0xf1, 0xfa, 0x56, - 0x37, 0x34, 0x37, 0xff, 0x03, 0x9d, 0x9f, 0x9b, 0x82, 0x23, 0x62, 0xca, - 0x61, 0xfb, 0x78, 0xb6, 0xff, 0xcf, 0x06, 0xf9, 0xa0, 0xbb, 0x85, 0x51, - 0x3b, 0xaf, 0xdd, 0x03, 0xb0, 0xd2, 0x5f, 0xf8, 0xa7, 0xb9, 0xcc, 0xcf, - 0xa0, 0xb2, 0x86, 0x9f, 0x9e, 0x46, 0x42, 0xc2, 0x27, 0x4c, 0xe9, 0x3d, - 0xff, 0x0e, 0x61, 0x2d, 0xda, 0xe7, 0xeb, 0x2f, 0xdc, 0x29, 0xff, 0x8b, - 0x2f, 0x16, 0x0d, 0x65, 0xd2, 0xde, 0x4f, 0x0f, 0xf2, 0x8a, 0x6e, 0x8f, - 0xec, 0x4f, 0x28, 0x40, 0x5f, 0xb8, 0xdc, 0x3d, 0x95, 0x97, 0xff, 0x9b, - 0xb1, 0xe0, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x28, 0x55, 0xff, 0xf3, 0xf6, - 0x18, 0x46, 0x36, 0xfc, 0xda, 0x74, 0xb2, 0xfc, 0xdf, 0x66, 0x3b, 0x4b, - 0x2f, 0xff, 0xfe, 0x03, 0x18, 0xf0, 0x6e, 0x1f, 0x03, 0xb2, 0x59, 0xb0, - 0xa0, 0x98, 0x96, 0x54, 0x11, 0x47, 0xe2, 0xcb, 0xf0, 0x3e, 0xe9, 0x85, - 0x65, 0xfb, 0x3f, 0xc7, 0x25, 0x97, 0xdc, 0x36, 0xba, 0xb2, 0xf4, 0xfe, - 0xdf, 0x63, 0xf2, 0x22, 0x9e, 0x93, 0x51, 0xa3, 0x60, 0xa1, 0x2b, 0x7f, - 0xcc, 0x7e, 0x63, 0x52, 0x43, 0x59, 0x7f, 0xe7, 0x83, 0x7c, 0xd0, 0x5d, - 0xc2, 0xa8, 0xa4, 0x97, 0xfb, 0x3c, 0x58, 0x3f, 0x01, 0x65, 0xe9, 0x21, - 0xac, 0xb3, 0x7c, 0x46, 0x8b, 0x0e, 0x3a, 0x98, 0xd1, 0x95, 0xcc, 0x25, - 0x97, 0xff, 0x45, 0xe7, 0x67, 0x7d, 0x3d, 0x03, 0x16, 0x5f, 0x61, 0x4e, - 0xcb, 0x2b, 0x0f, 0x9f, 0x48, 0xd7, 0xb3, 0x8d, 0xf8, 0x89, 0xe1, 0x1f, - 0x6a, 0x59, 0xa2, 0xfb, 0x47, 0x99, 0x08, 0xce, 0x83, 0x1b, 0x46, 0xa1, - 0xfe, 0xc9, 0x62, 0x8e, 0x6b, 0xf9, 0x51, 0x2d, 0x76, 0x3c, 0xc0, 0x43, - 0xde, 0x3e, 0x1c, 0x77, 0xf9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x44, 0x8b, - 0xda, 0x7d, 0x2c, 0xbf, 0xf3, 0xc1, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4b, - 0xcb, 0x37, 0x33, 0xe8, 0x60, 0xe5, 0xff, 0xd3, 0xbd, 0xe2, 0x3d, 0x7d, - 0x09, 0x89, 0x65, 0xe8, 0xe9, 0x9f, 0xac, 0xbf, 0xfd, 0xe9, 0xe0, 0x83, - 0x9d, 0x45, 0x85, 0xfa, 0xcb, 0xed, 0x03, 0xbc, 0x59, 0x7e, 0xd7, 0xec, - 0x3e, 0x2c, 0xbd, 0x25, 0xf2, 0xcb, 0xf7, 0x44, 0x61, 0x4a, 0xcb, 0x1f, - 0x8f, 0x10, 0x87, 0x2f, 0xff, 0x7b, 0x7c, 0x97, 0xfb, 0xb8, 0x3c, 0xd7, - 0xcb, 0x2f, 0xf1, 0xe9, 0xc6, 0x64, 0x2a, 0xcb, 0xa7, 0xab, 0x2f, 0xf9, - 0xf6, 0xd4, 0xfd, 0x03, 0x62, 0xcb, 0xff, 0xc0, 0x19, 0xe8, 0x48, 0xb7, - 0x00, 0xe5, 0x12, 0xca, 0x82, 0x69, 0x06, 0x4d, 0xe5, 0x0f, 0xcc, 0x88, - 0x5b, 0x87, 0x57, 0xf7, 0xfe, 0x9d, 0x1b, 0x16, 0x5f, 0xfa, 0x2e, 0x4e, - 0xf7, 0xf4, 0xfd, 0x12, 0xca, 0x93, 0xf1, 0x72, 0xeb, 0xe3, 0xdc, 0x11, - 0x8b, 0x2f, 0xd9, 0xb6, 0x71, 0x8b, 0x2f, 0xe7, 0xd9, 0xf8, 0xff, 0xac, - 0xbf, 0xfa, 0x28, 0xc2, 0x14, 0xef, 0x7d, 0xf3, 0x12, 0xca, 0x59, 0x7d, - 0xdc, 0xd4, 0x4b, 0x2f, 0x75, 0xf7, 0xac, 0xb4, 0x30, 0xf0, 0x5c, 0x8e, - 0xfc, 0xfc, 0x9d, 0xf8, 0xb2, 0xa2, 0x3c, 0xed, 0xe4, 0xd5, 0x29, 0x85, - 0xe2, 0x63, 0xc2, 0x6a, 0xed, 0x4a, 0xca, 0x82, 0xe6, 0xe0, 0xd3, 0x0c, - 0x8e, 0x28, 0xed, 0x75, 0x0c, 0x8f, 0x10, 0x11, 0x37, 0x4a, 0x37, 0xc6, - 0x73, 0xb8, 0x67, 0x76, 0x12, 0xcb, 0xfa, 0x37, 0x3d, 0x87, 0xd5, 0x94, - 0x33, 0xc4, 0x31, 0x5b, 0x87, 0xf2, 0xcb, 0xf8, 0x81, 0xd8, 0xa4, 0x55, - 0x97, 0x9b, 0x67, 0xa5, 0x96, 0x37, 0x3c, 0xf0, 0x17, 0xdf, 0xe0, 0x77, - 0xc5, 0x39, 0xa5, 0x97, 0xe2, 0xcd, 0x84, 0xde, 0xb2, 0xfb, 0x36, 0x13, - 0x7a, 0xcb, 0xf0, 0xe7, 0xe1, 0xbc, 0x63, 0xd1, 0x22, 0xbb, 0xff, 0xe3, - 0xd4, 0x6f, 0x49, 0xf7, 0x80, 0x08, 0x25, 0x65, 0xf1, 0xed, 0xfe, 0xea, - 0xcb, 0xce, 0xe1, 0x54, 0x52, 0xea, 0x59, 0x41, 0x36, 0x71, 0x14, 0x5f, - 0x17, 0x67, 0x4b, 0x2f, 0xfb, 0x35, 0xbb, 0x83, 0xcd, 0x7c, 0xb2, 0x8c, - 0xf7, 0x77, 0x90, 0xdf, 0xfa, 0x61, 0xe7, 0x3f, 0xb9, 0x22, 0xac, 0xad, - 0x95, 0x27, 0xc2, 0x10, 0x62, 0xa0, 0x44, 0xa3, 0xa5, 0xaf, 0x3f, 0x91, - 0x1d, 0xe9, 0xd7, 0x56, 0x5f, 0xc3, 0xfa, 0x74, 0x7f, 0x2c, 0xbe, 0xee, - 0xd8, 0xd2, 0xca, 0x81, 0xf7, 0x98, 0xe1, 0x17, 0xdf, 0x8c, 0x5d, 0x67, - 0x16, 0x5f, 0xfa, 0x4a, 0x7d, 0x18, 0x04, 0x00, 0xac, 0xbc, 0x07, 0xe2, - 0xcb, 0xe8, 0x80, 0xf1, 0x2c, 0xbf, 0x4f, 0xef, 0xf7, 0x16, 0x5e, 0x23, - 0xfd, 0x65, 0xec, 0xef, 0x16, 0x54, 0x9b, 0x8e, 0x0e, 0x54, 0x13, 0x28, - 0x19, 0x44, 0x47, 0xfe, 0x1c, 0xfc, 0x93, 0xac, 0x37, 0xed, 0xd7, 0x21, - 0xee, 0x2c, 0xa5, 0x97, 0xee, 0x77, 0x53, 0xc5, 0x96, 0x9f, 0x8d, 0x9f, - 0x42, 0xef, 0xfb, 0xfe, 0x6a, 0x7e, 0x81, 0xb1, 0x65, 0xee, 0xbe, 0xf5, - 0x97, 0xed, 0x81, 0x14, 0xee, 0xac, 0xbe, 0x04, 0x53, 0xba, 0xb2, 0xe7, - 0xda, 0x31, 0xe9, 0xec, 0x5b, 0x52, 0x8d, 0xd7, 0x3b, 0x03, 0x8d, 0xff, - 0x49, 0xff, 0xc9, 0x63, 0xf5, 0x65, 0xff, 0x1f, 0x22, 0x32, 0xec, 0xfe, - 0xb2, 0xff, 0xe2, 0xee, 0xf9, 0xd7, 0x1c, 0xa7, 0x75, 0x65, 0x41, 0x1c, - 0xda, 0x2e, 0x61, 0xc7, 0x0e, 0x6f, 0xf4, 0xee, 0xf2, 0x58, 0xfd, 0x59, - 0x73, 0x92, 0xcb, 0xfe, 0x9f, 0xe3, 0x7a, 0x58, 0xe4, 0xb2, 0xbe, 0x3c, - 0xfd, 0xe2, 0xb5, 0x04, 0x54, 0xea, 0x10, 0x97, 0xf4, 0xb4, 0x7e, 0x06, - 0xea, 0xcb, 0xfb, 0xe8, 0x71, 0xcb, 0xe5, 0x95, 0x2a, 0xee, 0x20, 0xc1, - 0x91, 0xf5, 0x1c, 0x39, 0xb4, 0x50, 0x21, 0x8d, 0xff, 0x8a, 0x28, 0xda, - 0x9f, 0xa0, 0x6c, 0x59, 0x7f, 0xfb, 0x7c, 0x97, 0x7b, 0xec, 0x18, 0x3b, - 0xc5, 0x97, 0x4b, 0x16, 0x54, 0xa2, 0x93, 0x10, 0xb4, 0x99, 0x7f, 0x49, - 0x45, 0xd3, 0x15, 0x65, 0xff, 0x43, 0x35, 0x16, 0x14, 0xb1, 0x65, 0xfb, - 0xf9, 0x0e, 0x31, 0x65, 0xfe, 0xcd, 0xb9, 0x31, 0x03, 0x4b, 0x28, 0xd1, - 0x2b, 0xa3, 0x82, 0x28, 0xba, 0x18, 0xb2, 0xfd, 0xc1, 0x74, 0xed, 0x2c, - 0xbc, 0x52, 0xc5, 0x94, 0x33, 0xc5, 0xd1, 0x55, 0xf7, 0xdb, 0xb9, 0x05, - 0x97, 0xf9, 0xf5, 0x16, 0x77, 0x73, 0x7a, 0xcb, 0xd3, 0xff, 0x16, 0x54, - 0xa6, 0x4e, 0x32, 0xec, 0x5b, 0x72, 0x20, 0x13, 0x34, 0x73, 0x7c, 0xfe, - 0x7d, 0x96, 0x5f, 0xbc, 0x01, 0x70, 0x96, 0x5f, 0xe6, 0x89, 0xfb, 0x07, - 0x1a, 0xcb, 0xf7, 0x44, 0xda, 0x7c, 0xb2, 0xff, 0xa7, 0xdc, 0x7e, 0xce, - 0xb7, 0x16, 0x5f, 0xf9, 0x8e, 0x31, 0x22, 0x84, 0x97, 0xcb, 0x28, 0x67, - 0xf8, 0x67, 0x77, 0xf0, 0x3d, 0x3a, 0xcf, 0xd6, 0x5f, 0x6b, 0xf1, 0x37, - 0xac, 0xbc, 0x6d, 0x75, 0x65, 0x40, 0xfc, 0xbc, 0x5d, 0xc2, 0x7a, 0xd9, - 0x3e, 0x51, 0x91, 0x7c, 0x51, 0x11, 0x9f, 0xa1, 0x4d, 0xd8, 0x49, 0xdf, - 0xe2, 0xee, 0x70, 0xdf, 0xcb, 0x2f, 0xb9, 0x0e, 0x3a, 0xcb, 0xfe, 0xfc, - 0xfd, 0x9a, 0x78, 0xb8, 0xb2, 0xff, 0xfa, 0x05, 0x3b, 0x09, 0x17, 0x3d, - 0x9b, 0x9a, 0xfd, 0x65, 0xe8, 0x0f, 0x7a, 0xca, 0x93, 0xf6, 0x75, 0x7b, - 0xfa, 0x33, 0x33, 0xaf, 0xd5, 0x97, 0xf7, 0x8c, 0x67, 0xa0, 0xac, 0xb0, - 0x56, 0x5b, 0x98, 0x7d, 0xa1, 0x2f, 0x72, 0xdb, 0xdf, 0x45, 0xc5, 0x95, - 0xf1, 0xe9, 0x39, 0x9d, 0xe9, 0x31, 0xac, 0xa9, 0x54, 0x3e, 0x33, 0x1f, - 0x88, 0x99, 0x0b, 0x07, 0x86, 0xff, 0x08, 0xaf, 0xf6, 0x35, 0x03, 0xe0, - 0x18, 0xb2, 0xed, 0xfc, 0x59, 0x7f, 0x03, 0x93, 0x10, 0x34, 0xb2, 0xfd, - 0x9b, 0x67, 0xb8, 0xb2, 0xa4, 0xfc, 0x3c, 0x32, 0x45, 0xf7, 0xb3, 0x5c, - 0x59, 0x41, 0x3c, 0x9d, 0xe5, 0xb7, 0xfd, 0xfc, 0xfe, 0x26, 0xf9, 0x2d, - 0xc5, 0x97, 0xff, 0xed, 0xa7, 0xb3, 0xe3, 0xec, 0x0f, 0x41, 0xf4, 0xac, - 0xa9, 0x4e, 0x9b, 0x21, 0xda, 0x64, 0x8e, 0x81, 0x7f, 0xff, 0xf7, 0xa7, - 0x5f, 0x04, 0xfe, 0x8c, 0x2b, 0xc6, 0xe7, 0x9c, 0x32, 0x43, 0x59, 0x7c, - 0x62, 0xc7, 0xe2, 0xcb, 0xf8, 0x4e, 0xce, 0xc5, 0xf2, 0xcb, 0xc4, 0x6c, - 0x59, 0x5a, 0x3f, 0x0f, 0xc9, 0x7a, 0x61, 0x7f, 0x8a, 0x5c, 0xbb, 0x84, - 0xb2, 0xff, 0x4f, 0xdc, 0xdd, 0x9d, 0x0a, 0xb2, 0xff, 0x79, 0xe2, 0x3d, - 0x4e, 0xcb, 0x2f, 0xec, 0xdc, 0x64, 0xe7, 0x56, 0x5d, 0x8c, 0x59, 0x43, - 0x4f, 0xe3, 0x21, 0xd0, 0xc3, 0x0f, 0x17, 0xb9, 0xc0, 0x0d, 0x37, 0x97, - 0xdf, 0xb7, 0x0d, 0x93, 0xc5, 0x97, 0xe7, 0xdf, 0x9a, 0xe2, 0xca, 0x19, - 0xe9, 0xb0, 0xaa, 0xfd, 0xe7, 0x23, 0x62, 0xcb, 0xe1, 0x62, 0xe4, 0xac, - 0xbf, 0xbf, 0xfb, 0xfc, 0xd6, 0x2c, 0xbf, 0x83, 0x27, 0xd3, 0x82, 0xcb, - 0xf9, 0xff, 0x64, 0xe7, 0x56, 0x54, 0xa2, 0x27, 0x0c, 0x0c, 0xb2, 0xf8, - 0x7e, 0x7f, 0x96, 0x5f, 0xdf, 0xf2, 0x7d, 0x3c, 0x59, 0x52, 0x7a, 0x2e, - 0x47, 0x7e, 0xfb, 0xbc, 0xcd, 0x96, 0x5d, 0x9b, 0x2c, 0xa7, 0x3c, 0x12, - 0x2a, 0xa9, 0x4f, 0xd3, 0x08, 0x8c, 0x9b, 0xd0, 0xad, 0x27, 0xee, 0x30, - 0xdd, 0xf8, 0x56, 0x5b, 0x8b, 0x2e, 0x31, 0x78, 0x6a, 0x40, 0x31, 0x7b, - 0xf7, 0x0a, 0xcb, 0xe6, 0xa2, 0xe4, 0xac, 0xbf, 0xb8, 0x4f, 0xb4, 0xf5, - 0x65, 0xfb, 0x67, 0xe6, 0x41, 0x65, 0xff, 0xf0, 0xf3, 0x7b, 0xea, 0x21, - 0x20, 0xfd, 0x76, 0x2c, 0xa9, 0x3f, 0xb3, 0x28, 0xbf, 0x9f, 0xef, 0xd8, - 0x7c, 0x59, 0x52, 0x99, 0xc7, 0xc3, 0xb1, 0x12, 0x6a, 0x15, 0x1e, 0x20, - 0xbf, 0xdd, 0x36, 0x9e, 0x29, 0x62, 0xcb, 0x37, 0x6c, 0x3a, 0x0a, 0x78, - 0xe5, 0x22, 0x67, 0x3f, 0xf6, 0x87, 0x3c, 0x08, 0x46, 0xcf, 0x92, 0x9d, - 0x7e, 0x86, 0xe1, 0xc6, 0xab, 0x14, 0xe0, 0x96, 0xa3, 0x06, 0x61, 0x77, - 0xa3, 0xea, 0x79, 0x47, 0x7f, 0xca, 0x84, 0x29, 0x41, 0x1c, 0x94, 0x6f, - 0xd9, 0x56, 0x31, 0xf0, 0x82, 0x12, 0x36, 0x5d, 0xc5, 0x1b, 0xf6, 0x82, - 0xee, 0x15, 0x45, 0x38, 0xbf, 0xbc, 0x6c, 0x27, 0x15, 0x65, 0x9b, 0xe1, - 0xf0, 0x99, 0xa5, 0xfd, 0x2c, 0xcd, 0xc9, 0xea, 0xcb, 0xef, 0x49, 0x0a, - 0xb2, 0x98, 0x7a, 0x3d, 0x2f, 0xbe, 0xce, 0x09, 0x05, 0x97, 0xfe, 0x3d, - 0x3f, 0x3d, 0x98, 0x41, 0x59, 0x7e, 0x66, 0xb5, 0x9c, 0x59, 0x7f, 0xfe, - 0xf0, 0x3b, 0xa9, 0xef, 0x30, 0x3c, 0x9d, 0x05, 0x65, 0xff, 0x70, 0xc5, - 0xc8, 0x4f, 0xfc, 0x59, 0x7f, 0xff, 0x77, 0x22, 0x23, 0xe7, 0x64, 0x43, - 0xd4, 0x53, 0xe5, 0x97, 0xf7, 0xd0, 0x96, 0x02, 0x25, 0x97, 0xf3, 0x07, - 0x3c, 0x01, 0x2c, 0xbd, 0xdf, 0x1a, 0xca, 0x93, 0xf9, 0x33, 0x07, 0x2d, - 0xbf, 0x9f, 0xee, 0x46, 0xe0, 0x8b, 0x2f, 0xdd, 0xfa, 0x12, 0xd2, 0xcb, - 0xf9, 0xc4, 0xcd, 0xf3, 0xc5, 0x95, 0x87, 0xb2, 0x02, 0xab, 0xff, 0x1f, - 0xdc, 0x2c, 0x1f, 0x9d, 0xa5, 0x97, 0xff, 0xd9, 0xb6, 0x7d, 0xde, 0x19, - 0x4f, 0xee, 0xc5, 0x94, 0x2a, 0x23, 0xf8, 0x7f, 0x7f, 0xec, 0xf4, 0xeb, - 0x1b, 0xb4, 0xd3, 0x49, 0x2e, 0x36, 0x96, 0x5d, 0xb3, 0x79, 0x57, 0xe7, - 0x02, 0x21, 0x91, 0xe1, 0xe0, 0x4a, 0x3e, 0x56, 0x33, 0xaf, 0x43, 0x57, - 0xf2, 0xc2, 0x84, 0x5f, 0x61, 0x61, 0x1e, 0x49, 0xb8, 0x87, 0x7e, 0xd0, - 0x5d, 0xc2, 0xa8, 0xaf, 0x17, 0xff, 0xf6, 0x0f, 0xd2, 0x23, 0x7e, 0x93, - 0xff, 0xce, 0x4f, 0xe9, 0x2c, 0xdf, 0x11, 0x27, 0x1e, 0x69, 0x7f, 0xf3, - 0x77, 0x83, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x91, 0xd7, 0x63, 0x16, 0x5d, - 0xdc, 0x59, 0x41, 0x35, 0x9f, 0x0b, 0x5e, 0x3f, 0xe5, 0x65, 0xe0, 0xe3, - 0x12, 0x37, 0x2f, 0x2f, 0xb1, 0xfe, 0xfd, 0x65, 0xf0, 0x5d, 0xc2, 0xa8, - 0x92, 0x15, 0xb1, 0xe8, 0xe8, 0x8e, 0xff, 0xfc, 0x7a, 0x3d, 0xc9, 0x2d, - 0xa7, 0xa7, 0xe7, 0xd9, 0x65, 0xf9, 0xe1, 0xd3, 0xd9, 0x65, 0x49, 0xff, - 0x9a, 0xbd, 0xff, 0xe7, 0xec, 0x5c, 0xc1, 0x8c, 0x0f, 0xa8, 0x2c, 0xbf, - 0xfc, 0x1f, 0x1f, 0xdd, 0xcf, 0xbb, 0xad, 0x4a, 0xcb, 0xff, 0xc5, 0x9f, - 0xc7, 0xe6, 0xa7, 0x67, 0xd0, 0x56, 0x5d, 0x06, 0xf2, 0xa8, 0x93, 0x1f, - 0xbd, 0x0a, 0x2f, 0xc8, 0x38, 0x97, 0xd4, 0xcb, 0xf0, 0x3e, 0x09, 0xf1, - 0x65, 0xfb, 0x3c, 0xdf, 0x18, 0xb2, 0xcd, 0xe5, 0x58, 0xfb, 0xca, 0x62, - 0xfd, 0xaf, 0xa5, 0x37, 0xff, 0x37, 0x78, 0x37, 0xcd, 0x05, 0xdc, 0x2a, - 0x89, 0x29, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xbc, 0x57, 0xfd, 0x06, 0xf9, - 0xa0, 0xbb, 0x85, 0x51, 0x26, 0xac, 0xdf, 0x0f, 0xed, 0xcd, 0x2f, 0x83, - 0xe3, 0x25, 0x97, 0xee, 0x73, 0x0b, 0x16, 0x5f, 0xf6, 0x83, 0x3b, 0x66, - 0x17, 0x56, 0x5f, 0xd3, 0xc3, 0x64, 0xc4, 0xb2, 0xf9, 0xcf, 0xee, 0x2c, - 0xb7, 0x96, 0x56, 0x22, 0xf8, 0xc9, 0x98, 0x71, 0xe2, 0xd6, 0x88, 0xaf, - 0xf1, 0xff, 0x85, 0x3f, 0x71, 0x65, 0xed, 0xf3, 0x05, 0x97, 0xe9, 0xef, - 0x33, 0xab, 0x2f, 0x39, 0x0f, 0x0f, 0x17, 0xc3, 0xd7, 0xfc, 0xfa, 0x87, - 0x30, 0x1a, 0xd9, 0x65, 0x19, 0xf5, 0x80, 0xc6, 0xff, 0xb3, 0xf1, 0xfa, - 0x4a, 0x5a, 0x59, 0x7d, 0xff, 0x30, 0x96, 0x54, 0x9e, 0xd9, 0x9c, 0xdf, - 0xf8, 0x01, 0xe7, 0x1f, 0x5d, 0x91, 0x56, 0x5f, 0xbd, 0xde, 0x67, 0x56, - 0x51, 0x9f, 0x43, 0xa0, 0x5f, 0xff, 0xbc, 0xe7, 0xff, 0x30, 0x57, 0x66, - 0xa7, 0x09, 0x65, 0xc6, 0x35, 0x95, 0x27, 0xd5, 0xe5, 0x4b, 0xe8, 0x02, - 0x7e, 0x59, 0x7f, 0xdb, 0x1f, 0x38, 0xff, 0xf8, 0x0b, 0x2f, 0xe3, 0xd0, - 0xca, 0x7e, 0x59, 0x7f, 0xfc, 0x07, 0xfb, 0x30, 0x51, 0x5e, 0x19, 0xf7, - 0x56, 0x5f, 0xde, 0xcc, 0xef, 0xb1, 0x65, 0xff, 0x4c, 0x39, 0xad, 0x39, - 0x75, 0x65, 0x41, 0x53, 0xa0, 0xe1, 0x10, 0x18, 0x45, 0x44, 0x43, 0xa2, - 0x3f, 0x1d, 0xf0, 0xb7, 0xaa, 0x5b, 0xcb, 0x2f, 0xec, 0xef, 0x64, 0xb6, - 0x59, 0x7f, 0xce, 0x3d, 0xf8, 0x36, 0xd7, 0x2d, 0xd5, 0x95, 0xe3, 0xf1, - 0x01, 0x6d, 0xe7, 0x20, 0xac, 0xbd, 0xe9, 0x11, 0x65, 0x74, 0xdc, 0x34, - 0x37, 0x7f, 0x13, 0xfe, 0x4e, 0xc5, 0x97, 0xf4, 0x8f, 0xdb, 0xa6, 0x4b, - 0x2f, 0xfa, 0x59, 0xec, 0x18, 0xcf, 0xe5, 0x95, 0x87, 0xd0, 0x65, 0xf7, - 0xdb, 0x87, 0x22, 0xac, 0xbd, 0x0f, 0x85, 0x59, 0x7f, 0x33, 0xbe, 0x07, - 0x78, 0xb2, 0xf0, 0x07, 0xf2, 0xcb, 0xff, 0x9f, 0xf0, 0xf8, 0xf4, 0x47, - 0xde, 0x2c, 0xbf, 0xdb, 0xf2, 0x1e, 0x92, 0x15, 0x65, 0xc5, 0xb2, 0xca, - 0x94, 0xc8, 0x46, 0x4b, 0x83, 0xe6, 0x5e, 0xe3, 0xdf, 0xa2, 0x08, 0x6b, - 0x6e, 0xac, 0xbb, 0x58, 0xb2, 0xf7, 0xe2, 0x6c, 0xb2, 0xb4, 0x78, 0xff, - 0x88, 0x90, 0xb5, 0xd8, 0xc5, 0x97, 0xb5, 0xbf, 0xcb, 0x2f, 0xd9, 0xa7, - 0x91, 0xac, 0xa9, 0x3d, 0xb7, 0x16, 0x21, 0xfb, 0xff, 0x68, 0x0c, 0xf1, - 0xcb, 0x1c, 0x96, 0x5f, 0xf7, 0x25, 0x85, 0x90, 0x92, 0x59, 0x5a, 0x3f, - 0x3e, 0x9e, 0xdc, 0x50, 0x59, 0x74, 0x76, 0xd2, 0xca, 0x01, 0xb3, 0x8f, - 0x16, 0xac, 0x3f, 0x9d, 0xc5, 0x5b, 0xff, 0x82, 0x07, 0x1e, 0xfc, 0x1b, - 0x6b, 0x96, 0xea, 0xcb, 0xe3, 0xf4, 0xef, 0x59, 0x5a, 0x3f, 0x3e, 0xa8, - 0x5f, 0xe9, 0xd4, 0xfd, 0xfe, 0xa5, 0x65, 0xc5, 0xb2, 0xcb, 0xe9, 0xf0, - 0x37, 0x56, 0x5e, 0xec, 0xe9, 0x65, 0x4a, 0x2b, 0xb6, 0x22, 0x88, 0xd1, - 0xc5, 0xc0, 0x4b, 0x73, 0x85, 0x65, 0xfc, 0x11, 0x83, 0x61, 0x18, 0xb2, - 0xfe, 0xfb, 0x98, 0xc7, 0xe2, 0xcb, 0xe1, 0xe7, 0xa5, 0x65, 0xfd, 0x9b, - 0x6a, 0x7e, 0xd2, 0xcb, 0xfc, 0x01, 0xe4, 0x27, 0xfe, 0x2c, 0xa9, 0x47, - 0xce, 0x0b, 0x19, 0x8b, 0x97, 0x11, 0x0f, 0x0b, 0xef, 0xd1, 0x73, 0x86, - 0xc5, 0x97, 0xb8, 0x0f, 0x96, 0x5f, 0xff, 0x6c, 0xd3, 0xc3, 0x9c, 0xcd, - 0x35, 0x3a, 0x15, 0x65, 0x89, 0x65, 0xfc, 0xfd, 0xe6, 0xd8, 0xd2, 0xcb, - 0xff, 0xf8, 0xf8, 0xf0, 0xe1, 0xef, 0xe0, 0x1c, 0xe0, 0x7b, 0xd6, 0x5c, - 0x20, 0x8b, 0x2b, 0x13, 0x50, 0x88, 0xa5, 0xc7, 0xbf, 0x54, 0x21, 0x0e, - 0x97, 0x88, 0xbb, 0x74, 0x7b, 0x76, 0xcb, 0x3c, 0x96, 0x61, 0xbf, 0x04, - 0x91, 0xc3, 0x8f, 0x25, 0x78, 0x06, 0x1f, 0xdf, 0x2d, 0x44, 0x45, 0xa8, - 0x4a, 0x7a, 0x34, 0x17, 0x85, 0x57, 0xf0, 0x8a, 0x28, 0xc6, 0x39, 0x1a, - 0x2f, 0x63, 0x43, 0x12, 0x3a, 0x2b, 0xfc, 0x0d, 0x9b, 0xee, 0x6e, 0x18, - 0xab, 0x2a, 0x5d, 0xf7, 0x7e, 0xd0, 0xa9, 0x85, 0xa0, 0xd0, 0x1c, 0x7f, - 0x27, 0x38, 0xd9, 0x14, 0x6d, 0x2c, 0x2d, 0xf4, 0xb9, 0xfe, 0xa6, 0x82, - 0x19, 0xe2, 0x53, 0xd3, 0x77, 0x23, 0x35, 0xa6, 0xd6, 0xa4, 0x2e, 0x8d, - 0xcd, 0xa6, 0x6b, 0xda, 0x72, 0x62, 0x14, 0xa6, 0x81, 0xd2, 0x26, 0xf3, - 0x33, 0x0f, 0x21, 0x69, 0x49, 0x61, 0xc4, 0xdc, 0xcf, 0xd6, 0x9f, 0xf8, - 0xeb, 0x3b, 0x8d, 0xd9, 0x4a, 0x11, 0x5f, 0x20, 0x3e, 0xaf, 0x72, 0x2d, - 0x9a, 0xb2, 0x0d, 0x3d, 0x5c, 0x0c, 0x3d, 0xf7, 0xc0, 0xff, 0x4e, 0xf8, - 0x6d, 0xcb, 0xee, 0x2b, 0x57, 0xff, 0xcb, 0x67, 0x7b, 0xdc, 0x59, 0x4a, - 0x82, 0xf8, 0x6d, 0xf7, 0xd6, 0x0a, 0x2d, 0x53, 0x2f, 0xa3, 0xe7, 0x8d, - 0x04, 0xa7, 0x79, 0xee, 0x5b, 0xfc, 0x8a, + 0xd2, 0xca, 0x15, 0x50, 0x69, 0xc6, 0xd5, 0xa5, 0x1e, 0x97, 0x5f, 0xff, + 0xdf, 0x4b, 0x3a, 0xd9, 0x9e, 0xf0, 0x60, 0xe9, 0xf6, 0x34, 0xb2, 0xfe, + 0xe9, 0xea, 0x00, 0xdb, 0x59, 0x76, 0x9f, 0xc8, 0x96, 0x11, 0x96, 0xff, + 0x67, 0xb2, 0x77, 0xb3, 0x65, 0x94, 0xe7, 0xc8, 0x45, 0xf7, 0xff, 0xc5, + 0x9d, 0x3c, 0x2f, 0x67, 0x7c, 0x61, 0x59, 0x7f, 0xdd, 0x36, 0x73, 0xd0, + 0x21, 0x2c, 0xbf, 0xf7, 0xf0, 0x41, 0x7e, 0xbf, 0xdf, 0xce, 0x44, 0x2f, + 0xc9, 0x97, 0xf0, 0x1b, 0x67, 0xad, 0x62, 0xcb, 0xfd, 0xec, 0x15, 0xe0, + 0xe7, 0xd6, 0x5f, 0xec, 0xe6, 0x6e, 0xfa, 0x06, 0xb2, 0xe8, 0xd9, 0x65, + 0xef, 0x44, 0xcb, 0x2f, 0xf4, 0x19, 0x66, 0xc2, 0x49, 0x65, 0xbe, 0x19, + 0xf3, 0xe0, 0xbf, 0x87, 0x6d, 0xc0, 0xa6, 0x0d, 0xe3, 0x50, 0x42, 0x72, + 0xfb, 0xa2, 0xe7, 0x96, 0x5d, 0xa9, 0x2c, 0xbf, 0xf1, 0x67, 0x7a, 0x0d, + 0xbb, 0x81, 0x59, 0x7e, 0x18, 0x1f, 0x52, 0x59, 0x6e, 0x2c, 0xb3, 0x4b, + 0x2e, 0x3f, 0xd6, 0x5d, 0xe3, 0x59, 0x6e, 0x4e, 0x35, 0xb3, 0x0b, 0xd1, + 0x9f, 0x48, 0x0f, 0xe9, 0xd1, 0x37, 0xfb, 0xd5, 0xdf, 0xc2, 0xcb, 0xbf, + 0x85, 0x97, 0xe0, 0x4b, 0x09, 0xb9, 0x9a, 0xef, 0xc5, 0xea, 0x13, 0xcc, + 0x19, 0x18, 0xa2, 0xe6, 0x7f, 0xfc, 0x30, 0xc9, 0x46, 0xff, 0xf1, 0x3f, + 0xdc, 0x14, 0x57, 0x9c, 0x16, 0x7e, 0xb2, 0xfa, 0x35, 0x1e, 0x59, 0x58, + 0x7e, 0x2e, 0xa1, 0x7f, 0xd8, 0x7f, 0x77, 0x51, 0x9f, 0x2c, 0xbf, 0xfb, + 0xbe, 0xc6, 0x09, 0x1f, 0xe7, 0xdd, 0x59, 0x7f, 0xbe, 0x09, 0xe8, 0x48, + 0x1a, 0xcb, 0xef, 0xd8, 0xf2, 0x9c, 0x8d, 0x52, 0x20, 0xe1, 0xcf, 0x51, + 0xee, 0x07, 0x56, 0x5f, 0xdf, 0xb0, 0x4e, 0x09, 0xb2, 0xcb, 0xfb, 0x86, + 0x45, 0x1f, 0x2c, 0xbb, 0x3e, 0x59, 0x5f, 0x1f, 0xb7, 0x4c, 0xe7, 0xcb, + 0x2f, 0xff, 0x09, 0x0d, 0x44, 0xd2, 0x72, 0xcf, 0xba, 0xb2, 0xfb, 0xfd, + 0x1e, 0x96, 0x54, 0x27, 0x0b, 0x25, 0x67, 0x84, 0x6f, 0x0c, 0xda, 0x4c, + 0xbf, 0xb9, 0xc8, 0x94, 0x69, 0x65, 0xff, 0x6a, 0x18, 0x73, 0x43, 0xee, + 0xac, 0xac, 0x3e, 0x80, 0x96, 0xde, 0x07, 0xdd, 0x59, 0x67, 0x59, 0x74, + 0xdc, 0x59, 0x44, 0x7c, 0xfc, 0x21, 0xe8, 0xf0, 0x82, 0x17, 0x63, 0x4b, + 0x2f, 0xfa, 0x3e, 0xcf, 0xe3, 0x7e, 0x6c, 0xb2, 0xfe, 0x2c, 0xef, 0x1f, + 0xf5, 0x95, 0xa4, 0x40, 0x7e, 0x2f, 0xc3, 0xcb, 0xfc, 0x5b, 0x1e, 0xbe, + 0xf6, 0xcb, 0x2f, 0xfa, 0x36, 0xe6, 0xb0, 0x31, 0xb2, 0xcb, 0xfd, 0x85, + 0xb6, 0x0d, 0xfc, 0xb2, 0xa6, 0x3e, 0xc6, 0x8e, 0xaf, 0xec, 0x66, 0x0c, + 0xda, 0x59, 0x73, 0x21, 0x65, 0x30, 0xf0, 0xdc, 0xb6, 0xfe, 0x2c, 0x9b, + 0xc7, 0xd5, 0x97, 0xd9, 0x9e, 0xe2, 0xca, 0x84, 0xde, 0x32, 0x14, 0x4e, + 0xcf, 0xd2, 0x1d, 0xc2, 0xdb, 0xcd, 0x34, 0xd2, 0x4b, 0xf6, 0x0a, 0x40, + 0xe2, 0x46, 0xe6, 0x82, 0xf9, 0xbb, 0x4d, 0x34, 0xb2, 0xec, 0xfd, 0x65, + 0x61, 0xbf, 0xe9, 0x45, 0x42, 0x26, 0x7a, 0xf3, 0x7f, 0xfc, 0x72, 0x7e, + 0x31, 0x8f, 0x27, 0x17, 0xc6, 0xb2, 0xf8, 0xf5, 0xff, 0x16, 0x5f, 0xfd, + 0x00, 0x6d, 0xe4, 0xf9, 0x63, 0x4f, 0xfa, 0xcb, 0xfe, 0xe6, 0xa0, 0x1d, + 0x23, 0xde, 0xb2, 0xff, 0xc5, 0x9c, 0x0f, 0x8c, 0x30, 0x4b, 0x2f, 0xff, + 0xf0, 0xad, 0x3e, 0x85, 0x6f, 0xcc, 0x91, 0x01, 0xf9, 0x84, 0xb2, 0xa1, + 0x1b, 0x58, 0x74, 0x67, 0x97, 0xf6, 0x80, 0x1f, 0x1f, 0x16, 0x5f, 0x34, + 0xe5, 0x25, 0x97, 0xfd, 0xd8, 0xfb, 0xf7, 0xf7, 0x8d, 0x65, 0xbc, 0xb2, + 0xb0, 0xf2, 0xc2, 0x75, 0x7d, 0xc3, 0x64, 0x2c, 0xbf, 0x16, 0x76, 0x34, + 0xb2, 0xf3, 0x4d, 0x34, 0x92, 0xfc, 0xe2, 0xf8, 0xfa, 0x91, 0xb9, 0xa0, + 0xa8, 0x44, 0x01, 0xa3, 0x5e, 0xdf, 0x0c, 0x59, 0x7b, 0xa6, 0xd2, 0xcb, + 0xdc, 0x7d, 0x2c, 0xaf, 0x8d, 0xcf, 0x47, 0x6f, 0x7a, 0x06, 0xb2, 0xb1, + 0x12, 0xa6, 0xae, 0xe4, 0x57, 0xdf, 0xe6, 0x4c, 0xb2, 0xfb, 0x98, 0x5d, + 0x59, 0x58, 0x78, 0x8e, 0x47, 0x6e, 0x4e, 0x54, 0x13, 0xf1, 0x0e, 0xa1, + 0x43, 0xe8, 0x5b, 0x13, 0x8d, 0xe9, 0xfc, 0xf9, 0x65, 0xa4, 0x35, 0x5e, + 0x99, 0x29, 0xd8, 0xd7, 0xaa, 0x7b, 0x56, 0xbe, 0x09, 0x64, 0x57, 0xe6, + 0xdf, 0xa3, 0x60, 0x2c, 0xbf, 0xf8, 0xfb, 0x1a, 0xc2, 0xeb, 0xb9, 0x2c, + 0xa9, 0x97, 0x5c, 0xb4, 0x47, 0xe8, 0xc9, 0x4a, 0x5c, 0x60, 0x0c, 0x77, + 0x96, 0xdf, 0xfd, 0x18, 0x58, 0x37, 0x91, 0x46, 0xcb, 0x2f, 0x83, 0xa8, + 0x92, 0xcb, 0xb7, 0xc2, 0xcb, 0xff, 0x1e, 0x83, 0xe3, 0x0f, 0x8c, 0x96, + 0x5f, 0xff, 0xfe, 0x72, 0xff, 0xbe, 0xc6, 0x77, 0x99, 0xbf, 0xc6, 0x1f, + 0x18, 0x80, 0xd9, 0x65, 0x6c, 0x8b, 0x9f, 0xcf, 0x6a, 0x11, 0xed, 0x90, + 0xd1, 0xac, 0x5f, 0xa9, 0x39, 0xdc, 0x1d, 0x34, 0xba, 0x09, 0x46, 0x31, + 0x78, 0xa3, 0x7a, 0xcb, 0xff, 0xb5, 0x01, 0xf1, 0x3f, 0x4a, 0x1a, 0x59, + 0x73, 0x6c, 0xd6, 0x5c, 0x7e, 0x59, 0x5b, 0x1b, 0x0d, 0x0d, 0x54, 0x22, + 0x55, 0xdc, 0xef, 0x4c, 0xfb, 0x2c, 0xbd, 0xc0, 0x6e, 0xac, 0xac, 0x37, + 0xdd, 0x1e, 0xbf, 0xd0, 0x52, 0x23, 0x8f, 0x96, 0x54, 0x37, 0xe6, 0x72, + 0x8e, 0x58, 0x70, 0xd1, 0xc9, 0x77, 0xe2, 0xca, 0x58, 0x0c, 0x67, 0xff, + 0x42, 0x94, 0xd6, 0xe6, 0x8c, 0xdf, 0x52, 0x82, 0x3d, 0x2c, 0x9d, 0xe3, + 0x1e, 0xfe, 0x17, 0x85, 0x1d, 0x4f, 0x23, 0x09, 0xec, 0xfe, 0x5e, 0xf5, + 0x79, 0xf8, 0x59, 0x08, 0xbf, 0xb8, 0x41, 0x7e, 0x39, 0xa4, 0x64, 0xb2, + 0xe9, 0xe8, 0xd6, 0x5f, 0xf4, 0xdd, 0xdc, 0xe4, 0x4c, 0x0d, 0x2c, 0xbf, + 0xc3, 0x82, 0x64, 0x0e, 0x16, 0x54, 0x1f, 0x83, 0x9f, 0xdf, 0xf7, 0xa3, + 0x6d, 0x44, 0x9f, 0x4b, 0x2f, 0xc4, 0x28, 0x91, 0xfa, 0xcb, 0xff, 0x13, + 0xfd, 0xdf, 0x40, 0x72, 0x65, 0x95, 0x87, 0xd2, 0x45, 0x57, 0xf6, 0xd0, + 0x46, 0xcc, 0x59, 0x7f, 0xe3, 0xf4, 0x0f, 0xc0, 0x8f, 0xb8, 0xb2, 0xf1, + 0x16, 0x2c, 0xac, 0x3d, 0x97, 0x3f, 0xbc, 0x31, 0x7f, 0x59, 0x7e, 0x66, + 0xb4, 0xfd, 0x59, 0x53, 0x8f, 0x1e, 0x07, 0xef, 0xfe, 0x99, 0xc3, 0xe8, + 0x97, 0x4b, 0x18, 0xb2, 0xfe, 0xd6, 0x11, 0x3e, 0xcb, 0x28, 0x27, 0xe0, + 0x68, 0x97, 0xff, 0x43, 0x5f, 0x4b, 0x93, 0xb3, 0xd8, 0x4b, 0x2f, 0xff, + 0x33, 0xe9, 0x72, 0x76, 0x11, 0x61, 0xfe, 0xb2, 0xfe, 0x7d, 0x02, 0x3e, + 0x92, 0xca, 0x33, 0xfa, 0x74, 0xcb, 0xf9, 0x8f, 0x83, 0x3d, 0xeb, 0x2f, + 0x1f, 0x80, 0xb2, 0xa7, 0x2e, 0x2f, 0xb6, 0x09, 0xe5, 0x08, 0xb1, 0x90, + 0x64, 0x28, 0x8c, 0x83, 0x50, 0x85, 0xf3, 0x3b, 0xc2, 0x4c, 0x88, 0x7b, + 0x0c, 0x10, 0x10, 0x6f, 0x2e, 0xb3, 0x76, 0xc4, 0xac, 0x04, 0xe9, 0xea, + 0x1e, 0xed, 0x98, 0xe2, 0xa7, 0x98, 0xff, 0x67, 0xb8, 0x7b, 0xb6, 0xac, + 0x7a, 0x33, 0xc6, 0x38, 0xf6, 0xd7, 0x0d, 0x56, 0xd2, 0x10, 0xf3, 0xc1, + 0xf6, 0x2b, 0xdc, 0xed, 0xa9, 0x09, 0xf2, 0xac, 0x12, 0x47, 0x59, 0xd0, + 0xe5, 0xee, 0x9a, 0x8b, 0x3b, 0xe4, 0x1a, 0xdb, 0xf3, 0xea, 0xc7, 0x1c, + 0xeb, 0x2b, 0xbd, 0xd9, 0x4c, 0x13, 0x57, 0x3a, 0xfa, 0xae, 0x12, 0x59, + 0x4f, 0xd6, 0xf5, 0xaf, 0x4b, 0x7b, 0x59, 0x53, 0xfd, 0x63, 0x5e, 0xdb, + 0x8d, 0x6c, 0xad, 0x63, 0x4f, 0x2f, 0x9b, 0xfb, 0xb6, 0xf9, 0x40, 0x14, + 0x84, 0xad, 0xf3, 0x8c, 0x8d, 0x4e, 0x72, 0xcf, 0xd3, 0x49, 0x04, 0xb4, + 0x51, 0x7b, 0x93, 0xba, 0xb4, 0xde, 0x56, 0x23, 0xfc, 0xb5, 0x60, 0x92, + 0x5e, 0x66, 0x75, 0x65, 0xfe, 0x76, 0x47, 0x4f, 0x86, 0xb2, 0xfd, 0x9a, + 0x13, 0xbc, 0x59, 0x66, 0xff, 0x9f, 0xb1, 0x0e, 0x70, 0xc6, 0xff, 0x37, + 0xcd, 0x05, 0xdc, 0x2a, 0x8b, 0x8d, 0x7f, 0x9b, 0xe6, 0x82, 0xee, 0x15, + 0x45, 0xd6, 0xbf, 0xf9, 0xbb, 0xc9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4a, + 0x2a, 0x89, 0x59, 0xaf, 0x92, 0x85, 0x40, 0xca, 0x85, 0x86, 0x28, 0x4a, + 0x4e, 0x70, 0xb7, 0x76, 0x1e, 0xaf, 0xab, 0x13, 0x67, 0xf8, 0x5a, 0x70, + 0xdf, 0xa8, 0x3b, 0x88, 0x37, 0xff, 0x9b, 0xb1, 0xe4, 0xdf, 0x34, 0x17, + 0x70, 0xaa, 0x25, 0xa5, 0xff, 0x4f, 0x0f, 0x1e, 0xeb, 0xc7, 0xdb, 0x8b, + 0x2f, 0xff, 0xfe, 0x6b, 0x73, 0xb3, 0xd8, 0xd9, 0x3d, 0x91, 0xb6, 0xbe, + 0xb6, 0xf5, 0x3c, 0xce, 0x9d, 0xe9, 0xf5, 0x97, 0x9d, 0xc2, 0xa8, 0x8d, + 0xd7, 0xef, 0xa6, 0x28, 0x1a, 0xcb, 0x68, 0x27, 0xa2, 0xe5, 0x17, 0xfb, + 0x46, 0xdb, 0x0f, 0xa1, 0xa5, 0x97, 0x81, 0x3e, 0xd2, 0xcb, 0xed, 0x02, + 0x3f, 0x59, 0x46, 0x7f, 0x93, 0x1c, 0x08, 0x43, 0x7f, 0xff, 0xfd, 0xbc, + 0xb3, 0x9c, 0x1c, 0x66, 0x82, 0xee, 0x16, 0xf2, 0x3e, 0xc7, 0x78, 0xa8, + 0xc3, 0xd6, 0xf4, 0x22, 0xf0, 0xcc, 0x2f, 0xee, 0x37, 0x6b, 0x9b, 0xa2, + 0x2c, 0xbf, 0x6e, 0x37, 0xd3, 0x6d, 0xa5, 0x97, 0xc3, 0x2c, 0xfd, 0x65, + 0xf4, 0x34, 0xc1, 0xac, 0xac, 0x3c, 0x5d, 0x11, 0x5f, 0xf9, 0xe4, 0xdf, + 0x34, 0x17, 0x70, 0xaa, 0x25, 0xf5, 0xb1, 0x65, 0xef, 0x3f, 0x56, 0x5b, + 0xbc, 0x35, 0x82, 0x08, 0x5f, 0xc2, 0x1f, 0xdb, 0x9a, 0x69, 0x65, 0x41, + 0xee, 0x19, 0x45, 0xfd, 0x07, 0xbb, 0x9f, 0x49, 0x65, 0xfb, 0x42, 0xf8, + 0x1c, 0x59, 0x5e, 0x3d, 0xa1, 0x0c, 0x2f, 0x46, 0xa1, 0x65, 0x61, 0xbe, + 0x32, 0x3b, 0xd2, 0x7f, 0x2c, 0xbd, 0xfc, 0x34, 0xb2, 0xff, 0x61, 0xea, + 0x5e, 0x36, 0x96, 0x56, 0xe9, 0xe8, 0xf8, 0x7a, 0xf8, 0x1e, 0x3d, 0x2c, + 0xbe, 0xe7, 0xa3, 0xcb, 0x2f, 0xf4, 0x6d, 0x05, 0xb6, 0x6e, 0x2c, 0xbb, + 0x9a, 0x59, 0x50, 0x7d, 0xfb, 0x11, 0x4c, 0x6d, 0x66, 0xed, 0xad, 0x7a, + 0x0a, 0x11, 0x72, 0x1d, 0x41, 0x8d, 0x7f, 0xe2, 0x7d, 0xd3, 0x6d, 0x3c, + 0x30, 0x87, 0xd0, 0xd4, 0x28, 0x5c, 0x70, 0x7f, 0xad, 0xb3, 0xe4, 0xa2, + 0x42, 0x46, 0xff, 0xfc, 0x50, 0x16, 0xfd, 0xf8, 0x27, 0xc2, 0xcd, 0xee, + 0xb2, 0xff, 0xe9, 0x74, 0xe6, 0x6e, 0x33, 0xdf, 0x03, 0x59, 0x7f, 0x9b, + 0xfe, 0xff, 0x7e, 0x7f, 0x2c, 0xa1, 0xa3, 0x6b, 0xe5, 0x82, 0x49, 0xbf, + 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0xab, 0x5d, 0x3e, 0xd8, 0x59, 0x71, + 0xee, 0x2c, 0xbb, 0x34, 0xb2, 0xf6, 0x7d, 0xd5, 0x97, 0xc6, 0xc7, 0xde, + 0xb2, 0x85, 0x3e, 0xf7, 0x19, 0xfc, 0x5b, 0xa3, 0xb7, 0x8b, 0x71, 0xa5, + 0x97, 0xf7, 0xa3, 0x78, 0x07, 0x8b, 0x2f, 0xe7, 0xf4, 0xc5, 0x03, 0x59, + 0x7f, 0xf7, 0x7d, 0x1c, 0x27, 0x03, 0x6c, 0xf4, 0xb2, 0xf4, 0x4b, 0x16, + 0x5f, 0xc6, 0x00, 0x94, 0x4c, 0xb2, 0xa4, 0x8e, 0xb1, 0x97, 0xfc, 0x5b, + 0x32, 0x3f, 0x86, 0xef, 0xf8, 0x5c, 0x2c, 0xfb, 0xae, 0xc5, 0x97, 0xb7, + 0x23, 0xab, 0x2f, 0xe8, 0x60, 0x90, 0x41, 0x59, 0x74, 0x69, 0x65, 0x68, + 0xf0, 0xf8, 0x5b, 0x7b, 0xe7, 0xf9, 0x65, 0xee, 0xbf, 0xcb, 0x2f, 0xff, + 0xa5, 0xe7, 0x90, 0xaf, 0xcc, 0x07, 0x38, 0x6b, 0x29, 0x65, 0x61, 0xed, + 0xee, 0x28, 0x53, 0xa2, 0x70, 0x9e, 0x2e, 0x19, 0xac, 0xbf, 0xb3, 0x41, + 0x77, 0x0a, 0xa2, 0x40, 0x54, 0xe3, 0xcf, 0x6e, 0x2d, 0x7e, 0xe3, 0x93, + 0xfc, 0xb2, 0xec, 0x25, 0x97, 0x02, 0x16, 0x54, 0x8f, 0xe3, 0xe2, 0x6d, + 0xd2, 0x7d, 0x0a, 0xdf, 0xd1, 0xad, 0xa3, 0x5b, 0x2c, 0xbe, 0x36, 0x47, + 0x16, 0x54, 0x8f, 0x40, 0x52, 0xfb, 0xfd, 0xad, 0x99, 0x05, 0x2e, 0x2c, + 0xa8, 0x3d, 0x7c, 0x23, 0xbd, 0xb3, 0x8d, 0x65, 0xfe, 0x09, 0xe3, 0x40, + 0x2e, 0xac, 0xac, 0x3d, 0x0f, 0x87, 0x6f, 0x73, 0x92, 0x59, 0x73, 0x67, + 0xcb, 0x2f, 0xff, 0x64, 0xde, 0x76, 0x77, 0xd1, 0xd0, 0x31, 0x65, 0xed, + 0xc8, 0xea, 0xca, 0x85, 0xe9, 0xbd, 0xa1, 0x1a, 0x33, 0xdc, 0x8c, 0x2c, + 0x54, 0xcd, 0xd3, 0x99, 0x98, 0x98, 0x45, 0xe8, 0x5d, 0x3c, 0x3a, 0x0a, + 0x1c, 0xfd, 0x70, 0x01, 0x14, 0xf8, 0xe8, 0x83, 0x7b, 0x89, 0x77, 0xfe, + 0x94, 0x67, 0xfe, 0xcc, 0x20, 0xac, 0xbf, 0x41, 0x16, 0x75, 0x65, 0xf6, + 0xb4, 0x61, 0x59, 0x7c, 0x59, 0xb3, 0x7c, 0x44, 0x24, 0xf9, 0xe8, 0x84, + 0xb7, 0xf9, 0xbf, 0x9d, 0x99, 0x03, 0x59, 0x4d, 0xd5, 0x01, 0xea, 0x38, + 0x39, 0xf4, 0xcb, 0xfc, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x2c, 0x85, 0xfc, + 0x7b, 0xdb, 0xf2, 0x3e, 0x59, 0x7f, 0xff, 0xe8, 0x90, 0x07, 0x8d, 0xc4, + 0xcf, 0x10, 0x1f, 0x77, 0x05, 0x8e, 0x2c, 0xb1, 0xf1, 0x14, 0x7d, 0x32, + 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0xd7, 0x5f, 0xfe, 0xc1, 0x23, + 0x66, 0xf9, 0x37, 0x47, 0x04, 0xb2, 0xff, 0x37, 0xcd, 0x05, 0xdc, 0x2a, + 0x8b, 0x91, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xbb, 0x17, 0xfe, 0x79, 0x37, + 0xcd, 0x05, 0xdc, 0x2a, 0x8a, 0x39, 0x66, 0xf8, 0x88, 0x06, 0x1a, 0x5f, + 0x14, 0x49, 0x8b, 0x2f, 0xfd, 0x3d, 0xb6, 0x59, 0x1a, 0x96, 0x1e, 0xf5, + 0x96, 0x92, 0xcb, 0xf6, 0x82, 0xee, 0x15, 0x45, 0x2a, 0xbf, 0xe0, 0xf8, + 0xe6, 0xf4, 0x6d, 0x8b, 0x2f, 0xff, 0x7a, 0x36, 0x8f, 0x1e, 0xf7, 0x1f, + 0x8d, 0x63, 0x73, 0x75, 0x74, 0x9b, 0xe2, 0x32, 0xe6, 0x6e, 0xaf, 0x93, + 0x0d, 0x78, 0x6e, 0x5f, 0xf9, 0xcf, 0xb2, 0x00, 0xdc, 0xbf, 0x59, 0x77, + 0x80, 0xb2, 0xcd, 0xc6, 0x9f, 0xaf, 0xc4, 0x2c, 0x8d, 0x5b, 0xc5, 0x3c, + 0x3e, 0xbf, 0xff, 0xf9, 0xfb, 0x9f, 0xc4, 0x9b, 0x88, 0xcc, 0x1c, 0x4a, + 0x06, 0xee, 0x2a, 0xca, 0xd9, 0xb7, 0x39, 0x94, 0xf6, 0xe6, 0x47, 0x93, + 0xf1, 0x81, 0xd2, 0x37, 0xd8, 0x7a, 0xf0, 0xe4, 0xfc, 0xa0, 0x90, 0x78, + 0x9b, 0xd8, 0x60, 0xef, 0x94, 0x3e, 0xd2, 0xd5, 0xff, 0x49, 0xbe, 0x68, + 0x2e, 0xe1, 0x54, 0x47, 0x0b, 0xfe, 0x36, 0xf9, 0xa0, 0xbb, 0x85, 0x51, + 0x5a, 0xac, 0xdd, 0xd1, 0x1b, 0xd4, 0x7b, 0xff, 0xcd, 0xd8, 0xf2, 0x6f, + 0x9a, 0x0b, 0xb8, 0x55, 0x12, 0xda, 0xf0, 0x35, 0xfa, 0xcb, 0xb3, 0xe5, + 0x97, 0xf8, 0x78, 0xc6, 0x41, 0xf1, 0x65, 0xfb, 0xb1, 0xe1, 0x38, 0xb2, + 0xf6, 0xf0, 0x49, 0x65, 0x05, 0x15, 0xff, 0x0f, 0x18, 0xb9, 0x19, 0x6f, + 0x29, 0xbd, 0xe1, 0xc9, 0x65, 0xf8, 0xf7, 0x07, 0x1f, 0x2c, 0xbf, 0xec, + 0xf8, 0xb3, 0xff, 0xde, 0x4b, 0x2e, 0x60, 0x56, 0x5f, 0xc7, 0xf7, 0x07, + 0x84, 0xb2, 0xfe, 0xfb, 0x25, 0x1a, 0xfd, 0x65, 0x61, 0xee, 0x39, 0x6d, + 0xff, 0xf4, 0x16, 0xdc, 0x03, 0x9c, 0xb9, 0xe7, 0x92, 0xca, 0x84, 0xc7, + 0x24, 0x75, 0xa7, 0x3f, 0x10, 0x5f, 0x14, 0x7d, 0x25, 0x97, 0xfe, 0x32, + 0xef, 0x06, 0x07, 0xd4, 0x96, 0x5f, 0xdb, 0x40, 0x5f, 0x5f, 0xac, 0xbf, + 0xff, 0x3f, 0xdf, 0x8f, 0xcf, 0x2c, 0xe1, 0x01, 0xc4, 0x59, 0x7d, 0xfe, + 0x6a, 0x65, 0x97, 0xe3, 0xf4, 0x09, 0x32, 0xcb, 0xdd, 0x86, 0x2c, 0xa0, + 0xa6, 0x9f, 0x31, 0x16, 0x8f, 0xbc, 0x5f, 0xfa, 0xbf, 0x49, 0x37, 0x0a, + 0x6f, 0xfe, 0x16, 0x0b, 0xb9, 0xa3, 0xd9, 0xd8, 0xb2, 0xf9, 0xe4, 0x29, + 0xac, 0xb0, 0xab, 0x3c, 0x68, 0xaf, 0xe3, 0xd1, 0x1f, 0x78, 0xb2, 0xfd, + 0xa7, 0x2c, 0x85, 0x94, 0xa8, 0x86, 0xc6, 0x7e, 0x7e, 0x22, 0x72, 0xcb, + 0xa0, 0x96, 0x5e, 0xc3, 0x1a, 0xcb, 0xc3, 0xc6, 0x2c, 0xb8, 0xc8, 0x66, + 0xdc, 0xc6, 0xea, 0x0f, 0xda, 0x49, 0x97, 0xf0, 0x7c, 0x7a, 0x81, 0xac, + 0xbe, 0xc9, 0xa5, 0x8b, 0x2f, 0xfc, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, + 0x13, 0x02, 0xfc, 0x07, 0xe9, 0xf5, 0x65, 0xe2, 0xd8, 0xd6, 0x5d, 0xc8, + 0x59, 0x50, 0x7b, 0x2e, 0x4e, 0x01, 0xcb, 0xcd, 0x1f, 0xeb, 0x2f, 0xb8, + 0xe5, 0xfa, 0xca, 0x98, 0xf0, 0x3f, 0x1e, 0xbf, 0xc0, 0xfb, 0x47, 0x0c, + 0xc5, 0x97, 0xef, 0x1c, 0x16, 0xcb, 0x2f, 0xfa, 0x36, 0xe9, 0x40, 0x3b, + 0xc5, 0x95, 0xe4, 0x49, 0xf0, 0xce, 0x7c, 0x9e, 0xf8, 0xb3, 0x90, 0xb2, + 0xba, 0x7a, 0x7b, 0xcc, 0xef, 0x48, 0xff, 0x59, 0x7d, 0xf0, 0x4c, 0x55, + 0x97, 0xe7, 0x17, 0x3c, 0xeb, 0x2b, 0x11, 0x19, 0x31, 0x27, 0x47, 0x40, + 0x49, 0x7f, 0xfa, 0x5e, 0xc2, 0x68, 0x4d, 0x78, 0xe1, 0x8b, 0x2f, 0xe3, + 0xee, 0x32, 0x3c, 0xb2, 0xff, 0x8f, 0xfd, 0xbd, 0x1c, 0x07, 0x16, 0x5f, + 0xe9, 0x3f, 0x78, 0x65, 0x0b, 0x2d, 0x1a, 0x3e, 0xc2, 0x3b, 0xaf, 0x93, + 0x09, 0xd2, 0x5f, 0x61, 0x31, 0x7f, 0xec, 0x66, 0x6b, 0x3e, 0x96, 0x75, + 0x65, 0xfc, 0x7e, 0x36, 0x60, 0xab, 0x2b, 0xc7, 0xd7, 0xc3, 0xeb, 0xff, + 0xcf, 0xb7, 0x8e, 0x0b, 0x26, 0x3d, 0x1a, 0xca, 0xc4, 0xc3, 0x3b, 0x0a, + 0x26, 0x88, 0xac, 0x4b, 0x2d, 0x3e, 0xb2, 0xf8, 0x27, 0x83, 0x59, 0x70, + 0x71, 0x65, 0xc0, 0xfd, 0x25, 0x36, 0x0f, 0xcf, 0x61, 0x0f, 0x85, 0x0c, + 0x87, 0x42, 0xd7, 0xe1, 0x62, 0x58, 0x35, 0x97, 0xfb, 0x06, 0x59, 0xdf, + 0x62, 0xcb, 0xa7, 0xb0, 0xae, 0x30, 0xa5, 0xdd, 0xfd, 0x65, 0xdb, 0x92, + 0x59, 0x5b, 0x1b, 0x2d, 0x0c, 0x5f, 0x84, 0x9f, 0x72, 0xfd, 0x65, 0xfd, + 0x9e, 0x71, 0x0b, 0xab, 0x2b, 0x63, 0xd9, 0x19, 0x65, 0xe7, 0x70, 0xac, + 0xbf, 0xb3, 0xbd, 0x8d, 0x71, 0x65, 0xfc, 0xe5, 0xfe, 0xdf, 0xf5, 0x65, + 0x42, 0x26, 0xf6, 0x23, 0x21, 0xb9, 0xf2, 0xdb, 0x37, 0x86, 0x6c, 0xfe, + 0xc9, 0x72, 0x1d, 0x1c, 0x68, 0xf9, 0x1c, 0x60, 0xad, 0x41, 0x86, 0xcf, + 0xd0, 0xb7, 0x98, 0x87, 0x45, 0xac, 0x22, 0xf4, 0x29, 0x5d, 0xb4, 0xa3, + 0x96, 0xe4, 0xa5, 0x2e, 0xc2, 0xa4, 0x09, 0xdb, 0xca, 0x1a, 0x31, 0x9f, + 0x5b, 0x12, 0x31, 0x6b, 0xcc, 0x06, 0x92, 0x5f, 0xf9, 0xe4, 0xdf, 0x34, + 0x17, 0x70, 0xaa, 0x26, 0x35, 0xd1, 0xb8, 0xb2, 0xed, 0x62, 0xcb, 0x46, + 0x8d, 0x7f, 0x86, 0x6c, 0xdc, 0xd1, 0x7c, 0xc1, 0xdf, 0x3d, 0xdf, 0xf0, + 0xcf, 0x7b, 0x7f, 0xbf, 0x12, 0x4b, 0x2f, 0xe6, 0xcb, 0x62, 0x78, 0xca, + 0x5e, 0x59, 0x79, 0xb5, 0x64, 0x35, 0x94, 0xd8, 0x3e, 0x16, 0xd0, 0xf6, + 0xfe, 0xd6, 0x80, 0x59, 0xe5, 0x97, 0xd1, 0xb4, 0x69, 0x65, 0xee, 0xc4, + 0xcb, 0x2f, 0xd9, 0x13, 0x1b, 0x16, 0x5f, 0x6c, 0x01, 0xee, 0xac, 0xbf, + 0xed, 0xc3, 0xfe, 0x76, 0x4d, 0x0c, 0x59, 0x46, 0x8c, 0xc9, 0x88, 0xb4, + 0x3a, 0x21, 0x3e, 0xe1, 0x3d, 0xa4, 0xb2, 0xfc, 0xc8, 0xce, 0xf1, 0x65, + 0x4f, 0x26, 0xe0, 0x62, 0x37, 0xfb, 0xd1, 0xad, 0x84, 0x07, 0xeb, 0x2f, + 0x81, 0xb3, 0x92, 0xcb, 0x9f, 0x8b, 0x2f, 0xa7, 0x77, 0x3c, 0xb2, 0xd1, + 0xe3, 0x75, 0xc1, 0x6b, 0xd0, 0x5b, 0x2c, 0xbf, 0xff, 0xfe, 0x97, 0x3d, + 0x80, 0x6b, 0x9e, 0xc0, 0xe7, 0x27, 0x67, 0xd2, 0xe1, 0x91, 0xac, 0xbc, + 0xee, 0x15, 0x45, 0x62, 0xbe, 0x71, 0x20, 0x55, 0x94, 0x13, 0xcb, 0xd1, + 0x45, 0xfb, 0xc6, 0xc8, 0xd2, 0xcb, 0xff, 0xc0, 0x7d, 0xb5, 0x8c, 0xf4, + 0x6c, 0x40, 0x59, 0x46, 0x7e, 0x44, 0x4f, 0x7f, 0xe9, 0xfc, 0xe9, 0x60, + 0x20, 0xba, 0xb2, 0xff, 0x16, 0x73, 0xfc, 0xc1, 0xac, 0xa1, 0x55, 0x19, + 0x04, 0x9b, 0xe1, 0xcd, 0x43, 0x2f, 0xd0, 0x94, 0xe9, 0x06, 0xf3, 0xfb, + 0xff, 0x85, 0x07, 0x78, 0x59, 0xbc, 0xb3, 0x8b, 0x2f, 0xfe, 0x2e, 0x8a, + 0x08, 0x2c, 0x0c, 0x69, 0x65, 0xfe, 0xd6, 0xdd, 0xe3, 0xfc, 0x15, 0x95, + 0x07, 0xf2, 0xe8, 0x77, 0xfd, 0x2c, 0xee, 0x14, 0x35, 0x3e, 0xb2, 0xff, + 0xde, 0x8d, 0xef, 0xa0, 0xf9, 0xf7, 0x16, 0x5f, 0xe8, 0x2e, 0xfb, 0x3f, + 0x75, 0x97, 0xd0, 0xc7, 0xea, 0xca, 0x91, 0xe9, 0xf0, 0xca, 0xfd, 0xb9, + 0xdc, 0xfa, 0x65, 0x97, 0xdc, 0x98, 0xc5, 0x59, 0x7f, 0xff, 0xff, 0x9f, + 0xbd, 0xcf, 0x71, 0xe4, 0x65, 0xd9, 0x66, 0xb3, 0x99, 0xe7, 0xd8, 0xa2, + 0x4b, 0x2f, 0x76, 0x26, 0x59, 0x50, 0x8f, 0x9c, 0x2d, 0xe1, 0x20, 0x90, + 0x8b, 0xbf, 0x81, 0x3e, 0x1f, 0x1b, 0x6d, 0x65, 0xf7, 0xd2, 0x8f, 0x2c, + 0xbf, 0xdc, 0x1f, 0xb0, 0x20, 0xdd, 0x59, 0x6c, 0xe1, 0xed, 0x00, 0x8e, + 0xe8, 0xde, 0xb2, 0xbc, 0x6f, 0xc8, 0x9e, 0xfe, 0xf1, 0x8c, 0xb0, 0x0b, + 0x2f, 0xf4, 0xcf, 0x87, 0x21, 0x18, 0xb2, 0x86, 0x88, 0x00, 0x90, 0x19, + 0x65, 0xfe, 0xfa, 0x53, 0x49, 0xf5, 0xb2, 0xcb, 0xe9, 0x74, 0xf6, 0x59, + 0x50, 0x7b, 0x4e, 0x6f, 0x50, 0x9f, 0xbe, 0x46, 0x9c, 0x70, 0x89, 0xba, + 0x5d, 0x59, 0x7f, 0x66, 0xd8, 0x59, 0x25, 0x95, 0x07, 0x87, 0xa1, 0x7b, + 0xfe, 0x31, 0x19, 0xd0, 0x3e, 0xd0, 0xb2, 0xff, 0x68, 0xc1, 0xb7, 0xfc, + 0x92, 0xcb, 0xc7, 0x1b, 0x2c, 0xa1, 0x9e, 0x94, 0xc6, 0xd7, 0xe0, 0xfa, + 0x08, 0x55, 0x95, 0x87, 0x96, 0xe4, 0x77, 0xcc, 0x7f, 0xba, 0xb2, 0xff, + 0xed, 0xa3, 0xb3, 0x84, 0x08, 0x9b, 0x82, 0x71, 0x65, 0xff, 0xcf, 0xbb, + 0x84, 0xe3, 0x7c, 0xfb, 0xab, 0x2f, 0xfb, 0x51, 0xc7, 0xd1, 0x44, 0x96, + 0x56, 0x91, 0x97, 0xe4, 0xfd, 0xe8, 0xb7, 0xa4, 0xfb, 0x2c, 0xbd, 0x3b, + 0x66, 0x2c, 0xbf, 0x8a, 0x36, 0x28, 0xd9, 0x65, 0xba, 0xb2, 0xff, 0xc2, + 0xf3, 0x99, 0xe3, 0xe0, 0x36, 0x59, 0x43, 0x3d, 0x20, 0x08, 0xd4, 0xf6, + 0x9f, 0xf6, 0x43, 0xbb, 0xe3, 0x23, 0x1d, 0x72, 0x02, 0x7f, 0xbf, 0xf1, + 0x38, 0xfd, 0x1b, 0xfc, 0xe4, 0xb2, 0xe8, 0xdd, 0x59, 0x7f, 0xff, 0xe0, + 0x6d, 0x81, 0x9c, 0x40, 0xdc, 0x9d, 0x9f, 0x4b, 0x00, 0x5f, 0xf1, 0x65, + 0xff, 0xed, 0xb3, 0xee, 0xf0, 0xca, 0x3f, 0x76, 0x2c, 0xbf, 0xdf, 0x16, + 0x0f, 0xce, 0xd2, 0xca, 0xc3, 0xff, 0x74, 0xba, 0x1a, 0x6d, 0x8e, 0x7c, + 0x43, 0x3c, 0x87, 0xad, 0xf7, 0x65, 0x06, 0xb2, 0xff, 0xb0, 0x7b, 0x63, + 0x59, 0xe7, 0x59, 0x7f, 0x6d, 0x34, 0xa7, 0xbd, 0x6c, 0xb2, 0xf8, 0x8b, + 0x3a, 0xb2, 0xfc, 0x7b, 0xba, 0xd1, 0xac, 0xa6, 0xc2, 0x20, 0x60, 0xdf, + 0xc4, 0x17, 0xf6, 0xc2, 0x4f, 0xb9, 0x7e, 0xb2, 0xfb, 0x76, 0x38, 0x4b, + 0x2f, 0xfd, 0xe7, 0x67, 0x7d, 0x1d, 0x03, 0x16, 0x56, 0x1f, 0x1c, 0xc4, + 0x97, 0xff, 0x66, 0x0a, 0xc8, 0x3d, 0x68, 0xda, 0x59, 0x7f, 0xc7, 0x23, + 0xf0, 0x18, 0xc3, 0x59, 0x76, 0x75, 0x65, 0x42, 0x69, 0x59, 0x09, 0x6d, + 0x11, 0x75, 0x12, 0x7c, 0xe2, 0xf1, 0x3e, 0xe2, 0xcb, 0xde, 0x36, 0xed, + 0xaa, 0xd9, 0x38, 0xb6, 0xb2, 0xb8, 0x87, 0xce, 0xd0, 0x86, 0x91, 0x30, + 0xcd, 0xf2, 0x54, 0x70, 0xae, 0x81, 0x85, 0xff, 0xc4, 0x06, 0x77, 0x34, + 0x25, 0xb5, 0x18, 0xdf, 0xa5, 0x1c, 0x3b, 0xe7, 0xe4, 0x25, 0x0d, 0x7e, + 0x4a, 0x1e, 0xec, 0x79, 0x60, 0x41, 0xde, 0x43, 0x3f, 0x0b, 0xa1, 0x23, + 0x7d, 0xdc, 0x51, 0xbd, 0x3c, 0x27, 0xdb, 0x0b, 0x2e, 0x91, 0x2c, 0xbf, + 0xb5, 0xa8, 0x1e, 0x31, 0x65, 0xb7, 0x56, 0x5c, 0x7b, 0x8b, 0x2f, 0xb6, + 0xd4, 0x6c, 0xb2, 0xe1, 0x3f, 0x59, 0x77, 0xfc, 0x59, 0x7b, 0xc6, 0xc5, + 0x96, 0x99, 0x65, 0x19, 0xe3, 0x38, 0xc0, 0x07, 0x2f, 0xce, 0x5b, 0x03, + 0x8b, 0x2f, 0xd8, 0x3d, 0x38, 0x56, 0x5f, 0x3e, 0xd1, 0xa5, 0x97, 0x40, + 0xfc, 0x79, 0x24, 0x4f, 0x46, 0x9d, 0x3c, 0xc5, 0xac, 0x14, 0x71, 0x9f, + 0xc9, 0x09, 0x7f, 0x85, 0xbd, 0x71, 0xb7, 0x96, 0x5e, 0xe0, 0x9c, 0x59, + 0x7d, 0xf6, 0xd0, 0x2a, 0xca, 0xd8, 0xf5, 0x06, 0x22, 0x01, 0xeb, 0xbb, + 0x0b, 0x2f, 0xf7, 0xda, 0x83, 0x93, 0xf1, 0x65, 0xf6, 0x14, 0x49, 0x65, + 0x61, 0xe9, 0xb0, 0xce, 0xd3, 0xeb, 0x2f, 0xf6, 0x7f, 0xcc, 0xe9, 0xe9, + 0x65, 0xce, 0x35, 0x97, 0xbb, 0x9c, 0x59, 0x52, 0x36, 0x40, 0x16, 0xb8, + 0xb7, 0x56, 0x5e, 0xd3, 0x92, 0xca, 0x59, 0x7f, 0x9a, 0x2c, 0xdf, 0xa3, + 0xe2, 0xcb, 0x7c, 0xb2, 0xa0, 0xfb, 0x70, 0x6c, 0xc2, 0xc0, 0x6b, 0x7e, + 0xcd, 0x4a, 0x18, 0xb2, 0xfe, 0x3d, 0xb0, 0x8c, 0x55, 0x97, 0xf9, 0xf5, + 0x2f, 0xcf, 0xee, 0x2c, 0xbe, 0xe7, 0x23, 0xf5, 0x95, 0x0a, 0x89, 0x86, + 0x43, 0x82, 0x86, 0xca, 0xc2, 0x1f, 0x42, 0x19, 0xce, 0x48, 0x9c, 0x05, + 0xad, 0x1a, 0xdf, 0xfb, 0x0b, 0x05, 0x2c, 0xef, 0x8d, 0x65, 0xfc, 0xe2, + 0xe0, 0xde, 0x4b, 0x2b, 0xc7, 0xd2, 0x47, 0x97, 0xf1, 0x91, 0x66, 0xf7, + 0x59, 0x7a, 0x42, 0x75, 0x65, 0xed, 0x69, 0xd6, 0x5f, 0x4b, 0xf0, 0x49, + 0x65, 0xf6, 0x84, 0x8f, 0xd6, 0x54, 0x1e, 0x41, 0x92, 0x5f, 0xe3, 0xd9, + 0xd9, 0xff, 0xf0, 0xb2, 0xe3, 0x1a, 0xcb, 0x9f, 0xe5, 0x97, 0xf8, 0x3e, + 0xc9, 0x8a, 0x06, 0xb2, 0x86, 0x79, 0x46, 0x2f, 0x7b, 0xa7, 0xfa, 0xcb, + 0xef, 0xe0, 0x82, 0xb2, 0x85, 0x37, 0xff, 0x0e, 0xdf, 0x4b, 0xb8, 0x22, + 0xcb, 0xc1, 0x8f, 0xd6, 0x5e, 0xd4, 0x31, 0x65, 0x19, 0xff, 0xb9, 0x18, + 0x08, 0xda, 0x1d, 0xbe, 0xd8, 0x4c, 0x99, 0x65, 0xe8, 0xd6, 0xcb, 0x2a, + 0x0f, 0x08, 0xc9, 0x69, 0x65, 0xe0, 0xc7, 0xeb, 0x2e, 0x82, 0x91, 0xa8, + 0xc0, 0xbb, 0xee, 0x91, 0xee, 0x2c, 0xb8, 0x1c, 0x59, 0x7e, 0x04, 0xb9, + 0xe1, 0xac, 0xa8, 0x3c, 0x02, 0x17, 0xa9, 0x2b, 0xd6, 0x19, 0x0f, 0xc5, + 0x86, 0x3d, 0x33, 0x26, 0x88, 0x18, 0x6b, 0xe6, 0x02, 0x86, 0xff, 0x1f, + 0x7a, 0x90, 0x02, 0xad, 0xec, 0x97, 0xf3, 0xcb, 0x73, 0x70, 0x1b, 0x2c, + 0xb1, 0x2c, 0xba, 0x5b, 0xab, 0x2b, 0xc7, 0xbf, 0xc3, 0x5e, 0x88, 0x5a, + 0x7d, 0x65, 0xfd, 0x9b, 0x0f, 0xc7, 0xa5, 0x95, 0xe3, 0xc2, 0x21, 0x4b, + 0xff, 0x46, 0x72, 0x74, 0x18, 0xc4, 0x92, 0xcb, 0xf8, 0xda, 0xe6, 0xd8, + 0xd2, 0xca, 0x23, 0xf0, 0xea, 0x05, 0xc5, 0x0b, 0x2f, 0xd0, 0x3e, 0xe0, + 0x56, 0x5e, 0x82, 0x0a, 0xcb, 0xff, 0xf9, 0xda, 0x1b, 0xbe, 0xb6, 0xf3, + 0xf7, 0xc7, 0xa9, 0x2c, 0xbf, 0x04, 0x3e, 0x8d, 0x2c, 0xaf, 0x22, 0x7c, + 0x86, 0xc0, 0xb9, 0x52, 0x4c, 0xac, 0x24, 0x3c, 0x15, 0xec, 0x31, 0xaf, + 0x9c, 0x9e, 0x65, 0x97, 0xe3, 0xe9, 0x64, 0x96, 0x5f, 0x43, 0x51, 0x32, + 0xcb, 0xff, 0x73, 0x7b, 0xfa, 0x05, 0xce, 0xf1, 0x65, 0xc2, 0x31, 0x65, + 0xf8, 0xb3, 0x40, 0xc5, 0x97, 0x9f, 0xe2, 0x59, 0x7f, 0x74, 0x9f, 0xfc, + 0x25, 0x97, 0xe2, 0x7f, 0xf0, 0x96, 0x54, 0xe3, 0xd2, 0xe9, 0x65, 0x0d, + 0x12, 0x40, 0x6e, 0xad, 0x93, 0x9e, 0x14, 0x84, 0xc9, 0xa6, 0x23, 0xf2, + 0x0f, 0xe3, 0x05, 0x0b, 0x8b, 0xee, 0x61, 0x05, 0x65, 0xfd, 0x05, 0xd0, + 0x83, 0x65, 0x97, 0xfa, 0x1a, 0x18, 0x1f, 0x52, 0x59, 0x7b, 0x91, 0x25, + 0x97, 0xef, 0x66, 0x8f, 0x8b, 0x2e, 0x3d, 0x7c, 0x78, 0x7d, 0x1c, 0xb6, + 0xf5, 0x95, 0x09, 0x83, 0xc8, 0x87, 0x45, 0xde, 0x77, 0xdc, 0x2e, 0xbc, + 0x46, 0x2a, 0xcb, 0xec, 0x19, 0xef, 0x59, 0x74, 0x33, 0x0d, 0xfb, 0x8e, + 0x5f, 0xa5, 0xd3, 0x67, 0x56, 0x5f, 0xcf, 0xf7, 0xa0, 0x42, 0x59, 0x6c, + 0xf8, 0xf5, 0x9a, 0x28, 0xbf, 0x9e, 0x4e, 0x59, 0xb8, 0xb2, 0xfb, 0x9e, + 0x79, 0x2c, 0xa3, 0x47, 0xce, 0xa1, 0x00, 0xe5, 0x20, 0x2e, 0xbd, 0x85, + 0x32, 0xcb, 0x7c, 0xb2, 0xef, 0xe1, 0x25, 0xcd, 0x34, 0x92, 0x8c, 0xd8, + 0x34, 0x2f, 0x7e, 0x2c, 0xef, 0x8d, 0x23, 0x73, 0x43, 0x7c, 0x29, 0x67, + 0x16, 0x54, 0x8f, 0x68, 0x67, 0x17, 0xf9, 0xc5, 0x82, 0x2c, 0xea, 0xca, + 0x84, 0xcb, 0x06, 0x38, 0xf0, 0xbd, 0x22, 0x2b, 0xde, 0x86, 0x96, 0x5e, + 0x7f, 0xb8, 0xb2, 0xdc, 0x59, 0x7c, 0x27, 0x9f, 0xab, 0x2b, 0x63, 0xee, + 0x18, 0xef, 0xc3, 0xbd, 0x11, 0xbd, 0x05, 0x32, 0xcb, 0x8a, 0x16, 0x52, + 0xca, 0x59, 0x53, 0x16, 0xcc, 0x0b, 0xbb, 0xd3, 0x2c, 0xb8, 0xff, 0x59, + 0x50, 0x6b, 0xff, 0x18, 0xa9, 0x91, 0x71, 0xa1, 0xc7, 0x2b, 0xfd, 0x42, + 0xff, 0xe1, 0x4c, 0x83, 0xc8, 0x63, 0xfd, 0xd5, 0x97, 0xbf, 0xcd, 0xc5, + 0x97, 0xff, 0xc5, 0x9f, 0xe3, 0x01, 0x1a, 0xd1, 0x80, 0x2b, 0x2f, 0x8a, + 0x19, 0x32, 0xca, 0x15, 0x1a, 0xda, 0x45, 0x22, 0x0e, 0xa7, 0xdf, 0x33, + 0x80, 0x62, 0xcb, 0x0a, 0xb2, 0xd3, 0xeb, 0x2e, 0xd0, 0xab, 0x2a, 0x0f, + 0x84, 0xc8, 0xc0, 0x24, 0x20, 0xa5, 0xff, 0xf7, 0xb2, 0x5c, 0x8e, 0xcb, + 0x37, 0x5c, 0xbf, 0x59, 0x7f, 0xc7, 0xdf, 0x67, 0xdd, 0x04, 0x2c, 0xbe, + 0x12, 0x35, 0xfa, 0xca, 0x91, 0xee, 0xf8, 0xe6, 0xec, 0xe2, 0xca, 0x83, + 0x71, 0x22, 0x2a, 0xe2, 0x60, 0xfd, 0x87, 0x3d, 0xbf, 0x59, 0x7e, 0x38, + 0x64, 0x31, 0x65, 0xb1, 0x65, 0x19, 0xf8, 0xb9, 0x4f, 0x04, 0xa7, 0xc9, + 0xac, 0x2a, 0xca, 0x85, 0xca, 0xac, 0x87, 0xb1, 0xc6, 0x29, 0xe8, 0x4f, + 0x3c, 0x76, 0x7b, 0x87, 0xb7, 0xd0, 0xd0, 0x26, 0x59, 0x7b, 0x91, 0x32, + 0xcb, 0xfb, 0x07, 0x9b, 0xcf, 0x4b, 0x29, 0x65, 0x98, 0xb2, 0xba, 0x5e, + 0x6f, 0x0b, 0xbe, 0x1b, 0xbf, 0xcb, 0x2f, 0xdf, 0x9e, 0x8c, 0x55, 0x97, + 0x38, 0xab, 0x2e, 0xcf, 0x96, 0x50, 0x53, 0x26, 0x98, 0x91, 0x83, 0xbe, + 0x4a, 0x72, 0x2f, 0xc8, 0x88, 0xa6, 0x7c, 0x5e, 0xec, 0xf2, 0xcb, 0xb3, + 0x4b, 0x29, 0x65, 0x2c, 0xb4, 0x2c, 0xa6, 0xd9, 0xa6, 0x20, 0xbe, 0x85, + 0xde, 0xcd, 0xee, 0xb2, 0xfb, 0xde, 0xcf, 0xd6, 0x5a, 0x3c, 0x78, 0x02, + 0x0e, 0xd4, 0x23, 0xad, 0x82, 0xce, 0x79, 0xfb, 0x8d, 0xfc, 0x7d, 0xcf, + 0x38, 0xab, 0x2f, 0xee, 0x8c, 0xff, 0x82, 0x59, 0x7b, 0x6f, 0x1a, 0xcb, + 0xbc, 0x0f, 0x8f, 0x29, 0xcb, 0x6f, 0x34, 0xd3, 0x49, 0x2f, 0xf4, 0x6c, + 0x26, 0x79, 0xfe, 0x48, 0xdc, 0xd0, 0x5f, 0xe3, 0x1b, 0x97, 0xd2, 0xe2, + 0xca, 0x19, 0xfd, 0x79, 0x1e, 0xec, 0xd9, 0x65, 0xf9, 0x82, 0x46, 0xbf, + 0x59, 0x4b, 0x29, 0x65, 0xa1, 0x85, 0xb7, 0xe1, 0x75, 0x07, 0xcd, 0x24, + 0x0b, 0xff, 0x72, 0x25, 0xec, 0xc2, 0x97, 0x16, 0x50, 0xd3, 0xc5, 0x0c, + 0x32, 0x0c, 0x8a, 0x67, 0xde, 0x90, 0xde, 0x86, 0x1a, 0xcb, 0xd2, 0xc0, + 0xac, 0xac, 0x44, 0x38, 0x54, 0x3e, 0x1b, 0xbe, 0x3d, 0x6e, 0x0d, 0x65, + 0xfb, 0xd1, 0xf3, 0xee, 0xac, 0xbf, 0xfe, 0x8e, 0xf0, 0x79, 0xd3, 0xf0, + 0x33, 0xee, 0xac, 0xaf, 0x1f, 0xdf, 0x4a, 0xef, 0x16, 0x79, 0x65, 0xb4, + 0xb2, 0xff, 0x66, 0x04, 0x4d, 0xfe, 0xc5, 0x97, 0xfd, 0x05, 0xd2, 0xce, + 0xc3, 0x4b, 0x2f, 0xf6, 0x35, 0xc3, 0x2f, 0xa4, 0xb2, 0x96, 0x51, 0x9f, + 0xb6, 0x8d, 0xdc, 0xd2, 0xf7, 0x74, 0x05, 0x96, 0xe2, 0xca, 0x23, 0x5c, + 0x01, 0xdb, 0xfc, 0xfb, 0xf0, 0x7b, 0x63, 0x4b, 0x2f, 0xff, 0xd8, 0x38, + 0xcf, 0x84, 0xe9, 0x90, 0x25, 0x9c, 0x59, 0x73, 0x3f, 0x59, 0x73, 0x92, + 0xca, 0xf1, 0xad, 0x71, 0x8b, 0xf3, 0x0f, 0xbf, 0xba, 0xca, 0xd9, 0x58, + 0xa6, 0x42, 0x80, 0xc8, 0xb7, 0x46, 0xe6, 0x11, 0xd4, 0x2a, 0xdd, 0x6f, + 0xf2, 0x02, 0x36, 0xec, 0x20, 0x80, 0x41, 0x76, 0x85, 0x59, 0x7d, 0x0c, + 0x7f, 0x2c, 0xbd, 0xde, 0xba, 0xcb, 0xed, 0x02, 0x37, 0xac, 0xbf, 0x66, + 0xeb, 0x97, 0xe3, 0x3e, 0x17, 0x21, 0xe8, 0xe5, 0xf7, 0xba, 0x7b, 0x2c, + 0xbf, 0xc1, 0x3e, 0x16, 0x6f, 0x75, 0x97, 0xd1, 0xb3, 0xc9, 0x65, 0xf7, + 0x79, 0xe3, 0x59, 0x7f, 0x13, 0xf7, 0x6c, 0x69, 0x65, 0x2c, 0xa2, 0x37, + 0x3d, 0x2e, 0xa8, 0x3f, 0xbc, 0x5b, 0xbf, 0x66, 0xc5, 0x9f, 0x2c, 0xbe, + 0xe9, 0x97, 0x16, 0x5f, 0x4e, 0xcf, 0xba, 0xb2, 0xfe, 0xe4, 0x7d, 0x2c, + 0xea, 0xca, 0x09, 0xe9, 0xf0, 0x96, 0xfd, 0xf6, 0x74, 0xf8, 0xb2, 0xb1, + 0x17, 0xc6, 0xe8, 0xe4, 0x55, 0x0a, 0xbb, 0x24, 0xf3, 0x89, 0xbf, 0x11, + 0xe8, 0xcf, 0xd0, 0x9d, 0x22, 0x0e, 0x43, 0x9a, 0xf0, 0x80, 0x11, 0x65, + 0xe2, 0x3d, 0xd5, 0x97, 0xc3, 0x9f, 0x71, 0x56, 0x54, 0x1e, 0x26, 0x0f, + 0x5a, 0x7d, 0x65, 0xd0, 0x35, 0x94, 0xd9, 0x35, 0x5e, 0x14, 0xb7, 0x16, + 0x59, 0x8b, 0x2d, 0xa5, 0x94, 0xe6, 0x88, 0x02, 0x35, 0xa3, 0xd3, 0xf1, + 0x9d, 0x2c, 0x33, 0x45, 0x7f, 0xa0, 0xa4, 0xec, 0xc1, 0xac, 0xbe, 0xeb, + 0x19, 0xa5, 0x97, 0xff, 0x7f, 0x04, 0x17, 0xef, 0xe2, 0x14, 0x96, 0x5e, + 0x91, 0xf9, 0x65, 0xf7, 0x4b, 0x24, 0xb2, 0xf8, 0x0d, 0xbe, 0x3a, 0xcb, + 0xf6, 0xc3, 0x77, 0x69, 0x65, 0x61, 0xe7, 0x80, 0x9a, 0xff, 0xf1, 0xf3, + 0xd8, 0x32, 0xcd, 0xe5, 0x9c, 0x59, 0x52, 0x4e, 0x40, 0x53, 0x1f, 0x88, + 0xe6, 0x46, 0xd0, 0xe1, 0x38, 0x74, 0x86, 0xe1, 0x02, 0xb2, 0x9b, 0x0e, + 0xa7, 0x5a, 0x7b, 0x2a, 0x88, 0xef, 0x76, 0x86, 0x2c, 0x8c, 0x07, 0x2a, + 0x3f, 0x27, 0x33, 0x45, 0x8c, 0x68, 0x2e, 0x3f, 0x47, 0x78, 0x71, 0xf0, + 0xcd, 0x1b, 0x9e, 0xa3, 0x85, 0x64, 0x6d, 0x9e, 0x9d, 0x58, 0x78, 0xe5, + 0x7f, 0x8d, 0x81, 0xb6, 0x78, 0x52, 0xb0, 0xf9, 0x2d, 0x13, 0xb2, 0xe5, + 0xc0, 0xf7, 0xbd, 0x8d, 0xa4, 0xa9, 0xf8, 0x4d, 0x09, 0x1d, 0x56, 0xe3, + 0x3d, 0xff, 0x37, 0xe9, 0xe6, 0xe7, 0x01, 0x0b, 0x2f, 0xff, 0xf8, 0x10, + 0xdf, 0xbc, 0x3f, 0x39, 0xf7, 0x35, 0xfb, 0x1e, 0x4b, 0x29, 0xba, 0xa9, + 0xcd, 0xa3, 0xf7, 0x19, 0xed, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0xb7, 0x5f, + 0xf9, 0xe4, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x27, 0x05, 0x9b, 0xe2, 0x20, + 0x18, 0x69, 0x74, 0xf6, 0x35, 0x96, 0xf2, 0xcb, 0x71, 0x65, 0x00, 0xd1, + 0x4f, 0x88, 0xdf, 0xb1, 0xae, 0x1b, 0x4b, 0x2f, 0x4b, 0x9c, 0x59, 0x7c, + 0x17, 0x70, 0xaa, 0x2d, 0x15, 0xff, 0xec, 0x1f, 0x9c, 0x59, 0xa6, 0x3e, + 0x01, 0x8b, 0x2b, 0x47, 0xf7, 0xe2, 0xeb, 0xe0, 0xb9, 0x7e, 0xb2, 0xfb, + 0xf8, 0x20, 0xac, 0xbf, 0xe9, 0xb3, 0x77, 0x9a, 0xd3, 0xf5, 0x65, 0xde, + 0xe2, 0xcb, 0xfa, 0x1c, 0x80, 0x3c, 0x59, 0x7e, 0xd0, 0xc1, 0xde, 0x2c, + 0xbf, 0xe8, 0xdb, 0xd9, 0xcf, 0x66, 0xea, 0xcb, 0xff, 0xa0, 0x3c, 0xc0, + 0x94, 0x7f, 0xc8, 0x59, 0x7f, 0xdb, 0xf9, 0x9e, 0xc2, 0x10, 0x6b, 0x2a, + 0x0f, 0xfb, 0x10, 0xeb, 0xc8, 0xda, 0x28, 0x5d, 0x5f, 0xec, 0xd7, 0xf9, + 0x30, 0x9c, 0x59, 0x61, 0x56, 0x5f, 0xf6, 0x46, 0xd9, 0x36, 0x8f, 0x8b, + 0x28, 0xcf, 0x2e, 0x61, 0x2b, 0xff, 0xf6, 0xd1, 0xd8, 0x1e, 0xa3, 0xcf, + 0xc2, 0xcf, 0xd6, 0x5f, 0xfa, 0x3e, 0x7d, 0xdf, 0x13, 0xee, 0xec, 0xb2, + 0xff, 0xd1, 0xaf, 0xf0, 0xf3, 0x5d, 0x02, 0xca, 0x84, 0x6c, 0x79, 0x57, + 0x88, 0xb4, 0x34, 0xf4, 0x4c, 0x9e, 0x68, 0x40, 0xf2, 0x1f, 0x57, 0xee, + 0x30, 0xca, 0x4b, 0x2a, 0x15, 0xa1, 0xe4, 0xad, 0x47, 0x48, 0xa5, 0x97, + 0xb5, 0x02, 0xac, 0xbf, 0x1f, 0x4a, 0x18, 0xb2, 0x9b, 0x07, 0x9d, 0x30, + 0x5f, 0x87, 0x6f, 0xf4, 0x33, 0x5a, 0x3f, 0xf8, 0xb2, 0xf0, 0x71, 0xa5, + 0x96, 0xc9, 0x8f, 0x47, 0xc6, 0x96, 0x69, 0x65, 0xfe, 0xd4, 0x77, 0xe9, + 0x67, 0x56, 0x5d, 0x83, 0x59, 0x7b, 0x67, 0x62, 0xcb, 0xd1, 0xfe, 0xa0, + 0xd9, 0x98, 0xb5, 0xff, 0xdf, 0x73, 0xa5, 0x9b, 0xfb, 0xd3, 0x1a, 0xca, + 0x34, 0x78, 0xcc, 0x25, 0xc6, 0x70, 0x18, 0xdc, 0x72, 0x59, 0x7f, 0x66, + 0xfe, 0x82, 0x02, 0xb2, 0xff, 0x7b, 0xc0, 0x15, 0x91, 0xc5, 0x97, 0xff, + 0xe3, 0xe1, 0x66, 0xf7, 0xe9, 0x67, 0x3c, 0xf3, 0x2c, 0xbf, 0x47, 0x78, + 0x27, 0x96, 0x54, 0x23, 0xe0, 0x62, 0xd8, 0x5f, 0x31, 0xa9, 0x2a, 0xdf, + 0x11, 0xfd, 0x25, 0x97, 0xbf, 0x7f, 0x2c, 0xbf, 0xdb, 0x61, 0x8d, 0xdc, + 0x55, 0x95, 0x87, 0xa0, 0xe3, 0xb7, 0xf6, 0xa0, 0x70, 0x53, 0x2c, 0xb7, + 0x16, 0x56, 0xc6, 0xfb, 0xc5, 0xb6, 0x6e, 0xd9, 0x64, 0xee, 0x4f, 0x27, + 0xdb, 0x12, 0x0c, 0xa7, 0x21, 0x30, 0x12, 0x2f, 0x88, 0xa6, 0x22, 0xd1, + 0xeb, 0x05, 0xfd, 0x2d, 0xbb, 0xf8, 0x42, 0x14, 0x21, 0x79, 0x18, 0xcf, + 0x63, 0x35, 0x02, 0x44, 0xfb, 0x90, 0x8b, 0xd7, 0xb8, 0xce, 0x2c, 0xbf, + 0xcd, 0x98, 0x3e, 0xcb, 0x38, 0xb2, 0xff, 0xf8, 0xfa, 0x7a, 0x7f, 0xe3, + 0xc6, 0x18, 0xfd, 0x65, 0xd2, 0x92, 0xcb, 0xff, 0x3f, 0xa7, 0x46, 0x77, + 0xc6, 0xc5, 0x97, 0xff, 0xf8, 0xfb, 0x1e, 0x2c, 0xee, 0xa3, 0xc5, 0x9b, + 0xc0, 0x15, 0x97, 0xff, 0xd1, 0x9d, 0xf3, 0xcb, 0x40, 0x1c, 0x14, 0x2c, + 0xa9, 0x26, 0xa5, 0xba, 0x9e, 0x01, 0x7d, 0xe7, 0xfb, 0x8c, 0x17, 0xfe, + 0x82, 0x89, 0x67, 0x30, 0xe1, 0x65, 0xff, 0xff, 0x47, 0xec, 0x79, 0x4e, + 0xef, 0xb1, 0x98, 0x18, 0xf4, 0x31, 0x65, 0xff, 0xf1, 0x97, 0x41, 0xe3, + 0xe4, 0xfb, 0xf7, 0x06, 0xb2, 0xbc, 0x8b, 0x9e, 0xb4, 0x5f, 0xc7, 0xb4, + 0xe7, 0x2f, 0xd6, 0x5f, 0x05, 0xdc, 0x2a, 0x8b, 0x69, 0x7f, 0xff, 0x43, + 0xf6, 0x3d, 0x1c, 0x33, 0x91, 0xeb, 0x06, 0xb2, 0xfe, 0xf4, 0x32, 0x0c, + 0x6b, 0x28, 0xd3, 0x04, 0x98, 0x8f, 0x46, 0x1c, 0x2e, 0xea, 0xbd, 0xfc, + 0xf2, 0x1f, 0x8d, 0x8b, 0x2f, 0xf6, 0x4c, 0x50, 0xc9, 0xc1, 0x59, 0x77, + 0x8c, 0x67, 0xc5, 0xd2, 0xdb, 0xfe, 0xcc, 0x0c, 0xf8, 0x3c, 0x7a, 0x59, + 0x7f, 0xf7, 0xb3, 0x6e, 0x1e, 0xa3, 0x71, 0xc9, 0x65, 0xd8, 0x6b, 0x2d, + 0x01, 0x3d, 0xbd, 0xe8, 0xd6, 0x9e, 0x96, 0x5f, 0xfd, 0x9d, 0x86, 0x13, + 0x8a, 0x59, 0xfa, 0xcb, 0x7e, 0xb2, 0xa6, 0x3e, 0xb2, 0x16, 0xea, 0x1d, + 0xff, 0x8c, 0x87, 0x10, 0xcd, 0xc7, 0x92, 0xcb, 0xfe, 0x8f, 0xfd, 0x1b, + 0x16, 0x71, 0x65, 0x41, 0xfc, 0x00, 0xfe, 0xff, 0xfc, 0x63, 0x07, 0x78, + 0x3c, 0x97, 0xa3, 0x78, 0xe1, 0x65, 0xff, 0x44, 0xa3, 0x5b, 0x46, 0xb6, + 0x59, 0x7f, 0xfa, 0x25, 0xe8, 0x21, 0x4b, 0x36, 0x12, 0x4b, 0x2f, 0xff, + 0xf4, 0x19, 0x3f, 0x41, 0xdc, 0x1c, 0x30, 0x7e, 0x8f, 0x96, 0x5f, 0xe3, + 0x60, 0xe3, 0x82, 0x71, 0x65, 0xee, 0xe0, 0x56, 0x5f, 0xff, 0x11, 0xef, + 0x3e, 0xfa, 0x3b, 0xc8, 0xdf, 0x0b, 0x28, 0xd3, 0x4b, 0xd2, 0x5f, 0x97, + 0xfa, 0x68, 0x01, 0xcb, 0x9c, 0x6b, 0x2f, 0xfe, 0xeb, 0x9e, 0x46, 0x9e, + 0x47, 0xe5, 0x97, 0xe7, 0x1e, 0x17, 0xeb, 0x2a, 0x47, 0xd0, 0x68, 0x37, + 0xfd, 0x1a, 0x9d, 0xc8, 0xf3, 0x9a, 0xcb, 0xfd, 0xe3, 0x79, 0x74, 0xf6, + 0x59, 0x7e, 0x1c, 0x61, 0x1a, 0xca, 0x9e, 0x99, 0x42, 0x2d, 0x91, 0xd8, + 0x8e, 0x20, 0x6a, 0x79, 0x1f, 0xb8, 0x61, 0x5b, 0x31, 0x6e, 0xa1, 0x2c, + 0xc8, 0x4a, 0x7a, 0x14, 0xee, 0x42, 0x4a, 0xfc, 0x8e, 0x17, 0xa9, 0x40, + 0x7e, 0x68, 0x86, 0x7c, 0xe4, 0x43, 0x3b, 0xfa, 0x1b, 0xeb, 0x30, 0x55, + 0x97, 0xff, 0xf6, 0xa3, 0xa6, 0x13, 0x1c, 0x1e, 0x8e, 0x47, 0xc5, 0x97, + 0xf0, 0xaf, 0xa1, 0x80, 0x96, 0x5f, 0xf4, 0x74, 0xc2, 0x63, 0xf8, 0x96, + 0x5f, 0xff, 0xff, 0xf8, 0x65, 0x01, 0xf8, 0x27, 0xf3, 0x1c, 0x85, 0xc2, + 0x7f, 0x9a, 0x0f, 0x8e, 0x62, 0x36, 0x71, 0x65, 0xff, 0x8f, 0xb1, 0xd3, + 0xdf, 0x04, 0x15, 0x97, 0x7d, 0x25, 0x95, 0x88, 0xf2, 0x78, 0x4e, 0x00, + 0xfa, 0xfe, 0x29, 0xf3, 0xd6, 0x0a, 0xb2, 0xee, 0xf9, 0x65, 0xff, 0x67, + 0x78, 0x27, 0x3b, 0x9e, 0x59, 0x5b, 0x1e, 0x8b, 0x05, 0xef, 0xc7, 0xb3, + 0x20, 0x2b, 0x2f, 0xcf, 0xf4, 0x84, 0xe2, 0xcb, 0xff, 0x1c, 0xdc, 0xde, + 0xe5, 0xb0, 0x06, 0xb2, 0xcd, 0xf1, 0x58, 0xd8, 0xab, 0x1a, 0x2e, 0xf4, + 0x65, 0x44, 0x67, 0xc8, 0x40, 0xf4, 0x8f, 0x79, 0x40, 0x85, 0x54, 0xdd, + 0x74, 0x1f, 0x23, 0x28, 0xfe, 0x5a, 0x55, 0xcc, 0xf2, 0xcb, 0xf6, 0x82, + 0xee, 0x15, 0x45, 0xca, 0xbf, 0x9c, 0x7e, 0x82, 0xd9, 0x65, 0x9b, 0x8c, + 0xfe, 0x30, 0x5f, 0xc6, 0x97, 0xed, 0x05, 0xdc, 0x2a, 0x8b, 0xb5, 0x7b, + 0x6c, 0x69, 0x65, 0xff, 0x1c, 0xa4, 0x65, 0xfc, 0x49, 0x65, 0xcc, 0x9f, + 0x59, 0x7c, 0x7a, 0x7f, 0xd6, 0x5e, 0x7d, 0xa1, 0x65, 0x9b, 0xe2, 0x38, + 0x98, 0x68, 0xe3, 0xc4, 0x71, 0xc1, 0xa0, 0x11, 0x54, 0x3f, 0x7c, 0xbe, + 0xc5, 0x52, 0xa7, 0x54, 0x0e, 0x50, 0xf6, 0x1b, 0x8b, 0x59, 0x89, 0x9d, + 0xae, 0x26, 0xdd, 0x85, 0xdf, 0xa9, 0x40, 0xc5, 0x58, 0x24, 0x72, 0x17, + 0x5d, 0x8c, 0x62, 0xff, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x8a, 0x9d, 0x7e, + 0x26, 0xfa, 0xe7, 0x16, 0x5d, 0xb6, 0x2c, 0xbe, 0xe1, 0x7b, 0xab, 0x2f, + 0xf7, 0xa3, 0x90, 0xc0, 0x4c, 0xb2, 0xa4, 0x7a, 0xc3, 0x22, 0xbf, 0x81, + 0xf3, 0x1d, 0xc9, 0x65, 0xba, 0xb2, 0xfb, 0xee, 0x40, 0xd6, 0x58, 0x0b, + 0x2f, 0x43, 0xed, 0x38, 0xda, 0x7e, 0x47, 0x46, 0x7f, 0x44, 0x8b, 0x7c, + 0x17, 0x70, 0xaa, 0x2b, 0x95, 0xfe, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x16, + 0x72, 0xe7, 0x92, 0xcb, 0xfa, 0x5d, 0xf8, 0x27, 0xc5, 0x94, 0xe7, 0x8a, + 0x01, 0x6b, 0xf8, 0xf9, 0x84, 0xed, 0x2c, 0xad, 0x26, 0x03, 0xe2, 0xe2, + 0x84, 0x37, 0x48, 0x6f, 0xff, 0x76, 0x39, 0x38, 0x1c, 0xec, 0x4d, 0x31, + 0xac, 0xbf, 0x8d, 0x9b, 0x03, 0x52, 0x59, 0x7e, 0x66, 0x4c, 0xff, 0xac, + 0xbe, 0xdb, 0xcf, 0xd5, 0x94, 0xe7, 0x98, 0x02, 0x9b, 0xfd, 0x1e, 0x9c, + 0xc9, 0xe5, 0xb3, 0xd5, 0x95, 0x89, 0x94, 0xf9, 0x38, 0x9e, 0xf7, 0x90, + 0xdf, 0xf1, 0x43, 0x38, 0x09, 0x38, 0xab, 0x2f, 0xda, 0xce, 0x9b, 0x16, + 0x5c, 0xcd, 0xeb, 0x2f, 0x14, 0x0d, 0x65, 0x61, 0xb4, 0x71, 0x9b, 0xf4, + 0xd0, 0xc8, 0x11, 0x65, 0xd2, 0x62, 0xca, 0xc3, 0xc0, 0x22, 0x9b, 0xff, + 0xde, 0x39, 0xde, 0x73, 0xec, 0x60, 0xcd, 0x65, 0xff, 0x8f, 0x53, 0xa1, + 0x87, 0xba, 0xf3, 0x2c, 0xbe, 0x0b, 0xb8, 0x55, 0x12, 0x12, 0x85, 0x3f, + 0x1d, 0x21, 0xdf, 0xf6, 0x77, 0x80, 0x94, 0x6d, 0x0b, 0x2f, 0xff, 0xc7, + 0xa0, 0xfb, 0x27, 0x6b, 0x58, 0xd4, 0xfc, 0x31, 0x65, 0x49, 0x12, 0x9c, + 0x39, 0xbf, 0xf6, 0x33, 0x1f, 0x5b, 0x4e, 0xf9, 0x8b, 0x2f, 0xf7, 0xdc, + 0xe9, 0x43, 0x38, 0xb2, 0xff, 0xf3, 0x90, 0x67, 0x0c, 0x9e, 0x5e, 0x79, + 0x2c, 0xa3, 0x3f, 0xe0, 0x1a, 0x5f, 0xd8, 0x47, 0xff, 0x21, 0x65, 0x9b, + 0xc9, 0x55, 0x0e, 0x42, 0xeb, 0xe8, 0x5b, 0xf8, 0x8f, 0xf8, 0x60, 0x70, + 0x86, 0xe3, 0xf2, 0xcb, 0xff, 0x38, 0x93, 0x11, 0xf4, 0xa3, 0xf5, 0x95, + 0x24, 0x63, 0x7c, 0xe3, 0xd1, 0x6b, 0xff, 0xbc, 0x6c, 0xce, 0x4e, 0xfa, + 0x59, 0xd5, 0x97, 0xd2, 0xf0, 0x9c, 0x59, 0x74, 0xf1, 0x9e, 0x56, 0x5f, + 0x41, 0x83, 0x8b, 0x2c, 0xdd, 0xb5, 0x4f, 0x0f, 0xc4, 0x55, 0x08, 0xdf, + 0xc4, 0x77, 0x67, 0xbe, 0x9e, 0x7d, 0x0c, 0x59, 0x7f, 0x7b, 0x9c, 0xcd, + 0x6c, 0xb2, 0xe8, 0x69, 0x65, 0xff, 0x7f, 0x83, 0x27, 0xfe, 0x37, 0xac, + 0xac, 0x44, 0x09, 0x97, 0xb8, 0xbd, 0xe6, 0x6f, 0x92, 0xcb, 0xff, 0xde, + 0x8f, 0xd8, 0xf2, 0xe7, 0x73, 0x18, 0xb2, 0xf4, 0xcc, 0x99, 0x65, 0x9b, + 0xb6, 0xb6, 0x58, 0xa4, 0x36, 0xec, 0x44, 0x38, 0x53, 0xe4, 0x64, 0x27, + 0x1b, 0x5c, 0xc7, 0xda, 0x39, 0x62, 0xdf, 0x97, 0xde, 0x5c, 0x39, 0x46, + 0x21, 0xc2, 0xde, 0xc2, 0xa0, 0x05, 0xa2, 0x0f, 0xee, 0x25, 0x5f, 0xfe, + 0xcf, 0x9b, 0x8c, 0x0f, 0xa9, 0x79, 0xa7, 0x59, 0x7f, 0xf3, 0x7f, 0x3f, + 0x0b, 0x3b, 0xd8, 0xfd, 0x65, 0xff, 0xe6, 0xec, 0x79, 0x37, 0xcd, 0x05, + 0xdc, 0x2a, 0x89, 0xf1, 0x7c, 0x0d, 0x47, 0x16, 0x5f, 0x85, 0x7e, 0xe3, + 0x4b, 0x2e, 0xeb, 0x71, 0x9e, 0x58, 0x08, 0xa8, 0x2c, 0xe0, 0x43, 0x41, + 0xdd, 0xa5, 0x9f, 0x6a, 0x31, 0xc6, 0x27, 0x79, 0x19, 0xe1, 0x4b, 0x7f, + 0xff, 0x00, 0x9c, 0x59, 0xdd, 0x16, 0x0a, 0x59, 0xe3, 0x62, 0xcb, 0x8f, + 0xe5, 0x97, 0xff, 0xd2, 0xd9, 0xb4, 0x89, 0xe7, 0x60, 0xea, 0x74, 0xef, + 0x4f, 0xac, 0xa9, 0x1f, 0xe7, 0xe2, 0xf7, 0xff, 0x8b, 0x6f, 0x41, 0xf7, + 0x86, 0x5f, 0x49, 0x65, 0xff, 0xba, 0x2c, 0x14, 0xb3, 0xc6, 0xc5, 0x97, + 0xfc, 0x2c, 0x14, 0xb3, 0xc6, 0xc5, 0x97, 0xc0, 0x27, 0x16, 0x71, 0xfb, + 0x74, 0xfa, 0xfd, 0xaf, 0xcf, 0xd0, 0xb2, 0x98, 0x7c, 0x60, 0x3b, 0xbf, + 0x66, 0xb3, 0x26, 0x59, 0x66, 0xec, 0x54, 0xd1, 0xe8, 0x6c, 0xb9, 0x1f, + 0x23, 0x23, 0x10, 0x8a, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x59, 0x2f, 0xfc, + 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x13, 0x72, 0xcd, 0xf1, 0x10, 0x0c, + 0x34, 0xa0, 0xa6, 0x12, 0x70, 0xdf, 0xbe, 0x0b, 0xb8, 0x55, 0x12, 0xb2, + 0xff, 0xfb, 0x5b, 0x14, 0x69, 0xc6, 0x4f, 0xa8, 0xde, 0xb2, 0xb4, 0x7f, + 0xc0, 0x2e, 0xbf, 0x14, 0x0d, 0xfc, 0xb2, 0xff, 0x41, 0x94, 0x4a, 0x37, + 0x16, 0x5f, 0x31, 0xe4, 0xdf, 0x0f, 0xec, 0xc8, 0xb8, 0x4d, 0x7e, 0x6e, + 0xf3, 0x73, 0x16, 0x5f, 0x9b, 0xe4, 0xa3, 0xf5, 0x94, 0xdd, 0x13, 0x5b, + 0x23, 0x48, 0xaa, 0xff, 0xdd, 0xc6, 0xf9, 0xa3, 0xd9, 0xd8, 0xb2, 0xff, + 0xf4, 0xf4, 0xd8, 0x6c, 0xf3, 0x1e, 0x47, 0x9a, 0xc5, 0x97, 0xfe, 0x3e, + 0x01, 0x84, 0x7b, 0xc0, 0xeb, 0x2f, 0xff, 0xbd, 0x12, 0x3f, 0xf5, 0x9e, + 0x72, 0x79, 0x2c, 0xbf, 0xa0, 0x9c, 0x2f, 0xba, 0xb2, 0xff, 0xa0, 0x31, + 0xa3, 0xec, 0x05, 0x65, 0x49, 0x1e, 0x5f, 0x1f, 0xb1, 0x3b, 0xa5, 0xd7, + 0x0f, 0x4b, 0x2f, 0xda, 0x0b, 0xb8, 0x55, 0x12, 0xe2, 0xff, 0x8d, 0xe5, + 0xd2, 0x86, 0x71, 0x65, 0xfa, 0x53, 0xe7, 0xe7, 0x59, 0x7a, 0x27, 0xe1, + 0x65, 0xff, 0xfd, 0x80, 0x0f, 0xa0, 0xa2, 0x4e, 0x38, 0xfb, 0x46, 0xb2, + 0xff, 0xdc, 0x32, 0x07, 0x67, 0xcf, 0x7b, 0x16, 0x5e, 0x32, 0x85, 0x95, + 0x24, 0x7f, 0x0c, 0xab, 0x74, 0x79, 0x8b, 0x3b, 0xd0, 0xef, 0xfe, 0x04, + 0x88, 0xe5, 0x1d, 0x20, 0x49, 0x65, 0xfa, 0x39, 0xfb, 0x3a, 0xb2, 0xdd, + 0x33, 0xec, 0xf2, 0x1d, 0x82, 0xb2, 0x9c, 0xdc, 0x7e, 0x4f, 0x7f, 0x4b, + 0xd8, 0x58, 0x15, 0x97, 0xa5, 0xee, 0xac, 0xbb, 0xd0, 0x67, 0x93, 0xe2, + 0xcb, 0xff, 0x88, 0xff, 0xe6, 0xa0, 0xe4, 0xfc, 0x59, 0x58, 0x7d, 0xe6, + 0x59, 0x7f, 0x68, 0xd8, 0x4f, 0x25, 0x97, 0xfe, 0xfa, 0x59, 0xdc, 0xd0, + 0x63, 0x16, 0x5e, 0x79, 0x37, 0x9e, 0x2b, 0xab, 0x71, 0x1a, 0x3c, 0x8f, + 0xb0, 0x5f, 0xc6, 0x8f, 0x19, 0xaf, 0x21, 0xe7, 0xd8, 0x6c, 0x80, 0x83, + 0x70, 0xb2, 0xff, 0xc0, 0x63, 0x7e, 0x41, 0x77, 0x34, 0xb2, 0xff, 0x87, + 0x8d, 0xe3, 0x47, 0xe0, 0x2c, 0xbf, 0xf8, 0x70, 0xdf, 0x73, 0x3e, 0x97, + 0xa3, 0x8b, 0x2c, 0xdc, 0xd7, 0xc7, 0x19, 0x3b, 0x37, 0xc8, 0x5e, 0x81, + 0x03, 0x79, 0xd5, 0xff, 0xcd, 0xde, 0x4d, 0xf3, 0x41, 0x77, 0x0a, 0xa2, + 0x3b, 0x5f, 0xff, 0x02, 0x3e, 0x91, 0xb1, 0xac, 0x21, 0xfa, 0x16, 0x5f, + 0x99, 0xf7, 0x4d, 0xa5, 0x97, 0x6c, 0xdc, 0x67, 0xf4, 0x22, 0x85, 0xfe, + 0xf6, 0x49, 0xbb, 0x97, 0x16, 0x56, 0x1f, 0x37, 0x4c, 0xaf, 0xf9, 0xb3, + 0x3f, 0x82, 0x9f, 0x4a, 0x16, 0x5f, 0xd3, 0xd9, 0x40, 0x70, 0x96, 0x5c, + 0x0f, 0x2c, 0xbf, 0x9b, 0x25, 0x9e, 0x7e, 0xac, 0xbe, 0xc6, 0x6a, 0x16, + 0x5f, 0xb0, 0x8f, 0xf1, 0x16, 0x51, 0x1e, 0x4f, 0x08, 0x6f, 0x66, 0xa1, + 0x65, 0xff, 0xfa, 0x46, 0x3f, 0x1e, 0xf9, 0xd8, 0x3d, 0x18, 0x02, 0xb2, + 0xfa, 0x35, 0x1a, 0x59, 0x46, 0x8b, 0x6d, 0x10, 0xf4, 0x6c, 0x45, 0x9b, + 0xd3, 0x46, 0xcb, 0x2f, 0xa0, 0xbb, 0xc5, 0x97, 0x63, 0x50, 0x6f, 0xf8, + 0x3d, 0x7f, 0xf6, 0x73, 0xb9, 0xe8, 0x2e, 0xf8, 0xd6, 0x5d, 0x3d, 0x71, + 0x65, 0xfd, 0xf9, 0x67, 0xdd, 0xc5, 0x97, 0x6d, 0xba, 0xb2, 0xfc, 0xe2, + 0xfb, 0x3f, 0x59, 0x7f, 0x17, 0xef, 0xa7, 0x11, 0x65, 0x4f, 0x28, 0xbc, + 0x81, 0xcd, 0x8b, 0xb4, 0x36, 0xe5, 0x37, 0xff, 0xff, 0xef, 0x47, 0xbc, + 0xe3, 0x27, 0x9d, 0xcf, 0x47, 0x27, 0x01, 0xe7, 0x4b, 0x37, 0xc2, 0xcb, + 0xf6, 0xb5, 0x1b, 0xf8, 0xb2, 0xff, 0xc7, 0xa9, 0xdc, 0x2c, 0x64, 0x05, + 0x65, 0xfe, 0x07, 0x4d, 0xf7, 0xbc, 0x96, 0x5f, 0xcf, 0xbc, 0x78, 0x42, + 0xac, 0xb4, 0x11, 0xf1, 0x88, 0x69, 0x7f, 0xff, 0xf7, 0xf0, 0x41, 0x7e, + 0xbf, 0xdf, 0xce, 0xc2, 0x38, 0x07, 0xbd, 0x0b, 0x2f, 0xfc, 0x40, 0x94, + 0xec, 0x32, 0x86, 0x2c, 0xbf, 0xb9, 0x23, 0x0b, 0xe9, 0x65, 0xe7, 0x70, + 0xaa, 0x2a, 0x15, 0x41, 0xea, 0x84, 0xb6, 0xfe, 0x89, 0xa4, 0x0f, 0xa4, + 0xb2, 0xff, 0xc7, 0xff, 0x27, 0x73, 0xf7, 0xc9, 0x96, 0x5f, 0xc0, 0x09, + 0x8a, 0xe1, 0x59, 0x7f, 0xd9, 0xec, 0x27, 0xd6, 0x8d, 0x65, 0x49, 0x5b, + 0xd6, 0x42, 0x24, 0x52, 0xa0, 0xc2, 0x8b, 0xe2, 0x73, 0x73, 0xd4, 0x24, + 0xd8, 0x43, 0xe3, 0x02, 0x43, 0xe1, 0x75, 0xed, 0xd7, 0x85, 0x96, 0xc5, + 0x97, 0xff, 0xc6, 0xc8, 0x0c, 0xe0, 0xf8, 0xf7, 0x21, 0x90, 0xb2, 0xa4, + 0x7c, 0x7e, 0x10, 0xbf, 0xe9, 0x74, 0xf4, 0xe1, 0x32, 0x59, 0x7b, 0xfe, + 0x62, 0xcb, 0xfd, 0xc0, 0x4c, 0xe3, 0x79, 0x96, 0x54, 0x8f, 0x4b, 0xe1, + 0xdb, 0xf4, 0x8a, 0x18, 0x2a, 0xcb, 0xff, 0x99, 0x9a, 0x2c, 0x3d, 0xe7, + 0xa7, 0x59, 0x5f, 0x1f, 0x5b, 0x94, 0x5f, 0xf8, 0xb2, 0x6e, 0xfb, 0x34, + 0x7c, 0x59, 0x7f, 0xf3, 0xb2, 0x73, 0x1f, 0xb3, 0xbd, 0xdd, 0x2c, 0xaf, + 0x22, 0x1c, 0x8f, 0xaf, 0xdf, 0x11, 0xb0, 0xd6, 0x5f, 0xa2, 0x7b, 0xdb, + 0x1a, 0x59, 0x7d, 0x3e, 0x0d, 0xc8, 0x59, 0x50, 0x9a, 0x46, 0x42, 0xa4, + 0xc8, 0xb4, 0x4e, 0x02, 0xeb, 0xfb, 0xaf, 0xe9, 0xc3, 0x85, 0x97, 0xfd, + 0xde, 0x09, 0xc7, 0xfa, 0x58, 0xb2, 0xff, 0x3c, 0xa7, 0x70, 0x1f, 0xba, + 0xcb, 0xe8, 0xf4, 0x6c, 0xb2, 0xc6, 0x33, 0xd8, 0xf1, 0xb5, 0x42, 0x3a, + 0x70, 0xbc, 0x10, 0x93, 0xbf, 0x70, 0xfd, 0x83, 0x59, 0x7e, 0x94, 0x77, + 0x36, 0x59, 0x5b, 0xa7, 0x9f, 0xa2, 0x7b, 0xdf, 0x77, 0x16, 0x5f, 0xdc, + 0x32, 0x0f, 0xa1, 0x65, 0x61, 0xf7, 0x68, 0x97, 0xa3, 0xb7, 0xfe, 0xce, + 0xf3, 0x59, 0x37, 0x8c, 0x55, 0x97, 0xff, 0xf1, 0xf3, 0xfc, 0x91, 0x47, + 0xef, 0xef, 0xa5, 0x9d, 0x59, 0x7f, 0xd8, 0x2e, 0x30, 0xe7, 0xe1, 0x8b, + 0x2b, 0x48, 0x97, 0x25, 0xcb, 0xfe, 0x9c, 0xe5, 0x9b, 0x93, 0xbc, 0x05, + 0x97, 0xe6, 0x8f, 0x58, 0x2a, 0xca, 0x84, 0xe3, 0xcc, 0xb9, 0xe1, 0xae, + 0x02, 0x21, 0x0f, 0xef, 0xf7, 0xf1, 0xdc, 0x27, 0xdd, 0x59, 0x7f, 0xa5, + 0x1a, 0xda, 0x35, 0xb2, 0xcb, 0xff, 0x73, 0x0b, 0xbc, 0xc9, 0x1f, 0x96, + 0x5f, 0xdc, 0xcd, 0xe5, 0x1f, 0x2c, 0xb8, 0x1e, 0x59, 0x5c, 0x44, 0x1f, + 0x4f, 0x67, 0xcb, 0xef, 0xff, 0xa3, 0xfc, 0x29, 0xcf, 0xd9, 0xde, 0xcf, + 0xdd, 0x65, 0xc5, 0xb2, 0xca, 0x84, 0xd5, 0x72, 0x17, 0x06, 0x66, 0x4a, + 0x77, 0xff, 0xbd, 0x0c, 0x27, 0x14, 0xb3, 0xff, 0xbf, 0x59, 0x7f, 0x6f, + 0x76, 0x79, 0xa7, 0x59, 0x7f, 0xff, 0x8e, 0x3a, 0xfe, 0x6f, 0xcf, 0x61, + 0x76, 0x77, 0xf2, 0xe2, 0xca, 0x92, 0x38, 0x4d, 0x30, 0x8c, 0x2f, 0x70, + 0x1a, 0x59, 0x78, 0xfc, 0x6b, 0x2f, 0xed, 0x39, 0x7f, 0xfc, 0x2c, 0xac, + 0x44, 0xa1, 0x97, 0x38, 0xe8, 0x83, 0x77, 0xfd, 0x22, 0xce, 0xf3, 0xf3, + 0xf9, 0x65, 0xfa, 0x18, 0xff, 0x75, 0x65, 0xd9, 0xb2, 0xcb, 0xff, 0x87, + 0xcc, 0xd1, 0x67, 0xfc, 0xcd, 0x2c, 0xae, 0x22, 0x0f, 0xa5, 0x00, 0x17, + 0xbf, 0x60, 0x40, 0xfb, 0x2c, 0xaf, 0x1e, 0xc1, 0x18, 0x5f, 0xff, 0x68, + 0x81, 0xf1, 0x61, 0xe8, 0x3e, 0x8e, 0x2c, 0xbf, 0xff, 0x82, 0xfe, 0xc9, + 0x46, 0xa3, 0xfd, 0x40, 0x8e, 0x4b, 0x2a, 0x11, 0x53, 0x89, 0xf7, 0xec, + 0xd9, 0xcb, 0x7a, 0xcb, 0xff, 0xff, 0xfd, 0x12, 0x9d, 0xdf, 0x1c, 0x72, + 0x76, 0x00, 0x31, 0xbe, 0x76, 0x6b, 0x59, 0xbc, 0x0f, 0xe5, 0x97, 0xde, + 0x27, 0xdc, 0x59, 0x50, 0xab, 0x93, 0x23, 0x15, 0xf4, 0x32, 0x88, 0x87, + 0x85, 0x3d, 0x84, 0xbd, 0xfe, 0x7f, 0xb8, 0x6c, 0x80, 0xac, 0xbe, 0xd9, + 0xe3, 0x4b, 0x2f, 0xff, 0xf3, 0xee, 0x9e, 0xde, 0x73, 0xec, 0x78, 0xfb, + 0xcc, 0x1a, 0xca, 0x74, 0x42, 0x00, 0x8a, 0xff, 0xdb, 0xde, 0x58, 0x43, + 0x28, 0x92, 0xcb, 0xb6, 0x85, 0x96, 0x2c, 0x3d, 0x50, 0x1f, 0x5f, 0xf9, + 0xdf, 0xc7, 0xce, 0x01, 0xff, 0x59, 0x66, 0xf3, 0xd3, 0x6e, 0x4a, 0xd9, + 0x3f, 0x9e, 0x4c, 0x27, 0xb1, 0x78, 0x8c, 0xbf, 0x67, 0xc9, 0x16, 0x0e, + 0x1f, 0xd9, 0x2f, 0x20, 0x58, 0x54, 0x06, 0x10, 0x3f, 0x11, 0x1c, 0x22, + 0x75, 0x1e, 0xb3, 0x23, 0x36, 0xf4, 0xa7, 0xf7, 0x4e, 0x28, 0xe2, 0xb9, + 0x1d, 0xa7, 0x65, 0x8f, 0x81, 0x9a, 0x7e, 0x16, 0xe2, 0x3b, 0xee, 0x13, + 0x5f, 0x05, 0xdc, 0x2a, 0x8a, 0xa1, 0x7f, 0xb5, 0x1b, 0xfe, 0x96, 0x75, + 0x65, 0x68, 0xf9, 0x00, 0x5d, 0x7f, 0x85, 0x8e, 0xf3, 0x7e, 0x0d, 0x65, + 0xff, 0x9e, 0x4d, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x6b, 0x5f, 0xf7, 0xa1, + 0x9c, 0xe3, 0x90, 0x56, 0x5f, 0x46, 0xa3, 0xab, 0x2e, 0x7e, 0xac, 0xa1, + 0x9b, 0x76, 0x10, 0xd9, 0xbe, 0x26, 0x68, 0x29, 0x13, 0x0d, 0xbc, 0x98, + 0xed, 0xf7, 0xed, 0x05, 0xdc, 0x2a, 0x8a, 0xb1, 0x7b, 0x50, 0xc5, 0x97, + 0xfd, 0x12, 0x8d, 0x6d, 0x1a, 0xd9, 0x65, 0x9b, 0xe2, 0x22, 0xb4, 0x68, + 0x43, 0x97, 0xfa, 0x3e, 0xec, 0x79, 0xf7, 0x56, 0x5f, 0xda, 0x04, 0xdc, + 0x8e, 0xac, 0xaf, 0x1f, 0x28, 0x0d, 0xaf, 0xfe, 0xff, 0x09, 0xbc, 0xbf, + 0xe6, 0xce, 0x4b, 0x29, 0xb9, 0xf5, 0x41, 0x15, 0xfc, 0x6c, 0x3e, 0xfa, + 0x16, 0x5f, 0x44, 0xd1, 0xf2, 0xca, 0xf1, 0xe7, 0xf0, 0xb2, 0xff, 0xa4, + 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x24, 0x45, 0xfd, 0xb4, 0x6b, 0xce, 0x6b, + 0x2c, 0xdf, 0x11, 0x2c, 0xe4, 0x5c, 0x47, 0xbf, 0x68, 0x2e, 0xe1, 0x54, + 0x5a, 0x4b, 0xff, 0x3c, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0xfa, 0xb3, + 0x7c, 0x44, 0x03, 0x0d, 0x2f, 0xff, 0x37, 0x63, 0xc9, 0xbe, 0x68, 0x2e, + 0xe1, 0x54, 0x50, 0x8b, 0xff, 0xb1, 0xa6, 0xfd, 0x15, 0xf8, 0x7d, 0x85, + 0x97, 0xee, 0x80, 0x2f, 0xc5, 0x97, 0xc7, 0xb4, 0x69, 0x65, 0x30, 0xf2, + 0xbc, 0x51, 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xa3, 0xd7, 0xfd, 0x12, 0x8d, + 0x6d, 0x1a, 0xd9, 0x65, 0xff, 0xff, 0xfe, 0x10, 0xf5, 0x34, 0x7b, 0x59, + 0xe7, 0x13, 0x98, 0x2b, 0x97, 0xf0, 0x51, 0x37, 0xa1, 0x65, 0xf9, 0xf8, + 0x7d, 0x85, 0x97, 0xfd, 0x13, 0x41, 0x44, 0xde, 0x85, 0x95, 0x08, 0xee, + 0x16, 0x12, 0x04, 0x4d, 0x7f, 0xff, 0x60, 0xfd, 0x02, 0x37, 0xe9, 0x3f, + 0xfc, 0xe4, 0x7e, 0x92, 0xf3, 0xc9, 0xbc, 0x2a, 0x11, 0xc2, 0x22, 0x34, + 0xec, 0x65, 0x13, 0xe6, 0x97, 0xff, 0x67, 0x9b, 0xf4, 0x57, 0xe1, 0xf6, + 0x16, 0x59, 0xbe, 0xca, 0xd3, 0x59, 0x2a, 0x8b, 0xac, 0xb5, 0x0e, 0xac, + 0xa3, 0x69, 0xf4, 0xf9, 0x31, 0xe4, 0x66, 0x82, 0xd6, 0xe8, 0xc1, 0x96, + 0x95, 0xf4, 0x37, 0x0e, 0x30, 0x59, 0xa3, 0x08, 0xf4, 0x2d, 0xbf, 0x2a, + 0xec, 0xba, 0x7b, 0xff, 0xe6, 0x83, 0x3d, 0x36, 0x7f, 0x6d, 0xea, 0x79, + 0x9d, 0x3b, 0xd3, 0xeb, 0x2f, 0xff, 0xfd, 0xb9, 0xd9, 0xec, 0x6c, 0x9e, + 0xc8, 0xdb, 0x5f, 0x5b, 0x7a, 0x9e, 0x67, 0x4e, 0xf4, 0xfa, 0xca, 0xf2, + 0x60, 0x0d, 0x37, 0x5f, 0xfd, 0xe8, 0x3d, 0x1b, 0x6c, 0x3e, 0x86, 0x96, + 0x5f, 0xfb, 0x8d, 0xb3, 0x16, 0x74, 0xef, 0x4f, 0xb7, 0x83, 0xed, 0x09, + 0x2d, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0x22, 0x5f, 0xee, 0x02, 0x26, 0xe9, + 0xef, 0x59, 0x66, 0xf8, 0x7d, 0x53, 0x1a, 0x5f, 0xd9, 0xa0, 0xbb, 0x85, + 0x51, 0x15, 0xaf, 0xfb, 0x75, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x57, 0x0a, + 0x6e, 0x88, 0x37, 0x39, 0xbe, 0xf3, 0xe8, 0xd6, 0x5f, 0xbb, 0xf8, 0x85, + 0x25, 0x97, 0xef, 0xe0, 0x82, 0xde, 0x0f, 0x2d, 0xc8, 0x6f, 0x9b, 0xed, + 0x36, 0xea, 0xcb, 0x1f, 0xc7, 0xd3, 0xf9, 0xfd, 0xfe, 0x6c, 0xcf, 0x45, + 0x01, 0x7f, 0x96, 0x5f, 0x67, 0x9f, 0xab, 0x2f, 0xf6, 0x1e, 0xbf, 0xfc, + 0x1c, 0x59, 0x66, 0xc9, 0x1e, 0xb7, 0x08, 0x6f, 0xed, 0x47, 0x5c, 0xb1, + 0x65, 0xff, 0xfb, 0x87, 0xe7, 0x39, 0x19, 0x74, 0x1e, 0xd6, 0x2c, 0xa3, + 0x3f, 0xee, 0x95, 0xdf, 0xd1, 0xff, 0x3a, 0x50, 0xb2, 0xf8, 0x2e, 0xe1, + 0x54, 0x53, 0xcb, 0xff, 0x14, 0x6f, 0x3e, 0xca, 0x30, 0x96, 0x56, 0x8f, + 0xaf, 0xa5, 0xd7, 0xf8, 0x7f, 0xce, 0x8d, 0xd8, 0x92, 0xcb, 0xfc, 0xc3, + 0x18, 0x1f, 0x52, 0x59, 0x73, 0x0d, 0x65, 0x41, 0xe4, 0x84, 0xce, 0xff, + 0xfe, 0x8e, 0xf0, 0x0c, 0x09, 0xff, 0x19, 0xe3, 0x00, 0x56, 0x5f, 0xd0, + 0xce, 0x41, 0xec, 0xb2, 0xa4, 0x88, 0x8e, 0xad, 0xdf, 0xff, 0xcf, 0xa7, + 0xf7, 0xd2, 0xce, 0xe1, 0x03, 0xa0, 0x15, 0x65, 0xf4, 0xa3, 0x36, 0x59, + 0x7f, 0xfe, 0xd0, 0x35, 0xa8, 0x2c, 0x14, 0xfd, 0xec, 0x11, 0x65, 0x05, + 0x1a, 0x9f, 0x2e, 0x68, 0x8a, 0xf1, 0x97, 0x56, 0x5f, 0xfc, 0xf2, 0x6e, + 0xe3, 0xc9, 0x7a, 0x02, 0xb2, 0xff, 0xc7, 0xa7, 0xd8, 0x60, 0x7d, 0x49, + 0x65, 0xff, 0xa5, 0x85, 0xfc, 0x30, 0xf5, 0x25, 0x97, 0xff, 0xcc, 0xc3, + 0xe8, 0x3b, 0x12, 0x31, 0xe0, 0xd6, 0x51, 0xa2, 0x33, 0xf3, 0xeb, 0xfa, + 0x35, 0xb4, 0x6b, 0x65, 0x97, 0xf4, 0x6c, 0xe3, 0x7e, 0xac, 0xa9, 0x1e, + 0xe6, 0xe1, 0x7d, 0xf4, 0xe2, 0x79, 0x2c, 0xbf, 0x3e, 0x78, 0xd8, 0xb2, + 0xb8, 0x79, 0x3b, 0xc8, 0xea, 0x11, 0x25, 0x8d, 0xd7, 0xfd, 0xbd, 0xd9, + 0x38, 0x7e, 0x36, 0x2c, 0xbf, 0xe2, 0x86, 0x44, 0x8b, 0x18, 0xb2, 0xff, + 0xf3, 0x23, 0x5b, 0x47, 0xd8, 0x06, 0xde, 0x0d, 0x65, 0x62, 0x30, 0x8c, + 0xf5, 0xcd, 0xaf, 0xf1, 0x07, 0x37, 0xe7, 0x78, 0xb2, 0xe7, 0x15, 0x65, + 0x41, 0xe5, 0xe8, 0xd2, 0xfc, 0xfb, 0x46, 0x80, 0xb2, 0xa7, 0x96, 0x42, + 0x4c, 0xf7, 0x09, 0x28, 0x85, 0x4c, 0x88, 0x72, 0x13, 0x02, 0x91, 0x06, + 0x10, 0x5f, 0x42, 0xa4, 0xe1, 0xef, 0x31, 0x8b, 0x06, 0xfc, 0x8a, 0xf0, + 0xcb, 0x28, 0xc4, 0x79, 0x0e, 0x3d, 0xef, 0x13, 0xe4, 0x37, 0xff, 0x9e, + 0x4d, 0xc6, 0x4e, 0xd7, 0x39, 0x1f, 0xac, 0xbf, 0xfd, 0xff, 0xe0, 0xe3, + 0x71, 0x09, 0xd8, 0x50, 0xb2, 0xff, 0xe2, 0xcf, 0xdc, 0xbf, 0x6e, 0xc3, + 0x62, 0xca, 0xd2, 0x25, 0x3a, 0x9d, 0x7e, 0x7f, 0x78, 0xe6, 0x59, 0x7f, + 0xf6, 0x70, 0xcb, 0xf2, 0xce, 0xfb, 0x16, 0x5f, 0xf8, 0xcb, 0xf2, 0xce, + 0xfb, 0x1b, 0xf8, 0xfa, 0x84, 0x28, 0xa6, 0xea, 0xba, 0x87, 0x19, 0x29, + 0xc3, 0x87, 0x90, 0x8d, 0xb6, 0x2c, 0xbf, 0x14, 0x01, 0xb7, 0xc5, 0x97, + 0xf0, 0xba, 0x71, 0xb9, 0x2c, 0xa1, 0x9f, 0x46, 0x08, 0x39, 0x5d, 0xff, + 0xc6, 0x3d, 0x3f, 0xcd, 0xf5, 0xa8, 0xd9, 0x65, 0x37, 0x3f, 0x5f, 0x16, + 0xdc, 0x38, 0x59, 0x7f, 0xfe, 0xf4, 0x6b, 0x24, 0x6f, 0xdc, 0x19, 0x80, + 0x29, 0x2f, 0xd1, 0x2f, 0xc1, 0x25, 0x97, 0xc1, 0x77, 0x0a, 0xa2, 0xb3, + 0x54, 0xc7, 0xaf, 0xa2, 0x9b, 0xee, 0x9b, 0x38, 0xb2, 0xee, 0x0a, 0xb2, + 0xf7, 0x00, 0x15, 0x94, 0x29, 0xb5, 0xe0, 0xc5, 0xf7, 0x35, 0x1b, 0xd6, + 0x5f, 0xdb, 0xc8, 0xa3, 0x76, 0x65, 0x97, 0xe6, 0x41, 0x4b, 0x8b, 0x2a, + 0x0f, 0x67, 0x63, 0x2b, 0xee, 0xf0, 0x4e, 0x2c, 0xbe, 0xe7, 0x21, 0xa5, + 0x96, 0x75, 0x95, 0x87, 0xae, 0x64, 0x9c, 0x23, 0xbb, 0x63, 0x59, 0x7f, + 0xb9, 0x31, 0x97, 0xb3, 0xe5, 0x95, 0x07, 0x97, 0x82, 0xf7, 0x3f, 0x56, + 0x5f, 0x7e, 0x21, 0x49, 0x65, 0x0c, 0xdd, 0x74, 0x5a, 0xff, 0xfa, 0x19, + 0xdc, 0xc2, 0xef, 0x3d, 0x9f, 0xba, 0xcb, 0xfc, 0xc8, 0xfb, 0x9a, 0x8d, + 0xeb, 0x2e, 0x7e, 0x2c, 0xa8, 0x3c, 0xc0, 0x1b, 0x5f, 0xe9, 0x18, 0x37, + 0x67, 0x9f, 0xd8, 0xb2, 0xfc, 0x51, 0xe3, 0xc5, 0x95, 0x87, 0xc4, 0xe7, + 0xb6, 0x35, 0x97, 0xf7, 0xb0, 0x89, 0xfa, 0xb3, 0x85, 0x85, 0xf8, 0xbb, + 0xe8, 0xde, 0xb2, 0xf3, 0xbb, 0x16, 0x5f, 0xb2, 0x6d, 0x81, 0xfa, 0xcb, + 0xfb, 0xb1, 0xaf, 0xe2, 0x65, 0x96, 0x6f, 0x0b, 0xad, 0x43, 0x16, 0xc8, + 0x55, 0x7c, 0x46, 0x6a, 0xf3, 0x11, 0x69, 0xf5, 0x8f, 0x3e, 0x78, 0x75, + 0xaf, 0xc8, 0x4a, 0x13, 0x5c, 0x84, 0x1f, 0x4c, 0x00, 0x75, 0xbc, 0xa4, + 0x41, 0xbd, 0xc2, 0xbb, 0xfe, 0x3e, 0xbe, 0xb6, 0x83, 0x1a, 0xcb, 0xfa, + 0x19, 0xb0, 0x35, 0x25, 0x95, 0xe3, 0xe7, 0xe9, 0xc5, 0xfe, 0xdb, 0xd8, + 0x1f, 0x46, 0x96, 0x5f, 0xf4, 0x14, 0xb8, 0xc7, 0x21, 0x56, 0x54, 0x1f, + 0x67, 0x4d, 0x2e, 0x81, 0xac, 0xbf, 0xe0, 0x33, 0x03, 0xaf, 0x9d, 0xa5, + 0x95, 0x23, 0xf2, 0xe1, 0x0f, 0x45, 0xaf, 0xf6, 0xa0, 0x5c, 0x23, 0x15, + 0x65, 0xff, 0xfb, 0x07, 0xe8, 0xe4, 0xec, 0x3d, 0x1b, 0x20, 0x55, 0x97, + 0xff, 0xa1, 0xa1, 0x18, 0x59, 0xf7, 0x63, 0x1a, 0x59, 0x7e, 0xe9, 0x96, + 0x31, 0x65, 0xf4, 0x9c, 0x9b, 0xe2, 0xa2, 0xaf, 0xa3, 0x00, 0xd1, 0x79, + 0x19, 0x88, 0xad, 0xb8, 0x99, 0x7f, 0xe7, 0x93, 0x7c, 0xd0, 0x5d, 0xc2, + 0xa8, 0x91, 0x57, 0xff, 0xee, 0xfb, 0x09, 0xbf, 0x5d, 0xfe, 0xe0, 0xa2, + 0xba, 0xcb, 0xff, 0x3f, 0x5b, 0xc1, 0xe8, 0xfa, 0x05, 0x94, 0xdd, 0x1c, + 0x90, 0x98, 0xeb, 0x37, 0xfd, 0xad, 0x1b, 0x59, 0x31, 0xb1, 0x65, 0xf0, + 0x63, 0xd0, 0xb2, 0x8c, 0xf6, 0xc8, 0xea, 0xfd, 0xa0, 0xbb, 0x85, 0x51, + 0x64, 0xaa, 0x0f, 0x53, 0x08, 0x2f, 0xdb, 0x19, 0x4b, 0x8b, 0x2f, 0xf0, + 0x36, 0xf4, 0x77, 0x86, 0xb2, 0xfe, 0x00, 0xcf, 0x4f, 0xc5, 0x97, 0x1f, + 0x56, 0x5f, 0xbb, 0xc1, 0x38, 0xdf, 0x11, 0x3f, 0xa2, 0x8e, 0x1a, 0x74, + 0xb6, 0x9b, 0xa6, 0x39, 0x90, 0xbf, 0xbe, 0xc6, 0xb9, 0xe5, 0x97, 0xfb, + 0x70, 0xd9, 0x1e, 0x10, 0x6b, 0x2f, 0xef, 0x1e, 0xa2, 0x5c, 0x59, 0x7f, + 0x33, 0xa7, 0xe7, 0x62, 0xcb, 0xff, 0xc7, 0xdf, 0x67, 0x70, 0xa3, 0xb0, + 0x35, 0x95, 0x07, 0xe8, 0xe5, 0xb7, 0xfc, 0xe7, 0xd0, 0x69, 0xf8, 0xdf, + 0x64, 0xc6, 0xf0, 0x8c, 0x26, 0xfe, 0x85, 0x0d, 0xff, 0xfe, 0xeb, 0xfd, + 0xfb, 0x71, 0x94, 0x7d, 0x2e, 0x99, 0x7f, 0xc5, 0x97, 0xf6, 0x68, 0x2e, + 0xe1, 0x54, 0x5b, 0x0b, 0xff, 0xf7, 0xa7, 0x16, 0x6b, 0xbc, 0xf4, 0xd3, + 0xa7, 0x7a, 0x7d, 0x65, 0xfb, 0xcf, 0xa3, 0x62, 0xca, 0x6c, 0xa2, 0x20, + 0x2c, 0x37, 0xb4, 0x0f, 0x2c, 0xbb, 0xad, 0xe0, 0xf1, 0x34, 0x4f, 0x4d, + 0xd3, 0x34, 0x38, 0xc1, 0x6f, 0x67, 0x31, 0x65, 0xf0, 0x5d, 0xc2, 0xa8, + 0xb6, 0xd7, 0x3e, 0x96, 0x5a, 0x4b, 0x2d, 0xcd, 0x8d, 0x38, 0x05, 0xab, + 0x47, 0xfb, 0xd5, 0x5b, 0xf8, 0xfd, 0xd3, 0xfc, 0x0b, 0x2f, 0xfa, 0x25, + 0x1a, 0xda, 0x35, 0xb2, 0xcb, 0xe8, 0x9b, 0x86, 0xb2, 0xa4, 0x9a, 0x26, + 0x42, 0x62, 0x62, 0x22, 0x2e, 0xdc, 0x3a, 0xbf, 0xf0, 0xf4, 0x60, 0xd9, + 0xbf, 0x35, 0xd5, 0x94, 0xdd, 0x12, 0xb8, 0xa5, 0x7b, 0xd9, 0xb2, 0xcb, + 0xf9, 0xc7, 0x98, 0x42, 0xac, 0xbe, 0xc2, 0xfd, 0xbc, 0xc7, 0x92, 0xc1, + 0xdb, 0xa7, 0xb2, 0x59, 0x7a, 0x7b, 0xf0, 0xd6, 0x5e, 0xdb, 0xc6, 0xb2, + 0xff, 0xd3, 0xdc, 0xf6, 0xda, 0xbd, 0xf6, 0x14, 0x7e, 0xb2, 0xf4, 0xf1, + 0x9e, 0x13, 0xc5, 0x65, 0xfb, 0xb1, 0xe7, 0x99, 0x65, 0xfd, 0x05, 0x21, + 0x81, 0x8b, 0x2f, 0xf7, 0x8c, 0x49, 0xb8, 0x0f, 0x96, 0x5f, 0xf6, 0x6b, + 0x51, 0x27, 0xf9, 0xa5, 0x97, 0xc1, 0x77, 0x0a, 0xa2, 0xf0, 0x5f, 0xbb, + 0xf0, 0x4f, 0x4b, 0x2f, 0xfc, 0x7f, 0xf2, 0x77, 0x3f, 0x7c, 0x99, 0x65, + 0xff, 0x9f, 0xb1, 0xbf, 0x5a, 0xcf, 0xb8, 0xb2, 0xb4, 0x8d, 0xb6, 0x17, + 0x78, 0xa4, 0x90, 0xef, 0xda, 0xeb, 0x6b, 0xc9, 0x96, 0x5c, 0xfc, 0x59, + 0x7a, 0x7f, 0x3c, 0xb2, 0xbf, 0x36, 0xbd, 0x16, 0xbf, 0x32, 0x0a, 0x4e, + 0xb2, 0xfe, 0x82, 0xee, 0xf7, 0x0a, 0xcb, 0xfa, 0x51, 0xbe, 0x35, 0xf2, + 0xcb, 0xff, 0xf9, 0xf6, 0x9a, 0x51, 0xad, 0xbb, 0x2c, 0xdd, 0x72, 0xfd, + 0x65, 0x49, 0x17, 0xf8, 0x5c, 0xe6, 0x17, 0xf7, 0x5f, 0xd3, 0x87, 0x0b, + 0x2f, 0xf9, 0xfb, 0xc8, 0x31, 0xe3, 0x16, 0x54, 0x1f, 0x31, 0x17, 0xdf, + 0x16, 0x18, 0xab, 0x2f, 0xf9, 0x9f, 0x4b, 0xb8, 0x5d, 0xe2, 0xca, 0x61, + 0xed, 0x88, 0x43, 0x76, 0x0a, 0xb2, 0xfc, 0xf2, 0xe9, 0xec, 0xb2, 0xa0, + 0xf8, 0xb0, 0x8c, 0xc5, 0xee, 0xc6, 0x2c, 0xbf, 0x1f, 0x86, 0x7c, 0x59, + 0x63, 0xd8, 0xde, 0xc8, 0x5a, 0xfd, 0x85, 0xfe, 0xe4, 0x2c, 0xbf, 0x40, + 0x80, 0x7e, 0x2c, 0xba, 0x61, 0xac, 0xbf, 0xf9, 0x90, 0x19, 0xd8, 0x40, + 0x96, 0x71, 0x65, 0xfe, 0xf1, 0xb4, 0x2e, 0x9d, 0xa5, 0x97, 0xff, 0xfe, + 0x73, 0xeb, 0xb1, 0xcb, 0xf0, 0xf8, 0xf5, 0x21, 0x25, 0x84, 0xb2, 0xfe, + 0x8f, 0xbe, 0x93, 0xfc, 0xb2, 0x86, 0x99, 0x76, 0x0c, 0x05, 0x17, 0xc6, + 0xc0, 0x6b, 0xbf, 0xce, 0x5f, 0xf6, 0x7f, 0x3c, 0xb2, 0xff, 0xbc, 0xf2, + 0x61, 0xc6, 0xa4, 0xb2, 0xd9, 0xba, 0x7d, 0xde, 0x36, 0xb9, 0x9c, 0x59, + 0x7d, 0x05, 0x2d, 0xd5, 0x97, 0xff, 0xbe, 0xfd, 0xcb, 0xf2, 0xcd, 0xfe, + 0xc1, 0xac, 0xbf, 0xf8, 0xdf, 0xfe, 0x9f, 0x3d, 0x05, 0xd5, 0x97, 0xd9, + 0xd6, 0x82, 0xb2, 0xe8, 0xfd, 0x65, 0x49, 0x32, 0xfc, 0x2a, 0x14, 0x5c, + 0xc9, 0x3c, 0x9b, 0xc4, 0x3e, 0x91, 0xdf, 0xe7, 0xf3, 0x88, 0xcc, 0xe2, + 0xcb, 0x05, 0x65, 0xc1, 0x92, 0xca, 0x09, 0xa8, 0xe8, 0x8d, 0x36, 0x19, + 0x4d, 0x2d, 0x91, 0xb9, 0xec, 0x8a, 0x78, 0x8e, 0xcf, 0x05, 0x08, 0x2f, + 0xd8, 0xa2, 0x45, 0xa3, 0x37, 0xc8, 0xc5, 0x45, 0x3c, 0xf9, 0x90, 0xc8, + 0xa6, 0x87, 0x2b, 0x21, 0x29, 0xe8, 0x6e, 0xbb, 0x4f, 0xe4, 0xe4, 0x55, + 0xc8, 0xd5, 0xbb, 0x1f, 0x3e, 0xf6, 0x49, 0xf5, 0xbb, 0xdd, 0x81, 0xac, + 0xbe, 0xfd, 0xf5, 0xc5, 0x97, 0x7c, 0xde, 0x0d, 0xfe, 0x0e, 0x5f, 0xef, + 0xdb, 0xcd, 0x28, 0x2e, 0xac, 0xa6, 0xea, 0x86, 0xe2, 0x3b, 0x6d, 0x17, + 0x5f, 0x10, 0xc4, 0xf9, 0x65, 0xd9, 0xf2, 0xcb, 0xe7, 0xe0, 0x36, 0x59, + 0x46, 0x6e, 0xb4, 0x2f, 0x7c, 0x17, 0x70, 0xaa, 0x2f, 0x45, 0xff, 0x8f, + 0xbe, 0x27, 0x97, 0xef, 0xfa, 0xca, 0xd1, 0xf6, 0xb0, 0xba, 0xfd, 0x9d, + 0x28, 0xd9, 0x65, 0xff, 0x9c, 0xfa, 0x0d, 0x3e, 0xfc, 0x1a, 0xcb, 0xf4, + 0x31, 0xfd, 0x0b, 0x2f, 0x30, 0xfa, 0xb2, 0xff, 0x9f, 0xb2, 0x81, 0x7a, + 0x7b, 0x2c, 0xad, 0x1f, 0xe3, 0x09, 0x88, 0x72, 0xfd, 0x8c, 0xf3, 0x8d, + 0x65, 0x42, 0xa4, 0x41, 0xaf, 0x64, 0x23, 0x74, 0x45, 0xe2, 0x71, 0x21, + 0x77, 0xb8, 0x5d, 0x7f, 0xf6, 0x0f, 0xd8, 0xc6, 0x3c, 0xb0, 0x96, 0x5e, + 0x04, 0x7e, 0xb2, 0xfb, 0x7e, 0x13, 0x71, 0x9e, 0xfe, 0x90, 0xaf, 0xf3, + 0x7e, 0xe4, 0xd2, 0x71, 0xac, 0xa3, 0x3f, 0x2f, 0x1e, 0x53, 0x74, 0xd9, + 0x32, 0x34, 0x3a, 0x6d, 0x1d, 0x8f, 0x9c, 0x43, 0x3b, 0x68, 0x56, 0xca, + 0x1b, 0x19, 0x4b, 0x5c, 0x0c, 0x65, 0xa7, 0x3b, 0x1d, 0x34, 0xba, 0x2d, + 0x46, 0xbc, 0xc8, 0xfe, 0xfd, 0x1a, 0x2b, 0xb9, 0x7f, 0x1c, 0xd1, 0x4a, + 0x04, 0xe3, 0x68, 0x29, 0x98, 0x0d, 0x4b, 0xe1, 0xb8, 0x1e, 0x59, 0x7f, + 0xf0, 0x22, 0x62, 0xcd, 0xee, 0xc6, 0x1a, 0xcb, 0xec, 0xf3, 0xf5, 0x65, + 0xfe, 0xc3, 0xd7, 0xff, 0x83, 0x8b, 0x2c, 0xd9, 0x15, 0x13, 0x64, 0x8b, + 0xc2, 0x1b, 0xff, 0xdd, 0xe0, 0x23, 0x99, 0xd3, 0x63, 0xc9, 0x65, 0xe8, + 0xfb, 0x65, 0x94, 0x47, 0xcf, 0xd4, 0x9b, 0xfe, 0xef, 0x33, 0xe9, 0x78, + 0x02, 0xac, 0xbd, 0xc8, 0x99, 0x65, 0x19, 0xfd, 0x11, 0x0f, 0x0f, 0x2f, + 0x13, 0xf9, 0x65, 0xed, 0xf1, 0x25, 0x97, 0xbd, 0x9c, 0x59, 0x7f, 0xc4, + 0x62, 0xf7, 0xe0, 0x9f, 0x96, 0x54, 0x1f, 0xa0, 0xc7, 0xb0, 0x72, 0xfe, + 0x7d, 0x48, 0x80, 0xeb, 0x2e, 0x36, 0x2c, 0xa0, 0x1e, 0x16, 0xe1, 0x65, + 0xed, 0xef, 0xf2, 0xcb, 0xce, 0xe1, 0x54, 0x44, 0x6b, 0xe1, 0x42, 0xfa, + 0x59, 0x41, 0x3c, 0xb3, 0x28, 0xbf, 0x4f, 0x7d, 0xe3, 0xfe, 0xb2, 0xfc, + 0x6e, 0xcc, 0xd2, 0xcb, 0xc1, 0x11, 0x8b, 0x2f, 0xff, 0xfa, 0x26, 0xec, + 0x73, 0x51, 0xd3, 0x09, 0x8e, 0x70, 0xa4, 0xeb, 0x2a, 0x11, 0x0c, 0x43, + 0xd7, 0xff, 0x1f, 0x3d, 0x82, 0x8a, 0xf3, 0x87, 0xf2, 0xca, 0x34, 0xf3, + 0xf4, 0xd4, 0xc2, 0x1f, 0x17, 0x76, 0x15, 0x9b, 0xc8, 0x6f, 0xff, 0xbc, + 0x0e, 0x73, 0x18, 0x59, 0xbe, 0x74, 0x4f, 0x4b, 0x2f, 0xf6, 0xda, 0xce, + 0xf4, 0xfa, 0xb2, 0x96, 0x5f, 0xfd, 0xec, 0xe9, 0x44, 0xd3, 0x85, 0x27, + 0x59, 0x70, 0x22, 0x63, 0xd1, 0xe8, 0x5d, 0x42, 0x2c, 0xf9, 0x08, 0x5b, + 0xc5, 0x1f, 0x2c, 0xa9, 0x1e, 0x18, 0xc9, 0xee, 0x7f, 0x2c, 0xbf, 0xda, + 0xce, 0x63, 0x96, 0xcb, 0x28, 0x67, 0x91, 0x82, 0xd7, 0x0d, 0x8b, 0x2f, + 0xfe, 0xc6, 0x81, 0x1b, 0x74, 0x71, 0xde, 0x2c, 0xac, 0x3d, 0xd2, 0x17, + 0xbf, 0xfd, 0x23, 0x14, 0x57, 0xe4, 0x1e, 0xcf, 0xd5, 0x97, 0xff, 0xf3, + 0x9f, 0x63, 0xc5, 0x9d, 0xf1, 0xfb, 0x0d, 0xa5, 0x94, 0x68, 0xa3, 0xf2, + 0x5d, 0xff, 0xff, 0xee, 0xf4, 0xd9, 0xa3, 0xcf, 0x88, 0x1d, 0x8f, 0x4e, + 0xde, 0x7d, 0x9a, 0x16, 0x5f, 0xec, 0x32, 0x3d, 0x6b, 0x16, 0x5f, 0xf6, + 0x39, 0x7f, 0xa0, 0x47, 0xeb, 0x2a, 0x13, 0x07, 0xc2, 0x2f, 0x9f, 0x34, + 0x61, 0x7f, 0xfe, 0x66, 0xe9, 0xbb, 0xcd, 0xdf, 0x64, 0xbc, 0x7b, 0xd6, + 0x5f, 0xdf, 0x4b, 0x40, 0x8f, 0xd6, 0x5f, 0xbb, 0x1a, 0x8f, 0xd6, 0x5f, + 0xcc, 0xcd, 0x4a, 0x18, 0xb2, 0xa1, 0x1b, 0xf8, 0xb4, 0x66, 0x0e, 0x51, + 0x76, 0xcc, 0x49, 0x73, 0x4d, 0x24, 0xbf, 0xf3, 0x70, 0xf8, 0xe6, 0xf4, + 0x6c, 0xdf, 0xf3, 0x62, 0xd0, 0xc5, 0xf6, 0xf8, 0xec, 0x2c, 0xbf, 0x73, + 0x3c, 0x58, 0xb2, 0xa4, 0x8a, 0x63, 0x5d, 0xe9, 0x1d, 0xf9, 0xd9, 0x9f, + 0x75, 0x65, 0xee, 0x3e, 0xea, 0xca, 0xc3, 0xc7, 0x32, 0x8b, 0xff, 0xef, + 0x18, 0xbc, 0x3c, 0x2f, 0xc7, 0xa7, 0x0a, 0xcb, 0xfd, 0x1e, 0xc9, 0xa4, + 0xfb, 0x2c, 0xbf, 0xf8, 0xb3, 0xe9, 0x70, 0x67, 0xbe, 0x06, 0xb2, 0x86, + 0x8c, 0xa0, 0x28, 0x6e, 0x1a, 0x5f, 0xb8, 0x7a, 0x7f, 0xd6, 0x5f, 0xef, + 0xe3, 0x7b, 0xfb, 0xc6, 0xb2, 0x9b, 0x9e, 0xef, 0x4a, 0x2f, 0xfb, 0x80, + 0xd6, 0x8e, 0x0f, 0x4b, 0x2b, 0x11, 0xd8, 0xf0, 0x90, 0xe9, 0x25, 0xb7, + 0x56, 0x5f, 0x17, 0xd3, 0x42, 0xcb, 0xfe, 0x8c, 0xfa, 0x1e, 0x4f, 0x25, + 0x97, 0xf6, 0x7f, 0x38, 0xb3, 0xf5, 0x97, 0x30, 0x2b, 0x28, 0x67, 0x8e, + 0xe6, 0x17, 0xfe, 0xc6, 0xa7, 0x38, 0xce, 0x63, 0x85, 0x97, 0xcc, 0xef, + 0xee, 0xb2, 0xf8, 0xb1, 0xf7, 0x56, 0x5f, 0xa1, 0xa1, 0x3d, 0x0b, 0x29, + 0xb0, 0x9c, 0x2c, 0x0a, 0x0c, 0x8f, 0x1f, 0xb4, 0x42, 0xe8, 0x1c, 0x23, + 0x10, 0x8e, 0xfc, 0x59, 0xe7, 0xea, 0xcb, 0xd3, 0xb8, 0x15, 0x95, 0xba, + 0x78, 0xbe, 0x26, 0xbf, 0xc6, 0xff, 0xeb, 0x40, 0xf2, 0xcb, 0xfa, 0x05, + 0xc2, 0x31, 0x56, 0x5f, 0xd9, 0xdd, 0xce, 0xe7, 0x96, 0x56, 0x22, 0xdb, + 0xc4, 0xa4, 0x69, 0xc2, 0xdb, 0xff, 0xf8, 0x2d, 0xf4, 0xe4, 0x7d, 0x6f, + 0xa8, 0xe9, 0x3f, 0xeb, 0x2f, 0xfe, 0xe9, 0x47, 0xef, 0xdf, 0xc4, 0x29, + 0x2c, 0xbf, 0xfb, 0xc7, 0xa9, 0xce, 0x33, 0x98, 0xe1, 0x65, 0x4f, 0x2d, + 0x90, 0x6c, 0xf7, 0x0b, 0x28, 0x8c, 0x6f, 0x62, 0xd9, 0x42, 0x3c, 0x6d, + 0xf9, 0x1f, 0xa0, 0x57, 0xbe, 0x8d, 0x38, 0xda, 0xa6, 0x7e, 0xd4, 0x79, + 0x5e, 0x8c, 0x71, 0xe3, 0x10, 0xfd, 0xd8, 0xa3, 0xd6, 0xe4, 0x77, 0x1d, + 0x8d, 0xbf, 0x79, 0xd8, 0x8b, 0xdb, 0x88, 0xd7, 0x84, 0xf7, 0x96, 0x5d, + 0xb9, 0xe5, 0x97, 0xed, 0x05, 0xdc, 0x2a, 0x88, 0xb9, 0x7e, 0xef, 0xe2, + 0x14, 0x92, 0x5f, 0xb9, 0xdf, 0x47, 0xeb, 0x2f, 0xcf, 0xb4, 0x68, 0x0b, + 0x2c, 0xde, 0x11, 0xcf, 0xb0, 0xf6, 0x0d, 0x39, 0xa0, 0x0a, 0xa7, 0xca, + 0x69, 0xba, 0xab, 0x59, 0x4a, 0x55, 0xbf, 0xf9, 0x8f, 0x26, 0xf9, 0xa0, + 0xbb, 0x85, 0x51, 0x33, 0x2f, 0xfe, 0x1b, 0x72, 0x03, 0x88, 0x3f, 0x18, + 0xab, 0x2f, 0x78, 0xf8, 0xb2, 0xf9, 0xb3, 0xc8, 0xea, 0xcb, 0xfa, 0x38, + 0x51, 0xde, 0x2c, 0xa6, 0xd5, 0x3d, 0x26, 0x13, 0x54, 0xf2, 0x89, 0x76, + 0xd1, 0xba, 0xfb, 0x01, 0xad, 0x96, 0x5d, 0x3e, 0xd8, 0x59, 0x53, 0xd9, + 0xe0, 0xb6, 0xa9, 0x1d, 0xfc, 0xda, 0xdb, 0x0d, 0x86, 0xcf, 0x21, 0x65, + 0xd1, 0xc5, 0x94, 0xda, 0xa7, 0xad, 0xf2, 0x05, 0xff, 0xe6, 0xcf, 0x65, + 0x9e, 0xf3, 0xc8, 0xcc, 0x96, 0x5e, 0x09, 0xb1, 0x65, 0xff, 0x36, 0x3f, + 0x60, 0x9a, 0x3d, 0xa1, 0x65, 0xf4, 0xf7, 0xd7, 0x25, 0x97, 0xe6, 0xd7, + 0x3d, 0x4f, 0x0e, 0x42, 0xcb, 0xa7, 0xdb, 0x0b, 0x2e, 0xc0, 0x2c, 0xbf, + 0xfa, 0x18, 0xfd, 0xcf, 0xbf, 0x60, 0x9e, 0x59, 0x7f, 0xf8, 0x3a, 0x06, + 0xdf, 0x70, 0xf4, 0x1f, 0x62, 0xcb, 0xe3, 0xde, 0xff, 0xac, 0xa8, 0x3f, + 0x1e, 0xa6, 0xd7, 0x91, 0xb2, 0x50, 0xb8, 0xbe, 0x3d, 0x80, 0x4b, 0x2f, + 0xd9, 0xb6, 0x7f, 0x32, 0xca, 0x9c, 0x79, 0x9d, 0x22, 0xbf, 0xd3, 0xb0, + 0x9f, 0x5f, 0xcf, 0xac, 0xbf, 0xff, 0x31, 0xfd, 0x9f, 0x7f, 0xe6, 0x9f, + 0xd3, 0x78, 0x0b, 0x2f, 0xec, 0xe4, 0x08, 0x50, 0xb2, 0x86, 0x8b, 0xec, + 0x38, 0xf2, 0xd5, 0xb1, 0x65, 0xce, 0xc5, 0x95, 0xc3, 0x4d, 0xb8, 0x21, + 0x76, 0xe7, 0x56, 0x59, 0xb6, 0xb2, 0xdf, 0xc1, 0xaf, 0x00, 0xd5, 0xff, + 0xff, 0x14, 0x04, 0xa1, 0x93, 0xa3, 0x5a, 0x3d, 0xfa, 0x77, 0xfd, 0x65, + 0xfc, 0x6d, 0x1f, 0x30, 0x2b, 0x28, 0xd1, 0x2b, 0xc6, 0x8b, 0xe3, 0xec, + 0x49, 0x65, 0xf7, 0x78, 0xf3, 0x2c, 0xa6, 0xc2, 0xe1, 0x60, 0x47, 0x8e, + 0x1d, 0xf3, 0x39, 0xfa, 0x1c, 0x4e, 0xab, 0xfa, 0x9f, 0x61, 0x79, 0xbc, + 0x88, 0x42, 0x1b, 0xce, 0x41, 0x59, 0x7b, 0xa5, 0x0b, 0x2f, 0x88, 0xb3, + 0xab, 0x29, 0xb2, 0x6e, 0xe0, 0x6e, 0xb8, 0x7f, 0x4d, 0x2b, 0x5f, 0x3f, + 0x8e, 0x65, 0x97, 0xc5, 0x34, 0x0d, 0x65, 0xdf, 0x75, 0x65, 0xf7, 0xa6, + 0x81, 0xac, 0xac, 0x45, 0x10, 0x48, 0xcc, 0x8a, 0x62, 0x27, 0x18, 0xbf, + 0x43, 0x0b, 0x26, 0x59, 0x7f, 0x1e, 0x10, 0xfd, 0x0b, 0x2c, 0xc5, 0x94, + 0x33, 0xe3, 0x09, 0x3e, 0xe9, 0x5d, 0x2c, 0xbf, 0xb5, 0x04, 0x0c, 0xf2, + 0xcb, 0xed, 0x46, 0x12, 0xcb, 0xe7, 0xec, 0xbf, 0x59, 0x7f, 0xdc, 0x30, + 0xbe, 0x6b, 0x3a, 0xb2, 0xff, 0x3e, 0x8e, 0x37, 0x4d, 0x8b, 0x28, 0x28, + 0xda, 0x61, 0x67, 0x88, 0x3a, 0x47, 0xbc, 0xde, 0xff, 0xb5, 0x19, 0x28, + 0xf4, 0x7e, 0xb2, 0xff, 0xf4, 0xef, 0x39, 0xf7, 0xf0, 0x11, 0x3c, 0x96, + 0x5f, 0xf0, 0x8c, 0xce, 0x3b, 0xfd, 0x25, 0x97, 0xa7, 0x8b, 0x61, 0xb2, + 0xb2, 0xb1, 0x52, 0x58, 0x4c, 0x8e, 0x1c, 0xba, 0x4c, 0x61, 0xc7, 0xe9, + 0x64, 0x75, 0x7f, 0xff, 0xff, 0xff, 0xfa, 0x78, 0xb6, 0x72, 0x78, 0xcf, + 0x6d, 0xaa, 0xda, 0xa1, 0x6d, 0xc4, 0xf0, 0x9e, 0xf5, 0xb6, 0xe6, 0x00, + 0x6d, 0xae, 0x27, 0xfe, 0x13, 0xd3, 0xdc, 0x44, 0xf7, 0xba, 0xda, 0x76, + 0x74, 0xef, 0x4f, 0xac, 0xbf, 0xff, 0x7f, 0xf4, 0xbc, 0x08, 0xd3, 0x7f, + 0x18, 0x5f, 0x4b, 0x2f, 0xfd, 0xec, 0x2c, 0x17, 0x0b, 0x06, 0xb2, 0xff, + 0xef, 0x9a, 0x8e, 0x8c, 0xf7, 0x79, 0x0c, 0x59, 0x7d, 0xcc, 0xfb, 0xab, + 0x2f, 0xc3, 0xf4, 0x16, 0xcb, 0x2f, 0x8c, 0xbb, 0xc5, 0x97, 0xff, 0xfc, + 0xc3, 0xd7, 0x81, 0xc9, 0x46, 0xfd, 0x41, 0x77, 0xd9, 0x25, 0x95, 0x24, + 0x44, 0x99, 0x0d, 0xff, 0x8f, 0xa6, 0x13, 0x1f, 0x70, 0x2b, 0x2a, 0x49, + 0xdc, 0x61, 0xe6, 0x92, 0x48, 0x8f, 0xb0, 0xb1, 0xdc, 0x22, 0xbf, 0xc1, + 0x2c, 0xf7, 0xb3, 0xf5, 0x97, 0xce, 0xd3, 0xee, 0xac, 0xa7, 0x3d, 0x82, + 0x33, 0xbe, 0xec, 0xfe, 0x79, 0x65, 0xff, 0xfe, 0x72, 0x09, 0x60, 0xfc, + 0x09, 0xde, 0x89, 0x6a, 0x36, 0x59, 0x58, 0x88, 0x70, 0x12, 0xdf, 0xc2, + 0x14, 0x7a, 0x24, 0xb2, 0xff, 0xfe, 0x14, 0xb3, 0xf0, 0x72, 0x74, 0x8f, + 0xd0, 0x12, 0x75, 0x94, 0xc4, 0x45, 0x39, 0x6d, 0xf3, 0x50, 0xce, 0xac, + 0xbf, 0xd8, 0xd4, 0x74, 0x9f, 0x8b, 0x2f, 0x1f, 0x8d, 0x65, 0x41, 0xf8, + 0x19, 0x1f, 0x4c, 0xaf, 0xf9, 0xf5, 0x29, 0xd8, 0x11, 0xe2, 0xca, 0x9e, + 0xd7, 0x91, 0x62, 0x33, 0x1c, 0x94, 0x25, 0xa8, 0x5b, 0x7a, 0x14, 0xef, + 0x0a, 0xc2, 0x84, 0x90, 0x0b, 0x6f, 0x3e, 0xfd, 0xd5, 0x94, 0xb2, 0xff, + 0xfb, 0x87, 0x3f, 0x81, 0xf1, 0x91, 0x03, 0x5b, 0x2c, 0xad, 0x1e, 0xef, + 0x42, 0xef, 0xee, 0xe3, 0x3e, 0xc6, 0x2c, 0xac, 0x3d, 0x03, 0x22, 0xbf, + 0xcc, 0x63, 0xcb, 0x87, 0x25, 0x97, 0xf8, 0x59, 0xd1, 0xf7, 0xfa, 0x85, + 0x95, 0x87, 0xd6, 0xe6, 0x74, 0x69, 0xbc, 0xfa, 0x19, 0xc5, 0x08, 0xbb, + 0xff, 0xec, 0xdf, 0x83, 0xf4, 0x6f, 0x64, 0x13, 0x88, 0xb2, 0xfe, 0x1b, + 0xcb, 0x87, 0x25, 0x95, 0xa3, 0xff, 0x02, 0x95, 0xf0, 0x0b, 0xbc, 0x59, + 0x7d, 0xe8, 0xd4, 0x2c, 0xa0, 0x9e, 0x1e, 0x88, 0xaf, 0x4a, 0x7d, 0xa5, + 0x97, 0xda, 0x9d, 0xf9, 0xac, 0xbf, 0x3b, 0x45, 0x83, 0x59, 0x50, 0x7d, + 0xa6, 0x43, 0xa2, 0x5b, 0xff, 0x14, 0x04, 0x57, 0xcd, 0x1f, 0xeb, 0x2f, + 0xee, 0x61, 0xeb, 0x4e, 0xb2, 0xb6, 0x3e, 0xaf, 0xcf, 0xae, 0x8d, 0x2c, + 0xbf, 0xfb, 0x6c, 0xe1, 0xbf, 0x9b, 0xb4, 0xd3, 0x49, 0x2c, 0x7f, 0x9f, + 0x01, 0x0b, 0x5f, 0xf0, 0x1f, 0x53, 0xe0, 0xf1, 0xe9, 0x65, 0x42, 0x39, + 0xc7, 0x08, 0x4f, 0x13, 0xdf, 0xdf, 0x67, 0x81, 0xf7, 0xeb, 0x2f, 0xde, + 0x8d, 0xbc, 0x05, 0x97, 0xe3, 0x00, 0x4f, 0x65, 0x95, 0x88, 0x82, 0xe9, + 0x8c, 0xf9, 0x4d, 0xff, 0x16, 0x40, 0xaf, 0xd7, 0x15, 0x65, 0xfe, 0x66, + 0x04, 0x9f, 0x02, 0xb2, 0xe0, 0x4c, 0xb2, 0xe9, 0x72, 0x0f, 0x27, 0xf3, + 0x1b, 0xc1, 0x00, 0x56, 0x57, 0xc7, 0x96, 0x02, 0xeb, 0xfe, 0xdb, 0x3f, + 0x18, 0x1f, 0x52, 0x59, 0x7f, 0x3b, 0x8f, 0x7e, 0x0d, 0x65, 0xe7, 0x70, + 0xaa, 0x2c, 0xf5, 0xfe, 0x82, 0x17, 0x40, 0x82, 0x59, 0x7f, 0x7e, 0xe5, + 0x23, 0x62, 0xca, 0xd9, 0x1e, 0x03, 0x3b, 0x09, 0x71, 0x94, 0x7e, 0x65, + 0x7f, 0xbd, 0xde, 0x3f, 0xc2, 0x31, 0x65, 0xfe, 0x89, 0x1f, 0x63, 0xbc, + 0x59, 0x7d, 0xf8, 0x9e, 0x85, 0x95, 0xe3, 0xd6, 0x23, 0x2a, 0x84, 0x56, + 0x9c, 0x23, 0x2f, 0xf8, 0x57, 0x2c, 0xde, 0x59, 0xc5, 0x97, 0xfe, 0x79, + 0xd9, 0xbc, 0xb3, 0x98, 0x4b, 0x2d, 0xa1, 0x4f, 0xe3, 0xc7, 0x37, 0xf8, + 0xc7, 0x8c, 0x6b, 0x3c, 0xb2, 0xfd, 0xc1, 0x9c, 0x12, 0xca, 0x83, 0xd9, + 0xe9, 0x9d, 0xff, 0xdc, 0xe9, 0xcc, 0xd3, 0x8e, 0x3e, 0xe2, 0xca, 0x34, + 0x75, 0x3c, 0x20, 0x80, 0x43, 0x79, 0xa6, 0x9a, 0x49, 0x7f, 0xe7, 0x97, + 0x0b, 0x02, 0x3c, 0xd2, 0x46, 0xe6, 0x82, 0xfd, 0xb8, 0x06, 0x60, 0xd6, + 0x5f, 0xee, 0xfb, 0x37, 0xce, 0x93, 0x16, 0x56, 0x1f, 0x18, 0x0a, 0xef, + 0xfd, 0xf4, 0x88, 0x1f, 0xce, 0x0b, 0x3f, 0x59, 0x74, 0x0a, 0xb2, 0xff, + 0x87, 0x05, 0x1f, 0xcb, 0xc6, 0xb2, 0xa1, 0x12, 0x9b, 0x22, 0x98, 0xbd, + 0xfe, 0xf1, 0x8c, 0x9d, 0xe6, 0x59, 0x7f, 0x87, 0xe0, 0x3f, 0xf3, 0xf8, + 0xb2, 0xa0, 0xfa, 0x4c, 0xca, 0xff, 0x44, 0xee, 0xe7, 0x39, 0x0b, 0x2f, + 0xe1, 0x88, 0x30, 0x77, 0x8b, 0x2b, 0x47, 0xc8, 0x46, 0x97, 0xf6, 0x72, + 0x5e, 0x13, 0x8b, 0x28, 0xcf, 0x40, 0x88, 0x6f, 0x41, 0x0a, 0xb2, 0xa4, + 0xcb, 0xd3, 0x1c, 0x2e, 0x31, 0x94, 0x58, 0x47, 0x86, 0x37, 0xef, 0xa1, + 0x6c, 0x66, 0x53, 0x43, 0x33, 0x51, 0x89, 0xb2, 0x1f, 0x1e, 0x8e, 0x23, + 0xf5, 0x32, 0x85, 0x97, 0x21, 0x6b, 0xd8, 0x4c, 0x6f, 0x86, 0xa0, 0x84, + 0x17, 0x7f, 0xc5, 0x97, 0xe2, 0xee, 0x7e, 0xc5, 0x97, 0xda, 0x38, 0xea, + 0xcb, 0xfb, 0x85, 0x93, 0x43, 0x16, 0x57, 0xe7, 0x9d, 0xd2, 0x1a, 0x74, + 0x4c, 0x81, 0xda, 0xe8, 0xea, 0xcb, 0xef, 0xdd, 0xae, 0xac, 0xbf, 0xce, + 0xd7, 0x8e, 0x35, 0x0b, 0x2f, 0xc7, 0xbe, 0x0b, 0xab, 0x28, 0x69, 0xb3, + 0x8b, 0x0b, 0x1f, 0x88, 0x8c, 0x5b, 0x44, 0x9c, 0x32, 0xbf, 0xf6, 0x8f, + 0x92, 0x13, 0xd0, 0x50, 0xb2, 0xfb, 0x47, 0x0c, 0x59, 0x7f, 0xfe, 0xe8, + 0xa0, 0x8c, 0x1f, 0xb3, 0x9f, 0x4a, 0x3c, 0xb2, 0xb4, 0x8b, 0x66, 0x1f, + 0x11, 0x0d, 0xf7, 0xe0, 0xd6, 0xcb, 0x2e, 0x79, 0x2c, 0xbf, 0xff, 0xff, + 0x89, 0xda, 0xee, 0x78, 0xb3, 0xa0, 0xfd, 0x8f, 0x27, 0x1c, 0x13, 0xe9, + 0xe4, 0xb2, 0xff, 0x67, 0xbc, 0x0e, 0xb9, 0x2c, 0xb8, 0xc6, 0xb2, 0xfe, + 0xf0, 0x5f, 0x4f, 0x25, 0x96, 0x0c, 0x8f, 0x0f, 0x05, 0xaf, 0x1c, 0xdc, + 0x59, 0x50, 0x78, 0x8c, 0x27, 0xbf, 0xfc, 0xfd, 0x3f, 0x3b, 0x27, 0xc1, + 0xe3, 0xd2, 0xcb, 0xde, 0x8e, 0x2c, 0xb1, 0x8d, 0x3d, 0xe9, 0x85, 0xb5, + 0x08, 0x66, 0x42, 0xe4, 0x88, 0x7a, 0x99, 0x7e, 0x3f, 0x3b, 0x0d, 0x65, + 0xe7, 0x2f, 0xd6, 0x54, 0xe3, 0xc2, 0x09, 0x35, 0xed, 0xf8, 0x35, 0x96, + 0xd6, 0xc7, 0x85, 0x31, 0x25, 0xed, 0x3f, 0x96, 0x5f, 0xed, 0xcd, 0x66, + 0xe1, 0x01, 0xa5, 0x95, 0xb1, 0xeb, 0x34, 0x39, 0x7b, 0x70, 0xc6, 0xb2, + 0xb0, 0xf0, 0xdc, 0x92, 0xa1, 0x70, 0x00, 0xcb, 0xfd, 0x28, 0x0d, 0xe1, + 0xd6, 0x50, 0xc3, 0xbf, 0xff, 0xfe, 0xff, 0x99, 0x84, 0x28, 0x9a, 0x28, + 0xcf, 0x37, 0xe1, 0x66, 0xcc, 0xc6, 0x2c, 0xbb, 0x58, 0xb2, 0xee, 0x1a, + 0xca, 0xd8, 0xd6, 0x68, 0x5a, 0xfa, 0x08, 0x46, 0x2c, 0xac, 0x3c, 0x41, + 0x08, 0xab, 0x13, 0x12, 0xec, 0x3c, 0x2e, 0xff, 0x8b, 0x2f, 0x9f, 0xd0, + 0x22, 0xca, 0xc3, 0x75, 0xbc, 0x62, 0xfa, 0x1a, 0xc1, 0xac, 0xbf, 0xfe, + 0x07, 0xd2, 0xe0, 0xfd, 0x1a, 0x1b, 0xbb, 0x4b, 0x2b, 0x11, 0xf0, 0x6c, + 0x5a, 0x22, 0x22, 0x2b, 0xc4, 0xc3, 0x59, 0x7b, 0xa2, 0x62, 0xcb, 0xa3, + 0x65, 0x94, 0xe6, 0xd3, 0x83, 0xb7, 0x87, 0x12, 0x59, 0x7f, 0xd1, 0x81, + 0xf1, 0xef, 0x7d, 0x2c, 0xbb, 0x3c, 0xb2, 0x9c, 0xfa, 0xc8, 0x72, 0x7c, + 0xe6, 0xec, 0x1a, 0xcb, 0xba, 0xdb, 0x59, 0x52, 0x4d, 0xd7, 0x0e, 0xb8, + 0x9f, 0xd8, 0x45, 0xef, 0x30, 0x9f, 0x16, 0xbf, 0xb3, 0x5f, 0xb0, 0xf8, + 0xb2, 0xf7, 0x80, 0xd2, 0xcb, 0xf7, 0x3a, 0x71, 0xf2, 0xcb, 0x80, 0x15, + 0x95, 0x23, 0x7f, 0xf9, 0x45, 0xee, 0xe0, 0x8b, 0x2f, 0xe6, 0x03, 0x9d, + 0xce, 0xac, 0xbf, 0x3f, 0xbe, 0x97, 0x16, 0x54, 0x1e, 0xb8, 0x0b, 0xaf, + 0xd9, 0xcf, 0xe2, 0x65, 0x97, 0xf4, 0x7d, 0xc9, 0xda, 0x85, 0x97, 0x86, + 0x00, 0xac, 0xbe, 0x19, 0x44, 0x96, 0x51, 0x9b, 0xe7, 0x1d, 0xbf, 0xfc, + 0x24, 0xef, 0xba, 0x51, 0x93, 0xb3, 0xee, 0xac, 0xa8, 0x54, 0x90, 0x32, + 0xef, 0x97, 0x4c, 0x8a, 0x67, 0x4d, 0x10, 0xb0, 0xa7, 0xcd, 0xc0, 0x1f, + 0xbf, 0xc7, 0x33, 0x91, 0xfd, 0xc5, 0x97, 0x60, 0x56, 0x5f, 0xe2, 0xef, + 0x0a, 0x3b, 0xc5, 0x97, 0xfb, 0xc5, 0x87, 0xfc, 0x7e, 0xb2, 0xfc, 0x7b, + 0xf0, 0xb8, 0xb2, 0xb1, 0x11, 0xa6, 0x65, 0xa3, 0x3b, 0xfb, 0xee, 0xe7, + 0x81, 0xba, 0xb2, 0xfe, 0xd6, 0x7b, 0xc0, 0xea, 0xcb, 0xf7, 0x8a, 0x33, + 0x4a, 0x2f, 0xec, 0xd6, 0xc0, 0x71, 0xaa, 0x20, 0xd3, 0x73, 0x4d, 0x6d, + 0x6c, 0x8a, 0x19, 0x2a, 0x5f, 0x17, 0xb3, 0xe5, 0x95, 0x09, 0x8f, 0x9c, + 0x34, 0xdc, 0xaa, 0xfc, 0xe1, 0x36, 0x42, 0xcb, 0xfa, 0x5e, 0x27, 0xfb, + 0xab, 0x28, 0x27, 0xa8, 0x44, 0xd7, 0xc2, 0x13, 0xec, 0xb2, 0xb0, 0xf1, + 0x38, 0x43, 0x7e, 0x8e, 0xce, 0x63, 0x4b, 0x2f, 0x32, 0x02, 0xb2, 0xf8, + 0xbf, 0x07, 0x16, 0x58, 0xf0, 0xdf, 0xe8, 0x72, 0xff, 0x87, 0xce, 0x66, + 0x87, 0xec, 0x59, 0x7f, 0xdc, 0x72, 0x89, 0x8c, 0x7b, 0x2c, 0xbf, 0xd2, + 0x36, 0x47, 0x84, 0x1a, 0xcb, 0xff, 0xd1, 0x85, 0xf7, 0x73, 0x47, 0xb3, + 0xb1, 0x65, 0xd9, 0xe9, 0xc8, 0xb6, 0xe1, 0xcf, 0x4d, 0x2e, 0xce, 0x2c, + 0xb3, 0x5d, 0x3d, 0x39, 0xf3, 0xda, 0x84, 0xdb, 0x9e, 0x34, 0x2a, 0x9c, + 0xba, 0x69, 0x3c, 0x99, 0xc4, 0x28, 0xe5, 0x1a, 0x6e, 0x43, 0x2f, 0x44, + 0x3e, 0x6a, 0x28, 0xe4, 0xef, 0x73, 0x24, 0xb2, 0xec, 0x11, 0x65, 0xf7, + 0xfe, 0x81, 0xac, 0xbf, 0xbc, 0x6d, 0x14, 0x0d, 0x65, 0xff, 0x47, 0xec, + 0x8c, 0x2e, 0xf1, 0x65, 0xff, 0x73, 0x1a, 0xf3, 0xb0, 0xf8, 0xb2, 0xfd, + 0x9a, 0xd8, 0x1c, 0x59, 0x46, 0x8e, 0xa9, 0x88, 0xfc, 0x5b, 0xc3, 0x8d, + 0xe7, 0x37, 0xe3, 0xfa, 0x46, 0x4b, 0x2f, 0x1f, 0xb1, 0x65, 0xfd, 0xd7, + 0x63, 0x21, 0x8b, 0x2e, 0x86, 0x61, 0xf7, 0x84, 0x9f, 0xe1, 0xbb, 0xdc, + 0x01, 0x2c, 0xbf, 0xf0, 0x05, 0x60, 0x3b, 0xe8, 0x03, 0x6d, 0x65, 0x7c, + 0x7c, 0x24, 0x39, 0x7c, 0xc8, 0xd0, 0xab, 0x2f, 0xb3, 0xff, 0xdd, 0x65, + 0x61, 0xe3, 0x11, 0x1d, 0x6c, 0x88, 0x86, 0x9a, 0x2f, 0x87, 0xcf, 0x42, + 0xcb, 0xf1, 0x69, 0xa7, 0xd9, 0x65, 0x41, 0xf8, 0x61, 0x2b, 0x91, 0x5f, + 0xb3, 0xbc, 0x36, 0x2c, 0xb8, 0x04, 0xb2, 0xa1, 0x5e, 0xa4, 0x87, 0x32, + 0x30, 0x13, 0x85, 0xde, 0xa3, 0x93, 0x22, 0xce, 0x13, 0xdd, 0xe9, 0x2c, + 0xbd, 0xec, 0x25, 0x97, 0xfb, 0x0b, 0x3e, 0xec, 0x98, 0xb2, 0xee, 0xc2, + 0xca, 0x83, 0xc9, 0x73, 0x3b, 0xf6, 0x11, 0x40, 0xd6, 0x5f, 0x79, 0xfd, + 0x0b, 0x2f, 0xe8, 0xd9, 0x8f, 0x34, 0x2c, 0xb6, 0x96, 0x54, 0x8f, 0x82, + 0x62, 0x10, 0x17, 0x5f, 0xff, 0x38, 0xbd, 0x8d, 0x7f, 0xa8, 0xe9, 0x3f, + 0xeb, 0x2f, 0x82, 0x27, 0x78, 0xb2, 0xfe, 0x2c, 0xff, 0x0a, 0x4b, 0x2f, + 0xff, 0x85, 0x9c, 0x21, 0x3f, 0x78, 0x7f, 0x78, 0xda, 0x59, 0x58, 0x88, + 0x17, 0x2c, 0xa3, 0x46, 0x31, 0x42, 0x96, 0xa1, 0x54, 0x6e, 0x0b, 0xe9, + 0x93, 0xc4, 0x0f, 0x08, 0x52, 0x30, 0x04, 0x62, 0xf7, 0x73, 0x7a, 0xcb, + 0x8f, 0xab, 0x2f, 0x8c, 0x9c, 0x6b, 0x2f, 0xfc, 0xcc, 0xef, 0x04, 0xe3, + 0xcb, 0x16, 0x5f, 0xb9, 0x1a, 0xec, 0x2c, 0x6e, 0x6f, 0xe8, 0x68, 0x98, + 0xe2, 0xfd, 0xc6, 0xdb, 0x59, 0x7c, 0xd6, 0x48, 0xd6, 0x5e, 0xc3, 0x15, + 0x65, 0xf4, 0xce, 0xfa, 0x59, 0x4e, 0x7c, 0x04, 0x44, 0x20, 0xe5, 0xf7, + 0xb0, 0xf7, 0xac, 0xbf, 0xb8, 0x7a, 0xc0, 0x69, 0x65, 0xff, 0xfd, 0x34, + 0xe3, 0x08, 0x35, 0xc0, 0x4e, 0x61, 0xf7, 0xc6, 0xb2, 0xe8, 0x15, 0x65, + 0x41, 0xfc, 0x1b, 0x0d, 0x62, 0xa7, 0xa1, 0x46, 0x4e, 0x15, 0xd3, 0x11, + 0xb1, 0xeb, 0xc5, 0xdd, 0x23, 0xdc, 0x85, 0x2d, 0xcc, 0x6c, 0xaa, 0x2f, + 0xc5, 0xfb, 0x6f, 0xa5, 0x03, 0x59, 0x7e, 0xc2, 0x3f, 0xa4, 0xb2, 0xf6, + 0xb3, 0x16, 0x53, 0x60, 0xfb, 0x0c, 0xab, 0x84, 0xf7, 0xff, 0x4b, 0xa0, + 0x94, 0x99, 0xde, 0xc3, 0x16, 0x5f, 0xee, 0xf2, 0x36, 0xce, 0x0d, 0x65, + 0xde, 0xc5, 0x97, 0xff, 0xde, 0x81, 0xe0, 0x67, 0x61, 0x60, 0xfd, 0x0b, + 0x2f, 0xfb, 0x51, 0xe3, 0xdf, 0xa8, 0x92, 0xcb, 0xff, 0x8f, 0x0b, 0x33, + 0x93, 0xa5, 0xc6, 0x2c, 0xbf, 0xf3, 0xc7, 0xd2, 0x9d, 0xcf, 0x87, 0x0b, + 0x28, 0x69, 0xe8, 0xfc, 0x64, 0x68, 0xdb, 0xa6, 0x93, 0x0b, 0x69, 0x3f, + 0x87, 0x5d, 0x44, 0xbc, 0x2c, 0x69, 0x65, 0xfd, 0xe3, 0x63, 0xbe, 0xe2, + 0xcb, 0xf8, 0x71, 0xfe, 0xb3, 0xe5, 0x96, 0x9f, 0x59, 0x4d, 0xd1, 0x1e, + 0x11, 0xd6, 0x17, 0xb6, 0xcb, 0xef, 0xf8, 0xc5, 0x8d, 0x16, 0x18, 0xab, + 0x2e, 0x0e, 0xcb, 0x2f, 0xf4, 0x33, 0x0f, 0x53, 0x49, 0x65, 0xc7, 0x32, + 0xcb, 0xfd, 0xa8, 0x14, 0x65, 0x1f, 0x2c, 0xbe, 0x82, 0xc1, 0x56, 0x5f, + 0xfc, 0x01, 0xe6, 0xa2, 0x46, 0x38, 0x25, 0x97, 0xfc, 0x4f, 0xb4, 0x6b, + 0x4f, 0x25, 0x97, 0x76, 0x24, 0x7f, 0x5e, 0x42, 0xbd, 0xd3, 0xd9, 0x65, + 0xfb, 0xf7, 0xfa, 0x26, 0x59, 0x78, 0xa0, 0x2b, 0x2f, 0xfe, 0x19, 0xcd, + 0xe8, 0xd7, 0xd2, 0x8d, 0x2c, 0xbf, 0xfc, 0x64, 0xff, 0x73, 0x99, 0xa1, + 0xfb, 0x16, 0x54, 0xe5, 0x40, 0x92, 0x17, 0x09, 0xa1, 0xc2, 0x6d, 0x85, + 0xae, 0x3a, 0x45, 0x5c, 0x1b, 0xdc, 0x46, 0xb4, 0x2c, 0xbd, 0x99, 0xfa, + 0xcb, 0xde, 0x3e, 0xac, 0xb6, 0xfc, 0x3c, 0x82, 0x10, 0x10, 0x72, 0xfe, + 0xcd, 0x6c, 0x07, 0x1a, 0xcb, 0xd1, 0xb9, 0xc5, 0x94, 0x29, 0xe6, 0x84, + 0xba, 0xfd, 0xad, 0x66, 0xec, 0xcb, 0x2e, 0x6b, 0x8b, 0x2a, 0x19, 0x0d, + 0xa3, 0x84, 0x06, 0x4a, 0x65, 0x38, 0xc1, 0x77, 0x50, 0xd8, 0x71, 0xe1, + 0x87, 0x94, 0x6f, 0xfc, 0x33, 0xc1, 0x08, 0x3d, 0xe4, 0x62, 0x16, 0x5e, + 0x63, 0x88, 0xb2, 0xff, 0xb0, 0xa4, 0x58, 0xc8, 0x0a, 0xcb, 0xf7, 0x8f, + 0x7e, 0x0d, 0x65, 0xff, 0x87, 0x05, 0x84, 0x09, 0x67, 0x16, 0x5d, 0x3b, + 0xe5, 0x97, 0xe7, 0x61, 0xee, 0x62, 0xcb, 0xfb, 0x3c, 0xfd, 0x79, 0x96, + 0x5d, 0x1f, 0x2c, 0xaf, 0x8f, 0x13, 0xa5, 0xb5, 0x88, 0x90, 0x76, 0xdb, + 0xfe, 0xc0, 0x16, 0x74, 0x9d, 0x8b, 0x2e, 0x39, 0x96, 0x5d, 0x82, 0xac, + 0xa8, 0x35, 0xde, 0x17, 0xbb, 0x58, 0xb2, 0xf1, 0x36, 0x37, 0x16, 0x5f, + 0x1e, 0xa2, 0x4b, 0x2f, 0xb6, 0x64, 0x71, 0x65, 0x6c, 0x78, 0x98, 0x43, + 0x7f, 0xf7, 0xa3, 0xae, 0x0e, 0xbf, 0xd9, 0xd5, 0x97, 0xe0, 0xbc, 0x1c, + 0xfa, 0xcb, 0xc5, 0x9c, 0x59, 0x7f, 0xff, 0xe8, 0xf1, 0xb1, 0xbc, 0xd2, + 0x7d, 0x09, 0xe3, 0xfb, 0x81, 0x3d, 0x96, 0x56, 0xca, 0xe8, 0x06, 0x3a, + 0x29, 0xb1, 0x94, 0xee, 0x9e, 0x4d, 0x0a, 0xad, 0x10, 0xf9, 0x93, 0xf2, + 0x02, 0x16, 0xe3, 0x3f, 0x48, 0x80, 0x88, 0x21, 0x4e, 0xe0, 0xdd, 0xb8, + 0xb2, 0xff, 0x07, 0xf7, 0xfc, 0xc8, 0x2b, 0x2c, 0x7e, 0x3c, 0x52, 0x11, + 0xbe, 0x9c, 0xf2, 0x9e, 0x96, 0x5f, 0x9e, 0x5c, 0x1b, 0x16, 0x5f, 0xfd, + 0x9b, 0xf0, 0x78, 0x40, 0x96, 0x71, 0x65, 0xfb, 0x51, 0xe1, 0x06, 0xb2, + 0xb4, 0x7d, 0xae, 0x89, 0x7d, 0x07, 0xb4, 0x2c, 0xbe, 0x69, 0xf3, 0x8b, + 0x2f, 0xe1, 0xc7, 0xe5, 0x9f, 0x2c, 0xbf, 0xe9, 0x31, 0xe5, 0xde, 0x40, + 0xab, 0x2a, 0x11, 0x15, 0x84, 0x4e, 0x5d, 0x7b, 0x86, 0x4b, 0x2f, 0xc4, + 0x19, 0xdc, 0xf9, 0x65, 0xfe, 0x12, 0x51, 0xbc, 0xcb, 0xf5, 0x97, 0xcf, + 0xb9, 0x84, 0xb2, 0xb1, 0x13, 0xee, 0x36, 0x45, 0x7c, 0x36, 0xac, 0x55, + 0x6d, 0xba, 0x4c, 0xc2, 0x97, 0x84, 0xaf, 0xe4, 0x25, 0x0a, 0xb0, 0x43, + 0x32, 0xed, 0xa4, 0xb2, 0xf1, 0xf8, 0xd6, 0x57, 0xc6, 0xcf, 0xa3, 0x17, + 0xd9, 0xf4, 0xa1, 0x65, 0xe0, 0x3f, 0x16, 0x57, 0x8d, 0xf0, 0x84, 0x57, + 0xfc, 0x62, 0xc0, 0xe3, 0x7e, 0x79, 0x65, 0xfe, 0x36, 0xbe, 0x97, 0x30, + 0x2b, 0x2f, 0xe9, 0x09, 0xff, 0xf1, 0xf2, 0xca, 0xf1, 0xf3, 0xb9, 0xad, + 0xfc, 0x0e, 0x44, 0xc0, 0xd2, 0xcb, 0xc0, 0xcf, 0x96, 0x57, 0x8f, 0x33, + 0xa5, 0xf7, 0xf6, 0x33, 0x5a, 0x81, 0xac, 0xbf, 0x80, 0x5b, 0x70, 0xfe, + 0x59, 0x7f, 0xf4, 0x6c, 0xd3, 0xfd, 0xcc, 0x19, 0xf1, 0x65, 0x48, 0xfd, + 0x48, 0xbe, 0xfb, 0xff, 0xf3, 0xe5, 0x97, 0xff, 0xe8, 0xd7, 0xf9, 0xc2, + 0x03, 0xcb, 0xc0, 0x7d, 0x96, 0x5f, 0xbb, 0x8e, 0x41, 0x59, 0x5e, 0x3f, + 0xcd, 0xc5, 0x6b, 0xff, 0xc3, 0xf4, 0x7d, 0x22, 0xc6, 0x67, 0xdd, 0x59, + 0x7e, 0x66, 0x7b, 0x09, 0x65, 0x19, 0xf8, 0xba, 0x5d, 0xff, 0x01, 0xe4, + 0x3c, 0xcf, 0xf8, 0xb2, 0xfc, 0xcc, 0xd3, 0x92, 0xcb, 0xff, 0x7d, 0x2f, + 0x43, 0x1f, 0xe9, 0x62, 0xcb, 0xf8, 0xfe, 0xfa, 0x59, 0xd5, 0x95, 0x08, + 0x95, 0xc2, 0x6f, 0xd0, 0x2b, 0xc8, 0xfd, 0xe4, 0x33, 0xaf, 0xdf, 0x71, + 0x87, 0xba, 0xb2, 0xff, 0xef, 0x39, 0xf7, 0xf0, 0x11, 0x3c, 0x96, 0x5f, + 0x41, 0x01, 0xb6, 0xb2, 0xec, 0xe4, 0xe3, 0xe8, 0xf2, 0x1d, 0x42, 0x30, + 0xca, 0x12, 0x97, 0xf3, 0xed, 0xfb, 0x90, 0xd6, 0x56, 0xcb, 0xb0, 0xc3, + 0x85, 0x08, 0x5b, 0x4c, 0x8a, 0x68, 0x51, 0x68, 0x87, 0xd0, 0xa1, 0xe4, + 0x25, 0xbb, 0x18, 0xee, 0xf8, 0x75, 0xcf, 0x93, 0x5f, 0xd1, 0xfe, 0x4c, + 0x6c, 0x59, 0x7b, 0xb8, 0x4b, 0x2f, 0x79, 0xf7, 0x56, 0x5f, 0xe1, 0x20, + 0x33, 0xf9, 0xa8, 0x59, 0x7f, 0xc3, 0xc0, 0x6b, 0x60, 0x9e, 0xcb, 0x2f, + 0xde, 0x3d, 0x43, 0x16, 0x5f, 0xff, 0x07, 0xc0, 0x2c, 0xfa, 0x47, 0xc2, + 0xcd, 0xeb, 0x2f, 0xde, 0x8f, 0x18, 0xab, 0x2e, 0x79, 0xb8, 0x7f, 0x3b, + 0x8a, 0x35, 0x08, 0xd1, 0xf4, 0x28, 0x2a, 0x49, 0xd1, 0x8a, 0x37, 0xe1, + 0xf7, 0x36, 0xec, 0x61, 0x95, 0x0c, 0x88, 0xac, 0x9d, 0xa8, 0x38, 0x60, + 0x3c, 0x7a, 0x37, 0x4c, 0x05, 0x96, 0x62, 0xca, 0xf8, 0xd4, 0xb6, 0xc6, + 0x2f, 0xf7, 0x7f, 0x01, 0x70, 0xc6, 0xb2, 0xfd, 0x2f, 0xf3, 0x06, 0xb2, + 0xfa, 0x35, 0x9b, 0xcc, 0xf7, 0x1c, 0xd2, 0xff, 0xf4, 0xc5, 0x1d, 0xe7, + 0x7e, 0x09, 0x96, 0xcb, 0x2f, 0xc6, 0xc1, 0x26, 0xe2, 0xcb, 0xf9, 0xfe, + 0x91, 0x40, 0xd6, 0x5f, 0xf4, 0x7d, 0x23, 0x26, 0x3f, 0x56, 0x51, 0xa3, + 0x6f, 0xc9, 0x8e, 0x54, 0x45, 0xb7, 0xe7, 0x96, 0x6d, 0x0b, 0x2f, 0xfe, + 0x8e, 0x73, 0x19, 0xd2, 0x86, 0x71, 0x65, 0xfc, 0x2c, 0xec, 0x64, 0x05, + 0x65, 0xff, 0xd1, 0xf4, 0xba, 0x08, 0xd8, 0xfe, 0xea, 0xcb, 0xfc, 0x7e, + 0x70, 0xc1, 0x0d, 0x65, 0x1a, 0x2a, 0x08, 0xc3, 0x88, 0xf7, 0xf4, 0x07, + 0xf6, 0x03, 0xf5, 0x97, 0xec, 0xde, 0x64, 0x35, 0x97, 0xf8, 0x7c, 0x3f, + 0x7b, 0x06, 0xb2, 0xff, 0xe3, 0x8e, 0x13, 0xca, 0x77, 0x33, 0x4b, 0x2f, + 0x13, 0xe9, 0x65, 0xf3, 0xbb, 0xee, 0x2c, 0xbf, 0xff, 0xb0, 0x73, 0x8f, + 0xf8, 0x9d, 0xdf, 0x67, 0x7c, 0xf2, 0x59, 0x7f, 0xf8, 0xf9, 0xb6, 0x35, + 0x84, 0x0d, 0xc8, 0x6f, 0x88, 0x9d, 0x60, 0xde, 0xf2, 0x3b, 0xfd, 0xf4, + 0xb9, 0x34, 0xa3, 0x65, 0x94, 0x35, 0x6b, 0x01, 0x27, 0xd4, 0x3a, 0xd8, + 0x5d, 0xe3, 0x07, 0x28, 0x23, 0x3e, 0xc3, 0x84, 0x0e, 0xb7, 0xff, 0xf7, + 0x73, 0xee, 0xeb, 0x51, 0xdf, 0xc4, 0x29, 0x7b, 0x16, 0x5c, 0x7b, 0xd6, + 0x5f, 0x41, 0x4b, 0x8b, 0x2c, 0x7f, 0x22, 0x5e, 0x65, 0xef, 0xc6, 0x2f, + 0xf6, 0xb6, 0x28, 0x09, 0xc9, 0x65, 0x78, 0xfa, 0xdc, 0xe2, 0xfd, 0xe8, + 0xde, 0x38, 0x59, 0x78, 0x6e, 0x4b, 0x2d, 0xbd, 0x65, 0xfb, 0xee, 0x94, + 0x79, 0x65, 0xb5, 0x06, 0xed, 0xc4, 0xef, 0xbe, 0x9a, 0x34, 0xb2, 0xda, + 0x59, 0x58, 0x6d, 0x40, 0x49, 0x52, 0x47, 0xc8, 0xca, 0x74, 0xad, 0xc5, + 0xab, 0xfc, 0x5d, 0xd1, 0xef, 0xc1, 0xac, 0xbf, 0x6a, 0x4f, 0xf3, 0x4b, + 0x2a, 0x0f, 0x78, 0xcd, 0x2f, 0xf0, 0x3e, 0x97, 0x78, 0x0d, 0xd5, 0x97, + 0xf3, 0xb5, 0xfc, 0x7b, 0x8b, 0x2a, 0x48, 0x88, 0xf1, 0x01, 0x1c, 0xdf, + 0xef, 0xb5, 0x07, 0x27, 0xe2, 0xcb, 0xfe, 0xef, 0x01, 0x21, 0x06, 0x7d, + 0x59, 0x50, 0x7d, 0xce, 0x67, 0x7e, 0xf4, 0x64, 0x9d, 0x65, 0xec, 0x3e, + 0x2c, 0xbf, 0x77, 0xb8, 0x0d, 0x96, 0x5f, 0xff, 0x6c, 0xfe, 0xc1, 0xce, + 0xc2, 0x2c, 0x3f, 0xd6, 0x56, 0xc8, 0x94, 0x08, 0xde, 0x8a, 0x6f, 0xfb, + 0xc1, 0x37, 0x9d, 0xc7, 0xf2, 0xca, 0x1a, 0x64, 0xb9, 0x0b, 0xa7, 0x31, + 0xbe, 0x28, 0x91, 0xac, 0xbf, 0x39, 0x7d, 0x23, 0x59, 0x78, 0x10, 0x4b, + 0x2b, 0x47, 0xc5, 0xe2, 0x02, 0x27, 0xbf, 0xff, 0x70, 0xa7, 0x79, 0xcf, + 0xbf, 0x80, 0x89, 0xe4, 0xb2, 0xff, 0xff, 0xe8, 0xd6, 0x4d, 0x0c, 0xc0, + 0x98, 0xfd, 0x1d, 0xe0, 0xba, 0xcd, 0xc5, 0x97, 0xfb, 0xc6, 0x2c, 0xf8, + 0x34, 0x2a, 0xcb, 0xfe, 0x64, 0x10, 0x5c, 0xbf, 0x6c, 0xac, 0xbf, 0xe8, + 0xff, 0x5a, 0x86, 0x87, 0xf2, 0xcb, 0xfc, 0xd3, 0x3d, 0x06, 0x5b, 0x2c, + 0xbf, 0xfc, 0xf2, 0x3d, 0x67, 0xd3, 0x4a, 0x35, 0xb2, 0xca, 0x84, 0x40, + 0x7e, 0x69, 0x7f, 0xfc, 0x59, 0xcc, 0x64, 0x07, 0x3a, 0x7a, 0xc5, 0x94, + 0x14, 0xe8, 0x18, 0x73, 0xf9, 0xe7, 0x61, 0x79, 0xbc, 0x8e, 0xff, 0xd3, + 0x73, 0xa0, 0xfb, 0x06, 0xe4, 0xb2, 0xa1, 0x55, 0x46, 0x4a, 0x15, 0x75, + 0x3b, 0xfb, 0x6c, 0xd1, 0x86, 0x16, 0x5f, 0x10, 0xe1, 0x8b, 0x2f, 0xff, + 0x80, 0x5d, 0xe7, 0x8e, 0x62, 0x86, 0x3c, 0x96, 0x5f, 0x61, 0x01, 0xd6, + 0x53, 0x0f, 0xbf, 0x7a, 0x7d, 0xfc, 0xff, 0xe0, 0xde, 0x4b, 0x2a, 0x11, + 0xc3, 0x90, 0x90, 0x32, 0x4b, 0xef, 0xf8, 0xda, 0x36, 0x56, 0x51, 0xa6, + 0xdb, 0xe8, 0xc3, 0x9c, 0xca, 0xfc, 0x7e, 0x08, 0x92, 0x59, 0x50, 0xd8, + 0x49, 0xed, 0x08, 0x21, 0xc6, 0x27, 0x92, 0xe4, 0x83, 0x1c, 0x51, 0xc6, + 0x35, 0x34, 0x64, 0x6c, 0x84, 0xef, 0xa3, 0x45, 0x78, 0x4b, 0x7e, 0x5c, + 0x52, 0xbf, 0xbb, 0x1d, 0x18, 0x0d, 0x6f, 0xfe, 0xc2, 0xff, 0x33, 0x5c, + 0xec, 0x62, 0xcb, 0xfe, 0xfb, 0xb9, 0xa3, 0xd9, 0xd8, 0xb2, 0xf6, 0x6f, + 0xc5, 0x96, 0xcf, 0xd1, 0x32, 0x48, 0x5c, 0x3a, 0xbc, 0xdb, 0x91, 0xac, + 0xbe, 0x82, 0x79, 0x2c, 0xbf, 0x66, 0xeb, 0x97, 0xf3, 0x8f, 0x03, 0x44, + 0x17, 0xf6, 0xff, 0x1c, 0x6b, 0x8b, 0x2f, 0xff, 0xd2, 0xe1, 0x67, 0x78, + 0x78, 0xce, 0xeb, 0x1a, 0x59, 0x7f, 0xef, 0x1b, 0x07, 0x93, 0x46, 0x69, + 0x65, 0xe9, 0x47, 0xcb, 0x2f, 0xc0, 0x20, 0x47, 0xcb, 0x2f, 0xa3, 0xf7, + 0x62, 0xcb, 0x4b, 0x11, 0x4b, 0x31, 0xf3, 0x07, 0x48, 0xa2, 0xbe, 0x4c, + 0xf0, 0xa1, 0xf5, 0x7d, 0xc3, 0xec, 0x2c, 0xa8, 0x3c, 0xa7, 0x29, 0xbf, + 0x16, 0x7e, 0x0e, 0x2c, 0xbf, 0xd3, 0x89, 0xf6, 0x28, 0xf9, 0x65, 0xc1, + 0x3c, 0x3d, 0xdd, 0x14, 0x5f, 0x07, 0xc7, 0x32, 0xcb, 0xff, 0xf1, 0x60, + 0xa2, 0x98, 0xfc, 0x73, 0x78, 0xfe, 0xea, 0xca, 0x83, 0xfb, 0xc2, 0x3b, + 0x81, 0xa5, 0x97, 0x82, 0xfa, 0x59, 0x7f, 0xed, 0xb0, 0x99, 0x9d, 0xe0, + 0x9c, 0x59, 0x7e, 0xe0, 0x63, 0x42, 0xac, 0xbf, 0x8f, 0xff, 0xa5, 0x9d, + 0x59, 0x5b, 0x23, 0x2f, 0xc2, 0xee, 0x39, 0xfa, 0x06, 0xf2, 0x9b, 0xe9, + 0x77, 0x3f, 0x59, 0x7b, 0x87, 0x32, 0xca, 0xf8, 0xf0, 0x38, 0x47, 0x7d, + 0xdf, 0x00, 0x2b, 0x2f, 0xd8, 0x63, 0xf1, 0xac, 0xad, 0x8f, 0x26, 0x44, + 0x77, 0x9f, 0x42, 0xac, 0xa8, 0x45, 0x66, 0x36, 0x39, 0x1d, 0xff, 0xd9, + 0xf7, 0x78, 0x65, 0x1f, 0xbb, 0x16, 0x5e, 0x93, 0x3c, 0xb2, 0xb6, 0x3e, + 0x1e, 0xa2, 0x5f, 0xff, 0xfa, 0x7f, 0x3b, 0xe7, 0x1b, 0x72, 0xcd, 0xe5, + 0x9c, 0xe1, 0xfd, 0xd5, 0x97, 0xf7, 0xdc, 0xc6, 0x46, 0xea, 0xcb, 0xc2, + 0x9b, 0x4b, 0x2f, 0xcd, 0x1e, 0x10, 0xab, 0x28, 0xcf, 0x18, 0x03, 0xd5, + 0x89, 0x96, 0x99, 0x1b, 0xb9, 0x13, 0xbd, 0xfc, 0xe1, 0xf3, 0xee, 0x71, + 0x65, 0xd3, 0xb1, 0x65, 0xf7, 0x0a, 0x18, 0xb2, 0xa0, 0xdc, 0xfc, 0x31, + 0x7b, 0xd2, 0x62, 0xcb, 0xbc, 0x6b, 0x2b, 0x0d, 0x9f, 0x47, 0x6a, 0x19, + 0x10, 0xf2, 0x75, 0xc4, 0x31, 0x63, 0xb5, 0xf9, 0xf7, 0xd0, 0xa7, 0x78, + 0x75, 0x94, 0x67, 0x3c, 0x8e, 0xef, 0xa7, 0x40, 0x69, 0x9f, 0x54, 0xbf, + 0xd1, 0x31, 0xfd, 0xc0, 0x31, 0x65, 0xcc, 0x0a, 0xca, 0xd8, 0xf3, 0x06, + 0x6b, 0x7f, 0x0c, 0x41, 0x96, 0x79, 0x65, 0xfb, 0x38, 0x0d, 0x6c, 0xb2, + 0xb0, 0xf5, 0xc8, 0xba, 0xe9, 0x75, 0x65, 0xfc, 0x11, 0x9e, 0xf8, 0x1a, + 0xcb, 0x79, 0x65, 0xbf, 0x59, 0x77, 0x24, 0xb2, 0x86, 0x88, 0x07, 0x17, + 0xfc, 0xbf, 0x82, 0x20, 0x12, 0xbf, 0xb6, 0x9a, 0x53, 0xde, 0xb6, 0x59, + 0x79, 0xf3, 0x4b, 0x2f, 0x16, 0x75, 0x65, 0xef, 0x63, 0x16, 0x57, 0x8f, + 0x48, 0x86, 0xfa, 0x37, 0x77, 0x49, 0x65, 0xe8, 0xfb, 0x8b, 0x2f, 0x8c, + 0xa5, 0x8b, 0x2f, 0x6c, 0xe4, 0xb2, 0x86, 0x7b, 0x2c, 0x1d, 0xe9, 0x05, + 0xf4, 0x0d, 0xe4, 0xb2, 0xfd, 0x9d, 0xf1, 0xf5, 0x65, 0xf9, 0xfe, 0xcf, + 0xba, 0xb2, 0xbc, 0x7a, 0x1d, 0x27, 0xbc, 0x02, 0xd9, 0x65, 0x6c, 0x8b, + 0x01, 0x5c, 0x7c, 0x45, 0x6d, 0x96, 0x5b, 0x4b, 0x2b, 0x63, 0x46, 0x62, + 0x57, 0xf6, 0xd3, 0xb6, 0x77, 0xf2, 0xcb, 0xbe, 0xea, 0xcb, 0xff, 0xfd, + 0xe3, 0xd6, 0x31, 0xfe, 0x6e, 0x30, 0x3e, 0xa4, 0xff, 0xac, 0xbf, 0x61, + 0x0f, 0xd0, 0xb2, 0x9b, 0x0a, 0xd3, 0xa2, 0x11, 0x32, 0x2e, 0xc6, 0xf3, + 0x86, 0xff, 0x95, 0x5c, 0x84, 0x8c, 0x40, 0x30, 0xd3, 0x25, 0xff, 0x60, + 0xc6, 0x7f, 0x67, 0xdd, 0x59, 0x7c, 0x12, 0x89, 0x2c, 0xaf, 0x1e, 0xdb, + 0x9d, 0x5f, 0xe8, 0xce, 0xc7, 0x9c, 0x6b, 0x2f, 0x47, 0xd2, 0x59, 0x5b, + 0x1f, 0x78, 0xc8, 0x77, 0x0c, 0x6f, 0xb3, 0x0b, 0xab, 0x2f, 0xff, 0x6f, + 0x32, 0xff, 0xc6, 0xf2, 0xe9, 0xec, 0xb2, 0xf8, 0x0f, 0xa9, 0x2c, 0xa8, + 0x3e, 0xe1, 0xa6, 0x5f, 0xfc, 0x38, 0x1e, 0xa3, 0xa6, 0x13, 0x1a, 0xcb, + 0xe9, 0xa3, 0x72, 0x4b, 0x2f, 0xee, 0x84, 0x63, 0x66, 0x96, 0x5f, 0xba, + 0x65, 0x1f, 0xac, 0xbf, 0xe9, 0xb9, 0xbd, 0xcb, 0x60, 0x0d, 0x65, 0xfc, + 0xed, 0x7f, 0xfb, 0x27, 0xd6, 0x5f, 0x67, 0x71, 0x8b, 0x2e, 0x21, 0x56, + 0x53, 0x9b, 0x80, 0x10, 0xd4, 0x91, 0x19, 0xc6, 0xcb, 0xdf, 0x9f, 0xeb, + 0x2f, 0xb6, 0x9b, 0xc6, 0xb2, 0xde, 0xc3, 0xc2, 0x00, 0xf5, 0xff, 0x81, + 0xbb, 0xe3, 0x9c, 0x2e, 0xb3, 0x8b, 0x2f, 0xfc, 0x4e, 0x2f, 0x9e, 0x5c, + 0x31, 0xac, 0xbf, 0x78, 0xc9, 0xf6, 0x59, 0x60, 0xac, 0xbe, 0x04, 0xb3, + 0x81, 0x37, 0x04, 0x4f, 0x4c, 0x45, 0x69, 0x3b, 0x5e, 0x2c, 0xea, 0xcb, + 0x84, 0x62, 0xca, 0x33, 0xd5, 0x22, 0x2e, 0x8d, 0xdd, 0xf0, 0xd6, 0x54, + 0x2e, 0x41, 0x6c, 0x67, 0x28, 0x47, 0x0c, 0x87, 0x10, 0xc5, 0x26, 0xf8, + 0xbc, 0xc9, 0xe6, 0x86, 0x4f, 0x99, 0x48, 0x9f, 0xb1, 0xa3, 0x80, 0xb6, + 0xfe, 0x9d, 0x34, 0x74, 0xb7, 0xac, 0xbf, 0xfa, 0x37, 0xea, 0x35, 0x87, + 0xe8, 0x1a, 0xcb, 0xa3, 0x16, 0x51, 0x1e, 0xcf, 0x11, 0x2e, 0x76, 0x2c, + 0xba, 0x69, 0xf5, 0x94, 0x46, 0xc7, 0x78, 0xb5, 0x49, 0x1f, 0xf9, 0x08, + 0xb3, 0x52, 0xb3, 0x76, 0xc4, 0x20, 0x33, 0xdb, 0x2d, 0xd3, 0xcc, 0x21, + 0x27, 0xb2, 0xa6, 0xd5, 0x4c, 0x9e, 0x23, 0x8d, 0xad, 0x01, 0xb4, 0x26, + 0x89, 0xc6, 0x5d, 0xa3, 0xc0, 0x94, 0xaf, 0x61, 0xcf, 0x57, 0x65, 0x37, + 0x88, 0x59, 0x52, 0x81, 0x9d, 0x14, 0xfa, 0x5e, 0xc9, 0xca, 0xa9, 0x9a, + 0x76, 0xd3, 0x53, 0x8c, 0x4c, 0x96, 0x01, 0xea, 0x5a, 0xa3, 0xce, 0x37, + 0xff, 0x2f, 0x5d, 0xb7, 0x0a, 0x92, 0xa4, 0x75, 0x72, 0xb1, 0x0c, 0xed, + 0x28, 0x8c, 0x11, 0x89, 0x6f, 0x7a, 0x6a, 0x14, 0x13, 0xf2, 0xe6, 0x04, + 0x9d, 0xe3, 0xdc, 0x8f, 0x12, 0x9b, 0xc2, 0x12, 0xd8, 0xd4, 0xbd, 0x88, + 0x1a, 0xeb, 0xff, 0xcd, 0xd8, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x13, + 0x62, 0xfd, 0xa0, 0xbb, 0x85, 0x51, 0x56, 0xae, 0xe4, 0xfa, 0xcb, 0x37, + 0xc3, 0xce, 0xe1, 0xa5, 0xdb, 0xf4, 0xb2, 0xe7, 0x85, 0x97, 0xfa, 0x52, + 0x07, 0x4a, 0x24, 0xb2, 0xfb, 0x3c, 0xfd, 0x59, 0x66, 0xce, 0x1e, 0xa1, + 0x19, 0xdf, 0xff, 0xfa, 0x51, 0xbe, 0x0f, 0x41, 0x36, 0x3c, 0x8c, 0xb0, + 0x78, 0x2a, 0xcb, 0xff, 0xda, 0x39, 0x64, 0xb1, 0xe4, 0x50, 0xc5, 0x97, + 0xfb, 0x58, 0x18, 0xfc, 0x46, 0x2c, 0xbf, 0xde, 0x8d, 0xb5, 0x18, 0x35, + 0x97, 0xff, 0xe8, 0xda, 0x35, 0x0c, 0x88, 0xef, 0x8f, 0xf6, 0x2c, 0xa8, + 0x44, 0x47, 0x8c, 0xef, 0x40, 0xe6, 0x59, 0x52, 0x4e, 0xb8, 0xda, 0xf4, + 0x8f, 0xe8, 0x5e, 0xf4, 0x8a, 0xfb, 0x4c, 0xf0, 0xd6, 0x5f, 0xfd, 0xa9, + 0x16, 0x77, 0x90, 0xce, 0x85, 0x65, 0x41, 0xf3, 0x80, 0x8e, 0xf0, 0xf0, + 0x96, 0x5f, 0xdc, 0x91, 0x85, 0xf4, 0xb2, 0xf3, 0xb8, 0x55, 0x15, 0xa2, + 0xfc, 0x2f, 0xdc, 0x07, 0xcb, 0x2a, 0x11, 0x02, 0x12, 0xd3, 0x28, 0xbf, + 0xfc, 0x7d, 0x94, 0x07, 0xc7, 0xde, 0x3f, 0xcb, 0x2b, 0x47, 0xf1, 0xbc, + 0xba, 0xfe, 0xfe, 0x76, 0x68, 0xc5, 0x59, 0x7f, 0xd8, 0x33, 0xe4, 0xc5, + 0x03, 0x59, 0x43, 0x3e, 0x97, 0x31, 0xbf, 0xe8, 0x0e, 0x33, 0x0b, 0xb3, + 0xeb, 0x2f, 0xf7, 0xa3, 0x5f, 0xf7, 0x36, 0x59, 0x77, 0x8d, 0x87, 0xdf, + 0xe3, 0xab, 0xd2, 0xfa, 0x4b, 0x2f, 0x03, 0x9d, 0x59, 0x7f, 0xff, 0x39, + 0xee, 0x41, 0x02, 0x47, 0xce, 0x44, 0xe6, 0x05, 0x65, 0xff, 0xf0, 0x63, + 0x59, 0xfb, 0xb1, 0xe5, 0x06, 0x15, 0x97, 0xff, 0xd2, 0xe6, 0x14, 0x31, + 0xf3, 0xbd, 0x86, 0x2c, 0xb7, 0x61, 0x13, 0x6e, 0x9d, 0x4c, 0x4c, 0xf7, + 0xc3, 0xbd, 0x87, 0xcd, 0xff, 0xff, 0xdb, 0xcf, 0xbc, 0xc1, 0x43, 0xe3, + 0xee, 0x4c, 0x50, 0xcd, 0xb1, 0xa5, 0x97, 0xff, 0xe7, 0xeb, 0xe7, 0x07, + 0x92, 0xf4, 0x6f, 0x1c, 0x2c, 0xa8, 0x46, 0x5b, 0xbb, 0x5f, 0xe8, 0xeb, + 0xfa, 0x70, 0xe1, 0x65, 0xf4, 0xba, 0x7b, 0x2c, 0xbf, 0x73, 0x8e, 0x5b, + 0x2c, 0xbf, 0x09, 0xd2, 0x8f, 0xd6, 0x5b, 0xcb, 0x2b, 0xe4, 0x42, 0xb0, + 0x8f, 0x79, 0x40, 0x85, 0x37, 0xff, 0xdf, 0xbe, 0x79, 0xcf, 0x72, 0x18, + 0x4e, 0x2a, 0xcb, 0xff, 0x1f, 0x30, 0x7f, 0x72, 0x35, 0xc5, 0x97, 0xf9, + 0xe5, 0xd3, 0xdb, 0xc6, 0xb2, 0xa1, 0x30, 0x4c, 0x41, 0x0a, 0x89, 0x9f, + 0xdf, 0xb3, 0xc7, 0x8c, 0x59, 0x7e, 0x3f, 0xd8, 0x7a, 0x59, 0x7f, 0xff, + 0xe2, 0x7e, 0xc1, 0x3e, 0xd1, 0xa8, 0x39, 0x43, 0x0a, 0x05, 0x59, 0x7f, + 0x7a, 0x02, 0x4e, 0xc5, 0x97, 0xfe, 0x73, 0x0f, 0x9c, 0x1b, 0x68, 0xd6, + 0x51, 0x9f, 0x51, 0x16, 0x5f, 0xfb, 0x1f, 0x68, 0xc2, 0x1f, 0xa1, 0x65, + 0x6c, 0x9b, 0xe0, 0xc9, 0x8c, 0xa3, 0xd0, 0xd2, 0x22, 0x0b, 0xfd, 0x28, + 0xd6, 0xd1, 0xad, 0x96, 0x5f, 0x4b, 0x58, 0xc5, 0x97, 0xb3, 0x5f, 0xac, + 0xbf, 0x4d, 0x83, 0x66, 0xea, 0xcb, 0xf8, 0xa1, 0xaf, 0xa5, 0xc5, 0x95, + 0xf2, 0x26, 0x8c, 0x8a, 0x61, 0xdd, 0xc2, 0xcb, 0xc2, 0x80, 0x2b, 0x2a, + 0x13, 0x2e, 0xc8, 0x67, 0x8a, 0x83, 0x7f, 0xff, 0xff, 0x61, 0x87, 0x3b, + 0x05, 0xde, 0x4e, 0x83, 0x1e, 0x32, 0x76, 0xa0, 0xe4, 0xfc, 0x59, 0x7f, + 0x67, 0x5c, 0xbf, 0x02, 0xcb, 0xfb, 0xd3, 0x8f, 0x67, 0x25, 0x97, 0xf3, + 0x38, 0x32, 0x76, 0x96, 0x5f, 0xbf, 0xe3, 0x68, 0xda, 0x36, 0x8b, 0x2a, + 0x0f, 0x9d, 0xcb, 0xaf, 0x9f, 0xf7, 0x1a, 0xca, 0x92, 0x60, 0xb8, 0x5a, + 0x50, 0x96, 0xe1, 0x05, 0xff, 0xb3, 0x53, 0x16, 0x0f, 0xd1, 0xbd, 0x65, + 0x9b, 0xb6, 0x19, 0xe8, 0x0d, 0x91, 0x99, 0xed, 0xa6, 0x23, 0xd8, 0xda, + 0x16, 0xc3, 0x21, 0xc8, 0xc5, 0x05, 0x84, 0x68, 0x61, 0x21, 0xf1, 0x69, + 0xc6, 0xb7, 0xa8, 0xc0, 0x58, 0x43, 0xe8, 0xef, 0x5e, 0x3b, 0x72, 0x8d, + 0xf7, 0x86, 0xbd, 0x8e, 0x34, 0x44, 0x1b, 0xf6, 0x82, 0xee, 0x15, 0x45, + 0x80, 0xbf, 0xcc, 0x3e, 0x02, 0x35, 0x32, 0xcb, 0xe0, 0x89, 0xe9, 0x96, + 0x54, 0x1e, 0xcb, 0x9a, 0x5e, 0xfc, 0x4f, 0xd6, 0x5f, 0x87, 0x99, 0xff, + 0x16, 0x59, 0xbe, 0x23, 0xd7, 0x90, 0x8d, 0xe9, 0x00, 0x84, 0x17, 0xed, + 0x05, 0xdc, 0x2a, 0x89, 0xd5, 0x7f, 0xff, 0xbd, 0x1b, 0x0f, 0x18, 0xdf, + 0x9c, 0xcf, 0x1f, 0x01, 0xb2, 0xcb, 0xf3, 0x76, 0x3c, 0x9b, 0xe2, 0x26, + 0x66, 0x34, 0xbf, 0xff, 0x88, 0x11, 0xde, 0x61, 0x37, 0xff, 0xf8, 0xf3, + 0xec, 0xb2, 0xfe, 0x26, 0xf3, 0x3c, 0xce, 0xb2, 0xa1, 0x11, 0xd8, 0xbd, + 0x78, 0x58, 0x62, 0xcb, 0xf6, 0x6d, 0xb4, 0x05, 0x65, 0xf3, 0x84, 0x46, + 0x2c, 0xbd, 0x2e, 0x37, 0x14, 0xfa, 0x48, 0x77, 0x79, 0x4d, 0xfb, 0x41, + 0x77, 0x0a, 0xa2, 0xd9, 0x5f, 0xdd, 0x7f, 0x4e, 0x1c, 0x2c, 0xbf, 0xff, + 0xa4, 0xdf, 0x90, 0x78, 0x33, 0xce, 0x67, 0x4a, 0x16, 0x54, 0x22, 0x21, + 0xcb, 0xef, 0xd2, 0x83, 0xdf, 0x0b, 0x2f, 0xfa, 0x38, 0xdf, 0xc6, 0x17, + 0xd2, 0xca, 0x83, 0xe2, 0xfc, 0xa2, 0xff, 0xa2, 0x51, 0xad, 0xa3, 0x5b, + 0x2c, 0xb3, 0x7c, 0x4e, 0x6d, 0x90, 0xbc, 0x78, 0x41, 0x91, 0x15, 0xff, + 0x89, 0xbb, 0x00, 0x7d, 0x27, 0xdc, 0x59, 0x7f, 0xf0, 0x38, 0xdf, 0x34, + 0xfc, 0xf3, 0xc9, 0x65, 0xcf, 0xfe, 0x22, 0x23, 0xf4, 0x2b, 0xbe, 0x0a, + 0xcb, 0xe0, 0xbb, 0x85, 0x51, 0x73, 0x2c, 0xc5, 0x95, 0xa3, 0x7e, 0xd1, + 0x75, 0xfc, 0x59, 0xf7, 0xe7, 0x25, 0x96, 0x6d, 0xac, 0xad, 0x1e, 0x1e, + 0xf2, 0xeb, 0xa6, 0x62, 0xca, 0x59, 0x66, 0xf0, 0x98, 0x1e, 0x2b, 0x9b, + 0x2b, 0x44, 0x93, 0xe3, 0x17, 0xed, 0x05, 0xdc, 0x2a, 0x8b, 0xbd, 0x7f, + 0xa4, 0xdf, 0x9a, 0xd1, 0xb4, 0xb2, 0xcd, 0xf0, 0xfa, 0xdc, 0xd2, 0xff, + 0x8a, 0x1f, 0x6f, 0xb9, 0x03, 0x59, 0x70, 0xff, 0x59, 0x78, 0xb3, 0x65, + 0x96, 0x6e, 0x33, 0xed, 0x14, 0xe4, 0x86, 0x2a, 0x21, 0x3e, 0x71, 0x98, + 0x84, 0xe4, 0x0a, 0x37, 0xd0, 0xa8, 0x3a, 0x7b, 0x4e, 0xa5, 0x26, 0x32, + 0x1c, 0x7e, 0x86, 0x93, 0xc2, 0x2b, 0xf8, 0xf8, 0x0a, 0x1a, 0xbc, 0x8c, + 0xcb, 0xb0, 0xbf, 0x12, 0x12, 0xb7, 0xfe, 0x69, 0xcb, 0x6e, 0x19, 0x7d, + 0x25, 0x97, 0xff, 0x66, 0xd8, 0xd7, 0xb3, 0x98, 0x64, 0xb2, 0xff, 0xc6, + 0x45, 0x82, 0x84, 0x0e, 0x35, 0x97, 0xec, 0xf0, 0xdc, 0x96, 0x5c, 0x6d, + 0xfc, 0x8e, 0x62, 0x40, 0xe2, 0x18, 0x87, 0xb7, 0xee, 0x71, 0x8f, 0x25, + 0x97, 0xff, 0xff, 0xfe, 0xf1, 0xf8, 0xe0, 0xba, 0x4e, 0x2e, 0x74, 0x1d, + 0x3e, 0xf3, 0xc7, 0xa8, 0xd9, 0xf4, 0x15, 0x97, 0x72, 0x16, 0x5f, 0xfd, + 0xf0, 0x40, 0xff, 0x77, 0x30, 0x85, 0x59, 0x78, 0x9d, 0xbc, 0x26, 0x38, + 0x12, 0x8e, 0x42, 0x63, 0xa2, 0xd7, 0xf3, 0x62, 0x79, 0x8f, 0xb5, 0xb2, + 0xcb, 0xc0, 0x76, 0x2c, 0xbf, 0x66, 0xe7, 0x60, 0x55, 0x96, 0xdc, 0x59, + 0x5b, 0x1b, 0xfc, 0x2b, 0xad, 0x1f, 0xe1, 0x2d, 0xdf, 0xfb, 0x8e, 0x2f, + 0x8e, 0x08, 0x10, 0xb2, 0xff, 0xde, 0x7e, 0xbf, 0xbe, 0x96, 0x75, 0x65, + 0x61, 0xfe, 0x19, 0xed, 0xcf, 0xfa, 0xcb, 0xff, 0xff, 0xc4, 0xed, 0x16, + 0x77, 0xb9, 0xb4, 0x13, 0x8b, 0xc6, 0x46, 0x10, 0xd6, 0x5e, 0xc1, 0x38, + 0xb2, 0xb1, 0x15, 0x3a, 0x17, 0xdc, 0x75, 0xbf, 0xde, 0xc1, 0x9e, 0xfd, + 0x62, 0xcb, 0xfc, 0xf2, 0xd6, 0x4f, 0x9f, 0xeb, 0x2f, 0x3b, 0x85, 0x51, + 0x2b, 0xaf, 0xf0, 0xae, 0x5f, 0xf7, 0x3c, 0xb2, 0x82, 0x7b, 0x86, 0x51, + 0x7f, 0x3f, 0xd9, 0xec, 0x0a, 0xcb, 0xfd, 0xf1, 0x38, 0xaf, 0xd9, 0x2c, + 0xbf, 0xfb, 0xd9, 0x20, 0xf8, 0xc7, 0x05, 0xd5, 0x94, 0x67, 0xef, 0xd3, + 0x4a, 0x92, 0x75, 0x83, 0x34, 0xd4, 0x24, 0x98, 0x42, 0x50, 0xa4, 0xbf, + 0xd1, 0xa7, 0x93, 0xfd, 0x25, 0x97, 0xff, 0xff, 0xda, 0x72, 0x12, 0x3f, + 0xc9, 0x7a, 0x37, 0x8e, 0x1b, 0xe1, 0x02, 0x59, 0xc5, 0x96, 0xd9, 0x65, + 0xb6, 0x59, 0x50, 0x69, 0x24, 0x25, 0x7e, 0xc0, 0xbc, 0xb1, 0x65, 0xff, + 0xfe, 0x7f, 0xb9, 0x9a, 0xef, 0x5f, 0x61, 0x18, 0x3f, 0x18, 0xab, 0x2f, + 0xfb, 0x0f, 0x79, 0x46, 0x6a, 0x4b, 0x2f, 0xff, 0xdd, 0x96, 0x6e, 0xb9, + 0x7f, 0xf7, 0x75, 0x18, 0xd2, 0xc6, 0x8d, 0xc5, 0xfb, 0xee, 0xfa, 0x3f, + 0x59, 0x4e, 0x89, 0x50, 0x35, 0xdf, 0xfe, 0x98, 0xa3, 0xbc, 0xef, 0xc1, + 0x32, 0xd9, 0x65, 0xff, 0x3b, 0x43, 0xf4, 0x6d, 0x8d, 0x2c, 0xa3, 0x44, + 0x39, 0x26, 0x5f, 0xff, 0xb0, 0xff, 0xc6, 0x1f, 0x79, 0x0c, 0x7f, 0xba, + 0xb2, 0xff, 0xff, 0x05, 0xf4, 0xfe, 0xfa, 0x59, 0xdc, 0xec, 0x01, 0xb7, + 0x8b, 0x2f, 0xfe, 0x7f, 0xa4, 0x7d, 0xf4, 0x6b, 0xb0, 0xb2, 0xff, 0xa1, + 0x9d, 0xc9, 0xa4, 0x7d, 0x59, 0x52, 0x4c, 0xd8, 0xd5, 0x58, 0xc9, 0xd4, + 0x4b, 0xff, 0xfd, 0xe3, 0x18, 0xf1, 0xaf, 0xba, 0x07, 0xe7, 0x9a, 0x71, + 0xac, 0xbe, 0x03, 0xea, 0x4b, 0x2f, 0xe2, 0x8d, 0xbf, 0x7e, 0xac, 0xbf, + 0xce, 0x31, 0x37, 0x20, 0xa4, 0xb2, 0xd0, 0x13, 0xe4, 0x61, 0x75, 0xff, + 0xfe, 0x3e, 0xbb, 0x1c, 0xbf, 0xf3, 0x4e, 0x5b, 0x04, 0xf6, 0x59, 0x43, + 0x4c, 0x3c, 0xe1, 0x0c, 0xe4, 0xf7, 0xfc, 0x0f, 0xd8, 0xf2, 0xe6, 0x86, + 0xb2, 0xff, 0xff, 0x61, 0x97, 0x70, 0xa7, 0x13, 0x8e, 0x0b, 0x68, 0xd2, + 0xcb, 0x67, 0x51, 0x33, 0xbc, 0xee, 0xff, 0xff, 0xf7, 0xb1, 0x9f, 0xbe, + 0xb3, 0x7e, 0x0f, 0x38, 0x07, 0xfb, 0xcd, 0x3f, 0x56, 0x5f, 0xf4, 0xc1, + 0xf1, 0xb2, 0x34, 0x2a, 0xcb, 0xfd, 0xfe, 0x6b, 0x33, 0xbc, 0x59, 0x58, + 0x7d, 0xdf, 0x1d, 0xd0, 0xd7, 0x42, 0x72, 0x35, 0x70, 0x9e, 0xfa, 0x37, + 0x32, 0x86, 0x6f, 0x4a, 0x84, 0x87, 0x4d, 0xff, 0xb3, 0xde, 0x79, 0x7f, + 0xac, 0xf9, 0x65, 0xff, 0x67, 0xdc, 0x1c, 0x39, 0x6c, 0xb2, 0xff, 0x9e, + 0x5c, 0xf4, 0x68, 0x41, 0xac, 0xa9, 0x22, 0xcf, 0x10, 0x3a, 0x73, 0x7f, + 0xff, 0xbd, 0x81, 0x2c, 0x66, 0x16, 0x74, 0x1f, 0xb1, 0xe4, 0xb2, 0xff, + 0xff, 0xfb, 0xf8, 0x20, 0xbf, 0x5f, 0xef, 0xf3, 0x79, 0xf6, 0x5e, 0x3e, + 0xf0, 0xc6, 0xb2, 0xff, 0xc7, 0xbc, 0xfb, 0x34, 0xa0, 0xba, 0xb2, 0xa4, + 0x98, 0xff, 0xcb, 0xfd, 0x84, 0x05, 0xff, 0xe7, 0xc9, 0x7b, 0x0c, 0xb3, + 0xf9, 0xfc, 0x59, 0x79, 0xe4, 0xde, 0x79, 0x67, 0xa6, 0x44, 0x2d, 0x65, + 0x09, 0xd1, 0xc3, 0x27, 0x23, 0xde, 0xf9, 0x53, 0x46, 0x6c, 0x85, 0x27, + 0x88, 0x3f, 0x26, 0x28, 0xcc, 0xb9, 0x3b, 0x65, 0xd8, 0xcb, 0xc4, 0x8d, + 0x3b, 0x70, 0xde, 0xe6, 0xde, 0x2c, 0xbf, 0x76, 0x00, 0xdb, 0xc5, 0x97, + 0xec, 0x0b, 0xcb, 0x16, 0x5f, 0xec, 0x19, 0x97, 0x73, 0xcb, 0x2f, 0x67, + 0x9b, 0xb6, 0x88, 0x9c, 0xc1, 0xaf, 0x15, 0x91, 0x35, 0xee, 0xc0, 0xd2, + 0x5f, 0xfe, 0xf3, 0x9f, 0x62, 0x47, 0xad, 0x81, 0xfa, 0x4b, 0xfc, 0xe1, + 0x6e, 0x3d, 0x36, 0xdb, 0xc1, 0xf3, 0xf0, 0x72, 0xcd, 0xc6, 0xd9, 0xe6, + 0x86, 0x36, 0x66, 0x53, 0xe8, 0x3b, 0x1d, 0xde, 0xf8, 0x5d, 0x5f, 0xfe, + 0x6e, 0xc7, 0x93, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0x98, 0x57, 0xf8, 0x31, + 0xf7, 0x4a, 0x24, 0xb2, 0xff, 0xfe, 0xcd, 0xee, 0x3f, 0x43, 0x7e, 0xfb, + 0x06, 0x0e, 0xf1, 0x65, 0xff, 0xed, 0x3f, 0xed, 0xc5, 0xec, 0x9a, 0x8e, + 0x49, 0x65, 0xd8, 0xdf, 0x11, 0xc4, 0x46, 0x62, 0x2f, 0xdc, 0xcd, 0xeb, + 0x2f, 0xc7, 0xef, 0x66, 0xcb, 0x2f, 0x4f, 0x7e, 0x1a, 0xcb, 0xff, 0x9b, + 0x39, 0xf4, 0x80, 0xfd, 0x28, 0x92, 0xcb, 0xe6, 0xd5, 0xc9, 0x31, 0x65, + 0xfa, 0x79, 0x9e, 0x67, 0xa9, 0xf6, 0xc2, 0xcb, 0xf6, 0x32, 0x77, 0xdb, + 0xab, 0x2f, 0xe8, 0x66, 0x7f, 0x1b, 0xd6, 0x50, 0xcf, 0x77, 0xc5, 0xd7, + 0xfb, 0x58, 0x18, 0xfc, 0x46, 0x2c, 0xbf, 0x75, 0xf6, 0x7e, 0x2c, 0xbe, + 0x86, 0x1e, 0x96, 0x5a, 0x26, 0x3c, 0x9d, 0x14, 0x5f, 0x41, 0x1c, 0xcb, + 0x2f, 0xff, 0xd9, 0xb4, 0xec, 0xc2, 0xef, 0x9c, 0xf8, 0x7b, 0xd6, 0x5f, + 0xbd, 0x03, 0x31, 0x56, 0x51, 0xa6, 0xd1, 0xa2, 0x26, 0x3f, 0x11, 0x47, + 0x48, 0x77, 0x15, 0xee, 0x6c, 0x75, 0x65, 0xed, 0xef, 0xc5, 0x97, 0xfe, + 0xe0, 0xf4, 0x4e, 0xd6, 0x7d, 0xd5, 0x97, 0xf9, 0xa2, 0xcf, 0xbb, 0x9e, + 0x59, 0x58, 0x7e, 0x86, 0x81, 0x7f, 0xa1, 0xf6, 0xe8, 0xaf, 0xa5, 0x97, + 0xe7, 0xd4, 0x9d, 0x8b, 0x28, 0x69, 0x94, 0x60, 0xdf, 0xa1, 0x24, 0x44, + 0x00, 0x34, 0xbf, 0xdd, 0xf3, 0x9f, 0x0f, 0x7a, 0xcb, 0x36, 0x16, 0x51, + 0x1e, 0x4c, 0xf9, 0xa5, 0xfe, 0xe4, 0x61, 0x0f, 0xd0, 0xb2, 0xfe, 0x8c, + 0x21, 0xfa, 0x16, 0x5e, 0x06, 0xa6, 0x9c, 0x7b, 0x9c, 0x31, 0xbf, 0xfd, + 0xa3, 0x06, 0x0b, 0x9d, 0x04, 0x7c, 0x35, 0x97, 0xff, 0xe3, 0xd7, 0x9e, + 0x70, 0xf2, 0x5e, 0x8d, 0xe3, 0x85, 0x97, 0xfc, 0xd0, 0x9e, 0x80, 0xf8, + 0xe6, 0x59, 0x7c, 0x06, 0x60, 0xd6, 0x5b, 0x3e, 0x3d, 0xed, 0xc3, 0xcb, + 0xf6, 0xa2, 0x5c, 0xc5, 0x97, 0xc7, 0xdc, 0xde, 0xb2, 0xff, 0x63, 0x5d, + 0x27, 0xff, 0x8b, 0x2a, 0x0f, 0xfb, 0xc4, 0xfd, 0x23, 0xac, 0x55, 0x1e, + 0x18, 0x40, 0x68, 0xdf, 0xc9, 0x65, 0x0b, 0x6e, 0xc2, 0xa6, 0xff, 0xfa, + 0x0b, 0xba, 0x83, 0x91, 0x63, 0x1e, 0x4b, 0x2f, 0xff, 0x40, 0x78, 0x0e, + 0x9f, 0x8c, 0x5f, 0x1a, 0xcb, 0xf9, 0x91, 0x85, 0xd9, 0xf5, 0x96, 0xe2, + 0xcb, 0xf4, 0x61, 0x76, 0x7d, 0x65, 0xee, 0x3f, 0xd3, 0x8f, 0xa6, 0x62, + 0xf6, 0x08, 0xd0, 0xd3, 0x43, 0x62, 0x67, 0x61, 0x67, 0x7f, 0xdb, 0xb8, + 0x3f, 0x46, 0xe9, 0x42, 0xca, 0x33, 0xf3, 0x73, 0x6b, 0xfe, 0xd6, 0xc1, + 0xf1, 0xf8, 0x1d, 0x59, 0x5e, 0x3d, 0xb7, 0x20, 0xbf, 0xc6, 0x2e, 0xb3, + 0x68, 0xd9, 0x65, 0xfa, 0x19, 0xbf, 0xf9, 0x2c, 0xae, 0x1f, 0x07, 0x4d, + 0x6e, 0xe9, 0xac, 0xb8, 0x18, 0xb2, 0xa4, 0x79, 0xc2, 0x91, 0x7e, 0x2d, + 0x7f, 0xdd, 0xf6, 0x19, 0x74, 0x1e, 0x59, 0x7f, 0x6b, 0x0f, 0xd0, 0x35, + 0x97, 0xb4, 0x0e, 0x2c, 0xbf, 0xfe, 0xf4, 0x16, 0x77, 0xd9, 0xdf, 0x40, + 0x1b, 0x6b, 0x2f, 0x7b, 0x3e, 0x59, 0x7f, 0xff, 0x9a, 0xdc, 0xee, 0x7a, + 0x71, 0x66, 0xc7, 0x85, 0xf4, 0xb8, 0xb2, 0xfc, 0xd7, 0x73, 0xc1, 0x59, + 0x58, 0x89, 0x07, 0x67, 0xa1, 0xa6, 0xd3, 0x85, 0x86, 0x3a, 0xea, 0x45, + 0x0a, 0xdb, 0xff, 0xdd, 0x27, 0xff, 0xd8, 0x63, 0x07, 0x78, 0xb2, 0xfb, + 0x7e, 0x0e, 0x16, 0x5f, 0xf4, 0x6c, 0x24, 0xbc, 0x07, 0xd9, 0x65, 0x42, + 0x2a, 0x71, 0x2b, 0x44, 0x77, 0xfe, 0xfa, 0x53, 0xbc, 0xe1, 0x82, 0x1a, + 0xcb, 0xe0, 0x6b, 0xf6, 0x2c, 0xac, 0x3e, 0x5e, 0x20, 0xdf, 0x16, 0x18, + 0xab, 0x2f, 0x8e, 0x3e, 0xe2, 0xcb, 0xf0, 0x9d, 0x28, 0xfd, 0x65, 0x30, + 0xfb, 0x3a, 0x43, 0xbc, 0x8a, 0xfb, 0xbd, 0xc6, 0x96, 0x5f, 0xfe, 0x8e, + 0xff, 0x12, 0xcf, 0xbb, 0xb0, 0x1a, 0x59, 0x7e, 0x86, 0x69, 0xe6, 0x59, + 0x7b, 0x8f, 0xfa, 0xca, 0x84, 0x73, 0xe1, 0x89, 0x91, 0xf9, 0x3b, 0xa5, + 0x17, 0xfb, 0xd1, 0xa8, 0x61, 0xc9, 0x65, 0xcf, 0x25, 0x97, 0xff, 0x8f, + 0x91, 0x29, 0xd9, 0xe3, 0xd7, 0x9d, 0x65, 0x0d, 0x12, 0x5b, 0xa6, 0x5e, + 0x16, 0xbf, 0x4d, 0xc1, 0x83, 0x65, 0x97, 0xf3, 0xff, 0xad, 0x1e, 0x96, + 0x5e, 0xec, 0xec, 0x59, 0x50, 0x7e, 0xff, 0x95, 0x88, 0x5d, 0x7d, 0xe0, + 0x7d, 0xd5, 0x97, 0xff, 0x7b, 0x23, 0x45, 0x8c, 0x60, 0x26, 0x59, 0x7f, + 0x74, 0x4f, 0xff, 0x8f, 0x96, 0x5f, 0xf1, 0xf4, 0xf1, 0x85, 0x9b, 0x2c, + 0xa3, 0x3e, 0x9f, 0x19, 0x5f, 0xdc, 0x31, 0x58, 0xfd, 0x59, 0x7f, 0xed, + 0xf0, 0x2c, 0x8c, 0xa3, 0x50, 0xb2, 0xff, 0xff, 0xda, 0xcd, 0xf0, 0x5d, + 0x9c, 0x21, 0x47, 0x9c, 0xfb, 0x1d, 0xe2, 0xcb, 0x83, 0x3d, 0xac, 0xbd, + 0xb0, 0x24, 0xb2, 0xf1, 0x67, 0x27, 0x1b, 0xac, 0x1c, 0xa1, 0xaa, 0x15, + 0x0c, 0x2b, 0x7e, 0x21, 0x32, 0xed, 0x1f, 0xef, 0x85, 0x45, 0xf4, 0xed, + 0xfb, 0x9b, 0x2c, 0xa1, 0xaa, 0xc9, 0xc9, 0x47, 0x22, 0xb2, 0xdb, 0x75, + 0x65, 0xb7, 0x56, 0x5e, 0xc1, 0xf5, 0x65, 0x98, 0x66, 0xc3, 0x42, 0x97, + 0x77, 0x16, 0x5f, 0xe8, 0x28, 0xfd, 0xdf, 0x71, 0x65, 0xed, 0x3c, 0x96, + 0x5f, 0xe1, 0xfb, 0x03, 0xe8, 0xd2, 0xca, 0x1a, 0x20, 0xf4, 0x68, 0x43, + 0x97, 0xee, 0x67, 0x75, 0x0b, 0x2f, 0xff, 0x36, 0xca, 0x18, 0xe7, 0xe7, + 0xef, 0xa1, 0x65, 0x42, 0x67, 0x47, 0x09, 0x9e, 0x17, 0x80, 0x9e, 0xe7, + 0x0a, 0xcb, 0xf8, 0xe6, 0x70, 0xf2, 0x4b, 0x28, 0x27, 0x89, 0xe1, 0x6b, + 0xff, 0xfd, 0xe3, 0x21, 0xeb, 0x03, 0xe8, 0xf6, 0x6b, 0x50, 0x2a, 0xcb, + 0xf0, 0x37, 0x0f, 0x18, 0xb2, 0xfd, 0xd7, 0x61, 0xf5, 0x65, 0x0a, 0x7a, + 0x24, 0x55, 0x7e, 0xf4, 0x64, 0x9d, 0x65, 0xff, 0x8f, 0xa0, 0xd3, 0xf2, + 0x73, 0x3a, 0xb2, 0xa1, 0x11, 0x23, 0x22, 0x72, 0x6b, 0xfe, 0xc3, 0xc6, + 0x16, 0x6c, 0xeb, 0x2f, 0xd1, 0xde, 0x7e, 0x05, 0x97, 0xf7, 0x8f, 0xa5, + 0x0c, 0x59, 0x7d, 0x1b, 0xca, 0x16, 0x54, 0xc7, 0x9c, 0xc2, 0xca, 0x6c, + 0x2e, 0x41, 0xc2, 0x26, 0x46, 0xc0, 0x18, 0x42, 0xcc, 0x45, 0xe8, 0xce, + 0x88, 0xbb, 0xa6, 0xc0, 0x77, 0xbf, 0xfe, 0x76, 0x0f, 0xd1, 0xe8, 0x60, + 0x3e, 0x97, 0x16, 0x5f, 0xfe, 0xce, 0xf8, 0xf3, 0xee, 0xe6, 0xf8, 0xe2, + 0xcb, 0x7b, 0xc8, 0x9c, 0x25, 0x1b, 0xe7, 0xe7, 0x76, 0x59, 0x7f, 0xf3, + 0x97, 0xa0, 0xc6, 0x7c, 0xc9, 0x2c, 0xbd, 0x1f, 0x88, 0xb2, 0xfe, 0xcd, + 0xfe, 0x3f, 0xe6, 0x59, 0x58, 0x8d, 0xb1, 0x4a, 0x1c, 0x8c, 0x90, 0x78, + 0x3d, 0x71, 0x42, 0xcb, 0xef, 0x47, 0x21, 0x65, 0xe1, 0x07, 0x8b, 0x2f, + 0x36, 0xf9, 0x3d, 0xac, 0xad, 0x8f, 0xe4, 0x62, 0xae, 0x43, 0xc1, 0xdb, + 0xdc, 0x3e, 0x2c, 0xbe, 0x9c, 0xc9, 0xc1, 0x59, 0x7b, 0x02, 0x35, 0x97, + 0xdd, 0xc2, 0xfd, 0x65, 0xfc, 0xed, 0x47, 0x73, 0x65, 0x95, 0x87, 0xd8, + 0x63, 0x84, 0x45, 0x7d, 0xb0, 0x80, 0xd9, 0x65, 0xf4, 0xd2, 0x8d, 0x96, + 0x54, 0x1e, 0x56, 0x13, 0x5f, 0xc7, 0xdf, 0xc4, 0x29, 0x2c, 0xbe, 0x6a, + 0x6e, 0x42, 0xcb, 0xf4, 0x0b, 0x9d, 0xe2, 0xcb, 0xff, 0x3f, 0x78, 0x3d, + 0x3f, 0x7c, 0x05, 0x97, 0xff, 0xc6, 0x40, 0x96, 0x72, 0x77, 0x4e, 0x7f, + 0x36, 0x59, 0x5b, 0x26, 0x5c, 0x29, 0x07, 0xc5, 0xfe, 0x24, 0x22, 0x8e, + 0x9f, 0xd9, 0xbb, 0x61, 0xbe, 0x80, 0x9e, 0x86, 0x5b, 0x25, 0x13, 0xd9, + 0x13, 0x6a, 0xa4, 0x36, 0xb2, 0x98, 0x94, 0x08, 0x38, 0xe0, 0x72, 0x59, + 0x30, 0x65, 0x3b, 0xfd, 0x0c, 0xf3, 0x31, 0x9a, 0x3a, 0x1d, 0x43, 0xd5, + 0x90, 0x93, 0xf4, 0x6e, 0x8f, 0x0d, 0x9f, 0xe1, 0x54, 0x52, 0xbe, 0x79, + 0x39, 0x79, 0xd9, 0x41, 0x60, 0x86, 0x0e, 0xf3, 0xc6, 0x87, 0x27, 0xe1, + 0x26, 0x24, 0x74, 0x97, 0xfa, 0x63, 0xee, 0x6b, 0xb0, 0xb2, 0xff, 0xfb, + 0x47, 0xf4, 0xb8, 0x7d, 0xf4, 0x6b, 0xb0, 0xb2, 0xff, 0x46, 0x9e, 0x67, + 0xe4, 0x96, 0x5e, 0x77, 0x0a, 0xa2, 0xae, 0x5f, 0xce, 0x27, 0xbc, 0xec, + 0x59, 0x41, 0x3d, 0x6f, 0x14, 0x5f, 0xc7, 0xaf, 0x38, 0x8c, 0x59, 0x43, + 0x46, 0xce, 0xa1, 0x13, 0xe2, 0x2b, 0xe2, 0xdc, 0xe3, 0xac, 0xb9, 0x9b, + 0xab, 0x2f, 0x6a, 0x18, 0xb2, 0xff, 0x46, 0xbb, 0x05, 0x1f, 0xac, 0xbf, + 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x3c, 0x5f, 0xbc, 0x7a, 0xf3, 0xac, + 0xbc, 0x5d, 0x75, 0x97, 0x60, 0xf0, 0xf0, 0xb8, 0x4f, 0x52, 0x45, 0x7b, + 0x21, 0x07, 0x7f, 0xa5, 0x1a, 0xda, 0x35, 0xb2, 0xcb, 0xfd, 0x3e, 0xfe, + 0x82, 0x8f, 0xd6, 0x5f, 0xfd, 0xb4, 0x68, 0x39, 0xce, 0x60, 0xf1, 0x65, + 0x42, 0x2b, 0xb0, 0xd5, 0xcd, 0x6f, 0xfb, 0xa6, 0x06, 0xde, 0x4b, 0x69, + 0x96, 0x5f, 0xc5, 0x9d, 0xe6, 0x34, 0xb2, 0xa0, 0xfb, 0x1c, 0xfe, 0xff, + 0x04, 0xf9, 0xe3, 0xe9, 0xac, 0xbf, 0x66, 0xce, 0x5f, 0x2c, 0xaf, 0x8f, + 0x68, 0x8c, 0xaf, 0xdd, 0xee, 0x17, 0xeb, 0x2f, 0xf6, 0xe3, 0x8e, 0x30, + 0x66, 0xb2, 0xcd, 0xe1, 0x74, 0xcc, 0x66, 0x79, 0x19, 0x49, 0x9a, 0xcc, + 0x49, 0xa1, 0xaf, 0x0e, 0x3c, 0x38, 0x4a, 0x1a, 0xdc, 0x84, 0xcf, 0x5e, + 0x84, 0x22, 0xdc, 0x28, 0xbf, 0x68, 0x2e, 0xe1, 0x54, 0x58, 0x2b, 0xc2, + 0xc6, 0xcb, 0x2f, 0xff, 0xe2, 0x03, 0xec, 0xcc, 0x9f, 0x06, 0xb3, 0x52, + 0x12, 0x4b, 0x2f, 0xd8, 0x59, 0xde, 0x2c, 0xbf, 0xda, 0x76, 0x09, 0xa7, + 0xf9, 0x65, 0x9b, 0xe2, 0x61, 0xa2, 0x9a, 0x04, 0x79, 0xd8, 0x04, 0x26, + 0xbf, 0xcd, 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0xca, 0x5f, 0xfb, 0x3b, 0x2c, + 0xfe, 0x7f, 0x3c, 0x05, 0x97, 0xed, 0x05, 0xdc, 0x2a, 0x8b, 0x4d, 0x7f, + 0xce, 0x3f, 0x3c, 0xde, 0x36, 0x2c, 0xbf, 0xd0, 0xcc, 0x19, 0x47, 0xcb, + 0x2f, 0xc0, 0xcd, 0x3f, 0x16, 0x59, 0xb8, 0xd3, 0x08, 0xc4, 0x26, 0x1a, + 0x78, 0xe7, 0x86, 0x57, 0xe9, 0xe9, 0xb1, 0x3c, 0x67, 0x8c, 0xf1, 0x59, + 0x7d, 0x3c, 0x5b, 0x0d, 0x89, 0xe5, 0x65, 0x4f, 0x47, 0xf4, 0xda, 0xa8, + 0x97, 0xfd, 0x3d, 0xf8, 0xda, 0x1b, 0xb8, 0x56, 0x5f, 0x9b, 0x55, 0xb3, + 0xcc, 0x85, 0x97, 0xf8, 0x46, 0x67, 0x78, 0x7c, 0x59, 0x63, 0x59, 0x53, + 0xd9, 0xe2, 0x4f, 0x9a, 0x5f, 0xe8, 0xdb, 0x4e, 0x3d, 0x9d, 0x65, 0xff, + 0xfb, 0xce, 0x5d, 0x96, 0x6b, 0x20, 0x8b, 0x3a, 0xb2, 0xa4, 0x88, 0x53, + 0x34, 0xbf, 0x6f, 0x71, 0xe3, 0x16, 0x5f, 0x32, 0x3e, 0xe2, 0xcb, 0xfc, + 0x6c, 0x3d, 0xc8, 0x23, 0x59, 0x7e, 0x82, 0x0f, 0xa1, 0x65, 0xff, 0xd9, + 0xdf, 0x1b, 0x33, 0xbc, 0x13, 0x8b, 0x2f, 0xfd, 0x8f, 0xfc, 0x4b, 0x51, + 0x84, 0xb2, 0x82, 0x88, 0x13, 0x45, 0xbe, 0x3d, 0xfe, 0xde, 0xb2, 0xfc, + 0xfc, 0x82, 0x99, 0x65, 0xfe, 0xdc, 0x3f, 0xe7, 0x3f, 0x80, 0xb2, 0xa4, + 0x9f, 0x86, 0x11, 0x4c, 0x53, 0xe2, 0x3f, 0xcc, 0xca, 0x14, 0xfc, 0x22, + 0xe9, 0x36, 0xe1, 0x3d, 0xfe, 0x60, 0x8c, 0xfd, 0x87, 0xfa, 0xcb, 0xf4, + 0xed, 0x68, 0x1e, 0x59, 0x7e, 0x31, 0x60, 0x82, 0xb2, 0xb8, 0x7a, 0x5d, + 0x2b, 0xa8, 0x45, 0x5e, 0x42, 0x26, 0xf1, 0xe6, 0xcb, 0x2f, 0xff, 0x07, + 0xc7, 0xe3, 0xd4, 0x4c, 0xd6, 0x79, 0x65, 0x78, 0xf9, 0xc8, 0x72, 0xf7, + 0xe5, 0x8b, 0x2e, 0x2c, 0x59, 0x53, 0x8d, 0x8f, 0xe3, 0x97, 0xfe, 0xe1, + 0xce, 0xf3, 0xea, 0x37, 0xc2, 0xcb, 0xfe, 0x63, 0xca, 0x7c, 0x1e, 0x3d, + 0x2c, 0xad, 0x91, 0x43, 0xa2, 0x46, 0x20, 0xdf, 0x9f, 0x9d, 0x81, 0xac, + 0xa8, 0x3d, 0x80, 0x18, 0xdf, 0xe7, 0xd4, 0x9f, 0xae, 0x15, 0x95, 0x09, + 0xcf, 0x7a, 0x34, 0xb0, 0x10, 0xdf, 0x40, 0x91, 0xe5, 0x97, 0x1e, 0xcb, + 0x2f, 0x3b, 0x85, 0x51, 0x6e, 0x2b, 0x63, 0x7e, 0x11, 0x7b, 0xff, 0x1e, + 0xbc, 0xf9, 0xad, 0x8f, 0x65, 0x97, 0xd3, 0x40, 0xf1, 0x65, 0xff, 0xb3, + 0x58, 0x53, 0xbd, 0x9f, 0x34, 0xb2, 0xfc, 0x5f, 0xbf, 0xdc, 0x59, 0x50, + 0x7d, 0x4e, 0x83, 0x5f, 0x26, 0xdd, 0xa6, 0x1f, 0x11, 0x39, 0xff, 0x21, + 0x19, 0x7f, 0xe7, 0x19, 0x3e, 0xff, 0x44, 0xdc, 0x59, 0x7d, 0xbe, 0x02, + 0x4b, 0x2f, 0xe2, 0xfe, 0x62, 0x86, 0x2c, 0xa1, 0x51, 0x22, 0xc4, 0x07, + 0x23, 0xbc, 0xc2, 0x35, 0x96, 0x25, 0x97, 0xfd, 0xe0, 0x3f, 0x33, 0x08, + 0x55, 0x97, 0xee, 0x3f, 0xce, 0x35, 0x96, 0x31, 0x9f, 0x07, 0x4e, 0x2a, + 0x11, 0x65, 0x83, 0x7e, 0x70, 0xbf, 0xe8, 0x72, 0xf6, 0x61, 0x05, 0x65, + 0xfe, 0xef, 0x4f, 0x66, 0x41, 0x2c, 0xa9, 0x1f, 0x46, 0x1a, 0xdf, 0xe2, + 0x03, 0x23, 0x9d, 0x35, 0x97, 0xe8, 0xd6, 0xb3, 0x71, 0x65, 0x41, 0xee, + 0x11, 0x95, 0xfe, 0x00, 0xcb, 0x37, 0xbc, 0x96, 0x5f, 0xf4, 0x8f, 0x7e, + 0x79, 0xcb, 0x65, 0x97, 0xa3, 0x34, 0xb2, 0xff, 0x7a, 0x24, 0x73, 0xf8, + 0xc5, 0x94, 0x33, 0xd0, 0xe0, 0xdd, 0xfa, 0x77, 0xdc, 0x31, 0x56, 0x56, + 0xc7, 0x9d, 0xf1, 0x15, 0x7c, 0x8f, 0xc7, 0x86, 0xc5, 0xff, 0xb3, 0xc7, + 0xa1, 0xfa, 0x08, 0x2b, 0x2f, 0xfc, 0x59, 0xde, 0x44, 0xee, 0xe0, 0x56, + 0x54, 0x8f, 0xf0, 0xcf, 0x6f, 0xf7, 0xd2, 0x82, 0xee, 0x7e, 0xb2, 0xfb, + 0x9f, 0x9f, 0x56, 0x54, 0x95, 0x26, 0x3c, 0x66, 0x1c, 0x85, 0x20, 0x08, + 0x44, 0x34, 0xbf, 0x3e, 0xbb, 0x9e, 0x59, 0x7f, 0xe3, 0xfe, 0x24, 0x3f, + 0x41, 0x05, 0x65, 0x48, 0xf9, 0x7a, 0x4f, 0x7f, 0x76, 0x59, 0xac, 0xe2, + 0xca, 0x59, 0x78, 0x0f, 0xd5, 0x97, 0x02, 0x16, 0x54, 0x8d, 0x97, 0x47, + 0x29, 0x65, 0xf7, 0xc1, 0x3d, 0x2c, 0xb9, 0x8d, 0xf1, 0x11, 0x9f, 0x1e, + 0xb0, 0x87, 0xa1, 0x77, 0xd8, 0x32, 0x1a, 0xca, 0x83, 0xec, 0xde, 0x97, + 0x7f, 0xc5, 0xdf, 0x3c, 0x80, 0xe2, 0x2c, 0xa8, 0x4e, 0x8f, 0x08, 0x8e, + 0x32, 0x57, 0x23, 0xbc, 0x5d, 0x9f, 0x59, 0x7a, 0x74, 0xf3, 0x3c, 0xac, + 0xbf, 0xe9, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x8a, 0x1d, 0x46, 0x88, 0xf9, + 0x87, 0xdc, 0xa6, 0xfd, 0xfc, 0x07, 0x18, 0xb2, 0xfa, 0x26, 0xc1, 0xac, + 0xad, 0x1e, 0x57, 0x0a, 0x2f, 0xee, 0x71, 0xfd, 0x81, 0x59, 0x7f, 0xd9, + 0x03, 0xf4, 0x6e, 0x44, 0xcb, 0x2f, 0xf7, 0xe7, 0xef, 0x67, 0xee, 0xb2, + 0x98, 0x7d, 0xff, 0x9d, 0xdf, 0xf8, 0xb7, 0x1c, 0x9d, 0x83, 0xc1, 0xac, + 0xa8, 0x4c, 0x83, 0x08, 0x9e, 0x13, 0x9b, 0x84, 0x77, 0xfe, 0x71, 0xc0, + 0xfc, 0x64, 0x6c, 0x59, 0x7f, 0xed, 0xef, 0xae, 0x3f, 0xb9, 0x1b, 0x2c, + 0xbf, 0xdc, 0x00, 0x73, 0xa5, 0x0b, 0x29, 0x88, 0xaf, 0xf1, 0xe7, 0x50, + 0x6f, 0x77, 0x02, 0xb2, 0xec, 0x0a, 0xcb, 0x64, 0xe3, 0x65, 0xd1, 0xcb, + 0xff, 0x4b, 0xcf, 0xaf, 0xbb, 0x07, 0xb2, 0xca, 0x23, 0xe8, 0x01, 0x45, + 0xfe, 0x94, 0x6b, 0x68, 0xd6, 0xcb, 0x2f, 0xcf, 0xcd, 0xf9, 0xc5, 0x95, + 0x07, 0xbd, 0xf9, 0xad, 0xfa, 0x77, 0xb3, 0xf7, 0x59, 0x7f, 0x49, 0xf5, + 0x9f, 0x75, 0x65, 0xfc, 0x37, 0x67, 0x8d, 0xa5, 0x95, 0x07, 0xb9, 0x85, + 0xd7, 0xfd, 0x9d, 0xe0, 0x9c, 0xd3, 0xf1, 0x65, 0xf8, 0xfb, 0xc7, 0x35, + 0x97, 0xfd, 0xaf, 0xf4, 0xe5, 0xdc, 0xfd, 0x65, 0x6c, 0x9a, 0x06, 0xa1, + 0x0c, 0xc2, 0x0f, 0xce, 0x80, 0x4d, 0x78, 0x4d, 0x49, 0x65, 0xfc, 0x27, + 0x41, 0x19, 0xb2, 0xcb, 0xfb, 0xff, 0x39, 0xf8, 0x0b, 0x2a, 0x47, 0xf7, + 0x30, 0xf6, 0x8b, 0xef, 0xfe, 0x09, 0x47, 0x4b, 0x37, 0xe6, 0xa4, 0xb2, + 0xfc, 0xfc, 0xe6, 0xdc, 0x59, 0x7b, 0x8c, 0x75, 0x97, 0xf8, 0x85, 0x13, + 0xde, 0x07, 0x16, 0x54, 0xc7, 0xa4, 0x01, 0xcb, 0xff, 0x81, 0xcf, 0x3c, + 0xa0, 0x87, 0x9e, 0x59, 0x7f, 0xc2, 0x47, 0xf3, 0x4a, 0x35, 0xb2, 0xcb, + 0xf8, 0x9f, 0xa2, 0x40, 0x56, 0x53, 0x0f, 0xa8, 0x8f, 0x6f, 0x8b, 0xfd, + 0xbf, 0x59, 0x7f, 0xff, 0xa6, 0xe1, 0x8e, 0x74, 0xc6, 0x5b, 0xde, 0x67, + 0xe8, 0x21, 0x65, 0x62, 0x2b, 0x0c, 0x85, 0xc9, 0x6f, 0xf7, 0x27, 0x76, + 0x70, 0xf0, 0x96, 0x5b, 0xf5, 0x96, 0x3f, 0x8f, 0x27, 0x46, 0xf7, 0xe7, + 0xfb, 0x9d, 0x35, 0x95, 0x0a, 0xcc, 0x70, 0xc0, 0x54, 0x43, 0x7b, 0xd1, + 0x1f, 0xa3, 0x59, 0x27, 0x6e, 0x14, 0x5f, 0x9c, 0x02, 0xc0, 0x8b, 0x2f, + 0xd3, 0xde, 0x6a, 0x38, 0xb2, 0xa6, 0x3d, 0x52, 0x29, 0xbf, 0xb3, 0xa6, + 0x3c, 0x62, 0xcb, 0xe2, 0x91, 0xb1, 0x65, 0xa7, 0x4c, 0x79, 0xae, 0x59, + 0x7f, 0x19, 0x6f, 0x79, 0x9b, 0x5a, 0xcb, 0xfa, 0x26, 0xfa, 0x59, 0xbd, + 0x65, 0x4c, 0x7c, 0xdc, 0x35, 0xa1, 0xa2, 0xcd, 0xe1, 0x21, 0x7f, 0xfe, + 0x1f, 0xa0, 0x46, 0xfd, 0x27, 0xff, 0x9c, 0x8f, 0xd2, 0x5c, 0x3c, 0x59, + 0x7e, 0xda, 0x36, 0x7e, 0x2c, 0xac, 0x44, 0xc9, 0xad, 0x38, 0xb5, 0xdf, + 0x81, 0x65, 0xff, 0xb9, 0xbd, 0xe6, 0xd9, 0xc9, 0xe6, 0x59, 0x7f, 0x3f, + 0xf3, 0x14, 0x0d, 0x65, 0xff, 0x16, 0x6c, 0x3f, 0x61, 0x75, 0x65, 0x42, + 0x30, 0x26, 0x18, 0xf2, 0x11, 0x17, 0x54, 0xf4, 0xde, 0xfe, 0x4f, 0x25, + 0xd3, 0xc4, 0xf9, 0xb4, 0x76, 0x89, 0x53, 0xf2, 0x8c, 0xc8, 0x72, 0x8a, + 0xf2, 0x3a, 0x41, 0x61, 0xc2, 0x18, 0x71, 0x7d, 0x09, 0x63, 0x7d, 0x9a, + 0x53, 0x66, 0xa5, 0x2f, 0xb2, 0x19, 0xfe, 0x8e, 0x65, 0xe1, 0xe5, 0xfc, + 0x32, 0x89, 0xff, 0x91, 0xb1, 0x76, 0x5f, 0xf0, 0x21, 0x97, 0xbe, 0x32, + 0x29, 0xf8, 0x60, 0x09, 0x0e, 0xdb, 0xff, 0xf0, 0x5b, 0xff, 0xac, 0xf8, + 0x3e, 0x3e, 0xf1, 0xfe, 0x59, 0x4d, 0xd5, 0x1b, 0x02, 0x3d, 0xbb, 0xe8, + 0xd4, 0x98, 0xb2, 0xfb, 0xee, 0x3e, 0xea, 0xcb, 0xf6, 0x0c, 0x1d, 0xe2, + 0xcb, 0xcf, 0xa1, 0x56, 0x59, 0xbe, 0xc8, 0x8d, 0x32, 0x22, 0x26, 0x68, + 0xa2, 0xff, 0x37, 0xcd, 0x05, 0xdc, 0x2a, 0x8b, 0xcd, 0x50, 0xf9, 0xa9, + 0x32, 0x73, 0x1c, 0x67, 0x99, 0x5f, 0x5e, 0x7d, 0x3e, 0x4b, 0xa8, 0xf7, + 0xd8, 0xa7, 0xe8, 0xd2, 0x4a, 0xd0, 0x7f, 0x72, 0x17, 0x5b, 0xd1, 0x6f, + 0xf3, 0x7c, 0xd0, 0x5d, 0xc2, 0xa8, 0xa5, 0x97, 0x36, 0x1b, 0x45, 0x97, + 0xe3, 0x6f, 0xcc, 0x35, 0x97, 0xed, 0x05, 0xdc, 0x2a, 0x8b, 0x09, 0x7f, + 0xfd, 0x84, 0x38, 0xf4, 0x7d, 0xdc, 0x27, 0x62, 0xcb, 0xc4, 0x00, 0xac, + 0xa8, 0x3e, 0x97, 0x4d, 0xb7, 0x16, 0x5f, 0xc0, 0xd1, 0xec, 0xec, 0x59, + 0x66, 0xed, 0xa2, 0x68, 0x83, 0x20, 0xc2, 0x86, 0x42, 0x77, 0x84, 0x13, + 0xe2, 0x37, 0xf9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x59, 0x6b, 0x49, 0x65, + 0xda, 0x15, 0x65, 0xf3, 0x71, 0x5b, 0x49, 0xe9, 0x65, 0x24, 0xbc, 0xdc, + 0x46, 0x2c, 0xa0, 0x9e, 0xdf, 0x0c, 0x00, 0x17, 0x42, 0xa2, 0xcc, 0x84, + 0x78, 0xe7, 0x74, 0x79, 0x65, 0xbf, 0x59, 0x70, 0xbb, 0x2c, 0xb6, 0xa4, + 0x6a, 0xf0, 0x4a, 0x82, 0x7c, 0xee, 0x83, 0x71, 0xfe, 0xb2, 0xff, 0x9f, + 0xe2, 0xcf, 0xbb, 0x02, 0xac, 0xbf, 0xdb, 0x7d, 0xc8, 0x1f, 0x4d, 0x65, + 0xfa, 0x33, 0x7c, 0x71, 0x65, 0xa1, 0xcf, 0x75, 0xa3, 0x5a, 0x74, 0x5f, + 0x14, 0x25, 0xef, 0x9e, 0x52, 0x1a, 0xcb, 0xf9, 0xf5, 0x1d, 0x7f, 0x2c, + 0xbc, 0xd3, 0x4d, 0x24, 0xbf, 0xe8, 0x97, 0xdd, 0xd4, 0x67, 0xc9, 0x1b, + 0x9a, 0x0b, 0xfe, 0x03, 0xfd, 0x9d, 0x3f, 0x9a, 0x59, 0x74, 0x71, 0x65, + 0x0d, 0x30, 0xbf, 0x11, 0x12, 0x67, 0x13, 0x37, 0x9d, 0xdc, 0xda, 0x4f, + 0x4b, 0x2f, 0xe3, 0x7e, 0x7f, 0xc0, 0x2c, 0xbf, 0xf8, 0xe4, 0xfc, 0x72, + 0x00, 0x73, 0x89, 0x2f, 0xff, 0xef, 0x1f, 0x7d, 0x9c, 0x2c, 0x60, 0x39, + 0xe7, 0x92, 0xcb, 0xfd, 0x12, 0xeb, 0xf9, 0xc6, 0xb2, 0xf9, 0xfa, 0x2f, + 0x16, 0x54, 0x23, 0xc7, 0x10, 0xb4, 0xb4, 0xe6, 0x56, 0x92, 0xcb, 0x49, + 0x65, 0xa4, 0xb2, 0xf3, 0x4d, 0x34, 0xb2, 0xc1, 0x48, 0xdc, 0xd0, 0x54, + 0x1f, 0xb0, 0xa2, 0x26, 0x23, 0xf9, 0x8d, 0xfc, 0x0e, 0x79, 0xe5, 0x8b, + 0x2f, 0xff, 0xb8, 0x61, 0xc2, 0xd4, 0x16, 0x0f, 0x05, 0x59, 0x4c, 0x3f, + 0xae, 0x96, 0xdb, 0xe5, 0x96, 0x15, 0x65, 0x80, 0xb2, 0x88, 0xd1, 0xf0, + 0x4a, 0xb0, 0xfd, 0xf8, 0x45, 0xd3, 0x5b, 0xb8, 0x2a, 0xcb, 0x62, 0xcb, + 0x81, 0xfc, 0x1a, 0x81, 0x8c, 0x5c, 0x70, 0xb2, 0xfe, 0x28, 0x61, 0x46, + 0x2c, 0xbf, 0xfa, 0x1f, 0x5d, 0xf6, 0x0c, 0x1d, 0xe2, 0xcb, 0xa5, 0x0b, + 0x2f, 0x70, 0x62, 0xac, 0xad, 0x91, 0xc8, 0x32, 0xd9, 0x85, 0x7c, 0x54, + 0x04, 0x41, 0x05, 0xaf, 0xff, 0xff, 0xbd, 0x1d, 0xe1, 0xf3, 0x91, 0xdf, + 0x1c, 0x6b, 0xf6, 0x3c, 0xa5, 0xd1, 0x56, 0x5c, 0x7f, 0x2c, 0xba, 0x18, + 0xb2, 0xff, 0xfd, 0x04, 0x09, 0x4a, 0x3b, 0xec, 0x60, 0x91, 0xfa, 0xcb, + 0xff, 0xe3, 0x20, 0x4b, 0x39, 0xa8, 0x39, 0x3f, 0x12, 0x54, 0x91, 0x47, + 0xd5, 0x6b, 0x82, 0xde, 0x19, 0x25, 0xbb, 0x42, 0x5e, 0x44, 0x23, 0x86, + 0xae, 0x46, 0x62, 0x2a, 0x60, 0x48, 0x75, 0x18, 0xfb, 0xc2, 0xc0, 0xa3, + 0x2d, 0xe4, 0x67, 0x5d, 0x5a, 0xde, 0xff, 0x3e, 0x2e, 0x24, 0x2e, 0x6f, + 0xff, 0x37, 0x63, 0xc9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x51, 0x8b, 0xf9, + 0xf5, 0xfb, 0x23, 0xf5, 0x97, 0xd1, 0xa8, 0xfd, 0x65, 0x78, 0xf4, 0x77, + 0x97, 0xdf, 0xc4, 0xfd, 0xf1, 0xcc, 0xb2, 0xfd, 0xe8, 0xdb, 0x06, 0xb2, + 0xfc, 0x4f, 0xfc, 0x08, 0xb2, 0xff, 0xc7, 0xf4, 0xb8, 0x59, 0xdf, 0x01, + 0x65, 0xff, 0x1e, 0xbd, 0x98, 0x52, 0xe2, 0xca, 0x91, 0xfb, 0x04, 0xfe, + 0xf9, 0xf7, 0xe1, 0x2c, 0xaf, 0x8f, 0x13, 0x44, 0x57, 0xfe, 0x79, 0x07, + 0xc6, 0xc8, 0xd0, 0xab, 0x2c, 0xde, 0x13, 0xbd, 0xc2, 0x49, 0x8b, 0x7c, + 0x51, 0xd8, 0x71, 0x34, 0x47, 0x52, 0x67, 0xf6, 0xea, 0x38, 0x56, 0x36, + 0x3d, 0x28, 0x87, 0x91, 0x87, 0x76, 0x53, 0x45, 0xff, 0xcd, 0xde, 0x4d, + 0xf3, 0x41, 0x77, 0x0a, 0xa2, 0x39, 0x5f, 0xfe, 0x6e, 0xc7, 0x93, 0x7c, + 0xd0, 0x5d, 0xc2, 0xa8, 0x9c, 0x97, 0xf9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, + 0x59, 0x8b, 0xbb, 0xfa, 0xcb, 0xf6, 0x78, 0xb3, 0x4b, 0x2f, 0x6d, 0x1a, + 0x59, 0x7e, 0x97, 0x1d, 0x8d, 0xfc, 0x7b, 0x64, 0x31, 0xc2, 0x6b, 0xe9, + 0xe1, 0x3d, 0x4f, 0x53, 0xc1, 0x65, 0xfe, 0x1e, 0x32, 0x3e, 0x86, 0x2c, + 0xa9, 0xe8, 0xfb, 0x27, 0x81, 0xcd, 0xfe, 0xd4, 0x77, 0xcf, 0xf4, 0x96, + 0x5f, 0xff, 0xff, 0x44, 0xde, 0x8e, 0xc0, 0x87, 0xa9, 0xa3, 0xd9, 0xbf, + 0x3d, 0x83, 0xdf, 0x8b, 0x2b, 0xf4, 0x5a, 0x91, 0xa5, 0xff, 0x67, 0x1f, + 0xbf, 0x88, 0x52, 0x59, 0x6f, 0xd6, 0x5f, 0xdc, 0xd6, 0xb3, 0xee, 0x2c, + 0xa9, 0xe8, 0xf0, 0x70, 0x4a, 0xf6, 0xb1, 0x8b, 0x2f, 0x8c, 0xb3, 0x8b, + 0x2d, 0x9d, 0x37, 0x9b, 0x83, 0x97, 0xff, 0xfe, 0xf4, 0x10, 0x39, 0xc8, + 0x13, 0xa6, 0x40, 0x96, 0x73, 0xa6, 0xb2, 0xfb, 0xbd, 0x3d, 0x2c, 0xa9, + 0x22, 0x37, 0x4d, 0x77, 0x9c, 0x82, 0xb2, 0xf7, 0x23, 0x8b, 0x2f, 0x6d, + 0x3f, 0xc5, 0x97, 0x4f, 0x7e, 0x59, 0x63, 0x59, 0x7f, 0xa3, 0x91, 0xdd, + 0xb1, 0xa5, 0x96, 0x36, 0x1e, 0x21, 0x08, 0x5f, 0xf8, 0x87, 0xe8, 0xe7, + 0x33, 0x50, 0xb2, 0xef, 0xdd, 0x65, 0xfb, 0x3e, 0xee, 0x42, 0xcb, 0xa7, + 0x08, 0xb2, 0xfc, 0x1f, 0x39, 0xb6, 0xd6, 0x5f, 0x81, 0xc8, 0xe4, 0x2c, + 0xad, 0x1e, 0x93, 0x95, 0xdf, 0xc5, 0xff, 0xb3, 0xf7, 0x59, 0x7c, 0x51, + 0xbc, 0x0b, 0x28, 0xcf, 0x45, 0xcb, 0xae, 0xf9, 0x8b, 0x2f, 0xfe, 0x9f, + 0x72, 0xff, 0x3b, 0x04, 0x7f, 0xac, 0xb3, 0x79, 0xe9, 0x75, 0xc2, 0x21, + 0xc5, 0xb1, 0x1c, 0x9e, 0xc6, 0xc5, 0x90, 0xb6, 0x09, 0x1f, 0xc3, 0x73, + 0x0e, 0x68, 0x85, 0x8c, 0x0e, 0x4c, 0xdb, 0x3d, 0x21, 0x7e, 0x13, 0x81, + 0xb3, 0x7b, 0x94, 0xf9, 0x00, 0x83, 0x17, 0xf6, 0x68, 0x2e, 0xe1, 0x54, + 0x5b, 0xab, 0xf3, 0x71, 0xfa, 0x34, 0xb2, 0x9b, 0x9f, 0x1f, 0x8e, 0x6f, + 0xe2, 0x76, 0xfa, 0xdb, 0xcb, 0x2f, 0x1f, 0xdf, 0xac, 0xbf, 0xfb, 0x4e, + 0x1f, 0xc1, 0xce, 0xc7, 0xcd, 0x2c, 0xb8, 0x85, 0x59, 0x7c, 0x17, 0x70, + 0xaa, 0x29, 0x05, 0x61, 0xe2, 0xe8, 0x5e, 0xff, 0x4b, 0xa6, 0xd7, 0x3f, + 0x35, 0x97, 0xfe, 0xcf, 0x1e, 0xff, 0x66, 0x10, 0x56, 0x5f, 0xd9, 0xec, + 0xf1, 0x8a, 0xb2, 0xf3, 0xc9, 0xb8, 0xd3, 0x4e, 0xc8, 0x46, 0xfc, 0x43, + 0xc3, 0x5e, 0x9f, 0x5f, 0xe3, 0x18, 0x3b, 0xc6, 0x12, 0xcb, 0xe6, 0x99, + 0x1c, 0x59, 0x79, 0xca, 0x4b, 0x2c, 0xdc, 0x2a, 0x9c, 0x34, 0x60, 0xc8, + 0xdb, 0x1d, 0x8c, 0x06, 0x6d, 0x11, 0xdf, 0xfc, 0xdd, 0xe4, 0xdf, 0x34, + 0x17, 0x70, 0xaa, 0x25, 0x25, 0x6c, 0xcc, 0x48, 0x99, 0x51, 0x8b, 0x4f, + 0x09, 0xcf, 0xe7, 0x8a, 0x4a, 0x38, 0x6e, 0xca, 0x94, 0xdc, 0x67, 0xbc, + 0xdd, 0xc2, 0xb2, 0xfe, 0x32, 0xcf, 0x60, 0x56, 0x5f, 0xc6, 0x5d, 0xc3, + 0x1a, 0xcb, 0xa5, 0xd5, 0x97, 0x8a, 0x1a, 0x59, 0x74, 0x37, 0xd1, 0xb3, + 0x21, 0x7a, 0x92, 0x22, 0xfa, 0xbd, 0x7b, 0x80, 0x62, 0xcb, 0x36, 0x16, + 0x59, 0xbc, 0x93, 0x0b, 0xfa, 0x16, 0xc6, 0x47, 0x3e, 0x3b, 0x78, 0x9e, + 0x4b, 0x2f, 0xda, 0x0b, 0xb8, 0x55, 0x13, 0xb2, 0xff, 0xec, 0xf1, 0x3c, + 0x81, 0xfb, 0x1e, 0x4b, 0x2f, 0x3c, 0x9b, 0xe1, 0xfc, 0x11, 0xa5, 0xdb, + 0x6e, 0xac, 0xbf, 0xff, 0x18, 0xc0, 0xc8, 0xdc, 0x71, 0x94, 0x16, 0x05, + 0x65, 0xfe, 0x94, 0x6b, 0x68, 0xd6, 0xcb, 0x2f, 0xfd, 0xe2, 0x79, 0x03, + 0xf6, 0x3c, 0x96, 0x54, 0x1f, 0xa6, 0x1a, 0xdf, 0xff, 0xb3, 0xe9, 0x7a, + 0x38, 0xfa, 0x8d, 0xcf, 0x38, 0x56, 0x59, 0xbe, 0x2a, 0x22, 0x64, 0x24, + 0xfc, 0x66, 0xe3, 0x65, 0x0c, 0x9d, 0xc2, 0x0b, 0xe3, 0xd4, 0x6f, 0x59, + 0x76, 0x79, 0x65, 0xff, 0xd3, 0xee, 0x5f, 0xe7, 0x60, 0x8f, 0xf5, 0x96, + 0x6f, 0xc3, 0xff, 0x9f, 0x23, 0x10, 0x5a, 0xff, 0xe7, 0x67, 0xb0, 0x2f, + 0xe8, 0x12, 0x4b, 0x2f, 0x18, 0xdd, 0x65, 0xff, 0xc5, 0x9e, 0x7d, 0xb3, + 0x84, 0xff, 0x2c, 0xba, 0x7d, 0xb8, 0xd1, 0x45, 0xe4, 0x4e, 0x0d, 0xd4, + 0x2e, 0xd0, 0xca, 0x32, 0x96, 0x4a, 0x70, 0xe4, 0x3a, 0xa7, 0xe1, 0xbb, + 0x7f, 0xee, 0x41, 0x7d, 0xff, 0x39, 0x1f, 0xac, 0xbf, 0xff, 0xff, 0xef, + 0x1f, 0x5d, 0xfe, 0xe7, 0x39, 0x0c, 0x8f, 0x66, 0x6d, 0x85, 0x9f, 0x77, + 0x91, 0xf2, 0xcb, 0xfd, 0x12, 0x3e, 0x99, 0x49, 0x65, 0xff, 0xff, 0x82, + 0x0d, 0xb3, 0x7c, 0x7d, 0x83, 0x79, 0x08, 0xce, 0x63, 0x8d, 0x65, 0xf0, + 0xdf, 0xad, 0xf1, 0x37, 0x60, 0xa0, 0xea, 0x12, 0x9c, 0x31, 0xbf, 0xfa, + 0x6f, 0xb8, 0x6c, 0x9c, 0x26, 0xb3, 0x65, 0x97, 0xc1, 0x77, 0x0a, 0xa2, + 0x2c, 0x5f, 0xc7, 0xa7, 0x20, 0xc2, 0xca, 0xd1, 0xec, 0xf8, 0xba, 0xff, + 0xcf, 0x26, 0xf9, 0xa0, 0xbb, 0x85, 0x51, 0x2e, 0xaf, 0xff, 0xec, 0x1f, + 0xa0, 0x46, 0xfd, 0x27, 0xff, 0x9c, 0x8f, 0xd2, 0x59, 0xbc, 0x26, 0xe3, + 0x90, 0xa4, 0x61, 0x14, 0xfa, 0x65, 0xff, 0xcc, 0x79, 0x37, 0xcd, 0x05, + 0xdc, 0x2a, 0x89, 0x89, 0x7f, 0x7a, 0x0b, 0xe9, 0x9d, 0x65, 0xfe, 0x73, + 0x9f, 0x3f, 0xf0, 0x6b, 0x2f, 0xb0, 0x30, 0xde, 0x0f, 0x8f, 0xc5, 0xd4, + 0xdd, 0x1d, 0xde, 0x85, 0xad, 0xfb, 0x41, 0x77, 0x0a, 0xa2, 0xa9, 0x5b, + 0x16, 0x56, 0x1e, 0x20, 0xa6, 0x97, 0xfe, 0x03, 0xea, 0x45, 0x8d, 0x3f, + 0xeb, 0x2f, 0xfe, 0x7e, 0x68, 0xda, 0xef, 0x8f, 0x52, 0x59, 0x7f, 0xda, + 0x7f, 0x7d, 0x2c, 0xeb, 0x71, 0xa2, 0x13, 0xc7, 0xf4, 0xdd, 0x1f, 0x4f, + 0x0a, 0x6b, 0xff, 0x3c, 0x9b, 0xe6, 0x82, 0xee, 0x15, 0x44, 0xe8, 0xbe, + 0xe1, 0xfc, 0xd2, 0xca, 0x61, 0xf8, 0x7e, 0x99, 0x7f, 0xe6, 0x40, 0xad, + 0xc1, 0xb1, 0x46, 0xcb, 0x29, 0xb9, 0xf3, 0x19, 0x1d, 0xfe, 0x37, 0xd4, + 0x49, 0xf7, 0xac, 0xbf, 0x4d, 0x13, 0x47, 0x16, 0x5f, 0xcd, 0x61, 0xef, + 0xc2, 0x59, 0x46, 0x7a, 0xfc, 0x29, 0xbc, 0x7e, 0x85, 0x8d, 0xcd, 0x0d, + 0xff, 0xc0, 0xe7, 0x63, 0xe6, 0xe0, 0x98, 0xd8, 0xb2, 0x98, 0x7e, 0xde, + 0x2d, 0xbf, 0xf3, 0xc9, 0xbe, 0x68, 0x2e, 0xe1, 0x54, 0x4e, 0xeb, 0xf7, + 0x40, 0xec, 0x34, 0x97, 0xfe, 0x28, 0xee, 0x73, 0x33, 0xe9, 0x2c, 0xa1, + 0xa7, 0xe7, 0x91, 0x90, 0xb0, 0x89, 0xd3, 0x3a, 0x4f, 0x7f, 0xc3, 0x89, + 0x43, 0x76, 0xb9, 0xfa, 0xcb, 0xf7, 0x0a, 0x3f, 0xe2, 0xcb, 0xc5, 0x83, + 0x59, 0x74, 0x37, 0x83, 0xc3, 0xfc, 0xa2, 0x9b, 0xa3, 0xfb, 0x13, 0xca, + 0x10, 0x17, 0xee, 0x37, 0x0f, 0x61, 0x65, 0xff, 0xe6, 0xec, 0x79, 0x37, + 0xcd, 0x05, 0xdc, 0x2a, 0x8a, 0x15, 0x7f, 0xfc, 0xfd, 0x96, 0x11, 0x8d, + 0xbf, 0x36, 0x8d, 0x2c, 0xbf, 0x37, 0xd9, 0x8e, 0xd2, 0xcb, 0xff, 0xff, + 0x80, 0xc6, 0x3c, 0x9b, 0x87, 0xc0, 0xec, 0x16, 0x6c, 0x28, 0x22, 0x65, + 0x95, 0x24, 0x51, 0xf8, 0xb2, 0xfc, 0x0f, 0xba, 0x61, 0x59, 0x7e, 0xcf, + 0xf1, 0xc9, 0x65, 0xf7, 0x0d, 0xae, 0xac, 0xbd, 0x1f, 0xb7, 0xd8, 0xfc, + 0x88, 0xa7, 0xa4, 0xd4, 0x68, 0xd8, 0x28, 0x4a, 0xdf, 0xf3, 0x1f, 0x98, + 0xd4, 0x10, 0xd6, 0x5f, 0xf9, 0xe4, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x29, + 0x25, 0xfe, 0xcf, 0x16, 0x0f, 0xc0, 0x59, 0x7a, 0x08, 0x6b, 0x2c, 0xdf, + 0x11, 0xa2, 0xc3, 0x8e, 0xa6, 0x34, 0x65, 0x73, 0x09, 0x65, 0xff, 0xd3, + 0x79, 0xd9, 0xdf, 0x47, 0x40, 0xc5, 0x97, 0xd8, 0x51, 0xb2, 0xca, 0xc3, + 0xe7, 0xd2, 0x35, 0xec, 0xe3, 0x7e, 0x22, 0x78, 0x47, 0xda, 0x86, 0x68, + 0xbe, 0xd1, 0xe6, 0x4a, 0x33, 0xa0, 0xc6, 0xd1, 0xa8, 0x7f, 0xb2, 0x58, + 0xa3, 0x9a, 0xfe, 0x54, 0x4b, 0x5d, 0x8f, 0x30, 0x10, 0xf7, 0x9f, 0x87, + 0x1d, 0xfe, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x11, 0x22, 0xf6, 0x9f, 0x4b, + 0x2f, 0xfc, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x12, 0xf2, 0xcd, 0xcc, + 0xfa, 0x18, 0x39, 0x7f, 0xf4, 0x6f, 0x79, 0x8f, 0x5f, 0x4a, 0x26, 0x59, + 0x7a, 0x7a, 0x67, 0xeb, 0x2f, 0xff, 0x7a, 0x38, 0x20, 0xe3, 0x53, 0x61, + 0x7e, 0xb2, 0xfb, 0x40, 0xef, 0x16, 0x5f, 0xb5, 0xfb, 0x0f, 0x8b, 0x2f, + 0x41, 0x7c, 0xb2, 0xfd, 0xd1, 0x18, 0x50, 0xb2, 0xc7, 0xe3, 0xc4, 0x21, + 0xcb, 0xff, 0xde, 0xdf, 0x05, 0xfe, 0xee, 0x0f, 0x35, 0xf2, 0xcb, 0xfc, + 0x7a, 0x71, 0x99, 0x0a, 0xb2, 0xe8, 0xea, 0xcb, 0xfe, 0x7d, 0xb5, 0x1f, + 0x48, 0xd8, 0xb2, 0xff, 0xf0, 0x06, 0x7a, 0x12, 0x6d, 0xc0, 0x39, 0x4c, + 0xb2, 0xa4, 0x9a, 0x41, 0x93, 0x79, 0x43, 0xf3, 0x22, 0x16, 0xe1, 0xd5, + 0xfd, 0xff, 0xa3, 0x46, 0xc5, 0x97, 0xfe, 0x9b, 0x91, 0xbd, 0xfd, 0x1f, + 0x4c, 0xb2, 0xa0, 0xfc, 0x5c, 0xba, 0xf8, 0xf7, 0x04, 0x62, 0xcb, 0xf6, + 0x6d, 0x9c, 0x62, 0xcb, 0xf9, 0xf6, 0x7e, 0x3f, 0xeb, 0x2f, 0xfe, 0x9a, + 0x70, 0x85, 0x1b, 0xdf, 0x7c, 0x4c, 0xb2, 0x96, 0x5f, 0x77, 0x35, 0x32, + 0xcb, 0xdd, 0x7d, 0xeb, 0x2d, 0x2c, 0x3c, 0x17, 0x23, 0xbf, 0x3f, 0x23, + 0x7e, 0x2c, 0xa9, 0x8f, 0x3b, 0x79, 0x35, 0x42, 0x61, 0x78, 0x98, 0xf0, + 0x9a, 0xbb, 0x50, 0xb2, 0xa4, 0xb9, 0xb8, 0x34, 0xc3, 0x23, 0x9a, 0x3b, + 0x5d, 0x43, 0x23, 0xc4, 0x04, 0x4d, 0xd2, 0x8d, 0xf1, 0x9c, 0xee, 0x19, + 0xdd, 0x84, 0xb2, 0xfe, 0x9d, 0xcf, 0x61, 0xf5, 0x65, 0x0c, 0xf1, 0x0c, + 0x56, 0xe1, 0xfc, 0xb2, 0xfe, 0x20, 0x76, 0x68, 0x15, 0x65, 0xe6, 0xd9, + 0xe9, 0x65, 0x8d, 0xcf, 0x3c, 0x05, 0xf7, 0xf8, 0x1d, 0xf1, 0x46, 0x69, + 0x65, 0xf8, 0xb3, 0x61, 0x37, 0xac, 0xbe, 0xcd, 0x84, 0xde, 0xb2, 0xfc, + 0x38, 0xf8, 0x6f, 0x38, 0xf4, 0x48, 0xae, 0xff, 0xf8, 0xf5, 0x3b, 0xd0, + 0x7d, 0xe0, 0x02, 0x08, 0x59, 0x7c, 0x7b, 0x7f, 0xba, 0xb2, 0xf3, 0xb8, + 0x55, 0x14, 0xba, 0x96, 0x50, 0x4d, 0x9c, 0xc5, 0x17, 0xc5, 0xd8, 0xd2, + 0xcb, 0xfe, 0xcd, 0x6e, 0xe0, 0xf3, 0x5f, 0x2c, 0xa3, 0x3d, 0xdd, 0xe4, + 0x37, 0xfe, 0x89, 0x79, 0xcf, 0xee, 0x40, 0xab, 0x2b, 0x65, 0x49, 0xf2, + 0x84, 0x18, 0xa8, 0x13, 0x28, 0xe9, 0x6b, 0xcf, 0xe4, 0x47, 0x7a, 0x35, + 0xd5, 0x97, 0xf0, 0xfe, 0x8d, 0x1f, 0xcb, 0x2f, 0xbb, 0xb6, 0x34, 0xb2, + 0xa4, 0x7d, 0xe6, 0x38, 0x45, 0xf7, 0xe3, 0x17, 0x59, 0xc5, 0x97, 0xfe, + 0x82, 0x8f, 0x4e, 0x01, 0x00, 0x2b, 0x2f, 0x01, 0xf8, 0xb2, 0xfa, 0x60, + 0x3c, 0xcb, 0x2f, 0xd1, 0xfb, 0xfd, 0xc5, 0x97, 0x88, 0xff, 0x59, 0x7b, + 0x3b, 0xc5, 0x95, 0x06, 0xe3, 0x83, 0x95, 0x24, 0xca, 0x06, 0x51, 0x31, + 0xff, 0x87, 0x3f, 0x24, 0xeb, 0x0d, 0xfb, 0x75, 0xc8, 0x7b, 0x8b, 0x29, + 0x65, 0xfb, 0x9d, 0xd4, 0x71, 0x65, 0xa3, 0xe3, 0x67, 0xd0, 0xbb, 0xfe, + 0xff, 0x9a, 0x8f, 0xa4, 0x6c, 0x59, 0x7b, 0xaf, 0xbd, 0x65, 0xfb, 0x60, + 0x4d, 0x1b, 0xab, 0x2f, 0x81, 0x34, 0x6e, 0xac, 0xb9, 0xf6, 0x9c, 0x7a, + 0x7b, 0x16, 0xd4, 0x23, 0x75, 0xce, 0xc0, 0xe3, 0x7f, 0xd0, 0x7f, 0xf2, + 0x18, 0xfd, 0x59, 0x7f, 0xc7, 0xc9, 0x8c, 0xbb, 0x1f, 0xac, 0xbf, 0xf8, + 0xbb, 0xbe, 0x35, 0xc7, 0x28, 0xdd, 0x59, 0x52, 0x47, 0x36, 0x8b, 0x98, + 0x71, 0xc3, 0x9b, 0xfd, 0x1b, 0xbc, 0x86, 0x3f, 0x56, 0x5c, 0xe4, 0xb2, + 0xff, 0xa3, 0xf9, 0xde, 0x86, 0x39, 0x2c, 0xaf, 0x8f, 0x3f, 0x78, 0xad, + 0x49, 0x15, 0x3a, 0x84, 0x25, 0xfd, 0x0d, 0x1f, 0x81, 0xba, 0xb2, 0xfe, + 0xfa, 0x5c, 0x72, 0xf9, 0x65, 0x42, 0xbb, 0x89, 0x30, 0x64, 0x7d, 0x47, + 0x0e, 0x6d, 0x14, 0x08, 0x63, 0x7f, 0xe2, 0x9a, 0x76, 0xa3, 0xe9, 0x1b, + 0x16, 0x5f, 0xfe, 0xdf, 0x05, 0xde, 0xfb, 0x06, 0x0e, 0xf1, 0x65, 0xd0, + 0xc5, 0x95, 0x08, 0xa4, 0xc4, 0x2d, 0x26, 0x5f, 0xd0, 0x53, 0x74, 0xc5, + 0x59, 0x7f, 0xd2, 0xcd, 0x4d, 0x85, 0x0c, 0x59, 0x7e, 0xfe, 0x03, 0x8c, + 0x59, 0x7f, 0xb3, 0x6e, 0x44, 0xc0, 0xd2, 0xca, 0x34, 0x4a, 0xe8, 0xe0, + 0x8a, 0x2e, 0x96, 0x2c, 0xbf, 0x70, 0x5d, 0x3b, 0x4b, 0x2f, 0x14, 0x31, + 0x65, 0x0c, 0xf1, 0x74, 0x55, 0x7d, 0xf6, 0xee, 0x49, 0x65, 0xfe, 0x7d, + 0x4d, 0x9d, 0xdc, 0xde, 0xb2, 0xf4, 0x7f, 0xc5, 0x95, 0x09, 0x93, 0x8c, + 0xbb, 0x16, 0xdc, 0x88, 0x04, 0xcd, 0x1c, 0xdf, 0x3f, 0x9f, 0x65, 0x97, + 0xef, 0x00, 0x5c, 0x25, 0x97, 0xf9, 0xa2, 0x7e, 0xc9, 0xc6, 0xb2, 0xfd, + 0xd1, 0x36, 0x8f, 0x2c, 0xbf, 0xe8, 0xf7, 0x1f, 0xb1, 0xad, 0xc5, 0x97, + 0xfe, 0x63, 0x8c, 0x49, 0xa5, 0x05, 0xf2, 0xca, 0x19, 0xfe, 0x19, 0xdd, + 0xfc, 0x0f, 0x46, 0xb3, 0xf5, 0x97, 0xda, 0xfc, 0x4d, 0xeb, 0x2f, 0x1b, + 0x5d, 0x59, 0x52, 0x3f, 0x2f, 0x17, 0x70, 0x9e, 0xb6, 0x4f, 0x94, 0x64, + 0x5f, 0x14, 0x4c, 0x67, 0xe8, 0x53, 0x76, 0x12, 0x77, 0xf8, 0xbb, 0x9c, + 0x37, 0xf2, 0xcb, 0xee, 0x4b, 0x8e, 0xb2, 0xff, 0xbf, 0x3f, 0x66, 0x9e, + 0x6e, 0x2c, 0xbf, 0xfe, 0x91, 0x46, 0xc2, 0x4d, 0xcf, 0x66, 0xe6, 0xbf, + 0x59, 0x7a, 0x43, 0xde, 0xb2, 0xa0, 0xfd, 0x9d, 0x5e, 0xfe, 0x9c, 0xcc, + 0xeb, 0xf5, 0x65, 0xfd, 0xe3, 0x19, 0xe8, 0x2b, 0x2c, 0x15, 0x96, 0xe6, + 0x1f, 0x68, 0x4b, 0xdc, 0xb6, 0xf7, 0xd3, 0x71, 0x65, 0x7c, 0x7a, 0x4e, + 0x67, 0x7a, 0x0c, 0x6b, 0x2a, 0x15, 0x0f, 0x8c, 0xc7, 0xe2, 0x26, 0x42, + 0xc1, 0xe1, 0xbf, 0xc2, 0x2b, 0xfd, 0x8d, 0x48, 0xf8, 0x06, 0x2c, 0xbb, + 0x7f, 0x16, 0x5f, 0xc0, 0xe4, 0x4c, 0x0d, 0x2c, 0xbf, 0x66, 0xd9, 0xee, + 0x2c, 0xa8, 0x3f, 0x0f, 0x0c, 0x91, 0x7d, 0xec, 0xd7, 0x16, 0x50, 0x4f, + 0x27, 0x79, 0x6d, 0xff, 0x7f, 0x1f, 0x89, 0xbe, 0x0b, 0x71, 0x65, 0xff, + 0xfb, 0x68, 0xec, 0x78, 0xfb, 0x23, 0xd0, 0x7d, 0x0b, 0x2a, 0x13, 0xa6, + 0xc8, 0x76, 0x99, 0x23, 0xa0, 0x5f, 0xff, 0xfd, 0xe8, 0xd7, 0xc1, 0x3f, + 0xa7, 0x0a, 0xf3, 0xb9, 0xe7, 0x0c, 0x10, 0xd6, 0x5f, 0x18, 0xb3, 0xf8, + 0xb2, 0xfe, 0x13, 0xb1, 0xb1, 0x7c, 0xb2, 0xf1, 0x1b, 0x16, 0x56, 0x8f, + 0xc3, 0xf2, 0x5e, 0x98, 0x5f, 0xe2, 0x87, 0x2e, 0xe1, 0x2c, 0xbf, 0xd1, + 0xf7, 0x37, 0x63, 0x42, 0xac, 0xbf, 0xde, 0x79, 0x8f, 0x51, 0xb2, 0xcb, + 0xfb, 0x37, 0x19, 0x19, 0xd5, 0x97, 0x63, 0x16, 0x50, 0xd3, 0xf8, 0xc8, + 0x74, 0x30, 0xc3, 0xc5, 0xee, 0x70, 0x03, 0x4d, 0xe5, 0xf7, 0xed, 0xc3, + 0x64, 0x71, 0x65, 0xf9, 0xf7, 0xe6, 0xb8, 0xb2, 0x86, 0x7a, 0x6c, 0x2a, + 0xbf, 0x79, 0xc8, 0xd8, 0xb2, 0xf8, 0x59, 0xb9, 0x0b, 0x2f, 0xef, 0xfe, + 0xff, 0x35, 0x8b, 0x2f, 0xe0, 0xc1, 0xf4, 0xe4, 0xb2, 0xfe, 0x7f, 0xd9, + 0x19, 0xd5, 0x95, 0x08, 0x89, 0xc3, 0x03, 0x2c, 0xbe, 0x1f, 0x9f, 0xe5, + 0x97, 0xf7, 0xfc, 0x8f, 0x47, 0x16, 0x54, 0x1e, 0x8b, 0x91, 0xdf, 0xbe, + 0xef, 0x33, 0x65, 0x97, 0x66, 0xcb, 0x29, 0xcf, 0x04, 0x8a, 0xaa, 0x13, + 0xf4, 0xc2, 0x23, 0x26, 0xf4, 0x2b, 0x49, 0xfb, 0x8c, 0x37, 0x7e, 0x15, + 0x96, 0xe2, 0xcb, 0x8c, 0x5e, 0x1a, 0x90, 0x0c, 0x5e, 0xfd, 0xc2, 0xb2, + 0xf9, 0xa9, 0xb9, 0x0b, 0x2f, 0x9f, 0x68, 0xea, 0xcb, 0xf3, 0x91, 0xee, + 0xef, 0x59, 0x6e, 0x11, 0xe6, 0xef, 0x22, 0xbf, 0x6c, 0xfc, 0xc9, 0x2c, + 0xbf, 0xfe, 0x1e, 0x6f, 0x7d, 0x4c, 0x24, 0x9f, 0xae, 0xc5, 0x95, 0x07, + 0xf6, 0x65, 0x17, 0xf3, 0xfd, 0xfb, 0x0f, 0x8b, 0x2a, 0x13, 0x81, 0xf8, + 0x76, 0x67, 0x1d, 0x42, 0x93, 0xc4, 0x17, 0xf1, 0xb4, 0xf3, 0x43, 0x16, + 0x5e, 0xdc, 0x8e, 0x2c, 0xae, 0x9e, 0x68, 0x0b, 0xac, 0xdd, 0xb2, 0xe8, + 0x4a, 0xe7, 0x94, 0x88, 0x9c, 0xff, 0xda, 0x1c, 0xf2, 0x21, 0x1b, 0x3e, + 0x4a, 0x75, 0xfa, 0x1b, 0x87, 0x1a, 0xac, 0xd3, 0x82, 0x5a, 0x8c, 0x19, + 0x85, 0xde, 0x8f, 0xa9, 0xe5, 0x1d, 0xff, 0x2a, 0x10, 0xa5, 0x04, 0x72, + 0x51, 0xbf, 0x65, 0x58, 0xcf, 0xc2, 0x08, 0x48, 0xeb, 0xb7, 0x21, 0x3d, + 0x7e, 0xd0, 0x5d, 0xc2, 0xa8, 0xa7, 0x17, 0xf7, 0x8d, 0x84, 0xe2, 0xac, + 0xb3, 0x7c, 0x3e, 0x13, 0x34, 0xbf, 0xa1, 0x99, 0xb9, 0x1d, 0x59, 0x7d, + 0xe8, 0x21, 0x56, 0x53, 0x0f, 0x47, 0xa5, 0xf7, 0xd9, 0xc1, 0x24, 0xb2, + 0xff, 0xc7, 0xa7, 0xe7, 0xb3, 0x08, 0x2b, 0x2f, 0xcc, 0xd6, 0xb3, 0x8b, + 0x2f, 0xff, 0xde, 0x07, 0x75, 0x1d, 0xe6, 0x07, 0x91, 0xa0, 0xac, 0xbf, + 0xee, 0x18, 0xb9, 0x28, 0xff, 0x8b, 0x2f, 0xff, 0xee, 0xe4, 0xc4, 0x7c, + 0xec, 0x08, 0x7a, 0x9a, 0x3c, 0xb2, 0xfe, 0xfa, 0x50, 0xc0, 0x4c, 0xb2, + 0xfe, 0x60, 0xe3, 0x80, 0x25, 0x97, 0xbb, 0xe3, 0x59, 0x50, 0x7f, 0x26, + 0x60, 0xe5, 0xb7, 0xf3, 0xfd, 0xc9, 0xdc, 0x11, 0x65, 0xfb, 0xbf, 0x4a, + 0x1a, 0x59, 0x7f, 0x38, 0x99, 0xbe, 0x38, 0xb2, 0xb0, 0xf6, 0x40, 0x55, + 0x7f, 0xe3, 0xfb, 0x85, 0x83, 0xf3, 0xb4, 0xb2, 0xff, 0xfb, 0x36, 0xcf, + 0xbb, 0xc3, 0x28, 0xfd, 0xd8, 0xb2, 0x85, 0x44, 0x7f, 0x0f, 0xef, 0xfd, + 0x9e, 0x8d, 0x63, 0x76, 0x9a, 0x69, 0x25, 0xc6, 0xd2, 0xcb, 0xb6, 0x6f, + 0x0a, 0xfc, 0xe4, 0x44, 0x32, 0x3c, 0x3c, 0x09, 0x47, 0xca, 0xc6, 0x75, + 0xe8, 0x6a, 0xfe, 0x58, 0x50, 0x8b, 0xec, 0x2c, 0x27, 0xc9, 0x37, 0x10, + 0xef, 0xda, 0x0b, 0xb8, 0x55, 0x15, 0xe2, 0xff, 0xfe, 0xc1, 0xfa, 0x04, + 0x6f, 0xd2, 0x7f, 0xf9, 0xc8, 0xfd, 0x25, 0x9b, 0xe2, 0x24, 0xe7, 0xcd, + 0x2f, 0xfe, 0x6e, 0xf2, 0x6f, 0x9a, 0x0b, 0xb8, 0x55, 0x12, 0x3a, 0xec, + 0x62, 0xcb, 0xbb, 0x8b, 0x28, 0x26, 0xb3, 0xe1, 0x6b, 0xc7, 0xfc, 0x2c, + 0xbc, 0x1c, 0x62, 0x46, 0xe5, 0xe5, 0xf6, 0x3f, 0xdf, 0xac, 0xbe, 0x0b, + 0xb8, 0x55, 0x12, 0x42, 0xb6, 0x3d, 0x1d, 0x11, 0xdf, 0xff, 0x8f, 0x47, + 0xb9, 0x05, 0xb4, 0x74, 0xfc, 0xfb, 0x2c, 0xbf, 0x3c, 0xba, 0x7b, 0x2c, + 0xa8, 0x3f, 0xf3, 0x57, 0xbf, 0xfc, 0xfd, 0x9b, 0x98, 0x31, 0x81, 0xf5, + 0x25, 0x97, 0xff, 0x83, 0xe3, 0xfb, 0xb9, 0xf7, 0x75, 0xa8, 0x59, 0x7f, + 0xf8, 0xb3, 0xf9, 0xfc, 0xd4, 0x6c, 0xfa, 0x0a, 0xcb, 0xa4, 0xde, 0x15, + 0x12, 0x63, 0xf7, 0xa1, 0x45, 0xf9, 0x07, 0x12, 0xfa, 0x99, 0x7e, 0x07, + 0xc1, 0x3e, 0x2c, 0xbf, 0x67, 0x9b, 0xe3, 0x16, 0x59, 0xbc, 0x2b, 0x1f, + 0x79, 0x4c, 0x5f, 0xb5, 0xf4, 0xa6, 0xff, 0xe6, 0xef, 0x26, 0xf9, 0xa0, + 0xbb, 0x85, 0x51, 0x25, 0x2f, 0xda, 0x0b, 0xb8, 0x55, 0x17, 0x8a, 0xff, + 0xa4, 0xdf, 0x34, 0x17, 0x70, 0xaa, 0x24, 0xd5, 0x9b, 0xe1, 0xfd, 0xb9, + 0xa5, 0xf0, 0x7c, 0x64, 0xb2, 0xfd, 0xce, 0x61, 0x62, 0xcb, 0xfe, 0xd0, + 0x63, 0x6c, 0xc2, 0xea, 0xcb, 0xfa, 0x38, 0x6c, 0x89, 0x96, 0x5f, 0x39, + 0xfd, 0xc5, 0x96, 0xf2, 0xca, 0xc4, 0x5f, 0x19, 0x33, 0x0e, 0x3c, 0x5a, + 0xd1, 0x15, 0xfe, 0x3f, 0xf0, 0xa3, 0xee, 0x2c, 0xbd, 0xbe, 0x24, 0xb2, + 0xfd, 0x1d, 0xe6, 0x75, 0x65, 0xe7, 0x21, 0xe1, 0xe2, 0xf8, 0x7a, 0xff, + 0x9f, 0x52, 0xe6, 0x03, 0x5b, 0x2c, 0xa3, 0x3e, 0xb0, 0x18, 0xdf, 0xf6, + 0x7e, 0x3f, 0x41, 0x43, 0x4b, 0x2f, 0xbf, 0xe6, 0x12, 0xca, 0x83, 0xdb, + 0x33, 0x9b, 0xff, 0x00, 0x3c, 0xe3, 0xeb, 0xb0, 0x2a, 0xcb, 0xf7, 0xbb, + 0xcc, 0xea, 0xca, 0x33, 0xe8, 0x74, 0x0b, 0xff, 0xf7, 0x9c, 0xff, 0xe6, + 0x0a, 0xec, 0xd4, 0x61, 0x2c, 0xb8, 0xc6, 0xb2, 0xa0, 0xfa, 0xbc, 0xa9, + 0x7d, 0x20, 0x47, 0xcb, 0x2f, 0xfb, 0x63, 0xe7, 0x1f, 0xff, 0x01, 0x65, + 0xfc, 0x7a, 0x19, 0x47, 0xcb, 0x2f, 0xff, 0x80, 0xff, 0x66, 0x0a, 0x2b, + 0xcb, 0x3e, 0xea, 0xcb, 0xfb, 0xd9, 0x9d, 0xf6, 0x2c, 0xbf, 0xe8, 0x97, + 0x35, 0xa7, 0x2e, 0xac, 0xa9, 0x2a, 0x74, 0x1c, 0x22, 0x03, 0x08, 0xa9, + 0x88, 0x74, 0x47, 0xe3, 0xbe, 0x16, 0xf5, 0x4b, 0x79, 0x65, 0xfd, 0x9d, + 0xec, 0x16, 0xcb, 0x2f, 0xf9, 0xc7, 0xbf, 0x06, 0xda, 0xa5, 0xba, 0xb2, + 0xbc, 0x7e, 0x20, 0x2d, 0xbc, 0xe4, 0x15, 0x97, 0xbd, 0x02, 0x2c, 0xae, + 0x9b, 0x86, 0x86, 0xef, 0xe2, 0x7f, 0xc9, 0xd8, 0xb2, 0xfe, 0x81, 0xfb, + 0x74, 0xc9, 0x65, 0xff, 0x43, 0x3d, 0x83, 0x19, 0xfc, 0xb2, 0xb0, 0xfa, + 0x0c, 0xbe, 0xfb, 0x70, 0xe0, 0x55, 0x97, 0xa5, 0xf0, 0xab, 0x2f, 0xe6, + 0x77, 0xc0, 0xef, 0x16, 0x5e, 0x00, 0xfe, 0x59, 0x7f, 0xf3, 0xfe, 0x1f, + 0x1e, 0x88, 0xfb, 0xc5, 0x97, 0xfb, 0x7e, 0x4b, 0xd0, 0x42, 0xac, 0xb8, + 0xb6, 0x59, 0x50, 0x99, 0x08, 0xc9, 0x70, 0x7c, 0xcb, 0xdc, 0x7b, 0xf4, + 0x41, 0x0d, 0x6d, 0xd5, 0x97, 0x6b, 0x16, 0x5e, 0xfc, 0x4d, 0x96, 0x56, + 0x8f, 0x1f, 0xf1, 0x12, 0x16, 0xbb, 0x18, 0xb2, 0xf6, 0xb7, 0xf9, 0x65, + 0xfb, 0x34, 0xf0, 0x35, 0x95, 0x07, 0xb6, 0xe2, 0xc4, 0x3f, 0x7f, 0xed, + 0x01, 0x9e, 0x38, 0x63, 0x92, 0xcb, 0xfe, 0xe4, 0x30, 0xb2, 0x50, 0x4b, + 0x2b, 0x47, 0xe7, 0xd3, 0xdb, 0x8a, 0x4b, 0x2e, 0x9e, 0xda, 0x59, 0x40, + 0x36, 0x73, 0xe2, 0xd5, 0x87, 0xf3, 0xb8, 0xab, 0x7f, 0xf0, 0x40, 0xe3, + 0xdf, 0x83, 0x6d, 0x52, 0xdd, 0x59, 0x7c, 0x7e, 0x8d, 0xeb, 0x2b, 0x47, + 0xe7, 0xd5, 0x0b, 0xfd, 0x1a, 0x8f, 0xbf, 0xd4, 0x2c, 0xb8, 0xb6, 0x59, + 0x7d, 0x1e, 0x06, 0xea, 0xcb, 0xdd, 0x8d, 0x2c, 0xa8, 0x45, 0x76, 0xc4, + 0x53, 0x1a, 0x38, 0xb8, 0x09, 0x6e, 0x70, 0xac, 0xbf, 0x82, 0x30, 0x6c, + 0x23, 0x16, 0x5f, 0xdf, 0x73, 0x18, 0xfc, 0x59, 0x7c, 0x3c, 0xf4, 0x2c, + 0xbf, 0xb3, 0x6d, 0x47, 0xda, 0x59, 0x7f, 0x80, 0x3c, 0x94, 0x7f, 0xc5, + 0x95, 0x08, 0xf9, 0xc1, 0x63, 0x31, 0x72, 0xe2, 0x21, 0xe1, 0x7d, 0xfa, + 0x6e, 0x70, 0xd8, 0xb2, 0xf7, 0x01, 0xf2, 0xcb, 0xff, 0xed, 0x9a, 0x79, + 0x73, 0x99, 0xa6, 0xa3, 0x42, 0xac, 0xb1, 0x2c, 0xbf, 0x9f, 0xbc, 0xdb, + 0x1a, 0x59, 0x7f, 0xff, 0x1f, 0x1e, 0x5c, 0x3d, 0xfc, 0x03, 0x9c, 0x8f, + 0x7a, 0xcb, 0x84, 0x11, 0x65, 0x62, 0x6a, 0x13, 0x14, 0xb8, 0xf7, 0xea, + 0x84, 0x21, 0xd2, 0xf1, 0x17, 0x6e, 0x9f, 0x6e, 0xd8, 0x67, 0x92, 0xc4, + 0x37, 0xe4, 0x92, 0x38, 0x71, 0xe4, 0xaf, 0x00, 0xc3, 0xfb, 0xe5, 0xa9, + 0x88, 0xb5, 0x09, 0x4f, 0x46, 0x82, 0xf0, 0xaa, 0xfe, 0x11, 0x45, 0x18, + 0xc7, 0x23, 0x45, 0xec, 0x68, 0x62, 0x47, 0x45, 0x7f, 0x81, 0xb3, 0x7d, + 0xcd, 0xc3, 0x15, 0x65, 0x43, 0xbf, 0x78, 0xda, 0x15, 0x32, 0xb4, 0x2a, + 0x83, 0x8f, 0xf8, 0xe7, 0x1b, 0x26, 0x8d, 0xa5, 0x85, 0xbe, 0x97, 0x3f, + 0xd4, 0xd0, 0x43, 0x3c, 0x4a, 0x7a, 0x6e, 0xe4, 0x66, 0xb4, 0xda, 0x94, + 0x87, 0x4c, 0xe8, 0xb4, 0xcd, 0x7b, 0x4e, 0x4c, 0x4a, 0x94, 0xd0, 0x3a, + 0x44, 0xde, 0x66, 0x6a, 0x8a, 0x2d, 0x29, 0x30, 0x38, 0x9c, 0x4f, 0xfa, + 0xd4, 0x56, 0x1d, 0x67, 0x71, 0xbb, 0x29, 0x42, 0x6b, 0xe4, 0x87, 0xd5, + 0xef, 0x42, 0xb3, 0x56, 0x7e, 0x0f, 0xab, 0x81, 0x87, 0xbf, 0x10, 0x5f, + 0xe9, 0xdf, 0x2d, 0xb9, 0x7d, 0xc5, 0x6b, 0x38, 0xf9, 0x6d, 0x12, 0x7b, + 0x8b, 0x54, 0x60, 0x5f, 0x18, 0x6e, 0xfa, 0xc3, 0x01, 0xaa, 0x65, 0xf4, + 0xfc, 0xf1, 0xa0, 0x94, 0xef, 0x3d, 0xcb, 0xc0, 0x51, 0xa0, }; -static const unsigned kPreloadedHSTSBits = 548791; +static const unsigned kPreloadedHSTSBits = 554859; -static const unsigned kHSTSRootPosition = 548123; +static const unsigned kHSTSRootPosition = 554190; #endif // NET_HTTP_TRANSPORT_SECURITY_STATE_STATIC_H_
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json index 043e753..ee914e7 100644 --- a/net/http/transport_security_state_static.json +++ b/net/http/transport_security_state_static.json
@@ -252,6 +252,7 @@ // Other Google-related domains that must use HTTPS. { "name": "apis.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "build.chromium.org", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "bugs.chromium.org", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "chrome.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "chrome-devtools-frontend.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "chromiumcodereview.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, @@ -7922,7 +7923,97 @@ { "name": "yagihiro.tech", "include_subdomains": true, "mode": "force-https" }, { "name": "zenithmedia.ca", "include_subdomains": true, "mode": "force-https" }, { "name": "zhendingresources.com", "include_subdomains": true, "mode": "force-https" }, - { "name": "zmy.im", "include_subdomains": true, "mode": "force-https" } + { "name": "zmy.im", "include_subdomains": true, "mode": "force-https" }, + { "name": "3s-hosting.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "7kovrikov.ru", "include_subdomains": true, "mode": "force-https" }, + { "name": "adderall.space", "include_subdomains": true, "mode": "force-https" }, + { "name": "agwa.name", "include_subdomains": true, "mode": "force-https" }, + { "name": "alanlee.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "alpha-force.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "anoncom.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "antocom.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "atletika.hu", "include_subdomains": true, "mode": "force-https" }, + { "name": "auto-anleitung.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "bakaweb.fr", "include_subdomains": true, "mode": "force-https" }, + { "name": "bdikaros-network.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "breitbild-beamer.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "bullbits.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "cat-box.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "cgtx.us", "include_subdomains": true, "mode": "force-https" }, + { "name": "chriskyrouac.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "chuckame.fr", "include_subdomains": true, "mode": "force-https" }, + { "name": "clawe.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "clickforclever.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "clickphish.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "cmacacias.ch", "include_subdomains": true, "mode": "force-https" }, + { "name": "cmlancy.ch", "include_subdomains": true, "mode": "force-https" }, + { "name": "comodo.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "crimson.no", "include_subdomains": true, "mode": "force-https" }, + { "name": "curveweb.co.uk", "include_subdomains": true, "mode": "force-https" }, + { "name": "debank.tv", "include_subdomains": true, "mode": "force-https" }, + { "name": "dentistglasgow.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "denverprophit.us", "include_subdomains": true, "mode": "force-https" }, + { "name": "digitalehandtekeningen.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "docid.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "dotbigbang.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "edesseglabor.hu", "include_subdomains": true, "mode": "force-https" }, + { "name": "edp-collaborative.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "emilong.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "ensured.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "ensured.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "geekwu.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "glasschmuck-millefiori.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "goge.site", "include_subdomains": true, "mode": "force-https" }, + { "name": "graavaapi.elasticbeanstalk.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "gyz.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "gz-architekten.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "httpsecurityreport.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "ibron.co", "include_subdomains": true, "mode": "force-https" }, + { "name": "icewoman.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "ifxor.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "innovaptor.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "interlun.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "jamesmilazzo.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "jmk.hu", "include_subdomains": true, "mode": "force-https" }, + { "name": "jobmob.co.il", "include_subdomains": true, "mode": "force-https" }, + { "name": "joretapo.fr", "include_subdomains": true, "mode": "force-https" }, + { "name": "kamcvicit.sk", "include_subdomains": true, "mode": "force-https" }, + { "name": "kintore.tv", "include_subdomains": true, "mode": "force-https" }, + { "name": "lakhesis.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "langguth.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "latitude42technology.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "linuxmonitoring.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "mediawikicn.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "mikeg.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "nalao-company.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "nodi.at", "include_subdomains": true, "mode": "force-https" }, + { "name": "number.me", "include_subdomains": true, "mode": "force-https" }, + { "name": "pekoe.se", "include_subdomains": true, "mode": "force-https" }, + { "name": "php-tuning.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "prytkov.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "qapital.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "s13d.fr", "include_subdomains": true, "mode": "force-https" }, + { "name": "saorsat.ie", "include_subdomains": true, "mode": "force-https" }, + { "name": "sazuz.cz", "include_subdomains": true, "mode": "force-https" }, + { "name": "scotbirchfield.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "sectia22.ro", "include_subdomains": true, "mode": "force-https" }, + { "name": "shiftplanning.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "shinju.moe", "include_subdomains": true, "mode": "force-https" }, + { "name": "ssbrm.ch", "include_subdomains": true, "mode": "force-https" }, + { "name": "sslcertificaten.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "sslcheck.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "strijkshop.be", "include_subdomains": true, "mode": "force-https" }, + { "name": "synackr.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "szagun.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "techassist.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "tobiasofficial.at", "include_subdomains": true, "mode": "force-https" }, + { "name": "traffixdevices.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "trusteecar.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "vjirovsky.cz", "include_subdomains": true, "mode": "force-https" }, + { "name": "windscribe.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "wlaws.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "xolphin.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "zellari.ru", "include_subdomains": true, "mode": "force-https" } ], // |ReportUMAOnPinFailure| uses these to report which domain was associated
diff --git a/net/net.gyp b/net/net.gyp index a85822f..06b080a 100644 --- a/net/net.gyp +++ b/net/net.gyp
@@ -529,6 +529,8 @@ 'base/test_data_directory.h', 'cert/mock_cert_verifier.cc', 'cert/mock_cert_verifier.h', + 'cert/mock_client_cert_verifier.cc', + 'cert/mock_client_cert_verifier.h', 'cookies/cookie_monster_store_test.cc', 'cookies/cookie_monster_store_test.h', 'cookies/cookie_store_test_callbacks.cc', @@ -1645,6 +1647,7 @@ 'android/junit/', ], 'test_type': 'junit', + 'wrapper_script_name': 'helper/<(_target_name)', }, 'includes': [ '../build/android/test_runner.gypi',
diff --git a/net/net.gypi b/net/net.gypi index de1c4bf..2f7fd9e 100644 --- a/net/net.gypi +++ b/net/net.gypi
@@ -81,6 +81,7 @@ 'cert/cert_verifier.h', 'cert/cert_verify_result.cc', 'cert/cert_verify_result.h', + 'cert/client_cert_verifier.h', 'cert/crl_set.cc', 'cert/crl_set.h', 'cert/ct_known_logs.cc', @@ -88,6 +89,7 @@ 'cert/ct_known_logs_static.h', 'cert/ct_policy_enforcer.cc', 'cert/ct_policy_enforcer.h', + 'cert/ct_policy_status.h', 'cert/ct_verifier.h', 'cert/ct_verify_result.cc', 'cert/ct_verify_result.h', @@ -99,6 +101,8 @@ 'cert/internal/name_constraints.h', 'cert/internal/parse_certificate.cc', 'cert/internal/parse_certificate.h', + 'cert/internal/parse_name.cc', + 'cert/internal/parse_name.h', 'cert/internal/signature_algorithm.cc', 'cert/internal/signature_algorithm.h', 'cert/internal/signature_policy.cc', @@ -1367,6 +1371,7 @@ 'cert/internal/name_constraints_unittest.cc', 'cert/internal/nist_pkits_unittest.h', 'cert/internal/parse_certificate_unittest.cc', + 'cert/internal/parse_name_unittest.cc', 'cert/internal/signature_algorithm_unittest.cc', 'cert/internal/test_helpers.cc', 'cert/internal/test_helpers.h',
diff --git a/net/quic/congestion_control/pacing_sender.cc b/net/quic/congestion_control/pacing_sender.cc index 2c17a17..9789da1 100644 --- a/net/quic/congestion_control/pacing_sender.cc +++ b/net/quic/congestion_control/pacing_sender.cc
@@ -79,7 +79,7 @@ } // The next packet should be sent as soon as the current packets has been // transferred. - QuicTime::Delta delay = PacingRate().TransferTime(bytes); + QuicTime::Delta delay = sender_->PacingRate().TransferTime(bytes); // If the last send was delayed, and the alarm took a long time to get // invoked, allow the connection to make up for lost time. if (was_last_send_delayed_) {
diff --git a/net/quic/crypto/crypto_secret_boxer.cc b/net/quic/crypto/crypto_secret_boxer.cc index 1fbf0709..08a4317 100644 --- a/net/quic/crypto/crypto_secret_boxer.cc +++ b/net/quic/crypto/crypto_secret_boxer.cc
@@ -15,6 +15,7 @@ using base::StringPiece; using std::string; +using std::vector; namespace net { @@ -34,21 +35,34 @@ // It's not terrible, but it's not a "forget about it" margin. static const size_t kBoxNonceSize = 12; +CryptoSecretBoxer::CryptoSecretBoxer() {} + +CryptoSecretBoxer::~CryptoSecretBoxer() {} + // static size_t CryptoSecretBoxer::GetKeySize() { return kKeySize; } -void CryptoSecretBoxer::SetKey(StringPiece key) { - DCHECK_EQ(kKeySize, key.size()); - key_ = key.as_string(); +void CryptoSecretBoxer::SetKeys(const vector<string>& keys) { + DCHECK(!keys.empty()); + vector<string> copy = keys; + for (const string& key : keys) { + DCHECK_EQ(kKeySize, key.size()); + } + base::AutoLock l(lock_); + keys_.swap(copy); } string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const { scoped_ptr<Aes128Gcm12Encrypter> encrypter(new Aes128Gcm12Encrypter()); - if (!encrypter->SetKey(key_)) { - DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed."; - return string(); + { + base::AutoLock l(lock_); + DCHECK_EQ(kKeySize, keys_[0].size()); + if (!encrypter->SetKey(keys_[0])) { + DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed."; + return string(); + } } size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length()); @@ -86,17 +100,25 @@ sizeof(packet_number)); scoped_ptr<Aes128Gcm12Decrypter> decrypter(new Aes128Gcm12Decrypter()); - if (!decrypter->SetKey(key_)) { - DLOG(DFATAL) << "CryptoSecretBoxer's decrypter->SetKey failed."; - return false; - } - decrypter->SetNoncePrefix(nonce_prefix); char plaintext[kMaxPacketSize]; size_t plaintext_length = 0; - const bool success = decrypter->DecryptPacket( - /*path_id=*/0u, packet_number, /*associated data=*/StringPiece(), - ciphertext, plaintext, &plaintext_length, kMaxPacketSize); - if (!success) { + bool ok = false; + { + base::AutoLock l(lock_); + for (const string& key : keys_) { + if (decrypter->SetKey(key)) { + decrypter->SetNoncePrefix(nonce_prefix); + if (decrypter->DecryptPacket( + /*path_id=*/0u, packet_number, + /*associated data=*/StringPiece(), ciphertext, plaintext, + &plaintext_length, kMaxPacketSize)) { + ok = true; + break; + } + } + } + } + if (!ok) { return false; }
diff --git a/net/quic/crypto/crypto_secret_boxer.h b/net/quic/crypto/crypto_secret_boxer.h index 266b34a..09ec23c 100644 --- a/net/quic/crypto/crypto_secret_boxer.h +++ b/net/quic/crypto/crypto_secret_boxer.h
@@ -8,9 +8,11 @@ #include <stddef.h> #include <string> +#include <vector> #include "base/macros.h" #include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" #include "net/base/net_export.h" namespace net { @@ -22,31 +24,37 @@ // thread-safe. class NET_EXPORT_PRIVATE CryptoSecretBoxer { public: - CryptoSecretBoxer() {} + CryptoSecretBoxer(); + ~CryptoSecretBoxer(); // GetKeySize returns the number of bytes in a key. static size_t GetKeySize(); - // SetKey sets the key for this object. This must be done before |Box| or - // |Unbox| are called. |key| must be |GetKeySize()| bytes long. - void SetKey(base::StringPiece key); + // SetKeys sets a std::list of encryption keys. The first key in the std::list + // will be used by |Box|, but all supplied keys will be tried by |Unbox|, to + // handle key skew across the fleet. This must be called before |Box| or + // |Unbox|. Keys must be |GetKeySize()| bytes long. + void SetKeys(const std::vector<std::string>& keys); // Box encrypts |plaintext| using a random nonce generated from |rand| and // returns the resulting ciphertext. Since an authenticator and nonce are - // included, the result will be slightly larger than |plaintext|. + // included, the result will be slightly larger than |plaintext|. The first + // key in the std::vector supplied to |SetKeys| will be used. std::string Box(QuicRandom* rand, base::StringPiece plaintext) const; // Unbox takes the result of a previous call to |Box| in |ciphertext| and - // authenticates+decrypts it. If |ciphertext| is not authentic then it - // returns false. Otherwise, |out_storage| is used to store the result and - // |out| is set to point into |out_storage| and contains the original - // plaintext. + // authenticates+decrypts it. If |ciphertext| cannot be decrypted with any of + // the supplied keys, the function returns false. Otherwise, |out_storage| is + // used to store the result and |out| is set to point into |out_storage| and + // contains the original plaintext. bool Unbox(base::StringPiece ciphertext, std::string* out_storage, base::StringPiece* out) const; private: - std::string key_; + mutable base::Lock lock_; + // GUARDED_BY(lock_). + std::vector<std::string> keys_; DISALLOW_COPY_AND_ASSIGN(CryptoSecretBoxer); };
diff --git a/net/quic/crypto/crypto_secret_boxer_test.cc b/net/quic/crypto/crypto_secret_boxer_test.cc index 185e0a70..c73c581c 100644 --- a/net/quic/crypto/crypto_secret_boxer_test.cc +++ b/net/quic/crypto/crypto_secret_boxer_test.cc
@@ -16,12 +16,9 @@ TEST(CryptoSecretBoxerTest, BoxAndUnbox) { StringPiece message("hello world"); - const size_t key_size = CryptoSecretBoxer::GetKeySize(); - scoped_ptr<uint8_t[]> key(new uint8_t[key_size]); - memset(key.get(), 0x11, key_size); CryptoSecretBoxer boxer; - boxer.SetKey(StringPiece(reinterpret_cast<char*>(key.get()), key_size)); + boxer.SetKeys({string(CryptoSecretBoxer::GetKeySize(), 0x11)}); const string box = boxer.Box(QuicRandom::GetInstance(), message); @@ -38,5 +35,46 @@ &storage, &result)); } +// Helper function to test whether one boxer can decode the output of another. +static bool CanDecode(const CryptoSecretBoxer& decoder, + const CryptoSecretBoxer& encoder) { + StringPiece message("hello world"); + const string boxed = encoder.Box(QuicRandom::GetInstance(), message); + string storage; + StringPiece result; + bool ok = decoder.Unbox(boxed, &storage, &result); + if (ok) { + EXPECT_EQ(result, message); + } + return ok; +} + +TEST(CryptoSecretBoxerTest, MultipleKeys) { + string key_11(CryptoSecretBoxer::GetKeySize(), 0x11); + string key_12(CryptoSecretBoxer::GetKeySize(), 0x12); + + CryptoSecretBoxer boxer_11, boxer_12, boxer; + boxer_11.SetKeys({key_11}); + boxer_12.SetKeys({key_12}); + boxer.SetKeys({key_12, key_11}); + + // Neither single-key boxer can decode the other's tokens. + EXPECT_FALSE(CanDecode(boxer_11, boxer_12)); + EXPECT_FALSE(CanDecode(boxer_12, boxer_11)); + + // |boxer| encodes with the first key, which is key_12. + EXPECT_TRUE(CanDecode(boxer_12, boxer)); + EXPECT_FALSE(CanDecode(boxer_11, boxer)); + + // The boxer with both keys can decode tokens from either single-key boxer. + EXPECT_TRUE(CanDecode(boxer, boxer_11)); + EXPECT_TRUE(CanDecode(boxer, boxer_12)); + + // After we flush key_11 from |boxer|, it can no longer decode tokens from + // |boxer_11|. + boxer.SetKeys({key_12}); + EXPECT_FALSE(CanDecode(boxer, boxer_11)); +} + } // namespace test } // namespace net
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc index e9191a7..9e3b2ec 100644 --- a/net/quic/crypto/proof_verifier_chromium.cc +++ b/net/quic/crypto/proof_verifier_chromium.cc
@@ -23,6 +23,7 @@ #include "net/cert/cert_verifier.h" #include "net/cert/cert_verify_result.h" #include "net/cert/ct_policy_enforcer.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/ct_verifier.h" #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" @@ -284,12 +285,25 @@ const CertVerifyResult& cert_verify_result = verify_details_->cert_verify_result; const CertStatus cert_status = cert_verify_result.cert_status; + verify_details_->ct_verify_result.ct_policies_applied = + (result == OK && policy_enforcer_ != nullptr); + verify_details_->ct_verify_result.ev_policy_compliance = + ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; if (result == OK && policy_enforcer_ && (cert_verify_result.cert_status & CERT_STATUS_IS_EV)) { - if (!policy_enforcer_->DoesConformToCTEVPolicy( + ct::EVPolicyCompliance ev_policy_compliance = + policy_enforcer_->DoesConformToCTEVPolicy( cert_verify_result.verified_cert.get(), SSLConfigService::GetEVCertsWhitelist().get(), - verify_details_->ct_verify_result, net_log_)) { + verify_details_->ct_verify_result.verified_scts, net_log_); + verify_details_->ct_verify_result.ev_policy_compliance = + ev_policy_compliance; + if (ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY && + ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST && + ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS) { verify_details_->cert_verify_result.cert_status |= CERT_STATUS_CT_COMPLIANCE_FAILED; verify_details_->cert_verify_result.cert_status &= ~CERT_STATUS_IS_EV;
diff --git a/net/quic/crypto/proof_verifier_chromium_test.cc b/net/quic/crypto/proof_verifier_chromium_test.cc index ddd4c94b..19ccdef 100644 --- a/net/quic/crypto/proof_verifier_chromium_test.cc +++ b/net/quic/crypto/proof_verifier_chromium_test.cc
@@ -12,6 +12,7 @@ #include "net/cert/cert_verifier.h" #include "net/cert/ct_log_verifier.h" #include "net/cert/ct_policy_enforcer.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/ct_serialization.h" #include "net/cert/ct_verify_result.h" #include "net/cert/mock_cert_verifier.h" @@ -56,13 +57,14 @@ FailsTestCTPolicyEnforcer() {} ~FailsTestCTPolicyEnforcer() override {} - bool DoesConformToCTEVPolicy(X509Certificate* cert, - const ct::EVCertsWhitelist* ev_whitelist, - const ct::CTVerifyResult& ct_result, - const BoundNetLog& net_log) override { + ct::EVPolicyCompliance DoesConformToCTEVPolicy( + X509Certificate* cert, + const ct::EVCertsWhitelist* ev_whitelist, + const ct::SCTList& verified_scts, + const BoundNetLog& net_log) override { ADD_FAILURE() << "CTPolicyEnforcer::DoesConformToCTEVPolicy() should " << "not be called"; - return false; + return ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; } }; @@ -73,11 +75,13 @@ MockCTPolicyEnforcer(bool is_ev) : is_ev_(is_ev) {} ~MockCTPolicyEnforcer() override {} - bool DoesConformToCTEVPolicy(X509Certificate* cert, - const ct::EVCertsWhitelist* ev_whitelist, - const ct::CTVerifyResult& ct_result, - const BoundNetLog& net_log) override { - return is_ev_; + ct::EVPolicyCompliance DoesConformToCTEVPolicy( + X509Certificate* cert, + const ct::EVCertsWhitelist* ev_whitelist, + const ct::SCTList& verified_scts, + const BoundNetLog& net_log) override { + return is_ev_ ? ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS + : ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS; } private:
diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc index db545f9..a96943f 100644 --- a/net/quic/crypto/quic_crypto_server_config.cc +++ b/net/quic/crypto/quic_crypto_server_config.cc
@@ -225,8 +225,8 @@ server_nonce_strike_register_window_secs_(120), enable_serving_sct_(false) { DCHECK(proof_source_.get()); - default_source_address_token_boxer_.SetKey( - DeriveSourceAddressTokenKey(source_address_token_secret)); + default_source_address_token_boxer_.SetKeys( + {DeriveSourceAddressTokenKey(source_address_token_secret)}); // Generate a random key and orbit for server nonces. server_nonce_entropy->RandBytes(server_nonce_orbit_, @@ -235,8 +235,8 @@ scoped_ptr<uint8_t[]> key_bytes(new uint8_t[key_size]); server_nonce_entropy->RandBytes(key_bytes.get(), key_size); - server_nonce_boxer_.SetKey( - StringPiece(reinterpret_cast<char*>(key_bytes.get()), key_size)); + server_nonce_boxer_.SetKeys( + {string(reinterpret_cast<char*>(key_bytes.get()), key_size)}); } QuicCryptoServerConfig::~QuicCryptoServerConfig() { @@ -480,6 +480,11 @@ return ok; } +void QuicCryptoServerConfig::SetDefaultSourceAddressTokenKeys( + const vector<string>& keys) { + default_source_address_token_boxer_.SetKeys(keys); +} + void QuicCryptoServerConfig::GetConfigIds(vector<string>* scids) const { base::AutoLock locked(configs_lock_); for (ConfigMap::const_iterator it = configs_.begin(); it != configs_.end(); @@ -1335,8 +1340,8 @@ } else { // Create override boxer instance. CryptoSecretBoxer* boxer = new CryptoSecretBoxer; - boxer->SetKey(DeriveSourceAddressTokenKey( - protobuf->source_address_token_secret_override())); + boxer->SetKeys({DeriveSourceAddressTokenKey( + protobuf->source_address_token_secret_override())}); config->source_address_token_boxer_storage.reset(boxer); config->source_address_token_boxer = boxer; }
diff --git a/net/quic/crypto/quic_crypto_server_config.h b/net/quic/crypto/quic_crypto_server_config.h index 0dabc29..617779c 100644 --- a/net/quic/crypto/quic_crypto_server_config.h +++ b/net/quic/crypto/quic_crypto_server_config.h
@@ -194,6 +194,14 @@ bool SetConfigs(const std::vector<QuicServerConfigProtobuf*>& protobufs, QuicWallTime now); + // SetDefaultSourceAddressTokenKeys sets the keys to be tried, in order, + // when decrypting a source address token. This modifies only the default + // boxer, which is to say, it is a no-op if a key was specified in the Config. + // Note that these keys are used *without* passing them through a KDF, in + // contradistinction to the |source_address_token_secret| argument to the + // constructor. + void SetDefaultSourceAddressTokenKeys(const std::vector<std::string>& keys); + // Get the server config ids for all known configs. void GetConfigIds(std::vector<std::string>* scids) const;
diff --git a/net/quic/crypto/quic_crypto_server_config_test.cc b/net/quic/crypto/quic_crypto_server_config_test.cc index b07f46d..f3f9de7 100644 --- a/net/quic/crypto/quic_crypto_server_config_test.cc +++ b/net/quic/crypto/quic_crypto_server_config_test.cc
@@ -462,7 +462,7 @@ memset(key.get(), 0x11, key_size); CryptoSecretBoxer boxer; - boxer.SetKey(StringPiece(reinterpret_cast<char*>(key.get()), key_size)); + boxer.SetKeys({string(reinterpret_cast<char*>(key.get()), key_size)}); const string box = boxer.Box(rand, message); MockClock clock; QuicWallTime now = clock.WallNow();
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc index d68f3bb4..78044e8 100644 --- a/net/quic/quic_chromium_client_session.cc +++ b/net/quic/quic_chromium_client_session.cc
@@ -210,6 +210,7 @@ cert_verify_flags, net_log_)), crypto_config)); connection->set_debug_visitor(logger_.get()); + connection->set_creator_debug_delegate(logger_.get()); net_log_.BeginEvent(NetLog::TYPE_QUIC_SESSION, base::Bind(NetLogQuicClientSessionCallback, &server_id, cert_verify_flags, require_confirmation_)); @@ -510,7 +511,7 @@ ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL; ssl_info->pinning_failure_log = pinning_failure_log_; - ssl_info->UpdateSignedCertificateTimestamps(*ct_verify_result_); + ssl_info->UpdateCertificateTransparencyInfo(*ct_verify_result_); return true; }
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc index e50708b0..bb1a57a 100644 --- a/net/quic/quic_chromium_client_session_test.cc +++ b/net/quic/quic_chromium_client_session_test.cc
@@ -64,7 +64,10 @@ new SequencedSocketData(default_read_.get(), 1, nullptr, 0)), random_(0), helper_(base::ThreadTaskRunnerHandle::Get().get(), &clock_, &random_), - maker_(GetParam(), 0, &clock_, kServerHostname) {} + maker_(GetParam(), 0, &clock_, kServerHostname) { + // Advance the time, because timers do not like uninitialized times. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + } void Initialize() { socket_factory_.AddSocketDataProvider(socket_data_.get()); @@ -95,8 +98,6 @@ ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem")); verify_details_.cert_verify_result.verified_cert = cert; verify_details_.cert_verify_result.is_issued_by_known_root = true; - // Advance the time, because timers do not like uninitialized times. - clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); session_->Initialize(); session_->StartReading(); }
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index e796745e..c7aa954 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc
@@ -1511,27 +1511,7 @@ // does not require the creator to be flushed. packet_generator_.FlushAllQueuedFrames(); char buffer[kMaxPacketSize]; - SerializedPacket serialized_packet = - packet_generator_.ReserializeAllFrames(pending, buffer, kMaxPacketSize); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - DCHECK(serialized_packet.encrypted_buffer == nullptr); - continue; - } - if (serialized_packet.encrypted_buffer == nullptr) { - // We failed to serialize the packet, so close the connection. - // CloseConnection does not send close packet, so no infinite loop here. - // TODO(ianswett): This is actually an internal error, not an encryption - // failure. - CloseConnection(QUIC_ENCRYPTION_FAILURE, - ConnectionCloseSource::FROM_SELF); - return; - } - - DVLOG(1) << ENDPOINT << "Retransmitting " << pending.packet_number << " as " - << serialized_packet.packet_number; - serialized_packet.original_packet_number = pending.packet_number; - serialized_packet.transmission_type = pending.transmission_type; - SendOrQueuePacket(&serialized_packet); + packet_generator_.ReserializeAllFrames(pending, buffer, kMaxPacketSize); } }
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index 0fdbf63..875f7df 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h
@@ -161,8 +161,7 @@ // points. Implementations must not mutate the state of the connection // as a result of these callbacks. class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor - : public QuicPacketCreator::DebugDelegate, - public QuicSentPacketManager::DebugDelegate { + : public QuicSentPacketManager::DebugDelegate { public: ~QuicConnectionDebugVisitor() override {} @@ -490,9 +489,12 @@ } void set_debug_visitor(QuicConnectionDebugVisitor* debug_visitor) { debug_visitor_ = debug_visitor; - packet_generator_.set_debug_delegate(debug_visitor); sent_packet_manager_.set_debug_delegate(debug_visitor); } + // Used in Chromium, but not internally. + void set_creator_debug_delegate(QuicPacketCreator::DebugDelegate* visitor) { + packet_generator_.set_debug_delegate(visitor); + } const IPEndPoint& self_address() const { return self_address_; } const IPEndPoint& peer_address() const { return peer_address_; } QuicConnectionId connection_id() const { return connection_id_; }
diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h index 132a9ea..bdc5e6ae 100644 --- a/net/quic/quic_connection_logger.h +++ b/net/quic/quic_connection_logger.h
@@ -29,7 +29,8 @@ // This class is a debug visitor of a QuicConnection which logs // events to |net_log|. class NET_EXPORT_PRIVATE QuicConnectionLogger - : public QuicConnectionDebugVisitor { + : public QuicConnectionDebugVisitor, + public QuicPacketCreator::DebugDelegate { public: QuicConnectionLogger( QuicSpdySession* session, @@ -39,7 +40,7 @@ ~QuicConnectionLogger() override; - // QuicPacketGenerator::DebugDelegateInterface + // QuicPacketCreator::DebugDelegateInterface void OnFrameAddedToPacket(const QuicFrame& frame) override; // QuicConnectionDebugVisitorInterface
diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc index 8b1f05e..5818a62 100644 --- a/net/quic/quic_flags.cc +++ b/net/quic/quic_flags.cc
@@ -128,10 +128,6 @@ // strike register. bool FLAGS_require_strike_register_or_server_nonce = true; -// If true, use the shared writer directly for QuicTimeWaitListManagers instead -// of creating a wrapper object. -bool FLAGS_quic_time_wait_list_manager_use_shared_writer = true; - // When turn on, log packet loss into transport connection stats LossEvent. bool FLAGS_quic_log_loss_event = true; @@ -139,10 +135,6 @@ // QuicPacketCreator. bool FLAGS_use_stream_frame_freelist = true; -// Call QuicConnection::OnSerializedPacket instead of returning the -// serialized packet when frames are retransmitted. -bool FLAGS_quic_retransmit_via_onserializedpacket = true; - // If true, for QUIC authenticated encryption algorithms, last 8 bytes // of IV comprise packet path id and lower 7 bytes of packet number. bool FLAGS_quic_include_path_id_in_iv = true;
diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h index 352e841..71983cb 100644 --- a/net/quic/quic_flags.h +++ b/net/quic/quic_flags.h
@@ -41,11 +41,8 @@ NET_EXPORT_PRIVATE extern bool FLAGS_quic_drop_non_awaited_packets; NET_EXPORT_PRIVATE extern bool FLAGS_quic_utils_use_fast_incremental_hash; NET_EXPORT_PRIVATE extern bool FLAGS_require_strike_register_or_server_nonce; -NET_EXPORT_PRIVATE extern bool - FLAGS_quic_time_wait_list_manager_use_shared_writer; NET_EXPORT_PRIVATE extern bool FLAGS_quic_log_loss_event; NET_EXPORT_PRIVATE extern bool FLAGS_use_stream_frame_freelist; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_retransmit_via_onserializedpacket; NET_EXPORT_PRIVATE extern bool FLAGS_quic_include_path_id_in_iv; NET_EXPORT_PRIVATE extern bool FLAGS_quic_cede_correctly; NET_EXPORT_PRIVATE extern bool FLAGS_quic_crypto_proof_use_ref;
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index 68b90998..f4e0cf7 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc
@@ -404,7 +404,7 @@ QUIC_BUG_IF(length > 0) << "Failed to copy entire length to buffer."; } -SerializedPacket QuicPacketCreator::ReserializeAllFrames( +void QuicPacketCreator::ReserializeAllFrames( const PendingRetransmission& retransmission, char* buffer, size_t buffer_len) { @@ -416,7 +416,6 @@ const QuicPacketNumberLength saved_length = packet_.packet_number_length; const QuicPacketNumberLength saved_next_length = next_packet_number_length_; const bool saved_should_fec_protect = fec_protect_; - const bool saved_needs_padding = packet_.needs_padding; const EncryptionLevel default_encryption_level = packet_.encryption_level; // Temporarily set the packet number length, stop FEC protection, @@ -438,27 +437,14 @@ DCHECK(success); } SerializePacket(buffer, buffer_len); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - packet_.original_packet_number = retransmission.packet_number; - packet_.transmission_type = retransmission.transmission_type; - OnSerializedPacket(); - // Restore old values. - packet_.packet_number_length = saved_length; - next_packet_number_length_ = saved_next_length; - fec_protect_ = saved_should_fec_protect; - packet_.encryption_level = default_encryption_level; - return NoPacket(); - } else { - SerializedPacket packet_copy = packet_; - ClearPacket(); - // Restore old values. - packet_.needs_padding = saved_needs_padding; - packet_.packet_number_length = saved_length; - next_packet_number_length_ = saved_next_length; - fec_protect_ = saved_should_fec_protect; - packet_.encryption_level = default_encryption_level; - return packet_copy; - } + packet_.original_packet_number = retransmission.packet_number; + packet_.transmission_type = retransmission.transmission_type; + OnSerializedPacket(); + // Restore old values. + packet_.packet_number_length = saved_length; + next_packet_number_length_ = saved_next_length; + fec_protect_ = saved_should_fec_protect; + packet_.encryption_level = default_encryption_level; } void QuicPacketCreator::Flush() {
diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h index a6f3add..fcb4b50 100644 --- a/net/quic/quic_packet_creator.h +++ b/net/quic/quic_packet_creator.h
@@ -138,10 +138,9 @@ // Used for retransmitting packets to ensure they aren't too long. // Caller must ensure that any open FEC group is closed before calling this // method. - SerializedPacket ReserializeAllFrames( - const PendingRetransmission& retransmission, - char* buffer, - size_t buffer_len); + void ReserializeAllFrames(const PendingRetransmission& retransmission, + char* buffer, + size_t buffer_len); // Serializes all added frames into a single packet and invokes the delegate_ // to further process the SerializedPacket.
diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index d43193b..b5dddcf 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc
@@ -579,20 +579,15 @@ PendingRetransmission retransmission(CreateRetransmission( frames, true /* has_crypto_handshake */, true /* needs padding */, ENCRYPTION_NONE, PACKET_1BYTE_PACKET_NUMBER)); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); - } - SerializedPacket serialized = - creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - serialized = serialized_packet_; - } + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, QuicPacketCreatorPeer::NextPacketNumberLength(&creator_)); EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)); - EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, serialized.packet_number_length); + EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, + serialized_packet_.packet_number_length); { InSequence s; @@ -604,7 +599,7 @@ EXPECT_CALL(framer_visitor_, OnStreamFrame(_)); EXPECT_CALL(framer_visitor_, OnPacketComplete()); } - ProcessPacket(serialized); + ProcessPacket(serialized_packet_); delete stream_frame; } @@ -619,16 +614,10 @@ frames, true /* has_crypto_handshake */, true /* needs padding */, ENCRYPTION_NONE, QuicPacketCreatorPeer::NextPacketNumberLength(&creator_))); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); - } - SerializedPacket serialized = - creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - serialized = serialized_packet_; - } - EXPECT_EQ(ENCRYPTION_NONE, serialized.encryption_level); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + EXPECT_EQ(ENCRYPTION_NONE, serialized_packet_.encryption_level); delete stream_frame; } @@ -643,16 +632,10 @@ frames, false /* has_crypto_handshake */, false /* needs padding */, ENCRYPTION_NONE, QuicPacketCreatorPeer::NextPacketNumberLength(&creator_))); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); - } - SerializedPacket serialized = - creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - serialized = serialized_packet_; - } - EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, serialized.encryption_level); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, serialized_packet_.encryption_level); delete stream_frame; } @@ -668,16 +651,10 @@ frames, true /* has_crypto_handshake */, true /* needs padding */, ENCRYPTION_NONE, QuicPacketCreatorPeer::NextPacketNumberLength(&creator_))); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); - } - SerializedPacket serialized = - creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - serialized = serialized_packet_; - } - EXPECT_EQ(kDefaultMaxPacketSize, serialized.encrypted_length); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length); delete frame.stream_frame; } @@ -702,24 +679,18 @@ frames, true /* has_crypto_handshake */, true /* needs padding */, ENCRYPTION_NONE, QuicPacketCreatorPeer::NextPacketNumberLength(&creator_))); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - EXPECT_CALL(delegate_, OnSerializedPacket(_)) - .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); - } - SerializedPacket serialized = - creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); - if (FLAGS_quic_retransmit_via_onserializedpacket) { - serialized = serialized_packet_; - } + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.ReserializeAllFrames(retransmission, buffer, kMaxPacketSize); // If there is not enough space in the packet to fit a padding frame // (1 byte) and to expand the stream frame (another 2 bytes) the packet // will not be padded. if (bytes_free < 3) { EXPECT_EQ(kDefaultMaxPacketSize - bytes_free, - serialized.encrypted_length); + serialized_packet_.encrypted_length); } else { - EXPECT_EQ(kDefaultMaxPacketSize, serialized.encrypted_length); + EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length); } delete frame.stream_frame;
diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index f4bd4fc5..f9d6ed1 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc
@@ -307,12 +307,11 @@ return packet_creator_.SerializeVersionNegotiationPacket(supported_versions); } -SerializedPacket QuicPacketGenerator::ReserializeAllFrames( +void QuicPacketGenerator::ReserializeAllFrames( const PendingRetransmission& retransmission, char* buffer, size_t buffer_len) { - return packet_creator_.ReserializeAllFrames(retransmission, buffer, - buffer_len); + packet_creator_.ReserializeAllFrames(retransmission, buffer, buffer_len); } void QuicPacketGenerator::UpdateSequenceNumberLength(
diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h index 63b0c0c..6d956e3 100644 --- a/net/quic/quic_packet_generator.h +++ b/net/quic/quic_packet_generator.h
@@ -148,10 +148,9 @@ // Used for retransmitting packets to ensure they aren't too long. // Caller must ensure that any open FEC group is closed before calling this // method. - SerializedPacket ReserializeAllFrames( - const PendingRetransmission& retransmission, - char* buffer, - size_t buffer_len); + void ReserializeAllFrames(const PendingRetransmission& retransmission, + char* buffer, + size_t buffer_len); // Update the packet number length to use in future packets as soon as it // can be safely changed.
diff --git a/net/quic/test_tools/quic_test_packet_maker.cc b/net/quic/test_tools/quic_test_packet_maker.cc index 02e2ecd..86e10df 100644 --- a/net/quic/test_tools/quic_test_packet_maker.cc +++ b/net/quic/test_tools/quic_test_packet_maker.cc
@@ -463,7 +463,7 @@ scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeMultipleFramesPacket( const QuicPacketHeader& header, const QuicFrames& frames) { - QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), + QuicFramer framer(SupportedVersions(version_), clock_->Now(), Perspective::IS_CLIENT); scoped_ptr<QuicPacket> packet( BuildUnsizedDataPacket(&framer, header, frames));
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 5619247..9526c1c 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc
@@ -95,6 +95,7 @@ #include "net/cert/cert_verifier.h" #include "net/cert/ct_ev_whitelist.h" #include "net/cert/ct_policy_enforcer.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/ct_verifier.h" #include "net/cert/ct_verify_result.h" #include "net/cert/scoped_nss_types.h" @@ -2410,7 +2411,7 @@ ssl_info->cert = server_cert_verify_result_.verified_cert; ssl_info->unverified_cert = core_->state().server_cert; - AddSCTInfoToSSLInfo(ssl_info); + AddCTInfoToSSLInfo(ssl_info); ssl_info->connection_status = core_->state().ssl_connection_status; @@ -3126,13 +3127,24 @@ // TODO(ekasper): wipe stapled_ocsp_response and sct_list_from_tls_extension // from the state after verification is complete, to conserve memory. + ct_verify_result_.ct_policies_applied = (policy_enforcer_ != nullptr); + ct_verify_result_.ev_policy_compliance = + ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; if (policy_enforcer_ && (server_cert_verify_result_.cert_status & CERT_STATUS_IS_EV)) { scoped_refptr<ct::EVCertsWhitelist> ev_whitelist = SSLConfigService::GetEVCertsWhitelist(); - if (!policy_enforcer_->DoesConformToCTEVPolicy( + ct::EVPolicyCompliance ev_policy_compliance = + policy_enforcer_->DoesConformToCTEVPolicy( server_cert_verify_result_.verified_cert.get(), ev_whitelist.get(), - ct_verify_result_, net_log_)) { + ct_verify_result_.verified_scts, net_log_); + ct_verify_result_.ev_policy_compliance = ev_policy_compliance; + if (ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY && + ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST && + ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS) { // TODO(eranm): Log via the BoundNetLog, see crbug.com/437766 VLOG(1) << "EV certificate for " << server_cert_verify_result_.verified_cert->subject() @@ -3158,8 +3170,8 @@ return valid_thread_id_ == base::PlatformThread::CurrentId(); } -void SSLClientSocketNSS::AddSCTInfoToSSLInfo(SSLInfo* ssl_info) const { - ssl_info->UpdateSignedCertificateTimestamps(ct_verify_result_); +void SSLClientSocketNSS::AddCTInfoToSSLInfo(SSLInfo* ssl_info) const { + ssl_info->UpdateCertificateTransparencyInfo(ct_verify_result_); } // static
diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index d8a1549..0df9d4f 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h
@@ -146,7 +146,7 @@ // vetor representing a particular verification state, this method associates // each of the SCTs with the corresponding SCTVerifyStatus as it adds it to // the |ssl_info|.signed_certificate_timestamps list. - void AddSCTInfoToSSLInfo(SSLInfo* ssl_info) const; + void AddCTInfoToSSLInfo(SSLInfo* ssl_info) const; // Move last protocol to first place: SSLConfig::next_protos has protocols in // decreasing order of preference with NPN fallback protocol at the end, but
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index 4b09828..e36346f 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc
@@ -39,6 +39,7 @@ #include "net/cert/cert_verifier.h" #include "net/cert/ct_ev_whitelist.h" #include "net/cert/ct_policy_enforcer.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/ct_verifier.h" #include "net/cert/x509_certificate_net_log_param.h" #include "net/cert/x509_util_openssl.h" @@ -92,64 +93,6 @@ const uint8_t kTbMinProtocolVersionMajor = 0; const uint8_t kTbMinProtocolVersionMinor = 3; -void FreeX509Stack(STACK_OF(X509)* ptr) { - sk_X509_pop_free(ptr, X509_free); -} - -using ScopedX509Stack = crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>; - -// Used for encoding the |connection_status| field of an SSLInfo object. -int EncodeSSLConnectionStatus(uint16_t cipher_suite, - int compression, - int version) { - return cipher_suite | - ((compression & SSL_CONNECTION_COMPRESSION_MASK) << - SSL_CONNECTION_COMPRESSION_SHIFT) | - ((version & SSL_CONNECTION_VERSION_MASK) << - SSL_CONNECTION_VERSION_SHIFT); -} - -// Returns the net SSL version number (see ssl_connection_status_flags.h) for -// this SSL connection. -int GetNetSSLVersion(SSL* ssl) { - switch (SSL_version(ssl)) { - case TLS1_VERSION: - return SSL_CONNECTION_VERSION_TLS1; - case TLS1_1_VERSION: - return SSL_CONNECTION_VERSION_TLS1_1; - case TLS1_2_VERSION: - return SSL_CONNECTION_VERSION_TLS1_2; - default: - NOTREACHED(); - return SSL_CONNECTION_VERSION_UNKNOWN; - } -} - -ScopedX509 OSCertHandleToOpenSSL( - X509Certificate::OSCertHandle os_handle) { -#if defined(USE_OPENSSL_CERTS) - return ScopedX509(X509Certificate::DupOSCertHandle(os_handle)); -#else // !defined(USE_OPENSSL_CERTS) - std::string der_encoded; - if (!X509Certificate::GetDEREncoded(os_handle, &der_encoded)) - return ScopedX509(); - const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data()); - return ScopedX509(d2i_X509(NULL, &bytes, der_encoded.size())); -#endif // defined(USE_OPENSSL_CERTS) -} - -ScopedX509Stack OSCertHandlesToOpenSSL( - const X509Certificate::OSCertHandles& os_handles) { - ScopedX509Stack stack(sk_X509_new_null()); - for (size_t i = 0; i < os_handles.size(); i++) { - ScopedX509 x509 = OSCertHandleToOpenSSL(os_handles[i]); - if (!x509) - return ScopedX509Stack(); - sk_X509_push(stack.get(), x509.release()); - } - return stack; -} - bool EVP_MDToPrivateKeyHash(const EVP_MD* md, SSLPrivateKey::Hash* hash) { switch (EVP_MD_type(md)) { case NID_md5_sha1: @@ -867,7 +810,7 @@ ssl_info->token_binding_key_param = tb_negotiated_param_; ssl_info->pinning_failure_log = pinning_failure_log_; - AddSCTInfoToSSLInfo(ssl_info); + AddCTInfoToSSLInfo(ssl_info); const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_); CHECK(cipher); @@ -875,9 +818,11 @@ ssl_info->key_exchange_info = SSL_SESSION_get_key_exchange_info(SSL_get_session(ssl_)); - ssl_info->connection_status = EncodeSSLConnectionStatus( - static_cast<uint16_t>(SSL_CIPHER_get_id(cipher)), 0 /* no compression */, - GetNetSSLVersion(ssl_)); + SSLConnectionStatusSetCipherSuite( + static_cast<uint16_t>(SSL_CIPHER_get_id(cipher)), + &ssl_info->connection_status); + SSLConnectionStatusSetVersion(GetNetSSLVersion(ssl_), + &ssl_info->connection_status); if (!SSL_get_secure_renegotiation_support(ssl_)) ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; @@ -1479,13 +1424,24 @@ server_cert_verify_result_.verified_cert.get(), ocsp_response, sct_list, &ct_verify_result_, net_log_); + ct_verify_result_.ct_policies_applied = (policy_enforcer_ != nullptr); + ct_verify_result_.ev_policy_compliance = + ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; if (policy_enforcer_ && (server_cert_verify_result_.cert_status & CERT_STATUS_IS_EV)) { scoped_refptr<ct::EVCertsWhitelist> ev_whitelist = SSLConfigService::GetEVCertsWhitelist(); - if (!policy_enforcer_->DoesConformToCTEVPolicy( + ct::EVPolicyCompliance ev_policy_compliance = + policy_enforcer_->DoesConformToCTEVPolicy( server_cert_verify_result_.verified_cert.get(), ev_whitelist.get(), - ct_verify_result_, net_log_)) { + ct_verify_result_.verified_scts, net_log_); + ct_verify_result_.ev_policy_compliance = ev_policy_compliance; + if (ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY && + ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_WHITELIST && + ev_policy_compliance != + ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS) { // TODO(eranm): Log via the BoundNetLog, see crbug.com/437766 VLOG(1) << "EV certificate for " << server_cert_verify_result_.verified_cert->subject() @@ -2147,8 +2103,8 @@ return 1; } -void SSLClientSocketOpenSSL::AddSCTInfoToSSLInfo(SSLInfo* ssl_info) const { - ssl_info->UpdateSignedCertificateTimestamps(ct_verify_result_); +void SSLClientSocketOpenSSL::AddCTInfoToSSLInfo(SSLInfo* ssl_info) const { + ssl_info->UpdateCertificateTransparencyInfo(ct_verify_result_); } std::string SSLClientSocketOpenSSL::GetSessionCacheKey() const {
diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h index 6e464d7..70d195ad 100644 --- a/net/socket/ssl_client_socket_openssl.h +++ b/net/socket/ssl_client_socket_openssl.h
@@ -197,12 +197,13 @@ // Called from the SSL layer whenever a new session is established. int NewSessionCallback(SSL_SESSION* session); - // Adds the SignedCertificateTimestamps from ct_verify_result_ to |ssl_info|. + // Adds the Certificate Transparency info from ct_verify_result_ to + // |ssl_info|. // SCTs are held in three separate vectors in ct_verify_result, each // vetor representing a particular verification state, this method associates // each of the SCTs with the corresponding SCTVerifyStatus as it adds it to // the |ssl_info|.signed_certificate_timestamps list. - void AddSCTInfoToSSLInfo(SSLInfo* ssl_info) const; + void AddCTInfoToSSLInfo(SSLInfo* ssl_info) const; // Returns a unique key string for the SSL session cache for // this socket.
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index d225390..d54f0035 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc
@@ -22,6 +22,7 @@ #include "net/base/test_data_directory.h" #include "net/cert/asn1_util.h" #include "net/cert/ct_policy_enforcer.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/ct_verifier.h" #include "net/cert/mock_cert_verifier.h" #include "net/cert/test_root_certs.h" @@ -699,10 +700,10 @@ class MockCTPolicyEnforcer : public CTPolicyEnforcer { public: MOCK_METHOD4(DoesConformToCTEVPolicy, - bool(X509Certificate* cert, - const ct::EVCertsWhitelist*, - const ct::CTVerifyResult&, - const BoundNetLog&)); + ct::EVPolicyCompliance(X509Certificate* cert, + const ct::EVCertsWhitelist*, + const ct::SCTList&, + const BoundNetLog&)); }; class SSLClientSocketTest : public PlatformTest { @@ -2349,7 +2350,8 @@ MockCTPolicyEnforcer policy_enforcer; SetCTPolicyEnforcer(&policy_enforcer); EXPECT_CALL(policy_enforcer, DoesConformToCTEVPolicy(_, _, _, _)) - .WillRepeatedly(Return(true)); + .WillRepeatedly( + Return(ct::EVPolicyCompliance::EV_POLICY_COMPLIES_VIA_SCTS)); int rv; ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv)); @@ -2381,7 +2383,8 @@ MockCTPolicyEnforcer policy_enforcer; SetCTPolicyEnforcer(&policy_enforcer); EXPECT_CALL(policy_enforcer, DoesConformToCTEVPolicy(_, _, _, _)) - .WillRepeatedly(Return(false)); + .WillRepeatedly( + Return(ct::EVPolicyCompliance::EV_POLICY_NOT_ENOUGH_SCTS)); int rv; ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
diff --git a/net/socket/ssl_server_socket_nss.cc b/net/socket/ssl_server_socket_nss.cc index 53ed48c..6258c87c 100644 --- a/net/socket/ssl_server_socket_nss.cc +++ b/net/socket/ssl_server_socket_nss.cc
@@ -83,21 +83,21 @@ scoped_ptr<SSLServerSocket> CreateSSLServerSocket( scoped_ptr<StreamSocket> socket, - X509Certificate* cert, + X509Certificate* certificate, const crypto::RSAPrivateKey& key, - const SSLServerConfig& ssl_config) { + const SSLServerConfig& ssl_server_config) { DCHECK(g_nss_server_sockets_init) << "EnableSSLServerSockets() has not been" << " called yet!"; - return scoped_ptr<SSLServerSocket>( - new SSLServerSocketNSS(std::move(socket), cert, key, ssl_config)); + return scoped_ptr<SSLServerSocket>(new SSLServerSocketNSS( + std::move(socket), certificate, key, ssl_server_config)); } SSLServerSocketNSS::SSLServerSocketNSS( scoped_ptr<StreamSocket> transport_socket, scoped_refptr<X509Certificate> cert, const crypto::RSAPrivateKey& key, - const SSLServerConfig& ssl_config) + const SSLServerConfig& ssl_server_config) : transport_send_busy_(false), transport_recv_busy_(false), user_read_buf_len_(0), @@ -105,7 +105,7 @@ nss_fd_(NULL), nss_bufs_(NULL), transport_socket_(std::move(transport_socket)), - ssl_config_(ssl_config), + ssl_server_config_(ssl_server_config), cert_(cert), key_(key.Copy()), next_handshake_state_(STATE_NONE), @@ -337,7 +337,8 @@ int rv; - if (ssl_config_.require_client_cert) { + if (ssl_server_config_.client_cert_type == + SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT) { rv = SSL_OptionSet(nss_fd_, SSL_REQUEST_CERTIFICATE, PR_TRUE); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_OptionSet", @@ -359,15 +360,15 @@ } SSLVersionRange version_range; - version_range.min = ssl_config_.version_min; - version_range.max = ssl_config_.version_max; + version_range.min = ssl_server_config_.version_min; + version_range.max = ssl_server_config_.version_max; rv = SSL_VersionRangeSet(nss_fd_, &version_range); if (rv != SECSuccess) { LogFailedNSSFunction(net_log_, "SSL_VersionRangeSet", ""); return ERR_NO_SSL_VERSIONS_ENABLED; } - if (ssl_config_.require_ecdhe) { + if (ssl_server_config_.require_ecdhe) { const PRUint16* const ssl_ciphers = SSL_GetImplementedCiphers(); const PRUint16 num_ciphers = SSL_GetNumImplementedCiphers(); @@ -384,8 +385,8 @@ } for (std::vector<uint16_t>::const_iterator it = - ssl_config_.disabled_cipher_suites.begin(); - it != ssl_config_.disabled_cipher_suites.end(); ++it) { + ssl_server_config_.disabled_cipher_suites.begin(); + it != ssl_server_config_.disabled_cipher_suites.end(); ++it) { // This will fail if the specified cipher is not implemented by NSS, but // the failure is harmless. SSL_CipherPrefSet(nss_fd_, *it, PR_FALSE);
diff --git a/net/socket/ssl_server_socket_nss.h b/net/socket/ssl_server_socket_nss.h index 6bdcf112..788e452 100644 --- a/net/socket/ssl_server_socket_nss.h +++ b/net/socket/ssl_server_socket_nss.h
@@ -29,7 +29,7 @@ SSLServerSocketNSS(scoped_ptr<StreamSocket> socket, scoped_refptr<X509Certificate> certificate, const crypto::RSAPrivateKey& key, - const SSLServerConfig& ssl_config); + const SSLServerConfig& ssl_server_config); ~SSLServerSocketNSS() override; // SSLServerSocket interface. @@ -139,7 +139,7 @@ scoped_ptr<StreamSocket> transport_socket_; // Options for the SSL socket. - SSLServerConfig ssl_config_; + SSLServerConfig ssl_server_config_; // Certificate for the server. scoped_refptr<X509Certificate> cert_;
diff --git a/net/socket/ssl_server_socket_openssl.cc b/net/socket/ssl_server_socket_openssl.cc index 6ba2e17..adde711 100644 --- a/net/socket/ssl_server_socket_openssl.cc +++ b/net/socket/ssl_server_socket_openssl.cc
@@ -15,13 +15,45 @@ #include "crypto/rsa_private_key.h" #include "crypto/scoped_openssl_types.h" #include "net/base/net_errors.h" +#include "net/cert/cert_verify_result.h" +#include "net/cert/client_cert_verifier.h" +#include "net/cert/x509_util_openssl.h" #include "net/ssl/openssl_ssl_util.h" #include "net/ssl/scoped_openssl_types.h" +#include "net/ssl/ssl_connection_status_flags.h" +#include "net/ssl/ssl_info.h" #define GotoState(s) next_handshake_state_ = s namespace net { +namespace { + +// Creates an X509Certificate out of the concatenation of |cert|, if non-null, +// with |chain|. +scoped_refptr<X509Certificate> CreateX509Certificate(X509* cert, + STACK_OF(X509) * chain) { + std::vector<base::StringPiece> der_chain; + base::StringPiece der_cert; + scoped_refptr<X509Certificate> client_cert; + if (cert) { + if (!x509_util::GetDER(cert, &der_cert)) + return nullptr; + der_chain.push_back(der_cert); + } + + for (size_t i = 0; i < sk_X509_num(chain); ++i) { + X509* x = sk_X509_value(chain, i); + if (!x509_util::GetDER(x, &der_cert)) + return nullptr; + der_chain.push_back(der_cert); + } + + return X509Certificate::CreateFromDERCertChain(der_chain); +} + +} // namespace + void EnableSSLServerSockets() { // No-op because CreateSSLServerSocket() calls crypto::EnsureOpenSSLInit(). } @@ -30,17 +62,17 @@ scoped_ptr<StreamSocket> socket, X509Certificate* certificate, const crypto::RSAPrivateKey& key, - const SSLServerConfig& ssl_config) { + const SSLServerConfig& ssl_server_config) { crypto::EnsureOpenSSLInit(); return scoped_ptr<SSLServerSocket>(new SSLServerSocketOpenSSL( - std::move(socket), certificate, key, ssl_config)); + std::move(socket), certificate, key, ssl_server_config)); } SSLServerSocketOpenSSL::SSLServerSocketOpenSSL( scoped_ptr<StreamSocket> transport_socket, scoped_refptr<X509Certificate> certificate, const crypto::RSAPrivateKey& key, - const SSLServerConfig& ssl_config) + const SSLServerConfig& ssl_server_config) : transport_send_busy_(false), transport_recv_busy_(false), transport_recv_eof_(false), @@ -50,7 +82,7 @@ ssl_(NULL), transport_bio_(NULL), transport_socket_(std::move(transport_socket)), - ssl_config_(ssl_config), + ssl_server_config_(ssl_server_config), cert_(certificate), key_(key.Copy()), next_handshake_state_(STATE_NONE), @@ -242,8 +274,30 @@ } bool SSLServerSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { - NOTIMPLEMENTED(); - return false; + ssl_info->Reset(); + if (!completed_handshake_) + return false; + + ssl_info->cert = client_cert_; + + const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_); + CHECK(cipher); + ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL); + + SSLConnectionStatusSetCipherSuite( + static_cast<uint16_t>(SSL_CIPHER_get_id(cipher)), + &ssl_info->connection_status); + SSLConnectionStatusSetVersion(GetNetSSLVersion(ssl_), + &ssl_info->connection_status); + + if (!SSL_get_secure_renegotiation_support(ssl_)) + ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION; + + ssl_info->handshake_type = SSL_session_reused(ssl_) + ? SSLInfo::HANDSHAKE_RESUME + : SSLInfo::HANDSHAKE_FULL; + + return true; } void SSLServerSocketOpenSSL::GetConnectionAttempts( @@ -566,11 +620,28 @@ if (rv == 1) { completed_handshake_ = true; + // The results of SSL_get_peer_certificate() must be explicitly freed. + ScopedX509 cert(SSL_get_peer_certificate(ssl_)); + if (cert) { + // The caller does not take ownership of SSL_get_peer_cert_chain's + // results. + STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl_); + client_cert_ = CreateX509Certificate(cert.get(), chain); + if (!client_cert_.get()) + return ERR_SSL_CLIENT_AUTH_CERT_BAD_FORMAT; + } } else { int ssl_error = SSL_get_error(ssl_, rv); OpenSSLErrorInfo error_info; net_error = MapOpenSSLErrorWithDetails(ssl_error, err_tracer, &error_info); + // This hack is necessary because the mapping of SSL error codes to + // net_errors assumes (correctly for client sockets, but erroneously for + // server sockets) that peer cert verification failure can only occur if + // the cert changed during a renego. crbug.com/570351 + if (net_error == ERR_SSL_SERVER_CERT_CHANGED) + net_error = ERR_BAD_SSL_CLIENT_AUTH_CERT; + // If not done, stay in this state if (net_error == ERR_IO_PENDING) { GotoState(STATE_HANDSHAKE); @@ -615,11 +686,21 @@ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - ScopedSSL_CTX ssl_ctx(SSL_CTX_new(SSLv23_server_method())); - - if (ssl_config_.require_client_cert) - SSL_CTX_set_verify(ssl_ctx.get(), SSL_VERIFY_PEER, NULL); - + ScopedSSL_CTX ssl_ctx(SSL_CTX_new(TLS_method())); + int verify_mode = 0; + switch (ssl_server_config_.client_cert_type) { + case SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT: + verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + // Fall-through + case SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT: + verify_mode |= SSL_VERIFY_PEER; + SSL_CTX_set_verify(ssl_ctx.get(), verify_mode, nullptr); + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback, + ssl_server_config_.client_cert_verifier); + break; + case SSLServerConfig::ClientCertType::NO_CLIENT_CERT: + break; + } ssl_ = SSL_new(ssl_ctx.get()); if (!ssl_) return ERR_UNEXPECTED; @@ -666,10 +747,10 @@ return ERR_UNEXPECTED; } - DCHECK_LT(SSL3_VERSION, ssl_config_.version_min); - DCHECK_LT(SSL3_VERSION, ssl_config_.version_max); - SSL_set_min_version(ssl_, ssl_config_.version_min); - SSL_set_max_version(ssl_, ssl_config_.version_max); + DCHECK_LT(SSL3_VERSION, ssl_server_config_.version_min); + DCHECK_LT(SSL3_VERSION, ssl_server_config_.version_max); + SSL_set_min_version(ssl_, ssl_server_config_.version_min); + SSL_set_max_version(ssl_, ssl_server_config_.version_max); // OpenSSL defaults some options to on, others to off. To avoid ambiguity, // set everything we care about to an absolute value. @@ -693,11 +774,11 @@ // as the handshake hash. std::string command("DEFAULT:!SHA256:!SHA384:!AESGCM+AES256:!aPSK"); - if (ssl_config_.require_ecdhe) + if (ssl_server_config_.require_ecdhe) command.append(":!kRSA:!kDHE"); // Remove any disabled ciphers. - for (uint16_t id : ssl_config_.disabled_cipher_suites) { + for (uint16_t id : ssl_server_config_.disabled_cipher_suites) { const SSL_CIPHER* cipher = SSL_get_cipher_by_value(id); if (cipher) { command.append(":!"); @@ -712,7 +793,52 @@ LOG_IF(WARNING, rv != 1) << "SSL_set_cipher_list('" << command << "') returned " << rv; + if (ssl_server_config_.client_cert_type != + SSLServerConfig::ClientCertType::NO_CLIENT_CERT && + !ssl_server_config_.cert_authorities_.empty()) { + ScopedX509NameStack stack(sk_X509_NAME_new_null()); + for (const auto& authority : ssl_server_config_.cert_authorities_) { + const uint8_t* name = reinterpret_cast<const uint8_t*>(authority.c_str()); + const uint8_t* name_start = name; + ScopedX509_NAME subj(d2i_X509_NAME(nullptr, &name, authority.length())); + if (!subj || name != name_start + authority.length()) + return ERR_UNEXPECTED; + sk_X509_NAME_push(stack.get(), subj.release()); + } + SSL_set_client_CA_list(ssl_, stack.release()); + } + return OK; } +// static +int SSLServerSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx, + void* arg) { + ClientCertVerifier* verifier = reinterpret_cast<ClientCertVerifier*>(arg); + // If a verifier was not supplied, all certificates are accepted. + if (!verifier) + return 1; + STACK_OF(X509)* chain = store_ctx->untrusted; + scoped_refptr<X509Certificate> client_cert( + CreateX509Certificate(nullptr, chain)); + if (!client_cert.get()) { + X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_CERT_REJECTED); + return 0; + } + // Asynchronous completion of Verify is currently not supported. + // http://crbug.com/347402 + // The API for Verify supports the parts needed for async completion + // but is currently expected to complete synchronously. + scoped_ptr<ClientCertVerifier::Request> ignore_async; + int res = + verifier->Verify(client_cert.get(), CompletionCallback(), &ignore_async); + DCHECK_NE(res, ERR_IO_PENDING); + + if (res != OK) { + X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_CERT_REJECTED); + return 0; + } + return 1; +} + } // namespace net
diff --git a/net/socket/ssl_server_socket_openssl.h b/net/socket/ssl_server_socket_openssl.h index fd782497..52f8241b 100644 --- a/net/socket/ssl_server_socket_openssl.h +++ b/net/socket/ssl_server_socket_openssl.h
@@ -20,6 +20,7 @@ typedef struct bio_st BIO; // <openssl/ssl.h> typedef struct ssl_st SSL; +typedef struct x509_store_ctx_st X509_STORE_CTX; namespace net { @@ -32,7 +33,7 @@ SSLServerSocketOpenSSL(scoped_ptr<StreamSocket> socket, scoped_refptr<X509Certificate> certificate, const crypto::RSAPrivateKey& key, - const SSLServerConfig& ssl_config); + const SSLServerConfig& ssl_server_config); ~SSLServerSocketOpenSSL() override; // SSLServerSocket interface. @@ -105,6 +106,7 @@ void DoWriteCallback(int result); int Init(); + static int CertVerifyCallback(X509_STORE_CTX* store_ctx, void* arg); // Members used to send and receive buffer. bool transport_send_busy_; @@ -140,7 +142,7 @@ scoped_ptr<StreamSocket> transport_socket_; // Options for the SSL socket. - SSLServerConfig ssl_config_; + SSLServerConfig ssl_server_config_; // Certificate for the server. scoped_refptr<X509Certificate> cert_; @@ -148,6 +150,9 @@ // Private key used by the server. scoped_ptr<crypto::RSAPrivateKey> key_; + // Certificate for the client. + scoped_refptr<X509Certificate> client_cert_; + State next_handshake_state_; bool completed_handshake_;
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc index ac2d44e..9a2dd9ca 100644 --- a/net/socket/ssl_server_socket_unittest.cc +++ b/net/socket/ssl_server_socket_unittest.cc
@@ -20,6 +20,7 @@ #include <queue> #include <utility> +#include "base/callback_helpers.h" #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -29,8 +30,11 @@ #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" +#include "build/build_config.h" #include "crypto/nss_util.h" #include "crypto/rsa_private_key.h" +#include "crypto/scoped_openssl_types.h" +#include "crypto/signature_creator.h" #include "net/base/address_list.h" #include "net/base/completion_callback.h" #include "net/base/host_port_pair.h" @@ -40,6 +44,7 @@ #include "net/base/test_data_directory.h" #include "net/cert/cert_status_flags.h" #include "net/cert/mock_cert_verifier.h" +#include "net/cert/mock_client_cert_verifier.h" #include "net/cert/x509_certificate.h" #include "net/http/transport_security_state.h" #include "net/log/net_log.h" @@ -47,18 +52,34 @@ #include "net/socket/socket_test_util.h" #include "net/socket/ssl_client_socket.h" #include "net/socket/stream_socket.h" +#include "net/ssl/scoped_openssl_types.h" +#include "net/ssl/ssl_cert_request_info.h" #include "net/ssl/ssl_cipher_suite_names.h" #include "net/ssl/ssl_connection_status_flags.h" #include "net/ssl/ssl_info.h" +#include "net/ssl/ssl_private_key.h" #include "net/ssl/ssl_server_config.h" +#include "net/ssl/test_ssl_private_key.h" #include "net/test/cert_test_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" +#if defined(USE_OPENSSL) +#include <openssl/evp.h> +#include <openssl/ssl.h> +#include <openssl/x509.h> +#endif + namespace net { namespace { +const char kClientCertFileName[] = "client_1.pem"; +const char kClientPrivateKeyFileName[] = "client_1.pk8"; +const char kWrongClientCertFileName[] = "client_2.pem"; +const char kWrongClientPrivateKeyFileName[] = "client_2.pk8"; +const char kClientCertCAFileName[] = "client_1_ca.pem"; + class FakeDataChannel { public: FakeDataChannel() @@ -110,11 +131,24 @@ // asynchronously, which is necessary to reproduce bug 127822. void Close() { closed_ = true; + if (!read_callback_.is_null()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&FakeDataChannel::DoReadCallback, + weak_factory_.GetWeakPtr())); + } } private: void DoReadCallback() { - if (read_callback_.is_null() || data_.empty()) + if (read_callback_.is_null()) + return; + + if (closed_) { + base::ResetAndReturn(&read_callback_).Run(ERR_CONNECTION_CLOSED); + return; + } + + if (data_.empty()) return; int copied = PropagateData(read_buf_, read_buf_len_); @@ -302,8 +336,10 @@ SSLServerSocketTest() : socket_factory_(ClientSocketFactory::GetDefaultFactory()), cert_verifier_(new MockCertVerifier()), + client_cert_verifier_(new MockClientCertVerifier()), transport_security_state_(new TransportSecurityState) { - cert_verifier_->set_default_result(CERT_STATUS_AUTHORITY_INVALID); + cert_verifier_->set_default_result(ERR_CERT_AUTHORITY_INVALID); + client_cert_verifier_->set_default_result(ERR_CERT_AUTHORITY_INVALID); } protected: @@ -314,25 +350,10 @@ scoped_ptr<StreamSocket> server_socket( new FakeSocket(&channel_2_, &channel_1_)); - base::FilePath certs_dir(GetTestCertsDirectory()); - - base::FilePath cert_path = certs_dir.AppendASCII("unittest.selfsigned.der"); - std::string cert_der; - ASSERT_TRUE(base::ReadFileToString(cert_path, &cert_der)); - - scoped_refptr<X509Certificate> cert = - X509Certificate::CreateFromBytes(cert_der.data(), cert_der.size()); - - base::FilePath key_path = certs_dir.AppendASCII("unittest.key.bin"); - std::string key_string; - ASSERT_TRUE(base::ReadFileToString(key_path, &key_string)); - std::vector<uint8_t> key_vector( - reinterpret_cast<const uint8_t*>(key_string.data()), - reinterpret_cast<const uint8_t*>(key_string.data() + - key_string.length())); - - scoped_ptr<crypto::RSAPrivateKey> private_key( - crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector)); + scoped_refptr<X509Certificate> server_cert( + ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der")); + scoped_ptr<crypto::RSAPrivateKey> server_private_key( + ReadTestKey("unittest.key.bin")); client_ssl_config_.false_start_enabled = false; client_ssl_config_.channel_id_enabled = false; @@ -340,20 +361,79 @@ // Certificate provided by the host doesn't need authority. SSLConfig::CertAndStatus cert_and_status; cert_and_status.cert_status = CERT_STATUS_AUTHORITY_INVALID; - cert_and_status.der_cert = cert_der; + std::string server_cert_der; + CHECK(X509Certificate::GetDEREncoded(server_cert->os_cert_handle(), + &server_cert_der)); + cert_and_status.der_cert = server_cert_der; client_ssl_config_.allowed_bad_certs.push_back(cert_and_status); HostPortPair host_and_pair("unittest", 0); SSLClientSocketContext context; context.cert_verifier = cert_verifier_.get(); context.transport_security_state = transport_security_state_.get(); + socket_factory_->ClearSSLSessionCache(); client_socket_ = socket_factory_->CreateSSLClientSocket( std::move(client_connection), host_and_pair, client_ssl_config_, context); - server_socket_ = CreateSSLServerSocket(std::move(server_socket), cert.get(), - *private_key, server_ssl_config_); + server_socket_ = + CreateSSLServerSocket(std::move(server_socket), server_cert.get(), + *server_private_key, server_ssl_config_); } +#if defined(USE_OPENSSL) + void ConfigureClientCertsForClient(const char* cert_file_name, + const char* private_key_file_name) { + client_ssl_config_.send_client_cert = true; + client_ssl_config_.client_cert = + ImportCertFromFile(GetTestCertsDirectory(), cert_file_name); + ASSERT_TRUE(client_ssl_config_.client_cert); + scoped_ptr<crypto::RSAPrivateKey> key = ReadTestKey(private_key_file_name); + ASSERT_TRUE(key); + client_ssl_config_.client_private_key = WrapOpenSSLPrivateKey( + crypto::ScopedEVP_PKEY(EVP_PKEY_up_ref(key->key()))); + } + + void ConfigureClientCertsForServer() { + server_ssl_config_.client_cert_type = + SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; + + ScopedX509NameStack cert_names( + SSL_load_client_CA_file(GetTestCertsDirectory() + .AppendASCII(kClientCertCAFileName) + .MaybeAsASCII() + .c_str())); + ASSERT_TRUE(cert_names); + for (size_t i = 0; i < sk_X509_NAME_num(cert_names.get()); ++i) { + uint8_t* str = nullptr; + int length = i2d_X509_NAME(sk_X509_NAME_value(cert_names.get(), i), &str); + server_ssl_config_.cert_authorities_.push_back(std::string( + reinterpret_cast<const char*>(str), static_cast<size_t>(length))); + OPENSSL_free(str); + } + + scoped_refptr<X509Certificate> expected_client_cert( + ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName)); + client_cert_verifier_->AddResultForCert(expected_client_cert.get(), OK); + + server_ssl_config_.client_cert_verifier = client_cert_verifier_.get(); + } + + scoped_ptr<crypto::RSAPrivateKey> ReadTestKey(const base::StringPiece& name) { + base::FilePath certs_dir(GetTestCertsDirectory()); + base::FilePath key_path = certs_dir.AppendASCII(name); + std::string key_string; + if (!base::ReadFileToString(key_path, &key_string)) + return nullptr; + std::vector<uint8_t> key_vector( + reinterpret_cast<const uint8_t*>(key_string.data()), + reinterpret_cast<const uint8_t*>(key_string.data() + + key_string.length())); + scoped_ptr<crypto::RSAPrivateKey> key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector)); + return key; + } +#endif + FakeDataChannel channel_1_; FakeDataChannel channel_2_; SSLConfig client_ssl_config_; @@ -362,6 +442,7 @@ scoped_ptr<SSLServerSocket> server_socket_; ClientSocketFactory* socket_factory_; scoped_ptr<MockCertVerifier> cert_verifier_; + scoped_ptr<MockClientCertVerifier> client_cert_verifier_; scoped_ptr<TransportSecurityState> transport_security_state_; }; @@ -378,21 +459,17 @@ TEST_F(SSLServerSocketTest, Handshake) { Initialize(); - TestCompletionCallback connect_callback; TestCompletionCallback handshake_callback; - int server_ret = server_socket_->Handshake(handshake_callback.callback()); - EXPECT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING); + TestCompletionCallback connect_callback; int client_ret = client_socket_->Connect(connect_callback.callback()); - EXPECT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING); - if (client_ret == ERR_IO_PENDING) { - EXPECT_EQ(OK, connect_callback.WaitForResult()); - } - if (server_ret == ERR_IO_PENDING) { - EXPECT_EQ(OK, handshake_callback.WaitForResult()); - } + client_ret = connect_callback.GetResult(client_ret); + server_ret = handshake_callback.GetResult(server_ret); + + ASSERT_EQ(OK, client_ret); + ASSERT_EQ(OK, server_ret); // Make sure the cert status is expected. SSLInfo ssl_info; @@ -412,16 +489,101 @@ EXPECT_TRUE(is_aead); } +// NSS ports don't support client certificates. +#if defined(USE_OPENSSL) + +// This test executes Connect() on SSLClientSocket and Handshake() on +// SSLServerSocket to make sure handshaking between the two sockets is +// completed successfully, using client certificate. +TEST_F(SSLServerSocketTest, HandshakeWithClientCert) { + scoped_refptr<X509Certificate> client_cert = + ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName); + ConfigureClientCertsForClient(kClientCertFileName, kClientPrivateKeyFileName); + ConfigureClientCertsForServer(); + Initialize(); + + TestCompletionCallback handshake_callback; + int server_ret = server_socket_->Handshake(handshake_callback.callback()); + + TestCompletionCallback connect_callback; + int client_ret = client_socket_->Connect(connect_callback.callback()); + + client_ret = connect_callback.GetResult(client_ret); + server_ret = handshake_callback.GetResult(server_ret); + + ASSERT_EQ(OK, client_ret); + ASSERT_EQ(OK, server_ret); + + // Make sure the cert status is expected. + SSLInfo ssl_info; + client_socket_->GetSSLInfo(&ssl_info); + EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info.cert_status); + server_socket_->GetSSLInfo(&ssl_info); + EXPECT_TRUE(ssl_info.cert.get()); + EXPECT_TRUE(client_cert->Equals(ssl_info.cert.get())); +} + +TEST_F(SSLServerSocketTest, HandshakeWithClientCertRequiredNotSupplied) { + ConfigureClientCertsForServer(); + Initialize(); + // Use the default setting for the client socket, which is to not send + // a client certificate. This will cause the client to receive an + // ERR_SSL_CLIENT_AUTH_CERT_NEEDED error, and allow for inspecting the + // requested cert_authorities from the CertificateRequest sent by the + // server. + + TestCompletionCallback handshake_callback; + int server_ret = server_socket_->Handshake(handshake_callback.callback()); + + TestCompletionCallback connect_callback; + EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED, + connect_callback.GetResult( + client_socket_->Connect(connect_callback.callback()))); + + scoped_refptr<SSLCertRequestInfo> request_info = new SSLCertRequestInfo(); + client_socket_->GetSSLCertRequestInfo(request_info.get()); + + // Check that the authority name that arrived in the CertificateRequest + // handshake message is as expected. + scoped_refptr<X509Certificate> client_cert = + ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName); + EXPECT_TRUE(client_cert->IsIssuedByEncoded(request_info->cert_authorities)); + + client_socket_->Disconnect(); + + EXPECT_EQ(ERR_FAILED, handshake_callback.GetResult(server_ret)); +} + +TEST_F(SSLServerSocketTest, HandshakeWithWrongClientCertSupplied) { + scoped_refptr<X509Certificate> client_cert = + ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName); + ConfigureClientCertsForClient(kWrongClientCertFileName, + kWrongClientPrivateKeyFileName); + ConfigureClientCertsForServer(); + Initialize(); + + TestCompletionCallback handshake_callback; + int server_ret = server_socket_->Handshake(handshake_callback.callback()); + + TestCompletionCallback connect_callback; + int client_ret = client_socket_->Connect(connect_callback.callback()); + + EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT, + connect_callback.GetResult(client_ret)); + EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT, + handshake_callback.GetResult(server_ret)); +} +#endif // defined(USE_OPENSSL) + TEST_F(SSLServerSocketTest, DataTransfer) { Initialize(); - TestCompletionCallback connect_callback; - TestCompletionCallback handshake_callback; - // Establish connection. + TestCompletionCallback connect_callback; int client_ret = client_socket_->Connect(connect_callback.callback()); ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING); + TestCompletionCallback handshake_callback; int server_ret = server_socket_->Handshake(handshake_callback.callback()); ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING); @@ -499,13 +661,13 @@ TEST_F(SSLServerSocketTest, ClientWriteAfterServerClose) { Initialize(); - TestCompletionCallback connect_callback; - TestCompletionCallback handshake_callback; // Establish connection. + TestCompletionCallback connect_callback; int client_ret = client_socket_->Connect(connect_callback.callback()); ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING); + TestCompletionCallback handshake_callback; int server_ret = server_socket_->Handshake(handshake_callback.callback()); ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING); @@ -521,7 +683,6 @@ // socket won't return ERR_IO_PENDING. This ensures that the client // will call Read() on the transport socket again. TestCompletionCallback write_callback; - server_ret = server_socket_->Write( write_buf.get(), write_buf->size(), write_callback.callback()); EXPECT_TRUE(server_ret > 0 || server_ret == ERR_IO_PENDING); @@ -552,11 +713,10 @@ Initialize(); TestCompletionCallback connect_callback; - TestCompletionCallback handshake_callback; - int client_ret = client_socket_->Connect(connect_callback.callback()); ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING); + TestCompletionCallback handshake_callback; int server_ret = server_socket_->Handshake(handshake_callback.callback()); ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING); @@ -616,9 +776,9 @@ Initialize(); TestCompletionCallback connect_callback; - TestCompletionCallback handshake_callback; - int client_ret = client_socket_->Connect(connect_callback.callback()); + + TestCompletionCallback handshake_callback; int server_ret = server_socket_->Handshake(handshake_callback.callback()); client_ret = connect_callback.GetResult(client_ret);
diff --git a/net/ssl/openssl_ssl_util.cc b/net/ssl/openssl_ssl_util.cc index b91acdd..ae3834a 100644 --- a/net/ssl/openssl_ssl_util.cc +++ b/net/ssl/openssl_ssl_util.cc
@@ -16,6 +16,7 @@ #include "base/values.h" #include "crypto/openssl_util.h" #include "net/base/net_errors.h" +#include "net/ssl/ssl_connection_status_flags.h" namespace net { @@ -205,4 +206,42 @@ net_error, ssl_error, error_info); } +int GetNetSSLVersion(SSL* ssl) { + switch (SSL_version(ssl)) { + case TLS1_VERSION: + return SSL_CONNECTION_VERSION_TLS1; + case TLS1_1_VERSION: + return SSL_CONNECTION_VERSION_TLS1_1; + case TLS1_2_VERSION: + return SSL_CONNECTION_VERSION_TLS1_2; + default: + NOTREACHED(); + return SSL_CONNECTION_VERSION_UNKNOWN; + } +} + +ScopedX509 OSCertHandleToOpenSSL(X509Certificate::OSCertHandle os_handle) { +#if defined(USE_OPENSSL_CERTS) + return ScopedX509(X509Certificate::DupOSCertHandle(os_handle)); +#else // !defined(USE_OPENSSL_CERTS) + std::string der_encoded; + if (!X509Certificate::GetDEREncoded(os_handle, &der_encoded)) + return ScopedX509(); + const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data()); + return ScopedX509(d2i_X509(NULL, &bytes, der_encoded.size())); +#endif // defined(USE_OPENSSL_CERTS) +} + +ScopedX509Stack OSCertHandlesToOpenSSL( + const X509Certificate::OSCertHandles& os_handles) { + ScopedX509Stack stack(sk_X509_new_null()); + for (size_t i = 0; i < os_handles.size(); i++) { + ScopedX509 x509 = OSCertHandleToOpenSSL(os_handles[i]); + if (!x509) + return nullptr; + sk_X509_push(stack.get(), x509.release()); + } + return stack; +} + } // namespace net
diff --git a/net/ssl/openssl_ssl_util.h b/net/ssl/openssl_ssl_util.h index af8b9ab..545c9581 100644 --- a/net/ssl/openssl_ssl_util.h +++ b/net/ssl/openssl_ssl_util.h
@@ -7,7 +7,11 @@ #include <stdint.h> +#include <openssl/ssl.h> + +#include "net/cert/x509_certificate.h" #include "net/log/net_log.h" +#include "net/ssl/scoped_openssl_types.h" namespace crypto { class OpenSSLErrStackTracer; @@ -69,6 +73,15 @@ int ssl_error, const OpenSSLErrorInfo& error_info); +// Returns the net SSL version number (see ssl_connection_status_flags.h) for +// this SSL connection. +int GetNetSSLVersion(SSL* ssl); + +ScopedX509 OSCertHandleToOpenSSL(X509Certificate::OSCertHandle os_handle); + +ScopedX509Stack OSCertHandlesToOpenSSL( + const X509Certificate::OSCertHandles& os_handles); + } // namespace net #endif // NET_SSL_OPENSSL_SSL_UTIL_H_
diff --git a/net/ssl/scoped_openssl_types.h b/net/ssl/scoped_openssl_types.h index a8c3c34..92ce7d7 100644 --- a/net/ssl/scoped_openssl_types.h +++ b/net/ssl/scoped_openssl_types.h
@@ -6,18 +6,31 @@ #define NET_SSL_SCOPED_OPENSSL_TYPES_H_ #include <openssl/ssl.h> +#include <openssl/stack.h> #include <openssl/x509.h> #include "crypto/scoped_openssl_types.h" namespace net { +inline void FreeX509Stack(STACK_OF(X509)* ptr) { + sk_X509_pop_free(ptr, X509_free); +} + +inline void FreeX509NameStack(STACK_OF(X509_NAME)* ptr) { + sk_X509_NAME_pop_free(ptr, X509_NAME_free); +} + using ScopedPKCS8_PRIV_KEY_INFO = crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>; using ScopedSSL = crypto::ScopedOpenSSL<SSL, SSL_free>; using ScopedSSL_CTX = crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free>; using ScopedSSL_SESSION = crypto::ScopedOpenSSL<SSL_SESSION, SSL_SESSION_free>; using ScopedX509 = crypto::ScopedOpenSSL<X509, X509_free>; +using ScopedX509_NAME = crypto::ScopedOpenSSL<X509_NAME, X509_NAME_free>; +using ScopedX509Stack = crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>; +using ScopedX509NameStack = + crypto::ScopedOpenSSL<STACK_OF(X509_NAME), FreeX509NameStack>; } // namespace net
diff --git a/net/ssl/ssl_cert_request_info.h b/net/ssl/ssl_cert_request_info.h index 13f91d69..8b9e542 100644 --- a/net/ssl/ssl_cert_request_info.h +++ b/net/ssl/ssl_cert_request_info.h
@@ -32,7 +32,7 @@ // // struct { // ClientCertificateType certificate_types<1..2^8-1>; -// DistinguishedName certificate_authorities<3..2^16-1>; +// DistinguishedName certificate_authorities<0..2^16-1>; // } CertificateRequest; class NET_EXPORT SSLCertRequestInfo : public base::RefCountedThreadSafe<SSLCertRequestInfo> {
diff --git a/net/ssl/ssl_info.cc b/net/ssl/ssl_info.cc index 545bf7e..aca1f43 100644 --- a/net/ssl/ssl_info.cc +++ b/net/ssl/ssl_info.cc
@@ -6,6 +6,7 @@ #include "base/pickle.h" #include "net/cert/cert_status_flags.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/signed_certificate_timestamp.h" #include "net/cert/x509_certificate.h" @@ -36,8 +37,10 @@ token_binding_key_param = info.token_binding_key_param; handshake_type = info.handshake_type; public_key_hashes = info.public_key_hashes; - signed_certificate_timestamps = info.signed_certificate_timestamps; pinning_failure_log = info.pinning_failure_log; + signed_certificate_timestamps = info.signed_certificate_timestamps; + ct_compliance_details_available = info.ct_compliance_details_available; + ct_ev_policy_compliance = info.ct_ev_policy_compliance; return *this; } @@ -56,15 +59,17 @@ token_binding_key_param = TB_PARAM_ECDSAP256; handshake_type = HANDSHAKE_UNKNOWN; public_key_hashes.clear(); - signed_certificate_timestamps.clear(); pinning_failure_log.clear(); + signed_certificate_timestamps.clear(); + ct_compliance_details_available = false; + ct_ev_policy_compliance = ct::EVPolicyCompliance::EV_POLICY_DOES_NOT_APPLY; } void SSLInfo::SetCertError(int error) { cert_status |= MapNetErrorToCertStatus(error); } -void SSLInfo::UpdateSignedCertificateTimestamps( +void SSLInfo::UpdateCertificateTransparencyInfo( const ct::CTVerifyResult& ct_verify_result) { for (const auto& sct : ct_verify_result.verified_scts) { signed_certificate_timestamps.push_back( @@ -78,6 +83,9 @@ signed_certificate_timestamps.push_back( SignedCertificateTimestampAndStatus(sct, ct::SCT_STATUS_LOG_UNKNOWN)); } + + ct_compliance_details_available = ct_verify_result.ct_policies_applied; + ct_ev_policy_compliance = ct_verify_result.ev_policy_compliance; } } // namespace net
diff --git a/net/ssl/ssl_info.h b/net/ssl/ssl_info.h index 40dec28..3935369 100644 --- a/net/ssl/ssl_info.h +++ b/net/ssl/ssl_info.h
@@ -18,6 +18,12 @@ namespace net { +namespace ct { + +enum class EVPolicyCompliance; + +} // namespace ct + class X509Certificate; // SSL connection info. @@ -44,12 +50,14 @@ // Adds the specified |error| to the cert status. void SetCertError(int error); - // Adds the SignedCertificateTimestamps from ct_verify_result to - // |signed_certificate_timestamps|. SCTs are held in three separate vectors - // in ct_verify_result, each vetor representing a particular verification - // state, this method associates each of the SCTs with the corresponding - // SCTVerifyStatus as it adds it to the |signed_certificate_timestamps| list. - void UpdateSignedCertificateTimestamps( + // Adds the SignedCertificateTimestamps and policy compliance details + // from ct_verify_result to |signed_certificate_timestamps| and + // |ct_policy_compliance_details|. SCTs are held in three separate + // vectors in ct_verify_result, each vetor representing a particular + // verification state, this method associates each of the SCTs with + // the corresponding SCTVerifyStatus as it adds it to the + // |signed_certificate_timestamps| list. + void UpdateCertificateTransparencyInfo( const ct::CTVerifyResult& ct_verify_result); // The SSL certificate. @@ -115,6 +123,18 @@ // List of SignedCertificateTimestamps and their corresponding validation // status. SignedCertificateTimestampAndStatusList signed_certificate_timestamps; + + // True if Certificate Transparency policies were applied on this + // connection and results are available. If true, the field below + // (|ev_policy_compliance|) will contain information about whether + // the connection complied with the policy and why the connection + // was considered non-compliant, if applicable. + bool ct_compliance_details_available; + + // Whether the connection complied with the CT EV policy, and if not, + // why not. Only meaningful if |ct_compliance_details_available| is + // true. + ct::EVPolicyCompliance ct_ev_policy_compliance; }; } // namespace net
diff --git a/net/ssl/ssl_server_config.cc b/net/ssl/ssl_server_config.cc index 8b3e67f..f423933 100644 --- a/net/ssl/ssl_server_config.cc +++ b/net/ssl/ssl_server_config.cc
@@ -13,7 +13,8 @@ : version_min(kDefaultSSLVersionMin), version_max(SSL_PROTOCOL_VERSION_TLS1_2), require_ecdhe(false), - require_client_cert(false) {} + client_cert_type(NO_CLIENT_CERT), + client_cert_verifier(nullptr) {} SSLServerConfig::~SSLServerConfig() {}
diff --git a/net/ssl/ssl_server_config.h b/net/ssl/ssl_server_config.h index 36d1286b..95bea0b 100644 --- a/net/ssl/ssl_server_config.h +++ b/net/ssl/ssl_server_config.h
@@ -14,8 +14,16 @@ namespace net { +class ClientCertVerifier; + // A collection of server-side SSL-related configuration settings. struct NET_EXPORT SSLServerConfig { + enum ClientCertType { + NO_CLIENT_CERT, + OPTIONAL_CLIENT_CERT, + REQUIRE_CLIENT_CERT, + }; + // Defaults SSLServerConfig(); ~SSLServerConfig(); @@ -53,9 +61,21 @@ // If true, causes only ECDHE cipher suites to be enabled. bool require_ecdhe; - // Requires a client certificate for client authentication from the client. - // This doesn't currently enforce certificate validity. - bool require_client_cert; + // Sets the requirement for client certificates during handshake. + ClientCertType client_cert_type; + + // List of DER-encoded X.509 DistinguishedName of certificate authorities + // to be included in the CertificateRequest handshake message, + // if client certificates are required. + std::vector<std::string> cert_authorities_; + + // Provides the ClientCertVerifier that is to be used to verify + // client certificates during the handshake. + // The |client_cert_verifier| continues to be owned by the caller, + // and must outlive any sockets using this SSLServerConfig. + // This field is meaningful only if client certificates are requested. + // If a verifier is not provided then all certificates are accepted. + ClientCertVerifier* client_cert_verifier; }; } // namespace net
diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc index bcfd92b..28c31a8 100644 --- a/net/tools/quic/quic_dispatcher.cc +++ b/net/tools/quic/quic_dispatcher.cc
@@ -443,13 +443,7 @@ } QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() { - if (FLAGS_quic_time_wait_list_manager_use_shared_writer) { - return new QuicTimeWaitListManager(writer_.get(), this, helper_.get()); - } else { - time_wait_list_writer_.reset(CreatePerConnectionWriter()); - return new QuicTimeWaitListManager(time_wait_list_writer_.get(), this, - helper_.get()); - } + return new QuicTimeWaitListManager(writer_.get(), this, helper_.get()); } bool QuicDispatcher::HandlePacketForTimeWait(
diff --git a/net/tools/quic/quic_dispatcher.h b/net/tools/quic/quic_dispatcher.h index 0e0a1afd..6b8f5e57 100644 --- a/net/tools/quic/quic_dispatcher.h +++ b/net/tools/quic/quic_dispatcher.h
@@ -239,11 +239,6 @@ // The writer to write to the socket with. scoped_ptr<QuicPacketWriter> writer_; - // A per-connection writer that is passed to the time wait list manager. - // TODO(jdorfman): Remove this when deprecating - // FLAGS_quic_time_wait_list_manager_use_shared_writer. - scoped_ptr<QuicPacketWriter> time_wait_list_writer_; - // This vector contains QUIC versions which we currently support. // This should be ordered such that the highest supported version is the first // element, with subsequent elements in descending order (versions can be
diff --git a/net/udp/datagram_server_socket.h b/net/udp/datagram_server_socket.h index 1dd444f4..bad1fee 100644 --- a/net/udp/datagram_server_socket.h +++ b/net/udp/datagram_server_socket.h
@@ -31,23 +31,19 @@ // |address| is a buffer provided by the caller for receiving the sender // address information about the received data. This buffer must be kept // alive by the caller until the callback is placed. - // |address_length| is a ptr to the length of the |address| buffer. This - // is an input parameter containing the maximum size |address| can hold - // and also an output parameter for the size of |address| upon completion. // |callback| is the callback on completion of the RecvFrom. // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. - // If ERR_IO_PENDING is returned, the caller must keep |buf|, |address|, - // and |address_length| alive until the callback is called. + // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address| + // alive until the callback is called. virtual int RecvFrom(IOBuffer* buf, int buf_len, IPEndPoint* address, const CompletionCallback& callback) = 0; // Send to a socket with a particular destination. - // |buf| is the buffer to send - // |buf_len| is the number of bytes to send + // |buf| is the buffer to send. + // |buf_len| is the number of bytes to send. // |address| is the recipient address. - // |address_length| is the size of the recipient address // |callback| is the user callback function to call on complete. // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address|
diff --git a/net/udp/udp_socket_posix.h b/net/udp/udp_socket_posix.h index 2219f2e6..969f98d 100644 --- a/net/udp/udp_socket_posix.h +++ b/net/udp/udp_socket_posix.h
@@ -86,23 +86,19 @@ // |address| is a buffer provided by the caller for receiving the sender // address information about the received data. This buffer must be kept // alive by the caller until the callback is placed. - // |address_length| is a ptr to the length of the |address| buffer. This - // is an input parameter containing the maximum size |address| can hold - // and also an output parameter for the size of |address| upon completion. // |callback| is the callback on completion of the RecvFrom. // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. - // If ERR_IO_PENDING is returned, the caller must keep |buf|, |address|, - // and |address_length| alive until the callback is called. + // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address| + // alive until the callback is called. int RecvFrom(IOBuffer* buf, int buf_len, IPEndPoint* address, const CompletionCallback& callback); // Sends to a socket with a particular destination. - // |buf| is the buffer to send - // |buf_len| is the number of bytes to send + // |buf| is the buffer to send. + // |buf_len| is the number of bytes to send. // |address| is the recipient address. - // |address_length| is the size of the recipient address // |callback| is the user callback function to call on complete. // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address|
diff --git a/net/udp/udp_socket_win.h b/net/udp/udp_socket_win.h index ea72e28b..f36c19b 100644 --- a/net/udp/udp_socket_win.h +++ b/net/udp/udp_socket_win.h
@@ -91,23 +91,19 @@ // |address| is a buffer provided by the caller for receiving the sender // address information about the received data. This buffer must be kept // alive by the caller until the callback is placed. - // |address_length| is a ptr to the length of the |address| buffer. This - // is an input parameter containing the maximum size |address| can hold - // and also an output parameter for the size of |address| upon completion. // |callback| is the callback on completion of the RecvFrom. // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. - // If ERR_IO_PENDING is returned, the caller must keep |buf|, |address|, - // and |address_length| alive until the callback is called. + // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address|, + // alive until the callback is called. int RecvFrom(IOBuffer* buf, int buf_len, IPEndPoint* address, const CompletionCallback& callback); // Sends to a socket with a particular destination. - // |buf| is the buffer to send - // |buf_len| is the number of bytes to send + // |buf| is the buffer to send. + // |buf_len| is the number of bytes to send. // |address| is the recipient address. - // |address_length| is the size of the recipient address // |callback| is the user callback function to call on complete. // Returns a net error code, or ERR_IO_PENDING if the IO is in progress. // If ERR_IO_PENDING is returned, the caller must keep |buf| and |address|
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 7c08b7f..55f5235 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc
@@ -6567,6 +6567,20 @@ EXPECT_EQ(redirect_url, r->url()); } +TEST_F(URLRequestTestHTTP, UnsupportedReferrerScheme) { + ASSERT_TRUE(http_test_server()->Start()); + + const std::string referrer("foobar://totally.legit.referrer"); + TestDelegate d; + scoped_ptr<URLRequest> req(default_context_.CreateRequest( + http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d)); + req->SetReferrer(referrer); + req->Start(); + base::RunLoop().Run(); + + EXPECT_EQ(std::string("None"), d.data_received()); +} + TEST_F(URLRequestTestHTTP, NoUserPassInReferrer) { ASSERT_TRUE(http_test_server()->Start()); @@ -8397,7 +8411,8 @@ TEST_F(HTTPSRequestTest, ClientAuthTest) { EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; - ssl_config.require_client_cert = true; + ssl_config.client_cert_type = + SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT; test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config); test_server.AddDefaultHandlers( base::FilePath(FILE_PATH_LITERAL("net/data/ssl")));
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc index b4c07e8..13c16c9 100644 --- a/pdf/pdfium/pdfium_engine.cc +++ b/pdf/pdfium/pdfium_engine.cc
@@ -156,14 +156,14 @@ // printing). // TODO(noamsml): Real font substitution (http://crbug.com/391978) if (!pp::Module::Get()) - return NULL; + return nullptr; pp::BrowserFontDescription description; // Pretend the system does not have the Symbol font to force a fallback to // the built in Symbol font in CFX_FontMapper::FindSubstFont(). if (strcmp(face, "Symbol") == 0) - return NULL; + return nullptr; if (pitch_family & FXFONT_FF_FIXEDPITCH) { description.set_family(PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE); @@ -254,7 +254,7 @@ if (!pp::PDF::IsAvailable()) { NOTREACHED(); - return NULL; + return nullptr; } PP_Resource font_resource = pp::PDF::GetFontFileWithFallback( @@ -406,7 +406,7 @@ // Returns a VarDictionary (representing a bookmark), which in turn contains // child VarDictionaries (representing the child bookmarks). -// If NULL is passed in as the bookmark then we traverse from the "root". +// If nullptr is passed in as the bookmark then we traverse from the "root". // Note that the "root" bookmark contains no useful information. pp::VarDictionary TraverseBookmarks(FPDF_DOCUMENT doc, FPDF_BOOKMARK bookmark, @@ -532,8 +532,8 @@ current_rotation_(0), doc_loader_(this), password_tries_remaining_(0), - doc_(NULL), - form_(NULL), + doc_(nullptr), + form_(nullptr), defer_page_unload_(false), selecting_(false), mouse_down_state_(PDFiumPage::NONSELECTABLE_AREA, @@ -543,7 +543,7 @@ last_character_index_to_search_(-1), permissions_(0), permissions_handler_revision_(-1), - fpdf_availability_(NULL), + fpdf_availability_(nullptr), next_timer_id_(0), last_page_mouse_down_(-1), most_visible_page_(-1), @@ -572,7 +572,7 @@ // PDFiumEngine. FPDF_FORMFILLINFO::version = 1; FPDF_FORMFILLINFO::m_pJsPlatform = this; - FPDF_FORMFILLINFO::Release = NULL; + FPDF_FORMFILLINFO::Release = nullptr; FPDF_FORMFILLINFO::FFI_Invalidate = Form_Invalidate; FPDF_FORMFILLINFO::FFI_OutputSelectedRect = Form_OutputSelectedRect; FPDF_FORMFILLINFO::FFI_SetCursor = Form_SetCursor; @@ -616,7 +616,7 @@ IPDF_JSPLATFORM::Field_browse = Form_Browse; IFSDK_PAUSE::version = 1; - IFSDK_PAUSE::user = NULL; + IFSDK_PAUSE::user = nullptr; IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow; #if defined(OS_LINUX) @@ -651,79 +651,6 @@ #ifdef PDF_USE_XFA -// This is just for testing, needs to be removed later -#if defined(WIN32) -#define XFA_TESTFILE(filename) "E:/"#filename -#else -#define XFA_TESTFILE(filename) "/home/"#filename -#endif - -struct FPDF_FILE { - FPDF_FILEHANDLER file_handler; - FILE* file; -}; - -void Sample_Release(FPDF_LPVOID client_data) { - if (!client_data) - return; - FPDF_FILE* file_wrapper = (FPDF_FILE*)client_data; - fclose(file_wrapper->file); - delete file_wrapper; -} - -FPDF_DWORD Sample_GetSize(FPDF_LPVOID client_data) { - if (!client_data) - return 0; - FPDF_FILE* file_wrapper = (FPDF_FILE*)client_data; - long cur_pos = ftell(file_wrapper->file); - if (cur_pos == -1) - return 0; - if (fseek(file_wrapper->file, 0, SEEK_END)) - return 0; - long size = ftell(file_wrapper->file); - fseek(file_wrapper->file, cur_pos, SEEK_SET); - return (FPDF_DWORD)size; -} - -FPDF_RESULT Sample_ReadBlock(FPDF_LPVOID client_data, - FPDF_DWORD offset, - FPDF_LPVOID buffer, - FPDF_DWORD size) { - if (!client_data) - return -1; - FPDF_FILE* file_wrapper = (FPDF_FILE*)client_data; - if (fseek(file_wrapper->file, (long)offset, SEEK_SET)) - return -1; - size_t read_size = fread(buffer, 1, size, file_wrapper->file); - return read_size == size ? 0 : -1; -} - -FPDF_RESULT Sample_WriteBlock(FPDF_LPVOID client_data, - FPDF_DWORD offset, - FPDF_LPCVOID buffer, - FPDF_DWORD size) { - if (!client_data) - return -1; - FPDF_FILE* file_wrapper = (FPDF_FILE*)client_data; - if (fseek(file_wrapper->file, (long)offset, SEEK_SET)) - return -1; - // Write data - size_t write_size = fwrite(buffer, 1, size, file_wrapper->file); - return write_size == size ? 0 : -1; -} - -FPDF_RESULT Sample_Flush(FPDF_LPVOID client_data) { - if (!client_data) - return -1; - // Flush file - fflush(((FPDF_FILE*)client_data)->file); - return 0; -} - -FPDF_RESULT Sample_Truncate(FPDF_LPVOID client_data, FPDF_DWORD size) { - return 0; -} - void PDFiumEngine::Form_EmailTo(FPDF_FORMFILLINFO* param, FPDF_FILEHANDLER* file_handler, FPDF_WIDESTRING to, @@ -876,47 +803,18 @@ FPDF_LPFILEHANDLER PDFiumEngine::Form_DownloadFromURL(FPDF_FORMFILLINFO* param, FPDF_WIDESTRING url) { - std::string url_str = - base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(url)); - - // Now should get data from url. - // For testing purpose, use data read from file - // TODO: needs the full implementation here - FILE* file = fopen(XFA_TESTFILE("downloadtest.tem"), "w"); - - FPDF_FILE* file_wrapper = new FPDF_FILE; - file_wrapper->file = file; - file_wrapper->file_handler.clientData = file_wrapper; - file_wrapper->file_handler.Flush = Sample_Flush; - file_wrapper->file_handler.GetSize = Sample_GetSize; - file_wrapper->file_handler.ReadBlock = Sample_ReadBlock; - file_wrapper->file_handler.Release = Sample_Release; - file_wrapper->file_handler.Truncate = Sample_Truncate; - file_wrapper->file_handler.WriteBlock = Sample_WriteBlock; - - return &file_wrapper->file_handler; + // NOTE: Think hard about the security implications before allowing + // a PDF file to perform this action. + return nullptr; } FPDF_FILEHANDLER* PDFiumEngine::Form_OpenFile(FPDF_FORMFILLINFO* param, int file_flag, FPDF_WIDESTRING url, const char* mode) { - std::string url_str = url ? - base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(url)) : "NULL"; - - // TODO: need to implement open file from the url - // Use a file path for the ease of testing - FILE* file = fopen(XFA_TESTFILE("tem.txt"), mode); - FPDF_FILE* file_wrapper = new FPDF_FILE; - file_wrapper->file = file; - file_wrapper->file_handler.clientData = file_wrapper; - file_wrapper->file_handler.Flush = Sample_Flush; - file_wrapper->file_handler.GetSize = Sample_GetSize; - file_wrapper->file_handler.ReadBlock = Sample_ReadBlock; - file_wrapper->file_handler.Release = Sample_Release; - file_wrapper->file_handler.Truncate = Sample_Truncate; - file_wrapper->file_handler.WriteBlock = Sample_WriteBlock; - return &file_wrapper->file_handler; + // NOTE: Think hard about the security implications before allowing + // a PDF file to perform this action. + return nullptr; } void PDFiumEngine::Form_GotoURL(FPDF_FORMFILLINFO* param, @@ -1453,7 +1351,7 @@ FPDF_CloseDocument(temp_doc); PDFiumMemBufferFileRead file_read(buffer.data(), buffer.size()); - temp_doc = FPDF_LoadCustomDocument(&file_read, NULL); + temp_doc = FPDF_LoadCustomDocument(&file_read, nullptr); FPDF_BOOL imported = FPDF_ImportPages(output_doc, temp_doc, "1", i); FPDF_CloseDocument(temp_doc); @@ -2338,7 +2236,7 @@ } pp::VarArray PDFiumEngine::GetBookmarks() { - pp::VarDictionary dict = TraverseBookmarks(doc_, NULL, 0); + pp::VarDictionary dict = TraverseBookmarks(doc_, nullptr, 0); // The root bookmark contains no useful information. return pp::VarArray(dict.Get(pp::Var("children"))); } @@ -2728,7 +2626,8 @@ if (!doc_ || !form_) return false; - if (static_cast<int>(pages_.size()) > index && pages_[index]->available()) + const int num_pages = static_cast<int>(pages_.size()); + if (index < num_pages && pages_[index]->available()) return true; if (!FPDFAvail_IsPageAvail(fpdf_availability_, index, &download_hints_)) { @@ -2743,7 +2642,7 @@ return false; } - if (static_cast<int>(pages_.size()) > index) + if (index < num_pages) pages_[index]->set_available(true); if (!default_page_size_.GetArea()) default_page_size_ = GetPageSize(index); @@ -2776,7 +2675,7 @@ ProgressivePaint progressive; progressive.rect = dirty; progressive.page_index = page_index; - progressive.bitmap = NULL; + progressive.bitmap = nullptr; progressive.painted_ = false; progressive_paints_.push_back(progressive); return progressive_paints_.size() - 1; @@ -2942,7 +2841,7 @@ int page_index = progressive_paints_[progressive_index].page_index; pp::Rect dirty_in_screen = progressive_paints_[progressive_index].rect; - void* region = NULL; + void* region = nullptr; int stride; GetRegion(dirty_in_screen.point(), image_data, ®ion, &stride); @@ -3007,7 +2906,7 @@ int stride; GetRegion(rect.point(), image_data, ®ion, &stride); if (!region) - return NULL; + return nullptr; return FPDFBitmap_CreateEx( rect.width(), rect.height(), FPDFBitmap_BGRx, region, stride); } @@ -3386,7 +3285,7 @@ if (image_data->is_null()) { DCHECK(plugin_size_.IsEmpty()); *stride = 0; - *region = NULL; + *region = nullptr; return; } char* buffer = static_cast<char*>(image_data->data()); @@ -3396,7 +3295,7 @@ // TODO: update this when we support BIDI and scrollbars can be on the left. if (!buffer || !pp::Rect(page_offset_, plugin_size_).Contains(offset_location)) { - *region = NULL; + *region = nullptr; return; } @@ -3518,7 +3417,7 @@ int page_index) { PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); if (page_index < 0 || page_index >= static_cast<int>(engine->pages_.size())) - return NULL; + return nullptr; return engine->pages_[page_index]->GetPage(); } @@ -3531,7 +3430,7 @@ index = engine->GetMostVisiblePage(); if (index == -1) { NOTREACHED(); - return NULL; + return nullptr; } } @@ -3835,7 +3734,7 @@ int page_number, const RenderingSettings& settings, HDC dc) { - FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, buffer_size, NULL); + FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, buffer_size, nullptr); if (!doc) return false; FPDF_PAGE page = FPDF_LoadPage(doc, page_number); @@ -3907,7 +3806,8 @@ int page_number, const RenderingSettings& settings, void* bitmap_buffer) { - FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, pdf_buffer_size, NULL); + FPDF_DOCUMENT doc = + FPDF_LoadMemDocument(pdf_buffer, pdf_buffer_size, nullptr); if (!doc) return false; FPDF_PAGE page = FPDF_LoadPage(doc, page_number); @@ -3941,7 +3841,7 @@ int buffer_size, int* page_count, double* max_page_width) { - FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, buffer_size, NULL); + FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, buffer_size, nullptr); if (!doc) return false; int page_count_local = FPDF_GetPageCount(doc); @@ -3969,7 +3869,8 @@ int page_number, double* width, double* height) { - FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, pdf_buffer_size, NULL); + FPDF_DOCUMENT doc = + FPDF_LoadMemDocument(pdf_buffer, pdf_buffer_size, nullptr); if (!doc) return false; bool success = FPDF_GetPageSizeByIndex(doc, page_number, width, height) != 0;
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc index deccb5b4..41bcd9c7 100644 --- a/pdf/pdfium/pdfium_page.cc +++ b/pdf/pdfium/pdfium_page.cc
@@ -317,7 +317,7 @@ FPDF_LINK link = FPDFLink_GetLinkAtPoint(GetPage(), new_x, new_y); int control = - FPDPage_HasFormFieldAtPoint(engine_->form(), GetPage(), new_x, new_y); + FPDFPage_HasFormFieldAtPoint(engine_->form(), GetPage(), new_x, new_y); // If there is a control and link at the same point, figure out their z-order // to determine which is on top.
diff --git a/printing/metafile_skia_wrapper.cc b/printing/metafile_skia_wrapper.cc index 016b68d..df0e926 100644 --- a/printing/metafile_skia_wrapper.cc +++ b/printing/metafile_skia_wrapper.cc
@@ -32,7 +32,7 @@ SkMetaData& meta = skia::GetMetaData(canvas); SkRefCnt* value; if (!meta.findRefCnt(kMetafileKey, &value) || !value) - return NULL; + return nullptr; return static_cast<MetafileSkiaWrapper*>(value)->metafile_; }
diff --git a/remoting/android/client_java_tmpl.gni b/remoting/android/client_java_tmpl.gni index b2d9aca..3d1e6e5f 100644 --- a/remoting/android/client_java_tmpl.gni +++ b/remoting/android/client_java_tmpl.gni
@@ -36,6 +36,7 @@ "//remoting/android/java/src/org/chromium/chromoting/DesktopView.java", "//remoting/android/java/src/org/chromium/chromoting/DummyActivityLifecycleListener.java", "//remoting/android/java/src/org/chromium/chromoting/DummyClientExtension.java", + "//remoting/android/java/src/org/chromium/chromoting/help/CreditsActivity.java", "//remoting/android/java/src/org/chromium/chromoting/help/HelpActivity.java", "//remoting/android/java/src/org/chromium/chromoting/help/HelpAndFeedbackBasic.java", "//remoting/android/java/src/org/chromium/chromoting/help/HelpAndFeedback.java",
diff --git a/remoting/android/java/AndroidManifest.xml.jinja2 b/remoting/android/java/AndroidManifest.xml.jinja2 index d330d6c..9655d85e 100644 --- a/remoting/android/java/AndroidManifest.xml.jinja2 +++ b/remoting/android/java/AndroidManifest.xml.jinja2
@@ -61,5 +61,6 @@ android:configChanges="orientation|screenSize" android:uiOptions="splitActionBarWhenNarrow"> </activity> + <activity android:name="org.chromium.chromoting.help.CreditsActivity"/> </application> </manifest>
diff --git a/remoting/android/java/res/layout/credits.xml b/remoting/android/java/res/layout/credits.xml new file mode 100644 index 0000000..eb1215d --- /dev/null +++ b/remoting/android/java/res/layout/credits.xml
@@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/colorPrimary" + android:elevation="4dp"/> + <WebView android:id="@+id/web_view" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> +</LinearLayout>
diff --git a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java index f53926c..11aa445 100644 --- a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java +++ b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
@@ -133,9 +133,7 @@ Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT); intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, new String[] { ACCOUNT_TYPE }); - if (intent.resolveActivity(getPackageManager()) != null) { - startActivity(intent); - } + ChromotingUtil.startActivitySafely(Chromoting.this, intent); finish(); } });
diff --git a/remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java b/remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java index 0f26ea7..3d2bb492 100644 --- a/remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java +++ b/remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java
@@ -4,15 +4,19 @@ package org.chromium.chromoting; +import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.Log; /** Utility methods for chromoting code. */ public abstract class ChromotingUtil { @@ -68,4 +72,25 @@ throw new Resources.NotFoundException("Attribute not a color."); } } + + /** + * Starts a new Activity only if the system can resolve the given Intent. Useful for implicit + * intents where the system might not have an application that can handle it. + * @param context The parent context. + * @param intent The (implicit) intent to launch. + * @return True if the intent was resolved. + */ + public static boolean startActivitySafely(Context context, Intent intent) { + if (intent.resolveActivity(context.getPackageManager()) == null) { + Log.w(TAG, "Unable to resolve activity for: %s", intent); + return false; + } + context.startActivity(intent); + return true; + } + + /** Launches an external web browser or application. */ + public static boolean openUrl(Activity parentActivity, Uri uri) { + return startActivitySafely(parentActivity, new Intent(Intent.ACTION_VIEW, uri)); + } }
diff --git a/remoting/android/java/src/org/chromium/chromoting/help/CreditsActivity.java b/remoting/android/java/src/org/chromium/chromoting/help/CreditsActivity.java new file mode 100644 index 0000000..0fda299 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/help/CreditsActivity.java
@@ -0,0 +1,59 @@ +// 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. + +package org.chromium.chromoting.help; + +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import org.chromium.chromoting.ChromotingUtil; +import org.chromium.chromoting.R; + +/** + * The Activity for showing the Credits screen. + */ +public class CreditsActivity extends AppCompatActivity { + private static final String CREDITS_URL = "file:///android_res/raw/credits.html"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.credits); + WebView webView = (WebView) findViewById(R.id.web_view); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(getString(R.string.actionbar_help_title)); + + webView.getSettings().setJavaScriptEnabled(true); + webView.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + // There are no internal links in the Credits page, so open any links in a + // Web browser. + ChromotingUtil.openUrl(CreditsActivity.this, Uri.parse(url)); + return true; + } + }); + webView.loadUrl(CREDITS_URL); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } +}
diff --git a/remoting/android/java/src/org/chromium/chromoting/help/HelpActivity.java b/remoting/android/java/src/org/chromium/chromoting/help/HelpActivity.java index 7c79f97..8026e3c2 100644 --- a/remoting/android/java/src/org/chromium/chromoting/help/HelpActivity.java +++ b/remoting/android/java/src/org/chromium/chromoting/help/HelpActivity.java
@@ -27,6 +27,7 @@ import android.webkit.WebViewClient; import org.chromium.base.Log; +import org.chromium.chromoting.ChromotingUtil; import org.chromium.chromoting.R; import org.chromium.ui.UiUtils; @@ -37,7 +38,6 @@ private static final String TAG = "Chromoting"; private static final String PLAY_STORE_URL = "market://details?id="; - private static final String CREDITS_URL = "file:///android_res/raw/credits.html"; private static final String FEEDBACK_PACKAGE = "com.google.android.gms"; @@ -56,24 +56,12 @@ */ private static Bitmap sScreenshot; - /** WebView used to display help content and credits. */ + /** WebView used to display help content. */ private WebView mWebView; /** Constant used to send the feedback parcel to the system feedback service. */ private static final int SEND_FEEDBACK_INFO = Binder.FIRST_CALL_TRANSACTION; - /** Launches an external web browser or application. */ - private void openUrl(String url) { - Uri uri = Uri.parse(url); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - - // Verify that the device can launch an application for this intent, otherwise - // startActivity() may crash the application. - if (intent.resolveActivity(getPackageManager()) != null) { - startActivity(intent); - } - } - private void sendFeedback() { Intent intent = new Intent(Intent.ACTION_BUG_REPORT); intent.setComponent(new ComponentName(FEEDBACK_PACKAGE, FEEDBACK_CLASS)); @@ -148,13 +136,14 @@ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // Make sure any links to other websites open up in an external browser. - String host = Uri.parse(url).getHost(); + Uri uri = Uri.parse(url); + String host = uri.getHost(); // Note that |host| might be null, so allow for this in the test for equality. if (initialHost.equals(host)) { return false; } - openUrl(url); + ChromotingUtil.openUrl(HelpActivity.this, uri); return true; } }); @@ -179,11 +168,11 @@ return true; } if (id == R.id.actionbar_play_store) { - openUrl(PLAY_STORE_URL + getPackageName()); + ChromotingUtil.openUrl(this, Uri.parse(PLAY_STORE_URL + getPackageName())); return true; } if (id == R.id.actionbar_credits) { - mWebView.loadUrl(CREDITS_URL); + startActivity(new Intent(this, CreditsActivity.class)); return true; } return super.onOptionsItemSelected(item);
diff --git a/remoting/base/chromium_url_request.cc b/remoting/base/chromium_url_request.cc index 36dd7be9..073f6ac6 100644 --- a/remoting/base/chromium_url_request.cc +++ b/remoting/base/chromium_url_request.cc
@@ -12,8 +12,18 @@ ChromiumUrlRequest::ChromiumUrlRequest( scoped_refptr<net::URLRequestContextGetter> url_context, + UrlRequest::Type type, const std::string& url) { - url_fetcher_ = net::URLFetcher::Create(GURL(url), net::URLFetcher::GET, this); + net::URLFetcher::RequestType request_type = net::URLFetcher::GET; + switch (type) { + case Type::GET: + request_type = net::URLFetcher::GET; + break; + case Type::POST: + request_type = net::URLFetcher::POST; + break; + } + url_fetcher_ = net::URLFetcher::Create(GURL(url), request_type, this); url_fetcher_->SetRequestContext(url_context.get()); } @@ -23,6 +33,11 @@ url_fetcher_->AddExtraRequestHeader(value); } +void ChromiumUrlRequest::SetPostData(const std::string& content_type, + const std::string& data) { + url_fetcher_->SetUploadData(content_type, data); +} + void ChromiumUrlRequest::Start(const OnResultCallback& on_result_callback) { DCHECK(!on_result_callback.is_null()); DCHECK(on_result_callback_.is_null()); @@ -53,8 +68,9 @@ ChromiumUrlRequestFactory::~ChromiumUrlRequestFactory() {} scoped_ptr<UrlRequest> ChromiumUrlRequestFactory::CreateUrlRequest( + UrlRequest::Type type, const std::string& url) { - return make_scoped_ptr(new ChromiumUrlRequest(url_context_, url)); + return make_scoped_ptr(new ChromiumUrlRequest(url_context_, type, url)); } } // namespace remoting
diff --git a/remoting/base/chromium_url_request.h b/remoting/base/chromium_url_request.h index fe842c7..9cca077 100644 --- a/remoting/base/chromium_url_request.h +++ b/remoting/base/chromium_url_request.h
@@ -22,11 +22,14 @@ class ChromiumUrlRequest : public UrlRequest, public net::URLFetcherDelegate { public: ChromiumUrlRequest(scoped_refptr<net::URLRequestContextGetter> url_context, + UrlRequest::Type type, const std::string& url); ~ChromiumUrlRequest() override; // UrlRequest interface. void AddHeader(const std::string& value) override; + void SetPostData(const std::string& content_type, + const std::string& data) override; void Start(const OnResultCallback& on_result_callback) override; private: @@ -44,7 +47,8 @@ ~ChromiumUrlRequestFactory() override; // UrlRequestFactory interface. - scoped_ptr<UrlRequest> CreateUrlRequest(const std::string& url) override; + scoped_ptr<UrlRequest> CreateUrlRequest(UrlRequest::Type type, + const std::string& url) override; private: scoped_refptr<net::URLRequestContextGetter> url_context_;
diff --git a/remoting/base/url_request.h b/remoting/base/url_request.h index ab1d674..bcb1b642 100644 --- a/remoting/base/url_request.h +++ b/remoting/base/url_request.h
@@ -15,6 +15,11 @@ // Abstract interface for URL requests. class UrlRequest { public: + enum class Type { + GET, + POST, + }; + struct Result { Result() = default; Result(int status, std::string response_body) @@ -41,6 +46,10 @@ // Adds an HTTP header to the request. Has no effect if called after Start(). virtual void AddHeader(const std::string& value) = 0; + // Sets data to be sent for POST requests. + virtual void SetPostData(const std::string& content_type, + const std::string& post_data) = 0; + // Sends a request to the server. |on_response_callback| will be called to // return result of the request. virtual void Start(const OnResultCallback& on_result_callback) = 0; @@ -50,7 +59,8 @@ class UrlRequestFactory { public: virtual ~UrlRequestFactory() {} - virtual scoped_ptr<UrlRequest> CreateUrlRequest(const std::string& url) = 0; + virtual scoped_ptr<UrlRequest> CreateUrlRequest(UrlRequest::Type type, + const std::string& url) = 0; }; } // namespace remoting
diff --git a/remoting/client/plugin/pepper_url_request.cc b/remoting/client/plugin/pepper_url_request.cc index 43322d482a..31f3ef8e 100644 --- a/remoting/client/plugin/pepper_url_request.cc +++ b/remoting/client/plugin/pepper_url_request.cc
@@ -15,13 +15,21 @@ namespace remoting { PepperUrlRequest::PepperUrlRequest(pp::InstanceHandle pp_instance, + UrlRequest::Type type, const std::string& url) : request_info_(pp_instance), url_loader_(pp_instance), url_(url), callback_factory_(this) { - request_info_.SetMethod("GET"); request_info_.SetURL(url); + switch (type) { + case Type::GET: + request_info_.SetMethod("GET"); + break; + case Type::POST: + request_info_.SetMethod("POST"); + break; + } } PepperUrlRequest::~PepperUrlRequest() {} @@ -30,6 +38,12 @@ headers_ += value + "\n\r"; } +void PepperUrlRequest::SetPostData(const std::string& content_type, + const std::string& data) { + AddHeader("Content-Type: " + content_type); + request_info_.AppendDataToBody(data.data(), data.length()); +} + void PepperUrlRequest::Start(const OnResultCallback& on_result_callback) { on_result_callback_ = on_result_callback; @@ -94,8 +108,9 @@ PepperUrlRequestFactory::~PepperUrlRequestFactory() {} scoped_ptr<UrlRequest> PepperUrlRequestFactory::CreateUrlRequest( + UrlRequest::Type type, const std::string& url) { - return make_scoped_ptr(new PepperUrlRequest(pp_instance_, url)); + return make_scoped_ptr(new PepperUrlRequest(pp_instance_, type, url)); } } // namespace remoting
diff --git a/remoting/client/plugin/pepper_url_request.h b/remoting/client/plugin/pepper_url_request.h index 5bacef9..67b299de 100644 --- a/remoting/client/plugin/pepper_url_request.h +++ b/remoting/client/plugin/pepper_url_request.h
@@ -17,11 +17,15 @@ // UrlRequest implementation that uses URLLoader provided by Pepper. class PepperUrlRequest : public UrlRequest { public: - PepperUrlRequest(pp::InstanceHandle pp_instance, const std::string& url); + PepperUrlRequest(pp::InstanceHandle pp_instance, + UrlRequest::Type type, + const std::string& url); ~PepperUrlRequest() override; // UrlRequest interface. void AddHeader(const std::string& value) override; + void SetPostData(const std::string& content_type, + const std::string& data) override; void Start(const OnResultCallback& on_result_callback) override; private: @@ -50,7 +54,8 @@ ~PepperUrlRequestFactory() override; // UrlRequestFactory interface. - scoped_ptr<UrlRequest> CreateUrlRequest(const std::string& url) override; + scoped_ptr<UrlRequest> CreateUrlRequest(UrlRequest::Type type, + const std::string& url) override; private: pp::InstanceHandle pp_instance_;
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn index 4316326..1d24f31 100644 --- a/remoting/protocol/BUILD.gn +++ b/remoting/protocol/BUILD.gn
@@ -103,6 +103,7 @@ "connection_tester.h", "connection_unittest.cc", "content_description_unittest.cc", + "http_ice_config_request_unittest.cc", "ice_transport_unittest.cc", "input_event_tracker_unittest.cc", "input_filter_unittest.cc",
diff --git a/remoting/protocol/http_ice_config_request.cc b/remoting/protocol/http_ice_config_request.cc new file mode 100644 index 0000000..1ae6414 --- /dev/null +++ b/remoting/protocol/http_ice_config_request.cc
@@ -0,0 +1,210 @@ +// 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 "remoting/protocol/http_ice_config_request.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/json/json_reader.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/values.h" +#include "net/base/url_util.h" +#include "remoting/protocol/ice_config.h" + +namespace remoting { +namespace protocol { + +namespace { + +// Ensure ICE config is correct at least one hour after session starts. +const int kMinimumConfigLifetimeSeconds = 3600; + +// See draft-petithuguenin-behave-turn-uris-01. +const int kDefaultStunTurnPort = 3478; +const int kDefaultTurnsPort = 5349; + +bool ParseLifetime(const std::string& string, base::TimeDelta* result) { + double seconds = 0; + if (!base::EndsWith(string, "s", base::CompareCase::INSENSITIVE_ASCII) || + !base::StringToDouble(string.substr(0, string.size() - 1), &seconds)) { + return false; + } + *result = base::TimeDelta::FromSecondsD(seconds); + return true; +} + +// Parses url in form of <stun|turn|turns>:<host>[:<port>][?transport=<udp|tcp>] +// and adds an entry to the |config|. +bool AddServerToConfig(std::string url, + const std::string& username, + const std::string& password, + IceConfig* config) { + cricket::ProtocolType turn_transport_type = cricket::PROTO_LAST; + + const char kTcpTransportSuffix[] = "?transport=tcp"; + const char kUdpTransportSuffix[] = "?transport=udp"; + if (base::EndsWith(url, kTcpTransportSuffix, + base::CompareCase::INSENSITIVE_ASCII)) { + turn_transport_type = cricket::PROTO_TCP; + url.resize(url.size() - strlen(kTcpTransportSuffix)); + } else if (base::EndsWith(url, kUdpTransportSuffix, + base::CompareCase::INSENSITIVE_ASCII)) { + turn_transport_type = cricket::PROTO_UDP; + url.resize(url.size() - strlen(kUdpTransportSuffix)); + } + + size_t colon_pos = url.find(':'); + if (colon_pos == std::string::npos) + return false; + + std::string protocol = url.substr(0, colon_pos); + + std::string host; + int port; + if (!net::ParseHostAndPort(url.substr(colon_pos + 1), &host, &port)) + return false; + + if (protocol == "stun") { + if (port == -1) + port = kDefaultStunTurnPort; + config->stun_servers.push_back(rtc::SocketAddress(host, port)); + } else if (protocol == "turn") { + if (port == -1) + port = kDefaultStunTurnPort; + if (turn_transport_type == cricket::PROTO_LAST) + turn_transport_type = cricket::PROTO_UDP; + config->turn_servers.push_back(cricket::RelayServerConfig( + host, port, username, password, turn_transport_type, false)); + } else if (protocol == "turns") { + if (port == -1) + port = kDefaultTurnsPort; + if (turn_transport_type == cricket::PROTO_LAST) + turn_transport_type = cricket::PROTO_TCP; + config->turn_servers.push_back(cricket::RelayServerConfig( + host, port, username, password, turn_transport_type, true)); + } else { + return false; + } + + return true; +} + +} // namespace + +HttpIceConfigRequest::HttpIceConfigRequest( + UrlRequestFactory* url_request_factory, + const std::string& url) + : url_(url) { + url_request_ = + url_request_factory->CreateUrlRequest(UrlRequest::Type::POST, url_); + url_request_->SetPostData("application/json", ""); +} + +HttpIceConfigRequest::~HttpIceConfigRequest() {} + +void HttpIceConfigRequest::Send(const OnIceConfigCallback& callback) { + DCHECK(on_ice_config_callback_.is_null()); + DCHECK(!callback.is_null()); + + on_ice_config_callback_ = callback; + url_request_->Start( + base::Bind(&HttpIceConfigRequest::OnResponse, base::Unretained(this))); +} + +void HttpIceConfigRequest::OnResponse(const UrlRequest::Result& result) { + DCHECK(!on_ice_config_callback_.is_null()); + + if (!result.success) { + LOG(ERROR) << "Failed to fetch " << url_; + base::ResetAndReturn(&on_ice_config_callback_).Run(IceConfig()); + return; + } + + if (result.status != 200) { + LOG(ERROR) << "Received status code " << result.status << " from " << url_ + << ": " << result.response_body; + base::ResetAndReturn(&on_ice_config_callback_).Run(IceConfig()); + return; + } + + scoped_ptr<base::Value> json = base::JSONReader::Read(result.response_body); + base::DictionaryValue* dictionary = nullptr; + base::ListValue* ice_servers_list = nullptr; + if (!json || !json->GetAsDictionary(&dictionary) || + !dictionary->GetList("iceServers", &ice_servers_list)) { + LOG(ERROR) << "Received invalid response from " << url_ << ": " + << result.response_body; + base::ResetAndReturn(&on_ice_config_callback_).Run(IceConfig()); + return; + } + + IceConfig ice_config; + + // Parse lifetimeDuration field. + std::string lifetime_str; + base::TimeDelta lifetime; + if (!dictionary->GetString("lifetimeDuration", &lifetime_str) || + !ParseLifetime(lifetime_str, &lifetime)) { + LOG(ERROR) << "Received invalid lifetimeDuration value: " << lifetime_str; + + // If the |lifetimeDuration| field is missing or cannot be parsed then mark + // the config as expired so it will refreshed for the next session. + ice_config.expiration_time = base::Time::Now(); + } else { + ice_config.expiration_time = + base::Time::Now() + lifetime - + base::TimeDelta::FromSeconds(kMinimumConfigLifetimeSeconds); + } + + // Parse iceServers list and store them in |ice_config|. + bool errors_found = false; + for (base::Value* server : *ice_servers_list) { + base::DictionaryValue* server_dict; + if (!server->GetAsDictionary(&server_dict)) { + errors_found = true; + continue; + } + + base::ListValue* urls_list = nullptr; + if (!server_dict->GetList("urls", &urls_list)) { + errors_found = true; + continue; + } + + std::string username; + server_dict->GetString("username", &username); + + std::string password; + server_dict->GetString("credential", &password); + + for (base::Value* url : *urls_list) { + std::string url_str; + if (!url->GetAsString(&url_str)) { + errors_found = true; + continue; + } + if (!AddServerToConfig(url_str, username, password, &ice_config)) { + LOG(ERROR) << "Invalid ICE server URL: " << url_str; + } + } + } + + if (errors_found) { + LOG(ERROR) << "Received ICE config from the server that contained errors: " + << result.response_body; + } + + // If there are no STUN or no TURN servers then mark the config as expired so + // it will refreshed for the next session. + if (errors_found || ice_config.stun_servers.empty() || + ice_config.turn_servers.empty()) { + ice_config.expiration_time = base::Time::Now(); + } + + base::ResetAndReturn(&on_ice_config_callback_).Run(ice_config); +} + +} // namespace protocol +} // namespace remoting
diff --git a/remoting/protocol/http_ice_config_request.h b/remoting/protocol/http_ice_config_request.h new file mode 100644 index 0000000..3610e9288 --- /dev/null +++ b/remoting/protocol/http_ice_config_request.h
@@ -0,0 +1,43 @@ +// 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 REMOTING_PROTOCOL_HTTP_ICE_CONFIG_REQUEST_H_ +#define REMOTING_PROTOCOL_HTTP_ICE_CONFIG_REQUEST_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "remoting/base/url_request.h" +#include "remoting/protocol/ice_config_request.h" + +namespace remoting { +namespace protocol { + +// IceConfigRequest that fetches IceConfig from using HTTP. If the config has +// been fetched succesfully but some parts couldn't be parsed then the returned +// config contains all entries that were parsed successfully and +// |expiration_time| is set to Now, i.e. the config is considered expired. +class HttpIceConfigRequest : public IceConfigRequest { + public: + HttpIceConfigRequest(UrlRequestFactory* url_request_factory, + const std::string& url); + ~HttpIceConfigRequest() override; + + // IceConfigRequest interface. + void Send(const OnIceConfigCallback& callback) override; + + private: + void OnResponse(const UrlRequest::Result& result); + + std::string url_; + scoped_ptr<UrlRequest> url_request_; + OnIceConfigCallback on_ice_config_callback_; + + DISALLOW_COPY_AND_ASSIGN(HttpIceConfigRequest); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_HTTP_ICE_CONFIG_REQUEST_H_
diff --git a/remoting/protocol/http_ice_config_request_unittest.cc b/remoting/protocol/http_ice_config_request_unittest.cc new file mode 100644 index 0000000..65cb9a4 --- /dev/null +++ b/remoting/protocol/http_ice_config_request_unittest.cc
@@ -0,0 +1,211 @@ +// 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 "remoting/protocol/http_ice_config_request.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "remoting/base/url_request.h" +#include "remoting/protocol/ice_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace remoting { +namespace protocol { + +namespace { + +class FakeUrlRequest : public UrlRequest { + public: + FakeUrlRequest(const Result& result) : result_(result) {} + ~FakeUrlRequest() override {} + + // UrlRequest interface. + void AddHeader(const std::string& value) override { NOTREACHED(); } + + void SetPostData(const std::string& content_type, + const std::string& post_data) override { + EXPECT_EQ("application/json", content_type); + EXPECT_EQ("", post_data); + } + + void Start(const OnResultCallback& on_result_callback) override { + on_result_callback.Run(result_); + } + + private: + Result result_; +}; + +class FakeUrlRequestFactory : public UrlRequestFactory { + public: + FakeUrlRequestFactory() {} + ~FakeUrlRequestFactory() override {} + + void SetResult(const std::string& url, const UrlRequest::Result& result) { + results_[url] = result; + } + + // UrlRequestFactory interface. + scoped_ptr<UrlRequest> CreateUrlRequest(UrlRequest::Type type, + const std::string& url) override { + EXPECT_EQ(UrlRequest::Type::POST, type); + CHECK(results_.count(url)); + return make_scoped_ptr(new FakeUrlRequest(results_[url])); + } + + std::map<std::string, UrlRequest::Result> results_; +}; + +bool operator==(const cricket::ProtocolAddress& a, + const cricket::ProtocolAddress& b) { + return a.address == b.address && a.proto == b.proto && a.secure == b.secure; +} +bool operator==(const cricket::RelayServerConfig& a, + const cricket::RelayServerConfig& b) { + if (a.ports.size() != b.ports.size()) + return false; + for (size_t i = 0; i < a.ports.size(); ++i) { + if (!(a.ports[i] == b.ports[i])) + return false; + } + return a.type == b.type && + a.credentials.username == b.credentials.username && + a.credentials.password == b.credentials.password; +} + +} // namespace + +static const char kTestUrl[] = "http://host/ice_config"; + +class HttpIceConfigRequestTest : public testing::Test { + public: + void OnResult(const IceConfig& config) { + received_config_ = make_scoped_ptr(new IceConfig(config)); + } + + protected: + FakeUrlRequestFactory url_request_factory_; + scoped_ptr<HttpIceConfigRequest> request_; + scoped_ptr<IceConfig> received_config_; +}; + +TEST_F(HttpIceConfigRequestTest, Parse) { + const char kTestResponse[] = + "{" + " \"lifetimeDuration\": \"43200.000s\"," + " \"iceServers\": [" + " {" + " \"urls\": [" + " \"turn:8.8.8.8:19234\"," + " \"turn:[2001:4860:4860::8888]:333\"," + " \"turn:[2001:4860:4860::8888]\"," + " \"turn:[2001:4860:4860::8888]:333?transport=tcp\"," + " \"turns:the_server.com\"," + " \"turns:the_server.com?transport=udp\"" + " ]," + " \"username\": \"123\"," + " \"credential\": \"abc\"" + " }," + " {" + " \"urls\": [" + " \"stun:stun_server.com:18344\"," + " \"stun:1.2.3.4\"" + " ]" + " }" + " ]" + "}"; + url_request_factory_.SetResult(kTestUrl, + UrlRequest::Result(200, kTestResponse)); + request_.reset(new HttpIceConfigRequest(&url_request_factory_, kTestUrl)); + request_->Send( + base::Bind(&HttpIceConfigRequestTest::OnResult, base::Unretained(this))); + ASSERT_FALSE(received_config_->is_null()); + + // lifetimeDuration in the config is set to 12 hours. HttpIceConfigRequest + // substracts 1 hour. Verify that |expiration_time| is in the rage from 10 to + // 12 hours from now. + EXPECT_TRUE(base::Time::Now() + base::TimeDelta::FromHours(10) < + received_config_->expiration_time); + EXPECT_TRUE(received_config_->expiration_time < + base::Time::Now() + base::TimeDelta::FromHours(12)); + + EXPECT_EQ(6U, received_config_->turn_servers.size()); + EXPECT_TRUE(cricket::RelayServerConfig("8.8.8.8", 19234, "123", "abc", + cricket::PROTO_UDP, false) == + received_config_->turn_servers[0]); + EXPECT_TRUE(cricket::RelayServerConfig("2001:4860:4860::8888", 333, "123", + "abc", cricket::PROTO_UDP, false) == + received_config_->turn_servers[1]); + EXPECT_TRUE(cricket::RelayServerConfig("2001:4860:4860::8888", 3478, "123", + "abc", cricket::PROTO_UDP, false) == + received_config_->turn_servers[2]); + EXPECT_TRUE(cricket::RelayServerConfig("2001:4860:4860::8888", 333, "123", + "abc", cricket::PROTO_TCP, false) == + received_config_->turn_servers[3]); + EXPECT_TRUE(cricket::RelayServerConfig("the_server.com", 5349, "123", "abc", + cricket::PROTO_TCP, true) == + received_config_->turn_servers[4]); + EXPECT_TRUE(cricket::RelayServerConfig("the_server.com", 5349, "123", "abc", + cricket::PROTO_UDP, true) == + received_config_->turn_servers[5]); + + EXPECT_EQ(2U, received_config_->stun_servers.size()); + EXPECT_EQ(rtc::SocketAddress("stun_server.com", 18344), + received_config_->stun_servers[0]); + EXPECT_EQ(rtc::SocketAddress("1.2.3.4", 3478), + received_config_->stun_servers[1]); +} + +// Verify that we can still proceed if some servers cannot be parsed. +TEST_F(HttpIceConfigRequestTest, ParsePartiallyInvalid) { + const char kTestResponse[] = + "{" + " \"lifetimeDuration\": \"43200.000s\"," + " \"iceServers\": [" + " {" + " \"urls\": [" + " \"InvalidURL\"," + " \"turn:[2001:4860:4860::8888]:333\"" + " ]," + " \"username\": \"123\"," + " \"credential\": \"abc\"" + " }," + " \"42\"" + " ]" + "}"; + url_request_factory_.SetResult(kTestUrl, + UrlRequest::Result(200, kTestResponse)); + request_.reset(new HttpIceConfigRequest(&url_request_factory_, kTestUrl)); + request_->Send( + base::Bind(&HttpIceConfigRequestTest::OnResult, base::Unretained(this))); + ASSERT_FALSE(received_config_->is_null()); + + // Config should be already expired because it couldn't be parsed. + EXPECT_TRUE(received_config_->expiration_time < base::Time::Now()); + + EXPECT_EQ(1U, received_config_->turn_servers.size()); + EXPECT_TRUE(cricket::RelayServerConfig("2001:4860:4860::8888", 333, "123", + "abc", cricket::PROTO_UDP, false) == + received_config_->turn_servers[0]); +} + +TEST_F(HttpIceConfigRequestTest, NotParseable) { + url_request_factory_.SetResult(kTestUrl, + UrlRequest::Result(200, "ERROR")); + request_.reset(new HttpIceConfigRequest(&url_request_factory_, kTestUrl)); + request_->Send( + base::Bind(&HttpIceConfigRequestTest::OnResult, base::Unretained(this))); + EXPECT_TRUE(received_config_->is_null()); +} + +TEST_F(HttpIceConfigRequestTest, FailedRequest) { + url_request_factory_.SetResult(kTestUrl, UrlRequest::Result::Failed()); + request_.reset(new HttpIceConfigRequest(&url_request_factory_, kTestUrl)); + request_->Send( + base::Bind(&HttpIceConfigRequestTest::OnResult, base::Unretained(this))); + EXPECT_TRUE(received_config_->is_null()); +} + +} // namespace protocol +} // namespace remoting
diff --git a/remoting/protocol/port_allocator.cc b/remoting/protocol/port_allocator.cc index cac6725..3f6684a 100644 --- a/remoting/protocol/port_allocator.cc +++ b/remoting/protocol/port_allocator.cc
@@ -66,7 +66,7 @@ set_flags(flags); SetPortRange(network_settings.port_range.min_port, - network_settings.port_range.max_port); + network_settings.port_range.max_port); } PortAllocator::~PortAllocator() {} @@ -102,26 +102,36 @@ void PortAllocatorSession::OnIceConfig(const IceConfig& ice_config) { ice_config_ = ice_config; + ConfigReady(GetPortConfiguration().release()); - // Creating relay sessions can take time and is done asynchronously. - // Creating stun sessions could also take time and could be done aysnc also, - // but for now is done here and added to the initial config. Note any later - // configs will have unresolved stun ips and will be discarded by the - // AllocationSequence. + if (relay_enabled() && !ice_config_.relay_servers.empty() && + !ice_config_.relay_token.empty()) { + TryCreateRelaySession(); + } +} + +scoped_ptr<cricket::PortConfiguration> +PortAllocatorSession::GetPortConfiguration() { cricket::ServerAddresses stun_servers; for (const auto& host : ice_config_.stun_servers) { stun_servers.insert(host); } - cricket::PortConfiguration* config = - new cricket::PortConfiguration(stun_servers, username(), password()); - ConfigReady(config); - TryCreateRelaySession(); + scoped_ptr<cricket::PortConfiguration> config( + new cricket::PortConfiguration(stun_servers, username(), password())); + + if (relay_enabled()) { + for (const auto& turn_server : ice_config_.turn_servers) { + config->AddRelay(turn_server); + } + } + + return config; } void PortAllocatorSession::TryCreateRelaySession() { - if (flags() & cricket::PORTALLOCATOR_DISABLE_RELAY) - return; + DCHECK(!ice_config_.relay_servers.empty()); + DCHECK(!ice_config_.relay_token.empty()); if (attempts_ == kNumRetries) { LOG(ERROR) << "PortAllocator: maximum number of requests reached; " @@ -129,16 +139,6 @@ return; } - if (ice_config_.relay_servers.empty()) { - LOG(ERROR) << "PortAllocator: no relay servers configured."; - return; - } - - if (ice_config_.relay_token.empty()){ - LOG(WARNING) << "No relay auth token found."; - return; - } - // Choose the next host to try. std::string host = ice_config_.relay_servers[attempts_ % ice_config_.relay_servers.size()]; @@ -151,7 +151,8 @@ "&password=" + net::EscapeUrlEncodedData(password(), false) + "&sn=1"; scoped_ptr<UrlRequest> url_request = - transport_context_->url_request_factory()->CreateUrlRequest(url); + transport_context_->url_request_factory()->CreateUrlRequest( + UrlRequest::Type::GET, url); url_request->AddHeader("X-Talk-Google-Relay-Auth: " + ice_config_.relay_token); url_request->AddHeader("X-Google-Relay-Auth: " + ice_config_.relay_token); @@ -179,13 +180,7 @@ LOG(WARNING) << "Received unexpected password value from relay server."; } - cricket::ServerAddresses stun_servers; - for (const auto& host : ice_config_.stun_servers) { - stun_servers.insert(host); - } - - cricket::PortConfiguration* config = new cricket::PortConfiguration( - stun_servers, map["username"], map["password"]); + scoped_ptr<cricket::PortConfiguration> config = GetPortConfiguration(); std::string relay_ip = map["relay.ip"]; std::string relay_port = map["relay.udp_port"]; @@ -200,7 +195,7 @@ config->AddRelay(relay_config); } - ConfigReady(config); + ConfigReady(config.release()); } } // namespace protocol
diff --git a/remoting/protocol/port_allocator.h b/remoting/protocol/port_allocator.h index 947173f..bf0ebff 100644 --- a/remoting/protocol/port_allocator.h +++ b/remoting/protocol/port_allocator.h
@@ -49,10 +49,25 @@ const std::string& ice_pwd); ~PortAllocatorSession() override; -private: + private: + bool relay_enabled() { + return !(flags() & cricket::PORTALLOCATOR_DISABLE_RELAY); + } + + // BasicPortAllocatorSession overrides. void GetPortConfigurations() override; + + // Callback for TransportContext::GetIceConfig(). void OnIceConfig(const IceConfig& ice_config); + + // Creates PortConfiguration that inclues STUN and TURN servers from + // |ice_config_|. + scoped_ptr<cricket::PortConfiguration> GetPortConfiguration(); + + // Attempts to allocate relay session. void TryCreateRelaySession(); + + // Result handler for UrlRequest objects in |url_requests_|. void OnSessionRequestResult(const UrlRequest::Result& result); scoped_refptr<TransportContext> transport_context_;
diff --git a/remoting/protocol/transport_context.cc b/remoting/protocol/transport_context.cc index d75e403..70c8b96 100644 --- a/remoting/protocol/transport_context.cc +++ b/remoting/protocol/transport_context.cc
@@ -11,6 +11,7 @@ #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "remoting/base/url_request.h" +#include "remoting/protocol/http_ice_config_request.h" #include "remoting/protocol/jingle_info_request.h" #include "remoting/protocol/port_allocator_factory.h" #include "third_party/webrtc/base/socketaddress.h" @@ -79,7 +80,12 @@ if (ice_config_.is_null() || base::Time::Now() > ice_config_.expiration_time) { - ice_config_request_.reset(new JingleInfoRequest(signal_strategy_)); + if (!ice_config_url_.empty()) { + ice_config_request_.reset(new HttpIceConfigRequest( + url_request_factory_.get(), ice_config_url_)); + } else { + ice_config_request_.reset(new JingleInfoRequest(signal_strategy_)); + } ice_config_request_->Send(base::Bind( &TransportContext::OnIceConfig, base::Unretained(this))); }
diff --git a/remoting/protocol/transport_context.h b/remoting/protocol/transport_context.h index a944e247..0cc62411 100644 --- a/remoting/protocol/transport_context.h +++ b/remoting/protocol/transport_context.h
@@ -42,6 +42,11 @@ const NetworkSettings& network_settings, TransportRole role); + // Enables standard TURN servers. + void UseTurn(const std::string& ice_config_url) { + ice_config_url_ = ice_config_url; + } + // Prepares fresh JingleInfo. It may be called while connection is being // negotiated to minimize the chance that the following GetIceConfig() will // be blocking. @@ -73,6 +78,8 @@ NetworkSettings network_settings_; TransportRole role_; + std::string ice_config_url_; + scoped_ptr<IceConfigRequest> ice_config_request_; IceConfig ice_config_;
diff --git a/remoting/remoting_srcs.gypi b/remoting/remoting_srcs.gypi index d54bf2bc..8d5521e 100644 --- a/remoting/remoting_srcs.gypi +++ b/remoting/remoting_srcs.gypi
@@ -119,6 +119,8 @@ 'protocol/host_stub.h', 'protocol/host_video_dispatcher.cc', 'protocol/host_video_dispatcher.h', + 'protocol/http_ice_config_request.cc', + 'protocol/http_ice_config_request.h', 'protocol/ice_config.cc', 'protocol/ice_config.h', 'protocol/ice_config_request.h',
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi index b8e49d75..5e36aa0 100644 --- a/remoting/remoting_test.gypi +++ b/remoting/remoting_test.gypi
@@ -321,6 +321,7 @@ 'protocol/connection_tester.h', 'protocol/connection_unittest.cc', 'protocol/content_description_unittest.cc', + 'protocol/http_ice_config_request_unittest.cc', 'protocol/ice_transport_unittest.cc', 'protocol/input_event_tracker_unittest.cc', 'protocol/input_filter_unittest.cc',
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h index e432174..7ce21a66 100644 --- a/skia/config/SkUserConfig.h +++ b/skia/config/SkUserConfig.h
@@ -216,6 +216,13 @@ # define SK_SUPPORT_LEGACY_GETDEVICE #endif +// Workaround for poor anisotropic mipmap quality, +// pending Skia ripmap support. +// (https://bugs.chromium.org/p/skia/issues/detail?id=4863) +#ifndef SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE +# define SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE +#endif + #ifndef SK_SUPPORT_LEGACY_REFENCODEDDATA_NOCTX # define SK_SUPPORT_LEGACY_REFENCODEDDATA_NOCTX #endif
diff --git a/sql/mojo/BUILD.gn b/sql/mojo/BUILD.gn deleted file mode 100644 index b7a3793..0000000 --- a/sql/mojo/BUILD.gn +++ /dev/null
@@ -1,62 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//mojo/public/mojo_application.gni") - -source_set("mojo") { - sources = [ - "mojo_vfs.cc", - "mojo_vfs.h", - ] - - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] - - defines = [ "SQL_IMPLEMENTATION" ] - - deps = [ - "//base", - "//base/third_party/dynamic_annotations", - "//components/filesystem/public/interfaces", - "//mojo/common", - "//mojo/platform_handle", - "//mojo/shell/public/cpp", - "//third_party/sqlite", - ] -} - -mojo_native_application("apptests") { - output_name = "sql_apptests" - - testonly = true - - # Instead of using the code in //sql/test/sql_test_base.h, we should use the - # mojo test base class. - defines = [ "MOJO_APPTEST_IMPL" ] - - sources = [ - "../connection_unittest.cc", - "../statement_unittest.cc", - "../test/paths.cc", - "../test/paths.h", - "../transaction_unittest.cc", - "sql_test_base.cc", - "sql_test_base.h", - "vfs_unittest.cc", - ] - - deps = [ - ":mojo", - "//base", - "//base/test:test_support", - "//components/filesystem/public/interfaces", - "//mojo/public/cpp/bindings", - "//mojo/shell/public/cpp:sources", - "//mojo/shell/public/cpp:test_support", - "//sql", - "//sql:redirection_header", - "//sql:test_support", - "//testing/gtest:gtest", - ] -}
diff --git a/sql/mojo/DEPS b/sql/mojo/DEPS deleted file mode 100644 index 9dd7c02..0000000 --- a/sql/mojo/DEPS +++ /dev/null
@@ -1,6 +0,0 @@ -include_rules = [ - "+components/filesystem", - "+mojo/shell", - "+mojo/public", - "+mojo/util", -]
diff --git a/sql/mojo/mojo_vfs.cc b/sql/mojo/mojo_vfs.cc deleted file mode 100644 index bd962d0..0000000 --- a/sql/mojo/mojo_vfs.cc +++ /dev/null
@@ -1,455 +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. - -#include "sql/mojo/mojo_vfs.h" - -#include <stddef.h> -#include <stdint.h> -#include <utility> - -#include "base/logging.h" -#include "base/rand_util.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/trace_event.h" -#include "components/filesystem/public/interfaces/file.mojom.h" -#include "components/filesystem/public/interfaces/file_system.mojom.h" -#include "components/filesystem/public/interfaces/types.mojom.h" -#include "mojo/public/cpp/bindings/lib/template_util.h" -#include "mojo/util/capture_util.h" -#include "third_party/sqlite/sqlite3.h" - -using mojo::Capture; - -namespace sql { - -sqlite3_vfs* GetParentVFS(sqlite3_vfs* mojo_vfs) { - return static_cast<ScopedMojoFilesystemVFS*>(mojo_vfs->pAppData)->parent_; -} - -filesystem::DirectoryPtr& GetRootDirectory(sqlite3_vfs* mojo_vfs) { - return static_cast<ScopedMojoFilesystemVFS*>(mojo_vfs->pAppData)-> - root_directory_; -} - -namespace { - -// Implementation of the sqlite3 Mojo proxying vfs. -// -// This is a bunch of C callback objects which transparently proxy sqlite3's -// filesystem reads/writes over the mojo:filesystem service. The main -// entrypoint is sqlite3_mojovfs(), which proxies all the file open/delete/etc -// operations. mojo:filesystem has support for passing a raw file descriptor -// over the IPC barrier, and most of the implementation of sqlite3_io_methods -// is derived from the default sqlite3 unix VFS and operates on the raw file -// descriptors. - -const int kMaxPathName = 512; - -// A struct which extends the base sqlite3_file to also hold on to a file -// pipe. We reinterpret_cast our sqlite3_file structs to this struct -// instead. This is "safe" because this struct is really just a slab of -// malloced memory, of which we tell sqlite how large we want with szOsFile. -struct MojoVFSFile { - // The "vtable" of our sqlite3_file "subclass". - sqlite3_file base; - - // We keep an open pipe to the File object to keep it from cleaning itself - // up. - filesystem::FilePtr file_ptr; -}; - -filesystem::FilePtr& GetFSFile(sqlite3_file* vfs_file) { - return reinterpret_cast<MojoVFSFile*>(vfs_file)->file_ptr; -} - -int MojoVFSClose(sqlite3_file* file) { - DVLOG(1) << "MojoVFSClose(*)"; - TRACE_EVENT0("sql", "MojoVFSClose"); - using filesystem::FilePtr; - filesystem::FileError error = filesystem::FileError::FAILED; - // Must call File::Close explicitly instead of just deleting the file, since - // otherwise we wouldn't have an object to wait on. - GetFSFile(file)->Close(mojo::Capture(&error)); - GetFSFile(file).WaitForIncomingResponse(); - GetFSFile(file).~FilePtr(); - return SQLITE_OK; -} - -int MojoVFSRead(sqlite3_file* sql_file, - void* buffer, - int size, - sqlite3_int64 offset) { - DVLOG(1) << "MojoVFSRead (" << size << " @ " << offset << ")"; - TRACE_EVENT0("sql", "MojoVFSRead"); - filesystem::FileError error = filesystem::FileError::FAILED; - mojo::Array<uint8_t> mojo_data; - GetFSFile(sql_file)->Read(size, offset, filesystem::Whence::FROM_BEGIN, - Capture(&error, &mojo_data)); - GetFSFile(sql_file).WaitForIncomingResponse(); - - if (error != filesystem::FileError::OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_READ; - } - - if (mojo_data.size()) - memcpy(buffer, &mojo_data.front(), mojo_data.size()); - if (mojo_data.size() == static_cast<size_t>(size)) - return SQLITE_OK; - - // We didn't read the entire buffer. Fill the rest of the buffer with zeros. - memset(reinterpret_cast<char*>(buffer) + mojo_data.size(), 0, - size - mojo_data.size()); - - return SQLITE_IOERR_SHORT_READ; -} - -int MojoVFSWrite(sqlite3_file* sql_file, - const void* buffer, - int size, - sqlite_int64 offset) { - DVLOG(1) << "MojoVFSWrite(*, " << size << ", " << offset << ")"; - TRACE_EVENT0("sql", "MojoVFSWrite"); - mojo::Array<uint8_t> mojo_data(size); - memcpy(&mojo_data.front(), buffer, size); - - filesystem::FileError error = filesystem::FileError::FAILED; - uint32_t num_bytes_written = 0; - GetFSFile(sql_file)->Write(std::move(mojo_data), offset, - filesystem::Whence::FROM_BEGIN, - Capture(&error, &num_bytes_written)); - GetFSFile(sql_file).WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_WRITE; - } - if (num_bytes_written != static_cast<uint32_t>(size)) { - NOTIMPLEMENTED(); - return SQLITE_IOERR_WRITE; - } - - return SQLITE_OK; -} - -int MojoVFSTruncate(sqlite3_file* sql_file, sqlite_int64 size) { - DVLOG(1) << "MojoVFSTruncate(*, " << size << ")"; - TRACE_EVENT0("sql", "MojoVFSTruncate"); - filesystem::FileError error = filesystem::FileError::FAILED; - GetFSFile(sql_file)->Truncate(size, Capture(&error)); - GetFSFile(sql_file).WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_TRUNCATE; - } - - return SQLITE_OK; -} - -int MojoVFSSync(sqlite3_file* sql_file, int flags) { - DVLOG(1) << "MojoVFSSync(*, " << flags << ")"; - TRACE_EVENT0("sql", "MojoVFSSync"); - filesystem::FileError error = filesystem::FileError::FAILED; - GetFSFile(sql_file)->Flush(Capture(&error)); - GetFSFile(sql_file).WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_FSYNC; - } - - return SQLITE_OK; -} - -int MojoVFSFileSize(sqlite3_file* sql_file, sqlite_int64* size) { - DVLOG(1) << "MojoVFSFileSize(*)"; - TRACE_EVENT0("sql", "MojoVFSFileSize"); - - filesystem::FileError err = filesystem::FileError::FAILED; - filesystem::FileInformationPtr file_info; - GetFSFile(sql_file)->Stat(Capture(&err, &file_info)); - GetFSFile(sql_file).WaitForIncomingResponse(); - - if (err != filesystem::FileError::OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_FSTAT; - } - - *size = file_info->size; - return SQLITE_OK; -} - -// TODO(erg): The current base::File interface isn't sufficient to handle -// sqlite's locking primitives, which are done on byte ranges in the file. (See -// "File Locking Notes" in sqlite3.c.) -int MojoVFSLock(sqlite3_file* pFile, int eLock) { - DVLOG(1) << "MojoVFSLock(*, " << eLock << ")"; - return SQLITE_OK; -} -int MojoVFSUnlock(sqlite3_file* pFile, int eLock) { - DVLOG(1) << "MojoVFSUnlock(*, " << eLock << ")"; - return SQLITE_OK; -} -int MojoVFSCheckReservedLock(sqlite3_file* pFile, int* pResOut) { - DVLOG(1) << "MojoVFSCheckReservedLock(*)"; - *pResOut = 0; - return SQLITE_OK; -} - -// TODO(erg): This is the minimal implementation to get a few tests passing; -// lots more needs to be done here. -int MojoVFSFileControl(sqlite3_file* pFile, int op, void* pArg) { - DVLOG(1) << "MojoVFSFileControl(*, " << op << ", *)"; - if (op == SQLITE_FCNTL_PRAGMA) { - // Returning NOTFOUND tells sqlite that we aren't doing any processing. - return SQLITE_NOTFOUND; - } - - return SQLITE_OK; -} - -int MojoVFSSectorSize(sqlite3_file* pFile) { - DVLOG(1) << "MojoVFSSectorSize(*)"; - // Use the default sector size. - return 0; -} - -int MojoVFSDeviceCharacteristics(sqlite3_file* pFile) { - DVLOG(1) << "MojoVFSDeviceCharacteristics(*)"; - // TODO(erg): Figure out what to return here. (This function is super spammy, - // so not leaving a NOTIMPLEMENTED().) - return 0; -} - -static sqlite3_io_methods mojo_vfs_io_methods = { - 1, /* iVersion */ - MojoVFSClose, /* xClose */ - MojoVFSRead, /* xRead */ - MojoVFSWrite, /* xWrite */ - MojoVFSTruncate, /* xTruncate */ - MojoVFSSync, /* xSync */ - MojoVFSFileSize, /* xFileSize */ - MojoVFSLock, /* xLock */ - MojoVFSUnlock, /* xUnlock */ - MojoVFSCheckReservedLock, /* xCheckReservedLock */ - MojoVFSFileControl, /* xFileControl */ - MojoVFSSectorSize, /* xSectorSize */ - MojoVFSDeviceCharacteristics, /* xDeviceCharacteristics */ -}; - -int MojoVFSOpen(sqlite3_vfs* mojo_vfs, - const char* name, - sqlite3_file* file, - int flags, - int* pOutFlags) { - DVLOG(1) << "MojoVFSOpen(*, " << name << ", *, " << flags << ")"; - TRACE_EVENT2("sql", "MojoVFSOpen", - "name", name, - "flags", flags); - int open_flags = 0; - if (flags & SQLITE_OPEN_EXCLUSIVE) { - DCHECK(flags & SQLITE_OPEN_CREATE); - open_flags = filesystem::kFlagCreate; - } else if (flags & SQLITE_OPEN_CREATE) { - DCHECK(flags & SQLITE_OPEN_READWRITE); - open_flags = filesystem::kFlagOpenAlways; - } else { - open_flags = filesystem::kFlagOpen; - } - open_flags |= filesystem::kFlagRead; - if (flags & SQLITE_OPEN_READWRITE) - open_flags |= filesystem::kFlagWrite; - if (flags & SQLITE_OPEN_DELETEONCLOSE) - open_flags |= filesystem::kDeleteOnClose; - - mojo::String mojo_name; - if (name) { - // Don't let callers open the pattern of our temporary databases. When we - // open with a null name and SQLITE_OPEN_DELETEONCLOSE, we unlink the - // database after we open it. If we create a database here, close it - // normally, and then open the same file through the other path, we could - // delete the database. - CHECK(strncmp("Temp_", name, 5) != 0); - mojo_name = name; - } else { - DCHECK(flags & SQLITE_OPEN_DELETEONCLOSE); - static int temp_number = 0; - mojo_name = base::StringPrintf("Temp_%d.db", temp_number++); - } - - // Grab the incoming file - filesystem::FilePtr file_ptr; - filesystem::FileError error = filesystem::FileError::FAILED; - GetRootDirectory(mojo_vfs)->OpenFile(mojo_name, GetProxy(&file_ptr), - open_flags, Capture(&error)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) { - // TODO(erg): Translate more of the mojo error codes into sqlite error - // codes. - return SQLITE_CANTOPEN; - } - - // Set the method table so we can be closed (and run the manual dtor call to - // match the following placement news). - file->pMethods = &mojo_vfs_io_methods; - - // |file| is actually a malloced buffer of size szOsFile. This means that we - // need to manually use placement new to construct the C++ object which owns - // the pipe to our file. - new (&GetFSFile(file)) filesystem::FilePtr(std::move(file_ptr)); - - return SQLITE_OK; -} - -int MojoVFSDelete(sqlite3_vfs* mojo_vfs, const char* filename, int sync_dir) { - DVLOG(1) << "MojoVFSDelete(*, " << filename << ", " << sync_dir << ")"; - TRACE_EVENT2("sql", "MojoVFSDelete", - "name", filename, - "sync_dir", sync_dir); - // TODO(erg): The default windows sqlite VFS has retry code to work around - // antivirus software keeping files open. We'll probably have to do something - // like that in the far future if we ever support Windows. - filesystem::FileError error = filesystem::FileError::FAILED; - GetRootDirectory(mojo_vfs)->Delete(filename, 0, Capture(&error)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - - if (error == filesystem::FileError::OK && sync_dir) { - GetRootDirectory(mojo_vfs)->Flush(Capture(&error)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - } - - return error == filesystem::FileError::OK ? SQLITE_OK : SQLITE_IOERR_DELETE; -} - -int MojoVFSAccess(sqlite3_vfs* mojo_vfs, - const char* filename, - int flags, - int* result) { - DVLOG(1) << "MojoVFSAccess(*, " << filename << ", " << flags << ", *)"; - TRACE_EVENT2("sql", "MojoVFSAccess", - "name", filename, - "flags", flags); - filesystem::FileError error = filesystem::FileError::FAILED; - - if (flags == SQLITE_ACCESS_READWRITE || flags == SQLITE_ACCESS_READ) { - bool is_writable = false; - GetRootDirectory(mojo_vfs) - ->IsWritable(filename, Capture(&error, &is_writable)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - *result = is_writable; - return SQLITE_OK; - } - - if (flags == SQLITE_ACCESS_EXISTS) { - bool exists = false; - GetRootDirectory(mojo_vfs)->Exists(filename, Capture(&error, &exists)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - *result = exists; - return SQLITE_OK; - } - - return SQLITE_IOERR; -} - -int MojoVFSFullPathname(sqlite3_vfs* mojo_vfs, - const char* relative_path, - int absolute_path_size, - char* absolute_path) { - // The sandboxed process doesn't need to know the absolute path of the file. - sqlite3_snprintf(absolute_path_size, absolute_path, "%s", relative_path); - return SQLITE_OK; -} - -// Don't let SQLite dynamically load things. (If we are using the -// mojo:filesystem proxying VFS, then it's highly likely that we are sandboxed -// and that any attempt to dlopen() a shared object is folly.) -void* MojoVFSDlOpen(sqlite3_vfs*, const char*) { - return 0; -} - -void MojoVFSDlError(sqlite3_vfs*, int buf_size, char* error_msg) { - sqlite3_snprintf(buf_size, error_msg, "Dynamic loading not supported"); -} - -void (*MojoVFSDlSym(sqlite3_vfs*, void*, const char*))(void) { - return 0; -} - -void MojoVFSDlClose(sqlite3_vfs*, void*) { - return; -} - -int MojoVFSRandomness(sqlite3_vfs* mojo_vfs, int size, char* out) { - base::RandBytes(out, size); - return size; -} - -// Proxy the rest of the calls down to the OS specific handler. -int MojoVFSSleep(sqlite3_vfs* mojo_vfs, int micro) { - return GetParentVFS(mojo_vfs)->xSleep(GetParentVFS(mojo_vfs), micro); -} - -int MojoVFSCurrentTime(sqlite3_vfs* mojo_vfs, double* time) { - return GetParentVFS(mojo_vfs)->xCurrentTime(GetParentVFS(mojo_vfs), time); -} - -int MojoVFSGetLastError(sqlite3_vfs* mojo_vfs, int a, char* b) { - return 0; -} - -int MojoVFSCurrentTimeInt64(sqlite3_vfs* mojo_vfs, sqlite3_int64* out) { - return GetParentVFS(mojo_vfs)->xCurrentTimeInt64(GetParentVFS(mojo_vfs), out); -} - -static sqlite3_vfs mojo_vfs = { - 1, /* iVersion */ - sizeof(MojoVFSFile), /* szOsFile */ - kMaxPathName, /* mxPathname */ - 0, /* pNext */ - "mojo", /* zName */ - 0, /* pAppData */ - MojoVFSOpen, /* xOpen */ - MojoVFSDelete, /* xDelete */ - MojoVFSAccess, /* xAccess */ - MojoVFSFullPathname, /* xFullPathname */ - MojoVFSDlOpen, /* xDlOpen */ - MojoVFSDlError, /* xDlError */ - MojoVFSDlSym, /* xDlSym */ - MojoVFSDlClose, /* xDlClose */ - MojoVFSRandomness, /* xRandomness */ - MojoVFSSleep, /* xSleep */ - MojoVFSCurrentTime, /* xCurrentTime */ - MojoVFSGetLastError, /* xGetLastError */ - MojoVFSCurrentTimeInt64 /* xCurrentTimeInt64 */ -}; - -} // namespace - -ScopedMojoFilesystemVFS::ScopedMojoFilesystemVFS( - filesystem::DirectoryPtr root_directory) - : parent_(sqlite3_vfs_find(NULL)), - root_directory_(std::move(root_directory)) { - CHECK(!mojo_vfs.pAppData); - mojo_vfs.pAppData = this; - mojo_vfs.mxPathname = parent_->mxPathname; - - CHECK(sqlite3_vfs_register(&mojo_vfs, 1) == SQLITE_OK); -} - -ScopedMojoFilesystemVFS::~ScopedMojoFilesystemVFS() { - CHECK(mojo_vfs.pAppData); - mojo_vfs.pAppData = nullptr; - - CHECK(sqlite3_vfs_register(parent_, 1) == SQLITE_OK); - CHECK(sqlite3_vfs_unregister(&mojo_vfs) == SQLITE_OK); -} - -filesystem::DirectoryPtr& ScopedMojoFilesystemVFS::GetDirectory() { - return root_directory_; -} - -} // namespace sql
diff --git a/sql/mojo/mojo_vfs.h b/sql/mojo/mojo_vfs.h deleted file mode 100644 index dc835938..0000000 --- a/sql/mojo/mojo_vfs.h +++ /dev/null
@@ -1,45 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_MOJO_MOJO_VFS_H_ -#define SQL_MOJO_MOJO_VFS_H_ - -#include "base/macros.h" -#include "components/filesystem/public/interfaces/directory.mojom.h" - -typedef struct sqlite3_vfs sqlite3_vfs; - -namespace sql { - -// Changes the default sqlite3 vfs to a vfs that uses proxies calls to the -// mojo:filesystem service. Instantiating this object transparently changes how -// the entire //sql/ subsystem works in the process of the caller; all paths -// are treated as relative to |directory|. -class ScopedMojoFilesystemVFS { - public: - explicit ScopedMojoFilesystemVFS(filesystem::DirectoryPtr directory); - ~ScopedMojoFilesystemVFS(); - - // Returns the directory of the current VFS. - filesystem::DirectoryPtr& GetDirectory(); - - private: - friend sqlite3_vfs* GetParentVFS(sqlite3_vfs* mojo_vfs); - friend filesystem::DirectoryPtr& GetRootDirectory(sqlite3_vfs* mojo_vfs); - - // The default vfs at the time MojoVFS was installed. We use the to pass - // through things like randomness requests and per-platform sleep calls. - sqlite3_vfs* parent_; - - // When we initialize the subsystem, we are given a filesystem::Directory - // object, which is the root directory of a mojo:filesystem. All access to - // various files are specified from this root directory. - filesystem::DirectoryPtr root_directory_; - - DISALLOW_COPY_AND_ASSIGN(ScopedMojoFilesystemVFS); -}; - -} // namespace sql - -#endif // SQL_MOJO_MOJO_VFS_H_
diff --git a/sql/mojo/sql_test_base.cc b/sql/mojo/sql_test_base.cc deleted file mode 100644 index baecd048..0000000 --- a/sql/mojo/sql_test_base.cc +++ /dev/null
@@ -1,164 +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. - -#include "sql/mojo/sql_test_base.h" - -#include <stddef.h> -#include <stdint.h> -#include <utility> - -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/util/capture_util.h" -#include "sql/mojo/mojo_vfs.h" -#include "sql/test/test_helpers.h" - -using mojo::Capture; - -namespace sql { - -SQLTestBase::SQLTestBase() - : binding_(this) { -} - -SQLTestBase::~SQLTestBase() { -} - -base::FilePath SQLTestBase::db_path() { - return base::FilePath(FILE_PATH_LITERAL("SQLTest.db")); -} - -sql::Connection& SQLTestBase::db() { - return db_; -} - -bool SQLTestBase::Reopen() { - db_.Close(); - return db_.Open(db_path()); -} - -bool SQLTestBase::GetPathExists(const base::FilePath& path) { - filesystem::FileError error = filesystem::FileError::FAILED; - bool exists = false; - vfs_->GetDirectory()->Exists(path.AsUTF8Unsafe(), Capture(&error, &exists)); - vfs_->GetDirectory().WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) - return false; - return exists; -} - -bool SQLTestBase::CorruptSizeInHeaderOfDB() { - // See http://www.sqlite.org/fileformat.html#database_header - const size_t kHeaderSize = 100; - - mojo::Array<uint8_t> header; - - filesystem::FileError error = filesystem::FileError::FAILED; - filesystem::FilePtr file_ptr; - vfs_->GetDirectory()->OpenFile( - mojo::String(db_path().AsUTF8Unsafe()), GetProxy(&file_ptr), - filesystem::kFlagRead | filesystem::kFlagWrite | - filesystem::kFlagOpenAlways, - Capture(&error)); - vfs_->GetDirectory().WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) - return false; - - file_ptr->Read(kHeaderSize, 0, filesystem::Whence::FROM_BEGIN, - Capture(&error, &header)); - file_ptr.WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) - return false; - - filesystem::FileInformationPtr info; - file_ptr->Stat(Capture(&error, &info)); - file_ptr.WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) - return false; - int64_t db_size = info->size; - - test::CorruptSizeInHeaderMemory(&header.front(), db_size); - - uint32_t num_bytes_written = 0; - file_ptr->Write(std::move(header), 0, filesystem::Whence::FROM_BEGIN, - Capture(&error, &num_bytes_written)); - file_ptr.WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) - return false; - if (num_bytes_written != kHeaderSize) - return false; - - return true; -} - -void SQLTestBase::WriteJunkToDatabase(WriteJunkType type) { - uint32_t flags = 0; - if (type == TYPE_OVERWRITE_AND_TRUNCATE) - flags = filesystem::kFlagWrite | filesystem::kFlagCreate; - else - flags = filesystem::kFlagWrite | filesystem::kFlagOpen; - - filesystem::FileError error = filesystem::FileError::FAILED; - filesystem::FilePtr file_ptr; - vfs_->GetDirectory()->OpenFile( - mojo::String(db_path().AsUTF8Unsafe()), GetProxy(&file_ptr), - flags, - Capture(&error)); - vfs_->GetDirectory().WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) - return; - - const char* kJunk = "Now is the winter of our discontent."; - mojo::Array<uint8_t> data(strlen(kJunk)); - memcpy(&data.front(), kJunk, strlen(kJunk)); - - uint32_t num_bytes_written = 0; - file_ptr->Write(std::move(data), 0, filesystem::Whence::FROM_BEGIN, - Capture(&error, &num_bytes_written)); - file_ptr.WaitForIncomingResponse(); -} - -void SQLTestBase::TruncateDatabase() { - filesystem::FileError error = filesystem::FileError::FAILED; - filesystem::FilePtr file_ptr; - vfs_->GetDirectory()->OpenFile( - mojo::String(db_path().AsUTF8Unsafe()), GetProxy(&file_ptr), - filesystem::kFlagWrite | filesystem::kFlagOpen, - Capture(&error)); - vfs_->GetDirectory().WaitForIncomingResponse(); - if (error != filesystem::FileError::OK) - return; - - file_ptr->Truncate(0, Capture(&error)); - file_ptr.WaitForIncomingResponse(); - ASSERT_EQ(filesystem::FileError::OK, error); -} - -void SQLTestBase::SetUp() { - ApplicationTestBase::SetUp(); - - shell()->ConnectToInterface("mojo:filesystem", &files_); - - filesystem::FileError error = filesystem::FileError::FAILED; - filesystem::DirectoryPtr directory; - files()->OpenFileSystem("temp", GetProxy(&directory), - binding_.CreateInterfacePtrAndBind(), - Capture(&error)); - ASSERT_TRUE(files().WaitForIncomingResponse()); - ASSERT_EQ(filesystem::FileError::OK, error); - - vfs_.reset(new ScopedMojoFilesystemVFS(std::move(directory))); - ASSERT_TRUE(db_.Open(db_path())); -} - -void SQLTestBase::TearDown() { - db_.Close(); - vfs_.reset(); - - ApplicationTestBase::TearDown(); -} - -void SQLTestBase::OnFileSystemShutdown() { -} - -} // namespace sql
diff --git a/sql/mojo/sql_test_base.h b/sql/mojo/sql_test_base.h deleted file mode 100644 index 2fba5aa7..0000000 --- a/sql/mojo/sql_test_base.h +++ /dev/null
@@ -1,92 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_MOJO_SQL_TEST_BASE_H_ -#define SQL_MOJO_SQL_TEST_BASE_H_ - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "components/filesystem/public/interfaces/file_system.mojom.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "mojo/shell/public/cpp/application_test_base.h" -#include "sql/connection.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sql { - -class Connection; -class ScopedMojoFilesystemVFS; - -// Base class for SQL tests. -// -// WARNING: We want to run the same gtest based unit test code both against -// chromium (which uses this implementation here), and the mojo code (which -// uses a different class named SQLTestBase). These two classes need to have -// the same interface because we compile time switch them based on a -// #define. We need to have two different implementations because the mojo -// version derives from mojo::test::ApplicationTestBase instead of -// testing::Test. -class SQLTestBase : public mojo::test::ApplicationTestBase, - public filesystem::FileSystemClient { - public: - SQLTestBase(); - ~SQLTestBase() override; - - enum WriteJunkType { - TYPE_OVERWRITE_AND_TRUNCATE, - TYPE_OVERWRITE - }; - - // Returns the path to the database. - base::FilePath db_path(); - - // Returns a connection to the database at db_path(). - sql::Connection& db(); - - // Closes the current connection to the database and reopens it. - bool Reopen(); - - // Proxying method around base::PathExists. - bool GetPathExists(const base::FilePath& path); - - // SQLite stores the database size in the header, and if the actual - // OS-derived size is smaller, the database is considered corrupt. - // [This case is actually a common form of corruption in the wild.] - // This helper sets the in-header size to one page larger than the - // actual file size. The resulting file will return SQLITE_CORRUPT - // for most operations unless PRAGMA writable_schema is turned ON. - // - // Returns false if any error occurs accessing the file. - bool CorruptSizeInHeaderOfDB(); - - // Writes junk to the start of the file. - void WriteJunkToDatabase(WriteJunkType type); - - // Sets the database file size to 0. - void TruncateDatabase(); - - // Overridden from testing::Test: - void SetUp() override; - void TearDown() override; - - // Overridden from FileSystemClient: - void OnFileSystemShutdown() override; - - protected: - filesystem::FileSystemPtr& files() { return files_; } - - private: - filesystem::FileSystemPtr files_; - - scoped_ptr<ScopedMojoFilesystemVFS> vfs_; - mojo::Binding<filesystem::FileSystemClient> binding_; - sql::Connection db_; - - DISALLOW_COPY_AND_ASSIGN(SQLTestBase); -}; - -} // namespace sql - -#endif // SQL_MOJO_SQL_TEST_BASE_H_
diff --git a/sql/mojo/vfs_unittest.cc b/sql/mojo/vfs_unittest.cc deleted file mode 100644 index 395f7ba..0000000 --- a/sql/mojo/vfs_unittest.cc +++ /dev/null
@@ -1,340 +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. - -#include <stdint.h> -#include <memory> -#include <utility> - -#include "base/macros.h" -#include "components/filesystem/public/interfaces/file_system.mojom.h" -#include "mojo/shell/public/cpp/application_test_base.h" -#include "mojo/shell/public/cpp/shell.h" -#include "mojo/util/capture_util.h" -#include "sql/mojo/mojo_vfs.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/sqlite/sqlite3.h" - -namespace std { - -// This deleter lets us be safe with sqlite3 objects, which aren't really the -// structs, but slabs of new uint8_t[size]. -template <> -struct default_delete<sqlite3_file> { - inline void operator()(sqlite3_file* ptr) const { - // Why don't we call file->pMethods->xClose() here? Because it's not - // guaranteed to be valid. sqlite3_file "objects" can be in partially - // initialized states. - delete [] reinterpret_cast<uint8_t*>(ptr); - } -}; - -} // namespace std - -namespace sql { - -const char kFileName[] = "TestingDatabase.db"; - -class VFSTest : public mojo::test::ApplicationTestBase, - public filesystem::FileSystemClient { - public: - VFSTest() : binding_(this) {} - ~VFSTest() override {} - - sqlite3_vfs* vfs() { - return sqlite3_vfs_find(NULL); - } - - scoped_ptr<sqlite3_file> MakeFile() { - return scoped_ptr<sqlite3_file>(reinterpret_cast<sqlite3_file*>( - new uint8_t[vfs()->szOsFile])); - } - - void SetUp() override { - mojo::test::ApplicationTestBase::SetUp(); - - shell()->ConnectToInterface("mojo:filesystem", &files_); - - filesystem::FileError error = filesystem::FileError::FAILED; - filesystem::DirectoryPtr directory; - files_->OpenFileSystem("temp", GetProxy(&directory), - binding_.CreateInterfacePtrAndBind(), - mojo::Capture(&error)); - ASSERT_TRUE(files_.WaitForIncomingResponse()); - ASSERT_EQ(filesystem::FileError::OK, error); - - vfs_.reset(new ScopedMojoFilesystemVFS(std::move(directory))); - } - - void TearDown() override { - vfs_.reset(); - mojo::test::ApplicationTestBase::TearDown(); - } - - void OnFileSystemShutdown() override { - } - - private: - filesystem::FileSystemPtr files_; - scoped_ptr<ScopedMojoFilesystemVFS> vfs_; - mojo::Binding<filesystem::FileSystemClient> binding_; - - DISALLOW_COPY_AND_ASSIGN(VFSTest); -}; - -TEST_F(VFSTest, TestInstalled) { - EXPECT_EQ("mojo", std::string(vfs()->zName)); -} - -TEST_F(VFSTest, ExclusiveOpen) { - // Opening a non-existent file exclusively should work. - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_CREATE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - // Opening it a second time exclusively shouldn't. - scoped_ptr<sqlite3_file> file2(MakeFile()); - rc = vfs()->xOpen(vfs(), kFileName, file2.get(), - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_CREATE, - &out_flags); - EXPECT_NE(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); -} - -TEST_F(VFSTest, NonexclusiveOpen) { - // Opening a non-existent file should work. - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - // Opening it a second time should work. - scoped_ptr<sqlite3_file> file2(MakeFile()); - rc = vfs()->xOpen(vfs(), kFileName, file2.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); - file->pMethods->xClose(file2.get()); -} - -TEST_F(VFSTest, NullFilenameOpen) { - // Opening a file with a null filename should return a valid file object. - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen( - vfs(), nullptr, file.get(), - SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); -} - -TEST_F(VFSTest, DeleteOnClose) { - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen( - vfs(), kFileName, file.get(), - SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - file->pMethods->xClose(file.get()); - } - - // The file shouldn't exist now. - int result = 0; - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_FALSE(result); -} - -TEST_F(VFSTest, TestNonExistence) { - // We shouldn't have a file exist yet in a fresh directory. - int result = 0; - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_FALSE(result); -} - -TEST_F(VFSTest, TestExistence) { - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); - } - - int result = 0; - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_TRUE(result); -} - -TEST_F(VFSTest, TestDelete) { - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); - } - - int result = 0; - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_TRUE(result); - - vfs()->xDelete(vfs(), kFileName, 0); - - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_FALSE(result); -} - -TEST_F(VFSTest, TestWriteAndRead) { - const char kBuffer[] = "One Two Three Four Five Six Seven"; - const int kBufferSize = arraysize(kBuffer); - - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - for (int i = 0; i < 10; ++i) { - rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize, - i * kBufferSize); - EXPECT_EQ(SQLITE_OK, rc); - } - - file->pMethods->xClose(file.get()); - } - - // Expect that the size of the file is 10 * arraysize(kBuffer); - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - sqlite_int64 size; - rc = file->pMethods->xFileSize(file.get(), &size); - EXPECT_EQ(SQLITE_OK, rc); - EXPECT_EQ(10 * kBufferSize, size); - - file->pMethods->xClose(file.get()); - } - - // We should be able to read things back. - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - char data_buffer[kBufferSize]; - memset(data_buffer, '8', kBufferSize); - for (int i = 0; i < 10; ++i) { - rc = file->pMethods->xRead(file.get(), data_buffer, kBufferSize, - i * kBufferSize); - EXPECT_EQ(SQLITE_OK, rc); - EXPECT_TRUE(strncmp(kBuffer, &data_buffer[0], kBufferSize) == 0); - } - - file->pMethods->xClose(file.get()); - } -} - -TEST_F(VFSTest, PartialRead) { - const char kBuffer[] = "One Two Three Four Five Six Seven"; - const int kBufferSize = arraysize(kBuffer); - - // Write the data once. - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize, 0); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); - } - - // Now attempt to read kBufferSize + 5 from a file sized to kBufferSize. - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - const char kBufferWithFiveNulls[] = - "One Two Three Four Five Six Seven\0\0\0\0\0"; - const int kBufferWithFiveNullsSize = arraysize(kBufferWithFiveNulls); - - char data_buffer[kBufferWithFiveNullsSize]; - memset(data_buffer, '8', kBufferWithFiveNullsSize); - rc = file->pMethods->xRead(file.get(), data_buffer, - kBufferWithFiveNullsSize, 0); - EXPECT_EQ(SQLITE_IOERR_SHORT_READ, rc); - - EXPECT_TRUE(strncmp(kBufferWithFiveNulls, &data_buffer[0], - kBufferWithFiveNullsSize) == 0); - - file->pMethods->xClose(file.get()); - } -} - -TEST_F(VFSTest, Truncate) { - const char kBuffer[] = "One Two Three Four Five Six Seven"; - const int kBufferSize = arraysize(kBuffer); - const int kCharsToThree = 13; - - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize, 0); - EXPECT_EQ(SQLITE_OK, rc); - - sqlite_int64 size; - rc = file->pMethods->xFileSize(file.get(), &size); - EXPECT_EQ(SQLITE_OK, rc); - EXPECT_EQ(kBufferSize, size); - - rc = file->pMethods->xTruncate(file.get(), kCharsToThree); - EXPECT_EQ(SQLITE_OK, rc); - - rc = file->pMethods->xFileSize(file.get(), &size); - EXPECT_EQ(SQLITE_OK, rc); - EXPECT_EQ(kCharsToThree, size); - - file->pMethods->xClose(file.get()); -} - -} // namespace sql
diff --git a/sync/api/model_type_service.h b/sync/api/model_type_service.h index a2ec808..469ed4a 100644 --- a/sync/api/model_type_service.h +++ b/sync/api/model_type_service.h
@@ -19,7 +19,6 @@ namespace syncer_v2 { class DataBatch; -class MetadataBatch; class MetadataChangeList; // Interface implemented by model types to receive updates from sync via the
diff --git a/sync/engine/conflict_resolver.cc b/sync/engine/conflict_resolver.cc index 0af30977..d32460c 100644 --- a/sync/engine/conflict_resolver.cc +++ b/sync/engine/conflict_resolver.cc
@@ -116,6 +116,8 @@ // f) Otherwise, it's in general safer to ignore local changes, with the // exception of deletion conflicts (choose to undelete) and conflicts // where the non_unique_name or parent don't match. + // e) Except for the case of extensions and apps, where we want uninstalls to + // win over local modifications to avoid "back from the dead" reinstalls. if (!entry.GetServerIsDel()) { // TODO(nick): The current logic is arbitrary; instead, it ought to be made // consistent with the ModelAssociator behavior for a datatype. It would @@ -228,28 +230,42 @@ // specifics. entry.PutBaseServerSpecifics(sync_pb::EntitySpecifics()); } else { // SERVER_IS_DEL is true - if (entry.GetIsDir()) { - Directory::Metahandles children; - trans->directory()->GetChildHandlesById(trans, - entry.GetId(), - &children); - // If a server deleted folder has local contents it should be a hierarchy - // conflict. Hierarchy conflicts should not be processed by this - // function. - DCHECK(children.empty()); - } + ModelType type = entry.GetModelType(); + if (type == EXTENSIONS || type == APPS) { + // Ignore local changes for extensions/apps when server had a delete, to + // avoid unwanted reinstall of an uninstalled extension. + DVLOG(1) << "Resolving simple conflict, ignoring local changes for " + << "extension/app: " << entry; + conflict_util::IgnoreLocalChanges(&entry); + status->increment_num_local_overwrites(); + counters->num_local_overwrites++; + UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", + OVERWRITE_LOCAL, + CONFLICT_RESOLUTION_SIZE); + } else { + if (entry.GetIsDir()) { + Directory::Metahandles children; + trans->directory()->GetChildHandlesById(trans, + entry.GetId(), + &children); + // If a server deleted folder has local contents it should be a + // hierarchy conflict. Hierarchy conflicts should not be processed by + // this function. + DCHECK(children.empty()); + } - // The entry is deleted on the server but still exists locally. - // We undelete it by overwriting the server's tombstone with the local - // data. - conflict_util::OverwriteServerChanges(&entry); - status->increment_num_server_overwrites(); - counters->num_server_overwrites++; - DVLOG(1) << "Resolving simple conflict, undeleting server entry: " - << entry; - UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", - UNDELETE, - CONFLICT_RESOLUTION_SIZE); + // The entry is deleted on the server but still exists locally. + // We undelete it by overwriting the server's tombstone with the local + // data. + conflict_util::OverwriteServerChanges(&entry); + status->increment_num_server_overwrites(); + counters->num_server_overwrites++; + DVLOG(1) << "Resolving simple conflict, undeleting server entry: " + << entry; + UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", + UNDELETE, + CONFLICT_RESOLUTION_SIZE); + } } }
diff --git a/sync/engine/syncer_unittest.cc b/sync/engine/syncer_unittest.cc index b2a92de..b968e927 100644 --- a/sync/engine/syncer_unittest.cc +++ b/sync/engine/syncer_unittest.cc
@@ -283,6 +283,7 @@ &cancelation_signal_)); debug_info_getter_.reset(new MockDebugInfoGetter); EnableDatatype(BOOKMARKS); + EnableDatatype(EXTENSIONS); EnableDatatype(NIGORI); EnableDatatype(PREFERENCES); EnableDatatype(NIGORI); @@ -2670,11 +2671,11 @@ } TEST_F(SyncerTest, ParentAndChildBothMatch) { - // Disable PREFERENCES which is enabled at the setup step to avoid - // auto-creating - // PREFERENCES root folder and failing the test below that verifies the number - // of children at the root. + // Disable PREFERENCES and EXTENSIONS which are enabled at the setup step to + // avoid auto-creating root folders and failing the test below + // that verifies the number of children at the root. DisableDatatype(PREFERENCES); + DisableDatatype(EXTENSIONS); const FullModelTypeSet all_types = FullModelTypeSet::All(); syncable::Id parent_id = ids_.NewServerId(); @@ -3755,6 +3756,82 @@ } } +// This ensures that for extensions, we resolve the conflict of local updates +// and server deletes in favor of the server, to prevent extensions from +// being reinstalled after uninstall. +TEST_F(SyncerTest, ConflictResolverAcceptsServerDeleteForExtensions) { + ASSERT_TRUE(context_->GetEnabledTypes().Has(EXTENSIONS)); + + // Create an extension entry. + int64_t metahandle; + { + WriteTransaction trans(FROM_HERE, UNITTEST, directory()); + MutableEntry extension( + &trans, CREATE, EXTENSIONS, trans.root_id(), "extension_name"); + ASSERT_TRUE(extension.good()); + sync_pb::EntitySpecifics specifics; + AddDefaultFieldValue(EXTENSIONS, &specifics); + extension.PutSpecifics(specifics); + EXPECT_FALSE(extension.GetIsUnappliedUpdate()); + EXPECT_FALSE(extension.GetId().ServerKnows()); + metahandle = extension.GetMetahandle(); + extension.PutIsUnsynced(true); + } + + // Make sure the server has received the new item. + SyncShareNudge(); + syncable::Id id; + { + syncable::ReadTransaction trans(FROM_HERE, directory()); + Entry entry(&trans, GET_BY_HANDLE, metahandle); + + EXPECT_EQ(metahandle, entry.GetMetahandle()); + EXPECT_FALSE(entry.GetIsDel()); + EXPECT_FALSE(entry.GetServerIsDel()); + EXPECT_GE(entry.GetBaseVersion(), 0); + EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion()); + EXPECT_FALSE(entry.GetIsUnsynced()); + EXPECT_FALSE(entry.GetIsUnappliedUpdate()); + id = entry.GetId(); + } + + + // Simulate another client deleting the item. + { + syncable::ReadTransaction trans(FROM_HERE, directory()); + Entry entry(&trans, GET_BY_HANDLE, metahandle); + mock_server_->AddUpdateTombstone(id, EXTENSIONS); + } + + // Create a local update, which should cause a conflict with the delete that + // we just pushed to the server. + { + WriteTransaction trans(FROM_HERE, UNITTEST, directory()); + MutableEntry extension(&trans, GET_BY_HANDLE, metahandle); + ASSERT_TRUE(extension.good()); + sync_pb::EntitySpecifics specifics; + AddDefaultFieldValue(EXTENSIONS, &specifics); + specifics.mutable_extension()->set_disable_reasons(2); + extension.PutSpecifics(specifics); + EXPECT_FALSE(extension.GetIsUnappliedUpdate()); + extension.PutIsUnsynced(true); + } + + // Run a sync, and expect the item to be deleted. + SyncShareNudge(); + { + syncable::ReadTransaction trans(FROM_HERE, directory()); + Entry entry(&trans, GET_BY_HANDLE, metahandle); + EXPECT_EQ(metahandle, entry.GetMetahandle()); + EXPECT_TRUE(entry.GetIsDel()); + EXPECT_TRUE(entry.GetServerIsDel()); + EXPECT_FALSE(entry.GetIsUnsynced()); + EXPECT_FALSE(entry.GetIsUnappliedUpdate()); + EXPECT_GE(entry.GetBaseVersion(), 0); + EXPECT_GE(entry.GetServerVersion(), 0); + } +} + // See what happens if the IS_DIR bit gets flipped. This can cause us // all kinds of disasters. TEST_F(SyncerTest, UpdateFlipsTheFolderBit) {
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 116f2511..2cfe693 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -222,6 +222,9 @@ "test": "ozone_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -515,6 +518,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -807,6 +813,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 2c20188..0db6c55 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -511,6 +511,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -918,6 +921,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1055,9 +1061,15 @@ "test": "cacheinvalidation_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cast_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cc_unittests" }, { @@ -1100,6 +1112,9 @@ "test": "device_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "display_unittests" }, { @@ -1118,6 +1133,9 @@ "test": "gcm_unit_tests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "gfx_unittests" }, { @@ -1208,6 +1226,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1223,6 +1244,9 @@ "test": "remoting_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "sandbox_linux_unittests" }, { @@ -1336,9 +1360,15 @@ "test": "cacheinvalidation_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cast_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cc_unittests" }, { @@ -1372,9 +1402,15 @@ "test": "device_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "display_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "extensions_browsertests" }, { @@ -1390,6 +1426,9 @@ "test": "gcm_unit_tests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "gfx_unittests" }, { @@ -1448,6 +1487,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1463,6 +1505,9 @@ "test": "remoting_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "sandbox_linux_unittests" }, { @@ -1555,9 +1600,15 @@ "test": "cacheinvalidation_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cast_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cc_unittests" }, { @@ -1580,9 +1631,15 @@ "test": "device_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "display_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "extensions_browsertests" }, { @@ -1592,6 +1649,9 @@ "test": "gcm_unit_tests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "gfx_unittests" }, { @@ -1625,6 +1685,9 @@ "test": "midi_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1640,6 +1703,9 @@ "test": "remoting_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "sandbox_linux_unittests" }, { @@ -1715,9 +1781,15 @@ "test": "cacheinvalidation_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cast_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cc_unittests" }, { @@ -1760,6 +1832,9 @@ "test": "gcm_unit_tests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "gfx_unittests" }, { @@ -1850,6 +1925,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -2007,9 +2085,15 @@ "test": "cacheinvalidation_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cast_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "cc_unittests" }, { @@ -2052,6 +2136,9 @@ "test": "gcm_unit_tests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "gfx_unittests" }, { @@ -2133,6 +2220,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -2366,6 +2456,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -2614,6 +2707,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -2862,6 +2958,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -3110,6 +3209,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -3358,6 +3460,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -3606,6 +3711,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -3842,6 +3950,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -4066,6 +4177,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -4277,6 +4391,9 @@ "test": "midi_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -4507,6 +4624,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -4755,6 +4875,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -5003,6 +5126,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -5251,6 +5377,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -5499,6 +5628,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -5747,6 +5879,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -5995,6 +6130,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -6348,6 +6486,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -6771,6 +6912,9 @@ "test": "device_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "display_unittests" }, { @@ -6885,6 +7029,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -7197,6 +7344,52 @@ } ] }, + "MD Top Chrome ChromeOS material-hybrid": { + "gtest_tests": [ + { + "args": [ + "--top-chrome-md=material-hybrid" + ], + "test": "ash_unittests" + }, + { + "args": [ + "--top-chrome-md=material-hybrid" + ], + "test": "aura_unittests" + }, + { + "args": [ + "--top-chrome-md=material-hybrid" + ], + "test": "browser_tests" + }, + { + "args": [ + "--top-chrome-md=material-hybrid" + ], + "test": "interactive_ui_tests" + }, + { + "args": [ + "--top-chrome-md=material-hybrid" + ], + "test": "ui_base_unittests" + }, + { + "args": [ + "--top-chrome-md=material-hybrid" + ], + "test": "unit_tests" + }, + { + "args": [ + "--top-chrome-md=material-hybrid" + ], + "test": "views_unittests" + } + ] + }, "MD Top Chrome ChromeOS non-material": { "gtest_tests": [ { @@ -7249,6 +7442,86 @@ } ] }, + "MD Top Chrome Linux material": { + "gtest_tests": [ + { + "args": [ + "--top-chrome-md=material" + ], + "test": "aura_unittests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "browser_tests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "interactive_ui_tests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "ui_base_unittests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "unit_tests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "views_unittests" + } + ] + }, + "MD Top Chrome Win material": { + "gtest_tests": [ + { + "args": [ + "--top-chrome-md=material" + ], + "test": "aura_unittests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "browser_tests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "interactive_ui_tests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "ui_base_unittests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "unit_tests" + }, + { + "args": [ + "--top-chrome-md=material" + ], + "test": "views_unittests" + } + ] + }, "Site Isolation Linux": { "gtest_tests": [ {
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json index 6486f7b..5f3718a 100644 --- a/testing/buildbot/chromium.gpu.fyi.json +++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -4957,6 +4957,54 @@ } ] }, + "Optional Mac 10.10 Retina Release (AMD)": { + "gtest_tests": [ + { + "args": [ + "--use-gpu-in-tests" + ], + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "1002:6821", + "hidpi": "1", + "os": "Mac-10.10" + } + ] + }, + "test": "angle_end2end_tests" + } + ], + "isolated_scripts": [ + { + "args": [ + "webgl_conformance", + "--show-stdout", + "--browser=release", + "-v", + "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", + "--webgl-conformance-version=2.0.0", + "--webgl2-only=true" + ], + "isolate_name": "telemetry_gpu_test", + "name": "webgl2_conformance_tests", + "override_compile_targets": [ + "telemetry_gpu_test_run" + ], + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "1002:6821", + "hidpi": "1", + "os": "Mac-10.10" + } + ] + } + } + ] + }, "Optional Mac Retina Release": { "gtest_tests": [ {
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json index 3813022..a6aad2f 100644 --- a/testing/buildbot/chromium.linux.json +++ b/testing/buildbot/chromium.linux.json
@@ -98,15 +98,9 @@ "test": "mojo_system_unittests" }, { - "test": "mojo_view_manager_lib_unittests" - }, - { "test": "net_unittests" }, { - "test": "resource_provider_unittests" - }, - { "test": "sql_unittests" }, { @@ -123,9 +117,6 @@ }, { "test": "unit_tests" - }, - { - "test": "window_manager_unittests" } ] }, @@ -512,6 +503,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -863,6 +857,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1265,6 +1262,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1645,6 +1645,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, {
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json index edc9c06..b01e0a7 100644 --- a/testing/buildbot/chromium.mac.json +++ b/testing/buildbot/chromium.mac.json
@@ -271,6 +271,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -596,6 +599,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -921,6 +927,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1252,6 +1261,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1577,6 +1589,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1903,6 +1918,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, {
diff --git a/testing/buildbot/chromium.memory.fyi.json b/testing/buildbot/chromium.memory.fyi.json index 8ebfbcc..7a2068e 100644 --- a/testing/buildbot/chromium.memory.fyi.json +++ b/testing/buildbot/chromium.memory.fyi.json
@@ -566,6 +566,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -865,6 +868,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1113,6 +1119,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index 3a399da..5057b1d 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -167,6 +167,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -447,6 +450,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -647,6 +653,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, {
diff --git a/testing/buildbot/chromium.webkit.json b/testing/buildbot/chromium.webkit.json index f364a2a..83522a26 100644 --- a/testing/buildbot/chromium.webkit.json +++ b/testing/buildbot/chromium.webkit.json
@@ -198,6 +198,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -475,6 +478,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1053,6 +1059,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, {
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json index 4c4af1c..ae4a5f4 100644 --- a/testing/buildbot/chromium.win.json +++ b/testing/buildbot/chromium.win.json
@@ -280,6 +280,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -813,6 +816,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, { @@ -1194,6 +1200,9 @@ "test": "net_unittests" }, { + "swarming": { + "can_use_on_swarming_builders": true + }, "test": "ppapi_unittests" }, {
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index f64ed598..e0515aba 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -407,7 +407,7 @@ }, "ppapi_unittests": { "label": "//ppapi:ppapi_unittests", - "type": "unknown", + "type": "console_test_launcher", }, "printing_unittests": { "label": "//printing:printing_unittests",
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process index 5a23cea..167edeb 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process +++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -1,6 +1,12 @@ # These tests currently fail when they run with --site-per-process. # See https://crbug.com/477150. +# https://crbug.com/587909 - Regression caused by recent render pipeline throttling changes. +http/tests/misc/dns-prefetch-control.html [ Timeout ] +http/tests/navigatorconnect/connect-cross-origin.html [ Timeout ] +http/tests/security/document-origin.html [ Timeout ] +http/tests/serviceworker/navigation-redirect.html [ Timeout ] + # https://crbug.com/584984 - Recent, uninvestigated yet regression. http/tests/security/opened-document-security-origin-resets-on-navigation.html [ Crash ] @@ -64,13 +70,13 @@ http/tests/security/javascriptURL/javascriptURL-execution-context-iframe-src-setAttribute.html [ Failure ] http/tests/security/javascriptURL/javascriptURL-execution-context-iframe-src-setAttributeNode.html [ Failure ] http/tests/security/javascriptURL/javascriptURL-execution-context-iframe-src-setAttributeNS.html [ Failure ] -http/tests/security/xss-DENIED-iframe-src-alias.html [ Crash Failure ] +http/tests/security/xss-DENIED-iframe-src-alias.html [ Crash Failure Timeout ] http/tests/security/xss-DENIED-javascript-with-spaces.html [ Failure ] # https://crbug.com/582245 - no exception, b/c BindingSecurity::shouldAllowAccessTo exits early when |!target|. http/tests/security/xss-DENIED-getSVGDocument-iframe.html [ Failure ] http/tests/security/xss-DENIED-getSVGDocument-object.html [ Failure ] -http/tests/security/xssAuditor/block-does-not-leak-location.html [ Failure ] +http/tests/security/xssAuditor/block-does-not-leak-location.html [ Failure Timeout ] http/tests/security/xssAuditor/block-does-not-leak-referrer.html [ Failure ] http/tests/security/xssAuditor/full-block-script-tag-cross-domain.html [ Failure ] @@ -83,7 +89,7 @@ # https://crbug.com/582551 - testRunner.overridePreference doesn't impact all renderers. http/tests/security/powerfulFeatureRestrictions/geolocation-on-sandboxed-insecure-origin.html [ Failure ] -http/tests/security/powerfulFeatureRestrictions/geolocation-on-secure-origin-in-insecure-origin.html [ Failure ] +http/tests/security/powerfulFeatureRestrictions/geolocation-on-secure-origin-in-insecure-origin.html [ Failure Timeout ] # https://crbug.com/584845 - bad message - RFH_NO_PROXY_TO_PARENT from OnDispatchLoad. http/tests/security/contentSecurityPolicy/1.1/frame-ancestors/frame-ancestors-nested-cross-in-same-none-block.html [ Timeout ] @@ -234,7 +240,7 @@ http/tests/misc/selectionAsMarkup.html [ Missing ] http/tests/navigation/pushstate-whitelisted-at-blob-denied.html [ Missing ] http/tests/security/cookies/third-party-cookie-blocking-main-frame.html [ Missing ] -http/tests/security/frameNavigation/xss-ALLOWED-parent-navigation-change.html [ Missing ] +http/tests/security/frameNavigation/xss-ALLOWED-parent-navigation-change.html [ Missing Timeout ] http/tests/security/originHeader/origin-header-for-https.html [ Missing ] http/tests/security/powerfulFeatureRestrictions/durable-storage-on-insecure-origin.html [ Missing ] http/tests/security/referrer-on-client-redirect.html [ Missing ]
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations index cd9e501..bbefe01 100644 --- a/third_party/WebKit/LayoutTests/LeakExpectations +++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -157,3 +157,12 @@ # ----------------------------------------------------------------- crbug.com/582376 cssom/cssvalue-comparison.html [ Leak ] crbug.com/582376 fast/css/getComputedStyle/computed-style-with-zoom.html [ Leak ] + +# ----------------------------------------------------------------- +# Pasteboard leaks on Linux +# ----------------------------------------------------------------- + +crbug.com/587874 [ Linux ] editing/pasteboard/copy-backslash-with-euc.html [ Leak ] +crbug.com/587874 [ Linux ] editing/pasteboard/paste-and-sanitize.html [ Leak ] +crbug.com/587874 [ Linux ] editing/pasteboard/paste-text-events.html [ Leak ] +crbug.com/587874 [ Linux ] editing/pasteboard/paste-without-nesting.html [ Leak ]
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests index fbb12c92..e69cf60 100644 --- a/third_party/WebKit/LayoutTests/NeverFixTests +++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -44,13 +44,6 @@ # <progress> on Mac is always animated. So it's hard to get a reliable pixel result. [ Mac ] fast/dom/HTMLProgressElement/progress-element.html [ WontFix ] -# These test the CoreAnimation plugin model which we never intend to support on -# Windows and Linux. -[ Win Linux ] compositing/plugins/1x1-composited-plugin.html [ WontFix ] -[ Win Linux ] compositing/plugins/composited-plugin.html [ WontFix ] -[ Win Linux ] compositing/plugins/large-to-small-composited-plugin.html [ WontFix ] -[ Win Linux ] compositing/plugins/small-to-large-composited-plugin.html [ WontFix ] - # Mac's popup behavior is different. [ Mac ] fast/forms/select/menulist-onchange-fired-with-key-up-down.html [ WontFix ] [ Mac ] fast/forms/select/popup-with-display-none-optgroup.html [ WontFix ] @@ -213,6 +206,10 @@ # would let us differentiate test_shell and WebKit DumpTreeNode. crbug.com/7482 [ Win Mac ] http/tests/misc/timer-vs-loading.html [ WontFix ] +# On Linux bold emoji are already supported. +crbug.com/551843 [ Linux ] fast/text/fallback-traits-fixup.html [ WontFix ] +crbug.com/551843 [ Linux Win ] fast/text/emoji-font-weight-mac.html [ WontFix ] + # These tests are too slow with our MESA backend. We can re-enable when we have # bots running tests on real hardware. Don't want to just delete these because they # pass in the virtual test suite version.
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests index 41c9d47..8fe0f7a 100644 --- a/third_party/WebKit/LayoutTests/SlowTests +++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -279,3 +279,6 @@ crbug.com/552556 [ Win Linux ] virtual/threaded/fast/scroll-behavior/overflow-scroll-animates.html [ Slow ] crbug.com/570656 [ Mac ] fast/writing-mode/Kusa-Makura-background-canvas.html [ Slow ] crbug.com/570656 [ Mac ] fast/text/unicode-fallback-font.html [ Slow ] + +# This is really only slow under MSAN, but it's forbidden to add this expectation to MSANExpectations. +crbug.com/584807 [ Linux ] printing/webgl-oversized-printing.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index ed7e7f25..21845a5 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -598,9 +598,6 @@ crbug.com/441840 imported/csswg-test/css-shapes-1/shape-outside/values/shape-outside-polygon-004.html [ Failure ] crbug.com/441840 imported/csswg-test/css-shapes-1/shape-outside/values/shape-outside-shape-arguments-000.html [ Failure ] -crbug.com/586413 imported/csswg-test/css-snap-size-1/snap-height-baseline-001.html [ Failure ] -crbug.com/586413 imported/csswg-test/css-snap-size-1/snap-height-center-001.html [ Failure ] - crbug.com/505151 imported/csswg-test/css-writing-modes-3/abs-pos-non-replaced-icb-vlr-003.xht [ Failure ] crbug.com/505151 imported/csswg-test/css-writing-modes-3/abs-pos-non-replaced-icb-vlr-005.xht [ Failure ] crbug.com/505151 imported/csswg-test/css-writing-modes-3/abs-pos-non-replaced-icb-vlr-011.xht [ Failure ] @@ -1006,6 +1003,37 @@ crbug.com/570894 [ Win ] imported/csswg-test/css-flexbox-1/flexbox_flex-natural-mixed-basis-auto.html [ Failure Pass ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-vert-001.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-vert-001.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-horiz-001.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-vert-001.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-vert-001.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-004.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c.html [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001a.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001b.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001a.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001b.xhtml [ Failure ] +crbug.com/553838 imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-003.html [ Failure ] +crbug.com/553838 [ Mac ] imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a.xhtml [ Failure ] +crbug.com/553838 [ Mac ] imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b.xhtml [ Failure ] + crbug.com/471066 [ Mac10.6 ] fast/text/apply-start-width-after-skipped-text.html [ Failure ] crbug.com/471066 [ Mac10.6 ] fast/text/bidi-explicit-embedding-past-end.html [ Failure ] crbug.com/471066 [ Mac10.6 ] fast/text/emphasis-overlap.html [ Failure ] @@ -1349,6 +1377,8 @@ crbug.com/571773 inspector/console/worker-exception-message-contains-stack.html [ Failure Timeout Crash Pass ] +crbug.com/551843 fast/text/fallback-traits-fixup.html [ NeedsManualRebaseline ] + crbug.com/399951 http/tests/mime/javascript-mimetype-usecounters.html [ Pass Failure ] crbug.com/572710 [ Debug ] inspector/extensions/extensions-sidebar.html [ Timeout Pass ] @@ -1357,16 +1387,6 @@ crbug.com/579493 http/tests/security/xss-DENIED-xsl-document-securityOrigin.xml [ Timeout ] -crbug.com/445329 svg/custom/mouse-move-on-svg-root-standalone.svg [ NeedsRebaseline ] -crbug.com/445329 svg/custom/pointer-events-text-css-transform.svg [ NeedsRebaseline ] -crbug.com/445329 svg/custom/mouse-move-on-svg-root.xhtml [ NeedsRebaseline ] -crbug.com/445329 svg/custom/pointer-events-text.svg [ NeedsRebaseline ] -crbug.com/445329 svg/custom/text-hit-test.svg [ NeedsRebaseline ] -crbug.com/445329 svg/custom/pointer-events-image-css-transform.svg [ NeedsRebaseline ] -crbug.com/445329 svg/custom/mouse-move-on-svg-container.xhtml [ NeedsRebaseline ] -crbug.com/445329 svg/custom/pointer-events-image.svg [ NeedsRebaseline ] -crbug.com/445329 svg/custom/mouse-move-on-svg-container-standalone.svg [ NeedsRebaseline ] - crbug.com/572723 [ Mac10.7 Release ] inspector/sources/debugger/debugger-completions-on-call-frame.html [ Timeout Pass ] crbug.com/572723 [ Linux Mac10.9 ] inspector/sources/debugger/debugger-disable-enable.html [ Pass Failure Timeout ] crbug.com/572723 inspector/sources/debugger/debugger-uncaught-promise-on-pause.html [ Timeout Pass ] @@ -1384,4 +1404,18 @@ crbug.com/585724 fast/js/JSON-parse.html [ NeedsManualRebaseline ] +crbug.com/587136 [ Linux Debug ] http/tests/security/xss-DENIED-cross-origin-stack-overflow.html [ Timeout Pass ] + crbug.com/587593 [ Android ] fast/js/pic/cached-single-entry-transition.html [ Pass Failure ] + +crbug.com/587779 [ Mac10.10 ] fast/dynamic/window-resize-scrollbars-test.html [ Timeout Failure Pass ] + +crbug.com/587950 [ Mac Win ] virtual/threaded/animations/background-shorthand-crash.html [ Failure ] + +crbug.com/588056 [ Linux Debug ] webaudio/periodicwave-normalization.html [ Timeout Pass ] + +crbug.com/588061 [ Debug ] inspector/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps.html [ Pass Failure ] + +crbug.com/248063 [ Win ] plugins/plugin-clip-subframe.html [ Pass Failure ] + +crbug.com/588103 fast/xmlhttprequest/xmlhttprequest-responsetype-arraybuffer.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations index 793b191..a08466e 100644 --- a/third_party/WebKit/LayoutTests/W3CImportExpectations +++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -151,7 +151,9 @@ imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/conditional3 [ Skip ] imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/css21 [ Skip ] imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/filters [ Skip ] -imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox [ Skip ] +## Owners: cbiesinger@chromium.org +# imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox [ Skip ] +imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/reftest.list [ Skip ] imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/fonts3 [ Skip ] imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/images3 [ Skip ] imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/lists-3 [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/accessibility/listbox-enabled-states-expected.txt b/third_party/WebKit/LayoutTests/accessibility/listbox-enabled-states-expected.txt index 4cc6ad8..d891afb 100644 --- a/third_party/WebKit/LayoutTests/accessibility/listbox-enabled-states-expected.txt +++ b/third_party/WebKit/LayoutTests/accessibility/listbox-enabled-states-expected.txt
@@ -2,12 +2,12 @@ This tests that we report the correct enabled state on a listbox. -PASS accessibilityController.focusedElement.isEnabled is true -PASS accessibilityController.focusedElement.childAtIndex(0).isEnabled is true -PASS accessibilityController.focusedElement.childAtIndex(1).isEnabled is true -PASS accessibilityController.focusedElement.childAtIndex(2).isEnabled is false -PASS accessibilityController.focusedElement.childAtIndex(3).isEnabled is false -PASS accessibilityController.focusedElement.childAtIndex(4).isEnabled is true +PASS axSelectElement.isEnabled is true +PASS axSelectElement.childAtIndex(0).isEnabled is true +PASS axSelectElement.childAtIndex(1).isEnabled is true +PASS axSelectElement.childAtIndex(2).isEnabled is false +PASS axSelectElement.childAtIndex(3).isEnabled is false +PASS axSelectElement.childAtIndex(4).isEnabled is true PASS successfullyParsed is true TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/accessibility/listbox-enabled-states.html b/third_party/WebKit/LayoutTests/accessibility/listbox-enabled-states.html index 1b2deca..22944000 100644 --- a/third_party/WebKit/LayoutTests/accessibility/listbox-enabled-states.html +++ b/third_party/WebKit/LayoutTests/accessibility/listbox-enabled-states.html
@@ -27,12 +27,13 @@ document.getElementById("selectElement").focus(); - shouldBeTrue('accessibilityController.focusedElement.isEnabled'); - shouldBeTrue('accessibilityController.focusedElement.childAtIndex(0).isEnabled'); - shouldBeTrue('accessibilityController.focusedElement.childAtIndex(1).isEnabled'); - shouldBeFalse('accessibilityController.focusedElement.childAtIndex(2).isEnabled'); - shouldBeFalse('accessibilityController.focusedElement.childAtIndex(3).isEnabled'); - shouldBeTrue('accessibilityController.focusedElement.childAtIndex(4).isEnabled'); + var axSelectElement = accessibilityController.focusedElement; + shouldBeTrue('axSelectElement.isEnabled'); + shouldBeTrue('axSelectElement.childAtIndex(0).isEnabled'); + shouldBeTrue('axSelectElement.childAtIndex(1).isEnabled'); + shouldBeFalse('axSelectElement.childAtIndex(2).isEnabled'); + shouldBeFalse('axSelectElement.childAtIndex(3).isEnabled'); + shouldBeTrue('axSelectElement.childAtIndex(4).isEnabled'); } </script>
diff --git a/third_party/WebKit/LayoutTests/accessibility/selection-states-expected.txt b/third_party/WebKit/LayoutTests/accessibility/selection-states-expected.txt index cf42a233..6d3161f2 100644 --- a/third_party/WebKit/LayoutTests/accessibility/selection-states-expected.txt +++ b/third_party/WebKit/LayoutTests/accessibility/selection-states-expected.txt
@@ -2,13 +2,13 @@ This tests that we report the correct selection-related states. -PASS accessibilityController.focusedElement.isMultiSelectable is true -PASS accessibilityController.focusedElement.childAtIndex(0).isSelectable is true -PASS accessibilityController.focusedElement.childAtIndex(0).isSelected is true -PASS accessibilityController.focusedElement.childAtIndex(1).isSelectable is true -PASS accessibilityController.focusedElement.childAtIndex(1).isSelected is false -PASS accessibilityController.focusedElement.childAtIndex(2).isSelectable is false -PASS accessibilityController.focusedElement.childAtIndex(2).isSelected is false +PASS axSelectElement.isMultiSelectable is true +PASS axSelectElement.childAtIndex(0).isSelectable is true +PASS axSelectElement.childAtIndex(0).isSelected is true +PASS axSelectElement.childAtIndex(1).isSelectable is true +PASS axSelectElement.childAtIndex(1).isSelected is false +PASS axSelectElement.childAtIndex(2).isSelectable is false +PASS axSelectElement.childAtIndex(2).isSelected is false PASS successfullyParsed is true TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/accessibility/selection-states.html b/third_party/WebKit/LayoutTests/accessibility/selection-states.html index 820412e..c01b879b 100644 --- a/third_party/WebKit/LayoutTests/accessibility/selection-states.html +++ b/third_party/WebKit/LayoutTests/accessibility/selection-states.html
@@ -25,13 +25,14 @@ document.getElementById("selectElement").focus(); - shouldBeTrue('accessibilityController.focusedElement.isMultiSelectable'); - shouldBeTrue('accessibilityController.focusedElement.childAtIndex(0).isSelectable'); - shouldBeTrue('accessibilityController.focusedElement.childAtIndex(0).isSelected'); - shouldBeTrue('accessibilityController.focusedElement.childAtIndex(1).isSelectable'); - shouldBeFalse('accessibilityController.focusedElement.childAtIndex(1).isSelected'); - shouldBeFalse('accessibilityController.focusedElement.childAtIndex(2).isSelectable'); - shouldBeFalse('accessibilityController.focusedElement.childAtIndex(2).isSelected'); + var axSelectElement = accessibilityController.focusedElement; + shouldBeTrue('axSelectElement.isMultiSelectable'); + shouldBeTrue('axSelectElement.childAtIndex(0).isSelectable'); + shouldBeTrue('axSelectElement.childAtIndex(0).isSelected'); + shouldBeTrue('axSelectElement.childAtIndex(1).isSelectable'); + shouldBeFalse('axSelectElement.childAtIndex(1).isSelected'); + shouldBeFalse('axSelectElement.childAtIndex(2).isSelectable'); + shouldBeFalse('axSelectElement.childAtIndex(2).isSelected'); } </script> </body>
diff --git a/third_party/WebKit/LayoutTests/animations/composition/motion-rotation-composition.html b/third_party/WebKit/LayoutTests/animations/composition/motion-rotation-composition.html new file mode 100644 index 0000000..5837f512 --- /dev/null +++ b/third_party/WebKit/LayoutTests/animations/composition/motion-rotation-composition.html
@@ -0,0 +1,89 @@ +<!DOCTYPE html> +<body> +<script src="../interpolation/resources/interpolation-test.js"></script> +<script> +assertComposition({ + property: 'motion-rotation', + underlying: '20deg', + addFrom: '10deg', + addTo: '20deg', +}, [ + {at: -0.3, is: '27deg'}, + {at: 0, is: '30deg'}, + {at: 0.3, is: '33deg'}, + {at: 0.6, is: '36deg'}, + {at: 1, is: '40deg'}, + {at: 1.5, is: '45deg'}, +]); + +assertComposition({ + property: 'motion-rotation', + underlying: 'auto 20deg', + addFrom: '10deg', + addTo: '20deg', +}, [ + {at: -0.3, is: '7deg'}, + {at: 0, is: '10deg'}, + {at: 0.3, is: '13deg'}, + {at: 0.6, is: '16deg'}, + {at: 1, is: '20deg'}, + {at: 1.5, is: '25deg'}, +]); + +assertComposition({ + property: 'motion-rotation', + underlying: 'auto 20deg', + addFrom: 'reverse 10deg', + addTo: 'auto 20deg', +}, [ + {at: -0.3, is: 'auto 261deg'}, + {at: 0, is: 'auto 210deg'}, + {at: 0.3, is: 'auto 159deg'}, + {at: 0.6, is: 'auto 108deg'}, + {at: 1, is: 'auto 40deg'}, + {at: 1.5, is: 'auto -45deg'}, +]); + +assertComposition({ + property: 'motion-rotation', + underlying: '20deg', + addFrom: 'reverse 10deg', + addTo: '20deg', +}, [ + {at: -0.3, is: 'auto 190deg'}, + {at: 0, is: 'auto 190deg'}, + {at: 0.3, is: 'auto 190deg'}, + {at: 0.6, is: '40deg'}, + {at: 1, is: '40deg'}, + {at: 1.5, is: '40deg'}, +]); + +assertComposition({ + property: 'motion-rotation', + underlying: '20deg', + replaceFrom: 'reverse 10deg', + addTo: '20deg', +}, [ + {at: -0.3, is: 'auto 190deg'}, + {at: 0, is: 'auto 190deg'}, + {at: 0.3, is: 'auto 190deg'}, + {at: 0.6, is: '40deg'}, + {at: 1, is: '40deg'}, + {at: 1.5, is: '40deg'}, +]); + +assertComposition({ + property: 'motion-rotation', + underlying: '20deg', + addFrom: '10deg', + replaceTo: '10deg', +}, [ + {at: -0.3, is: '36deg'}, + {at: 0, is: '30deg'}, + {at: 0.3, is: '24deg'}, + {at: 0.6, is: '18deg'}, + {at: 1, is: '10deg'}, + {at: 1.5, is: '0deg'}, +]); +</script> +</body>
diff --git a/third_party/WebKit/LayoutTests/animations/responsive/motion-rotation-responsive.html b/third_party/WebKit/LayoutTests/animations/responsive/motion-rotation-responsive.html new file mode 100644 index 0000000..cd48b03 --- /dev/null +++ b/third_party/WebKit/LayoutTests/animations/responsive/motion-rotation-responsive.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<script src="resources/responsive-test.js"></script> +<script> +assertCSSResponsive({ + property: 'motion-rotation', + from: 'inherit', + to: 'auto 40deg', + configurations: [{ + state: {inherited: '50deg'}, + expect: [ + {at: 0.25, is: '50deg'}, + {at: 0.75, is: 'auto 40deg'}, + ], + }, { + state: {inherited: 'auto 20deg'}, + expect: [ + {at: 0.25, is: 'auto 25deg'}, + {at: 0.75, is: 'auto 35deg'}, + ], + }], +}); + +assertCSSResponsive({ + property: 'motion-rotation', + from: neutralKeyframe, + to: '80deg', + configurations: [{ + state: {underlying: 'auto 50deg'}, + expect: [ + {at: 0.25, is: 'auto 50deg'}, + {at: 0.75, is: '80deg'}, + ], + }, { + state: {underlying: '40deg'}, + expect: [ + {at: 0.25, is: '50deg'}, + {at: 0.75, is: '70deg'}, + ], + }], +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic.html b/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic.html index fd5c4ad..2906262b 100644 --- a/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic.html +++ b/third_party/WebKit/LayoutTests/bluetooth/getCharacteristic.html
@@ -112,7 +112,7 @@ return assert_promise_rejects_with_message( service.getCharacteristic('wrong_name'), new DOMException( 'Failed to execute \'getCharacteristic\' on ' + - '\'BluetoothGATTService\': Invalid Characteristic name: ' + + '\'BluetoothRemoteGATTService\': Invalid Characteristic name: ' + '\'wrong_name\'. ' + 'It must be a valid UUID alias (e.g. 0x1234), ' + 'UUID (lowercase hex characters e.g. ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService.html b/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService.html index 354d48c..6b0cdbbc 100644 --- a/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService.html +++ b/third_party/WebKit/LayoutTests/bluetooth/getPrimaryService.html
@@ -148,7 +148,7 @@ return assert_promise_rejects_with_message( gattServer.getPrimaryService('wrong_name'), new DOMException( 'Failed to execute \'getPrimaryService\' on ' + - '\'BluetoothGATTRemoteServer\': Invalid Service name: ' + + '\'BluetoothRemoteGATTServer\': Invalid Service name: ' + '\'wrong_name\'. ' + 'It must be a valid UUID alias (e.g. 0x1234), ' + 'UUID (lowercase hex characters e.g. ' +
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/1x1-composited-plugin-expected.txt b/third_party/WebKit/LayoutTests/compositing/plugins/1x1-composited-plugin-expected.txt deleted file mode 100644 index 7c834872..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/1x1-composited-plugin-expected.txt +++ /dev/null
@@ -1,2 +0,0 @@ - -PASS: no layers found
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/1x1-composited-plugin.html b/third_party/WebKit/LayoutTests/compositing/plugins/1x1-composited-plugin.html deleted file mode 100644 index 92b6e26b..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/1x1-composited-plugin.html +++ /dev/null
@@ -1,36 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <style type="text/css" media="screen"> - embed { - height: 1px; - width: 1px; - border: 1px solid black; - } - </style> - <script type="text/javascript" charset="utf-8"> - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } - - function doTest() - { - window.setTimeout(function() { - if (window.testRunner) { - document.getElementById('result').textContent = window.internals.layerTreeAsText(document); - testRunner.notifyDone(); - } - }, 0); - } - - window.addEventListener('load', doTest, false); - </script> -</head> -<body> - <embed type="application/x-webkit-test-netscape" width="1" height="1" drawingmodel="coreanimation"> - <embed type="application/x-webkit-test-netscape" width="100" height="0" drawingmodel="coreanimation"> - - <div id="result">Test only works in DRT</div> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin-expected.png b/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin-expected.png deleted file mode 100644 index cc2cdc0..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin-expected.txt b/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin-expected.txt deleted file mode 100644 index ff2b62f..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -layer at (0,0) size 800x600 - LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x170 - LayoutBlockFlow {HTML} at (0,0) size 800x170 - LayoutBlockFlow {BODY} at (8,8) size 784x154 - LayoutEmbeddedObject {EMBED} at (0,0) size 300x150 - LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin.html b/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin.html deleted file mode 100644 index 7e261e3d..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/composited-plugin.html +++ /dev/null
@@ -1,8 +0,0 @@ -<!DOCTYPE html> -<html> -<body> - <script src="../../resources/plugin.js"></script> - <script>startAfterLoadAndFinish();</script> - <embed type="application/x-webkit-test-netscape" drawingmodel="coreanimation"> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/large-to-small-composited-plugin-expected.txt b/third_party/WebKit/LayoutTests/compositing/plugins/large-to-small-composited-plugin-expected.txt deleted file mode 100644 index a106f303..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/large-to-small-composited-plugin-expected.txt +++ /dev/null
@@ -1,2 +0,0 @@ - -PASS: no layers found
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/large-to-small-composited-plugin.html b/third_party/WebKit/LayoutTests/compositing/plugins/large-to-small-composited-plugin.html deleted file mode 100644 index 6bde5a1a..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/large-to-small-composited-plugin.html +++ /dev/null
@@ -1,53 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <style type="text/css" media="screen"> - embed { - width: 300px; - height: 150px; - border: 1px solid black; - } - - embed.small { - height: 1px; - width: 1px; - } - - .container { - height: 100px; - width: 200px; - overflow: hidden; - } - </style> - <script type="text/javascript" charset="utf-8"> - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } - - function doTest() - { - window.setTimeout(function() { - document.getElementById('plugin').className = 'small'; - // Need to wait for compositing layers to be updated. - window.setTimeout(function() { - if (window.testRunner) { - document.getElementById('result').textContent = window.internals.layerTreeAsText(document); - testRunner.notifyDone(); - } - }, 0); - }, 0); - } - - window.addEventListener('load', doTest, false); - </script> -</head> -<body> - - <div class="container"> - <embed id="plugin" type="application/x-webkit-test-netscape" width="1" height="1" drawingmodel="coreanimation"> - </div> - - <div id="result">Test only works in DRT</div> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/small-to-large-composited-plugin-expected.txt b/third_party/WebKit/LayoutTests/compositing/plugins/small-to-large-composited-plugin-expected.txt deleted file mode 100644 index 139597f9..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/small-to-large-composited-plugin-expected.txt +++ /dev/null
@@ -1,2 +0,0 @@ - -
diff --git a/third_party/WebKit/LayoutTests/compositing/plugins/small-to-large-composited-plugin.html b/third_party/WebKit/LayoutTests/compositing/plugins/small-to-large-composited-plugin.html deleted file mode 100644 index d8ec148..0000000 --- a/third_party/WebKit/LayoutTests/compositing/plugins/small-to-large-composited-plugin.html +++ /dev/null
@@ -1,54 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <style type="text/css" media="screen"> - embed { - height: 1px; - width: 1px; - border: 1px solid black; - } - - embed.large { - width: 300px; - height: 150px; - } - - .container { - height: 100px; - width: 200px; - overflow: hidden; - } - </style> - <script type="text/javascript" charset="utf-8"> - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } - - function doTest() - { - window.setTimeout(function() { - document.getElementById('plugin').className = 'large'; - // Need to wait for compositing layers to be updated. FIXME remove. - window.setTimeout(function() { - if (window.testRunner) { - document.getElementById('layers').innerHTML = window.internals.layerTreeAsText(document); - testRunner.notifyDone(); - } - }, 0) - }, 0); - } - - window.addEventListener('load', doTest, false); - </script> -</head> -<body> - - <div class="container"> - <embed id="plugin" type="application/x-webkit-test-netscape" width="1" height="1" drawingmodel="coreanimation"> - </div> - - <!-- we should have layers after the resize --> - <pre id="layers">Layer tree appears here in DRT.</pre> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/insertHTML-aborted.html b/third_party/WebKit/LayoutTests/editing/execCommand/insertHTML-aborted.html new file mode 100644 index 0000000..bb2db14 --- /dev/null +++ b/third_party/WebKit/LayoutTests/editing/execCommand/insertHTML-aborted.html
@@ -0,0 +1,19 @@ +<!DOCTYPE html> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<iframe src="javascript:onunload = function() { + if (!top.container) { + top.container = document.createElement('div'); + top.container.appendChild(frameElement.parentNode); + } +}"> +</iframe> +<script> +window.onload = function() { + document.designMode = 'on'; + test(function() { + document.execCommand('selectall'); + assert_false(document.execCommand('InsertHTML', false, '<p>para</p>')); + }, 'InsertHTML command should return false if it failed.'); +}; +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed-expected.png new file mode 100644 index 0000000..0344995 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed-expected.txt b/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed-expected.txt new file mode 100644 index 0000000..d1b75c1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed-expected.txt
@@ -0,0 +1,7 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x120 + LayoutBlockFlow {HTML} at (0,0) size 800x120 + LayoutBlockFlow {BODY} at (8,8) size 784x104 + LayoutBlockFlow {SPAN} at (0,0) size 100x100 [bgcolor=#000000] + LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed.html b/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed.html new file mode 100644 index 0000000..b432f20 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/backgrounds/background-color-image-border-radius-bleed.html
@@ -0,0 +1,11 @@ +<!doctype HTML> +<style> +#SPAN_1 { + display: inline-block; + height: 100px; + width: 100px; + background: rgb(0, 0, 0) url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AEdABsE69gYUwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAuElEQVR42u3d0QkAIAhAQY2Ga/+FbIX+NLi3QOBhn5VRp0JjWkYARECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEA0ot14dn4wn7IhriwBERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgeq7zJQff9dkQIAICRECACAgQAREQIAICRECACAgQAREQIAICRECACAgQARGQsV0ZVwURCcMfSQAAAABJRU5ErkJggg=="); + border-radius: 50% 50% 50% 50%; +}/*#SPAN_1*/ +</style> +<span id="SPAN_1"></span>
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed-expected.png new file mode 100644 index 0000000..0344995 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed-expected.txt b/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed-expected.txt new file mode 100644 index 0000000..a1e87fc --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed-expected.txt
@@ -0,0 +1,7 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x120 + LayoutBlockFlow {HTML} at (0,0) size 800x120 + LayoutBlockFlow {BODY} at (8,8) size 784x104 + LayoutBlockFlow {SPAN} at (0,0) size 100x100 + LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed.html b/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed.html new file mode 100644 index 0000000..4d906de --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/backgrounds/background-multi-image-border-radius-bleed.html
@@ -0,0 +1,11 @@ +<!doctype HTML> +<style> +#SPAN_1 { + display: inline-block; + height: 100px; + width: 100px; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AEdABsE69gYUwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAuElEQVR42u3d0QkAIAhAQY2Ga/+FbIX+NLi3QOBhn5VRp0JjWkYARECACAgQAQEiIEAERECACAgQAQEiIEAERECACAgQAQEiIEA0ot14dn4wn7IhriwBERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgAgJEQIAICBABERAgeq7zJQff9dkQIAICRECACAgQAREQIAICRECACAgQAREQIAICRECACAgQARGQsV0ZVwURCcMfSQAAAABJRU5ErkJggg=="), url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AIQEyEwRG+NuwAAADRJREFUeNrtwQENAAAAwqD3T20ON6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4MdZQAAS6V1nwAAAAASUVORK5CYII='); + border-radius: 50% 50% 50% 50%; +}/*#SPAN_1*/ +</style> +<span id="SPAN_1"></span>
diff --git a/third_party/WebKit/LayoutTests/fast/borders/border-image-repeat-round-expected.png b/third_party/WebKit/LayoutTests/fast/borders/border-image-repeat-round-expected.png index b54b028..4b83eff 100644 --- a/third_party/WebKit/LayoutTests/fast/borders/border-image-repeat-round-expected.png +++ b/third_party/WebKit/LayoutTests/fast/borders/border-image-repeat-round-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/borders/border-image-side-reduction-expected.png b/third_party/WebKit/LayoutTests/fast/borders/border-image-side-reduction-expected.png index 8cf203f..13249c7 100644 --- a/third_party/WebKit/LayoutTests/fast/borders/border-image-side-reduction-expected.png +++ b/third_party/WebKit/LayoutTests/fast/borders/border-image-side-reduction-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-fill-liveness-expected.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-fill-liveness-expected.html new file mode 100644 index 0000000..8eac6be3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-fill-liveness-expected.html
@@ -0,0 +1,9 @@ +<canvas id="canvas" width="100" height="100"></canvas> +<script> +var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +ctx.fillStyle = '#ff0'; +ctx.fillRect(0, 0, canvas.width, canvas.height); +ctx.fillStyle = '#00f'; +ctx.fillRect(70, 70, 20, 20); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-fill-liveness.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-fill-liveness.html new file mode 100644 index 0000000..b6cd2f1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-fill-liveness.html
@@ -0,0 +1,29 @@ +<svg style="display: block; width: 0; height: 0"> + <defs> + <filter id="merge"> + <femerge> + <femergenode in="FillPaint"></femergenode> + <femergenode in="SourceGraphic"></femergenode> + </femerge> + </filter> + </defs> +</svg> +<canvas id="canvas" width="100" height="100"></canvas> +<script> +var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +var canvas2 = document.createElement('canvas'); +canvas2.width = 20; +canvas2.height = 20; +var ctx2 = canvas2.getContext('2d'); + +ctx2.fillStyle = '#00f'; +ctx2.fillRect(0, 0, canvas2.width, canvas.height); + +ctx.filter = 'url(#merge)'; +ctx.fillStyle = '#0f0'; +ctx.drawImage(canvas2, 40, 40); + +ctx.fillStyle = '#ff0'; +ctx.drawImage(canvas2, 70, 70); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-stroke-liveness-expected.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-stroke-liveness-expected.html new file mode 100644 index 0000000..8eac6be3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-stroke-liveness-expected.html
@@ -0,0 +1,9 @@ +<canvas id="canvas" width="100" height="100"></canvas> +<script> +var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +ctx.fillStyle = '#ff0'; +ctx.fillRect(0, 0, canvas.width, canvas.height); +ctx.fillStyle = '#00f'; +ctx.fillRect(70, 70, 20, 20); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-stroke-liveness.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-stroke-liveness.html new file mode 100644 index 0000000..ee116cb --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-filter-stroke-liveness.html
@@ -0,0 +1,21 @@ +<svg style="display: block; width: 0; height: 0"> + <defs> + <filter id="merge"> + <femerge> + <femergenode in="StrokePaint"></femergenode> + <femergenode in="SourceGraphic"></femergenode> + </femerge> + </filter> + </defs> +</svg> +<canvas id="canvas" width="100" height="100"></canvas> +<script> +var canvas = document.getElementById('canvas'); +var ctx = canvas.getContext('2d'); +ctx.filter = 'url(#merge)'; +ctx.fillStyle = '#00f'; +ctx.strokeStyle = '#0f0'; +ctx.fillRect(40, 40, 20, 20); +ctx.strokeStyle = '#ff0'; +ctx.fillRect(70, 70, 20, 20); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/absolute-positioning-definite-sizes.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/absolute-positioning-definite-sizes.html index dd4de5c6..1fbd9b8 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/absolute-positioning-definite-sizes.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/absolute-positioning-definite-sizes.html
@@ -2,7 +2,7 @@ <link href="resources/grid.css" rel="stylesheet"> <style> .grid { - grid-template: 1fr / 50px 1fr; + grid-template: 50px 1fr / 1fr; position: absolute; left: 50px;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/flex-content-distribution.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/flex-content-distribution.html index bdabea23..4ee0513 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/flex-content-distribution.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/flex-content-distribution.html
@@ -4,13 +4,13 @@ <link href="resources/grid-alignment.css" rel="stylesheet"> <style> .freeSpaceForColumnsGrid { - grid-template: minmax(20px, 0.7fr) / 100%; + grid-template: 100% / minmax(20px, 0.7fr); width: 50px; height: 100px; } .freeSpaceForRowsGrid { - grid-template: 100% / minmax(20px, 0.7fr); + grid-template: minmax(20px, 0.7fr) / 100%; width: 50px; height: 100px; }
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution-vertical-lr.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution-vertical-lr.html index ccb2781..9a01571 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution-vertical-lr.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution-vertical-lr.html
@@ -2,6 +2,7 @@ <html> <head> <link href="resources/grid.css" rel="stylesheet"> +<link href="resources/grid-alignment.css" rel="stylesheet"> <script src="../../resources/check-layout.js"></script> <style> body { @@ -20,22 +21,6 @@ grid-auto-rows: auto; } -.alignContentSpaceBetween { - align-content: space-between; -} - -.alignContentSpaceAround { - align-content: space-around; -} - -.alignContentSpaceEvenly { - align-content: space-evenly; -} - -.alignContentStretch { - align-content: stretch; -} - .thirdRowFirstColumn { background-color: magenta; grid-column: 1;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution-vertical-rl.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution-vertical-rl.html index 2553e60..7effcf8 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution-vertical-rl.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution-vertical-rl.html
@@ -2,6 +2,7 @@ <html> <head> <link href="resources/grid.css" rel="stylesheet"> +<link href="resources/grid-alignment.css" rel="stylesheet"> <script src="../../resources/check-layout.js"></script> <style> body { @@ -20,22 +21,6 @@ grid-auto-rows: auto; } -.alignContentSpaceBetween { - align-content: space-between; -} - -.alignContentSpaceAround { - align-content: space-around; -} - -.alignContentSpaceEvenly { - align-content: space-evenly; -} - -.alignContentStretch { - align-content: stretch; -} - .thirdRowFirstColumn { background-color: magenta; grid-column: 1;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution.html index 8172973..438d4a5 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-distribution.html
@@ -2,6 +2,7 @@ <html> <head> <link href="resources/grid.css" rel="stylesheet"> +<link href="resources/grid-alignment.css" rel="stylesheet"> <script src="../../resources/check-layout.js"></script> <style> body { @@ -20,22 +21,6 @@ grid-auto-rows: auto; } -.alignContentSpaceBetween { - align-content: space-between; -} - -.alignContentSpaceAround { - align-content: space-around; -} - -.alignContentSpaceEvenly { - align-content: space-evenly; -} - -.alignContentStretch { - align-content: stretch; -} - .thirdRowFirstColumn { background-color: magenta; grid-column: 1;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-vertical-lr.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-vertical-lr.html index 2d17e8c..26b861d6 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-vertical-lr.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-vertical-lr.html
@@ -2,6 +2,7 @@ <html> <head> <link href="resources/grid.css" rel="stylesheet"> +<link href="resources/grid-alignment.css" rel="stylesheet"> <script src="../../resources/check-layout.js"></script> <style> body { @@ -9,7 +10,7 @@ } .grid { - grid: 50px 50px / 100px 100px; + grid: 100px 100px / 50px 50px; position: relative; width: 300px; height: 200px; @@ -19,34 +20,6 @@ width: 20px; height: 40px; } - -.alignContentStart { - align-content: start; -} - -.alignContentEnd { - align-content: end; -} - -.alignContentCenter { - align-content: center; -} - -.alignContentLeft { - align-content: left; -} - -.alignContentRight { - align-content: right; -} - -.alignContentFlexStart { - align-content: flex-start; -} - -.alignContentFlexEnd { - align-content: flex-end; -} </style> </head> <body onload="checkLayout('.grid')">
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-vertical-rl.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-vertical-rl.html index 2fa908d..a47b922 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-vertical-rl.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content-vertical-rl.html
@@ -2,6 +2,7 @@ <html> <head> <link href="resources/grid.css" rel="stylesheet"> +<link href="resources/grid-alignment.css" rel="stylesheet"> <script src="../../resources/check-layout.js"></script> <style> body { @@ -9,7 +10,7 @@ } .grid { - grid: 50px 50px / 100px 100px; + grid: 100px 100px / 50px 50px; position: relative; width: 300px; height: 200px; @@ -19,35 +20,6 @@ width: 20px; height: 40px; } - -.alignContentStart { - align-content: start; -} - -.alignContentEnd { - align-content: end; -} - -.alignContentCenter { - align-content: center; -} - -.alignContentLeft { - align-content: left; -} - -.alignContentRight { - align-content: right; -} - -.alignContentFlexStart { - align-content: flex-start; -} - -.alignContentFlexEnd { - align-content: flex-end; -} - </style> </head> <body onload="checkLayout('.grid')">
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content.html index fa7ce4d..f697eae9 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-align-content.html
@@ -10,7 +10,7 @@ } .grid { - grid: 50px 50px / 100px 100px; + grid: 100px 100px / 50px 50px; position: relative; width: 200px; height: 300px;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-content-alignment-stretch-only-valid-for-auto-sized-tracks.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-content-alignment-stretch-only-valid-for-auto-sized-tracks.html index b09c031..c7785a3 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-content-alignment-stretch-only-valid-for-auto-sized-tracks.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-content-alignment-stretch-only-valid-for-auto-sized-tracks.html
@@ -2,6 +2,7 @@ <html> <head> <script src="../../resources/check-layout.js"></script> +<link href="resources/grid-alignment.css" rel="stylesheet"> <style> body { margin: 0px; @@ -11,10 +12,6 @@ display: grid; background-color: grey; position: relative; - align-content: stretch; - justify-content: stretch; - align-items: start; - justify-items: start; } .definiteSize { @@ -55,7 +52,7 @@ /* track sizes allowed to be stetched */ .autoTracks { grid: auto auto / auto auto; } -.autoMaxTracks { grid: minmax(20px, auto) minmax(20px, auto) / minmax(40px, auto) minmax(40px, auto); } +.autoMaxTracks { grid: minmax(40px, auto) minmax(40px, auto) / minmax(20px, auto) minmax(20px, auto); } /* content-sized tracks disallowed to be stetched */ .minContentTracks { grid: min-content min-content / min-content min-content; } @@ -72,7 +69,7 @@ <div style="position: relative"> <p>cols: 'auto' | rows: 'auto' | definite-sized container | definite-sized items </p> - <div class="grid definiteSize autoTracks" data-expected-width="200" data-expected-height="300"> + <div class="grid contentStretch itemsStart definiteSize autoTracks" data-expected-width="200" data-expected-height="300"> <div class="item firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item secondRowFirstColumn" data-offset-x="0" data-offset-y="150" data-expected-width="40" data-expected-height="80"></div> @@ -82,7 +79,7 @@ <div style="position: relative"> <p>cols: 'minmax(20px, auto)' | rows: 'minmax(40px, auto)' | definite-sized container | definite-sized items </p> - <div class="grid definiteSize autoMaxTracks" data-expected-width="200" data-expected-height="300"> + <div class="grid contentStretch itemsStart definiteSize autoMaxTracks" data-expected-width="200" data-expected-height="300"> <div class="item firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item secondRowFirstColumn" data-offset-x="0" data-offset-y="150" data-expected-width="40" data-expected-height="80"></div> @@ -92,7 +89,7 @@ <div style="position: relative"> <p>cols: 'min-content' | rows: 'min-content' | definite-sized container | definite-sized items </p> - <div class="grid definiteSize minContentTracks" data-expected-width="200" data-expected-height="300"> + <div class="grid contentStretch itemsStart definiteSize minContentTracks" data-expected-width="200" data-expected-height="300"> <div class="item firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item firstRowSecondColumn" data-offset-x="40" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item secondRowFirstColumn" data-offset-x="0" data-offset-y="80" data-expected-width="40" data-expected-height="80"></div> @@ -102,7 +99,7 @@ <div style="position: relative"> <p>cols: 'max-content' | rows: 'max-content' | definite-sized container | definite-sized items </p> - <div class="grid definiteSize maxContentTracks" data-expected-width="200" data-expected-height="300"> + <div class="grid contentStretch itemsStart definiteSize maxContentTracks" data-expected-width="200" data-expected-height="300"> <div class="item firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item firstRowSecondColumn" data-offset-x="40" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item secondRowFirstColumn" data-offset-x="0" data-offset-y="80" data-expected-width="40" data-expected-height="80"></div> @@ -112,7 +109,7 @@ <div style="position: relative"> <p>cols: 'minmax(20px, max-content)' | rows: 'minmax(20px, max-content)' | definite-sized container | definite-sized items </p> - <div class="grid definiteSize minMaxWithMaxContentTracks" data-expected-width="200" data-expected-height="300"> + <div class="grid contentStretch itemsStart definiteSize minMaxWithMaxContentTracks" data-expected-width="200" data-expected-height="300"> <div class="item firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item firstRowSecondColumn" data-offset-x="40" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item secondRowFirstColumn" data-offset-x="0" data-offset-y="80" data-expected-width="40" data-expected-height="80"></div> @@ -122,7 +119,7 @@ <div style="position: relative"> <p>cols: 'minmax(20px, min-content)' | rows: 'minmax(20px, min-content)' | definite-sized container | definite-sized items </p> - <div class="grid definiteSize minMaxWithMinContentTracks" data-expected-width="200" data-expected-height="300"> + <div class="grid contentStretch itemsStart definiteSize minMaxWithMinContentTracks" data-expected-width="200" data-expected-height="300"> <div class="item firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item firstRowSecondColumn" data-offset-x="40" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item secondRowFirstColumn" data-offset-x="0" data-offset-y="80" data-expected-width="40" data-expected-height="80"></div> @@ -132,7 +129,7 @@ <div style="position: relative"> <p>cols: 'minmax(auto, max-content)' | rows: 'minmax(auto, max-content)' | definite-sized container | definite-sized items </p> - <div class="grid definiteSize minMaxWithMinAutoTracks" data-expected-width="200" data-expected-height="300"> + <div class="grid contentStretch itemsStart definiteSize minMaxWithMinAutoTracks" data-expected-width="200" data-expected-height="300"> <div class="item firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item firstRowSecondColumn" data-offset-x="40" data-offset-y="0" data-expected-width="40" data-expected-height="80"></div> <div class="item secondRowFirstColumn" data-offset-x="0" data-offset-y="80" data-expected-width="40" data-expected-height="80"></div>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-alignment.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-alignment.html index ea340f4c..3d2ba825 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-alignment.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-alignment.html
@@ -14,7 +14,7 @@ } .grid50And100 { - grid: 50px 50px / 100px 100px; + grid: 100px 100px / 50px 50px; width: 200px; height: 300px; position: relative; /* For the <p> comments */
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-flex-content.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-flex-content.html index 5f00fef..361f9abc 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-flex-content.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-flex-content.html
@@ -5,13 +5,13 @@ <script src="../../resources/check-layout.js"></script> <script src="../../resources/js-test.js"></script> <style> -.gridPercentAndFlexContent { grid-template: 50% minmax(30px, 2fr) / 50px; } -.gridTwoDoubleMaxFlexContent { grid-template: minmax(10px, 0.5fr) minmax(10px, 2fr) / 50px; } -.gridIgnoreSecondGridItem { grid-template: minmax(300px, 3fr) minmax(150px, 1fr) / 50px; } +.gridPercentAndFlexContent { grid-template: 50px / 50% minmax(30px, 2fr); } +.gridTwoDoubleMaxFlexContent { grid-template: 50px / minmax(10px, 0.5fr) minmax(10px, 2fr); } +.gridIgnoreSecondGridItem { grid-template: 50px / minmax(300px, 3fr) minmax(150px, 1fr); } -.gridRowsPercentAndFlexContent { grid-template: 50px / minmax(30px, 2fr) 50%; } -.gridRowsTwoMaxFlexContent { grid-template: 50px / minmax(10px, 1fr) minmax(10px, 2fr); } -.gridRowsTwoDoubleMaxFlexContent { grid-template: 50px / minmax(10px, 0.5fr) minmax(10px, 2fr); } +.gridRowsPercentAndFlexContent { grid-template: minmax(30px, 2fr) 50% / 50px; } +.gridRowsTwoMaxFlexContent { grid-template: minmax(10px, 1fr) minmax(10px, 2fr) / 50px; } +.gridRowsTwoDoubleMaxFlexContent { grid-template: minmax(10px, 0.5fr) minmax(10px, 2fr) / 50px; } .gridMinMaxFlexFixedAndMinContentAndFixed { grid-template-columns: minmax(0.5fr, 35px) min-content 25px; } .gridMinContentAndMinMaxFixedMinContentAndFlex { grid-template-columns: min-content minmax(20px, min-content) 2fr; }
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-tracks.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-tracks.html index be20bb2..f18bebc 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-tracks.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-gutters-and-tracks.html
@@ -18,12 +18,12 @@ .gridMultipleCols { grid-template-columns: 30px minmax(10px, 50px) 80px; } .gridMultipleRows { grid-template-rows: 90px 70px min-content; } .gridAutoAuto { grid-template: auto auto / auto auto; } -.gridMultipleFixed { grid-template: [first] 15px [foo] 25px [bar] 35px [last] / [first] 37px [foo] 57px [bar] 77px [last]; } +.gridMultipleFixed { grid-template: [first] 37px [foo] 57px [bar] 77px [last] / [first] 15px [foo] 25px [bar] 35px [last]; } .gridFixed100 { grid-template: repeat(2, 100px) / repeat(2, 100px); } .gridWithPercent { grid-template-columns: 10px 20% repeat(2, 30px); } .gridWithDoublePercent { grid-template-columns: 60% 40%; } -.gridWithRowPercent { grid-template: 20px / 10px 20% 30px; } -.gridWithRowDoublePercent { grid-template: 20px / 60% 40%; } +.gridWithRowPercent { grid-template: 10px 20% 30px / 20px; } +.gridWithRowDoublePercent { grid-template: 60% 40% / 20px; } .width220 { width: 220px; } .height100 { height: 100px; }
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content-vertical-lr.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content-vertical-lr.html index 1a70d27..da24529 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content-vertical-lr.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content-vertical-lr.html
@@ -10,7 +10,7 @@ } .grid { - grid: 50px 50px / 100px 100px; + grid: 100px 100px / 50px 50px; position: relative; width: 300px; height: 200px;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content-vertical-rl.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content-vertical-rl.html index e2f600a..9d9d736b5 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content-vertical-rl.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content-vertical-rl.html
@@ -10,7 +10,7 @@ } .grid { - grid: 50px 50px / 100px 100px; + grid: 100px 100px / 50px 50px; position: relative; width: 300px; height: 200px;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content.html index 399f468b..22a0a44 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-justify-content.html
@@ -10,7 +10,7 @@ } .grid { - grid: 50px 50px / 100px 100px; + grid: 100px 100px / 50px 50px; position: relative; width: 200px; height: 300px;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-shorthand-computed-style-crash.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-shorthand-computed-style-crash.html index 834a39e8..6878fe8 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-shorthand-computed-style-crash.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-shorthand-computed-style-crash.html
@@ -6,7 +6,7 @@ </script> <style> .grid { - grid: 100px / 50px; + grid: 50px / 100px; } </style> <p>This test checks that getting computed style of grid shorthand does not crash.</p>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-shorthand-get-set.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-shorthand-get-set.html index e07282a..523aabdd 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-shorthand-get-set.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-shorthand-get-set.html
@@ -7,7 +7,7 @@ grid: none; } .gridWithTemplate { - grid: 15px / 10px; + grid: 10px / 15px; } .gridWithInherit { grid: inherit; @@ -87,8 +87,8 @@ debug(""); debug("Test getting and setting 'grid' shorthand through JS"); - testGridDefinitionsSetJSValues("10px / 20px", "10px", "20px", "none", "row", "auto", "auto", "10px", "20px", "none", "initial", "initial", "initial"); - testGridDefinitionsSetJSValues("10px / [line] 'a' 20px", "10px", "[line] 20px", "\"a\"", "row", "auto", "auto", "10px", "[line] 20px", "\"a\"", "initial", "initial", "initial"); + testGridDefinitionsSetJSValues("20px / 10px", "10px", "20px", "none", "row", "auto", "auto", "10px", "20px", "none", "initial", "initial", "initial"); + testGridDefinitionsSetJSValues("[line] 'a' 20px / 10px", "10px", "[line] 20px", "\"a\"", "row", "auto", "auto", "10px", "[line] 20px", "\"a\"", "initial", "initial", "initial"); testGridDefinitionsSetJSValues("row dense 20px", "none", "none", "none", "row dense", "20px", "20px", "initial", "initial", "initial", "row dense", "20px", "20px"); testGridDefinitionsSetJSValues("column 20px / 10px", "none", "none", "none", "column", "20px", "10px", "initial", "initial", "initial", "column", "20px", "10px");
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-template-shorthand-get-set.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-template-shorthand-get-set.html index 177771e..f09780f 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-template-shorthand-get-set.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-template-shorthand-get-set.html
@@ -7,37 +7,37 @@ grid-template: none; } #gridTemplateSimpleForm { - grid-template: 10px / 15px; + grid-template: 15px / 10px; } #gridTemplateSimpleFormWithNoneColumns { - grid-template: none / 15px; + grid-template: 15px / none; } #gridTemplateSimpleFormWithNoneRows { - grid-template: 10px / none; + grid-template: none / 10px; } #gridTemplateSimpleFormWithNone { grid-template: none / none; } #gridTemplateComplexForm { - grid-template: 10px / "a" 15px; + grid-template: "a" 15px / 10px; } #gridTemplateComplexFormWithLineNames { - grid-template: 10px / [head] "a" 15px [tail]; + grid-template: [head] "a" 15px [tail] / 10px; } #gridTemplateComplexFormWithLineNamesMultipleColumns { - grid-template: 10px / [head] "a b" 15px [tail] + grid-template: [head] "a b" 15px [tail] / 10px; } #gridTemplateComplexFormWithLineNamesMultipleRows { - grid-template: 10px / [head1] "a" 15px [tail1] - [head2] "b" 20px [tail2]; + grid-template: [head1] "a" 15px [tail1] + [head2] "b" 20px [tail2] / 10px; } #gridTemplateComplexFormWithLineNamesMultipleRowsAndColumns { - grid-template: [first] 10px repeat(2, [nav nav2] 15px) / "a b c" 100px [nav] - [nav2] "d e f" 25px [nav] - [nav2] "g h i" 25px [last]; + grid-template: "a b c" 100px [nav] + [nav2] "d e f" 25px [nav] + [nav2] "g h i" 25px [last] / [first] 10px repeat(2, [nav nav2] 15px); } #gridTemplateComplexFormWithAuto { - grid-template: 10px / "a"; + grid-template: "a" / 10px; } #gridTemplateComplexFormOnlyAreas { grid-template: "a"; @@ -46,8 +46,8 @@ grid-template: [first] "a" auto []; } #gridTemplateConsecutiveAreas { - grid-template: 10px / "a" - "a"; + grid-template: "a" + "a" / 10px; } /* Bad values. */ @@ -59,13 +59,13 @@ grid-template: 10px; } #gridTemplateSimpleFormNoRows { - grid-template: 10px /; -} -#gridTemplateSimpleFormNoColumns { grid-template: / 10px; } +#gridTemplateSimpleFormNoColumns { + grid-template: 10px /; +} #gridTemplateSimpleFormNoColumnSize { - grid-template: [line] / 10px; + grid-template: 10px / [line]; } #gridTemplateSimpleFormWithFitContent { grid-template: -webkit-fit-content / 10px; @@ -86,31 +86,31 @@ grid-template: 10px none / 20px; } #gridTemplateComplexFormWithRepeat { - grid-template: 10px / "a" repeat(2, 50% [title]); + grid-template: "a" repeat(2, 50% [title]) / 10px; } #gridTemplateComplexFormWithWrongRepeat { - grid-template: repeat(2, 50% [title] a) / "a"; + grid-template: "a" / repeat(2, 50% [title] a); } #griTemplateComplexFormdWithFitAvailable { - grid-template: -webkit-fit-available / "a"; + grid-template: "a" / -webkit-fit-available; } #gridTemplateComplexFormNoColumnSize { - grid-template: [line] / "a"; + grid-template: "a" / [line]; } #gridTemplateComplexFormMisplacedRowsSize1 { - grid-template: 25px / 10px "a"; + grid-template: 10px "a" / 25px; } #gridTemplateComplexFormMisplacedRowsSize2 { - grid-template: 25px / "a" [name] 10px; + grid-template: "a" [name] 10px / 25px; } #gridTemplateComplexFormColumnsNotParsing1 { - grid-template: a / "a" [name] 10px; + grid-template: "a" [name] 10px / a; } #gridTemplateComplexFormColumnsNotParsing2 { - grid-template: "B" / "a" [name] 10px; + grid-template: "a" [name] 10px / "B"; } #gridTemplateComplexFormWithNoneColumns { - grid-template: none / "a" [name] 10px; + grid-template: "a" [name] 10px / none; } #gridTemplateNoColumnsRowWithTwoEmptyTrailingLineNames { grid-template: [first] "a" auto [] []; @@ -216,23 +216,23 @@ debug(""); debug("Test setting grid-template-columns and grid-template-rows back to 'none' through JS"); - testGridDefinitionsSetJSValues("10px / [line] 'a' 20px", "10px", "[line] 20px", "\"a\""); + testGridDefinitionsSetJSValues("[line] 'a' 20px / 10px", "10px", "[line] 20px", "\"a\""); testGridDefinitionsSetJSValues("none", "none", "none", "none"); debug(""); debug("Test getting and setting grid-template shorthand through JS"); - testGridDefinitionsSetJSValues("18px / 66px", "18px", "66px", "none"); - testGridDefinitionsSetJSValues("10px / [head] 'a' 15px [tail]", "10px", "[head] 15px [tail]", "\"a\""); + testGridDefinitionsSetJSValues("66px / 18px", "18px", "66px", "none"); + testGridDefinitionsSetJSValues("[head] 'a' 15px [tail] / 10px", "10px", "[head] 15px [tail]", "\"a\""); testGridDefinitionsSetJSValues("'a'", "none", "0px", "\"a\"", "none", "auto"); debug(""); debug("Test setting grid-template shorthand to bad values through JS"); - testGridDefinitionsSetBadJSValues("none / 'a'"); - testGridDefinitionsSetBadJSValues("25px / 'a' [name] 10px"); + testGridDefinitionsSetBadJSValues("'a' / none"); + testGridDefinitionsSetBadJSValues("'a' [name] 10px / 25px"); testGridDefinitionsSetBadJSValues("'a' / 'b'"); testGridDefinitionsSetBadJSValues("15px"); - testGridDefinitionsSetBadJSValues("15px / 20px none"); - testGridDefinitionsSetBadJSValues("25px / 10px 'a'"); + testGridDefinitionsSetBadJSValues("20px none / 15px"); + testGridDefinitionsSetBadJSValues("10px 'a' / 25px"); </script> </body>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/implicit-tracks-before-explicit.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/implicit-tracks-before-explicit.html index cb10514..f482726 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/implicit-tracks-before-explicit.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/implicit-tracks-before-explicit.html
@@ -4,7 +4,7 @@ <link href="resources/grid-alignment.css" rel="stylesheet"> <style> .grid { - grid: 100px / 50px; + grid: 50px / 100px; font: 10px/1 Ahem; position: relative; margin: 25px;
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/maximize-tracks-definite-indefinite-height.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/maximize-tracks-definite-indefinite-height.html index 528d913..75fbca0c 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/maximize-tracks-definite-indefinite-height.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/maximize-tracks-definite-indefinite-height.html
@@ -1,15 +1,13 @@ <!DOCTYPE html> <link href="resources/grid.css" rel="stylesheet"> +<link href="resources/grid-alignment.css" rel="stylesheet"> <link href="../css-intrinsic-dimensions/resources/height-keyword-classes.css" rel="stylesheet"> <style> .grid { grid-template-rows: minmax(0px, 100px); width: 40px; - - align-items: start; - justify-items: start; } .max-height-35 { max-height: 35px; } @@ -24,31 +22,31 @@ <h2>Check the behavior of grids under max-content constraints.</h2> <div class="max-content max-height-35"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX XX XXX</div> </div> </div> <div class="max-content max-height-min-content"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX X</div> </div> </div> <div class="max-height-min-content"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX</div> </div> </div> <div class="max-content max-height-fill-available"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XXX X XXX</div> </div> </div> <div class="max-content"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX XXX XX</div> </div> </div> @@ -76,125 +74,125 @@ <br> <h2>Check the behavior of grids under min-content contstraints.</h2> <div class="min-content"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XX XX</div> </div> </div> <div class="min-content min-height-50"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX X</div> </div> </div> <div class="min-content min-height-fit-content"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX XXXX</div> </div> </div> <div style="height: 200px;"> <div class="min-content min-height-fill-available"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXXX XXXX XXX</div> </div> </div> </div> <div class="min-content min-height-min-content"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX</div> </div> </div> <div class="min-content min-height-35"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XX</div> </div> </div> <div class="min-content min-height-max-content"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">X XXX X</div> </div> </div> <div class="min-content min-height-50"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XXXX XXXX XXXX XXXX</div> </div> </div> <div class="min-content max-height-50"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea min-height-fill-available" data-expected-width="40" data-expected-height="100">XXXX X X XXXX</div> </div> </div> -<div class="grid min-content" data-expected-width="40" data-expected-height="0"> +<div class="grid itemsStart min-content" data-expected-width="40" data-expected-height="0"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="0">XX XX XX</div> </div> -<div class="grid min-content min-height-50" data-expected-width="40" data-expected-height="50"> +<div class="grid itemsStart min-content min-height-50" data-expected-width="40" data-expected-height="50"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="50">XX X</div> </div> -<div class="grid min-content min-height-fit-content" data-expected-width="40" data-expected-height="100"> +<div class="grid itemsStart min-content min-height-fit-content" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX XXXX</div> </div> <div style="height: 200px;"> - <div class="grid min-content min-height-fill-available" data-expected-width="40" data-expected-height="200"> + <div class="grid itemsStart min-content min-height-fill-available" data-expected-width="40" data-expected-height="200"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXXX XXXX XXX</div> </div> </div> -<div class="grid min-content min-height-min-content" data-expected-width="40" data-expected-height="0"> +<div class="grid itemsStart min-content min-height-min-content" data-expected-width="40" data-expected-height="0"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="0">XX XXX</div> </div> -<div class="grid min-content min-height-35" data-expected-width="40" data-expected-height="35"> +<div class="grid itemsStart min-content min-height-35" data-expected-width="40" data-expected-height="35"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="35">XX XX</div> </div> -<div class="grid min-content min-height-max-content" data-expected-width="40" data-expected-height="100"> +<div class="grid itemsStart min-content min-height-max-content" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">X XXX X</div> </div> -<div class="grid min-content min-height-50" data-expected-width="40" data-expected-height="50"> +<div class="grid itemsStart min-content min-height-50" data-expected-width="40" data-expected-height="50"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="50">XXXX XXXX XXXX XXXX</div> </div> -<div class="grid min-content max-height-50" data-expected-width="40" data-expected-height="0"> +<div class="grid itemsStart min-content max-height-50" data-expected-width="40" data-expected-height="0"> <div class="sizedToGridArea min-height-fill-available" data-expected-width="40" data-expected-height="50">XXXX X X XXXX</div> </div> <br> <h2>Check the behavior of grids with definite available space.</h2> -<div class="grid" style="height: 100px;" data-expected-width="40" data-expected-height="100"> +<div class="grid itemsStart" style="height: 100px;" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX X</div> </div> -<div class="grid max-height-35" style="height: 100px;" data-expected-width="40" data-expected-height="35"> +<div class="grid itemsStart max-height-35" style="height: 100px;" data-expected-width="40" data-expected-height="35"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="35">XX XX</div> </div> -<div class="grid min-height-50" style="height: 10px;" data-expected-width="40" data-expected-height="50"> +<div class="grid itemsStart min-height-50" style="height: 10px;" data-expected-width="40" data-expected-height="50"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="50">XX XXXX</div> </div> -<div class="grid min-height-50" style="height: 20px; data-expected-width="40" data-expected-height="50"> +<div class="grid itemsStart min-height-50" style="height: 20px;" data-expected-width="40" data-expected-height="50"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="50">XX XXX XX XXX XX XXX</div> </div> <div style="height: 100px;"> - <div class="grid" style="height: 37%;" data-expected-width="40" data-expected-height="37"> + <div class="grid itemsStart" style="height: 37%;" data-expected-width="40" data-expected-height="37"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="37">X X X X</div> </div> - <div class="grid min-height-50" style="height: 37%;" data-expected-width="40" data-expected-height="50"> + <div class="grid itemsStart min-height-50" style="height: 37%;" data-expected-width="40" data-expected-height="50"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="50">XX XX XX</div> </div> - <div class="grid min-height-35" style="height: 37%;" data-expected-width="40" data-expected-height="37"> + <div class="grid itemsStart min-height-35" style="height: 37%;" data-expected-width="40" data-expected-height="37"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="37">XXXX</div> </div> </div> @@ -202,58 +200,58 @@ <br> <h2>Check the behavior of grids with indefinite available space.</h2> <div class="fit-content"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX</div> </div> - <div class="grid min-height-35" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart min-height-35" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">X XXXX X</div> </div> - <div class="grid max-height-min-content" data-expected-width="40" data-expected-height="0"> + <div class="grid itemsStart max-height-min-content" data-expected-width="40" data-expected-height="0"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="0">XX XX XX</div> </div> - <div class="grid fit-content" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart fit-content" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">X XX X</div> </div> </div> <div class="fit-content" style="height: 125px;"> - <div class="grid fill-available" data-expected-width="40" data-expected-height="125"> + <div class="grid itemsStart fill-available" data-expected-width="40" data-expected-height="125"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">X XX X</div> </div> </div> <div class="fit-content min-height-50"> - <div class="grid" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XXXX XX X XXX</div> </div> - <div class="grid min-height-35" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart min-height-35" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XXXX X X</div> </div> - <div class="grid max-height-min-content" data-expected-width="40" data-expected-height="0"> + <div class="grid itemsStart max-height-min-content" data-expected-width="40" data-expected-height="0"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="0">X XXX XX</div> </div> - <div class="grid fit-content" data-expected-width="40" data-expected-height="100"> + <div class="grid itemsStart fit-content" data-expected-width="40" data-expected-height="100"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="100">XX XXX XX X</div> </div> </div> <div class="fit-content min-height-50" style="height: 75px;"> - <div class="grid fill-available" data-expected-width="40" data-expected-height="75"> + <div class="grid itemsStart fill-available" data-expected-width="40" data-expected-height="75"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="75">XX X</div> </div> </div> <div style="height: 25px;"> - <div class="grid fit-content" data-expected-width="40" data-expected-height="25"> + <div class="grid itemsStart fit-content" data-expected-width="40" data-expected-height="25"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="25">XX X</div> </div> - <div class="grid fill-available" data-expected-width="40" data-expected-height="25"> + <div class="grid itemsStart fill-available" data-expected-width="40" data-expected-height="25"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="25">XX X</div> </div> - <div class="grid fit-content min-height-35" data-expected-width="40" data-expected-height="35"> + <div class="grid itemsStart fit-content min-height-35" data-expected-width="40" data-expected-height="35"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="35">XX X</div> </div> - <div class="grid fit-content max-height-min-content" data-expected-width="40" data-expected-height="0"> + <div class="grid itemsStart fit-content max-height-min-content" data-expected-width="40" data-expected-height="0"> <div class="sizedToGridArea" data-expected-width="40" data-expected-height="0">XX X</div> </div> </div>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/min-width-height-auto-and-margins.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/min-width-height-auto-and-margins.html index 35705f2..bc70b5c 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/min-width-height-auto-and-margins.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/min-width-height-auto-and-margins.html
@@ -3,6 +3,7 @@ <link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet"> <link href="../css-intrinsic-dimensions/resources/height-keyword-classes.css" rel="stylesheet"> <link href="resources/grid.css" rel="stylesheet"> +<link href="resources/grid-alignment.css" rel="stylesheet"> <style> .columnsSmallerThanContentBox { grid-template-columns: 80px; } .columnsSameAsContentBox { grid-template-columns: 100px; } @@ -29,8 +30,8 @@ background-color: blue; } -.fitContentOnlyHeight { width: auto; align-items: start; } -.fitContentOnlyWidth { height: auto; justify-items: start; } +.autoWidth { width: auto; } +.autoHeight { height: auto; } </style> <script src="../../resources/check-layout.js"></script> @@ -77,35 +78,35 @@ <!-- Checking shrink-to-fit in row-axis --> <p>Stretching allowed in column axis, fitContent in row-axis | column smaller than content-box, row bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyWidth columnsSmallerThanContentBox rowsBiggerThanMarginBox"> + <div class="grid fit-content autoHeight justifyItemsStart columnsSmallerThanContentBox rowsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="100" data-expected-height="60">XXXX</div> </div> </div> <p>Stretching allowed in column axis, fitContent in row-axis | column of same size than content-box, row bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyWidth columnsSameAsContentBox rowsBiggerThanMarginBox"> + <div class="grid fit-content autoHeight justifyItemsStart columnsSameAsContentBox rowsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="100" data-expected-height="60">XXXX</div> </div> </div> <p>Stretching allowed in column axis, fitContent in row-axis | column bigger than content-box, row bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyWidth columnsBiggerThanContentBox rowsBiggerThanMarginBox"> + <div class="grid fit-content autoHeight justifyItemsStart columnsBiggerThanContentBox rowsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="100" data-expected-height="60">XXXX</div> </div> </div> <p>Stretching allowed in column axis, fitContent in row-axis | column of same size as margin-box, row bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyWidth columnsSameAsMarginBox rowsBiggerThanMarginBox"> + <div class="grid fit-content autoHeight justifyItemsStart columnsSameAsMarginBox rowsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="100" data-expected-height="60">XXXX</div> </div> </div> <p>Stretching allowed in column axis, fitContent in row-axis | column bigger than content-box, row bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyWidth columnsBiggerThanMarginBox rowsBiggerThanMarginBox"> + <div class="grid fit-content autoHeight justifyItemsStart columnsBiggerThanMarginBox rowsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="100" data-expected-height="60">XXXX</div> </div> </div> @@ -149,35 +150,35 @@ <!-- Checking min-height: auto logic --> <p>Stretching allowed in row axis, fitContent in column-axis | row smaller than content-box, column bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyHeight rowsSmallerThanContentBox columnsBiggerThanMarginBox"> + <div class="grid fit-content autoWidth alignItemsStart rowsSmallerThanContentBox columnsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="110" data-expected-height="25">XXXX</div> </div> </div> <p>Stretching allowed in row axis, fitContent in column-axis | row of same size than content-box, column bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyHeight rowsSameAsContentBox columnsBiggerThanMarginBox"> + <div class="grid fit-content autoWidth alignItemsStart rowsSameAsContentBox columnsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="110" data-expected-height="25">XXXX</div> </div> </div> <p>Stretching allowed in column axis, fitContent in row-axis | row bigger than content-box, column bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyHeight rowsBiggerThanContentBox columnsBiggerThanMarginBox"> + <div class="grid fit-content autoWidth alignItemsStart rowsBiggerThanContentBox columnsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="110" data-expected-height="25">XXXX</div> </div> </div> <p>Stretching allowed in row axis, fitContent in column-axis | row of same size as margin-box, column bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyHeight rowsSameAsMarginBox columnsBiggerThanMarginBox"> + <div class="grid fit-content autoWidth alignItemsStart rowsSameAsMarginBox columnsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="110" data-expected-height="25">XXXX</div> </div> </div> <p>Stretching allowed in column axis, fitContent in row-axis | row bigger than margin-box, column bigger than margin-box</p> <div class="container"> - <div class="grid fit-content fitContentOnlyHeight rowsBiggerThanMarginBox columnsBiggerThanMarginBox"> + <div class="grid fit-content autoWidth alignItemsStart rowsBiggerThanMarginBox columnsBiggerThanMarginBox"> <div class="item" data-offset-x="20" data-offset-y="20" data-expected-width="110" data-expected-height="25">XXXX</div> </div> </div>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/min-width-height-auto.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/min-width-height-auto.html index 39f39d5..7f8b89d 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/min-width-height-auto.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/min-width-height-auto.html
@@ -1,6 +1,7 @@ <!DOCTYPE html> <html> <link href="resources/grid.css" rel="stylesheet"> +<link href="resources/grid-alignment.css" rel="stylesheet"> <style> .grid { grid-template-columns: 5px; @@ -36,11 +37,6 @@ max-height: 75px; } -.noStretch { - align-items: start; - justify-items: start; -} - .minHeightSmaller { min-height: 12px; } .minWidthSmaller { min-width: 12px; } </style> @@ -81,31 +77,31 @@ <!-- Verify that the same cases without 'stretch' work as expected as well. --> <div class="container"> - <div class="grid noStretch"> + <div class="grid itemsStart"> <div class="ahem" data-expected-width="100" data-expected-height="25">XXXX</div> </div> </div> <div class="container"> - <div class="grid noStretch"> + <div class="grid itemsStart"> <div class="ahem minSmaller" data-expected-width="100" data-expected-height="25">XXXX</div> </div> </div> <div class="container"> - <div class="grid noStretch"> + <div class="grid itemsStart"> <div class="ahem minBigger" data-expected-width="150" data-expected-height="75">XXXX</div> </div> </div> <div class="container"> - <div class="grid noStretch"> + <div class="grid itemsStart"> <div class="ahem maxSmaller" data-expected-width="10" data-expected-height="10">XXXX</div> </div> </div> <div class="container"> - <div class="grid noStretch"> + <div class="grid itemsStart"> <div class="ahem maxBigger" data-expected-width="100" data-expected-height="25">XXXX</div> </div> </div> @@ -118,7 +114,7 @@ </div> <div class="container"> - <div class="grid noStretch"> + <div class="grid itemsStart"> <div class="ahem" data-expected-width="75" data-expected-height="50">XXX X</div> </div> </div>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-align-items-changed.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-align-items-changed.html index 559f9fc..f9a0e259 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-align-items-changed.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-align-items-changed.html
@@ -2,7 +2,7 @@ <link href="resources/grid.css" rel="stylesheet"> <style> .grid { - grid: 100px 100px / 150px; + grid: 150px / 100px 100px; position: relative; } #fromStretch { align-items: stretch; }
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-align-self-changed.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-align-self-changed.html index 7576eb3..17ac929 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-align-self-changed.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-align-self-changed.html
@@ -2,7 +2,7 @@ <link href="resources/grid.css" rel="stylesheet"> <style> .grid { - grid: 100px 100px / 150px; + grid: 150px / 100px 100px; width: 200px; position: relative; }
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-indefinite-heights.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-indefinite-heights.html index b257d3db..3a735f9 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-indefinite-heights.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-indefinite-heights.html
@@ -2,7 +2,7 @@ <link href="resources/grid.css" rel="stylesheet"> <style> .grid { - grid: 50px / minmax(5px, 1fr) minmax(5px, 2fr); + grid: minmax(5px, 1fr) minmax(5px, 2fr) / 50px; } #fromIndefinite { height: auto; }
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-justify-items-changed.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-justify-items-changed.html index ec5b08d4..91022b1 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-justify-items-changed.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-justify-items-changed.html
@@ -2,7 +2,7 @@ <link href="resources/grid.css" rel="stylesheet"> <style> .grid { - grid: 150px / 100px 100px; + grid: 100px 100px / 150px; width: 150px; position: relative; }
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-justify-self-changed.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-justify-self-changed.html index 90371cc..271a859 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-justify-self-changed.html +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/relayout-justify-self-changed.html
@@ -2,7 +2,7 @@ <link href="resources/grid.css" rel="stylesheet"> <style> .grid { - grid: 150px / 100px 100px; + grid: 100px 100px / 150px; width: 150px; position: relative; }
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/resources/grid-alignment.css b/third_party/WebKit/LayoutTests/fast/css-grid-layout/resources/grid-alignment.css index 7b1f1efb..f765235f 100644 --- a/third_party/WebKit/LayoutTests/fast/css-grid-layout/resources/grid-alignment.css +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/resources/grid-alignment.css
@@ -40,6 +40,10 @@ .alignContentRight { align-content: right; } .alignContentFlexStart { align-content: flex-start; } .alignContentFlexEnd { align-content: flex-end; } +.alignContentSpaceBetween { align-content: space-between; } +.alignContentSpaceAround { align-content: space-around; } +.alignContentSpaceEvenly { align-content: space-evenly; } +.alignContentStretch { align-content: stretch; } /* justify-self */ .justifySelfAuto { justify-self: auto; }
diff --git a/third_party/WebKit/LayoutTests/fast/css/background-clip-text-expected.txt b/third_party/WebKit/LayoutTests/fast/css/background-clip-text-expected.txt index 2cfc793..084e4fb0 100644 --- a/third_party/WebKit/LayoutTests/fast/css/background-clip-text-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/css/background-clip-text-expected.txt
@@ -1,17 +1,13 @@ -This tests checks that the '-webkit-text' and 'text' keywords are parsed correctly in the 'background-clip' and '-webkit-background-clip' properties, and that 'background-clip' is parsed correctly in the 'background' shorthand. +This tests checks that the 'text' keyword is parsed correctly in the 'background-clip' and '-webkit-background-clip' properties, and that 'background-clip' is parsed correctly in the 'background' shorthand. On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". -PASS test("background-clip: -webkit-text", "background-clip") is "-webkit-text" -PASS test("background-clip: -webkit-text", "-webkit-background-clip") is "" PASS test("background-clip: content-box", "background-clip") is "content-box" PASS test("background-clip: padding-box", "background-clip") is "padding-box" PASS test("background-clip: border-box", "background-clip") is "border-box" PASS test("background-clip: text", "background-clip") is "" PASS test("background-clip: text", "-webkit-background-clip") is "" -PASS test("-webkit-background-clip: -webkit-text", "background-clip") is "" -PASS test("-webkit-background-clip: -webkit-text", "-webkit-background-clip") is "-webkit-text" PASS test("-webkit-background-clip: text", "background-clip") is "" PASS test("-webkit-background-clip: text", "-webkit-background-clip") is "text" PASS test("background: url() padding-box", "-webkit-background-clip") is ""
diff --git a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-listing-expected.txt index eee58dc..86b28f0 100644 --- a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -22,16 +22,6 @@ -webkit-box-pack: start -webkit-box-reflect: none -webkit-clip-path: none --webkit-column-break-after: auto --webkit-column-break-before: auto --webkit-column-break-inside: auto --webkit-column-count: auto --webkit-column-gap: normal --webkit-column-rule-color: rgb(0, 0, 0) --webkit-column-rule-style: none --webkit-column-rule-width: 0px --webkit-column-span: none --webkit-column-width: auto -webkit-filter: none -webkit-font-smoothing: auto -webkit-highlight: none @@ -124,6 +114,9 @@ bottom: auto box-shadow: none box-sizing: content-box +break-after: auto +break-before: auto +break-inside: auto buffered-rendering: auto caption-side: top clear: none @@ -134,6 +127,13 @@ color-interpolation: sRGB color-interpolation-filters: linearRGB color-rendering: auto +column-count: auto +column-gap: normal +column-rule-color: rgb(0, 0, 0) +column-rule-style: none +column-rule-width: 0px +column-span: none +column-width: auto content: cursor: auto cx: 0px @@ -224,9 +224,6 @@ padding-left: 0px padding-right: 0px padding-top: 0px -page-break-after: auto -page-break-before: auto -page-break-inside: auto paint-order: fill stroke markers perspective: none perspective-origin: 384.5px 0px
diff --git a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt index ecf65d52..2628e39 100644 --- a/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -22,16 +22,6 @@ -webkit-box-pack: start -webkit-box-reflect: none -webkit-clip-path: none --webkit-column-break-after: auto --webkit-column-break-before: auto --webkit-column-break-inside: auto --webkit-column-count: auto --webkit-column-gap: normal --webkit-column-rule-color: rgb(0, 0, 0) --webkit-column-rule-style: none --webkit-column-rule-width: 0px --webkit-column-span: none --webkit-column-width: auto -webkit-filter: none -webkit-font-smoothing: auto -webkit-highlight: none @@ -124,6 +114,9 @@ bottom: auto box-shadow: none box-sizing: content-box +break-after: auto +break-before: auto +break-inside: auto buffered-rendering: auto caption-side: top clear: none @@ -134,6 +127,13 @@ color-interpolation: sRGB color-interpolation-filters: linearRGB color-rendering: auto +column-count: auto +column-gap: normal +column-rule-color: rgb(0, 0, 0) +column-rule-style: none +column-rule-width: 0px +column-span: none +column-width: auto content: cursor: auto cx: 0px @@ -224,9 +224,6 @@ padding-left: 0px padding-right: 0px padding-top: 0px -page-break-after: auto -page-break-before: auto -page-break-inside: auto paint-order: fill stroke markers perspective: none perspective-origin: 50% 50%
diff --git a/third_party/WebKit/LayoutTests/fast/css/quirk-inherit-ua-only.html b/third_party/WebKit/LayoutTests/fast/css/quirk-inherit-ua-only.html new file mode 100644 index 0000000..dbdb531 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/css/quirk-inherit-ua-only.html
@@ -0,0 +1,19 @@ +<!DOCTYPE html> +<title>The '-internal-quirk-inherit' CSS value is only exposed to UA stylesheets</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<style> +#t1 { + color: blue; + color: -internal-quirk-inherit; +} +</style> +<table id="t1"></table> +<table style="color: -internal-quirk-inherit"></table> +<script> +test(function() { + var tables = document.querySelectorAll('table'); + assert_equals(getComputedStyle(tables[0]).color, 'rgb(0, 0, 255)'); + assert_equals(tables[1].style.color, ''); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/remove-shorthand-expected.txt b/third_party/WebKit/LayoutTests/fast/css/remove-shorthand-expected.txt index a34a613e..637573fd 100644 --- a/third_party/WebKit/LayoutTests/fast/css/remove-shorthand-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/css/remove-shorthand-expected.txt
@@ -42,10 +42,10 @@ removes "border-spacing" and adds "". Removing -webkit-columns -removes "-webkit-column-width, -webkit-column-count" +removes "column-width, column-count" and adds "". Removing -webkit-column-rule -removes "-webkit-column-rule-width, -webkit-column-rule-style, -webkit-column-rule-color" +removes "column-rule-width, column-rule-style, column-rule-color" and adds "". Removing list-style removes "list-style"
diff --git a/third_party/WebKit/LayoutTests/fast/css/remove-shorthand.html b/third_party/WebKit/LayoutTests/fast/css/remove-shorthand.html index 42d8efc..61610e59 100644 --- a/third_party/WebKit/LayoutTests/fast/css/remove-shorthand.html +++ b/third_party/WebKit/LayoutTests/fast/css/remove-shorthand.html
@@ -19,8 +19,8 @@ -webkit-margin-collapse: collapse; \ padding: 2em; \ -webkit-text-stroke: orange 1pt; \ - -webkit-columns: 100px 3; \ - -webkit-column-rule: thick dashed silver; \ + columns: 100px 3; \ + column-rule: thick dashed silver; \ border-spacing: 10px 20px; \ -webkit-border-radius: 6px 8px; \ overflow: scroll; \
diff --git a/third_party/WebKit/LayoutTests/fast/css/script-tests/background-clip-text.js b/third_party/WebKit/LayoutTests/fast/css/script-tests/background-clip-text.js index 76a6ceebb..a0e60e3 100644 --- a/third_party/WebKit/LayoutTests/fast/css/script-tests/background-clip-text.js +++ b/third_party/WebKit/LayoutTests/fast/css/script-tests/background-clip-text.js
@@ -1,4 +1,4 @@ -description("This tests checks that the '-webkit-text' and 'text' keywords are \ +description("This tests checks that the 'text' keyword is \ parsed correctly in the 'background-clip' and '-webkit-background-clip' \ properties, and that 'background-clip' is parsed correctly in the \ 'background' shorthand."); @@ -13,8 +13,6 @@ document.body.removeChild(div); return result; } -shouldBe('test("background-clip: -webkit-text", "background-clip")', '"-webkit-text"'); -shouldBeEqualToString('test("background-clip: -webkit-text", "-webkit-background-clip")', ''); shouldBe('test("background-clip: content-box", "background-clip")', '"content-box"'); shouldBe('test("background-clip: padding-box", "background-clip")', '"padding-box"'); @@ -23,9 +21,6 @@ shouldBeEqualToString('test("background-clip: text", "background-clip")', ''); shouldBeEqualToString('test("background-clip: text", "-webkit-background-clip")', ''); -shouldBeEqualToString('test("-webkit-background-clip: -webkit-text", "background-clip")', ''); -shouldBe('test("-webkit-background-clip: -webkit-text", "-webkit-background-clip")', '"-webkit-text"'); - shouldBeEqualToString('test("-webkit-background-clip: text", "background-clip")', ''); shouldBe('test("-webkit-background-clip: text", "-webkit-background-clip")', '"text"'); shouldBeEqualToString('test("background: url() padding-box", "-webkit-background-clip")', '');
diff --git a/third_party/WebKit/LayoutTests/fast/css/style-enumerate-properties.html b/third_party/WebKit/LayoutTests/fast/css/style-enumerate-properties.html index 6d1cd22..350420cd 100644 --- a/third_party/WebKit/LayoutTests/fast/css/style-enumerate-properties.html +++ b/third_party/WebKit/LayoutTests/fast/css/style-enumerate-properties.html
@@ -42,7 +42,7 @@ testFailed("Invalid CSS-mapped property order: '" + p + "' after '" + previous + "'"); break; } - if (++cssPropertyCount <= 100) + if (++cssPropertyCount <= 150) previous = p; else { if (seenFilter)
diff --git a/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-in-object-model-expected.txt b/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-in-object-model-expected.txt index 8eca5ec..955cf76 100644 --- a/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-in-object-model-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-in-object-model-expected.txt
@@ -10,7 +10,7 @@ PASS style.removeProperty("--foo") is "papayawhip" PASS style.getPropertyValue("--foo") is "" PASS computedStyle.setProperty("--error", "") threw exception NoModificationAllowedError: Failed to execute 'setProperty' on 'CSSStyleDeclaration': These styles are computed, and therefore the '--error' property is read-only.. -PASS cssText is "#test2 { --variable: value; }" +PASS cssText is "#test2 { --variable:value; }" PASS successfullyParsed is true TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-in-object-model.html b/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-in-object-model.html index ab4ea32..66c81c9 100644 --- a/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-in-object-model.html +++ b/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-in-object-model.html
@@ -33,5 +33,5 @@ '"NoModificationAllowedError: Failed to execute \'setProperty\' on \'CSSStyleDeclaration\': These styles are computed, and therefore the \'--error\' property is read-only."'); var cssText = document.styleSheets[0].rules[1].cssText; -shouldBeEqualToString('cssText', '#test2 { --variable: value; }') -</script> \ No newline at end of file +shouldBeEqualToString('cssText', '#test2 { --variable:value; }') +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-serialization-quirks-mode.html b/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-serialization-quirks-mode.html index 011922f..0fad2d6 100644 --- a/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-serialization-quirks-mode.html +++ b/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-serialization-quirks-mode.html
@@ -21,8 +21,8 @@ <script> test(function() { - assert_equals(document.styleSheets[0].cssRules[0].cssText, "#test1 { --camelCase: blue; color: var(--camelCase); }"); - assert_equals(document.styleSheets[0].cssRules[1].cssText, "#test2 { --Aå: 100px; width: var(--Aå); }"); + assert_equals(document.styleSheets[0].cssRules[0].cssText, "#test1 { --camelCase: blue; color: var(--camelCase); }"); + assert_equals(document.styleSheets[0].cssRules[1].cssText, "#test2 { --Aå: 100px; width: var(--Aå); }"); assert_equals(document.styleSheets[0].cssRules[0].style.getPropertyValue("--camelCase"), " blue"); assert_equals(document.styleSheets[0].cssRules[0].style.getPropertyValue("color"), "var(--camelCase)"); assert_equals(document.styleSheets[0].cssRules[1].style.getPropertyValue("--Aå"), " 100px"); @@ -30,4 +30,4 @@ assert_equals(document.styleSheets[0].cssRules[2].style.getPropertyValue("background"), "var(--colour)"); assert_equals(document.styleSheets[0].cssRules[2].style.getPropertyValue("color"), "var(--color)"); }, "Custom properties serialization"); -</script> \ No newline at end of file +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-serialization.html b/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-serialization.html index e7257f6..ebbbd512 100644 --- a/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-serialization.html +++ b/third_party/WebKit/LayoutTests/fast/css/variables/custom-properties-serialization.html
@@ -22,8 +22,8 @@ <script> test(function() { - assert_equals(document.styleSheets[0].cssRules[0].cssText, "#test1 { --camelCase: blue; color: var(--camelCase); }"); - assert_equals(document.styleSheets[0].cssRules[1].cssText, "#test2 { --Aå: 100px; width: var(--Aå); }"); + assert_equals(document.styleSheets[0].cssRules[0].cssText, "#test1 { --camelCase: blue; color: var(--camelCase); }"); + assert_equals(document.styleSheets[0].cssRules[1].cssText, "#test2 { --Aå: 100px; width: var(--Aå); }"); assert_equals(document.styleSheets[0].cssRules[0].style.getPropertyValue("--camelCase"), " blue"); assert_equals(document.styleSheets[0].cssRules[0].style.getPropertyValue("color"), "var(--camelCase)"); assert_equals(document.styleSheets[0].cssRules[1].style.getPropertyValue("--Aå"), " 100px"); @@ -31,4 +31,4 @@ assert_equals(document.styleSheets[0].cssRules[2].style.getPropertyValue("background"), "var(--colour)"); assert_equals(document.styleSheets[0].cssRules[2].style.getPropertyValue("color"), "var(--color)"); }, "Custom properties serialization"); -</script> \ No newline at end of file +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/variables/multiple-writes-to-inline-style.html b/third_party/WebKit/LayoutTests/fast/css/variables/multiple-writes-to-inline-style.html index 4217352..a2d1938b 100644 --- a/third_party/WebKit/LayoutTests/fast/css/variables/multiple-writes-to-inline-style.html +++ b/third_party/WebKit/LayoutTests/fast/css/variables/multiple-writes-to-inline-style.html
@@ -9,10 +9,10 @@ testElem.style.setProperty('--foo', 'first'); assert_equals(testElem.style.getPropertyValue('--foo'), 'first'); assert_equals(getComputedStyle(testElem).getPropertyValue('--foo'), 'first'); - assert_equals(testElem.getAttribute('style'), '--foo: first;'); + assert_equals(testElem.getAttribute('style'), '--foo:first;'); testElem.style.setProperty('--foo', 'second'); assert_equals(testElem.style.getPropertyValue('--foo'), 'second'); - assert_equals(testElem.getAttribute('style'), '--foo: second;'); + assert_equals(testElem.getAttribute('style'), '--foo:second;'); }, "subsequent writes to inline style overwrite older values.") </script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/variables/round-trip-inline-style.html b/third_party/WebKit/LayoutTests/fast/css/variables/round-trip-inline-style.html new file mode 100644 index 0000000..6fb9b22a --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/css/variables/round-trip-inline-style.html
@@ -0,0 +1,15 @@ +<!DOCTYPE html> +<script src="../../../resources/testharness.js"></script> +<script src="../../../resources/testharnessreport.js"></script> +<div id='testElem'></div> +<script> + +test(function() { + testElem.style.setProperty('--bar', 'value'); + testElem.setAttribute('style', testElem.getAttribute('style')); + assert_equals(testElem.style.getPropertyValue('--bar'), 'value'); + testElem.style.setProperty('--bar', ' value'); + testElem.setAttribute('style', testElem.getAttribute('style')); + assert_equals(testElem.style.getPropertyValue('--bar'), ' value'); +}, "round tripping style works for variables."); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/webkit-text-display-none-expected.txt b/third_party/WebKit/LayoutTests/fast/css/webkit-text-display-none-expected.txt deleted file mode 100644 index 8df776c..0000000 --- a/third_party/WebKit/LayoutTests/fast/css/webkit-text-display-none-expected.txt +++ /dev/null
@@ -1,10 +0,0 @@ -This test checks that setting display to none on the body element does not change -webkit-text. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -PASS getComputedStyle(d).getPropertyValue('color') is 'rgb(0, 128, 0)' -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/fast/css/webkit-text-display-none.html b/third_party/WebKit/LayoutTests/fast/css/webkit-text-display-none.html deleted file mode 100644 index 3b23146..0000000 --- a/third_party/WebKit/LayoutTests/fast/css/webkit-text-display-none.html +++ /dev/null
@@ -1,19 +0,0 @@ -<html> -<head> -<script src="../../resources/js-test.js"></script> -</head> -<body style="display:none"> -<script type="text/javascript"> -description("This test checks that setting display to none on the body element does not change -webkit-text."); - -document.body.style.color = "green"; - -var d = document.createElement("div"); -d.style.color = "-webkit-text"; -document.documentElement.appendChild(d); - -shouldBe("getComputedStyle(d).getPropertyValue('color')", "'rgb(0, 128, 0)'"); -document.body.style.display = "block"; -</script> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Document/document-charset-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Document/document-charset-expected.txt index 54f8f75d..2b5fb1e 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/Document/document-charset-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/dom/Document/document-charset-expected.txt
@@ -1,10 +1,7 @@ -CONSOLE WARNING: 'Document.defaultCharset' is deprecated and will be removed in M50, around April 2016. See https://www.chromestatus.com/features/6217124578066432 for more details. Initial document.charset: KOI8-R - document.defaultCharset: defined - document.characterSet: KOI8-R document.inputEncoding: KOI8-R @@ -13,8 +10,6 @@ document.charset: KOI8-R - document.defaultCharset: defined - document.characterSet: KOI8-R document.inputEncoding: KOI8-R
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Document/document-charset.html b/third_party/WebKit/LayoutTests/fast/dom/Document/document-charset.html index 1594696c..d417528 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/Document/document-charset.html +++ b/third_party/WebKit/LayoutTests/fast/dom/Document/document-charset.html
@@ -11,14 +11,12 @@ document.write("<p>Initial</p>"); document.write("<p> document.charset: " + document.charset + "</p>"); // MSIE -document.write("<p> document.defaultCharset: " + (document.defaultCharset ? "defined" : "undefined") + "</p>"); // MSIE document.write("<p> document.characterSet: " + document.characterSet + "</p>"); // Firefox document.write("<p> document.inputEncoding: " + document.inputEncoding + "</p>"); // DOM3 document.write("<p>Setting charset to UTF-8... (expected to have no effect)</p>"); document.charset = "utf-8"; document.write("<p> document.charset: " + document.charset + "</p>"); -document.write("<p> document.defaultCharset: " + (document.defaultCharset ? "defined" : "undefined") + "</p>"); document.write("<p> document.characterSet: " + document.characterSet + "</p>"); document.write("<p> document.inputEncoding: " + document.inputEncoding + "</p>");
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Range/collapsed-range-bounding-client-rect-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Range/collapsed-range-bounding-client-rect-expected.txt new file mode 100644 index 0000000..e25c7934 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/dom/Range/collapsed-range-bounding-client-rect-expected.txt
@@ -0,0 +1,7 @@ +Tests that Range::getBoundingClientRect returns top and left coordinates for collapsed ranges as mandated by the spec. + +This is a testharness.js-based test. +PASS Check position for non-collapsed range. +PASS Check position for collapsed range. +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Range/collapsed-range-bounding-client-rect.html b/third_party/WebKit/LayoutTests/fast/dom/Range/collapsed-range-bounding-client-rect.html new file mode 100644 index 0000000..aaee747e --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/dom/Range/collapsed-range-bounding-client-rect.html
@@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html> + <head> + <title>Tests getBoundingClientRect for collapsed ranges</title> + <script src="../../../resources/testharness.js"></script> + <script src="../../../resources/testharnessreport.js"></script> + </head> + <body> + <p> + Tests that Range::getBoundingClientRect returns top and left + coordinates for collapsed ranges as mandated by the spec. + </p> + <script> + function topLeftPosition(rect) + { + return rect.top + ', ' + rect.left; + } + + function topRightPosition(rect) + { + return rect.top + ', ' + rect.right; + } + + var textNode = document.getElementsByTagName('p')[0].firstChild; + var stringToMeasure = 'Range::getBoundingClientRect'; + var startIndex = textNode.textContent.indexOf(stringToMeasure); + var endIndex = startIndex + stringToMeasure.length; + + test(function() { + var range = document.createRange(); + range.setStart(textNode, startIndex); + range.setEnd(textNode, endIndex); + + assert_equals( + topLeftPosition(range.getBoundingClientRect()), + topLeftPosition(range.getClientRects()[0]), + 'Position of bounding rect should match first rect.'); + }, 'Check position for non-collapsed range.'); + + test(function() { + var range = document.createRange(); + range.setStart(textNode, startIndex); + range.setEnd(textNode, endIndex); + + var collapsed = document.createRange(); + collapsed.setStart(textNode, startIndex); + collapsed.setEnd(textNode, endIndex); + collapsed.collapse(); + + assert_equals( + topLeftPosition(collapsed.getBoundingClientRect()), + topLeftPosition(collapsed.getClientRects()[0]), + 'Position of bounding rect should match first rect.'); + + assert_equals( + topLeftPosition(collapsed.getBoundingClientRect()), + topRightPosition(range.getBoundingClientRect()), + 'Position of collapsed rect should match right edge of ' + + 'non-collapsed range.'); + }, 'Check position for collapsed range.'); + </script> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/document-attribute-js-null-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/document-attribute-js-null-expected.txt index ba143dad..5df2b39 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/document-attribute-js-null-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/dom/document-attribute-js-null-expected.txt
@@ -1,10 +1,8 @@ -CONSOLE WARNING: 'Document.defaultCharset' is deprecated and will be removed in M50, around April 2016. See https://www.chromestatus.com/features/6217124578066432 for more details. This test setting various attributes of documents to JavaScript null. TEST SUCCEEDED: Got the expected exception (9). [tested Document.xmlVersion] TEST SUCCEEDED: The value was null. [tested Document.documentURI] TEST SUCCEEDED: The value was the string 'UTF-8'. [tested Document.charset] -TEST SUCCEEDED: The value was undefined. [tested Document.defaultCharset] TEST SUCCEEDED: The value was the string 'UTF-8'. [tested Document.characterSet] TEST SUCCEEDED: The value was the string 'UTF-8'. [tested Document.inputEncoding] TEST SUCCEEDED: The value was null. [tested Document.selectedStylesheetSet]
diff --git a/third_party/WebKit/LayoutTests/fast/dom/document-attribute-js-null.html b/third_party/WebKit/LayoutTests/fast/dom/document-attribute-js-null.html index 73e03f1a5..e9acb7e 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/document-attribute-js-null.html +++ b/third_party/WebKit/LayoutTests/fast/dom/document-attribute-js-null.html
@@ -69,7 +69,6 @@ {name: 'xmlVersion', expectedExceptionCode: 9}, {name: 'documentURI', expectedNull: null}, {name: 'charset', expectedNull: 'UTF-8'}, - {name: 'defaultCharset', expectedNull: undefined}, {name: 'characterSet', expectedNull: 'UTF-8'}, {name: 'inputEncoding', expectedNull: 'UTF-8'}, {name: 'selectedStylesheetSet', expectedNull: null}
diff --git a/third_party/WebKit/LayoutTests/fast/dom/everything-to-string-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/everything-to-string-expected.txt index 1081ee9..3c66e8cfd 100644 --- a/third_party/WebKit/LayoutTests/fast/dom/everything-to-string-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/dom/everything-to-string-expected.txt
@@ -1,6 +1,5 @@ CONSOLE WARNING: 'window.webkitStorageInfo' is deprecated. Please use 'navigator.webkitTemporaryStorage' or 'navigator.webkitPersistentStorage' instead. CONSOLE WARNING: 'webkitIndexedDB' is deprecated. Please use 'indexedDB' instead. -CONSOLE WARNING: 'Document.defaultCharset' is deprecated and will be removed in M50, around April 2016. See https://www.chromestatus.com/features/6217124578066432 for more details. Test for bug 8131: Some properties and methods of window and document objects cannot be converted to a string. Should say SUCCESS:
diff --git a/third_party/WebKit/LayoutTests/fast/dom/shadow/inherit-into-slotted-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/shadow/inherit-into-slotted-expected.txt new file mode 100644 index 0000000..2f2c33c --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/dom/shadow/inherit-into-slotted-expected.txt
@@ -0,0 +1,11 @@ +A changed inherited property on a slot parent should propagate down to slotted elements. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS getComputedStyle(s1).color is "rgb(0, 0, 0)" +PASS getComputedStyle(s1).color is "rgb(0, 128, 0)" +PASS successfullyParsed is true + +TEST COMPLETE +This text should be green
diff --git a/third_party/WebKit/LayoutTests/fast/dom/shadow/inherit-into-slotted.html b/third_party/WebKit/LayoutTests/fast/dom/shadow/inherit-into-slotted.html new file mode 100644 index 0000000..9e0e4a3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/dom/shadow/inherit-into-slotted.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<script src="../../../resources/js-test.js"></script> +<div id="host"> + <div slot="s1">This text should be green</div> +</div> +<script> + description("A changed inherited property on a slot parent should propagate down to slotted elements."); + + var root = host.attachShadow({mode:"open"}); + root.innerHTML = '<style>.p1 { color: green }</style><div id="p1"><slot name="s1"></slot></div>'; + var p1 = root.querySelector("#p1"); + var s1 = host.querySelector("[slot]"); + host.offsetTop; + shouldBeEqualToString("getComputedStyle(s1).color", "rgb(0, 0, 0)"); + p1.className = "p1"; + shouldBeEqualToString("getComputedStyle(s1).color", "rgb(0, 128, 0)"); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-cancel-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-cancel-expected.txt index de48ed76..270069b 100644 --- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-cancel-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-cancel-expected.txt
@@ -3,9 +3,6 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". -PASS successfullyParsed is true - -TEST COMPLETE PASS actualWheelEventsOccurred is 0 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-cancel.html b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-cancel.html index 22f414d..62aece2 100644 --- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-cancel.html +++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/pad-gesture-cancel.html
@@ -2,13 +2,23 @@ <html> <head> <script src="../../../../resources/js-test.js"></script> -<script src="../../../../resources/run-after-layout-and-paint.js"></script> <script> description("Tests basic use of GestureFlingCancel"); var expectedWheelEventsOccurred = "0"; var actualWheelEventsOccurred = 0; + function notifyWhenFlingIsOver() { + if (!window.testRunner) + return; + if (eventSender.isFlinging()) { + window.setTimeout(notifyWhenFlingIsOver, 0); + return; + } + shouldBe('actualWheelEventsOccurred', expectedWheelEventsOccurred); + testRunner.notifyDone(); + } + function recordWheelEvent(event) { shouldBe('event.clientX', "10"); @@ -18,22 +28,18 @@ shouldBeTrue("event.wheelDeltaX > 5"); shouldBeTrue("event.wheelDeltaY > 5"); actualWheelEventsOccurred++; + shouldBeTrue("actualWheelEventsOccurred == 0"); + if (window.testRunner) + testRunner.notifyDone(); } - document.addEventListener("mousewheel", recordWheelEvent); - if (window.testRunner && window.eventSender && window.eventSender.gestureFlingStart) { eventSender.gestureFlingStart(10, 11, 1000, 1000, "touchpad"); eventSender.gestureFlingCancel(); + document.addEventListener("mousewheel", recordWheelEvent); testRunner.waitUntilDone(); + notifyWhenFlingIsOver(); } - - runAfterLayoutAndPaint(function() { - shouldBe('actualWheelEventsOccurred', expectedWheelEventsOccurred); - isSuccessfullyParsed(); - if (window.testRunner) - testRunner.notifyDone(); - }); </script> </head> <body>
diff --git a/third_party/WebKit/LayoutTests/fast/events/wheel-fling-cancel-expected.txt b/third_party/WebKit/LayoutTests/fast/events/wheel-fling-cancel-expected.txt new file mode 100644 index 0000000..874516d --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/events/wheel-fling-cancel-expected.txt
@@ -0,0 +1,10 @@ +Tests that a mouse wheel event will cancel an in progress fling gesture. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS actualWheelEventsOccurred is 0 +PASS successfullyParsed is true + +TEST COMPLETE +
diff --git a/third_party/WebKit/LayoutTests/fast/events/wheel-fling-cancel.html b/third_party/WebKit/LayoutTests/fast/events/wheel-fling-cancel.html new file mode 100644 index 0000000..4cccbb5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/events/wheel-fling-cancel.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> +<script src="../../resources/js-test.js"></script> +<script> + description("Tests that a mouse wheel event will cancel an in progress fling gesture."); + + var expectedWheelEventsOccurred = "0"; + var actualWheelEventsOccurred = 0; + + function notifyWhenFlingIsOver() { + if (!window.testRunner) + return; + if (eventSender.isFlinging()) { + window.setTimeout(notifyWhenFlingIsOver, 0); + return; + } + shouldBe('actualWheelEventsOccurred', expectedWheelEventsOccurred); + testRunner.notifyDone(); + } + + function recordWheelEvent(event) + { + shouldBe('event.clientX', "10"); + shouldBe('event.clientY', "11"); + + // Test deliberately does not equality check wheelDeltas to not be fragile in the face of curve adjustment. + shouldBeTrue("event.wheelDeltaX > 5"); + shouldBeTrue("event.wheelDeltaY > 5"); + actualWheelEventsOccurred++; + shouldBeTrue("actualWheelEventsOccurred == 0"); + if (window.testRunner) + testRunner.notifyDone(); + } + + if (window.testRunner && window.eventSender && window.eventSender.gestureFlingStart) { + eventSender.gestureFlingStart(10, 11, 1000, 1000, "touchpad"); + window.eventSender.mouseMoveTo(100, 100); + window.eventSender.mouseScrollBy(0, 1); + document.addEventListener("mousewheel", recordWheelEvent); + testRunner.waitUntilDone(); + notifyWhenFlingIsOver(); + } + +</script> +</head> +<body> +<span id="e"></span> +<span id="f"></span> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-rotate-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-rotate-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-rotate-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-rotate.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-rotate.html new file mode 100644 index 0000000..eb960b9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-animate-rotate.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> +<style> + @-webkit-keyframes rotate { + 0% { -webkit-transform: rotate(0deg) } + } + + img { + -webkit-transform: rotate(90deg); + } +</style> + +<!-- The blue sector of the image should be at 3 o'clock (viz., rotated by 90 deg). --> +<img onload="rotate(this)" width="400px" src="resources/red-at-12-oclock-with-color-profile.jpg"> + +<script> +function rotate(element) { + if (window.testRunner) + setTimeout(function() { testRunner.setColorProfile('sRGB', new Function()) }, 0); + + element.addEventListener('webkitAnimationStart', start, false); + element.addEventListener('webkitAnimationEnd', end, false); + + if (window.testRunner) + element.style.cssText += '-webkit-animation: rotate linear 1s'; + else + element.style.cssText += '-webkit-animation: rotate linear 4s'; +} + +function start(event) { + if (window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', new Function()) }, 100); +} + +function end(event) { + if (window.testRunner) + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-animate.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-animate.html new file mode 100644 index 0000000..2dc27eb --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-animate.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> +<style> + .fade { + -webkit-animation: fader linear 0.5s + } + + @-webkit-keyframes fader { + 0% { opacity: 1.0 } + 50% { opacity: 0.0 } + } +</style> + +<!-- The blue sector of the image should be at 12 o'clock. --> +<img onload="fade(this)" width="400px" src="resources/red-at-12-oclock-with-color-profile.jpg"> + +<script> +function fade(element) { + element.addEventListener('webkitAnimationEnd', end, false); + element.classList.toggle('fade'); +} + +function end(event) { + if (window.testRunner) + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-clip-text-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-clip-text-expected.txt new file mode 100644 index 0000000..10f9a06 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-clip-text-expected.txt
@@ -0,0 +1,3 @@ +▅▅▅▅ + +▅▅▅▅
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-clip-text.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-clip-text.html new file mode 100644 index 0000000..e5e0ae8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-clip-text.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + p { + -webkit-text-fill-color: transparent; + background: url("resources/red-at-12-oclock-with-color-profile.jpg") -10px 100px; + -webkit-background-clip: text; + + font: 147pt Ahem, sans-serif; + position: relative; + top: -220pt; + } + + p + p { + top: -480pt; + } +</style> + +<!-- There should be no red on this page. --> +<body style="overflow: hidden"> + <p>▅▅▅▅</p><p>▅▅▅▅</p> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cover-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cover-expected.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cover-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cover.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cover.html new file mode 100644 index 0000000..dc5ff2a9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cover.html
@@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + div { + background: url("resources/red-at-12-oclock-with-color-profile.jpg") no-repeat; + background-size: cover; + display: inline-block; + height: 376px; + width: 48%; + } + div, img { + margin: 5px; + } +</style> + +<body> + <!-- The _blue_ sector of the <img> image should be at 12 o'clock. --> + <img width="48%" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <!-- Same for the <div> background image: should be at 12 o'clock. --> + <div></div> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-expected.txt new file mode 100644 index 0000000..1a4baf5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-png-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-png-expected.txt new file mode 100644 index 0000000..1a4baf5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-png-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-png.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-png.html new file mode 100644 index 0000000..3f0d28f --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade-png.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + p { + background-image: -webkit-cross-fade( + url("resources/red-at-12-oclock-with-color-profile.png"), url("resources/green-256x256.jpg"), 6%); + margin: 6px 6px 6px 9px; + display: inline-block; + height: 180px; + width: 372px; + } + + p:nth-child(even) { + transform: scale(-1,1); + } +</style> + +<body style="overflow: hidden"> + <!-- The blue sector of the background images should be at 12 o'clock. --> + <p></p> + <p></p> + <p></p> + <p></p> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade.html new file mode 100644 index 0000000..3cc2ddf --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-cross-fade.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + p { + background-image: -webkit-cross-fade( + url("resources/red-at-12-oclock-with-color-profile.jpg"), url("resources/green-256x256.jpg"), 6%); + margin: 6px 6px 6px 9px; + display: inline-block; + height: 180px; + width: 372px; + } + + p:nth-child(even) { + transform: scale(-1,1); + } +</style> + +<body style="overflow: hidden"> + <!-- The blue sector of the background images should be at 12 o'clock. --> + <p></p> + <p></p> + <p></p> + <p></p> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-repeat-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-repeat-expected.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-repeat-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-repeat.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-repeat.html new file mode 100644 index 0000000..d468dd1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-repeat.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + div { + background-image: url("resources/red-at-12-oclock-with-color-profile.jpg"); + background-size: 50% 50%; + background-repeat: repeat; + display: inline-block; + height: 376px; + width: 48%; + } + div, img { + margin: 5px; + } +</style> + +<body> + <!-- The _blue_ sector of the <img> image should be at 12 o'clock. --> + <img width="48%" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <!-- Same for the <div> background image: should be at 12 o'clock. --> + <div></div> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-space-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-space-expected.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-space-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-space.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-space.html new file mode 100644 index 0000000..3325f837 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-background-image-space.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + div { + background-image: url("resources/red-at-12-oclock-with-color-profile.jpg"); + background-size: 49% 49%; + background-repeat: space; + display: inline-block; + height: 376px; + width: 48%; + } + div, img { + margin: 5px; + } +</style> + +<body> + <!-- The _blue_ sector of the <img> image should be at 12 o'clock. --> + <img width="48%" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <!-- Same for the <div> background image: should be at 12 o'clock. --> + <div></div> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-border-fade-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-fade-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-fade-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-border-fade.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-fade.html new file mode 100644 index 0000000..afca2c0c --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-fade.html
@@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<style> + .fade { + -webkit-animation: fader linear 100000s infinite 10ms + } + + .border { + border: 5px solid transparent; + border-radius: 200px; + } + + @-webkit-keyframes fader { + 0% { opacity: 1.0 } + 50% { opacity: 0.0 } + } +</style> + +<!-- The blue sector of the image should be at 12 o'clock. The image should be contained + within its CSS border radius circle: it should not look square. --> +<img onload="fade(this)" width="400px" src="resources/red-at-12-oclock-with-color-profile.jpg"> + +<script> +function fade(element) { + element.addEventListener('webkitAnimationStart', start, false); + element.classList.toggle('fade'); +} + +function start(event) { + event.target.classList.toggle('border'); + + if (window.testRunner) + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.setColorProfile('whacked', new Function()); + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-source-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-source-expected.txt new file mode 100644 index 0000000..ab45984 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-source-expected.txt
@@ -0,0 +1,2 @@ + +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-source.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-source.html new file mode 100644 index 0000000..72572632 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image-source.html
@@ -0,0 +1,58 @@ +<!DOCTYPE html> +<html> +<head> +<style> + div { + border-width: 21px 30px 30px 21px; + width: 75px; + height: 75px; + margin: 10px; + display: inline-block; + border-image-source: url("resources/border-image-srgb-color-profile.png") !important + } + + div.rr { + -webkit-border-image: none 21 30 30 21 repeat repeat; + } + + div.rs { + -webkit-border-image: none 21 30 30 21 repeat stretch; + } + + div.sr { + -webkit-border-image: none 21 30 30 21 stretch repeat; + } + + div.ss { + -webkit-border-image: none 21 30 30 21 stretch stretch; + } +</style> +</head> + +<body> + <div class="rr"></div> + <div class="rs"></div> + <br> + <div class="sr"></div> + <div class="ss"></div> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +}; + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image.html new file mode 100644 index 0000000..e8f862ea --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-image.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + div { + -webkit-border-image: url("resources/red-at-12-oclock-with-color-profile.jpg") 8 2 round; + height: 320px; + width: 90%; + } +</style> + +<!-- The blue sector of the <div> background image should be at 12 o'clock. --> +<div></div> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-border-radius-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-radius-expected.txt new file mode 100644 index 0000000..600b53fd --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-radius-expected.txt
@@ -0,0 +1,3 @@ + + +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-border-radius.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-radius.html new file mode 100644 index 0000000..343839b --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-border-radius.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> +<style> + .border { + border: 5px solid transparent; /* So the <img> under test can not be a directly composited. */ + border-radius: 220px; + height: 240px; + } +</style> + +<body> + <!-- The blue sector of the test images should be at 12 o'clock. The images should be + contained within their CSS border radius circle: they should not look square. --> + <img onload="load(this)" title="png image" src="resources/red-at-12-oclock-with-color-profile.png"> + <img onload="load(this)" title="jpeg image" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img onload="load(this)" title="webp image" src="resources/webp-color-profile-lossy.webp"> + <br> + <!-- The blue sector of the test images should be at 12 o'clock. The images should be + contained within their CSS border radius circle: they should not look square. --> + <img onload="load(this)" title="png image" src="resources/red-at-12-oclock-with-color-profile.png"> + <img onload="load(this)" title="jpeg image" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img onload="load(this)" title="webp image" src="resources/webp-color-profile-lossy.webp"> + <br> +</body> + +<script> +var images = 0; + +function load(element) { + element.classList.add('border'); + + if (++images == 6 && window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-clip-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-clip-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-clip-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-clip.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-clip.html new file mode 100644 index 0000000..1d05bd4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-clip.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<style> + .t0 { -webkit-transform: translate(-60px, 0px); } + .t1 { -webkit-transform: translate(405px, 0px); } + .t2 { top: -462px; left: 240px; -webkit-transform: rotate(-90deg); } + + img { + position: absolute; + clip: rect(30px, 300px, 240px, 90px); + } +</style> + +<body style="overflow: hidden"> + <!-- There should be no red on this page. --> + <img class="t0" title="jpg image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img class="t1" title="png image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.png"> + <img class="t2" title="jpg image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.jpg"> +</body> + +<script> +var images = 0; + +function load() { + if (++images == 3 && window.testRunner) + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-drag-image-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-drag-image-expected.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-drag-image-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-drag-image.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-drag-image.html new file mode 100644 index 0000000..95c7ccd --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-drag-image.html
@@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + img, canvas { margin: 5px; width: 48% } +</style> + +<body style="overflow: hidden"> + <!-- The _blue_ sector of the <img> image should be at 12 o'clock. --> + <img> + <!-- The red sector of the <canvas> image should be at 12 o'clock. --> + <canvas></canvas> +</body> + +<script> +if (window.testRunner) { + // The pixel result will be the drag image. The blue sector if the + // drag image should be at 12 o'clock. + testRunner.dumpDragImage(); +} + +window.onload = function() { + if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + if (window.testRunner) + testRunner.waitUntilDone(); + + var image = document.querySelector('img'); + image.onload = function() { + runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged); + }; + + image.src = 'resources/red-at-12-oclock-with-color-profile.jpg'; +}; + +function changeColorProfile() { + window.testRunner.setColorProfile('whacked', profileChanged); +} + +function profileChanged() { + setTimeout(drawImagePatternToCanvas, 0); +} + +function drawImagePatternToCanvas() { + var canvas = document.querySelector('canvas'); + var ctx = canvas.getContext('2d'); + var pattern = ctx.createPattern(document.querySelector('img'), null); + var scale = 0.1870; + + ctx.clearRect(0, 0, canvas.width = 300, canvas.height = 300); + ctx.save(); + ctx.scale(scale, scale); + ctx.fillStyle = pattern; + ctx.fillRect(0, 0, canvas.width / scale, canvas.height / scale); + ctx.restore(); + + if (window.testRunner) + runAfterLayoutAndPaint(dragImage); +} + +function dragImage() { + var image = document.querySelector('img'); + + if (window.eventSender) { + var x = image.offsetLeft + image.offsetWidth / 2; + var y = image.offsetTop + image.offsetHeight / 2; + + eventSender.dragMode = true; + eventSender.mouseMoveTo(x, y); + eventSender.mouseDown(); + + eventSender.leapForward(1000); + eventSender.mouseMoveTo(x + 100, y + 100); + eventSender.mouseUp(); + } + + if (window.testRunner) + setTimeout(function() { window.testRunner.notifyDone() }, 100); +} + +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-filter-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-filter-expected.txt new file mode 100644 index 0000000..7f9642c3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-filter-expected.txt
@@ -0,0 +1,3 @@ + + +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-filter.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-filter.html new file mode 100644 index 0000000..09e1632 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-filter.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> +<style> + .filter { -webkit-filter: blur(3px) } +</style> + +<body> + <!-- The blue sector of the images should be at 12 o'clock. --> + <img width="250px" title="png image" onload="load(this)" src="resources/red-at-12-oclock-with-color-profile.png"> + <img width="250px" title="jpeg image" onload="load(this)" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img width="250px" title="webp image" onload="load(this)" src="resources/webp-color-profile-lossy.webp"><br> + + <img width="250px" title="png image" onload="load(this)" src="resources/red-at-12-oclock-with-color-profile.png"> + <img width="250px" title="jpeg image" onload="load(this)" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img width="250px" title="webp image" onload="load(this)" src="resources/webp-color-profile-lossy.webp"><br> +</body> + +<script> +var images = 0; + +function load(element) { + element.classList.add('filter'); + + if (++images == 6 && window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-group-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-group-expected.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-group-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-group.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-group.html new file mode 100644 index 0000000..3ec8cb1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-group.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + div { + -webkit-border-image: url("resources/red-at-12-oclock-with-color-profile.jpg") 8 2 round; + margin: -18px 0 5px 0; + height: 250px; + width: 794px; + } +</style> + +<body style="overflow: hidden"> + <!-- The blue sector of the images should be at 12 o'clock. --> + <svg width="800px" height="150px"> + <pattern id="pattern" patternUnits="userSpaceOnUse" width="400" height="150"> + <image xlink:href="resources/red-at-12-oclock-with-color-profile.jpg" width="400" height="400"/> + </pattern> + <rect width="800" height="130" style="fill: url(#pattern)"/> + </svg> + <div></div> + <iframe onload="load()" width="800px" height="185px" scrolling="no" frameborder="1" + src="resources/red-at-12-oclock-with-color-profile.jpg"> + </iframe> +</body> + +<script> +function load() { + runAfterLayoutAndPaint(changeColorProfile); +} + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-iframe-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-iframe-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-iframe-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-iframe.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-iframe.html new file mode 100644 index 0000000..e75043c --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-iframe.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<body> + <!-- The blue sector of the image should be at 12 o'clock. --> + <iframe onload="load()" width="780px" height="260px" scrolling="no" frameborder="1" + src="resources/red-at-12-oclock-with-color-profile.jpg"> + </iframe> +</body> + +<script> +function load() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +} + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-expected.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-pattern-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-pattern-expected.txt new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-pattern-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-pattern.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-pattern.html new file mode 100644 index 0000000..207dd7a0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-pattern.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + canvas { transform: translateZ(0); } + img, canvas { margin: 5px; width: 48% } +</style> + +<body style="overflow: hidden"> + <!-- The _blue_ sector of the <img> image should be at 12 o'clock. --> + <img> + <!-- The red sector of the <canvas> image should be at 12 o'clock. --> + <canvas></canvas> +</body> + +<script> +window.onload = function() { + var image = document.querySelector('img'); + + image.onload = function() { + runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged); + }; + + image.src = 'resources/red-at-12-oclock-with-color-profile.jpg'; +}; + +function changeColorProfile() { + window.testRunner.setColorProfile('whacked', profileChanged); +} + +function profileChanged() { + setTimeout(drawImagePatternToCanvas, 0); +} + +function drawImagePatternToCanvas() { + var canvas = document.querySelector('canvas'); + var ctx = canvas.getContext('2d'); + var pattern = ctx.createPattern(document.querySelector('img'), null); + var scale = 0.1870; + + ctx.clearRect(0, 0, canvas.width = 300, canvas.height = 300); + ctx.save(); + ctx.scale(scale, scale); + ctx.fillStyle = pattern; + ctx.fillRect(0, 0, canvas.width / scale, canvas.height / scale); + ctx.restore(); + + if (window.testRunner) + window.testRunner.notifyDone(); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-svg-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-svg-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-svg-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-svg.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-svg.html new file mode 100644 index 0000000..1bedbf3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas-svg.html
@@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + canvas, img { margin-bottom: -10px; } +</style> + +<body style="overflow: hidden"> + <!-- The red sector of the canvas images should be at 12 o'clock. --> + <p title="html canvas 2d element"> + <canvas width="700px" height="200px" /> + </p> + <!-- The blue sector of the source images should be at 12 o'clock. --> + <img title="source image to draw on canvas"> +</body> + +<script> +window.onload = function() { + var image = document.querySelector('img'); + + image.onload = function() { + runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged); + }; + + image.src = 'resources/color-profile-image-data-url.svg'; +}; + +function changeColorProfile() { + window.testRunner.setColorProfile('sRGB', profileChanged); +} + +function profileChanged() { + setTimeout(drawImageToCanvas, 0, document.querySelector('img')); +} + +function drawImageToCanvas(image) { + var canvas = document.querySelector('canvas'); + + canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); + canvas.getContext('2d').drawImage(image, 0, 0); + + if (window.testRunner) + runAfterLayoutAndPaint(setWhackedColorProfile); +} + +function setWhackedColorProfile() { + window.testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { window.testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas.html new file mode 100644 index 0000000..49eecff --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-canvas.html
@@ -0,0 +1,58 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + canvas, img { margin: 5px; width: 380px } +</style> + +<body style="overflow: hidden"> + <!-- The _blue_ sector of the <img> image should be at 12 o'clock. --> + <img> + <!-- The red sector of the <canvas> image should be at 12 o'clock. --> + <canvas></canvas> +</body> + +<script> +window.onload = function() { + var image = document.querySelector('img'); + + image.onload = function() { + runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChanged); + }; + + image.src = 'resources/red-at-12-oclock-with-color-profile.jpg'; +}; + +function changeColorProfile() { + window.testRunner.setColorProfile('sRGB', profileChanged); +} + +function profileChanged() { + setTimeout(drawImageToCanvas, 0); +} + +function drawImageToCanvas() { + var canvas = document.querySelector('canvas'); + canvas.getContext('2d').clearRect(0, 0, canvas.width = 380, canvas.height = 380); + + var image = document.querySelector('img'); + canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height); + + if (window.testRunner) + window.testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { window.testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-expected.txt new file mode 100644 index 0000000..600b53fd --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-expected.txt
@@ -0,0 +1,3 @@ + + +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-filter-all-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-filter-all-expected.txt new file mode 100644 index 0000000..92367f364 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-filter-all-expected.txt
@@ -0,0 +1,25 @@ + +GRAYSCALE + + +NONE + + +BRIGHTNESS + + +SATURATION + + +SEPIA + + +BLUR + + +OPACITY + + +BLUR+HUE + +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-filter-all.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-filter-all.html new file mode 100644 index 0000000..24510d3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-filter-all.html
@@ -0,0 +1,90 @@ +<!DOCTYPE html> +<html> +<style> +html { + width: 100vw; + height: 100vh; +} + +body { + text-transform: uppercase; + text-align: center; + font-family: sans-serif; + letter-spacing: 0.1em; + font-size: 12pt; + padding-top: 12px; +} + +img { + width: 170px; +} + +#grayscale { -webkit-filter: grayscale(1) } + +#none { -webkit-filter: none } + +#brightness { -webkit-filter: brightness(.6) } + +#saturation { -webkit-filter: saturate(4) } + +#sepia { -webkit-filter: sepia(1) } + +#blur { -webkit-filter: blur(2px) } + +#opacity { -webkit-filter: opacity(.8) } + +#blurhue { -webkit-filter: blur(3px) saturate(3) } + +div { + display: inline-block; + padding: 8px 8px; +} +</style> + +<body> + <!-- The blue sector of the images should be at 12 o'clock --> + <div> + <img id="grayscale" src="resources/red-at-12-oclock-with-color-profile.jpg" /><p>Grayscale</p> + </div> + <div> + <img id="none" src="resources/red-at-12-oclock-with-color-profile.jpg" /><p>None</p> + </div> + <div> + <img id="brightness" src="resources/red-at-12-oclock-with-color-profile.jpg" /><p>Brightness</p> + </div> + <div> + <img id="saturation" src="resources/red-at-12-oclock-with-color-profile.jpg" /><p>Saturation</p> + </div> + <div> + <img id="sepia" src="resources/red-at-12-oclock-with-color-profile.jpg" /><p>Sepia</p> + </div> + <div> + <img id="blur" src="resources/red-at-12-oclock-with-color-profile.jpg" /><p>Blur</p> + </div> + <div> + <img id="opacity" src="resources/red-at-12-oclock-with-color-profile.jpg" /><p>Opacity</p> + </div> + <div> + <img id="blurhue" src="resources/red-at-12-oclock-with-color-profile.jpg" /><p>Blur+Hue</p> + </div> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +}; + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-object-fit-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-object-fit-expected.txt new file mode 100644 index 0000000..1a4baf5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-object-fit-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-object-fit.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-object-fit.html new file mode 100644 index 0000000..d67ece7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-object-fit.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> + +<style> + .test { + object-fit: contain; + } + + img { + border: 2px solid black; + height: 200px; + width: 250px; + } +</style> + +<body> + <!-- The blue sector of the images should be at 12 o'clock. --> + <img class="test" title="png image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.png"> + <img class="test" title="jpg image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.jpg"> +</body> + +<script> +var images = 0; + +function load() { + if (++images == 2 && window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 200); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-profile-match-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-profile-match-expected.txt new file mode 100644 index 0000000..3fa127a --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-profile-match-expected.txt
@@ -0,0 +1,2 @@ + +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-profile-match.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-profile-match.html new file mode 100644 index 0000000..22712d7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-profile-match.html
@@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + img { width: 258px } +</style> + +<body style="overflow: hidden"> + <!-- The blue sector of the images should be at 12 o'clock. --> + <img title="png image" onload="load()" src="resources/blue-wheel-srgb-color-profile.png"> + <img title="jpeg image" onload="load()" src="resources/blue-wheel-srgb-color-profile.jpg"> + <img title="webp image" onload="load()" src="resources/blue-wheel-srgb-color-profile.webp"> + <br> +</body> + +<script> +var images = 0; + +function load() { + if (++images == 3 && window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +} + +function changeColorProfile() { + /* The test images have an sRGB color profile and so should pass through + * color correction unchanged if the output device profile is sRGB. + */ + testRunner.setColorProfile('sRGB', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-pseudo-content-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-pseudo-content-expected.txt new file mode 100644 index 0000000..5467dc4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-pseudo-content-expected.txt
@@ -0,0 +1 @@ +pseudo-content image replacement
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-pseudo-content.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-pseudo-content.html new file mode 100644 index 0000000..b1755d1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-pseudo-content.html
@@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> +<style> + h1 { + overflow: hidden; + width: 792px; + height: 160px; + } + + h1:before { + content: url("resources/red-at-12-oclock-with-color-profile.jpg"); + display: inline-block; + margin: 0px -20px; + } +</style> + +</body> + <!-- The blue sector of the content image should be at 12 o'clock. --> + <h1> + pseudo-content image replacement + </h1> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 0); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-shape-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-shape-expected.txt new file mode 100644 index 0000000..379b7bc --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-shape-expected.txt
@@ -0,0 +1,2 @@ + +Lorem
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-shape.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-shape.html new file mode 100644 index 0000000..8975a77 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-shape.html
@@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> +<style> + .css-shape { + shape-outside: circle(); + shape-margin: 2px; + width: 200px; + border-radius: 50%; + float: left; + } + + p { + font: 10pt Ahem, sans-serif; + width: 250px; + } +</style> + +<body> + <!-- The blue sector of the image should be at 12'oclock --> + <img class="css-shape" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <p>Lorem </p> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 20); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-svg-resource-url-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-svg-resource-url-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-svg-resource-url-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image-svg-resource-url.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-svg-resource-url.html new file mode 100644 index 0000000..b1f22bd07 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image-svg-resource-url.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<!-- https://crbug.com/411183 Test case for svg with <image> children + having xlink:href dataURL sources that contain a color profile. --> +<body> + <!-- The blue sector of the 3 test images should be at 12 o'clock. --> + <img onload="load()" src="resources/color-profile-image-data-url.svg"> +</body> + +<script> +function load() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +} + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-image.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-image.html new file mode 100644 index 0000000..63f01330 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-image.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<style> + img { border: 1px solid transparent; } /* Prevent direct compositing. */ +</style> + +<body> + <!-- The blue sector of the images should be at 12 o'clock. --> + <img width="250px" title="png image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.png"> + <img width="250px" title="jpeg image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img width="250px" title="webp image" onload="load()" src="resources/webp-color-profile-lossy.webp"> + <br> + + <!-- The blue sector of the images should be at 12 o'clock. --> + <img width="250px" title="png image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.png"> + <img width="250px" title="jpeg image" onload="load()" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img width="250px" title="webp image" onload="load()" src="resources/webp-color-profile-lossy.webp"> + <br> +</body> + +<script> +var images = 0; + +function load() { + if (++images == 6 && window.testRunner) + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-expected.txt new file mode 100644 index 0000000..600b53fd --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-expected.txt
@@ -0,0 +1,3 @@ + + +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-filter-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-filter-expected.txt new file mode 100644 index 0000000..163b4d3f --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-filter-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-filter.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-filter.html new file mode 100644 index 0000000..feab74d --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-layer-filter.html
@@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<style> + img { + transform: translateZ(0); /* Enable direct compositing. */ + width: 250px; + } + .filter { + -webkit-filter: blur(3px); + } +</style> + +<body> + <!-- The blue sector of the images should be at 12 o'clock. --> + <img onload="load(this)" title="png image" src="resources/red-at-12-oclock-with-color-profile.png"> + <img onload="load(this)" title="jpeg image" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img onload="load(this)" title="webp image" src="resources/webp-color-profile-lossy.webp"> +</body> + +<script> +var images = 0; + +function load(element) { + setTimeout(function() { element.classList.add('filter') || next() }, 100); +} + +function next() { + if (++images == 3 && window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', profileChanged) }, 100); +} + +function profileChanged() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + window.testRunner.dumpAsTextWithPixelResults(); + window.testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-layer.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-layer.html new file mode 100644 index 0000000..cbccc8a --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-layer.html
@@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<style> + .layer { + transform: translateZ(0); /* Enable direct compositing. */ + width: 250px; + } +</style> + +<body> + <!-- The blue sector of the images should be at 12 o'clock. --> + <img onload="load(this)" title="png image" src="resources/red-at-12-oclock-with-color-profile.png"> + <img onload="load(this)" title="jpeg image" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img onload="load(this)" title="webp image" src="resources/webp-color-profile-lossy.webp"> + <br> + <!-- The blue sector of the images should be at 12 o'clock. --> + <img onload="load(this)" title="png image" src="resources/red-at-12-oclock-with-color-profile.png"> + <img onload="load(this)" title="jpeg image" src="resources/red-at-12-oclock-with-color-profile.jpg"> + <img onload="load(this)" title="webp image" src="resources/webp-color-profile-lossy.webp"> + <br> +</body> + +<script> +var images = 0; + +function load(element) { + element.classList.add('layer'); + + if (++images == 6 && window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-mask-image-svg-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-mask-image-svg-expected.txt new file mode 100644 index 0000000..1a4baf5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-mask-image-svg-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-mask-image-svg.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-mask-image-svg.html new file mode 100644 index 0000000..593c0a67 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-mask-image-svg.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + img { + -webkit-mask: url(resources/color-profile-mask-image.svg) top left; + -webkit-mask-size: 33% 33%; + -webkit-mask-repeat: space; + width: 380px; + } +</style> + +<body> + <!-- The blue sector of the images should be at 12 o'clock. --> + + <svg onload="load()" xmlns:xlink="http://www.w3.org/1999/xlink" width="380" height="380"> + <defs> + <mask id="mask" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox"> + <circle cx=".5" cy=".5" r=".47" fill="white"/> + </mask> + </defs> + <image xlink:href="resources/red-at-12-oclock-with-color-profile.jpg" width="380" height="380" mask="url(#mask)"/> + </svg> + + <img src="resources/red-at-12-oclock-with-color-profile.jpg"> +</body> + +<script> +function load() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +} + +function changeColorProfile() { + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-object-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-object-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-object-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-object.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-object.html new file mode 100644 index 0000000..6517052 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-object.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + object { border: 1px solid transparent } +</style> + +<body> + <!-- The blue sector of the image should be at 12 o'clock. --> + <object width="500px" data="resources/red-at-12-oclock-with-color-profile.jpg"> + </object> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-reflection-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-reflection-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-reflection-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-reflection.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-reflection.html new file mode 100644 index 0000000..a28b0dc --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-reflection.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + .reflection { + -webkit-box-reflect: below 5px -webkit-gradient(linear, left top, left bottom, + from(transparent), color-stop(0, transparent), to(white)); + background: url("resources/red-at-12-oclock-with-color-profile.jpg"); + height: 260px; + } +</style> + +<!-- The blue sector of the image should be at 12 o'clock and reflected below. --> +<div></div> + +<script> +window.onload = function() { + document.querySelector('div').classList.add('reflection'); + + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +}; + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-fill-text-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-fill-text-expected.txt new file mode 100644 index 0000000..9b89b7d2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-fill-text-expected.txt
@@ -0,0 +1,2 @@ +▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-fill-text.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-fill-text.html new file mode 100644 index 0000000..bf62265 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-fill-text.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<!-- There should be no red on this page. --> +<body> +<svg width="98%" height="400px"> + <pattern id="pattern" patternUnits="userSpaceOnUse" y="20" width="400" height="120" patternTransform="scale(0.4)"> + <image onload="top.load()" xlink:href="resources/red-at-12-oclock-with-color-profile.jpg" width="400" height="400"/> + </pattern> + <text x="10" y="0.5em" style="fill: url(#pattern); font: 108pt Ahem, sans-serif; -webkit-font-smoothing: none;"> + ▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅▅ + </text> +</svg> +</body> + +<script> +function load() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +} + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-foreign-object-expected.txt b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-foreign-object-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-foreign-object-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-foreign-object.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-foreign-object.html new file mode 100644 index 0000000..bfb8bd3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg-foreign-object.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"/></script> + +<!-- There should be no red on this page. --> +<body> + <object onload="load()" data="resources/color-profile-image-foreign-object.svg" width="480" height="360" type="image/svg+xml"> + <p style="color: red">SVG not supported in this browser</p> + </object> +</body> + +<script> +function load() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +} + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/color-profile-svg.html b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg.html new file mode 100644 index 0000000..30aef9a --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/color-profile-svg.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<script src="../../resources/run-after-layout-and-paint.js"></script> + +<style> + body { overflow: hidden; margin: 0px } +</style> + +<!-- The blue sector of the image <pattern> should be at the 12 o'clock. --> +<body> + <svg width="800px" height="400px"> + <pattern id="pattern" patternUnits="userSpaceOnUse" width="400" height="400"> + <image onload="top.load()" xlink:href="resources/red-at-12-oclock-with-color-profile.jpg" width="400" height="400"/> + </pattern> + <rect width="800" height="400" style="fill: url(#pattern)"/> + </svg> +</body> + +<script> +function load() { + if (window.testRunner) + runAfterLayoutAndPaint(changeColorProfile); +} + +function changeColorProfile() { + testRunner.setColorProfile('whacked', done); +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-image-data-url.svg b/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-image-data-url.svg new file mode 100644 index 0000000..b328e7a --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-image-data-url.svg
@@ -0,0 +1,17 @@ +<!-- https://crbug.com/411183 Testcase for svg with <image> children + having xlink:href dataURL sources that contain a color profile. + <image> type order is: webp, jpeg, png --> +<svg width="700" height="400" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- fast/image/resources/webp-color-profile-lossy.webp data url --> + <image x="0" width="200" height="200" xlink:href="data:image/webp;base64,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"> + <title>webp image</title> + </image> + <!-- fast/image/resources/red-at-12-oclock-with-color-profile.jpg data url --> + <image x="210" width="200" height="200" xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4gHqSUNDX1BST0ZJTEUAAQEAAAHaTVNGVAIQAABtbnRyUkdCIFhZWiAH1QAKABIAEAACAARhY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLE1TRlQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAljcHJ0AAAA8AAAACZkZXNjAAABGAAAAGR3dHB0AAABfAAAABRyWFlaAAABkAAAABRnWFlaAAABpAAAABRiWFlaAAABuAAAABRyVFJDAAABzAAAAA5nVFJDAAABzAAAAA5iVFJDAAABzAAAAA50ZXh0AAAAAENvcHlyaWdodCAyMDA1IEhlbnJpY3VzIE1hZ251cwAAZGVzYwAAAAAAAAAKQ29sb3JTcGluAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUgABAAAAARbMWFlaIAAAAAAAAGKXAAC3hwAAGNlYWVogAAAAAAAAJJ4AAA+EAAC2w1hZWiAAAAAAAABvoQAAOPUAAAOQY3VydgAAAAAAAAABAc3/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECAgICAgICAgICAgMDAwMDAwMDAwP/2wBDAQEBAQEBAQEBAQECAgECAgMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwP/wAARCAMgAyADAREAAhEBAxEB/8QAHwABAAIBBQEBAQAAAAAAAAAAAAUGBAECAwkKCAcL/8QAYxAAAAUDAgIHBQYEBAIFBQUZAAECAwQFBhEHIRIxCBNBUWFx8AmBkaGxFCIywdHhChUW8SNCUmIXJBolM3KCGDRYktInNUVTVFdpc7IZJig2Q3STlJeiqLPV6FWVpcLDxdj/xAAeAQEAAQMFAQAAAAAAAAAAAAAABQEDBAIGBwgJCv/EAGYRAAEDAwMCBAQDBAQIBgsIEwEAAgMEBREGByESMQgTQVEJFCJhMnGBI0KRsRUzUqEWF1NicpKTwSRDgrLC0RklNERVY3Ois9LTGFRXg5Sjw9Th4/DxJlZYZWh0lqbVJzVFhOXE/9oADAMBAAIRAxEAPwD+f+CICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICLIZiS5P8A5vGkSOz/AAWXHd+77iVCVttivl5eI7PZqurkJ7QwySn+DGuKsy1FPD/XTsZ/pOA/mVMMWpdEn/ze3K68XPiRSZyk4/7xMcPzHKdl8OHiE1Hg2HYvWFYwjPVFZ7g9uPfqbTluP1UdLf7FB/XXmlafvKz+XUpFGn16ucraqqf/AKJH6n49apGByDQeB7xdXLp+X8POqG5/ytG+D/03l4/VYbtXaZZ3vUB/J2f5ZWSnTO+Vb/0/IL/vyYCP/p5SRvCj+HL41a4AwbC3EZ/ylVbov/S1rMfqrB1tpYf/AN3Z+jXn+TVu/wCGN8Fzoai86jSS+s8T8XwwfHPMAWbGPA/zrvYW/wBzroFT/DfS/wD4UH+zl/8AUWz/AIa3t/8AwQz8qhSj+k4xWX4X/johBL9i3kf5t3sLv7m3QrW3WemnHAufP/k5f/UXGrTq9Ec6DIP/ALkiE5/+rlKyIKs+HF42KEEzbCXE4/ydVbZf/RVr8/osluqbA/8ADcmfwcP5tWM5Yt4N/it2qHj/AOLjm7/+qNeRs+v8DXi+t2fmPDvqh2P8lRvn/wDQ+Zn9Fktvtnf2uUP6uA/nhR71s3HG/wC3oFZZIu1ymTUp/wDWNnhHHt68NviH04Cb9sRrGjYBnqls1xY3Hv1Opw3+9ZMdxt8v9XXQuP2e0/71EuxpDB4fjvMn3OtLbP8A/PSQ4rudgvtleY7zZaukkBxiaGSI5/J7WlZbXNcMtcCPsuERK1ICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICLKiQZs90mIMOVNePGGYkd2S6eeWG2ULWefISdpsl5v8AWR2+xWmqrbg78MUET5pD+TI2ucf0CsVFVTUkZlqqiOKL3e4NH8SQF+mUjRLU6scKmrWmQmlJJfXVZbFLSSTxg+qmONyVZI8/dQew7YaE8A3i13AMT7Zs5cKKjc0O824uitzQ04weirkimdnOcMiccc4Wybhudoi3ZEl9jlkBxiIOl/vYC3+Lgv0imdGC5HSbXWbhpFOSZkbjUJmVUnkJzunLhQGTWae5RkQ7jaJ+Dhu9dRSza83RsNohfgvZSx1NwmYM8ghwooi7H9mZzf8AOK2XXb5WVhe222eomI7F5bGD/DzDj9Ar1B6NdpRd6jV63U1ERfdbVFgMmfaZpSzIe+DhDtno74OOxVs6ZNabiaku8wxxCaWhiPvlvk1UuD9pwR7ra1VvVqCfijt9LA33IdIf4lzR/wCarVE0b05p6Uki3WZKyPdydKmy1KPxQ7INns7EEO0GlPhveDbS8cQZs/DX1LTnzK6rrqku/wBKN9T5H6CEBQNRuPrKsJLrw5jfZjWM/vDer+9T7Fq2zT0pTBt6ixST+E2aXCbWW+cmsmeNR5PmZ5HY3Tfh52E0myFmmtldKURjHDorVQtf75Mgg8xx+7nE/dRUt+vdY4uqbxUyZ/tSPI/h1YWapCGy4EIShJckoIkpLfsSnBEQ5jo6WlooI6ajpo4aZgw1jGhrQPYNaAB+gWKHOeepziXe5WG5zP12iQb6LIZ6LEXvkXm+iyG+iw3C/MXmrIYop/mrt2P6EMyPsFnxdgsRPMXz2WdH+JZSOwWXKRizgLnIWysxq5iGgrKauQ0IWk0rSlaT5pURKSfmk8kYxKulpa2CSlraaOameMOY9oe0j2LXAg/qFlRktOQcFRsi27emkopdDpEji5m7Toi1eZLNnjI/EjHD2o/DrsDq6OZmptk9J1xk/E6a1UL3++RIYPMafu1wP3UtDX10eOislAH+c7H8M4VflaWWLNSZKobcdRnnrIcmXGNPkht/qvikx1z1X8NTwX6qjlEmzcNBUuORJQ1ldSlv+jGyp8j9DCQpeG+XRnTmp6h9w3+eM/3qsTdCLbkbwKnVoKv9Lhx5rRH4JNph3b/vmOrusvgw7CXXqk0VuNqazzEH6ZjS18QPphvk0suB7Gck+6l4dQ1B/rYGO/LI/wCv+Sp1Q0BrrXGql1qmziLJoRKakQXVFnYvuFMaJWO9REOo+uPgu7xWoVc2gN1bBeIWZLGVcVTb5njPAAYK6EOx/ama37hSsV6gfjric0/oR/Hj+SolS0svyl5N235UpsiNXW05TVQSZFnP3Iq3Hk4Iu1BDplr34e/i/wBvDK+6bL3Guo2tLvNtrobk0tGcnoo5JZm4Azh8TTjnCkI6umlx0TD9eP54VFkRZMRw2pcd+K6XNqQy4y4WOeUOJSoseQ6i3ixXvT1ZJbr/AGeqobg38UVRFJDIPzZI1rh+oWR6A+i4BFIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIv0a09JNRr2Ug7dtOrS4y1JT/MH2PsFNTxb8Sp8448U0kW/wB1RmOdNsvDPvtvA+E6A2zudZQvcB8y6P5ekGfU1VQYoMY5OHk49Fs2/wC4OjdMBwvGoKeOYDPltd5kh/8Ai2dTv4gL6Ntroa3A8TT94XPT6ShSTUuDRmXKnLSfFgkLlSPskVtXDzNBOkWe0eh+2fwi9fXYU1ZuruRb7TA4ZdT0Eb62oHP4XSyeRAx2Ocs89o+64bvXiQtERfFp2xzVDgeHzERsP3DW9bj+pYV+50Lo06V2+SVPUeRX5KUoI367MXJQa044lFDjlFhYUrsU2rbYege3Pw1fCpoUQz3HSVVqG4ta3MlzqXys6h3Ip4BT0xBP7skUmBxk9zxZdd69eXcuEVxZSQkn6YGBpx/pu6n/AKhwX6hEo1Jo7Jx6TTKfS2MEXU0+HHht4ItsoYbbI8EO8Wk9E6M0RRi36M0lbbTQYA8ujpoaZhA7ZbCxgP5nJWxqi5XC4yCa4V008vvI9zz/ABcSuKQXI8fsN6xlXIj3UQ8nn8eWf0Gaw9lIxHt7KLdLu7jLl2jLYVnMIUW6WPj9e4ZjFnRlRzhc8b/oMlvosxh7KPcLn68Rkt9FlsPZYLhbn8RkN9FlsPZYiy5/EXmq+1YTnL4+AyGrKZ3UW+Zfe27D5Z7veQy4/TlZ8XosNHMXz2WbH+JZSPW4suUjF2CyEl4Ht8vMWis1mAQuVI0FZLFzJGgrKYudItlZbFkJFsrNj9Fkp7vy9GLRWdGOyyU8/l8BZKkYhyslBchacpCIdltlUynVJHVVGBDntYMurmRmZKMHzwl5CyLPgNn6s0LonXVEbbrbSFsu9Bgjy62lgqmAHvhszHgZ+wCk4C9pyxxH5L89q+idg1clKapr1HeUlRE7SZK2UEo/wqOM/wDaI2CPsJCR0V3K+F74SNfiae3aOq9N3NzXYltVS+JnUc4JppxUUwDT+7HFHkcZHcSkT3EYdg/3fywvyet9G+rskt23q5EqCSIjRFqTS4Mg990pfa+0R1qx2qJsh52bpfBq3DtLaqt2i3Ot14p2jLaa4Rvoag88sbNH8xTvdjkOf8u049OFlth6xlpwfv8A/d/uX41X7AvG2TUdYoE+OylRp+1tNfaoSsdpS4pvMEWO9RGPNrdXwseILZaSc7i7V3Wit7HEfNMi+Zozj1FXTGWnxjkZkBx6LS6nmbyWEj7cqnDr+rKAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiuFo6f3rfswoNn2zV6++R4cVAiOLjMFlJGqVNWSIkVJcRZNxaSHIGgNqtx90rkLTt7oy4Xasz9XkROcxnbmWU4iiHI5ke0fdbc1Dq7TOlKb5rUV7p6SL063gOd/osGXuP+i0r7MsfoI3NMJuXqHckSgMmRqVSKElFVqRmSk4Q7Pc4KdHM053QUjcelO03wqtbXnyLhvBrWns9IeTSUIbV1XcfS+dxbTRHGeWfMjOOF1u1R4qrJTF9Po+yyVcn+VnzFH+YjGZHc4/F5a+tbR6PGk1h9W5SrUiT57WcVWv8A/XM/J4+8k5SPskdRY2NppvA9OtqfBV4b9pvl6mxbeU9deozkVdyxXT592iUeRGR6GGGMhdftQbw6/wBV9bK+/SRUjv8AioP2Mf5HpPW7/lucv0x5tKCJKUklKCJKUpIiIkkWCSREREREQ7dU7WRsZHG0NjaAAAMAAcAADsB6BbJjeXElxySoWSnmJKIqTgcoR9JZz3/n2nuM+MqTiPooV9O5+IzozwpKI8BQ76dj9/7DOjPIUjEeQoh4t/P6mM1hUjGVFOkW/n67hmMWew+3ZRbxc/XrmMuM9lnxngKMdLn5DLZ6LNjPZR7hDJastiwHC38xkNWUxYa+3yF9qyWrDc5Hv9O4X291ks7qKkdvPkfPyPkMyP0WdD6LDR+gvFSEfcrKRyL13i05SEPICyEi0VnMXMkaCsli5kjQVlRjsudItlZkayEC0VnRhZKCForPjHIWUghacpCIeqykELLlIxDsstBCy5SUIWUguQsuUlEOwWaguQsOKlYW9llpSSiMlERpMuEyMskZHzIyPYyMYsrGSsfHIwOjcCCCMgg8EEHggjuD3UpC3kKiXDpHYV0ca51CYiSnMZn0nFOlZLP3ldQn7O6o883G1jp5u74EvDBvL8zU6h20prffJOTWWzFBUZ5+pwhb5Erj6meCUlZhoKaox5kQz7jg/wBy/Abm6MFZjEuRaVYYqrZbpgVMkwZnM/utykcUN7CcbqJoeU+8fwfde2T5i5bJ67pr3RjkUdeG0dWOT9LKhpdSykDHMnyoznhYc2m5SC6lmDvs7g/x7L52r9qXHa8j7LcFGn0t3/IcphaWXSyZZZkJ4o7yTxzQox5X7kbPbo7Q3Q2fcvQtys1bn6fmIXNjk78xTDMMo4P1RvcPuoKpoqqkd01EDm/cjj+PZV8cbLFQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEX0JpV0YNYNXFMSqBbb1Mt91aSXc9xE5SqMTZrbJa4qnW1SqkpCHOLhjNO5LtLmOyeznhO3r3skp6jTGln0unHuGa+szT0obloJY5zTJOQHZ6YI5D74XEOvd8du9vRLBdr02e7tBxTU+JZs4OA4A9MeSMZkc39V2LabdAvTa0Ux599y5N/1lskrXEd6ym22y7wKSpKYDDv2uclKlZSb7vAeN2+wer2zXw2NpNG/KXTcytl1PfGgExHqp6BrsHI8pjvNmAJyDLKGOx9UOOF051p4rtaahdNSaVp2Wm2ngPGJKgjPGXuHSwkd+hmR6PX1vDodJoUJqmUSmU+kU6OWGINNiR4URssEn7rEZDbZGZEWTxk8bj0e05YLDpa2U9l01ZqW32iIYZDTxMhibwBwyNrWg4AycZPqV18qbpX3WqkrrnXTVFY/8T5Hue8/m5xJ/6liSEc9t/XzG6I3LIhcq/KRz2+okoXdlLwO7KBkoz2fmJGJylYXYUHITsf1EhGVKQu5CgpCdj2EhEVKwnsoWQn9+39xnxlScJ91Dvp5/l9RmxnspGI9lCvlj3GM+MqSiKiXi5/L9RmMPZSEZ7KLeI9/I/XIZkazoiot38vX0GWxZzFHufmMlizGKPc5jJastiw17b+AvhZLfZYbnI/XYL7fRZDPRRL58/Aj8S5dx8hmRjspCIdlho/QXis+PuVloLl7had6qRiHDVkJ58haKzWLlIaCspgXOkWystg7LnT2C2VmRjsslBC0VnRhZKBacs+IeqykbF9ezIsuUjEOyykELTlJRBZiC5Cw4qTib2WW2W4suKk4W8hZqCGO4qWhas1tIsOKlYGdlmtkLDipaFuSs5BchjuKl4m9uFvlU6BU4y4VShRZ8R0sORpjDUlhexl95p5K0GZEZ9mSG29Saa07q601Ni1VYqO5WWYYfBUwxzxO4I5jka5pIBODjIzwQpVkMcreiWMOYfQgEL8BvHoxWfXSdlWy+9a1QWRqSwjjmUhxfCRERxnV9fGJSiyfVr4SzskeYW+fwp9ldeCsu+09wm0lqFwJELeqptz3YGAYZHedACRkmGUsbk9MOOFEVuirfWZdRuMEx9uW5/I9v0K+S720av2xDceqlIXMpaFHw1mlcc2nmjiUSVPKQgnoZqJOcPIR7x4076eC3f/YCSpqtW6PfWaWY49NyoOqpoy3LgHSOa0SU5IbnpqI4jjtnutj3TS94tXU+amL6cfvs+pv6+rf1C/Kx1RW3UBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF9QaK9ETWTW1UedRqGq3rUcUXWXfcqHoFLU3kuI6cyaPtlXcIjLBMING+608x2X2X8KG729klPV2Symg0s4/VX1gdFBj1MQx5lQee0TS3+09vdcG7meIXbjbFstLcroKy/gcUlMRJLn08w56IR/pkH2aV2y6Q9BjSDSlMWp1iEeoV1s8Dv82uOO0umRH0/e4qbQcuQmyQrOFvm+vkeSHrxsn4FtmtrflLrfaL/AAj1YzDvPrGNNPG8c5hpPqjGD2dKZX+oLSugG4fil3E16Z6K3VP9EWF2R5VO4iV7f/GT8POfUM6G+mCvqx6O20hLTbaG220JQ2hCSShtCCwhCEJIiShKSwRFsQ7z0wZEyOKJgbE0AAAAAADAAA4AA4AHAC4EjmfI5z3vLnk5JPJJPck+pPqoKS3sfd68BKxO7KVgf2VcktYNW3r4CUif25UzA/OOVX5CNz27+78u0SUTuymIXcBQMpvn6/YhIxO7KVgf2VefRz8PeJKN3ZS8Tuygn08y/f0YkIz2UrE7soKSjGfEuz0YkYndlKQu7KDkJIyMSEZ5ClYnchQr6f0P6DOjKkoioZ9P4uefLGwz4z2UlERgKHeL5kM2NSMRUU8W3yGYwrPjPKi3Sxt3HuMthWdGfVRzvI8d4ymeizWenuo90vqMliy2LCX+ovtWS1YTnb5/kMhqyWeiipG2TI8ng+3w+Qy4/ThZ8XOOFhI5/AX3KQi7lZiTzjJny/IWT6qSixx7rnSLZWaxcyefL3C2VktC5yGgrMaudItFZsfoslHZt7+8Wneqzoh2WUghacpCIcBZSBZcpKIdlloLkLLlJRDssxBchYcpSELMbIWXFSkDSFmoLkMdxUtC3gLObIY7ipeBqzWiFh5UvTt7LObSMdxUvAzOFntpGM4qagZ2Wc0kY7ypenYeFnJbStJoWklIWk0qSoiUlSVFhSVJMjIyMj3I+ZDDnZHNHJDNG18L2lrmuAIcCMEEHggjgg8EcKZgjBGCOCvw+/OjfYd6k9MgR/6WrbnEv7bSWkJhvunvmXTPuR18R81N9WrxMee+/wD8OTYfeUVt507b/wDBXWsmXfMUDGimleec1FFlsRye7oTC/uSXFQN32+st5DpIo/lqw/vMHBP+czsf0wV8P6haGX7p2p2RPpx1SiIUfBXaQlyTCJG+Dltkn7RAUZFycSSe5Rjw73/8E++nh8lqa7UGnjctGsd9NzoQ6am6ecGduPNpicdpmNb/AGXu7rh+/wChr9p/qlmp/Ooh/wAZHkt/5Q7t/Xj7r8cHUZbOQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEX7box0edV9eqx/K9O7YlVCKy4SancMzMG3KQg0qUa59WfSTBOcKD4WW+sfWeCSg8jlfazZTcbeO6i2aH0/JPE12Jah/wCzpYBgnMszvpBwOGN6pHdmsK4x3J3g0DtRbvn9YXyOGdwzHTs+uomPAxHE36sc8vd0sA5Lhhd1ugvs7dLdKShV+/uo1NvZjq30qqEc02lSpKFNOEdPojuSqK2lt7OzOMjIzw0gevGxngV2225NHfNchmotWsw4eazFFC8EEeXTu/rSCOHz9QOeImleZu6/jD11r41Np0n1WTTLst/ZuzVytII/aTD+rBB5bD04xy9y+6HIrTDSGGGm2WWW0NMtNIS20002kkNtttoIkoQhBESUkRERFgd+KVsUEUUMMbWQsaGta0ANa0DAAA4AA4AHAHZdWmTvle6WV5dI4kkk5JJOSSTySTySe6gpTPr98CWhepSCRVyU1z2+Xh5CUif2UzA/tyq/Kb58i9fESUTuyl4H9lXJTfMScLuymYH9lXZLe59/kJSJymYH9vZQMpvO+Ozy+gkYnKVgeq7JRgz29fUScTuApiB3AUBJRuZiSicpaF3CgZKMGeM/l8BIROUtC4ccqCkI3P8Av8xIxHspSF3ZQj6efy9GQz4z2UpEeyhZCeZ+fkM6I9lJQnsFDPFt4/QZ0ZUlEf4KKeLYxmxnss+I9lFPFz+IzGeiz4z2Ua72jKZ6LNj9FHOl9RksWYxYS/zMZDVktWC4WSPs/v2b5F9vcLKZ6KKf5qxvt3eHzGZH2Cz4v3crCR+gvlSEfcrMR2euwWXKTh7Bc6RbKzGLmSLZWWzkrISLZWYxc6RbKzY1lIFpyz4wslBchZcpGIdlloIWXKRiGcFZiC3FhxUpE3kLLQQsuUnEFnNlyGO4qXgb2Wa2QsOKlYW8hZzZcix5jHce6mIW9gVntpGO4qZhZgDhZzaRjvKmIGdlntp5F6+oxnFTMLeAFINp5evqMZxU1Azss9tPIYzipqBnbhSDSRjvKmqePtws0mkOIU24hLjbiVIW2tJLQtCiNKkKSeUqSpJ4Mj2MhhVEUNRDLT1ETZIJGlrmuAc1zXDBa4HIIIJBB4I4KmYYg4FrgC0jBBXzdqX0WLNvQpFTtnq7QuBzicM4rRnRJrqjWo/tVORgoqlqXuuPwkRF+Ax5reI34bO0m7Jr9R7cCPSuuZMuPkszbqh5LnHzqVuPILieZKbpAAGYXlbD1LtHZL8JKq2YorkectH7Jx5/Ewfhz7sx+RXXzf2l166az/sV00d6MytRlEqjH/M0qcnCVEqNObI2jVhRZQrhcSexpIeFG9Xh23a2BvZtG4+lpaane7ENXH+1o6gYBzDUN+gnB5jf0StOQ5gIXXLUmkL/AKVqPJu9C5sZP0yD6o3/AOi8cZ+xwR7L89HCC2ygIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIpah0KtXNVoFBt2lVCt1qqSG4lOpdLiPTZ0yS6okoajxo6FuuKMz7CwRbnsM62Wy43mvpbXaaGWpuU7wyOKJpe97j2DWtBJP5BYF0utsslvq7reK+GltkDC+SWV4YxjR3LnOIAH/AN4Lub6MvsrpbyKdenSPfXCZUUeZD0ypMkvtTiVJ65KbrrEdwyiluglxIxm5+JK3UGXCPSvYjwGy1fyWpd6JnRQfS9lshd9bhjOKqZp+j0zFEeruHSNPC82d7vHhTxOrNNbMxCSQdTH3KVv0jnBNLC4fV64lk+nsWscOV3F0Gz7bsuhwbbtKhUu3aDTWUMQaTSIbMGFHbbQlsjSywhBLdUSC43FZcWrdSjM8j1J03YbFpW1Udj03aaehtEDQ2OGFjY2NAAHZoGScfU45c48uJOSvOa66ivWpbpVXrUF1nrLrO4ufLM9z3uJJPdxOAM8NGGtHAAHC4pTOOLbvz5/HtG7YX9lrgk7KvyGufr0Ykon9lLwydlX5TXPbb14CSif2UvA/tyq3Ka57bb+uQlIXqZgf2VdlN88fkJOFymIH9squymvxeuzyyJSF/ZTMD+yrUpvc/XoxKQu7Kagf2UBIb2UXdk/XISUTuyloX9lXZbfP16MScLuymad/ZV+SjJZ8Ph+YkonKXgcoCSjY9u8v7iSidyFKwu7KAkJ9eQkYipeFygpBbn3b+u8SEZUpEeB7qFkJ28vXIZ8ZUnEeVCvl+IveM+M9lJRHsol4uflsM1nopCP0US8XMvD6fMZjCpCM9lGO9vruGWxZ0ajneRjJYsxnosBfMZDVlNWE52jIb6LJZ6KJkb58j/PuGZH6KQh9OPVYaOfrxF9ykIu5WWjsFlylIvRZCfR/33ForMj5C5kjQVlx8Fc6RbKzGBZKBaKzo1koIWnKQiH8VloIWXKSiHKy0ELLlJQhZjZCw5SkI4WY2XIWHFSsLckLObIY7ipeFqzWyGO8qWp2LPbLJjHcVMwNyQs9shjuKmYG5wpBpIxnlTdPH2We0nwGM8qYp48qQbSMZxU3Ts7KQbSMZ5U3BH24Ug0gYzypynj7KQbSMZ5U1BH24Ug2kYzipmCPGOFx1Oh0mv0+RSa3TodVpsttTUiFOjtyY7iVJNJ5bcSoiWRKPhUWFJPcjIxtfVWltNa1slfprV1ipbjYalhbLBURtljcCCPwuBw4Anpe3DmnlpBAKz5rZRXKllorhSsnpHjDmPaHNP6H+49x6cr4Q1d6G7zZSq/pU4p9sutfftGa6XXIIi4zTRZ7qiJ7krhYeMlciStR7Dxd8TXwwKij/pHWPh2qHTU465JLNO8eY0AdRFDUPI8z97pgmIf+FrJXn6V161xsDKGzXTRTi5vLnUzzz7/snk8+uGO57AOPZfBdRp0+kTZNNqkOTT58N1TMqHMZcjyWHUHhSHWnUpWkyPw3HjvebLd9O3Susl+tk9HeKaQslhmY6OWN7eC17HAOB/MfccLrNV0dVQVM1HW074qqNxDmPBa5pHoQeVhCMWMgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIvrPow9DXWDpSVom7PpR0ezIT6UV7UCtNOsW/TUk4lDkeGeEu1qq7nwxo/EosGazQkjMc27PbCa73muPlWCj8ixRuxNWzAiCPnBDfWWT2jZk+ri0crr/vj4kNutirYX6ir/mdSStzBQQkOqJOMhz/SGL3kkwOQGhxOF6T+jn0NNH+jBR0N2hSSrV4SI5NVnUCuMMvXDUFG3wOswzTxtUWmryrEePjJKMlrc5j2L2X2A2/2YoWiw0IqNRPbiWumAM7+MEM7iGM8/RH3z9bn914tbyeJHcTfG4ufqKv+W06x+YaCBxFPHzkF/YzSDj9pJntlrWdl9GSWckfYOwsT8YXDcEmCFXpDPPbv7/eJOJ6l4ZOyr0tnJHt5+XPuEnC9TFPJyFW5LXMsevgJSJ/ZTUEnYqvSWue3fzx6MScT/upeCTtyq7Ka57evqJOF6mIJO3Krcpo8nn8v1EpE/spqCTsq7Kb57ev1EpC7spmB/ZVqW1z5/PyMSsL+ymqd/blVuS3gz+e39xKROUzC9V+U3z8vWCPmYkoXKXp3/dVyS3z8M7CUid2UzC/sq/IRzLb0XbzElG7speF3ZQElH4vRchIxO7KWhd2UDJT6Lw7+8SMRUtCVCPp/EM+M9lJxHsoN8voe39vESEZUpEVEPEfv3IZrCpCM/wAFEPF2eYzWKRjKi3S/QZbCs6MqNcxg/wBfEZTfRZrFgLL9RktWU1YTnNQvt9FlM9FEye3P+k+RHnt79hmxLPh9Me6wkc/XiL7lIxdysxAsOUrF6LnTyFsrMZ2XOnsGgrLj7hc6RaKzY/RZKOwWnLOiHZZSOwWnKRiHZZaCFlykoh6rLbIWHKThCzUFyFhxUrC3sFmtkLDipaBvKzmyGO4qYhbwFntlsMdxUxA3AWe0nAxnlTVOzAWe0nkMZ5UzTs7KQbTyGM4qbgZwFINJGM8qbp41Itp5DGeVNwR9lINI5DGeVN08fZSLaRiuKnYI1INp5DGcVNQR9lnto5DHcVMwR9lINp5DGcVNwR9vZSDaOQxnOU3BF2C/KNVNCbG1dgqTXIX2CuttmiBc1PbbRVIpkjhQh/PCioRS2y07nYvumkdW/ER4UNqfEZa3t1TbBSatjZiC507Wtqo8DDWydm1EQ4/ZS5wAOh8fdbU1rtdpnX9KWXOm8q5gYZURgCRvHAPo9vb6Xe3BC6o9XNCb40fn8Fch/bqDIcNNNuWnoW5TJRGo0pafPdcCZtu07gzyRpNRHkfPX4hvCruj4c7wYdVW75rS0ryKa5U4LqaUZwGvPeCb3ilweQWF7SCujG4G1mqNvKrFzpvNtLz+zqYwTG77O9WP/wA12PsSF+LDrQuNUBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBFvaacfcbZZbW888tDTTTSFOOOuOKJDbbbaCNS1rUZERERmZmNTWue5rGNJeTgAckk9gB6krS97ImPkkeGxtBJJOAAOSSTwABySey7tOhP7KGvX43RdUeknFqFsWW+lupUPTdCzh3NcrBlxRJFyLwb1v0aRs59n+7Mfbxnqkr4h3x2D8H9fqX5HVm6MUlJYCA+KiB6Z5x+6Zj3hiPfo4le3H4A4FeZHia8fVq0q+56F2WnhrtStJjnuJHXTUzuz20w7VEzfw+ZzCx2f6xzcL0I0G0bcsugUy17SolMt23qJEbhUqj0mI1CgQYrKcJbZjsJSjKj3Uo8qWozUozMzM/UexWq1afttFZrJb4qW1U7AyOKJoYxjR6AD+JPcnJJJJK8hrrqG86lu1dfNQXSesvFTIXyzSvL5HuPclziT9gBw0YAAAAW2SzkjyXr6jckT8YVYZMEcquSWcZ27fXMSkT+ymYJM4VflMcz7+zH5CShk7KXgl7KvSWufv7BJxPUxC/sq1MZwZ7Fjl++RKQSZUzTy5AVclNc9veJSJ/blTUEnblV2Uzz29wlIX9lMQSdlWpTXP189hKQv7Kagk7Kty28Ee3rIlIX9uVNU7+3KrUtrn+nrcSsL+ymqeTsq1La57evd2CWhf2U3Tv7KvSW8ke2+/Z/YSUTlMQP578KtS0YM+49vj8hKwu4CmoH5AVdkowZ+uf5GJOIqYhd2VflI3P1z/ISUTuyl4HcBQEhHPw9GJKI9lLQu7KCfL1t/YSEZUrEVByE8/A/WwkIz2UpEeyhnyxnzGdGeykYj2UO+XP5jNjPZSMR7KKeLs8RmMWfGVGudvfuMpqzWeij3O312jJasxiwXC5jIb6LJZ6KJk5wfPGD8u0ZsWOPdSEOOPdYSMZ7T+Xf5i+7KkIs5KzECw5S0YWQkWys1noudO/PcWysuIBc6RbKzYwslAtOUhEFloIWXKRhCy0FyFhyk4hwFmNlyFhylIR2WaghYcpaELObLkMdxUvA3gLObLchjuKmIG8hSDZchjuPdTMLewUg2nkQxnFTcDOwUg0nkMV5U3Ts7KQbTv5DGeVN08eSFItJGM8qdp4+ykGk8hjPKm6ePtwpJpIxXlT1PH24Ui0nkMV5U3Tx9uFINJGM8qbp4+ykGkDGe5TdPFwFINI9fmMZ7lN08XY4Ug2gYznKbgi7LPbQMZ7lMwRduFpUqLS69TZdHrVPiVSmT2VR5kGayiRGkNLLBpcbcI0mZcyMsGkyIyMjIbd1Hp+xass1x07qW009dYquMxzQTMEkcjD3DmuBH3BHLSA5pBAKz6m1UN1op7dcqOOeilaWvY9oc1wPoQf7j3B5HIXWTr90NajbiZ936VMyqvQWyVKqFqmrr6tSWy3fdpStnKlBa/F1e77aM44yTkeHniv8Ah2XbRguu4GxcE1fpRoMs9sJ66qlb3e6mP4qmBv4vL5njbn+sa0uXSfdvw0V1nZV6j0BFJU2oZdJS/iliH7xi9ZGDv0/jaM46gMr4DWhba1NuJUhaFKQtC0mlaFpMyUlSTIjSpJlgyPcjHlM9j4nvjkYWyNJBBGCCOCCDyCDwQey6iOa5jnMe0h4OCDwQR3BHuto0rSgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIrbY1iXhqXdVHsmw7eql03VX5bcKlUWkRlypkp9xRJzwoLhaZbzlbizS22ndRkW4lbJZLtqO6UlmsdvlqrpO8NZHG0uc4n7DsB6k4AHJIC2/qnVWndE2G46n1XeIKCw0kZfLNK4NY1o+57k9mtGXOPABK9SvQW9lrZ3R7YpWpWszFJvrWY2GpUGnmhFQtTT6Q6glKRS0PI6msV+MSjbOctJttK4jjl+Fwepmwnhesu3wo9T6zjhr9Z9Icxhw+CkJH7gIxJM3sZSOlpz5Y7OXhP4p/HZqPd6Wv0VttLUWrbbqLXyZMdVXtB7ykHMMDvxCEHqcMCU92LtZksc9vy+g7mxSdl0Khl7cqvyGcZ2/ISUT84UtDJnCgJbOOzw/tjcSUL/ALqWp5Puq9LYyRnjl65CShk7BTFPLjAyq7JZ57e7Be75iUik7cqYhk7cqtymTLO3vx9BKRSduVMwSZxyq7LZyRljIk4X4I5UzTyYI5VZlMmWf2ErDIFNwSAquSmj32PAlIX9lMwSdvdVuWzz29fASsL+ymqeTsqzKa57euwSsL+ym4H9lWpTXPn68PISsL+ymoJOyrExrntzzj9feJaB/ZTlO/sq3JbxnkJSJ3ZTUL84Vblt8/R/mJSF3ZTNO/sq3KRsZ9/gJWF3ZTUDuQFXpSM5PzElC5TEDlXpKdz8RJxHgKXhdwFASE8/p63EjEeyloXdlByU8/ISER7KUhd2UI+nn4kM+MqTiKh3y549HzGdGpKI4US8XP3GM2NSEXoox0tzGUxZsfoo5wufrtGU1ZjFguYyfLkMhvospnooiT27Y2P8xmxenKkIPTlYSOYvuUjF3KzECw5S0SyE9gtlZrOwXOgWysuMLIQLRWdGspBC05SEQ5WWgWHKTiCzEELLlJxBZrZCw5S0A5Wa2XIY7ipaFvZZ7ZDHcVMQN7LPaLO+MevEYzypmmZ2We0nl628xjPKmqdmSD6KQaSMZ5U5Ts5CkmkjGeVO07OBwpBpPIYrypumj7cKSaTyGK8qep4+ykWkchivcp2ni7HCkmkeAxXlT1PH24Ug0nkMZ5U3Tx9uFItI5bDGe5TlPFnHCkWkDFe5TlPFyOFItIGM9ynKeLss9tAxnOU1BF2Ui0jkMZ7lOU8WMe6zm0DHc5TEEWccLPbQMZzlMwQ9l8b9Ibog0PUxuZddiNwrdvvq1vPxiSmNRrlcQnJJlpQngg1J0i4SkJLgWeOsLmsecPiz8B2md4W3DXO2cNPady+kvkjAEdJcHAZxKAOmGod+ETgBrzgzDvIuuO83hptWvIqnUGkmRUWrukuc3hsNUQOz8DEcp7CQDBOOsd3LqDuG3a3alYnUC46ZLpFYpzymJkCa0pl9lxJmWcKLC21YylSTNKi3IzIeCmqNK6i0VfrjpjVdnnoL9SSFksMzSx7HD7HuD3a4Za4cgkLzXvdku2nLnV2a+UElNc4HFr43ghwI/mD6EZBHIKhRt9RSAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi/cuj50dtU+k3qHTtNtKbefrNXlGh6p1BaVtUW26V1hNvVmv1HhUzAgs52z991X3UEpWw3hofQuo9wr7T6f01QmWqfy53aOJmeZJHdmtH8SeACVxdu9vFoTZHR9ZrTXt3ZTW+PLY4xgzVMuMthgjzl7z9uGj6nEBexToZ9ArSvob2c2xRYzFz6n1eI3/WepM+Kn+YznlII3KTQG3OM6JbcZeSbaQZOPnlx5SlGSUetGzWy2l9pbU1tHG2p1NK0fMVbh9Tj6siB/q4h6NHLvxPJOAPnN8SXiv154j9RvluUz6HQ1PIfk7dG79mwZ4lnIx51S4cue76WfgjAAJd9kyWNjwXbv3efxHPMUi63wy891ByWM9nrx8RIRSKVhlx6qvymNlERf39/ISMUnblS0EvY5VfksbHt+Xd374EnFJ2UvDL25Veks4yR7/Tz3ElFJ2UvDJ2VclsYzt29xiUhk7KZp5c4VdlMEfFtv7/AA2EpDJ25UzBKeOVW5LJ7ljv7BKRSdjlTUEvY5VZmM8z7BKwP7KappOwVZlM8/X1ErC/spuCTsq5LZ/Ft2n67hKQv7Kap5O3KrEtrGfeJaF/ZTdPJnCrMtrnt7vPwEtC/spunk7cqsTGc5Pnz5F+YloH9gpymk7cqry2+fhnzEtC5TtO9VyU3se3zEpC7spmB/ZVmS3+It+8hLRO7Kbgf2KrklGCUXcJOJ3ZTMLuxVclI57bl4CUicpmB2cKAko3M/mJKJ3ZS0LuygZKS94kYipaEqCkF8c+vqJCMqUiKhnyyQzoypKIqIeL6DNYpCMqLd5+vXaMtiz41GuFz94ymLMYsBzmfkMhvZZbOwUTJwRny5H3+IzYvRSEHosFHP14+BjId2UlDjJys1PLs+H54FgqVjWQkWis5i50C2VmRhZKOwWnLOiHZZSC5C05SMQ7LLbLkLDlJwjss1shYcpWEZxkLNbIWHFS0DeFnNkMdxUvA3lZ7ZDGcVNQN4Ug0kYzypqBnAOFItJ2GM8qcp2cBSDSRivKnKaPspJpPIYzyp2nj7KSaT4DEeVPU8fZSLSRjPKnqaPspNlAxHlT9NH2Ui0gYr3Kdp4uykWkchjPcpynj7e6kW0DFe5TsEXZSTSOQxXuU7TxcDhSDaBjOcpyCLspFpAxnuU3TQ9uFnto5DGc5TcEXbhSDaBjOcpqCLA7LPabGO9ymKeHsSOFntoGM5ymYIskcL8J126Olna40NTc9pukXdCZV/IrqjNF9pjuEkzTDqSU8Jz6W6rZSFfebP7zZkZGSurHiW8LWhPEXp57LlCyh1zTxn5O4sb+0YccRTgY86nceHMd9TPxxkEEO4s3d2L0vu5aHR1kYptSxMPy9W0fW0+jJQP6yI+rTy38TCCCD0e6l6Y3fpPc8q1rxpjkGayanIklJKXAqkPiNLc6nSsEiRHc7cfeQeyiIx87G7G0euNl9XVmjtdWl1PcIyTG8ZMNRFnDZoJMYew/blp+lwB4Xkjr7b7U+2uoKjTuqaB0NW3ljxzHMzPEkT+zmn7cg8EAr8+HGa2SgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIvq7oi9D7VjpjalRbE05pxxqVEWxJvK9p7L39PWfR1uETkyoPoTh+a6nJR4qD66Q5giIiyouQ9t9tNRbm32Oz2SHpp2kGadwPlwszy5x9XH91g5ce3GSOA/EL4itA+HLRU+qtZVgfXyBzaOijcPmKuYDhkbT+FgPMkrvojbyecA+1nowdEvSbojaaQtO9MaOlLqyalXVd85lk7lvWuJaJt6rVqYhJqJtODTGioP7PEawlBGZrWv1j222701tnYYbJp6mHWcGaZwHmzyY5fI729GMH0sHA5yT8ze+PiB1/4hNa1WsdcXElgy2lpGOPy1FBnIihYeM+skpHmSv+pxwGtb++SGOeSz4H8sjk2OT2XE8UvbCg5DGD8Pn4+YkIpOFKQy5CgpLHM8frny7hIRSdgpWGXsMqBlMbmeC/cSMUilYJe3Kr0pg+75Z+Ak4ZFMQShV6SwRkexZ9dwk4pP4KXglx+Srspgt8+Qk4pOymIJeyrUpjGSx+YlYZOym4Jc45VblsYz3eXmXcJSGTOFNU8ucKtymS3LHj3fTsyJWGQ8KagkPCq0xjGduecbfES8EnblTlPL25ValNc9vyErC/spuCTtyqzLa5nj18zErC/7qbp5PTKq8prnt3+u4S8L+ynYJOyq8trn+gloX9lOU8nZVaY1uf09d4l4HqeppOyrMpvmWOXx+gloXKagf2VYlt4M/mJaF3CnKd/CrcpGDP9xKQu7KagdkBVuWjGdu8SkLlM07uyrslHPb13fAScTlMwOUDJTsfeQkoj2UtAeygZCeeBIxnspWI9lVqpU6XTiM6hUYMEscWZkuPFLHfl9xGSEDqHX2hdFsEmsNaWm0x4zmsrKelGPfM8jBj7qdoaGurCBSUcsp/zGOd/zQV+b1HVDT+EayduqluGg1EZRFuzty7COE3II9+4dftQ+PbwfaUklium/VlkkZkH5Qz14yPY0MNS0/oSFvOj0Nq6pDTHYZwD/bAZ/wA8tVHma42G2Z9VIqcvGxHHpric+X2tcU/iODr18W/whWh720Fw1FcwOxpraWA/l85NSH+IC3RT7XarfjzIoI/9KQH/AJgcqxJ17tzKvs9HrTpZPBulBYyXfhMqQZZHFd3+NPsbTPe2wbT6rqmDsZzQU2f0ZVVWP71OQbT3nA8640zT9ut382tUM9r1DM/8K25Ki7OsqTTZ/BMNzA2RU/G801GT8j4d66Qf+MvEUf8AzbdL/MqSj2oqB+O9MB+0ZP8A0wox3XInOVsKLn/8NER7+VKMRx+ORAzPleGJzvz1EB/KxuWdHtcWd74P9j/9tWOnWsi520furBH/AP6shcZ8cyB2PN8ML2/lqIH+djaslu25Z2vOf/iv/tizW9b4v/3S3X0+KKk2v/6aG2JSl+OFpmQj57w610TfXy7xFJ/c63RfzCyRoGRo4ubT/wDFkf8ATKlGNbqCZl9opNXaLtNr7G/j/wBaQwZjfdn+NfsXUPY2/wC02rKVhPJgNBU4/R9VS5/uVDomtb/V1kTvz6h/uKnY2sVlu4616oxc8zfgLURef2Vck/qOWbH8Xfwe3d7G19x1Hawe5qrY54H5/JzVZ/gCtP8AgpdYxx5Tvyd/1gK0wtRLJlmkmrjp6TVy+1KdhbnyIzmNsEXvHPWm/H74OdWSRRWvf6yRSPIAFYZ7eMn0Jr4aZo/Uhaf6EukP46Nx/Ih3/NJVzhVCnTizCnw5hd8WUxILHfllxY7G6b3A0HrVhk0bra0XaPGc0VZT1Qx75gkkGPutYp5ocCWFzfzBH8wphBDc7lnwhZjZCw5SsAWe2XL165DHcVMQN7LOaIYzypmnb24Ug0nkMZ5U1Ts5CkWk8iGM891OU7OWhSLSdyGK8qdp2cgYUk0kYryp6nZ2Ui0nl6/sMV5U7TMzjhSbSeQxXlbgpo84Uk0jkMR7lPU0XZSbSOQxXuW4KeLtwpJpAxXuU7TxdlItI5DFe5TtNF2Uk0gYr3Kepou3HKkWkDFe5TtPEBhSTTfIYz3Kdpoc44Ug2gYrnKbgixjhSDTYxnuU3Tw9ipBpG5DGe5TVPDyOFntoGO5ymoIu2As5tAx3OUxBDgDhZzbfIY7nKWggzgkL831b0Zs3Wm1ZFsXbCSa0kt6j1uO2gqrQagaDS3MgPqLJpM9nWVH1byNlFkkqTwpvfsfoXfnR9TpPWlADIMupapgHzFHNjAlhefQ9pIieiVvDhkNc3Z25W02ld2dNzae1NRjqGXQTtA86nkxgSRu9vR7Cel7eHDOCOhjWrRK8tD7qet254puw3lOO0Ovx21/yytwSUZIfjOKLDb6E4J1lR8batj2wZ/OXvtsLrfYLV8+mNWUvXRPJdS1bAfIqogeHscezwP6yM/Ux3B4wT4ybt7Qar2f1JLY9RU5dSPJNPUtB8qojzw5p9HD99h+pp78YK/HBwkuKkBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF9l9CjoUao9NbVCPZdkxXaXadJdjSr/v+VGcXR7To61/e+9gkTK3NQk0xYqT4lq+8rCCMxyRtntnfNzL6y12xhjoIyDPOR9ETP8ApPP7jByTycAErrd4mfEzoXwz6Gm1Lqads+oKhrm0FA1wE1VMBxx3ZCw4MspGGjgZcQF7iOjv0aNLei1phR9LdKaE1S6RTm23anU3UNrrdz1g20pl1yvTiSTkyfJURmRH9xpH3EESSHqpobRtg0DY6awafpBHTMGXuP45X+skjv3nH+AHAwF8vW8O9mut9tcXHXWvro6e4zOIiiBIhpoc/RBAzOGRtHf955+pxJX7G8yW+S37M8/MxvuOT7rjmKX7qEfYznby2P49hjPjk+6k4pcY5UJIYzkseuYz45OxUpFL2OVAyGMZLs5eODEjFJ6qVhl7FQUpnntt6MSMUnbnlSsEnbnlV+Sxz227BJRSdlLQS9vdVyUxjO23dj6mJOGTspmCXOFXZbHMyL5fmRd4k4ZOwUxTy9hlVyWxkj238CL1kSkMmFM08uMeyrMpjPEWNvXZ5iWhk7Kcgl7cqsymMcRd3dj3CVhk7KbglzgqszY+c/3wJaCRTdNKqrLZ5n5+vkJeF/ZT1PJ2VZls89vXxEtC/spunk7KrTGdzEvC/sp2nlzhVeYz+LYvX1EtA/typ2nk4HKqsxrnt3+thMQP7cqeppO3Kq0tvc9vl5CXhcp2nf2VYmN8yx3iWgd25U5TP7HKrEpGx+/5dwloXdlOQO5C/LbuvmzLQQpdyXLRqOosf4EqY0UxXEXEXBCbU5MdyX+lsxx/r7fTaDaeB824249ptL24/ZTVDPmDkZHRTML6h+Rz9ETvT3C31p7S+pdQuDbLZamoH9prD0D05ecMH6uC+Yro6WGn9PU4zQafWLkdStaSdS0ilQVcJ4JSXpfFKNKj5f4HIdFtwPi1bD6ZdJTaC0xedSVbSR19LaCldjsRLOJKjB+9IOOfXC5wsewOrasMku1XTUTCBxkyv/VrMN4/8ovwCvdKe+qiZpo9NolBa+9hZMu1OXg8cOXZakRj4cf/ABBZHSHXfxb/ABD6g6odE6fsOnqXJw9sL66oAPYGSpd8ucfakGSeeOFy1adiNLUWHXGtqquTjjIiZ/BgLv8Az1+P1jU7UCu8aaldlZcbcUalMR5SoMc+e3UQSjtGks8sDpvrbxb+JfcMTx6q3s1DNSSnLoYap9JTn7GnpPIhxzwOjC5Ft2h9JWrpNFYKYPA4c5oe7/Wf1H+9Uh1519ZuPOuPOK/E46tTi1ealmajHX2qq6qtnfU1tTJNUuOXPe4vcT93OJJ/Urc7I44mhkTA1g9AAB/ALjGOtaAiAiAiAiAiAiAiAiAi3tuONKJbS1trTyW2pSFF5KSZGQyKWsq6CeOqoaqSGpactexzmOB+zmkEfoUVqpt+XjSeEoNx1RCEHlLL0lUtguW3Uy+vaweOWB2K0N4wfE/twKeLSW+WooaSI5bDNVPrKcfYU9Z58ODjkdGFYdTU789ULSfy5/iOV+j0nX27IWE1KFS6u3ksqNpyDIwWckTkdRsFn/6EeB3W0B8YbxH6c6YNc6c0/qSlyMvdC+gqSB3AkpXfLjP3pDgjjjhaPk4B+EEfrx/fn+a/WaJ0grRmGlFWh1KiuGaS4zQmoRSzsZqdj8EgiL/6CO+e2/xhPD7qh0VLuDpa96ZrHOaOvpZcKVue5MtOI6jA+1GeOfTCvsiDTw7/AHf9a/aqDdVt3ClKqLW6dUTPP+ExJb+0FgsnxxVmiSjBd6CHoNtxv5svvBAybbTc2z3h7s/soKhnzIwMnrpXllSzA5+uJvr7FStO0cK4NJHKbypymZ2KkmUjGeVO0zM84Uk0kYryp6mZ2OOVJNJ5DEee6n6eP8IwpNpPIYryp+mj7YUm0nkMR5W4KaPspNlHIYj3LcNNF2Um0jkMR7lP00XZSTSOQxXuU9TRcjhSTSOQxXuU9TQ9lJtIGK9yn6aLspBpAxXuU5TxduFJtN8hivcp+nhxjCz2kDGe5TVPDkhSLbfIYrnKcghPHCkG2+QxnOU3BDwOFntt+Ax3OUzBDgZws5tvOBjucpeCAnCz22z7hjucpmGE8cLNQgY7nKWii7DCo2pulVoau2nOtG8aeiXClIUuJLQlKZ9JnEkyZqFOkGRqYkNHzx91ZfdURkOLd2tqNGbz6Pr9Ga1trZqGUExyAAS08uPpmhf3a9p/Rw+lwIK2vuBtlpbdHTFZpbVVCJaOUEseMCSGTH0yxO7tc3+Dhwchef8A150Fu7Qa7XaDXmlzKNMW69bdxstKTBrMFKtt90sT2EmRPMmeUnuWUmRj5zvED4f9YeH/AFjNp/UERmssxc6irWtIiqYgeOezJWjAkjJy08jLSCvEHezZLVOyeqJLLe4jLaJiXUlU1pEc8YP8GytHD2HkHkZaQV+GDgVcMICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICL666GPQ31Q6amrtO010+iOQ6RFNioX1esmO4ui2XbnXJRIqE50iJDs6QWW4kYj6yQ8ZERYJRlvzbzb69bi3+GzWqPpgGHTTEfRDHnlzvcns1vdx491168SfiO0N4aNvazWurqgSXCTqjoaJrgJqyoxlsbB3DG/ilkx0xs5PJAPvD6N/Rk0v6KmlNB0m0qoyKfR6U0hyqVV5ts6zdFbW2kp1frstKSXKnzXCM8GfAyjCEESUj1I0TpGx6FsVLYbFThlOwZc4/jlf+9I8+rj/AAAwAvlQ3n3v1zv1r267ga9uRmuM7iIogT5NLCCeiCBnZsbB693uy52XFftL7HPBeZfmN8RyfdcaxS+5UM8xnPv223Px9fvnRyKSilUK8xzyXft39xbjOjk+6ko5e3KhX2OeS/X+5jPjk7KSil7KDkx8ke3Lb4iQikx6qVhlx6qvyGOZeZCSjk7KWhl7KBkR+ZY2xsJGKTscqWhl7HKrsqPz2+uD5GJOGRTEEvblV2Uxz228tsZEnDJ291MQS9ueVWpTGDPY9+8SsMnZTcEuccqszI+DPBFg+4SsEuccqapps45VYmMbGeN/j3CWgk5CnKeXkKsymdjyXuP5bCWhepyCTkKqTWMGrbt9+N8eWwmIJOAp6ml4Cq0tnHFtz5CXhf2U7TydlV5rP4vW35iXgf2U7TSdlVJjPP3+/tExA/sp+nk7KrTGj+9t3+u0TED+ynaeTsvzG8rotizac5Vbqr1LoFPQSz+01OYzEQ4aCSakMIcUTsl0uIvuNktW/IRGrdwtE7dWh9911qqhtVpaD+0qZWR9WMZbG1x6pHcj6I2udyMBb403Yr5qSsZQWG1T1dWcfTExzyM9i4gYaPu4gfdfCWo3TjsWlKkQrDo067ZiDcbRU5nHR6JxoXwk42l5tVTltKIjMv8ADZyXaPPXdb4pu2mmTV23ajS9VqC4ty1tTP1UdEHA46mtc01MzcZODHBnjDl2p0b4XdU14hqdV3KK305wTGzE02CM4OCI2H/lPx7L4mvfpNauXup9p24VW9TXjeSVMtlCqU0TLx46pyYlblTfJKNvvPGR77Dzb3S8eXiW3TNVS1Ou5LNY5OsfK2oGjZ0O/cdM1zqqQY4w+dwPPHK7NaY2R290wInx2cVla3H7SpPmnI9QwgRN554Z+q/BXn3pLq35Dzsh91XE4884t11xR/5luOGpa1eJmOn9TVVNbUS1dZUSS1Ujsue9xc9xPcuc4kk/cnK5XiiihjbFDG1kTRgBoAAH2A4C4hYVxARARARARARARARARARARARARARARARARARb23XGXEusuLadQfEhxtakOIV3pWkyUk/IZFJV1VBUw1lDUyQ1cbupj43Fj2kdi1zSHNP3BBVQSDkHBX6pbWtOoFtG223V1VaG31ZfYq0k5yOrRtwIkqUia0Rp2+67gu4d1to/iHeKbaI0lLS7gSXywRdA+Uu4NazoZ+42dzm1kY6fp/Z1DQOOOFnQXGpgIw4Ob7H/AK+6+jrS6TFszzZjXRTpNAkK4EKmx+KoU01KVg1qShCZkdBFz+45jvHqds38XjajVJo7XvHpOr01dHdLXVVP1VtAXF2OpzWtbVQMAwSBHUY5y9bmodQUhLWVLTGffuP7uQvpeg1yi3DETOodUg1SIokn10KQ2+SOLPCl1KT42Vng/uqJKvAeneh9ydA7n2WPUO3ur7feLM4D9pSzMlDSc4bI1p64nnB+iRrH8HLVvy3yQ1LBJBK17Pscq0sp5Ddbyty0sfZSjSOQw3uW4aaLtwpRlHIYj3LcNLFnHClGkchiPctw00XbhSTSBiPcp+mi7KTaRyGK9yn6aLspNlvwGI9y3BTQ9hhSTSOQxXuU9Tw9hhSTTYxXuU/TQYxwpFpAxXuU5Tw9hhSLbYxnuU7BD2wFItt8hiucpyCHtxypBpvkMZ7lNU8PYrObRyGO5ymIITxxws9tvf19BjOcpqCHkDCzm0Cw5yl4Ys8BZqEcthYc5SkUPbhZjbfIzFhzlLQwdiQqJqjpRaWsNn1GzbwgpkwZiFLiTEJSU6kVBKTKPUqc8ZcTMlhR/wDdWnKVbGOLd2tqtIbyaNuOi9ZUAloZmkxyADzaeUD6JoXfuvaf0cMtdkFbT3H2v0tuxpSv0lqyiElFMCWSADzIJQPplid+69p/Rwy08Fed/XXQ67dB71lWrcrCnoTxuSberzTaigV2l8Zpbkx1mXCiQ3sl5oz4ml7Htgz+cvfbY7Vuw2tqrSmpYS+ifl9JVNBEVVBnAew9g4dpGd2O4PGCfBXevZfVWyOsKnTOoYS+jfl9LUtB8qphzw9p9HDtIzux3B4wT+LDhVcPoCICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICL6D6MPRo1P6Wer9taOaVUpU2t1yQlypVV9t3+T2tQWVo/mdx12Q2k/s9OpzKuIy/E6vhbRlSiG59IaSu+tb5SWGzQ9VRIfqcfwxsH4nvPo1o/UngclcRb4b2aH2A27ve4+vK8RWylZiOJpHnVU5B8qngafxSSO49mty93AK9/XRD6H+lvQw0couk+mlPS68hDE+9LxmMNJr993UphKJtdrDyE5Q0SjUiHFSfUw4+EIyo3Fr9Pdv9E2Xb+wU9ks8f1cOllIHXNJjl7z7ejW9mN4HOSfkp8Q3iJ134k9x7nr/WtWWxkujo6Njj5FDS9WWQQtPc9nTSn65pMudgdDW/SbrWPfnHl3GOQGPyuFo5MqMeZzk+3cz/AEIZbH9lmxyYwod9jmoiLx8u8ZscnopGKXsFDSGMl5eue5jOikUlDLhQj7GcnjBkXP1sM+OTsM8KTilxgZ4UJIj5I9t998eewkIpMYUpDLjHKgZLGSPbctvzEhFJj1UrDLj14VfksZyWOWfj2lkScUmMKXhlxhQElgzyRlj3CRikxjlS0MuMcqty4/Mj57/ty7BKwydlNU83Y+irUqPse2/yISkMnb2U1BN29lWpUf8AER+PISsMnZTUEvZVeWxji29/eQl4ZM4U7Ty5xyqvMY3M8bHn17xLQSdhlTtPLwBnlVaaxkj27/y+JiXgkwQp2mlwQqlMZxxbcs+fmJqCTtytwU8vblVeYzz28zEvA/typynk7cr591a1m0y0ggLnX5dVPpLqm1ORKS2v7XXZ+EmpKYVJjccx0l4wSzSloj5qIce7l777XbNW41+v9WU9JMWkx07T5lVLxwI6dmZDnsHODWZ7vC5d2/221vuJVtpdKWGaojDgHykdEEf3fK7DBj2BLvZpXVRq97Qy566uVS9KKC1a1NM1tJuKtoZqFffRl1HWRYBcdMphOINKi4vtLiTLZRDy63h+JlrS9/N2fZuwss1sOWitqg2escPqHVHFzTwZBBHUJ3gjIc0rvpt54QLHamwV+vrq6urRgmngJjgaeDh0nEsmDkceW0j0K6+7luy5rxqTtYuqu1S4Km8ZmuZVZj0x0uLGUtdatSWW/ul91BJSWOQ83NWa01bru7TX7WepK26XiQnMtTK+V/Po0vJDW+zWgNHoF26stgsmnKJlusNqgpKJvZkTGsH5nAy4/c5P3VfG2VLoCICICICICICICICICICKSh0asVFpT9PpVSnMpcNpT0ODKlNJdSlK1NqcYaWgnCQtJmWc4Mj7RyhorZDejcq1VF9252h1Rf7JFUOgfUW21V1dAydrI5HQvlpYJY2ytjlie6MuDwySNxHS9pOFU3K3UbxFV18MUhGQHva045GcOIOMgjP2KjjIyMyMjIyMyMjLBkZbGRke5GRjjKWKSGSSGaNzJmOLXNcCCCDggg8gg8EHkFZgIIBB4W5ttbriGmkmtx1aW20JLKlrWokoSku01KPBDKtturrxcaC02ulfPc6qZkMMTBl8ksjgyNjR6ue9wa0epICo97Y2Oke4BjQST7AckqTn0Ks0plt+pUybBZdc6ptyUwtlK3CSa+AuMiPi4SM/cOU9w/D9vdtLZ7fqDc7au+WGzVdSaeGWupJaZskwYZPLb5jWnq6GucBjkNcRnpOMGkuttr5HxUVdFLI0ZIa4HA7Z4+6iRw+pBARARARARARARARARStHrlYt+YifRKnNpcxsyNMiFIcYWeM4SvgUROI3P7qiMvAbt0Zr3Wu3V6g1FoTVVfaL3GQRNSzPhfx6O6CA9vJy14c055BWRTVVTSSCWmncyQeoOP8A7/6r6ksTpVVmmmxCvemorUQuFB1WnJbiVRtP3E8b0Y+GHM4UkZnw9Soz7THqxsV8WnXmnTRWPfbTcd9tIw019IGU9ewfSOqWH6aapwA4npFO9xOS9xXIFl3AnpnMiutP5kX9tnDh27jsf0wV9qWTqDZ9+RUybZrUWcskEp6CpXUVKLkiM0yIL3C+jhzuoiNHcZj2P2f8Rezu/NrFy2z1rTVs4YHSUrj5VZBkZIlppMStxnBe0OjJ/C8rmiw3i1XmNr7fVtefVvZw/Np5H8l+lMo5DmF7lvqlh7cKUaRyGI9y3BTRdlJtI5DEe5bgpockKUab8BiPctw00OccKTaRyGI9y3BTQ9lJMtjFe5T9LBnCk20DFc5T8EP2Ui03yGK9ynaeHAAxypJpvkYxXuU7TwYAJUg0368RjPcpunh7HHCz20DHc5TMMOcYCkGmxivcpung7DCzm0eH7iw5yl4Ye3HKzW2xjucpeCDGOFmttiw5ylYIOxI4WahAsOcpWKFZaEchYc5ScUWcccL8r1q0TtHXSx51m3VGJK1JckUKuMNoOpW9WCbNMeownFFujOEvsmfA+1lJ4PhUnhze3ZnSO+eiK7R2qqfDyC+lqWgedSVGMNljJ9OwkjP0ysy04PS5vHO8GzOlN69FVukdT0+HkF1NUtA82lnxhssZPp6SRn6ZGZacHpc3zg6s6VXZo1e1Vse8IZsT6e4aosxtK/sVXpy1H9kqlPcURdZGlNlnHNCspVuQ+cfdXa3VWz2tLponV1J0V9O7LJAD5c8RP0TRE92PHPu05aeQV89u6m1+qtoNZXTRWraToroHZZIAfLniJ+iaInux4592nLTyF+ajjhcdICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICK76bac3pq7fdraa6eUGdc16XnWIdCt6iU9pTsibPmuE2gjJJGTTDKcuOuKwhppKlqMiIzGfa7ZXXm4UlrttO6WuneGMa0ZJJ/wBw7k+g5W2Naay01t7pW+611hdYqHTVtpnz1E0hAayNgye/dx4a1o5c4hoySF/QC9nH7PuxugjovEoDLMOuaw3fGiVLVi/SaQp+oVTh61q2qK4aeOJalvKWbbDZHmQ6Sn3DNS0pb9JNq9ubft1Y2UzA2S+TgOqJvVzv8mw+kbOwH7xy49wB8kfjK8XOqPFTuVUXaSSSl26tz3x2qhyQ2OLODUzDs+qqMdT3HiNnTEwANcX9gTrJcjLYcssf7FdR45PUFRjzJZPYsfvtgZbH9lnRyduVEvMYyeNuwZkcn3WfFL255UU+znJ8sdoy45FnxS4UPIYLcyLbt7e/fvGdHJ91JQy9slQj8b8R8+39dhnxy9gpKKbsFCSGM8i8cf27xIRSe6lIZcd1ByI/PbHu5+HuGfFJ2UpDL2UBJj5zt2fH6CSil7KWgmxjlV6TH54Lft8fPPISUUnblS8E3bJVdlR89hePPJ/2EpDL91MwTfdVqXH58sERiVhlU1TzKsy42cnj9xKwy9lN083YZVYmR8ke2DL4mJaCXBCnKebBHKqstjZRdp5+pmJiGTsVPU8vIKq0tjn24z2bCXhk7Kdp5ey/EtVtTLA0jt9+59QrnpltUpBOdSqa8X2uoPtpSZxaZBb45dRlKNaS4GkKMuIs4LcQOtdy9E7Y2STUGuNQwUFuaD09bvrlcP3IYxl8r+R9LGnGQXYHK5O0FojVu4V3isekLHPW15x1dA+iNp/elecMjbweXEZwcZPC6Udf/aV3Rc6ptvaI0xdoURXWMKu+rNsyLomtn1zfW02CfWwaElaFJUlSvtD6VFklIHlxvX8Q/VV/+bsOzlC60Wg5aa6YNfWyD6h1RM+qOmBGCCfNlBGQ5h4XpntJ4KrHY20133OrRcbmMO+UiJbTMPBxI/h85ByCB5cZB5Dl1hVquVm46lKrNfqlQrNWnOrel1Gpy35syQ4tRrUp2RIW44r7yjwWcF2Dzlu95u+oLjVXe+3Oesuk7i6SaaR0kj3Hklz3kuP8V3ittrttmooLbaaCGmoImgMjiY1jGgDGA1oAUWI1ZyAiAiAiAiAiAiAiAiAiAiAi/RrPsSNdsJ+Q3XfsUqK91UmGdN+0G2hZGph5Lv8AMGONt4kqL8JGSkmXcZ+mXgt+H3pbxjaGv+o7b4gTY9VWitEFbbXWT5x0UcrS+lqWVAu9L5kNS1srRmFjmSwTMILWskk2bqLVU+n6mKF9q82CRuWv8zpyR+IY8t2COPU5BB9wP3ezrX/pOmP077d9v66e7N677N9l4esjxWOq6v7RJ4uH7Nni4izxYxtv9Bfgq8KX/uPtrL/tp/h7/hF89qCe5/M/I/IdHnUdDS+R5Pzlb1dPyXmeb5rc+Z0+WOjqfxVqO+f0/XRVnyvk9MQZjq6s4c52c9Lf7WMY9O6/OXNGOscW5/UmONal4/k+ccSjPGf5oWcZHmbcvge/0hca+v8A/dPdHnzvk6f8HM9PW4uxn+nhnGcZwM+wW8mbk9DGs/oXsAP672/+KXJG0aSzIYeXca1oaebcUhulE0tRIWSjSh06k6TajxsrhVjuMZ2mfgiUtk1HYbzXeJiaejpKyGZ8cNhFPK9sUjXlkc5vU4he4Nw2Uwy9Bw7odjB0zbkOkhljbZgHOaRky5AyMcjyxkfbIz7qd1cU0VrNks08aqrE6kj5msmZRq4S8GuLwHYL4yU9qj8JVsgr5WCuk1db/lmknqdK2nri/oA74g83JP0gH+0WqK2+DzfXloPSKd+fyy3/AH4XzOPlkXNiAiAiAiAiAiAiAiAiAiAizKfUZ9Klsz6ZNlU+bHWlxiXDfcjyGlpMlEaHWlJWW5d+DEvYr/fNL3WjvmnLvU0F5p3h0U9PI+KVjgcgtewtcOR78+qvU9TUUkzKilmdHM05Dmkgj9QvsLTLpb1mkKj0vUSIqu08uBsq7BQ21WI6S6tPHLj/AHI1SSlJKMzLqnDM9zUPV/w8fFQ1npc0Omt+7Y6+WIdLBcqcNZcIm/SOqaP6YqsABxJHkzEnJfIeFzNpTd6qoXR0uo4TPTdvNYAJB2/E3s/17dJ/NdgFnXhbN8Utur2tV4lXhKJBOHHc/wAeK4ojPqZkZXC/FeI0mXCtJZweMluPanbXdvbveLTsOqduNVUtztLgOry3ftIXEH9nPE7EkMnBHTI1ucEtyOV2b09d7Vf6RlbaK1k0BxnpPLT7Ob3afzAV8Zb8Bvp7lvelh7DClWW+XrzGG9y3HTQ9sBSTTecDEe5T9NBnHClGm+WBiPctw08PbCkWm+QxnuU7TQcgqTab5DEe5T9NAeDhSLbfgMVzlOwQ5xxwpBtsYznKagh7DCkGm/AYz3Kbp4O2As9tsY7nKZhg9AFmtt+vXeMdzlLQQn1Czm2+RmLDnKXgh7EhZraMjHc5S0MOccLMbQLLnKThhGQMLLQjwFlzlJxRegCy0I+IsOcpOKHGOOV83dJ/o2W/0hrEepriGKfe1FaelWbcXAklxpeONdKnrIuJ+j1M0klxJ7tLw4jBkZK6z+Jrw82Hf/RM1vkYyn1pRNc+gq8DLH9zBKe7qebGHN/cdiRuCCHdffEr4crDv/oea3PayDWdG1z7fV45ZJ3MEp7up5sYcDyx2JG8gh3m8um167Zdw1e1bmp0ilV2hTn6fUoElBodYksKNKi3IuNtZYUhRbLQZGWxj539Tabvej7/AHbTGo7fJS3uimdFNE8YLXtOD+YPdpHDmkEcFfO3qfTN80bqC7aX1Jb5KW+UM7oponjDmvacH8we7SOHNII4KgBBKBQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEXIyy9Ieajx2nH5D7iGWGGUKdeeedUSGmmmkEpbjji1ESUkRmZnghVrS4hrQS4nAA9VokkjhjkmmkayJjSXOJAAAGSSTwABySeAF7h/YvezJa6K2nsXpB6xUOP/AOUFqZQm10qmTWW3peldj1VpuQ1RkKWSih3TX45ocqSmz42GTTFNWeuSO9+x22DdJW9uo7zAP8I6qP6WkZMETuen7SPGC/HIH0f2gvl7+JT43X78avn2h25uj/8AFFZKoiWVhLWXStiJaZjjHXSwOy2nDuHvBnAx5bl3qOtHufy/TvHYdj15Yxydgo9xoj7PRjKa9ZjHkeqjnmeexYPt7P7jJY/7rMjk+/KinmOe2cevLYZjJOyz45eyinmMZMiyWM+X5jMZJnus+OXOMqJeY5mRb93Z+4zGSdgpCOXsCVDPsZzgvMsb8u4Z0cnZSMUuMZKhH4+5mRe4SEcvoVKRTdgVAyI55Pbft8/XvEhFJ91KwzduVByY5Hk/j+fyEhFL2ClIZuwVflRzPOOWN/2ElDL7qXgmAxlVyVHzkyL13iUhl+6mYJuwyq3Lj8zIu3u3/YhKQyqZp5uwKrEuPgzPHy5dngJaGXOOVOU82cDKq8uPjOO0S8MmcKcp5s4VRqhMRWX5Ul1qPGjtOPyZD7iWmWGWkG468864aUNtNNpNSlKMiSRGZnghJtqo4YnzTytZCxpc5ziA1rQMkkngADkk8AclbioTLPJFBDG58z3BrWtBLnEnAAA5JJOABySunbpW+04sfT1yp2Xoa3Av+8mftMKXdbpm7ZdBkpSbRnDUy4hy5JsZ5RnhtSYhKRutwjwOju+Hjm07o353Te1scV21I3qY6rdk0cDsYyzBBqXtPsREC3lzwcL0Y2E8EGqNXsotS7ovltGm3dL2Uo4rJ25z9eQRTMcMfiBlIdw1hGV0NajaoX7q1ckm69Q7nqdz1uSpeJFQfNTMNlRkZRKdERwxafDbIiJLTSEJIklsPKnWuvNX7iXufUOs79UV91eT9Ujshg/sRsGGRsHADGAAADherWjdDaT2/s0Fg0fZIKG2MA+mNvLyP35HnLpHnklzyTklUEbRW7EBEBEBEBEBEBEBEBEBEBEBEBEBFP27clRticqfTjZUtbK2HWJKXHI7zajJRE4ht1lfE2tJKSZKIyMu4zI+w3hp8Tm5nhU1/PuDtm6ilrp6KSkqKWtjllo6mGQtcBNHDPTSl0UrGSwvjmje17ekl0T5Y3xN4s1HfKUUlZ1BocHBzSA4EexII5HBBB/iAR+5WxqfTpkB525ZkCmzkzHG2mIsaf1a4hMx1Nunk5n31PKcL8RbJLbtP348KnxWds9b7eXm6+KLWun9Ma/jvU0VPS0NDdvJkt7aakfFUOybl+0dUyVcZ/bs+mJn7Jv45OLb5oespquNllppZqUxglznR5D8uyP3OMBp7evf2pC9YbkJayRCoSkEpRIM41QyaSM+Ez/6zLcyHQqv+NP4nYq6siodDaAfRNleI3Ghu+XMDiGOP/bscluCeB+Q7LdDdurKWtLqmqDsc/VH3/2a2f8AGG5v/kNCLx+yz/zqeBhu+NV4py1wGg9AAkdxQ3bI+4zfCP4gj7LV/i6sn/vqq/1o/wD2SpNfuis3K605VJKXEMErqI7LaWY7PHjjUltO6lqwWVKNSsFjOB0T8RPix3t8Ud1tNx3a1Kyoo7eHikpKeFlNR0xlx5jmRMyXyP6Wh0sz5ZelrWB4YA0bmtNittkZIyghIc/HU4nLjjtk+32GB9lXh1uUwgIgIgIgIgIgIgIgIgIgIgIgIrLat4XLZNVZrVr1eXSKg0acuRnDJt9BHk2JTCssyWFEZkaFpURkZjfm3W52vNptR02q9vdT1VrvcZH1xOIbI0d45ozlksZyQWSNc0gnhS9lvt209WsuFnrnwVLfVp4I9nDs4fYgrsf0a6XVvXQqHQNRExrZry+qjs1pBmigVJ0y4CN81qUulSHVkX4jNkzVspJFge4vhm+JbpDcE27R+9sUFi1e/ojZXMy231L8dOZOok0kj3Y/EXQEu4fGBhduNu987ReH09r1UGUdzOGiXtBIe3JJzG4n3+jJ7jsvuSL1bzbbrK0OtOoS4042pK23G1kSkONrSZpWhaTyRlsZD1AZUw1MMVRTzNkp5GhzXNIc1zXDLXNcMgtIIIIJBByF2ko42yNZJGQY3DII5BB5BBHBB91MMt4wLD3LctLBjGQpJlvIxXuU/TQZxwpNpvwGI9yn6aDtwpJpvkMV7lP08PIACkW2+RDGe5TcEHYAKQab5DGe9TdPBgj3Ug23gYznKbggxjhZzbfgMZzlMQQcDI5Wc232mQsOcpeGDsSFnNt9ox3OUvDD2JCy22xYc5SkMOVlob5ELLnKSihzgYWYhsWHOUpFD7LKQjAtFykY4gMe6yUI+AtFyz44ifThdfHTr6J6dX7ae1Ksentf8SrUp6lTIkdtLb130CGhbq4KjIiJ+r05viVFNf3nEEbRH+Ah0E8a/hkZutp6XcPR1C3/ABh2yA+YxoAdXUsYJMZ/tTxDJhJ5c3MWfwBdB/HD4VG7uaal3H0Tb2/4x7VTnzI2gB1fSxguMR/tVEIyYS7l7cxA/gC6AVoW0tbTqFNuNqUhxtaTQtC0GaVoWhREpKkqLBke5GPDJ7HxvfHIwtkaSCCMEEcEEHkEHuF4JvY+J745GFsjSQQRggjggg8gg8EFbRpWlARARARARARARARARARARARARARARARARARARARARARARARARARARARelz2EHszS1YuWH0ytb7aRI01sqrON6OW1WWeKNe16U5RE7eMqnr3l25aUkyKL1pdTKqKc8LiGFkfZbYjbUXWrj1lfKYG2wP8A+Dsd2kkH/GEerIz+HPDnj1DSvFD4qvjaOgLJU+G/bC9FmtbnTg3ephP1UVHJ2o2yD8FRVt/ren64qc92OlaR7I1tF3F8viO54f8AdfOE1/3WE6yRbmfbsWOf6C+x/osmOT0WA61kj+J+uzkMlj8LMZJjCj3WcFuXln4fEZLH+xWWyT2KjHWTLO2xdv8AbkMtj+3KzY5O3PKjHmeeCPtz593hgZccizo5eyh32ee2wzY5OykYpO3KiXmM52x3nvv37DMjkUhFL25ULIYzkyLz/YhnxyYxlSUMuMZKg5EfPZ35+XISEUv3UpDNj1UDJjnkzIs/ISMUowpWCYYwSoGTG5njbu32LGRIxS9lLQTduVXJUbGTxt4dnL6iThlzhTME2cBVyVH/ABGRc+e3rbIlIZO3KmYJu2SqzMjcyxkS0MvblTdPP25Xznrzrnpd0d7Ok3tqnckag0sjdap8JHDJrlfmNt9YdOoNKStD9RmGlRZxwtt8RG4tBGRjbmutzNIbY2J+oNYXZtPSchjB9UszgM9EMecvd29mtyOpzQVzJtRtbrreHUcOmNCWV9XXcGR5+mCBhOPMnlILY2d8Zy52CGNcRheXjpd+0P1S6SkqdbVAXK080m6xTbFrU2WtNVr7KXTWiRdtUYUg5prJKf8AlWuGKjG5LMzUfk3vh4p9bbtvqLNbpH2rRGcCmjcfMmGeDUyDHXnj9m3EYx2cck+53h68H2hNlYKW93ZrLxr/AAC6qkYDFAcYLaSJ2ejHP7V2ZTnu0YC68R1bXcBARARARARARARARARARARARARARa4Pu9bdnvBPdbibWfZ+2+NwRbuqWfZ59mPiW5eWQKfqp63LYqFyVFECIRNpIuskylkZtRo5KSlTqiIyNajM8JSR5UruLJl2M8L/AIZtfeKnc+h250OGQQsj+Yr66VpMFBRte1j55AC0yyFzmsgp2OD5pHAdUcbZJY4i9XmlslE+sqck5w1o7vd7D29yewHucA/QdM0xtSAylMiGupv4LjkTHnS4lY+9wMMuNsoRnciMlKL/AFGPpO2t+FT4Q9vrLTUmotFz6p1B0Dzay41NQOp3SA7y6Wmlhpooy7JY1zJZWg4M78ZXD9bri/1UhdDUCCL0awD+9zgXE/qB9glT0wtSeypMeI5TH8HwSIbzpkSsYTxsPrdZWgjLciJJn3lzGjdL4U/hD3Bs9TTab0dUaV1AWHy6u21NQ4NdjDfMpKqWemkjBAL2sZDI8dQEzCeoVotcX+kkDpqgTxerXgf3OaAQf1I+y+erjtqoW1UV0+YRLIy6yNJQRk1KYM+FLqOL8KiPZSTPKVd5YM/my8Tvho1/4V9zq3bnXTGTRuj8+hrYmuFPX0jnOayeIOyWPDmuZPA4l8MrS3L2GOWTl+zXmlvdE2spsjnDmnu13sf5g9iPvkCANKi5kfr+466ZCl1tFUQEQEQEQEQEQEQEQEQEQEQEQEQEX07ol0nrw0pej0mpKeuiy+IkOUeW+o5lNbNeVOUWY4ajY4SM/wDBXllWeSTwY7yeGPxz7k7AzUmnbxJJfNs8gOo5nky0zc5LqKZxJjxk/sXZhdnsw4cOb9st7tQaDlhoa7qrtN5wYnO+uMZ7wvP4fX6Dlh+3ddu2nWoloanUNq4LPqrVRhnwIlR1YaqFNfWni+zVGGajcivFg8ZyhWD4VKIsj392o3n283u0vFq3by/MrLecCWM/RUU7yM+VUQkl0b++O7XYJY5wGV6LaI1bp3XNrju+nK9s1NwHN7SRuI/DIzux39xwcE4X6a03y2HI73Lkqmg7DClGm+QxHuW4KeDsAFIttjFc5TsEOAOFItNcthjPepynp+3CkW2+QxXOU5BBjHus9tvlsMdzlMwQYwSOVnNt8jGO9ymIIexws5tsY7nKXhhz3CzG2/AWHOUrDD24WYhvkLDnKUihJI4WYhHgLLnKTih7YCykIwLJcpKOLGMBZSG+8hZLlIRw+4WQhHuFsuWdHCT+SykI7BZLlIRxegC6OfaH9FIrNqr+udgUtLdq16YhN8UqCjDVBrsoz4K2zGT/ANjS6y6R9dwFwMyj5JS4keNfjt8NTdK3SfeTRFtDdN1soFwhjH001S/tUNYPwwzn8fT9LJj2aJAvEf4hPhU/wOuc2+OgrWG6Wr5gLlBEPppap/apaz92God/WdP0xzHs1sjV1UjzVXlegIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIuwT2bXQYujp49Iyhabxik0zTe3OoujV27G0OkiiWfFfLjp0R1tJl/PrmfT9jhJynC1qdUZIaUY35t3oqp1xqGC3Ny23R4fO/+zGD2H+c8/S3+PYLqN40fFLY/Cns3ddZzlk+s6zqprTSEjM1W5vEjwT/AFFM0+dMecgBgBc9oX9GaxbHtPTSzbZ0+sWhQLZs6zqLT7etug0tlMeFS6TTI6I0SMyhO6jJCMrWo1OOuGpazUpRmfodbqWkttHS2+hhbHSQsDGNaMBrWjAH/wBnuTyeV8bmqdUag1rqS96u1VdZa3UdyqZKipnld1PllkcXPcT+ZwGjDWtAa0BoAFmcbI9/XvEk1+FBsfjhYa2z32+Pv+PIXmu+6yWv+6wnWixy5d3rvGQx/wB1ksk+6jnGSMty9/6eAyWvPusxkhHqo9xoy7PeMpj8rMZJlRrrGxmWMF2cj/sMpknZZscvZRLzHcWNvR5PsGYyT3Kz45fcqJfj45Fv2l65GQzY5PupCKX3Khn2Ofn6MZscikopeyhZEfJnt5kXxIZ8UnZSUM3blQMiPzwXux489uwSMUvupaGbtkqCkxz+8fj2F7siRil7BSsE3YKuyY3PbbG/6e4ScUvb3UxBN255VblRsGZY2Pf14iUhl7HKmYJs4OeV1m9Ob2gmmPRFpbtvxThXzrJUIRv0exIswiYpCHkmUWqXlJjKU7SoClffbjkZSpCCykkJMnBwbvR4itNbR0bqGDortZSMzHSh30xg/hkqHDljT3DBh7x26QQ5d2/C34Rtb+IWvju8/m2vbiGXpmrnM+qYg/VFRtdxK8dnSHMUZ4PU4Fi8lmtmuup3SEvabfuqVyyrgrUlTiIjCjNmk0SEtw3E0yh0xCvs1NgNHjCEFlWCNRqPceT2udfaq3Gvk+oNWXV9TXOyGg8RxNznoiYPpYwew79ySeV9Am2W1eh9odMU2k9CWWOktjAC93eWZ4GDLPIfqkkPu7gZw0AcL8gGzVyGgIgIgIgIgIgIgIgIgIgIuRLa1dnx/TtBBg+q5Ux1Hz9fAD9inH6LnTFLP4c579/h44A/3KuFkJiGR4Ii+e/MvA8ZD7rT3AyuYoh7fDl/csHsA+61Dj8lyFELtLs8OfvItg++eFT9Vv8Asm3bnPPHuAemUC/edL4DUaiSpJJ/xpk9ZLVgiM2o7TaWk7FyStaz/wDEPp8+DBom0WXw2av1tBCP6dveppo5pPUwUEEDKaI8do5J6qQd+Zz+Q4Y3EqZJLxT0xP7KOEED7uJLj+uAP0X5vXbqrlYlOrRPlQ4fGZMRIjzkdtLJKLg602VIVIcPGTNRn97kREREXjb4kPiB+IjfXXF+uVt3GvGn9AGdzaK2W2rnooY6Zj8w/MmndE+rndhssstQX4lJELIYmxxM5BtGlbVbKaJrqSOWqx9T3tDiXY56cg9I9ABjjvk5J0od11yjy2lrnSpkPjIn4ct5yQ2pk1Fx9SbylrYcLJmk0GX3uZGWSN4b/iBeIjYvXFhuVy3Hu+oNAidra22XKrnrYZKZ78zGmNQ+V9JO3qdLHJTlmZQPOZNE6SN676UtNzpZGMpI4qrBLXsaGnPp1YwHDsCDnjsQcFfo+qMBmTRIspSf8aJPQlC+3qpDbiXUeSlNoP8A8I9kPjPaGs158OGkNdTU7f8ACCyalhihk9fl66CdlRD25D5IaWTnsYeDyQePtu6mSO71FMD+ykhJI+7SCD+gLh+q/AjifXu8Tx8B8wq5nP2XCcQ+0iPf0ZbdxeYFFjqiblsZcsdmeRci5HuGe/CcdlwnGx2mXgHPoqLgU2pOMlz7vHl79g91VbDIy59+PeXMEWgIgIgIgIgIgIgIgIgIgIrtYOol26Z1+Pcdn1V6mT2jSl5sjNcOfHSriOJUIij6qVGX/pUWSzkjI9xyRtZu1rzZrVNLq/b++yUV1jID2j6op2A5MU8R+mWM+rXDju0g8rdOkNZ6i0LeIb3pu4Ogq246h3ZI3OeiRnZ7T7Ht6EFdz/R86S1p61Q0U17qLfvqMwTk63XX/wDDmpbIuumUJ10yXMjEe6mjy80nnxERqH0IeF/xkaI8RNvjtNT5Vq3Khi6pqFz/AKZgB9ctG5xzLGDy6M5ljHLupoL16j7Kb46b3Tpm0EhZR6tjZmSmLuHgd3wE8vb6lv42juCASvq5psdvXuXZing7cKRaa5bDFe9TtPT9jhSTTeBivcp6ngx6LPbbGM9ymYIO3Cz22+0xjucpmCD1Kzm2+0Y7nKYhg9SFmttjHc5S0MOcZWYhsWXOUpDDnHssxDfIWHOUpDDwFloR3Cy5ykoouwCyUIFouWfHDghZSECy5ykIoc/kslKOQtFyz44u3CyEox4i2XLOjixwFG1+3KNddDqtt3FT41WodcgSabVKdLbJyPLhS2lNPNOJPkfCrKVFhSFESkmRkRiGvtltWpbPc9P3yhjqbPWQvimieMtfG8Yc0j8jwRgtOCCCAVhX3Tdm1VY7rpzUNvjq7HXQPhnhkGWyRyNLXNI/I5BGC04c0ggFeYDpUdHqr9HTVCo2s91su1qp1lXsqtLSvhqFEecPEV5aiL/rGkuH1EgsnlSSWRmlZGPnc8R2yFz2L3ErtOSdUunKjM1BOc4kp3H8Dif+NhP7OQc8gOBIcF8xHio8Pd38Ou6Fw0tN1y6Wqs1FtqSDiWmcfwOJ/wCOgP7KUc8gPBLXgr5qHAS61ICICICICICICICICICICICICICICICICICICICICICICICICKwWnalxX1c9v2ZaNIm1+6LqrFPoFv0WnMqkTqpV6rKbhwYUZpBGanH5DqU9xczMiIzF+lpp62pgpKWIvqZXhrWjklzjgAfqojUF+s+lrHd9SahuEVJY6CmknqJpCGsiiiaXve4nsGtBPuew5X9Gr2afQdt3oI9G239OW2oszUu50xbr1huVgmVKq95SY2DpcaQ0R9ZRLVjuHChlxrJXC48R5eMh3+250fT6K09BbwAbjJh87/7UhHYH+ywfS39T6r42fGt4obz4qd6LvrJ75I9E0JdS2imd1DyaNrv61zT2mqnDzpuARlkZH7MFdhGRyG166hrdkZLZAtOFopBK7P28hkNeVUOIWI43j9fH8hfa/KyGPysF1olc85+Hl2HgZDH4WUyQjso91nmRkePL4H5mMlknYrLjl7HKjnWee2S5b/mMpj1mxydueVGPMd3v/YhlMkWbHKot5jiyfI/kMyOTCzo5cKGfYznblnbHrcZ0cikopfuod6OR5wX08fmM6OTtypKKbtyoKRH57e7GN/Dl3CQjl7KUhm7cqBkx+Z9heHmXu3EjFL2UtDN2UBJjlg9tjzn89hIxS9uVKwzcjnldD/tJPao2/ocmsaJ9H2oQbk1gWh6DdF4MKZm0HTbiTwLhxVpNbFWvE8n/AIZZZgERG4anTJCOqW+/iYptHR1WktC1DJ9VEFs04w6Okzx0t9Hz/b8Mfrl3A9V/Bf4D7vuibdubu7Ry0W3QIfTUjssnuOOQ9w4dFR/53D5+zMRgud5ULguGuXZW6pctzVaoV2v1ua/UavWKpKdmVCoTpKzW9JlSXlLcdcWo+09iwRYIiIebFfX1t0rKm43Gqknrpnl75HuLnvceSXE8kle9VotFrsFsobLZLfDS2mlibHFDE0MjjY0YDWtbgAD/AOyeVDjEUigIgIgIgIgIgIgIgItyUKUZYI9z5/X4B3RZCI5nzPf5bcw9OQnr9lmNxd/w7bc+7sMs8wPqmfusxEQs75P5dpYIM/ZFlIjEX+UvLn7i7Q9+SmSshMfwBFyEwX7H+wIFyEyWOXr3+IIuQmv9viCLUmdvw+iBVX61p1UEFHlUlxRJdS6cyOkzPLjbiUIeSnJ4Pq1II8c/vGfYePoe+CzvtZJNLa/8O94uEcWoYa913tzHnDqiCaKKGtiiJOHOpnwRTmMAPLKmWQdTIpDHxNuLbJPPpbtG0mIt8t/2IJLSfzBIz24A9RnirGnSn5LsikyY7TbyzWcWX1qUtKUeVE280h5SkZM8EaclyyYjPEd8Gq7ao13fNXeHvXlot9kuNQ+d1suvzUUdI+R3XIymq6WCsfJB1OcYopadr4WhsZll/GNdo3CjhpY6e7Usj5GDHWzpJcB2y1xbg+5B574C0o+nSmJLUirSY7rbKyWUaL1qkuqSeUk486hlSUZIskSTM+WSDw4fBqu2l9dWPV3iE15aLhY7dUMnbbLV81IyrfG7rjZU1dVBRvjg6mtMsUVO58zS6MSxD6yu+4Uc9LJBaqWRkjxjrf0gtB74a0uyfYk8d8Fcuo9QbOPEpKFEpxbxTJCS3NDbaFtspV3dapwzxz+7nlzlfjT76WeHSm3vh6tFwjk1BUV7bvcGMOXQU8EU0FFFLg4aamSeWcMI6wKWN56WSM8zRt1bJDPV3aRhEQZ5bD7kkFxH+iABn/OI7jj8iNkj5kfr6j53/uuWFxmyRgi4lR89hH65+Yfl2QFcKo5blgt9zLBeHgCEDnhYi4vPGOee/buLfvBOcrEXFPu7jyXw/P5hycp9sLCXHPbG3u59/bgV/RUWOptadzLbvLf+wp7Kq2AiAiAiAiAiAiAiAiAizqZU6hRqhDqtJmyadUoD7cqFOhvLYkxpDSuJt1l1syUhST+JbHsJG03e6WG50N6stwlpbtTStkimicWSRvachzXNIII/+weFl0FfW2utprjbaqSCvheHskYS1zHDsWkcgruH6LXTApuoJQbC1Kkx6Xe6Uoj0mtuGhim3TwlwpYeMzS3Drmxfd2bkc04WXCr3O8IHjqt25sdv233Zq4qTcEAMpqs4ZBcMcBrzw2Kr+3DJ+7emQFrvUXw5+Jy362NHozXtQym1aAGwznDYqvH7rvRlR9vwy/u4cMHsPaa5D0je9d8Ken7cKQab5GMZ7lNU8HY4Ug033kMZ71N09P6kLPbb8BjOcpmGDtkLNbbFhzlLQw8jhZrbedxYc5S0MOSDhZiGxYc5SkUPbjhZaEeAsucpKOLtwspCPAWXOUhFFj05WShAtOcpGKHHcLKS3yMWS5Z8cPYrnSjwFsuWbHEPQLnSgWy5ZkcXIPqudKPAWy5ZbIj7L5t6VnR5pfSK0qqdrKQyxdlJJ6s2PVnCbScGutNY+yOurxw0+sNJ6h8uJJFlK8/cIdfvEhspb98tua/TrmsZqSm6p7fMcfs6gD8BJ7RzgeXJyMfS/wDcC64+Kzw72vxFbU3PSxYyPVlIHVFsnOB5VU1v9W5x7RVDR5UoyAMsfn9mAvLdWqNVLdq9ToNbhSKbV6POlU2pwJTZtyIk2G8tiTHdQrcltuoMu4+ZbD54bra7hY7nX2e7Uj4LnSzPiljeMOZIxxa5pHuCD/uXy2XqzXTTt3udhvdFJTXijnfDNE8YfHJG4texwPqHAj+8cKMEeoxARARARARARARARARARARARARARARARARARARARARARARAReq7+Hr6ASajOmdOTVGhkqHS3p9t6CU2qQm1tyKkSXoVz6itokmo+Gmks6fTXSb/AO3VIdQslNIMdkNjtGB0jtYXGH6WkspgR69ny8+34Wn36iDwF4K/F88Wpo6Wm8LuhbpipnbHU32SJ5BbHkPpreS3/KYE9S0u/AIWOaQ9y9b5GO0TXr581uyMlshVMLcRjJa/7rThbiMZLJFTC1MiP19RktflU5Cx3GuZkXu/QZDX+6vMk9CsJxoj2Ms9vrvF9r8eqyWPI9VHusYLlkvj59nd8BkskyVmMlyVGOsc8e7vGWyRZscvbKjXmD7sH89+XwGVHJ91mxy/fIUS+xkjwW/ef0x3jNjkx6qQilxjnhQ78fOdty9YyM2OT+CkYpu3soWRHyR7b+P5+Qz45PupOGbGOVBSI3M8b89/yISEUvblSsM/bleaj2qHtX2bWcuHo49GC4Gn7iL7TSNR9VKQ+h1iiKPiZm2xZsxo1Nu1NOTRKnoM0snlDRmZGsdOt+vEUaAVmidB1ua3llTVsP4PR0ULh+96PkHbs3nJXtX4D/AJLfWWjeXfG0ObZz0zW61ytIdN2LKmsYeRGeDFAeXjDpABhq8uz770l56TJedkSJDrj7777i3Xn3nVm468864aluOuLUalKUZmZnkx0Ke9z3Oe9xLycknkknuSfUle58UUcEccMMbWQsaGta0ABoAwAAOAAOABwAuIaVrQEQEQEQEQEQEQEW9KFK5F9fryBFlNxsnvk9vPsPcvWwqP70zjkqRbi75MsHtnb6n2bDSclVH9yzm4+Mbb7b48PzFcc8KnbhZSWCLfBe8v2DGMIshLRdhGf0BFzpZPuIvXkCLkJgu/9wRcpMljl8fXYCLkJryIEW8m/P3ECLXqj8QRcrJvRnUPsLcaeaUSm3EKNK0KLtJSTI/2E7pjVGotFagtOq9JXuqtupaCZs1PU08jopoZG9nRyMIcDyQecOaS1wLSQbU0MNTE+CeJr4XjBBGQR9wrzDvqoNIJEyI1MNJY61K/sriu418LbjWf+6lJYHsBtT8aXejSlqpLXuptxatWTQtI+aindaaqX+y6YxU1XSFwPfyaOAOaAC0Oy87Brtu7dPI59DVvgB/dI8xo/LJa7+LitZd9VB1Bphw2YhqLHWLWcpxHijKGms5/1JUXgLm5/wAanenUturbdtdtnZ9LyzNDW1M8z7tVQ8DL4fMgo6Qv6gcefRzsDT0mNzsPVKLbq3Qva+trJJgPQDy2n88Fzv4OB+/oqS+zMkJcqD5PupceJDkp0lGlb60qXwdYZYUrhbM8FyIvIeUOqKbc/W1Fe969X091uNurrs2nqbvUtlfHPcJ4pp2wGpeOmScw08r/AC2OJjijALWN6Ad8wOoqZ0dup3Rse2PIjGAQwEDPT6DJHPqT+awja8C+A48WWthsl/p+AH2RcSmC8uYJ3XEpk+ws7doIuFTWOZdvPv8AWA7pyuBTBHnHj3cw+6LEdjEfNPhnHPY9vAE7ZWA5F7t8b/XlsKgd07LAdimRntgz8PIzxjmf6inHHCep5WGtBoPB/H+4ItgIgIgIgIgIgIgIgIt7bjjTiHWlrbdbWlxtxtRocbcQZKQtC0mSkLQoskZbkY1xySQyMlieWytILXAkEEHIII5BB5BHIK1Me+J7JI3lsjSCCDggjkEEcgg8ghdt/RB6Y6KuqmaXatVJDdTMmoVrXhNcJCJ5lhtik1x9ZklEs9ksyFGRLPCV74UPZTwZeOD+mf6L2n3jugF1+mKguUrsCb0ZT1bzwJOwjmPDuGyHOHL1B8LHiriur7ftzudXhtx+mOjrpDgSejYahx4D/Rkp4d+F/OCu1JpoerL3r0yp6fsSs9tsYznKaggxgkLObbGO5yl4YOxws5tsWHOUtDBnkhZiG/AWHOUrDD9lloRkWXOUlFDlZSECy5ykooiVlIR2YFkuUhHD2AHKyUo5C0XLPjixj3WQlAtlyzWRE4yudKPXrcWyVmMj9gudKBbLlmRxepC5kpFslZTI+wC5Up9euY0ErJZGSF0pe076NBRJEfpD2fT8MTHI1J1JiRI6UpalmSI9IulaWsHiWSSjS1mj/tCaWpWVqHk/4/thxTzw72aYosQyubDc2MaAA/hsNWQP7f8AVTOx+Ly3F2XFeJ/xP/DKKOen8RGjrfiCZzILzHGwANkwGU9cQ3/KYEM7un8Yie52XuXTQPLheMaAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi+tug90Urr6Z3ST080Ltono0KuVD+aXrX20oNq17DoxpmXPXHTccab65uEnqYyM8Tsx5psiM1YG49KaeqNT3yitMGQx7svd/Yjby538OB7kgLr74oN+7B4bNltX7p3stfU0sPlUUBJzVV02WU0AwCcF/1ynGGRMkeSAF/Se050/tLSiw7Q00sOjxaBZti29S7YtukQ2mmWYdKpERqJGSomW2kOSXUt9Y86aeN55anFZUozPvZbqalt1HS0FHGGUsLAxjR6NAwP19z6nnuV8WmstXag19qvUWtdV3GSr1HdayWpqJXkkvlleXu7kkNGeljc4YwNY3DWgK7EYlWS8LbGFuIxlMkyAqYW7IyGyEfkqYW4jGVHJx91pIW8jGS2THqtOFrnPMZTJPcqi41tke/b65jIa9XGvIWE4z3l8tvf5DIa/wC6yGSexUe6zzwW3PyGSyRZkcnblRrzGc4LfPrv7RlMk91mxy479lFvMeHZtn6d4y2SLOjl+6iX2MkZGWD/AC7y8xmxyeykIpeRhQ0iOfPGD5Fz3IvdnYxnRS/dSUM33XmQ9r37VgrUO4uix0a7hSdyONyKPqzqVRpJKOhNuIUzNsq1pzCjIqo4hRony2zyykzZbMjNZjqPv1vs6hZV6H0bWf8ACyCyqqGH8APBhicP3j2kcO34Rzle3nw8fAUb8LPvvvVZz/QoLZrVbZm/15ByytqmOH9UDh0ETh9ZxI8Y6QvKkta3FrccWpxxxSlrWtRqWtajNSlrUozUpSlHkzPczHRwkkkk5JXvQ1rWNaxjQGAYAHAAHYAey2ii1ICICICICICICLUiMzwW57/IsgiyURzPdW+3Iuef2D9U/RSbUU/IsY79/j2BnKH9MqRbjkWPu4/fsyCfYLMQyW22/MCn81lJaM8bYL1+QIudDJd2T2BFkJa8C9wJ7rlJrw+IIuUms/sWARcpMH3fH5e4wRchMd+PXuBFyEwXeZ+4E/MK527bEec39unEtTHEaWWCUpBO8B8K1uKSZL6sl5IiIyMzI87c/Zv4b/w6NKb/AGmDvjvd8zLt+aySC322KR8AuBp3GOoqKmojLZm0rJg+nZHTvilklilLpWMjDZePNX6tntc39G23pFX0gveRnozyA0HjqxyScgAjgk8XNdvURbfVnTYxJxjKEmhzlj/tUGlzO/ePZq5/D68Gd1s5sdR4frHHSFuOuET09QOCM/NQTR1OR1E5Mp5APdoxx6zVeoY5PNF1kLvvgj/VII/uX55XbeKmSW/s5OOxpJmTJGXE4hwlFlk+Evvn94uE8ZMjx2b/ADtePrwPVvhW3K05T6HfWXTbfUzni1l7fNqoqpjmNkt0nltHnSDzYnUzwxrp2P6Ol0kUjncs6W1K290czqkNZWQ/j9Glp7PGew4OR6EexCnKRZrZcEiq/eM8KTDQoyIu0vtC0nkz/wBqfeZ7kO+Xg++ELTPprRuH4qXvc+RjZYdPQSOZ0ZwWi61MZDurHLqOke3pPT5tUT5tMNsX/XpBkpLGOAcGUjP+o0/8536N7FZd5NsIpMSM0TTRJmtqbYbJCCJtDElKjQ2kiwhKnCzgsEZl3jk34xsOhdG+FnajbXTEVrtZh1jSzUtsphBT9FHDbbxHLLBRxdHTTxzVETHvZGI2yzMDj1PGcLb41NRe66snL35p3Bzzk5cXxkAuPqQDgZzgfZfmRsHvjHljtHzYLmBcRsGXZy7s/UE9Vxm14H7yyCey4ja8Phz+YIuJTX9jL1sCLgUyXd4+HyBPyWOpk/P6gixltEfgfrtBFhuR87GRfAvWQ4T0Ue7F3yRYPcvDz5mfbgD/AHKnKjXY2ORGXu54zkU++VXnJ91hGk08/X6CqLQEQEQEQEQEQEQEWqVKSolJM0qSZKSpJmSkqI8kZGW5GRirXOa4OaSHA5BHcFVa5zXBzSQ4HII7gruD6FPTCKsfyzSHVSpl/NUpahWZdU50i/mCEESGKFV5Dh7zEpIkxnlHlZfcUeeEx7GeCfxluvDbXs9utdP+2jQ2K3V0rv60DhtLUPP/ABgHEMhP1DEbjnpK9XvB54rW3d1u2q3MuP8A2zAEdBWyO/rQOG007j/xgHEUh/EPocc4K7Xm28D1Sc5epMMGMcLObb5bDHc5S8EByMjhZrbeBYc5SsMGMLLQ34Cw5ylIoO2QstDfwFlzlIxQfwWUhvlsLJcpGOHGBjhZKEYFsuWfHDj0WQlHeLRcs2OH3C50o8BbLlmsj7cLnSgWy5ZbIgOw5XMSRoJWS2P7LlJPrsGglZLYwt3IWnPAHKvAKv3VbNEvS265aVxwWalQripcykVWC+hC0Pw5zK2HSInErSh1BK4m14yhaSUW5EY29qOyWrVVju+m75SNns9dTvhmjcAQ5j2lp75wRnLT3a4Bw5AKgtVaXsmtdNXzSWpKFlTYbjSyU88TgCHRytLXdwQHDOWOxlrgHDBAK8oXSF0YrOguq9z6dVYnHY9OlfbLfqSiSSKxbk/L9IqCDQpaesVHPq3k5yh9taTIjIfOnvRtfdNn9xL/AKJuILoYZOunlOMTU0n1QyDBIyW/S8d2yNc04wvk08RGy162C3Z1Rtzdw59PTy+ZSTHGKijl+qnmGCRks+iQZy2Vj2kAhfiY4rXCKAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi91nsI+g//AOTb0bS1wvekHD1c6RMKBXermMONT7a0ubV9qtCiGh+Ow9Her6VFVpKfvEpL0Ys5awO1e0+mhY7L/SlVHi41gDue7Yu7G+/1fjP5t9l8sPxVPFB/jo3oO1+l7j5m3ujpZIMscDHU3M/TVzZa5wcIMfKRnggsmOMPXewRjl5ki8rsLcRjKZL7nhaSFvyMpsv3VMLcRjLjlHvwtJC3ZGS1/wB+VpW4jGWyXPdUwt5GMlr/ALrThbiMZLJVQhbVIJRDKbJ65VQ4tWI418PXMZLXrIZIsB1kjI9vcWxfoMhknPdZTJDkcqNeYLtLfw9GMtknss2OU+iinmDMj28eXLPLzGWyRZ8cuMLzy+2U9qIx0eqDVOjLoLW2ndcLop5sXzdlPfS4WlVuT2VEqFDcRxZvmsx1/wCHvinR1G4ZG8tBN9c9895f8GaSXSemqj/8IZm4mlaf+52OHYf+OeO39hvP4iMewHw3/AxLu/daHe7de2ObtfQzdVDSSNI/pSojP43g/wDeMLh9XH/CJAGDEbXF/jNffflPvSZLzsmTJdcfkSH3FvPvvvLNx1551w1OOuuuKNSlKMzUZ5PcdDnOc9znvcS8nJJ5JJ7kn3X0jxRRQRRwQRtZCxoa1rQA1rQMAADgADgAcAcBcQ0q4gIgIgIgIgIgIuRDZrMuZF5c+e3nsCKRYjZxgvM/mZcsZDH35Q56fupZqOREW2+C+JchQe/qq/qs9tkixkt+76+IqqLKQ0Z+BAiykMkXZ7zDP8UWQlrw/QEWQlozxz/IEWQlg/L1t4giyEsEXZ8Q+6LmSz4fD58/EEXMUc+74gi5CYPvIgRchR9u34fTkCL9Qgf8pRWFIIjNqF1xJPYjUbanjI8d6j3H2B+Fy6y7X/Do0Dquzxskq7Vt/VXSNrmgNdM2nqrh0uAxkGUkOPdwySckrgK9MFbq2qgkOGvqmsP5Zaz+SoiJlQRIKUUmQp3i4jNTizSoiPJoUgz4TbPH4cYHzBWHxXeIewbm0u7kG7t+m1pHVCZ8k1ZPLHM0ODnU80D3uhkpXgCM0zozCI8MawBrQOZ5bHaZaN1AaCIU5bjAaAR9we4cPfOc+q/TVtNOm0taCUbS+taMy/As0LRxF48Dhj7OrjpjRO4TdAavvtigqpbTUsuluknaOqlnlpJoGzAZLQ9sFVJwS5rJOiZv7WGJ7OvLJqmk+ap4pS0SDoeB+8A4HH8Wj9MjsSFXqpX0sEpmCRPO4MlPmXE02fL7hcnVdx/h8+Q8kfGT8WfTG3kt2258NZpb5rRnVFNeXYltlG/lpFG0HpuM7DkiXPyLHBhBrGmSNu+9PaFmqxHV3jqipjyI+z3f6X9gfb8X+jwVRZBPSXVPSHHHXVfiWszM9uwuxKS7CLYuwfO5rvcDW25+prjrPcHVFbeNUVbsyVNVK6WQgZ6WNJ4ZGzOI4ow2ONv0sY1oAXLNLSU1FCynpIWxwN7BowP/AL/uTyVinH7Pr6zsNoLIXGbBl2BlFwqZ8PiWSwCcLgUwXcXmW4IuBTB9nwP3gix1M89j/L4AnP6rHU1/cgRcC2i7SDun3WKtnu358/yBFiLaLu9fDxBFgPRiMjyRZ8uXcCDjlRb8UtzwZl/9L3F3gCTwqYUa40aMn2Z5dpAqrhBEBEBEBEBEBEBFvbccZcQ60tbTrS0uNOtqUhxtxCiUhaFpMlIWhREZGR5IxrjkkikZLE8tlaQQQSCCDkEEcgg8gjkFa45JIpGSxPLZWkEEEggg5BBHIIPII5BXeb0HOlu3qXTomlOos9CL/pMYm7frMlxKSvClxkERMPqVgiuCC2n72+JLZcRYWSiV7d+Cjxat3HttJtZuHXga9pI8UtQ84+fhYOGuJ/76iA5/yzB1DDw7q9r/AAUeKmLcahpdr9wK4DXdLHilneQPn4WD8Lif++owOf8ALMHUMPDg7svQ34bj0Qc5ek0MGMccrLQ33iy5yk4oPcLMQ2LLnKThg9SspLYslykI4ftwslCBaLlIRxdgAshKMC0XLOjiwudKBbLllxwn1XOSRbJWY1nZcyUDQXLKZF79lyERELZd7rIDQEyMd0voFrwtpmLDn9/dVwtpmMZ0nHBVcLrm9o10e/8AitpSeodvQTfvbS6PJqPAw2tcmrWeo+urlPJLbTi3XKYRfbWi2IiQ6WcrHSDxtbN/4w9vTrOz0vXqqwMdJhoJdNRnmePABJMX9ewcYDZB+8vOL4kXh2/xs7THcXTtD5muNKxvmw0EvqLefqqocBpLjD/3TGOAA2UZy9edMeKa+cJARARARARARARARARARARARARARARARARARARARARdk3sqehu/0z+lvZdm1iA7I0vsRbOoerEkkn9nVa9DlsKi2+t3hWlL92VdTMFJGRn1K3V8kGN4aH0//hDfqaCRuaKL9pL/AKLTw3/lHA/LK6W+PTxHReGzw+al1HbqtrNc3UG32pufqFVOx3VUAZBLaSLrnJ/ttjb+8F/RRix40KNHhw47ESHEYajRYsZptiNGjMNpaYjx2GkoaZYZaQSUISRJSkiIiwQ7eRSNaA1oAaOMD2/JfHXPNNUzTVNTM6SokcXOc4lznOccuc5xyXOJJJJJJPJ5WSRjMZN2weFZwtxGMpkvuVpIW8jGU2T27LThbiMZUcvplUIW8jGXHL2WkhbiMZbJM4GVpwtxGMpkmCqYW8jGUyTK0kLcRjKZJjC04Wux8xlseCqchY7rOc4Lbu9c/IZLJFeZJjusBxnPZj9y5/QZLJFlslwupT2rftFKF0EdH/5fbL1OqvSA1Kp8+HpnQJHVyG6FFIlw5moFahcXGumUZ5RpitrIm5c1PAfEht1I4t3Z3Og0BZfLpHNfqOqaRAw89A7GZw/stP4QeHO47Ar0D8A/g6uviq3E+cvcc0G0llmjfcp25aZ3cPZQQv7CSYcyuGXRQnqGHPYV4Crnue4b1uKt3ddlYqFw3NclTmVmu1uqyFyqhVKpPeXImTZchwzU4688szPkRciIiIiHnlV1dTX1U9bWTulq5Xlz3uOXOcTkkn7r60rHY7Rpmz2zT2n7dDR2SigZDBBE0NjiijaGsYxo4AAGPc9zk8qCGOpVARARARARARARZLTBqMjMu3ljxMveCpkcqWYjltkuXYec5I/EjzkOfVVOf/u/JSjTBERbbfQPX8kWahrkRF7/AKgizG2cdmT7+wPyRZSWvDPyIEWUhrPZn3fl2giykMcslnHZ9O0EWUhnuL4F8dwRZKWD7sf2BFkpjeGfP+xgEWSmP7vL0YJ6LnTG/wBpn27+s9oJlcxRsFyIg9EXIUc/d4F7/qYIrw0gzpaGyIzUcHgIiIzMzNjBERFuZmPr72YttwvHwz9OWi0UE1VdaraiphhhhY6WWaWW1TsjiijYHPkkke4MYxgLnuIa0EkBcB3F7I9YzSSODY21wJJOAAHjJJPYD3VS/lsj/wCTSP8A8E5/7I+YT/3J3in/APyadwP/ANnrv/8AU1zP/Ttk/wDDNL/tY/8A1lb6glxUF5DRLNxSEI4UEZqMlLQlZERb7oM8+A+pfxk0O5LvApr+x7aWK71ev6iwWuiZSUFPUTVz46iqoKWviZT07HVDiKKSqE7WsPTEJS8BjXY4T086j/wmpZKyWNtKJXu6nEBoIa9zSSePxBuPvjCp/wDLZP8A8mk//gXP/Z7h8s//ALk7xT//AJNO4H/7PXf/AOprm3+nbJ/4Ypf9tH/6y4nIDzZcTjTqEmZERrbUkjPmScqTgzwRjaGttkN6NtbVT33cbaHVFgsktQIGVFytVdQwPncx8jYWS1UEUbpXRxSvbGHF5ZG9wBaxxF+muVurHmKjr4JZQM4Y9riBwM4aScZIGfuPdY5x/I/LYcXrNXCqN/t+AIsdUf8ATcgRY643P7uMdpcg7Kqxlx+f0Pn+oKixls+Hx3L4gixlsduMc+QIsRbJl2e8vXgCLFW1/cvzBFjLaI9jLHiQIsRxoy5lkgRYTjBGR+PP12ByEUU9GznHljO3PHLGxEXmBxjsnH9yiXmDIzMiPOTM8898bdmMCg7/AGQnKxBVEBEBEBEBEBEBFI0mrVOg1OBWqNOk02q0uWxOp8+G6pmTElxnCdZfZcTulaFpI+4+R7DPtV0uNjuVDeLRWyU10ppWyRSxktfG9hy1zSOxBH+48KQtV1uVjuVBebPWyU10pZWyRSxktfHIw5a5pHYgj/ceF6Ouh30m6d0hrJ+zVVcaJqRa0aMxdVNaNLaagyeGWLkgR88SYk5ZYeSnKWXz4SwlSCH0D+FHxJUO/Ojfl7q+OLcK2RsbWxDAEreGtq42+jJDxI0cRyHAw1zQvol8IHiSt3iB0X8tdHxxbjWuNja6FuAJW/hbWRN7hkh4kAyI5T08NcwL7OQ2O1TnLudDB291lIbwLJcpGOHByslLYtFyz44e3sshKO4Wi5Z0cOewWQlAtlyzWRY9FzJQNBKymRrmJOBaLllNYAtc4Fl8mOFdAW3IxnSKuFtyMd0g9+VqwtpmMd8uVXC0yMZ0n3WrC4Hm2pDTrD7Tb7D7a2XmXkJcaeacSaHGnW1kpDjbiFGSkmRkZHgxhzmOaN8MrA6JwLXNIBBBGCCDwQRwQeCO60SwQ1EUsFRE18D2lrmuAc1zXDBa4HIIIJBBGCOCvLl0ytCXNBdbK/QoMZbVoXGpd0WW7g+qKj1B9w3qYleEkblFnEuOZf6EoV/mIeCPiX2ndtJujd7TSQlunK0mqoj6eTI45iB94H9UZ/zQ0+q+Vrxp7CSbAb46gsFDTObo65E11td+78vM49UAPHNNL1Qn/NDHfvBfKI6+rqUgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIvfH7EXoeF0YOiJRrzualfY9UukD9g1Euc5LBtz6VazsZX9BW451hdY0TFGkqnOoyX+PPURllBDsrt3ZhZrIyeVmKyqw93uG/uN/QHP5uXyc/E88RZ3x8Q1y03ZK/wAzQmkfMt9N0uzHLVBw+eqBjg9UzRA0/wBiAEHDiu5YjHIrJQccrzdwt5GMpkpHqqELcRjMZMPdacLeRjKjmxzlaSFuIxlslyPuqELcRjKZLhaSFvIxlsl9lpIW4jGWyXBHK0kLeRjLZJ2VCFuIxlsl9+604W8jGUyTscrSQt2e8ZbJfcrThfN3Sy6TOnHRC0LvTXbU2UaKJa0QmqXR2H2mKldlzzUOIoNq0hTpKI6hV5TZlxcK+pZS46aVJbUQh9Uapt2kbHW325P/AGETfpaDzI8/hY37uP8AAZPouaNgNktZ+IfdPTW1eiIM3SukzLM5pdHS0zCDPVS4/wCLhac4yOt5ZGCC8Ffzeuk70kNSOlhrTeet+qVRVMuO7Z6nI8Bt11ym23Q46lN0a2aK26ZmxSqND4WmyIi4jJSzLiUY859UakuWrb1W3y6y9VTM7gejGj8LG+zWjgfx9V9nOx+zGjNgdtNN7YaFoxHZrfEA6QgCSpndzNUzEfilmflzjzgYaDhoX4CNvrlpARARARARARARZjMfiMs75P3ePmBTOFMMR98mW5EWMcvfsXYHoilG2SIt9+7vBP0Wcho+3l67wRZqG/DHh2mH8kCykNdnvwX5gizEMcs/AEWYhrsx7iBFmIj8vdsQJ9lmtx/DHl3Aiy0R+W3vBFlIj5Lv7NtiBFkojn3EXruDhFkpjeBnt3Ai50xT/wBJfDIIuUop+BevIEU8060hppBr3Q2hJ/dVzSkiP/L3j6lvCh49fCPt94bNk9E6x3mo6LVNr07SU9VA6kuL3QzRxgPYXRUb43Fp4yx7m+xK4Uvml79V3i41NPbnOgfK4tPUzkE9+XZXL1zf+o//AFVf+yOwf/ZJvBF/8PdD/wDIrp/9RUT/AIIak/8ABbv9Zn/rJ1zf+r/81X/sh/2SbwR//D1Q/wDyK6f/AFFP8D9Sf+C3f6zP/WWnXtf6j/8AVX/7If8AZJvBH/8AD3Q//I7p/wDUU/wP1J/4Ld/rM/8AWWDONEhpKEHxGTiVY4T5ElZczLH+Yea3xTPFx4dd+/D7o/R+0e5lNetSU2sqSskgjp62JzaaO23aB8pdU00LCGy1ELOkOL8vBDSA4jeGibDdrXdaior6Mxwup3NBJaeS+M44JPYH+ChlRT/0l4H68x4H8lcorgXFx2GXluQIsdUfwI/kH80WMuP4GX0D9EWKuP4Z8iBBlYbkfuLPgYIsNxj3Gff2+sgixFs45lj6Aiw1sdxY9dgIsJbXMjL9DBFirb5ljzIEWE41jJl8PWARYTjRHnbfu/QEUY/GI84Ii595Y2PzItzBFDPMczIvvEXojxnciAgjHsgwsIyMjwZYMEWgIgIgIgIgIgIv0fSbVG6dG77oV/2jKNiqUaSSnY6lrTFqlPcMkzqTPSg8uQ5zGUKLfB4UW5EN/wC2O5GpNp9aWbXGlqksuVJJktJIZNEeJIZAO8cjfpI9OHDkBci7U7n6n2e11Y9e6Sqei50cmXMJIZPEeJYJQO8crctPtw4cgL1IaM6q2xrZp9QdQrTd4oFXZNEuE44hcqj1aOSE1GjzTRgvtMF1WM4TxoNKyIiURD6NNq9z9O7u6Hs2udNS/wDAqpmHxkgvgmbjzYJMfvxuPfA6mlrwAHBfUNszujpjerb6xbg6TlzQVbMSRkgvp52ACWnkx+/E44zgdTS14ADgF+spRgcgFy5ejix6crISjItFyzo4s+i50o7iFsuWYyLngLmSgaC5ZTIlybELL3geqyA0BaGYxnSla8LaZjGdJ91qwtuRjOkJVcLbkY7pPuq4W0zGM6X2WrC2GYxHS8kkquFtMxjOn/gtWF8H+0F0MLV7RGfXqTD6+8dM/tN0Ujqm+KTMo6GS/qSlJ4S4lk5AaKQhP/xkYiLdQ6leL/a8bjbX1V1t9P16jsfVVQ4GXPhA/wCExDHJzGBIB/ajGO66DfER2FG8Wxdw1BZ6LzNZ6X666n6Rl8lOG/8ADYBjk5iaJmj+3CABly82g8U18y6AiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi7DPZedE57phdMTTXTuowXpOn9tS/+ImqT5NunGbsm1JEaVIpj76I8hpldz1RyLTG+MiIzlmfJJ43HpS0/0xeqWne3NMw9b/8ARb6f8o4H6rqB459/4/Dr4dNa6xo6prNW1sf9H2xuR1Gtqmua2RrS5pIpohLUuxziID1C/owx2mYrDMaMy1HjR2m2I8dhtDTDDDKCbaZZabJLbTTSEklKUkRERYIdmWTdLQABgL435ZJZ5ZJ5pHPme4uc5xJc5xOSSTySTySeSeSsgjGWyYHCtYW4jGUybtkrSQtxGMpkoOOVpwt5GMpkxHrwqYW4lDMjnB9VpIW8jGW2ZacLeRjLjm7LSQtxGMtk3C04W8jGZHLz3VCFuIxmMl9ytJC3kYy2SeoK04XFKmRYEWTOnSY8KFCjvS5kyW83GixIsZtT0iTJkPKQyxHYZQalrUZJSkjMzIiGUJ2MY573hrAMkk4AA7kn2+/8Vrgp56ueClpYHy1UrwxjGAuc9ziA1rWgEuc4kAAAkkgDlfz/AL2xntDpnTY18ftSxqo7/wCT7o7PqNDsGOyt9Ma7q4hxcSu6hy2nDQlw6qprqYBcCeqgISe6nFmfRvdvcCTWd8NLRyn+gaRxbEBnD3dnSn/S7N44b9yV9bfw5PB9T+GXaeK/aooW/wCN3UcUc9e4hpdSQEB8FvYRnHlA9c/1HqncRw1jQOnccSL0YQEQEQEQEQEQEWawwZmnb72/d9fAiD8k59+FNR4+C8/WO/mCcqVbaIsbf28Q590Wc2137gn2WYhv12ECLMbazyIE/ms9tki5F+pgizW2PDt9cwThZ7bHLbHh+veCLObY5bYL359wIs5uP3F7zBP1Wa3H5YTkwwizURT7fcRfTAIstEUuxPx27P3BP1WSmNy2+BZ9YBAudMXP+Uz8/huQIucohl/l7+wE791yFEPwL4An3W77J49nrsBP0T7J+XcCLacQ+zB/AEXGqIefwl3Z9eYIFwqimX+U/cCLgVG5/Qy7PqCfyWKuKR80/AEWKuL3fP8AQEWE5GxnJY8gRYbkbwz5fUyBFguR852z4dpAiwXGPDzI/XeCLAcZ54+AIsFxrnt4+P7giwXGcF3kCeuPVYa2/Lz/AFIEWG618fr+oIsBxvPMt9/oCKMfj5zyzg+fv5n2bh2RQsiOeTMiweT7OfcZ4zuAx6p7rAMjI8H6+IItARARARARARARfcPQb6Tb2gepDdHuGWv/AIaXxJi0+5G3FOG1RagpaWadczKE8RJ+xmvgk/dPjjqPtQky7e+EDxBzbLa8Zar3Un/AC8PZFVAk4glJ6YqtoHboz0y8HqiJ9WgrvB4HPEzNsJuRFZ9Q1Z/xa3yRkVYCT000pIbDWtAzjyyembg9UJPqxpHpWjLZkMsyY7rUhh9tt5h9lxLrLzLqCcadadQakONuIUSkqIzIyPJD3sjninijngla+B7Q5rmkFrmkZBBHBBHII4I5X0sUphqIYamnlbJBI0Oa9pDmua4Za5rhkEOBBBGQQchZiUChcpFkXb2XKSSIW3OwO6ymxgLUzGO+XuArgC2mYxXyZzkrVhbDMYr5cZC1YW0zGM+TuVXC2mYxnzfxWrC2mYxXy+5VcLaZjFfMPRagFsyMV0v3VcLaZjGdMOeeVqwuJ1Dbza2nUIdadQpt1pxJLbcbWk0rQtCiNK0LSZkZGWDIxiSvbKxzJGhzHAgg8gg9wQeCCOCPZaZIo5o5IZo2vie0hzSAQQRggg8EEcEHgheWrpfaLr0N1zuy14sdbVs1Z7+qLPcNK+qVb9ZddebiNuKaaQ4qkTEvRFcJGRGzntIeFPiF23dthujf7JTxFtkqHfM0h5wYJiSGA4AJieHxHH9j7r5RPGVshJsLv1q7SlLTOZperf8APW44PSaSpc5zY2ktaCaeQSU7sesefUL5iHCK6sICICICICICICICICICICICICICICICICICL23fw+vRUTpH0YKv0hLipqWLz6RFTKRRHXm2TlQtMLWky6fQUNuJcW6y3cFb+2TVIUSesYTGXgy4THMugrf8lbn1z24mqDx/oN4H8Tk/wXzG/F036O4W+Vu2hs1aX6b0fB0zBpPS+51TWSTkggAmnh8mEEZ6Xmduc5XoAJQ5CZOfVeSWFuIxlMmB9VpwtxKGU2c+6phbyMZbJh78rSQtxGMpk2OFpIW8jGWyYe60kLcRjLZMfdUwt5GMxk2fVaSFvJQzGT9lpwtxGMxk3sVpwt5GMxk2cArSQt5GMyOb0ytJC86Ht9faCL0U0uY6JWl9d+zan6xUc5motQpk5tM60NLZC3ox0tao5qkwatfL7KmiIzacTTm3FfeS+kxwpvTrt1qtg0zbZ8XCrZmUg8shPHTxyHSYx6fRn3C9jvhNeEZu5uupfEDrm1deh9OVPRb45WEsq7o0B3mjqw18VC1wd2e01DmDh0Tl4pB1IX0yoCICICICICICLMYYyZKMs5Ise/4fUM4Byn5qbYjkWMlk/LbzwHP5JypVpvBEXMz9fkCYWe03jHefrIJ/NZyG/wBzBFnNs95eOPXiCLPbaM8YIEUg0xywW/j3iiKRaY8PefLt7BVFItMf3MgRZ7UfJlgveYJypFuKXaWfXaCLPbjeGPLfyBFnNxT7CIvr8e8FRZaIhdxn68eQfzVVmIiH/pIvHvBO6yExO/5fsQIudMQv9Jn7gRcpQ/8AZy/ffGA7ot32M/8AT8Pp4Ai1+xnj8Beu0EWw4f8As8PEEXEqGRf5TLYEXCqJ5+R7Anr3WOuIe/3SP3eiBFiLieBkee79ARYbkQ+7Jb+AIsJyN/Yy+hkCqo92KXdg/cCosB2NjmRAnqo51jvLbvIEUe6xjmXf2An2Uc6z3l5HuCKPcaxz+ONj8wRYDrPMyLzLHosAiwFt/D6dgIsJxrPn7viCLAcbzsfMPuijH2CPOxZxz9+3Z2B3wqKEfY57YUWD5eHeWTPYPZVUeZGWx7GCICICICICICICLv79mt0lz1AtBzRS8Kj1t32LBJ+1pMyQk5Fcs9pTbX2MjdNLsibbzjhIMiNajiqQexNqHs14Ed/Xaz0w/anU9d1amtEPVSPe4dVRRDA6OeXPpiQ3u4mEtPAYV9AHw0vEv/jB0hLsnrC4der7FB10L5Hjrqbe0hvl/Vhz5KQkNxlxMDmHgRuXadsQ9BnyAL1WAHotpmMR8pK1YW0zGK+XHdagFtMxiuk+/C1YWwzGK+X2KrhbTMYr5vutQC2moYj5+61ALYZjFfN7nhagFtMxivmVcLaZjFfLk5K1YW0zGM+ZVwthmMV0xwQStWF1pe0z0bK9tIoOptLiE5XtL5fW1BbaUdc/aNYeZjVFKlGpK1pplQ6iQSSI+Fs3Vd46WeNHb5uptA0utKKDN1skmXkAZdSzENkBOckRv6Hgc4b5h915f/FI2SGudmqDdO00gdqDSk3VMQB1Pt1S5rJgSSCRBN5UwAz0sMzvdefweUK+dZARARARARARARARARARARARARARARARARftvRu0Ur3SM150o0QtslFU9Sb1ottnJSSjKnUyTJS7XKu6aUOcLNHorMiUs8GRJaMZVDSuraunpWd3uA/Iep/QLjHejcy1bN7U6+3PvJHyNltk1T0/5SRrcQRDkfVNMY4hz3eF/TRsazrf06sy07BtOEim2xZVuUW1bfgIJBFEo9Bp0el05kzbQ2lS0RYyeJRJLiVk+0c805ZBFFDGMRsaAB9gML4kNUaju+sdSX/Vl/qTNfLnWTVVRIc/XNPI6WR3JJALnHAycDA9FayUM5s/YKAwtxGMlk3rlUwtxKGUyfGMnhaSFuJQy45wfVaSFvJQy2TqhC3EoZbJ+xJWnC3kYy2THjlacLeShmMnHutOFuJQzI58LSQt5GMyObPqtJC3kYzI5vTK0kL8S6SGv1j9GDRHUPXPUKUlm27At+VVlxCfSxLrlUMiYotu001IcNVRrtUdajNYQvhNzjNJpSoY94v1LYbXW3asd+whYTjPLj+60fdxwBx6+y5O2Y2l1Rvjudo/a3SEBdertVtiD+kuZBF+Kaok5GI4Ig6R2S3Ib0g5cF/NO6QOuV9dJPWS/wDW3UeeufdmoFwTK3NT1ilxqZGcV1dNodNSZISxS6LT22o0dtKUpS22WxGZjo3ervWX66Vt2rn9VTM8uPsB6NHsGjAA9gvtZ2j2u0tsttxpLbHRlIIrBaKRkLOMOkcBmSeQ89Us0hdJI4kkuceV+NiLXI6AiAi14VcJKwfCZmklYPhNSSSaiI+RmklFnuyQff0VMjJbnn/7v+orQFVARZDLZqMlY2L9+/xFe3dBj1U5HYxg+Z+/y7cCnvlMfdTDTeMd/l8+8EWe01jB9ph+iLPbb9d57fUEWe01yPHkCKQaaM/1BFJNM+GC8tzBFJtMZxkvcXb8DyCe6k22OW3u/UECk2Y3LJeRECKSaj8tvcXMFRSTcU+4iL693mCqpFqKfIk+8y+AYRZ7cTltny+HME/NZ7cQ8/hIgRZiIZdpb/H+wIstEQuxHx2/fcFRZCYZ7bEXL13GCquUonj8i8ARbiieiIwRPshd/rl3dpgn5racT1gsfMURcSoZ/wCkj5dnrkKouBcT/by9fkCLFXDLux58+0AhysNyGfdnzL6doFPdYDkTtwZfT5dgIsB2KeDyWS7/AFtuKoo12MZZxy+h5FMIo92PzLHmR+IIox2NzwW+fXYCKMdY57YPuPtBFGOsc9vMjBFGusc9vcZcgRRjrJl5d/d4Bwn5KPdazy5/XmCZKj3G/wC3cCLCdbznv7P7h+aKOcbI85Lf1sCKJkMZI9tyzjH094r6hMqf05te27s1CtC2LxuVyzLbr1egUmr3MiA3UDozE10o6JTsV6XBbSx9oWhDry3OGO2pTppWSDQrd2gbHYdSa10zp7VF+da7FWVkcM1UIxL5DXu6Q8tL42hvUQHvLsRNLpC14Z0O35thpvTWrtxNIaW1lqZ1l01X18VPUVoiE3yzZHdAe5jpImhvWWtkkc/ELC6UteGFju53pLez002ofR4mK0Zt+YV+aepkXQ5U5cn+YXDfNNZZ/wDrgp1SfV1DKpDNPbOVCYisto66P1TTXHIUZ+qu/Hgp0LaNk6k7XWWX/DCyh1WZXu8ypr4mt/4TFK49LS5sY82COJjW9cflxx9Uzifa7xMfDx21sfh2rHbMafm/w908H1xmkf51Xc4Wt/4XDM49LS9sQM1NFBGxvmReVFF11Dien7RLQrUTpAXg3ZundKRLlNslLq1XnuORKDb1O4+D+YVqoIZfOO0tf3W20IcfeVs22syPHmTtRtDrfefU7NL6JtwkqA3rmmkJZT00eceZPIGu6QTw1rWukeeGMcQcePGyGxG4viC1jHozbq1Nlq2s8yoqJSY6WkhzjzamUNd0NJ4YxrXyyO4jjeQcdncb2REtVEJcvXOO1chsKcOPGsJ2RRESiTlMUpjt1Rp7rCllg3+oQpJHnqTxwn39g+GlUm0h1Tu4xt96CeltvLoA/HDOs1bZC0ngyeW0gHPlHGD6gU3wg6t1jD6vfSNmpDGT0MtTn0wfjhnmOrmSuaTwZfKaQDnyTjpd1ydIHo0an9G+4Y9HvynR3qXVTfVbl2UdxyXb1fajmXXJjSHGmX4dQjpWk3okhDb6CUSiJTakOK6Ob0bDa/2LvcFr1jRMdb6jqNNWQkvpqgN/EGuIa5kjcjrhka2RuQ4B0bmvd5weIPwzboeG3UVPZte26N9rq+o0dfTkyUlU1h+oMeWtdHKwEeZBK1kjQQ4B0bmSO+fRwuuvaAivemWolxaUX3bOoNqyVRq1bNTYqEcuI0sy2Unwy6fLIiMnIdQiqW06kyMjQs9hu/QWtr3t1q+w6007OY7rQVDZG84DwOHxv92SMJY4EHg9lvzbHcXUe02vNM7haUqTHerZVNlZzhsjRxJDIOcxysLo3gggtceF6yNKtS7e1e0+tfUS2HicpVy0xmalg3EuPU6YWW59LlmSUGUqnTELaXlKcmniIsGQ+ivb/X9l3J0ZYNa2GXNur4A8Nzl0b+0kT+B9cTw5juBnGQMEL62dpty9O7w7eaW3G0vMHWm50rZOnILoZPwywScDEkMgdG7IGenqAwQv0AzG6XzH0K5GwtpmMV8w9DytQC2GYxJJu/K1ALaZjDfMMcFasLYZjFfN6krUAtpmMR8w5AVcLYZjFfN35WrC2moYrp+OCq4W0zGK+b1ytWFtNQxXz9+VqwthmMV8xPqq4UNcNDpl0UGtW1Wo5SqRX6XPo1TjK4cPQalFdiSmy4kqSlSmXjweDwe4h7xRUV6tdxs9xj66CqgfFI0+rJGlrh/AnH35UPqPT1r1Zp++aXvlMJrNcaSWmnYcfVFNG6OQcgjJa44ODg4K8luqtg1HS3Ua89PqqRnLtSvz6UTp5xKiNOmunzkZSnLc6A408k8cljwl1vper0Vq7UOla3+voap8ef7TAcxvHbh7C1w+xXx47u7d3Pabc3W+3F3B+ctFxmp+r/KRtdmGUcDiWEskHHZwX5+NrLjlARARARARARARARARARARARARARARARelr+HH6N5XNq1qv0oK3EJVO0xobenVkuudYXFd96MKkXFNj/4fVKVSbUjlHX9/JFVS2G7tJ048+eseOGDpb+Z7/wB3H6rxS+MrvObJt/oHY22VGKy+VRuFaBjiko3BtOx3OQJap3mDjGaXuvYORjkJkx91862FuJQyWT/dacLcRjKZN7FUwt5KGUyfstOFuJQyWzjtlUwtxKGZHOfdaSFvJQymTrSQt5KGZHPj1WkhbiUMxlQOFpIW8lDMjn7crSQt5KGYyf7rSQt5KGbHUdvZaSF42v4hnptq1B1PoPQ6sSr9ZaGkz7FzaqOQpBnHrGpU+Jmk0CT1MpbLzVlUWSa1oW2laJ05xJ7sljr/ALu6pNbWQ6dpZP8Ag0B65cHgyEcN7/uD7d3fZfR98IDwxDSGh7t4jNVW/GotQNdTWsPb9UNtjf8AtZ29TQ5prZm4BDiDDAwjiQ58044WXtYgIgIueLFlTpUaFCjPzJkx9mLEiRWXJEqVKkOJaYjRmGkrdfffdWSUISRqUoyIiMzGprXPc1jGkvJwAOSSewA9SVanngpYJqmpmZHTRsLnvcQ1rWtGXOc44DWtAJJJAAGTwvf30CvZR6KaWdBmlaF9JLSu0NR7u1VcY1F1liXDBblyaPd1RppxaRQaBXohRK3b87T+hSPsLcynSmX25zs16O8SJA766E2rstr0RFY9R2uGoq6rE1QHjJbIRhrWOGHMMTT0hzHAhxeWuw5fJR4r/HxuZrvxSV+6ey+vLjZtPWEOt9nfTvLGzUkcnVLPPA/rhqGV87fPdDURPY6BtNHNEXQrpr6df8O/qHYBVrUXoVVqbqnaEduRUZejV1zITGplGjssrkSW7RrxogUa/I7ZIX1UN1MKq8JIaa/mD6snw9rnw+XCg8646MmdVUgBJp3kCZo7ny3cNlHs09L+wHmOK9IfCv8AGE0fq02zR3iYtkVi1E9zY2XilY91tmcSGtNXBmSahccjrmYZqXPVJJ8pE3A81tdtq4LUr1Vte6aJVrbuOgVCTSq5QK7TplJrNHqcJ1bMun1SmTmo8yBMjPINDjTqELQosGWR1vqKaopJpaaqgfFURuLXNeC1zSOCCDggj1BHC9qrNeLTqC12++WG509bZauJssE8EjJYZo3gOZJFKwuY9jgctc0lpHYrdGZzg+fn7y35ELP3Uj2yVNtNkREeA90Ui03jc8ZPkCLPbb8P1/sCKQZa5GZeReHvBFItNZ8u/H0BFKss8ttuwvgH6opRljltk+wuwgRSjLB7bb9p/oCKWZjl3eZnvgEUqzGM+ReZn8wRSrMb/SXmZ+twRSjMXctsn8QRSbUM9s/DHP3eQe6KRaidyfj8AyikG4hbdp9xF2h3VVmtwz2wksbF3/LzD7KizEQTPnnHw/QE/NZKYJY5F67dvEP5J2XMUJPd8v7AgW8oZY/Cfw9ZBOy1+xl/pP5fmH5ouM4Se4/h+QJ6riVBSfryBFjLgnvgvhv8gTCxHIZlkzSR+4PyRYLkTtxgEWC7E5maeXb65h9kUc7DLfH07vAE5/VRb0U+0sH3luQIot6N3pyRAn5KLejHjlkvmX6giino+eZZ7j/UEUU9HxzLy7wRRLzHhg98dxgii3WeeC37S7PPIIot1nGT7PmQIo11rPZv9fEEUc437vy/uCLBdbI88skCLtc9mtYehd4f1NU6/bketau2jPZnQ03A41UaZHtyYhLUOsUCiusoipmxKglbUl51Mh2OtTC21tm7wl6dfD90ds5qoahr73YmVe6FrmbIwVJEsTaV4DY56aBzQwSMl6mSveJXxOMLo3x+b0r2M+FxoHYHWo1Vc9Raajrt5bNUNljFY5s0DKKQBsdTSUzmiMSxzB0c0kjZnwuNO+KSIzdI+c/aF9HZOkWqrl627AKPYep782swm4rPBEol0pUl64qKSUZbjx5DrxTYqSJtBNvrabTwxzMcNeNvY8bYbkv1XY6MM0ZqF0k7A0YZBV5BqqcAcNa4uE8IHSOmR8TG9MBK6/8AxFfDkNmt3X6303QCPb/VT5KmMMbiOlrgeqspQBwxj3PFTA36GhkkkMbemnJXZn7PnpDJ1i0jZs+vzuuv7S9iFQ6mchxByazbRoWzbddSRq6x9bcaOcOUv7yuuYS44eX059AfBRvc3dLbOLTN5q+vWenmRwS9RHXPS4IpageriGt8iU8nrjD3nMzc+o3w8PEW3efaCHR+oK/r3A0tHFTT9ZHXUUeC2jqh6uIYw087uT5kTZJDmduebUZqyegRo7rFqNp7bsWbVb8v9qrUilyWCZhQqxcLMWJAo0mRGcRJXatuusz5saOk2zSUhUdBo4zeGTriPSfg52w3P1xoqyRy3C8XkTQxOb0xxzVLWMjge5pDjSUxbUTxRjpx5hhaW9RkWXuPFojwCbN7x7j7eadinut/1A2engezpjiqKtrI4qZ72EPNDSObVVMMTSzpErqdhZ1mZdYWnvtNekVQbyh1W/atSr6s52Y2VXtb+m7cobrFOW5/zB0GqUam06czUGWlZZOY7KaM0kS0nk1DoDorx8b32fVFLcdY3Knu+mHSjzqT5WmgLYifq+XlgijkbI0fg858rCQA4HJK8vtvPic+Iywazo7rr270l+0c+YfMUPydHTObCT9fys9NDDK2VrT+zNQ+dhIAe05Ll3W6j2Xp90ttA36ZGlQ6nbl+W9Gr9m3ASEOO0WtqirkUKst8JLehzqXMWbMxkjS7wG/GcxxOJHqvrnSui/Els5NQQVEVRY7xRNqKKpwCYJ+gup5x3cySJ5LJmcP6fOp346ntXtluRorb3xcbBT2ymqoarTl/tzKu3VeAXU1SWF9LUDGXRywSEx1EYw/pM9LJjqkavKncVAqtq1+uWxXYq4Nbtyr1KhVeE4Rk5EqdJmPQJ0ZeSI+JmUwpPuHzt3uzXHTt5u2n7vTmG60NTLTzMPdksL3RyNP+i9pH6L5StRWC66U1BfNL32kdBe7bWTUtRGe8c0EjopWH7texw/RQwi1DICLtj9mD0gjti7qloVcc7hod6OOVezVSHTJuDdkZn/naa11jyW20V+A1lKUoM1SI6SLdZj0O8B28psGoq7aa91WLTdHGaiLjxHVtb9cQycAVEYyABkyRtA/EV63/AAtvEKdL6xuWw+pK7FivbjUW4vd9MVexv7WFuXAAVcTcgBpJmhaBy8rvcNQ9Ynz/AHXvhhbDMYUk3fBWoBbDMYck2e5WoBbTUMSSfuqgLYZjFkmHfK1ALaahiPn9itQC2GYxXz9+VUBbTUMN9R3wtQC2moYz5+OStWFsMxivnHutWFtMxjPmJ9VXC2mYxnTd+VXC6Oval6VlSb1szV2nsEUW7qeq16+tPEeK3QWydpch37nARzaM4bSfvZxD5Dze8Z+jRSaisGuqWP8AY10Xy855/rYRmNx4x9cR6Rz/AMUvBD4tm0X9Da50PvNboMUl5pjQVZGf+6qRvVA93GB5tM7oHOcU3ZdUA6SLx9QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEX9Eb2U/R7/8AJs6DOidnTYZw7puyif8AFO9kLUSnf6k1BQxWkx3iJKeB+lW+cCCtJ5NJxcZHIFqZ8rRwx/vEZP5nlfHn49d3v8dPik3N1HTVPmWK31X9F0RHb5a3l0Jc3k5bLUefOD6iVdixKEwyoXTnC3EoZDKjtytJC3EoZTJ/XKphbiUMpk591pwtxKGSyoHr3VMLeShlsn+604W4lDLZOPUrSQt5KGWyc8crThbiUMxlR6FaSFvJQzGT/daSFvJQzI6gcLSQvlnpq9JugdEPoz6pa7VtcdyXatBdjWlS5DiEnXr5rH/V1p0ZtC1J64pFWeQ48lJ8RRWnVf5RiXu/R2S01lxeQXMb9I93nho/j/dlc7+GfZC7eIbezQu1dsa8U9fVh1XK0E+RRQ/tKqYkA46YmlrCeDK5jfVfzVbyu64b/u25b4u2pyazdF312qXJcFVluLdk1Cr1ma9PnynVuKWszdkvqMiyfCWCLYh1OqKiarnmqah5dPI4ucT6knJK+1PTmnrPpLT9k0vp+hZTWO3UsVPTxMADY4YWCONoAAHDWj8zyq2LKmkBEBF6DvYB9BP/AI+a+SelBqBR1SNKejrVoL9qNSUuoiXVrb1bNSt5ps0EkpETTuG43WJKScSaZztNSpLrLjyBz/sLob+nr87U1fDm1W54LM9n1Pdn5iIYkPP4jH3BcF5EfFr8VP8Ain2nh2N0jcQzXusad7aotwX0tlyY6gnP4X3B4dRxnpOYG1pBZIyJy7r/AG6nT+qPRS0EpujWld0Trf1415bkswq1b9SlUy4tP9NaXJabuK7afUabLjVCi1mvTeGlUt9JpUZHNeZWl2IRlzPvjr2TSthjs9rqnR32vBw5hIfFC0/XICCC1zj9DD/pkHLF5l/Cx8JVHv5uxW7ka8scVZtTpRzS+GojbJT19ylaTT0kkcjXRzQwMzVVLDkZ+VjkaY6gg9XXs9f4gnUO0avbelHTcM7/ALHmSoVIh66wIbUe+7RaeXGhMS77plPZah3rQICC6yRMYZbrSEdY6v8AmDhpQXGO3+/1wo5aa1a1/wCEUJIaKkDEsfYAytAxIwdy4ASdyfMPC70+L34RWkNRW+9a+8MI/onVMcb5n2OR5dQ1ZaHPcyhlkJfRVDz9McL3uoyeiNvyjAXHup9qv7OzSjpn6DXjqLb1tUSJ0ibDs2oXTpxqLRWW2andsOh05VWKw7ilwDS1c1EuGBFNimuSuuVTJTqHY622lyW3+ZN1dvLVrOwVtxp6ZjdRQQl8MzRzIGjq8p5H42vAwwuz0OILSAXB3mp4BfGNr7w07s6a0beL3VSbOXa5R0txt8xLo6R88nlfP07JOaaenkeH1DYugVMTXRzNfIyB8XgRYaxju/QdA8r67VJtIzgz93uD3RSDaPD9i/XcEUi01nB/AEUk01n8wRSzLXLbuwXf2AilWGeW2/xwCKXYYzyLbt9d24IpdmOWNixy94J6qXYjd5YLu9ECKZZjcslt9f7Ail2IucbYSWMl+4ff1RS7EXuIvP3Aq/yUozF5YLfbs2/cFT9FKNQjPmW39vcCKRahlzJOT7cflgE/kpBuGrbYiL6dvMEWWiGW3b7sn8QTt+Sykwy2+54fl2cuQIuZMMz/AMpfAMJ+q5ChH/p+X17QRDhK/wBPy7/3BFtVCP8A0l4bAqLgVD70euQcqqxVwi7Cx7seQIsVcM99sl47gnGFHuwy3ynHuyXbuCKOdhY3L14gii3omM5LB9/67APuijHonenJd5F6yCeyiH4plyLbljHrBgiiHo3PBe4+f0BPuod+NzMi8yBFEPMc9vd3eQIod+PjPcfo9wRRD7P7AiiXmdzPHmWOfiCfyUU61jy+nYCKNeb5mRcvoCKNcb/b9DBF+m6IasVrQ/U+2NRaL1riaTMJit01tSUprVuTFIZrdIc6wja4pUPJsqURkzJQ26X3kEOUNm9z7ts9uLpzXdqLnNppQ2oiBA+YpZMNqIDn6cvjyWE56JWxyDlgXMuwG8t82F3X0puXZHPeyjnDaqBpA+aopSG1VOc/TmSPJjc4ERzNilA6o2lehrWLT2z+lXoJMo9Mnw5tPu2iQ7osK4sKNun1xMVUu36oZcJvsJ4nlRpjfCTxR3nmjJKzPHu9uhonS3iR2XqbZbq2KWhudJHWW6q9I6gML6aY8dTRlxinbgPEb5YyGu7fS5vNt1ovxceHuss9quEM9tvNBFX2ms56YaoRmSknIwXNGXGGoZgSCKSeIhryceejRXUu7OirrxBuCdAnQn7brE21NQbaWk25E2hnLTDuKkONqW2lUyK5HKRFNSiQUuO0o8oIyPxF2j3A1J4dd4qO8VtJNFPb6qSjuVKeHPg8zy6mEgkAvYWiSLJ6RNHG45aCD86GxW6OrvChv7QX640E8NVa62Wgu9EfpfJTeYIqynLSWgyRlgmgJPSKiGF5ywEH0I9IHTSi9J/o9V23LfqEOYm66DTbs0/rqTUUNyrMNs1u2ZnWLJC2YVWQZRnlqQam40pZ8PEREPbvefQdq8QGyl3sdlrYpW3GjirLdUc9BmaGz0r8nBbHMMRPcRlsUrz09QAX0YeILbOyeKLw8X3TmnrjDM27W+GvtNUCfLM7WtqaKTJwWxVAxDI4tLmQzPPT1ABeV2vUGtWtWqpblxUyZRq7RJ0im1alVBhcebAnRHFNSI0hlZEpDjbiTLuMtyMyMjHzxXiz3XT11uFjvlBLS3ekmdFNDI0tfHIw4c1zT2II/I9xkL5VL/YL1pa93XTmo7ZNRX6hnfDPBK0skiljJa9j2nkEEfke4JBBXok9mEu5VdGCOmvNyUU5q/brRZ5yEupQ7bSm6S885E6wiSqN/VLtTTlH3ONKu3I9uPAC6/Hw/wADbwyQULbxWCi6s4NKRC5xZn9z5s1Q+njqDvXK+iz4Xz9Su8LtO2/Ryttzb/Xi39YcA6jIgc4x54LPnnVoy36eoO9crpZ6YsqkzOlBrc/RSSUIr7qjDnColJVU4iWYlccJROOkfWVpmQrn28k/hLyt8T1RbarxA7sS2oD5T+mJWnHOZWBrJz3Pedsh7+vYdh4p+MiqtFb4o98J7IAKL+np2nByDPGGx1Jzl34qlsp7+vZv4R81DgddaEBFK0Kt1S2q1Sbhokt2BV6HUYdVpk1hakOxp0CQ3JjPIUk0qI0OtkfPcthIWm6V9julvvNrqHQ3GlmZLE9pwWvjcHNII9iApawXy6aYvdo1HY6x9PeKGpjngkaSHMlieHscCMHhwH59l6wdAtXKbrfpLZ+otPU0l6s01DVbhtKSf8tuGD/ytagKSkz4CamtqU2R7mytB9o9/wDajcij3Q2905rKlc0SVMIEzAf6uoZ9M0Z9sPBLQf3S0+q+uTw9bvWzfXaHRm5NucwT1tMG1MbSP2NZF+zqYiB26ZQXMB5MbmH1X7Aahv19R3XNWFtMxhvm+61ALYahhvn+61ALYahiPnHPK1ALaahiPqD6LUAtpmMR8w91qAWw1DFfP7FVwtpqGM+fPcrVhbTUMV8/sVXC2moYzqj7rVhbTUMZ8+PVVwvlzpkaZ/8AFbo83/Qo7Bv1ejU/+sKAkjIl/wA1tlLk8228kfE5MppSY5FzM3hwtvxpca12w1LbY4+qtgj+Zh9/Mgy/A+7mdbAP85dTvG9tR/jf8NW41gpqfzL1Q039JUYHf5ihBmLR7mWDzoQPUyLzBjyEXydoCICICICICICICICICICICICICICL646B+g7vSW6Xeg+jyor0qkXHflLnXWTLPXdTZdsmu5rudcSf3EoO36TIQRq24lkXaL9NH5k8bT2zz+QXXvxV7rR7KeHrdbcUTtZcKK1SspcnGaypxTUgHrn5iWM4HOAfZf0mWkNsNNsstoZZZQhppppCW2mm20khttttBElCEJIiIiIiIi2G8GzYHBXxcyOfK98kry6RxJJJySTySSeSSeST3XLxDIbUK3hbiUMhlQD6rThbiMZLZvuqYW4lDKZUe5WnC3EoZLKjPGeVQhbyUMts+OxWnC3EoZTKhacLcShlsqPYqhC3koZjKhaSFvJQy46n2ctJC3koZrKn0K0kLxzfxFPS3VeurNjdEu1KobluaTxGb21GbivZYm6hXJC/6ipUnqJTjL/9MWs+TvCttK236m4k90EOJdxr2aqpp7RE/wDZRDqf93nsDz+63n83L6Nfg6+H0aZ2/wBUeIC/0OLzf5DRW4uH1Mt9M/8Abyt6mgt+Zqm9OWuIdHTMI4cvNUOMl7VICICL9B0o0vvTWvUqxtJNO6S5XL31Euek2nbVNQakIeqdYltxWXZb5IWmHTYaVm/KkLLq40ZtbqzJCFGUharZWXq5UNpt8XXW1ErY2D/Occcn0A7uPYAEngLaOvtc6a2z0VqncHWNwFLpiz0MtVUyHkiKFhcQxuR1yPwGRRj6pJHNY3LnAL+lH0bNDtI+gR0V7Q0wj1y3rYsbSi1XKnfeoNwyadbdNqtddQdRvS/rnqk55iLCTVKq466SpDykxIpMx0r6tlsi9HdOWS06E0vSWxs8cVDSxdUsryGBzu8kr3EgDqdk8n6W4bnAC+LDendDcLxYb76i1xLa6yu1Tf68R0Nvp2yVMkUAPl0dBTRMDnP8qINbiNgMspkmLeuR5Pwb7Rb2Q2jftD1x9cbK1GqNj61zLTo0W3L3Yqki9NL7ztqLEcl25EqNDKa83T6ZJZlkuPUaE+y2SX1yHI01a99i7h7SWfcEtvdFcXQXoxNDJA4yQyMAywFueAc8PiIHJcWvJXa3wc/EN3H8HzZtrtTaNhum2cdwmdUUTom0dzo6lzwyofHP0AySNLMSU9cx7ssbCyala3jz42J/D0dO+p6vU6zb8p1gWnpkiuMMV3WKm3zb9xUn+n2n1nOqNt2m3LiXxUak7Ea/5SNNp1OQt5xtLzrKOsWjgCi8P+upbtHRVsdPDbOsB9QJWPb0Z5LI8iUnH4Q5jASR1EDJHr1qj4v3hToNvK3Uulqy7XDXBpXOgs8lDUU8vzBaOiOpqyx9FHGHn9rJDUVBaxrzGyR3Q13sY131QsjoadFS9tQpLrdPtXRDS1EK2Yk5w5C50+jUmNbdhW2SlLZVKm12uKgwEZUjjdfI1KSWVF2+v10odG6Urri4htLQ0uGA85LWhkTPTJc7pYO3J9F85W0ug9UeJbf7S2jII3TX/VN+66l7B0iOOaV1TX1PY9LIIPPndwcNYQA44B/miNozgviPM0L7i/5KSbRywWxctuYIFINN5PwLmfrIIpNpGTLb9iLmCKVZa5bbF+4IphhnltufLuIu8EUwwxnGC27T7+/uBFNMMbFgtuwscwRTLEfkZlvtgu7AdkU0xH5ZLfu7MB+id1NsReRmXPsBFMsRuWS9wIpliGZ4yWC7uz38wVFLsROWCz7u764BVUq1D2Iz5+XrAIpNqGe2E924fmmP4qRagZwZkZ+iPlgEUg3BxjJF8AVMrKRCLu+XfnvBV9/Zc6YX+w/XxBO65ShH/o8fMEWhwz/0ECdlsOFzyjkCLgXCLf7vPw93YCLFXBLu+XrkCLBdgY7D+f1FEUa7DMiPKfkK+yKMdh88Fg+7kYIot6JzynB95fp5mCfmoh+H3F+/ogKKGei88Fvvku/nsCKGkRSPO2DLfHu7O8OUULIjZ7MH2eIJ7+6hH4/PbBl68QRQz7HPb3euwMooV9jGe0j+oIod9nc9t+w/dyMEUO81zPG3aWOQIv2LQro7Xz0gblfotroYp9IpRR3rjumoodOl0WNIWpLTfC0ROTqnLS2s48VBpU5wKNSm20qWnm/Y7YPW2/GoZ7RpiNkFqpQ11XWTB3kU7HEho45kmfh3lQtIL+lxc5jGue3sb4b/AAx7ieJjVVRYtHRx01lo+h1dcJw75ekY8kNGG/VLUSBrvJp2kOf0Oc50cbXyN7SqD7NPQuDTWmLgrd93FVTQX2qot1Sn0aKbxcRGqDTY1MkKismRl9x1+SojL8eNh6e2H4d+y9BbooL7er3cLn0/XKJoqdhdzzHCyF5Y3t9L5ZjkfiwcL2Q018Kjw+Wy0w02pNQaiul56f2k7Z4aWMu55igZA8xt7fTJNO7I/Hg4Xxv0nugDVtKaHOv3S+qVW87QpiVyK9RalHYdui34CEqU7Viep7MaLW6VGSWZCm47DsZH+IpC2iccb6p+IzwPXTbGzVmttuLlVXfSlOC6pgla01lLGMkzdUTWMqIWf8aWxRvhb9Za+MSPj6S+LD4cd62c0/X7ibS3esvuiaUF9XSzsY6vo4RkuqOuFrI6qnZ3mcyGKSBv7RzHxNlki/W/Zq6/ESahoBc048pVNuHTl2Q64rKVccy47YY4lKQ2lCiXUY6EkRGapZmeeAj5U+HzvgCyt2Q1DWfUDJVWsuJPHL6qkbngAfVVRNAGSakk56Qua/hZ+I0Flx8OWqq89QMtZZXPc48HMlbQtySGgHqrYWgAEurC456Aaj7Tjo8pgVCna/2xASiFVlxLf1Dais8JMVdKSZoNyvGlSi4apHQUGQrhQlLzMfJqcfUZQ/xBtkBQV1BvZp2jAo6osprm1jcBs4+mmq3YPaVoFPIcNAfHAcufMSoH4pXhzFsuds8RGlbeG0FY6OkvDWNwG1AHTSVrsE8TsApZXYa1skdOcukqHFXX2YnSFVV6RUuj7dE411G32ZNwaevyXUmqVQVu9ZXbcbW66TjjtIlP/a4zaUrUcZ5/8LcdJDefw+97zdLXX7K6hrM11E19TbXOIy+nLs1FKCTkuhe7zomgOJifN+FkDQuQPhbeIs3izXPw8apr+q5W5j6u0Oe4ZfSl2aqjaXOBLqeR3zELGhx8mScfTHTtC+oOlS50VLMn2tenSH0ri1pFWlrp8O+m7OKtMNVGmx0yIVDuKZS30VWScmG0tUViSzIjLbZdLYkqIdifEPJ4dtLVmntU72beR1bamQxsrxRee0SxNDmU9S+Jwmf1MDjFHKySJzWSDgNcF2t8VcnhS0XX6W1p4iNqoq1lXMYo7oLd8yxs0LA+Olq5IHCd/XG1xgimjlhcyOUDAa4L5Y1l9pvpnbdlO2n0caBNkVo6b/KqNWZ1Aj25aVoRDjEyxIpVDXwyZ8ympPEeMuLHhoWlKlG6hJtL687o+PrQVi0pJpvY2zSvuvkeTBPJTtpqOiZ09LXQwHDpHxdo4jFHC1wDnGRgMb+qu8/xO9stN6Jl0l4b9PzyXr5byKaolpWUdBb4+jpa+CmOHyyQjiKF0MVO1wa5xlY0xP6OpcuTPlSZ02Q9LmTJD0uXKkuLekSZMhxTz8h95w1OOvPOrNSlKMzUozM9x5J1NRUVlRPV1cz5KqV7nve4lznvcS5znOOSXOJJJPJJyV4aVdXVV9VU11bUPmrZpHSSSPcXPe95Lnvc45LnOcSXOJJJJJ5WOLKx0BEBF2v+y61sO370uLRWszOGlXowu4LWS85/hx7npUf/AKxhtdY8lDf83o7fHhKTUpyIki3UO+3gc3OdZ9RXjbW41OKC5NNRS5PDamJv7RgyQB5sIzgDJdE0eq9dfhS74nT2udR7HXutxab5GaugDjwyup2fto25cAPmKZvVgNJc+naBy5d6RqHp4+o9Mr30AWw1DCfUccFagFsNQxHz/wCctWFtNQxH1A9CqgLYahiPn9ytWFtNQxXzgeq1YWw1DFfUH3VcLaahiPn+/K1ALaahjPqPZasLaahivnz3KrhbeIY7px7rVhcayS4hTbiUrQtKkLQsiUhaFEZKSpJkZKSpJ4Mj2MY0kwc0tcAWng59fzVHxskY+ORgdG4EEEZBB4IIPBB9QvKr0i9OV6Ua2ai2OTLjMKl3HLkUYlo4OOg1bhq1EWki+6aSps1tOS2ykx49bnaZOj9e6nsAYW08VU50WRjMMn7SL/zHAfovj/8AE7thJs7v1udt+IXMoKO6SPpsjGaSoxUUpHpjyJWDI4yCvxQbDXAyAiAiAiAiAiAiAiAiAiAiAiAiAi9Kn8OFoaivava39ISpwW3YuntoUzTu2JLpuZauK+ph1OsyYqUmTan4Vv26TCzVnhbqOCL72RlUp6HF/r2Xit8ZjdF1q282w2hoaotnvFxkuFS0Y5p6FnlQtce/S+oqC8AYy6n+2F6+uISbag+6+d/C3EoZDKkfoqYW4lDIbOOOVTC3cQyWz49VpwtxKGQ2o7cqmFuJQym1A91pwtxKGSyc+6phbiUMplTjuVpwt5KGYypHfK04W8lDLZUn3WnC3EoZbKgccqmF+Z61as2zoVpJqNrFeL6Wba03tCuXbVCU6TCpTdIhOyI9NYdNt0ky6rLS3FY+6rLzySwYuz3BlJTTVMjv2bGlx/T/AK/Rb2202/ve6e4OjdudORF16vVxgpIuOoNMrw10jhkZZEzqlfyPoY45X8yvVzU659adUL/1avSWubdOot2Vu7q2+pfGSZtanvTVRmVcKMRoTbiWWiwRJabSREWBwLV1MtZUz1UxzLI4uP6r7cdvdD2PbTQ2ktv9NU4isVnt8FJC0DH0QsDOo8n6nkF7zk5c4nPK/OxjreKAiAi9Yn8N30SLRnnqL0yLjqduVq7aHNmaW6cW5Eq0OfXLKZnQW3rtu+vUqM449RJlywpBU2l9f1bzkNuevg6p9pxXavw56TpH/wBI6wqJY31bHGCFgcC6PI/aSOaPwl4PQzOCWiQ4w4E+A3xnvEHqGlGjfDhZqKsptP1UTLncah8T44K0seRSUkErgBOyme01NV5fUxszqRvV5kUjG5/8RZ06iaj0DoK6dVk+skHR7+1+k0+W8g0MINFSsDTicTLqWnkvuE3X57DqVcJt0taDybiS1+IbXGGwaHt03J6Zaog+neKE49+JXA+0RHqsT4OXhYL5bt4p9Y236GedQWFsjAcuOY6+4s6hkFo6qCB7SM9Vc1wx0FdH3Qs9p/0s+g/Mj03TG9U3NpkqSb9S0d1BKbcFgvdc5xyn6LHTMi1Sz6k/k1G/S5MVLznCqQ3ISkkDhLRu5uq9EPbHbKzzbbnJp5cvi+5aMh0Z+7C3J/EHdl6heJXwNeH/AMUMEtZrjTRodb9HTHd6Dop68YGGtmcWPirI28AMqo5SxuRC+EuLl6GrY/iYNGJduMP3b0ZNUKVdn2ZRyKZbt32pXrdVKS0XATFdqTNu1JMd58jI+KnGppGD/wAQxz/TeJayupwavTNU2rxy1kkbmZx6Od0HH/I4+68ibz8ELcyK8SRae3wsU+n+sdMtRSVUFQGZ56oIjURlwb2xUAOP9gLpg6fHtOdefaNXBa9iM2z/AEHpbAuCOdl6N2pOn3DPuK657y6bSKpdNV+yQHbtuYkTjjQWmYcaNGJ5RNMG64465wpr/c+/bj1FLQNpfl7W2QeXTxkvL5Dw10jsDzH89LQGtAycNyST6e+ETwLbTeC6z33Vkt8/pbXs1G75281bGU8dPSMAklipYuuQUlN9HmzvfNJLJ0NMkvlsZGzvOgewd6NrPQrtKFqzcdR0s1/tSy6rfWo2ttPqDc+jUOpyon9Q1+i3Pb8mW3Qq5ZliwGFRUusOwpSijrkJlElxTZ85M2F00NE0jLvUupNQRQOlmqQctaSOt7XsJ6XRxAdOQWu4LurBwvKyp+LVva/xQahqdvLJDqDaC4XSKgt1kkjMc08TX/L089LUNYZ4K2vkcJSx7J4gZGQmAuYHjyIVCJBiVOoxaXPVVqbGnzI9NqioblPVUoDMhxuJUVQHluvQVTY6UuGytSlN8XCZmZZHTyVsbZZGRSdcYcQHYx1AHg4PbI5we3ZfSHb5quooKKor6P5eufCx0kXWJPKeWgvj8wANf0OJb1gAOx1AAFcrTeMbfue2wtrL7qVYaPYu0+YIphhrltt2F68gQKZjs7kWOfPbs7gRTkdnltyx7zBFNx2ORmWe4u7sBFOx4/LbfYE/TlTseNjG2/rmCKcjxuWCyZ8+4vRB7opyNExgzLf1vkEU2xEyRZLbbb1zD+aKZYiGeMJPn78fPYEUyxBLbJfL+4eyKVah8vu59ZD0T391JNQjPs8iItwTPZSDVP8A9vr5B7plZiYKdsl7u76giyEwiL/KfZyL9iDhOVyFCL/QfmCLX7GX+j+4IuNUMv8ASfw/TmHsqrgXBSfZ8vh2AqZ/isNyBty9H+gIsB2CZZwW3dz9bAijXYfPKeeQRRL8HY8F38veH80UO/EMuZZ+u/5Aihn4nPBZz2ePaCKFkRM9mD7v3BP0UFIjcyMsdxgihJEbOSMt/XzBFBSI/eW/Yff4Ai+htCOixcus7ia5UJDts2IxIU09W1MdZOrDjKyTIiUCK7wtumgyNDkpeWGl5IidWlTZduPDj4TNWb5ubqC41DrVt7HKWuqizMtS5pw+OjY7Adjlrp3/ALKN2WgSva6Md6PCZ4HNceJKRuqLrVvsu1UUxY6sczqmq3MdiSKhjdhrunlr6l/7GJ+Whsz2PiHZZbmgfR60np8c3bZtFt1akI/n19uU2qVGXJbIldY1Mr/FGiPKwRmiG2w3tngIerOmPD34cdn7fTeZpeztmOB83dXQzzSPHOWyVf0Ru7Etp2RN9egL200b4WPCZsLaqPzdG2Fk7iG/PXp1PU1EsjeepstdmOJ54JbSshZ6hgXFqfoL0ebytOfU7ptq0LepjMB2d/W9BbpVsyqawtHEiolWoDbMWWyg1cSESikR1KV+AzVvXdDYXw76x0lX3XVGmrPbrXHTul/pOlEFE+FhGRN8zEGskaM5a2YSxEn8BLudW8vhk8KevtDXO9ay0jYbTZo6V039MUTaa3yQMIyJ/m4GsjlYCcsbUCeFxdxG4u56ALsp1Ep9wVmDbdXduCgxKjKj0itv09yku1WntPLRGnrpzzjrsJUhsiVwKVxFnciPYvAjUtHZLfqC8UOm7w+4WCKpkbT1LojA6eJriGSmFznOj6289LjkeuOw+YPV9Bp21aov9t0jfn3TTEFXIylrHwOpnVMDXERzOp3Oe6Ivbh3Q52RnkA8DvW6D9q0u2ujnZcmAhg5l0PVq5KzKZwZyp79XmU9knFYyS4VLp0eOpPIlNH3mPdfwTaYtmnfDvo2roWM+buj6irqHt/fldPJE3J944YYoiPQsPuV9Kfw7NG2fSnhU0BW22OM195fVV1VI3B8yZ9TLAzJ94qeCGEj0dG73K6nbT016THSg1KnXnEcr8eqNVqQc6/axMn0KiWpIiSUoVT6TPZSh1l2kKNKG4VNbW8yREZoSkjUXl5pPb3xG+JXcSt1fSvrmXJtY7zLnPJJTU1E5j8GKGVuC0wEhraaka6SMAHoa0Fw8Z9D7V+LLxebrXHXlE+5Mu7a95mvFTLNSUlvfHIAYaeZoDmupiWtZSULHyxAA+W1oLx29XrrjY3Rv09oNI1ov9u9L0j0JiJIi0ylRyue81tkcZc87cKe+xBZktkZOSJchmM8tC8L41E2PWPV+8+jPD5oOyWnd7XTbxq+OiaxzIYW/OV5H0GU0vmObG14yHyzyxxSOa8h3WfLXuDrvxBbfeFrbPTtk333JZfteRW5kb44Kdnz9zI+gzGi857IWvbkPmqZooJXMkIf5jvKXn0rF2P0DU0tUdO6DPsCAV2v3ZYkB9x2YxS2otT+1sQY05UaI1UYUNZ9StBEaSaPqlmrdSvDW46plsm4g3H0HZJrJQ/0o6tt0TiZGwtZL1tjZIWMEscZ+gtAI6D5bi7kn5vLtrSfTu6w3Z2z07Uactv8ATL7haYXOdK2nbHP5jImSlkbZoojiNzQCAw+U8v5c70VWNdFj9K3QRqbNityLf1AtyXQrqo6FoORRqx1Rw61T21uJdUxMpVRT10N9SeM0kzISRcSR726L1Jo/xLbKw1tVTNfZb3QPp6yAEdUE/T0VEQJBLXwy/XBIR1Y8mcAdTV9N+3urtBeL3w8wXGtpGyad1HbJKWvpgQX01T0+XVQgkOLJKeYeZTSub1dIgqGgdTV5uqwi4+jlrfKcti4aXUbg0vvJaqPcFHlQ6jSaoVOkZjuuKhSpMdUep09ZNzIinDW11jkd0krStJeDteL7sTu9U/4P3ynnvWnrqfIqYHslhmETstJ8tz2lssZ6Z4S7qZ1SQSAPa4D5n7mNS+Gvfar/AMF9SUtTqHSt6Jp6ynkjmp5xC76HHynvYWTxHoqKcu6mdctNMA9r2jD1y6Rurmv1aOoai3M/KprEhcij2rTScp9p0PjJSSKnUdDq0LkIbWaDkyFPzFo2W6osEMvdjfTcnei5/P63v75KFjy6GkizHR0+cgeVCCQXAHp82UyTOHDpHALN3w8SW7viDvH9JbjanfLbo5C+noYcw0FLnIHk04JBcGnp86V0tQ5vD5XDhfgw4hXBCAiAiAiAitFk3dV7Bu+2r1oLyo9YtetU+t09wlcJG/T5Lb5NLPCv8J9KDbWWDyhRkJvTV/uGldQWbUlqkLLjQ1Mc0Z/zmODsH7Ox0n3BK3XoXWN5291lpfXOnpzHerTXQ1UJBx9cLw/pPf6XgFjhg5a4hetSwr1pOollWtfNCcJylXVQ6fWoZEvrDZTNjodciuL4UZfhPmplzYvvoPYe7mm9U0GrdOWXU1teDQ11NHM3nOOtoJaTxyw5a7gcgr7ENu9bWfcrQuktfWCQOtF3oIaqPnPSJWBzo3HAy6J3VG/gfU08K1moST6j2W9MLaahiPn9ytWFsNQxJKjuFqAW01DEkqPcrUAthqGK+o9iqgLaahivqO/PC1ALbxDFdUN91XC28QxnVBWrC28Qx3T+hKrhbeIYzpxz9S1YW3iGO6owq4XSN7UvT5NOvbT/AFMiR0oZuaiS7YqzqDVldTt58pcB14jM0k5IplTNtJljKYvgOh/ixsAhv2ndUwxgNqoHQSEf24T1MJ+5Y/H5M+y8Cvi9bZttWvttt16KmDYLtb5KCocM8z0T/Mic70y+CfoBGMtg+y6px1HXjqgIgIgIgIgIgIgIgIgIgIgIgIgIved7D7RpOkvQA06q8mMyzWtY67c2rFTWlgm5ColWmIoFtIeeNJOPNqti3Ij7e5pSUg+HmedJm6DgFfKR8UHcg7geLbWNuhmc62acpaa1RDqy0PiYZ6kgZwD8zUSsd6nyxnsMdvXELjaheeWFu4hkNqB7rThbuIZDZ/uqYWpKGQ2oPuqYW8lDJZUj1WnC3EoZLage6phbiUMplR7FacLcShksqe2TwqYW4lDKZUDjnlaSFvJQy2VJ91pwtxKGWyq7cqnSvOr/ABE/SXVYnR90/wCjdQambFd1suI7ju2PGkPtSEaf2JIiyo8aUlvhQuJXbufimlKjMl/y5wjLAgNUXEtoo6NjvqkOT/oj/rOP4L2L+Dtso3VW7mrd57tRddr0xR/L0jnNaWmvrmua5zc8h8FI2UEgcfMMOcrxmDj9fSQgIgIgIv1jRjXTWDo73xTtSNEtRLn02vSmGRM1q2qgqKcqNxpcXTqxT3UvUuv0aQtBddBnMSIb5FhxtRbCVs18u+nq6O42W4S01a3s5hxkezhy1zT6tcC0+oK2BuTtZt3vDpes0Xudo+hvWmp/xQ1MYd0uwQJIZB0ywTNBPRNA+OVndj2lV3UnUW8tXb/vHVDUKtSLjvi/riqt1XTW5KWm3ajWazLcmTX0sMIajRWCcd4WmWkIZYaSlttKUJSRY9yuNZdq+sudwmMlbPI573H1c45JwOAPYDgDgcKY0Xo7Tm3uktOaG0hbGUel7TRxUtLC0kiOGFgYxvU4lznYGXPcS97iXvJcSTV4zedy3NXuMt+wyPwGFk8BbnVhjtbFy9239g9eyd/TC9GfsBOg+erOr9R6Wt/UdL+nmh9R/lenLMxOWbg1jdix5SKo0yth1uTD07o01Ms1GppSKrMguNKWcd5KexWwGh/6XvEmrrhDm3ULumEHs+owD1YxyIWnq9PrdGRnpdjxo+Lz4pxt5tvReHfSNyLNZaqg824ln4qezBzmGIuDgWvuMzDDgBwdSw1bJA0TRF33z7fnpuHp/p1Seh3p9Wequ/VWFGuHVyRAfYU/RdNI8tSqTaz7jalSIUy+KxC619JG2s6XDU2slMTsK334gtc/0dbYdG26bFZVtD6ggjLYQfpjPqDK4ZPY9DSDlsi6nfB+8K/+GOtbh4ldY2zq03p+V1NaGyNcGzXNzMTVTQcNeyhhf0MJ6miqmD2FstLx5ZtBNFb26Q+rtgaL6dwP5hd+oVwxKFTErJf2WAyslyqrXKktBGtqk29SI786Y4RGbcaO4oiMyIj6o2CyV2o7zbrJbY+qsqZAxvsB3c4+zWNBc4+jQV9Ae7W6Gldl9t9Y7o61rPJ01ZaJ9RLjHVI4YbFBGDwZqiZ0cELeA6WRgyAcr0g+0J9jD0bNEOjhVNctMNSalplVtIrEozN1QLwcnXDbOq9dhMU6iMS4hdbKrNoXvfFceI+rjHKpKpUhDaIsNonHy7J7j7JaYsWmZr9a7o6lmo6dokEmXsqHgBoI5Lo5ZXHsOqPqIAYxuXLxN8FvxQt8d198rdtNr3QkF+t2pLvO6kkohHT1Vop3ulncx/DIKyhoIG/ikENYIo3vfPVSFkR8ybDXLbc8ZHVVe+/ZTTDWcbfL3AinI7OMF2mW59wIp2MxnB42I9ix67QRT0Zjcslue3l+oIrBGjYxtv6/MOEU9Hjcixv2n+nfgEVgjRSLH3cnzBFPR4uMZLfJdh+4EU7GiGoyyXbyx2BhFPMRCTj7ufL6n3ZBFMswjPGS7tsbdnxBOFMMwiLG3u5fqHKKTaicsJ8Nw/RDwpFuAZ4MyP35+gFP1Wcinl/p8/XuD0T81kpp+34fkCfZcxU//b8vW4IUOn/7QT+a41U//bsCfksVdP8A9vyD9UWE7AMs7GXr3gijnYZ/6c+7u5YBFGvQiVnBe7Hb+YIod+FjJkWP0D7IoZ+IR5I04Pw+G3cCH3UFJhmWcF8vPz5AigpEUtyxgyBFAyIucljfs9eYeiKvyY3PJe/HIPdFI2TZzl8Xra9otqU0dfrcCnPSEJ4lxojz6ft0tKTwSlRIaXHMdvCN9bY6Lm3F3C0boeGQs/pO4QwOeByyJzx50gHr5cQe/Hr0rkrZzb2o3X3U0BtzTyujN4utPTPeOTHC94M8oHqYoBJIB69OF3B6r3vSdBdJZNVotMiITRYkC3rSouFohKnuo+zU5h0kLQ4qNEYaW+8RLS442yoiUS1EY96t5twLL4b9lam8WK0wtjoIYaK3UuCIvOcOiFrgC0mONjXTSYcHvZG8B3W7K+mvxA7o6e8JPh6rL7pmx07YrZT09vtNHgiEzvHl07Hhpa4xRMY+ebDg+RkTwHdbg5dG173bdN+1qVcV21qdXKtKWo1vzXTWhhtS1LTFhRy4Y8CE0aj4GWUIbQXJJDwI1prrVu4l+q9S6zvtRX3iYnL5XEhgJJEcTPwRRNzhscbWsaOzQvmC3C3J1zurqat1fuBqWqul+ncSZJnlwY0kkRwxj9nDC3OGQxNZGwfhaFl2jSdV9QoZaX2UV0V+jyJ7NVdtiDJlHQo0wjUyip1BDjqKZTmkqcPieeU23xYMz4iIy3Poa3bubjUbdq9Di63GzSVLZ3UMT3mlZJyBPMC4QQtBJzJIWM6sEnqAI3ltvad9d2aBuyu3Avd1sEtWypfboZJDRRy8tbUzhzhTwNBcSZZXRx9WCSXNaR9hI9njU4umFx1SqXGqfqgmlfb7foFHJBUBiVFNMp+jy5chr7TVahUozao7TqPszDD6yM+tQXEO8UHw7rtR7X6jut21IZ90RSebSUlPj5Vj2YkdTvkc3rnmmYHRMe3yYo5XAnzWDqXo9TfCnvlv2a1Zer3q41O8wovPoaGlx8kySPEr6WWV7PMqZ52NdBHIzyIYpngnzmDrX7H7PvU6NWtO6lpTUHeouLT+fOlwoL+USH7arE9yWt1LbmHVLplelPtPljDKXmCP8REXNPw/Nzqa97d3Haq4y9GotPzyvjjdw91HUSl5IBwcw1T5WSD9wSQDjqAHYT4XG8dJqLaq7bKXWcx6r0vUzSRQv4e+gqpjIXBpw4mnrJJo5R/xYlpwcdYAsXTO1D1805taAnRy2ybtyosSW7kvKiwHavX7dlPPryyzS24zkajxZrbmSqSkPn1yzSRx3SaW7u7xg6/302801Qt2i06G6cqI3iruFPEZ6qkkc8/SIQwsp2SB2fmy2Q+Y4tBgkEb5N9ePTdDxJ7V6RtjNitKBmk6qOQV10pIXVNZRSPefobTtjcyljla7Iri2U+a4taaaYRSS/G3Qb0VubUDVGuao6rWk/ctss0aabdY1CpjtX/nt2zZtNXDmwjrzbx1OVBhsPLXK/wAQmjNKc8SyNPUjwVbQak15uVedytztLPuGm2UkmJ7pC6f5mukkhMb4/mQ7znxxtkc6b6wzLW56nAt6M/Dy2G1buZu7qDd3eLRkl10lHQykVN5gdU/N3KWWAxyRfOB3nyRRtlc+f9oIyWNz1PBbWvaY3bRqtqtZ1lUg4puWFaPVVREZrqygTa/IbnR6Uok8LZFHo8aK8lKE8KEySLOckmM+Ifqq0XLc7SukLUYzJZLV0zBgx5UlS4SNh44+iBsMgDRholAznhsP8VbWlivG8midB2UxGTTllDagRt6RDLWPErKc4w0BlMynla1oAa2YDJOWt+DaPqlqLa9n1+wrcvGuUS0LomsVCvUWmTFxGKjJZjLhn1rrJIlIYlRVkiS0hxLUpLTZPJX1TfD0zsu5OvNOaVveibDqqso9L3KZstTTxSFjZXtYY/qc3Dw17CGysa4MmDIxK1/ls6fP3T+7u5mk9Fai270zravoNGXaobNV0sEpjbM9rDH9Tm4kDZIyGTRteI52siEzX+VH0/lUhHP8uZdmS2MuRjZA+645+yr0loyz2YyZbFvjPcfaYcDOOUUcCICICICICICLve9l9qwdxaZ3LpVUpZuVGwKoVUorTrji3VWzcTrzzjTJLylLFOrbbxmRHt9qSWB6V+DjXjrlo67aKrJ81Vrm8yEEkn5eckkDPpHMHfl5gX0G/Cd3fOp9rdU7RXOs6rnpyr8+la5xLjQ1rnOc1ueA2GqEhIB489owu0I1DuBJU49V6zhq2moYj6nvytQC2GoYj6nnvwtXStpqGI+oHPKrhbTUMZ9RnsVqwtpqGM6oPutWFtNQxn1AGeVXC2moYz6nvgqoC2moYz6g+pWrC2moY7qge6rhbTUMd9RzwVqwviX2gdjFenRsuia00hyfY1QpN5RFG2SnSZhPqptWS2siNTaCpNUecV2H1RZ5EOC/EFZxfdtrpK1oNRQyMqG8c4aeiTB/8m9xPpwugvxK9uhrvwqauuEMLXXHT1VT3SM9OXBkTzBUAHuB8tUSvd6HyxnsF5zh5zL5dUBEBEBEBEBEBEBEBEBEBEBEBFO2vb1Ru65rdtOjtG9VrnrtIt6lskRqN2o1qoR6bCaJJbqNyTJSWC3PIEgDJ7KKvt4o9PWS8X+4v6bfQ0stRKfaOGN0jz+jWkr+nVpfY9N0v010+01o5I/lWn9lWtZVONtsmkLhWvRINFjuE2WyTdahEo/ExB/NdTnO9Scr4ftc6ordc611drW45+fu9zqqyTJyQ+pnfM4Z9cF5H5BXviFxtQPdbVwt3EMhs/sVTC14hebP91TC3EoZDagj1WnC3EoZDalUwtxKGQ2oHuqYW4lDJZUkeq04W4lDKZVY7lULVuJQymVIPqtOFuJQyWVPsVpIW8lDLZVfdU6V/Pr9sP0h1dIfp4auToFQOdaWlspnRyz+qmnMgphWM5JjV+VCNP8AgJaqd5SalII0ZJSFpyZ4yNpXWpNTWPOfpb9I/Tv/AH5X10fDo2fbs94VNvqWqo/K1BfWOvFXlnQ/rrg10DX/ALxMVG2nj+rsQ7gZXV6I1d5kBEBEBEBFuQWVEXj6+AoUxnPspuK3kyPJ7Z5ctuw+wVxwn81PMo2I+/xBF/SD6AFh2vo10DujRQ7Pp8qXTGdCbNvuSxTWY79Rr1w3xbkfUG55URpCmGpEutXFX5Ko6FL2JxCDcMi4h6Q7f0FLZtBaZgo4yYhQRykAAuc+VgmeR2yXPe7Az6gZ9V8Uvi/1bfty/FrvjdNSVscdc7VlZQNdIXNigp6Gpdb6VryeotZDT08QkcG92veGDPSvAR0g9aL06Ret+petOoLjv9U6hXXUq5NhLckOtUKGp37NR7Yg/alG+3TLXo8dinxUL+8hiMgj3yPPjUd7rdR3y53y4k/N1ErnEZOGjOGsGeeljQGNHsAvsM2Y2v0vsttZoXa/R0bf6As1vigY8BoM7wOqaqf0/SZaqZz6iVw4dJI4jjC9TXsEug09pzYVT6YOpFDTHu7VGlqoejsOoR0nKomma3UO1W8Gm3VKXEmX9PjobirNtt1NJiE42tUeoqI+1Xh+0G620Eus7nBisq2dNMCOWwfvSfYykYacA+W3IJbIvAf4vviwi1rq6g8NOh7qX6bsE4nvL43HonuYBEVGSMB7LfG4vlHU5hq5eh7WzUQI+PvbudNJep2qtO6J1h1Y3LG0cmoq2pT8GQo41w6qSIppZor5tLUzIiafUmUbSiyXDVJkptxPHFbMtj+IDXP9KXaPSFvm/wC19E7qnIPD5yPwn3ELTj/yjngjLAu0vwg/Cu3Qe3lb4jNX23GrdTxGG2NkaOqmtLX5M7cjqa+4zN6wfWlhp3sd01Dwfof2JPs97OuLS+8uklr1YtFu6mao02p2BpraN30eHVaS5ZLcrqLtu9+m1KK+g5Ndq0IoNOeT1brEeHJcSakymlp3HsRtxRVVprdUaht8c0VW10MEcjQ5vlZxJIWuB5e4dDDwQGuI4eCOGfiv+NPU9h3A0tsTs7q+qtlw0/PFcbpWUUz4phXFnVR0TZYntIbTwvNRUxnqbJJNAx2HU8jTQ+k37EbUC79c72ldEm0qNp5o9TqfFehI1V1Bf+z1+63W1zqxA05gwqFcFz0+14JymoUdVdcR1sqM+43IVEVHWqJ1ZsLc67UFe/RlFHTWRrQR8xMcPk7uEIDHvEYyGt808ua4hxYWlcgeHv4tWiNK7Q6Sg8S+pqq97oTzPDzZ7a3qpqMERwSXOR9RTUklXJ0PnkFva7oilhY+BtQ2Zo6StXtDNUej7f8AVdMdXrRqNmXlRybefp842H2JsF9bqYdXo9ShuyKdWKNONlfUyozrrKzQpPFxIUlPX++2C76auU9pvdE6CuZyWnBBB7Oa4Etc04OHNJB59QQvX/ajdzbve/RVs3C2v1NBddLVWWtlj6mujkaAXwzxPDZYJ4+pvXDKxj2hzTjpc0mlxmeR4/LcQy5IVhiscjxuffy/sHZFYY0fltufy94J91Y4sbGNt+7l7wRWCNG5bbnv+uQRWKJEPbJdx8uX17AKKwRovLBe/AfdMKejQtiyX0FU+6nGIhnjCceOPp3CiKaYg8vugnKmGIBYLbu5kByUz/FSjcDHNJevEEWaiEki2L5F+ewIspMP/Z3Ai5Chmf8AkL5gUWpwzx+AgHdPdcSoXejH6/AECx1wk9xlv3H8gT2WE5BLB4LO/h65Ansot+AW/wB3AfZFESIO57evAPTCfl3UK/EMs5Tn3HtgwRQkiGRkeCD1wU9lAyYuM5Lv7NwT3VflROewIq7JjZIyMt+/15AmFX5UbOSMtyBF+g6CyI9M1p08kSj4G13A3BQr7pZkVONIpsVP3jSWFypaE9++xGew7AeFa40tq8Q21FVWP6YnXRsIPH46iOSniHJH4pZWD354BOAe0fgpu1FZfFVsjWV8vRA+8tgB4/rKqKWmhbyR+KaZjffngE4B+/8ApfWnOurRWrrp6FvP2xU6ddLrDaVqcdhQUSoVQWlKCMuGHCqTkhZnsTbKj54HrL45NG3HV+wF8ltcbpKi0VkFwcxuSXRQiSKYgD0iinfM7PAZE49wF7hfEg0BdteeF7Uc9midLVWKvpro6NoJc+CASwVBAHpDBUyVD88COF5/EAulqSznJ433z+48Gl8zS7X9OekT0f8ATbRW3qxDjUy3Kg/FONUrHtqMUu45dxQEIZnOvk+8cl5iSZk6zNqEkkqZWlHWm4k0F7SbXeJnw5bY7EaYvVBS0lsuckJZNaqJnmVslbCA2Vz+t3W5khxJFU1c3SY3tZ5pkaWD6E9mPGF4TdnfDRo7UNsoqGz3iWnMdRZbdH5twluFOAyd0nW7zHslOJYayun6XQvbH5zpGOjH6l0dekTTteoV0KKj/wBO1i3Kk0S6R9qVOJVDqCHP5XOKabEUn5Cnor7b6UtpJtSU9iyHMPhq8Sls8QlBqpws39G3q21Tc0/mGXNLMD5EvmdLOp5cyVkoDGhhDfR4XPfhD8Xdn8U1s1q8WD+iNQ2isbml8wzZopwflpvOLIw+QvjmZM1sbQwtYRkPBXwX0trSuDo8a2UPW7TSd/JUXjKnVE0NdWpli5GSa/qKDKhLWf2ykXAxKS+4lRcHWOupLh4WzHRXxaaS1B4dt7bHvZtrXfJR3mWSbDenpZWNx83E+Mn9pBVNeJXtI6et8rR09MZXmv45ND6p8KXiK054idobl/R0OoJpp+lvT0sr2dPz0MkJcfNpq1kjZntLenrkmaOjpiK/brG9o5ptPpDRah21cdtXAy22mV/IYzFdoUxwkISt+E45MiVKJ1rnErqHWnCaTguvcPJjnXQvxFduLjaIhuDpu422/saA/wCVY2pppDgAujJkjmj6jk+W9jwxuB50hyV2U21+K9tNdrFCN0tI3W0aojaBJ8nGyso5SAAXxOdLHPF1Oy7yZI3iNuB8xKclflusXtJkv06VR9F7XnwZ0htTRXdeDUInIBKSaVu0u3YcmfHfkFnLTsp/gQZfejrI8Fx7uz8RRlTb6q07O6angq5Glvz9eI8x5GC6GljfK1z/AFY+aXpGPqgfnA4s3w+K7HWWussmwmkKmnr5Wlv9JXNsXVFkYLqeiifMxz+cskqJixpH10z84HUhWahUazUp9Xq86XU6pU5cifUKjPfdlTZs2U4p+RKlSXlLdfkPurNSlKMzMzHl9cbjX3evrbrda2WpudTK6SWWRxfJJI8lz3ve4lznOcSSSSSTkrxou12ud+udwvV7uE1Xd6uZ8000z3SSyyyOLnySPcS573uJLnOJJJJKgHUZyXf6wMJR6i3U7GR9nr4AnqoSU1zM+W5GXZv8QH27of71Aup4Vnjl2eu4PsndcYIgIgIgIgIvrvoOannph0i7KkSJX2ei3e65Ytc45BsRzj3CtpqmvSDP/DNESutRXPvciSe5Dm/w86wdo7dKwzSTdFBXE0k31Yb0zYEZd6YbMI3c9gCu6PgB3XO1Hic0HU1VV5VivbzaqrL+hnRWFrYHP/dIjq207+ewB5C9LxqHq5JUjJyV9VAathqGG+pJ9VqwtpqGM+o93cLVhbTUMZ9SB6qoC2moYr6rOcFaulbTUMV9T3+pVwtpqGM+p7rVhbTUMd1T6KuFt4hYdPnuVXC2moY7qj2K1YWnELDqnGPZVwq5d1vxbutW5bVncP2O5aBV6DK4kktKWKvT5EB1XCfM0IfMy8SETd4I7tbLla5z+wqYJIj+T2Fp/mts610tR620bqzRtwA+Ru1tqaOTIyAyphfC449cB+f0Xkuq9MlUWrVSjTkdXNpNRm0yWgyMjRKgSXYshBke5cLrRkPKurppKKqqaOYYmikcx35tJaf7wviwvdprLBebvYrgzpr6KqlglHtJDI6N4/RzSFHDHUYgIgIgIgIgIgIgIgIgIgIgIuxv2S2lh6te0F6ONGdYaep1r3bI1LqvXo6xlEbTekT7vh9Y2aFpUUitUuIyRKLh4nSyMO4TeRRzvzzjH8eP966a/EC12Nv/AAi7y3KOVzayut7bbF0nDi64yx0j8HII6YZZXnHOGlf0H+IbRbUD1K+Q7C3cQvtnxjlUwteIXm1CphbiUL7aj/OVMLdxC+2p+6p0rXiGQ2pHuqYW4lC+2o+6phbiUMhtQfQrThbiUMllT91TpW4lDJZVD3WnpW4lDJZU+xVCF8/9K3WmL0eOjbrZrVJdJp3TzTu465Scttuk9chwlwbViKbdMm1lOuWXEZMjyWHOR8jyfmy1rjnsFy3sHtpPvBvRtltnCzqZeLxTwS8kYpusPqn5HI6KZkr+P7Pcd1/M/qE+bVZ86qVGS7MqFSmSZ8+W+rielTZjy5EqS8rbidffcUpR9pmIQkkknuvtko6SmoKSloaOFsdHDG2ONjeA1jAGtaB7NaAB9gsMUWQgIgIgIgIsqMjiMz7zx/fs7RTnKKxR2+X6e/kK+6fophpO5Z3wH5IvRf7Mf217PRp0/oPR66S1v3Fdel9rNvQ9PtQLTai1K7bMpD0g32bXrtEnS4CbhtinvPuHEkMyCnQI+I6WZDKWUR+xW2G97dMW+n07qankmtUWRDLHh0kbc56HNJHWwc9JB6mD6QHN6Q3xl8dXwt5d89YXfeTY280Vu17cCH3C31ZdHSVswb0mqgnjZIaeqka1omjfGYKiTMzpIZDK6bsasyX7Enpo9JG2VacaJ/8AFzXG/Jsu5qzGoNjavWVa1LOGy/Uq/dOpFv1SfZGl82M0ouOc+qJUVz5b6E4kyHiJXI1E/Y3W+pqX+jbH85fahxe4MiqIo24y58kzHGKAj1eel/W4gfU489MNTUvxUfC9sffjrXdT/Bzai0RMpoX1FdZ66qm63Nip6W21EUdddGOP4YGebTCnijccwQxkt7FvaDdLK3+g70W7jvikIpkK85sJGn+ilssRoSIqrxnU59ijSGqRwFFO3rLp8ZdQktEgmDZipjFwqebI+R9xtX0+g9J1NfCGNrnN8mmYAMeYWkNPT26IgC8jGMNDe7gulvgv8OV68WfiCsekrk6ol0vFKbjfKpznl/yUcrXTNM2ev5mukcKeN/UXh8rp+WxPI8XvQv6NV59OHpQ2vps7UKvLbuSszr21avV952ZUaXZ8Wc3ULzuWZOkolKerdVkTExIjjxLS9VZzBOGSVKUXR3RGl67XurKS1mR5bK8y1EpOXNjBzI8k5y5xPS0nOZHtzwSV9Tnij320t4SvD9qDXjKOmjkoKWOhs9C0Bkc1a+Mx0NKyNpbiCFrDNM1haWUlPKWfU1rT7eeklr7pL0BujQu8ZdHjQ7YsSjUOwtLtOqVJTBdr1WjU7+XWjZVHekFJXHjR6fTlOyH1JfXGp8R580OqRwL736p1HZtudKmufAG0lOxsUELTjrcG4jiaTnADW5ceS1jXOwSMH5Pdhdl9yvGdv4zStLc5JdQXiqnuF2uczfMFPC6XzKyuma3oDnOklDIowYxLUTRRB0bX9TfOnof7dXpH0rWFqt66Qrbu/Rus1B9qsWbaVq0yjVu0abKdUtiXZVV+1RZlSmUk+Euoq8qUmWwS0G406pL7fWCw+IbVEF7E+oY4p7G9x6oo42tfG09jE7ILi32kc7qGRlpIcPdbdn4OmxN02ultOz1ZX2vdSlhaYK6srJZ6etlYAHMroeh7ImTcnzKKGEwyFrgyWNroX2nSqDfftpumBKuvVijnZmgOkNtSUSaTa3UtVCh0Wruy0WvaKLwXTDlVW6rlrbTk95+Sj7OiNClfZ2WSNKFX7PHcd+9cOrLzB5GmqGI5bHjLGOz5cfmdOXSSPBeS4dIax/S1vAMZuRWaN+En4WqfTm2l0F23t1TcGFk1Z1OjqJ4Aw1daaIS9ENHSU7mUzI4neY6Wem8+WU9Tm/K3T06AquhtqPbtsW1qDG1LpF60ypXBb1GRTZbWoNBodPlRoK5V102BGfpJwH5z6o8Wcy82UxyO9/y7PVmQ2NuXtsdB3WlpKS5irhqGOexvSfOYxpAzI1oLcFx6WvBHUQfpbhdqfBB42I/FloG/ai1DoZ+nrnZ6mKmqpzLGbZU1ErHyBlFLK9s/mtjaJZqeRj/IZJF+3l6wV8IMxXGnFtOtradaWptxpxCkONOIM0rQtCiJSVoURkZHgyPYcWua5pIcMOHfK75RyRyxslieHROAIIIIIIyCCOCCOQRwVYYkbGDMsme/jvyFP5LX/NWSJH5GZZM8dnL+wIrJDiZwePXeHKfdWWNGzgiLOO3v8O8P0RWOLEwRHjP7bBlMKfjRDPGS8g/JFYY0LYvu/DIfzT3U5HhEWMl8voXkCKXZidyeZdvME/VSbUHONu/JgikG4HL7vyFfsn5LMRA5bfLu+Yp6ouYqfguXy7P2BEOB/tD2RcSoH+3lsCFYjkDn93l3kCr7KPdg89vDPkQKn81GvRDLmWS8gRRL8IjzgvdyPcE5UHJhc9t9+zv7OwwTlV6TDMjM8d5HgjBFASYpHk8H5ft7gQquSYvDn7p4M/XkCfryq3Mil97Hy+QIq1Kj5ztg+ef3x3AigzN+JIZmRXVx5UR5qTHfaPhcYfYWl1l5tXMltuIIyMuRkMmirKq3VlJX0M7oq2CVkkb2nDmPY4OY5p9C1wBB9wsu319Zaq+iuluqXw3CmmZLFI04dHJG4PY9p9HNcA4H0IXcVpTqFR9W7Fh1c0w3ZbkU6ZdVFWlDrcSpdSbU2K/FeJZLgT0GbjXFxJWw4RGZqJRF9Fexe7Ng342xt2oWinfXvh+XuVIQHCKo6emaN8bs5hmGZIuoFr4XhpJcHgfV74bN8dMeJjZy06qa2lkuclP8rd6IgPbDVdHRPFJE/OYKhpMsPUHNfBIGuJc2RretvXzos3XZNZqFbsij1C5LImOvS2W6Yw7PqVvJcUtxdPnQ2SdmPQoic9XKSlSeqIutNKiyrys8SHg51pt1f7nqDb2x1V228ne6Vgp2Omnog5xJgmiZ1SvijH4KgNc3ywPOLXgl3in4tvAJuDtPqi76o2r03WXvaqpkdNG2ljdPU24OcXGnnhZ1zPgiH9XVNa5vlACcse0uf842zozqXfVUbpVuWZXZTjjjaHZcinyYNMhJWoi62fU5jbMKI2RHxfeXxKIj4SUew656J2R3X3Au0Nn0xoW4yzOcA6R8L4YIgSB1TTytbFG0Zzy7qIB6WuPC6nbeeHTe3dK+QWDR22t2mqHvaHyyU8kFNCHEDrnqZmshiaM5+p3U4A9DXHhdwGh2kVudHXTqa3U6nT/5k+0ddvq6HnCjU83IbDhk0y/JJpbVFo0dS0tG4SVLUpx00oNw0J9vNhNmtM+GjbOtiu11p/6Tez5q61zndEPVG04a1zw0tpqZhc2MvDS4uklLWGQsb9GPhj8P+j/CBs/cIb3e6T+mZGfO3u5Pd5dP1RMdhrHyBhZSUkZc2IvDXPc6WZzWOmMbeovpV63Oa4ahKm043mrNtpqRSLRjOktCnmFupXUK6+ysiUzKrbzKFcJkk0R2WUKLjQoz8iPFTvu/fTcV9fbS9ui7Yx1Pb2OyC5hcDLVOafwvqXNaenALYmQscOtjifCXxq+JZ/iT3Xfc7QZGbe2dj6W1xvBBewuBnrHtPLZKx7WO6SAWQR08bx1scT8nSG/0PYdZV09UJIbxnbcvoH6ooh5Gx+G5doJ7qKdTz9eZBlFGvJ3z3giiJLeSPbPrs7hUHCqPRV2S2ZHnB7fHHZtz5CnvyFTPHJWECICICICICLnjSX4cmPMiurYlRH2pMZ9s8OMvsOJdZdQfYttxJGXiQuRSyQSxTwvLZWODmkdwQcgj8jysilqqihqqatpJnR1cMjXscOC17CHNcPuCAR9wvV/o3fzOp2lVg360vjXc9sUuoTcJSgm6r9nTHrDBJQZpSUeqsPN7f6eRch6/aM1UzVGktPagacuqqWN7vtJ04kHHtIHD9F9kOxm4cG7Oz23G40L+p92tFPNLwBio6AypZgcDoqGSs4/s9h2X6SahOvqiexXK/StpqGI+p9crUGraahjuqlq6VtNQxXVBOeVXC2moY76jGeVXC2moY7qnI7rVhbeIWHVJ91XC04hYdUZzyq4W3iFh1QBx1KuFt4hYdUj3VcLTiFh1T3WrC80nTOtA7M6SuqMFDaG4tXrTV1w+rTwtqaumFGrb/CnhSRdXPmPNnjbKDHntu1bP6L3B1HE0ARzSiZuO2JmiQ/wcXD9F8mXj20MdA+LHeC2sia2kra9txi6RgFtwijq34GAB0zSysOOMtK+XRxyunyAiAiAiAiAiAiAiAiAiAiAi9Fv8OTpqVa6Quumq0mCb8ewNK6ZacGatC+qh1jUK5GZaFNLLDf2ldIsiW3g8mTbitt8jbepajyqanizy9+f0aP8ArIXjj8ZTWptm0G1ugYarpmu9+lqnsBGXw2+mLCCO/SJa2J3+k0ey9g+Rs8TduV86+Fu4hdbP91TC14hdbUfdUwteIX21KphbuIXm1P3VMLUlC+2p/wA5Uwt3EL7an0yqYW7iGQ2p+604WvEMhtV91TpW4lDJZUj3VOlbiUMhlT7OWnC6B/4hzWdyyeiRY+kkF91moa16mRftyW3zbJ61NO4qLgqrLjaS4nkKuKfRjxnhLh3I8kMyOYyNdzwvWj4P22rdT+IPVG4NVE11HpmyO6Mtz01VwcaeIg+hFOysHvzxheLMal9LSAiAiAiAiAilore6SxuWDPBdu3n4eYcd0PryrCwnBfl3cgT8gpRlG3if9wRSbSc48Tx7gRfsGkGsGqWhN603UTR6+7i09vOlZRFrttz1w33Iy1tuP0+oxz6yFWKRLU0nr4Utp+I+lJE42othKWi9XWwV0dys1fLTVrOz2HBx6tI7OafVrgWn1BWxdxds9Abt6WrtFblaSor1peo5fT1MYe0OAIbJG7h8MzMny5oXMljJyx7Tyvprpd9OfXPpvVbTyr6zy6GlWm9quW5SKZa0KbSKFKqM6Ycqu3hJpEmpVGNHua5EMRGpi43URlNwWEtstpTgbh1nr6/67mts17fHmmh6GtjBawuJy+QtJID34aHFuG4a3AAC4a8M/hH2k8KNu1rbtrqWrIvlxFTNLVvZNUMjjZ0QUTJmxRvdS0xdM+BsvmSh08pfK9zsr1I+w76NFlaS9FtvWKJWLaunUTXSS3UrkqlBqdKribSt+jLebt7T52bTZEtMCrwykrn1aKpTT7UyWmPIb4oiDLtfsLpags+kxe2TxTXK4Hqe5jmv8tjfwQ5aThwz1yN4Ic4NcMsC+fv4te/Gq9x/EG7a+ptlfbtEaRjMVNFURTQfOVM4aam4iOVrPMhf0tp6OUB0boYXTQv6ah66Pfa2dMlfSo6R861rRrCZ2jWikipWfZRwn1OU25LhS821ed8JNDrsaYio1OIUOA+gzacpsNl1vhN93i6+bz65OrtTyUdFP1WOgLo4sH6Xv/42X2PU4dLCOCxoI/Ec+wfwzfCs3w67FUeo9TWsxbp6sZFW13mNAlpaYgmhoOQHMMUT/PqI3Yc2qnljfkQx460LfotSrtVplDo0CVU6vWJ8OlUmmQmVyZtRqVRktxIMCHHaSpx+VLlvJbbQkjUpaiItzHEcMMtTNFTwRl88jg1rQMlznHAAHqSeAF6JXK5UFmttwvF2rI6e1UkD5ppZHBscUUTC+SR7jw1jGNLnOJwACSvdz0I+jdavQb6LNIti45lJpVcjUuZqLrXdsiSyinlcz9Nbl15x6pKX1P8AIrQpcNuBHcLgbXHh9eaScecNXoht/pWj280dDSVcjGVAYZ6qQkdPmFuX5d26ImgMaexDerGXFfGx4vd/dS+MbxIXLUFgpKqptElTHa7BRMY4ymkbKWUwbF+L5itme6placubJP5IcWRMA6+bk9uho1RtTajAtDQe57osdUxmnT9SiuOlW7ctagU12Q0xUIFmybekrnQE9c45CbnVeE91TuXG461LQnius8R9gprvLHQ6ammt3V0uqOtrJHhucFsRYct5JYHysODyGkkLvxpv4LG7t627t9bqne2223WQiMsVpNLPVUlPJMGudHLXsqmCOQ9LW1DqahqI+tmGSTNa15+xekV0XNAvaH6CUzVCyINIjXvctpJujSzVGJTmKTXHpa4alQbbvR5tn7VPo6pjJwpkaV1rlNdJxTHA4lRL33qzRmmN1dMRXi2xxtuM0HmU1QGhryccRynGXMyOh7XZLDktwQc9UvD54md9PAFvrcNuNaVtXJoy3XX5O9WaSV09M2MSASVdvaXdEVQI3Cop5oehlUzy2zdcbm9PkUXS5VOmyqfPjuRJtPkvwpkV5HC7HlxXVMSGHUH+Fxp5s0qLsMh0GkjkhkkhlYWyMcQQe4IOCP0K+uChraS5UdJcKGdstDPE2SN7Tlr2PaHMc0+oc0gg+xU5Dj8WDwNCyvVWiLHzgiLz/QE7KzxIuMGZeBcs+7BAiscWJnBmXd/YgRWaJD5bF2Y2BFYY8XGMFk/Lb4gim48IzxlO/wAORB+vCKcYhdvD3fsH5p6qXZg8tvl39nIE9FJtwOWEn8g9EWYiB/t37Ni88gi5ygeHh8u4P0TshwM9nf6IEXCqBn/L2d35Y7gRYjkDwL4cuzHZzBMKNdgc/un63DlFEPwef3f07OfM+Yfqn8lCSIWM4Lv7PXaCKEkReZGXoueeWAT+ar8uHz+79O7xLcP1RVmXExkyLffl3/oH2RVuVGIyPbffv7f7gnCrEqPgzIy/bIfyRVmZGxnBY39bdwIqxLY5qIvP9D7gT2U5YWo11aWV5NdtiWSCd6pFTpcjjXTKxFbUpRRp7CVINRt9Yrq3Eml1pSjNKiyoj5X2f3l1tsnqdmpNG1/S1/S2pppMup6uNpJEczARnp6nGORpbJGSSxwBcHc3bD+IDcXw76yj1doC5BrZOltXSS9TqWthYSRFURggkt6nGKVhbLC5zjG8Bzw776tTpj6V1aG0d0nVbNqJIP7S1Ip82tU83SxtDmUWNLlvIWR7G7GZMjIyxgiM/WTQnxANlNQW+B2snVun7uG/tGPglq4OoAf1UtJHJI5p9PMgiIIIIxhx9wttfiieHfVNrpn7gOuGl76GftWSU89dTdQA/qZqKKaV7TnjzaaFwIIIxhzuS5umdopQ4qnKTUK3dko21KbiUmh1CARO5whEiRcTNGQ0hR7mpBOmSeSTPBHK6p8e/h+sdG6axXS4XusLT0x01JPCOrsA99aylDR6lzRJgdgT9Km9afE48Lum6B8+m71dNQ1xYS2KkoainHVnAbJJcWUYaPUuY2UhucNc76T1xa79Je+9ZiVS3iRbVmNvE41a9MkOuplm24TjD1enqSyqrSGHEkpCerajoUklJaJZcZ+aO/nit3A30e+0ztbatDtf1MoIHud5hBBY6rmIaah7SAWjojhaQHNiDx1nyB8Tvjb3R8STn2Opa2y7cMk6mWymkc8Slrg5j66chhqnsIBYPLigYQHNhEg6z8nSWskosb9n5Dq6umSgJDfz5+YIP7lByEfoYIoV5GDPw+gJ+iiHk4M/j+oIo15Gx7eXcCfZRTycly3/ALdoIFAym9z2LfOc+HmXICOPVCc4UKZYMy7jwCLQEQEQEQEQEXfN7Mi/FV/RW4LKkOLXJsG63vs5Kc4ybo1zsnU4baEnuhJVSPOPnjcd9PDNqR1ZoiuscjyZaCrPTz2jmHW0D7dYlX0Z/CW3DdqXYbU2gqmVzqvTd5d0ZdnFLXtM8QA9AKhlWfbnjC7ITUOxDqrPJcvVPpW01DGfU49Vq6Vt4hjuqvXKr0rbxDGdU85JVcLbxCw6pGe61dK0NQx3VQ91XpW3iGM6qPoeFXpW3iFo1J79SrhacQsOn47quFpxC06oHv2VcLbxCyansq4XSN7UK1Sgal6e3i1HNtu5LPl0aQ+lKuB+dbNVW+ZrVunrUwq+ynv4UF3DqX4gaINv1lurWYE9M6Mn3dE/P8emQD8gvns+MLosWzdva/XcNL0w3axSUr3gHD5qCoLySe3UIq2Jv+i0ey6whwAvIBARARARARARARARARARARARex/+HV0+/kPRb1d1GdSSZOoOsrlGZ+6ZKcpNiWtR0xXTWZYUj+aXPOQWORoMcdaxq8V9NTg/hiz+rif9zQvnF+MXq7+ld9dvdGxnMNo02Jnc8CWuqpi4Y9D5VNAT+YXoP4htRtSfdeQ/SteIXW1Kpha8QvNqW+6phbuIXhUduVTC14hebUfdUwteIXRUY9Uwt3ELoqOxVMLUlC+ypIPdU6Vu4hfFXz9lTpW4lDIbVDgZWnpWpKGQ2qz2Kp0rxZ/xDOrP9Y9MOzdMIy1HB0d0opDEpo3eNKbkv2dJuiouJaJJEz1lvFSCPczVwZ5YIbjt56qfrPqT/Acfzyvpa+EBoD/Bzw6ak1xM0fNajv8AM5pxgmmoWNpowT64qPmz6AZx3yug8Zy9YkBEBEBEBFvbLiWRflki8TDn0RT8VHbjngi2+PaHoqY+6nGk7EXf65gqqVaTt65gik2U757C7Pz+IeiKYYT245fUEU3HRy8NzDKL930j111l0RVcatJdSLrsNF5UGp21dEW36o9Gg1yjVeBKpctio05fWwZElmLMc+yyjb+1QXTJ6M406lKymbRqG+WE1X9DXSanE0bo5Ax2A9rgWkOHYnBPS7HU08tIcAVxruJs5tbu02wt3J0Hbby+11kVVSOqYQ6SnnhkZKx0UgxI1pexvmxdXlTsBinZJGXMNGjN8tuWxef0EMuS16BfYg9Ds9QtR6h0o73pfWWdpRNXSdOo8yOao9c1KfipW/WmUvMqZkRrHpkonEKIyNFSlR1oVxxlkXY7w/aG/pO6y6wuEOaGjd0wAjh85HLhkYIiacg/23NI5aV4s/F98U40LoCh8OWkLj06r1LCJro6Nw6qe1NfhsDul3U19wlYWkEYNLFM1w6Z2k9mvtstS7ksjok0u1rfdkRI2qmpdDtK55jJERLtun0mt3W9Suu4eNldUqtCh8XCaesjtPNqyhaiPlPxCXert2iIaKlJaytq2RSEf5NrXyFv/Kcxv5tDh2JXQf4O+3Wn9Z+KS5alvsTJZ9M6eqK6kjdzirlnp6Js3TnDhDDUT4yD0yvieMOa0jybU+I6+6yww048++4hpplpCnHXXHFEhtpptBGpxxxaiJKSIzMzwQ6PtDnuDWglxOABySV9TMsscEUs88jWQsaXOc4gNa0DJJJ4AA5JOAByV7mehVpbUejz0RdHtPr0cRTKtalnTq7daJryUIoNQuasVm+q5Tpr7nC23/T8ivOxnVZNtJsHhRpIlH6PbeWWXSuhLBa7ieieCnL5cn8DpHPme0n/ADC8tPp9PfC+LDxj7mW/f3xXbt670dGam1XS7x01CY25NTFRwU9tppY2jJPzTKZkrBjqPmjLQ4kDyuaY6UVTpedK6dalmMPxKdqNqRdN0VCoNtGtNs2TNuCbWqtWXydaYQk6bSJHC0lxLfXSlNNERKcSkdGrPY59wNdy0FuaWw1lZLI5wH9XCZC9zzwPwtPGQMuLW9zhfVPuPuna/CF4UaLVus5o5bjpzTdDRRQl2PnblHSR00FMzpc4nzZ2ZeWOd5cLZZclsZcu2bp/+z46OekWk9R1msGsztMqhQk0ejNWi6p2vUW+qvKONT4UOnNSpLU+jV2U0y7LkutLeim2064cdGFrHOG7m0uj7Bp+fUlqqXUMsXQwRcvZO84a0NBIcx5wXOIJbgOd0DBK8svh3/EL8R+7271r2U17ZINUW+4GoqDXjppam107OuaWWZzGOiqaWMuZBDHI1k/W+KIVD+prF0hwo3LJbeXrvHVVe/PurPDjZMjMj7CL13h9gqevKtUOJywWfP57Ais0aNyIi8z29/ZzBV/mrDEictu/w/uCKxxoXLbG3rIfoinY0Lw5ft2AUUyxC2Lb18ARSjUHvL5e7kCLORB/27eu3sD07qi5yg+AKqHA8OfP3Ai4VQcb47O7IfyRYTsHn93tBFGPQiLO3b67ATlQz8LYzx9PiCH2UFJhc/u9/MvLxFU9lXpULnt5frkUyirsmLjJGW3l9QCKtzInPb6h+qKqTI2M7fLs5H7gT1VYmRiMj2L0e5CqKrS4+ckZcs+vcKJ2VUlsYM8lsCKry2Pxbd+PgH5oqvLZ/EnHeZCiKsy2tjMi3Ln5/oKoq1KawZljYy+H6GCKuSW+e3Ln5Air0lvdRe/9QRQUhHPx+v0DlFBSEfmR/qCKIfT+4foii3U/p8QRRbqdzL148wRQktG3L35Lbv8AeHKH05VfeThfif02Isgn8lwgiAiAiAiAi7HPZl3n/Itb67aTqjKPfFmzW2UEvhI6rbkhqrxVGjBkvhphzSLkZcWe8dg/Dle/6O1jX2xx/ZVtG4Dn9+Ih7f8AzPMXqf8ACU13/g/4hNQ6KmcfltQ2KVrRnGaihe2pjOPXFP8AND0Izntld8ZrHdV1WB6r6PelbTWMd9XnsVXpW3iGO6qPP1LV0rbxDHdU/dVwtOIWHVPc5VcLbxCw6q/gq9K04hZNT91XpWnELLqk9squFt4hZdP6ZVcLTiFsz/dVwtOIWXVA7EquF1qe06tv+Y6RWVdCCI3bavlMFzYzNMK4qROJ5fFjZP2ykxyPvNRDg/fOmFTp62Vo/FBVYP8AoyMdn+9rV5JfGA0h/Sexu32sY25mtOoxC7jkRV1LN1HPoPNpoAffIXR0OrK+dVARARARARARARARARARARARe+72OdkM2N7Ozo9spZUzMuinXbfFRUrGZD10Xxcc6A9jBYT/ACIoiC55SgjHCmq6vzL9XDP0sLWj9GjP9+V8mPxGtTyap8Ym70hlDqahmpKKMD90U1FTskb/ALfzSfuV2dcQ2+2f7rpBha8QuCf0yqYWvELgn7c8qmFrxC4J/v6pha8QutqCPVUwteIXxVH3yVTpWvELoq1TpW7iF9tUPdU6VrxC82qb7qnSt3EL7an7qmFrxC82p57qmF/OS9oRqcnWHps9Jq/WZbk2BP1cuqjUaS4ozN2gWhM/o+gKQXEsktHR6CyaSI8ERjkqiYY6OmYe/QM/mRk/3lfZR4Q9DnbrwybI6UkpxFVxaepZpmj0nq2fNz54GT5078nvlfHAyl2OQEQEQEQEWTGTkzP3AnuFY4yCIiIsd59nvAKg7AqZZL73kCqpNouXxBFKsJ2L4/Dt+IIpmOj8PZnB9/cCKdjo5bcz+Qfqn5qwRUb58se7+4Iv3HQvR28detVbF0gsKF9uum+q7Fo0AlJUcaEyslyapWagpJKU1SqFSY78yWsiM0RmFqIjMiI5ewWSu1HebdZLbH1VlTIGt9gO7nO/zWNBc72AK463b3Q0rstttrHdHWtX5Om7LRPnlxjqkcMMigjB4M1RM6OCFp4dLIwEgHK91f8A7kfs+OiIhttJxtPdC7EJtpBrJip3bcDrpqPdX2hCbhv+8qka1YI2W5U0zIkspwn0LJsm2OiPa2W6m/J0j/7/AK5pXfkHP9Gjj46I2bo+ObxRnqPma61leck4LoaKmA+3SflbbQxADOHuipxkuldl3wr0UNYtL/ao9Ge9NF+ka/Bl6l0i6K7XKvRaPKj0O4qZRpten1Sxr4sPjiuNoZtKHWioanOrlkgoyUz0LROST/G2ir9Zt5tI3DT2q3NN3ZM97mNIZI1pe50M0PH/ABYd5WcOx0jzARIOrux4nNp9yvhn+InR+8GwME0W3lVbaangqJ2PqKWWeOmihuNuuWHg9VbJB/SAb1wlxlc6ie19G4w/o3Rm9kdoL0edRafqfU7jubVW4rcms1GzIt0Q6TT6DbtTjqNyJWnKXAadVV65TnSS5EeddSzHdSTqWeuS043JaQ2L0xpa7RXmarmrauJwdEJA1rI3Ds/pb+J7TgtJOGkdQb1AEbK8RfxV989+tv6/bW26ftul9P3CF0VwfRSTy1NVC8YfTiaVw8imlbls8bGGSVhMTpvKdIyT839qh04bfsexq/0bdM67Hqeo17Q3aPqHOpMtLrdjWlKI2qpQpkiOam03Hc8biiuRTV1kaA46p1KFOsGqB3w3LpLXa6rR9mqQ+71LSydzT/UxHhzCR/xkgy0t7tYXF2CW55b+Fl4Ib/rrXVg8R+5VjkpturJM2otMU8ZabnXsOYKmNrsE0dE/EzZsdE1SyNkRe2Ofp/QfZPdFc9GdG1avXZTij6h6zQ4VQhIfL/mqFpuRIl27TzQpslRZVxuq/mckkrUS2FQ0rJLjKklKbE6H/wAHdPHUFfDi73FocM92U/eNv2Mn9Y73Hlg4LSFsL4sHinbvNvGzaHSdyMm3mjJpIpC38FVeOY6uXIP1sowPkoSQOmQVj2F0czXHrh9pv0mj1v1mPTi16j9p040flTqQwuM605Cr97rUUe5a4l1g1JlxqebJU+IpSlJSTLzjZkmQrPB++Wuf8JtRmy0M+bNbnOYMEYkn7SPyO4bjy29+ziOHlepnwrPCuNj9lhudqq2eXuTrKKKocHtcJaS1j66KmLXAGN83UaucAAuL4I5AXU7cULoE9GAukhrHEiXBAekaZWO3GuG/3UrfjtT2FOOJo1qplxXWJLL9yTI60rNpxt1MNiQtC0uJQYgdpNC/4camjjrIibFSgSVHJAcMnoiyCCDIQc4IIY15BBAXLXxDPFafC1sdV1mmrgyLda/ufR2gFrHmFwANTcDHI18bm0Ub2lgka9jqmWmY9j43PC/denT0M9PujTIt647DvR5ylXrVZ0Wnae15Dkyu0pmFHRJmz6bW2U8FRocJ59pjhlobktG82XWyTNakT28G2Nn0K6jr7PdXGnqpHBtNJl0jA0Zc5jx+KNpIb9YDhlv1POSOJPhw+ObcrxXQ6l0luJoKMXiwUcUk16pMRUlQ6V/lxQz0rjmGrlaySXNO58EnlykQ0rQxrvguLHwSSxufyIcHfqvVBWaHF5ZL1gEzkq0RIn4du4+RcvRAiskWJnG2eXZzD+SKwR4fLb4fXvD3winGIZbbe/HxFE7qVaheHb8sCqr/ACWciD4fHkfd7gVPzWQUHP8Al+WPmK+yfqtTg8zx8vcKJ9lwLg7fhz5f2BMe6wXoR7/d+WPL3gEHoop+Fz29fqAT9FCyYfPb4bd/aCc/ooCVD5/dxz8ARVuXE57H65+8MIOyrEyLz2/sXvBUVYlR/wASTLOM4+eweyqqtNjbHtzz47Zx8g/JMqoy2MGZY2Pn594e6KqTWMGe3y57+QfonHcKqTWNjPB49fAwRVOY1se25eiMEVWmM4IzIvH12giq8tr8Xjv27ePyBO+FV5Tf4i7S9ZAp7qtym9+XPPlk/d3girkpHbjlz93YKn1RQElHPw3FEUFIRk1F378gxwihnk5I/p5fAEUQ6nn65AijH0lsfeCKIkozkuXPljt+JbgUye6rkpON+0jx8Nu7IJ6rCBEBEBEBEBF+49Gm7SsjXzSe41vKjxo16UeDOdSeOCm1t/8AklSNW6SNBQai5kjPBkN4aAun9D6003Xl5bG2qY1x/wA2Q+W7/wA1xXYjwl61G3viU2V1TJOY6SK/0sMzh6QVb/lJ89sjyZ35HsvUKah6Buqxk8r7CulacQx3VXsVXpW3iFl1UT6qvStOIWHVPueFXC28QsmpH9pVwtOIWTVD3VelacQsmq/iq9K28QtOqe4yq4WnELTqonPKr0rTiFp1QfUquFpxCy6p+6rhfJ3TfoLdwdGTUtBtmt+kRqNX4plzbcpNfpkiQ5yPb+X9ck/BQ2HuTEKzRt5YRlzGtePzY9pP/m5XST4i2l49T+D7d2MxF1RQw0lbGR+6aatp3yO/2Hmg/YrzjjqIvk+QEQEQEQEQEQEQEQEQEQEQEX9J7ol2m1YXRa6ONmNRyinbWhuldIkMlxEZTYlkURuoOL4jNXWvzicWvP8AmUY60Xit8+73OXq4dUSH9Oo4/uXxaeIG/wAmq99d5dSPm8z53VN0laf8x9bMYwMejWdLR9gF9CcQwRUdsOXEOFrxDWKjHqFTC14hcFQUwnELgqFTC14hcFT91TC14hcFR7FMLXiFwVHsVTC14hdFR91TC14hdbU/dUwt3ELoqj7p0qiaqXq3pzphqPqE9wG1YlhXheTpOf8AZm3a9vVGtrJef8nDBPPgM2klM9RTwB3L3tb/ABIH+9bq0Hpl+stc6M0hHnzLrdqSjGO+amojhGPvl/C/mN1CdKqk+dU5zqn5tRmSZ0x5W6npUt5ciQ6ozyfE464Zn5jnIDAwOy+32kpYKGkpaGlYG00MbWMaPRrAGtH6AALDBZCAiAiAiAikIifw533zsWdvEs94foqD8+VY46cY9xfAviXIO3ZVPdSzJbZ9eBgilGi3L3ECeil2U8vh+YJ6/ZTccvoRefuBFOxkYxtyL6gisUVH4S95+4EX0R0ededTOjTqVR9WNJqvFo130hqTDQ5UKXArECdS6gSG6nSZ8Ke04lUKosI6txTSmZCUmZtuIVhRT2m9S3bSd2gvNlnEdbGCOWhwLT+JpBHZw4OMEehB5XE+9eym33iB2/um2m5lrkq9L1TmPIjmkgkjmjyYpo5IyMSROPU0PD4ycB8b25C++em17S2/emrYemNhzrTi6eUm2VyK9flNotXlzqTeF6kbsKlVCM1KbTKhUOj0tS3I8R9yQ43JmO8brvVMuDf+4e7Vy19bbRbZKIUsERL5mscS2WXkNcAeQxrclrSXEOcck9LSuovg3+Hporwja33E1tSamkvtzuIZTW2WogZHPQ0GBJNG9zCWPqJ5ulss0bY2uigj6Y4/NlYvgyzrkuWy67TLps+v1q1rlo0pMykV+3qnNo9ZpkpJGlMiBUqe9HmRXeBRkakLIzIzI9jMcWUdbV2+phraCqkhrIzlr2OLHtPuHNIIP5Fd9tSaZ05rCx3HTOrLDR3PTtZGY56WqhjqKeZh56ZIZWuY8ZAOHNPIBHIX2RVOnl0yLnoKrbrHSI1IcpbzK4zyYFWaotQkR3ME61IrVFi0+syEPJ+6olyFcSTNJ5SZke8qndDcCspjST6sq/JIwelwY4j7vYGvP3y7twutNi8CnhA05fI9RWvw/adFzY8PaZYHVETXjs5tPUPlp2lp5b0xDDgHD6gCq50aLOpOpXSG0Ysy61uSaJeOqdl0e4escJT0+BVbjgtVGMp57i4nqiy4priVxHxOZwo9jidH0FPedXact1ec01TXQskz3c10jeoc+rhkZ57+q5B8R+rrttp4d959Z6TY2O92bStxqKTpGGxSwUcpheGtxhsLg1/SOkYZjLRyPXL029T7h0X6Leql6WZGkJr8OiQLfo8uChSCt5d0Van2wdfJbK21RToUeqKfjrIlJTJQ0Si4DUZd89zr5Waa0JfrnbYz822JsbC0f1fmvbF5nHboDupp7dQbnjK+S7wMbWac3u8V20uh9bVUZ09PXy1dRHK7PzYoaeau+Uw4Hr+afAIpWkgmF0paesAHyMUKk1Kt1KBS6ZElVSr1ifFp9PgxWnJM6o1OoSERokSO0glOvypcp5KEJIjUtaiItzHnZBDPV1EVPBG6SpleGtaOS5zjgADuSSQB7lfZjc7la9PWi4Xi7VcNJZKGmkmmlkIZFDBCwvkke44ayOONpc5xwGtBJ4C9bvRE0Do3RU0Ep9ErjlOhXI/DfvTVOvuOsFGarCoRSJ0Zyo8am1Ua1KawUVtZLJlRMuSCJJvLHobtzpGm2/0jDS1bmNrS0z1UmRgP6cuHV/YiaOkHOD0l+AXFfHF4z/ETfPF/4irjftPxVU+mGTMtdgow13mOphL0RPEOMipuE7zO9paZGmWOnLnNgjx0F9J/XGq9JTWqt3fHTNct5mQm2tPaLwPKdj25DkuNU5SYf3loqlekuKlyEESlE/I6sjNLaMdK9xdX1Ou9V1Vxj6zRB3lU0fORGDhv0/25CS9w79TunkAL6dPBp4crL4T9gLDoysdTs1PJEa+91ZLA19bIwOmBl/Caeija2micSGGOEzEB0j891+h3Qf0voHR5pli6r2fT7guivNruu7aibKjrtDr0+ESGadb9VgNlVIh29AJEfgZWtt+Sl1ZpWh3gHa/SO0OnaPQ1PZdS2pk9wmHnTOx+0jkc3hsb2/W3ym4bhpIc4OOCHYXz8eIj4i+8mpPFRdtzNkde1Vq0hbHC3WyAOHylXRQy5dNW08xNPKK2brnLpWNfDA6GMPY+HzF8AV32d+ukaHW7mt2g0xFJROnyKJZdRumDPv8ARQ0uuvQEVBcCns2vLqqIXCl1DEwlLeIyQg8lngO47C63bFW3G3UUXyoe4x075muqfLyS0O6WiEv6cZDZOXZAB4Xrfoz4tXhfmuGmdG6y1NXG/Op4I6y709tlhsZrC1rZ3QCad9yjpTN1GN81IQ2Ih0jm/Vj48/lUqny5MCfFfhzoMh6JNhymlsSYkuM4piRGksOpQ4y+w8g0LQoiUlRGRlkcIzRTU80tPPE5k7HFrmuBBa5pwWkHkEEEEHkHuvUa3XG33i30F3tVdFU2uqhZNDNE5r4pYpWh8csb2ktfG9hDmOaS1zSCCQcqeiReW3d68xbKzFYo0TYtvoAThTseH6P3+AIpdmH4d2e/tBV7qQbhY7N8AndZJQv9vr9w/VU7ocLtx8vDyD8k9lwLhY7Pf65cgTKwHYfPY8Y+vyDA9DyiiH4fh6yGfZUUHJic9se7u578zBVwq9Ki89vl+wJwqxLi89u/s9d4JxhVabF57fXuBUVSmx+e3gXaCqqtMYyR4Lf0ewJ+qqE+PzPHf2b+sgn5KozGcke3YZf39wqq9lUpjP4ix3+s+Ip+ZVFUJjWDMsbHkvAgTlVWY1+Isdh7eiBFVJbf4sf5TPGPXgCd1V5je+fd8QT7KsS2/wARe/v/ACBFXJKOZd5Z/sCFV2Sn8y9e4EUDITz8D39e8EUI8nGS9YMPZP1UQ8ndXhuCBRbydvI/qHuiiny28wRVyYgj4uZ9vjnf4foAz7cqvsosFRARARARARc8aQ9Ekx5cdZtvxX2pDDhc0PMuJcbWXilaSMa45HRSMlYcPaQR+YOQsikqp6Gqpq2meW1MMjXsI9HMIc0/oQCvWLaFfTc9pWvcqOHguG3aJXEcP4eGrUyNPTw/7cSNh3/obqK2goqwHiWFj/8AWaHf719s2hNQs1jojRuro8eXdbVSVgx2xU08cwx9sP4Vg4xddWcd+VuvpWnGLLqs+6r0rTiFl1UfdV6Vt4hZNT91XC04hbNR91XC04haNT91XC04hbdUj3TpWnELRqu/Kr0rTiFs1OPVV6VpxC26pTC/NdZaOi4tI9T6Etsnv5rp/d8JtG//AG71AnpjKTjB8bcjhUXiRCGv2Kyy3aldyH00jf4scB/euI/EBp5mq9it5dNvh6zW6XukTR/nvophGRj1a/pcPuAvLOOnq+LtARARARARARARARARARARTtrUZy4rmty32iUp2u12kUZtKc8SnKpUI8JBJxvxGp/YaJZBFFJKezWk/wABlRV9uTLNZLzd5CAylpZZj+UUbnn/AJq/p6QYkanQodPhtJYhwIseHFZT+FmNFaQww0n/AGttIIi8h0+dUlznPc76icn9V8O9VUTVlTUVlS8uqJXue4nuXOJc4/qSSsriFRP91YwteIaxUfdUwnENYqPumFrxC4Kn7qmFrxC4Ko/2lTpWvELnzSdK14hcFUPRU6VrxC4Kr0BVOla8QuCq44cnSteIXRVY4yqdK+Avam3m7Y3s+elNWmH1R3ZmmzlopcSvgWab+rlGsV1tKiMjy6xcSk7dhjculnfM3+2Rf+M6v9Rpf/0V208CWm49U+LvYm2SxB8cd6FXgjI/4BBNXAn8nU4P6L+eyOe19eyAiAiAiAiAimIidy23Ii5Yxn5c9wPp7J2VhYLBf3BFLMp2L4gik2C3L1zME7qYYLdPhv8AMEwpyMnPDy3PPrxIFXhT0ZOcbcz9EQKiscZHdvyL4/kCKyxE54dvl8QRWeGjbPrcEVpht7pLwzj3AitsNH3iyWxY+IIr1b02fR6lTqxSpkinVSlTYtTptQiOrYlwKhBfblQ5kV5syWzIiyGUrQojI0qSRluLsE01NNFUQSFlRG4Oa4HBa5py0g+hB5HssC6Wy33u23GzXejjqbVVwSQzRSNDo5YZWFkkb2nhzHscWuaeCCQe69G2hftW9Ib/ALFTY/Sott6mVeTTCotx1mLbSbrsG8oLsco0yVVreiMyqhTHp6TM5ENuFLiKyo21ISpLKO3WmN/dOXW1/wBF68ozHO5nRI8R+bBM0jBLowC5pdz1MDHN9iAQ0fOxvp8I7ejQGvf8OvCZqVlXaIqr5mjp5K35C7W2Vry+NkFXI6OGZsRAEVS6pgnHAkY9wMz/AKQ6KNkdA+8r2r9+9GvS85M+z345u39Opt7t29TaxUkyDag2xCv+pmuBWEQzW6o4VOZKOwtBKWg3G0q3ht/a9prndKy8aIsXVNTEZqHNnEbXuzhsQqHfS/pyT5cbelpGSMgHrj4v9dfEH0NoPTm3Pij3X8q23qN3TaIp7W6snpoSzMtdLaIf21MZQ1jRVVkvnSteWsf5cjm/nXtPekWVp2fB0Eteepu4r8jtVO9norqScptkNvrTHpDi0GTjMi6KhH++RGRnBjuoWRokJztrxA65/oy1RaPt82K+saHTkfuwA8MPqDK4cj/JtcDw8Z5v+ED4V/8ADnX1f4j9YWwO0ppqV0FqbI09M92cwF9Q0EdL2W+F+Wk9quaGSM+ZTOx8s+zU6O3/ABC1Ed1duSD1ln6Yy2FUVDyT6isX8ptEmnJTthxq1o60TXPvJNMlcTZSDcIuNthND/07fX6ouEObXb3Dy89n1Pdv6QjEh7fWY+4yF3Y+Ld4p/wDFVtTT7FaRuPRrvWEDvm3MP10tmDjHPn2dcZA6kZwQadlbnof5bj2W9MfpXI6P1Ep9uWo3CqOp10xXJVNamET8O2qMh1UddwVGISkqlOyZDbjMJlRpbcdbcWszQ0bbvOm7W57dB0UFDbGsk1HUtJYHctijBx5r2+uTlsbeASHE5DS13lN8PPwKTeLDU901VreWpo9mLJM2OpfF9EtfVuaHihp5SCIxGwslq5QHPjjkijYA+dssXxtpl7R3UClW5c8TUWiwLzuP7Kh+zapDixaGyVQcfbZehXIxT+ojuUxiOs32lxmUPqW2bSzMnUus8N6e8RV7pLfc4tQ0LKu4dOad7WiMdRIBbMG4HQB9QLGhxI6T+IOb6T7wfBm2u1FrDQ9ds9qmq07o8zll4p55JK14gaxzmTW58wc8VD5AIZGVMroWtkbOwAwuhn+H7luWt35dNcvG5HmJNduKoPVOpvx4seEwuS+ZZ6qLFbbZaQlKSSWC4jxlRqUZmfAN5u9bf7pXXi4va6tqJC95a0NGT7NGAMDA9/UknJXrvtrt3pbabQeldt9F0ssOlrPSMpqZkksk0gYzJ+uWVznuc5xLjyGtJ6WNYwNaN8SNnB4222/btEYt8KyxY34du73GH80U/HicsF3fl3EH6oppmJy2AJ+akW4e3LyD1wiykw8Fy39fQE9lqcPt4eXaCeyxnIm3Lw3+geqKOeibHt6+geyooaRE54Lv+ePmGVUKBkxtj2+XZg/DvIDhFW5cbntuCfyVWmRue37eYdkyqpNjluWO/wBfMEVRnMH97bmCfkqhLZwai5Z8gVFUp7H4tu/1v5AqqmTWsGosbHnyzggRVCc1gzPHI/7h9kVRntfix3/kKISqjMb7fPs7xVFU5rf3leOS9fAPZPRVWYjZRd2fXuyCdlWJaN/MvXzBFWZKefPt9fMEVclIxxbeXxD9U+6gZKfxeJZAIPZQUhPPy9fDAD7Iod9O/mQIot0ufkCKJeIuE/ME/NQMxPie+S/t55AfcBD2xhQaiwoyPvPmH5dk49Oy0BEBEBEBEBF6VuiJXl3B0bNIZ7jhurYtZNFNRq4lYtyfOt9CTMzM8obphF7h280PcTPpGxPc7JEAZ/qEs/6K+uvwKahk1T4R9irnLL1yR2YUmScn/gE81CAfybTgfovo7jG5jVgZC7adK04haNWq9K04haNUe2VXpWnELRqTyOpVwtOIW3VPrlMLTiFs1A9Cq4WnENBqPuq4WnELZqfumE4ho+Z+6rhacQ0Go9MphcMhpqUw9GfSTjEhpxh5B8ltOoNtxB+CkKMhafMHtc134SMKzVUsNZTVFHUxh1PLG5jwexa4Frh+oJC8nVXgqplWqdNWRkun1GbBUR8yVEkux1Eed8kbY6lzx+VNNF/ZcR/A4Xw6322vs17vFokBElLVSwnPvFI5h/5qjhaUUgIgIgIgIgIgIgIgIgIvonohW6V29K/oy2wpKVN17X7R+lvkv8P2aZqBb7Mk1bH91MdSjPwIRF/m+WsV6qPVlJM7+EbiuHfEPeP8H9gt7r4CQ+k0ld5W479TKCoLcf8AKAX9IziHTfz+3K+MHCcQ1iowe6YWvEKif7phOIaxUcd1TC14hq+YxjlMJxDUKj7qmFrxC4KjvymFrxDUKjODnhUwnENYqfumFrxC4Knj8Spha8Q1iq9ynSul/wBvTc66H0AavSkOGhN56s6a206kjx1yIsirXgTZl2kTlqJXj/aOQttT8xqQH/JwPd/zWf8ASXpR8KKxtuni1t1e5mTbdP3GpB9i5sVHn+FUR+q8Qg7BL6fEBEBEBEBFqXMvMuX7bio79kU7ET8sY7T5b89xThPQjKnmS2LzIgRSzRfT6gilGC38MYBApiOnfPcREBKKejFy8C9bgin4qd08ti9d4fmn5qxxU5x4nnvBFZoifyL8gRWqGj8O3PcEVqgo3z4/IuX0BFbISNiPHP8APyBPRXCAjYvLPv8A1BFcKe3yMy549dofZP1X1d0e+ktrD0dqwVR04ud6PS5UlEisWhVetqNoV4ySSDOo0ZTzSW5RtoJJSoy48xKS4UuknJHvHSOvNTaKqfPsdeRTuOXwvy6GT/SZkYOP32lrx26sLrb4iPCbsh4n7H/Re6Wk2SXeKIsprlT9MFypBkuAhqel3VH1EkwTsmpySXGIuw4ZF13ldOvOrFUvG6qlTmbi1BuZjr5VRnpp9Borc59iBTYX2+qSVN0y3aBBJphC33TSxFYI1KPBmeBdrrcNY6kqLlcKiNtbW1Ay5zumOMOIa0FzjhkcbcNBccNY3k+q3ft7oDR/ht2VtOidHWirl0vpm0SFsUEJnrKt0THzzyCGBgdUVtZN5krmxR5lqJSGNGQB6eLHomnXRZ6PcBluoRv6NsG1F1qq1+P9nNdyTn2/ts6rMqQ4bcydctTkkmI2Ti+I3WWWzNJIId/7VS2LbjQ8TRO0WmhpjI+QY/auI6nPGPxOlefoGTnqaxvGF8hWv77ut40fFLcJXWuZ24Gqb42kpqN/WBQxNd5MNM7qGYoaCnZmpeWt6RFNUSgOLyvO3qXqNcGsGoVy6h3K5mo3FUFyG4iHHFx6XTmiJimUeEbhqMolMgtoaR2q4TWrKlKM/P7VGo6/Vd9uN+uB/bzyEhuSQxg4ZG3P7rGgD78k8kr6+th9mdK+H/afRu0+j4h/RVqpWsfKWtbJVVDvrqaubp7y1EznyO9GgiNuGMaBFwmeW3d3Db65e/mrdCY/Dgvp9OWQT81a4cf8O3cCKzRY/Lv55x9fIP5J+asUaNsW3r+wIpxiMWOXcCKUai8j4fl7xTuqrMTE8D9fQVVEOJguQfzRYrkTnkvRfAUVVFvxcdnf67CFVRQsmNz25B/JFXZcfnt+geyKtS2OfL8vRBhOCqpMY57d/u/QEVRnM8/0BFT5zP4tuz0ZZBPXuqZPZIs7d5+IIqlObyRnjs+nMEVKnt89j+H07A/miqE9vJGff6Pw3BOwVOnIyk/h7y+gJ6qnzUbKLu7u4EH3VSnI5njx8Pj5gn81VZiN1duSyHsndVWWnY/BQIqzLTuryBFXJSd/Mj+gIq/IT28+ZeH1BFAyCx8TL1ncE9FDPlj6fP3AiinS/MjD8kUU6X4i9fmCqoKWX1x29njgOFRQDn41ef7B+iFbARARARARARd+fs7asqf0cYUNSuIqFeV00pBZ/Al52HW+Eu7K6wZ+8dhNua3/APBmKIu/BNI3+J6v+kvp7+FFeXXbwlW+hc/ItuoLjTD7BzoqzH8aon9V90cQ3w6q4PK9KulOIaDV/flV6VpxC0av1yq9K04hpNTj14TpWnELXzY91XpWnENJquMkp0pxC38191XpWnENDqnPdOlacQ0GozxnhVwnENJqOeEwtOIaTUduVXC8vGtVM/k2sWq1JIiJNO1GvWI2RcuqYuSpIaMvA2yIdc7xH5V2ucfoJ5P+ccL4t/ERZ/8AB7f7e+xgAMpNXXeJuO3Sy4VDW4/5IC/MhHLhxARARARARARARARARARfbvs2aZ/Nunp0UYvDxdVrNaVTxzx/JZSqzxf+D7Bn3Da2t5fJ0jqKTP8A3o8f6w6f966w+NKt/o/wo7+T5x1aaq4v9s3yf7/Mwv6GXEOmAnx6r5AcJxDV8wPUpha8QqKj7phOIavmPuqYTiFRUJha8Q1Co+6YTiGr5n7pha8Q1fMH3VMJxDUKnHqmFrxDWKj7phOIavmvuqdK89X8RZcLkbo0aG2ul1SW61riutuNErBO/wBOWHdEJBqT/mJtVze4zHMOzv7a8XWf+zTdP+s9p/6K9fPg52dk29m6V8cwF9NpcQg+3zFdTPP8flv7l5AR2EX0RoCICICICLe2WVpLbn2+ufd4gisEUtsdx5+PmXgH3RTjOxJ/sGM+qKVbL8iBApZgu3HM8AimI5fUgRT0cvyL6CqKwxS3z3Fj5CiKyRE7p8CIwKKzRC2SXeYIrXDTuXkH6IrVBTsXZt9e/wCIIrdDT+Av39cwCK4wk7J9xfmH2TsFcYKMJI/d7+QIrlT2/wAPlv8A3BByrpDRgi23My8g/ki/a4OpWoB2IvTFd3Vx2wHanDrB2q9MW/Smp8IpBx3IrT3WLhsEuSpxbLKkMOukhxaFLbQpM1/hHfTZXacddJjYzI1/kl2WBzc4wDktGTktaQ0uw4guaCOMRsxtUNzYN5o9BW1m6UdHLSi5MiDKl0M3R5gkczpbK8tYI2zSNfNHEZIo5GxyyMdwwWvw7euzcQvZcnK5QWvw7d3f5gM/omFcITXLbfbPIP5IrbCa2SZFyD1KfZWiIzsW2eXYCfyVljMbFt69fEPZPup6PHzjbu3BFMMx89nyD7Is1MX/AG593y3D9UW5UUueP3BFhOxsZ9bhx+id1ESI+M7d/wAvDxyCBQUljY9vXPl4B7J+irctjnsGSnr2VWmMlv2/n9Q9EVUnNc9u3145BP0VPnNY4vf8T7gRU2e3z8A909VS57fP3+i8ATsqdMb2UXmfrOA+6Kl1Fv8AF7/z5e8P5IqbNR91XgZkH6IqdMTsrz+v1AoqdNTurbsz8PoCKozU/dPblkE+5VUlp5e8sgiqkxP4/wBvL3gn3VYll8y/IEVblFyP4h6Iq9JLn5gqKBkl+LwMFVQr5bH4GZ/IE/kop0vqCKKdLdXl29gcIoSWnJH3lv37b5z2bEHP6JhV17/tD93f5dvkGMIuIEQEQEQEQEXdV7Mqpqd0rv8ApJrM0wL/ACnpQZ7I/mlvUlhRkXYSjpXyHLe39UWWusi6uBPn+LG/+qvop+Dbdn1Gye6diL8tpdUicD2+ZoaZh/j8t/cuyniG+/mzjuvYPpWnENBqv87hOlOIaTUj3TpWnENBqT7quFpxDSanvlMJxDT8z91XC04hpNQUwnENJqPumE4hp+Y+6rhacQoahMJxDSajPcpheazpRxfsfSH1gaxjjvmsy8f/AH86U7P/AIvtGRwrfv8A+s3E+8hP8eV8ePjWof6O8WO/9PjHVqarl/2z/O/v8zK/BBELq8gIgIgIgIgIgIgIgIgIuxj2SkEqh7RPoxMGni6u6rmnY8aXp3eNTJX/AITiZ9w2JubL5OhdRPz/AMU0f60rG/7103+IHVGj8He+EoOM0FMz/a3Cjj/v6178eIdJ/PXyYYTiFfPTCcQr5/Kpha8Qr8wfdMJxCvzA90wnEKip+6YTiGr5j7pha8QqKk+6phOIavmfunSnEK/NfdOla8QqKrPqnSvM/wDxH89SLO6J9M4vuy7m1dnGnOxqp1L0/jpVj/aVUP4jn/Yp3mz6lf8A2WQD+Jl/9Ve2PwZaQO1Hv7W4+qOitLP9pLXu/wDoh/BeVMdiF70ICICICICLka/GX647S+IJjKsMTkW/aXhz9+TPYV9B7pnj9FOs80+X5indFKNc/f67ARS0cti8wRTMYuXmR+s7AeyKfjFy8VF9fAE/RWGKX1/QPuqKyRC3935fQFVWeGX4C9foCK1wy+hdngW2feH6orZCLYvcQfoit0Mt0+BfmCK5Qi/AXo/23BFcoSSIiLx9bh7J+qudPR+H3f2BFc4SfwAnCudPRy9ei2BPZXWAgtvXnz8QTHsrpBR+Ev7BhP0VwhN8hUorfDRy9wonCtURv8Px7uzsBO6s8RrOA7JwrFHZLbby2+QqinY8fix2F2n3Fk9hRB65Um3F5YTnJH3Y+mS5CvflVHst6oxEW6Mdmx7ltnnz5DThU7qLfj4Llz5YLu58tyFUwoSSzz28PIEVelNc8F+vLy7A/NFWJjfPbv8AWQRVWajGdg/kiqM5HP5Y+AIqfORz2+HPy2BFTJ6fxeHyBPsqVPR+Lbff3c/cCcKlzU7q8vLtD7p/JU2oJL73hktvRcwT81Spqdl8y9/7eAJ7qnTS3V5Z8feCeypk0i4jx4/qH5p6YVSmp/F6zkE9lUZhfIz+AJwqrMLdXfv8jBPZViWX6fME+6rUstj8z/v7wRV6SX4v09dwd0UBJL8XxBFCv8leQIoh7t9wIot7mYIoWUWCPfv9Z8w7+nKoq4+X3/38tsciwCquAEQEQEQEQEXb17MGSZ0TWGLnZmq2ZIIu45MS42zP3lEL4Df+jJ/Lgr25/eaf7ivfT4LVUZNOeIGhz9MddaH/AO0iuDf/AKIfwXahxDeZqvuvb7pTiFPmvunSnENJqvunSnEKGp+6YWnENJqinSnENPzP3VelOIPmfunStOIU+ZPumE4hp+Y+6YTiFPmcJhacQoajtyq4XnS6ZDH2bpMarNkWOKr0qR/+N21RJRn7zeyOML6c3WsPuR/zQvkZ+IXSCi8ZW+cIGM3Cmk/2tuo5f7+vK+ZRErpigIgIgIgIgIgIgIgIgIuzr2NjRPe0l6NqFFkkv6rve+Pobqa+k/cpshxtu8/y9u9RO+0H99TCP966P/EgkMfgu3ocO5bah/rXu2t/3r3k8Q6OCfPqvlMwnEK/MD3TCcQeeOUwnEHzHH3TCcQCox6pha8Q1Cp+6phOIU+Z+6YTiFfmPumE4gFR90wnEHzI90wnEK/NYI5TpXmE/iRHv+W6HDOfxv8ASBdx/wDQm9FEZ93XDsp4eZfNOr/t8r/f8yvcX4MMf7bxHSY7NsA/ibyf+ivLsOyi9z0BEBEBEBFys/jx3kHsisMTkXif6AePyT24U4yW6fDfBB3RSrXZ5gilY/IgRTUfkReuQeyKfjf5fP8AXzBFYYnL3/mCKyxfgCc+6tEPmny/MEVqh/PJF9A/kitsItklz3/LbmQIrdCIuJPkXrAIrlCL8Hl6MvgCdvzVyhFsnz8/yBPdXWnl+H12Aq/zVzhFukvD9wVP5K6QC/Dy/Ps3BFdoBcvdzL1kP5p9lc4JcuXZ2AiuUEvw/H17g9kVvhFsXrw9wcKqtkNP4Q/kqK0RElt2/r/cE5Vliozjx7/gCKxxUZJJd+TPs2I8d4JjkqdZaIyxyxjt9GK9k9yO61dZwW5evzFPuihZLafvJ23LOOwjLPuwHbPsqfZVySksGfbv8hTnKqq5LTsfh68iFfyVFVpifxfsXIOVVVSaX4vefZnmfuD0+yKozS58u8vp7hRFTZxF97v3Ff1T0VMnlsYe/CFUmeX4vy27wRUqaX3j5doIqbUO3137fAPdO6pM0t1eu8FT9VTppbq9+/68g+yqqbO/F37/AK/UEVQm/wCf8/eQoiqMwjwfmfu5Cv6oqrM2M/0BMqrS+XvP6gircv8ACrwP8vcCKuSf83r1sCqoKR/m8vn6MFRQj/I/L0YIoh78gRRb3MPuihJZZSfu7fEvdgM4RV2R+IvD5+7wwHP6Jjv7rHBEBEBEBEBF2y+zAc/wtbUZ/C5pwvH/AH032X/9g3TpubyxVjPq3/pL3g+Ci7qh8Skfs7T5/iL0P+iu13iG5vm/uvdfpTiFPmR78phOIPmfumE4hQ1P3VcJxAKn7phOIU+ZPoUwnEKGp+6YWnEKfMfdMJxB8x3TCcQfMcd0wnEKGfKYXnr6byCR0odUCLtcs9fvcsC1XD+JqGyLu7quNQ736f8AmtXyYfEriEXjZ3sYBwXWk/61jtjv96+UhGrosgIgIgIgIgIgIgIgIgIuzn2NzxMe0j6Nq1Hgjf1XZ98jQ3UyOkvep0hxhvMcba6lPsID/CqhXSD4j0Zl8F+9DQOem1H/AFb3bXf7l7xOIdCPmPUFfKfhOIPPTCcQfMd+UwnEKfMY4TCcQCp+6YTiFBU/dMJxB8z35TCcQGp+/KYTiD5k+6dKcQp81jPPCdKcQfNJ0rzD/wASA3xRuhy9jZt/pANZ7uub0VVj39QO0fhql806057fKf8A/UvcP4Mj+mbxGx+7bCf4G8j/AKS8vQ7TL3MQEQEQEQEXKz+Mj8fHuPwD3Ce6sUTkXrP6h24TjKm2eafiCKVa7PMEUsxyT5+uwEU1Gx93z+Ycop6N/l8+36bgisUQyx4kZfUFRWOJ3euQKvurRDPdH5gitcTt9dvyBO6tsLknzL8wRW+Ee6fcfrmCorlD5pBVVxhck+uz5bAnCutPPPDv3GCdlc4Z7p8gRXSAf4fW4J7K7QD2T5Z+e3cHHumcq6QT/DuH6IFcoJ8v394IrdCP8J+BevAxX9U7K2Qz5e7xz+YoitMQ9i/Tt+uwfzQ+iskVRbH5cu8EVkjLL7pnjYsHjs+fM8e8VxnCpl3dTrLhFvn+/wBBRV/kt7rpH29hgnp2UJJWRmo+4jLPrABUKrUpRb93b9eYfzVcKuS1c/XP3gnCq8w+fv8A7Aiqc0+YJ6qozjwStu/9PcHqn6KmzjLf3gnsqXUD5+vrgEVKnn+IvMEVKmn94/f2/kCe6ptQP8Xj8PmYIqVNP8fkHtwn6qmTj3Vz5HyBFTpx7n+XxBPv6KoTD/H6z8OzIIcBVGYeyv8AveveCKqzD+8fv9H5AqqrS+R+Z/UUVFW5ZnhXmf02+Qqnqq7K/wA3l+oIoGRzV5fUEUI9yV5AiiHe3yIEUW/z9xh9/RP5qGlcjzy7vp3ciAeiFVuQW5HnIZ9E/RY4IgIgIgIgIu2H2YhcLWti/wDU5pyj/wBRN9H/AP5BL2yURCY57kf3Z/617x/BPj/Y+JWTHd2nx/AXs/712s8QlPmwPVe7XSnEHzX3TpTiFfmvunSteIBU8YymE4g+Z/zkwnENXzPHdMJxB8yD6phOIPmPumE4g+YTCcQfMDtlMJxCvzI91TC89/TccJzpQaoKLfDlno97dg2q2fwNA25Xu66uV35fyC+S34lsgl8bW9rh6OtI/wBWx2xv+5fKgw10VQEQEQEQEQEQEQEQEQEXYp7JmcVO9of0ZJBq4esuq5oOeW9U08vCmkn/AMRy8e8cZ7xM69tNVj2hYf8AVmjd/uXTrx/0prPB9vdCBnFBTP8A9lcKST+7oyvfHkedfn8918nOE4g89MJxDT8xgDlMJxCnzHOepVwtOIU+Y57phOIUNR3CYTiD5jIxnlMJxChqPumFpxDT8yfdVwnEKfMlMJxChqCmF5qf4jWEp2z+ilUsfdiXLq3BNWOSqjS7AkJTn/cVLP4DtZ4XagOq9Zw+pjpj/B04/wCkvar4NlSGai37os/VJRWl/wDs5a9v/wBKF5YB2+Xu+gIgIgIgIuVr8Zet+8PyT9VYIhkRFzyXPt5cu4PdOM8KdZPdPl67wRSrXZ5gilY57F5gimYx/h8DBUU/H7PP9QCqrDFPn6+AIrJEPfzL8viCYVohnug/XZ5gitUM/wAvyD1RWyEf3U+ZevmCfmrdCPBp8iL6+Ycp6q5Qlfg9fqBVArlCVsnzLtBFc6er8OC9cw91XlXSGr8B8+zb3BlFc4Cvw+Resl4giu0BfLfy5e/yD9E7q5wF8j8v2+QFP5q4wV8vX7gndW6G5yz4es94cJ/JWuI5yx4dwfmis8V3l8dg7KiscZ0sFvn9QVVOxnyLHIyPGfAvRhymDx7qVbk4LYywXPOx/EPyVB7eq1XKMy/ERF/tPJn+nIP1WrH2UY/IIyMsljt3Pf34IwVCoKS7nPj8foK/qirspwt/PPruFPZFWZjnPfv/ALfIE7qqzV8+Xh8/PcMJ+iqM1fP393b8cnsCKnTl89z5evAVRUyev8XkYp6oqVPV+L3gn8lS5qvvKPwP6giptQV+L54/X3AipUxX4/gCKmzTyavIOyKmzjLJ+/6AiqUw9l/AEVRmH2d5n9QRVaYf3lHnsP1uCKrSz28Ofz7CBFWpZ5I/Ps8P0BFXpJ/iBPzUDJP8XkCKEfPY/XYZh7ooh3t9wIot78R+QIoWX+FXru8+YeqYVdkH94v0359/5An68rHBEBEBEBEBF22+zMZNFE1elY2eqlnRyPvONEuJwy932sviLjZfLaOe5P8AuXv78FKjLdN+ISux9MldaI8/+TiuDv8A6UfxXaLxDWKn7r3GwteIavmu3KdKcQr8zn1VOlOIavmfY8phOIBU/dMLXiGoVOfVMJxCvzH3TCcQqKng/UqYTiFfmT7phOIBOmFrxDUZx7pheeDphyPtPSU1UcI88NXpUf8A/FLaosUy9xs4GDMeqVx/+7svkO+IdVCs8Zu+swOem4U0f+yt1HF/d0YXzSLS6XoCICICICICICICICICL7Z9m/U/5T07uirK4uHrdZLTpueX/v1KVR+H/wAf2/HvGwd04vN261m32t8rv9RvV/uXWXxm0X9IeFXfmDGenTdVJ/sWib+7oyv6DOR5jmUe6+RLCZFPOTCZFDMPdMLTI0+cmE4hTzwq4TiGnzx7phacQp8wEwnENJqB6FMJxChqfuq4WnELZqR7phOIU+a+6dK8/H8Q5QXJfRt0QuZLZqRRdbl0Vx0k5Jr+orFuaYglK/yk4q2veZDtB4V68HVupKEu5fbuv/ZzRt/+kXrl8H66sp96Nz7IXgOqdMCYD3+XrqZh/h8yvIuO8y+hNARARARARb2/xp8/38QRT8Q/PO2fy/MD7Ip1k/w9oIpZo/qR5/uCKVYP5H+YIpmOf19dgIp2Of5bfAEViinvjw9317wRWOIr7yfEvRB2RWeIf4fME/krXDVy8SL44BU9Va4Sti+PZ5n8wVfdW2Gr8B+7t9ECK4wlbJ9x/UEVygrynGfHHkGPVFcKev8ADv78b+eQT1V1hOfdTvyMPyTurjAc5b+v7giukBzlvjl63FUVzgu8hRU7+vKuEJ0tt+4Puq/zVthu8t+71zBFaYjxFjfsBFZYr/I8/Pl+4Ip+M+W2+wJ+qmGZHLfl2dwJ+qzkyfHw9YyATstVSfHu3L5An2WG7J8QQqHkSOe/1Pt/IE49FAyn+fv9/duCeirct7nv6+ofkmFVpjvPwBPRVOa6WD3M/p+oIqfNd/Fy9wKvKps9z8XrsBU9VS57n4jLx8O8URU6Yv8AFy7vD1uKoqXUF7K3x54/QgT+aps1f3VH4mf5B9kVOmK/GfrYM4RU2ardR+B/Pz8AHdFUZitjPxM/yAp3VUlq/M/z5+8EVUlq/Gf7eO/uFUVYlnv7jFEx6qtyz8fEE9lXZJ/i8TBFBST/ABfDzD7IoV89j8Tx8ARRLp/UEUU8e59mwZQKElnkvf8At7iD9E7cKuvHlZ7bZPfvPBZ+AYx+aep5XECICICICICLuS9m3AUzphflVNJkmdfZQUrxsr+W0ClPmRH2kk6p8xH1k3lvjb9s/wC7/cvo7+DDZnwbH7rX5zCGVWqhAD7/AC1BSvP8Pmv712McQxBVL2N6VrxDWKrtynSnENXzP3TpWvENXzP3VMJxDUKn2KYWvENQqPumE4hqFQPdUwteIahUfdMJxDUKj/OTCcQqJ/umFrxCvn+uVTC843SdlfbOkDq49nPBe1Xi5/8AvF0oWPd9nwMknOD9h/JfG94267+kPFr4gp856dTVcX+wf5P93l4X4SKLq2gIgIgIgIgIgIgIgIgIvoXoj3D/AEn0qujVcxqSluha96RVR41fh+zw7+oD0klbl91UdKiPwMbX1xTGs0Xq6kA+qW2VTR+boHgf3riDxB2f/CDYbeqyAEvqtJ3aIY79T6CcN/8AOIX9GjiHkyahfG3hacQ0mf7phOIafmPumE4hpNR91XC04hpNR90wtOIaDP35TCcQ0+efdVwtOIafPPumE4hpM/3TC04hoM/3VcJxDQZ/XqTC6bvbrW6uudAyrVNLZrTZ2q2nFyOKIs9UiVIqtok4fcRuXUlH/iHYbwwXJsG6MNMXc1NBURj7kdEv8oif0Xo98LC8Ntfiut9E5+DcrBcacfctbFV4/hSk/ovFQPR9fTEgIgIgIgItyTwoj/X8t8gndTkRXmR8sfPvxkP14VfRT7R7EYKilWj5H4fQPRFKsHz8v07OYIpiOfP3dgcp/NTsdXzIvkCcqwRVYNJ95eveCFWOKrHD549fAEVmiK8vX9wQK1Q1fhP3fvy3BFa4K98e74gndWyEvYu8jL9w/kiuEBeSIvD6AqK4QHC+6C1d1b4DmMb9oKiu0BwjIvH9uYIrdBe5ZP3eO4fkiuUF4tvXxDCK4wnjLh39eQKit8J/l69GHuq+ytUN/lv3dvl594eyK0RZHLfux3/UE9+VYo0nkefX9g4TGVNx5XLcEKlW5Wxb8se/9w4zyn2WYmV4/PxLcOMJ7Icrx9b/AED8lQfmsZyX4+vj4AqqLflbHv7vkH5IoSTJLfcvXgHdFXZT/Pfv+AKnZVmY/wA9/XzBV7KqTX+e+e0+wPzRVGc9z37D9c88gT3VOnPfi32/T9w/mnp91TZzv4t/l7vEE/mqjOcwRlnvz7wT9VSp7m6uwPblFT57mCPfsxz8fqCoqfOXhJ79/rv5Aq+/Cp0xeyj7z8uQfZFU5q8Fju2+IIqpLV+LHYR/Ew/NPv6qqy1bK8TPAfkn81WZSt1eHn8QRVyUrcy7v0D2RV6Qry5mYJyoGQZ/E+/13Aih5B/MwRRTp7+89wT81FPH+LcFVQcs8kfy/TIeq0+3uq84eVnvnu57eG4Ywq/yWwEQEQEQEQEXeZ0A6cqn9HyHKNPCVau65qmk/wDWlpyJRuIu/CqSZe4bXu9T01fRn8LAP5n/AHr6l/hJWJ1q8IVur3MwLnqG5VIPuGOho8/xpCP0X2vxCMFT916b9K14hcFT91TpWvENYqfRUwnENYqEwteIahUD0KYTiGoTj3VMLXiFwT8fiTCcQCfj8SYWvELgnPuqYTiGoTlMLXiFROQmF5ntZKl/ONXNUaqRkaahqFeUtBly6p64qitoi8CbMhuBn4Gf6I/kvib8Rt5/wi8QW+V9BBZV6vvErcdul9wqHNx/ySF+bjUuGUBEBEBEBEBEBEBEBEBFOWxWF29ctvV9ozS5Q65Saw2pOeJK6ZPjzUGnG+SUxsMergbVUtTSu/DJG5p/JwI/3qLvlubeLLeLS8AsqqWWE/lJG5h/5y/phQpsaow4lQhupeiTozEyK8n8LsaS0h5hxP8AtcaWRl5jxmlL4ZJIZQRIxxBHsQcEfxXxNVNNNR1NRSVDC2oie5jgfRzSWuH6EELJyLXm+uVZwmRoMw90wtMjSZ0wnENJnVcLTiGg1H3TC04hoNR35VcJxDR8x3TC04hoM/3VcLTiGgz/AHTC04hbNRj95Vwvg72nlpO3v0B+k9RWWTkOxNOnLrS2lHGok2JW6Re7riUkRnlpm3lK27CHK2xl4ZbN29DTufgPrPJ/28b4AP1MgC7WeB7UDNMeLHY+5Syhkcl5FKSTgf8ADoZqIA/magBeA8etS+s9ARARARARAT8+ymIavw9uUkW/ftz8zIBynurCyeSBFLMny+HruBFKMKwZfU/7+IIpiOe5fD5ginIyvw+eDyCKejKxjwP694J+iskZXb5GCKyRF7p7jLH5Byis8NexF2lj5fQEVqhuYNPiXzIFUK2w3NyLPP8AYj+YKn3VsgOci7u/IFPzVvgu4UReXzD9UVwhu4NJ52PHxL3B+iK5QH/wlncPuit0R7BkeeePIE/IK3wX+R5+frtD0RW+FI5b45b+jD7IrZDkYxuW31BMq0xJJFjfuDnARWWNK5b7bfkfmCYyp+PL5b7dvkHun3U2zL8S/X6h7IpFuZnG/u7T7fcK+/uiyymePo9z3LkKD7oVoqZ48/Xb5gnZcDkzx88+twT2UY9L57+tuQIFDyJXPf1kwT7KAlSee+MdoJ6Kty5PP731BFV5kgt9/X9wT7KpzZH4t88/XzBAqfOf/F9fifzBFUJj2TPflnPkH6IqlPf/ABesduO0wVVTJrpGZ+Gc/H9QVFUJ7uTPx7uXfkE7qoz3djLPP6AnsqjMX2fEEVSmryo/jn0QIqtMX91W+5nt9A/RFV5St8d24IqzJVnPiYIq5KVuo/d694IoGSrn4ECKCkH2dxeuwE+6h3z38iMEUU6eM+X1BFFOnsff7s+tgQqAlq38N+7J8z7/ANA/6lX0+yg1GZmeefjnO23buB7qi0BEBEBEBEBF6G+itSFUHo96VwVoNtb1tJq5pMuE8V+dMrqFGRkR5UiokfvHG15qg651nPZ3T/qgN/3L7FvAPpmTSng82DtksXRJLZBV4IwcXCeauBP5tqQf1X0DxCPFR25Xb3C14hcE+OMqmFrxDWJyqYWvENYqCmE4hrFR91TC14hcFSffhMLXiGsVH3VMJxDWKgH1TC14hr+Y+6pha8Q1io+6YXE/IbjMPSHlkhmO0486s+SGmkGtxR+CUpMxrbOTwO6x6qogoqWprKl4bTRRue9x7BrQXOP6AEry4VWaqpVSpVFRmap8+ZNUZ8zVKkOPmZ53yZuDfPA4HZfCffrk+83y83iQkyVdXNMc+8sjnn+9ywAUSgIgIgIgIgIgIgIgIgIgIv6M3RUuxu+ujF0eLwakFJO49E9L6s+8RqMzmyrLoq57a+IiV1rM7rELz/mSY8dNwKV1q11rK3FnSIbpVNA/zRM/pP5FuCPsV8bm/Wn36W3w3g06+Lo+T1Pc4mj/ADG1kwjIx6OZ0uH2IX75kbO84+64mwmRpM33VcLTI0GbHqmFpkaDMEwnENBnHuq4WnELZqPuq4WnELZqPumFpxDQZz7quFpxC2Zz+qYWmRbM5x3VcKjan2i3qFprqHYLxo6q+LGu20HTc/7Pq7loFQoq+PP+ThmnnwElYbybLfrJeWkl1JVwzD3/AGUjX/8ARW6dDahfpDWuj9WRg+Za7pSVYx3zTTxzDH3yzhfzaJ8GTTJ02mzWzZmU+XJgy2VfiakxHlsPtqzg8tutmR+Q9xYpGTRRzROzG9oIPuCMg/wX2jUlVBW0tNW0z+qmmja9h92vAc0/qCCsQa1kICICICICKSirL7pFtgyz3d595+AH3z6J349VYo6i2x2kQJ7KWZVt5foCKUaPHb2/IEUuyr8j/IwRTcdX5GHsinY6uXj6/IP5orFFXkkn8fXgQIrHEXsXgYfkn6qzw3Ny32Pfwzy7AQ8K0RHMEW/IwRWuG7sk8lt7+YIrVEdwaTI9jIv2BArfDd2Sfdj167wRW6E7lJF2/HkH3T1VsgyOW/rmHsiuUN8jIi8j5+QeiK0QZGMEZ+YJlW2HJ5bl6IEVrhyuW5dgJ7KzxZXLfu8cgisMaXy39ZLsBFPMS+W4eyKYZmbF97u9eIon5qQbmePd25z8BXsiySm7c/XP3ECd1oc3/cf0+WSBFwLmf7vn7wyiwHZnj68jBPdREiXz39Z8DIAnGB7qDky9j3+YIq7LlFvvj13girMuTzweOZfp34BOVVZskt9w9EVSmyOfr3eYKiq0x/BH3nnIKuVTp8jZW5+ufeCKpTHsEeT9cvzBFUZjpZUecluXruBFUZruVGRc/f7iL3gSAOpx4CqxjpHNjY0l7jgADJJPYAdyT6LnYsO8Ks318SiSSaUSTSuW5Hg8SVfeStCJrzDjiDItjSky38Rx1d92tvbLK+nrNSwuqG5BbEHz8jgguia9oI9QXAjBHddzdvPh7eMXc63U1407shcYLRK0ObLcJKW2BzHDLHsiuE9NPIx4wWvjie0gtcD0kFU647Iu6hNOSKlQ5jUZBcS5TBNzYzaM4JTz8JyQ0wSj/wBZp38RnWHcjRGpZWU9o1FA+qd2jf1RSE+zWStY5x/0QfXHC2puz4K/FHsjb6m87jbN3SmsEIzJV0/k19JG3PD5qiglqYoGnjBndHyQ0gOIC/K5i+zu/L9xvddXFWJa/wAR57fP1zAIq3JX+LwL68wRV2Sr8zBFAyFZ27zBFCPqyZ+ePgCKHePOfPx79w90UY8ex+P0BFFPqwXr6B3RV2UvPF2kXLGeW592SAqvbhRIKiAiAiAiAi5o7DsqQxFYSa3pLzTDKC5rdeWlttJeKlqIgJAGSeFkUdJPX1dLQ0sZfUzSNjY0dy57g1o/UkBenC1aQm3LYtu3kY4KDQaPRk8P4eGl0+PBTw+GGNhwVUVvzFTPP/be538SSvuj0Bphmi9CaK0dHjy7TaKOiGO2KWnjgGPthnCsHEKNn9epbswnELgnPumFrxC4Khw9eVTC14hcFQQqYWvELgqfumFrxC4KgKmFrxC4JwfVUwtcjWJh/aTCZGsSn3VMLXI1iY+6YX57q3WE0DSvUitLc6r+WWLdcxte/wD27NDnKjpTjJ8S3+FJeJjNoXmSspGHsZG/wyM/3LhzxD6hj0lsJvVqZ83lmh0pdZmn/PZQzmMDHq6TpaPuQvNIOSF8SCAiAiAiAiAiAiAiAiAiAiAi923sib2ZvX2fegTqXlOy7Yp91WVUEqxlh62r1uGFBZzk8p/khxFlywSiHlB4jaF1p3g1c0txFO6GZv3EkEZcf9p1j9F8rPxCtMyaZ8XW7MZjDaeumpa2Mj94VNFTvef9t5oP3C7J8jg0zffldLcJkWzMUwtMjQZT2VcLTI0GX7phMjQZR7quFpkWzMPRMLTI0GYquFpkWzMe2UwtMi2Zvuq4WnELZnHuq4WnELRnVcL+fF09NN06TdMnpH2OzFchwYOqtz1ekR3CMjboV2S/6toaUHwpJTZUmtskkyLBkQ9rNnb9/hNtboS8ukD5ZLbCx595IW+TJ/8AORuX13+FHWZ1/wCG/ZnVEk4kqpbDTRTOHrPSs+Unz3wfNgfke6+RxyUuwaAiAiAiAiyoysGZdpmWPHntz8Q5VfRWKMvKSz27Hy558PMD3VMYCmmT3x3gikmj5e8vzBFLMKySfh+gIpmOr8J+7z+oIp2MvYvA+zuME9lYIqy5evAEViiOYx3GRAiskVzx5ftn5mCK0RHfwn3lg+XwBFaILuMEZ+R57A+6K0w3eRdpcvcCK2QXy2LO3IE5VshSOEyLPr3gnfKtcR/Blg+4O6fdW2FJ5b/P1gE/NWuLI/Coj37S7fPzBMKzw5RFgzPuBV7q0RJfLffb8g/JU/NWWNM5b+sd59gIrBHmFtv3e4E9lNszdi3+YKvopVqb/u7j/XIKnqs9E3bmKKvbCyEzS7DL4l+pcxX7Ki1Ob/u92cZ+ODyH5ouBU3b8X543BMrBdm/7viYJ7qKembHv39vxDsmfVQkmZz37/LmCDsVASpnPfOPpyBPRVuXL57/ME7qrTJWc79/M+0VT9VWZMjOTPkR7F+Y0p91VZsnnv9c/kKoqjMf4jPfbc/XMOyKqzn+e/h8vkCfyVTmv4JW/v7+YIv2Cx7KZpzLNYqrBOVV4kusMvoIypqD3bwhWcTFJwalGXE3+EsGSjPpPvBu1V6grarTenqsx6ehcWSPYSDUuHDskf8SDkNaOJPxuyC0N+n/4cfw9tP7P6XsO9W8Ngjq94rjCyopaaoja9llheOqLpjfkf0lIwtfNK4B9Ln5aIMe2aSVdeqlv2vJcp5NyKrUGTJL7EM20sR15LiaflOKwTyUnulCV4MsK4TG39FbLap1jQx3YyRUVqkGWPl6i+QejmRtGegn95xaCOW9QXL/ic+JxsN4b9U1m37KOv1Nr6lcG1VPQGJtPRv4Loamrld0ioa05MMMc7mOBZOYX8KKt3Wq1a1KbgT0SKFKeWTbSpqm3ae4tZklDf21vh6pajPm622j/AHZ2GfqnYfV+naWS40EsVwpIwXOEQc2VoHJd5bvxAezHOd/m45W0thfiyeHTeW/UWjtW0Fdo/UFY9scLrg6GW3yvkPS2L52MgROcTjNVBBDyB5pcelVXVnRqFVoMy4LTiIh1mMyt+TSorZIi1ZtsuJw47CCJMeoE2RmRILheUWDLjVxHO7U7z3C11lJp7VdW6ezyPDGTvOZICeAHOPL4s4BLjmMcglo6VxV4/wD4Z2kNead1DvH4f9Pw2rceigfUVVrpYxHSXVkf1yughYA2nrwwOcwQtEdW8BjoxO/znfCcpfZ3Fn0Q7mL5o8quyl7HvzzvnvBFX5K/xfAv2D9EUFIXueOwsetwRQzytj8PXyIEUQ6rn7z+PiCKMePkXr9gRREhfPu5+4u7vBP0VblKzt3n8858OwAiwgRARARARARfsPR+tkrv1q0zoS2lPR37tpcyY2ks8UCkPfzieStjIknEgryZ7YETfar5Kz3Kpzgthdj/AEnDpb/eQuyfg80KNyPFDsZpKSnMtJNqKlmmaPWno3/OVAPBwPJgfk+y9FvEOv4nC+0nC14hcbP6ZTC14hdE/rlUwt3ELonPuqYTiFxtR91TC3cQuioTCZFwTBUwtci4Jh7qmFrkXBN7FMLXIuCY8cqmFrxDWJznKYXzF0x66ih9HTUNZrND1VjUihxiLm4uqVynMPo5lt9h64z8CE9p0ma7Uw7gBxP6NOP78Lor8SfVMWlfBlvFIZS2pr4aShjA/edVV1MyRv8AsPOJ+wXQIOTl8iaAiAiAiAiAiAiAiAiAiAiAi9dv8Pxf/wDOujJqvp66olSLD1fcq7P3jNSKVe9s0hUZs0meEo/mdtzVFjmazHm94yrV8nrrTd6aMMrLb0H7vglfk/6ksY/QL56fi5aS/oze7QOr4xiG66dEJ44MtFUyhxz6nyqmEfoF32ZHTwzLygwmRoMpTC0yLZl+6rhaZGgyj3TC0yLZmCrhacQtmf8Aiq4WmRaMxTC0yLRm93KuFpkW3TKuFpkWzMq4WmRaM33TC8e3t7dL/wCk+lnaWpEdCihas6X0p6U4bXAlVw2PMkW3PbS4SjJ7q6AdKM9iNPFjlgx6leCzUv8AS22NzsMjv21ruL2gZz+yqGiZp+2ZfO/gvoq+E/rn+n9gNQaLmcPmdP3yUNGc4p61jamM49Mz/ND1BxnvldHA7hr1FQEQEQEQEW9tXCojM8F28/oXMEVgiL2IjwWNy7+eTDj9EU40rkfuBFKNK/UE/VSjCy5d+5Ail2F7479/Ll2cg7gopyOvlk+Zb+fkCKdjOcjz4H+X0BFYYznL4l+YIrHFd/Cedj27gT+av9o0C4LurVOtu1qLVbir9VkIi02j0aDJqVRnSHD+43GiRG3H3Vdp4SeC3MyIRd6vdn05a6y9agulPRWinYXSTTSNjjjaPVz3kNH8eTwOVDah1HYNJ2av1Dqe9U1vsVLGXzVFRIyKKNo7l8jyGge2TkngZPC/dtXdAtVej9Ntan6pW8VAmXbQf59S2mpsWotJablLiy6dJlwXH4SavTlk2qQwhxzqkSGjM/vjYW2G8m3+8NLf6zQV5NZTW2r+XlJY6MklocyRjHhrzDIOoRvc1vU6OQAfSuMdm9/tr9+6PU9w2xv5rqS013ysznRvhJcWB7JmMkDZDTyjrEUjmt63RSgN+jn8+iP/AIDI+7P0I8DlFczq0Q38YwexgqforXDfzg88vp+oKv6q0w5OSLc/08u7kCqVaIcnhMizyLw9x+QKitcOXy37tsiqeiskaTjBke3d+e3MURWSLM5b/v2eYJ7KwxpvLf4B6Jn3U/Hm+Pdn393eH80UyxN8fX12BPzUo1NIv83d289vPsMMpys5E7/d89gRc5T/AB27sgnplDneP9w7oPVcK53+70RfIEWE5O7eL5h9kUY9O57/ADPu8w/VPdQ0iaW+4fpymFBSZpHnf5+uwOydlXZUznuCcKuSZPFnJ7b9oeifyVbmS+e+Ozy/UE4VUmSjPOD8OfrkH807qsTJGCMs79/f4gn8lVZb/wCJXw5+IoPunstbThIrFz06M8klsMrXMfSaeNJoiJN1KHC5Ghx5KUnnbCu3kONt3NQS6c0Fe6ylk6ayVrYIyDggzHpcR92xl7hjnIHbuO7fw8NoLdvT4tNr9OXykE+m7fNLdKtjmlzHx2+MzxRvaMAxy1YpoZA49JZI4EOz0O/YdQrlVbFtyZUdRJnzFFAgHsZtPPIWpyTjJH/yzCFKSeDLrOEjLBjp7tNo6PWesKSirGE2qnaZ5x6OYwgNjz/4x5a0jIPR1kchfR/8QjxI1nho8OWoNS6dqmxa+vEzbXa3ZHVDUVDJHS1YaeT8pTRzSxu6XMFT8uyQdMmD8RTJBrUtalGo1GZqUZ5NSjMzMzM9zM/qPQprWMayONoaxowABgADgAD0A9l8dVRUVFXPPVVU75aqV7nve8lz3ucSXOc52S5ziSXEkkkkklVma4pJqSZGleTIyMsGRlzIyxlJkYq1zXBrmkFpGeOxH2K0yxSwSyQTxuZOxxa5rgQ5rgcFpBwQQRgg8j15X3dpDXSrdhUA3p8eXUYcVyFMbbksvyY6IkuTFglMQhRuNOuQWW1ffIlKI8nnOR567vWM2XXuoBBQyRW6aYSRkscxjjJGx8nlkjpc0SuePpOBjAx2X2J/Do3TZuZ4StoHXTVdHcNaW63SUlZGyqiqKmBlJVVFNR/NsY4yxSyUUVNJiYCRweHOLi7qPXpqOxGg3xeEOGSURo9y1tlltJmaWm2qjISTKd/ws44Szv8Ad33HeHQ1TU1ujNKVdW4mpkt1O5xPdxMTfqP+l+L9eF8snioslm014l9/9P6djbHZaPWN3ihY05bExlfOBC3ucQkGIZJcAzDiTkr8ykuZMzzy5fMbqXAmFAyF/LJ924J+Sg31bY79z7AT+ah31Z27zBPVRbp/MwRRbysmZgmMeqhJa/u7Hg/mZe4g9spz6BV59XErHcXzPn9AT81wgiAiAiAiAi+7/Z+Wr/N9XqxczhH1FoWrLW0rgyRVGvPt0yOk1ZLg4qeUs+0zx5jj7cev+VskVMD9U8wH/JZ9R/8AO6f4r1v+Djt7/hJ4kdSa6nYflNN6fmcw4yBU1720sYJ9M0/zh9ScY7ZXczxDhBs/blfTlhbuIXm1Cpha8Qutn9cqmFrxC6J/uqYWuReE3fKpha5FwTKmFrkXGzZ9Uwtci42btgqmFrkXRMVTC14hrE/35TC14hcE/wB1TC6+vaJ3F9h0stG3EGROXBeSZi9zyqJQqXMN1OO0vtVTYM+7BDfGiW+bW1c/oyLH6ucP9zSvHf4zmsP6J2F260VG7E141MJnc8mKgpZuoY9R5tVAT7EBdOI5KXzXICICICICICICICICICICICL0Dfw+OoxUXXvWzS+RONli+9MabdEOGta+rl1awbiaiJS0gso+0opV5yl5PBm22ruHTDxqWU1Gi9K6hZFl9HcHROP9llTESSfsXwMH5kLyN+Lvo03LafbLXMNL1S2q+SUr3gDLIq+nLzk9+ky0cTf9Jw916z8jzXMy+f8AwtMi2Zj7phaZFt0v3VcLTItmX7quFpkaDL7JhaZFsy/dVwmRbdN91XC25FozJhaZFozH3VcLTItOl+6rhaZFoyquF0Ze3r0jXefRZszVKEy49P0e1FjHNNtk3CZti/oqaFU3nHEnxNITX4VJLOOE87mWCHczwRatFr3KvGmJpAILtQEt5xmalJkYAPX9k6c/ovUj4UO4LdN77ak0JUyNbSajs7ujLsZqaBxniAHqTA+r+/HGV5AR6rL6I0BEBEBEBFqR439ejBFLRHNyPv2z4meMmXuDtkFMqwsKyXo9wRSjKti7y38+8EUm0rGPD6GH5opZleMHy7efxIEU1HXy354MvMP5p6qcjucj5Z+oIp+Is1GkiIzVxEREWTMzM8EREW+4ZwCSeFQkAEk4C+jbq6O+tenWnFsar3zp9XrWse8KqqkUGp1qOUGTIlnCOow1yaU+pFUp0WrQmnXITkhlpMtthxbXEgiUfGem94ttdYayvegtMatpa7U9vgEs8cLutrW9flvDZR+zkdE8tbM2NzjE57Gvw4kDiHSW/m0OvNf6i2x0brqiuWsbVTCeoigd5jGs8zypAyZoMUr4ZCxtQyJ7zA6RjZOlxLR+v9C3pEu9G/XC3L2l8TtpVRJ2tfsZDRvOnatVkxlSp8RtP31TqJKjtTG0pwp3qTayROGY2d4lNnY969q7zpenw3UMH/CqBxPSPmomu6Y3k8Bk7XOhcTw3rEnJYFsDxd7CReIjZbUGjaXDdVUx+ctjy7pb87Ax/RE8ngR1DHPp3k8M8xsuCYwvRx0xNBKZ0ntCZsG3zhT7so8ZN66Y1eM5GcanT0wjeKlszuIm10u7qW4bBKJxLJPKjvqNRMkR+Nnhn3grtht26Ssu4ki07UvNFdIXBwLIy/HmlncS0ko8zHSX9AmibgyErwC8H+/Ny8M2+dDX34TQaUq5TbrzA8ODo4jJ0+c6PuJqGYebjpMhjE8DcGYkeW5on4b70SW07GkRnnI8mNIbWy/HeZWbbzDzThJW0604k0qSoiNKiMj3H0GQzQ1MMNRTytkp5GhzXNIc1zXDLXNI4IIIIIOCOQvqWgqIKuCCqpZ2S00rA9j2ODmva4Atc1wJDmuBBa4Egg5HCsMR8iwWfL65FxXVZ4cnGMn6/LOQRWeLIwZGR7Hj3f3FU4VoiySMi337PXcKJ2Vkhy8YIz9dgIrNFmct+73B7orDHlcsH3dvb4cgRTkebjGT+ePcCcKcYm8t/Dt+vmQZT9VMMztvxev7gnspJqdyPi9YD7J6fdZqJ/Lf35IVRcxT/wDd8M+QpyqYWpz87cRZ9d3MFX0XCuf4/P4gqrDcn+PuPs5h2VFHPTuf3u/18wTHp6qIfnc9/n27h7IoOTN57/PmCcKCkSs5ye3dkCqqAlTC337/AJ/QVVFWZcvOd/h67RTlFW5UnGTM9/p4AnCrEqRxGe+3o8B+ZT0VXmSeZEfkXuBPdWbTCc01dqWHOFS5lOmsMmeMk6k2pajTnt6mKstt8GY4O8QdFPVbfGaEu6Kethkfj+yQ+Ln7dUrT7ZAK9Tvg96mtVh8X8dtuIi+ZvOmLlR0xfjIna+lrz5ZPZ5p6KdvHJY547Eqa13YknSaHMbQZxo82Ww+ojPCHJTLSo5qIuwyjLLPYeC7Rxd4aqyljvepKGR4FXNTRuYOMlsb3B4H+uw4Hcc+i74/G505fq7a/ZPVFHTSP07b73WQ1Tm5LY5aunhNKXgcAOFLUND3YDXFrAcyAH54tGbb0W6KVKudx5FIiSEyHDaj/AGpC32jJUYpTZGa/saXiJTvAhxSkp4eE+IzLshr+n1JV6SvFJpSNjrxLGWDqf0EMdw/yyePMLctZ1OYAT1dQ6QD4p+EW77Kae8Qe3Oo/EBV1MW3FvrW1MnlU/wAyx1REQ6lFXG0+Z8kybplqfKhqZJI4zAICJXSR/U10ab2TqTCKrRHY7MyUjijXDRVMupkGRqLMtttX2afhX3VGrheLh4eNOMDpppjcjXG2tY6z1kUj6OJ2H0lSHDo/8mSOuL3HTmM56uh2cr6W99fBR4WfG5pyLcfTddRU2pa6Lqp9Q2V0MgqCCeayONwp68g5ZIZSyrZ0+V8xF0dI+Nr50+vbTGW1UVrfRETISmBctGkPstpeMuJtCnWlNy6fK4S2JfCSjI+BS8ZHbLSW4OjtyKR9DG1hrCzMlJUNa49I4JAILJWc925IGOprc4Xz4eITwfeJLwUahpNV1c9SzTrKkMotQWieWKMSnLo2OkjdHU0NQQ3IZKGNc5rvIlmDC4fmdHoVfvq4EUikNLqNaqi5cg1SHySbi0Nuy5UmVKeVwo4uAzNaz3UeOZjdl7vdm0fZH3K5SCC1U4YwBrc4BIYxrGN5PoA0DgD2C69bX7YbmeI7dCl0Voqkku2v7xLUVDnTShvW5rZKmonqKiU9LerDnPlkd9UjgCep4zUblodbtmoPUqv0yXSqgxuuNMZU0s0n+FxpR5bfYXzS4hSkKLcjMZlovNqv1FHcbNXxVNE/s5hyM+oI7tcPVrgCPUBbd3D2019tNqat0buTpGus2p6f8cFVGY3Fp/C+M8slieOWSxOfE8cse4cqlvr595n7/WBJrY6hXlZM8duxevEDwqgFxDQMuKwKhHkw5L8SZHfiSozimX40ppyPIYdQeFtvMOpQ604gywaVERkLUE8NTFHUU8rZIHty1zSHNcD2IIJBHsQVm3K2XKy3CstN4t89JdaeQslhmjdFLE9pw5kkbw17HNPBa4Aj1ChXVYz8tviLqwVFPKwX5gigZS9zLfBb48Q55RQijyZn3gi0BEBEBEBEBF3I+z+tBVE0orV1voUiRedyOmyakcJLpVvtHAirSo91pOoPzPDYcA7pXTzb3S29rvpp4QT9nSHJ/wDNDD+q+m/4OG2btL+HjVW4lVE5tZqi+O8vLcdVJbmGnicD6g1MlYPbjjPK+8sjjdsy9dcLXIuib2Kphbsi6JuyYWuRdE33VMLXIutm7cqmFrkXRMVTC14hdE/Kpha5F1s/3TC1yLgm+6pha5F0TFUwtci4JQqYXUH7Ra5Snag2Narb5rboFrSqq+ylSuFmZcNSWyZLT+HrTiURpX/dUQ5a0DDi3VlVjmSXp/RjR/vcV84HxpNbi6bx7U7fw1fXDZ7BLVvYCcMmuNSWEEduow0MLv8ARcPdddo34vGJARARARARARARARARARARARdgvsstTT0r6eHR9rDjzTNPuW6ZGnVT69fVsrj6hUqdakTrFmtCUkxWKnFeI1Hw8TZZHCfiKsH+EWzWuKVrCZ4KUVTMcnNK9s7sfmxj2/kSuovju0QNeeFPd23Mjc6roqBtwi6Rkh1vlZVPwMHPVDHKw45w4r3mZHjGZSvlKwtMi2ZPumFpkWzKP0VcLTItmZMLTItmU+6rhaZFoyfdVwtMi2ZVXC0yLRl9MphaZFsyquFpkWjLj1VcLTItGVVwvwnpO6Rxteuj5rDo/IbJxy/LCuCjUzK0NkzcH2Ncy2ZRuOEbaSh3DFivGZ4L7nMuZby221jJobX+kdWMfhlDXRSP4zmLq6Zm4HP1ROe39fVcqbH7gzbUbvbdbiQvwy1Xanml4JzT9YZUtwOT107pWcf2ux7L+dxPgy6ZOmU2oR3Ik+nypEGbFeLhejS4jy48mO6nfhcZebUlRdhkPf2GaKohiqIJA6GRoc1w7FrhkEfYg5C+w2lqqeupaatpJmyUk0bXscOzmPAc1w+xBBH2KxRdV9ARARARARZcdZltsWOZ9vPb5fQOMclUVjjrM8Ge2xfMPsq57qWaVgy8fRiqKUaVy+H5kKIpNhe3l8/1BFMML5F2kCL6CsLo7a/akUc7hsHRbVK8aBvwVu3bFuSq0l5aS4lNxahEpzkSY6lO5oaWtZZLbcs8eaj3b2t0hXi1ap3Estvun+RqKynikH3cx8gc0fdwA788FcVas3z2X0Jc22XWm62nrXeD3gqbhSwzNHbL43yh7Bns57WjvzwVzaZ3rdmgmrlqXqmirhXZptdkKpybcuWnORXCl0qSn7fRaxT6hGKVBVJY6xhzLaX2DXxINLiUqLN1dp6xbpaBvmm33ESWK80D421FPIHDpkb9E0UjHdL+l3S9v1Fj8dLstJBkNd6V01vPtjqTSL7sJdNX+2SQtqaWUPHRMz9nPBJG7okDHdMjcOMcnT0v6mOcD7Cq5TNMum30ZFNRnylWRq/ZzU+j1FTbL1QtqtJM3IUs20Oqabr9m3LD6uQySzT9oiuMqM0Goj+fu01+tfDRvW2WWPo1Lp64lk0eSI6iHs9ucZMFXTP6mOxnokZIAHAY+XSyXPcLwgeIhk80Jj1dpa6mOeLLmx1dOeJGZIBNNXUj+qN5bny5Y5WgODSPIxfti3NpVfd1ad3jC/l9zWfWZdDq8ZKusaN+IvDcqI9hJSIE+OpD8d0iw6w4hZbKH0K6O1ZZddaWsWsNO1PnWS40zJonYwelw5a4fuvY7LJG92va5p7L6odA6407uVovTOvdKVnn6eutIyohcRhwa8cse3npkjcHRyMPLJGuaeQvQ37KbpHO6jaY1HRO5ZpyLo0njRnraefcUt+paeS3vs8SOWUmZnaNQWmJk1ESYkmI2kvuKMeQnj72Yj0brii3QsdN0WLUD3NqQ0YbHcGN6nu//wAuMGXABzLHUPcfqaF4Q/E+8PsWgNyLfvJp2jDNN6pke2ra0YbFdY29b3d/+/ogZ8AEmaKqe4/W0L5D9pt0cv8AhpqYxrLa8AmbN1SmvKrrcZpRR6NqCltUmom4ZF1bbd1x0KnN5MzXLRLPBJJBDtH4Dd7zrfQ8u2V/rOrU1giHy5cfqmt5IbHj1JpHEQH2idT9z1Fdzfhm+Iw7jbcT7O6nr+vWOmIW/Kue4F9RaiQyIDPJNC8imd6NgdS93dRXWtEkZIt9yL3l+pDv7+i9QFZIsjON9y8QT8uyssSVy3+v58wRWSNJxg87fnzMvMPzTsrJGlEeN9/PPy7gTCn40zGMn8D9cw+yKxRZpbfe9ZBFOsTCPGT8C8QPundSzMwy/wA2eXb+QfdPVSbM7sz8/XeCKRbn/wC7u9eAJ7LMRP8A93d2+f5Ai5in/wC4E9Ahz/8Ad899/eCLiVP/AN35/uCLEXP2/FnwyCepUe7P/wB3b63BPRRbswzzvj3ghwol+YREZ5+fIEUHJm899vX6AUKr0qZnOFd/eCfn3UBJlYzk8mHCKtypWTPft3P6e8EVclyiLJEf57/IEVZlSMZMz37C8ARQzdVkUyZFqEVzq5UOQ1JYXglElxhaXE8ST2UkzLBkexlkj2GBdLZR3m211pr4uuiqInRvH+a4EHB9CM5B9CAR2W7dBa31FtrrXSu4Gkq35fU1mr4aumkxkCWB4e0PbkdcbsdMkZ4kY5zDw4r6tt+47c1KoD8KQ2w449HS3WKK8vDzCiUg+uawpLqo5PElTLyDI0q4d0rLBdBtT6V1TtLqanrqSaRsTJS6mqmj6Xjn6XcFof05bJE7gjPDmHJ+uHYzfzYP4hmx130rf7fST11VRNivlhnfiemeHMPnw4c2V1OJwyWiroSHRyCPLoqljmN/Gbh6PE9x5arcr8RUdalGiPWW32XWE/5EHLhMyUyfFXUt4LsMcy2LxKUZp42al0/KKoAAvpi1zXe5EcjmFn5eY/PuF5q7r/BJ1GLxV1eyO79A+xyOcY6a9RzQzQD91hrKKGobUexk+TpyB+44jJ/U9KNPJFgUmc1UJjMyq1SUh6UcN19cBlmOlbcVqOUhqOtThk4pTizQk1Gok7kgjPiDdjcSDcC70U1vpHw2qmiLWCRrRK5zjl7n9LngDgBrQ4gYLu7iB6MfD78Gt18IO3ep7bq/UNNcdfXuubNVOo5Z30EMMDSynipxPDTvMhDnvnmdBG55cyPBZAxzvlfpG6hHcVxf0rTnzVR7ZecbkGhWW5lcwbctw+XEmnJM46clsvrTIzJRDsPsPob/AAf0+dSV8OLtcWgtyOY6fuwfYyn9ofdvlg4IK8bviyeKl27+7zdltJ3Hr290dO+OcscCyrvGDHVSZHdtC0mijB/DMKtwJZI0j9n6N2np27bi7uqTPDWLpZbVDS4kych2+SkuxUlkiNKqo4kpCsGZKaJnkZGOIN+9cC/X5mmaCXNrtzz1kHh9TjDz/wDFAmMezjJ6EL0b+Ep4WTtNtNPvhqy3+XrzWVOw0zXtIfS2YOElO3kDBuD2srX4yHQNojw5rwrtedh6d6iXNCiXdVWqrPpUJ1qn2i1XUQFxykF9plz3YUCQxWHpEhtLZmo1paJppGEfiUrZemdXaz0XYamfTludT0lRKHSVhgMgd0/SyMPka6FrWku4wXFz3fV+EN7N75eHbwz+Jndmx2refWUN41DaKB8VHp2O7MpHQiUedU1clLSTRXKWeZghLn+YyBkFPDiH+tkl+GukHovSdPKzQk2lUVzW7nfVGi2zIkNya3EkmttphUVKOF+ZTpbyzbQpSeJDiSSalmeS7Q7Sbm3HWVsurtQ0QikoWhzqlrS2B7cEu6s5ayRgAc4A4LT1ANAwvCH4hvgb0d4atc6Bg2g1K+uo9UzOigss8zJrnTzdTGRGEN6ZaijqJHOiikkYHxzN8l0kznBw/HrNcpVgalUV/U2gVhMGgVJEio0ZUQm5qZMfKoTkiHMNgpMRiYSHlozwvoRw/eSrffGpvntV6NuUei7tTmpq4S2OYPyzpd+MNezPS9zctDu7HHPBC6s7HnS2wPiU0VWeJvb+8Nsun7m2astzqcMqBNFk075aeoMYmginEc8kWempijMYL2SEH9p6VWpGnt/qtCNY0Wl12sym/t025IkJ1qrNsOqVDp9uu5QxKcfW6lbi2X0KUyXV9XgnFZ4t2R0hqzSjb/Lqaeemt7HdDKdzwYiR9b5xyWgYIaHsIDvr6s9Ax3z+KP4i/D5v/NtHb9j7XbL5q+qj+ZqbvBTPjr2xvJp6W0yZbHM+Qva+WSnqY3vgHy5g6BUSdf4zqV0ddSdN6HEuSrU9ifQ1w4L9Tm0t45B0GXMS0lcGrsKSh1k2JTvVdegnI6lGn75GokjfWk92dIavuM9poap0VxEj2xskHT5zWE4fEeQepo6ug4eBn6cAldUfEF8PfxF+HTRlr3D1XYIK7RklHTS1dRRSeb/Rk87WB1NXRkNfGYpniD5mMSUj3lgEwfI1i+cZC8Z8z+fd8Rybn+C6O+irkpe3PdWSPyAnJ+yLBBEBEBEBEBFzR2HpT7MWO2p6RJebYYaQWVuvPLS202gu1S1qIi8TFHOaxrnuOGgZJ9gFkUdJU3CrpaCihdJWTyNjjY3lznvcGtaB7ucQB9yvR1pbaDVgadWZZraeFVAt+nQpWFJVx1HqSeqjpKThJk9UXXV7f6u0dNb5djd7zcrkXZbNM4t/0c4YP0YAP0X3A+H/AGyg2b2T2v2xhj6ZLNZqaCXkHqqegPqn5HB66l8r+P7Xc91fsiNbL91y/hbsi+2ZUwteIXmz/dUwtci62b7qmFrkXWzAqmFuyLrZvuqYWuRdbKmEyLglCphbsi4JfuqYWuRdEpVMLXIuidMLz/8ASwuk7s1/1FmpWhcemVZu24vVq4kE3bkRikvcKuJRH1k2K6s8bZUOxOlKb5XT9sYRhzo+s/8ALJeP7iB+i+PD4hevTuH4wd6bnHK11FQXFtti6TlobbYY6N+Dkg9U0UrzjjLjhfOo3CulyAiAiAiAiAiAiAiAiAiAiAim7ar9QtW46BdFJcNmqW3W6VX6a6Rmk2qhR5zFRhuEotyNEiOk8l3DFr6OC40NZb6lvVTTxPjePdr2lrh+oJUZerTSX6zXaxV7OqgraWWCQe8czHRvH6tcQv6P2m97U/UnTyxNQ6SaP5XfdnW1eFPJtwnUpiXJRoVYYbJwtlG23MJJ+JDwJ1Ha6jT1/vdgqs/MUVXNA7PGXRSOjJx9y3K+MvWemKzResNVaPuAPz1quNTSSZGCX00z4XHHpksz+queRBmT7rbWFpkWzKq4WmRbdL90wtMi0Zc85VcJkWzIq4WmRbMv3TC0yLRl9lXC25Fp0vflVwtMi0ZPVVwtMi0ZfumFpkWjKq4Xhc9q5oQrQfprapQ4UH7Fa+pMlnVi1erhnEhnEvJyRIrkaGSf8FTdOu2PUGCJGCSlCckWR7geFfXbdebL6XqJp+u525poZ8u6ndVMA2Mu9cvgMT+e5J7r6mPAPuqN1fDPoWpqqrzL7ZWG1VWX9b+ujDWwOf8AvAyUjoH89yTyV1wDsUu5qAiAiAiAi3tq4VFz32258y/QEU5Fdxsffz7/AJ4Dv2THsF9OdFfQ9npJ642Now7fdD06Xec6REYuKvRpc1nrosR+d/LadEi9U3MrlSZjKbhMPyIrMiRwtdcla0JVxnvBuK/ajbvUWvWacqboLfG1xghc1pw5wZ5j3OyWxMLg6V7GSOYzL+gta4jh/frdaTZHarVe50ek6u9NtcTXupqdzGOw57Y/Nke/JZBGXB0z2Ryvjj6pPLLWuLe3D2hHstLN6OOgFqap6IO3RcDliSE0/WWVXZiJ06sU6tLiRqdesWFGajRaRDotZ/5V+LGbWSY01p1Z/wDLvvOdLfC94yb9urubetG7hso6VtyZ12tsLCxkT4g5z6Rz3FzpXSxftGSPIzJE9jR+1jjb57eDPx/am3r3i1Dt/upHQUbLuzzLKynYY44ZIA90lE97i58z54f2scsjmkywSRtH7aGJnRqyvGPgZfIejy9aV6Y/Z++ze0qsPS6jdJjpSQaJWqrULdbv6jWxd5sIsfT2zlU46tGrl3wahw0+q1pykqKW8iclUSnNmkjbN9BuI8jvFB4tdbal1lX7P7MVNRT0cVUaKWopcmsrqrr8p0NK9n1xxCT9m10JEs7skPETg13hb4yvHLuLq/X9z2G8PtXV0tvhrTb56qj6jX3Gt8zyXU9HJHmSGATfsWOgImqXAkSCFwa77D029qT0UtRdZre0Lsp28UnXag3bFqXk/azFKsSq1hS0Q6LR6Y0qopuWGzVHMMxXJFLjNE4aEq4EqJRcEau8F292lNvrruXqFtvPy0RqKmlFSZK2OLBfNLIej5dxjGXyNjqJHFvUR1EYPWnXfw9/EXona297v6qZayKOE1dZRNq3S3CGDBfPPKfL+Ve6EZfK2KrleW9Tm9Th0n589rn0SY162OXSYsilkV52BFZiajR4TBdZcVhpw0xX5BNnxPVCy3jTxr4TUqluuKcWSIjSRy/4Cd+ZtN6l/wATepK3/wDB66vc+gc88QVvd0Dc8COrGcDOBUNYGt6p3lc7/DH8TNRpLV52B1bcM6VvUjn2xz3cU1xP1Op254bHXNz0jIAq2MDGl1S8r5w9j90p02rdlT6NN41JDVAviTIr+nEiY6htqnXo1GSdWt5Drykkhm6adFS5Hb4iT9vjcDaVOyzzzH8QHZA36wUW8unqMuu1sY2C4NYCTJRl37KcgDk00ji2R2M+RJ1OIZAuffii+HI6l0xb/EBpWgLr5Z42010axpJloC79jUlrQcuo5XlsrsZ+Xl63uEdMMfYftJ+gzc2ub9F1i0aozNV1HpURuhXfbSJUGnSLtoEc80qqQH570OEuuUElracQ66S5UNSEoPjjobd4J8F3iisu1sdy273HuToNGzyGekqC18jaWd39bFIGB7xDPhrmlrSI5Q5zh0yuczrX8Pfxm6e2Yiu21G7N3dTbf1MpqKGrLJJW0VS7+uhkbG18gp6nDXtcxhEU4e546Z3vjoHQN6Md+9EOkaudJbXtmHZhUzTKuUun2k/UoE2olSI86n3LU6jVpFOky4MOTMk21EjwIzbj0h9T6yUltRNpc3l4rd8dK+Ii4bebI7USSXIz3yCWSqbG9kfnOZJTRRwtka172sbUzSTyOayNgY0tc9vW5m//ABteI7RPirum1Xh12Smlu5qNSU00ta2KSOLz3RyUkMUDZWMkkbGysnlqZnNjijEbC1z29bo+bpUe0H6OmqnR5Kx4lvVS9bs1EtemzptBaV/LIumNwm2iUy/OuObTn0S6zb9XaLq2oUd1EplKkuOsocwrI8Pvg+3n0FvMNVz3mntenrLXysZOf2rrnT5LS1lMyRpbDUQn6nTSMMTyCxkjmZGT4W/AZ4gtsfECNbVGoKWzaW0/c5o46l37Z94pcljmR0kcrCynqoHfW+olY6F5a6OOZ8eW9I8aRuRkeD7fXuHrEvcD8lY4snJEZHvjnnn+4KisMaTnBke/18w/kqqxxZfIjPu25An81YI0rGDI8l58gRT8eYR4yeN/fnYURTceWZYPJmXx+IqnqpuPO8fDY/7bAimGJ+xb+fj+wIpRucRluef7Anr34WaiYXYr4+twTlZKZp9iy+PrAKn3XKU1Xf8AA/cCr6Ic0+/s7yMEJXGqYf8AqIveCfmsdczmRq9xH3h+SLBcnEWcGXhn48wKKNfn5z979Q/RFDyJ3Pfs27QRQj8wz/zbd5gn5KFkzMEeD78nncAqZVfkyjMz327+fuIg9lXuoCVMwR4Pbfl5AirsmTzMz8v1MEVelSeZme/YWfmCKtyZGcmZ7b4LvMEUS3VptMktzoEt+HNYVxsyIzq2Xm1f7VtqSrB5wZcjLY9hh19voLpSTUFypI56KQYcx7Q5pH5H19j3HccrcektYap0FqC2ar0VqGstWpaOQPhqaWV8M0bh/Zewg4PZzTlr25a4FpIP6hS+kZeFMbSzVIdKrpJ5vutrgTFFt91TkMyiYIu3qCPPMzHBt68O+jrhK6a1VdVQk/uNIljH5CT6x/tCPYBeqW2Pxk/ElpChhtmvtO2LVcbAB8xLG+grHAcEPkpCKV3HYiia7OS5zs8Zdc6Us+TTJ0Sl2o3Sp8iM8xHqa64c37E462pCZLUX+TxCceZNXEjic4SURZIy2EFa/DZQUdxpKq5andVUUcjXOiFOI/MDSD0Of578NcBh2G5weCCuV9e/G11bqHRmoLFovYqGx6oq6OWGG4OvLqz5R8kbmCoiphaqUOlic4SRB8xYHtHW2RuQfxbSGw3dRr1jRJaFuUSmqTVbhdNS8Lioc/woBuEZH1tUkF1eyiWTfWLLdA5P3T1pHobSlRUUzg27Tgw0zeOHkcyY9om/V2x1dDT+JdEvAT4Y6vxUeIC02a908ku31pc243qUk/XTskBZSF+QTLXz4hOHCQQfMzsOYSvtfWvUpjTCzHZUM2k16qEumW3F4EGht8m09dPU0aTQcalMLJeOE0qcNtBkRLyXUDa3Q8uvdUMhq+o2inIlqX5OS3J6Y89+qVwIznIaHuBy1fRv48/FNQeErYqouWnzCzcS7tdQWOnDW9EcoY0S1Zjx0+Rb4nNkDeksdM6mgcAyUlvVM9XasmrlXiqM3+dFOKqFVTkOHP8A5gl77QUw5KlG6qR1/wB7iM88Q7+m2W827+ifkov6M8ryvK6R5fl9PT0dPbp6eMey+RFut9YM1i3cIamrjroV/wA98+Znmr+cEnnfMmckyGbzfr6ySS7k5X150a7Sreo961PWK9pUmq/ymW4xSXpuFFMry2zUt5lvhJpmFQ4zxE022lLbbriODHVGQ62b1ahtmjdNUW3WmYGQCeMGVrP3IAezj3L53A9TnEuc1rurPWCvbT4Y2z2uPEnvdqjxm75XSpuzrTVujoZankVN1ezJkiZgRx01rgkb5EUTWRRTywCAN+WcwbumreFvE1QbMYp9MmXORoq0+qqjsO1Ci0r/ABUxKazKIjfYXVH1KdW3xFhptKjSZOpMYXhxsF2BuuopauaOy8xRxBzhHNLx1yFv4XCMYY12DlziM/QQtzfGk3c29dHoLZmg0/bKzc0FtdV15iifV26h+sU9HHOAZYnVshdPLCXDphiieWEVLHj4Qtd25Gbipcy0YsyZcdNk/wA2pbVPppVeUiRSm11FUpunqjS0SEwmoynlcTakpSg1GWCHZi+NtElprae/Txx2mZnlSF8nlNLZSI+kv6mkdZcGjDgSSAO68Ptq5NxKXcDTF22ntdZV7hW6o+eomUtIK6ZslC11WZm0pinbKKdkLp39UT2tZG57h0tJH6vqx0lbx1UsyjWfWYMOmHDmql1+VTVOtNXC7GS2imG9CcJSoJRFm4462l1bTrxoWlLfVpSXHmitotP6K1Bcb9b6mSYSR9MDZMEwBxJkw8fj6h0ta4tDmsDmlzuoldyPE58RTd3xP7Q6P2n1fZqO2uo6t09znpC9jLrJE1raPzKdwJpvJcZpZ4mzSQzVDopWRQCCOMfK8pzmRGfM/gXZ48xyz7Lz25UA6riWe+cbfD9w9kXGCICICICICL6V6JVgnf2t9qsvsddSrZdXd1W4meuZJmiqbcgNPEf3CTJq7kdH3uZGexjYu416Fl0ncZGvxUTjyWc4OZMhxH3DOo/ou9nw4NnHbx+LDbukq6PzdPWGR14rMs62dFCWup2P/dAlrHU0f1dw53Bwu+TI6iNlX2AYWuRebL91TC1yLzZVTC3ZF1sv3VMLXIvNk+6pha5FxspwqYWuRdbMmFrkXmy/dUwtci6JSqYW7IuiUKmFrkXRL91TChLmrse2bcr9xzOH7LQKLU6zI4lEkjZpkJ+a4niPlxJZwXmM2hifW1lJSR/jlkawfm4gf71tTXeq6LQeiNYa3uWP6Ps9rqq2TJwCylgfO4Z9MhhH5leaOqVCRVqlUarLVxy6nOl1CUszya5E2Q5JeVk9z4nHDMdsI42RRsiYMMaAB+QGAvhcvt4rdRXy86guUnVca6rmqJXe8k0jpHn9XOJWCNaikBEBEBEBEBEBEBEBEBEBEBEBF7dPY0aup1R6C9gUmRIaerGktZuLS+ooJ8nJCYtLlorlureaNRuMtptyvxWG9iSomDxyPHjl4utMHTO9F6qo2EUl1girGcYHU9pilwexJlie4+o6+e4XzEfEi29OhfFLq2vhhc23agpqe5xnpw0ulYYKgA9ifmIJXu9R5gz3C7VcjrAZF0MwtMi2ZPuq4WmRbMnsq4WmRbMqYWmRaMn3VcLTItmRVwtMi06QZ5KrhaZFsyphaZFl0v3VcLbkWjIq4WmRadIq4XQn7ebo+KvXQ2x9f6LTzerWj9eOg3O/HYedfVY16Px4zMiSpviQiLRroZjkk1ERJ+3rMzwO+fgM3EFn1xfdvq6oxR3eDzYASAPmaYEuDc/vSQF5Pv5LV6v/AApN3RpndHVG0lzq+m26ipPPpmucA0V1G1znNbnkumpTJkA8+Q0YXktHrOvoDQEQEQEQEX1z0Zug10lelpML/hDp9LkWw1KXEqOodyOKt7T+lvNKaRJaduKUytNTmxOuQbsSnNTZqEKJXU8O44X3W8QO1ezUB/w11Kxt2LA5lFAPOrJAc9JELSPLa7BDZJnRREjHXnhde97fFHsr4f6Y/wCMPV0bL45gfHb6YCor5QQS0inaR5TH9JDZah0MJIx5meF2DXj7CfpcWrbC61bV2aQ6hVqKwbki0qBX67R6pLWSl/4NFqF1W7RKHLXwJSf/ADcmCRmZkWcFxdcdP/EP2Xu13ZQXazXu2UD3YFTLDDLG0ccysp5pZWjOf6tkvoTjJx1M0v8AFV8PV7vjLZe7BqKz217sNq54IJomA4+qaOlqJp2DOR+yjn9CcZOOppr+sdM7zSl5mt2Xfth3IhZsyGZNIuK1rqtuopdR1jL6GpdOq1HqkMjwpKVtut95Du6HWPVthyx9PX6buVJjLS2WCop52YOCMtfHJG4jgkOa5ejDXab1zpkujkpbnpK7URGWls1PVUtTHg4LSWSRTRPI4Ja5rvYr2/dELX2z+nL0WKbclxU2l1KTXqLVNN9Z7Pcw5BRcSKamm3PT3o6OrUzSbnpc5E+M2RmbcSchs1m4hRl8+m+G2t98O28lVa7VVzRRU1RHXWqqHDzAZOuneHHOZKeRhhkJGHSQud0hrgF8tPiO2i1L4Ut/q2y2StqIIKSqiuVlrBw805l8ylka456paWWN1PK4gB00Dn9IY9oXki6YXRtrvRS19vPSeplKk0ONJKt2HXZKDT/UVi1dx16g1HrCShDsyIlDkGbwkSEz4jyU/dJJn7hbD7s27ejbSwa1pCxlxezyqyFp/qKyIATMxyQ1xIlizyYZIyeSQPo58NG+Nq8Qe0GmNxKAxsusjPIr4GnPy1fCAKiPGSQx5LZ4M8mCWIn6iQOzPXv2pUzpGaTxuirojofWHZGodAtPTxdWrlTTKuKo1NblIippVrWnb7TjHWzKjFS1HfenudY2v70ZB7F1L2x8GtNtVribevcTcWBsdqqqmuEUMfTBHHiVxkqKmcg4bG4uexkLelw4mcOV0b2c8AFJsnuPUeIfdbdembDZaysuQhgi6KaOLEzzNV1lQQ7DInl8jGQN6XN+md45P1r0dOixoh7NDTI+k10qa7TKlq+qIuNQ6VCJmsNWxUahDcWzZ+n0HhQqv35UGSW1MqfEmJGZ6xLbjMREiXI4U3V3q3H8X+rxs7spbpodB9YdNK/MRqI2PANVXPyfJomHDoqfBlkd0lzZJ3RQRded7PELu148teDYPw8WmeDbMSB1RNJ1QmqijeA6tuMmT8vb43dL4aXBmlf0F7Jal0NND2BdD/pQ2l02dFq1dbltRaM+3W7jsi97Emy261HixX0qdp7Tz7keKdSp1ctaosG4tTDTa5H2hpKTJvJ9Xt+tmL74dNw7bZY7w+oiNNBWUda1pic5zSBIQ0Od5b4amN/SA9xDPKeTl66ZeJvw+6k8J+6tp09FfpKqF1JTV9BcGMMDnvaQJC1oc/ypKerif0tEjnCPyJCQX4Hmc6YPR0uvoc9IKXR6S7VYNtP1NF7aO3ew863Ico7M9EuA2zUmTSpq47OqKUxpG6HicaakElLb7Jq9m/D5u9Y/EFtTBcK5kEl4bCaO60rgC0SlhY8mM94KuPMkfdpa58WS+KQD6CPC1vvpvxS7J010ucdNNf2U5oL3ROaC0TujLJC6J2c01bGTLF+Jha+SAuL4ZQ3s+0i9tLTotnU6nazaWVyrXlTorMSVcVjTqU1TbhcZbJBVGXSKu7DVRZkgkkbyWXpDKnDUptLSDS0npjrz4cVXVahq6zbrXNNT6emeXNp61kpkgDjny2SxBwmY3swvYx4aAHF7gXnz73M+EtXVmqa6v2o3Io6XSs8jnspbhHM6WmDjnymTQB4njb2jL2RvDA1r3SOBkd8fdL32it/9KKCVj0eilpxpUiUxMlW7Hqa6nWbrlRHWnoTt01ZDEFh2BCksk/HgMMIZbfwt1chbTC2uyvh18IOktjas6ouNy/pjXhjLG1DoxHDSteCHiliLnuD3tJZJO95e5mWsbE18jX9vPCh4D9DeG+uOs7tdjf8AcsxuYyqdEIoKNjwWyNo4S6RzZJGOMclTJIXujyyNkDJJWSfBcZ89tyyWNu8dwF3zVhjSM4MjPx57/HtBFPRpGMGRgisEaUR4Mj35nvsfv2D7op+PKzjffbt+vwBFOxpuNs9235hjlOVPR5ecYVj37Hz9wJnGFNMTsGWVfHw3LADgIpdmYR437uXME5Uo1MMu3PvD2TspFqod5+X6eQIs5FQ8fXgH5IstNQ8f1wCLlKof7u/b9wT9E/mBf6v7fIEXEqodvF8wT3wsZdQ8fL15An3WC5P577+frtBPTKj3ZpnzPBeZgnft3UW9MIu3J8tz2+veCKIkTs53z5AihZEvmajzjsz2l/YOPRUUHJm5yRH7vLOAVeFASJRFkzPt2Lu/IURQUmTzMz59n6iqKvSZOcmZ/uYKuFASJGMmZ+RZLyyCoq/Jf5mZ774BP1UDIfxnfc+fh8QRQUh4sHvsXf2mX6Ai+puj1q1p/adOetmuIXQKpUp6pT1wylodplQUf+FFZlSEttuUpuK1skneNgjNazcQa+EdY979u9ZaluEV/tLxWW+CEMbTNBEsfq9zGkkSl55PT0vwGtDHdOV7nfC28Zfhr2S0hXbSbgU0mnNX3W5OnlvU7myUNYSOiniqJWsY+3sp4/ojEwlpQ50876mEymMfPGtupC9R71nVVhayodPI6Xb7Cslinx3FmctaDJPC/UX1KeVkuJKVJbMz4CMcs7YaLZofStLbpWg3Sb9rUOH+UcB9AP8AZjbhg9CQ5wA6ivPrx0+Jyp8Um/d/1jRSvGg7cDQWeJ2Riihe4/MOacYlrZXPqX5HWxj4oHOcIGk/ltu0Go3dcVGtmlEk6hXKhHp8c18RNMnIcJK5DxoJSkx4zXE44ZEZkhBmN3Xy8UlgtFyvVcSKSlhdI7Hc9IyGjPHU44a33cQuum1m3Ood3tx9FbY6VY03++XKGkhLs+XGZXhrpZCASIoWdUspAJEbHEAkYXbo6u19DtL1rJKm6DZlG+6k1EUmpTFrwkjVhSSn1urSdzwSCde5EksF55NF73O1uA4g3S41P/JjYB+n0QxN/MtZ3Ljz9j8rtr/Ax4W3uijczQmi7LwCcTVtS53qcOAqrnXzZJA8ts1QcNZE0BvTLeF01S8rjrN0Vp43qlWZrsyQZGfA3xnwsRmSP8MeHHSlpsv8raCIehlis1Dp60W+yW6Ppo6aIMb7nHdx93Odlzj6uJK+OPdbcvVO8e4usNztZ1hn1Jea19RKf3WBxxHDGP3YoIgyGFv7sUbG+i7COhxpR/JKDI1NrUU01W5GlwrdQ8guOJbyHEm9PQlX323axLa+6ZkR/ZmkKSZpePPU3xA65/pK6RaOt82aKjcHzkHh05HDD6ERNPP/AIxxBHVGF9B3wf8AwrjRWha/xJ6wthbqnUcTqe0tkaOqC1NePMqWg/Ux9wmZ9BIBNJBHJG4xVTs/LnTFuSz6rqg9TLXpFMjTaAy9EumtQGCYfrNwPOk5JYl9UaWZLtJSRNKdNPXKeU4hSjJCccubEWu/UOjY6u818z6apcHU0TzlsUIGGlueWiX8QaD0BoY5oBcc+ePxYde7T6q8StZp3bbSdsprxY4nwXm400flSV9zkf1zRz9BEcz6IBsL53M+YNQ6eKSR7IYg35Nrlv1+jwaXVKpR6lAp1ei/baLPlxHmYdUidY431sKStJMvpJTZkZJPJFvjcs8uUt2ttfUVlJR18UlVTv6JWNc0ujdgHD25yO47j7Z4IXnbftA620raNMai1JpS4UVgvNN8xQVM0EjIKuHqc3rglLeiQBzTnpcSOCRhzSaie+59okFtBARARARARARdu3QE08Oh2JXdQp0fgm3jPKBS3HELS4VCojjjS3GzVgjam1VbmTIt/s6dx1h3r1AKm80VihkzFSs63j/xkmMA/drMH/llfSv8GvZF2lNodX71Xai6Lrqis+WpHOa4OFvoXOa5zSeCyerdKCQOflmHOF9/5HCzZF7MYWuRebIqYWpGLzZceqphbsi8JVTC1yLzZfuqYWuRdEvuqYWuRdbL91TC1yLgkTC1yLrZPuqYWuRdEp9VTC1yLom+6phfJ3TVvErV0GuGI24hEy75tNtWMk3OFw2pbyp1SUhBGSlpOm091CuwusLPMhv/AG4ozX6mppCMxU7HSH8wOlv/AJzgf0XnR8Uzcsbe+ELWdtgna26alq6a0xDqw4sleaipLRnLh8rTSxu9B5gz3GejAdk18mCAiAiAiAiAiAiAiAiAiAiAiAiAi9DX8P3rQmh6qayaEVGahqLflq06/rcjOkvLtesuWdNq8eMoiNCXplCrxPLI8cSIGSP7uB0I8d+kXVeldI63p4SZaGqfTSkekVQ3qYXfZssXSPvL915AfFw21ddNB7cbq0dMXT2qvkoKhwxxBWM8yJzvXDJ4CwEZw6f7r1WZHl6ZPuvBfC0yLRk78phaZFsvVcLTItuk+6rhaZFsy+yYWmRZdJ91XC0yLZkVcLTItOk+6rhaZFsyJhbci0Xqq0yLZkVcL851d0zt7WbS+/dKbrZS9b9/2tWLXqJqaJ5UZFUhuMMVBls1tkqVTJSkSGfvJw60k8kJ/SGq7jorVWn9W2l+Lhb6uOdnOOrocCWE4P0vbljuD9Lit5bea2vG22udJ69sEhbd7RXw1MfOOoxPDnRk4P0yN6o38H6XHhfzxtUdO7i0j1GvfTG7YyolyWHc9YtersqTwEcukTXYhyGi4l5jy0NpdaPJkptaTyeR9FOltR23V+m7Hqizyh9sr6WOeM/5sjQ4A/duelw9CCF9gehdY2fcHRumNcafnEllutDDVQnOfolYH9J7fUwkscMDDmkKhieW60BF+kaS6Q6j66X3RdNNKLUqN5XrX1uJp1GpvUIV1LCOtlzpkuW7Hg06mwWSNx+TIcbZZQRqWoiG1tZ610vt9p6u1VrG8RUNhpgOuV+TyeGsa1oc973nhjGNc5x4AK2ZuBuHozazSty1rr6/w23TNIB5k0nURlxwxjGMDnySPP0sjja573cNaSvTT0OfYeafWImlXz0r6lF1Nu1s486NphQZMpjTqjuGwayj3LUSREq15zIslaTNptUSm8bZocRNZXk/KPfDx/6l1CazT+zlK+02Y5Y6vma01sozjMDMujpWuaCOpwknw4OaYHheI/iN+KFq7VRr9LbBUUlk0+epjrnO1rrhMOrHVTR5fFRsc0EBzhLUYcHMdTSN4/felH7WXotdEumnpno/TqPqxe9uxjo8CydN3qdRtNrKKI2bLECrXPTIciixSguJ4Dp1JYlvNKbU099lPBjjnaPwa7u7zVQ1Xrapns1gqX+Y+qrg+Wuquo5L44JHCV3WOfOqHRtcCHs80ZC4l2L8AG+3iBrRrbcSsqdP6YrH+dJW3ESTXGt6zl0kVNI9szusc/MVT4mODg+PzxkKK9m17Uyq9M+/ru0m1IsK37Jvel25JvO159pSqm7Qa5Q6fUYNOq1LmRKxJmzYVapx1WO824h9xqUz1x8DJtETuZ4pvCJR7FacsustLajqa+wTVTaWdlS2MTRSvY98cjXRNa10T/Le0gsDo3dH1SB5LJDxo+BKg8Nek9PbgaM1ZV3PTE9Y2jqY6tsQngnkjfJFKx8LWMfDJ5UjHNLGvif5Y6pRITH1W+3NtK2ba6X9sVyiRWIVYvzRq2biu5LLhcdRq1OuO7LTgVd9k1Gtt16g27Fi8RESFphEZFxEsz7mfDxvl2umyV3t1fM6Shtt9nhpsjhkb4KaofED6gTTySY5IMp9OkD0C+FVqO+Xnw63203Od8tttOpamnpMjiOKSmpKqSFpxghs9RLLjkgzHP09IH5R7Kzpd/8AkydIWDRLpqn2PSXWFdPs+9zkuGUGhVY3nEWdeasvMsxyo1UlqjS3lmaG6ZNkuGlS22+HfXjH2R/xu7XVNws9H5mtrEH1VJ0j65o8D5ql7EnzY2h8TRguqIomghrnZ5K8fnh0/wAe2zNVdLBQeZuJpoSVlD0j654cA1lF2c53nRMEkLG4c6pggYHBr3577/au9EgukVoHJvu06YcrVfRWLUbnoKIjDCp1yWjwNv3haqlmaH5DrcCKdRgtJ6xxUuKbDSOKUox5weCve87V7lw6bvdZ0aL1A+OnmLieiCpyW0tRjlrQXu8iZx6QI5BI92IQvJP4efiNOyu78GktRV/l7e6pkjpZy9zvLpqzJbR1WOWtBkd8vO49LRFKJZHdNOF0xexch2bN6ZzCroKKurwNLL3nafJk8JqVeKJVAjyFRErSrilM2RJrKyxg0oSpRHsO/vj/AKm/0+wErbMXihlvNIytLc/9ylszm9WP3TVtpR7EkD1XqB8UKr1PS+F6ZtgMgt01/oY7iW5/7iLahzesjGGGubRNOcgktB7rtU9qX0Hde+lNcml14aP1ClV2FaVFqVt1Kxa1Xm6Cmmy6lVW5y7tpD1QWmkyTnxiQxUEmtmSluBG6tMjJpa6beC/xHbZbLWnWdh15TT01RXVEdRHWRQmbzGxxlgppRGPNb0Oy+A4dGXTS9ZiwC/oF8PjxZ7PeHux7gaZ3Mo6ijqrlVxVUVfBTmoMrIoTGKOYRgzN8t3VJTnpfEXVE/WYcAyY1iU63/ZA9EWVXL8lUbUDV3UW/aNIm23RqrIgQq5L62FFlUK3p8yn/AG3+WWnaDMuW5UHYJIVUJCW1pJLrCTytS1d08ee+0Ft0zFUWvQdptkrWVEsTXvhbh7mzTsa/o8ypqjFG2Bs2fIjL2nqZIVmavrbz8TPxK01o0hBVWXbOx2adrKqeFsj4GYkeyoqY2SdHm1la6GFtM2oyKaNz2u6o5SOvjp3e0povSnt9rTWxNK6dSbJiTY9Sau+/qfTajqCU1okG9/TrUORNp1mxpGOqeW1IlyZTJERrZSa2z7ZeGDwgV+yd2frHUut5p9RyRujNLRPkjoeg9vPL2skq3N/EwOZFHG4khshAeO8Pg38B108Ot7k17q/cWep1ZLE6I0Vuklitvln8PzJe2OWuc38TGvjhiheSQ2Uhrx1Zx38YP4+u0th3oXpIpyO9y3LGxkfLB/uCqpyPI5b7/Us8gT+anI8jcjzv2lt58uwEU9GkkeDI9y5+P05An6qcjyT2Mj5cy+AJ74U7HlEeN8Hgsei7AQqbYl8iUfv+AIpliZjG+eXuIMIplicR4yfnv8iBFLMy+WFeO5/mCKSanGWMntt8vEEUi3PLvL1nkH3RZaJpHjfs5kYJ6LITM/3/AB9ECquUpvbxFt4gqJ9tM/8AN8ceG/MD+aLjVN71+viH3RY65qe0z+PYCfdYjk8uw9/Pvz478gRR7s7PI/h8vmCKNdmGecq7y9GGVVRb80izg+Rd/fz92TD+Sp+ahX5qjzvtvv8ATzDCKGfl88HkzP1nsBFCyJRFkzVv6+AIoGRKzxGZ7Z5efhnuAd0UJIk8zM+XIu71kE5UDJk53Ufl4gqqCkSOZ537C9doKig33+e/n+gIoSQ9nOD29YD+SKEfd595+PIgT3UO85z7i+YJ91+rdHu4qTbesFo1KtyGokFb1QpxzHjQlmLIq1Km02G684syQyyqTKQhbhmRIQo1KPhIxxru/aa+9be6gorbE6SqDY5OgZJc2KVkjwAOSelpIbgkkADkhd3fhz7g6T2z8Ymz+ptbV8dJYnTVdGaiQtbHBNX0NTR08kj3ENjjM07I5JXFrY43ue8hjXL7x6UVi3bfunDNPtBpyfMplci1eZRmltNv1SExDnx1Jjm6psnpEV2Sl1LPEXW8J8JKcShJ9Udk9T2HS2rpKq/vEUE1M6JkxBLYnuex31Yzhrg0tL8fTxkhpcR9AXxQNi92d+fDtRWHaSmkrrrbL5DcKi3xuY2Wtp4qepiIiLy3zJYHzNmbT9Q84Nd0NfOyBjvhPSno3X5ed2wIt1WvcFr2pDkJfr0+s06XRX34jJktcClontMvSJU4/wDDS42lSGSUazM+Ekq7Ma63f0xp+xVU1kvVJW3uRhbCyKRswDjwHyFhIa1n4i1xBdgNHfI8PPCr8OXfTeHdWxWzdHbLUGmNrqSobLc6m4Uk9ukkgjIc6mom1Uccks9TxE2WJj44GudM8ksEb+wjXLUqnaLaZSZtNRFi1V2Mi3bLpbSG0tNTfsptR3m4pFwlT6HEb61RYJv7iGskbiR1O250lV7g6wigrHPfRh5nq5CTks6suBd/bmcekHPVy5/IaV9BfjQ8Q2n/AAfeHGvuunIqal1LJTttWn6NjWBjKjyTHE9sIGBS26BnnOb0iP8AZw0xLXTx56nNGdN6jrPqXTredXJcgLfXXLuqinV9ezRY8ltyouHJUlxf2+ovPJYaUZKPr3yUr7pKMu6m4OrKXQWk6q5MDBUBohpo8DBlLSIx08DoYAXuGR9DCByQD8xXhB8PV/8AFp4g7BoqrmnksjpnXC91bnuMjKCOVrqp5lIe75mqke2nheQ4/MTtkf8AQ2Rze4rUiwol1aa1exadFtuDHepDVKpjldpiKhSLdjMsphpqMWCfCkplGp3GqIZKbJDqUZUSSMdD9LaimtGqqLUNVNVSSicySCKQskmcT1dDn/2ZX4EnBy0nAJwvrJ342Ztm4uwWpdnrFbrDR0ElsZR0jrhStqaK2RMYIBVQ0/AE1DS9bqIh8YjmZGXSNjDs9fly+z4uKHS1yrS1EpdxVFtlbqabVaC7bzUs0IUtDcWezWK42l58yJKCdQ22SjI1OJLJl2TtXiUtk9W2G86ZmpaUuA645hMW5OMuYY4jgdz0kux2aT38TtffBH11atPTXHbTfK33y+xxOeKWstz7YybpaXBsNSytuDQ+ThjBKyOIOIL52MJLfge4berVp1qo27cVOk0mtUmSuJPgS0cDzDyMGW5GaHWnUGS23EGptxtRKSZpMjPsZbLnQXmgpbnbKpk1BMwOY9pyCD/eCDwQcFpBBAIIXjBrjQ+rNttWX3Q2ubFUW3VdtqHQ1NNM3pfG9vPplr2PaQ+ORhdHLG5skbnMc1xhRnLaiAiAin7VtypXfclDtekNG9Uq/VIdKhoIuIidmPoZJxRZL/DZSo1qPJYSkzGDcrhT2q31tyq39NNBE57j9mjP8T2H3W8tvNDX3czXWkdvtM05lv15uEFJA0DP1zSBnUe30sBL3nIw1pOeF6LbPtinWVa1v2nSUEinW9SoVLjYTwG4mKyltchaeJWHZTpKcXuf31GOgd1u1RebnX3WqP7eeVzz9snIA+zRho+wX3DbY7fWPanbzRm2+m4g2yWW3QUkXHSXiFga6RwycPlf1Sv5P1vccqy5GG2T7rfOFuyLwkVMLXIvNkVMLXIutk+6YWuRdEipha5F1sv3VMLdkXmye6pha5F1snsVTC1yLolVMLXIutkHuqYWuRcEiYXU37Q29inXZZdgxn0qaoFJk3BUm0kr7tQrb32aG26ZkSTWzAp5rIizhMjxHYLaG3eXbbjdXt+qaUMb/oxjJI+xc7H5tXzm/Gm3UbdtxtrdnqGqDqezW6W41LRniorn+VC157dTKen62gZw2o784XXMOYF4kICICICICICICICICICICICICICL6l6FGtjvR56U+imq5yXo1LoF7U6Hc5tO9T1to3Fx27dLTij+4pBUOqPrwrbiQR9g4z3k0Y3cDbHWelRGHVNRRPMORnFRFiWA/wC1YwfkSuCfE1tkzeDYjc3QIha+uq7ZI+myM4q6fFRSkeufPiYOOcEr+g2262822804h1p1CXGnW1JW242tJKQ42tJmlaFpMjIyPBkY8BnuLHFjwQ8HBB4II7gj0K+RJ8b43vjkYWyNJBBGCCOCCDyCD3C3ZFoyLThaZFoye5VcLTItGRVwtMi26T7quFpkWzImFpkWy9VWmRbL/uqrTItGRMLTItl33VVpkWjIq4WmRaLyq4XlN9vB0YjtLU6zuk5bVONFB1MitWhfq47WGYl80CJ/1NUpHUxkNM/1FbbJN8S1qUt6nrUe6x60eAPdQXnS152tulTm42p5qKUE8upZnftGDJJPkzHPAADZmgdl72fCp3wGoNEaj2QvdZm62R5q6EOPL6Gd/wC2jblxJ+XqT1YDQGsqGgcNXn5HoevXFARfqGi2rt46DaqWNq/YM5UC6rDr8OuU5RrcRHnNNGpmpUWok0pC3qTXqW89CmNkZdbGfWnJZG0td6Lse4mj9QaK1HT+ZZ7jTOifwOphPLJWZ4EkMgbLG792RjT6LY25e3unN1tB6p281ZSiWw3WkfBJwC5hPMc0ecgSwShk0LsfTJG13oveVY936ZdNvowRa9BXKe081107qdIrESHMS1WaEdagS6FctFKd1BpjXBbFU+0RyfJo0pkxydQSkcOfnc1BZNWbB7tTW6oaxuptPXOOSNzm5il8p7ZYJejP1Qzx9D+nqyWPLCQc4+UvVGndb+GPfKe1VTY26w0reIpoXvZmGfyXsnppujq+qnqYvLkLOrJjkLHEOzjx5M+zF6Vtd6SGoHR2s6w5tYfsC4v5dVNR6mzIoWnMe3p6UTrcuifckhl2M21XaE+1MagxilVIyNxCGFracJPtxJ4sdnbftdpvc2+aijgjuVL1x0UZEta6ZmWTwMgaQ4mKYOjdK/y4B9LnSNa9pP0WyeODYS1bM6S3h1HquKniu1H5kVuiLZ7i6ojJZUU0dM0hxME7XROnk8qnB6HOla2RhPpn6Ans4tOeglSK9fFYuhF6auXBbp027L7ltpo1tW1bbTser1ShWzDkOccWjrm09l6ZOmOG9J+yNK4IyCU0flJ4jvFDqjxDVtu0/Q2k0Gi6aq66ekafNnnnIdHHLO4DDpA17mxxRDpZ5jxmVxD14keLPxl6y8VNwtWl7dYzbNvqSs8ykoWEzVNTUkOhinqXtGHTBkj2QwQt6I/Ne3qmcWvF81G6GHQU6ZdbubU+vUS39VLnqyIdBqV+2Zq1dNRXSH6HTWabAgU5Ns3m/bFGdpsdpK1RExEsuPqW8+04464pe39Lb8eIfYqgtOk7bcKmz2mAumZR1Vup2CQSvMj3v8+lE8oe4kCQyFzWBrI3taxobtfRfiX8VXhstlk0NaLpV2Gx05fPHQVlppYxM2eQySPkNTRtqZhI4lolMpe1gbHFIxjGNb5n/aKdAqodCbUCiO29V6hdGj2oP8xcseuVVLB1ykTqYbC6nadyuw48aFIqMNiU29HlNtMNzGFGZNoW06kvWrwteJCl3+01cGXSiio9c2zoFXFHnypGSZEdTAHOc9rHFrmvjc55ieAC9zXsJ9yfBf4t6TxP6Rucd5t0NBuRZ/LFdBF1eRNHL1CKrpg9zntje5rmSROe90LwAXubJGT6AvZKdLpHSK6P8fT+6qocnVXRGLTbZrZy3UHNuGzVNuMWdc6OJROy3GYcU6dOX99ZSIyXXVcUpGfNPxr7Ina7cuXU1lo+jRmoXyVEXSD0QVWQaqn9mgud58Q4HRIWMGIXY8hPiIeHR2zG8E2sdP0HRt/qmSWpg6Aeinrch1ZSn0YHPf8AMQN4b5cro4xiB2OmH2iOgV1dBrpZUjVnRyRLs61L2qz+o2llYo2EN2ndMOQ0u7rRbQts432anVCal9mKptcRdJqDUZROEh5JegPhY3MsviK2TrdE68ijr71b4BQ3GKXk1NO5pFNUnB6uqRjOh0gIkFTA+UdJdGT6i+CzeHT3iu8O9x263MhjueobVTNtt1hn5NXSvaRR1ZIPV1SRsLHyhwlbV08kwLC6Jx/eKL7dbXGNayKfWNHdMavd7bCWUXM1ULkplHfdQltKZU21mpUh5x9zhUp1LFSjtqWr7iW0lwjjqu+G/txPeXVVDry8QWJzsmnLKeSVoOSWsqC1oAHAaXwPcAPqLicrii5fCT2lqdQPrbbuZf6bTTn5NKY6WWZoJJLGVRY0BoyA0vppHBo+pzyepdXuvPSS1d6TF7rvzV26Ha7U22lxKRTIzSafbttUxSyWVLtyismcanxcpI3FnxyJK0kt911zKx3S2w2n0Ls/p1umtC2ZtNRl3XLI49c9RJjHmTyn6nu79I4ZGD0xsY36V6E7N7IbbbDaUZpDbWwNo7e5wfNK4mSpqpcY82pnd9Uj+4aPpjjB6IY42fSvyNl3lvgj5eH6DkdctKXYe5dp9vcZb9oIplh/GN/u/oCKaYf5b+R+u0E7KajyNiLOD2LzBCpqPI8dy7Pf9dgRTbErON8H62MEx6qaYlblvg/zz9A7IphiZjBGe23cHdFMsy8bkr5gilGphd+D23IAik2ppl/mz+XzyCKTan+Pz9ECffCzkTUn2+8g5RZaJhdisBhFzlNP/V9fj2gnYrkKaf8AqLu5931BEOarvL3n3Anstipp/wCv5hn7IuBczf8AH88f3BM8LFcml3/E8h68osF2f/u8i5AijXZpn2+Znt8gRRb0wu8zP12Aii3pZ9qsF3fl3Aih35nMiPy37u8EUM/K575PfyBPdQ78oizvk/oCKDkSeeT8SLxxsCKFfkd5/e7PDt7RVP5KFff577n8CLlt8BRFCyH85LPnv+feCKGfe/b9wRRDzvPfzMEUS87n8u8EUW85nJZ8w/RF+52X0odWLFgMUmLVIFwUyIjq4kO54btROKyRYQy1OjSoFUNlssEhC31JbSRJSRJ2HFGpdl9C6mqpa+aikpa2Q5c+meI+o+rixzXx5PqQwFxJJJPK9AtkviaeK3Y+w2/Slt1RQ37TFIwMgp7zTvq/JYBgRsqYZqatMTBgRxvqXMiaAyNrGDpX3P0dtcWNS6XMRdF10RV6uzJDqLUiwio6KbSo6SS0dP8AtRuP1hTxJU88tL73UpNKTSjBmvrLuxtw/R9bTustkqBp1sbQalz/ADTJI489fThsQHDWAsZ1HJBdkBvuR8Pnxp03iP0zeItzt0bM7eSasle2yQU/yDaSiiaAw0vnF0teZMPnqJG1NR5DSxjmRBpdL159JvV5WqeoMtynSTdtO2ftFHtpCTMmpLaXElUK0STJKuKsSWiUgzJKijttEZEojHZ7aPRDdFaWgbVRdN7rOmWoPq04PRF+UTTg4yOtzyDgheFfxEfFM/xOb8XOew3AybXadMtBaAM9ErA8CpuABAOa6VgewkNd8rHSse0PY7PYd0VtIv8Ahdp01PrEcmbtvFMatV3rW+rfp0ImlKpFEcNR5ScCO8t14jIjTIfcSeSQkx1Y3l1v/hjql9PQyl1loeqKHByHuz+1lH+m4BrcZBYxpHLive/4anha/wDc27DU951RQiHc3VYir7j1t6ZKWAMJoqB5JyDTxPfNM0hpZU1E8Zy2JhXXN0pteZ+q96zqNRak+nT+3JLtPpESNIcTCrkmM6aJFxSmkmlEk5LyD+y8ZH1cckmRJUteezW0O3dPo6w09dX0rf8ACSqaHyOcB1RNcPphae7ekf1mPxPyDkNbjw2+It4yrz4kd2bvpbSd+mGydhnfTUUMUjhBcJYnkS3OVgw2XzXgik6wfKpgxzQySWbq+jfZ9VK8JKb9gPy5kiyKezSjiMSXVuRYVwSnZKlopiHFGTPXwG1KkpbIk5Jo1bmWeL/EnS2SI6dqY4WNv8rpOotADnQtDcGQjvh5AYXc/jA4Bx3s+CXfd0LhHvLZqy5VU20dDFRmGOV7nRQXKV8pc2ka4kM8yna51U2MBuRTOeA5zS6ke0Fi0dq/bHkxkMIrcu1pZ1Y20kl52CxVFt0h6RhJcf8AifakJUZmrhb4eSSE/wCGyatfp3UEUrnGgZWN8vPYPMYMgHtx5ZI7ZOe5K4j+Npb9L02820NfQRQs1bU6cm+c6QBI+njq3NonyYA6vq+bjY4ku6Y+jhrGhdf47IrxTQEQEXYV0CNLjq90VnVCpxuKBbDS6PQFOI+49Xagz/zslvjaUhf8upq+HJKI0rkEfYOAN9tUCitdHpmml/4RVHzJcdxEw/SDzx1v/iGEeq9svg2+Hs6n3B1T4g7/AEGbNp+M0VuLm/S+4VLP28reppDvlaV3TlrgWyVLCOWrtjyOqwf919IWFrkXhIqYW7Iutk+6pha5F0SJha5F1sipha5F0SKmFrkXRIqYWuRdEn3VMLXIuiRMLXIutl+6phbsi6JPdUwtFLShKlrUlCEJNSlKMkpSlJZNSlHgiSRFkzF1ry4gN5K0SPZEx8srw2NoJJJwABySSeAAOST2Xnd1wvheo2q98XaTq3YlQrklil8a+Pgo9N4abSkpMtiT9hioPBbZMx3S0xa/6FsFqtpGJI4h1/6bvqf/AOcSvid8WG7Mm93iJ3Y3IE7pLdW3aVlJk9WKKmxTUgHpj5eKM4HGST6r8pE8uvCAiAiAiAiAiAiAiAiAiAiAiAiAiAi95fsztd//ACguhlo/dcyWcu5LYo//AA1vBakklz+oLFQzSEvvGSlcb1ToZQpqlHgzOTnA8JfEzoj/ABf7y6vtUMPRbqqX52n9vKqiZCB7BkvmxgezF8pXjZ2q/wAUXiS3FsFPT+XZa6p/pKkA7fL1xMvSPYRz+dCB6CNfemRwEX/ddUcLTItl6rhaZFsu+6qtMi2XItMi256rhaZFsvVcLTItlyJkW3P9lXC0yLRcqrTItl+EWmRbL/ZVwvmzpc9HyidKLo+aj6NVhLCJNyUVx+2Ki8hCv5LeNKzPtmrNrWlXVExU2UIdURZOO44n/MOSdndyq7ajcfTWtqMuMVNOGzsB/raaT6Z4yPXLCS3P77Wn0XNPh73euexe7ujdyLc55hoqkNqY2k/tqOX6KmIgd+qMlzAeBI1h9F/P7uu167ZNzXBZ9z0+RSbites1GgVumym1tPwapSpbsKbGcQ4lKiNuQyoiPG5blsY+ia0XWgvtqt16tVS2a21cDJontOQ+ORoc1wI9wQvrdsN8tep7JaNRWOsZUWeupo54JGkFr4pWB7HAjI5aR+XZQAkVLICLvo9iF0xv+G2ps/ot3zVuqsnVycdS06fnSDTGoOqDMdLa6Mwp1xLMeNftNjpZSnc11OLFbbTxyVmfnZ4+9j/8KdJ027mn6Lqv1lj6K0Mb9U1AXZEpwMudRvJcT6QSSucemJoHlH8T3w5f4aaIpN9dLW/q1Np6Ly7g1jfqnthdkTOwCXOoJHF5PpTSzve7pgaB6Tuk3rLU+j3oTqRrNSbAq2psrT6hHXnLQo0+PS5MuAzKjN1OoyajIYmHDpVBpzrs6Y43HkOoix3DS2oy28sdqNDUm5W4el9C1mo4bTDcqjyRUysdI1ry1xjY1gLeqSZ4bFE0vY0yPaC4Lxa2R23ot3t1NGbbXDVtPZILvV+QKuaN0rWSOa4xRtja5nXLPIGwQtdJG0yyMDnjK8UXSs9oF0k+l5UJLOol4u0Ww/tHW03SuzXJdEsWEltalRXKhATJdlXPUmOIzKVU3ZTjalK6nqUH1Ze82z3ht2s2Vpon6YsbZ9RdOH3CpDZat2R9QY/pDYGH/JwNjaQB19bh1H6ZthPCPst4eaOGTR2nG1Oq+jEl0rAyaueSMOEcnSG00bvWKmZE1wA8zzHDrP1x7D2Nq4rplRJdkM1c9NG7OumNrVIaRI/kCKK/Qaq7ZbNQcx9hKsuX3HhKhEr/AJg2Eyur/wAMnxwx4/pdFjY2aG/vg/wrNdTutYJb5xlE0Yqiwfj8oUhlEpH0dZh6vq8tdfPigzbejw31FPqeSn/w2dcqV1maS3zzM2eIVhjH4/JFC6YTEfs+swdf1+Uu2b28FUt+N0UNPqXPfhlcNQ1yt+Xb8VzhOe5Gptm3u3W5cVOOsKLFbqLCH1fgJT7RH95SB08+HRS3OTebUtZTRv8A6Mj09M2Zw/AHSVVIYmu9OpxY8sHfDHkcBy6E/CeorvL4gtXV9JFJ/Q8OlqhlQ8f1YdJWURgY706nGN7mD8WI5COA5edHojdJ27+iXrVbesNpRkVhEBmZR7otV+c5ToV32nVUITU6FLmNMSlRVdcyzKjPdU8libFZdU24lBoV6m71bR2Pevb+66FvcxgdK5stPUBge+mqY8+XM1pLergujkZ1NL4pJGBzC4OHtJ4htjNOeIja69bbainNM6ZzJqWqbGJH0dXET5VQxhcwP4c+KVnWwyQSyxh7C4PbfOlP049eOl/WY72p1ejQ7RpVQcqFs6dW0wum2hQJBtPxm5pR3HX5tYrKIklxs5s5599KXXENdU0s2xC7MeHnbfY2hlj0hbXSXueIMqK6dwfUzNyHFnUAGRRdTWu8qJrGEta5/W9vWoDw++FbaPw3W2WLQlofLqOohEdVcqlwkrKhuWuLOoBrIYS9rXeRAyNhLGOk8x7Q9fKTa/3/AFHOS7HqRZd5EfZy9e8PyRSbLuO3bPwBFKsu8iM/I/d2gilmHvj2l37c+YIpdh/GN8l9AT7KYYkbFueOw+4ECmGZPLJ45YMvW4Kn81LsyeWTwfYefRAqqYYl4xk9uz9c+IIpdiVy3yQIpVmXywfZ29gJ/JSjM3kRnyx67eYe6fopJqYXYrxLf3gmFntzD7d/mXYHqizETC/1Y95dnzBFlJmGX+YvXdgEXOU1Xf8AP+2wIuT7cou3v7fiHdFoc4z7fmCdlsVNUfby8S/IEXCuaf8AqLfxyCLFXMLtV+4J+ixFze718QRYDszvV29h+i7A/VPZRrs3GeE/1MEUW9LznJ94d0UW9K574Lz7e8EUS9L54+OfmBRQ78rng8n39hGH8kHPood6TuZZyYqih3pG579+TFEUO+/nJEeO8M/wTKh3nv2Lv8fiCKKee577+fIExwol53cyztnc+8/cCKMdd7O0/WARRzi/f+Zgn5qPec7PWQQKOceU2ZKQtSFJPJKSo0qIy7SMsGRihaCMOAIPutccj4XslieWyNOQQSCD7gjsVe9G6np9TdTbYqWpj8pm1abOTOeNiEqcwqoRjJdNRVGGzVIOkol8K3+rafWtKODg4VKUnZu4FPqWr0ld6TSUbXXmWPoGXdDgx3Enlk8eYW5DOpzACerqyAD2U8IF12QsHiF271B4hKuoh23oKsVD/LpzUROqYsPpBWRtJk+SbMGyVAiine9rPJMJZI97OxDpY9IKjUHS2LSbFr0Gq1XUyJJjQqrRp0aW1BtgsM1mel6O44pqRN4zhs7JUlSnlEpLjOB1X2b22rrjq6as1DbpIaO1Pa50crHNL5+8TMEDIbjzXdwQGAgtkyvfH4lHjZ0rozw7W7TWz2s6O46j1/TSxQVdDURTMp7SMR11SHxvcWST9RoYeGua51U9r2TUnSuoykUmo12qU6iUiI7PqlWnRabToTCeJ6VNmvIjxmGy2LjdecIizsWdx3Sraymt1HVV9bM2Ojhjc97j2a1oLnE/kBlfMnpnTd81jqKxaS0zbZazUVzq4aWmgjGXzTzyNjijaPd73BozgDOSQOV3z6Z2XbfR90ij0ydLjxolu0uXcF31oy4UTKqccpNZqH4UuOISTJMRkmRudQ00jdRb+deq77dNydaS1VPC501TM2Gmi9Wx9XTEz2B5L3n8PW57uAvsv2B2p0H4JvDJQ2C73OCC2WO3TXK9V+MNnrDEJa6p7BzmjobT0rCDL8vDTw/U8c9KGrmo9T1Xv+4L2qRLZTUpPVUuCpRGVMosQuopdPLh+4a2IqSN1ScE4+pa+ajHfTRel6TR2m7bYaUhxiZmR/8AlJXcyP8AfBd+EH8LQ1vovku8TO/F/wDElvVrXdq/NdEyvqOikpyQRSUEP7Okphj6eqOIAyubgSTullxmQr82G6VwKgIpCk0ufXKpT6NS47kuo1WbGp8GM0k1OPypbyGGG0kkjPKnFl5DHq6qChpaisqpAymiY57nHsGtGSf4BTWm9PXjV2oLJpbT1C+pvtxqoqaniYCXSTTPEcbQACeXOA+w5XoP0g08g6WaeW3ZcMm1OU2ElyqSUERfbazL/wAepy1GRFxk5KWaUGe5NpSXYPPbV+pZ9VaiuV6lJ6JH4Y3+zG3hjft9Iyf84kr7YvDJsfafDtsjoTaq1tYaigpA6rlaAPPrpv2lXMSAM9Uri1hPIibG30X6Zkbfa9c9LXIuByotci6H49VRa5Fxsipha5F0PTC1yLof6KmFrkXGyd+VTC1yLokVMLXIuiT7pha5FwPVMLXIuiT7qmF+BdJ2/v8Ah3oreVXZeNmp1SF/TNGMiI1fzGvEuGa0ZMsLiwTffI+w2hvnby0/05qy107mZp4nec//AEY8H+Bd0t/VdNPH7vD/AIk/CtuhqSlqfKv1wpf6KoiO/wAzcA6EubyMOip/PnB9DEugcdyl8biAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi9CfsCukAVu6n6m9HKryiTA1Fo7d+2g0vjPhum0WVMV6Ix/idWlVTtl8n1fdyZU0tx57+PrQBuOldM7jUcX/AAi2zmlqCMf1FQcxOPGfonHQOf8AjyvIT4su0n9MaH0RvLboM1dmqTQ1ZGP+5at3VA93Gf2VS3oHOM1PZeqoeUxcvBlaZFsv+6qtMi2XphaC2XKq0yNBf90WmRaLyq4WgtlyqtMi2X49VVaZFsvRaZFov+6qtMi06T7quFtMxjPl7jPKrheU325XRFVZGodF6VFmUvgtfUx5m3tSERGDJil3/CjYplakdTGSy03dtJjmlSlOKUuZDWo93Sz67+AHetuodN1+0N8q83e1NM1EXHmSjc79pEMuJJp5DkAAARyNA4YV72/C48Qg1Po65bEakrs3yyNNRbi93MtA937SFuXEk0krsgBoAhmaBwwroAHo6vWxARZ9LqlSodTp1ao86XS6vSJ8SqUqpwH3Is6nVKnyG5cGdClMqQ9GlxJTSXG3EGSkLSRkZGQx6ukpbhSVVBXU7JqKeN0ckbwHMex4LXsc05DmuaSHA8EEgrFrqGiulDWW240sc9vqInxSxyNDmSRyNLHse05DmPaS1zSCCCQeF7vOgZ0paD00ujNb16VRFOmXfDhrsTWO3H48NcYrthU9lmrSHaUTf2b+Q3hAkJnx2uA2SZkqj/eUy4RfPH4idorjsTutcrDRulZZHvFXbJw5wd8u55MYEmerzqZ7TE92erqjEvAe0n5VPFdsTdvDTvbd9NUDpo9OySCutFQ1zw75R8hdE0S56vPpJGmCR3UHl8bZuBIwnq+tr2C1oydfL7uK99RnY3R8RcztT0+sO0vtCL1qNFncNQKgXLcNRjKiUGnUGQ8cBLkVM2XUI7XW9ZDcMjHbW6/EUvUW3OnrZYNMB25RpBHWVdR0mlZKz6POghY7qmfM0CUiQxRwvd0dM7QV3lvXxXdQw7T6Vs+l9Gtfu6aERXCuq+k0UczMx+fTU8buueSdoE5bKYYqeR3R0VLAV9/6v9J3oUezI02Z09okC3bfqcKIuXbmiGnDceZedZmyYyVRqrczrr78qnNVIkI62s1yQb0htJm2clxBNn1v0TtNvz4r9Uyamr6mqqaSR4bPda0ubSxMa76o4AAGvLMnppqVgaxxw4RNcXjqTt3sf4mfG9rSTV9zq6yroZZAyovVxLmUcLGuw6KmAa1shjy7po6KMMjcQHiBji8eUPpndNrVDpramR70vpuLQLZt5iVS9P8AT+kvvP0a0KTMeadmOKlPpbdq1wVdUdpc+etDZyFNNoQ20w0yy37H7EbDaR2F0rLYdPufU3Wqc2SsrJABLUyNBDR0jIjhjDnCGEF3QHOc5z5Hve7348NHhm0N4ZdFS6Z0s6SrvdY9ktfXyta2arlYCGDpbkRU8Ic4QQBzhGHvc58kskkj/lSM9yLw22Lcc4Lsd7HKmWnCMue5cg9UUg05nzLz37wRZ7bnj+37AnCkWnfiXLy8QRSTTuMb+uQIpRl7lv3YP3+7YEUqy/4793YfkCKVYkY5H5kfrkKIpZmQXYe3dn6eQrwhUqzJ7zyXzL3AilWZPLB58A/VPdSjMruPfnjx5fME/kpRqZyyeD8PX6gEwpJqX/uz58/h2h7Is9uXywePP47bh2T7LObmmXbn0XLIKvdZiJxdp58ywH5IshM1J9uC8/WA9sKn5rIRLNaiQk1KUoyJJFupSjPBEki3NRn2d4oSGguccAd09uOV68vZm+wRsyZZlq66dOOn1WuVq5qbFrtt9HspNTt6nW5TpqCkU6RqhMgvQK9NuJ5g23jo7D0ViFxdVMOQ4bjDHz3eNj4sep6fUd+2t8L9ZBSW2imfBUX/AKY6iSokYemRtsY8SQMpw7qaKt7JJJsddP5LA2WTlbTmhoTDFW3ppc9wyIuQAD268YOf80YA7HPYegyDpN0MOjnSadAjafdGzRellFRT4Jyre02sj7YxhlKkLnTo1Pk1R+QuEhTrjrjrr7jfG4pSyyPImr3B8SW8VfWVlRrDWmpK4yGR/TUXGt6Hc8hjHSNjDQ8hrWta1jXdLA1pwt+tpbPb2taKenhbjA4Y3+84z/vUbV9Dug70l6VVIc7TLoz6zwXWzj1GXTre04uybEMmm2EuNVyjMSqpSJjLXAlt5l9l5r7vCpOwzLbul4oNla+hqqPXGttN1TT1RtkqLjSsfyXYMMzmxSscclzHsex3PU08rS+istxa4OpqaYeuAxx/iOR/Fecn2onsGbcsGxbu6Q3QmZq7NNtCnSrivfQCoz6hcDxUKGpyTWK3phXKi9KrD66JAzJfo896S69HZdVFkKdJmE77I+Bv4rd71VqiwbQ+JqWnfWXCZtPRX6OOOnHnvAbFDcoYw2FomfiNlXAyNjJHsE8QjMlSzj7UuhooIJa+zAgMBLojk8epYTzwP3STx2OcA+Tpc4uz1+Y961xesRyaZ53+Kg5TCwHJZf6s+W39wVFguyzxzIv7eIKoUa7LLvyeD35+twT0UY9KzniV7g9MJyop6V448O8EUU9J7jxz37/3BFFPSOe+C+Zgiin5Gd84IvifkCDsol5/x8i/X3h/NFFvPc98n9BRFFPO8yz5n+QqnCjXXcfoCKOdc55Pz/QgRYDrmPPsBFHuuEWc/l8ARQ0h7mRHk8e4ufzDPZPuoN9w1Hj3mffkOE9BjsuDJnjJnsWC8CyZ4LuLJgqkk4BPA7fzX2B0MJmmNI1NcrV+16HSa1EhlFsliqtmzS3KrUDXHlzHam5/ycSdHiGbUdLxoSs31Gk+sSgj4S31g1ZW6UbQactz5qF7+qqMZzII2YLWiMfU5hd9Ty0EgMAI6S5eoXwprp4fNMeICbVm8+sqW26rpqXybDHVtLKR1ZU9Uc076t37CGojhzDTMncxkjqh7mO86OEH936dushNswdHKDLPjkJiVy9XY7uCKOfDJoVCd4Ffe68+Gc8hRFhKYyiMyUoi488POhi59Rri4w/S3qipQR+9+GWUZ9uYmkeplBxgLuN8YzxUNhpbP4WtGXM+bMIbhfXxv4EZxLb7e/pPPmHpr5mOAw1tA9pIe8DrHHbBfPsgIgIuwXoK6QHXLjm6rVqLxUq2Frp1tJebM0Srgfa/5ma3xtGhaaREcwRkojJ55Jl+Ex1x3+1u2226n0hQy/8ADKsB82Dy2EH6WnByPMcPbHS0+69sfg9+GA6v1zdvEdqu3Z07p9zqa1h7fpmuUjP2tQ3qaWuFFC7AIcC2edhHMZx2xEY6kslGO6+kjC1yMhsgWnC3ZF5r/uqLXIuh/wB0WuRcDlRa5GsOwqLXIuh6YWuRdD8qi1yLgf8AdUWuRcD0wtci6H/dUwtci4HqmF1P+0D1C/mV0WvptDdI49uQ1V+sJTxFmq1hBN09lf3uEzi0tBuFtnEkdnNkbIYLXcL/ACt+uof5bP8AQj/ER/pPOD/oL5yPjQb1C+bhbf7FWyozRWKlNxrQM81da0Np2O5wTFSN8wcZxVHlddg5zXiMgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIv2Do/6vVrQTWrTPWKgGo6jp/d9IuA2CM8T6dHkJbrFLcIlo4marSHX4yyyRGl0xtDX+kKLXuitTaOuH/c1wo5Ic/2HOH7OQd+Y5A14+7Vx1u3t5bN2Ns9bbc3YD5O726Wn6v8AJyObmGUcHmKUMkHHdq/ob2ddlDvy0rZva2ZiZ9u3dQaTctDmoNJlJpVbgsVGA8ZIWtKVrjSE8ScnwqyXYPnWvdqr9P3i6WK6QmO5UdRJDK32kieWPHOOOoHBxyOV8f2o9P3TSmoL3pi905hvFuq5aadhz9MsL3RvHIBIDmnBxyOVYxElyhlpkWy4BVWmRbLyi0FsuVVpkaC5VWmRbL0WmRZc9VWmRadIf0VcLaZiw6XCrhaZM+QxXzHPHZag3P5rcSDPn2jHdITnlZDYc4yF+Ta9aI2f0iNIb50cvmOl2gXtRJNMVJJlLsmj1EiJ6k12ASlNmmfRqi23Ib4Vo4jRwGfCoxvHbrX172y1rp/XGn5SLjQVDX9OcNkZ2kif3+iVhcx2QcZyBkBcnbS7i6h2g3B0tuJpiYtu1sqWydOcNlj7SwScHLJoy5juDjOQMgL+f5rfo7eWgOq176Q37CXCuayK5KpEtXVqRHqEdtXHT6xAUZqS9TqvBW3IYWlSkm24W5nkfSJoDW9j3H0fYNa6dnElquFO2RvOXMceHxv9nxvBY8EAgg8L6x9tNwtO7q6F01r/AEtUiSy3OmbKznLo3HiSJ44xJE8OjeCAQ5p4X5SN4LfSAi7JfZhdNJvoda+pmXfPls6M6lRGLZ1OZaRUZjVITGddkW7fDFKpzch+fPtea862tKGXnVU+bLQ0g3FIHVrxZ7EO3v25MFkpmO11anmegJ6GmTqAE1IZHloYydoaQS5rRNFCXkNDl0u8cXhpf4jdpnU2naSN25NlkdU2wkxsM3UA2oonSyFrWR1LA1wJexgqIadz3BjXL7N6YntwdR9Qyqdj9FelztKLPcU7Fkak1pEV/Uutx+F1laqLDbXMpNkQ30uZJxCpdS+4haH4quNscF7IeAHS+mflL/u/Vx3i9gBwoYi4UMR4I81xDZKpwxggiODlzXRzDpcutvhz+F9ozR5otUb8V0V/1EAHNt0Jc22wu4I855DJa17cYLSIqblzXRTjpeuh6rVerV+pzq1XanUa1WKnJdmVKrVabJqNTqEx5XG9KnT5jr0qXJeUeVLcWpSj5mPRCjoqO3UlPQW+kigoYmBrI42tZGxo7NYxoDWtHoAAAvVe32+gtNFS221UMNNboGBkcUTGxxxsbwGsYwBrGgcBrQAPQKOGSsxZbDxpMiM8Y5cvAiIsY2D7JwApyO/tjO5d/lzwYIpZtzODIyz8QRSDbmcd/b3GCLObc5b8sY8wKKRadz5/XfYEUg07jy+gIpNp8tsn5H3Aik2n+WT8j7A/kikmn+W+D+oIpRmT44Pv9dgIpJqTy3xv2fn3CiHCkmpXLJ+/3iqKSalH3kf1/QEWe1L5YPB7bGHKFZ7cw9smZ/P9wTgrMRML/V7sgg91mJln/qz7/wBMgi50y+ece7+4Iu0P2NuiFvdIn2ifR7sy74Mep2hbtXrmp1w0yWhp6NUWtNrdqd10OnSojxmzPgT7sgU9mUwolIcircJSVJyR9JfiJbp3faPwhbtai09VPg1BWU8NtgkYSHRm41EdNNI1w5Y9lK+d0bxgtlDCCDgrcekqGOvv9BFKMxNJeR79ALh/FwGfsvVH7ef2h+ovQu0X09010Qq/9MatdIGVdcf+uIqkqrNiWFaMejNV+fbxHtTbouCoXLFiwZ6iWcRhmWtkkyUsPseF/wALLwj6P8Ru5GrdZ7m2/wCd0DpOOld8k/iGtrqp0pgZP/lKaCOnlkmgGBK99O2QmEyRScma2v1RaKOCno39NVOXfV6ta3GSPYkkAH05xzgjwc3Dd9wXbWZ9xXVX61c1wVV9Uqp124KpOrNYqMlRYVIn1OovSZsx9WN1uLUox9TFos1o0/bqW0WG1U1FaYG9McFPEyGGNv8AZZHG1rGD7NaAuE5JJJXmSV5c89yTk/qSuS2L3uiyK7Trosu5bhtC5qQ+mVSbjtes1GgV2lymz4kSKdV6TJiVCE+hRZJbTiVEfaLd8sFi1Na6ux6ks1JcLLUN6Zaephjnglae7ZIpWvjeD6hzSEillhe2WGRzJB2IJBH5Eche+z2FXtA7+6b3R3vS29ZKgiv6vdHyt21bVcu/habnXpZ110yov2TcdwttqSld0KkW3VIcx9LaEyyhtvqNT7j5j5Tfif8AhQ0n4aN3tO3jbqkNJt/qymqKmCk5LKOrppI21lPTk/8Ae3TUU00TC4mIyviGImRBc46Lvk94oJo6t3VVwOALvVzSD0k/53BBPrjPclePT2ruiFH6NftBek1pZbUWPTrVYviPetrQIbS49PptB1Qt2i6kwaLTW1ob4YNuHdaqa2RZSn7JwkpWMn9CXgT3SuO8fhN2W1veah019dbHUdU95DpJJ7bPNb3zSEE/XUfLCoceCfN6iBnA4p1NQst99uNNG3EXX1NHoA8B+B9hnH6LrpXMI85Vn6dnd4jtsoFYbkw+z4+R5yCLAdl88q9xB90/VYDko998e/s8ufIEUc7K3PB58T5AijXZJn25+Rb+8EUY7J8c57uQIot2Rue+T7v1BOFGvP8APffn4ECKMde5779vnt4gijHXs5LP6mCKOddIvPkRevMEUe45z7+0/LsIEWC65jf4eu8EUe45zM+fcCKLffwR77FuXZn3h/NM/wAVBSHjz4+/csYyZh+qrz3WCCogIgIty1rcVxOLU4rCU8S1GpXChJIQnKjM8IQkiIuwiwKNa1ow1oA+335P8Srks0s7/Mnlc+TAGXEk4aA1oyecBoAA9AABwFtFVbQEVtsWy61qFdtCs632FP1SuzmobJ8JqbjtqPikTJB5IkRobCVOOKMyIkp5iG1DfaDTVmuF8uUnTSU8Zcfdx/da33c44aB7lcjbSbX6p3n3G0ltlo2kMt/u9WyFnGWxtJzJNIeOmKGMOkkcSAGtPK9Cdh2FR9ObPoVm0Jok0+hwW4xOmgkOy5H45k+Rg1H182SpTisqPBqxnBEPNPUOo67U97uF9uD/APhNRIXYzkNb2axv2a3AHA7Z7r7W9lNqtL7G7YaP2s0fCG2a00jYg/Aa+eX8U1RJycyTyl0jsl2OrpBw0KzGhReJfD9hGslXLAcCtM45jKZKDjnhV7rcRjKZJ25VMLXIvh604W7IvNef0VMLXIuB6LUXA5UQXA5Frkaw9UWuRda9UWuRcD0UdWavBoFIqlcqjxR6dR6fLqc588f4USCw5JfWXEaSMyabPBZLJ7DNoqeor6uloaVvVUTSNY0e7nEAf3lQGqdSWfRumtQau1BVCCx2yimqqiQ4+iGCN0sjuSASGtOBkZOB6rzp6hXhN1Ave57yqBn9puGsTKgTZ5xHjOOGmFFTk1YREhobbLfkkd/LHaobJaLdaYP6uniazPuQPqd+bnZJ+5Xw971bnXbebdjX+6N6J+evdzmqen/JxudiCEcn6YYRHE3nswKmiVXF6AiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi9fvsOeksWqHR0qmhleqCXrs0JqJMUhp5xopMvTq5JEmdRVoQlCHHW6HV/tUNSlGrgZOOnJFgh46eO3bM6V3Ho9eW+nItF+jzIQD0trIQ1smT2Hmx+XIAMZcJD7r53fiibKHQ28dDulaaQt0/qqHqmIB6WXCma1kwJyQDPF5UwAx1PEzsd13d5HRMu+68xFoNHUi0yLZcqrTItl6LTItF4VVpkWjJ6KuFtMxZfJgZVcLQzGM+b0BWoBakkz5jGc8n1V5kee4XMlv18voLRcspkK5iQNBKymx8LkJI0ErIaz2C6IfbXdB9erunLPSb06o32jUTSqlHFvuDToaFTLp04YU6+dQUlgkyJlTs551TpGZOrVAW4WyWUkPQ7wG7/N0Xqh+1Gp67p0xeJuqke9x6aetOB0c8NZUgBvdoEwaeS8r1J+G74khoHVsuy2rrh06Svk/VQvkeeimuDgG+WOr6WR1YAb3aBO1p5MhXkcHtCvexARARARARARCPB5LsBFnx3+Rf5i9c+fIDj0T0PKnGH848MZ8Ml2AOw90Um25ncue23wBFINuZ7frv+4Is1tzlv8At5gizmneRGePHvD9EUg06ZeufiCKQae8du7IIpFp/lvt3dpAikWpBd+c9naCKQak9x58+fhsCKRakl2Hj5/IPRFnNyeWDx4luQIs9uUeCyfF9QQ8dlmIl8sKx8wRZiZfkYIshMsu8y954BFzpl9yuzt/vgFX+S7hvYQ6rUTTX2nXR8XcUqPDpt9pvjTJqZIXwparl52VWoVqRmzNxsjkVe62oMBsj4sqlEREZmRl5/fE/wBF3PWngt3WjtMT5Ku1miuJY0d4aSshfUuPB+mKmM0x7cR5Jxlbp0XUsp9RUJecNf1M/VzTj+LsD9V6TP4hroEaq9KjR7SzWvQy2qrfF+9H+TdsS5bCoLL8+4Lm07vNuiSZtTt2ktda9V61aFXtxpwoUVv7TKhz5Cy6xUdpo/Hr4S/io0RsbuBrjbfcu8wWzS+q2UrqetnIZBT19IZmsjnlOBFDVRVDm+dI7y45YYWnobK943/ruyVNzpaaso4y+eDqy0cktdjkD1LSOw5IJ74C8H84p1NlyafUYsmBPhPuxZkGaw7FlxJLCzbejyYzyUPR32nEmlSFpJSTLBlkfT/T1NPW08NXSTslpZGhzHscHMe1wyHNcCQ5pGCCCQRyFwuQWkhwIcO64GXJEp9mNGbdkSZLrcePHYQt5+Q+8om2mWWW0qcdedWoiSlJGZqMiIsjXLLFDHJNNI1kLGlznOIAaAMkkngAAZJPAHKAEkADkr3pfw6/Qn1W6MHR71U1a1kt+q2TdXSUrdkzqJY1fhSabcNHsLTqJdTVt1ev0qYlmXR6hctQvaoPNxXm0PJhNx3V460ko+XT4uHiS0PvbuxofQm312guVj0dTVjJqyB7ZIJa2vdSmoiglYS2WOnZRwMdIxxYZnSsbnoLnc06Ds9TbaGpqquMslqC0hp4IazqwSPQnqPHfGF5efbf6r0HVP2nvShq9tyGpdJtiv2ppqUllZLJ2sabWFbFmXWhxSHHW1KiXdR57BcOPuNJyRKyPaf4bGi7nobwW7K268ROZX1lLU3AtIxiK4VtTV02AQD9VLLC/nPLjg4wuOtYVLKnUVxfH+Brms/VjQ0/+cCuppcvf9T+A70LbSw3Jfer4AmFhuSu7Yu8w/VP5LBXJzncz+RAiwXZPiXkWfcCKOdk57cF6+ofyRR7sjbnj6gndRzr/PfBfMEUc6/47ev1BAo513Pl2ECLAdextnJ/QEWA454+/wDIEWE47jmfu/XcEWA45zM/gCKMffwXPux8PiHCZ9FByHzz3nyxkv1zgMoFHmfEZmfMwRaAiAiAiAiAiAi7i+g3oMqzbZXqlcsLq7lu6J1VAjyWSJ+k2y4aHCkES8uMyq0tBLPZCijpSW5LMdIPEBuIL5dm6QtVRm1UT8zOaeJJxkY44LYgceo6yT3aF9K3woPCYdtNETb/AOuLX0a31FT9FvjlYA+ktjiHeaOrLmS1rgHcBjhTtYORK4L74U3ns8h1zDl7HNesVbRHz9eQvNfhX2yYWGtnG+MeXI+335+Ivtk+6yWyZ9VjmRp7NhkMl9FeBB9eUIxlMk+6YW4jGSyTK04WuRfD1TC3ZF1r0WuRca9UQXQ5UWo1hyLXI1hyovhzp06mla2m8WxKfIJFXv2T1cxKFI61m3Ka40/NNSTSakpnTOqZIyxxIJwu8c47IadN01BNfJ480tC36c9jM8EN/Ppb1O+xLSvIv4vu/o282Lt2z9mrQzUusZ+mcNI62WylcySckEEgVE/kwgjHUwTtzjIXTqO26+X9ARARARARARARARARARARARARARARARARARfdHs5+k070V+lTp/fc6Y7Hsi4JP9Cajsk44TDlo3M/HjvVB5lL7Dbq7eqTceoN8ZmRHGMuRmOBvEltc3dnabUWn6eEOvtOz5qjOBkVEALgwHBI85hfCcej/surPjI2Rj342G1dpWlpmv1PSM+etxwOoVdM1zmxglriBURmSndj0kz6Be8pl9mQy1IjutvsPtoeYfZWl1l5l1JLbdacQakONuIURpURmRkeSHz+SB8T3xyMLZGkggjBBHBBB5BB7gr5TJIpIZJIZo3MlY4hzSCCCDggg8gg8EHkFb8iyXLQtMi2Xj3VVpkWnPVcLQzFl0n3VcLbkYz5RzytQC1JJmYxnyZV1kZJC5Eti0XLJZCuckC2SspsfbhchJGklX2sXISRpJV9rP4reRDSSrzWrbIiRpsaRDmR2JcOWw7GlRZLTb8aTGfbU0/HkMOpW08w80s0rQojSpJmRlgVjmlgljngkcydjg5rmkhzXA5DgRggg8gjkHkLLpnzU00NRTyujnjcHNc0lrmuactc1wwWuBAIIOQeQvEd7VfoKyuiLrW9c1nU5z/gfqrNnViyn2kPKYtesrcVJrNjSXF8RIOnKc62EfGrrIS0lsptRF74+D7xCQ706DZar5VD/D+zxsjqgSMzxAdMVW0Dv146ZeB0yg+jgV9I/gj8S0O/O3Edp1BVj/GTZI2RVjSR1VEQHTFWtA7+YB0zcDpmB9HNJ6qx3AXdlARARARARARakZkeSPBgiz4755LvLuPnjHwz8A91RTTD+cblk98Z9d4cKuO3Kk23M75we364D8kWc07nzIEWahz+36AizW3seJfT9wRZzbvI8+8vzBFnNv8AeePEg4RZ7cjl9QRZ7cjlnfxLmH5Is5t/Yu0tvMEWa3JPsP3Gf7gmVmIlFtnbxL1jkCLMRJzjcj2BFkplY7TLy3BFzplmXJfxz2An3WQUv3+8gT9FL0O5avbdZpFxW/U59Fr9AqcCtUOs0uU/BqdJq9KltTqbU6dNjrbfhzoE1hDrLrakrbcSSkmRkQwrlbqC72+utF1ooqm11UL4ZopGh8csUrSySORjstcx7HFrmkEOaSCMFamPdG5r43Fr2kEEdwRyCPyPK95Xs1/4hHo768WVa+m/TCu6h6D6/UuLAos69bjIqNpFqZIStMNm4Y9zHx0mwKxKbJLtSi1ZcKmtvKU5FkG2o48f5h/GB8KvdfbLUd61fsDYqnU+108j5mUkH7W6W9uOswOp+Ja2Jpy2nkpmy1BaAyaLrHmy8z2DW9DWwxwXSUQ1oABceGP++ezT7h2B7H0Hmf8AbmdJ62ukx7RTVus2PUaNWrF0yp9u6O2vcFBfpsyn3H/RkNbtzVtFSpBuRKwh686rUY8eX1r6nafGjESybQhtv2N+Gps9d9m/CXoS26jpKin1LeZZ7rUwTNkY+n+beBTwmOXDosUkcD5IulgbPJMenqc5zuPtY3CO436pfE4GCMBgIxz09zkd/qJAPPAC+RPZ+68Rujn02ei/rPUZSINCsvWOzXLrmOuIaREsquVJFs3vINx5xtlBs2jWpqiNakoyX3jJOTHPXio23m3c8OW9G3lLCZLlcdP1YpmAEl1XDGaijbgAk5qoYgcAnHYE8KMsdWKC72+rccMZK3P+iTh3/mkr2N+0r/iD+jpoTYd26cdDu9KJrp0hKpHmUKm3nbTaa1pHpk8718OTc790qxRr+rVPIuspkSlnOpjr3C7KkE039nk/Pv4PvhW7sbmansWrt/tPVGmtqoHsmkpKg+VdLiB0vbTim/raKF/4aiWpENQ1mWQxdbvNh5Uv+t6Gjhlp7VKJq48Bw5Yz757OI9AMjPc44PglqlcqFZqE+rVafKqVVqs2VUanUp0h2XOn1Ca+5JmTZsp5a3pEuVJcU444szWtajMzMzMx9PtHSUtvpKWgoaZkNFBG2OONjQ1jGMaGsYxoADWtaA1rQAAAABwuF3Oc5xc85cTkn3yopck+8z9+BkqixlycZ3wXogRYi5RZ5mrzBFhuSj7VYLuyCLCcke7xME91guSOZkfvPkCLAcf38fEEWA4/45P5ECcBYLjue33mCe6wXHuePefrxBFhLc+fzBFhOO+JZ+gfmiwXHO0/r6MO6KOffwR593jt2GH8kUK+/wA/vZMjPBHnmffsZ5DlOSow1Go8n+3jt4mCLQEQEQEQEQEQEX110RNAHtZb5TVq3FUdhWg/HmVxa0rJurT0qS7BoDSk4JX2g08b+5cLBGW5qIhwtvTuQzQ2nzR0Eo/wirWlsQGMxs7PmP8Ao9mccuP2K9C/h4eEao8Sm60V81PRO/xTaeljnrnEODaqYEOgoGEYz5pHXPyOmBrv3ntB72ER2mG22WW0MsstoaaaaQltppptJIbbbbQSUIQhKSIiIiIiLA89XSPke6SRxc9xJJJySTyST3JPqfVfWPTQwUkEFLSwsipY2BjGMAa1rWgBrWtGA1rQAAAAABgBbFI8BqDllNcuBSOz1++BrDlda71WOtvwF1rlea9Yq2S328i7PHIvB/ZX2yHjlYamcbkRl7jwfh4C+2T7rIbJnjK4dy57eu/kMhknfJVzg9lrkZLZPumFuyMlsgz3WnC1yLwflUwt2RdDvZFqLocFRbVuIbQtxxaW220qWtxaiShCEkalLWpRklKUpLJmexELrepzg1oJcTgY7lW5ZYoIpJ55GshY0uc5xAa1oGSSTwABySeAOSug3pIanr1W1XuGvMPKcodOd/kNtoNSurTR6Y442iQhBuOJQdRkqckKxtl3wHfXbvTI0rpW32+RgFa8ebN7+Y8Akdh+AYYPs1fGf46/EDJ4jfEdrbWVHVOfpGik/o+1tyekUVI5zWytaXODTUymWpd08Zlx6BfhA3wunqAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi9rPsg+lSXSI6L1JtO4al9r1H0Q+xWJcRSHusm1K3Go6v6Lr6+M+sdJ6lRzhuKwf+NCUZnlZDw08Z205213Wq7xbqbo0zfuqrhwMNZMT/wmIegxIRKB/ZlAHZfM58Q/YY7Pb519/s9F5ejNUdddT9IwyOoLv+GQDHAxK4TNH9iYADDV2tZHT9z10IwtMiy5/qqrbkY7peTyq4QiMxjukJ4VxrCcccLkJH9xZLlkth55C5UoGguWQyH3C5SSNBKyWswuQkjSSrzWLkJI0Eq+1i3kkaSVeaxcpJGklXmsXISRpJWQ1mV+F9Jbo62H0pdG7t0Z1DjGqj3HFJynVRllt6oW1cURLi6LctLJzhIp1LkrzjiT1rSltGokrMxyDtXudqLaLXFm11pmXFdSvw+Mkhk8LsebBJj9yRoxnB6XBrwCWhcsbN7p6n2V3AsW4GlZcV9I/EkRJDKiB+POp5MfuSNHfB6XhrwCWgLwI9Ivo/6gdGLV27dHNSIColftiapEea2063T7gor5qXSbipC3Sy9TatEInGzIz4Tygz4kmPo42w3I03uxoqza40tU9dtq4+WkgvhlHEkMgHZ8bvpPvw4cEL6fdqtztMbwaGsevdJVQktdbHktJBfDKOJYJQO0kTstcPXhw4IX4eN/rkRARARARARARakZkeSBFnMSMYyeDLBHnkex957GCccZUwxIzsZlnwz548A9Psq4Uo26R439/wCoKizW3eRK+IIsxDvjjly/MEWW27jG+D9cgRZqHiPtwfyBFmIeMu3wxzLwBFmNv+OPXduCdlmof5Z28SBOFlokHtvn5GCLLRJ5b48Dwf7giyUyfRHn3gnCyEyf93LvBFkJlH24P39n9wRdyHstPZDage08o+q1y23rFa2kVvaUVm2KDUZVdtmp3XUaxUbmhVSoNoptNp1WozbbFPi0zLq3n2+JTyCQSsOGjoP4z/HppfwcV+ibRd9A1t9ut7p6iaNsNRHTMiZTvjYTJJJHKSXuk+kMY7Aa4uLfpDt0ad0vNqBtTJHVNiZGQDkEk5yeACO2PddY+ttjw9ItaNXdKKfcD91QNMdT7+08hXRIo6bdkXJEsu66tbcavv0BNVrqKE7WGaamQqGU6YUZTnV9e8SesV3G261VPrnb7Qutqq1toqq8Wairn0zZfPbTuq6aKd0InMUJmERkMYlMMXmBvX5Ueekbfq4BTVVVTNf1Njkc3OMZDSRnGTjOM4ycdslfmP2sv9R/H0WNxvJY/quwTosey/6d3TGVTp2inR7vaVZtST1zWp15xU6f6ZKiEskOy4N43adLp1xFHUouNmk/zCX3NHg8dXN6fGh4adghV0+4m6lvZf4eDb6R3ztw6vRr6Wm8ySDPOH1Pkx+7wpu3advF06TS0LzEf33fSz/WdjP6ZP2XpP6LP8K/aVMTT6/0yNf6ldE5PVPStN9CI6qHb6XEtNLOPO1Iu6mO1yrRFvqWhxEWiUp3gQSkSCNeEeRG9Xxrr9WGrtewG18VFTHIbX3l3nT4yR1MoKWQQxO6cEGSrqW5JDoiG/Vvy3bcxN6X3WtLj/Zj4H+u4ZP6NH5rxUHK7s/MfQ0uKFxKlH5e8E91wKlH/q93rzBFjKke/wAzBPVYy5Jd/wAOzwD80WIuRjw9+4IsRcju38+RAiwnH+e+TBFhuPGfb28i7PmCLEcextnyLs+QIsJx0z7fd2AixFuc9/0IEWG492FnIIsJx3Hb+hfUPVFGvv4Lnnu+nvwH2VRz3UK+/wA+0z5Eff8ApsK+yp+vKj1KNR5PmYonstARARARARARARX/AEx03uPVi9KPZNrxzdqFVfInZCkKVGpsBsyVMqcw0boiw2cqVuWTwktzG29WaptejbFXX+7y4poW8Nz9T3n8Mbf85x4Ht39Fyrsrs9rHfjcjTm2eh6My3m4SgOeQTHTwt5mqZiPwxQsy5x4zw0cuC9FGmGmVvaSWTRrHtprhg0tnikS1ISiRVKk8SVTqnL4ckciW6WcZPhQSU5MkkPMjVuq7nrO/11/ur/8AhEzvpbnLY2D8Ebfs0flk5OOV9iewuy2j/D5thpza/RUOLbQx5llcA2SqqXgGeqlxn9pK4Zxk9DAyMEhgKvK2+4bfa5cztesdSOYuByvB3ZcKkeu0XA5XQ5cCkfmNYcrgcuFSMi4HK6HLHW3kjL1+wuNcrzX+qxVsF5+73ZF5sivtlWIpvHIu0+f07MC+2Qq+1+e649y5lgZLZVcWpGMlsvGFTC3ZGQ2TgFacLXIuteqL5O6Yeq//AA50sl0mnSequS+evoNO6tfC/Gpqmy/ndQTwnxJ4IjhMpP8A1vEZchy/s3pX/CPVMVZUR9VsoMSvz2c/P7Jn+sOo/Zv3Xm18UHxHDYzw73LTVjr/ACteav8AMt1N0uxJFSlo+fqRjkdMLhA0/wBucEHLSukgd3V8lqAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi+/PZtdKt7ondJ207rqk1xjTu8lNWLqWxxH1CbcrEpko9cW3xJSp62aoTUxJmZH1SHE8lmOu3ig2iZvBtTeLRSQB2paEGroT6+dG09UQPtPH1Rn/OLT+6F1M8aOwse/2x+oLDQ0zXaxtoNdbXfvfMQtPVADzxUxdUJ/zix37oXuuYksS47EqK+zKiymW5EaTHdQ9HkMPIS4y+w82pTbrLraiUlSTNKkmRkeB8+c5lgkkhnicyVri1zSCC1wOCCDyCDwRjIPC+V6WCWnllgnicydji1zXAhzXA4LXA4IIIwQeQeCubBmMR0nfBVAwnHC3pT6/YWXO9VkRx+i5ko9evAWyVlMjxyuQkjQSr7WLkIhpJV9rfst5JGklXWsHsuQiGklXmtW8kihKutYuQkjQSshrPsuUkjQSshrP4rmSkaCVlMjXMlItkrKZGutD2mns/qN019J/ttuMwKbrpp7AnS9Oq5IJDCK3GMlSpdi1iYSeJunVd4jVGcXluLLUSz4ULcUXazwo+JGu2G1l5F0fJLt9c5GNrYhkmJ34W1cTfV8Y4eBzJGMclrQu5Xg/8S1w8PutDS3Z8k23F0kY2thbkmF3DW1kTfV8Y4kaOZIhgZc1gXhpuW2q/Z1wVm1LppE+g3Hb1Sl0it0apx1xZ9MqUF5TEqJKYcIlIdadQZdpGW5GZGRj6BrVdbdfLbQ3i0VsdTa6mJskUsZDmPY8Za5pHcEH/AHHlfRRarrbr5baG8WitjqbXVRNliljcHMkjeA5rmkdwQcqEGes9ARARARARAROW5bAizGZGDIj2PlsWSLOCI8fIP1Q9jlSzEnOx9vbyLBkYIpRp4jLw7wPdFmodMt+Zesgiy0OkfL9gRZSHf7fuCLKQ8ZfoYJystD2fDz9bAiykPGXI/wAyDCLKTI5dniR+sgiyUSP92fy/IEWQmRy5l79v7AiyEyP92T8f22BFzFI/bB/v4gi9cv8ACrdK3TuxtRekL0Wb2r9Pt+6tZU2PfWkyak+xCZuatWVGuamXhasOTIW39quF+kViDNgxEZW9HhTFJLLeD8NfjT7L6q1NpTa3ebT1tlqrNYPnKO5eW0uNPFVup5KWpc1oPTA2WKaKaQ4DXy04PDsjkrbq4wQT1tuleGyS9LmZ9S3Ic388EED7FduvTa/hz+ip0u9ca3r3buo2oWg913/cs66dWqRasCi3RbF51uqvFLrFw0en1tUWTaNz16e49InSEvzYMiQ6bpQ0OG4t3o34d/iv72bFbcW7bK66UtepbJa6RlNbJal8tPUUsMY6YoJXw9TamnhYGxwsLIpmMaGGoc0MDNy3bQ1uudW+tjnfDI92XgAEOJ7kA/hJ7k8jPp7/AEfox7J/2V3s8rRm6o1nTiwHP6Qgrm1rXHpQVyj3nNpTbJJWVRbfu1mFYNq1EibNLbtIpVPkOcZtkauPhPibcHxveNPxUXyn0ZQatubRXyBkVo09DLSMkJP4CKYvraiPnLm1VTPG3AcQOnKz6TTenbHEah0DPpHMkpDsf630g/6IBXyt0lf4mz2fujn8zpGjcfUTpPXVEI24q7MorljaeLmINROx51733Hp9XJhBpwUmm0OqsOZI0KUn7w5l2i+EB4n9e/J12vprVo6yv5cKqUVlcG+hZSUbnxZPqyesp3t7OAPCj6/X1lpeptKH1Eg/sjpb/rOwf4NK8+PSX/iben1rK1PoujcTTzowWxLTKYQ/ZdITe+oZxJLii6mVe18sTqWxIaikSESaZRaXJQs1OIWlRt9X6kbQ/CA8MWgH01w17PddY3hhaSKuX5Sh6mju2koyyQtLskx1FXUMI6WuaQHdeyq/X16quplKGU8f+aOp3+s7j9Q0FedE5PiZ7/TkPVr+S2MuNUgz+YIuFUn/AHEXkCLgVI8zBOVjrkH3kQqixlv+/wAfWwoixVvGfb8OXxBFjLeIu3n2ECLEW8Z9uMb+IIsVbvrt/YEWKtzH6F+YfdFhuO57cFy8OwEWG48RZwfv8QVQox+RjlvufeXbjt8hUEe6p+YUQ8/3bnncs+PbnJ5Maf1T81hGZmeTPJiqLQEQEQEQEQEQEUhSaTUq7U4FGo8KRUqrU5TMKnwIjanpMuVIWTbLLLad1KWtXkXM8EMasrKW30lRXV1Q2KjiYXPe44a1oGSSfspSyWW7aju9tsFht0tXeqydkMEMTS6SWWRwaxjGjklxIH954XoF6KvRtg6DWaT1TbjytQLljx37nqLZE4mE3gnWbfhPmRGqLCWeXVFhLrxcRZJKTHm3vBulUbiXzopHOZpulcRAw8dZ7GZ4/tPH4QeWt47khfVr4EfCBbPC5t6ay9xwz7s3mFj7jO3DhAzh7KCF/cxwnBlcPplmHUMtZGV9RKb59ngOJA5d9mv7LEW3jP0F5rlkNf2WOpHr3douByvNcsdSPAXQ5Xg5cKk/3FwFXA5cCkei9ENYcrocuE0C4HK4HLgUju9foNYcrocsdTWez15douh6vNkWI41j5ln3bi816yGSLHNBlnmfM+XrIvtkwrwcCtnr18BktkzxlVWjjrbLa3XXENNNIU4464pKG220JNS3FrUZJQhCSyZmeCIhlRudI9rGNJe4gADnJPt+foFZnmhpYJqmpmbHTxtLnvcQ1rWtGXOc44Aa0AkkkAAZK6HektqwvVvVCrVaK+py3KKaqDbDefuHTYbqycnEnJkS6nKNbxn/AKTSXYPQPbHSQ0hpWio5mYuUw82c+vW4DDfyY3DfzBPqvjg8fHiRl8S3iF1NqS3Vbn6FtRNvtTc/SaWB7uqoAyQHVc3XOT/YdG3s0L5+HIa6VICICICICICICICICICICICICICICICICICICICICL2Q+xp6X3/H3QMtH7vqhytTtC4kKjccp5xybcGnazOPa9YNbzzzr7tESkqZIV90kpaYPGXMjw88c2yn+Lncb/Day0nRpPUL3S4aAGw1o5qI8AAAS/17BySXSDs1fOV8Rzw6jandk7i6doejRGqZHzYaAGU9wH1VUWA0Bom/7pjHJJdMM4Yu5ZKB0ZJXnayPsuUkeA0ErJbGM9lyEkaSVeaxbySNJKvNZ7reSRpyrwYuQiGklXWt9FvJI0kq81n8VyEkaSVfazK5UpGglZDI1zJSNBKymRrmSkWyVlMj7Bc6UC2SsyOPthZCUC2XLNjiXQ97Xn2Ybev1v1LpIaFUVtvWu2Keb962pAYQgtUbdgsmpcyGhvhIr2o7CMt5IyqDBG2Zk8lBr9D/AAVeLJ23Fypdrtwq8nQdXJilqHnPyEzzw1xP/espPP8AkXnqGWFwb6U+CPxYy7c19JtXuHXk6CqpMUlQ85+QmefwOJ/70lJ5/wAg89Qyxzunx6PsPxX3o0ll2NJjOuMSI77a2X2H2Vm26y804SXGnWnEmlSVERpMsHuPbiOSOaNksTw6JwBa4EEEEZBBHBBHII4IXt0x7JWMkjeHRuAIIOQQeQQRwQRyCO64hrWpARARARARARARczbpoMs8u08mfyDhFKMyTLG/nk9y33Lnj9wGPRU4PZSbMjODz/fHy5hn0VfQLPQ6R9vryBFlJeMtj+IJ9llpd8c+f6mH2Rc6XfH4/qCLIS8ZY3wCLJS/3/EEXOl4j7SMFVc6XzLtP6gqLmJ8/A8Ai5UyOXMvn4/AEWZCqkuny4s+BLkwZ8GSxMhTYj7saXDmRXUPRpUSSwpt6PIjvNpWhxCiUlSSMjIyyLU8EFVBNS1MLJKaRhY9jgHNc1ww5rmnIc1wJBBBBHByqgkOBacOH/3cLvS6J38RV7Rjo0N0ugXZf1I6TNg09DUb+ntd4kquXSzESeHTgap0qTTb9kVBaSIkO1iXWWWyLZnmPOPez4WHhS3dfWXOy6Zn0jqaUl3n2ZzYacuPbrt0jZKJrO5LaaOlc71kW7rbra+UAaySYVEI9JOT/rjDs/mXfkvTL0Uv4nroIa1lT6Hr5Sr26Kd5STjsuyLliyNRdL3Jb6GkJZh33Z1LKuxE/ajWS3qpQKbEZa4Frfwayb8id6fg/eJDb41Vx2zrrdrSws6iGwObQ3ANBJJdR1Unku+nBDaetnlc7qa2LhvVv23a/tFX0trGvp5fv9TP9Zoz/FoH3X2vqN7Nj2SftFrUf1Ho+luhV9sXA2tmPrT0abjpFtVNdRLjcVMlXLpNUo1DuGuRftOHEVlioGkiSh1sybQlPX/Sni28cHhUvUek6/WepLbJSnLrVfoJaiMM4HS2C5Rumhhd05aaV8IPLmP+pxMrPYdN3yMztp4Xh378RAOfzYcE/wCln7rot6TX8JvKQmfWeh70nEPmSnVwdP8ApD0bqVdX99aUFqhp7TFoW9jDaG12shJn95TySyPR7aH42sTjTW/fbaAtGAH1tklyM8An+j62QED94kXBxA4EZ4W0K/bg/U+2V/8AyZB/02j/AKH6rxdnJ/3H6+Q9+VxathyOfM/zBFxHI8iBFwqfPvP14n2AgXCp4u/s8w/VFjqf7t/E/WwIsdb5n2+4vWARYynfdnu3MEXAtzbc8fUwRYq3uePj+4IsVbuOZ5P189hRFguSCLO/j7u3HuFT6Iot6SR5327eWNiyfZnP6CiqCVFuvGrZJnj+5YwfgK885VPYrHBEBEBEBEBEBEBFvaacecbZZbW686tDTTTSFOOOuOKJKG20JI1LWtRkRERGZmY0ve2NrnvcGsaMkngADuSfQBa4opJpI4YY3Pme4Na1oJLiTgAAckk8ADkld4nQn6I6dNaXF1S1EgIVf1XjE5QqNIbJX9IUuQ2RpdfJWSOvzm1ffwRFGbPhLKzUaegG/W9B1VVzaR0zUEachfiWVp/7pkaew/8AEsPb+2eeGgZ+jf4dHgbj2ktdDvXutbAdza6HqoaSRoP9F08g4keD/wB+zNPP/veM9AzI53R2FuNeGfyHWlr165Mf91hLb/uL7XLKa9Yi2xea5X2vWKtv3H9Rea5X2v8A4LGWjs+Iuhyvtd6rHUjuFwOV4O91wKSLgKuAriNI1gq4HLhUjt5C4HK4HLhNPrv8xrBV0FcCm/D479nzFwOVxr1jLaLy9fIXWvV9sixVteG+/nt2+JC81+PVX2yffhfFfTQ1g/4fWB/R1Hl9VdF9svQuJpakP0+3En1dVmZQ4hTa52fsrZ7kZKcPmkc87E6LOpNR/wBOVsWbRbiHc9nz9428g5Df6x33DfdeWPxV/FJ/iX2VO12l7h5e4WsYpIMscRJTWwfTVzgtc0tNRn5SM8gh8xxli6Zh3lXysoCICICICICICICICICICICICICICICICICICICICICL6c6H3SUuTona/WNrHb5uyIlHnfy67qKhSCbuKzKrwxbho7hOIcR1q4iutjrxlqU02sjI0jife3au1bybcag0PcgGzTx9dNKc5hqo/qhkGCDgO+l47Ojc5pzlcKeITZmy787U6n27u4ayeoi8ylmOc09ZF9VPMMEHAf8ATIOzonPaQQV797Cva2NS7LtbUCzKpHrVqXjQqdcVBqkVxt1qXTapGblR1GbS3Etvtpc4HW88TTqVIVhSTIfOFqKwXbSt9u+m77SOgvFDUPhmjcCC18bi09wMg4y13ZzSHDghfKfqfS960XqO+aT1HQvpr7bqqSnnjcCC2SNxa7uAS04y12MOaQ4cEK3kQhSVDBueVvIhpJV1rey3kQ0kq61q3kkaSVeaxchJGklXms+y5CSNJKvtZ7hcyUjQSsljFypSNBKyWMXMlItkrKZHyudKBbJWZHGshKBbLlmxxfZZCUf2FouWdHF2WUhHIWnOUhFEOOOV5sPa8eyeVdjdxdKjo0W6R3Ky3IrGrWmlGjER11lpCnpt6WtBYThVUaQk1z4jacvJI3kEZksh6m+CvxjizOtm0G6tz/7VOLYrdXSu/qieG0tQ8/8AFk8QyE/ScMccdJXqt4MfFwbWLZtHujc/+1xLY7dXSu/qieG0tQ8/uE8QyE/ScRuOOkrylrQtpa23EKbcbUpDja0mhaFoM0qQtKiJSVJUWDI9yMew7XNe1rmuBaRkEdiPcL1tBDgHNOQVtFVVARARARARARARbkqNJkZHy9fMFTCzGpODSWcb5z8dvh5gPuqk+uFJtS+WTIvHOeflkMenqg7KRbkFtvseBTKfqstDpHyPAqiyUPH27+8EWQh7Pb3bHvz7ARc6XeXP3foHui5Sd8S+gIuYnj23P+/iCLlJ/wAS9fAEXIT/AHkCLkJ8u/17wRbuvL/V8gRfbemfs3+n5rJYkPU3S/ofdIO9LBqkFVTol1UfTG5l0m46clDiyn2s8/AYVdEN3qlJbdp6ZKHHCNCDNf3R191d4r/DRoPUc+kdYb6aXt+pYZPLlp5bhTiSB+QOioAefl3DIJbMYy1p6iA3lStPY7xVQientkz4SMghhwR9vf8ATK/EtP8AVvpDdFHUabVNNr81Y6PuqNvS3aVWlW1W7p04u2nyYi3GpVDuKFGfpVQNtBrW2/BmtqbUlSkONmSlEfIOp9GbXb0aVp6LVum7JqfR1UwSRCohp66me12C2WBzhIzJwCyWJwIIDmuBAKxYaitt05dBNJDUNODglp/I9v4FehToufxR3Tf0Uepdu9KawLO6TFsFHgSV1eXAa0d1a/lc+HFfps2PXrZor1k1aC5TnkSWjftz7ROJZKVNJKyWXl7vH8Hjw77gR1l12c1LX6RvHU9oiY83S2+YxzhIx0NRKKuN4eDG7oruiHBApyW9K3pb9f3akLWXCFs8fvjofjHHIHSeOfw5PuvL39oLvHsGtgLYb/mfxBFxHI8g7ouJTxn2mfkCLiU74/PJ+twRcKnT8/PYgRcKniLmfhsCLHU93fH9wRYy3e0z9GCdliuPl3+/uLx9wfyRRzsruPPftnbP6AnH6qMdk89878i29+23aCAg+qwlLUrmfo+YJlbARARARARARARARbkpUtSUISpa1qJKEJI1KUpR4SlKSyalKM8ERcxQkNBc44aFVrXPc1rWkuJwAO5PsF3O9CLoWKobdL1j1ZpZFWHUNTrLtSezn+WNuJJbFeq8dwsFNcQZKjMqL/DIyWos8JDotv7vuLg6r0Po2s/4ECWVVQw/1hHBhjI/dHZ7h3/CPVe8Pw9PAZ/Qf9D7771Wf/tuQ2a1W2Zv9SCMsrapjv8AjCMOp4XDLBiR4z0hdqTjXuPsHUBr17aMkWAtvw9xjIa5ZTXrCca5nj3DIa9ZTJPusFbfh6+oyGuWU16xVt+GS7RdDlfa/wC6xVoF5rlfa5YykY/UXQ5X2uysdSP1FwOV1rlwKQZC6CrocuE0/wBxrBVwFcSkb7/sNYcrgcuFSN/A/XuGsFXA5cJoI/Wf2Maw5XA7CgbirNKtiiVW4a3Kag0iiwJNSqEp5SUIZjRGlOuGRrUkjcUSeFCc5WoySW5iStlDWXavo7ZQQmStnkaxjRyS5xwP09SfQZJ4CgNX6x0/oHSmodbaquLKTTlro5amoleQAyKJhe7uQC446WNzl7y1jcuIC89WsmptS1b1Br151DjbZmP/AGajwVGk002iRMtU6GnhSlPGlkuNw8ZU6tRnnI9LND6UpdF6at1ipsGRjeqV/wDbldy935Z4aPRoAXxe+KDfy/8AiT3p1funey5lNVTeVRQEjFLQw5ZTQDAAyGfXIcZfM+R5JJX5cN3Lr6gIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIvSf7DbptJgzJfQ71GrHDFqLs2v6K1CozEJQxPNLsy4rEbVIJJ4nkk5sBsnP+2J9tCDNxJDyy+IFsIaiCHe7S9FmaINhujGNOSzhsNWcf2OIpjj8PluJw0rx1+Jl4bPnKen8QekLfmohayC7xxsJLo8hsFaQ31j4hnd0/gMT3Owxy9PBEPJrK8XQ1byIacq6GreSRpJV5rP4rkIhpyrwblciUjSSr7GLlSkaCVksjXMlI0ErJZGuZKRbJWUyP8AgudKRoJWYyPsudKBaLlmRxdlkpQLZcs6OL+KyEIFpzlnxxY/NZaECy5ykYof4rLQgWHOUpFD6ALzT+1j9j4m7f6j6TPRTtxtq5iTJrOpmklHjoaZrxpJT866bJhMpS21VVERuS6egiS8eXGSIzNA9U/Bv42jZf6L2p3huhNq+mKhuMrsmH0ZT1TjyY+wjmPLeGvOMOXqL4S/F4+2stu2W6txLreOmOiuEhyY/RkFS48lnYRzHlvDXnGHLygvsPxX3ospl2NJjOuMSI77a2X2H2Vm28y8y4SXGnWnEmlSVERpMsHuPYyOSOaOOWKRronNBa4EEEEZBBHBBHII4IXqux7JGMkjeHRuAIIOQQeQQRwQR2K4hrWpARARARARARARARciXFJPvLtLvPvzjmH5Is1qSWSwZkfL68yLfl7g5ynOMZUg3KLBEfiW3y58w9Snf1We3JI8b+7t+WSDHsnoOOFlJeSf9w/knK5ku9x+7yBPTuucnj7SBFzJeLHMyBMrkJ3xIwRbyd9EYJ+S3k74n9QRduXsNOjTYHSx9pf0fdNtU6fEruntCfurVC5bYnR2pUG606bWxUrmoduVKLJZfiTKLUboiQSqMd5Cm5VOS+weOsIy6S/EP3c1Nsv4TNzdV6Nqn0+p6htPb4KhhLX03z1RHTyzxuaQ5srKd03kPaQ6OcxyD8OFuPSdBDcb7RQVDQYRl5Hv0gkA/YnGR6jIX9Ajple2A6BnQG1Ls/RrpA6oz6TftyRKbUpdu2ZaNavR6w7YqTyo1OuO+W7fjvnQqbIS0txmI0mRVHYyCeaiLaU2tfzNbD+BzxIeJPSd815tnpCKbTdK+SNs9VUxUorKhg6nwUhmI857SQ10jiynbIeh87Xtc1vMl01LaLPPFS1lQRM4A4a0u6QexdjsPtycc4Xnh/ir+jFp5cGnfRo9oPplFoUl67atA0jvu7bdTCcjagW7dNozr50cuyTLhIT/ADUqdSbbqcRuoOKdU7ClQmOMm2WEj1C+DPu/qi16n3Z8MerpqljKGF9yo6afrDqKenqWUl0pmtef2fXJPTyuhAaGyx1EmC+SQrZe4VvhfDQXmnAy49DiP3gR1MP3wARn2IHoF21Xh0M+gh7XP2YVgWxo5W9ONUa5pnolQdLtB+kFbNOlUW7NOdTdMrDo9KpVt1x2sUakXrRrdmTI8RNbtysQ2DepsxEhMdqSUKW10psW/fiO8Efi81Nd9dUF1s9uu+oZrheLLUPbLTV1BX1kskk8IilkpZZ2tdIaWuppXdM8TojK+L5iF+45bXaNSWCGOleyR8cQZHIBgtcxoAByA4Dt1NcOxzgHBH80GW1IgypMKW05Hlw5D0WUw4RE4xIjuKZfacLfC23EGRl3kPrOhmiqIYqiB4dDI0OaR2LXDII+xByFwUQQSD3BWIbvrPwF1Uwthu+JF8QRcani78+HzBFxG93ECLhU8faZf2BFjreIs9vmHGEWMuQRc1F7z2/QP5p7LBclEWe3zPlzLsM85wKj0yU/IqPdleOc45cj8T7DwRCmPRO+SsJTyldhdvj6wHPun5LhBEBEBEBEBEBEBEBFyNNOvutsstuPPPOIaaaaQpx111xRIbbbbQRrW4tZkRERGZmeCGl72RsdJI4NY0Ekk4AA5JJPYD1K1xxyTSRwwxufK9wDWgEkknAAA5JJ4AHJK7r+hR0Ef5Cmlau600lK6yZMz7Ssie0S0UslETkesXBHWRpXNIsKZjK2b/EvfCR0L358Q39Ims0XoSsIoeWVNUw4Mno6KEjs30c8d+zfde6HgO8ADLK6z7z752gOuw6ZrdapmgiE8OZVVjDwZBw6KnIww4fICcNXbE61zyW3YOnDHr2pjk7KMdawMpj1nRyKPcaPtGSx6y2PWC4367xktcspj1guNdpC+16ymPWGtv49wvtcslr1iLR3e8XmuV9rljLRzMvgLocr7XLGU33fD3i8HK+1/usdSfXaLgKuhy4FIFwOV0OXCaRcBV0OXEafXaNQK1hy4TR+foxrDlcDl1WdPPXMn3mdF7amEbcZTFRviRGeSZLkYQ9TaApTef8AsCMn5CTV+M20mWUmO4Hh22+Mcb9d3WD6nAspGuHYch83Pv8AgYcduog8heAXxdPFmK2qpvDBoa6ZpoHMqL7JE8EOkwH01vJb6R5E9Q0u/rDExzQY3LrFHbFeD6AiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAinLZuWu2bcVDuy16pLotx23VYNbolWgOqYmU6p02Q3Khy47iTI0uMvtEfcfIyMjMhH3a1W6+Wy4Wa7UjJ7XVQviljeMtfG9pa5pHsQT/McqNvFntmoLTcrHeqKOptNZA+GaJ46mSRyNLXscD3BaSP5cr3nez96YVB6ZugNDv1tyNE1At5Ma29U7fZUylVLutiPk6gww0ZGikXGw39rinwoIsraIstGY+drxI7JXHYvce4adc179N1PVPb5jn9pTk/gJPeSAny5OTnDX5+sL5gvFR4fLn4ed07lpgse/SdWXVFtnOT5lM539W5x7y07j5UvJJwx/8AxgX3KRDr6SuuDWrkJI0kq81i5EpGklX2R/ZcpJGglZLWcfZcxJGglZLY/suVKBoJWSyP7LnSkWyVmMjWQlItErNZH2WQlAtlyzY4lkoQLTnLPii7cLJQgWXOUhFCstCBZc5SUUXYALMQgWXOUnDF2GFmtt4GO5yloYcY910Ee1J9jZQOkMxW9e+jNTKfa+uLbT9Quyx45MwLc1VJtHWLmQ0ESI9FvkiSZdYXDHqJGROEl4iW56OeEXxx3LbSSg263Wq5avb8kMp6s5fPb88BrjyZaT7cvh7tywlrfQTwyeLGv0L8lobcapkqdGkhkFSculovQNf3MlN9vxQ/u5Z9LfG/c9r3FZVw1m0ruolTtu5reqEilVuhVmG9AqdLqEVZtvxJkSQhDrLrai7SwZGRlkjIx7h2m7Wy/WyhvNlr4qq1VMbZIpYnB8cjHDIc1wyCD/8AYPK9ZrfcKG7UNLcrZVxz2+dgfHJG4OY9rhkOa4ZBBUEJFZiAiAiAiAiAiAiAiAi5EuqT258+fxD1QYBzhZSZPZnHx5n+ooOPRBnv6rNblHzznt7O/sPuFSVT3HqstEsu/G3h4gtXsMrKTI7vr4gOVT25XOT5H2kCLkJ4j7fXYH80XITvbxB2Rb+uP/V68/eHZF2Keyg6UVz9EPp/dHTW627SujUBmj3XMtu7rFsqi1K5bquewL2odTtS9o9u25STTNrtwUe36s9VKdGIyQ5UIDHWGTZKHV7xnbR2je/w1bpbfXW90dtfPRNnpquqlZBT09ZSyx1NI6eeT6IYZJo2U88ndsM0nT9RCmtPV8lsvFFVMjc8B2HNaCSWuBDsAdyAcge4C9RPt2PYidKHpo9IikdNHoYW9F1IXqnYFlU/UXS67K/TNKr5pFbtmgsUigXDT4OrD9k0+n06oWhFgRZ1KqD8KrU6pRXDcYV1ziY3kJ8On4gu0Wwe19dsJvzc32ptnudU+huFNDJcaSWKeZ0s0L324Vb3vZUumkiqIWS088EjemQeW0y7+1bpSvula26WtnX5jGhzCehwIGAQH9IwRgEEggjtzx9t3R0Ael50hv4e+i9CjWPShVv9MTSuyqFSdP7MrOoGmtfTLn6Lakpk6Zw6Re9EvKsWNT0XFo1BboTJy6nGagqkKbeUy0gnB1/s/iW2R2w+JvcN/wDQusxU7GXmvmkraqKiroell0oemvdJSS0sVW/yLo91W/y4HulDA5ge9xapWSz3Kt0Y21VNP03ONgDWlzT+B304cHFvLPp5Ix64C/b/AOH16AfSB9nx0QdQ7Q6TlOp1o6i6l6zVTUZVjU+6KDdcaz7dZsu0LahFVa3bNRqttOV6oSqHJdklClymERkRyN018aUce/Ez8Su2nib3w0vfNpKqWt0vabDHQ/Nvp5qd1TOaqpnf5cU7I5xCxs0bWebHG8yGX6A3pLsrRtnrLNbZ469obPJKXdIIPSOlo5IJGeDnBIxhfzWNWa/AuPVPUq4aU+Uil12/7yrNNf2w/T6pcVSmwnfuqUn/ABIz6T2My35j6udGUFTa9H6UtlYzpq6a2UsT2+z44I2OH6OBC4OqXB9RPI38Je4j8iSvz03e9R/H1gblyOysrjN4u8wRcRvl5AnK4VSPHHj3c+0+zAeipn7rFXKIs7mfw88+Qr7BVWI5KPfkXfvnl29hCn6qiwnJWc78R89j2+PLHxAf7lVYqnVq7TLyMwT8lxAiAiAiAiAiAiAiAiAik6LRatcdWp9CoNNmVes1WU1CptMp7DkqZNlPK4W2I7DSVLcWo/DBFkzwRGYxK+vorXR1NwuNUyChhYXPkeQ1rWjuSTwB/wDeUjaLRdL/AHSgslkt81Xd6qVscMMTS+SR7jhrWNaCSSfb8+y7++hl7P8ApmlDVO1N1ghxa1qQtDUqiW64Tcqk2USy4ifeSZKbqFxmRkXGeWopZJGVmak+cO+fiRq9YvqtKaJnfBpcEtlmGWyVWPQerIPt+KT1w3APvt4KvALatrm23c/eGiirdxyGyUtE7D4LbkZD3jls1Z9+WU/ZvVIepvZc814eu4dVWPXq1HIop5nsxt3DMY/7rOjk+6jnWvDbsGSx6zY5FGOtc8+PryGWx6zWSKPcazz/ALDJa9ZbH+ywXG/7jIa5ZTHrDcbI/PvIX2uWSx6wnG/D9xfa5ZTHrEUjw3F4OV9rvvwsZbfhv6+QuhyvNf8AwWOtGfPvF0O/gr7XfwWOpGPWwuAq61y4FI9foLgcrrXLgUkXAVdDl+BdIvWen6I6dz7iWpp24qiTlLtOnLNBnMrDjeSkOIVnih0xtXXPHhRbJT/nIcj7Y6FqdfamprY0FtsixJUPGfpjB7A/2nn6W9vU+i6meM7xPWjwt7NXbWBkjk1rXB1LaaZ2D5tW5v8AWuae8NK0+dLwQcMjI/aArz41WqVCt1OfWKrLenVOqS5E6fMkLNb0mVKdU8+84o9zUtxZn3FyIelFHSU1BS01DRwtjpIWBjGgYDWtGAB+QXx6X6+3fVF7u2o9QXCWrvldUSTzzSEufLLK4ve9xPcucSfYdhwsAZKiUBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF9w9AHpkXF0L9eaNfrByKhYFf6m3dULZbU6aKva0h4uKfFabUX/XVvPK+1xFYVlSVNmRpcUQ4A8R+x1s3127rtOSdMWpKbM1BOcfs6ho/A4n/iph+zkHGAQ4EFoXW/xR+H60eIfbG4aWn6ItT02Z7dUEDMVS0cMcT/xM4/ZSjjgh4IcwFe86zLvtnUG1LevezazCuG1bqpEKuUCtU55L8Oo0yoMJkRpDSy3SZoXhaFES21kaVESiMi+dm+2W66avFzsF8oZKa8Ucz4ponjDmSMOHAj8xwRwRggkEFfMlftOXnSt8u2m9Q2+SlvlDO+GeGQYdHIwlrmkfmMgjIcCHNJBBVqJIiCVHtjXKSRoJWQ1i5SSNBKyWxrmSkaCVksjwudKRbJWWyP7LnSkWyVmxxrISjuFolZscfsFkIQLbnLOjiWUhAsucpCKLsspCBZc5SUUXsstCBZc5SUUXss1tHIWHOUrBFjHCzm2+Qx3OUvBDjBws1psWHuUtTw5wurv2ifsqdH+nZQnrlgfy/TbX+l0849A1LhwCOLXUR0KOFRb/AIcNBPVmmIXhDcpJKmxGzwg1oSlou2/hk8YOtvD1cGWqo8267cTS9U1C5/1REn6paNzuIpD3dGcRSHl3S4l67UbDeIbVG0FSy2T9dfoqR+ZKVzvqiJ/FJTOPDHHuWHEbz36SS5eHLpH9GTWjooaj1HS7W6zZ9qXHDU65AkrSqRQ7kprbptIrNs1ltP2Os0p48GTjR5RkiWlKth9AO127GhN49L0urdA3yOstj8B7RxLA8jJiniP1RSD2cOcZaSOV66aK11pjcKyQX/StzZU0LwMgcPjdjJZKw/Ux49QfzBI5X4EORlu5ARARARARARARARARARakZlyP1sf5Ai5EvKI9zz3/ALdwemEPbjuucpOO/wCHcXdvvkATyn6LKTLx/mzvjn279u3MUznIVMDk5XOmWeMbeBke3bvgVwfVVOVzJlkRb9/f2F+f5h/1IOfyW8pZd59nPx57Aq8/qu1f2JnSK0f6LXtNujfr3r1ecXT/AEm08ia5zbtuyZT6xVmqa3WOjlq7bVHZbplAp9VrNQl1W4a1Ehx2Y0d111+QhJJ3yXTrx+baa23g8Jm6m2+3NjfctZ3R9pbTU7XxRmQxXu2zykyTPjiY2OGKSV7pHta1jHEn3n9LVlNb77Q1lXJ0U7PM6j3xmN4HAyTkkAYz3XsWuP8Ai0fZl0SpvQKZp/0vrxitKUSK1bml+l8WmSSSs0kplq7tb7WrKUrSRKLrIjZ4PciPJF4a2r4MPiwuFIypq9TaIoZj3inuFe6Rv5mmtVRFx2+mQ/bjBPJT9w7ExxDYalwHqGMx/fID/coH/pdHs2//AJivTc//ACb6Eef/AKSYkv8AsKnin/8Ax+2//wDl13//AIGtH+Mayf8AvWr/ANWP/wBout/2in8VRQdb9B9RtDehnovqRp3UtUaBVbLrOserVRtam3HbVn1+JIpdxt2nZtnVW7Yca6KvSJLkePUl1rNLNanWWlyCZda7TeF/4PNft/uNpfcLfbXtqulLaKmOqitluZUPgnqYXCSA1NVUx0zjTxyND3wClxUYDJHtiL2Phb1r9lXST0lspXsdIC0vfgENI5w0E8kcZ6uPQZ7eNA5hd/yxyx2cjIe7C4yHutipZY8ccs+O++BTn2Tt+S4TlnnPYWNs9nbvvyBPf2WOuV/u38FHzx3ZwXyFfRD+Sx1SS7M93yLfsFOeyZK4VPqM+z9t+fmZh+qLhNRnzM+73d3kCLQEQEQEQEQEQEQEQEQEQEX6po/ovqLrrd8SytNrelVyrPmhct9JG1TKPDWvgVUazUVJOPT4TZ5+8s8qwZJIz2G0Nba70xt7ZZr9qm5sp6NuQ0d5JXYz0RM7vcfYdvUgLkba7ajXe8eqaTSGgLFJW3WQguI4igYTgyzyn6Yox6uceezQTwvSf0Ueg7YPRlpTdXkfZbv1SmxCaq14yIpEzTUupScmnWtHfSblPhKV91Tx4kPpL73CkzQPLHeLxAaj3XrHUUXXRaRjfmOmDuX4/C+oI4e71DfwMPbJAcvoe8Kng50L4dKBl5qvKu25k0XTNXuZ9MIcPriomuGYmHs6Q4lkbw7paSxfYj7Wc+HzHCEb13eikxhRDzPMsevgM5j+ykI5OxUS613kMxj1IRyexUW61zyX5DLY/ss6N/bCjnWuz5jKY9ZjHqOdaxnYZTHrNjk+6j3G+8hkNcstj/YrCW33lt8BkNcslr/usNbedj5i+1yyWv8AZYS28cy9/rAvtd7LJa/2WKtvvF1rlfa9YykdhkLwcr7XfdY6kd5ZIXA5Xmu9u6xlIwLocrzXKFrtXpVu0ipV6uTWKbSKRDfn1CdJWTbMaJGbNx11aj54SWCIsqUoyIiMzIhn2+irLnW0tut8Dpa2d4YxjRkuc44AH/X2A5PAUNqfVNh0Vp29as1RdIqLTtuppKionkPSyOKNpc9xPrwMBoy5ziGtBcQF56ukXrbUtcdQptwLNyNblO46badLUayKHSm1/wDnLqFGf/PVJZdc8eCwZkkiIkkPSzbHQVLt/pqntrcOukuH1EnH1SEfhH+YwfS39T3K+QDxkeJ6+eKbeC6ayn64NH0fVTWqlJOIaRruJHAn+vqXDzpjxhxDAA1jQvwQcjLqcgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIvQn7Fr2hKNNbih9E7WK4EMWBd1Tcc0ouGrOkmPaN2zzI3LUlTl/+bUG530kcbrD6qNPVjiQh5Rl5p+O7w0nVVsn3k0RbS7UlFEBcIYx9VTTM7VDWfvTQD8fT9T4R2cYwvLX4g/hX/wytU+9+grWXaroIQLlBEPqqqVnapawfinpm/j6fqkgHZzo2hes0kjxrJXiO1i5CSNBKyGsXMlI0ErKZGudKBbJWXHGudKRbJWYyNc6UC2SsyOP2CyUoFouWfHH2AWShGRac5Z8UWT2WUhAsucpGKL0WWhAsucpGKL2CzG0cthZc5SkMOMcLNbbGO5yloYexWa2gWHOUtDF24Ug2j167RjPcpqCEcccrPabGM9ymqeH3C/Bukr0UNDul5p1K0y1zsyJdFEX1z1IqbRphXNatRfaJo6vateQ25KpFQJKU8WOJl7gSTrbiSIhyJtXvHuBsrqeHVe319fSV4wJIz9UFQwHPl1EJIbIzvjOHNySxzScrlDb3XWqtu7wy9aVuToKngPYfqimaDnoljyA9vfHZzcktIJyvEr7Q32OWvvQplVO+rUjz9Yuj91i3o9+0OnOuV20Y6nzbbiahUKIh1dL6tK0Yns8cF3i3U2ojQXvd4Z/HBtzvzDSaevEkdj3JwAaSV4EVS7GS6jlcR5mcH9i7ErcdnAhx9SNpPEFpfcqKC3Vpbb9WYwYHu+iU45NO846/X6DiQezhyuncd3V2BQEQEQEQEQEQEQEQEQEQEQEQEWuT7z7O3u5fAPXPqi3dYv/AFH3+seYemEW4nll258+YJ+q+wugT0RLw6fXSx0o6Jdi3Zbli3Tqx/XX8rum7GKnKoFL/oTTa8dSpv29ijsyKiv7bTbNejNdWhWH3kGrCSUZcJeIve6zeHLZvWO82obNVXC0Wb5TzKenMbZpPm66moW9BlLWDpfUte7JH0tcBk4CkrRbZLvcKe3RSBsknVgnOB0tLj2/0cL0f/8AQ+elrv8A/ZZ9HbwzQdStv/6HvsPLz/s3OzX/AMDOp8f+Wof/AGq3p/i3uP8A4Rh/g7/qT/ofXS2/9LPo69v/AMA6l+7/AOBBT/s3GzX/AMDOp/8AbUP/ALVP8W9x9LjB/B3/AFLT/ofXS2/9LPo6+H/UGpf/AOxA/wCzcbM8/wD8mtT/AO2of/ap/i3uH/hCH+Dv+peP83nD7SLyL9cj2x9MLjlbTcWf+Y+3t7/LAItuT7/XL6B6YRaAiAiAiAiAiAiAiAiAiAiAiAiAi7D+iR7O7VbpIvQrpr7MvTzSbjS47dFTiLRVbgZS6SFsWnS30oVNJZJVmW5wxUcOxrMySfWbejxNaP2tZUWi2vZc9ZYwKeNwMcJxkGokGenHH7NuZDns0ZK7n+G7wXbg76z0t9urJLLt1kF1ZKw+bUNzgto4nY8zOD+1diJuO7jhp9I+kGg2megNoMWXpjbjFEpiOrcnTFmUis1yW2g0fzCt1RSEv1CWZGZlnDaOIyQhJGZDy21ruJqvce9SX3Vl0dUVZyGNH0xRNJz0RR5wxvb3JwOokhe9+0W0e3+y2mYtK7f2NlJQ8GWQ4fPUPAx5lRNgOkf3xnDW5IY1oOFf32sZ29eHZkbcjeuY4pO3KhpDOMmXr4DOikUlDJ7qHfZyRjNjepGKTBUQ8zzIy7PXvGax/ZSEcnbCi3meYzGP7LPjk7KMcaMs7bfQZTX/AMVmsfn15Ue632Y2GSxyzI3qNda5930GUx6zGPWC43j9ezfkMhrllsesFxvHZsYyGuWSx+fzWItv13C81yyGvWG43jyF9rsrJY/P5rEWjs7Bea5ZDXfxWOtHwF1rv4q8138VjKRjl6/YXQ5Xg5dOHTp6SKbnqb2jtk1InLdo0pKrxqUNWWqzWGN00dmQX/a0+luH/i8H3HZBYyokEO8Ph82tNppI9b3+lxc52f8ABmO7xRH/AI0j0fIPw55az0BcvnY+KN40Br29VPh120vAfoy2VAN2qYT9NZWR9qRsg/FT0jv6zp+iWoHdzYmk9bI7TLxlQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEXIy87HdafYdcYfYcQ8y8ytTbrLraiW2604g0rbcbWkjSojIyMskNL2MkY+ORgdG4EEEZBB4IIPBBHcLS9jJGPjkYHRuBBBGQQeCCDwQR3C9mHshPaKM9JmxY2hOq9Zj/8AHbTuioTTahMebZlal2bTG22G6u2lRpKXctDY4UVBLZcbzRFJNP8A2qh4ZeNbwxv2p1DLuHo6hd/i8uc562NBLaGpkJJjP9mCU5MJdw12Ys/gC8E/HL4VHbS6kl3J0TQO/wAXF1qCZI2AltBVSEkx8Z6aeY5MJPDHZhB/qwu7hKR0HJXn+xnsuZKRbJWXHH9uFzpSLZKzGMXOlItkrLjjz2HCyUpFslZ0cfoslCBaLlnxxduOFlIQLJKkY4/sslCBac5SEUWccLMbQLDnKUghx6LMbQLDnKVhhWa2jkLDnKUhi5Cz22+QxnOU1BB2Wc2gY7nKYgh7cKRab5DGe5TdPD2yFJNN8hivcp6mg7cLLegRZ8WTBnRY82FNjvRJkOWy3Jiy4khtTMiNJjvJWy/HfZWaVoURpUkzIyMjwLLKianmiqKeV0c8bg5rmktc1zTlrmkYIIIyCOQRkLcVG2SJ8csTy2VpBBBIII7EEcgg8gjt3Xnc9oN/D+aXa4qruqXRGkUfRvVKT9uqlQ05mEuNpXeM9bZyFIphRmH37Fq06SlREbSHKapx77zbCUmoemnhr+JHq7b8W/SO9Ec980i3ojZWtw64UzM9OZOogVcbG4/EWzgN4fIThdx9rPElerGyms2t2yV9pGGtqBzURjt9ecCZoHviTA/E48Lx6679HjWjozX5UNNdctPbh08u2AtzhhVyGtuLVIraiSmp0Kpt9ZTq3Sn0rSpuRFddbUlZbkZ4Htvt5uZoTdfTtNqrb/UtNc7LIB9UTsujcf8Ai5Yzh8UgwQWSNa4EHhd4rBqOyaot8V0sNxjqaJ3qw8g/2XN/E1w9WuAK/FxvtTaAiAiAiAiAiAiAiAiAiAiAiAiAi+wOgT0w7t6AvSx0p6WljWlbt9XTpP8A11/K7WuyRU4tv1T+u9Nrx00m/b36O/GqKPsVOvJ6S11a05fZQSspNRHwn4i9kbN4jdm9Y7M6gvNVb7ReflPMqKcMdNH8pXU1czoEgcw9b6ZrHdQP0ucRzhSVouUlouFPcYo2vkj6sA5weppb6c9nZXo+/wCmG9LP/wBEvo7f/wA/1K//AG2PLr/sI2zX/wAMup/9jQ/+yW9P8ZFw/wDB8P8AF3/Wp23P4xXpHRaqw9dvQ10TrdESaftNPtzUG+7WqrpE60ayYrFThXhEjmpglpLigu4WpKtySaVR90+CFtfLRyMsu+V/p68/hfPR0lRGODjMUbqZzucHiVvAI4JyNbNyK0OBktkRb9nOB/ic/wAl6IIPtRNaOmh7Mi5OnV7MS09NKnqnpi7cv/E3o89Ia07qvKqIqlh0qJW73sC36npvqdp0X9UotqpR63QpX/ON1yDIjx1RYcx9bcfzMqPCRoTYrxX2rw9+LK83WLSF2bB8jebNUU9NGY6uR0VLWTR1tBW/8HM7H0tXH+ydSysklE08EYdLvIX6qudifdrFGw1EeeqOQFxy0Zc0Frm84Ic089QwMAnj+WIPr0XAiAiAiAiAiAiAiAiAiAiAiAiAiAi/TNKNHdTNb7riWVpbZ9YvC4JSkcUamRlLjwGFqMlTqrOXwQ6XAaJKlLefWhBEk9zxgbU1jrjSmgLPNftX3uChtrAfqkdgvI/cjYMukeeAGsBOSFvDQ+gNY7kX2n05omwVFwu0hH0xtyGD+3I84ZGwckve5rRg8r0S9En2SNjaW/yu+ekE9TtRr8ZKLOiWfHJT1iW5KSgnkpmE8007dFQivqIjNxKYZLb+6h1J8Q8yd5vGfqDV3zen9tmS2vTrupjql3FXO3OPpwSKdjh7EyYdy5hGF7BeHfwB6V0W+g1Tu7JDedTt6Xso25NDTuxn9pkA1UjXY/EBCC38MgIK7bjhMRWGo0VhmNGjNNsRo7DaGWI7DKCbZZZabJLbTTSEklKUkRJIsEQ6YieSaR8s0jnyucS5xJJJJySSeSSeST3K9MqNsNPFDTwRtZAxoa1rQGta1owGtAwAABgADAHAUPIZ55IZsb+ymYpO2FBSGeewkIn9uVKwyduVCvtcyMvLPeM+N/qpOJ/Y5UM+zgzMZ8b8qSikzgKIeZ2Pt9c+QzI3qQik7KKea9d4zWPWfHIot5rmRlyGUx/ss6OTthRjrfMsDLY5Z0b/AFysBxvnsMlrllsf2Ue43jO23kMlrvvyspj8+vKwHG8ZGQ1yy2PWG4jHZkX2uWSx2VhrR4bC+138VkNd9+ViONn2FsYvNcslj/dYi0e/wF4OV9rl8C9NXpMtaUW+7p9Z05v/AIh3PAUUmQytDjtrUSUlTa5yyLJM1Oe3xJjEosoTl0i/CY7G7D7Uv1jcmalvdOf8GaST6QRgVEreej7xsOC/HBP0Z7heXPxIPGozYzSU+0e3lzZ/jZvVKRLKwgvtlFKC0ynGeiqnbltOHcsYTOB/VOPRmta3VrddWpxxxSluOLUa1rWszUta1qM1KUpR5Mz3Mx6CNa1jWsY0BoGABwAB6BfMrJJJLI+WV5dK4kkk5JJ5JJPJJPJJ7raKrQgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIrnp5qDeGlN72zqLYNbmW5eFoVeJW6DWILqmn4k6G4S0Z4TLrGHk5Q62f3XG1KSojIzEFqbTVk1hYLtpjUdAyqslbC6KaJ4yHMcMH8iO7SOWuAI5CgtTaasesbBdtL6kt0dXY66B0U0Txlr2OGD+RHdrhy1wBGCAveB7PLp12T039H4teYdh0TVm048WnapWN1qEv0+p8PVtXFRmzPjl2vXlINxhwizHdNTDhEpKTX88XiY8PV+2B1tNbpGPn0bWOc+gq8HD4+5hlPZtRDnDm/vtxI3IJDfnS8TXhwv3h91xLb3skqNEVr3Pt9XgkPZ3MEp7NqIc4cOz24kbkEhvYOlI61ErrmxnuOFzpSLZKzI484WQlItErOjj/gslCBaLlnRxfZZKEi0SpCOPtwspCBacVnxR9llto7xYc5SkMWAMhZiECy5yk4Yj7LMQjwFhzlKRRduFnNt8thjucpiCHtws5tAx3OUxBF6KQaRyGM9ymqeHspBpsYz3KbpocY4Um0jkMR7lP00PbhSTTfIYr3Kep4e3Ck2WxiPctwU0PZfjuv3Rj0L6U9iStOdedOLe1Dtl9L5w0VeKRVShS30ISdTtutxzaqtBqSDaQrrYzrZqNCSXxJLhG+NuN2NwdodQw6o261RU2y7NI6jG79nK0Z/ZzxHMczDkjpka7GSW4PK3/pXUN90rXMuNhuMlPU8Z6T9LwP3XtOWvHfhwOM8YK8lPTo/hvdVtPDrGoHQruB/V+zW/tE5zSS6JEOn6n0aOk5b5xbdrB/ZaHfDTLCGkIQsoE9a1YS28e5+zPh8+KNo7UwodN78W1tkvh6WC404c+glP0jqmi+qWkJJcSR50QAyXMHC7kaH38t90bDRaupxSVpwPOZkwuPHLm8uiJOT+80e4XmjvOybx06uSq2df1rV+zLrocp2HV7cuekzqJWadJZcU041Lp1RYjymjJaDIjNOFYyRmQ9VbFf7Hqe10d805d6aus9QwOjngkZLE9pGQWvYS08H349V2EpqqmrYI6qjqGS07xlrmEOaR9iMgqriXV9ARARARARARARARARARARARfVPQm6IWpXTy6TumfRQ0grlj25qJqr/AFn/AE9WdSKlX6PZcP8AobT669SKt/OajbFs3hXI/wBoodnyWo/UU6Rxy3Gkr6ttS3UcP79716V8O20+q949a2+4VWmbP8r50VCyGSqd83WU9DH5TJ56aJ3TLUxuf1zsxGHlvU4BjpC126e7V0FvpnsbNJ1YLiQ36WlxyQCewOODyu/3/ohXtJ//AJt3Qd//ACla9f8A/NI84P8As1fhY/8AxB3A/wDkNo//AI4t3f4ur3/76pf9aT/2S5o/8IT7SBUhhMrXPoRsxVPNJkvR9Q9d5MhqOa0k86xGc6OMRuQ823k0oU60laiIjWkjyWiX41nhdEchh2+186YNPSHUVpaCccAuF7cQCe5DXEDnpPZVG3N6yM1dLj/Sk/8AZr12ex79mjSvZNdE65NMbr1NpN+3rd961jVzVm/WGJFu2XSpCLdo1Fj0ahN1eXxMWxa9Et7rXKhLTHflPPvvOoZaJlhnxZ8bfiqrPGTvJa9WWfSk1usNFQR263UhImqpAZpZTLKY28z1Es3SIYy9kbWRsYXv65JORNN2Nunre+CScPlc4ve7s0cAYGfQAdz35PHAH8prUefQqrqFflUtZko9s1K87on24wTfUkxQplcnSKQyTPEvqibp7jaeHiPhxjJj7C9L01xo9NadpLu/qu0VDTsnOc5mbExshzxnLw45wMrgGYsdNK6Mfsy44/LPCpgnVaQEQEQEQEQEQEQEQEQEQEU/bFq3Letcp9tWhQKxc9w1V9uNTqLQqdKqlTmvuLS2huPDhtPPufeWWTJOC5mZEI67Xi1WG31N1vdygpLZC0ufLM9scbQBkkucQB/Hn0Uha7TdL5X01rs1vmqrlM4NZFEx0kjieAGtaCT/AAXeD0VfYo6g3f8Ay28Ok9V3dOrbWTEtGndBejzL6qTKjjukxWqknr6XayHGluJUhP2uUlScKQ2e5dAd4PHnpqy/NWTaaiF0ugy01swc2kYfqGYmcSVBBwQT5cZByC5eguy3gH1JqJ1Je92691qtBw4UUJa6skH0nplf9UdOCMggeZKDwWsPK9A+lWhelegtqMWXpPZdHs+htJa+0Ip0cjn1SQ0Si+3Vqpum5Pq05RrUfWPuLNPGZJ4U7Dza1huDq/cW8SX3WV9nrrg4npL3fRG0/uRRjDI2cDhjRnAzk8r1d27280VtlZIrBofT9PQW4Y6ugfXK4D8c0hy+V/J+p7jjJAwOFdZTPPYQML+3K5Pgk7cquymdz25iTif25UxBJ25UBJZ57CSiepaGTsoOQzz2EhG/7qUik+6g5DO5+vHAkI3qUhk7KHeayR5549dozY34wpGKTGMKGeaxksbGM5j8491JRvzj3UU8zz2wMyN6z4pOyi3muZd3ohlses6OTsox1rORlses5kijHW+eSGWxyzWP+6wnG+ewvtcspj+yj3WuewyWPWWyTssBxsyyMhrsrKY/Kw3G/D1+oyGuWUx6xFo8PMXmuV9rl8xdJvpCUDQCyXqm6tifeVZbei2jb3Gk3JUvHCupzU54maTTjPicUZf4isNpyZmaeWNp9tbjuRf2UjGujscBDqmbHDW+kbPeR/YD0GXHgDPUfxi+LTTHha24nukkkdVuJcGPjtVDkFz5MYNTMO7KWnz1PceZH9MTMlzizztXPc1bvK4KtdFxz36nW63Nen1GbIUanHn3lGo8ZM+BtssJQktkpIiLYh6bWm1UFjttHabXTNioKeMMY0dgB/MnuT6nJXydax1hqPX+qL5rPV11lrdSXKofPUTSElz5HnJ79mj8LWjhrQGgAAKBEittICICICICICICICICICICICICICICICICICICICICICICICICICICICICICICL996NHSR1L6Kmrdt6v6X1M4lZokhLdSpT7jv8ouehPLT/MrdrbDZl18CoMkac/iaXwrR95JDjjdXa3Su8GjLporVtJ10NQ3LJAB5kEoH0TRE9nsPPs4ZaeCuOd1NrtK7waLuuidXUYkt9Q3LHgDzIJQD5c8RP4XsPPs4Za7IJXvi6IPSy0v6ZOj9F1W02npbdWhiBeVoS3mjr1jXQlhK5tCrDCFHxNGrK4kpJdTMj4WjCiWhHznb17N6t2O1tXaO1TT5YCX01S0HyaunzhssZPr2EkZ+qJ+WnI6XO+dnebZXVux+tq3SGqafMWS+lqWg+TV0+cNljJ7H0ljP1Rvy05HS531WlI4fJXF8cefyWQlAtFyz44+3CykIFouWfHEshCBaLlnxxdgsttAsucpKGL3CzEIFlzlJwxZ5wstCBYc5ScMSzm2xjucpeCHtws5tHIWHOUvDF2Wc0jkMZ7lMU8PbhSDTfIYz3Kbp4e3CkWkchjPcp2ni7cKSaQMV7lO08PZSTSOQxXuU9TRduFJtIGI9y3BTRduFJso5bDEkctw0sWSOFLMo5DDe5bkpYcABfLfSj6CHRY6Z9uLoHSC0lt67pbcZyPSbxjM/yW/rd4m1IQ7Q7wpnUViMTHGaksOLdimr8bShy5tH4h93tiro247bazqaKEvDpKZx82jm5yRLTPzE7OMFzQ2THZ4XI2ltS3/TMoktFwfGwnJYfqjd/pMPH6gB33XlT6ZP8MhrNYx1a8ehpf8AC1itls35bel9+PwbW1KgsF9skfZaPcH/AC9o3Z1DDbaE9cdKkOLVhLbh7j2C2N+LBoXUHydj3z03JY7qcNNfSB9RQvP0jqkh+qpp8kuJ6fmGNAyXNC7Hab3coa5scN+pTT1B48xmXRn7lv42/wDnD7rzWas6K6uaD3bMsTWfTe89MbvgrWh+g3pQKjQZyib4OJ6Kmcw03PiH1ieF5hTjSiURkoyMeqGjdeaL3Es0GodC6pobtZZAMTUszJmc+jugksdwcteGuGOQuW6Sto6+FtRRVLJYD2c0gj+7t+RX5iN2LKQEQEQEQEQEQEQEQEQEX3F7N3pm/wD1Pnpo6M9L3/hv/wAXP+Ef/ET/ANz3+sP6B/qD+vtKb60x/wDts/pa9P5T/Kf60+3f+9knr/s3U/4fWdajr/4o9i//AHSmxOudlP8ACn+hf6a+S/4Z8t855PydwpK//uf5il8zzPlfK/r2dHX1/V09DpWyXP8Aoe501x8jzPL6vpz056mOb3w7GOrPY9sL1Tf9Mz/+dv8A/wCmB/8AuuDyB/7Bj/8ApRf/ALt//wC/W/v8Zf8A+Zf/AJ7/AO1J/wBMz/8Anb//AOmB/wDuuB/2DH/9KL/92/8A/fp/jL//ADL/APPf/al1p+0E/ic+lt00dIr00E030zs3ov6ZaiwZ9BviXbF0V2+NT6/Z1RN5mfZh31LgWpTKZQq7THfstVOHRo8qewbjXWtRnno6+1Phs+E7sxsTrWxbi6o1XXat1Xa5GTUjZ6eKkoIalmCyq+Ua+okklikHmU/m1L44n9L+h8sbJGwd411cbnTS0kEDYIHjDsEucWn93q4ABHBw3J98EheaMeqq2QgIgIgIgIgIgIgIgIgIr9pxpXqTq/cca0dLrGui/rllqQlqkWtRptXlpJfFwuySiNONw4/3Dy68pDaSI8qLA25qjV+ltE2uW9au1BSW61MHMlRK2NvHo3qILncj6WguPoFN2DTWoNVXCK1abs1TXXF54jhjdI78z0g9I/znYA9Su87ozewa1Quj+XXP0nrxi6aUJfVSV2BaDsSv3zLZ/wCWe+z1Ktf41t291zS1oV1R1B9CiwaEGPPvdb4iWkrR81adprI+63AZaKypDoaRp+oZZFxPNggEdXktIPDnBd4NrvAvqW9OprjuXeG2y3nBNLAWy1ThwcPk5hhyCRx5zgRy0Lv/ANCOiPoB0YaImj6OadUe231MIZqFyvtfzS760aUJQpyq3LO62pyCd4SNTSFNxyP8LaeQ83tw96NyN2a812uNTz1UYcSyAHy6aLnOI4GYjGPRxBf7uK9IttNo9vNrKJtJovTcFNMWgPnI8ypl/wDKTPy859WgtZ7NC/aZbPP18BsSF/Zcy08nZVyUzz2/M/2EnC/spmCTsq9Ja5+v7iTif2UxBJ2Veks89vh4iTif2UvBJ25UBJZ/sJGJ6loZFAyGcZ/QSMT1KwydlCyGc525jPjepOGTtyoR9rHZ8Bnxv+6lIpPuoh9rJci9dvkM2N+FIRSYUQ61jOfd3jNY/wBlIRv7YUU8zz7PWwzGPWfHJ2UY61kjxzI/XmMtj+3ss6N/b2Ua614b+uQymPWZHJ91HON4GS1yzWPWA43tuMlrllsf7LBdb57ev1GQxyymPWAtv4DIa5ZTXr8V1x1ltDQux5953XII1JS5GoVEZWgqlcNXNs1R6bBbUeyOLCnnj+4w1lSt+FKt+bf6Gve4WoKaxWeLgkOllIPRDHnl7z/c1o5e7gcZI4Q8Q3iF0N4btublr3WtWHSgOjo6Rjh59dVdOWQQg9h2dNKfohjy52SWMd5u9WNVbr1kvWqXtd0s3p09w0xITal/YqRTkKP7LTIDaj+5HjIPGea1ZUe5j1H0bo+z6HsNJYLLD008Y+pxx1yPP4pHn1c4/oBwOy+Tfe3ejW2/e4V63F13XmW6VT8RxNJ8qmgBPlU8DT+GONvHu45e7LnFfmo3UuJUBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF9ddDDpj6n9CzV2nalWBLXMpEpTFPvqypL7iKLedudclcinzWiPganRyy5EkkXWR3iIyPBqI+Ft9djtJb76KqtK6khDK1mX0lU0DzaWfGA9p7lh7SM7PbwecEcO73bK6T3y0ZVaW1LAGVTcvpaloBlpZ8YD2H1ae0jM9L28HnBHvY6MvST0t6V2lNA1b0orTVRo1VaQ1VKU642VZtatobSc6369ESZri1CE4ZkRmXA8jC0GaTHzqbr7Wau2d1jcdGaxoDFXQuJjkAPlVERP0TQu7OY4fq05a7kL5+dztqtWbQ6vr9H6tojHWROzHIAfKqIifomicfxMcPTu05a7BC+ikIHGJK2THGshKRaJWfHH7BZaECy5ykYYe2VloRkWXFSUUecLLQgWXOUpFFjAwsxtAsOcpSGHss5tAx3OUvDEOOFnNo5DHc5S0EPbhZ7SOQx3uU1TxcjhSDTfIYr3Kcp4cY4Ui0jkMZ7lN08PbhSLSOQxXuU7TQ9uFJNI5DFe5T9PD24Um0jl+YxXuU9TRchSrKOQw3uW46WLtwpRlHLYYb3LcVLF24UqyjOBiPctx0sWcKXZRyGE93dbmpYvwhfnOr+gOivSHtN+xdctLrJ1UtR9DySpF6W/ArbcNUgmyekUuTKZVNo01ZMo/5iI6y+XCWFlgbn0TuPrzbO8x6g2/1dX2e8NI/aUsz4i7GcNka09MrBk/RI1zOTwt4WmqrLfMJqKpfFL7tJGfz9D+RyvPF0rP4XXo0akqqtx9FXUu6Oj/ccg5MqLZd0Jk6i6ZrkvSOtREiyJcuPe1uwm21KSlRy6pwERETWOXpls98W/dXSwo7XvBpSk1JbG9LXVVP00VcGgY6nNa00szycEjy6fPOX5XLFo1zXtDY7jA2Zv8AaH0u/h+E/wAAvN70oPYU+0h6L6qnU6hohM1jsinHUHv650Ife1EgKp0A+JVQm2zEixb8pDK43+IZyaU2hJEouM+EzHqLtL8QnwubtikpKbX7LHf5egfKXYCif1v/AHGzuc6kkId9P0VBJ445W/aK/wBsrQOifoefR/0n+PY/oV1FVOl1Oiz5dKrNOnUmqQHlR51NqcSRAnw5CMcbEuHKbakR3k53StJKLuHdOkq6SvpoayhqY5qSRvUx8bg9jgfVrmktcPuCQpkEEZB4WCMhEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBFmU+nVCrTY1NpUGZU6jMdSxDgU+K/NmynlZ4WY0WMhx991WNkpSZmLFTU01FBLVVlQyKlYMue9wa1o93OcQAPuSrkUUs8jIYI3PlccBrQSSfYAck/kuzPo+ex76d3SDOBUIekcrS20pv2J3+rtYnnLHiFBmHxFNiW/JjyLyqTaWC4yNinKQojIuIsjqluV42/Dztr8zTT60Zd7zH1D5a2gVbutv7rpmubTMOePrnBHPHC5v0b4dN1tZOhkh086ht7+n9tWEwN6T+8GEGZwxz9MZH35Xef0eP4e/QawTp9c6RN+3BrPXWDYkSLVt9L9kWCiQ091io0h6LJfuutxFoSSVGUmn8ZGZG3jn59bmfEq3E1GKm37ZadprFb3ZaKibFVWFpGOoBzRTxOB5A6Jsejl3B0F4MNG2w09Zri8T3SqGCYYs09PkHsSCZpB9w+LPPC7oNOtGdK9E7catHSXT609PbdZQ0n+WWtRodJbkqYJZNvVB6O2Uupy0k4r/GkuOunxHlW46Kan11q/Xt0fetZ6lrLnc3E/tKiV0hbnGQwOPTG3gfSwNbx2XdHSmmNNaRoGWzTFjpqGhGPohjazqxnBeQMvdyfqeXO+6sktrJH2ePkIuF/Zb6p5OQqtLa3Pb13duRLwv7cqdp5OByq7Ka57YEnE/typiB/blVyU1ufr8uwSkL1NQSdlXZLPMScT+yl4JOygJTOx7evMSMT1LwSdlX5DXhv2fPHmJOJ/3UvDJ91AyGeYkInqVhk7KEfa5+vRCQjepOKTsoZ9nnsM6N6kopPuoV5rGfmM5j1JxyKKfZI87YGZG/GFnxSYwoh1rOS9f3Gax6kI39lFvNY5fsMtj1nRyZUa61zPt9ZGWx/ZZsb+yjnW8ke3n3+jGSx33WZG/B7qOcb7D9cxlNd7LMY/2WC433+v0GQ1yymPX5Lq9qpZ2jFmVO9b0noiU+E2pMSGhSTn1ioKSZx6bTmTPiekyFbbbITlStiG89FaPvmur7S2GxUxfUyH6nH8EbP3nvPo0fxJ4HK4v3o3t0NsLoK67ga8uQht8DSIogR51VOQeingYeXPefXsxuXuw0LzZ69673dr7e8q67keVHgMm5Gty32nFHAoVL4zU3HZQZ8K5LuynnTLicXue2B6l7dbeWXbiwQ2e1sDql2HTzEfXNJjlxPo0dmt7NC+VTxIeIvXPiV3CrNbavqDHQs6o6Gja4mGips5bHGOxe7h0shHVI/k8BoH4gN/rr8gIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIvtXoPdOHVToQaqxr3siS7VrQqzsaJqFp7LkuIo120ZDhcZkjJohVyEgzVElpLiQr7qsoMyHA+/wDsBpDf7R8tgv8AEIb1CHOo6xrQZaeUjj7viccCSMnBHIw4ArhTfHY3Se+WlJbFfohFdog51JVtA82nkI9+7oncCSMnDhyMOAK95vRl6TGk/Sw0sourGkVfZq1GqLbbNVpbq20Vy1qyTZKl0G4IBKNyFUIqzMiMy4HUYWgzSY+drdfanWWzer6/R2tLc6GuiJMcgBMVRFn6ZoX9nMcP1afpcAV4O7lbW6s2l1VWaT1bQGKsjJMcgBMU8WcNlhd2cx3r6tP0uAIX0YhA4wc5bNhi+yy0IFlzlJRRduOFloRgWHFSUUeAMBZbaBZc5ScMXIWc2gY7nKXhixx6rMbRyFhzlKwRdlnto8BjucpiCHtwpBpHIYz3Kbp4e3CkGkchjPcpuni7KQaRyGM9ym6eLtwpJpAxXuU7TRfZSLSBivcp2mi7KSZRyGK9yn6WIZClWkDDeVuOmj7KVZRy2GG9y3FSxYA4UoyjkMSRy3HSRdjhSzCPAYcjluSkj9cKWYRyGFIVuSkj7FSzKeQw3lblpWYA4UsynchhPK3LSx8hfOmvPQn6JfSnhvROkH0etK9UnXiaT/OrjtSnldkdLLRstJg3nTm4N205KGj4SKPOaLBF3Fjk/brfrebaGdk22u5d4tDG5/ZQVD/lzk5PXSvL6Z+Tz9cTvX3K3TQyzRY6JSB7Z4/h2XR1r5/CsdBvUdyRUdDtRtX+jxVZD0l1NOKZC1WsmOl4yU01Hot0Lpl1obYVnGa+rKTItsZP0A25+L/4gdLtipdwNL2TU1G1rR19LrfVOx3Jlpw+nJP2oxzz6rdNPUSvAEgBK6Vtb/4VDp9WH1kzRm/9ENfaaS3yZgxq9UdMbuWhvhNlbtKvOIdqNHIJRlhNeXwqTvtgz736B+MJ4ctRdMGudN3/AE5VYGXuhZX0wJ7gSUrvmDj70gyDxzwpNkLn/hwuofWT2VHtGtA0TpGp3Q213plLpzqmpdw2/ZFQv61mTSa8OLuiwSuagJZWSDNKzkEky3HdTQ3jB8MG4xp4tJ746dlq5RlsM1UyjqD24FPWeRNkZ5HRlajTVAGfKdj7DP8AJfBlTpVUos16m1mmz6TUYyjRIgVOHIgTWFkZkaXosptp9pRGXJSSMdiaSspK+COqoaqOalcMtfG5r2n8nNJB/QqyQQcEcrAGSqICICICICICICICICICLOp1MqVYmM0+k0+dVJ8hXCxBp0R+bMfUZkRJZjRm3XnFZPklJjHqqulooH1NbUxw0ze73uDGj83OIA/UrXHHJK4MiYXPPYAEk/oF9raT+zR6fOtqYcjTvona01Cmz3EtRa7XbPnWRbbueHLiLjvcrdoimUEsjUonzIiHA+svFX4c9BGePU28lhiqoxl0UVSyqnH28il86XJxwOjK3nadt9d3zoNt0rWvjceHOjMbP9eTob/eu0PSL+Gq6bN4kiXqze+j2icDiZJ2G/Wp+ol0ISslG6punWpFTbThskRFg60jiM9tsmOpGtfio7C2Pqh0bYL3fqjBw4RMooOO2X1DvPGf/wBVOB39ly3YvDFrm5FrrtXUVBFxwXGaT74bGOg4/wDKhdsWjP8ADddD3T9bE/WC/NUdc6kw6w6qAcqJptaL5NGanGnqRbq6hci23lEWcVpOE5LfOS6b66+KRvdqRslPonTto09SuBHX0urqkZ7ESTBkGR/+qnnn0XO2mvCvoWhLZL9cqy4ygj6ciCI++Ws6pOf/ACoXbfpB0SOjP0c4jcXRDQ7TjTlbROJ/mtBtyCdyvk62TThTLrnImXLOJTacGT0tzYz7zz0w1tvPutufM6bX+4F0ujXY/ZzTv8gYOR007C2BnPP0xj09guyOlND6O0k1rdOabpKR4/fZGPMPocyuzI79XFfr0xvczweBsqB3AXJNM/gKsTG+Z9/u+vMS0DuynKd/ZVeY3sf647uZnzEvA7kKcp38hVmU3z9fXmJWJ3ZTcD+yrMxnn+n5dglYH9lN08nZVuS1zz9C+glYn9lNQP7KvSmuewk4X9lMQSduVXpLXMSUT+yl4X9lX5LXPb8/W4konqXhkUBJZPc8b8/38xIxPUtBJ25UDIa57HnP6/mJKN6lYpOyg5DXPBeiGfG/spSGTtyoZ9rnkZ8b1JRSKGfa57DOjepKKT7qIdbwZ7bevoM1juykY39vdRL7XMZkb1nxSdlFPN5z4DMY/Cz434UW60ZGe3r9RlsflZ0cmQo5xrfb15jKY9ZrJFHutEeTx68RksesuN54X5DrBqzZWilm1C9b5qbcGnxEKRDiIUlVRrE80GbFMpkYzJciS8rBbFwoT95RkQ3ronRt+15fKaw6fpTJUvOXOP4ImZ5kkd+60fxJ4HK4x3l3s0JsPoe46717dWwW+IERRAgzVU2MsggZnL5HHv8AusH1PIAXms6RPSIvLpD3m9cNwOrg0KCt1m17XZdUqDRYClHwmotkyKjISRG88ZZUexYSREPVDbLbKx7Z2JlttrBJcZADPOR9cr/+iwfut9O55Xy6+JjxMa68TGuptT6nndBYYC5lBQMcTDSQk8cdnzPGDLKRlx4GGgAfPo5JXXBARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARfX/Qy6amr/Qm1Sh6haZ1JUqjzHI8a97DqL739N3rREOEbsKoR0KxHnNIycaW2RPR3MGRmWUnwnvpsPonfrSM+mtV0vRXMBdS1bAPPpZSOHsJ7sJ/HGfpeODzgjiXeHZrSG9GmJdPanpumpYC6mqWAedTS44ewnu0/vxn6Xjg84I95/RA6YOjXTR0sp+pmktbbW62lmHeNlz32E3VYlwG0lb1Hr0FCuI2l7qizEJ+zTGfvoMlEtDfzs72bJa52J1fU6U1nQEMOXU1UwH5erhzgSwvPr6SRk9cTuHDBa53h3ups9q/Z3VE+m9U0f7M5dT1LAfIqYs4EkTj6+kkZ+uN3DhgtcfrVCMDhlxWwIo8YWUhAsuKkYo/ss1tAsOcpaGLjssxtAsOcpWGIE9lnNoGO5yl4Yvss9pAx3uUzTw9lINo5DGc5TUEXZZ7aOQxnuU3BF2Ui0jkMZ7lN08XZSLSOQxXuU7TxDhSLSOQxXuU7TRcjhSjKMY2GI9y3DSxYxwpNlHIYj3LcFLFnHClGkchiPctw00WcKVZTjAw3lbjpo8YClWU8hhvPdbjpY+GhSrKeQw3lbjpY+ylmU7kMOQrclIzkKVZSMOQrclIz3ClWU8hiPK3HSs7BSjKRhvK3FSs7eylGi5DEeVuGmb2Uo0QxHlbhpm5IVAv7Q7RXV1ko+rGkGl2p8cm+pJjUOwLTvVomuXVdVclJqSCbx/lxgbj05uBrzRbzJo3W13tMmc5oqyopTn3zBIw5+6mooIpP6yNrh9wD/NfBV/8AsPvZPanvyZFy9CDR6nvy1OLeXYTVzaVpJxwzNS2WNMbitGOwriPJEhBER9g7Fac8fvjH0nHFFat/r5LGwAAVZguHA9Ca+Gpcf1JWW2x2mfHmUTMn2y3/AJpC+K7w/hYfZSXS46ui0DXvTlLhmaGbM1klzm2M8iZPUOg346ok9nGpZ9454snxevGJaGsbX3HTl0I7mqtjWE/n8lNSD+ACuDRlll/dlZ+Tv/WDl8xXX/B+dC6e68uyOk/0nLaaWpSmGbmb0svHqSPdKFLptkWUp1Ke/wC6Zjlmz/Gz32p2Rtv+0uk6p4HJgNwps/o+qqsf3qp27tsn9XXTt/PpP8mtX4bV/wCDh08WtZ0Xp6XpARk+BNV6PtEq6iLs4lxdWaGSvcksjkCi+OFqYBvz/h1oJHevl3iWP+51ul/mU/xYQP8Aw3h4/OMH/phUmV/BvwyM/s/tEpLZc09d0Tmn8F4mjpJxsnt4Cei+OHOceb4ZGn8tQkfzsblcbtQHdr8R/wDEf/blhJ/g64jR/wDMe0OkvY59T0UG4+eXI19JGURfMZB+N7M8fs/DMxv56hJ/lZGrKj2gDsZ1D/8AMf8A25TMH+EF0+jGk6z06rxqCCxxppuglEpClF28KpWqtbJOfFJjBqPjXallBFD4e6GN3p13eWT/AJtvi/mFIQ7L07iPM1A8/lCB/OQr9Vtv+Ew6HtOdZXeXSU6SVyNoUk3mbdb0ytHriLHEhK6jZl4qaJXvMhs+6fGW3uqWPbY9rNLUryODMa6px98MqqbP9ynKXZGwkj5m71bh/m+W3+bHr6Ftf+GW9l/azjS6xQtcdQUtmRrau/V2TDQ+Rc0u/wBBUOyHUkrt4FIPuHGl3+K54tLu17aK4aftpPY01ta4j8vm5aofxBW56LZfRMZBmiqph/nykf8AowxfWFl+xn9l/ps9HkW30NNJpz0VSFtLvdu4tTE8bZkaVus6j166WHjyWfvoMjPsHDd98c3i11SySO6b5XmNjwQRSmGh4PoDRQ05H6Fb5t22OgqNzXR6Yp3Ef5Tql/ukc8L7Ks3RnSHSto2NMNKtN9OI/Vm0bNh2NbFntdXjHV9Xb1LpyeDbljA4Ovmuda6vf5mrNYXS6SZzmrq56k598zSP5XI1rtdrtwxbrbTwD/xcbGf80BWGUndW+feIyE8BbrgPAValIwZ+Pu/fYhKwngKbgdwFVpaPxeWeXh2H3iXhd2U7Tu7Ksy0GZH3+79BKwuU3TuAwqvMbznBb57sn5EJeB3blTtM/tzwqxLRkj79z7z+AloXYwpundjHsqzLb5+uQlYXdlOU7+yrMtrBqLHfg/wC/YJaF/ZTdO/gKuS2tj237PHtxjtEnC/kKYp38hVmU3uZdnZ68hLRO7Kbgf291X5LXMSUT+ymIH9lXpTXPYScT+yl4JOygJDXPt9cxIxP7KWhk7KBktbHt8sZ/YSMT+ylYZOQoGS1jO3rn8xIxP7KWhk7KCfa5/p7xIRvUpFIoV9rnj1uM+N/ZScT+yh32s52GbG9SMT+yh32vAZ0b/upKKT7qJeb57eWRmsd25WfG/tyol5rGdvXiMxj8rPjfn1Ua61kjMiGUx6zmPxhRbreM93rcZbHZWcx+ce6/BteddbA6PtlyrwviopQtZLj0G34zjZ1q5amSDU3ApkZR8XAn8Tz6i6qO395R54Uq5F27291JuTfYrJp+mJAwZpnA+VBHnl8jv7msH1PPA4yRw3vv4g9v/Dvomq1jrm4jzSC2ko43N+ZrZ8ZEULD6DgyyuHlxM+pxJLWO8zvSA6Ql9dIa837ou2UbFPjrdZt22ori/wCU2/T1KM0MRmlHh2StODdfUXG4rc9sEPVjbfbXT22dijtNlh6qlwBmncB5kz/UuPo0fusHDR/FfMb4hfEVuB4jtbT6s1pWdFDGXNo6KNx+Xo4SeGRtP4nkYMkrh1yO5OBgD8IHIa4EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEX0J0Zuk/rB0S9T6Vqro3csih1uGptirUxxS3qDdVH61LkihXHTONLNRp7+NuIuNpf321JVuONN1tpdE7zaSrNH64tTaigkyY5BgTU8uMCWCTux4+3Dh9LgQti7h7c6U3Q05U6Z1dbWz0T8lju0sMmMCSF/djx9uCOHAjhe7X2f3tHNGOnfY7T9vy41o6wUOG0d+6UVKWn+aU59KEk7Wradd4FXBakxeTbfbI3Y55bfSlRJUv56PEj4X9deHjUD47lC+t0TUSH5S4Mb+zeM8RTgZ8moaPxMd9Lx9UZIJDfGXevw+6s2XvTmVkbqvSczz8tWtb9Dh6RzAZ8qZvq0/S/wDFGSMhvY+2gdXXOXDUMXYrMQgWHFSkMfZZraBYc5S0MfZZzaOQx3OUvBFnHCkG0DGe5TVPF2Wc2jkMdzlMwRduFntI5DGe5TNPF24Uk0jkMV7lPU8XZSDSBjvKmqeP7KSZR+QxHuU/SxcgKTaQMR7lP08XbCk2UchivctwU0XbhSjKBiSFbhpI+eylGUbkMN5W4qWPkcKUZTyGI8rcVLH2UqykYbytx0salWU4wMN5W4qWPAClGk8vd5DEee63HTMA6RhSjJDDeVuGlbgjClWS2GI8rcVK3gKTaLGBiPK3BTNwRhSTRDFeVP0zfVSLRbEMV5U7Tt4CkWuYxnqbpxys9v18xjuUzAstHb6IWCpSEHKxXz57GXjsXfvsXIXo1JxenKiX+3G3bnbx7sDMj9FIReihJG5n2+Xu8BnxdgpOHsFCP433MyPPifd3ZEhH6KTizx7qvSiLB+Hnnfz5chJQqYg7qvSiPw3z3Z2EnDhS8BCrkpP4sF34x45EpCeymYD+FV2UR4PP7CThPIwpmA8jCrMtHPb37+PLwErCfupqnd25ValpLJ7Z9e8SsJ7Kbp3cBVmWndXnzL9+wS0J7Kbp3cBVmSjn5mJWJ3ZTcDuyrMtHMjLt5F3H47iVhd2IU3Tu7EFViU3z5+P6iWhd2U5A7sq3LbxnHr5biVhd2ypqnf2yqzLb57fX3bci5iVhd91N07/uq5Jb3P1uJSJ3ZTMD+yrktrBmeP7f3MSkL+ymaeTsq7Jb5iTid2UxC/soCS1zElE/spaF/ZV+Q1z9fQSUT+yl4X9lAyGuf6esCQiepWGTsoOQ1z9euQkInqVhkUDIazkSMb+ylYpMYUK+1z2GfG9ScUn3UK+1jPrfcZ0b1JRPUS+1z2GbG9SEUnZQ7zXMZ0b1IxyKLeaz65jLY9Z0b8KKebwZ7Fjtx65jMY5Z8b8/mvj7pT9LLTzoz22t2qyGa7flTjuHbFjw3y+3S3DSZIqFWWjiOlURhWDW6vC3TwhojMzUnmzaHZvU2610ayjjdT6dicPPqnD6Gj+xHn+slPo0cN/E8gAA9YfE34tdA+GvTcklymZX69qIz8lbY3DzHkjiaoIz5FM08uefqk/BE0kkt80GsOst+a53lNvW/wCru1GoPmtqBCQam6ZRIBrNbVNpMPiNuLFbzvj7zivvKMzHqvojQ2ndvrHBYdOUQipm4L3HmSV+MF8ju7nH+AHAAC+bfd/ePXu+Osa3W24F4fVXKQkRxjIhpos5bDTx5xHG37cuP1OJdyvyobwXFqAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiu+nGpN96RXpQdQ9NbprFmXpbM1qoUW4KFMchT4chpZKIiW2fC9Hd4eFxpwlNOoM0qSZHgQGqNLae1pYrjpnVVogrrFVxlksMrQ5jmn7HsR3a4Yc08ggqJvlis+pbVWWS/W6KrtNQwtkikaHNcD9j2I9CMEHkEFe072ZntntPulO1Q9H+kDKoWmnSC+zswqbVFuNUqyNU5bDZJU5RXZDhR6DdMxKDcVTVrJp5fEUVX4WR4ReK3wLal2hfcNbbbQ1F1226i58YBkqre0ntKAMzU7c9InA6mjBlHd68rt9vCpeNvH1ep9ERzV+jOouez8dRSNPP1gDMsLe3mgdTRjzB3ee+ltA863OXVGCLtws1tAx3OUvBFlZ7aOQx3OUxBF2Kz20DGcVMwRrPbQMdxUzBH2We0jkMZ7lNU8XZSDSBjPKm6ePtwpFpAxnlTlPHyFJtI5DEe5bgposYGFJMo3GK9ynaWLkKSaSMR5W4KePspRpGCGI9y3DTR4AGFKNJ5DEee63DTR4DQpRlPIYjytw0sfAUmynkQxHnutw0zMdIUqynl2DDeVuKlj5HspRpIxHlbhpmdlJspGI8rcFKzspNpPIhiPK3DTs4AUm0QxXlT9M3sfRSLRDFeVO0zeB7qRbLlnmMV3qpyAZ6c91ntluMdymoBys9sY7lMQDhZaeXr+4sHupSEcdliPcz5+RF4Z7sdovs7BSMfYKHf2M+R+ZGXeM2P0UjF2Ch3yIzPz7893mM6PPCkYs4Cg38FxY7M8/LlgSEfopSLnCgJOT38fDx7RIxKXhwoCUX5793L4CSiKlYCq7KLc/Lw+JbFtsJOHspiDsFXJKfxc/Ln3+WTEnEeymYT2VbllsZnn5F25Mu8SsJ5wFNU55wFXJafhuJSEqZpz/FVuYjbPYXZ6LGRKQOU1TuValI3PuMvny57CVhPAU3A7ge6rctvczxnv9bCUhd2UzTv4Cq8tG57Y9wloXcBTlO7gcquS2+Z/3/YSkLlM071W5TZblz5+u8SsLuymoH9lW5TfPHrz32EpE7spqB/ZV6U2e/v9YwJOFw4UvA8cKuSWufr6iUif2UzC/soCS34CRid91LQv+6gZLXPbv9etxIxP7KWgf2UDIa57CRiepWGTsoN9vn4mfISEblKRP7KDktcy8/XYJCJ/ZSkMnYqDfb9flyGfG5SkT1DPtc9vAZ8b+ykopO3Kh3mueSIZrHqRjf2US+1z+YzY3qQif2UQ81jIzGPUhG/K6temZ7Qy0tEkVKwNLHqZeWqvVOR5kxK0Trcsl91BkSqitpfVVOssEollDSo0NqwTx80Dt1sZ4aLzr51LqPV8ctDo/Ic1vLJ6oA/uAjMcR7GQjJGegdnLzx8WHj403s4y4aG2wmp7tuZ0FkkoIkpLe4jGZSD0zVDfxCAEtYcece8a86t33hc9+3FU7svGt1C4bhrEhcqo1SpPrkSX3VmZ44lHhtpGcIQkiQhOxERD05stktOnbZSWayUEdNbIGhrI2ABoA/mT6k5JPJK8D9U6r1Hre/3LVGrLzPX3+skL5p5nF73uJz3PYDs1ow1o4aABhVoSq2+gIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIuRl56M81IjuuMSGHG3mH2XFNPMvNKJbTrTqDSttxtaSNKiMjIyyQ0SRslY+KVgdG4EEEZBB4IIPBBHBB7qjmte1zHtBaRgg8gg+hXpS9ml7dm5dKitvRLpkTaremm8dDVGtrWJto6jedlxklwQIl4NEaZF2W5FPDX2ojXUYrPDnr0NkkvLHxVfD0tWsDdNfbHQQ0GqXEyz20nopap3d7qY/hp53fi8viGR2f6tzsrpNvZ4T7dqB1bqnbaKOlvjiXy0f4YJz+8YfSGQ9+niN7v7BJJ9g1iXtZ2pVqUK+tP7mot4WfcsBmp0K47fnx6nSanBfTxIejSoy1tmaTylaDwtpaTQskqSZF4laisN80reLhp7Ulqnob3SSGOaCZhjkjeO4c1wB+4I4cCHNJBBXn5W2W5WS4VVqu9DLTXGB5bJHI0te1w9CD/AHHsRggkFXhtA2+5yyYIlnNoGO5yl4IuykGkDGeVNU8fbhSDaOQxnFTcEfZZ7aOQx3FTMEfZSLKO8Yr3Kdpou2QpJpIxXlT1PH2Uk0kYryp6mj4CkmUjEeVP0sfPZSjSRivK3BTx5wMKSZTyGI8qfpo+ylWU8hiPK3FSx9vZSbKRiPK3BSsBUmykYkhW4aVnCk2kjEeVuCmZjBUmynkQxHnup+mZ+EKTaIYrytwUzeQpFohivKnadvA4Uk12e4Yr/VTtOPwqQbLchjOU3AMkLPbIY7lMwDjus1vkMdyloRgBZaS2P1+YsnupOIYCwXuavyLG+2e0xkM9FIx9gop8zPPcW2eXf4jMjHZSEQAx7qHf5n67CGbH2UjF2Cg5BZzvnOd+feXZgSEfGFJxHGFAySIuLG/ZsXj8hIxZ4UtATwoGTyMvhz8/cJGLuFKw9wq/KIyPz38+w8HgSUJ4UvAchV6SW57ev1EnEeBypiE8DlVuWn8Rc/DHj3ZyJSE9ipmnPYquSU5JXxyJOI9lMwO7Kuy0ZI9vPvwXcWRKQnspmB2COVWpSfHbP649+RKwlTUDlXJSMke/LsyRfH3CUhd2UzA7BCrUtB4Pb17thKQuU1Tu5CrklGSVt3+jEpE7spmF3blVuS3zPf19CErE5TUD1XJTeM+PoxJwuUzA9QEhvnsJKJ3ZS0L+yrctrnjy8fDz5CUhf2UzTv7ZUBIb57GRCSid2UvC/soKQ3zEhE5SsL+ygZDec7d/YJGJ3ZSkL8YUHIb5nj6iQjcpWF/3UJIa2Pb6/kM+N/blScT+RyoN9vc9hIRvUpE9Qz7XMZ0b1JRSdlCvtcxnRvUlE9U667it6zqFU7muus0237fo0V2bVKvVZTUODCjNFlTjz7ykoI1HslJZWtRklJGoyITlntlyvlxpLTZ6GWpuU7w2OONpc9zj6AD+JPYDJJACx71qOyaVs9fqDUd1gorJSxmSWeZ4ZHGwdy5ziB9gO7iQGgkgLz29Mv2nlXvj+cabdHiROt20HSXT6xqEtv7JcNxMn92UxbyD4naHSXzy3158Mt5vOOqSvA9K9jfCbRaf+R1TuZFHVXoYfFRg9UMJ/dMx7SyDv0cxtdjPWRleMXin+IXeNYsumgdkKiag0q8GKe5EdFVVNPD20w/FTQO/D5nE8jc/1bXFp6cHHHHnHHnnFuuurU4664pS3HHFqNS3HFqM1LWtRmZmZmZmY7wta1jWsY0BgGABwAB2AHoAvLN73yPfJI8ukcSSSckk8kknkknuVsGpaUBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF2CdBj2kvSM6B10FJ04rv9R6b1OShy7NI7nfflWhW0G6lx2XTkkan7Zr2CPhmw+BZ8RpdS4gzSOtniD8LO2HiItBi1Rb/AJXVMTCKe4wANqYjjAa/0nh94pcgYBYWEArirczZ7SG6FF0Xml8q7sH7KqjAEzOOA70kZ7sfkerS08r3JdBf2lXRr6d9utL04uFNs6mQ4qX7m0duqVGjXnR1JZJ2RJpZF1Ue6aI2aV4mQiVwkgzdbZPBD5/PEH4V90/Dxc3t1RbDV6Ue/EFyp2udSy84DZO5p5Tx+ylxnIDHP7rzl3C2a1dtnVkXal8+zOdiOqiBMTucAO9Yn9vof7/SXd12LNo5DrE5y2DBF2ws9tAxnOU1BGpBtHIYz3Kagj7LPaRyGO9ymaeLOFJNIL1gYj3KfpohwFItI5DGeVOU0fbjhSTSRivKnaePsFJtJ2IYjz3W4KaMgDhSTKeQxXlT1MzspNlPIYjytwUsfYqTaSMV5W4KaPACkmkjEeVP0zPfupNpJERfX9hiPK3BTMAACk2i5fmMV5U/TN7BSTJDFeVPUzexUk0QxHlT1O33Ui0XL9xjPKnadvZSLRcu4YrypynbyOOFntl69d4x3FTUDePus5vkMdyl4Ow91mo5EMdylouAFlF+Ht+f1IWj3UnDjAWE9jJ4+efDtMX2dgs6PsFESN88j8dvhyIZsfopGLjChpH4j37vWwzo+ykoewUM+RHkt+38xnRnsVIxHsVBSS/Fgvd3blzEjEeylYT2UFI3I9voeOe+4kI/RSsPccqvyiP998dhiShKl4Cq/JTgzPtPx/cSUR7KXgPAVflJyauZlzz7uw8iThPAUvAeAq5ITz8MiTiPZTMJ7KuyU8+/fv8AnsJOI9lMQO7eyrkpB4Py7i5+8SkJHCmYHDIVckI5+XaX1EpEVMwuVclN54t9ty/sJOJ3bhTED8Y4Vbko5+Bn6x3iVid2U1C7sq7KRufx3/MScLuymIHcBV2U2Z528S8cCThd2UzA8cKvSG+e3IScTlLwvUBKazk8eflue/L4CRhf25UvBJ25Vdkt89t9/XeJOJ3ZS8L+yg5DfP1+4kYndlKwv7KCkN88F57CQicpWF/ZQUhvmJCN3ZSkT+yhX2ue3x/v2jPjf2UnFJ2UJIb57Y88jPicpOF6hX2uYz43qSif2Xxh0pOmXox0WqO4u8qums3pJYN2i6eUJ9l646iam+saemEZraodMVxJzIk4IyURtpc3Ic67R7Ga63drWtsdEYLE12Ja2YEQM5wQ3sZX9/oZ7fUWrgffPxQbZ7BW1ztSXD5vVD2Zgt1O5pqZOMh0ndtPEeP2knfP0Nf2XmC6T/TK1f6UtcU9d9T/AJNZkN810GwKI66zQKcknFLbkTSyl2tVXcuKTI4jLBEgkJIiHrPtLsboraK3hllpPPvr24mrJQDM/jBDfSKP2YzA7lxcTleFe/Xib3L8QF183VFw+W01E4mnt8BLaaIZyHPHeeX3llyeAGBjQAvkwcyrrsgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIrDad23RYlx0i77KuGs2pdNAms1Gi3BQKjKpVXpc6OoltSYU+G4zIYcSZdisGWSPJGZCMvNltGorXW2W/WyCstFTGWSwzMbJHIw8FrmOBaR+Y47jlY1ZRUlxpZ6Gvpo5qOVpa9j2hzXNPcFpyCF6o/Z7/xEUymFQtK+nVCeqcJP2Gl0zX62oCVVKM2hs4yHNRrXgsoKopyls3alTyJ/8a3Y7qjNY8g/Er8MuCq/pDV/h7nbFOeuSSzzv+hxJ6iKKoefo/e6YJiWfhayVg+ldQtxvDFTyOqLxt68RycudRvd9J5ziCQn6fXEb/p7AOaOF6y9N9RrA1btCjX9pjeNu35ZlwRWZlHuW1qrDrFJmsvNNvElMqG46lqS0h1JOsucDzK/uuJSojIvGvVGl9R6MvVdpzVljqrdfaZ5bJBURuikaQSPwuAy0kHpcMtcOWkjBXVCrs9xs1bNbrrQy09fGcOZI0tcP0PcH0IyD3BIX6I2gbYc5ZUEfbAUi0jkMZ5U5TR9uFItpGM8qcgjHHCkWk8hivKnaePge6kWUZGK9ynaaLJCkmk8hivKnqePsFJNJ5bDFeVPU8fbhSbSeQxHlbgp2duFJtJ5EMV5U9Tx8gKSaTnAxHlT9OzJCkmi3IYryp+nbyFJNEMV5U7TtUk0WwxXlT9M3gZUi0XIYrz3U5Tt7cKRaLkMZ5U5Tt5HCz2yGM9TUA/gs9sthjuUzAOAs5vkX5jHcpeEcD3WYjsFhylYhjCyTPCdu7z/AHMWvVScYHCwHDLPL8sbjJYFnMHHdRb3b+LyyW3PYjIxlsWdH6dlDP8AM+/bt8CGdH6KSi7BQz3M/M/qYzo/RSUXYKDkF+LfB47yM/HJkQz4j2UpCeyg3y59nngy27dxIRlSkR7KAlFsfn+vZyElCeVLQHlQElJY5fT4chJRFS0B+6gJJc/f8fgJGIqWgKrkpJkZ5zvuXxEnEeApmA8BV6SndWNvH1kScR4Cl4DwMqvSk4zz8NyPn9dxJwnKmIHZVblJ3PHy/TuIhKQngKagPAVflo3Px9bd4koXcKXp3cBVyU3gz8eXItxKQu7KZgfwFXpSNj/v6MScLuyl4HchV6Sjn3+4SUTlMQu7KuyUbntzM8c/kZ9olIndlMQO4CgZDfP9/lvsJCJ3ZSsL+yr0pvn4bHt8M+4ScTlMQPUDIQeT9/r4CRjd2UrC7soSQ3zGfG5SkT+yg5DZ77d4kInKUheoR9vmYkI3KTif2X5/fF4Wlp9b1Su2+Lio1qW1SI7kmo1quz49Op8Ztttbpkp+StsnHlpbMkNo4nHFfdQk1GRDcen7JedS3Ols1gtk9ZdZ3BrIoWOe9xJx2aDgDIy44a0ckgLHveprDpO0Vd+1LeKehs1O0ukmme2NjQAT3cRknH0tGXOPDQTgLz6dL32wb03+a2J0WYrkOMf2qDN1arUQilvIUkmVLs2hSmjKIWTWaJssjc/CptlB/eHpRsp4JWQfJ6h3emD5fpe23RO+keuKmVp+r0zFH9PcOe4cLy639+IbVVDa7S2xkLoYT1MfdZmftHDGM0cDgej16ZpQX9i2Nh5XQ/X7hrt11moXDc1XqNertWkuTKlV6tMfn1CbJeUanHpEqQtx1xRmfaeCLYsEPQ622232ehprZaqKKmt8LA1kcbQxjGjsGtaAAvLe63a53241l3vVwmqrpUPL5ZZXukke493Oe4kk/mfsocZyj0BEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF9gdEnp29JroTXam59BdRqjQoMl9DlwWPVSOtWBdTRIcaU1XbXmKVBcf6t0+CUx1ExlWFNupMiHCe8/h52o37sptO4umIqioY3ENXH+yrKc5BzFUN+sDIGY39UThkOYcraGrdC6Y1tSfK3+2tkeB9Ejfplj9fokHI+7Tlp9QV7KOgb7ffoxdJo6NYmu6oPRu1immxCa/n9QUvSq56g67GjtpoV5y+FNvPSXpH3YtYNlKSQfDJdPA8N/ET8OPdjagV2otvBJqnREeXHyWYuEDAHE+dSt/rg0DmSm6icjMTAuo+sthdRaaMtbYi642gc/SMTsHJ+qMfjAx3jyf8wLv+gvxp0WNNhSGJkOYwzKiS4rzciLKjSG0usSY0hlS2n2H2lkpC0maVJMjIzIx5v1EctPLLBUROZOxxa5rgQ5rgcFrgeQQcggjIPBXFMUDo3OY9pDwcEEYII7gj0I/uUu0nchhPKmaePkcKRbSMVxU3AztwpJpIxXlT1NHgD3Ui0kYzyp2mZ9lJNJ5DFeVPUzOykmk8hiPKnqaPspJpIxXlT1Mz7KSZSMV5U9SsUk0kYryp6nZ2IUi0QxXlTtO3sFItkMVxU7A3sMKRaIYzypynbypBovWBjPKmqZqz2y7e8Y7ypmBvqs5vsGM5TEI4CzkFyFhyl4h2GFlpFgqUj9FkH+Hwx8PeLY7/AHUjF2GFHO4IzMj27NvEZLPTKz2egKjHT4s757N8cvgMtnHos6MYxwoh/ZR7fl2FzwM2PsFIRdhyoZ8tz9fUhnR9gpKI8BQkjmff3kR8+/GMDPj9PZScXp7KDfLnnxz7/eQkI/RSsR7YUFJIuE98EXn37CRiPI91KQnke6gJJbH5+siRiPKloTyoGSWxn9N9vkJGI8qWgPKr0pODPxzt68hJxHOFLwOyAq/KT248Pz7RJQn0UvA70Vflp25l3eJ92O0SUJUvTu+yrktHP+3z7dhKQuUzTuVflI25Hz8C+p5MxJQlS8DlXZSMkZ93o8ZEnC5TEDsYCr8hHMvRCSid2UvC7sq7JRzEnE7spiF3ZV+S3nf1/bYSUTlLwPUBIRzz6PsEjE5S0LlAymuexfv2dgkYX9lKwP7KAkN89v7/AKiSicpeF/ZQkhvn2jPjcpOF6r1QWxFYflSnmo0aM05IkSJDiGWGGGUKceffecNLbbTTaTUpSjJKSLJ7CTpmyTSRxRMc+VxAa0Akkk4AAHJJPAA5JUgKiOGN800jWQsaS5xIAaAMkkngADkk9hyV099Lb2uGhWhqqnaWlComtepEU3YzhUeYabBoUxtb7Kyq1zMEaaw6y6z95inG6RkosvoPI7t7MeC/cLcAUl51iH2HSz8OHmt/4ZM0gH9nAf6sEHh8/SQRxG5dNt5vHJt5t0Kuy6J6NQarZlp8t2KKFwJB8ydv9aQR+CDqBzzI0rzU9IbpXa49J+4VVzVi8plUiMuqXSLVp+aZaNBbNKEJbpdCjKKKl3gbLifd62S4eTU4ZmPVLbTZ3b/aa2C36NsbIZnDElQ/9pUzHk5kld9WOeGN6WAcBowvJvdPevcfeO6/0lrnUMk8LTmKnZ+zpYBgDEULfpBwOXu6pHd3PK+chyeuKEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF2gdCX2uvTH6D0in0ayb3VqBpTGeScrSDUl6ZXrUTHynrUW5LU+ms2e+tKCIlQHkM5IuNlwth1K378Fux+/8AHU11+sAtusXN+m5UIbDUdXoZ248qpH2maXf2Xt7rYOqtttL6sD5qyiENyI4niw1+f87jpf8A8oE+xC9hPQj9vJ0L+lcVItS+K4XRx1bnGxFO1NTKjFZtKr1B00tk3bGofDFokgnnc8DM8qfI3JJJWe48Sd/Ph3b67O/O3jT9v/wo0ZHl3zFCxxqI2DnM9F9UrcDu6EzM7kloXXjUO0WpNPGSekj+dt4yeqMHrA/zo/xfq3qH5LvHhOsS2GJUV5mTFktNSI0mO4h5iQw8gnGXmXm1KbdZdbUSkqSZpUR5LJDz+nZJDJJDNG5kzXEOaQQQQcEEHkEHgg8grZUEJa7pc0gg859PzUw0nkMJ5U5Ts7KRbTyGK4qdgZjAUi0nkMV5U7Tx8BSTSeQxXlT1OzACkWk4GM8qcp2YxhSTSdiGK8qep2YA4Ui0nl6+oxXnup2nZ+EYUi0W5DGee6m6doy32Ug0XIYryp2nbyFINkMZ6moB6qQaIYzypqnbxlZ7ZcthjuKmYW8DhZrZchjuUvCBws5HYLDlLRdwspIslSUfouZX4eWce8voZi2O/dSMfYDKwHO3u8MF39wyWrOYop7czMs+4/DvIzGYxZ8fool/mfrsIZkfYLPi7BRD/M+34l3cs5GbH2CkYuwUG/zV5n394kI/RSkXooR4ufd49nPtwM9ik4/71CSCIyUWcn78/keRIRHBCk4TggqAkFsfMy9H3YEjEeQpaE8hQUktj5+7t9ZEjEeylYDyFX5SSwZ4M8c/78xIwn0UvAecKAkp2P8ALYi+okojypaA8qvyk/dPb5e7vElEeQpeB3IVelI2Pl62594k4XKYgcoCSjZXbz293iJGJ3ZSsLuQq/JQR5/TIkonKYhd2VdkowZkZfL6CUid2UxC7OFASkbn4/l+QkYnKWgdwFASUcy8/j2HjtElE7spaB3ZV+Q3jO3rzPcSMTuyloX9lByGyPO318j5c8iQjd2UrE/sq3PU1GaekSHW2GGW1vPvPLQ0yyy0g1uOuurUSG220EalKMyIi5mJSnD5XsjjYXSOIAABJJPAAA5JJ4AUoyZkbHSSPDY2gkknAAHJJJ7AD19F1L9Kr2t3Rg6Pf8yt61qv/wAa9RYpusf09Ys2O9b9OmIyngr154fpTBNrMuJuIUx7YyNKT3HczZ/wY7s7l/K3O70X9A6Yfg+dVtIme0+sNLxIcjs6Ty2+oJXWHdPxj7W7cCpt1nqv6d1KzI8mlcPJY4cYlqeYxg9xGJHehAXms6UntGekr0qXplNue6VWbp8+4rqNOLIdk0mgKZyfVorUknTqdxupJR5OW6prf7raS2HqftF4YNq9n44Kq02j57UrRzW1QbJNn1MTceXAPtG0O93FeZu7Xia3T3efPSXi8Gi0248UNKXRwY5x5pz1znnvI4t9mt7L4NHYlde0BEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF2XdDb2tnTc6Ej9PpumWqcu6tOIjzSpGkepv2m8bDdik9GXIj0qPLlNVe1HH2oxN9bSpcNREe5KLY+qu+Pgx2D38jqarVekGUeqHtPTcaHppqsOw4B0jmtMdQAXZ6aiOQe2O62nfdE6e1B1SVlCGVZ/42P6X+vcjh3f94FerzocfxKPRH1nKlWv0l6LVujJfMhDTD1flfa7v0lnTCYcW44i4qZDOvW00+82SUJnwVMNmsuOVgjUPHTfD4WG9GhTWXbaquh1Zp9pJELemmuLG5AAML3eTOQDkmGUPdj6Yc8Lie6bW3W3udLbJW1VMPT8Mg/Q/S79Dn/NXonsC/rE1Ptqn3lpveVr37aVUQTlOuWz67TLjocwuBtw0sVOkSZcNxxCHUmpHHxIyXERGPMjUenNQ6SutTY9UWOrt15hOHwVMMkEreSOY5GtcASDg4wccEraYpJ6WUw1MLo5R3DgQf4EL9AaSNtvKl6dnZSLSeQxXlTtOzkBSLSeXr+wxXlTlOzkKRaLchjPKnadvIwFItJGK8qbp2DhSDRDGeVOUzVINEMZ5U3TtWe2XIYzipmBvAwpBsuRDHeVNU7ewWc2XIY7ipiFvIHos1vmQsOUrCORws1HMhjuUtF3Cyk+tvyFkqSjXKrljOC88eiGgd1IR49lHrMu/wCnrIymrObn2UY6ZKyeD9F5jKYMYWbGMYGVEP8AM8/Pt2LuMZsfYKQi7BQ75bnjHb25/cZ0Z7KRiPAyoiQXeZn3eXuMZsSkYSoN8sGeOwz8eR/oJCPspSLsFCyCMzUR5LO3jjcvAZ0fGFJQnGCq++Wx47j/ADLvElGeyl4j2yoOQnY/05+jEhGeQpSI8hQUktjIvz5+GBIxHlSsJ5Cr8lOcl+nwEjEeyl4XYwoGQnY9u/uP6n3iRjPZSsJ7KvyU8yx8DL5CTiPZS8J7cqvPpLfy7+YkoypeI9lBSEbq5+/67CRid2UtC7sq7KRgz5e7x/MxJxHgKXgdwFAyUbHtv9P2EhE7kcqWgdyOVASEc/0ElE7spaF3Zfnl73haNg0Kbc98XPb9n23T0mudXbmq8Ch0qMXCtZE9PqT8aMlSktqNKeLiUZYLI3NYLJetR3CC02C01NbdJD9EMEb5ZHdhwxgc71GTjA9VdrrzarHRS3K9XKCkt8f4pJpGxsH5ueQM8cDPPoF0f9J/26PRt0vKo0DQmmVHXq7WCcZarEb7RbenEST1SFIcXW58X+b11tpxZpWUOITK+E+CRjcd/dpfh87pat+VuW4dXFp2yuwTG7pnrXNycjymO8uIkDIMsnUM/VF6LqnuH419AaYE9Domkkvl1AIEgzDSNOBj9o4eZIAe4YwNOOJPVedXpOe0c6VfSqemwb71Ak29ZEh1w2dOLD6+2bRbj9a+plmoMx5C6lcK2W3zR1lQkSTMi2Ii2HpxtP4X9n9n2QVGntNtqb+0DNbV4nqScDJYXAMhBIziFjP17roTuV4h90t0nTU991A6CyOJxR02YacDJwHgEvmwDjMz3n8l8KDsKuD0BEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBEBF9AaBdKrpG9Fy403V0fdZb+0qqxqJUpNq16XFpNUSSm1G1W7feU/Qa4wo2k5blxnkGRchxvuNs/thu5azaNydDW68UWPp+Yha6SPvzFMMTRHk8xvafusOst1DcGeXW0rJWf5w5H5HuP0IXpH6KX8U9rRaH2GgdLzRe39XKUgurev/S52PYV7oy61/zM+15SJNmVpaGiX9yKdGI1Y3HltvD8IXQl6+YuWyuuqmy1h5FHXh1ZSng/SyoaW1UWTjmQVJxlbOq9CUZJfbqh0Z/su+pv6H8Q/XqXpX6LftmfZ19K/wCwU+xekFb1l3jPNSG9P9ZOr0xuvrkpSZR4yq/ITbNakOceEop1SmKUZGREPKzdzwMeJzZz5ip1DttU19jj5NZbM19Pj3d5LfPiA9TNBEAoKXT1zoTmWmLmD95v1D+7kfqF2rxHGZDTUhh1t9h9tDrLzK0usvNOpJbbrTiDUhxtxBkaVEZkZHkh0+ma+J745GFsjSQQRggjggg8gg8EFXKdnupRpPaMR5U3Ts9VINEMZ5U3Tt7KQaLYYzypunbwFINlyGM4qagbwFnIIY7lMxDspBohjPKmadvbIWa2QsOKloRyFmNluMdylYRys1sWXKVh+6ykkLJUjGPuuRzBJ3PbuIiPs8jGlvfgKQjB4wOVHOGeDxv4cj5eIyWeizWemVFudu2D8/D/AHZGWxZzPRRT/wCI/XYQzI+wWfF2CiX8ZPt+nIviM2PsFIRdgoeQXM8F275zn8yGbEfTKkYT6ZUG9zVy5n49okI/RSkXooaRuZ57Nt/2yM6LsFJQ9hhQT5YM8dmfMSEZ7KViPAUHII8n7+zGd/PxEhEeApSE8BQUhP4i8TIiPO/d9RIxHspSF34VAPp5iRjPZS8R7KCfTz25GZZ9ZEjGeylIj2UDKRufIu0uWfRiQhPCloHcKvSkkRnz95H4+RCTiOQFLwOyAoKSn59uxbkXx2EjEVKwOVbnqaZadeecQy0yhbjzzi0obaaQk1OOOLWZJQhCU5Mz2IiEnTh8j2MY0ue4gAAZJJ7AD1P2UrFIGAuc4BgGeeAMev6LrX6RXtTug50cPtsG8Na6Hdd0QzShyytLjRqBcfWKJR9TIVRn1UGlvI4cKROnRTSZlkdqNsfCH4gN0fIqLJoOoo7Q/kVVfmjgx7jzR50gPoYopAVxjqzf/avRPmR3HU8VRXN/4ml/4RJn2PQfLYftJIxdBnSO/iHtU7n+10bozaV0bTWnKLq2rz1CdYvG7F4cc/x4dvRksWtSlLa4PuSFVQiPO49Gtr/hm6QtPk1262sJ7rVDk0tGDTU44HDpnZqJMHPLBBxhdV9aeNbUlYJKXQen4aCH0nqMTzevLYxiFnpw7zV0S6ydInXHpB1xVw60ao3jqJUuI1R03DWJEim08srUTdKorSmaNSWU9YeERmGkkR8h6F6G2y2/21t4tmhNI0Nspcc+TE0Pf25klOZZDx3e9xXUfVOuNXa2q/ntV6iqq6o9PNeS1v2YzhjB9mNAX4wN9LaqAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAiAi+4ejX7SbpydEh+GnQjpJak2pRIchh8rLqNY/q6wH/s+yWnrHu5ut2yTakGaTNuM2vhPZRDgDdPws+H7eeOc7h7W2qsr3tI+aZH8tWDPqKumMU+c8jLyM+ixZqKlqOZYGl3vjB/iOV6Aejf8AxYmudsIp1H6UfR1sXVKEw0pmZeGlVYn6b3U8ZumpE2XQKwV1WvUZCWlcKm450tpXCRlw7jze3R+Dht9dnVVbtHudcLRUOOW01wjZW0445a2aL5edjc8gvFQ4Z9eFhm0xN5heR9jz/wBX+9d8fR8/iMvZfa3ohw69qvcegdwyGIynKPrdaE+hwESnSQT8du77YcuuzktR3TMutkzYpKQXFwp3IvO7cr4YXi10AZ57do6l1HbGudiW1VLJXlozgmmnFPU5cP3Y4pMHjJ7m4ylkj74P5Luf0x1k0i1mpCa9pDqlp3qlROradOraeXpbl5U9CH0kpo3ZVvVKossmsuxZpPO2MjorqzQ+tdDVpt2tdI3O0V+SPLraWemeSO+GzMYTj7AqUhaRgYX6w2Q2a5TMA57LObLchju9VLwDlqzmy5DHcpiEchZzZDHcpeALMb9dgsOUpAB6rMQQsuUpCFlo8fyFlykYu/JR1WCxgu3c9+zsBg57qQiAPOVHub88fDP7kMlqzGcKNdLnjHuPJcu88jKYs2NRL+cnjHohmR9gs+LsFEvluZ7b/oMyPsApCLsAomQWN+Zn65e4ZsZUhCfRQj5bnz38+7xPvIZ8Z7KTiPAUNILc88/Lw+fIZ0akolByOZ58+Z45CQi7BSkPYKEkEWT3z8PyyYz41JxHgKDklg1euzHbsYkIj2UpCeAq/IIsmW/h4CSjPqpeE8AqDkpLPrsEhEeFKQk4X5JqRqrpfpRTDrWqGo9i6dUjq3XCqV83ZQbUhKQyXE51cquz4LTvCXYkzPs5jemltH6t1jVig0lpe4XStyB0UlPNUP57ZbEx5H6gLXWXq0WaH5i73SnpYf7UsjIx/F5Gf0XUZrp7d72dukSZUWi6l13WquMsyDbpekNrzaxDXJbJZMsOXRcC7btZTT7hF/iR5kkkpPiwrkO6O33w8fE3rUwzV+laew29zm5kuU7YndJ7kU8InqMgfuvjZk8ZHdcYXvxH7X2EPZBdZbhUAH6aaMuGfQeY/wAuPn3a53HoulrXz+JV1guNM+l9HbQuz9OobzZNRro1Iqky/LjaMnCUqXGotKTblvQn1NlgkPqqLaTMzPiwQ727c/Cu0TazT1e5u4Vdc52nLoKGNtJAePwulk8+Z4zzlghJ+y4R1H4vNQziSHSem6ekYRgSTuM0g+4Y3y42n7O8wfmuk3Xvp7dL/pMvSi1j16vy46RKeeeO04FT/piymeu2U21aFsN0igGhKPuka2FqwW5mO+u3Ph02T2pZD/gPt1bqWtY0D5h8fn1Rx6mpnMk2c88PAz6Lr1qndHX+sy8ai1TVzU5JPlB3lwjPtFH0R/xaT918hDmtbBQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVltS87wsSrM16x7suWza5HNKo9atSu1S3qswpJ8STZqNIlQ5jRpVuXCssGIq8WKyaho326/wBmpa63u7xVEUc0Z/NkjXNP6hVBI7FdpOift0vao6EqjM270t76vOkxmG4v8k1hiULV2G5HZ6sm2v5hftLrVxRSQhskkqPOZWSclkdRte/D48IO4Ylfc9mLfQ1jnF3m2101ucHHOT0UkkULsk5w+JwzzhXmVM0ZBa/+PK7a9G/4uTpW20VMia4dGfRHVSJGU23UKjY1ZvDSiv1BnrDNx9bk+RqPQW5nVHguqgMtGZF9wtx0y1x8F7Z26mrm0Butf7PM8EsZVxU1whYccABgopi3P9qVzvuVIQ3eSPHXE1w+3H/Wu0nSX+Ld6ElycEfV7QTpDaVS1pR/zNutWTqfQmlqMiWT01FwWXWyQgsmSkUxwzx+Eh1F1n8GHfu1dUmitxdM3iEZ+mY1VBMR6YaYaqLJ9jOAPdTNPqCkHEsT2/lgj+Y/kuyLTH+IS9kbqXHjKZ6WNJsioPr4F0jUqwtS7KkRVYTj7TVKlaB2xwmascTc9xJYPJ4HVvVnw1PGjpWWUSbNzXCmaMiShq6Gqa7/AEY2VPn/AKGEH7Kdpr7angA1QafuCP8Adj+9ff2n3T06D+qLER7TvpgdGS8TnERx4lB1z01n1IzNRoJt2ktXL/M4z3EWOrcZQvwHXDUvh13+0jJMzU2yerKER93TWmuYz3yJDB5bh92uI+6n6Wvt8gHl1sRJ9nj/AK19YUupU6sQ2ajSahCqlPkp4406nSo86HIR/rYlRXHWXU+KVGQ4crKWqop5KatppIalhw5j2ljgfYtcAR+oU9AQ4ZByFLoL+wwnKRiAPdHM4PfvzgvryMG4Wez0yOVHunz3IufPwGSxZjFGOlnOT9+c52GUxZzPThRb5bn+pl2eQzI/RZ0XYKKf8vrn4mQzI1nxfmod89+74Hz8RmxqRi7KGf58+wuz8+Qzo/yUjF+SrdYqFOpUR6fVJ8OmwI6eORNnyWIcRhBf53pUlxtppG/NSiIStFTVNZNHTUdO+WoccBrGlzifYNaCT+gWeyRsbS+Rwa0epOB/1L5O1B6b3Q001alO370r+jjaZwyM34tb1o08hVEjJRI6tumOXF/MJL3EeOBtpS/Acx6b2C3z1U+JmnNnNUVgf2dFa617PfJkEPQ0fdzgPuseTVWmaEOdWagoosejpowf0HVn+AXwVqT7d72WOnrMhTvShpV5TmVcKaXp3ZOod4PyVFxZ+z1Kn2sVucP3SLiXOQk8lgx2K0t8PHxealkiDNpJqGncP6ytqqKmDfzY+o8/9BESoWp3g29oGnqv7ZXj92OOV5P5EM6f4uC68NU/4o/od0A1s6VaJa8alykpX/zFeas7TmiurJRkgmpi67dtYNCyweV05Bl3GOzWkPhG733EB+sNfadtUJI4hNTWygeuWiKmiyPtMfzW2a3xG6WpgW26z1tQ73d5cTf49Ujv/NC60dW/4o7pL3IVRjaOdHjR7TWNIU43AqF5Ve6tTa5BZ6wjbeSuC9p/RXJnVFg+shOtEZn909h2q0X8I7aq1/Ky643Mvd1lbgvZTR09DE845GHislDc/wBmVrvuFsq4+JjUsgkZaLDSU7T2MjnzOH8DE3P5tI+y6u9YPbM+0m1pVIar/SfvK06ZIZXH/k+lcWi6XREMOdYTjX22y6bSa7JJaXDSZvzHVGnBZHbfRPgY8LOhBG+3bS0NbVtcHeZcHS17iRjB6Kp8kLcYzhkbRn0XHN23l3KvHU2bVM8MRGOmDpgGD94g1x/VxXW9ct23VelTdrV43NcF2Vl81Kfq1y1qpV2pvGo+JRuz6pJlSnDUrc8rPJjtHarNZ7FSMoLJaaajoW9o4ImQxj8mRta0foFxxU1dXWymesqpJpj3c9xc4/q4kqviSWOgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIgIrRbt8XpaDqX7Tu+6LXfQrjS9btwVaiOpXnPElymy4yyVnfOciIudgsN7YY7zZKSrjIxiaGOUY/J7XBa2SyR8xyOafsSP5L6WtD2g3TzsAkpsjpq9K+1WkJ4ExqF0hdWadD4CxhCoUa7URFoLHI0GQ4rvfhr8O2pMm/7D6NrHk56pbNbnuz79Tqcu/vWdHd7rD/VXOoaPtI8f71+60b2zPtU6CSChdPLpHyCRjH87v8An3LnH+v+okVXrP8AxZHHld4GPCBceoz+HbS7c/5KjZB/DyfLx+izGan1BH+G7z/q7P8APK/UKZ7fb2vlJJKYvTZvt4klgv5pZWkNcM/+8qtadz1L95mNp1fw5PBTWEmXYW3Nz/k6q5Rf+irWYWWzWmp2Y6bs/wDVrD/NpVtY/iKvbJR0klHTJkrIu2RoP0YpSveqVos8o/eYhZPhjeByQ5dscwfld763/m3QLIbr7Vre12/+bh/9muKT/ES+2OlpNLvTHkkR8zj6EdGSIru2VF0XZUXuMa4vhk+B6Egs2PZ+t2vrv+ddCtf+MLWH/hg/7KH/ANmqXVPbze1wrBKKZ00b4a49jOmWXpJRDLs+6qjaewFIPyMhPUfw7fBhQkGHYm3nH+UqrjL/AOlrH5/VaHa/1g7vfJB+TYx/JgX5XWvbBe1Ar/H9v6cnSGZ4/wAX8nvqbbuP+7/TyKXwf+HA3fQeCXwl27p+X8P2mXY/ytI2b/0xkz+qsP1vq5/fUNUPyeW/ywvw67Onr0478JSbz6Y3SjuhpaeBUet6+ap1CJwb/cKHIulcVKd+RIIhyBZvDr4f9O4Ni2P0jSPBz1RWi3sdn/SbTh396wZtS6iqP6+/Vrx95pCP4dWF85XBel43Y6p66bsuW5Xlq41PXBXapWXVLzniU5UZUlZqzvkzyOT7bYrHZmCO0WakpWAYxDDHEP4Ma1RUtRUTnM873n/OcT/MqtCVVlARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARARf/2Q=="> + <title>jpeg image</title> + </image> + <!-- fast/image/resources/red-at-12-oclock-with-color-profile.png data url --> + <image x="420" width="200" height="200" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAMgCAIAAABUEpE/AAABDWlDQ1BJQ0MgcHJvZmlsZQAAeNqtkb1Kw1AARs+tgthBiwTpeAdxciiC4KqB6mCgJBmSbE0aYkCTcHPjDzgKrt3cBMFnUHfXOomCPoUgOjsESR0Fz3Q40wcfiDfL6butDhxmWtk729LzAzn3TJslOrSYHUZlsTUY7AFkeRbzGwFfLwiApzXL6bv8jfmoUBp4B1ZHcRmB6AKjY11oEGeAoTw/ADEGjKT2G8AIa78HDOXaJogJsJBMeTjlOj7RAGZenKo02ddyvdfbkLtxptKoKqU1TLKqhHoHAG0zP8iVU6QZ/4vnB7K2DxsBiOVJ08JLuLuA7mvTVq5g8RxuH5qWX8PmJ8yMo0od/XwiHr8BQKtHx3JfEfoAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfcBRgWKwp6a01BAAAgAElEQVR42uy9+bNl13Xf9/2u87rRjcbQxNQYCIKYBBKcB1DgTEEcAFImZYqiaJGmaWuoOJIqqbiUUIllxbJCyrIt2eW/IPnB8VAup6yyLJflWFFslSXLlkxFFGmBokiQgDDPQLP73b3yw977nLX23ue+143G0OD6lkpsvHffuefe+949n/tda30XEAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCj0HIsQkiS778gpHknArd8t/7f23T1/9tRPhnjGDyoUCoVCoVDotAhr+b8FZeo/lm+V/9oLpPYNQKfFOh0IytabyWnfUSgUCoVCodAz4SsZez374Bj/Len46bQQasa4bfw3xLU97zRIKxQKhUKh0HMtTyeFuaQaVmLqbg5lCNBhljSstA/kkjVQM/wnq6e6hdT2AY+hUCgUCoVCzwZXDb5ITj3HCNgUCEfNWw5v1j2n4Y/IKi1tByl3WNn/nYZCoVAoFAo9Czq1ZvCdM0Etq+U87vcH9z6B2foKAysUCj3zt8lQKBQ6gxIglX/wCHgeeA6mo8C5xFHV8yFHgAuJC6jnKS8AzqWepzgMHlY9TD1PcQ5wCLoDPQA9QJwDCECoQBUAkBS7wC75LcWuYHdHTyqeAp4CngSOE08pHgeeEjyW8AT4GPQJ4HHiccWjiqcV9ytOKp9UPA4Aak6eCToTWIqXMxQKBWCFQqHn7o1DIZDzsHMUvBi8FDgGHMV0OfRy4ErKxeClqseAw4BSqWp+XCspUQEWvtEZaZRKAMuPaP6GIC1HySkR1AHf1SNCVmqRUzn4U8T9wP0J9wN3K+4H7lE8DNyjeCjhwYQHlMdVNUgrFAoFYIVCL2IJkGgNl9qxpLNp1PyDM6Bkb6YYMyTVIM9yK+/cFJA6cBl5CaarlFdCrgKvwnQN03XQq1XOBRVKKIikkEw8qvl4tABkGQvqH4Za9lKSqkBa8Ct/vXnzyr1c811M5nD9WxtJ0T3eAclykmQ+7FMJX9Ppq9x8E/yG6l3A3bu4Zxf3KB9S3ZjXBdX6QvMClRswLS/EGNfs17vXsXtpQqFQAFYoFHrWeIsJWq/oBAFVd4FfyIkFhPI1nhmzQAWBVHiE50Au484VmF6qcgN4DeRa4kaZbtgoQKUyX98FaVNYTcGENBUGKlwyACxtoEM7DGlBis8LYOW7U2wEE4oflgABVVVVFGmaoIo/Trgz4U7F15V3qt51EndvcL/iBOYnoSMntjxZOvR1D7QKhUIBWKFQ6Dn8i9X2klxoIKMNVRQbrShGPaA4KSCoSQG5AAcvxYFrBDem6Ubyeplel+RqxU6mH9Fsl5UCHpVI5f5VnL2UWarg1rMBWATTqQHWCJ72BiyZf1KXb7GahPVBJpYbkixnRZJ6MuGuDX5f8ceKL+3yjzf4k43el/BYMQKRKvyWl4w4oDgJLK6XcRYDsEKhAKxQKPSs/6EWogFRynxLyam4U5ztLECUKf+LCSpEUoIHrtCD15M36s4rsHMz5PWQK7JHw5SBSPPBHeVUhKJCmXEnYSPFEisclpuqZMadZwJYQL73U3awSIg+I8CacYpa/jFXDFXLLGI5RjlREmqPTHfkpPz6Br93Uv8o4cuJX97oH+/ingQ13fQF7XKBdYEq+tZ7YFRADIVCAVihUOjZ+PNdruui1SCpiDXh4Mtw4HrsvBI7r8bO67DzRkAIqpaecybJ1KJpZgRqyoCSQJk2AKkKLW3piVgIT1WhAmWtRCas9GCpjs4/veAAi0yAGPsqZbiZlu56yXdJhQBKUjf5Wa3uXhIVMBFCrbYemP9ngu7iP+/i9xS/fxJfPImvnsTXlSeyJVjruTvArm+tK+dfn5lQKBSAFQqFzuBfammfSsa6ghIgi711ztV68EZOr9JzXg25hQder0JuUsavpQMdWpqUFEh5ZE81OzapVhszFaQMRyqkJqgWoFGmfMeohyqG2koP1qkCVvmRZ9CDdZqA1f1DRrfkXC2sTxYzl0JEQVGm4ufpkqqlU8Gsk8SBfEBBOoEvnMB/TPjdXf7hCdx5Ur9RMI6pe9JqCD41MCsUCsAKhUJn6G+1dufYAUAeuEAP3ogDr8I5r8HBt2DnrcBO7jvXiUhLcxQ0QaXmI5SK3kIz2CBNueFKUP0qAsmUyjR3HJFIIHVGNM22WQKSYud5dLDOVJM7u7OcIJt6A9HlB2UN0Whb3DdKgVKxu4OdeoOkSgEFiUCCCKA4cZL/4SR++6T+l5P4ww3+6ASeyOcwmkwMhUIBWKFQ6AwA1gTdKIBDF+PgK3DwVh64WQ+9c9q5cQNlylQiJHRTXCamQkFILIDC4r9IrvrVqhY3orqEFLjOIyWyY6VKitZIqpl4mKGrHutZAaxTb3I/TcCqvVPdtp9ESJ6jnM9JuQGEShm+kzJRSzT8fG7ZS2R7l0oFlZIBttxw2uUf7eLXT+iXjuO3T/KLu/qIdvkaoVAoACsU+rb+I6OWxmSd86iWlvRabUu103kJE6/WxTnH5JxX6qE36aG3ysG3punKTCS+oOZhRQHVyitzT5Eu56Seh+aTyT8IE/VZj1MISu2hFqQxJ2NvTWDEWKotCDXng1oiLLiyN2AZ0HGANXcvScmyGLwD2kTSPrx0WnnfnAcM+++y/YoCmOqXFTrvZ5zsEKarTnIuuSbcdQK/eQL/8QR/76R+4TjvhzHSSKiKOmidY8/yuCI0AiBCoQCsUOjF9kdmqAaKCdj4mwioPvBAARw8Oh161ebQG3D4HTjyTqRLwQPgLlRypvnc6LMnYI2oiPmSi7kg6Mf6lrD1YlN1gKXqk0LPGGAV+2o+27nxax+ANdPMCwSwOA8AqAMsexALWObI8/OZAAgESAJRnAQeOY5/c5y/+bT+pxPF2XJZsT55K8+fSu3cCroKhQKwQqEX0d8YwVSDFGasInOXU8YIEabEwzjyHTj0Bhx+Cw6/HwevzSGgiomaL9O6rORTygwqapBoNjJUDS3RhKSXMperx20FLEMezwVgkZwfRT69Z+JgzYwlXdL6swRYHNz4dABLkJQCQDUBIEVQnhwBmHASdz6NXzuB/3Acv/st/NeTOD5DVT5KrgsTU82dX3ysaJYPhQKwQqGz+W/M9qQTwKTYOJThhHOO6bmvweG347zbpoNv28yB4rmUpDkSQV1jUFImzl1TDWC1eJS9rqTGzkELWK5cqKXhWwegZs/BApa5zT4AqyDaniXCcpjSNObX++wJWHYywEZVPduANSKtMWD5/q2hg4W50R5UqpLl12DmRSE2mo7z331L/5+n8e+fxO/u4r4c95B/wWKdYigUgBUKvej+wuY9gEDO/ESZxsN05FV67pvTee/iee+Vgy/dbARpF5NgIwuPpLk5R+eVNlCpw4AejJJ3sGALfLQEpqqNAebIbAWwlvPpAcuM+1kT6zQAy5cIT2GKcK1E2DhYW1DpjDtYzxCwyOxdiZTDqv2tIjGBeXSBZaYAVCTc9RT/9ZP6G0/jd57mHyTNjGXr0lIrikFdoVAAVih0Vv6B7Sh2fbv6BTz8Kpz3Lj3/Npx/G9KOJIWWmEpJSNmdquyU0zuRAGyoMlf6LNMsYJQcJfgWdaprindwZkwjhzuL1dMA374Ba2uY+/4AKx8lnU09WEPAWqsPts9geSz1JtxMtVkOIJQsVcXMWIk6VbjCvAYRwA6hmp7EvzqOf/s4f+Nb+NIJfbS8IGVjZfRjhUIBWKHQWaspp3WecxHOvwXnvQsX3obDt8pGS89SSUcANgkQSQXEiI0mWcpZm5k8xMDNOmAtbVWLg9X1TnU/DlR/bRWwqFT7RT3zgAVbIjQbeJ5JTAMrQK2W856FJnd/Dg7vtgPWXB+cV/TMz6N5sOQS65DvLudG7FJ3JqBmbEi2tY7jtx7nrz2pv/4UfucEHoneq1AoACsUOpt15BKe+xa94N244AM49Gpwoua6jCCBqlSmTQLJeQnNZkMVrRlUklISYpMEUzL8xLzTplZ6MiG5Pncsre4CqtZrcUauzFtzxWlTruXG96pJ7mj63F18w7MBWO0UIUoH/Wk4WLN3VW7JUqp9bhws76KVmymU5t62A1b5QSyUnP+dSkJpbcZiAfX8U1P1P3McGqjUBEwTAKZv6RefwL98Er9+HL/zBO6Lv9FQKAArFHoutXRNzbRC443kWIUJ2MzZ3yzd6wT08EW48Dtx3nfjkg9SbgJFsQvdyfmUta1Ksxs0M03JLdrMGZvmslszF7oe824Y0Nb4XM7C3NtuqMh3ppfzoWGsmXV6J2w5h9YPa0uHjS+1Bljokr38Y2wASzxdYeuqnGnlvW4hoXXAkvFU4Ix+Lr+UpolezMlU+FtzsJYT5vBxUZuzmtxZq8WyqTvbCYsfBu6K7iiwyz98RH/lCfk3T6Tf+hYeIallr2IyxGsZsTTLkwRElcBulBpDoQCsUOh0/jbKdc/0Flf+kNkjYo0P5TkX4YI36dHvxktul0OvTjrJZkOZ0tyBnlLuXa7AxNY0gvWiFv/mTAAW1tKwzhhgzdfkZwxY3iF7QQPW1AezuxWE2wBruJ369ADL/tTUnWr5ChNUCJAb6pSXTwPpBP7rw/jlx/Gvn8DvnOCjqnSBpYsXOvgKRygWCoUCsEKh1b8MglSpuVWpAtaya3mZ9p8OytFb00vehos/ysNv0nlES+fW7ASdoIpUZuzrpF71xfy4HJZVx/Vs5lJdGgAWTIuS65FKDWBB21qbayFvJ/5OF7D6u9gDsIb5W88MsJq3tucGsNj9fwtY7G+5D8CS+gzas5qPZgGr70Vjc5JM0Lz0ECwVRp3KAsYNgaf5Xx7Wf/o4/u0j/L2kT9f7yq+JAKkcJkdDaEpol2OGQqEArFBo6x9G5haDUyhVw7yZF0gCJlz4Wlz0Ll74Ib349gI0iwezKaGOOkHnNim/u2ZkYi1NSE3KlDG9TgOw8m3sd5d80flynuAAyzHQswlY3sFyLtrplgjPFGBJ90ZJx17K/QHWXiOEawlYs/mkHrmaUuMWwKpFzMW+midUN6UxazlOEogQD+uvPsJffgz/7xP6/6Xaixd5WqFQAFYo9Mz/MERLD9YcOjRfKyGHr9CL3qmX/hlc8jFOhzRzVYJ2LUTlGApAaxeUWkeqASxxUVWGKlarhPCTgL5KWG6wAFY2wBQKulisep7qetg7wGoBzrFUv99QTw+wHOTtD7Dyzr6zC7CMfYWmK991uGsLWCNWWwBL2hNw9hVLUVsIJSjFu6oUh0SK5KkIYIOnHsY/e4S//Kj+xtO4R7FD7ObT8rG3gVyhUABWKHQKJla9itT9NtPOoc0lb8HRO3DZR3HkO/J0HnSTlFTWZCkavyqDS9KSOEUAmlz2Qd/nbvHDjtEVdknqPKS0rAleBaw1PBrUJfsq4fMAWKdRIsyAZStupw1YfX3wNABrz0Z4tJOJY8CazBNdgxsGh4JfHd04ipOxu0xA1yYva6JuiIl5shWpZG4xUSmgQBPwNL/8kP7TR/lrj+pvn8RxzbXCurDcReOHQqEArFBof38hO4pdXHAjLn0fLvkQLr2jRgZgznosNpJm+oEq6yI4qJaVzOX2S9L6TC2Vh1LZuGe338zLbTTlCcM9AAv91mcLWGTZMp1THlRtH/bc5/68AxZOvcn9hQBYKxtyzjBgDUuNZPsjBrxyP2ECQBUgkUoVgFJWPJa64eyMTkxSf2NJSn4SFIA+xH/xiP7qw/i1x/BlrZ89gq9CoQCsUGj/EgA455Bc9PZ02R287ON66Ip8KVKzpVh2kaT2RWmCClPhF6TFSaqt7vkriSoZt8pGGp1ZagEsXb5ViWMvwAJGaVgrgLWSNboOWFjiHs48YBkX5PR6sBrAYnfFPysAi1u6yvYHWL4+2DbXNwFd4p6BDTDlJ7Ci3gaY8jKlqX4imBQATvKb9+s/eRC/8hh+8zieDMIKhQKwQt/2v+7ZU8qRCpUxhEgqYKqr9wRIcuF1evH79MoPy8UfTJo/zjOlmrq+DNlZyGBteJpNKXh/qJb8VEEx6EMHRjpceEyzJXBU6TN9VLZKaH9KQJ1rkaa7S+B2FDrGKl4X9mFibQMsoAtBhXsOl//0Byz/HgWN9oDlYUg54CSuvQNyBbB8p5TfG+ixCeOIUd1CRVN9lvbocPcZpHZr9ZCuMKoP+tMrv+hiv2v8v/EAAcvHgwkANJEP6794AL/8MH7tCfxxyj6Zzs938vEmkZUVCsAKhV6chhSZWCLUIcAu60Y2KpBb2nUHSLz8Vhz7M3rVx6aDN2ymHIuQuCuaP79vZtDxy2TSvgDL+T1j/OrrfYthhmW3ILv1zB3qwZhYWwDLJGm9kADLwKUqKLn93zJWDwE9YK1tyNk/YE2r75jPCLBqBLx2pzEALGvIDQ+1BljDjC5rbo0AS/Nz26ZCGAdrNsDyTU7wzj/Vf3w/fuVB/JYCxC5yYd05WzKHxtEPyIZCAVih0Fn9i54RqoZAsu64SyiXlXNfMl3yns1LPypX/mDKVJJITZomEEgJEDYbkVtCWrjHThS244TJTAXOp5dsS7u1o5rVMRV0TBrW/gELNs99JWsUMOuc7VKdZwZY4xLhXoBV7SsF5BRjGsqjei4Baz8jhAqV5aT2ACzxhlMDWH19sAtwH2d05W/l0uFomU8B03YBUV1gQAJzvoOW5vpMT/fhH9yL/+tB/N9P48FyT+X3cAd5/BBCaoRmhQKwQqEXlYWVYYrIVcId4W7SHWJXL7oex27HSz8uF70LG01UQEShG9VSEExIoAo1JYpDq+TJxu/AcWDRVAlByxYzkC2lsRawsDDWELC877XWhrWkPNg+95aKaE5bzyBgDRhrL8Dyz94ZBqwGL3AqWwjX/CHsG7Bmu4i144y9b4Q2YnRaT8DaMj/Y21dTm02Rf7zUK62zJeZeZDkCc9BDrhtC5QChig3S4/j39+If349/9Si+osuqA7DskhJhCgcrFIAVCr1o8Kq0hxACUpmQFMe+k1fcodd8Ss69XrGr2MGuklQtI3tIilS7y1UV5d9Ld3ayTVQ6p1UtPlByZThtS2AzdNhZQuRu9y2Axdq3M/S98s32ACwLRs0OwdldM+38zwpgNfeOlRJh9kgUzWVZ1iKj6jNuE8ZlawPWPgFrP0tyuhurjDbkOMBasa9QUbdPwLIAtxbQsMW+6gHL2lcDI22x90qOg8wrOEvZHXn5tErJcnuKdz6g//Cb/OeP6H/a1BlGQUr5PgKwQgFYodCLBrGESTMmHTyAY7fxqg/rtZ8ROVcVVE1kcZ7SHL6Qe6tVl3XL5sKQekJaSxDVBrAqOixbnF0Pu64BFuY2L5rrctO8VZId1hfmdNsM0VUJnYP1vACWe1yDdPjTASyMltU8L4CFfex4ngFrn/ZVPz94SvYVWeqVtb/KOljLokOPhmXfwUTmScNcKKR9lvXpu/F/3I1/di9+fcME7kb7VSgAKxR6sf2iK3Zw+AJe+V245vtx1cc1Dz2lUvIjwJRSTgpNAFVSbglXQxv05orti6qwktodOJjrbsl2U6mrEs5VRZ/8hJWlhEw62PoMFzc68sm6XTf7ByyTnXDGAGueytzmYD0jwJpPQ7bSFXyUlGGLU9hCyNF7q5SXcw/AmkuZaysI8w2a+cFTzRfdE7BoHN/lsKY7TQAh8rTIDFLUJHlsMJcNAQKT6YVPSPfhn3wD//Be/Pq38BiiDSsUgBUKvXh+0Y8cw9W368s+jqs+KKopEdgwTaBigxxbJQnKbBopE0ur+ybXBCGKtKy76ct8gz73+bvWxEJT1/OOl6WlgYllind2p81o6U3XhtX3uds0LLQtXM86YOV73w9g1Q1CpwdY5ca6xxufDG3PZwew+h3PPWA1KwhHDfXakpB9DdYBq1sUPf94Wx8UkxBWTyPjFHP3VdLdCTsABEkpApWC7xtg2ikbDAkloQLcj1+9C//oXvzLJ3BfvCmFArBCoRemhEg10UoUKU8qCQ4knDTXx8QLr9SrPyIv/1S69G2YwBO7yh1wl5sdFcWGDZeYwAXXA1T72UuDuW0t94FY6JmpG/Qb7WZ2jNUWy9rxwGYp4SDxnI6rOEdIzDcQZ1kta547B8tYXLU6Wf7pXpC0xlhtK3prXqQBYLlHXR6CwJl5sMNuZKGW5R4NuPV5Cg2p9HyzFbDaApyDJ4CFRtHUBzvA2paAJUj2K338lSUnqf/WmoPQ0Bi71YQdXRUkHXXZL8/M5J+0mcOwMp9YTkl2RXeoSjART+i/+yr+z3vwzx/DN0BQRSHgLlRqR1eCXf4ZPVuhs1NTPAWhs/OTgSprV4hOeQEIyjIaIRQQHr0WN30Gb/qbvOmH9dyrKcAmTTIxmzJKqhv2qpcEY9gs/gptpwxZYSt/fV450yQ/uStyvVBl1yZpexuS3gfCaCrNeyHLz84Xa9vQUywi7xLR/O/y8+ZC7k+r/STGVUNn2822fZLTAfQ0c4vFqfHtOwWw7FesZ2a/7m/Wn+QCWN0N1jPcOaSr+aUxvNU+LdPKOdB/gFj7rk2f77cfsumCGsRfsXssK89n15omBrelseV8/5Y7H81d8Mxcf4Avu4ofvBTvPoeHT+gDx/FQJSqbLCeg1giIIKxQAFYo9BwSFhU7mFRTbv5QVXICFVAcvQ43/wW++X/jDZ/GecewC+wQaQNMKlAoKG0K0LzzZE7KgkLtjcqEYIktYIsUy/WuAg2HF/t6y4oyxhyaO50daywXv+X2ZEtBBrAKqll20eYsuQZYK/TE7kH6LybdzmGnClj+QRGj9vMGCGhZtbkl+6+4G502YJVfEfYZDQPAogea/QOW/a2gf1o5OqAFrJ7AVgBLtwEWGvtquaV0fGmPL9iwzBhuCKl3RAGP4MpjuP0Y3nOIh76F+0/gYYWU08AOsEEZVNQJ0bEVCsAKhZ5DwAJ2Ul6tVyhkB9zgoutw8w/hls9NN3w6Hb4cQuySU2ICOVm3CGmTO0ZgqMheTsxsOk3PCmcUc6QxH8YUatx125tSOjKlyrFRx+vqxZnNB/gF4Iixs1XvoXHC2LpfewNWJhF/As3UG/dhdJ0eYJlncNSWvgJY5dUhtwOWe0SnC1j1vnrHiDgVwOJKcvr83XY9jvcwxR1Nt8wP9vmiYpu0zBMl3r4yT29rX/mPIA6wKnmqQMANgdIOX0qBei4vP6a3X453HOQFJ3nfcT4ECMrKTJmN4lAoACsUem4pi5KvPoTiomv5yh+avvPzuOFTOHIMFZfyHrQyIqZaO34UnLprtzex1FCQNrwy30xhf8RyiWknqkcolyk29a/ZlJrrj6WdnCV6Xju8yL7JoOu52lPkYmZ0BZYesCyjkSOg0YFTNza6eAYAy7TwqDkpbRhuK2AZbOoBq7O4Tg+whuQ/PwFro4tjwFq3jgb1Qf+7KyPE8faVbrWvxndkJyvt0p6Gw+x9NZ315ndK80wnIBPIuqwwP5JzecVl+t4rcdsBHj6J+47jobytJ+AqFIAVCj0PBhbnObQLL9dXfQa3/gJu+kE9dDmmjEckFJpKczR3SSn0UfwhGitjaGItzco0xpW3i+qlRhtQKcN3S1uVbZNSB2ELpZW+8dlumGlMwa6e2VtGS+nQfE1bC20MWGzMNI8Pql0x1JzscvAz6WD5ZrAu0GGvEmHZ6/2cAFbPK0u59lkArKbxXNoGLJ5+fdC6aO4EBkXA1v3y9lVjmwEgJNtXwo0CQiFBzbsRsEMewqVX8QOX422HcOgpfP1beAxErfuHQgFYodBziVkXHJNX/CDe9nm+6kdw5FjpzFJCgE0SkTK2NIEpR08rCIosLeqq20wsyx/GHpLWy/GcoUtykXoam2Gu5Rla1OFiYulcnuOoKUqHbVgLROo6n6p9mCNWoHGSli+NWuw9Pp4RwNKmXAvZTw9WfULYHPe5BCyuANYIwlYBS/xT3AMWR7jW3JijRyFdgDu7J1Mcclv7CkP7igtdLQEQ80cgoUwCKOuiAxGIrX7PT1ASiuIQrrwcd1zOtx/AgafwjZN8itGCFQrACoWeOx25eHrFx9Nbfw6v/3E9/2oqkDQbULlhJWeCMl9QShg7SdZxw3p1mMlgvnro8EJM13dlHB32oDAfs+mUYne5neuGpjHazR6arwy7zl3PGLq2bYuJrYk1+il0972CJYMq4hkFrKK07OrZTw+WrNxjD1jS3eD0AEtLy3bPPdyHyzXucBffYtV3uDd0xZVDraQ52FFTl1xqf1BGJcjmZGTUdz8zeA0AkwlQxcT6V8G86zD/peY/EmYuPlh3bBI4D1ddye+5DLeQmyfx9V0cj/e8UABWKHTaftQEUZYYHbVT32ovFOeci5s+glv/F73lp3D+y3PCIXMpsHZ2m4imuTxH1rXBHSfQf1WHQNM2V1mXgp5gbPC6KRcuZMa6dcSj2HL/Fn3qjZd2X7u+hhbI1Jlh6iHJR4PaYmVpjmHb9j7bSA1pUK3R1wCWjniIbbVsSFeuj9s4cGTnSWVTqzdjWJcQtwbVFsIbhmBJX3pz75t9sbhBTx06WOJrZxitptHq162sIFR70j2rTX5P8wzvUu/Io96SdEptH+yoPliy46cGGclXehMAACAASURBVMnJj2ZyKSPaA5QGfF1eFwLYmWvwip1qZonqBXj5VfzeY7h5lyee4jc2ODEfVZaRwwaBJTq3QgFYoVBjFeS0TJ2gSiFUIRDkZYDkBCWuvw23/BTf+Tm85CaQkjSJoEwDFsKqVENyVCNbsMU2W9EiVpvXULugTKmOVG8tsPpQqi49a8UuYhug0M0Dztmh+YqhDXCMwhq8G0cfqdW4WfZGOoyP4gxdGFpMK4AF7OG3bf0lcPjor+zszDauVruwF2BxwM6D7Kjh7TEKwRr2rvWAtRb4zoaK/JMrPcatAFbfy+Xvms0D59AL9BkQ0+hUu9wv9cODrmhoHoj79LL0ci0tZYmlxq8C5mj4c+XGV+onjuDYLo4/iq+U2zItP83yMaxCdgBWKAArFELzYbs6KqoQTiVWHJNCr3g93vaTfM/fweW3FJtHASHKghtSckbVsga3TN8pe7woQ4L1MlIayXUlIHS+BrXtWZZR3EXNj9kZ36G/ZjcN723+VtlXyLZ6OAhoKEdT9hfX5bvWkmoyt3zxCKM4rjFlNYDFUwGsNoHC+nmuQ45p7DydKcBi30H/jAGrLxHK8KPF1g73IWDZNnNrbg239/QAhPXtznOBsgsXRf3U0vS2u/tld9emaNgYY0sxcUnkLwYkNygzKQLsKHexOcY3XYvvP4Kju3jocdydI+AB5pBSLcaV+M0AoVAAVihU181MkFQgo1r9F71c3/KXcdvf4cs/oHKAmqCYSh0lccNJmPK6urxutv9Yzs6ucNUzegNmMUvYO0Ozk0SOi4lNLmhzaaSrEqJpJq5Xep8puhTyTCjQMpxI1xDWmEt++E5tNkTf2jXORN3qNz1jB6svEeaHI2YTNubdRPosAhZOE7AG+Z8LT3DFn8Qa5koNJbPZ+muB/h5ZdABq6+3t1pcS759hbF9pb4NxZXhwaF8RiSYLdxmBbJxgqCDVhjoFqEjCSRU72LkMb3s5vucgDj7Nbz6Nh6UcwyyiiqHDUABWKNRc0FiuAgqdmPM/z3sJXv0pfNfn8apP48B5uelCElVURZBSdrrKNGB+T14iGLQN9eSyD8flVy2ek7blkWaWcL7ZXKHTeleuiWbZizwY91OPW/aYy4VwXghoNwOqcdE4R5KyJxhd8ajUMcUAsAjqXoC1Nkh4RkqEdCdnsy2H2PRCA6zt9UEMM9Z92zjWI0axkqKOLoOqt696wPIJ7B6wOvvKfF5ZwI6jucW1SHczgtA21PvHW3q2ZIlRSaRM5VdaiXQOLngp3ns53i7YPMqv7uLpuh0rBwgD2AkfKxSAFQotl0AtVwIhE6F684fwjp/hrT/JC69CKmGi2diiQphUp/ljrpAqaoKjaYtuy1VNYZI852Yms1LQTN9ta3U3a9PqWkJtr7PVf6pN37ZTqglr8HTE2oRODHqq6KuKNk8TpgnMotKSH9EQUt/bRKxUu4D1UNEzAliuB87XT1fmEF8IgMUVH2sIWM0dsZtezAu6vaPDPYEMXb4oBvFULU8P9w+Sg9046PZANwdn9Y2be4Td+lwXf/ajiPPv6gQkJMF8NBVSQGADiOSmdgGVpJ6Py6/F976ENyYcf4RfUqkUzlE5ORQKwAqFAOJlb+C7Pqvv+3u46CaKUqkEEiVBhTm+PSXJtYCpbm2m2tV71bvSppvKlAZVR4vdtB/iW1rdadrGdbGmquVCorHM3Af7bsVef1W2LfDEmrek5qqqirWlhIPq2/wQlDrEpLbPncDaAN42wFrKmtwvYPlnoTb168yLzoE7+wGrWTfU2FdrplRzEPF32ge4Y2UBM+r8oBhski5/a+w5dV6U6PKM2ZMxdNXlQSz1wcXHIjCR889qqSpCMAlUlVNZzZSDWCYAl+KVN/ATR/TCp/TBJ3mPCV6NMmEoACsUqm/IhOIll/OWH+Z7f1Gvfy9UIISKZoyR0pQ+YZOSQBI1JwnU4lqZPRpe+JqAT+MAuZupISf/sy2Hzb3zHNTUaILdzRwiMc5fMDnt3kvyre7dADxNjdMQhLrbO3rQzi4bzbS5p4yrVLSHg6Wn6GANaGaG2udiivCMA9Zo9G8FsDp+wii+YR2wdBWwqm8ka/ODzc5mayJyrSd9+RzArj44zMRq5hDdHkNjX2XsK1EqxXeW7FmjpGdRyQll5zqQEjBRLset1+K9O9h5Uu46ro8y6CoUgBV6sTtR5lpTK3vlE+iyQDkvthEc3OGrvxe3fQ5v+W/k8FFVQaoBjmquLZrHhTAIsjYDaLQBV42b5fqdasPWMlFoVzX3nKTLvdCZWMUSU785x7piHGVE5eR5zBFWDbLMXSjL1Qj9lmj2a3y6mFA110AtQLt006u21DTTTDLPVRN7wabI1QxqDqqEftxylbmWwy7bhJrzHACWYNDexBo8u/b72UzM2SdMBqeptDvDx8kOrkDWnKF4i9XW3dQAh8EvZT/dqc0cHxuvqzG3pBt7nCoOtzubzUClSW9X6zk1RDiNduNM3r5aEDC/HssZltlG+9rlsuBMZiX0XUFSmWdZYH4q35gHcfQ6fOBSvfkkv/UwviLY6OBDgPhtoPMHsojOCgVghc4iPwqTSm30FkBF60A1IEKdiSJxwjWv47s+q7f/bVx8HVKxdTClJddZjalj/ajKQ3Y2kLRBmFxyGaloo6HYFwqbJvSRvdF6JfN9Dfb3+RLeIAhee+cKiyc3vMa3wVf+CKOlhMM8T5OGNWi7slg134boEinG7hT38ZX9oPliSQ5W5XSLF3vAyr9nst5O5vvj0DNK50Jxzdnq/Zv+vgYI2JKluy/pioxUd6i+PrhmJvVv+uxWPQ3TGRqbbc3Wwmil9FpxEHNf1/p9wXRo5a/7WqTLpRWkDeQi3HATP3YhLn4KDzwmd5ePdZmfSpqu1s80+RMGTO5pKBSAFTobpFBqDqrSSYvxxLrJBlrf9S68BN/5w7j9l3jD+6HgZheTFCRKwMSKT3RBUE32ul0son0RrEMoy2R9odC6U9rFIjge6auEymaTspk3NF9arqus1cPWsyGX8FP1m+m6Qie7tNLWKhsC1jCktDd4HC86a+zZAyztGs72bnJfd7C4znBrgCUrJcK1aiP3salQRi5dv4Jw29Ib7gE6HrBaL603twaA1d8Rh1ul1VqDvt9rtThos6/QpXnRW2XNoKLo4Nmu2cJM4FSbty7HLdfgNiofw50n8VR+U5htOS2VRqhO+SMf4/06FIAVOotU+qFq0gA4Uev7nFKoKsArPsT3/Qze+hM452hx7uePm0oRauqucGx7pMoF1E/M2drKTGNdEINZcdOOtMPGK3DODlgC3+maupbL1Gg1zbwMZ/alzLKQ2TUYNkWxSVHQgnPO8dKWdRb3yy7UtefD0SLC4TJB89D61uFnD7B8S9npA9ZpO1g8RcCSls/aZ3IaPEY2y3k84ozbs0YBDTqNgk+HAe5iE62a9PZufpArVJf/PXk46+2rep6DeqitFU7m97hNfKg9ZGv3lcADNfBdQAiSbgRyDi66AbdfhBtO4skH+UfVygIxaZ4xLKnxIrFKOhSAFTr7PCyZrRchUh7zmwCl6uU34F1/RT78S3rsNTmxWVQVpMzrX6m6If1FvIkn8AsEbaHQzQmqb13XQdLo0mLl4tpd23ETOtp1VGsbGe8W76gxsTyMmaJPdzUHmyiv4eDhELDsEKRds9M6WMAooAtbwhqeE8Dq/LYR3u0DsLCyiPCMA9aeQVkyBKz+HrcC1shJGtwd2y549s8S95fevuedNikMTR6E2NmHUXS7jKYd+2ej28Oz/PZPJMGJSUlR3YGA2anCxXjlzfjYQRx6An/yFB4mJ2CT77p+xlJViWTSUABW6GwzsVy1Lq9llXToEN74CdzxOb7+z2E6UL+9URATsSlvzqKbPJTtwKVBH9uB5E2s0rSxWFiGaVhbzl3wwVyMI/qxPgXtrkKb12BPBs5FM0Ro1gJa02K+j7rrcDQGN7fht5dz2l06DSr525NsuMpj2CAFfgxYSzorn23AakuE5dXkqQJWvo1y5kcOyIk9HvW4OgastQ2Dg96p/qdIrt9p2/nEwd01IaJY2VQDV6bc276SDtHQ1jfLnfa1y8a+GmVfudlDWxxsgW8UQ29tLQAHwVT++kndCCVJabIi84pDuZrvuoq3bPD0w/hSYspjiWWfvPoJ3FAoACt0NkjqpKAAmKAJO3LNa/W2z8qHfk6Pvixb86Ipv8WV5a1SstHzx0rRZjstBr6RsZasSeNKiiwOUwcP84iW4QaagUF1JpNpTmfrqA2coeY/1aW6G8dNGv5ooE1dX3xbudNm/Z9voh+kZ7UhQVzJUOivvkuVUPXZBqy2RLiPKcItJUKWwTRiLZdiCECnAljYuiSHe23IaQDL9nQ3ANTP8Q3CSFc222AtX3QEcF1b1Xh40PKQdKugDag5opJu1U+/bdrUHE1gadvTlulKJ8399DoplbtCoQLcTBBAL8TVr8T3HebRJ/TuJ/FAYgJ2WBbypPCvQgFYobNLylydEgCq556Ht3xSP/y38Mo7VARaIqdAkZQ/fVKpkntPNeX2diVzQ1a3sNl3ptPQD3vnxrlNdGZUiy8mScHN8ZXJxqYjPv+ndlXCNVhxVcLlfm23vS8QmYdXhxGX/TmdWeUb1DA3io23Pu8DsEjvJ+kcdf88OFjS3cn+S4RYr3gSw/Lq6QOWjJ7V0wCsZkPOAv/dY5lGowkc1QftvbRVRV8KZPsJBKMdn6vjim1iVtNN3636kS6eXjyt9oMCDXWBOjP0VD7UQSgKPQAqEsArcct1ePcunnqYXzmJp1HMzchoCAVghV6Qqlt5BcM6FgAVXP8WvO+ncMdfx7mX2Gt/3lOsNGyTCUFlDo4qu22kiaTCUjsr22pIv3+QqO6FneZT+qm8eeMxWxpg05NkI6x8qrvbpjyijaWBrC3ncXkkFmJcRc+fv81W6NIrlkwqd0EnmhKnzpDEdsU1HFDaq+8SM9Ys5lkFLB+UZYzGjsX7J4y0O3zrlqS9crDca4Cy9neYg9XGbHTN+2NuG7GapzHVPUYIFcPGKa7GLoyqfpbneuOnPPs2S92+3ffbnTnovmorj7YgPY0KdnP2Vf3Pbd1X5HgPj3j7aq4b2pb5xioj+9it+n95sw6AOmN4hJe+Ah85gkuO455H8KeEQtZ6EoWRCB8KwAo9r4RVAmbM5+8ZtnZ45Ajf9hfw4b+NV74PKZFdc0Uz2tbHDXTOhwmdsv1AbXSCBRGfEeWnDttrLU2IlO0AI1vvpw3NQm/nNLN7M9m41AYPN9oYWEuPyDx6uDCig7C8WIb+5EbmTVtRGsVmEg0td3jQ/fjSeaa2SUvdlkZs27vT/If6e9yryV0GTEPd2uQ+mqUcm2Hzb4foNvsKPTmtOFjN3VYs0OYgewc0uIxZV8AerccZ3bU/vdUaoqc6jHbjmLtLHoxa+2pyLutqntba8CC3JUHUO+UAcAXYAS7Hm67GuwW7D+HLJ/RE/ZBIqiwfRdzurVAoACv0fFhY87WegOb4vuxpXf8m3PFX+YG/ivMvEZPR0HaM24D18k7bxoQ6n0MNceg2cmpv6Sp6S/GMiw9kqYX0xlatEvqHXbq1zGCjtnN/rrJZzkHZQUO9zrG3SVSHRa5RyqhujRvtP5DTNfluw7IhDLGpEs7IW3ihJp8NTLI9AYsE6e/BpcI+P4C1ZwPWnoA17HCfVg7i4ubXWafHoGlUk5W16UUO0xm6FNDu3LjWfcVBPVHqogP2Ww676cXhkzDaw6PDxTvit1MbstzkP5ELcMmN+NB505VP4puP6j25C1SZWPKRJfbthAKwQs8/X2XXqjKGKBKPnIe3fgof/YXppvdpkjlYndo1p89jfY0fsqDSXBWbEzhtV3hrYrG56KnvxLIW1yi2YGGgJbh8qfENfKm5ajas6C2oZ4YE/SxhG4Gq6vq6XLyq3eIz6vIiu/ZwG9Plwxo6gPOkaPId2Ky46WFohJIeerCKa+Njqh3w1H3tInweAatziVq7bhrgI9slP9RVwFpPwFqpJGpvgE31z6C961E6w3yGW3bjoM4k9n7bENSaNc+9fQUb02VWEm3peZ9GvW6y8ruRuw2S4gCYgB2ky/H6a/UdwIn7cecGx+c3KZa/iWjPCgVghZ5P5be7NJUtgQnXvJYf/Cw+/Dd47kVpRyCQlAAqNqCWWqLpl/KXP2XbTeU35Gidum+X5/j49ZkztFld7Pq02hIZbed4E8YAt6NGl2Iia9s3vavktij6lvY899Rik61uNov/1Hb4mFXKS+w7oPWiMMq1ak2v1t8a5Ln7PYNAA750RR/rGjovTCs1nmKJkD7gzHmNL2zA4j463DEY4lMPKOMNOTLKTAcG+LWFeLzppU3gJ3xTVMNtHfS0xEOW7quGqCbzerX+lu2+2itZdPLToC6vy+4RMtHthhE17y5UpHycBL1QLrtJv+cwjz6Bux7DvcQBRQInKCfUaZxQKAAr9Dx5WClvGZTpoN76cXz05/Haj0ABEWyAlD9NJ2CCCpDQrbths4Smmia0pYD6prqQEwcmFhqrRtEw0+wP0Vb0RoXIha+MKcV2ZY1638Fz2PwQ6/Bguc8BHplW66a/PnfqrjCTS7HSlVwrs80N3XTePJbo3DL0+xGBgYHXJ8jDo0uLUKdZIoSLh3i+AGsanfGegDX+KW/bNI32a/ZV02XFUSxCbojs90z39hVG43s0hTyPdIpBSns7q+gWHTZpogZ6uNJHP/alVr4+rTNoh7Y6L5jPb1YTqEgCyeXAa/SWl+HNu3j8XvxhEoUmQlPQVSgAK/T8AhahygM4do3e8ZP4xC/gJS9FifBJrMlWUIGAtlKGpWnJrVtll2LVcE9zmW9MLNAAiy+RkTViVFvbyoRJjfY6m/Yvd++dmdM06dueIW3SyMe7Al28KVwH2ApgWRwi2qu7d7zQNt0vTzgHP+WOtbLNsM0n9QlZVDQmZTOSsIpwfYkQz3+Tu5wWYMnIvmoJhn5/oqWoAQNhPV90Lf5KR6VJtQ7TKhitNkW199W0mrkuruWXwQ0JDtu8ZKstN7av2l7G9kHVH9epPMcJEGF+b6ICR3HFzfL9h/Xgg/qVp/BIffTBWKEArNDz6WAp3vB++d7P6Ts/TRWkDSjKMq6uUCFLllVtcmfj+DTXXevEqEMI2j1+atYEFnKq03lsIuC1PZoOOrF8//q8EsfExC/m2UwQ3phRe+n3HpVFrpnP+trf8p+26Klcmawc5QyZ6/Nc7xsCFmyPV1dM5EpOKbZtJCzcZsYkh1OEewKWi2loYvdfdICF9QD37TeW8fzgOF+0YRcZIQ5GMVfwPDQMi99rtfPypKy2Z7Uk1NpavbHHjqia/xRvX+VTFSBxIzrtQDdQgtN8/opr8Y7L+R1P47EH8eXYAh0KwAqdeWQqfevl8j7/YwcZnMqbnICKCy6Q9/04fuDv4upXZ0oQit8VWBZX5NFCYkmu6mjB9yI1mwetpzO3zas/gmqXylD/YZHFdtbbO/UVQOeJLR9lc8KWJ6GlCkgbHt+Bl/GwKmiys4oMDmop4ZiWL6iPeGAXKN9XCeHMA/fIbFSYYw7WVd0dVCX/y9KYdvT/qHOR5mEMo7C8R1UxmvPVUvtSbAbf8lzJOC2DfQgWbXo7BymjayDVoFgHuLC1uQ6JtLFSzA+mpklravvTx37SKPocKLjgl9jM0VBN/EfnPLEbvsuvMU2cWFPL4yinqum+ssXB3ierBFbuZX74Ylqy0Pa2uywGqY9iqn/cc84WR/clkNoxsNxdHWDmJXrjK3AHsHs//vAEjvtfVyHaEUMu8TRhd4UCsELbDalyqVZT5ynpn1paYiX3VPO6N+GjP613/CQOHQZJTaAoEqa0BGPNAZWzR+OinmigwL9taTWiuonCpRnL5Bo0G6Bdp7zd+auDuAO2i2W0IYamy3t+hszYnRsSRLfkpGltYjWW1LTl+o4xg1OmfWq0IhD+Iu6jsxwGLRDpsQ8DB6upburq70vLIattWBjD1YBw/ALEJSF29ePAcDdzn5aOJlyU47tfAyxye8roqMN9wGGOaPu4Jp93MMgpGAa4N0sDLWAtYDRmso57vOk1B8YOF+BgNKwHuwyHLd83gDhlutKBp9WMVXL0ApkHPqx4tkYdV4YJBBuBZHQ7iEOvwPsu5BWP4esP4x7klV/MPfKNJTuVuF5GSTEUgBXai6/mKlv1kBRUZvNq/gpE3v4D+omfx6s+OJGq+XOdgAkiSOI8laW9ifXy72yewW7BZSsgXSmtSyilQlUp64zl9wm6GAV2i2R0yShYYq5c4rp3mHxulrGU1LS0D7qpykmU6FHbCO+70Z0v1V3L6b9jIlK1Cfeaq3VNPgXZmV49YG0Bo74HS1e+sT/A4hAfx2t2xLcr9b9M2wDLumzdNXENsKb+LvYCLMEeEaMdYK3OD2K9jmZfgFFCqcIHuDeNX7LCIuh4rktUb5vJ2uIg7RGMR9tFt8Nn7tuJQvF3NI0YUcz9Dqy+cU79wrICKGQCgEQSSEJcqW94Kd5yEg/fjT8oQX7UBJCT6fVULkvpg65CAVihbZL89qdLFgImoA7UCKBy8ZX6Pf8DPv33cdGxChgKbhRCZTGw+qvKshGmNDbpMieI0u1uM0jpLjPz8pzFvprdpqY01mwbdPDEJR9BTLOXmSJsg6yc8dYlaioGRldNuiqVRGmduYo+fU963/M0n4YaY0Pd3Q2qhE3Kl4tdWI9rt6jHdR9I4SY3h0U2f3YzR56ig0XFlvHDcmKyuKynCVj+P2VlCyFGbV5r+20wasDqbSTpzkFWIh64jwXPvQFmsWbLdmeX3q5jlJRu3fJoS6Cu2VfDacQGHLdkQFijrm+9Eg9ttFOWXdzo+Imq70MCJqQdkZOEKI/yitfwozvkA/jyCTyR5sbAHKcshGJaNoWGQgFYoW3Sur5r/seOIndICJFw89v1+34aH/jxihFkCUkXgapCqKkJIFggqXMyzB69Qi7OKyqFQsUQnmouvOu1qgygzYXYwtN8p4pEw0SoSxIN4jjQmd+TzaGM0dUvVLZmVIm6NwZWzT8y4MJusbSJiV92PVvYsh0+g2z3hpbGfe62K45mDHMlz309a3QEWHBt/6cCWP1XuIzZ2eZ1OV0Hy4cjzEmu49M6VcDaXwLWaQCWTh132uCGfsGO9AMBvi9qmN6O0fLBNZLrd+yM+s1HVUjqlvKlrNMVsKV1TLumfpfmag5bgE+QiF1QCFHFIUUS7ihAeYW+5yiuegL3PoCv1SzTOSSrbKkADrT9iaEArHgKQu3lR63XIpCUm7pJ4Lv+PD75ebzqNu5CRJmoqpgIhQgSAKqIaOoMDzYujzFvmnh3H9o+Zzipnwpcvmff5xckGHZi0eQRdDt2hgGkTWI7sRoEP+PXUoarPGTGG6E+QVXhS6WwyOVqfzMiGgBq64nAgpva9bmj8cb6RKsmkH3F7iK3TRGOAItt0FnrREG32V9rJUKSqir1MZ8SYLlWd39dx+kClpxmxOjgWwNCguN1GQBWW7NzfNYYOaP09u0ra5qoLUty4plmODwoqyQ0fuwywjhpnjTjv85PyDBSVVba1+a7ngCAEyfV8ukmIXdFbCaIMl2J116DW0/wsW/oFxVJRZnf+Jae96CrUABWaA+JmYnfgSQmJSY9egx/9ifx6b/LCy/FhnmXhE4QUJPm90xJEEjSlIHITSj10/6Ln5/xRscx665qRneaLsS8eBs6rzscMlbT10X67cUjDIQvONqZR7Wji4Qdd1RvC6k2CVPF9GqqhPS99rXHg40LNJ+K+MYyQ2NLWINq86DWAhfYNqq3a3a2tGG53T7anEkDU2PbdA9HC/ttcvesus3B6mLsm+p0e7SxSba1PjgN2a6LGN0CWDLiA4wTsAauUm7A4oq1No/aYTXSHWv2FTxg9cODK/XBPfrNm0CvvomerfWo0vTLz/zXdetzsNhHc+MXCeEmqRzIxXiFIAlFKLvAAXCCHsFlr8dHyXQvvnRCn1QqObH6WNGAFQrACu3pYGW3SnNjAVWJKd3wOn78f8UdfzmvQS0X/gmSoApMhOY1qSWoAbrJ7ap9447ZJ8NlThBsnCH6Pqo5hbmJuGyiNReY8N3uDk90aTQ283Rsgt3X1w6i6UCa9zdbaqwjjmzu3GS7qzecvNvUdVC5r5ukrkHGqWLkYLkwjOEexjFgDQIYhoT0zKYIdWyIca7ADhLzIeRaENR+AAvoUzm2ARb3Bqz2OPKMAWtLHpUnD7V+3KABa7Qep1lZg8G6Q0c5TfYVRulctvS2ZfAQ68t2sLLwZ1qxBqWNsWhbr+iLg5PvL6wjlgmYhKrggWKSiwA5LRmlhqgC3IT3XIzLH8LXHsE9Kio6aZlH1ejBCgVghVYsArhGpwTJvKTv+F5+8m/qG95PTKC4QX+ZUwYWN8rkF62ECNlwUK1XBmVrIwzqUG4bca6tLRNztvK1xFy1a3nm4HJdWrj67Aa1QGbuji3xNEgwp3PlA6Y62tfNEvot0aNtxuriv9o2stZhgisjkvVywlG9zy9VHIBNA1szMZFzOznXAatHYBs3Sh8VAT+Myab1DUh5PGuFzM7IFOEKYLF7hH29VczzKIM4sebVclkDczzBeIZu3MDE5maNfaVm/TPKNC+kbavnyFhqo6qAYTqDdtyjllfmhyztnOAwLmGwK1qcX1W+PjX2WzmyCvfgPFnZVJ3r9qLLUzd/JErmZRLS+nx5cPUqvO4avPFpPvBN/VKdrdbls9Vy+vMzExFZAVihby/AmtgudpmvxXl3hOqR8/Chn+Bf/Ht67FrmXnb1FTdVP42vTbR4+eKMJjaEYQmSIsl2KwuH+do1Sdrni9JkadUqG5c7HY4T2stfn/9uzaF2HY6NjfALm93TsJCTadLXQV6Uhzz6B6se9gaXbNotOo13ZbF5qfGRHLZhcc128j1hOu632gOw/PYX1pHhgwAAIABJREFUD1jzy9TQjR8yGDZ58bkCLBnGsXZBTZ1ZtTUBy/9335vVBn4OU8u7SCrWPwrSPqnsj9BX/Rr7ahgu6guRyaer2/og2RcHfeREbylhZZ2idM/qkkTvuHCtX74lORcP5l/KBuZE3ZHz1y/GVTfjgwlP/ym+eBLfWkBqCd7VZSsYg64CsELfXi+8ptLDI6DWUtWOMAGTIuGqG/h9n50+8dfSdBCSagfn3FhdExbc3jouH2OhS7rBcgWt+QJqV/PRuVMW1NzCQdcP5PhhwbiuYUth4Ynowcg3VMGBi+M51aUCOGx1X0KzSHQVTBNnCh3gnWnb8ldudXhBe+E2rb7sOslKxoHOMRaUjpla508HgDVjKwfdSiPAsvOPo/HDAYvpnh7r8w5Yo3XUK7cZXqqxF2BN6w3yPV7M/ASfICImqrbPF5WVdAasrHb2OZwL148i4C0/ac9SMNkNWME1rvS2D5c6c2WJUJ/LYCYHy2Flwaa+nb+Jw+iqqHURwwHZea3ecRDn3Y8vP8GHy5+i5hDm0vpOCLhDjf73AKzQt5NMGFTiskgmKXaADV73bvy5n8Vtny7tBYsbtCQqeQha2Q1M3/Ttr7I6+1j0q3CcSeYtDZuiTFtxdLEOGMVcknP8ZjdOaHNHF3/FG1Q2GcuUO7uLcdtO3kZMqe3EWlIqlsx31UFPelcbXcbrdHACJYPLrWik7rsNyy2KHttU7Iy9HpieEWC90Bys9q4HF2Zut6+2A5Y065LGLec6jSvUuao1CGhg0/ZUH7I97Mi+cn4PfDhn3+PV8lO7xVnX9hvKXvmlU/c851+LXIJsetul36I9Irlp5XXsf0Qa76pS4wTsKBM2r8BbL8GND+MbD+BrE0SRipHFMnlN3WiUCAOwQt9+2gE2IKA7wqQAIcQG3/UD+PTn8Jp3gmBSTCydnhnE6OMu1WdTtbsi6qfJOexqqcfB1IrYxmDSfN9wQec8mWu28bqWccLWbnFf9DHxg9u3vUr54c+pDvVbSy1vYLd0bfikBxptxhKbvT2EX1Dtlu3o8gw3lVPtz78vxBjvDY4Fl1dhrQt+i4NlAatd0nx6gLUS03AmAIuDVr/TASy2K27QP9u2dDWt8Nl6+Hh7BEsw3fpCiJrWKIc+LhoeK336XdlxuNpZMYpuXwI/Och25yiPinu1ZHlm1YmDBHmO28sGxUGOX0RtSLTNnjBHTrLJZcwEXInvuBHf+TgevAtfzFsucmPW4plHiTAAK/RtpbxPENSpBLErIDj3XHzkx+RH/75efBWYSicTlEptOpHb9ctLAc83LVUUcODVrLKxnVTKDoaaD+xs2/G7YIf5vgRtXEJjWS1+W23Pn9PkF6MLHaWRQyDTasXNFQEDoAtJal/6dDzUUkybRNoQ1Sh/gWzBDp2px7VwBH+p1tXa374AC6cLWAlraHiGAQunA1jDQKztgEWuru3bYl/5/X26HuA+GB7cYl/5ytfYvyFp8Ej7Jv2+Kic1U8SbT/uLbveFvH7zj2tKMwzXJ2w1lcEGQCf/7C0fX3w+qii28C4UE0imCQLgAlz6RnzoJJ+4W/9glyfm7a3Q+k4bCsAKfTsBVh7NmhSaM9hxxRX8gf8Rn/wZlYN1mzNAlWqQcwlLyP4KXVe4yf6utQqaviUdlNHqzQ1j5Y9+bNrYLTzR7YouvlZ5Y1e/PNrO+Xf5WMbEGk8RVtdoSXhfpgjtwB/d+mptrt3jniq0JpaaKFRHHn57YLlwqMkXdYbTQjapnKgDu61n4kh0Pm0l7AygYsWmox8MfMaA9Vw1uZ8GYHElaL4BLNH2VPcNWFiLpBoBljapDe1O5ZF9NXtLM3J5ulIMlg869Bnvh+7Kdg1dTSuuEsbRDDrNKbIm+GriWnuWJSSdf/Esxg2Lg9mVtkTVHlYb31GFIlCBACkxTZCD2LkZtx+G3IMvP4HHiB0gkVNtoQzGCsAKfTsRFnKruxAJ8h1v0k/9LO740TL7ogRT3iSjZUy5Nk0vW1zIzqdpL6hz37faSG+28DRfpdXvGjbzZTYMapn1V7fphY1PpqNiIk04lfqLlk/hKutYdAgBjYllqplY2tLNyh31YV19CILZGEtboWyMqBlZ/LRmv8EQo6RTOiejTUZoOtnVOVhtZDxG5tjpAdYwpuG5anLfsmrwGQIWsV/AGgW4jzcuN49XOqtJTbhof+T5rCbLFu0wXdtrJe6EyZXzF4N6M67ZhdwdXfmtPgOraeRdrdcBlwWIGDS3tb3whiYncI/GdvegbKhuIiSbaPmj5PV4x1G+9AG58yG9Oy82ZCyBDsAKvZhBannb0Tn8e/lUq4JbP6B/6XO45XYpk375p4SgdT5mMqIxFViDr0wnVA5m913qwpaKapdVV33zLUf13V91hhiaTnaXu8726krHf31RjwuHEW3slc/NQlMAdeOEtbvWZiVwNn5o4KkduAPhjMBB07ia/dPNY3FQ2yRS+LYWYwquIWNBvQXI4CYgW6RQu0SSDb3Y0UjpGKsNjK29fQ1l7OVgCZvsb7TBrMa96M+fo6Q2bZlGSY433rjFNe1TJP7Tg3Dw3cVvbAMaxg1YE8bb+uA7mabRcXJ/JVxNsI07tyfQt0axta90vuXK0XS437CvA2Y4E38XMnITJ7gGL8GCkm3kLAeUNo35mPaxT+ZR28b25kHV16JGUeR1isAOeDVufqm+5hHcfR/uRB4ntOvl5/XvrFuiQwFYobOWr0TKbEuOwlv8HMnX+9s/yR/9RV53M7JHTlcI8w1DXKpgaoYJncUxKJwtVz0deRNNDa7ZUTjXqti7F7YdnctOsBll3C6dhR5cb3u9erqT17b/nW170sJgGWXm0FQ3WWnT1ZskqD5UyZ6zem+s32btArH8AmnX3m6Po62lNA5HGDg+e6585gBgRgYbOttyuXFHUB6wpMehHkS8sBKvsOZvkWPTCHtluHMEWI6E/KW6XZ7YJ2ANjC5t192sbNbrly4DmGyix6j3XMyv12yJrbV59Xch9jbN8Ud9USuuErnVTRTzu7S2o3q4c9DmMnRsqvT977T83SQ+DH1TU44UMIGX8epXytsf1Ye+xi8Qqpy/M/vZgvL1UABW6CwGLFWoYJqARM1R7AWNDp+Pj/138t/+kl54MVSQdPno1692aawLG3ZF706hek1uPbCakp/Pu1rQZenZmqNH2a/ncDSkNstKfWSDASO6TizrkGGOiaANZO+IoalumofTThouJ+KaovpFN/NhbE99+4zV7+s2wNLOl+J4EJLDq3gDWCSavUBcBxR/NvsFLGJvwGpSKtZXChI+Ud25XKcIWDIa/RMd3u9+AathQXQNWP3ynJXUABfxMIr9tM1VviJJ5wNhlHi+2FcuXHSwnEeGqNGWAt05cD37qnfIhmFUstKP32/FaXZUr2WrznTFtrFd0bXzz5/52nqiy3TgBjoRO8oj+pI38o6N7n4dXziJExBAxaznVGCHMV0YgBU62wkr/z2n8sZdCzaXXIof/J/56Z+hTJo5QBLz3EutL/krnL0uqWvG4rJV0H4ChG33oTd1lkKhoaw25kCXNTj9lKKziNy6ZXtVhnq/amG5WvriwmFqG/bncULOJthgno6u71vNh2FYe8wEuzfhoh0ttZufS95pWzldLiqLL8VmilOXjUTeU6xhic1E4agHaxWwutWQY8Dq2/b3DVjuKVJC5157e0WnjdzsKeqUAGvqLJOGfvYErGWigIOJPAdY1N4hox8/XIuMMpSgHGRy6iBvs42/0iESYbTaGYMpPG2yr+ZHMbOI+OCo2b7q6o8DTGyjGRT97p3xwzT+0zQKbfdI5LyrdmwQAyDuvb2mYf9AXcGQfyvfiPdOkLvw+8fxVE//RDS/B2CFznK+moAEIVREoEkw6dU34DN/jR/7MVVquQRuSBE/Z1ecE9t43q1VabtzugHAJpF8trv6WHA26Q9qC4FsIt3zDbgyBug2y2hdoTecIsR8qOqCmWRssmu6ssHu3fIYOqRq6nGEnbaznd193XDJqoYpPjYBE3Mbltpm/LXcLAcHfSO5rnCSa8JqRhr3AVjrWaPd1OEeJcL1XnVipcl9T8BqHATZN2B1KaNtxKhnJqJvmedKZ5XPqVqrA453J9O4QX16u3as0HZ6tSGfg34prlTKuHp88Z5Tnw4v1onkOLddVtrF2p2D443ODRK5V617XOosMboUU+ftcSnemzmDVKd1OFEU+kq+8whe8k1+6Qk8CIKcAN0BE6P5PQArdPZLWXZkZQNAX30Lfviv47ZPlguwANRJJYmqJuSmVd+iTnSM1e5/MWsKtfNL7PVJWFfaqG/dMYxR2xTcfps+78BdxtYj4NW8BxuDyrNCrno2ZcA2gJRDXFiC3X1CfRM0QLqZxB4SzAbDpv8MPhCrR6iuO6yD3TZ8YRWwmpXTvqrYPXxtPo/bfq89AGsPB2vO+6ivrHQv/tx9PochoWvD2jpCyG7b4B6ANbSvThWw5h3PzWW+TcBaTzyfvRNXa7M7cCw8ebriCpqs0Q9W2tvho9sbipJB2U57e6wpDnK8FUdhBhqGDpz4XqjmaREdvL5N/oV1/tAN3bBv4adi6WlbwEsJEYqmDUTqU3Q93ngZrrkXX32Q38zJgqlUEiIiKwArdJY7WIBAtZQGb/2A/PDn9Jb3QUhNEApVdXZn/EWHJihB6aNDDYUQvndKIfQdXVgKctqEj3PZ8GFH4ZbVfp51GoPKNjYtN/N0YktUndG1DD6qLVcOlvC0JpZbTAI0Ww61M+Hqf7ouq36LM9BtHoTjtn4n4Pys2hOYL83j8IV1wLLPknAO/Fzpc++haPALeHolwjo6WrvMfImwuVNZOaU9HCz/rf0AFrduKuxDsMYBDVzxpQZNS4PIKMyrjk2rUC12e2+JGPVO6VpX+D6Kd21AfBNVOjz+2DTa2j4Pv8DH+2GOhGSrNTX5F247XTW+pq0bTj6fAq6nzT6oNKko5ACwyyTVzboGN70MNz3Mb96DOxd2jB6sAKzQi8DDAkSh/O4/yx/7eb3pTcy975MQqglT+ehHpIpRBq3MCufObpnpZ2nuscDAdvse2nQlm4Hef/hvK4DVoBK2OVDFrVh8MXs10tG64q6EOe/Ymf0tau12N2OJc5nR5FcRFUS4JJwSzaPucsLsqGaz/k9b+4j22Vv+H03zVgVci21Lw5Y243tt+PsQsJhT6TvvpOW+ZwWw9hXTsDUH64wA1tTN7mEfgMUVbAIgHO1mrvbVEDXW0MQ118/5Kk1z1Wi7c+OTDeMeBusIx/VB7YMYhsOJU/sQxktsmq041m8b5n9ytNF5SFczm7Kl1fEswhIrajy25k+y8fwEooAQJ6EHIAmphJGCx3DNdXjzo7zvLnxRy7rCcLACsEJnh1U1lbJanfpphvv5kR/Rn/hbvOw6CGo+XhkCVJO6brcIL21G81KbJiSpXAjbICvO75TaLr1zHpg2O4zNZb98xFOOVr6omlUq+Y0taT0TsR8wl/9pp/Dq/h51nU9twVF9AEGGLvWGHH0lMXl2UdPMr74rq2l1H1UJfddUkxZBcHGYbIHSOXZNutWcoV/XbLeRVIvp4hfrmIur9tZgvdbXFpb592QObE3VXhk5Yb5lvl52dHmW6NLzOegiUu8ssEco72QQZioB5YNAEjO3Oj9wZfnkwZGZJ6MdR/Z8ukbsnDLXfm5h9X89ypQDdDxURv8tCI7Kc6WjyKVA+e6oOfNp6ib7fG+7cnCEpWNpLTfBuIytEybmizJaOyiA1KZ4rDSQNYkVcy/XcHJQfEU/Py3Le6a59ym31XPQlc8aneKeMfrQsvILUx8dSeB8Xvo6fc9TePBO+c/5z3yClI06JCBTZDcEYIVeoDaVjVax7TsHD+sP/AT+yi/iyFFAZTO6zrnCX/Pp0TeFq9kzuExw2QxMY8aoJSs7uaeDbndnh9RtO+p7vUzoenMhpC1J6lw0XPqZzN0Z36ipADVNS/M4oR/Wa1Y7N88V1VtEs7FXghiUZPfkd0YXesBqHbgOy/rqlY9dtFVe9eHsXGnDUuQkRlJrKCtdwRJ5sWWO+2HJ/FmSxUrnmXIuZZqZOwUkOfNuGR+txqQWPKMvEUpbd2O/Yxs1RExr+r/Zt6TzhdNsC2bKXcq+f9n30accSUVAkQhK9zJKt4Wwbd6i0iNgUxTzq6B1asiys2oMOnRwQ7YrYrhHdALWi3dYSWewpcAm+4rrD4Gr0fC1aWwl3mLPEqSt7nWpCuOhS/vhZzLI1U0gOgOSo0updzozSiaBiGzO1UNv5kee1Ef+RL6w0d28FlpAFc1LX6GYA/1CAVihF4aBBVB3UNbcmMCV8y/AJ/97/NjniZ3cJ6yTfSvxi/9c3YzNFZiNLbQ4UnM1cXF0PDxpWxhaOpB8T7VfA728m8758n4XntoudfWBCLT1mXp0dyLGJXIN9+wIxhpbvr7gXCgfcODQx/5/CKmjtYAm2pXUrqRVS5Tufq1VVr0KbR4FB83129uwmk2LCjj/Kc21UtZA/vxS7oICherGtmhz4c4SeV1euFxCIYBNfTk4A6V68mNXP7Q27QSXp1Yzsjm5IINl/x4NkqpFIs7gwmzqsL2KsxiAc2268+TavTrk2grCfjYQo7WAMug9J7vhvq7sqL1Z1ffRr7GUjJI/1+wrsfQ2aiqXUfrD9u4r8cutp5UHNY8iyl4bnWVlnRHHCwfbBYVN+H6/QLqhq3kQQcBcOpwUhCjkzbxjo8e/it89ISegO8o0KZaCYdBVAFbohaXctE7dUaT5XeDoxfjM/yR/8aeVpGwA7igTVKR0WM6LbpoLclcfNPtwln/MS2T6QmFpe1qqis3KFbhY0WXBs8M2LBno5X60j6Oa/2UeDtoABTPH1zJTwxbLA7WThmzQx5lYNuhrOG+YO5lMgUXtRkcdgINz/lTRuE/zc9zCWWdicQ6Ib+Ji5/UgOgjagJ8HNN/gEjuV8naABS4FALnJz7I44mSqq3jyXKguK3bLjJuUaX3VZdJAPXYTUou29P3dAFRSrSgX8Jgy65latXcqtTpzGRhVMS8mnwt81JaWEssCqcxebFgKPsG1D1WyCVhoW9eVIwrp6GRpXeqG3UZbd1ZIQrasi+7spcn2to/CRaWLm+9Ljc3D9+sOa+AnbazoYMsy/PTiEBCbjc6uU8IgqSj6MC23Bahxhc0LN63cHdrwffMYifx5In/xNXz3hOmr+oXjeIIsu7XmLTqhF4d24il4kShlcxm7eSeDgpddpX/ps/j4j6eNglDsYIONKMCUdN7uQjL3ISmNiZAIMX1OAmwcBejMMg0fzCaWLsVGNdzkmEFsC5GW/5QSJWMOozq/A9bv5iNZk0PzLoo52Wq+RGq1fuopqaprr1JQchLr4KHMV2PnlSmXgy9+SnkOS03MJrCTy2Tl3F5k46w6yCqrjfJbbqrP9OCjbb3Z4Agw7Nv6fmlOCBt/XFZ4UDWfrvOjkDmz1tiISTm1tE0VUBNUHwTuBu5lehDpQeIhxaPQB6jHVR+DPkl9SvVJ4ARxUvVp6IbYVSRgF9gFUsoNbirABEwb7BA7wA5ApnMUB4lDCYcEh4HzgfOJc4FLBecTFykuFl5MPSa4Erhk/rCgulhOqhDadLHS5KaKnfLLUJhNFRtAmLN5/ScdbX6LvH1IG6frg0L8a6Ednbh72Xqn+/S86++w60Q0yDwKAJs7rswq9Kl7RLKy1houMKKlK/8MKUYDCs2+oHz+9qmbRnW6WqPcD12tP2Pa+o5rr0tjmNXXSFUEiYokwAGdPs7Pnosj/0g/f6/eixJFohrhowFYoReaiKQQahKkDSZe/TL80M/gI38eqhRqUigoqkjgVJkKFTS4rCW2MaGiVOpcrClcRQCqqmr6iVOOiFfVQhK5pYBa2l/UdHlXhqt3KGV/84IBle2UM170e5ory8hyeciMpcUFqYFecyppTrOZe75Ll/c8ajcHK8zn6dDHGnJLsIMlPMKSnOcZzOkVMw+Vb82UafgPAiRznHwbM9lZb1NgrhRI7eXZ1lgzTCfd3kBrj+CeqKTVg0KN9KDm0ahlRY1SoXpS0u9r+lPVr0K/Br0Pm/tE70v6EDb3I50Aj8+YqHAVQJSNmTMtpJ4naxhYUiTgJExhVs0xU/EOEpr2tnK4Q8RB4tIJLxEeIy4jrqBetYPriSsUN5IHYbBJMvUriAQVWlPPcI5bWLD16rsnFfn4K48LpWWn9YLnnVV0v3u0d8T66WBwj7SZt9qc6tRBhmAJW3FLDNUFqGSC993o2pMHnXlakct0W4rCL6WCzFOT6nf7mIKyLbO6B2jbs3wow1wLb14daxw2Vd3GvvJPmitHniSm8toJkJRKle/hjx3E+f8An7uHd5bff4189wCs0AtMCkASEzcgr71Bf+Rnccf351qUbpSTaqLmDpgNlFLeF5PmN6pyzZ7RZ+kfUhKqCsnLCjUHF5SP7mkGCK22zcJYoNZpwC5FXcsPKueqkCwmljGHWC5tmjkMSZ211rwTzYwyuDirKotTpfNmmUJgpVbpE+yXd8gJ2NjRSYs+hXL6S5ZakuvZpffM5rIklp+lt58qzDTGkXkV4ImwzFvKclLl1Rlz1cDAM5SSP+9rAhK4eVDTlzR9CZuvIf0J0l26+VOkB9LuQ+7lgdT/qBcPUu3EXyUhXX4qeRpJ49d6+ao0N7P/OfqR48Bx1cd2B4eVSS/YwWWCK8irieuoLz+AmyfcRBzNp6T1t6N+JDjlco6xjZ0ztAJb7E2UUeCtends2e5smW/mjMmfjIwa/hr2tu5Rk9kytK8KfmnrljU/6JqldOAYyag1cBgHstbYbg9iFw7aRyFwsylNosR85hO6zNjOLWNHXflOJwXADZIgkTv10cn7+ZnDeuB/15+7G1/Z4KTmaMJArACs/5+9N4+35aqrxNf61rlvICTMkUEZZDYIhoDMoiKtyE8/7URr2y3+GrQRpR2gaUAD2EALKmq30A50t4itTaOioiAqgxAgCZnNSAZIArwQQvKSl+G9vHdrr/5jT9+9q859jyREklf1eYR7z61TVadO1a6113d911qWryYGC4rqq0c8Vi9+Lb/9ezEyPcVXVIpvj1RTlsI0Qp/+SU9CQa6MmMmkypfIFcVqaazbVEJvsZxEKgiB6Ly+42NbLeXjH35p18wFvgiGSEIjao2MLcZqOLnEv9CgaF5gwlhhn+RLfvkRWF5JbWdFnV3BZa4k5hVqzk/KUkwraHL8M4+INSBCajw/mdHw7EY0ZSegoDlIPqHZpr8bKHDzyjCehc3zOH4G44UaL8OBXQjXQTAMIdeP544ozKPDeghhzRUdcBAmaArIQrvO7Otxv7ZmF2HEnhHXAhdmtU+AYDhqAw8Y8MABDzc9dIVjDI8BvoZr/U23oqa2AF5Y75h6UBznSRo7BJ6sbFPVpbaxZZ/SV+57m8kpbo6fMwihs1lf/+1W6DOs++z1/q5R3GvQlYYtYJlmWMMeMzmDePRer7NRAf3rsRypbEtBWzHAEEbaIJj0bfyRbdjxDr32Yp4LJ6JdljvBc3lZ7nBIqn1+WGCw5BP6jcfZi14fnv5dSFUmsaAZeXtMxNpfIrFiBU3NQ6f+NcIM+Q4yADQhhDpIlV0wFOim2joXMv6Kbw+sD8fCx0SCKjh5dbEwlRDcOnkLTCnWoKARABhyFS/yUcE5NSivHzJdoiiHyOuEahoOxfJoPp74Q6wNhQSY3DlMH5YhTj7TFtJ7Q/pyot0AyhaQy3b1Lchn2x18IJ1Kpp7ekNcZ8xmQQFpArOrm7gFxNIwSiRBAy1uI7k6mkHmykEFPCDxwoQ6chvEc3nyBwqUYL8X+a2uW9OFew7CBR27owSs8aMAxG3j0Ck8c8AgApFVxTwLvYxZthzijZXT9Ba1tT83+SelyjRE3NgmcsZ5MgsmFyRSCKudgWYbiRkw14wWglErfJGfGuT/UWEB03leTVyba9nlvrfoQspZ4a/+qXpufQc9sovMsQJx+nHZH6rz1MYnE2ULYjtp4oa5RMX+DTfp1c3gMARyEU/C3b8drz+epefpkYDCR0MhC1oblGbgArGX5So7ujjNo5ujHPpk//To9+VkMo7iCRmBAwR8tfsoP8hmMJf8nAGMLifL6JoSsKGq23yCPumV1iKQ7Bglq8YdzyKSgkE0+g8MfblPlB1NCXXWPZZ0MZfJGIiSaB1jVpbPgpMAE0RI8as9qhlAVlrXYBUFFbZY+eD7C9sWMlgK7Ffx3VACWCSFWrgIFWaDi598kDaFAN4zQwDG/nQU7jqZBN1+mzVO5/xQeOJsHPjMeuAzjTd72PbE+sX8CK2HzsLvtmtpl/pkBgOmIDTxwAw8dcMwGvnk7nrDCA4ExAgbOOC+w6KznsFHplQsmw6TtfwqwZtoDE/ecABYyBqo0TNZImXy3nSZO6I2XevZEqAxT51w1zGGp2Y3P47MZgNUYVdiclotzWK0DWBmw9q5XVs3XGj6Mc9KrzkcDEyeILkvR5hIhu89eCgCD4VR98O06/myeSMFgIUoiuFBaC8BalttzoG8sMQ0Mdtwzw8/8Mp7wzAGIoc0MseBfhUWSA1tqYFP9a/D0VcElkUrJTeuZE4ru3Om9wdNOHgB5jOX2Xtip4OiuhHIKJpjgp+CQ0CzGip9ixDoSKzNhzrhyrBxSxE9FGOZxoQN5Gbd16KeBUAm+RBDmPjUqpxhmt5Bb8ELBgpIqiVW/IA8H4TTsYwBAmcZYt0wMho0EEEKABBuwCew7CftP5M1n4eZztHkRNm+ExnYod2DCycUPbwKrBVi18RWO3jhqGx62gWO24bHb8C078IRUmAcGhD6vKT16R2IY0gmOhvLsFO4eVbQYRdabxa+liKwthx3MvR1dq92cdfvMK1NEgta6HZgBfx5gOYpIE+fSHl116qvZVJxGLL/GAGJdmM9BLa/g/FTnFFqauPmne4ngSK1AE0boIYXTAAAgAElEQVSEAXYWTvhf+KUz8VGV4R1hgI3tlbYsC8Balq/IF+aiVqJxtuFJT+VL3oTjnqgwWBjDijwAygId4TGp5eXOL1f7i0orHQLGCq6vO6CHCHCIp4Ahj1dCBVj97lRsiRyY89AwlLqn2lIjGnppbEis+qeywVKdHF0RsNtjJrrWkVgV0uVjawCWUmtVgVMRRFZI6r+C5lSjVkIrD9dUCStAjDsKMdqsfL8ipE2jAQV7aRM3nYCbT8Te07HvbNx8EYNSt0Fzla2IEM27vM/nMotefz9Wes+PrBSIYQcetsIxO/CE7XjSDnxbnJJYNLoAiDHH2MBShpWnuLAFAMocknrEkMkqthRR45aexUmeYUJfdNPEnF1dLCDnKo+z0M1aZ1Tf+hc7B4a+bLfWVhRrioPr/Fo7bfsE64j9iU0GuF0HwDCHtefxmWMoOWHgfKXdQODAgI1NaBsYoHPxid/Dq87gRyGDBcbeoYXJWgDWstx+3xwpDcDm8ORvHX/29Xzc0xTNEjKrhLAprixiFVVdalvLS9eAr+h5EmuG93IYK9UHe4DlogyrCAlejNXSXV6v3FbZ5LsNG8SWMFaplLYUVFGDNTjM7a42L4YeY6WAwlxZa3AMkuW4U0216GdaJew4KlclrHyVl2o1AItNybUBWI1EzNI3K4kIYpCM2IyGoAew7wze+BHsOwX7ztBNFxfzKsbqYi1e1F99cwFqx+mUwjk8iasJkZxKQPEUrdBc9w3rsAMP345v2okn7sQzd+A4cJBACxYsY6ORGjDjhI5piW1aH/S0UB+Po662VemWroSHWnTTwdRXB6evptBwivwOyo119FXBPcNsAHbKbOoQWF8c7CRZDvD1uitMAn/QGopivsFwJtXbmjzUGDlAKzg9zXZ0AU79Hbz0DJygtDUb8/iyLAvAWpav6EAfWO7dJz+DP/tr+qYnJXOeJMQOkEW1sgdD8QHRAaY0mKiHOMgNX52Uu66fsufWkFiEV6O35E2roPLYTl4a5TRbjdgczaY8iQU06nKpUZ1njBWLfZ6jSkqscjxdBbMhsQoAqiRWL7SKGKvdVF8bneIn+BcxrTM65OrhWgANY54PbyaP0mH/58Y9/4C9H+O+M3TjpzDui9YasnLGQrIAi95Ozs2hzpRTKMy0q/OwvvtmWQwyKAWhlGvRaCGbcfRvNByxnY/cicfdBc/YqW/dhocwqaZSj+5ceJ9mn+LWmjlZSwLB6b6npE4rWqrb97rviHLmKo+Nr/oapkoThbg8+TRFV+7ADq5tHzDlrjRrVeWLgx291Cn9y162oK+6AGnO2zes1Xv5lVfgyBEYVkDAaDHLDLgAJ74VrzwDJ8gL/haEtQCsZflK1ySSKPIpzwo/+yY97puyX+doQjAigDAFWWAZ1NnxWMGJuON2Wy1UprtUI24rxlpHejkyhkSn9GoF7013XoOQMkkWrfjQF+OmGGuqnXcre77HbadZM+q6JggMjVqrbmGOxELBWJmu8xirZ7naXsgOZbpmzJ4G6wRtaVMERsGI/eDej+u6D9nej2Hv2WHfFa3lVFZURzjV+Bz5VXISQPl1hq9atCCT85DlMpGFcLC0nE9Db+NmTEzS0Tv5TTvxlCP17TvxjIGQNMTsaRlb+qorQvX1wbkuvHX0FbCFvP0gzYMdADqU4iDWqtFdkuBcLW+dbqzs17AeBjn6ymvzPQBq9VsHMRRFPg+zNKGr6qqDVpigq3haAjWIQ1J4jisM8cgCwsX65FvwylPxj3mis8Jh11yyAKxluV2/MEvP8Cd9K172Bhz7lGT5rdRNlgaAMZoXB8hUhOflYatWOdR6NxQ1d33+qgFY6F0eXK+fE4A3YXnBuQ90gneHnAolhjDt0cuAKf7/pjwf1hbd3DYBjjNq90S5xSLjtJ2wlXNljqr2BtZNdZ0BXomPpNmaUfQHHVTq3su/xvrgrN9O3NqBvbrhfbjuBLv+RNz4TyHsQ/ZFY+OqkB7tyVMsIS0DNh2VFZA8MHv59lyZ7PBEVHGOInXdAAwRYLVAykhJ6jBuC8tQ7PxX2LkDj7kLnnoEnnEknjPgLo5/mgVYE9ywXiTeRlavrQ+uB1jT7cjTV5H4GdZnEU5JuBLqjEMAWFOsVqDeodBXs1gQ05zE1qyr03hhjcnWBGxpUotsGOBiCRGIVR6eY1TikG68ECV65/Ckt+oVp+MjWHSQC8BaltueqWLXt2Vg4BO/Rb/wK3jCUwvnVGENG7ckb6GU9VjoAVaBUYHZv9uF900lXGOLsbzRVEYAHnhVHivkaMEsx+6csRrAVGCEpCwYr+hnrETXzME0NlQZ+QEqbNnYsmujkieWWqeGqV+XU2J54XxrpuDqgK3qq1ZmR0lZsKtNhBUEhJGkZLUQWaBhCJANAaMl+IXN3dr9Puz5IK4/ETd+ygUdGklpXO6hO9LtXtnkpAE4gsfsxJOO0rcfheeucDeAAyCMAwcJwDhED4iMqKL8PF5wA6x0/DIXem2u1DhbfJyqr7wvvGW1EFprid6Y6mDGV0hNhU1b35CZPXbe62uKg7OfAuuF7V7VDlSXCkzoq9roN0l9ntVdeaoMXWaiA1jTuGiPB8tJGKAxp0acw0/8tl5xJj7WOgxbto22hdZaANayfPn4igOSaLnE7K2ATT7+KXr5f+ETvwUwBTEmyRAVGaBR9iR4QTa8VPDQylffvKFokZbnslfnyNCt3Lg0tRtvmK0ZMVa1DO2MNMdOyOXU7pmv6muOjZEVvOtVt05BSI0Sa66dEGhrdsHJ4dfYMaQ6YNOQ6LCaCAWOTIK2UM0QXJsniFGbQwWFBPZfjd3v4+5/wPWf1PUXgYEOyiat+qLVuKPSY8FjrFgjPAKPuiuedHc++yh994B7xOtvgCF2/CZdfaREY5pPxB8JVZQ2hQ5gccu6W6+vWu8suo5esvUeB1+Wtn2++OgtpiZaqAjRmgTGZhczKAdOLtYBrIlgv2ImZUdZzCm9CroCepU9Ot95h64sw8d8KWhkOFufeAv+01k4sbLIjVPPcu8sAGtZvtzRliEH+Tlh+zc+Ea/4L3jKd8SCWhT9aHCSqQZjpW59gsrC82rD4y3Xy+QthNxbMzXHmtp7zmOsZs3KCU1WHluPLjn9OKr1aLO1vOskkyo8WZjBT03bYEdi+Y1khgmANjG/hc6yIRu7YyomC72OLZ1nr4VX9qGICGyM33Z0W7dc6BUOMI2eGm2UvvSX2v1+u/akcP25QJNn55/Ki9P6HRxd+WUVe0HjFXEEHnU3e/rdw784Ct8/YGCirCoAsjkqpVSdhnn6aoqu+t63goGK/HyCrnpGZ1bwPmfKNV+5c6amFffMbqrgnq7pctYFPn66zrG9Q1eOr9LUd3QdFbcOXXXqMb9BpPjVxueinM9kZkMMSl/0ifiH/45fPI+nQnQtKWG53ReAtSxf9pLUjAlhmYGBoz36MeE/vsme+ZwAWgIMToeOtnEPTn8dEsaiOTP0lFDoqKzQG5B6a4BDw1gTdkptrXBG8N6ZlLbKsCnGcmIsjcr0mG2ldq8KsK4pMsuwUM9JCs/xPYB1UyKZZFuFh2uN3WeqhLN+DWNIddJ4BONQYnNi8PYQCGAchd0f5NXvw7Uf1LVnM9SIYMZGUdYQZcCEEcr+C8tyZ0BaVuE/yTAYwhE85m749nvouXfDsy1jCAkDg8nSYzvmq7f2Vx3Amlo/WHojvkz6ChNx1ZZeoM5WdNLwOC+9Wldn7Fr5Wvl/j67Kp+vR1STbpyPJ0Oro/b4w13HZoaupTTxq9KgmJvWF+a9eaIFhBX5C7/+vePmluPAA9zO7CItLhM5X7zIsp+CrcwmZTB6ASHfgEcfoF16HZ3+vgIEMQ+J+yJqNmtgpF/saZ0MxoJ0DFdIfy5oNxC4ppS4jeRJ0376FdInILFugjz0lvV9lZcvSYKQ84qYjq6vlOGUmNVrRCbO1p3G7rmLSbJNdt8lyQvKUO7J6cBilDJVy8cul/lqofE73hWxAlo8kffh0fuopzStYQY2EZDCEvHFJN5yrXb/Pz7yOl70ZX/oY9l2VPOYRn6AxpEWpLpRKRXKXwTKnvcPhqpK+Xp62JSLUGIMlqUDdjC9er5N348/34EM387MbOHoD9xki6mYA6RAVjTDKNfF16qusLnfAKKmv8o7RaJ5ypl6BHZRfP/pIOeDCNehK5vfibrxeON8eJNlAqwJHrHds7+FOkcP7Q8WavcxaXnmCrclynjQEuHfJkWTNu9JnIb1av7weJXf5K5DJAD4QD78Xv+Y8nHkdrqmBRXTXzrIsAGtZDnXApUxMFPCDHoKfP57f/a9ogEUOZiQMgiyx9/nRygRZKloCJJpJoEFBSIR/XpNu2tTggPr28hayhTfy+0WFRMoqjyavPk6qK2aqPxQAU/fuhfYOG5FkxYE+DC2XGNkAqwY8eiDY/MmBt4yHoDgBjj/Ed7HOSFFXyK0F7CBUiRlrQFt2+QxxrwHB0tOTGsIB7fq/uPRN+MzrsetvcOOlCDdHGEhyyBwcowW05QNOu7XcZrkMt3fEYoLYXJMFphii4g/FDCLe/wf24rO78cFr+Vc34XTghp18xKBtlkka1ikBu74/n4jsUEVwMCXNNqw1Up+GHvqK2ySTZ4vioNs1e7SHtqdvKr1qyR7XCeg0+GyV8swNIE3VjwKxzqkLLUnmWa60ffqsQ06iBjV7kJi0GXbR0STIYGBgGMAD3BwwrABhfCi+4Z645/k8aw+uiVs3DdJCXy0Aa1m+vEV5YmK839fi517NH3i+gNxcki0NYylxhXmMVe5ZM6qyKgV90M3m6KBBQWZNblrllhxdQniZZsVYpUKBth28qL4SkuOEeFN+jZ6a6vknx6I7RNPgsASMUAETxfiRPQ/vGDWniWiKAO7zKUHElil0FqCNE7qaI0eGYsxQLoxp4CVx7cm47C361Ct56e/wurOx/3qXyCjCcm2zWD4Pg0I+r+ZaFUBsLCWDO+D9Hu9nT2JF4Y7l0YAt8Er85yZvvB5nXY2/upp/t4lLjEdu49fFa8ay0XC8mhsL0AkwGiYQamDjvT6HrvrKWtG22wRyef7JyNaVSv4Vc+PC/BE21bqmEEkPXzruqjVlKJHYHYZrjBvce81xco5z0hRdobfmorXEYe4/6D8UMrzL6joaYDYEbBJGmoEPxTfclXc9H6ffiD1gHNSGZU61AKxluSUYi/e6h/7DK/GjL0pkEwlYJDE4bGITHOjy7ZVhURxhnBmVhybMz/5pQa2rLxXSyCEN6wBTxzn5+mNT92tAUaKh5CEbHcuVgFYqMMihPW/90AW7FBst+TSRPEtmDiUsMRqeZCrr+0Jh2a/ruMyyfDbyssLQ+b6B5gi7FWipO0G44k+GC35Fn34Dd/099n5xgIIRMjaOz0Ip2CYnq5Ba7ylHYsUToWW8vWMuob33WMhL90PBW0UboPjOm7HrGpy4m++5UacI4xF4zEAEhCHZLDSgpwvsa0mmtFmbWn2yx0zrN0v/lu7tQ/sEsgmS8xvfQnrFiS3WTLyPu0s90OkYsg4CYs6sq+Pk0FZUS6WuEGZ0QrSKSudU7ZjgxcAMDSVL7qMCZRgeicdv58bZOHkf9g7A0jO8AKxlWV8biPOPAVSriSLsqCP1Uy/Hi15mQWCsBrqpmSyNTDGjmc5Ox8myvEbK8UmsMKsyMx1iScRYLo7VSMGqOk+TOxZgVwcqqaqs4HFe0kZlrq0iR3nupwQaotWB+c7mMromuj9XENUO5/EjBIdvojZKSnabTreSmok6YFSBoSOVCkb0EjHBkv82KL+/hMkIDLHz4MaL8Om3Def9R33qrdpzNvbvK85X/kSsJTtyIWnLFe64N0VB+xtMKm+vNLGO5vHfmb/Wmp+RzaTuMPMr//12P8x/CoKbvGEPzrsSf3Et/mE/vrgNR2/HvQUYQ0YYqRS4StRWMMf3EhanFBONVLWnWufbPi07Dq21QVVfTawNrPC6c9r2AesQiWeeenQ1OHRFsgVSc8ZdrfsDWkvVoifrQJhn0TGNOESvWYX3ekD3K9meOjdoxsE+DlR6DJ5E8Gx8cj9uBuO3pu52WJYFYC3wCkOMhAk2QCF17QgAdtxFL/hZvuw1cb6qoRpNwUvN6RTlif9hW5xyI1LBWFU8Hm/yWurri361zFerXSxow1f9Op5MaFBXw4p5jNVCMfosVDh5u/tEBd+oPZVAq7Fvn0QZaLY1O9V3KSRVRhx93S7Y7EtJul4070qUfvpzdHQNMRsyjomSQox1lair/xEXvBGfegMv+1PeeEX01GL1vlkWpCJSbjH11/cEbZirja6SfzqKNryK/Mght4zeiccTQavowHYTPv8lfOBa/P0NvHCF7Tv49UO+j2MRLUArRAfxGFZjmdkdLcoHpvZU6+krb9Rp4ExxcF7b3mrqHV3kN95V69BPoFp+a+44u85BjwU59zj07vAdIeddQ3splUNXmIZet35XNjHWwoS99Fd7deeiDHwcn7TP9p2rT25yE1BKik4VBmN7ayzLArAO00VQzFQHLdcIjCBe8GIc/0YnrToADvUB381b4QYMVQzmNEdl5uX87pJXJp2AyFFZxsrfZMkse424Sk0yIbnkQ+DQSRYesZmdTzVVrLitBZCpfuhAYc8w9bDM20Fpwq6pwVgNeGJluSyfaTrlWt5XFb+nOXj+JEpPIqeICwiGAZDhM3+Gc3+ZF/wqvnAC9l0T2cC0M1vQVXNFCyG2UrVAejXhb5xaMbM+ubkyWusqzyNCurPu3E8dBlSdn+3D1bvxyavwNzfwFDLcFY8eUpvduIIBXMVhhWMgE8KKdz7Z1ex6jdQcBsJE8N76MtCHOtN7R02ARaNtn3Oi8uiKExsqv/0OCNoEsni5Otu2wQm6UpkBzEmpDhVddUgRTVJkc4YzGiOAMSq0GO3N7Cn6ti/yi+fgFOT+Y9OQ1LYNrbYsC8A6fGfqCCjQCoZBCPjX/w7/+Tc5rJJXkmkYB1l9orcKdLTmBc0Ur4InzfFMbEmplh1rqixiD+bk+DQ1LHkXL09M+Ka62djciFZmRa+jd2Isp+qS6xZkZq5KWFDCrl10GLrZbwvUMmbqLRsaDsWBPnjznZj8WATnaa0Akjxwgy76fZz1i7jgv3H32dh/k5+oRsUGZcuA2F7PiVccUmZiNM+Ot8mqZftyY0TzYoXZ8UthVATfuWf2BFRpvIwmbRP7rsMFV+Cvr8VHNnnd3XDMCjtS9CRGwAwcSInMqLZtHlSXlNyV8OZ822vcYQ11dujHm5vXih41g6407/OJRtiODl357Q/OuXQWCA6zDFlCYHNWXhMPiG5cmxiZVnRV8GKeU6KN62mOAT6oMVruJB//zai1fAafc6UuP59nJuUq8yi/dBMvAGtZkjCbisNhIrH+v+/HL/+m3e1uCtCQiyLK2vB661bDggzVKn6ogMl7H3gXBqKDQfWRNLVXcGqDVjjFBpPV0VW56Kc6fVPrH5HBBYuxQqnnZIAzg+rg1PHrMJZvqXReVk2fodoGxrIvMYEkTQCWOzJ6NX0WqcVYkkxkUUE48Hmc+zs661W44H9wz2UIB9phd6Ciiwa8Vn1ZIj5QjtAEOCC6rBoZ64Ydxur82Sy75RbrClMqo9ypXRkzo5rOnmUSNz53bbwRl1+h9+3GBw/g6p320O06gtEUngyKEAfI1cCWlSHWm3/O9ST2zlW+ODjtWGypst5Ay9p5kjn4Mg3Y8Uioy7RmtbzKn4g1Y2ca52w9qquGopg0CmDOJr7jrvxnt5ap4iS4Gg4dxtc3EkUJIhA2gIHhODxjFy+9WOe7CoDvgViWBWAdzhP1ZCKYMlP1rd9hr/01PfDBiI6im0rIyVAxQ0YkRENEcXKnssFbUentVAdOa95AsYxMXJGt92IownP6wmLFQA4U+cma/MzOeVZ5KFOqgQ48OXym+nGcnMvJyIhIHKWgIVesrGRfycD19VZl087Mn5mDaLNkmOMCTRSNKSGS2HMJzvttnPpqXvInuOFz1aG70Wtn5oxL31+HE1SmH/GbzBem8lftMVY0X41z/Q0hgMnYgl1TXoua75ywNDWWOpvS9LgdIMaOgRv5+avw4av19zfjiu180E7ek5KlW3mN91UbRzNMBpsuiLAgpPpKg6UmvX6ULztuQSx5WbpHV81I4xzbOWd5VdAVXOlz0sCothcyOzI43ZV5MDQjpUf5NuLHsQl6m+7aRw91MzzGBgVioAWO8SI/gnd9pB5zOS6+DJckPUU0ndWiwVoA1rLkQVAgH/9EvOpX9bhjU88bhBVt9N6VoSOiGhMF1oCI5iYvhqIelLDyUnK4w6mNph0yaIkx7/XgxeOqoKRtm2kMQOlstwxNd47QdAh636lZUXxVo1eMyMY92Tk9FLV7t1k/kHo8WT6FQKM3AHObHxULnQL3nK+z38zTXodL/txuulLpw6j/yq1Mx9tBdFkAcIhPiIwUyuWVbOuZMJbNtVKWZ8yTgPvAvo56FPBtsIHadeeuwqrc/upzYLJAzeIdJPAmXvkFfPRL+NA+XraDX7cN90T2EEef4syaDIgKSthsv2iVZP52UqXXu75Cawy0evy0Bl01N4r5Pj7XOdhZPLSzw/R61zbIXgE2tbzKn0Iz/BYn4M/7bGGanzjxg+DEk2WaMB0DarPZfezEtAG4O+71ADz40zjvSuxKdjTUgq6+OgjlZflnnXOmPilSX/8w++VfD9/1vZAstpbF6DoDN5EjVPIXl2P+ysSvRgpKVWo5JrAVjRWa7D/ULdTUZ59SXFbxIYDIJHWNLgZChnQ5QJqCgqJxT87+E8DYYFcj+VBi+3KSYFA5gOmuY97fVjnQ8YEc6nZqvnKk8MsKPgQ6JzYCYGCJNWwCCn2uc5cAHYgRoLD7XHzqj3jxe3DtRcKYUrqZ6CnTIIzV1L3EtdYcoSVD0FOrK2oTMc8cFmt+g5484lQw5ErfKt0bcNYblRD8XeInlWRIZPiQ+Kx85sOddzxBHlIg5SjDCOMZCgPoNJNGhHvgEQ/E9z8Iz78HH0U1IYN9do18IvKM38FUfTWwsN01JRBNqHM2kSdt4sDpOv4a/18rGTsTORTbTGtH5TFydVN2gWuSDX1g8xZRg+0ZaMAlOpTWPncndcmpmq35aIEY8kdQnGbIVoAQPoC//DW84tO8iFpGkoXBOgyhbBSQWM67oJsN3ee++E+vwfc9zys1S3mKnSsTssFVdHm2Tkrl5kLGzsmTsVtx6iaKWsXzRbmZT6GiG8rDgfe1cj7xvmkxeV455qm4PyRpk0+tbmIKy0ECfa5gDhhRlbd7VhB1qsraZ4bGzZqVJCsarEy1JeCYZe8xANBIhuivEUqX4p5P48w34dT/jIv/hnuvEuMQHVp2YUq0aJbMPOxmePQPw1LXiPZXr/E14MDfBXa5i3WsipOk1rI45xgA8X7A97CYweEhwNuJ6+7sHJbmrqgt+AwBthdfuhIfuxIfvhmXH4EHb8e98lcTLDNYKUIzY6ANIArkh8qM1X43Hy9jc44Pjr4KTpu1VXGwk1516IrEkNNpGn1YBIVOtO6BY8OEVXRIRxrVgiZ6bZa4hrvyxP3QOrWCjS7BWnQVi4ydD34LJVX0pLHsuUqQ2h6KR+/kzjNx0j7sU2NTnOZvsXS+MFu327JaTsHtNu4xJrXSGEYAxIZ0IN4Edtcj9OKX6keer8GgTYRVg6dKuqAlQijyLum5k0CDkjuT67yT73ojpYRvVFimMjk0VR6L6uJoGtW7nJ0pnH1BJKbzSKc6Tfap70gYC/FgMv1mQIiDjuJ24q9gYqf8Dv05LZtq9poECBXSyE8lLaVnlzNT4gfRmsObQlhBqdFgU+MKgIYRGlS6pije+AX90+/Zp98drjxvQAAswCBZJL+WK/9gcCA+7ITARLcoGe7DGEnF9OUGZTojMo0qer7KcIIK8aobJeJMMF4eY7QIEO+tcNmay+lwJtHTd7Gb5+3ReZfhfQ/n9z0EP3mkHpCbWxGAgYESYCEOHUrmIkopmS0bRKdSahsS0Sm+G5nB2nm/NZV7P/yUMQxT7qcQbC2m19C70javt95Urve4saqSIyo43Qfn5PmRxe927dHV9K+N6lOyaoZVtz+aWQggnqcXXo1rfgevu4E3ILqbcZMghYBgGgLHRfC5MFh3wpm6km9viM/+kuMMGH7yZ/SqXzbm/PUSJeoxVtvuV8Ne4OSUnccVu3vY5Q0aSfrIF3ohu+jaDDX1hIDrYSx0lbOB6CyI4VoF0VBlTf4xU+6aKjHWeEw0H8PtpQ68k47Coq4S0KYJ+fzB2rHohPUwMpAhwVhERysMACwAhnDgep7xmzrxeFzwTt101SCNHFLNMoHVZbJ40Id7DaAko62+wA1kobrxNYKAMauA3gZ8AbVXwrJL2iCE6NYqrJS4w0cD/zbN3dMz6TTydC3iX89qIMTBiRA5BGovvvh5fPQqnHAA19wLxwIbxiiapmCRXzGMhMUZX2n6c5GF3k6THU/jjUDZ8DRkax8KZz26dSSOb/EzcKJqSoGA3jgUE/FT0/nojR5aUwbU5JwGXXnZ2fQD+sppt+tZ/T4ady7Bnz111UatJNJiE8MT+bRrec0ZOtHFSkTdYpzpLnYwC8C6U45lTIU1NmE2hh/5YbzxrWYKZpQwGEZ3h0neVN3Ky919N6tDr3CkdYciXb6fJmU355jAHkhVMFRddhq3rd4eolOLp2eqswxNr9eQv+r1lcerGtvczTvpTgfJ7lR0lBTowxbZ5SOWnsI4Uiv3XMaBVdlSXkLkWM7+XXz05Tjr7bj+CnJACrQnEAZEoXBfIlyWdSBrAGWIMj0YhkpciXitCOMgjYARv09eUanIYmoVM6SiwIhhEMT7E28hHshmkvM31OnLA6anYZpG2/T/N/HKz/IfrsJHiM2j8XhLmQSgRRFiTC8YjWbOxAFTa4aJHN4mjg8Fog2YOmywMg0AACAASURBVEI1RlAFc0zQVXqxWF75tkSvDyvVyVbVPm8o2tlrwXUCYi6KEa3ffadbt0nrpU8htImEy9w4RmdtimrxUPguk0IcggfwyfiWz9mlF+oc+G5SRieyRfx++y1LifB2ZLCS02GCTdJgUHj2d9gr3xC2DVHWpIEcpRUZaqjgFuUMpgqI6jiWFNpVXS7ncTDnRlo9g5I9lKIrlzKtU0SisSSX5cVZu513mU3cSQa0+YPFhspVdaIIIrgCX+Wt8mpMQaZEVG0yeUnmomghoHIBtO3Bj0PUqBz7IbkRSfXAKjhMe0k1K2Fl2IRSk2MsF8rO+/Nw/jtw0fsh5fn/mHcZYBgDoNGVKJdl/SIMwIh3Uj+UStcBo5FBUdYTFKIXPmzFIPEUlymeaoWQuUQjQRhTHjZyJHb6dmpBelnctEOSqmAg0lIjMNJwhU76HE663P7q4eH5D+O/ioUmqxGbHoe4KY8adNVEI3vPlFys6galDsEMM1afjfQKzlO01O7XDZhdDW6aTg1XbfT9id0H8Qw7Stf0BPOVafKwxgzCM2r+DLTlyJkHShtKGgiuwJEIwk7s+I/hdVfjmo/qb1fY2MSBYkHv1RDLsjBYdx6ExdICCDNKDHrssXb8G8M3PnYgQggws7E6azZuCIU88nKibOZUnKg6/4XpHemtNpt1VOXorkDoypRT0/YpNdU4arngQLI1eW+ZOZXwmZpdMzFQJxtf1Ij76I+nmsAnuASXlqFyEJ11+1w7T5pbRgtYMPUQIARe/o844dU4/c284pwkuU5nPbYtDD2kWpp0D+UZTwEnAC+1XKatT6zcU2B0HbQ1CTqjq5aoBKOCT1GN7SxLKPw1cMZi6NrOsYODWzIYMApmSC2/IHfroivwgS/hnJ2455H2YEuWJIwK69hD2zmLWuZyfMkvlbfa6JvujR2xNOe37jit1rG9upI2/gj9FiYYrmGPBk8a9UmFlUzycC3HY/XR1M4BcOK54BCStYz8MHFy76wopn2IGzAxJHcTO2AY7oq7P5hffw5Pu5K7shhj4a4WgHWnXYzVnoqC8IAH2vHHh2c/FxYkw4oFCVhphm4mSh6r5VD3JhYQbVJglBSpQCX2E03v2CmfM+MynpMvFFOqYTdxytUFurqeC6uRl0+V14qzVLWDRyeKQnGjcjKqWF+l7/ir9mBobO3Jdo/V+8sn8Dh5FtwK+Smf5scjgtnVlw6feEP4+K/gso9h/4FouG8YEoGYPeBdZUBzqXnLsm76cQOwV/ZsSJZaNotJmUkSRkv6Nk8J1+/Y6NlITkmRXEB+OfGlBfi6JbCOT3FO4S1b6m12APuutnN26UN79bm74eF3wT0MisGbBmtAUja+msmKrq8EW8tOzaMrH4kzye3pHdvL1z+LrjqUM8ypu6yOd82OymYbpVfWRXUwEXn7nReDK0o2Wln2j+RqbUqh9XqVP54YZTvEKqCGkRjA++Fr74Z7ncVP3oDr86Bp5DLxWwDWnZDAKu19K3DkXY+yX3hl+PEXxNnWEGt6CZnEUBw0nFB176x3ageXSEdlwVugdyorv2U5bkgoaMytM8O4s+pAC6Ax/6bpwTcYy7tClJFEbpRTj9vy2h08crr26PnjXqmMX3ZYLB/M+4v6X4sxvZe9i8NJv6WP/lI4993Ye12Z6JeA54mS3SfiLaWoQ7k1os/4P1E/ATsCmmnLh1FqrvnuGeGTMI1Z15fidrI6yy6AXj9AmsTsHM5nP51pucSD1HpgLuohVtRtH667Aift4sdG3Hgf++ZtGhpH0Hy7D+zxhKUA4uRimlmuxszTox8PpOLY0kiv3FTGq+ybf23IzNT3YdpOWPktJyR3jJG8DwUmin7M8U/W4qdMnnmhPf0snBN05Ydfa51dC0UXskWykausRTTwkThmE+F0nHiA+5MhH7E4vC8A644/bCWyxega1VK/M8EX/Qe86vgWp2RWqoCPHq+wAUwsNLhmWK5qLtVOxOKOzIEJtKPjhBGDx2Hqkr1q86Bac6o4ilBtEU65EGhRv8wKrRKsYQN9StBqNW2PBQYWzihnAiYDihjtl3BVYeboCHbLk2kA0QuUsCj1Ka0AysWpC/4Mf/8KnPjb2HNFdgLq55frvayWIewQn+/RauQA7cqN8C9HKLXTMqheURY9rPPbQmOjP/kCvDiYqQpJ4MXEBQFa0NXcOZteupp5nQDsRuy6FH9/Fc7cwPZ78dHJljMa+BKJR8nDwMgYcRgAGxAIKtuK5sKcfBTPmuKgMBG2ExNbefoJWR5/2ZYpHdYZPCzL45G1EguvJadz/PKq9jJMr1pV+5BtBwHPP3WC93qcyCGpBZW2DYMBbWKXz7EuTJiQY8YIAo/HU/Zg96n4eJ4KWm2+YT2ypWy+AKw7zOJsdkUkyXhGFsIP/iDf9BZsX2HixdLqmGpDH9Hw0b61sNb3+9lPo01pvfP6lJuK4VDM9YqWy6cSToRLbZ8gnczUhy634KvhuojaVFiTAbuKYR6ImizoNuowVRsDeohWlDvJY4s1bIUGjkimoHDFR+GqC/CB4/Gx3+KuM1Sp/gU03fYIi+mhRumsgOcB99GkbuLVzASsKpV72sP/s3w9myC8kvifYIDZ4oJ1K9EYk2nWhZ/jR67Xp4/iQ++C+xiSBynA7FkVBnDAGEXesZCnxpmddO7tU9U5OrMGJ2wvLM60jAgPnmJfg2boMR/pg9b6oQVYwoQVs+rmkIuD9NxVE4Y9UZT6T9ek8XCCn9x7A9pzgrk4HXiTHCFYGGBPxtM+bRddiHMJY0u5W0Kq4hINvQCsO8QDw1wMTOGSsns78PSn2ut+XV93f4YkxsxYpJ3Vm79dSiHPgSTvKOrQDNl4skfwkBtcWpjlABPZ6b0bHJQnaQ3bxT47sJRi5OwQ3LDi8xGdJ3tbUGxtGmp5ss1QLBZaWS81MabQpAIIGjBqSI7xJDRoFFZV+R4HmRBw4n/FPxy/Ov994eYbAIADo+3lcnl/pVisSIEEw8niT6SO0yjvKWL29Kgsc/oQoVP212/ILQDApoZrgL+F3iG+GPbXEJcmqls/dVSaKQ6C9vPGL+L0z/KEgH33xzcPsds3id/DkOMmSDOEIdJa1Kxpu80l1RTHdu9QUPJ2uiJj8RQ1b4U1SRtEtXvorB/UmTLYxO+qgUptinOL2zo7+y7wpy8azptdNbE8gZXPbxDkZIoub5S1AihuYOOhevSFdu7ndKnrmTJAolICAhZt1leIoV+W23ZGXtO+LM/SYh9/4IMfqjf/Bp7zvQij2RDgsFiYS6VRT9t77Obi/FDD++qf8n0UHALzq6kqnGq2IMrBMGbFJIQDtgGCeY8htzmWrUkIaZpa1o+ZfSQ1ZnAY8wFDVRhILj0QJUCQEBik6EEVWFcugYwqhxqb89uYQh8dqBhTiAEao0V7gDRCAyhsEhd9wE76vfBPf0GFlAVUYu9qwt2y3IZPbDIwue+mykly5oiu0zk2kgYEBuiJwBnpoRnXLG+ZWVbAZpmMMM1ohoADy4m/xQ+MqniMd2ka6fgo/MDj8RMPtu8YgpEIwkbKMAqSxR+MsGBsxOlTYsm5YWV4ZM4b3YOhVtveWGphTZYzOr2X60Ps0FUhnDjnJtoeT1Mx9Gp99FZYPQvVAaw5U9MwVXHNdCx5Wqsi0TDCDJsrrN6P976GP3upLqmzWyRDmmSEs/Y+WpaFwfoqGYCqCsAyFxsCRQh3Owove4X96x+TCLOskRZrNzO3wr5seaC+ebAVPLVRgz7U0EcWsjVdb3zV0/9ZqVRmpWvTJwivvUKl6ROp1BqQxjGpRgfC6ehL6a8JXmQv6zdklQKKzgxeZ0XvYqpq754MtKKngoJGcGVhTK7UEG68ZvjA6/WhN+jTJyf3LTMqWFXaL8ror8D9kozWUq8AGet35uYWr/GUAfl24PO1baFJLJlaVEetz4oIWXpCYVxO+61bVmAAZFgpnlhIxJdw3ufx0QP64tE4dsD2VQo1EmkbiPyvGbRKJBYnfqSV/il/9RYMDvfMoKtCjNmkLbHDOnOm8LOkEVpMM3ETbR0TCj6zdgu3AF2hTSHsioBz6CoffPsRhuQ7KmIQ8HA8YsR4Gk7cz5uT1WGKWTRQVomsZVkA1lczie7UQ0OempMDf+Lf4/jXMsMDRsdMV/uryccOGbUNaq3SPIOzMswU+4ZuQlPrbmhjbyoAwqQM13pc+Te6DB+6xOdeBVDBE30Fs2bZEI2Jluvm861kfVNhA6fg83aqGMsZX+VqQyr0rYARgg0QREtfwpn/F3/zi/rkO3jTdaz9Z3HTOSAaYREpfGXul5A5B2OMuUvNCAlgsdTdAeBt4K6UPkxl1GvrbH4ExAdH5TCbPOlluSVzyJRmxVA8pwgJthd7PoNPXGGnbte2o/EYkisyaIwBO0m1jRCVQHO+DDN9hZ19OSv15RROuTiYtd1fFrpS5zLlA2omXg/imuOJ6w+1LbOnlzp0NbgI5w7JsQ2fRl8W5JTz87or/2gPFnsMA0ljOJZP3I3dp+JEUIYhhmCn50M75i7LbcX4Lsttf0JFmAZhzGZQK/zL7+Hb/lBH3hUgLGDTQAwaRw5l5E/3qNpyYZzgTzrVulpheqPaX5EzoOHqbgBCKMa+lUiIbw8Z5tRKHBKskfKLsfKY63dK1b36zAr1iKO5e34RNWo6sFk55PKiUKqTKX+6HoMQklV34jmCyrHF6mGFkgGubpirjQFEkIwBCiM18NrLwgd/0057p667UrSceFOkP5nwkEW/6+UKv81vmEEYrVy6ZkBAiCHNBITHZ6AfsdQFwA09ZywdBArAZSkIi5n7rR3fEhIIpnR/x68xfp/QeCTu9zj80FPwsqPwtRvgJkdiGBSixbKJVmp/brNt4k1EVz1oYLaDb8FZfXG6qbLa0FA+ANIgaC1x1RhNuR2hdWRok3DgnRem3NVwMNFVuWKphs+b5cbc19FblWJiQzoibIcR2ARI3KQ9L8H//168GwYLgzCqpnMsy8JgffVPyZl7YdOke4XHH4M3/Boe8pBUKws0SGR0bmpmWNPgCLZBy6pdND50jy6vEG2vn/OQ982AbGqSmZOqPnSlbca7qaOJLCy2ptUXvfO7QnWiZ+aligzfmckQIeIvc8/G0lRYCDo6wTvy+kLvneeqnLlRP1UTtSkMZJA40MKp/xvv+SWc/ufad70rBVoKdeZIFY5Ey1zkK3S7yEvbGFH8KgJwwYAvkLugK4ld4OeJ/TPPfE7542m50ICBiYZcgPKtZOgtMcVlNJFKiwqIm3HT5TzpCpy6wZ33xmN2wgLCAFJckbPcVWenbq3FVBdBOEVXnmcaJvyBOeG8o3xckHMPTabclXMvblXtcSBdwy3Ne7XbHLqCS2nEHHc1+S5UK5vwGLT+2wBg3AQMGgwQd2L7g/GQs3DGlbpCTPVBLnfEArDuKFO8EvmU7oj73Bu/9Dp853d6YKQ+7bPm/QGgkcYuaNnhrc6LrnQawjuQEjBDyR+k6+jNQjE1qTQxF6bBZ6oNe2zgWxOr7HDY1FOeyiNG6Q+eGpBWfMbWBIvVg90XCvN5tLIVuRUKdRe9ZUxpj4qxwqCIay/Xe1+DD7wZu84vVcb2nC5mobfPMjuyh1w5lis/ByaZSEVRUUHibIrEcpG5x2SmJMc2Nw9LZ/ot/b4OGroiALtx+WX4yE34wn1wzA4eGQekISocozUxsUp9MlghVg/pmSrUNMMqNq9gyDUkFm+qvt85oxk6adfgxmDv1GAtrJlBV6q6qKmqvcVPKJVB69y2nMeEZd2U5QMfJtjLO6b2T49Jfs7EgFcQNtI+0/z1vnjAEdxxCk68EXtZ7rXlVlgA1h1onpfuscHwc7/Al7ykEUvB+xxUV14C7Wykji8k5jppvUUW28i/9uHV+SnAwzIXL+Pe26i0VBJq6joOJKnx1mqIpGloiVvZQ7Q2grA8HIvvZ6sQU86YJouFVtnWajSZCFgAbAVihCBjbpM57c9Xf/lqnfx/uP+m5Ur9Z56PrIE4DhhlJ35mBRWjofYg3B94Ivks8TjiCcCxMACb5E3JT7/ebkIJquLGemC3LLfxcgA3XY6TrsAZO3jX++EYAJuUFThFBGgFGkaDjZQVq/fIVDlJEzvbJwqtbil+rV2mcpcKwNZDaxKGswZdoYYbduW/YdKa5LVTQ+utMLSUG+fDpHtma+j8bvKN4/1ObfJw6KIeu78eg8ddj5tO4ofFyVNlWW5LvmVZbmtoBQRYrkX86I/o7f+7qYHMCKrks+xYlSKQ0GihQCgkGz9NttC0TyXYYk6q5dRdM/YNs5othjnvhqTHYv1TPoDGASxU5XEjCwuN5UT9kxdjuY+QPBeqSIt1tUYHBoS68dQ2mGRbgXH9m27ge18fTnwXdl+6PGS/SgDWvIKKqXaoYjwtACtSpvuNeDbwUtgxCCEyk6XnQgrgx6l3AH8m7PG3RDEUCIvfxu06HoIId8MDjsOPPouvuovuFgAahpD+HCdNK0jiNLjQh8O0lFLKazVha3TVSa+i7qoDNx5d9bqr3tW9b3hcs6Y6Un+qrOo6/tAiPMw5ZpV5wuSN88/1qRbNfTHhRfjxd+GP8j24yLAWBusOgFiTFgEUnvpUvulNuOfRWMGX8OaMPR3Rw+KIUNVD/omUnz+YMF2scKe4KDCRAI4xcM7Ymnt7zyQVzsq9rrm3CI0bql9TlbZrXdonBqTV4iFuM6QSEN1/ywEXU4aSpWOxb4wKYv6riTLy3A/qT1+mj76de691QUPL8lUHueI3n20b6rOLlPTvDO+S/Wvo3hSMhsJeMlAcYMIDwedu4AUBu8HTMsw3ppbQxWj09vw20wB2M67/DE+6EmcehfsejYdQ3CACNpGd9Rs+ZmKXwDl0hT7LeS26Qm0t7OOTMeG0On3YOnRVCDbOiMkw51zVo6vJXyt3ZW2YASZC+IOhq6TQ54wdVzxEGeyheMS5+KfP4/IkoVj4lgVg3QE4wXj13/e+eM1rh295JlbBRTJ0QQdNdY8TB3WHqVopFnszlRrwDKDVbalFbI3gyOk+6W95NYnKXglVHldxnJL6w/WSBZfBK8J5n1a5O5tDIrrknfp5aq4znGs8J6YMic/oHL/sb9+k97x+uOjkWiLVapi4uy7LV81tZMHKFbOiBeoo4u9kL5HuAgTQIqhqZw8MHONTcsRdgO8xPEZ8Pzgy61G4yE1u5+HQNkBFi/ercNGl+MSI/Q/BU8cYeUStQFJDYuanqnN1pgycmDJ0T7Jim+5ppEZZ32IUV3Zkb+7AproH112I+fJcsWfr6avGOqd19sIkxdwaLZkDfHMNj+066fCG+d6fOlsJwv149E7sOAkfv4n7llnHArDuGDO2ZOHz8y/lS346QBYGeYrW+qmCDwBsXa8mNifsXbLY/Hn27ULvetWNf/S2Vb0khkzunp3RVNZGNS6jUhkW6D9RhUyE1+6zqttz9rNTZalxgWEnRNN8vnUKTsk9iINRuy7AO1+mv3sr91wdYmJOtFmyUUuq/O3OTk1/JWlmvlZoVnytNjhAHIdwZOB7wW9hqu9kYaBqkBSamELFH4RvAJ5G/KlskzBGx6TlO789v3GNyYeMguEm7bmIH7kal9wPjzwC994AxbCSlYbhWQzRtNcRbEwZNExMCugClaeWV9PC3CwHVkjuvtDm6pIHdRMtOTxwknZ/DLN+V9YeiWXufx26omtzno3Q4WS1beSo8Bg89gbc+DF8ePGHWwDWHWIxAPbDP4zf+i0NBEyu1c51+irDiWrXKeeZWaKhgZK2h/wsact83cSti3xuw5a9yYIDK+7AUtnOCm3UYDSWhsToaqrSIUivsneYqzitR3NBt0fnTd8L5+kM2tkYx9NtXcDEx4EQLab0EAad9Mf2ruNx+nsxbiLLxFbKepylRvgVxlKugyL9Ol2QJYZmVpCWIcKhQIiE9CHgaQ0CpwFjVsGXRy+lkJ6A2syOGw8CvoF4pxTDq4Yh5uWR056SZbmtHzAmqkyIKAhB2PycnfkFnbaNR34tjyEsan8GE13k5zCRXnlP0ZLl7JXv5h5svq/w0NEVJ+gKre3nlOXyCjBzOgg4M3qgSUwtuK0UB70m3bbUXVmLrtj7wvd1UrbcG1LkdrxrFMBn4Fsvxvnn4dwFXS0A66t/EZ5wnN74Rj7gaxAs3Rbmb5a5zMG2ebxuyxXPWR9UopGY/K2Z8mQAZm0IsjeC722rGpKJbIGZmtAvl0nmwJkcfCyoUJ0eoa2H0hkwsKCnNhaowVhTUlwJESb+TElQETb57tfiL35du84rsTwAjBjT96FlxvaVwFUeWnlc5dcxs+4tfYYTDdTKNkwW+O9ML3axR/F7VCYgWdiDGNZttCzajdeVEY/EcOVKZ4JBBLWN1OxxLt/gbT0aqjgDMga3pGRTXI3PXY6T9uK6h+GpKwwGmKxlfSbSq0nusi/8NXWCvjLYo6vi73dQdDVMuCv0wKVn0bZAVxTWeW4VO4Yt0NUwD62EiZvDhMFq3MLiAB2oDVDAg/Dws3jqlbhiuWIXgPVV8yzJBjzNFX+Pu+PVr8Z3fVdqkEr5804sgOLOwBquV8XvrDRSGfGddCBJun2RMdpNcY5KM2J0OYBl/2re2zDRQQUtcdIb00QmFn8EJYNQFu/seOxyqrKuulecUbvaXzO60K9WV49wLDROf5bl8gyERnz2fP7RS/G+t3Lf9VAfu+NcfBZ0ddvQVBwsmrfJ1UE68DTLGHV/LT8YgyAGYriPxg+rfuWw1KCf+8/4V4bziItk50KP7ghXt6cngv9HvMEgcMztJoxeH9Mj7CDgFh9/WQ6Osvr/pmUv91zIj3yJF90XjzyS94ktDWZjNPQnxiFxUsEyP97ACIISe6W5hkll0CYhfY1TAznM5QwOjaGo49LodehVTu6nt11W9JxhVWPHMEwmsCWWp3vjFH4VxVWP8Jpf6YXzluIgKWAAvgb33Y5tJ/Aj+3lzZoVdQuhyCd/SxZZTcIvhFRgEA8FI6wKC6QUvwPOfn26PYKSCAI2qXj5oeOeU6pVjaAqCsRjYVelm0Um6G5NQZVX33GFacRR1Si3/tZt/5tUsw5ltsr3VLE/6jW4iFlv5SIMs7YitL33b1hhdFyRPtDknwaYSyu6vsKCBBBlErCiJJp38brzt3+uj71SaQM9oz5blFgCFBH3MOiBCEkFMGhvOwK/1S3cA/oeBRkrhR3O6puWph0gOeN221WMHbjP7fg4/wo0fJH/QbLUxHIPhjenqlSuMh6PF51IBWJEbs/hvekJi1RJOKNYAhyU66VYMnwkpyz6pd/8x/v3peHcaIMNg9c4dx9xLmkBGnW1mC+GWs1nnvb4uxblsUxN01WHECuzUi65Ma5Fl48VW9x7QJjCuQaXNkW+xDia01qSXoxkJKQsM0dd1xLgJ/Rs8/8f0AklkAEO2kQsLSLi11/my3OITF00QgowIAPTdz+EfvUP3vKfrzgsGUygAwhlQuXG62bKSDVXBUs6kqr/lsrOUm7Sr2WSyEOq8rFD/quqDVWptqGbo+WB8SiCdR5fY5f2hfctk/WqFlVaeXbPxUZcQBJg/BhPCGA9gRBA0EFIw/Pkb7P2/py9+Nn/OFbkZFq7qNoJcW0CxoqZq3DxS8h8POgv2Yqz4xVMWaGE8A3wUowg69gHi6tXwtDBcitGMB8osYSQ2NhmIAAU8TONHiHun2YVi5voFq9U3RXfauJRDLT+vw0zKb1v31+Wa+XIvMEHEII7xZr8Xvvbb8ZPP4fFUMJiADWBkGGSlbXAaBT0pHdYXC1F0iKp2TMy3MOWumt3NSNpbZKapt4LFa7uWOIkWn5Uj6aDVMPMAmjGRn3AnctP5ukogDCM1xE+6CawYduu6n8CPvR9/kyfQAbm2uvRb37JlKRHeOpAl1MnPgx7I170B3/gYYNNIQSZTHJQtScOd0RM6zwX3A3v9OtEXAc0ZZQEVi80QWeIW4SCsExu2hcCc2KZq31C0UFnIpc4Xnlm3L//MnDHLc1SVax6EK1/2si3vKEEQGoEh+kaaaCCGL34Of/gyvPetum53G9OoRct+i9HVOsJJTKXA5pHp6tql9GYWU58qA8TmZ5E2qSQOJI024mHQa0s5DwC4e2N4pviZAWYMpElGGhlC9BC1IAYO11n4y8DnAUe4C+towztpV1vtppinr9aVONHqyfwPsycKSxlxq8tsiA2GcdnLPRfj5N34zNfZsTt11AqBsIEeUdHhEkwKag26Gpx4vLCq02ZAZ9AAz111dTqvbbL16MoatkesfuvsEgzXdfz58Oau03y2gNghMzbpQA3Y6lh8S1a+I2BCWIEEdmDH3Xn3E3HiHl4HKj1kUhT0MoYuAOv2ZrCszow2NvifXqHn/ygAYkMQlaKfit2lk3Sjv+C7UhwxcSCY+cVHjXaba722Jo51avoCi21Wk+LVBt1McBnqx6ldjXIOVmV7ao6v24MXWuXGPjYS9ypBqyAPNJMhMFhkp+ycj/IPfiGc8BfcHIFQJfcLtjo0RDWLorbisdjaT7UVQ19GjI8tj71aZIM5gJWaOII9lvqxor4Cw2A/b8NHBgi2CWGMlcRhcwhDsPgo5SpKtIZrqWuCvoeoej2z92C4DAgs92aWW83qw+oDyZpnVvl1XemzcFoLwFq/qE4WE7M/fgZnXqHzj8b978Ovj5R3FAGt2sLfVGneWV55ayuu4a46X6vZRL9ZVXuHWjqjLJ8hDddxVE3qHbpCS4mVXEK/F+s1VcKWhqhoJe3eUotNE1TYwCBqADcZ+wvs4Xj4ftx8Aj6cW6QkLfz/ArD+WRAWFUUnAvDDz8Nv/EpsvCVCmvZgkwoQXStfO+CyVRBWR6wGPDmqqaG+Or90x4Sh8mT+xqrtv01iYK3c1Wm9wzTZFDTnALpsVFZ6jegtHXLaIH0fomfrMsVUdtsSat7iwXf8kJAEStJgEOyDb9cfZbwU1gAAIABJREFUvSZceDKk3DsgME6RuXQLbo2rrHWtnZUlTYGRDcYJqTPL6MxyQu4YDDNuDvHngPAshOeWFgqA24eXB9stwrBBBAtcMQRsyDAEkJsDV6NWRAgMwA1BP+1kOqJ9wnhG7TicE4phorjy5NwWsq0pPutg2YK3HDwhZDnlyoZsHDxAX8Cln8Up23DEA/HYaCgwOChT0JVN0BUbbVYjbO9yBlvuilPuqoy9HsSgjyDspPSTXQitf4TW+Sk0F3/76aaqdkzCfNofZgDc9HkRGDZoAcFgBIasCAbwdD79PHzqPP4Tc/PnMn7e8st8OQW3ePZFRS2Q8NjH8udfSm1AMARBRkmju3k1j4Tgpyc5t5kAJUPJ+nOy7mbOIvO3rHpwNJnXkNVSXoQromXn4DQ+ZXzUoDwVui6L2RFYK4gqVJiVaWaOsGHamlghlXegEFHXpOLHT/SYOV28iVTsADCIwwAEvOs/h7f/oi47G2M8jI3SHiONQBgWoeGWlFVsp1hHX23xwyx/4+uAU1QxW0pb4z4qcGBS6NXbJ6wutnixhDHQsLJgQ0wXhA3g9pECN43bBw3kJTXPoOw/Oz5ijTuDp6A8SJot/60z05qtJC5Iy42fGqJ4VUZgRAgQiBEgwmdw/p/il96DNwJYId7ZGUv1pgwyrx5IYTi9cRQcnBpAr5TvVvMkGUtq4cQToasheG6s8xG1ybtsAn0wdySYk0inu2wuKpFtHu2sQbxjxXggJU0l8BqicQk5Cj+Pn3sMHidF1zLTklG4AKx/Dg5rAIPuugM/9UIc900ozEm9HBkwBIyYuZ3YILXel7fwCam7kO2t7Afn3F3IYv7ANfXFXolrySSiwjUoBoNVJGSNF15HXqXb2lDTcOKTyyZHacSA1FTtgjCKV6DKBlP7pDJZJrnJZsGFwaTrruZbXoJ3vglX70IQYv6zDkQrQ3AAMQCbqcN/WdzpNW4NgNaiMT87b21Cscb1IP9Jc5AirCOQ4pWUpYaVPUIYgiLosUGETNIgwmSQGSwAJmG/mBoA1UxBwsBsTLelD9YUZm0BsGbv7ul5m/YhHsYAy1J/DEN+flseSgLIL9nn/xqv+0P8zPXYvZqorFpqpxYBTe76bOTq8vgMc819XlVhKm2MzcrxEDvsYq6I1n2/Ni3etaY5cxEcc7hqDbZzv2qy367+WGitKBLhNmBEIBC4ORIrRYcdGfR4POEn9ZN3xVGpkXC5ZheA9RU/RZPm1wBRwA//G7zopxIMAoUAWEBumdMIAIrCoFCwl9DV/5wxSwezRKFU58ABHBr+iWqaCllsWKDy5xaQqeljtKbmF5jfm4FOneuR8h3JZcfK9qeeTiujWhE7xEYtNt4NyQ/S8oGV0csYmMe4Wr4UjLGdC5ecjd96od7zu9h7E2rAobkZ8ghhTF/UnYFz+nJZkFneyMCB5uwWOX8dcJiaMsxaFRT79XVenfn1YU6qNHRdh2X9gSsKYkhPUJMkQYbHG2VBsBFGymAMBgrBEAKwMgePju1uKYnBOGCtGr1DRbOEXNFsFWSZpxY0s2wx1NBXwzBkJszKNtdpvw6PJYTeGCs4U4DRhL3Y93f8vd/HCy7TWYDIgFSsim3bAb7Q5mikphbmAHpn40lO63FyicvZMKeCm0SOG7jKLlm++JiKEHnKPNQNpqMq5Blqh2PV15d3oXdbUHRejW8fNGMsvQ5dzfNkwACFBHJFDYNSh3YRwL4QL34efigN5MUANv1vgQ2HuiwarEN4tDVVbXN2VsI3H4c3vp73vnd2cRvXxGtiLh6qjL9O9j47eTFivhe36S2ZZEXP7n3i1Y5G857f2U2Z2txDtpJz0mf+VOFVlSjUVkHvScOmfRCt0Kx0jlEJuhUpvnDK+/i/XoVPfmiFMcRvJLlOLkKBeaxQ4NH6NV0BwpKKfV3l66AhM1t01WFO2uXfla+7USaTjeGFWY9nEIRrbPWP4CBiCGFgiE4hgJmwIk0IGGgGG8fNF0lPL5FNJFerNxi+UPwrtz6G2fPZfcB4St0JS/p8rvEDa9i4uTN22JcF4pe9IQXDIBuv1AWftU/dU19zfzxiBRwgVmBAGGgrNyQODXJynNbkRUx6DzsrUS+c8uPggD7gGR266gMKXYVxFt61yTlT5LKuW3C6jnuic+oN1qGrzuO0uZ6FkeMKBsPD9bBTcPoufAEcEdu1ZWSMnwKxwlI6XADWrb/h8wPeYMgRHCKBI4/Cq1+FZ38nqCHEiBbrr30HFeicMpukBa5pAETnm549EKoyq+38s4KWcr6fNzFP9qGZDYO3WWcfC+1znTU9MPmHZMVnLh2nB23GLq2ZHWSsjg6pr7CQY7HnK/H2HPm3b+c7XqcLTiNCSLUeUPFSXsSY6fvsWKWtdeiOmgr0V2zroXDoiGrrFdaBlebXYaBovH4cX5pnLwIoPc1W5xguNGyKq2AEB8NAQDaM3ARX5AhYwPdtHvgNNr1q2D78TLANa/Oe13VQroum3kJAVsmP9c2YW8u/DvvxNs69QpK2C4G6SpdehjN2YueDcWzM07KMYdvSYVGTVl5qXRjOxNlBHgO1pT15D/SpNaiPo0an5XK4p8F2WXHlj8Tm8J/52J/JP2u7BW3WD8cP/62qzF+fBR3ugO1HoHA0jt6GbR/G3+/H/ljTFZUlGwaOS+lwAVi3xS2f7ogUELyChSiUff7zdfwrI2slGhViu9PsBhw8aZCHZkfheEPZLJ9FVlt29vcx6syoyJtcHo6f7UR3LjdF8/cm6zQL9UmgmlcI+b10b21eRMNjqSOq1GK1EnGYwrIIYWUWoifqMPJdv6E//lXsuhiKRx9ISARNNlWZHV6gaosGwHUdcxNgYaS5N/Kg1NShIKqD2pPOvB5MAIZNhPuHcKyr2oRx/AGzz690jqi8HqVAgiZIg2wz/PiB/f8zf5JADgIGe/uw8V6EcFBn+UMklnJ8JmcTGLFVwXSm+3ILI9PDa0abqCAJoQgP9vCai3HyyPEb9PQVQ259CQAHTxfxIOgKc+gqDpnrIpzNzS+njg+1D7Fv/Zu3Y0DX7d0crefSmK22GhfTpm442f4scZVZOs20HE4oNAAjtIpB64bj9NjLeflpOM1SIwliPSW1aS+T2QVg3dr73SJmj5FXcY4vCvbNT+Cv/xfd894pzUkjaNmBaW5R90t2jaL3IVj/1KkJNoSm3bdKZUR2ydCV/+pvLq+yL0k61bC0S4ZmExxYMJCvFjqf1NYsVM4mVK56qIIFmxAHdc+iECgEDgh626vwJ2/G7qtie8EQJ1OxPMhAHXYEwNZZNP5P3vYTa6RF7QaHXnh7yPjjNglOJoRB2BxoQxh/0F2xBDiOz1X4/mAHDCR3BYg2AGMYjxv13PHA2zbHH89YnVAwmqDt234V4UIOLlT8YDj14HiRaz/4FqTd7IndolvzcFssmgjDcuLkChj34oZLeMqN2PON+DaDDcliOHNXE7cFYJJdmJkqNhyVpugKtUUxp9Pnwa/z0/LtimzzB9FWBrtaAiceoWi16mveqy0UV3PElQZgHfya9Ysv550aBft6PPJ0O+Vz2mWRXsDI+qhZINbB6ZllOYRJVSopWKyh6C5H4rd+DS/8cWA0bEgUQ7RcUsMTdxhrNizQUc5tN1Z9l7+K3evsXivtf2puYNUImkxkKQtX8ho13wYuFccDwgCXlsBknaUckhM3UsqXWeKfEnjiEtjE45QIILdyOob0TxSlABn2XIvfewX+4n9AY8qCj8XBJsHBwHBYVQgP0WZpC2PxrSNxpgr0Q+SfbhlYbPJqjAgjuUGO+/eePupRYr7GjNDoAjWF1PuqKIZPrBWGeBfJiBBoF+3c8fhxkxjGW/xY2JphmsbpdOuXFUiGEBqZ/GTL0/cejo+mZDSoWCIQbAMbz8Lzn8833F33ro7qDih0qvOCnKyFNcP6tJmiUup78WpGax/hXByzOnSFSd/iFF3ZHLoaJmqNzhZ1Nv2w82LAnNyqHxAm21kBxLjJYchJHX+AP/gF/NwN3ANZbNYWLY29C8JaGKxbO5tC9Ngp8YDGH/tRvPYV8eoTRsZHO8t8C42teTd9aOmqrM1ioZrnTNy5bpLfgLQih2qV78VL3dUKMWG6MpWFOT1+rdw5XNhRTZhYxkvwc3q3U2fdng9YAudsWAXsugRvednw1++AQmqgzsBThdPrj+lwYa0OBfHMWgxszYFNN3iIlcF1259yM1sYYpFkEDa2IWySZnbyZnghNBKm6Oqe0qei061Bgo3EAAWCRkKUJRxvgsAd2/9tCJ8eNmJn+i0UPx16SXH2w073VRoJsU6L9uWc7TsLrlolXrtU/YkMRSWMF9tpV+uyh+HYnbjngGS354p3ckVAp15yPYOlgOgl7W0SzoxaPKMoOYEUvfepR1cDOM1djsfgK4M2Lf+5L5tz6AprHERbLwZx1oY083yd1YVl+ZplWFtUGgNwHI69BJ85E2cSIcVkqTQWLcsCsG7VCUpxtRFNCcCx38hffcNwn6OBgQSwyskwOlSCsBW/z6+5Hs2g4hlW7/diE6oGIJXCGd0KM9BNKpCm7GXiugCn6GLT3uj3y84vnk50xS5+x00Quzfnt1x4Dv7bS/HhvxBje0H0GrPMZMvZDcdx4858z28RZTMFMes8P7d+5B8iMjtEIDJ72AcFjpnRCrYawhg4fAl2ncZ/UcA4aRLEoPwrlCcBJgk0ISjPG7Bt9ebB3kEDQpLt45BLeIf+jfhPN0s4+a/DzCTF/3ZO8bNbxuFRNGSipVOoe2nMsYqOBOFyXHAlLnkQHn1P3LfVXamLbO3Q1dDqx7lG1c7JuOzRla9tYCLJWsc/kX0xrlWsq/TxTT3c2fJbNgFY/jiH9a5a3gEVzUlrjOkDsUqfL8pf8VA+5BP8+BdwVXTej95BXDDWArBu5VLRSLyqdtwFr3wZn/ucwJAj0tR6GXYBNtoKZjk0UYKROStULK86AFQNI/qo5j5Oh9wSuhUThKJb7+5M30VTEVPuJkl6e5X6qP8AxDSKMH2MpGsr6nYBhiFq3ECO4pkf539/OU76UAZRAYOG6izc+uPPY9w7Kkc1C3Vm4Iux9Ky1nlVqEfxWdNTW8Oigx7k1LOictLZWjPmNmJmgyEcNq1MYtgc9rWKoVN5Ibb714lLRKqZlY+PNG9uOj7ksg60EkuG2VUfMfurpyfS+rF4b57fQBWbP4rZpafjOPwZzABTFlwYGhs/zol284L54+H3xwCGNSkGwCJtyBl/ljTDRXYFk31fYB9FY5ZmarKQS8FdQXYFW1qMrFahnbpuFoi/ElWkKqpoDy28n18/jh1Zvy8klOikjihONvLWJuQSO5tEBBz7ID0GhzLm5oKsFYN02pDVS7Rn/6ofwK69dj6hmWav1MGtSLoSTes8/5AoFpS7bDzmE1x2Yd0PwwAwdJQa3zcqNVe15460gx28J8JjP3auqtnW1J7CYR9R6YgICMgCxsJ9GKJ38Xvzuq3D6SUQAB8KyZWgLIe/si5lJYW29aW1ZCp3DwtZ01C0rRW2RrnOrC1uBImXiSAwYA7Z/eKVzRzwLYcf/Y+/N42W5yqrhtZ7dfW9uBhIZQkiCgIQhKBHCJCCTQggQhACCoiAiw4uvn59+P4zy6quAYoLMkwwJMickhAReQJBZUIOAzGCICTMJRAgQIMM9Xc96/9hV1TXu2t338pFz7umcX37n9qmuruqu2nvt9axnrfr9rdGWUU4MbIgmif12PGk+e5FiwyksuMV2c2R0BqxRD+2EPfepu0EoiXHfisQeEnYPW3mpGzu4MReLb+NrX+PnroMb3BA3d/OZTChIC71SQUfVjrbfFdtuUkN+V+WfrNUwXVuJNmttXXTVKfkN/j90lsDd/wtLTNb1uOoUHMfmob6eHehqOsZqLpG9uxPueD4v/Aw+VVYP9qVBeBtg/aQeM9AxIwoQOOooO+VpOvIG7NJUfdaqj6jGnu8ArO42A2KRTssIO7wR2Ql75nIYYK2Mato3NKt4VrFFDRf31nrGGpXGpdvC0r6ByxT5poFDXO/UnJWqlZgqjOVAIQUYJPB9Z+PlT8XnPkMUYIAkFo3RZZ+4sdNSHjSKTUMzca6YfSWyaoyVSftCrTc0EQKLcnFvYhHC/IK5vdRmX1VxR3H/xvXXbJZVdJrYb8fz5jt/k+Ej8RIySoKHiNHnY+lJaX/UlSBj2jJj8BMeKzhOfrCx1Ljll7mAyCBKdNNcLL6jb34Nnz2ER9xERwOYw4ioiu1aXlkbXdV7tTaIsTa6anYFWkO91LIwHeGu6nUoNUQp9Q6mXS3ojL1dMNQpdIROB2LvomqaoLKESK3u7UGMVa/YHTDwcBz2z/jn7/MymSqYtY2xtgHWHjw85mDGq+zJf8RHPFRRVNsu8K1CXHVt04ckWeq6wLfGaPWE8Owo0xtKLXQNjCWzIYMtNWiu7jKHXUNRLR1NW0tFtDFWU2OlNkJsezeIhBkoFuS7TtfLn4YLvlCdiolFJLCWvfdbmrJK0BjNydsqsjEdqDepwcrBGWP/RNL2KWfno4SRzYQNaGcsd8rc3YyF8VOzHS+f4zM2+woI+Q3rRsL5jrfNZ++cz164Y/4km70HdmWZGc0FfIZAqsoIXxHjJj7DnG6DQTyaeG0/0nHsYth3rBzKEUVi6Rrr0c3hMnznS/jkgTjk5jjGGVfFLTTT1JJ3vA/YNaNq2GWVttEto3YMAS8Mqc6rox0wu0JPrt6U2NdEHbuQi2MYKHRx2Chx1fAyXequuofdJq4aB6YjceQPcdX78F5qTvg2uspdF2w/EvMd4AEo7ncfnnOGdu5MVsAtuZ+pzdSdqNTdWN1VSmWRsDwqbx1TaabAqBytgihUEkcNu4Sq2KfmK7F0YcBy+/LIWs83lPPe8n1omS94wyeiY/GgWBRynPtqnnaKvnJRfIPaorRaYXplfbU1UxoGk+86WpymPeBS9raK/0LmYaDhGLL2RL7eC4mFtJ/obH7RLswgES4yAC7RwkJe8guOgpoh6tsVYdcCmolemAcHJKdRHmMQ1zuj+gPZEyeFwY0TO4y/j7k5dA5pK7JZUT5QNdwwxKBRahbz3A/XTR+Fv7g/focArKBmDdeGZc+gVx1/bXJL7Ph5ctiOocFJtOwY+kYPTXmGdQPUNPgq9BBYu+TXE84vbSNaAGtgNBg5kgRx1beBIAAUu7l4qB7ydv7jNrjaZrD2ykOE+aHXwdP/Khx9S5VlizBycSrvAp6WZNVlNtawaBm0077pmi7t1c7bpnZLjVTbnQE9+9AqkrlLgnVTecCGipjtRJ0lU7XsOCx3bpXgi21rhdLmVOHM0+zUv/WvfRllgK8haJn92JK+bR1ENe6rPuqYUP5io/zKHlJWK7XXrUTP5K/8BK/U6C4EyM1E30EsaCG6t5sFyQCj6BRRgDOAJsEWMw9OICAUxhhYGwJcZgF7rLXCuLNoDqxc47NN6Oew9aOjm8HzZUcMEYRFRA4/xGVfxmf2w/43s9vP3ZrTW42u0PZNaLJQXe5KS9FVGl2hZ9Re6q66lhCtaSCMZNP2apq1ATQxHKE43DDYXIMNUmidt+MULCCxgBtCQNifB74P77saV2xDrG2AtVdubeFxv4M/fJLM66JVGyRlgKdMSdZAuTASV2wqmyqsxVpyPnbftOwWekZRbFYXyWW+11JWVZ1R0+CdQxGFTdW8xRDoxru3GhvJlkIh/sl5xkt12in+za/Gjk2HYAheMvdqpBZuvRkkXXETYRneCuvF2yXCmFflurAXK1acEbJYDDISIZalaVCcXLjbSteegpQstknMwA1pLltQc1gBc3kQF6B78VjDJ0ESiwTZnPYDS3/mY0ZWOR8dhuwtBogclrxmpwlxrxCH12B4FTnLpr2TStFARfX8CJdfiE/txI5b4o7o5Qw2oZW14VEnBqcFwqa4q45NPHrFxO7QXkE99EtyLYPTUfl5002+O943eoh71c+JbiyrXHCAjny3HKFDmVPCW+KW38DXP4KPb+vctwHW3njc8bb2zGfoOj9TQisRLHrubhy/dNPAaxhmLXvRy9umtHpnU/SkBsWsYSctVv7RS5OqfkageoBLaCUPsh0gyHYnYENctUy+4VKP1XizsrWQ9f5r1f0bXopTn4lLvl61LxqJmVRUgUIko7WdcetEDraDlofBFoNBFmzEVkpLHqsx34fyy+gl3gy+C1bvIhyc1yed39NsWTt9GpXUhmWIrxjjOcsGyWqWjJ5Y0bjHNKNgsfHQnNpBuCEUuO1Vu1+92Pgf851PB+eKCvrVge9ELuEQYBq44Yc+pc7ZNzVYbbrLyC5xVQ8UTW+ILYaxYuTRUqJdjkyhCocAGAj/MS6/CJ+Zc79b8Q4cKeG1K4PdCOdmjGBfG852/mCr2siuFUIvYRD9eMEWh0SxJQUbchPlaE1wMFgQ7fDpNMAC4G2I2d6YlTNKcTPc/CP8l4tx8TY62AZY+Q8rLaLLS8pAw64ZnvxHOv4+gJuKinK1Iap1EEt5u4CobPilisaqZe9aBvGiY3tOWpvhqct8Ype4ahgolLYqHXX7cgO1Dt5aKGAp0kKjVtkoWZZ/kC/NI40mV7DoomJcyA1GnP5inPrXuOTSGdxrb0HIu+TY1slm6PgmpCTkFXTtTLoNTNvfw3AtdawKmclyjdlW5bAm4615QaDRHSCCKDIQBb1cxogOzAjSvILrJhWMzQ9SCdhNUCiNQkzVx+bAARvF71991etU/CyAnfPnkkXTRjfnE1iD4esXWNP13zEE1js8jVFffewbfwkhkFtmUdLxuqsF73ERJhiu1pX/hY/vwn63wh1iRPFMdLaMPRveVIx+ydZztOqp2pd6rI5Le/NLaXrKDwbgGMih5Xg9pFsPXXW6BRtlzYGaIDDgFD+GrtgMoWVLZc/2AKLldOJA+Blce4GrPoAPLFDEww1lxoZt5xNiimvZVz+J2rYw3noqAPBhJ+hNrwuK15FBBbgT2F21qvRvATUWMI4Bqhgj5YlR5Xt7ILbOWNz9Nn1gwI7ui61tuyNVK7uwjvircwOXOYnN1MLm29V78IZBYnx5mSpYHX6k/xwoEIji9S/hy56pSy4hHJRqIf+Wpp8NFCcmyPSkns9SrNF0tmqVcA+E8E7SQcDNZ4AXIO16vngA/CZe3EzB6eD8fcEvsvmHKCzgIQRfCPCZBXcn5wU3SEIb0Axwcu6+gO61+4rXb/BarFpP9z/gAMDKaXlcZt7x+UxN+O0Tz3/J5PY5KvjOk02F+5hevh+YuDWG7tjzF5UFM8yvg8N+FyediD+Iq8u56vGIDdNOdjJwMISuULl3cjheUIORyRhCVxiBVjWGCeOuVP2DHEb/ShFXncBpaZRoYXeF5tWMZgKJwqFH8DfO1rmsMhOiCW41bDu2H22UsA2xypjjevzhEYfp8Y8HrKABC8DBAHhwK8wbl7EwAAoSV5i3SdmkLzPVvjW8Rbm1RvZGNJ+6O6ljHpZ+C8285yhyqtcpVfRMw+yK1fBciuibRFjcoQh62fFXTlGVfkuVB6nkNJMLhBmKM16BU5+tb309prSrjHGmt/olt9A11i7wNKe6+HuzytNHLStRKZ0X5tfv1jij9V4FwDBzKMhEuS1mfvjG4i+uuvrRKEkHYFEAho0HAtDu7+7a+dQwf627B1I2L1DMMC+4e4YYqjOTGTxI19u9+3mLjQcAHk/e5TJz0BhmvuEWBnHG2Kc9hkj638hYj2H65ekjQXbrYv11uHsnhGfrL48rHmvBjW/711+N5wTMT8TvQUZaIQ9l6k7N83eBi7WrbAEoGuiqvGK7IRlMdwuibQ3fg1ZqvJaDtEd5hP2WwyHv0Elo1dlzUwVfkYKD2wYvfTE2Yg+6cf57ety/4d8uxiXR/AcwYqFtZRYGoPY2ujJRYVkJE2B4/KP4+08ErgIQMJMQUAgmm2wb7FSxVcGZZpVQjWRojhcWmztqDa91xbA7gJLjvDCbK5i+KRebB1xlMLNcH9bjkpqG8t2xu4ptXpKCdYsiYTLBCYNLb34VXvy39o2vAualnUOj43Arcs11WRBDhlVRzI6kW3dOn1q+vyVWbPpbqecuHYxT8VcwBmBBBvcTf3TlmxZ+ZwgwW0r9avJAuxaL46VfmM/eQ7ta7sYZ6DCHB8wkBYq+eNQVV7y90C1oEo2+PPldO54F2y2uvLBc9awxXgRMeGvlQLHEC9OgPFMitlkBVpWORxASgR/hR1/CZw/CtY7m7STMKzEfW409LcDUDEgILYfmFk5CQ62aYIw6DYOD+c256KpRFux+id39DBzGQK/EhPK9/4oFORMEunHmcAJH4SYX4+Lz8LEIW2OCWRzHth/bAKt3j1KqI3EAHHsMnv1UO+RgMRgKl4FRBWOQo93i17tC+9d5pz43eGGrTYaN0bZNmnewIb/9Fs1KewdINaVUbMW+txxKS6cENiKfq8a+lqq9fm2bgG5FAAlGinjrmXzR3/BrXxJrrmyppwDHl12bkLVK5CtHMVYJvIwc4VEyLT1XNf9cFVHl72rMBr27QxYBpoDi6odcceWriQNYebN18HV9MbnfwnSLWXirBcFVBJoMNMKkG1191elX7X5CTKNzqW6GjVbu++16JpxL4c66jhJr5Oekv68cmJW28Bg0y+i0GW5BaNXGEVxaUAn0H/L7X8IXDrHrHaVbOzcMoWfHUNM2XU6oHzLYFksBQ/U4trOZw9h8UIGQTsPgELrSyJ+6DqIcglNjX/OgiDhxTQhGeFzSU1HnaITfDDc7j/96Mb6tMp1wm7vaBljjZCkqmZ6Z6aT/F8cfJzqie3Cd7Fn2Bje4qDgblHOC8q5wdSXkWZwuehEoA+VFNbsLe6WGehxiNwh6mDOrbBdqKp7LGMRY+NMy0rlCqi3rB7LhzRD1L/94Ll7wl/zSBbUu3qrPboBJ37S4qjOFN7sFB6d242g8cw6uGmo643rZvcPqAAAgAElEQVSAKWeDwam9n3s4sZ+Y2LE49Iqr3wTtapYo2jIYEE4w+qMsdNTMvgV+gmax1ExqsXjMFT9+S+E3jNmXKpsNo3A4Fqttx45ngDNZ0Ta+SKUQ5gPQnC1zdrJGu2Lne6mLg5MdCZveN4sDPNYSMMF+YJdd6F84DIcfhZ9vrd1GdFcV7nEmzN976Iq9VsE6Oacbj0PVndeh4XHVPZWYbaGxpTU7pT1m5N601trD24yhIycK0Fy7DXOgqKK19TM85Apd9U/4p1kMnyKA2bYAaxtgDYH6MlgpENADfgUveEZ17TkZ6BGabLD1oY20BGq462cIZo1hrGmjrOZmg0irZpt6w1Dp69D0FGUDfqFtFty69drUFtgi0upl2VKJRaBpyhCA97wrPPcvdcHnmx9hPc5Z02pVm/pyssFpLP4ys4DJ/sFkjW9s7pwMvFsbB/TfN+G0ucoc6Vf7SYvFr5ggY1UrrqvK0e+/XOercv/Y8Nvv3O/1xBUmE+2qxd/svvKv1Lykmx4mZhLMLtux48VjJljpCMJVqco9Qa45n2cCFwLT6UmDKHkrUVnlIERQ+j4u+wo+dxhudGPcvNPi1xiyWP/J2gmDXeFUQ7c0lhvYsx5t8WHsbTNo9TnsAERazwewtiRNM1Jh5HkmoVVdURFk2AFsVKSfEQHwO+OXP4GPn48LWA78zu2Mwm2ANUT8RIGx65Br4Wkn4ZZHx1QOMsgXwSAugHnlFaIR5mlwYYVxc8xJk1K1fzgEs5YoZ8hBR43sZy7LgjWj3rC+qt0VluyBLV/IzrmUpocD4UERvkViqmoZAAie92E++8/9M5+KtFaAia3Q0GWJsOz12aw36lhRpiqNDdguIGlZmSaQVipFjRFaKyGDBNLKxyKGINPVP/oHaP/yCmx7urXW6vTYD2sAcECwr8xmn5UVi6ufdvWVf1ixoWVLE9sl7p0737lr16+bXel0JhYkIzzfqrbvK1nn7wmDmHMRAjAKYLOFgiSqos9K+7yG81ilDAu18MAA/y6+cwkuvBFvdgRv3Jjz1AAxtGWlT/2EweaCkz3WKo2u0LNcDyM1QasNP0fAkPUmiJDncWV5JGAPXdW/BMJFd7jFUI74PAks9sfOd+P9V+KKKSZsG2DtwwQW4IIFSL/9EDvpj6EFSm5YYEy9rOd7H1lg+LJo2GWexsRVeQ7vI2NJeyBeUllq4aaeAV0zBgcdd6vuIigeeM+QvXqRVW/NJfdV1mMAsWBhCJXu8XOfxilPwUfPI1wwQh4UvJa2DzJ8mw9XxRpZj77y6JCJkerMIKJKp6MkpsZM1LUSyMCIk9bYIaXjossv2OSL21+9+3+waqusL+yA7+3c/7Qd89NnO949w0ELv2F1W0lGwOcEw9kbxZOvuuIkVZTnUoVT3rk0u2znrqfs3PHnZleKBWWB8zIbYc/qgDmNmZPvkm42zMzJSYD72tm704Eola29FeqyrdL+pbLOTAXMZEVMSbrEvnGxvnRL3OY6PKzyc4+fjRuMVMcCqoOulkOmGqvOBn6KlUTria6WC9vqVTaCrkLTbWvsVmpYnho4KaXqKdlbHlrEoG7Key4+REx+XB68ql0J4NE4+kJc9Al8ehlntP3YBlj9udxgfpMb4eT/rSMOBWsjKyY1UhoWp5eSLA5BI44QVDnirY5Sqm7zRgNmjYjre+RAVStsHNmyoaVKyGG7XMi26IpYhgmipZ2PkJQz0F0z4ksX2tP/VB96L0p9A1V+wFsnuXlQBVWTNWxkB2K81WvVcLpVY1JyuK4xtLQ2mBh7rVHafd/di+Ob6N3I/fZ7/q5rPWrG94T5J2fhP+azN1r44mJxYg3hCW0UN99/5wd++MNXRIeQZe41XGYmiNpv52v22/935vxXzig3ggzmvtHRJ65XB1xVudX8U1N+vpLoavLbScP31v9BLBsPt1gCVcNrl5ph5vJv2lcv1TeP4e1+htcNoEM7EPlzjqErNsN22rl+HXYqDI3v/c5BjqOrpiBMQ3YM7W7ErC6oXv5gzpe82lo3Zo4chuu9D+/7Pn/AbXy1DbDGL03hSY/Box9RYaSOV8DqwGgpfu80wE7G5mSq47s3G9mrj3TPk10DO6iyC2/QWA3Rek1Ulds0pFV9BLmM5KnNaUR8/zI87f/TO99S0e8mRmxFbRU3hoGZ0sghuQyS1tuZ0GpyMl7prwmmKh9XrfpBxevZeezujfug0bGxc/bFXbueRP0YdGMg5hDDji8EfntjcXyjW5VXXn08eWBT72slWyPDd/c/4JE7d5xq/BGDwy2UlVnWschp8JSPmfKzCzElksssGia2mfzGW53GecK+zQuzYly802eYS/gK/usH/M7tda+d3BXAgpEKatA/bUl7Y+3NQU5oBJB1c5TrbsHxcOWBDh+2o6bR0FGliStyoHQ4BX36xFXzefTmtWVgG8HDccR/89IP49+AGbdF7kNAfBtkAbf/RT7pMfAiRL8GdrwVfPzSbP7uKUpmOu7F2z+T7wihhnF1xcEBJ3xg9I9btr/5pWF7x/DdljJjVeCo5KmNS9veFlce4ZkkYAYIMuFvnsJz34LYLwgTPXb8eOVFuumJK+up1G0innkNNc+qkp3EWyTJtiwwsWp24SAKMZu53xgyNjzeuONMhR8r0EjFZ02U7Zi/U1K8eKp57LqSmlOaSwB2zE8/6Fq32zH/kDy4BTAYgnMmesCaBg3rbb/ernJQcs6X3txbX52Zk9S0mYdzA+AoYu14wajO9rfpjBfyL4QC1EzaAQvNC7Lt0h65qxpdhR66ijnk6NbdWnuwcsXLPsMUoICB/mkbzxZMs1bomnup/hnBT5iYsAbespkcsCAIOuC/ryfeXrc1aNsHaxtgjUCfRz4cR94AZoUvDOYD4D3/WvSpvyp7P77CpT9+O3ZHTGtHUlmNjdq2WWqtkgZunpgEZ60GaDcwAEUBik9/ir3hVcIGYITLqj4xzMorUJvyOhyMau5JahzJ1vr17CVzYd8UiZJDV6xnEDUp4aq9VVXctDNbmH3S3Fh6pgUjwYVg5KUEIBOd5VKgYGO5DzLw0gMOPvGAXX9k4ceUhVkR5JTBCqMClDB6XcndClPSN4x0BmTisHwVfKa6ayxNMrEM2LxIi6ToMR1ckiEAkJVOAmfptBfpr0wqoAUKr86wiUvqBnD28mSWY2EFZYZk5qr7dIYOr4u2RoMFG2YQNn3WaB4S98Lk4mOXlhrZZw4BOByHPxK/5Sy2Ne5tbnL7ER/3uSue95eYGRBA0SmOJUeVkTptdDbYANjmjahWIW343qxfmzbKSjA/TVlVaz/snBHbwtZSJtxbIvXKpBwu5rOF2CAEw4ueg5c9R1ddGV+iAHjbpb1M0dmUenY05CydqctolfeXJXijTKJipQk4n+jKZEHWaELM5bqIxeJhi8VRjU8S++13mmbfmrlH6xRKgBnNwQXu6oufZbw/42xCLx0chJ0733HAtU4MuIAmOETEcByZScGwgIJoncbXHCCYBqmTG0zC2T0htDBVCkSyETXnkDYtm6VoGSCIsKAgxNwwvwCfI8Kd+Mu1q0yjzNfgOIfQFSqY3kRXzRVoI5t5aTvVyQE0DViD1m/aKy+2VB/slQXDSO1yrRW+o093tWqCatOEkR4jYcfi1h/FRy/iV7bRxDaD1X7sfwAf+XDsPDB+JhTdPLZONJAKlpcd41+LqdVAr9hHj1F7Va2w/qt6enkMFR/H1h+doqHH4IJGxbC/etJA04dBVPlToygbX0B1i4/1re+g4/WvwqkvwOU/AiGYoFCAdTAOvEod3GQ1+xzKpxqdV0ZXmbAm/drJuTaNqzIJqtWw1CAzJweLJuNSBmpKXpbpQ1kvEcjdQZeXbRWqXbgt6ol2HfDHB+7/O+TlZiYRJjg1Dy6ykLFwwhmNthYJ2LGSzD+TYpxsxpy8tPak4Iip8BxUiQI1lbNGQPg1b24LAEoeCxC84KIeCX+I770OLzpbrwqQVbordkxHVQ57oYWEYskaQ+iqSV0wjC+jTWpoLtrQSgNlBmasyNtkWKIgODVDrfBwYUOwImp0WezErkfi1w/Qrm1AsQ2w2o/73k2P+Q1gw0D6QkyX8LTWxdpEOR7r1j3Kao1HvR/vHWUHY40cHsfAU7+DvAXIuDTp4xJrlayY4b3vtRc/C9/8BmAQCIesIKLyKpoBxuboTTSCT/pUddIGE5PZ5Bycj37yAdOeqOPHCKr8j26ofNYh0Yt4ke3QTKGac0xkgMm4w2J4cyXQFkF4sO8ecvCdd+16PSyYGxgYDACCUR5TPahZUDAWAMj52ik3nbNIlOEyoXkazOUcZ12vGXOpTXet5vCRm5Fd9tJZgPX6jbI4FhmCiEvw9dP43A/in6gm7aSauLKB1C5hqKjB7Gm1Tr9h8s5tLGmVD63QcQ2d/gZ9lVkG7Qs+PjMjnCyAaMVdPIqPvi+O2wYU+zDAqsZ01tWdgw/GY36LuBKQUKh0RTGg6GGg/qNobGZjlNL4k44IODorkBSKWmXNQYHqkLpNAfaSM1suymoFFrs3KZeiqfjDih0uBe8uRn+/z38az3mGn/+fBMBF9dl5s3saXZpts9wzo1YLDdjkzfurP7f1J+bJaXglOi2HgMmX2K8BqsbU0/0/mZbkgRgIGIvCSj/Dal6sxndzqfISLf+zAw86IcwupAA4jIRTIIOVLYNzxinU6IGEEwusbq+QrrKtyibm7KrjXIXsoOicXtExlrTKMFT3azKQCDTbDD0p9efWsv6Cg06Yo4hX3QX8/Mtw8vnRwIklugqCW2vJWpUFyx7DWSNdtQG8ZJVfQ7NbsDGae4CsORS3tVbtZMMy8Icdaq0B1AKWP+3S5KC1dWbvlLcX2F7+iBWOb+2BccUCFxh/N9mj8chr45Dy5i0JUeSpyLYB1qZHVwB9Voo2pBhT9uD74tfuIc1MAQCxEN1KFTayeaZ42dma7Ks6v/nQRT+5H58cceI2SlwR7TL+MnhwSHglg7FsG1EgZhRp37sMpzwtnHceqj7piL82dXNJxE/LTrdGq2BH594gZhwrinX2VpPaJCxYyWQ8p/y0nvu5iKIKAa0LhM7Z6OWpUGX5lgHhB+z/12F24fj6wogFMDMI8Flh4By2Y7IUmC87W8NXLL2rwW95kvpKkFj5dGyf2WrKmaOa0Cfiv67Zd7EMcENwE0m4Pop/eQGfehkugzSvSPu5WykWbXNCppFFbJVXE0YGuQD1K4bLwUQDUzKTjj6mqblEgrTian9sysDYRCZJMCI4CmruWgCLB+EBJ+D4GNpbhvvGPXCfs2/Y9xClAlyL2KEEQm6HXg+/81BTAKxqZjXCm+nDPajU10spz3wBw3hreeU195nTaegTq5D+HdzAWMJQIB67GVpNfru+amodl0fFMWGL6Oon/+s/x9vOdVvEt4oVehHa/NebmdVwKp3+ljPVZW6QkKWPRUpPgrlM0LaSgnsScPSpSnNQYXnF0UsOYORRlL5p0UTNAYT5v8A0lt4Y6VRxIZb1aKmAikmgMwmP0q/NcU/IvxjSSvY0Z7mGgD3xCYhZ7N01l9mCE3QUdIsnI/q79Nbn43+RXBBAEeP0YkNOjVcC2G8YjBZVVAsPIVkx7BpWqdsnyJ7oqg3Uyp8OadCbO5LTwRi6qg+lqwVz9nwoGG2AYMACYkAhbhBBELB4LB5zGA6t3qmIpzPb9/wb9jmAVRXmQ7kUI/Tg++Fed3YUoAsbFisVgmPBXEzTqHt1BU9jrx0Sv8MHdjh8w/jUQmQAY7W15MXYTdnHWLXmfbmBLQPr6zvRZxAKPetknvEaAHADZwRAl23izt0uVcDWNNO3vNqLhaSf3FlkQqXEnL2Hep36owOLJSUSNXmuUYSlueiCwUwwA3bMLiyLEYMgabYgdgJmIOgMM1JgmPx8VmWqkGeyvyr7mKCdMveW8wWNLRVCCJ34wojgzTZt+kIZ5BXVDI64qibOxBtepGcEYSeCQSaICPFe71mPxvHP4l8VcQ+H8RNbEcsc/9ZsCJn199Y/G67sbuUpdDXkp5jcg5MsUAAmCXRiBtjdcOcH4wEzkZhZ9ZkvsA2wtvwjWoyXduLg4Yfrdx9WPqsCgGMBRbjD2KA08KNxfrXsLlQmDdvbpn9BJ+6fNH/rg7L3EmYJUEthtuTipFKRYOW/6xQuNYN3yzQvkAIt8gM8/fU47cW64kpEMXtsECPgSn1omwFaJWwLunXDPPC053L1SXPIzIbBnM1WUmIx+yHCPNTUSJQE+myWulkarmkO0K4gNYgPSEI7ZiyIQAbFxGOGwSNs1MKyIMgkMs6BSuv5I6zkzYEMK4exWqG7RzFWhFl1FfIntAb4/4XCsli0qp1mHDKFq/jj1+Lv34R/KAATK2lIy4uBIwHMg2XBypJKlYtBG6SCHe6KPW6sD9Taq1RxQqyS/3yXZh7oPR8vCUlRLglDoNzhwMwQHoNHXg83EFwgZKFVqNkGWFv1IVQ6GqNDD3sAfukYymMuLIXgVmlLoxh2sO3O2b3++jBII5d1mtka/KfanFZiVz61qxJm1TcT4SPJhap81zvROmrwN1HqTkQE9cEP6oXPwyWXLHckEYEVott0C5hhTbGWOvd6uFxpn9hjC8fMsOcc+iQTlq1KhCAZX71suoz3Ep0uVJlNMx+XEtLLqrWLAALhxcxtfD3lbjQW4iIogAsDDbvX877qn1RHfjcJNFelKld94R4WozuXaLMlFkAITebPNmenoUdQRRmdIGaYOwoI/41vv4LP/wg+IGA3BanGND0vBlX2DexwVxFXBcCkfrdgkyqqywOGicpZG1p1/R0yCixT0KqB9foy9vR+SjN3k4ACBQBDAV4pbNwJd3gYHgRziUQosC8+9jmAFVsdSAFuN7kJH/PQ2AtXZ0kU0UE6Lm5YDBX1mouMta0WJrVTVXehqvyaJbBRD2at+l6tuAOpaEVotYbattt7+ZSWRsWqJFkXf13P/Vt84dMsJe1Wh+GIVQz75nFsL68IDjMKg1RHJr2xBi2RtojM5JbSjNeq/k8YEn5hSiA/sB/NnWVlsNK6e8GUAFxALO44gEKYWRHGmSdFJ1ITDEYWwWngPAcGZVq55iMzrJJvs9IVtedU1thuE8b3m5LHqoC96JEBqiJ0IOh8nf8C/u23+BUTjVagha6WJFiZKjhcFoysFbqvao27tlRcsSPeyigL7sm00iplVB2CyweH5VbjEC262fsCKAKthH7aSQSBj7VH3tRvSriiMkf7HN6Y7XsElhiFrobiIffCbY6u+pEigFgAIIIEMEZIaag3EMlnNJSJ7j1c65WtQ2dX1qTKGje1NGrentxJYuFSgQipaGvXm0ii7Ilv2bw3NBgqwAA/5Wl4/3votebfseTgIxoTsWn8GEqPJVJNM8BgpRV/ezHKMp0xjkEhPQtm9udPElf5zYbLM5L2PBunv8NO5WhF7m13iLckop5PMvv+99+RwuJ0VM4OIr7z7UtScW2l4SgoiH796x65Qe2U0nKQRPhM3zqhs33z0+ibLDSryc3tB8B90shDvW7/xHsN7nzsRPpP1pcNgNhI2//GN4vTSpAV5vBlK2o9CIsu+Yf1/mfzb5+HV1AoL50eukrCnQFoVd8pNbpCnuV6M1UwA12tT+mtRQfUt1ecIgxyxroqFg4Ruo3/4ok4/tl8icEdkLZLhPsCwoonfuMj8OjfBMBS7q2KjbEyOxlFwh9hqGHDh/6uGtSNKNYxIlrveYGW4q/6p9ljaCOFyHFNfZsfjlIqUt32yLi+ohj1nBFeBJZAInokB+iUZ+CM0xsuV7UrXbfouIkegVZJx6qVet2u3mmtkioZUJikOjIwx4SyZ9VeP2Rk5yEp/Un3yq1RI2v8OzT8ciCKrgmmU2WRUKhzzqfu9ziX0tx8jmKRbLScBF6TlFJfGJ5mqgbx1krIG8kO0DQiTF9pfSDVxFv1nivl+zX9UcBjkbAhuKvGW82icvRcvf559oxy8UnMUAiQFbNSnOsdA4WmJZVpIPoGUa3aeD9OVT3CSGUwiYGm5CJ1XFvrjknYU/nUtIIY9kyFONYLIlxlEdOE4tH4rZvqJq6fEDrcBljXUIaYeOj9cczPAS4y0VjRto/K1JUPaLYwDTPSGMuHXq0h06w9X9D0z4uK6TdBhOBudLhMhQfgrDPwitNwxdVbw0quX/DKQQ+Dc1gmtNKI9+DgvJ4z6aZN4XNsrhKy/Uwg1TGMGCuiLSyW8MrLee8XEeiVfJ702Bc2N6zmLoGpEmpi+zGklQOd17CHXa/Im3lRoQoqGHt5WpF2zZ4XZojtTcAVuPI1/tL/g7MEp+AIBsQcQwNmCGoHDjZhUB9a9dNvwtis1CCu+qzYKpVBX9GiPd8TOzVlqGU6T8EF/AJufiJ+rXxm30NY+xzAUnSVvcmR/O2HVN0bpQPvKMaqO+8aF1PbDmSwu8TbGEjTOCznTmDH+X3S+CQLBQqliUPP5qQ6LwII8FII5magPAR85pN4yYv5ja8gbk+OWrhsBmg1uOhP6GMykUfmxJbQ06y6k0mmJLNzMA0+9jxCJ2gR6ghC7n2O0yrEVrezd5ycEt4TmbQc1m0pyLRlx5Sr1iTsS7CS+XRdB2M1FyGZoO0aPS9w0QAxdjG/+fd43gX6PFA2UASBoEOiN6PCQ4than9c7T7BvoUVc7sFwenF8ESNYsgKcaVmw8EcEU8QxwSIguRv4+FH4SaQoH1OkrQvRuVI4K/dG8fcHCoMgApfXvY+hrSSgcQ+shbp+FoNdgIWeWCouy7vYiz51JomB322ZI8dbiyGB5JEYJkk6LvxrGfhvI/UdIdUQNiMnldjVkA1GYNKjJLTW7dSsk2ausiZPhMul2MVukwiakxbs2qP4RhiM9KXDRfg3mFh22v/8uaLLU5zccEhH6z+oVpeJXElJJr+TldtJ1zVYm1P+LBVQKSwCccAokxRi35s/4GPPo9/48QCi9gILWhWNk4vuwUbmTZdVJPgqAafaUKrMUuIlRfnK+MqT2K4Lopq0hBNdQlLT9fZQhu/iFs8EPcjUYesbwOsLfxw/NzP6jEnRjWea8GyRIhJjIWJEvgSiDA54PeYp2IKZg1d/X1jUjmkkTUH1ktLaAwHJhWMZJ5oAEx66t/gjDMrTtsgRQi2uSisBDs1xmNNdrwj2xU97c29KnRbu8iVGbe3XkpM6pgxXyY2AoDvXbv/si2EZfNBpZNbrE0U5Ts4THYg5jR75kDbNABaNY07vYcEUm88b5uvTigzmKqs8Ti6noM3/Z2eOkeIokADF4q5e0CZKjhcvOtE39gUugqDrTGZ01kCbLHZIFhIRaNbECPdglmzg6ASXcUKT28Z5lWL/Yw7gfAYPPxGutE+yObsiwyWnfCrOObmdQoUXZlrAjXE3ypdOgeLcd64heqSoveKhn0qa9IuaxIhaQhjrb76qcx8l3cmAJhIOEIcY974Rr76lbGSGN9TMMClMr53s6ArDHXn1Y7VOQ19mfNizny2KsORY6+QiCPMMeVaw8087WJQf7Zm5vO2rU4leN9bPzUhEdUAbk6KskkH1BwKKhMhraer69tQZZKvmdfqqvxZZ71RH1v8Hns9jGHT6bGcXuan0aOhsqDTcdq5eFOBQnTR5wRgs4kowBZy4pA/ewJatbdXcrSfsLaaMrUam2s87afYLFH0NMrL+U4wYeG4GiiO4a1P4PHcZzXf+9DjyMPtnBf7HX4RKu8hsBn30sedNvSpLYNjGoNIZ8vWPcj2FDLy+TPjGGwYKA+E3UziaZsG3Kr9CExloiClAv/5n3jcE3Hev7Gkg60yNPCqE3lzdOQ2W5+ac16zVWoMhI3RTpnoahLqpUmInO1XIsxWOv76+eYHhRVro5Gx2u1HyQ9iQyxFLDTiIDNuoDCyKGIR90zBDfuF/xCx7JofeVn9/KT7wOSWzecHtxm0Rch8i7Ed5hzM5PEk3i7+Ups1VO8YYZb393DNN3Ew0E10K6UghKnUYdwJd34+/uHnecvSZIEooE4u4aCJaEgM9ByGaMxqgUqiq1bQTaSRkL1PJJXsHYCVcnuvav1WObuGT/GTD8Zvf13f2qfgxj4nOsMD7+l3uDW0IGfABjgXCmFelYetcdH0f29cYQPJnt6GKc7WAiQHvCsD8jY9rrxtqdWYbTiIniftsrxK62zdrgDEDSiQQRIJ/d1zcd5HKnQFo7uMpbs2sHn8TupJYnBNP2gclc9RZUKcNXDJZCpwGves51k1huH2pDLFgLl92eQiRKdmACiHcS99v9HczsrFgmQktFPYjXHb2P7BJ7Ro6S0730ITmnTIoQ7g61x7g85VY2Zag40amYZbiZc0t+9fUbVjA4cSUa7hGMshuKnCWpAJHlfg/45/fwlPfoleIxQBlGhT0Kpa8g6PwtPoik3HwNyo2ZXPeB1CpoPdx9EY5QiUEyYuKByr25yA+70Ur9qn4EbYwudWygEqxEASh16Xf30SbnQd0CIwMHeRQREZWA/uNC1D1X6ms5lK3EU2tyHAbtaMxoEUR/CWRg7GWjfYsluEtU9Qb+caMmFpHLCi3ZMMpvKftiy2kKDwrOfjJS9ksezLFbBZZK0Na/quirmTfDJY5UlX4tZGV5kWAGOVo7Q3VdoNK2GskD7O/BbCQTqw2qz6XxmaDTPSiHGF9eSX0pnMyKrfrfrahWIy17nzvvmCM6yV5dz/7laq4k32NzQXEm3/Nq50SSNV7BbZNZOcDHm8Rq22Rpq+dT7P34Udv8S7hRi8Clg5/tIhkhAMCqBQGrt1vLLGJl2Wgc1lOGISBvmImWILXUlFW2xu7dC2MTvGjr1i90ct28XOaDr4vBngJlMAF4Q5/BAc9A6898e8wsoo4IByctmMbVGZzOhWPbFqOeBWepRLsvvdU3e7lWG25LjNADhtFcrUMwB+3+Szg6EG6+Ia+T1xy6Vtt9RuNky8dWUNykKgIXjUe4fDgCUAACAASURBVBLAbqIwxMC9gu99H057Oa+8KgbsbELcHRN/yWx7zwS6WglL5VstINkJ2LHSzhHF96fMHEXRmLnoIOxIzP19IIsp/XhOoOEYAJoUV+1JXjWmwn8mFV052UeZQvg0I7tGHk4HE+cIsyaB4GRi4zV+lW6u3a/Eyz+M9xXRapSR3aKbZoxBfD4jMSSoSlIa6sG75mic2yeYxK9rBal1pyUNvcoT/VgEgA2TA1dTcBRGuwvucD/+CmXRitRZSLItnQC9ZQGWBDKAsDrL+OCD8Yj7AXQVoIUy3Dl23vkUH5vnnoBOV4UnogxH3q6ziiqyOxwHOO/q7lDXUn30TvNITzsKIMTwoDLzNCYKXv59PP/5uOC/ohhUmzC8eWzpPwY+xl6b3+uXbxc5GUjX32HCSiBngs9R5ecrwTOhyU/FJ2m9JsrEGfXjF9PXQCbwneTDJg33J5cNyMu4zLlr0g2GeyXX/KdOagG+AL/KL71Qz/8RLrcYJGAyiF7ass9gC6CwAaa8xlVtPXvTkW0Q8fj0XBMHd7VSZbPPbMylvSuG6XFX6Sb35mGTmjkJBENwXGmYP0InHIyDatIqEFs7PWfrlgiNlJd3iAECH/Ar+PPHR6RggsvLUnf5f4zU43zk+bElSv105SDdfZXWqhgq+c+hw2vF6NVMMke3r99UJBVUuMxKD4towlvgr57GV722rD+WGb2bid0dq6P1YdakB3fa/zOHtco0RBhDbOn9jzWgZTbB9WuF6Rk90Tw4OdmvxD9lclSZxGQ6/yfntciOGMKK9WUkK4ZINpyu9I3n0K5pBJkGakNBBZtn3DDUHg0X8YtO3Qf3Ruw+h2ZgAEUWxEylGGXQO7Q/snN4BsnrE6TAPrE0gHvYTj6rNtM4j9UseQwWKPsv7JcsPR5hTK91uMEIioub4aYf42cv0Bcb3Jhx6/babd22SW/VpblzJx5xP0pOlL4MpsoYvQAjzeNoGYQ4p0lUX8tPwYds3/u70oj/e/8lPnqb0YdwWOKxACmwgGh0LUQXF4Rw9tl43emCQVGEEMJmW36MzXOTdE7OfIkMZfdKAvnJI1xVbp/poTWpBxqcXydtDpBnK7Xn6CrH1TP9OfRDfiYh++TnnDAvnUSlkyRZDlgfu4TyabCcfouxD3/SPe4aOkF6AJyUwx04A68/C2eJEpzRlQYKlZvtRntFG81Im3r2IbupQeWGT6Cr3tzRCRRpz18TIGysFDON88bsUQRJlAAnaMQCGwAocxQP1wPn2FF7sxIObVkNFrfuiVllWugkde87492nArQCCtbE/mViujrt+mzfAZYBT4cNHUo6axnu1lzPpO1UBt0ZtJTv5x9MfXbtlNKBjSVQ1AwM0m6Q0Bw0fPOreNRj8IEPl92RZRj9prFjSEwt+XYMK/XlZdaMVtrP5O+rGjqk59SOOH2wM7Hz5EqAb+y1iR2mu/ky7QmatEqm/nrSbSG9zUp+Cv1Ow/5+Bvef4+OQ3sPkGfVf1W9+7Jzg4F8T53jN4yB8OVgKd8c9Xo03HIkjCmAGFITBHWZCaIyGU2KsPTC4Kj/AIrn/zB12XOhU33m9JI/hF45hOFEsTVhj01UhoCBnTkdxfzz63fxANKYuo0i3KMTauhosuliieCngofeNbK8Hlk2FpcOzvCwR+lgkMytT2qmYvwE2a1nDrtKgKofSmspyDq0/+qRXzwV+0juuw9l6g8BqNoy0yTCSMLEANsBQNspgg896TvjAv0aBZ4R4IjYRuuq3Bw42DOZAqBxGJLM2l08pZVpcjiGnHOHRpENmfupOZt7zStKuNL81yX71hfZrvO8gSTP5lWGt7tE1KKucZMOVQjPH3mLsycwT0WZiLMqR2RQgI/Ah/PPz8GxBc8AjxhKDqk1HaoKroKtxaBXnLgkqRhzYPU/428+i9VprVWFiX12RvPxTVDcDAl0UZBRnDocM9mAcF99DrVzdLfjYyjYNNcDmHW+F5z7ZZnOBBglOknKRoAHFUoBVqZSqEYEZaqe+mEkpbRYRo6wyZFVpipG9N+0fGAdu0YqZHXmLKBELQRILaA7u5hln6jnP1Y9/1OiojQadm0ZLMdb/NUZi/YS4q/R+Ejgvp5qWmOkT5zKpkY9erJlthvXGyGvx+6l8++lGyMmCWk42c4IfzcxwzAllSkDAAeKD5e0NMhrKcMp3r5l9ntkb0QdSY7VCbIpyIUGYIHEpS78IXzqSN/wF3CoO5HNQWAAWsABtqCqhEaanL41iF4FFU/lOQWUAonGqLR0j6qt+NTChtepZqS3jSTqPgjCHBANELIBQacI2jsbN34t/uRiXRi0vN5uWN/+xZY1GS/QREf8J98b+BzhgKBxW1oiXBIx1yZ6W0i8AA8Z6aq08mm6fvgpH2Hf+LF9Sk1hq7Uc9SOTJdxk9QaCqG7K1z5grAm4UnDOGzH/lErzs5fj2d9r3kW8WYnfc+1tNI/emRefY9Dy580wt/KRJUs5m2AOt1UqHgRX939fQ3HQMM/dk3m3U4oeN2sfOq/PWfXX2kFgbaBdJEy6jiUMa/BA6r0oUZBMHtnySYHx5ebtXN3q2H+nkKQx+mIOu7vWF4X6Np8BVab0bXn/fxaWv0Evvirv8rN2QYiEAMwIOC5rEVWnWyodAz2TBzjhtJ9QtrTT3Pr7zuhRoSWVYk2uoJ6w4waj6ZyzdmGN2AHl/3Ouj+iQBaaatGwK9tY1GTRR+7kg883/yZw4CYkQeh2B+YinX6P7jGO3D3ooh2azHJYkkeY/NUseqdGg/7L2Lt3VaGYxai81ig8CKa7WyEmhP/Wuddc5mvQaqr6xfECRp1vVqX7VhMAdI5ccn57Buk2GFkzxT+kgS+Xf50vL19oAR06ycIiCmzLEmKZOEHUa6Pjt2DGOgNsd8IUdmPmltmtPimhmRObhBvsdHJh24iR5fw9cduq+On5WrTQKYt4fZbHQ12IiXg668YSia3uEkuTW2Wq4jd5TcDEMtWf0GRjlFA8QjccQ/4v2X4fvVzLU1Gawtq8EiKRgEHn83HHUjRgtyFOMOaSP9gA1hltRywh0RSyWeacU5jdS8s+xPRmRYBVCMvOOkJWn8k0iHiiAKJmzYGefizLM273XSCaPtzQ2eOSHldIolJuZVyaRJmLLSfpDtfplpa7mSy+hKSqmEV0Iaye0Vl9Gaz8xHimn8kfiaJm0+Ej0ZOd2jfXPaScw0Cb5XWtJ0/OIHzWYnOd1r8MPfxNPfzLN3Q0YL8Fmsdg23Cg7PAnkd6G1tU7tJsKfZ9azJiJqe+DJ3NWoNPwwrTTP33SSOwhH35d27cpVtgLVZHpICHNc5WL9xXwPdPaguVPtq11BUiNNB1/LRaaPwvJunx9NW+Ta9WPLlS5hwYaiAURtmrXIP09tu72HGUPpWXHaZ/8PL/VuXYhPaMdR6oE4fU3susZUIgAQOS5MNSIq9EvNizrSNpDZoUqeViRX6Bps5iCqT7sKIT0QmklsJveUAu8mTymGn8oXw+c4IaStaJDszJnmsdH0T2Y2rKxn2roTerlGPb+vSU/XyH+B7LhgsDuLjhFAetIpzQWOGGkHGPoWQhlbU0QR72Ae7nsvG1uHe9srq46oOR+CD4nfnVQE7IAnFI/TAa+M6YMdwa0s9tnKJUBTufzc8+dElSi5lWc1qcf/3+loZU6k38v4wqBdZVwJPcNzClC170knLU44cxsj51pakjHR32XzNpz8brz+9FWy9SdAV2lkfY7ZGk5aSyDMOzZnS0pNch0SZhFz5PlKY8m5Iu8lPBgImKJAc4LgSYt5zzJ3Quac/FqxoMToIp9JMVSakTux8EoJPXrf53rCZGKu54m0SWpNE4GZ4zL6MC0OwX9W9o7SiQccoiYfUm1OqVMJaNgfGckRGdzkmOIIBV9LuMSwTWtFxglQ7sKNfKGS7IX0MX9bvawQKFMZwIxz57/j0F3HRdhbh5nsQCBbwoHtGikZULIC1oXeiJOfJuKXmuOEjMMeTSsaBv2piXeJMGUMIw2XysWVTe/+RolO1PYPe8x6ccQZ8BoZNd/k3iZbIYJGrWS5lFjJWLeJglb6zNAcwuZ9JlLNSPXHVjfPrpMgrGq7KTq30ra0qJktzUfmhk4lTy3xh5nWbKQdEhk4/86rOLE83a/fLlpPNML+AiznwhuKM9/LdKINaMeIOjXEJSt8kLI7zxfjckYuuohClTeEPHAOHg24SDtWTBZPRAyMLYBEA125h49dxH2xhH/etXP0keOfb8l9fKbF0xSQNdXjU4OnbFPTsPakW36BGiLpSL5xwKO0lq9vkkaj1TP8EJz1RrXEbzADyxN/UW97BaMq7uRYNDSVN9Ytqf9dMZfrk7DIGevp9XoNZh/k0QH7xCBl6/Ewn0o6AJvPd8/nF9PM55qUT7HXymp28otOOnflGoyuZhfZflbDoTL9R2nc0bTG6hmcpMvxLBzdw96ZV6arurz9lboJlWsiD8aA3881UaA/82aE3y8+qWEUXn9pA+c6lLcP3wc3G5PCZyuOBj865MEUn8MXd8ch/xce3KoW1daNyAJ1wF4dF1koEVXvLjl06OfKsTlXbQVe12ljl9ki9lwbSo3IuZe+tNpSxsGjtsDRxecEL8bZ3xCAIbcLrpL1KdpJRbpVp5LOGDncytjnTCmvs5ZhqIcTqOTxp5600w5TmV/IJp2bT4l5hrfo77/dFjkXWJGitMcfUSfpwpTylhE9HQqCWc/kNHlKn8wOrmGtMCrbWIH03UZyO0aFZ7N97K97+Ar1QEFAMWUhlZan1MGU/OSd7b1TeZOFoxcHtdXTVP7Dod70wUSiAGRnuj3toO4vwGr+Y6EqQeOMj8fD7AAVRAAZtkBSLdomQPXt05bXy9at7Rfkjjz/EqpdN671iOKJSJUuPLlntW7ofZejLjtnlETngpmZST2nl5TR8/vN4zRtVeFjyaXtfhLhXxtD+rBknwqYDkNksRn+PgYZBnJSOh+u/bz4O6+9k0M4xx+MqbZKZ3ibRuJfGiDkdjvkdf2iXh2o0g1UcGfpmHIMn1fzrJHRLn9QYZOlcEmPOF/XzfZzX6SQYPJJOKS3H+iGTs2xCrhxRXQK9ZQLKXl3SEbM3AEMoB/dy7Wqzxu89aZKBmA0scqPbHwIMxqgBqvYaX7vOJOgC4CyNnvQqvPoCfEEIoLMMuq03pAaCcpZ6c6lox3u0BuRkJ/jymWq+kLq6+OHJa7zVsS+lav4U44L6FBNRTY6xHuJAIAEsJDyc97+JbtSZx1l/y9sA66dPV0S7fVb3XhTmHXdH3PTweIGYCqM55C1rAw2pl8YQenZGZmtFMtgNmxNlkCB7xz6EjANj4wAk58JRAAtgYQK0UW7196/EJz9nZkVpifUTkCBy71QBJgPUxgb0VSPwEpPKJOyblOasSoYloEx/Ck+3vE2SLqs22TXJnknWJ4HwclwqMk+t8xaJ9J58EihNgKWJwJVY2HTLQqYofqUrKn1TrOopP0aX9lF7vG1dIdDIAgZn6QaOylD9GBx7Ns65He4A+EykLP7Ecc/ERQ1m2BgPCQoF3BwsrZsdslnJ9cTY5rVQlsMURP8cP/NCvITYiDuEs8Y9hFqMEpcGPZ3RnhNFj9HZRwMTWSY10G8P7HANifJO3ls0KjzVcp3AVRIEJ/2mOuK+uAujV6oalReiHV63DbB+OuiqRL0qu90MAvffHw+6OxHMC8Cd7lpgOF1cQz+DKwbPAu+NTEMptmDkOIX036uDsYZfzqWJg7NbTR8rGgqAySEGGj0A5nRyBsjOebvOPpdwl9tyZLCfxBdX5UHulTrg9My0KqOWqVVfb5Yaoz0ye7sSrFXO9Jlf3lrVcSphE7q3Mgdziq0rXRWZ4vr0BTDoO5WT3phGt5Nc5uRLJg0+9uQKT6O6NLpq8o7188FcYhzOZ6IiPAJugBucrGd8HB97KB78FP4vAouaBKJXpnalS3KsTMxK+bRF7r7UTLEmSXyBGQnQtO74A5ijgGYOnI1zzuFbhUI0oBDNuSCC4KIa7eeTACjdetWZvVzdsJqMDEH2373fCaiR4/Rk+cWXk2Ar3recBIuqkFqtsgnMT8Bx+3P/uk8+frBSZQW/mR9bwaZBTTdyCgTvcTs8/feIwFLUHvP/ZiOgatDbfdB8geN/HU/9WwKKTtCNKl+uwb0Nmkcs2aSm/3vTZpeDr+rJ3sV5+TKK8jKeDM6T/kKf/ly01o07nSH4Xi8Rci8ArIG5M3YZjM/EOZNcDiLJb1Afw0xIyu0nLRlX9QudFNnkBxSmMRmSXk2ZKCpz1u/Uy9YA1ukYR2TEbGeGGKYhFFZJopzEN6vCqZVcSHK4ukwr1EEVl2QgSGMkNAgIj8Xjz8FZ98F94y1+K9zyc/j8F3A+KMBIg1w1DRYHe8CBg3jQMTjmBjzyIB34HVxWJTEGiyEfiK9ST7SUM+aEUp3Kkum5Aj/+Ea58BH7LWLB0bYgG5caG1Y1UDLoSsIVdBrMF++hqIkNwWL1Ob/9J4y4Jk47zQwGFbdqpNoysVuoGgDaDFiQj7r05bvBBfOTL+EbJlVhlgbT57Ru2QI2zealYdO7U/e9KmFQIkJEwQ0BJYjm75iLO0RQCjNg6+PjaoiuBLy+4cjeCPAYhcpmUmWBZfcjUpDQmHfJ/x9StUl+yG8AG4CFG8gGBrue9zN/x/oo2BxlALFj8RC4Tcji3dI/BVqKCNiYqwooOQDnvvoYcagwMZdatcuoya1iSJiZ7TGmGMuHUekp25KmpOjVBZHgKYHX7iUlzrHT+Tz6xlPhe0tdJPhe4XsT12uui9pEItojwS6Qp/DM+9Eq8/Nq4HsGFPHZYv8RefBAOhMwgoSjRFUTAxENw8OPwhA/yn3+gyz+Gj35MH/4izxeK0+xlD8FDIPmyLckk4xpDkIoAB+swxZmAd/DtL8TzIMkciJahIqRWYkeXUuJwcTCnDXC0C6rha9XxfPc2ntQEF5VfbexQVgLkkPd8vBYAC21EcjFqs4AdD8CvKB4crEzWhm1rsH76j0gLW33NEDjqZ/mwe0JeXbYuFU6QDF3ac9L5w3sq+MSlP15MbJGl8V7r25Ok4dGQgQqXvvLtOAVPLmIi9xoMBqhgNAlzv/DLOP2syq2EcRCBbO9q3Bl3Lggiqb03RqM0+0tN9plBzmmyKkcDlFO7wbhcbJIpycQ9mEoYHCvt5aOiST5vJe/yPcdek7N4gsybTCGcJGkGxftpodsan3nfL2qNKMycloUEw4q8kM0cONhisOhUoJyYGyjixfb8uDMAs4pZP1TXfwR+nctB1SRFVuqxeNz5+OIr+PJ76G7Vm8wjHf97/oQzedb78cFb4mbVuO7oetzk4StYJQQxEWAZV/wGvOFL+Ao9CIICAedulibvOR5UKbFvK/ljvG7IAZw0prXCuGniZEFwgLVqxJz4SLnJAZXOinFelgH+UBx3FG5Sa6mjTm4LOLxvfg1WGQcfOViHTMfdQUdeV2TJG8dVEUDBR+1luwC/XTHvM7FeBf8VowBoEOMPlB0xslDIW0kMl/ZbtFwvrCr+VrgWhgCESEfrZa/BJz4DgAix8yXWXrW3m2hLZrvi8/fK3psAYgxFYV0Z72CL36SvQQ7HMNZflp5u0xUiTCnTcxBSQqWOjPTowZdP1vL2IimyXlLhGIAY66wcpMfGAHcapSVItUyKdDJFZ7JIne/F3+zSXakCmz4MkuY7AIORs4XoBp2tcz6MDzYrYqRDOBWnXV+HO4UydBZOnYJTTsVLr8/rFxDKhVzUVVMsBAbZPXm3L+CCh+AhDWi1xkTuUfAV6hUrjLJP8JMv4UtUqtwXQEHNgEWvcuIc6EzyBLTqZdciI0Nw0Im6X5nBeMRNgtlqaLmq+gwrL/gxYoyCuFCJtDyUYpjFEbz+cbprCQ2FuvdzG2D9lB/LS0YAEHbuhxPuGqu3kceI1yXhmqjoepfqHNLoDTFbRYpnGrgiOxpAjRxJ4uLONCzpLI+6ydABc8duakEV9oHz8Ka3QEZSKCCP9VYr44D2/hc337VfNbhy3UvX8pNeMnumVtogB12tpAdKCLaQZ8+d1gwlPKhyOKc0SO1jjjFYsCeYaYyGmdx/+nQ6LZDICAHMEe9nsmsr9ZlOoufBE+/3k05eYCsVKBNHm0nQxv8fyB0eCjKocPPogjD/Y/4JVO+Kqsb7Z+CpkEX1OhFOxil/ipPAICGUw30lmBeIUKqzBEJv5psfxodiWB+SN/6QjUY7J11wCm/GWz6Af/GoroeT8pbWOTPv2QeH98rQYRT9jPhaaeSXVRUmozWZYT3WACYL1FylVtik3WUikIoH4B67sF8p/aUTWyFBZ/PbNJTrGivF3nc+msffmSpAecUxqgT9BVqiJR/CNDkmH53PjUPBluNNgl0eq6hq1WIKYyF94bYdUAaOhKUBDOr70LkwUAwi9MrX4mvfiNmiahxDNLZYx1Eh4iZjw93EljcjsXG/B+Bz/4nbH+ulDrTcppK9W2MRY+1ypxlohk6dZNUeeGQHtOXQYEhKmNNTNfKSRvp76NusZ3JayIvGm6y1ZRJFaPThI0/nZNVj8PnOL/ktkJNMXmJXgy9sApcOr5N+yRitNQiy65NN486xnXS6GnMuzvQdkdCqr8BjOUkaBc0IPxa3e6Of+Xj9flgQ2nCbw+TmBD5pHz+FJ0vlYE8FsaD5Y/H42/FYQRRuq2P/VCfV+Kny/KSWgoeoiIoMvwCcpbPvwGOXoXpV2EM9ck1gDo84r66OlX/4mr75arzSUKB6W8Minxgb8LVqDeY2NT1NZLuNa10wIpgZQlctXOUj6UBDp6YC2CgbO+GsoKdgx+Muv4RbV4WZLWKBvhVsGpYXgczvc7tI3vaDxGvR0uqrh7EN0iLt7J1HO/i2bxZHjyTZNNuqzadeKATBHQWwwTPP0dveNz2krPS9lLewrK64A4CVnPxB1+Jf/QV//pb4yMfx5jfxiMMEAxm/uxBV9lTsCq3aXky1vIBKw6Y0JErntU3yKGMlnrG9ZVJc+VAMU12HK2nYx7xV8w8gLfPqzPoJDVD6Q17b3j1xXglGMMfENXEwidDuQXyfxnmT/FBOO+Tk/ZK+RBN3WSaL1oZlhc0iAJldJxzy9MUpH9n4t4cUD/47P/kwu4EThoWR1EyhkPBivfB7+G49rlAhjuTP03OiX/Ib8NoyDBACqWhlyaItGiBQGOuIM7xd7zqY164PaYmxSruBNdb7Bvhb8a6z8GbAipL9D+NgaGDBrIGepMEB39uTxZgKpU9f+crpIENVnSFs7eOVSozVZEq3MOg43KWaU13bJcJrTomw/OUG1+GJ9wS9nazXRVrqqudSJedmp2H50+3CyJRSTWEvets3q9ZOTe7Bh3znNOJlV1Li5gBC7HGxV5+DH/6gdODbW9+LlWtHh2LXcig/TwcMj3g4fuEXI3HPhzxM3/gm/uRPcOCBhKG0pTOoXIqWaorya7VIidVChDGzypXQVbqEsYfmipP0QGYec7ommNnXNtgbP+mQnmN5NQkQB4ko5NlQ1fqtPsEzyVpl/ilnbyu5j+YUarHHNhyTnhqTcDkzeSnTbSsXYxnl4UDs/yg96lNXfeok/6OilHDwBXp+4My4n+QwUWaaXRwufQr+lIzQKA6PAcDdcPfH4XdvjlvcDLeMRu4C/xvffiKeeBhuELCT4K/i3ufy7MqtJxRxbJUgHIrrPksnGxpRCkD1HrZOiYoO4HL73qt5hmsRSpVSOhG5W4hozCxIK08ac0QpIB4nlooMZdVQ5SSCqq5ZpPfEufmCsM75CpBzAeBE3PswHMrSzGErlAg3vw9W80s+4Zf5xAdV7pje9n/qu17FIKlOqO2I+1TL0aqZ6ZxAWsg5gNYzbKbZ9GWkfd+swR22Dp5L7y41fbNU6Rn48jP8Ja+gKpvWvXVRy6ys3xoqOrhsGzz8hjj7LO7aCVrZ+aeCx91bj3ssvvx1fOHzsQgoCjKALH8HTJBC7NuxUQdwZPS1JdimnBDlVWET8qJFcnLu8rfHeKNcvm49wVVk6usnkdNK7gwY73zMDNVJ59/lKLIn8Up+yTXNNuWn3IydQhp4TT4z9iFgXW/65f8hwf+g+H+eXzz/AO4SzECCbjjaj36n3vlNfmsGeDCqiMKiL+lLD8SDDsX14oJLlTT0zrzTf9t3j8dxgAR+kV+4j45/L959BX4suCF8mV8+U2d/g9/4NZ5AsGLqWVBGHYvbvx1vu5gXl/nNgMFU9rOtMRhatG6/CF85DNe7He4ogiiqYdnH0/3QbgzUkK9Vyx+rWeXggEdogvdKeF+rgasqLkEY6qafZL809HvzKBu27TTCro1DPs7Pfh4XcSuwV1ujRNhEFPe/U0SNTNSPeybpNaGV7M7oOtX2+w2rq7HjmZ6TsTPExFZ7qFpeNbrCmEpRaKdTlX81bADg5T/k696EhVXrtr3ox+CsWztjPIJmEgHDHzzRrnNdkFxS4gEwXu8wnHl6+OCHcMfbCsUS+zIe8BxuADxYEXILZ4kZYiV2qonCc2amfOvFxOyVrl1OnuYeGjt1oEwfCSWKgPl1ugRYnGTRcup0OTgPU12T+ZAofS6D9gqJfsOaox0TYE2uBFaq4uVHGeasXqaMas0ML52/9Lu6FCKrJStdAF6pfwhhdxFKk/a5B0O4nJf/Ff83FL2VVPUI6ro69Nn+d1WUh35NJ16EiwTzUnFVuti8Sq96sv6snNYlAUGASOJP+RRTqPrX4M1ol9XHvQq8bLyBb7gc3zVF6n0qsC9pWdM1TSznCO+iogF0NenDnjQVisaNKUzmSXIOw3NKOVk2dhj7I+kPwN0EV1nE3XZyvwYwWDH/AMcchWc/ETvmABsrBkzZsg9QYdUoMGb1Xj3TxVbLMMT2DpuQK01osXcJsnF06jFSKe6qzY11X26YScALXolXnQGL1UGC2ntfSx0hFj9PggUou/2xeu3rK6TcagAAIABJREFUSiEFLR4RY2gCARpufCM87vE44gj8x8fxwx+WrCIhOANnpdA1xKExbRw6OPTnt+lNzkyJdf+kY1DaWGjSUmswahBTxgHIsxVdyV+qozda2yJhDeCbyZokmLB0iXlS7J8uqo6hyXTlN8cDfRLc53jnrlQrX8noJLN0XjgLLL7LH52g+5e3eDVSXBfX/Rq+8Qn7jBUAg9PFAuD5/MKtdeytcItyoKVAY1koE8nT8IrX4HUqu7OjkVYk0SngPJz3C7jNrXAzwAq4GSQSvBVu9Q687WJeTLKRpaP1GP044on4mi45hNe6K+7CLu7pMTpMeSKysz29jcc0yIcNac+nmCcu5a2SJwm8MVf31DMqh3sNaBXAAovA2U11w3/Chy/Bd4S9uuDfZrDWLUWVBum4+23sgAOAAlj0cEI6PgldtJTFOfX34PWl36aymoinyOsN7BNa5epKiqHVGlmdTDxT03UFnBd/HW98e3U2Ye+2xQomWCidjIPKKr75c54LFSzdyWT1jReH1vJQaY9/PL55MR73OB10UBySAwDnAjQLc42M8pZlJbAeusrnrhLoKqdQlfPyBLQa5EJyhEdY0TigeSRjmu5J3iufR1lVFD9ZqOowc1jF7BRTLvnN+aN5PB0ucPCoEtfGoNvCSsmDky9Pe4XklyDTn79UWIBUvG526uf5OVSjW1Ed0jP85IN1ILgj8k9BgSQY/sz+ZFkPU3yJSrdD6WP8eA2KGpokjzUH0P+Q/zNKDWawyIjHTZ+IJ1ImRR3sSJ0tf/STTAH0N+vci/n1/nxRdwiWmuBRdNUbz+ltg0bv58wmE0fGZoqmWah38m1GeuQ9adbYqRSpsj/UoKO9swgw4eoDMbsrbluWPrZLhNeUU9i1nz3glxxuIlA4LVnjm0YhzVslg2sdgFkjywuMpJRn9HSw6ZOqZEvIhLWJ4AHSqWfiU58qvXNV7O0LOsK4yGHFPRsf+iDc/W5AUGVJ54y6ewouxeQKge5xzDz1VJ73Edz+jnQWmANuM0p02xgc5Tu2BSuRHGMzej4OyOS3MtuvOrN1updtEmFkejVNvksiGGfSYSsB+zJrdpOCrcFPLzPDp18GxZR71tgFs544PSdnKae0moBc+ZKpdJEx5+yGhtDmqD0HYDYrwCfPT2JViQhVmOChuO6f+Z8YF4EQTYiKKfwXL3imTo57jxleoUyjEUHIYjwLyl4hq7Kfo6UWLtE3n4DHsxIAlUS7isfi9w7krijDCiXoWpNBEWwGOAvKPoZPnapXqQ0X1Ci39Yx1umN4x9eKo1NAMZkMPVoNbMxZNbRixvC+yqRTWZEOGAlF3OUAoRlg98fddmp/7j0aextg7VEJAAa77dF+/G0h97IU793Wv4HIpxSBVDE9/QypxGXUglmNlkNRGnHOXbF63YRuWvYbjiwdSudcCsAipgoRizh2FBd+Gee8K14DEgAT966u0ErRgEpLZR5ygP7+pQRBr4NdGc/j0kvRzJeOfYIoAOjoo/mx8/Syl/Lg/cnokLwgh6tghlS9abCBLn+CmZx70niiP0t1fKFy2LJVjbVyLN2xilVm4iWJpsI1NGEdNm7SP3YMgY1hsvyS4qpeD2McFfKyxnMsW3Mc13IQIXpRkpMwPZ+7mrK3iFqihbl9wN73VnuLSjo7Ks8KACcVT7mpburmRHAagAI0t5PDsy7Ft9wQUJogOwqAbhCKpQ9p+ZjVRslxCjjDzroU3ycYF4AEBCNwR90lLmIr0fhaC84qbw+Ke5i9Gf/nIl0YD8oRMzJIGCmOamoby/UBy1CN59t4bt/6MiQ3xuN6Jzowb/HvKavSgVSf0QmUQIENwoHiOLvTnXArdNOstwHWT6dCWMDBex0T/1UW0cevCU5nWC63H8l+Sl9Yw9dxe+XhU/XHpMlbtc9qho7iquFjEKuGQY/StDngkOOVZ+GzF8QWXy6XcnsR+JY5RcKMKmagHvpwHnr9BvvuiDLVc9+s6x+BJz7Bv/sd0Esdhqv8zOiC2ROeoAsvsJvfUioM/5e97wy3pCqzXuvddW433WS6SeqoZBUkiGIEcxYR0JlRMYDMDMqYxzGAoihmRwTRARXaMPr5gaCCKCoijEiQMAZy0JlRgaYJTcd7ar/r+7Grzq0TKtymle7v4T73gdPn1qlTp07V3muvd71rhWR01tGZvVUEM6vVfMPE2QCY0MHYswud1lH03UVd1N1Ls7kYNxoHOeadUXUS77KfEYTRgLRGuhFbJfZrZjNRlxU9W3+siSxgK4xrJuRaK9Qd3eEbUFrrq1qXGXW7MgTEwAA43zt1FAkhOpxgREie5R/hsZQRbpCYB4OIpbj3fdn7zQfDn8yyJBR5Hl5UOosmy2IvFCOW1rhZAJfrvlNx0gy/VUim/JHYDuSMV9/9mlYHQq78Gtx8Ck5LuMsAyRwu65er9poJiD7J1GrcPH2WAvbJxFL3VzUzZCVCRlXL1VppKUT0oXhJMOdTbS+tTUnwgwBrjSdyAZtuEl/6BAChqAwOLtw60tXZVD2sRVGa3EzRlYmtaAIw5lPSED7ttSVzelHKLzFWTbSzA6BBiMXq7rob8b1ziTxhLxYREliLqYMq/WrSu8RttsaXTq5cdZJoAuR23KeIHCd/GTvsgHe9u0iWtIH1fHmJbrFFfu21dtDBClJl6FkzbqkLU9VKdNURG2hLFK6TUnVX4jeLsTq6gjXXAdEhmrCZWWnFNwPlVseSX0OdsbVg193EoRVpNbyw1b+0Y922Y8G3+xeKNe3h6FJPbyiL1+3czQEwwCHj1K24/t3hvVQwxKKHBQjAy/yAZ2g/MpAhhJ6LpMzxZT/1WlwDxsJwxkEgSAfzZdtiGyk1kmcJ65REiJF5Dkn4GS5MnnyF8lOgwovwnNILcA1dRgFABrpXmuOc/e/j3Gt4vRfuD30Dmch7Nq3YJ2mtUAp5Y4e8nYkFkBlAU9HyNuAqb5ybRvmqgZ6sTH2OY4EiE8pKaRZK+uKkxTvA991Mm63/+Or/AwaLsCfsgsfvRNDlgluKLtZATDf54mMTBVX75EiCQaWi3FY0HJQOhUmrENWvErwVupWf11ML89ANpkSDO5BchfMgx2mn85qbQRT3uZKt59oksUKBYrO0W33+xELDroiS43BG+8Sn/cpLi5XLvffiU5/ELo/id86UZs6TpcIgA4H4+ROw3fYI1qW5aQ2KfQ2lnI51E8zS1R2NDWjNlb7WLMLWz9IxPLiV/xuo32YbXNPdxHUNGtnWoJOxoQ2zI1abuJOGomEdFK777M34ErMxI60r3c6W62r19Z1wzQsUIjNGOlfC5p6SfXEJFoM9znS9AM7j8xPSC6NyAoYAUwBfY6+nbDj7gpDejDeBuQghh+BUVkTRelGFJC7GZQRTaqGKZmvFxDzBh8HNbMc9mKrt5AbhWty6SN+ytGcKiOXEVFu8q0Cr5vV8BwH7oOihyl+lFjTWURo/YK2GZlufpVl8+qVBsgjgcdzhcdzpQSf3dYDBAv3pj0WxOEjxeW6FZFLdvviOl8LEm2H2+2ddVHNDCGi3mnox1Q37ZiWHK5UG6VK8/kZ89ycc+JCkcWxth5dHpoCcHAD3fjxf9tJCxcpg9II4XnKPjv8cZKlHMCbr5Buux0EH4R8Ow+I7hEjAS1cwCthyKzvu41m0jkN5d0lvK9lTV3msm3ua6ZA1KFDW7Xkgge8ejdKlGrXGhg4djRhaa2oNVFzH912z90KbR3mzbqx7zbEOFk90qx9PPESbJGtW1FfHOmMXVrjbMiZzKHifwTNm8nwFlr83O7ooMQOeHLHInbHL6/y1LgYwMHMo0sD8Kvz6DJzOMnAQKsyr/hXv21YPmfHSAWMR+WwD8LQC96RSVkFgSSqWfCpJr/R/m/245172kHNgBs/8THzvOl4LTns5yjrdlNcnhaiDo1VXgUplXqgjAhqeqe0KbCAaOPQ7Q1nVHX9y0AdjceoVnqN9oOxBgPVAM1ibb4r9907X9qBZY0jOXIFZlZ9YIbG6VIjrnx9Qo0MJPE2QiBPSCaqbVRXxcaxW2HY7FSuVErEJUIwpHNkjCZ76HVx/y6AbIMXVFAQW154vmlIuWEYCp35ZhUt9yp8v5493v1u3/ZF0KSZRPGGF3P5LX8FTnobFSxLrV7YcRgDxwJfFvfbq7GfYtd6HRqvPunmoFTk126B3BzStnMEaZPO1vqqZ5GtQEXX30JoVt3Q/f9Ahfruj8L+BIUN9ck5rY2OzyWo13LojZupSwp4tB4w1tewa+ienSYo9wOQ0MJov6i26NtyQIJO5EUhLrI/Gj2yrLZMnQ0I9Tsim32xvL3YGASGHp4DlE3A8yrYdQUKmgfqWkHE7bIei0bAI/CBwrn5oCmlVSsKSVnW2E6qSn1fpO1EaJt6KP5yqM6leAoIEqGzSurrLM+3ckiAhloW68cqGt9UEa7FXGebTkVrrEsg78NOOLpVpJnwhnrQFN3oQYD3QH+AJu+BRj0w1fSb+tbisJ4c9z7hUaZD35xjF2hjrOvROZlrJaEpqa771aobU2J41yZm3Tpvvk92zSt8sK+zUI9zdqJtv1jnnp0SIUoYJZ+ogNNwfC+PRn0wAmOvQw7HrboSxWDIWwxavuZZf+lIyrQkDBi4FDgo0ZDfeyBe9MDU4FpE7MiC6og4+qHV66K7Ybc4JaQ03xOzTcrqbsNfhgIYCU/P032B/3wXfYPY5feMQoQtbNtu6XnclfsfwxIbSYTMt1OoN0dxt0MBLoS3fsOHCbnbMYrfIy1mtSVrUjcoA0CPgJEULmgPgsPB6kAXnTlABwGZYcGR8C9iDiSQ8ZnEKztt12zv5zlTkE5DBAiToZTjocBzulCUcw7yaRgbx6XhuXvAsprJH5VbcMhg/taZDoaciIZAVJQIl09Gc8Wz84GbcZKAn6X1hZFod831s5I+Ni+rJpolCLOmiBm1ucyLhoJu+YKoqGb4dzdzrwJaP6YMFyEorHyBKEeCj+cgnabcHAdYD/OPPfiyMUJSZQNGpNp/+Cu00oJQ0ZATVwF3V31nDnqVjXmoTtmdrR8YoxpoNRZxajkkgmuQWg4BFP8DvbinZpJSdnEaCtRxeTuSAYaON+dEPluLMSLpA0CTgzUcCQISQRYAywaT0QiiJYK+8Chf+HIPUVdIUQNg+T+pISlU7+9asotEwOzZUkbpMZl0UVHXzN9psu5uBZscGwzqQNLGzrwGvjGzfoNNCY2rN/YRczQ4ODVi2y0vqrpCJGzdk8rQSgc0K9zpANtLiOl5txCwTn9bMM7ZyCgiAwcCe02h9IqdwtV31ZftSMRaVbYJk/Be9fQ88GgyiWdbLzUmJ+Sk4dTGWYCY4MHkY49/x7wfpYFdSYg0GEAjYVlt9GMdkZZ0r1abu4OLLcaUjFqWqosK4BiWqHHDKcoQkVBEUBAjX8LpFOB0QZaKc02NETnUYj23d5XUSkSHTUTbNXy1zXCUrUNJsQ3K8rYdxyBbV00xUlGhDxLSJz8TjHgRYf+2jZXFzGggs3BTP3xvogzQlD3fTBOqy9muu1vVS6TD9NpbwWq2wfLBWqkfxAyDio7+leGzMQEuNXNrEQS4HFBwOBDDe9mec9ePS/qT87DPny7Wmus7BkGmDjhAY4OEf/0kLtyyVqCYYQYPjrLN0/vlKL2JeZHVZynNOR2cOAm5f/6oEUknp7oxEyPd9cpfyWcemvNbiSDUVrq6m02VmYltEdMd8leZdzdZeobV6WEVUzVCsObtwnBDCmLN5qxN9F+VTl6pldxFVR+3/yMefWFEd7L/Vki1tX73qBi9stk9DWy8n2vwjZlfpqzxpCGDqtbeiNJZiTy3902bGcMEckW6RUJ8AYoAFGI36UO/D5eCcKm1OBdHf238/1Bdg/ULqbugtDfcdZf8KxJR6IxRJ0CRPx7ffxXdthA2TaYJgFB6PvX+Mn26JrcqM4aKYd6pOW4q7BzAnqRnSYm9N9BFwIEfpyJWKgpR9h9/7E+4AcyrpXlkZ2DkMcbxTB9+wTWjJVzWAIe9UA5kM+0YrMCP6quHKj41hKVXKnT5S+kz6OMrBGBkDptz6z8NTFmKzwakZmF/wQYC11n+qqXtwWbqV9tkZj34oYebRUztckaYUW0im8fthctNIrVECO9pojTrYNnNalXumwrVVLvSGMISJJXYAeTSQiiAWfZe/vR6AjGv5izGCLmSFhDO1q+yyc/zER4DgLBoVoai0WPnBOcUY7KIwsCQlko1yRktGfD3fbCtjMlU1QpCpMFkGZt95PqtCRjN1NKt1f+sRttYfG1go1IuUm7OKW6EeOmj2W2FQA9KqApH7r6/C7IVorfr9NUOxI20HXbwbuvQQtKLw5uuwO7vZeh4mQTpFTdNEi8Wo5XIaSfc8iCmEhnLRRRMtUBESjchJmtwgR3Y7Fv9T74jCPYZeKAgUDtBL99eBmdx7AKfhwS0PUafwtAt5WRFmmGgvEoogj8PHfo9bv4CTDtdhb8DrLsCFl/HyR+NRhcUomTa+g3cch+OKgYywcs24Nm0CZID/Tjd+jd+kTISN+jVqFkUSjAjYNRyY01BS7MgUQPW97WzpExyvAw5+Yv1b51ZYI4YACrl59mhu/YTkOEorALfW9vfy18It68GBzkQbMN1Ihk8einceaI5CEIlQsMpDY4StCb4ctbK0uperfZ82/EGs2zHY4M6ssaeq28/oDk2ZJC5frme/Xpf9Gms/RNOKvmImsXzhq6DTT+dBB1VvslSaLP58xuk46qhw3Y2RDhlJU0zZF8hS6GBGyn92vp72tMGFaoJDXLxk6m8eim6dg92xS6tNAzr0l03cviPkYrew5wYIhQ4CarSZVdYdQ8MM3eU0Tgwy6li6bQ5BUiP1Ov7XkWca/jl43LCTkQcTD6biGoOJGzfvxN3r/jrYbd3OG96CZNrzxDNQfXJwAHVHqAiY3F0hC96Xs+Sxconz47xdsONu2j2Xm/fusTt/Yb+4k3c4jQKcThgFRXkA83na6HerfrsltgIdyflcAOJi3L3T1A7LtQywKO956Fs0xx7Y63K/rIy3jyndGYN04bTgM5qn6T23pBAtE6Yfr72v4JWaGWc9A/K1671czlyP194/5bc20gYAxVSjwCzW3ppYZRs0TTaDKrQ830nZUgcavJH6apa0OZhDGdAD8spr7ZP42rtwAklIRR9oEXy0fiQVrjcMlqqBKMm8ZPMN8dzHBSXBdiopOegBIwxWK9yeeD/M2KZVDNMmvLwDRJ0ct9xxJVHElM5kNtUdfG153jktk3/1TFz+axDGtYyuCA+CDEVSanKZf9azeeBBQhRciJbWQyl0MH1jBx2Ma66NH/swN94UdFXyRRkDs0Dk2utxesq+Vt7R9BRhKH7/TMzGD6mBdJndINkZXXUhEmZLckzk1SaWFyeG+o03taHRwrS1FDhoahvsuVpJbECr3fsZu+Owv0RDYivF1eVzNX/dzf4IDYRlq04LbaqpVHBsPuCOJCsABTlkIGMOmSx3cyDfwx/32fyzf15x289WXPTZlZ87afVJJ04fv2j1129d8fsfrjzvgPxAR4QJ9OjFYlKwe7nsLVNvBWLaJQAxgmEhFvxtfEUa7zPrxTANGRiv4BVfxMko3DOJwly0TP+iHIX+E4Ah86KSpcVY8kw940pcWYyUZKp25UV30FqbH1mGIV7OXy3Ct5PZsiuvnylGetV9DF0NMUyd0ZVPNhsadVuo1VfVdyOOVAOr0Co2zoypfJEJSpqWwfMR/edzn82xccJ9LCc/rj850OsVg1UsKYxwGfCMPe3HH/bibqeBLgdLZSHayadZYE1VBxcrLw6rYHXrjFmtfs91m828UXGjDiFOjn2V1ddmxRX83EP540uBTMj/EiSW0SVTaSWj3/0Gj9qZMlnhGJiKhEzRN4iemH9Ad9yO9x2F//st3LuigM9GSdplB/z8YizcDAhDBWJozj++EYtO7SJ+ai4UduyBaq2Xdcwtad2muVqEbp7szTRVFw8k1MjOOvJV6GYhtgbQVrpflEIdt9SRvlqDLRuYoYadV72wx2mwhjfq/uTEzRoItvYNvCCA3GFmjv5WcevP9j/9wlUvKYfvCIaKg0xII9ZFvPjIOf9wE28GMtGZNOmwadfPp3/0ZH+aGFMv4WC0e1h42O3hDkWHEbnDghS30db/7f/LYuSX6OlVQuUyTu9uStnPZ+iMt+Gt/4M/pVpbJuaFaU0QYrmntT+XPQdP+yG+aSTEGqUvxtuVxpihZuqro7PjeORi7T/Zsjd1K3TWHowQK3G4sVhrgy/A23+MSwd1kvXL3j2sLwfKmQflST70edp3VxS1g9IrbiYYgWOYWhUmdeSfE58ZeXuOEbMCRBpHkbsm/Y68nDN7ruy8MvdwjApOU1+hgWdtODOH36IPEN88Dyd+nXksrI/XXsgTCdDSdGwFsqXecDgOOxQEmfoSvIj3YmKvCNpM8/D8DW3/F+uwwwnY9Couv88fu2t44QvDom/4lgsIyxzOtJYFJC650954BPv9LrYIDY9n+9cGjDLuPoB6I6IGINUsb29OJERbv/1A89S65+Y+uLWll5otF4XOCv3WhJk6N4Rmw6oGS61mT9SGb6Th0ppVhmZHWxC0tWXU3RTjJdrRvkiaMyqJmNxfGg/6Pyu+uUd/16JaV4rcI2Wk0lYAhYfjIQflLz87O+ceu1sAPJiZxEBebb95gx9WhCOn7QkJG9nGP8A5wRJbRUJyLbVlt+F/X4T9nTCSZVTXL/ifh+Ow3+C3y3Dftfjttbz+d7r+s/zM2/TOL+Hke3hvEXmK1AtUmLmTBHprlSmxQbHyj7hjJzz8UdjFEIf5noq/VBITp19NZIYw9qoJzNbIXzWEzya+dTND5jUuQlVopXpQpfr0w5librpg3AjR4P+N28/HFTZTTLX1SIW1PunxWSZFAeBGG+HHx2ifHYlMcoCBjMma15VuyJrPWMdsdSS6RumjKq2ldgarjTmbEGPcKv+ySV/roLXHHJEH/6vO+FH5N1OnRUb3KrMPYJ2BvvFGvPkWLNhCkoFiBIJSc8/iO7lwAZB6p7Oys7owICV80EVY3HUqGkeEktRH7L37qOz4z4wEsraqemelbUeHSN0G9qiLWgvdNFsNOSp1gG9WxFX3qbcLEkU3A/1ZeVrOisEawQHNWquG5+tYpS40VXeOauRBdyprVpKstctjVcVYQ29HUA5JzpfmLz51xdfKrF4xxThToZxDxUiEtAZMI8OdXPzMOc+6BTcX7LVEGSx+oP/hd+TvCIpAQIgeQ3Jc2Cd70n/xquRDA7hojrhJ3OBG3boFFlTnaQkv58Fn6MwR+U45naT/G+EBIYf+kjTJYLWJA/HCM3Cy08vxbmJu4MxZbuOr0MVRSO2enxMVLxPJKg2v59HBULTxyfLzOowS4ckODehfgmueh3cv5b2FB6mGkMC6jlrWsxJhemS0Xf9G++wEUPBULY+FgS6H0RXquyq8gxjQa4MtK2TSIFiKQy+ZbRgTBvscHuYm3jlV+Zc3WJW6xAuu0M8vqdQZfe3WBwsGjwDMKb73Xdpi85Rp70Vnj5PAd76DHbbX4f8ogEjoygdNxZUsC08u8Ew9oUim8MbEF95xt045xTkLdNVFit4AMupYllmBku5+RbOCLM1Snu7ZyWuQSzixJbDVirOZDBvvNxzPV+4unEJn99S6zsfq8TT3Qna0hK3r4mzF+s12ox1RcpfrFm1+DXVvl0UTzTHncdrrtBVfKxtaIlHUmkOBrQSIChLEfhEgISzwhT+bPn87bF8WpoOYA/YFnnCX7ix4pZhuewr4pH/Sy+gOt+RiiHvDyiPsiLIgQKBYcX9RJ26M+TOTHUGYV5ENPXmBEg5lKpCQrW3+wQulBHEBL70AF9uQH1DFc4EVskfe6DrkNb/N6KpbfvOE2WeixMrvV5RhIS8rNGo2VAzNBXsidnsMH5mUJ0yWPesPL7QeidwrMcmu+LRdTKl3JaUAeGHOpkEfp1dNEtIvJwDwOo+QNuN/DqXxzAjhFdPviE1I+3U2tvMxR/gJV/+MB4lgUGE8AysNtBx0nf4j3HlvAitpOOTavEBdmmlstj330r++r2zzTEdYArt/fjOWLuWXTuZDH6ZTThaU1iLl9xFLjGVAEIp0ZyvEjQ6Yib1DXtVbvrz02nEDySBFMqTRlgJlQYClRFWbEKQ1yYWooTA0PtMM9EnV/rgG1TPaUgtbK3FYI+vRiTPiiBS92YCqzqRqVvW+6lsPypRdao6Ypbd7g/9Wa9GztZ448cyMnNUGdXwr99ngBzbx7dBoezv+jBSDnCRlYg5lSiDCUsefU9jat9mjv/trVxxyyMpDnj79zD3zvQp6GV74EjOIyZfOKJOiCJLRnELGeMLKLwKMcIKFwwJ5/JwT99xoj4032mSbDbd94fwDftH7RWmVxHJxpS18weenT5rPeUiqdADAbfbno+ccXbreeGEDCuzrT3uNH0pB9ABKCpA5zuD3z9IZhfl40m4SZ+H7opEpQD45//nQ+nsAZoDkFFogkrXJk1ha+Kex+i4s+Ta+B/SKKZixaOGRIDkrWX+T8RDG4NEIDiqN12cc2IfmiwkzSBNBoIp5ldDJpqGVb/NKmBuAHEVaYs5UOS0+ewT8aXqUCs8OgwoJ3YMlwr/MEROQ6Zyj8MK9ZhzIanGZ1Xxi1n/8jvr39uphOcaNuDnYbN+OHbYRzOROq2rwTdCNf9CL3s6bbknEagFiuBbHDiNC8noFoNP/Lw88aECxc4BDDz8cX/pKKIZIEMBee+uzn8JT9rUU3OOgAcrBDIU5Xyjv7cINMDviiOwri2jRkZlcgf7YPbTd32Rnfg+wQDhiZJYhJmk/SYkGCRmY3x9hexede2tkYQN6axBs3Z/yYhfDrS5H2/yShjLfGlgxYcx5fNaLsTUVsze/ttnLYGIlbg3KcA3bTHy+9U/FYkBr0SwSAAAgAElEQVTmliuaYFCkXLREqAhx6+kFB69+xStW/O32/e0LJ6k01MpB+9bcb54699Sre79x9Ckj3EUpGnp96wc3RVcgI1/Sf8mXV3wppPIf8uSE/pr5r/le+IFr2ixLe4jKj+1/8K2r3kFyRL793qmjPxf+zc2pLB38Bj7vwv6Fj/FdBp05kQiKi7lkl2yn5VhJKacJfTiluIc9/or80nTtXICf/wveegWuLknyB7S9v3RqSP/dAdudo6/siIcTFKaJUNY0cyIMUwCzLQVOLilWKh5DFb62Mo7qD2Z2B1Y/76hZVnUuLn8R3yXFIbem9eFnvRG5F0LJtPDZ9eH4zGtBow9yNWsU5ZxJPZiMhopVxQRF+SRetFkmX16wg18NnILF4peVB+DozomaZziKEEe3YZEYBQMlL08L+e9n2Ok/dZb6zWobwFriFgNcDILjoJfjAx+gJ+lVYV1COhbfxSP+gatWe5HPIwC47U/4ymn43W+x625YuAAk3GmhqBmwaDlMLdok5v7Dm7joKwhuTKqJAJj/26f67zuaj9kFN9/K2+8AwCTvgBzK3BgQEWFuNLRZZHUxlmzGIt0LbWhUOnd5ozp0NVv7zQa2Zlaxhs1UU8dk6DUOzGmoSLYeYfUDjvQBNHz8BsoQ3fo0m084OmcStNq/kYxwgwEyM9FhDC4Au69+7EfvPfYTSz/91FVP29w3Axg5oFpEGKBd426HrDrkMb7rJeGSZbbCUvFfliNmCIDTzJ0gPr3q0w+PDwPocCIIeuu8t3y7d7qrn1nIXZSTILKf2wWP0WN3jjsNDZ/ks+Izv5l9a6ndLaeQk+pr+rZw+8vjwSh9rYwAtAHm34tlF2a/CJa5csulANDu0P/2OG8X7fJae/W79d4/2Z8L60RlYHwAwVX1sSEssbu20Vb74nERMBiSWJbkjP7B61XnPhmUUJqscBeG0m9nZqoa7fl4r6J3k9g3oqta3U797AIQfCS3OBMXLcbdGlq1P1giXIsXqGbipvwJ2ytZB5s6hQZWvdE5QaQpdXGkrasuN5lpVZy0Rvy0OmYdjoZuDodJV1/lhkDA0S+sY+BC339wYUzJDKSn228tX52WOmFso43xwQ8lDaIX9T4aKAUccaTuuU8DKcAAIsJw+hnYdbdw8MHhmt+ZmQAqGlg41cAA43fPDPs8kV/9CgDGkLsxQMx1wIvzA15GoP/Sg6Yvvij/0Ae4YOugXsHngR4kKTAzzzB7D/eO1EuD52dDvax5Hm2FLA3oCh06FlsFN3XgBm3hhnVFuvGNrfyZlfn7rPziUWMDNqv9N5/D5pJuc3pPl5LxrHB8A6vaSzxWsDQ+mHwLbX3K3V8+e8nZz1n5/CIRnhRj0Ay9n5Jn0njzwlXP/9m95++d7xkZHRnocxhcFJMvedxIGz6pv7foKDp+AfDc3rmUm1lUHiBYkGiMUjgv+1FaF5bb0uWADtAB0YFgZJAI4/f4vbPCdwoLhYKiCUY7zo99qG+LvE9GhYyxR0HofZzH7WQ7nYGzgMKEOhShhA+cxAVWnBNjYg1NOIfnCm5ApIO5IUJJ2NBvrAZOllhVjKww5tE4ZKaIoa7AcRppUJuLjTaNjXk7VT3ZcJjPMKRTjWJnkMkjx3SGbE/tkpqo6p23HywR3k8GKwmnZX7qG/G6Z9QffIeKXm3pEDVdh7P0zZr416Hq4Ugag3Xej1V3NaMKKtK2zOVGCRng+saP8eqjyo4MDsjhtc2wGuD2hsP9lH8HCDk5kCqKF12kffdLJJspVe84NNQZzeGBYa+94zOeGZ60j7vDgryfXXKlnXMmbvq9GSzVGBDBHhRt3kYrLr4QO+8SkmWKImi8884NPvB+LloEZGYm9UsbMCM1q+LgrPoKW+t9rUXAibvqLo2fbUPibD9Ca+2vVWHWpd7HtacNrJp4NTu5N3tWrcEGzS2ESa43Xtqr9j+OVPfqSorNT47sn/AIMYYYGGJ81bJXvmPZv2zW35yIYJAARioUKfDIiQxDsRgzZuhv2PjQs+ecm8uJXMiESBnge/T3/MnS8wbjp8MN3HSTLSI9yFO5MMkld4+PPXnVSTvnj3ZzS0nMhIS7ufifp/75rOyHhumoQIByVw6G7fHI36z8XY7cYDbTr6Mzw1mvDK9QwXS7aIqF5h2AewbmRJlujwfWRqkwIjf1HP00kn+VHz9EBxYWhyi651ye3CLaine1LlYD/DSpfOMdKnRrWgocACk1cFLqvtuiOxVchJ++Hh9TIYdfb4xGs/XlQMtbQ9p6E3vWY1N3mbncrO2rsuEnbZzHGsZbE7/+OObk6Y0wyCc8X7Ytp4GmfDJMOLym/ThglYTCIg8eNDAKBIOjT48y4fs/B11CkGLFIExay6MGt93KTzm5dFpPThlFeVTvfLuBjtSq7ZDlMwGfadCTgpmAK68KV14OGIOBPoUkKstkAGM/TGVCEHLGTMgPPog77wJ4LGxyAgQsWJjfs6wHA3J4mBnqrVYX2ZFLaMAEXaRFrWkz3eVZXUi4EZvQusiaqpd33TE0GCCtcTBRXXJ2+yJrLCa5C1ab7DxeAp2OhzECgMZ3W93hYOO6VzXvqvra6q4mfomDPYy/19DH9ylkfQIb53OPueeYly9/hctJh4IK+iq4nIVTc1Z5OaA+2ANgjht7N12bXeNcTWYmyd2B5A76WO3qRrgbTIyGIMmglHYDuENba+G7++89dOWhxVBUNiiZ9Ma5b/pO9p3lWkG4KxA5lSllRLjdjD98qvfpd+bvkFDRReDA+LLHYq+rs1/Bg5B5dCpSGSmnEf2ChaMkZrAcD1CVkEU7lgRHH0UsI36giw7BgYAnZ1SHiNwG7tC15TapPmOHk91BW/VVzYWURkjE5pTo5nu2EUcWjUr2dNttG9/0Ntzl6w+6Wp9KhEApltrzEXro5oBCYleH+hFGmi86V/Tok81BhZoQTW+KNWh5Lw39tncdtr0dBSqkbl5PjYSSkZdfywuuSA13sWg8xl8ijZyA/vmtKKZqQJSlXhno5EW47CpBgLNAeAkgBsAckiBmpuTs51kghSALnrmb2JNkboiBimIuBgC+1Vb9jx6jlE7Foj+UBP/zouy73xURzNwQixzZnnnWReGEDpHG3SuGDTW7hv6y7uxXnU1Ag1VBcc83FshQrx9q6Ixr7ewbaYLrcg7X2Gu0o9FGd68HNCb5rHEu9UjBtKEi2exJ27o9SVhEzOfHeYuWLHr58lcIkTDIgFhs5dFoFQgrT31vEthLTcmXzbn8eZs+/+bsZmiKnkUENzBA7CW6xFTkmVAGieBCbS2aFOZhg9flr7vhvpsOXfn6WNBi6a7HqXNP2X7eDt/ofXW5lkkpm97hQXSPBg+0vugf58cX43YiJrYt2XEKWuSLguYARuSWJJhZ7iDhNBMH2mp/wNBVOXukoPrUCpmm3gtwyaW4EghRheVQmdUxNOYPJraB7KRm6ml1W6jObV5Tm5tU8qt1gvDhhv0RzDaRG2vY+ehmhHIK8L/Rwt24o2s9a8xbf0TuMCQK+zX7Yb/HpDAEG/KlHHZFZ52f+7hIvNBATv4dVbvPgIqKZ3q9UfvkZyYes0hMcoTvIsCnGCmDQYgGE4QTzsCPLi7fY7C4sbVcIaQI4PJfAUFPewoQk0jT6LpzCf/+FbxvqUrvXabxhQ4xwBWKKzA1EhpNBGlEJGUIMqfRANGD9YjgigT7n/iYP2nfogWJBCOdIjd41avCbbeRKXE6ELJU8LBCFNLFNKhplmqzYGhAQl1eNSspfRelfPPxjJtXddQP1R3ARKuCVqBTNW5YKz9oFM7XVVdbhU0Nmdytf22GlQ2u8Q0OIGgzcB/52VCbLLrzq3uv3Bso7jLB0v2Tbo7L5lz2jQ3+4/sbnHP+3B/f2Lt5+/wR8zSvVLvzxHknvWPjf14eVtBNNKJfDF0KyUcqU3bI6len21900gDd2Lvh1+Gql/YPOGvFmQdOH8RCelkMSteGWw6c/9LTcNpKW+3yZPC+QFu9XK94OB56nd0QBMISx9a3/B7c9SLfX+xTIbUKfcVOe0N43TItgxXDTkSgAizPBCmQLlk5wj+wXEbqGUiUTBF8v5zLtsSCZ2KvgAyIoAjKpllh3DVhhV9b7BvrCtQkxbqG/1pr4D7hmarFfIvafJJD/YRn1PBah0LRJYZb+OcLcDUrBZz1ghVaryisXi/8+P1x313gGlgf1bBxbPuMXZzcxwuIZami9hzaGr5L5QapJB6OvrBOs5XGD0JUz9nHqmk85Qhcee1fifw2QsKOO+OjH7YDD3YIcvuHI/SlLxeu8Un0Pnr83hDeUpxnOmUk04AkWNh9jxWXXDgIKFTq2gbmnnpa+OcjYVHeMyrIZYSsT/YIwKSYBTqikFEAgzFWz2R3F4M6AGHDBevxlrQ6lVXHvOcuQSvVw2iVw6NRm4U1DTSsY6dGqpOt2685X9Cou2rebFwjNTjaBk+EkZ00O6cPRFfNxuuot3qvF2k5nCIkEXn0rGACZF+88+Rn3/csFDYHKT236PT90fzzPr7hx27ObhQTHZzDCfqrlr/uX5a9Y0Hc4iMbffTEjT6n6JRFRsYeGUF3CchS7852+Y6X3nMJUkkRqdcEi8PiW+zGffpPZnFIxde7ONxxzJxjv26L0iQqYp7Pe64/5+X9v90/fzGA03qnvWnqyHISN0eUZIYrVl29s+9M4D950b/0/uUqXl09S+4FK+budV/Wujan7c7dLtXXeui5MTgAFwJTLbGei6qv/Xlbqa675Tq66MAmQbeGPXdXdw3VQ0n+p379bL57WhLyBwHWX2IaR9j9EX7lJ0CKpS9wbaoMO3/SbgL2EZjFWe3BquHQbW80HixdvZDrduImcyKFXunrP8QhHybjX3pkScZaA4PTaMJ+z8TnjyegfZ6M5UvhRRiF0b2iTyRDAljpCEeME6ufN6OJLsXATMTq8871p+yXQqMBmNKEwrnbbcfFd2TRPPUeQUKkNjBGpZXsttv4n2+niQLhshBoVWlLd0KiI0xJn66h33BWmXQdd4LOplzdGZfWhOkuUKlZ0VXd5v4ArI55ON3/WedZ1RwFjUkmVa350HX/Rb2MfcJ/YVBMoSJQX5JDr773kA/e+cGSeHchpBH0rt5db9nsLRfM/Tk8kiGHegpSzAW3PIv2yLjjbqt3PnOD7xlCVF7EV8Hk005LVp9wAZl5/O3SGxbmm8MgV/JLh5uKtJyYDIQJHb3Bh76c/fvysCJ3N7i5fTAe+6rVr1zoC4G05uH3eme9qveayD4xR1hJWaSZ+nv19zoznvvm8I9nhe86lJRerWdpJOFnHZvX7Ov42CvxEjKHgorExRb7qw6ZNpVyB71eeNsRb9VtZjUSmjUAcEOgCiimdyk1KBix8rF862900/pjg7X+iNwpGBD3egRMBsCjjMMZzGgUno84qzW/atJ+itgWm4TUvZwPBsyWT9rhONk2iX4rtfCSgFiZaaq7svHj9yIBOgqB5/xiuFT/l/qRJc8pEz3K4cDPforH7KoddsKypQPWiqSPdn/40BSu8lux4SkcMRFXRnf240sO0lP3AyAxEBHREUyceuM/hcW3wxkzmYOCgqEw6XJDyLdacN8118w998cbvO1Iv2OJjEFBnAayhg64BsTQHCfShaNqTgVGm9dDc/RNxyPv6DLf5cG4rHt8s2Z01XxI95PQqkNyVZF4lwugVRffbJFafaMRkmwikTbxrDYK231w+pxOBCBuOr3pW5a+tTixKizOJdyT3XPQgr+7pXdtUJY86jIp53RijOd4iGa34uYbN7zRYMqjWQafFknlxmw+5+8wvcN/9X4rW+maVpjzo6kfvDp/dd+RFRZLVgAmCQiR+uGcH7516i134baooOgZCfSE6cf1n7BQC1W0GpP0MuKwxzw3M5M5RJ/6VXbVLmHHlbhPyKwc/aq4fGIr5aSBfd34kYF+ti54FV4sMTXjCHn9AXtbV6DGKKVxe8/Zy9gnUFYjfNhs99AMrQZt6J5MNog+0HucdvoNb9KDPlhr/zpM3fj77IjEXBnpDTECaDTwQGOsd52ivIRZ9Bm8NbxalbzR2GPkXRqvtkHQ4QxOcszEGjiHUw6KD0XCHb+7GRdeVbTo/KWBbzoZSUbKch6V8cYbKAOtjO+MqNiXDKaNwcoylQmGDFUL070e2CPynJk22DSedNLgzxEwBdBx3Y32nTMdVKBBaVeQEb3gfXOTmL/nfTSsetFz777h+vzNR2r+hqJDU3VsTXdDgVmZZo1DpS5lwY467maVTxfvTdSnvmDMwqqLc0SrMh1tyYn3U33VcLabnbRGoE9DAE6r/dXg8UgFuVUZtobfcsIcSv3tJuLFK164xfRmGMoV1l1TSw7e8oA/TN1c3MNGMQfc0AOMyhzI2Qc8k8Nllkkx+FSq2e2/av/LllzyH/d9c75PEXMMPcC/G84S0DNPncpuAEIahm7Mrj1w/v6vmvOqxbwzEm55OlQpMgv38k4nWFozQ/xR76eSzPtgEMxhRBB9CtkKWyEAdKk/8TzY5NbydZHEIpzCRbj8Wrs+2WEIOQvVrFdA1UjKzcRpZTD1xHrc09qJ5Y1TZzUzp6GS6I279eZqYJntW2zmIScj4I78idgeFZOjBwHW2rsQSWw6D09/dLpz6ZGGSvMdRmBHPdLSWCfFcAdi7QuHYRYwZqE2cf9o3CE6tCKOG5ZGjR+nRLiQgyF89xf40538q3i+JD2TA4bAIuqBpCcjrqTooAswuFJsWZeQ2gECE3P5dLIK5Mtf7ptvXqzhEogiCJs69j2+YmUQzYteRjMj8pBivYi4x54rX/d6QQGEbMWHjll25dVx/wNbtZLNIcoNr+oY9FsHdJr9SJuBWhdMM7K3weU07vw5+GdVCz9C9jQ0TjYHNuP+adubgd0aS+PRJjyvfjTUu5GNTPkNSK4O368BrASyZKkgpoRQD5x63dJDB+V8t5AGhs9u+Lkb5/zBNW2YB6NBMMZeaiSMpGShJwIWQVoW2TcED3Gv/l5nLv3uSfedvCBfsMA3f/uqd3i6Hpw/n/Oz63vXF/ouwBwAApgbXr3Bay+a859FSRFIgMxT32L0J8Yn0n2wVBTw3/pvmMhAThsI5mAOY18Oj2CPQhgDUgP6yiwjQ9WgfxLqWje4A+KPtvgM/QywpAqtjtwj0YGTJggNu3dOtCTtEBE4YS6rvlCTnOVbE6ObaY7SH7UyC49lI8IEyIRgyJ6J3TfTfCg+CLDW9lUoYbe/sZ22TeZpMnryz5yFRUIjfJ5pixgvVzc3lGKSXy2GAZyGGzp8GF1185EfatxwKaoSxp6iTAmJefzxpaVvwV9hBZYnjzxHFEQBdBeIXJZsulB0SgOQVQn8oQnJKAI2OltHZgZG0LbactUXTgqUUmZrGZhg3z0znH1uBkUqpYH2aFFOMoczGGCrPvbB1O4UEZMhqy/c9L5PfTxus7BLfWpWppoNnul13EkdP9FFVtUl4qYhqLiaWt3Ftbzu06E+TqeVlKp7sg63NQPT2WCRWoasO8XYhYRrBeKtpvBdysozz5vK7moD7BErH7bd9HbJ/k6ACQRvmfv7/9jk1J4CLYuYFh0wcwtRcJlNJdZERjfPMAWsNvS29gVHL3/fOXd//0mr9kl57BKOXH7kjvnD0+0l9j4955MpJAeEmFJ7kTn/bfXx8/L5ZjAHZdHyIJgbgNfHQzfTQqaDVhR0r91zWbgUcEkesrIwZlm0zIJoRgGImmEyRnwuxrsH1lnmADI4LtAlYB9izohRpqCBrJo4rcxyHmyCViOVR3Sbbdvn2UoEdUvtkik0CbFP3wHbPhqPXH8qhOsPwDIQez6yEO7AociUrt21wNfRuWpQBvY23NNhzzNMliZVx5t9R+rvkEqX7IBQFaIn14KfXM7LrytGlr/8laiUXsCZSOpUQRAyePE4+dWwdMCbzN8IhuHmTBJAULLgw8rPfhaIsSgiOgFnBND75GfNzUUDM/VM3qeCUswzHbH/2lfFJz1NEonMQ1FKBecfeyzuuKMOLnQp86GDb1YDHEFbyCBq3Jjq8FbDW08EQBjO4GveoGOGTwMUW1vOC3UBO91jFptNJVrjiVBv5zGrWudE8NqREK1/odP7KZAlBZw/ZeWTi/RfE9FPTkKnzzsdNtcth2JGBEiKsGBmClFIXqMWIUNwxI208atXvPK8JT854r43UUEzA5oEP3rlBymjQNlZc797ae+XibYmwkCV+uTpJ3575be3iFvCmJv1vOcWaPGA/GVHrzo6JL0pkj6MR/Xet8JWuPVgNI9gTllQyBmTX2jCiqzoOEd6CEil8Ia1K+b7SxAHGQTiEvzmPF0CoCevCWAeL4yovtLSOLnQx+apQQFHa5iN01qBKRHVWBGw6WgFE/OU89MDCOyN7dej3rz1RuTuEJ+4fTL+pUo/O1M9S+OdcaTXb+MFTTPz3+qfrOYdK7p4TdTaq15lj4pNvLV/nLLbopTGS+dcguUr0ujzV0H6ThlIKRZ2JRrcJCY65SpqeSH519fpoGvlw0573N566f4o9CUGJNeG0Pv0p7KrrgBplOh5cTzMLeuJztzmbdw/+gMonTVyy4lMhN1ww5wzviO1OKR3SVluoBY6WiI1Rxk2GyhUCx/NZqrjNcEuh9GlN7AODjawgONG6l0mwkHPaZftu7u0t76E5KD/v3oCzWwgIhx3cq8Tqte9ywjXMr6fkfM8fg6Lx+oRGaxPMGe+a3/3Infei2WQHJfP+SWimzEPU+ZREtkr8l0ZCLmJihkzWTzgvv3fvvyd2/W3H2TmpBZukkCkwgtWPX+/OfteMHW+bBrOt8171y+mL7I0+iVbLAjgk6effEP/uuPn/NvV4erf89bHaK/n589+0eoXpf0otT9Dd3Px/5k6HbIsriJ7kZnggNxzD5T6ZIADFuEcP2mo6v2TJ97wKV3HiIOQMxJhOVb+gD9/lp5gGIxyPsYeNXQFdtOts05ZNfEx2rTCHWRtHGmejW0T8UiFJCn0gwCoT9gTsfPx609azjoKsIqBo/ptb7WZnvkoYBrIimyYwsh7YjAOWAuG6kKdGoi9Kipi2+U1DI9meg8rYIuDfXL4zdm423pEWCwTc+QRF1xNpHwK/HXaWVV2qWj0PPpM9pcgRBip9tLP8D9NIV/9la8AziLN2pNMwRbfFT5/EgA3pzKHZak7XFlmfSijrH/YofmChUkJlpxpk9Bj7sc+6auW0wqoXjQfEXG//XyjzeecfRYYjJJiaZZhKcLCYYZpIUsZRwXOp9JE0sx7tQb9NhBOg+lz4pMjk26rZVerZUMDmiwfWNGp1rl+2krYdMFDzf196Gz0MBGoVVsLR9RmCchW/zoSIFjdcwJeI4BsHGxNwEaTjmH88eBbHnElKJ4xmrub5Oh52DRuXHzKopsXAO4NK2nRkWXwwuBYXrBCqS0edPKRq7c/eun7nr3yWQSBCITSIXOQu1yMjcet/Mjzes+7F/cF6ubs+nfNf8cnVnyCpU1d6WUKSG9e9bZiwVW4GBbG5gQjdTfvfM68563SakMOm8opFpYPREihiaGwF9aMVmliiygrd2h5lpKf8zoEs5yRMjECuEBXFGGtM4P8YOrxoflFzXOQT4BTxZ5GvLK7xAKiu+3ChKqfgMnRPd12SKesMPWmSXwG9tiSm9+pJYMZrpzm1kXUta7q/srEqZkvZNeH2IKNi/aKQmw0XFJqqf62tjB0kQSOqOObL5R6UbxQw/qOrCSaD2nSEX73l+Hq32tmpbnOcalVPn/cHAhj9j+Uc79nYJedUiStYEXWBDl1zNF2xx0xpH7SPCgHAAuS6FkePN96m5XHfpApM5ZeFDSFqbPPnnvWWeZWRNunNRaDzdtw2XEfXfblk+/56lex5x7FNCZLDVYknZkJESGYuztDcQd5uVBpjdlpZX26lIoamLOJZa/uyTYYE2nVl8w0yM9uUHrV1fIaanxrXE8cHHOXRoo1KFA2oOEGWNzRUr/V+azuwhg788XASNIQRBiy0gzc0yKCwApbCmAgFh6nPOfHDd9/z3svvP0nz1n57GI4YsAMcVXYEDL5j8N3mN7xpatfGpg5zZktmvvVz889SRAVZpZeBQoE4SUHk6LrExjV3Vz87PnPvcGvIyVYinoQYXJ0UFNN7JKpIm8zrGsVw5STnSoxv+YNZ+F8JOHazHQQa9w+mwt2XfIBu0iN28RVVBO6mvDyiQdcKxRjgc+i6JITWmBzd9W2+qvxB///AaxkbjVzw6dndn+Yh2LJQtJ8kBRYFHfH5uwucKq168HrmxBR07Da4UrlxPymVAX35DNQea9G24jyn1k6jPMuTVmYIV1+6xIfTo5KrBrKc4M/uUEHvUISITCXIEQB2eLF2be/A5gVedlGBtFMbplgZAz+mU/D00BrEFM/Cog5x37ILd29bgiuQDIjVhx4UL7jjo6YP//5i3947or3v7+/9dYAzGFuYA/qe2oWj9bLMvk04IZkotgudW+OZ544fzdo27sXIpsLjl1AyZohIdRbIXTZ/9r6qQNzdWdjbYXzNOi06r7oLkb/dfB6Zgul0rwlljHnIM8upeIEQQ/JH+q0QX7U+JV22t2nHb70jRhkmBYaeVWm0mJgdgtgAHTUivdvEzenHOrDeMyGH/rIhh/WzDI5ZQgSghBSfKgqVYFfZpc+b97zb8GtIfSSbzut3xODgtf48rdaoszW4u4BoBLSEE9RJuk8XJzk++XMErtNLpPmtZHEtdF0mokmR80i4ElS4PKBRjwOh5sBKx4Tzfuvm7Kj5AYIHpTtie2EZMQDwFQtFj0IsLpcc1AJDsjCj3af7eERRfeYO8cgeVM00hqArVYnjxGqaaI7QyPtVO09HPd60Oj90XyEUcK9y3DRr5my7NcHr9vmVqniQ87bcPVhh1rB6hMEFIhoxx7jK+41EC55EHqSDCmYlvJp33v31S94oSzp6oDH7B0AACAASURBVFMqSCQw7/gTs5tuDQo0AVlkDJaTnF6wcPlnPgUBDKnpfNmb3nT3+Rdwmy2dicTywpDaZBm95LeEqJA1I5iRCXJisa8BkNVRICPVLtT04jUAqWrmXTNE69g019p72KWtb61cUV106B3F8g07aZjIm6MbJ2IsdDPBarpUEBIrW1ZxBXghWSxuHxDcMt8m5SLUXbQnbvg5FNsCcBUOVU7yvDk/+tH8nwwGXyvWmNxcm71h1RGwQMsSN3P83M+9YuNXLMmWgBDK5NE047I0iZbfafccOe/NL9jgBTfb9cYolr1MsBx0c7LXipwanp+Vxd1fv0hYZG/ACfslr7oH99ZUzWpgUDF91LQZyjskQKODG5bPTLKjtkETGQ3vPO2ivlJUECikWGB6d+kJ3GWdRVTrR4mw8i0IABdsjKfuUPQeKwKuggSK45Rrtb1u/Kfuex1z0hqsrDo296k+qLzbQmEcZhWk+rjNyYSbQXT7/qW47n/dvaDx171Wi7qqU9MFutMOJOQiC+d+A4QQ/uNblgWqz2Bw0XJSDChY5TC1+gtfhKWzkMQaDlhYvHiDT31MQGR0WiLIIoIjTh91lBUqudyUeiLjvPPO9TsWm/UNIdUpzABk7jB5iFMxiJwblLeO3RO1Ps1tg+Oa9Il1romBzc3sSEMDINqSmyf6kbYae3a0s+qCsbr7INS9dTOuQoeA7dlGStcdSd33VVP+a4mPJBkpoxjD4Bqb8kxlY9oAiz9r5X5ozJ28cINf/GjDHxGISMosQrhx7q0v3uLFh25x+AnzPocUDVpZT8rzI1ccudfqPUtCVyIuyH62+yZ7vW3+W+6xO1EVZAogLuld/JYN3r77/N2+kZ1GypkJRs9CEj0qA2luondhpxqIvS7l3Qdykktx1vTf4g9n4+JGUqfVuUpjGc8NHEGzNePorIqhbk3HmN/1JB9KtNUNNYGeKD5XuapOOd1MceB8KnbYgpsUde/BGVgnGYV1uIswtZQkcvrRD8G2m5QgGkk6Uy6/mtrrylM/2v5T1rZnXjV2y03MkO4WqjN03YxnTnvTy0cxllX2oYYjJOA//lXZ3oNkuKl18qKbpdBbFNySSNeAKIZw1lm2qg8yhoDoNMKj2HPIBKfFVx8Sd96pbP6kU5CR6B13rJatYAbS6A5NgdHAuMfey1/5d2WrVZamIwPnffQz+UsOXPGMp65+znOCkP3kp/O/sWjq6l8bgtMlDzRjXwh1435zdnIXdNXcRVg3qdS9Y93Zbs14nlVkYZd/ToSbra+tbtmxT3BiBs7Ig5Ft6tTrrcL5kcwWTAq0GW8PHNlyPBinqsse31X1U2TW87haIcKpTMh50bzz912+32BETS89YPlLPqBjV/C+6m5H7Is/udHHn7fseUEEcXd21yfnf+rrGy4SLLj+q3fFFzb8whHLjihJZYhRyCC8ZdWbXzvn9XANsrtW2IqvzvnmV6e+vme+x65xV0OQ4q32P9eE39yB24ug0hii5VAmuiyHjDFxxquhniEBkMkjRoMQYuTkrJMUByFRoIIjgvgZfvlqPHcSw4TxCWLm6xuuAI09bs1snuz8XhT+ajX1Pktp+WihRnXh0EWdtKhhF+ULiXIh21abPhoPu2iY6lsHW0TXbYCloutFEPZ8GJMROKDUuySJQfROcGe8dKjRZkCVLX6jI3vNPtUCtgZPDroFvXhTsmbjibmEY0/OXEMiZ96Xdy/XpdckNJbQ5zqLrmaln1DymSuWMgYECVi4gIJCnz4HijJQlrTsEn2n7Vae8DlI5eVBE0XPrrt+7te+RpuD2HeDIaP1AVBhxTHHsliSp+KKDMwuvWzpGd9evcMjUuWlL+R/97cr/+5vN/riKRv926eyFSvT/l3BrGkWb/CyGmdZ0LmVb/B44NTQEPCCNiMJzCZVuks5pvszs/UhG0mP7gKqJm5W5xXSAKrqgNcIMmtGgePmC3X9hnV9keNNjoMDcPRpmSU/8AgQv5/7x5QtZp5kWGlUy/a/70Xf2vhbtSGMruvn3vSJTT76rnvf84WNTz5x/meW2krRpL5sA4/+mQ2PP3D677ea3hhyoZcEMQResPr5+698yfenzvaiMB8tESrMrur95upw9TRXT2mOK0B92pQ8l2LMItSj+oIlY72UPUjvudVEGzd+vyM9mx1f9UCwVzRkjijG5Bd4MX57N5Zuhg0nAyONTGPdI5w7OyxwJMnRMbn7z+uhmzWG1FVn+NbDMyD1hpfUlwluj8f2F+G31f2tmwB63RW5V89a2P3h0qC/36HoRnaVTNVcQ6OardLZbMYevbvxf6uKa0YMOCbYGmdNvSmFoLKCqUQfwn9wBa7/X8KQyBo1eoT91b9QTpqNmqs8RQfQn29nMStY4aFCj09+iu/wcHovLQELnwXmgCmo/9kTIJAiDDBzJue0Oe97L2VCH0AGYwAUybDqgP1XPXlvlf3iRGRyKN3nif3ttwcCE0FVDGZa9k+H3X3CZ0WAPfMhTVWXbEF0iJrpLs5t1jah0TW0oXRV7cjD7DVYXXzq66o5HQVeI0feXPUb72esI7eamwdbD6BSq7WJF0AX8300+srWkZ2lMW9AurYLszi7ZP7FAMyFIiJQANzwtrvftlG+YW16EhE8W7TRN56+zTM+tvGHlmUrKafM0JOvyiyssKX/FS4RAtBL7Nggk0HMFETGVFWHktgr2UViSnPcAE7Diu4QcI65EXnSjJECczCQSibDKfSme5VwojlWcWwdLDz+uoOjO/ql25MJdh3+5wf4+QzIYF08jtebLHjNg7EprDoJJqF6JSmkbbJrAHANyqpxdNWUpZOmexMJMTUpeRTwGHsIGGpcmR4EWC3U1eBLNwC20YbxqY9MqTgqNFnF4w4tCWhR21Vc0av3Z5tmyzmh/Dyi2WqAX8N9HDMZiA3OtjMXbsVZEpIoCY7zf1UYe7JwGNW608VK0mwEiIxeiBVfgOqwmN1xO888syCWGASYDGR+4hew4dwcIoNRhjSlef8Dx/af+mQxqSPBos2Pc84+Z+4FF0miRVgQoqIbp3zevJUf/VhirZh46BT0gZDa0Dl85tPieuVzX3DfIYcQOSwAeSH8MqpU0xeDeUotKVpifUS91Io4m2N2MObOgEb5TrOTePPsjrbWxY667Ptp1oBGkX6rbfrIJ63m003MTxzDTGgNh8aMgg11uHnirkYyi2bVhzj09dFhospjDtGNZ25yVrIqKVGQ6Ni8v9lLlu/vFlK6TgliUk+WJ+feZdmyW3u3EAEwJctQutmUQzv2d3zuyhekOCzOSJKxJCw5e86ZafxxK+ZIQzkXpkQs1xAARS5i0A4mEQyslVJUBlb3um92rKxsQ+/Y0BD115/tUqJrOa0A+CmvLFBOksAlwzCmOqk3CnzzelnVpESQshqjAaJShGJKeq32ALb5BDXwVUPKKiGm39lQGAZEIJbO2U74vnrURtpgQLJoXVWTr3uHxaG6GgHfcSF23Kqtsuv1FeWGJCZvBVuN79J8WXSUxpfvK7WVxotfp4Ne9Aclq75Vq/HL65hMACUUA946RJmOzx9d6HqSbpj7gfeZAIehKBcQmH7KU5f/5jq87rVauGlOE6H99p3+2rdWve2tCddwMGYJgk/96ztyj2YpndojZGYOrnrDYf0Fm6e2cyfoKqNqneXpVLKrSYEjsqLb+G8eEWRELpphTjrgkAofJBlMcwJklKU+oeS5VfGrHIcC47ikbhafWD3sEhE4kRjrbhDQqvVuZea65+pgLZk4jJzPAZvVhTJsFeY3g8UGxrG1ON6KXDHcajB4CoCYp3p3YAb4GZucnuQWEJwDmMVjFn9wp1UPB8wQUsYnMU2Lhh5UmDgYmAo0RfKMLCiPxLtXHAXCXIX7CtPyxD827zgDJRlC4ndbvUK6yNXXoKY8PvLMrJ85LBJ5QKdgFlgqkX8A/DJdu3IgMCoyJJPzY2ycTVoD4irs0UwdMM5ulmyfYcfrMxipvXSo/7RUM3fQwkdha5X6Y0sNr+ue7+O6p8Eq/cMK/sWIXbaCfAIKqwwoY19DQ4jN7GRbY/N+HKedh5mtcfzqlUpx/ZtSk8zixzcW4Jbc0wetgj+4Atf+MdHpM/h0XSpJdxwKJ+hsBNz8P9khfz/99W8WrSSKYqBBm2+2+oQTVp/wGarniGBITZfGwlQUhMmc2PCDx8z502LRWIRPG4Lk0tYLlh91NCAgS6dT5oRJ4F13bfLxT/G6X2eYs/xJey9/z3uSjRbpgi14z3vm/sfXxExgf8/HrXjIQzY55xwBnsCU0gQ+LQsyBTdFNytcuIJYbY+vo1gappPml7RKviYCqeqiv7ltrcGXq641EmspNWgNrrGJEqi67SeG1WCSRqruACYK20de2F0sX+f2PqLNGhnSDbkbIViKT4BdNf+qW+feut2qRyQSWKU2mbDj7vjo3z/k5ZEZg3ukmDmQUUq2JmLylnPmoEm5oL6F1937qucvfw7oyaaXpazjuqlbvj73/4gw7xG50+vW8OPnqlWr3vC9N2xQnjEvRaus1/U/gD8OYjDNXYNbzuEVB+tpLN1chbwc62Mj8mjUm1dTa8r3YqfXtgbsYAKGg0/avEu6jje+iQNwTu2kh/wKt5b5bOtosHdYB1m1KnqCxNc8hU/ZvnZDTizCVkE0a66JiX8de9VQ/W/i/Iea95q4w/EtG56pO35WlyCA8MUf8tLrYEwt1WWy8rpSmR5M3l2m/JFnDCEiZtfelF3763y/p3PefKUvQpEWAEip1yGwcNdJgRgxJAc1Ou9cMv+f3oi8L1rpQa7MzcilJ31eO+wEKku+jIDJRNhdSzY/4KANLvq5/emP4Y+3zbv8V/POPnvli/fHvDnZXUs3fuc7Nzrj/8qmCIpxyYkn3nfYYSt2fUz4nz9O3XZbwBShnAoMBu8pFMISGplCdQQ5LTSoi2br+dnMLaHe4gH1Iq3m8ta4iQMaXctRb6SORnOs2Xo9dLFO6FIJrfsi6nywuoc2tnZodkw3mkw3iikClWBGiMk9yP47+8NL7ntJcvskSJPTKG0Tt52ruf85/xfmhR+DqWSlUryUcjATYW6GQGV7rdz9S3efMtDLVw5Sr97klbf1boMczAYxOJhNjHqXTlJ0dhAde3IArQSsG34NLI1YORixDdRDtOC5eHxq0UzUYFoAVkDGyGONuUIOTV4jinI2vLb9Txhy3Jj8krod1oOqSeUj1uyW8Ftw+3m4mmXOLNZJJda6ajRaxU5PeLgQJxlyqjDkLAzQKzboUme2czY2aNUa4lDElVoXATXOtl1U+V69wiwtYpjs2mWpt+Li61MSxsC+XY0dJg8gd9UgE574EinxRDnP+uH8vffJvvc9K+qqIRHmKVM2E4ScgKVpgZa8aA1h/keO433LAIQUz6Fo5m5Yvdfe+QtekMRTOVW48tGzJUs3P+CAqVtu6lNBwQwwhptu2extb7a77l5w0AEbn/19Z0ofxYqXvHjVXnsLWP2sZ99+xulL3ndUvuVCGEPhcRxyc4f+H3tvHm/JVVYNr/XsOvf23J3upLszkDkMIRMRPmZewAERRUTEDwRiAviiIoNMfqKA348XkNfhVVSQyQgEiAoaBJkHhQASAiGEkJB05tBJd9Lp8Xb3vWc/z/fHrnNu3VO1d+069yb38MHxEG+fU1Wn5r1qPetZyzHoa0ttnLBI77HE+B2DPnX2KJPlSps/JWzQW2s6i3GBb7WAT+y0HPFZ7CRsVegnEnjS6UCxOmw6Rzy236I6bvYHDxLmg6MNjbRL137l82u/ZGWYIKFSGsAYXrD7f/7hna8xCoOfeBBCOdIKTy9wRN+Zg9Bk9uS549+z572DAgPJoBIyAG9Z+6bvTF0pCpqI9QEty18Zper0YV1Cynyw9yZlLK5IzAf7kSrmvoIrBB4MpobBccp0XmIVH7wW2kDCLMiqRkTDbUL1RNVvPnXEMHZiShVXaTDSWfjW8I4H1vUfgdNAGGFGTlzNBphMzDcQYQ4Cku93BK59I1ZKAxY0ydsy1r7IgZXSDY9a4olKEsuxjj9K44BY1yA7xZevxs+/ETMHh5s3iMaciDOuqiNufWpveNYXmtdSO4U+IDzlNP9LT9GHPWLuUY/UozbJ968prr/OffbTdspph1/6UlvI+fW+9pU1T3kq6MhZ4VSIwoWaE9116WX9004JebSAC3tLoGt/41krv3ipgxk8SJVeAQvOF7r1KLfjntDuZCiwZmr7f146t2mT2VA93Hd79215/gtWX/kd4wrDbNgDIZOHToYZtKVZddN4H1OsN+Khqqgr9i0ymtTQFsuDvKifdI2v1RkrZseKmh1UusrcGHBZr8ENPx9Z+EggpjVFtQxDl0cSl6tzjXxVex5DNY5zZAnpyRqnHHwuZnOAaNnpoYBo358ye/+P3fhvZqbhFLRh46wB/Mcj3v1/jvirfTwMKtQUIWVZ1AsxC3FQ/6y9z33TPW/kPBk/z119avVnL9hwfpjRlI6eap6FmW/c9ti+qu/w9Fcjfyc+qewcP/gqnCp+EoY9Z/BDK0TDKqz4NP73Y/BgwBk9QhO9IdUXX9lqjlFAzKsD2nysSP5CkkXAcvU7L/Aw5k7Fy36IXcEAKfRJwCbL3n3iNFhGiNHMSnbntKNtZVGDwAs9ORuRljWeIsFPw9eAlww802LHNeF0tfBJxEYFE2l7Uo6uQ3WlRyYOhbbyFJ9HdF+4ijMHbbBxAijLLGz7EcjLaXt550p7KjVzzpnftk3++i+dyRRDKaMg4LccOfPV1w+I9oEBsNrq170eEEKJadG+Z0Hzzor9z7mgf9opQxsQR3jzJFd96OJVX/gaqGaAOCOmzffhKELA3blDpGDfWc/B/O7nPX920xFlKbvEBMXU9dtWXfk9kmaz4oRawHvvRAyiNgf2SIXPaQpLl9jqEKQ18C5Rn+pUyWrEXq1/tNaD0najmR7c+QahCS/Tuq9pI2MUM5GqO2Ohpv2q/7NKTTUixZgmbMEEBL16UCCOZmJmNC/s4Xq5+v/d/MbX3flHzgCYCqV0biaA8+654LEzT/yb9W/797WXkBArPPo0OIE3PH7fE37j4LN+7sDPlme7ldV4kLC5y6aueOnal1ALw2FH5+H7KhSLj5pRz7CRLV0kd173aFX1lTMTtaz5ZaM4dPCf8Cw3Y7NfxLcfjdMNfTEaPIOJ8sJBp2I0WtnM9ua+ViQ0UgHU5BHRTBQ1P05aI0mGPB6qbC+dxvRpOOY23F1d3Ul7TaLIXQfG+AbD6UeXRgZk5IhKc3tCM781osGShQvMkcmjNlcUaTUJVDXOVNW/0vrPKaRMLGBpXsqvXmME4cz8ADxauOAmyqghs2I4Ogtnw0igAoH3psJpamHsqxRTXnwYJf7wtTjyqMFl5h2dh0194CJc/p3CqPQAFUVwx5pdu/Lga18N9IkiCHQ9DOa4a9fqP36tF/TgVQqYCKwPtUK1rxA6OjPPXgHzuvWoPa9+VVCmUtQQ7KZt/Z/+b2N/31k/1XNQRTGzZ+q62wtoX9QZREThIR5WpB2zcgyQ8uFOespWedx4yKkVHo2IvhcZYzKynDouqbNcXYN6YoW5kWXWcVJ1NRKu8XVs17huzdagwbYgPKOiAA6bFYAQc3MCZ1Jw5cWbLj5+7ujf3PV8lAQXQE+TUN485fDJf7HjL1+36/UfXf2RG4qbClBh95s74Wdmn3DyoVNsKAkt1UCecDB8Y9W3n7vhvH3cQxOgRz8LgagMV3SRKKoV7LYurYq0BoyvH3g3gE7MFMtEfLC0vxo4VxNlpCr0K/hemRFjJMTCgWWMwPO1J/axSaYqutLK8iV3OcykyixBlbX9Vh+QM3Hcl/DdklUwMUxcOuHkASxCzKn5gJ3lnGMBr3Q1hikNfZqOcYBc1jgvY1E5TQYbeR7uNCzQALIppad6F3DVRXGE1hrgRaMKGdxRRKm37sCVN48S3TZZaL468tWtsBrHucqw5ILTDg0GEbhg7AtxouYFZl7WrDcOY6vUIAoFZPrNbyTp2Q/7luaNQitmX/n72HjkIFWIBi/qVLDmD/+QB2fECqAAREzF0byICgRGgH1qEbRud73xzRwi/AF3ueK6mw88+xnb/+niSsOCTu/ev/JjH9n03vfpju1U0cIVA8JfKUKDGopAbvcGPe9RUVSihIeMBsNGGqZT318r6VVfvRHeJQerZcbgVM+xoZtUgiUCUJ1sZN4RDBRWXlUbuajGbURTl2IdDzXauI/QLSISfJ6qoC2WujN6HZmaFeX9hK4HM1NvSvKtR//Z2YfOfcjMQzy9M2dQwBn6RBFuO0f011+w5/ks02mMpbTaKtFlABTmAP/NlZc/Z/1zDsgMg+eVeRUn5dNliIpmrDezkZbrBJ4amcXGeu6C3atBajaYTJeT6LeR4WNA7RhwJbbdgh3HcrOzoTy83JT6oMOO5T9b0GrebjkW/apaR6qMuWxI15EBMIotvJG5iK2PADgTx5UdABNXG2zmaSaCwRokyhA9p+cet/AY5CjWNXoqMM8PbWGNKs8OpG01Wky2YlKAioH7QAzoVaEmwS7wi9/FHbsms7g31BINlUDI07znVH9EAYixL1LYgRn30t9b+8Sfmfr618wkxMyuefnLix07GZ5sCNL1ewDUn3bKzItfoqXb4cC3WHTVJz+58pJ/kwHEUcLR+nB0RTg0jgV1hQogPHjuOYd/+mesQiuG1oKDpx2/95efgUrIFoHD69fted752770xZ3/93PgikIVVoR+LUIBMRf6DF2IM0/4UbWCpLGtPpFs5UOXwOOclsDM2dObgIU2EzED9+FXaXv6BJbNbNhs3a76SsbO89Y4o5FpGn1KF9xeqK4wQBz4W8e/6F/XfcSZM4DBh905M/ggPyAHzLcShJmnR0leKQdOJwD+bt27n73h2YeKQ846GFNlXuxLMtfIjor4z5lMpEvlHbznC7hCVK20j5kz+Bq6yhkK077qMcm8dhtPqW0zpoXwjUNqWoavD8fJBXohmoCcRD2Mm8ih2YLfE0/cLG/6ZQ0mnM0XleVZJFRtF6pAp3WZ8zCfZGhZ4IIYeTaFgTdaPyxwn4hYP9jA9IGDzkGOrhJBMwgMCgrf9ilccdMEnlqNN3ojhFJ9Zk1kDLfwKCRES7NVcQLyjtuKiz7Y237H3MN+igdnVr7sZcXhQ0ZhAZoDVCBinPnLv5o79RSUdouh29LBdN35FxR799D6RKFirvRul5AWRjiopwtWgHrXRRf5jRtZ9m0S5kFBqR4OvVsa2h91oAoD9OD/ePzcmun1X/sqyICqGAxIKVKqWqJVwpg7ADJaAnNCWtKqrE44CXEXg07rn+Mgj+zGyXQ9NG24mkA89fM2NqKPUF+t1gyJ3k9kmG2OmpsHXAShoc+5z6/7fN/8Iw4+ory1qhIMkZ4cACgToQVbh6Eva3mK7nK7X73hFe9e/x6lV5qZH+SiZlmD5jxEjWHEkHPoh3fzyhGZyBQ7cTC/EauehseUDtLQIBsjjNDBH9UEkQRNEHOx7i6rWkgThJbU6sgYWZ/hWxcOcNaE+VCbjJW1nYeGm7H+H/GVe7CfcBNYH8Tkhj0rHKCnHKXsY36IQrLtUZO0nGaVEREVbw1ujpLxo2nB1sLPF6rjB9f6kM0aSjLnl2NSEn0C1W9vw6AzbrIwcs1xcYTHWowDTcj0oNGoQf0OgRiogve994iPXNI/5kiZOWCuMKOoD86KNB5+wmMOPvlJQmoogISKtGH1W94ydcMNZqT0ykZNZ2IFVSHmVFRIAaHKYv9vPPvwSaeGWuRghRzhQ5JPyLVQMxL90mgx9Gk5wO9+7vkGHP/nf21mQEEYTUFCTCALHgTaRty0DRUyVOpIGohn0io5TFtOumJsmjF0WjmnVmuMXTqhOWYi2njmV6t71YJjoi42Is1GXLlVX05zHyUKg9GZeYEdJqf//qh3XbPi+6+66zUnHjpJ4MKjoqgHHMWgIgaDI0sL5OEGv2PN2/92/V/tc4eo6g203sBtPFoErJdKGzc2lnLd6c7QOktN/C6DHTZBt9CQuPcd3KSYFRQW7hEtJbwGOFUrf6Yjn2slOWoYENkQNa0ZGK6ViKrXB9HkFqEj6+NhDiDkVG69AXeY9ScTyEwkwBq0zuP0owSmYHOLwaCUmHE4W9sD0Yy6amDLhga4EaRlyEFgTZ/X+hAX3q/nl1/6J0P169tw7Q+BsultMkmsakt8jrYmE3iRBgrU6JzTEGhTQOgENrOn2LYbKL/0RoFCqKtWHfxfbwnXKGmG4K0O2bVj/bsvVKpDT9mHiRN4Y7DLNwogQjOj0ema6T2veFWZPh1uDBYytpy7Z/f6t71t/Wc/W+zYrmvW7f0fjz/w3OfNnHOG0ZXiMHMg9j3vvP2f+cKaq64wCBV0QeciDuwv0LC2a5vyH9zTdvBIKrEyiQF09GjInKAreZPWXY0M9mnBewJR1X+uDiYagU6syXHkJ+r+FHXoVleLNyKbQd9+X11P+gBnTaYCNPrPtV/5xqr//uW9v/Lie35v4+EjDAMPES2fCggzC/7FvLt310dX/9s717xrp7sTgJkXONJUgy1fh7JdGo1lKt8zDTsSEyRsOCaExboGt30D1z8cJwMOJEO/Vwa0WhilnM9RKUe+tdaandWCETtSKel56yRI8IAMg7H0H2THfNaumNhu+YkDWOWVEzDVmVt1QSVuZFKr4bJ8BC1ZaCzuBGGx9B5q3g9JlO4a2S5beJump1BNhYV++RrMzKL0Z5isc0zA+jBWx15pSiN1IyGgfUcx7RugdIWquhD9V1BhnKVz9H3npmBzMMz+6q/1739aqSE1GcQa6uo//Us9dIBWQLwYfJnfTBWlkRpS3EOCrN/z0pfqxiPMDAxF2jLnedXll295wfnTBw+pkXTcP7PxU5/e9KlPbH/N/7PnvPNsgLwd4CE7X/WKtc87D1CI56wzWgAAIABJREFUU6iQVFHxAyeOLGv1nNIb4j6u45W98hmsHMzU9ein+/taZ2l0TGiEMiPTNwrMG5dfBzcJegYZ3g2N+CmGrlLIjCxU4dSrABDrOfb7qgfd4Q+v/9CH13/oKff84gPnTn/CwceddOgUUi2IPCGXrbnsS9Nf/n7x3S+t+rKgrx6go3phcZhzhZ9S9kkNGvkYvGtc1Rxkk5Oo03onaQTNaaC8/BUAANAZHvwvXP1wO5mw8nEvOsyNGit0wFXRaJqE2LyrydbIZBkLpCaYuQA0qXomji1D0ag/sWnIKi0Nze9w1lZaSHuIsE0LdnoMbzF5nknbiSLNB3sUclkE6sUAXI7PVgAqdYctJUXN45vbBs8dk+d4JRy5o42MxJ3uaA31JkMwolArnEDKXeUVPcNByBThaPDOic31OeVWrZx94W8bICZBy6AwgfZ+cP3q918oIAqoB2FizpXdUlQK0afBKFQ9fOr997zgRYONCjU9D7hVl1++9QUvnNo/M1fQmXNmUnj1JoKtb3nr4XPOPHj2uYCB5k1AHDj7nNmTT5264QcQUoU2EG1lsEeJGObYvIn6XaYZRLo4mJMSk6+tWWR6SRrx1G0R6k2FjUNvvQIYw22xTsOY92m6e66R64p9W593uBrqeyj6qiaQvvUdxQPmaGYFnIf/xKZPXKIf+zO82ZSeRoNq34n0PY3q/NwUizkrSG/mIY7qC9A7T/W0qdLDqSmNMafeNwaP1YgyW0+MxlkSh2D5qjhiUBouw/eIXwqMm9SIBkPFB6tBtB5begJ+SQQ8xSwVtA1INU5WU8dbo91StG5LADClnGP3Q0BXzTTMshMNk8ZglRkvwmM34NzjQ0Mt53Mi6x0KmuGZZkk1X35n4sLTovpu/kVL/mLrhwu3jgaW5lpm5oz4zs0yH2M1ca/6+DqUYTU+hnYE4jSKmQlt4FgNmhTWJ3tmVvYCGtRcz7wc2rP6ST+98m//GgAZXHCocKtf/nKaQEhvpBMW4JxRzVHhS3UGRTCrlH1/8CrQc0AJGAxwvXvu2vTHr5OZ/eampjw8oRA1JyJKIW3j+z80OEYmgxzqPQ9/qLkpACJF2dBIl9AqtUqaGr9NVP0ao1fq3Xat+TaoZcU0dswhuzUvP8qwdVEjgYkjoQI5UTaJfkYky6zpPMTEaiCZydO4GunzJHziRKFOQCOInrFPExoE9KLmDGoioiHdWQRQKZz6IJCnuam5oP0kDKLwnipSFIrQjbgYZDwGl5k+7dsmc4AM3hNaIhzQCvId3jxLPwAgdXRVDhw23+2exD3RVnqtmUFaZCDzXdLnMnJyFmA+y3QcVSHgndlDcOxx3IiyD2Di8MzErZBhEJJz8pF0IEypxlbCs37IPcqm1mr/guYZxWrbt7U3+4PTxZfvBSGJjW8P83nNsfOfCDQo3/1/Xonrtod450njr2K6lqHCNy1Vbm1fB0AabTCoVKw9PKX8XMpMJBEYodqTg/tWv+FPjnj0o6c/+enwsLPqQx+c+ta3g3jUgjgdAZkJQuItlU4EZpADj3/cgZ/9mfJJkRRoaERc/3/eNr3tWpqYeYU4U3Mm80ZmXPvlL0uAUKAGJTts/+mnT2kf6klPE7EgIxWhkRQJwb1CuiAXKxe20FUhMcbXR6kw70h4ToLTiiX2jECB4QJjsKweC53olByJQazDtfDh8BfrmCmxqmhzc803kojtlhhZUv2qEXo2bkJ9Ca1RnrGjH07vsgYdpIPU+bnCM0Z5QSnUSAcNF5EOrmULnfDCcHqLmYW2MaHlwKPWjo0YR5jDmA41VQnSdxAyVN51Rxr7ZaLcGgwOYtTr7PZLcUWAU4L5R/oBomp5eq/m3hLVyD+/8G2D7D8bSb8dvAfjWsP4q+2Zg/OUxEis8GD51Rzhyt+VAN9qlmJfVIMhOWnH25Eo0ZX+BGC1M1iAgrBTNg0aL9nhKDYDL8QZplaeKfMXE/Dckr9oya0b/XywROXXrodBSUxY7bm1DhW7gTYis/qdt/XeHalYejPnuMJtu371+c/f8OvPnL7u2tV/+qeJlacVAqOnd/QrVx/8ozdY+dQb/NvF4Ivd96z52CU97TlRM/OOIkIf6DQ4I01lZr9CqEYIzEhPmFu30WOKssKMEPYFImLs33POuXf//JOhJJ13RvQVVojG0ENMTJ1AAK2hxTmjWqauK/F5PcK5NUk6wdJlGnG18kbI0MChzW8iAX3y6V4kE6wTl0CrRUKnx5vWf7b2NHSyrRpj+pxfSacjTOarfGymfs22laJT+moSZXSQWlBa0eRwVh+SLHv40/bhr7nIU/9RjQ++aDTKMphQghLrQTjKTepBnDwNVhgOzemDNlPNGKyhWs3TUdZx5ynWWD3YN8HKEfMqZpxPknHCSVwmb7VfH1FuNZtNBAsBmtrlt2DgRQ5MFsc9jKRovQsvvqCAvAhhIRReKVQrnO996curH/d4T0n14rEPCxZaOvP0X5059fhSIkgAFKjCbfiLv+4d2O8JmKM4qkFAF46UmIB0s5u3EDCqwQlNIQCVKjJnSrrCqI5mEGe463m/ufsxj971pCdtfd+FG666CigKmK+cPa01oxzjhlbf9hxThsb/1vNqWmHBYobY+omXWWBqtERfQPm0JerUl1afpTGUMLGQ+mNGoo2xrnZq/Dwn1jChu29cvYTSPF93Vf/FVk1VJ+OGRnv9xK9josRYQSRqVNi3sA2gQsVgrbCmHf1YpACXrttEhl12s42ojVSaUxBsmJ6KMmMA98cWH555fwKwsq4KGM34oKNALf/Z7KNfAzrUPADUKDyPifiYh7cSv5iUyVsV1IcrPKj8fSPaY0iDB+SqH5a6vknyaIgF4MScRZdkWM3BWN4cAcMcxQXJfd+rFK4MA6mtsMIKKRRerJjbvOXAK34/lCVDrnMZAwms+uJnSGeEqARzdpiWjo7iaEq1gw99mBnI8F3o4YI6ehXXA1QL7atbofC7nvzzex77SAB7H/PoPY959JYPf/iYD168cuedFIMxTVfkVMTSnyDbTyt/+flGsovE3zEIkuBp6ugnMbI2ns+JEx5tllpIquPrUC8BWdKJMQkbrZjiO/F3opMxgYrQ1Csw7M1ccnCT2CH17k5UukQn6SFVwBDiI1fyJsVhWpFANvEQ5W4ROvHPtQuiQndr006rF7zZPCA0PBDHgnAGP3lVwgmMyjEAfs2UnX20kQFnVNV8TcdAm2rPjYXnxIHUeD2xUXdlbWvSWKasyrYaBPJmZqY2X43GoBRdcewl8d/X6XXbg5DIZOLo7gRrkqgPpofY/OJRMzEDFXpnvXA+eFMUopiLXhj0qhAVoT/wwufPbtwYFjZolgbB3o03TN+x08yLFubMdFZgQsIVKg5mdCJO9z7yUWBwHqVAYV6JtV+5VApDHwZRCqj9Net3vOCFgMDMoIDe+eu//v33X3jLM36NJq3EUkKJlYlfY/IdNCml0mKgxOolAhAzZeD50vJAo+b7crVm4KSFZQkFGDJczXLEXohL72PIOJ/gRJ4h7ZIA4vYLNr4hsdlzzvAhkBoJ3p68umFpSUXoNvvh1+36UjfWNKjRRgYdayvtpRutmmiq5nYuRAQwNalx+W5NdM6V5VRTgx6Co9fYyomMIpzIEqEYsXWd3W99Ca3MLzz1RxwWEu2gEgfdjfM2Lr8VpFebQxllsKLPBJL0gKhT3ATVffUmb0bAgf1JcnKvBtNmMlidJBed2g8XjhZi8BQDepS5Xr/n0Rcp4qP7NETBYt85Z+x94QuCylLNQAeqAoAUN9yoogKnNkclnQBQFFAUFiCWHtpy3P6nPTWchAYziFDU/Irb7+wZZymCPuGosusXnjxzwgnlEQZDVO6h9Wt7+3aHVKe0E1Xs705aIsTD+FqpLKDDGo7R35A+KzIraPn1vpF/pm0UEt7ujbnOsd9Nn+QxxquRQMqh5TKvtYR/aevsrezaeCvZ6tHQuOSRVUp8lVjyfcV8MIi4YXCwr+HaR/E0tWH3TL2yxs45zYnPo9YJ1kiIdCKfsidITlnWBxXU+2H9Zlt/Aw9y8vpBJ8/JXUilP/GIwSCjDK6tpVV/9Q7lK/es1sia4WlYhztaOXGkyxkgtXPO5tdw/maqySUklVujeAsgxdR/59YwYx+lvGfSNFjp1vrEWJVJbnV9qPVEodQpc32o9uAM6A3rg/XZxQAH9PuHf/t3B74MAxsaWoitKGCCntkcXUETUTOCQvi+FGYQ6Iq7XvzS6olnZkbt7dm98dIv9w3OqVlB4vCWo3b+1gWDNaUNCoJrvnv1MZ/9DKSMoG6Nc0aGJWkjkMpUZ7eWDrFoe/fWE6BVaBUTJzWepY0Fr7FPe0TM3BNmoWnX0BxP0ZgXfCyyBhEL+5gSq1FJloBcjV0pOdglXdobY/CMbf5Iq+YE2jQobXjb7wNX4UZv6hagqBj0GRfZMKavsgiQ6uQsmh7ychDVwpJomd5UthKehE032I4J7CKcPICl5kGcvBHwFqTJdJWrxGowK/OoSxOrlJh93OIpq+0YbEuQ0IhySxr0W6Vmy5SKq24Hy3NewMkxGl3aVud63/vYg9+UhxZefM/YD67TQi/oGbSR14HMwa/Y/7Sn7Pu5n2NI5pl3xHDByX32xJNJA3rQYAdksw6FNytCFXH6wLmn737aL5WmWWZlb6Jh3X982hgkrEKh0d/2O797eN0R4XnVho9nZse+/W9s4NmajiIewwk9HSecg64a58ox7koUIlvJnsZ1iGnS0yL3OgaKoY2ENUAjUolZwzfSUbFpENd9t+qo0vgmh6RpNF5PuLGnHVNbf6UTb70YzrtOXFVXfjF4bilvfUHPXrrCyJW41cEPRpOu0TRjOI7GKatFoStEAKIgV7xfuX4hymD3rKdw4xdsAo3cJzSLUHHaJoFT9AnXkHDJWpjlCOSisbnwN6SvRtoiRnRMleNttTrjwHAvA8xFPCCqa8t0MrQuAO0A4eymu3HjDszbV0zKmRVTAS9V19jwJhiDcYnlqDOiN+BIB72qVGNpoyVQNQocaHOmPXV+9fS+1/8RzBtFTJTDw6YkDHLolJMOnH3Oyu9c1tOVat5TClVAaQI6XTN9+1+/w+bTnzREvgtky/svAqRQqJgKD5597q6n/NyA5C+JLpCbPvXJI6+8ykuvZ75U2NMZVVQQPLrKrXYDjsyCdaRBAjnXQFxZebLX6a6qYilH8pVAXZ2UNI38R2tHW2sxcaSfMacrsFo8ivUD1k/dRpTWSl8hkoQzkiLVSK01zpjuWEyvD+LthOlaYfWrao51rGOxFb7Uf7o1cjETjZXzLrxZlnupkqIwIY2ERtDETIP+Yxu238wdx2Gzs1bg0ja2poHUOCW/riE5aDd2HxlHrAHwGfrD0e8020pMYqLkRPpgGXDqkaoqEDU1eJZjT8ITdoHkrwa9q/RpzCIr4j9bF/dFzXDRMHt6baMzRsWJBs/LbsY9B1G6KishE2jkXnWqvPdgXCa9kZrM4CzcdgsRqKjQVmIawMzTf2V201GkI6ihnwAKcuX1Nw6cP7nrD16lqzb23SwgoLMyPpr9NStv/ft39I9YxdKlna58VJD1H75o5Y4fAmokxPW8v/33frfEVWXbAsKOO+Gd7zGI42EjzCgCqlf2UYCmznh4y9EkYXOkLzBHEzqFOBHtpFyu6sETFp1o8zFHm9U7MvITR7TkQ7tR5NF4CbfSHMgYk/DX3UHzecHESRjDpq0O+Iir+xM8TetzTjqkLy1ES2v7ul7IY+zPGAJesBxpWjFh+ilxORBWuLcj3ED24tBldqNryRlM4qqUGZVFxiDNE8unB8GFg2l9lha7LMQcK1mx2L4/NimIyev3mrwuQgBTjmdvATU8x5edCC1nQMP5adDBe/7vGrFUz2/SpEH88Ohqw9vSJ1kT0or6nca2sc/LbwWVoWxV1pIwma+cYk1Okl3r+NS6hMSa2HyHI8OD+GHx/a1H3/PGN5bwCICZkDCuvvybR774dwiFeaodfMhPbX/3u/qbj+mZmfbpHcQOnviAW97xzv1nnRNC3wFPgzcANnXPnuMu/GAgQVUUajt/4Rf2nXlOMHERC9ICVfPH/cVfrty5nTYnWpB0FFMHcQ7OvJrjgc1bvvqhi771xv81u3WrgiYOgLNpQEMHUiODBWG6GbB1rOrayjfeh7HD3Zppk7ZRzUyzSfjLpzc/szGwdYcsBmG0tjeizRoebSlAXcHlUoGtTgAu81dG+LZJ0WNJmX9WUmu0y3jDIPkDWRajo6glp8vPZ+CkTFwVJ8+a1i30zi98h+OSHhYDka9n4ehpTE0ghzWRRqOb1+KE9SDMjEYrk1AytVMt8NGq/RehPgSraJ1jyvR4GPPCc2jh0D1ifNoo8Ir5b0mtOVEBCAxX/XDAdQwY7wlLuWTpGLWU7FQiZGMx4M8INXNGY1/NFXAAemY7X/lSmA3Mq0hSDSTXv+WtvRtv7t140+xJJ4Y4wplzz7npP7+w7sILp27dTvLgWQ/e99RfMqhDEE2E1sIyhWTT379jaucdKhAV075fu/72V76S9KGop2SQz6+69bZjPvUpACx6qnTGvtNSU+8pImpy429dANjuRz78y4+46MQPvP/Ef/lXN3tgjupUIEZKDlSN+WahrXkwpxQ48m2shtWQ69Imtut00BtTh2N1w0aZ0bBWVV+36ldY2EWb2ZiWqEXGxPv1Omm9l7DVYStWmOtwjUcsu/IX1bWLsKs0qtWgq7ExcxKQFjU8OA/EKmZX4ZaoVqnZPaFxK3RknBu33pecmBoJb4bBd6EEkuoxE4OJKSD3w4YtXHsrDk+aEGsCNVgix6xX6cMEdAQJqtngjNAuxJu2aNuHjBOHp6FVAREauhS7QK7KGUZr7k9cKB6L9TDaAoy2bQcwoMsMnAyn0YahGkSylX0JC4gJkVDLlCYiqgQgU4D3ffZ46KyHHPi1Z4WALAhhoZ5ra977j6u//W1Q17zvfbte/wYzDzoCCtl93vkgGcQcRhDeUKWNTG3Dx//9mH/6p4Dq6SBW3PnkJ8+uX8cSjJuW0d265cJ3uoMHzOi0UMzB6bT1VKDmxYma7Tr7jJ1PfKKABjXKzc89746nPv3kd/zd8V/4oncsIGjzmkp4leWblCKjfxARz/c0EdI9/DtlLNmIUdIG6NWxud6v14qQWh3kEy4Gie0amSbH+hxtZuWNmKzxh2INhiOe6Wkk12rfEDMmTSyh1bVhaXHbfUI0CMSGfosGuQE7lCoWQ1T1R/QhkMr0GpUxsVTzevjautgYQIqxKa06vquD22qrb8XOiUMzk1dVUhy3jiCoYqrwCoMkeMLqJ62MZT3bqEXU1fAqS42Zb6TI2xQfG1mla3bgll0EJESvyoSWB3M61JawZLCI9TRaEcz6+iHlRt3uP34NQytfqYwiYb1dM0e89919pyJyxL9/fOruu0EXbvxUIw1mJiEA2gABfXkfMK+wVVddeeKfvbUsA9J5xeEtW7a/5vcD7U+EH1MDNlz6la2f+XyhECmMs5QC3nnCOD/G3/jC37Kyu1YEMPhD69fe8KIXHdi8pWcOEYOrRKkohzisIqRGYROSrpitFbFWC9NExa2qtWp1oECbNWhCdpZYz0xkn1mSS3CEaXI3/0LLz+9DF8lUepYcp5V8SJ0jJOi0dRNiN0oo1VzZ4wwAt2DXD+z2wcBRVvqGBbWFYcnVkl9+LS/bejTLdxTJwN+uycL1KcvYciUgpvDHysYJHAcnj8Ey6MkbOMC7QYHFOCi0mqPVYJoR+irRoKcpoBlxf2/y5QrXp7ZRXJVvUyXFCDf27Vsxc8gGw37pL8qJk2ENQ/vy0VXX+mB+Wm1CjEWosteDzZk50Mj9z3724Yc+IvSSkQxuKwp3xF/96dSdOwFAVPbMHPO85970iU+UhgpCq0jiXElfuYCuQK668ooTfvclmDlsBGkCM8q2P3g1IIPsHdUySRpbPvBBUZiQpKGgmRZSwMwKGlT0pl/5lX2nP0gMWiJsBRyBYy+6aPVdO6w8Y10mcdXKYCW4qLR8Pm2U1XWUbSyHtZ5CVbZDRPItshI/mpi3q5FpmmZrnDLTfKFxQ9L/bKTNYnRdnb2L0XsxLmrxj3BjLGrEjmFiX2Hl/GAIJPQgD12Omx9oW6tTsY0BWqyPQytTZd1MrfLQa+bmhKw4gRpgp+qRP+kizHuddIRBgozcUrK7ll0fT+ROQ/UMcD3f1FvvXmxdZnwrUk8Gg1mu2s55O5TBekzMmZXoQmq8qd1Lz4s5LlDlBWAAZ+dAxyljX1at3f/KVxgMwzEDoNn09TdtuOQTgAp6himKn952/fG/9NTpPbsGfQZGGWbcE7AB9nWbLvrA/V/0+1MHDoop0AcEavvPPGPvYx9jamWnsYWSqm6++OKNV36HTvoC0nqYE9FCYUZBH2L9NWvvPO/8wsKjm8IsnI6rbrv5fl/4HKyATQlcml+J/ZEwYkhEuGROk6DEclirGHpLr+qQ2apTbvlWq0PGrrqQNDbt9GCQ2PbGYwcsaFZAhsq+ldJLP890Ku4nYgPGvpyX6kaRtoeYvEIAaAi6TIN9z26rjB0+Lz4umxaKCeQXcmZJXiofKqXb+ds3Zzjy0SQwE8dj/QTimUnUYPGUDWZ9IRWeweHRtCrarTJPjB6bxn1dDZTRPKzZNBktJuoaFdk1eKL6BSRn5MmAUIMjzKC0AiwNUeTqnV6AkD9lUjGjW2ZcNaKcoIHChOtjjmAlMwFt5P5bXWZrOI8KaSBp6qHunvOe5zduRGn1qSU/Bbfub/4KB/aBYkIP70gxWXH9tSc+/ufvfPlv737e+QNc5QPoJmnsr/3S14+88F0brrjSQmw5HelgMMdb3vA6QihqJqQ3OINO7dl73Ic/TDozK8zBoc8pmsIZrQgeVrc8/RkH164kXXn+DzjeEz74z9MHDvdE+3C+MKcG9ED+8GHnzq5be+IXvig0mgtpiBL+DwYWNB2I4sVAlgnr4qhqTPedxZiqVtYqJ/N7jFibNEVUF1clXEbrSvbGbYmZf8aEWY3K9JxRf5QZMqDGpQUnqtjzTKPGv5VSEpHhYsPf+c7v1c6ABK2VFu/nE06dbN9jrmA5++Q+g1YW7vChRGG4mreXCnHSoDRnMC4wpu6OdRp8p/K3XZlk4Bp+wiSiz+kW5mPWJwojaHNhd93fjpqUwLiJBlhidsomUA1u3huOWNhSNwwBTBTgWsFW4kBK9mnaphBkAm9phTSN+7mbGVWsLBr6G++cdzxFGAsni+wOD/ojDoGx4fBeMsrqstgCUKg3cSLS27u7uHtnf9ORWsYICKCrP/v5dR+/BOyR3uB7FFWoCEl3eO/WN//ZkRf+494n/LQ/7oTZk0521ne33uZ+eMsRn/vi9M47HKeCRRad0kNRCPt7Hvnog/c7HgiOVxgkjPHo9/zDirt2Bi9QE084MRUpzHtI36yY3brl1t/8jdKw1ERDhjTdEZf99/Ff/LwTzKk6R+fFE4DOrVx73QUv3H/s0Xc++nGnfOSft1x7LUxcIea1563vegI1R2eF2Ky5KdU5YQGSoAcLFgEapnFSV81NZl5vK/kxBEx1aJUeaxPoqm71GXsGyCwyJkpaichOxNsJc9ri6t18jc19I9M3rkwO/ZOAO0uS7nffiNAnJIWwAaGEKDTiFrsbmAPEw5wB8GVXjHXxHW0xcVykdXvrb+miFj4YT8UwaPQykAp/EjZMANUw8QCLR66xU44AggmWStCZzzuKYcTCqnJHYAYMQkzOZZ2Pt2ScIpLCW8aM0y6QKKrBUuKO/bj5rvLZJlxZ8/2Vy/akFTul677Yy64kreMDY7hKRVzfvKz54MXTn//cvt9/xf5n/rpBgvxhw5//OYKSkuLgzUiGgCIqCwGKO+7adPEHSVd4sUIhDmo9imdPRelN6MwL6UkAevfP/pyUvbE0BHZWp/cc3PqpT9JEpe9YOPVaSKgeQmhwDva9l/12GdHJ4CwvobX0ge99r8B59J1zZm7OoVCo2U0/+8QDx24xYOdDz9n50J86/hMff9DHPrFq551ScM47R08TmCgBXenR7wlA14efEjEzHcjo0jWmBH5qDS5MY680M5HuPUz4ttd5i9ZOugRgiiGJnBE9NlfMbx1xb/eY7irKgbVZGCCZP90Kg8bOxmmdawzQ1jpNIpF6uZiG0RgPww246w7s3ooNMizgWLUk0gFIJbVTjRm+3YixJu2KNnEfmUjTauO0VktIAp7CDUdy7Q7dO1l80aQBLNuyJlR8We7EoIJBg5c6Q0izH3T3LbApy6sWd7JQG2Pe5LehS3HeDXX0TeggCMiHoiSv2o49h5Qj1P8EHLVkPFm+lWUrw9Fp4hwllsDRBGLwcM4Z/NT2HRtf9drNv/rLK795mZBr3vXeqW3XeQYzcTEUZiawntEIg4cYxQBxRnXeUABwgj5IeprCFSw7EQWc1bVr7nnKkxVGGqGhz8FMtr7n74qZmZB1A0hfCvWFGbVQGgi956yz9j38UQO6CwRDVfLoSz628aabIUYTqCs944WHNm++9gXn68CpkB63PuUX//PP3rx/8xaoK0TJHgolvYP53qECBHuATgmh5kRBTehpGgVAQ5XSiPV5Zm9g47eIOK3n+I42Sq9aHUfriC3tiZpuAsg/dRMuo/n28SP1eiR9N5CnlEp0VsZWoFHPl5+o3fWm0enJrXHG6qmSyOO6r166IB7GhMAezFyF7ZXn9JqCavRvaxwca+iqk4No17FS29v2S8lN4ztmNA+BSpjAPMyLuS22btLwzOSVCI9ZJ9o3KYL6ClZxE4rFfTcamplkkJ+x20p7uXBcVNO1cFl6oZbbdNX2+fpouBGg9F1aNphlo8NAXY+1JKxVbETJTICOt315Ok9MA24O/R4Kc1D4Nd++euUzn7Xv2b++5uMfBxByA8tLhqpiUIoSBdhXOBER1T5dDwbFFiBcAAAgAElEQVRRKAM34EKjYtnZR4UWM6ecOjilA3dWnsmbP/1Zg4B9SkFVSPB8EPi+sufof/CKVyoMVJiFBHQKpvfsu//73q8ojCaiHkVh3swZePVznku4sgvJxBwAbPjWlevvvssoShWANgX0VVD4IqQYAmLmC5pKr+e9VgIKY2MtIuLuVsV32nG0cZoYCVTtEKyP5WZGOjOfKIfFSoQJtikR2JxOSkakSTBx0rbW7xqfdsJcVW3WyD/ri8opbmZyV4mqaDqsMJ+Fygmxzlt/P7whT4rgnQi1v8q1p9+z238GDwjMwvwhno8Q9JXBycYoxrFz2U4XMQKmR9NM9Fa2SyvtGFv33Z8ArDaAtdoYkkXMGXzofR8vfWnBIZQ0TFp4BVaHk9ZE506nhWROKXAKJUJOsJXaxuvutuHji9mEEFiNg8S9l0TW6dm31QgKALwz9K3XW6nFrBj7dE49TWRu7cUXT/lCCRRKK0gjDhkKmtNCClWvHoUED6yC0jcrRM0cTcC+M4GA6CkC3eQoCHeEQNASEvIE1l769an9M0YVODMx50VFnAdgdKBue/ozDhx/TOl7xsFoDTvuQ+9bNXNYRaECmXJqLAjr3/ng0+984hNC2BQJwBMC8OwPfuiGxz32h2edvfuh59LbhiuuOPVLXzzmmmsgkJA74YTGWVHXL8JF0xr/3FXqnhC8p8XvMaSVyIeujNw6spxYiTDm/BmrNiIi9E5nSyfqkrELKlbMSlDIaQSTs2kJ7NLVsiGzwHcvuYa2Wsy3As37GlyNlMUIENtsZwiRI4boipYSNklGyW8pQFJ0bLXkunX69WoVyxmVRm99BwIqhmPxEwar9XXsmuGuVGqWm9QYeCsKuRLAa4RBiTnY5vvhpqY086CUkcBQUAjatrvDyo0yVsve8pIMY2m8g6cHy/bLt6LrWtwTp5I9OlWbO8yQ50ehGZToiVqfcyJFuKERaiiC/5jzZkLhFPqqPRawPorSjT3E2cB5EdI7zArEUIS+r1ACHLCzkBCmtWGNclZklZkn+jRHmVL2oUbH/cefevOLXwwDy9hzC0tad/utp17ycQVojjRApRBVUorrzrsgjIFlohIdYGt/cO1X/+RP9m8+ClIaQ9zxuMdtf+xjT/r0Z879l3+eOnSA1oPN0nqFGV0Z8JNOskOyEBwTZo0XlTP22ZXoEEwwQMMpA+XTihjSCqq6dirN7rSqoGKorpE/q29aV7CS6BYcj3xazFPc4nVXtW+lwQZ9+ZRYVh+/DDBcxx0LC3yhgpZwQtexaKGlBVvorm1vW6x52rB90gEC6rE/KRG2vuT49ToQHpXYAnCmno2gRPO4oiTkKp/TNZvuYtM/06skTbYOrbr4YBlfEGpGg8otuwaW4QgERuC2JsFntEyASdpVLyEDH+sdywx4rtyAps15A0VJMTMp/ww+bKQTNShNhVODHzLSTEQUffpeIVRAhOKpUvZQAx509DDxKIQeYiBIP7V9R2hvthCzQyVk/+lnzZz0wFU33OjcMMfxMI1C5z2ufc1LWZ6iYuGpw4zkCe98D6ww6Tt6sBcwFmk3PvGJux/0oNBeY7AA+sW4//4PGPbfBgeK0INz088/aWbr0U9861sc1HMa0GAVYXTDpIBMc6McHgtt0qLxcnxj7X4JAgmRLsIYQZWJzBBJOUzwWzkWcfV1aySuGn0HEkhl5EmyiinzoUYrC5VmqkY+rPaHLgnEyQRkC2upzsyiEvL7gsQSo4pRYcPw2ZtxD0PIcRnwxqzRbWwTh+jKWduzfU4+j4zJZlEVEiQbrnIKHoe1E4dnlpv6WPD/CdHjV5HmqiDdfMVtQ7NNQTuG2GQxW/MPDZXXQvHggsiC+cTyUqkPJXTw9zAoShtWm8MWLjUIaHb3Af3hARs4bYU/bAIIrHkzjVpzewwPIaMTLZG5ken10GpIDQCBJSrd2Mv4hTLmkVoyTkDwnWLF/yxEkBcoE6MR4iYl/OXKKSCAOpjAkR5WwGTFjh3rvnJp5dKTsPtueNWr/drV4RMBlWIUM7vmJb+3/4EPthFZIXnEN7567GXfANVJT80ZFVBD0V+z9voLnh9SKg1S3oVNlQsCYA1gSKMGAL3z7Ad/70lP8mUsoiOUDg7WKO6edzuruXqiTZsVs/ocWw4PIPzu0FMUA1/Q4VeNQvgYpKtOOSLVR5dExUakOCK6H67nyHJGFNatOc1pr6mEQg4RH4qwM9FWtG2F0Y09j9Xfbfz1rs9jXUOa66s0ctogsM3U8opeHhJLy/9VbvLb7Z67sReUQQuUWjlYLHzPW183ulsnhs4RdXldft4H+gujeMZu9soIu6s6b1f+FviQa4aQ0EsF/AnYiBCOESE/frwAVtVfoQQKVBy/wQAPBUkoSq/RMUKLurZF1I5oi7NtOwhb+Hf1E23trTAzg2dwaghD/nU7sWf/pCH0+mDQ6W64+F9vrU42DoQxudjYBcfYXIIe2O+j76xH9IO71jF/9/cG0HwIyYGZGPeedcYVH/7wzc/4lYNbjlRREdn18Edf8aY33fn0p4b73SB2vLxczvybd5o6kjBvDg4GFGZ27VN/cW7tWpiEWy0ogBc6mFHL/B/Aw0BzgElQ2tPt37oFdEITGqWAFeasLm9KGJojoyMvjbHyj286EBBJU4l0FlBmGHZiJbvGFCb2QD7QjBnAxj7JXLGuF2/XRY1BVaY/WVzLiy14RDTjhDlX7sbBH+Buw1xQFpRstGWOiU0U1PCfo2EkIwArzUtp5zSUGJCKjbOjompgPuUl2BbxeKwmqYHum4xGheUuES4ogpNrpnD8ehvC0nKKHL/XMZLAM6uN0sLBjlBf1AoHVmdQGgsBrK+AwUIc3RB68podsoyEdfxZMM1FYSJjKPIFQzmjUSJrxaBGcSJelU4ETmDrbvjBCW94/S1v+BOYN7pBl7Id3rDh1pe87NaXvHyo0Cqd5A3BsR0AhQRO/MAHVu74oRTTxj6s6Bk8xEH3b9ly47OeU/rTgjChgWIGXbF/5sSP/suR11/vyZ1nnrnt156pwS+aMNiZH/jQQz75H0ZnFJqqUeipAkEsPwcZFb38AJklHzsbq36JgmDa76ouikdEqN7a9lhfbKKAlXCNR8RHvpXxiv1iZhWvWlAb254q4YmKJbK2yjlV6ow7ywCyQGZzTIPNe/HOpdfYjkfa/YxhDb2Vg0Rd3avpG/eCwcha8VPOWNkGoTKRQMMXHJ1ovgtgKNTmCVi/hiv32Qysib/5cQNYhhFXdnLDSlvtxFQ5v/9CTk7coZVth3y8dj901tdXbUiaN1jq05tJ4/EnBTAtg4ANQtu2y09eqHNrqQJt4pVO8CuzMzFW8Vk8T9b9pbAiHEFRmFOD88Qxn/qcgLe//GWz69YLqLAQDl26kpRNgiFIp2ShBhNgxb69J3/0X+EKY59W0NTTUbwzd/X//J3BSjuxQI4RwIr9hx/xhtcdecvtwoKcvd91157w39+69E9ee2j1mum9e8/44EUPuPSrnkopDHMiPaOFdEVQEhQUkn2aiJelGp0aWoXtrX189Skz0VXCrh1Jy/UcNRXy1OKx5Y8HgNAmSx/vAh/D7rzRL77VuHWpbkqZmz9YrxBtVz7Vs+womaTbrGEbdoI2dEgGtOrls2DMSsV8dBWYZxNRS8C1tH8bzCkGSNGDzgyrOLVBV+zHTPl8OnwqXb7X8pUImwYs27TS0C+/DM/rGCiRFkiaqjU3a3v7he9OTaGZYZl5dGjnIiOGdu+E4aY9+FF45WCg+4DTGhFgjfGLraAwZ1to4iQ0ajiFp4UIZwN062c+dcYFv7nuq18z+tI2FMPHCgJwauG+b1CDMwslYxz3gQ8WMwdc6HegBuURoLc++ME7/69zERLmDUoNipIVe/Y+4vWv3XTL7SLqpQ8TBx51+81nv/1d0/v3PfZNb33gV74uKiRDMo+qwrxwSrXXiaBKtzgkcqaRFDC1GmU1Rh3HoHYiQBpt7vA56xYzI0VSs5WZdZ2uJLbi1FZb1HRHQgJpjf3I1HjBLoZ+RnaGaeMtfUJTnyujwo3YVRkbhq7aTYORWfY4NW5Fb+ST6oo2v635ndrixqFch4nAVhmsj8RqqwAMW26VebGcZ0r5XwGCoFvtyBUoAyQHTzkkQrNcB7TLth9urLO5cYmuEajakUuL2T0EZos007KD+Lb9E5e0VJPKNnq4LxV9lSCWOik/8udKAIX83zUzlYLm6URYmBmcM6WjrLzjzjNe/ZoDp5x01+MeO3P6GYdOf9ChDRumb71l9a13rL38vw8dc8yOp/+qwVgWLJwCa6++6tRLPupkpbdZJz0xNQHUk7zmZb9nChHTsshRpvGc9fZ3bLr1VnGwvhQS5PckefK3vrn5D29cf/c9QAHxtAKkmDc3DZmDqvUMKo2QolUzlO81mjNO56Q+jwz/iSDhRMR4wt+hcQn5Fa5My4N0VS6WLd3KNtXnQl4lESlrseaon6XizFr3QFcure0G3nAEbdKKBsRttodmgxZCC+nteaPVIsqd1G6fR0fqpck6HFyVChLGEPgCs6OwqsxnDDJmcNiA+WMGsAZQyIaeQAA2rx7ubgMGDVY6j5rqLX7WkWyMdhj4jnBtDAmX5iEzGVLWgzhPB3rcvncCy4PV4afasZUYtFoXtSQy86USgeXaPUQ4BhU4K0iCRnPetCdOPZ3rK8xZYTK7/vpb1tz0PscCUAEhNOLgUZu++64L1cCyEVCNAthp//APJs5zrgd4BpWrUqavfeqTD2w5Wmg6sGBQg5Bb/uvLx1/+DUCgFAkhsSYsFN6xWHvP3X2RnnkTTxRA3yCOc3PmRLTwUjZI5hEnCcIpJ8QQeelGjdAqE4ElBFX1yVArZSLiwB6zm0KkyJhGeGlA1ljxzDcsvTfuAONlCyIeudjqGbYkxHan9ZyU+63hDuyuOJhrbWTpargwYuiQWeZrtWlY0tDo2vgI86WbDQT0w1/bjFWjTzvLipCXFWANK6RS/iVHrlFALJBYQeouYqoD1WGbUK4TH2jjcmD1E0XaJpP49M1548GaYaC49Dg0i7sP4EfzdR+E5LTKvBrF+I2fLL6taeGZ5AAV9QJnIg6mmDOBeWFBA8iel74DzOYgTglnAHDjC190cO2aYGQlpJqA2Pr5z2++8loRKtEPLld0Rj+3csUtz3xmCawIQMVEyd7+/Q9997tgJnRKNSDQVGbq4Ay+51f0Xd8cRQuVOZKiomaFSPCEB6NVs/Qeaw3SQYY/beZuz/ENT7hetZqbx3KgsdBrqtEaNG0NnwkcW+1GMVZccSu4GRtCpTc/Rxc/RqLzEiLF+T8mjMEyYAf2H4RfWbIC3lEGQpJFoKuGsXXEZGsxe2Ex7qYSk9iXdvalGKtk7TdipbP5/rjyv8HU4scMYAlsgL5LFkt0Sw9QZXXPVtBVI6JKnSKNkEvznK4a/8kIBmvr7asmKmaecOaJnrEfXJHsun22Z2bi6OqRAVKtahuzSN6oFULlROLE5q3exzOplJhIKJYLW6KKIEGFQAZUFETAQfBfCC0sa3YwCJ1iTlcfYeZZnvlDUsrf/8J/gHiF9Ez7LIzqYAb93m888/D69SifTGClpYOd9g//2Ds4IyxCCA/EExbq7yAJ59kXBCNbMoQfOgAklBw6Q+RColahDzIsM9CWP5g4CkOPysZD3+mrYbhhnSIaGYmHK5A2GkWG/j1NmdTTDBE3eIuJ6NNgtHHGHH/5kQlG0g9jv5sPoRa56/JnGS0uT5wqQ/bgwHW84wwcTTMHKijsj/awV3kpxuP/rD666VigagxeSmscROMEGgcQqJoMhNvpVqz28xtTgjPqsjGQyygB04bjWJYIE+5nNc+qtC6v0VejUWwe5cYarUHSNleNDwqxjYreCYYTmJndvgtqk1YiNLNwJy3vSjIwouRiF5uDeDK/SlMsY5NV+ZZCOaxYZeT2pJvef+CMt7z5zJe+dN3V3w+OoWY4+e1vX7FjF0ljvy/iYCKilF0nnHLbU59mQBHssqAglX7r5Zc/4EufcyzMjCbGPnUKcc+n4G41Aj1j/lVIyqIzua6Y0SiyxeYxrJ82Nkv7YMVOoYQgL2ZciTaRe84zQ2PttdP+ybGIS9R/x/C+WiRpXTegT3DPP2YvNWC77aP5eSdqs9HRbTg2pcY1axrdWiXwma1dmbN0MR3NeG3Gyoqjgy6efPtRZbCayBjF1tUVW4vhvpY4OGsV92UgyDTGqj4BVKkvQxcFHwcLYY3u0sg6B/VVCAkGbtg1eZd6cJu0arrF/H+xKEzTSmC0du83Lq2rJUQmj9UJ1aWl4qQz9j3U2dTWq7571Eteev3Tn3bHc55L0xM++3mQqpCioBYmc/QCwbXn/SbhzRCscQwCM1AedNGHRUSNIjSowA2DyWIxLHWP74SsKtA8aWes2LetkKLxmCYOX6vPQpoKanRbyCn5JTwgWtnZTnRXJ/+q6g+NkEn5CvTGIl2jRq1TA2AOa5UQ148003T9RSxrwuBSvW7EblbJG44MQJXRNbqllhw3xwpgHpthafwwjJgL4isqnwz/XjhqH2NrrWE8XTYzs2UDWPXD7gC/eWXTro/lFmEREUsSAWpx+IXFKMAS0eJVIzWU0GrB1x5wvG0fJu6+oPXWyzJxArnYqBP2WhKb9Xzcln5ibv0kDfsaK54kgT5NlKAzb2Ls3/9fP3Ly5z538MgtvQOHRJR0pjQedNpjIbc97GF3/9S5AjGGZxELddqTL/7oxltumKMUgSR3MA1Z1ikiCsnW/bRNeSsxkxNNiC4WGI0QKh3MEoMssWE44VnVWpzKT0dudBNNWImmuxEX39mXqaAaD9+MsagEJuu0Po3hjEjWOicTit2CPcMOMSlrZZoET/kjZuNkkoRismTAK6dXMTkiH4mVA3gpk2ASOylhz8FnFJtX5B1yjRx4yTjwiGO4cTmw4cGuF7yHqMvyAWfJXAUVjgGAtzsOYBIvdQXYaM0wBms19iyt+Cmm31pk9TCTYukWRWKFyqzDKukf1gJiK53NYmbf2psP0gXjDsJUrCcFD69Yff3zzxeIAjBvECNJ9PbufcB//FsBEib0hDNVFRNvdJK563JiiBK1NiSd3xtZq9Ydm892ZJJbMQ/MWLNhjoA91ofYCH0W6fyZrqPl4JIxPNPvVeTRFd+M18AYk7gtcsn32Ws7dttAf895T/Z871Dtfp9fIhKLi5DhjyajsP75Zq4inHUzvPz/K8Di0E0UBHV1wS2rDLpAwjPoo4u8fFI83kh3SXKa9NnTbKmQOntieCtBhJZhzgN+N2zfnQcn9mofz5QBi0sN64RsliRAbUl8H1pnVFFnPZU50AnEpK/WAzyoooWZKUlRzwJ9u+mnH7/v6GNDISio083MwFP++V+mZw56QmVa4BUidAUsILHW1UsAoEzU1eoflo+uctAzgKEmvTo0xhzSYwxWjAFKR9/kVBgzkU1CD55WiDduXRorpFevcRszUdcYrFLmhdPq/z6GBVejg8YEM1gC6E7sX0T1Jj29dFsCtTGhpONr/pIdJRpS5ETD51ts5Sr0DgSBjWHZ28ImwAeLg9zwNStsRXDXrOVQAqjFASyEI0kQ1n74MyVcWmPIOtqQ5tu1mYAqoePUBDtmJhBaVUeXVgFNJwiSIxVvtaRaPDGWE5aXlhuntcMNGiz0ldNSGu1S4EmhFhBTeLrCwWjO7HB/zfrtv/jUwHSCzmCB31q9/Y4H/sfHRQrCnM2KiQoE5mGOrrXkly4OtgKj2DRpgTa65xLW0XzCQTRWjKsPro0mDgnlUwKT5XAzMQfUBCzLQW+tuGoozIp1HXYSe41BoXUCW7HSXuYS0sqzOi6f7JcC2IED8wUTCwoAH5l4kcXBDMzU8G23btB8/FS5TzZv2gpMrcXUDA5Von+Xk8paXiP5UsFjBAhZMQ3rx/do2j7Uou+WhSS7/6K9EtrxEaF70niZVGjl37sm0QRr0NPfcI++j8Nwcp7s66PRvWQxH0NaCQQwjxJMxJSkGkWgRgXN+TCNoK+iRjGunJo5/KhXvOS4Sy4BHTGoDkIf/Ldvo4mV6QeFF0eat37R9DTV1e0zUQpsDY1BRiBM4pXeyZmNio0zZur0G49dc8J3zXRtDPjY6tqKZKIO8hpvY5rx4cZWlxxDP+MlIqShMMaKahijpJi/0ybjngsAuzAzCHhWg4+jK0QHmsagGyYmS4TR5Y+ni4GVCW+BBftnij0Hzmubl/V4Fst5opQX9+CT9cXAmyATArZW9xpBlbaD5Sgf5qvXZQbGytGEpQzWTACYWIF7ZnUyVHu1lR9lsOrEQOJmmvlhJstVHzwSTfI540RsZfI7B9MIo2GZdGHEC+a7IkIalJSQzyk0gkqokb0Dh8559ztP/MLnr33uBXc99ByDHv2l/zru+1dDHGmkG4RQkXSKYV/3oiKAYphmpA8x/RM56CGTGkHNnqrOaTXSV4hIpmJaJSTDbRpdoxDPck63KLYSY7FaXj61k3aEb6R5xjZtj21U2ua0ylrF9urYDz914qrREwsT1nVIg0F2yQGUHKQw40E96tJeMxe1kYLP/AjdXG1jG9U0MsxxaQad+IeGDZi+ZaHR/I9lVE5wWaxa5a7udcEfjRPIkp3F7WBrjFNBu5CI8xPrjr04MCkarOHtpqp6STzTj7H8JVzVRc7V6QG6a2E0UfPKh5ikbLzhlke98XW3PewR15x//hkfvthQUEohY1cVWk4qc+vT/3CgGvqvZsKp1ubENC5HxNa8aiPSitJyVO2xLMLF69lzXB6WBFWMMU2mx+ni13wJgVQOj1VFxmPssfucl6ABB3VuJw4chRVmIfK9KliSDCDVRCukPox9rjnj11I/0o+Mm6M/tJrFAlzF5WwPKybn1LH1U4sGTF0PqrTzYex0wiHKhEVHOI3OGwT+pNu+z8/55a7ndrh5ZZbecnL97ktAlvkT+cYNY5BwaWPMptNE6ITk/b552fGXf9OZaiH0RYjxbOTS8g0XIpCOnbYrnVDUetATa5gI3WvkjRrFT3VIlImu0lzI2D16YwOmhN8EkmqtujF9Ym8kViBTJjXett8HqKu+u9IivGV5KQy0w6a3Ye9RWDFwtuN8ss3CtrvORyQ6MEm7c9W9OFLnL0HX2/Rwjf1y13snCWCtKwC/kETMoXwyvKw6HKT8A59jYWrtgCzGkDEE7ABQ7jw8OXkNwytWVas+k/cl4llywHRv9zMu5vOE2gmAmIIU1X5RFCoqjlbaNSR+KGbjmeNsXp8gp4qaA6paz6UcHi4RGJyDS/INGhpBXsyDFHkuVojr8XOIosZNziFm0uRTJkxER7ete++67lQqjVGhXR1N7+NN3YEZDFaQo2qb1sFOuo96i7ckvTd4rIbXekzNT7TcR28Cwp5tcMjX9BI8T9cjnbdj5V46xh0WnhDvl+2T8HcdADCZ/sONJRJkK66WCiTl0GOdhD6LND5tDVdJLyc/edpQEDZbuJ76YNZOK0xK2J5InsnfsZnxNa1Zzjmzd4KhmVHfwyGz2jqXNhet80/DoMP80Tch8Bq7mtZqTzoGY2SDFxahsgr7dmTP3Ge0U1cslV6Z1tDG5b7nikF34YDBCDWIlV11sWFoDB5hyWGTLgLbdXutRsF7TWz/IwWwTEoZVvjnOscOCvd7HQgv+tcXZV5KM9IUSji7+/DwIWVyXkFhk5DXLDs71UkNtvho6nt1+mhKMfuE65kD+oJC1dOpmAMtE0QmFh5Dh61tca32DWOr7GPa8DrcT3BCOeZPadl77BeR7bee2KgRXJizzp2Kko3bshiIhnvHNPXewDexlVfVdLV3gtgrAMDdPISykVC6WIwuLZwaQ4O1OETVyEdYNSpI12MaQPDoXvas7mUFWKXL05Dam1rCg5ExgsliTjtrWawmf05zbzsGA2TXYZvAWPcmkiAxTnfKtb1X8coiEVWmXXuOFq3ROyCfxSEpJkbpw/cwDZjIFFUhSkp9NTKDmdO8V9rwvdGYtN7cV+37G9vCIIFdYkwP4sbuaPJ2R5tDVSOdlo8kWsHNiHlmJ8qq/lvVXEK0+VQtBlt0qhguSXPiIm9fsQ7TMYRl9/rNFgrgLsyULPUiopG7jIBLq8FKAqYEILbW0VjXoweImQ68Gpaz+37ZdNMEoLagf7K5izCBcBbJAmpTgvfiT9bWdPHsBZmSBKF7Dg4MsSboOg/PfPmD4o9cwGp+ik66m3Ix5FlrqJ8HRdQZrDRO8xSjLchpzszDGYMgrBsyoVZSTJtCjaC6+j9jhlXpTJ70BsYgV2OezxgWUJk0an5w+MiqxrDjGH5UmR0VY0gtx1bu3/dPR41ws26hPFEs1h47NBxQRr0VUuORdnm3ASl2P8QJl8olGHYVwGpMVW3hl/fwLWvYM0sZd2m3umYKpjbWkpouJOlybDqjco6PU/MwFpVWKLyYYdecDdsKJwl8zI+dauIEGDY+duiDSz9NTgKoGnsERZuPecwzLH9vCM1soYIbpHCEgxk6JqSpsrSle2KyKp5rXWYj5KoRS+Vz58j+ia1JlZupV74abavQRSrUmISTY5E1FCchmUudiM2p0zatRcZqE1yMvoopyfJ7BjuRWDk7Px+ixYilxVcwq7tFREZ0e5MBrmDALs6oeQEUcwK3lHGBTfipeuQXjr0d94ktfg0lPf0aTJXfEjCYeSF0mQ7d8mqwFu7wVUVb8uDSHMlMfm3pMVNHEtEIwCsMM4cn7QlqVNsug3Eu4MAuIGnxCTZLu12ZWGRsSIo88+j8H218zs6pDCZQ0ZIfhca8nTZ2Z37Koelao53V8Kg1ukQiIslCXM6ck+uc/wSSU2nK7OMbI26vXmQcoxQ43k5YJCJJV12XFvHE4p/zU3ru46HzMDyhAAUcZwDqzB4t1R7Qe3kJunKQEFM6V9iyoatlBljhdjssfsnaKfv/2HvXYFmyrDzsW2tnnew8CgcAACAASURBVMd99O17e7qHnoF5DwygZl4wHgFGQxDI4GAkETYG8xAWiEAiAtmEJSEUgTQC2+gRCDwCO2QpTISEhSAEZgAhjKUwsgyWZF4zI5gX08z0MD3Tj3v7vs+jcq/PP3ZWnazM3Dt3ZlWdyqG74vTtOnWyMnfufKwvv/Wtb2F4q8i2rxp17Wub+Rf/NsGZSTCMvHNSgfHJXOCdCZR8W0sMKT9sBOOEhmaQfCcRyc6nBHJcZV/mHo3O/fV2NRmUVOpNU3aKpaTV0SGNBRMMUBtdjajgS/AumfabMXIozV3l0Fpb7Uk8tBBvUAvCHOxybsimwedtyUN1U8yBALdxIoDBdFxiI+sbnc4O56xnSndAaeqrBLiC2TIM7/yAFbs9Uc6YR4IXdMyJ0tk+ad2R9af84hfbYKu97ucLWlUIJuTdEtN7tbgBHbTvI7qMxZrHJWibxHryuZkR6Cpt39DmyYbCwXT7v6HgKb38UDFZTjVDmspKg7mc7Fgvjkl3dspUf+dzP2lQMsJNIJ3KjJ26ZhabnxE01UY4pBFlj9hOwm4jXa7Ph5kgcQeni/stWyhkGxhohz5Yw2okL07J3XPnQ1HAKnrmEOtZG+S1JhwH2jYKzjKAWnXlGAhC7peTo6hblFV4m49FYgzWRni1fNZqtN/VoHK//FnNaQycvy8j/BQyeb70JCSygb1+V3VJVv6DTee5lHD47O0InkNarOPM2fvQ0t6vhG16XYs2NKWVQKVtAdnG7yExLnNSfQCnkyWUBSlxH56wUIovi9LCTeCYbQO1zcRiYZRU219hcJ6/AGshYl1OwgUdC2MHLZbj26EbPyGGQjQRITygEONRObUHqZoP08IPlWcu/MsP12GwEiF5HUyWafs04tk6//ON9AhKrz+/rXWMMer1Xu9cSadNQwzDtYXnCcVbAkKhq+MyhliQN9isoSdDok9w/fMN+iAE/XWjL3ICQSYMTmO5yBFOEBsnrkb0c9wgm4Uu547d3oEXBxgnKEGTs+C1qRRe39dXA5wMJRRkg6Cte1VBgxWq6KSh834+ASw7s1sNtYQXz2cwWzJe07XH02h4LkDwQRAee0zstZJhwRiX0aEMVp3dyccB49ipxs09Znmwzhg2zn7FpjcHcvViu21UFTRkVW2ZHVYbiouIENSear5GuVzb1yqHeUq0ykHSUzQBucZxV43Sv/p209WOG6GX8vNlQ7suTs69M5lyHQHct42zjmUOsUXCxNa4PhsdULQTS509MHOtWLmGM0PCWtJqAE4vsajom9Uu2M83gBV6MdpZLeGenkuK0PKGtg3cNgBpsUKeBIBTP7W7UkxpXn2O7TZpXjOruNUix/a2lnKrdoxPa8hG5OzSxMwga/v1JydNjOUnQ88QGKr75QrqikjLGygk7SCKDAvQWFiNdTZsr63tldAb7xPZzHaDmhzN1ghPhDRC6jW2yFnblgziB12njfOBZIjTbSy74/uwAMQJfVdmcO0wJLtKC9pGvkL6ZRXhGYMjO2Oxih2eJH4BhyV4uh9Kch5sa8dJu5mkzZyyvcytxnchpFCFJ4Ht00kkxVdzdoozp6UzToIdqZyh2qzY/ToGStbUKqUoutUdaejTY66PI0bYK66PQZZO1XyClIp1mElvpdfpqm7P2NhWp6g/xkqubM5p+6l3acrQOaXhVGw4GPWWGTaCfRux1VfYpnmWhFPDZDU97fWV5/sv1DOqOd/K6YGNDP1ZJ9yM4bb8fGjCYr7TeavXRCONOGOavK5zycaVnW739ktQ3AmMsOBrREC4GRG6UAdaUllfPG2Rb2lqbuXhPIeBapxmesBZEHaTEChll0FzZ07uHdNcFF0nR6YBetuvNnNJ9Hndrg/MLWM80S0SxPLOjk+Z19Tsj4eOJ21y2IYCMWHQ+UxFTsIxZpWOtYsA2pip0xS7c9L6B1D3NZQosFPVdv6ul7Fre0SNM/da/7RvZEgT1lmZPXk2e5IPejzY9mk/qHJzBI/V+9S0+5sb6atI4RfQKseNfeMMheVF2PrFHPvB6r/1X7n6IVf/1JgYcxXLvfqNHb0mUdBIGASyVxVEDDzwukr5LH9N10Q0Je0czGBtTyxfjTwwwAJwPrFmWBn9487tNjSIJ9vSkBokTS9eGZfizCx4jPElMUJuG3MyorlkWh0fwxYx6Na5WNriIR2be5ODvYTToNnbOHHSyU516tXSY87fl430fk7ncDPzrSM23c6oTktAJuZpy3AptDiIYOvZPD34Qem0gbk3Zv6NGV9mLCrPdkcbTRRgVcdqTznm3PVDfo2v4exuvsFc5OjrXxfPAQIQ3oIF66dWN7/zaXqT4321zpBGGG+mWYfOtsqJbw1KrSZmozM/FQN8sexhbJczyw46C0LRZ9DVLlltu1ih5c85KG3UCT7adRiJtNealg3jCgzrOxVDGzHEgGTVZGyEOYYFWy0/3PbT2vRbprIKWl6XH8hKL5uh0Gbskp0Lj1baaNd3O9dWp046VrtXw33ctaimmM5Zw8IiiFiyj7R0Hfi0p+viK7QJpOCWe+Eh4boxYtFI6VOsV/I5sVnnwMckosjyjtypMRpEKgz9bn2LCefSzn7MiLgz9GKpNiAb7Uo6AkQGp7XOCYyBpzTM6kRm60foEetZP7SPbuazEUIuUw22JqA8h4s6xnFOC3gRHsTyckiNzSIy3zWrwXq1xUORTVrhM6BfcLHRJtKf0gBrRSJHFztR1kTf+dCeU7l6Fpd7gHxS2qciuNp2w5lBDNbQGrpeH6nMtdUjeqeOeyOClUS3QWSbiw7a3DhtVu+Ae0znecY055huZ6rFx4VexPNu2EIWKbHaodvKRD9bhRRDW0fHzqhYGcHowXfiznaN4c5fZWXgnjMe2wQA2sjX08AuxmDpoK27KWmVp5MiVGhg/5tdBRcnfmROqWd/onYs3GhW2O5d2F7hygVnWYtt5q5TGyTPziT6yflg7YQ0Wmcwo7vfdNYtDiLPcuDICDOwobiwc1sJeDoah6VlXjkiuYSDV8Khqg0LYgE4gZNiHQZjmLV3/b32pDnwIu0QMUiNlIP/BjkvtEm79VOiicRrr+Pr+mxTrE34pF4eHqw3ydlULmz9KsL1kdla3hOuBTp359IwGYAlIhTRRsq0jZwSoCQGhhofyjCn2nUXa6PA/JUv9/pTh8BaRNOOJ5UdFtNtdgyDFP1tNLO9goB2MMj3Fz2fQ5PeXNvZNbZTaQKj9qtP5/0T4CyHu+q0bm9jnY2U+yWKCkdnJNtmXUPhaY4afRCGm9StDFhppDwhp4Zgo7lSzrWpl21tYdScUbf1MjHlCyL3+uFRgQnoAVutIbRVc/PR1Xx6LjuifYBpYKtEcoHJLFxSo3qmbxdILbCUtJ7Im30J8x8E2+RKTII9qIVOp9nmOCyVXkln9jBGGtX3MYioOtN87frEcR14EiNMrCQ9w72HaeU6MWt4ZQlDvezqeVWzv0kMtW2G1IqCZw3IE3Vz+am9hlp8uba6uWgapjR08Z2in17Lrhy8EiucbDiSt3FtvjdEw75rUxAq7cK1jbK+VO2CQDA5LMj2o3j7Ab43V9NLCuR/vZ1rin19y65UWvl41ag1AZ/XNg1c3E3Peq7YKkjKl+NphGBMkKijWzhptvNWe6g5pG4tyOhOz5GeG9NZ1Bvh+bkOm9Ib0denZwaZGozLljZorc6VbNa5PpYBSYju671r2se6t39zCq0KtF2pV1s0v9FNOq73xvuN9NSLmZSuCQUSDNY49RIGCrdjNF7CT/VT99Uug5jgSyEQAw2iWambYeBNxqRczhFIJUbSPGQ7PYBTAFiLMjkjHIfzkDkox/K+Ppx+G0pl5W8ukEMUCKUQnk7oQk8oVDaIZjaIk9bZ0+0BRERMEwbFgKFwMMalIZlPbH+l08287XHfmxOMVSO2OxUiWfKWqcvJlCq38VAaIeX3qtsUr9PLt62DDPLbP2cycCM2t8GpG81j4Vz0/uNerv5cuzkcIStF9/niJemAaMNgjvSxdatuALEbC+Gn0e9kMgBrOWfe4DSORfKrD8Z1y9nUUdmoP5YYIHRTaZJznmimt3nLpPBW2lZqs7Asc/0jmLA2/Iqp4DvZynSHSkQ8HZZ5wxiAy8ntJnr2dQ5jmdprkDrn9mQSG2cmuFmfXOltJp0DQQaJ+jcIAbd0aGJc4wQVYwXdIigQm3QY8rVIuk79/tDp4qAFog8wEA/KZGTLOwdYZ9BBSuPeeQGXjX2xfRJIxlki2aMSIemmVcOS75ezDm4YkRfbeElj/pBGOBfUEUmmfGrQUNHVZG1obWBD4ZRDpOUYk56hBIGq1m+eBjY+yZ/JTNV2TCnV2+Qu54pIQJ9Ypi8nfblZkXvmhbxOUnLbiGSz648h7KHi/XOLmAoXqp+2ExtGCGYa39UNbVEH8V0ASvHTUStPxqZBwFPDBVkDuHQ2icxnJjd1TLiRZYSgGOAALoi9aTV7xnr9lQcxWAkl9fqgaiOV2DFAk7nL+QYT+bO3qcrEtu4q5muKvBqFFWRJwCi1tQlBb4jQY7EQm+B4Ommq9ofbi52NcWKjoqV048JenNTpj1DX73f+KVZEuSt4tMHbWnsHY+UCu305UdCmUeOFQUag2+Y+5rR6WnG3x2w6Tu6Kk7nCWU9PpXzUkjmx5zz/+SL3sIwHFIWTiRm5n4MGqzdCd3om5QwvHwMNUs0PSsnFCgIawuERlY9p1JjQYLU3nZiHTsuJWKPl3krD1kgWxqKtTjgx0gjZfgq9XB36FFftvyYcDXJSe5mGCEhWEWI77qbbQxUTzLutk/E83wiCIng1yIJM4MYAylheLb1F3che966KwHxRdz+FkDkJgFVNhfekXzBKuumTMlYtuE4VIdYwtG0PrL2MASpC6qLkdTIXe000E1iITRJj4xbudU8Y8ac1qwhzuj4j6RG1Dj+3zhfbLZPTpY5LvJUoU0gA5QA7nHML/CHbCH69flfnE7kz7U87mSoMMevKH1h6bqtlJIjXorKtxuf5+OkFpDUG0dScqJM34F0xWNvDeT3EhEc5ncO0S4AloRejhJYwxuOlvM66YNCgI6RCdBUYDjPdjxqjbQzrxA1IpSquNAD7wkDyTUbqXkMGJqLYUKpuhHt4nQrK8YkI6a1x1uoxPim2770C7TZ6W4eyajued1pe9fZPbBBRCb15Ysfb09VZeFhfJqjdw1VJZkHVOmZKtzdJV8B1Ek4J1XOm7XgnvbRm2V3nkHr5rRH1hg3uUIwjDDJGaN7z1eUxBjGNpHu30tlBfDIYq4oCBygoFAoAwmpmObIFZKMZGinLXtX6cCoaxwl/hPmSvqJA4MidtULZJcBi7X8EUAGsoY2TrAc2rbiixz6PoKtOjDXUGn7s5MiC25M9nVp+cIQa/TwLABPOT+c5jDRa2lIXjkSjjzZiSPfMaXw9X73eyW/lVAK24WY6TqcNovJDdaJurteXoR3dkS17z8FnvTPWKyNbvyHjOq39OtHJBm1Ccwi5zV5QO31V4WYPwZ3bQZbeCmFmWyNcQi5hFwJb/QplePwdDZhGZ4FSXzla0h8VzNrlIdt1irBmUC53PWVxrGMdCTt5MFiHb6wgCwblwKNN2rgNOG+WmRI5CP5g03JqGOrquduuXpnyrPXBXJstW3Ni09vqNFNoo5neye8EVW3Zb6e4qvOJv0FQ5Sjq6gSkgWihlhz0U3dmbzeEyUmltZmtth68UwGGPjVYYhg5FlOdSzZG2Fam52COQU2v86+LT2nH0Y37Ymz2dQi3jBqt7gWr13UDcqUVHevrPShZ66fUzNbZ860BVJvc4wspwhqI4BJpHfuQMdwYBtok4kmUoW4P93iYQIwHiqm+FMKIa9G4lN9QYiwToySyh9vrVz0635cPTzO5ot52N+mZSejfR8Do+gAMVKzQMNWfpMnuLAFTGpQkcEAix5cTOzuts9qoPUZ49HqlYogDwjkE+0GbyFHlp+d8zQzjli7eKYwkDrCMHYiogbCX8atTJ24dd/TuIKi5T4OZEG0Q7MsHcMB9KV+waTijmRh4PMKO/KhAZNnoZ01ZeuPr2+givnoahSp2EV6c9nPeKvcQ86XcLG+0PSpuI5Aotr+MszKNZFNO+5pOLFVPrg2COzmFh70DaGCyRHKw2lx4em3tbEeio0Us1dfZqbzu5JY6/xrDSWkE0F5+G9BnS8bo6296G5m48/9uehrbDQawU1X+MmIeQgFQTLjMA+V4cfcGozqn0N8lZXUazocICPcN7UZm5MlCcRV4MT6fW+WstDA+skjfQM0+J9JnQ75VqXb9mlUosbGOzEIBTDwAXJqMm0Y77qokQk6nc/dam8vDQDn+CLFQvfFugw3cM86hKsFUtfOD7enCQJf5tkq9U9rVCafSVFmbEks4vyOjrfVSsJ+wacj/MIaTYgzNktaKZe7SrZ178cGaNFIbMeTnIgdta4TXfAKm7AS+JFrlNBaYApt1EUXNiMAA6YxQvQ1oMpy0tkYijMkCLTz5ll9hc3/vvVBFuJwMsgZI7syHoKWcY9zBdpKS0Xi8UXTgO5bfbjPLulGqyOUZm2h0ejxWhlNoTnTfXsKuM2AnPDAHYb41ma3eAedgyvZO9YK5NNDpXENOUWSsN3NMjJXu5dybkeyEDr0yqRgU6AUK9Y0OatGTgySGWhvsNtgnVGXtZsmNMU/NmzSdE5xIipCL4HCFe4Atqp9UBjqLSiS8rreTm3JvX+cr7vYiRbhrgfsEGKwzjzQB7p2sat0yHfcTJFO2OD1mxxA9PS19CQyDmZHvLtAU5cKMZ2fwpJsSrtnrZls0W5LBGqrW394g8322YoxgOi3ba/iZmKJMhVabV4u5MyRoAxiXzKgFAyZ286NDuzLHyJs2GkgDrxiwyEkhbaRkr9e9fcRJuNks51DsOEE3LAxvmnQOrEQICxdCk48qdPXn8jYKjDYImDY+BhwtRe6LudrhsdspwKIqaEJQhcY7QrEWR5PpYLG20UUUM60DmDDcgL7jYrKrIgBZ40UnQFm1gUtI02yqSUvOAGLNifOb+QyVe2N4gjK/gq8TGy1LERPlezkQqoHPYm4OjZwgombruaAQ8WRiJwQMv2M5JCNBtPal02wizXK1R55PV3Sm/NLETG9TwtiaEe/kM5T7Wcf+KsHhNU6M3iXXGc9ok7AY09a9ldViu8ZiNYe2KVBYCtrVIHIXKCWWH8xGLRaLXhlgyJKf58OmYQbg7OYalJjfCSnCZcJHbIfAWHd9toQ5MAK4N99pBoyRnzXXkzifLAn5vQCgF4g+OMP0tOHpx/pzpso6b/rnX1mdTjZlPuW3XdEb9Wu960lLuUcEs07bggSOaZsjdC7TIGN6qwIzuw12rryXpMnxNO+c3sRp1uvjVV+yV222QZK1l0zayQ3kfBip7iciYiITkvm6DLeEQ4yyA40oYxtKgFgkfllkixb/yuj4GJVZ31+wLZXO5vkscl9po3TrNKIyYja23i2F24nZR/YQCDbugBGe1xwwdUeZHKVOpvA8vcDoToIx3XdOAIgRXWkripydSjfa6/QITafzkKxP7LbI0uZAB7Fi6baJnb7tsWF3qprSbtqZTQAHweJeVJopFe9F2226K9Oyqw0cE1qxdR4Shu7y+WCsdeiuHP+FKTt4hYj5Itk3mkKCsFjAbhJKVh2nskLSIGpmKFO1/gC0tpNN/ozALczPdEfPbw3W6kTfKZMNmyd7xttGz5763vqKuLpWAFPpXpm+baV9NRNK6nyotFkoFkMPmf1tYpAoM4OWHnwvKh00GwmUKawqThJmEOhKLMZ8IpDRfzA2ab0rHESm9tJdOf6fic7NCeo0sf5BbFknb5fPjaUxU6YP/po46Tz1+ENp46X8rt1paqpIywC8iIUCoEFUGQ+U3UrvRCwZnFXsi3q6jd1PEBx3MJ8OZNh9/f+Zqf1dDwaxBXd11k5q5VWTRpg8MhOKRRPtnwKE1gjWKg2bcnoCxgDT+m4LvR+22aZM/qxRAJhpb9EpxmoD09hfe/VYOZq2tJ/7IKqy17shTQV1xvUc6mtQQ5tMZJMDPjJTnzmpyUzh1+iTP8H3tJmhEUadI4xYB7FZvcqz6RNagZt5CDPQFsGFeWxTDlthceKscyWZVWiDWLF16D27g9OAKqfASOy82XMNTN+xzcHOcwYits4JEbmKVAiKFzg+rAx+DZMstGnHgIQv5SD4ktO8OQZQtteZZxw7NXoG0mAxH0EmUoQxFNhprtiJHTs/j62kszVvY7tBdhYDdplaMayhKssUeCX4qhFbzAzq53kfyHQX6xz8uP6Pm2KqtvTdCTy8AtRHMKvizlmb3XxHqxFQhtkrl77RrxlG644BHbL624HBkoYC6fkHsFazpIpbpzvFSbapk38jqxXAxBBcuz5tHzPB3HKLPHYEsOr5wbT990Z4o21gpgRkGQTX1sxOxki4BiLpdRdLq6Ny0FVitJ2m82nGLtadsLG/ywWWyzTwVk6kjwGa+mpj/lj5uq6hPXnyUVfahzMfQSKvBmJNIdSg8Y8jtEYTbGls1xh5zK9/Qg+3xB70JdhrhZjePN0SlGxABTUciiX+KjXrx96Xr0Xajh25FQDWNDwjd1lFWJETC20a7s0hI8oNhqIlW6O0IWc9Oatlxo8BBiFh8vChXDyofFmn9jSVZ8WEDLvI9R9GRaYLQNMhIdEEBgOFR2mLgeq9hCYSKWOnBjGTgBoxkXhsH5HRuCZtNNUrxM60XMqxMB1K3tSHndbxpC1V04WTjSVHi9wbaejReCvmlDbiWt7qXS4qrGQMpSkyDHvPk70S4BDu4TOAlR+t2u9Hh9feYDcosDIvLHYdsNaH92XRc+/5LHKX5eVU/WJ6i5ZCS8M6CcqWnf6jN83ekyBz/EF/BQMd1eOaw82JQoT2bRoDndBzKhAT0ulBwvCEmxQiEvVM3VUinderXm9MY2IHE/OcL3gSdj8t5jfS6XRjr/t1pbsxdlphNfs9t+g69KnOe0FnzM6qU36ebpsTW0knsRfTuS+ToZ3DSEC6TlAVY1mWW4ntQmNzia6L+XgxZhiWMHbJbJ4zNO2YhdhUpHsXVjjU3d9vBUJ9CIeEF7hFZtAi13N+d8LRvEysd2HvFkfUIXL1Lbtmxz+H0zAUPp9ThKxDzGBIcHSaPB6bMvDQzaxQNnvcrH2rqG3IcK3A41NEV/X4GkLsaC+G/EfkNCAbLWbP95UYPUtDF26Ez0RX5rSyCnFPppwkYwzudGb9Yo7tnXajy/3qzdGk0VUMIsTAVj3YN6J7Asnl5wrTDXl69VjjgEKsIjKR9xyNSEY0SVxfYr+pB8L0LqfV+hN42TUpQscDkKm2IrGmcBjevKQ/Fg9zCu2L7zna+S7xPuUU3hYtsZ/HNg2r0FJEeGeuR56HMvYAr0VTkdw0ZlprtIQHVCCVe/sjs0n1yanzKLGG85uFICM6Fg/daCcgQIYZFTJ68owgBfOFXJ3wJX+vEenrnDC+6vWqiBFUsQV6IeYgKcwgt4U075VAewn/0jpcS6w5lktts1ObqnFL53PH4Zsc29IEgD6Hm1WO0ev0gFT09QhnC5qCgJAm3RRW6+M65Fq+b7Ti5UaEQ6MbP+d/vcNE/gjlHZTTOYoTqCJc3lXve3v6vrz84qZZq8i9QFptuLcPm4Y+ExCKgPxefDgpj4Z2KM23qhqEscY1VF4ivxGwLHxlKdhfB8ll+lplpiA7wWumyWcMjXUm7DpJJrQSlHW1eC8aizFeEfCkgCVgVidZ1QuV0Kd8R5/kqy2IbmeOEuAp3QwnAQ0TCCkGm3rRz6Zoqt7KzZitf/5Q8we2EZOFicMsAo/AIZRAAaj+x+5lmQG8GkRXJyXWAGFtSmwrEXBwp52ncXJP/BS4q10DLDanRin+6VO8/HAzJ32MjpL1j/r50FoKGOirS+jFe7Iwd5/sa5CVQOy7CS+rzEbIOZvrtRVNOEWlKS7kScqGsndpe4Ucnq/XSyyh32on0WKOsp01BzlVhA28JTIycTa0TVBm9qo3bZSjao/RJDEyr5eIGipB6526mOBsHTA0Dtyk051bfPCeatvpetz8NOyDrNATFUI0B6ztQJsBvBKR0rpX1oBc3GzZ3OD4+wxOOCU/7t0bjS7dsDwNT/uVM3sFJEk/eFrrKG4vATfaqcEICc6r/PRiajbunc7amQ2A17EYbf8pxwJqKBE12g8TQ2xR81FpWlPVaaCQ7zGRMBrtPKwx2X4nrkKX72iCHgNA+uX1HttKp8V8JiBIZCfbuCShB09MZronY+eQOhdbYrL8DjajcUw+mBvh2jpC7LUroJPo1DSR18swW4kvtOwUWwN4JQTmHEMm9eItsS3gsLPX0zgeZlz/hxxgSa1CU4BPzuPM04gZsq0tvEFyq3GKL1frqztMoDtfPeN2UeDI21CDyWinC4cCqcx+Ned5k80cw+iOijnZwzRI6lwsoX/KHFWj53Qs/PfCpnSrwZwD0Qmz0kbnvbBpUCFhbMne7tG9m4710sk3as/0i18famBsZ8McEjGTVcrpJLipy79R4zmd1ytxCJj0xyPNC1uWgaIswajFN22R5a2HLxsrxv8EjjvQ1O6O3m6NRrVCThSBkZBnjodMxTagxhhLiE3AMus6I1gZjkLxkmKCnPUG0U9njB9Egw1iiWLsFyLNaobiqnUU7oOoqUyaKpMYi9m4p8FZGmP10n6dzqUJtiaRW0R2SisGs9IICXmFhDH8NBoTjCOQeoVfo9HYoF2IMUCD4ODQFOGgxYbale389agc1JCKxm3ch3q4J0r5bCCb1W4+zQikYgfCaovx82DWU3ISDtcyMyY7TRnuutkzlUKAhEJMPlESphCjgoQaAGfweg75Ox0FrSx+FmbjzO6XB/YILxBhydce4Oohbh5NKlHYjveB0Fq/8d+SPmmHh97mOY3FxvXeyemsF4NTnfNQj3xtY4I0EZVu1ucLSQAAIABJREFUoY0Mj4YYCZTZMzhWFdgppWochdgcpsm2mNV7Yxrrxps5RgnI7lqYAEZpFXyn/1NbC9+gRtJOrZ0OUrHcZQ54Sqywc9fSnvIJTg557bQxSgWVUw6Z42jfSDE3zDs6D9/uHmpxlRc+k3sGX93KaBQKLckh5ZsT5YSzHEsF9gW4RlPq3hjR5CBMRJcpMAJQEzzF+Qq3x0bDmOcTwJIaS0MYCLt+LHCGOYSQ6rCZjrMM1b7P1/HZslq2zobDMvYCLIEQHvAEKA4HwMMFnpscidUJOMYhKkRMKTOVSQnjgJw/5VBi+UnARB+YjRhMtHe502IKEUXU0C5AnfsV8+uK9dJJsFaNwfQKqnIYpjTBgz6nq04uKk19jSDSMMQlP9NKoC7bSsxqTgXAFBid7W0uBt/rl4+Z1e8wu2eziIcxO4BICJsURAu3LCMg5pNeg3odDqLN0jgsun/hEbb2R1PgBk4gK4p/Afg8TBHKskyItV+fmhMEFPQiQXXVrpyzPKBtY8+G9oYs4/TtPV04aDykQnxFcAY3uUedfBCTrSMcZwS1QZXVOQu2YgLwEa2gE97unbf1tINoTp40TdSlCaf2wnXhXdrsqjNEde7gOlmqnJ48nV/JJ7fS2ChWURhTyreX7wRVOdnGzNxf7+wNymyOKPFbR7C/wTbPnWazbTv+nSDLGNh4lMVy7LLEHyFasus9ZT0YlO51mAOecmKuxtFV16mypLy43EMh7allp2dMwqlhh07urE9g9et1A0qhUkLmVAO+qJrGjCec1jn8FoHbEkkzc3ObrqZFCRPDy/Y/VVrAr4l18mv0MhORgxywers+Z7bNSbRmW1/In7Zj6GSqEg/rCUlTgidLoL1eXBVzdU/TjTkEWy+rlM/rDCoq7MVtOdnATOzSm2gb5PmeZsLaxNg4SIchoqvzrC6sPwaY2fqCue3iK+Kl2IMIg5lV8BldXubSVYNfx1vjg9FW6StklD12UhYhAcgl5HpWTleRxY5R1q59sMTOoJZArp8AAqnYSKEtGNDRcMo2NNC6EI8teMw8pnPIYGoLV0nSlx1MoXtlipLMdmxqf7K+JDz9p3FLJow6R3BUMV+oNJmUxmSZWcsRyc3YStLmpWnWre2n1bnRel4GeXXynfgjlnnMQUKJDFrvSBLdAzuXSVt0tleS09U7xpmlW1MP2tz69NIgGhLZHbhH02Oje1Gc20uhr8Te4uoiIBSiOkQa26Mm9opOuowFQIk6xKE9cLIjeJUEU1m8B+wGSsllwf5QA6wKqqzkSgU353LP28XqmFEMKBS03BTyOnCKGawiI5ur67p0MJaK74JUbXkNAF695wg/bb6qYdOwZppsIzxZb9laDvBKN4fJbwvYiZZihFNvM+mYHSjinu+pW3A8IMUc2BF3akh0VOycwPpBWTOWJ1o7I7uzYY42KyHMymSbBlnJx/rw9KYd18FAbd6oPeZYIWemfcaIMWz29hU7H0a4imz75QWv4T5AWQYsW+KHDvnUsKs+AcKi2Mv6YmJOjNYRZ0JADybmiJDlug/ewDF74/bzAWCxAS6phOHOiTxxgs/eqx8PnmnJsUbJHkfON2t8UqxdAGwgTdW/jFTuvD6IwBSw1xUTRFedqCVR9bbBnn3rw8FMPDSIJYqxU22er5fu6jU8TLgq5Gch2+KqHIF8Zs1XzOy+vcwyA9XJn+UUOvSWp6UdFjKLBBF3zEq8QbKYLjMPuCxnW1YgDspn5beaxiYcsNYEOvns4Jp0WvvES5d/7vo+i9fhgPASlCMLqCER9ojdGmId+rjVjb3GMF6duGp440IyfEvP1qMflTt3OT/jqxZvdpj40Z2eLFoRWQIBBBDCnjgB1dEW7JZWsvezI7H86URDbCVeORxd1bYidoaZ6u9z6ajOn/6FZXECAQZ4A+S1h7h6gGm/esv+E5G+bakwSDy7jX4amQaSvTGp0cIv8fWcGNkr4um0QcrpkdeJCdJF+MguT0uLtdP5qc7hoc+6M13rl3BIT5smdO5s55sRsvQcG/FGhcHKTaQLECQ8HXLGkLg0PoUaJLdvUDnHotch5ZxfVzB7NS5KBZ4WxzQabjCwAmzobPLsJ5MJi4a8QS+j2IqgHyDwBO6HXwRLZDGiy/AfCgarml8u9GmLKZYnTijmQYESCpQCCW4FQziqXiC1w644DRK1+ysmSycIr4DB80VOXuz4HAAVGABCFTTZefMlBboFyxiSL2v7fKa5IgzxN0+/if2KiJNW208r3cYn1tcloe5Cl26pDdQSzYLSuvLOaB1j4NBncxXbnXbSM6apj41tuZIgzBrkiYCxIndkeLinMWUvBOzEgjmOX8slG8xfY8Y6sDWUIFlWIYoCFfNQOuop56QTMXgIxIP1CVfH8nhZGB/H/Wl9WK9/PeJNuPMZr0xdVzvj2cnF7ozBWjhkKiTc4QWg4CXcfxgCmAmUwYYalKUPZ9P1SobdxkOXqgEUTI/Gq/1hbqqxdwAGFpUmRzxogtlHOV8R+pyhi+cpwOq6Bp44WRwZBUssygk33ZpmV+uxvDUYQCwEV4ZwKzW8/AAfuAcurCvEOIm24bZ4TDARN4Il2qATes6GhjZDTNBviWq+/K2nXal6bRdGFxbEVpgg4WItCBP6/ZjPO/JyizEcM6IkLR2q054LvavNAXn5JGtbYJ4wAs1ZOQWkFyPOitphpGJucJxTXGF+LloUJbyCJqqw0iAKlHMrnPPiaXlXyg4CxxAlfgwCdjqO7myPqvIu1pHJsguhVIt0ujBaBjZqO7Pnc10ZrF5+y7ue8kaLr8YAo0BIiID2cZxMDc9MDmDh48H8KTA3ZnBqMGGU6Os8jnJuJg7rL+/jpygpctb1lsbX7OH/WJziiwLM3VYW1rr/+uXxGIqNBmGscY4MQxFbWrmfgD4JlJNmidIjj3FOyPAIzUEzCeItFpMSlFiMsUufRZ0i7nXiZQ4eykctOZArR3SVKWxPcz+Z6q4zFtZoIZaFR9bQQoNqdEKlU4MHDCi9gnSUuVghekLCizrx3hukklbn+5EOPY6DDnEal6fT970mFDvu8by8s8tK/dVrcRhCjHDpUm5CzQZMnVDJasRZHnt0NrHdCw+42abzif0yL1virSdeAFj9s/3kkRipFIiJA2hBnJWgEGWzLK5tdLH28jlsXLiwKubXEOIV5LUzbSGy3XZbqkViNwhCDa0BXKflTi/66XV1R7YpfMLVaah3fNo2vdevqx0nEnnSdLiKpfZi8DHd8aY3mxmLgiPCcG8/QWFFEmSK4nMs3dFn756pcM9hvDKF7QZWhWdGMVa/GkXEADECHjIzlMQMcgTOvMxhM8AcYWrqhXb2oDuoP8+mlhyk098qAbb1OLiqHV76A70G+0vgIQwu5ZTuUr5xdfc5ZWT9fQ83ALyS8IuEgBARygKG8UmevgCw+k70J0+ohloBnUjSnWAtdGVbWxiR/s0Nyqrd53K5mIqQWHLypAg+d88qfGZn9RG7NseKZYISUAlr28zE5EoJp4BMH6yh1Fdiv3K4tEQpXOdiyG4gmECZscrBxJtObJTOsySss2IsQgOrNQyxerOc6V+jH0qKd0mgnBwEhmwBVgNaxXZzhFA9eH6ThFEIDwIoDKSVIGROmQlnJl5YwOYQhXiaKNW0FKMzltqM4znlmb0Ha1AT680+GSJpnzYV/T5XosLnyj5pAg1CETkDY9IVgDRuQ5XfHzoFvCQCuRgFXjoiuMQ5iMUbUiBPvsBg9VOWT5UGClxl4S6yCe+nbYMw6z61eq+b1FfMQFTqK0ioGXnskFcdnlshw3YucSdDHD2Lmrs1YliTD0ssk96LTOv2GLTqZarSlBuy1Vo5u5xTBNCJsRLILO2kGiP8MmFEoi10IqCi1kUipz4Rye7O6OuZk5M0jEG90Z1qwneV8FJRWV7U0QCcOlMfhAilQQBTL684vnvB864WH97fJ8qgz6AqSIqvXC03ijyG1ghv6bYQS3bvDFbVLQYEAK5w9hgPQvs4gbFyaigIE+nMpiUdhTowVmbDQc3YUKYPlg5n3RaoSkKlBmThOfqUHmFirhqTA1j27JF86BivPQQlMNc1xnQbKGo0icVxV83ADSnPKDqVR4Qv38Nz89r1v8gi7vSg1R+hSErlv7IxmiofHq2z/pwAkO4Mk6CmOjFBvid7Z+YxjZ/ybRLTDFY6CsY0750kVjtutSens/Bwg6RIDKj11ht2gqQYGkOe8RUy+hh2CrDarYHSeiwvJAkSxgJCwVy8o9GKyyy/+MZzf/Te3T92dHTlZC4iRi8CgXxo/+D/Pbjwry8c/OrlC2JnPTYGkUODGuys2dYwU4DVMVpv9ezn1DKG4f76Shw+gqLRHJAgQrlCR0YRMZcCQWaDwkFBU2vrz4RcthoamzCuVtWoq+siKCHGkF7gfg/Hz/IFBivjcsGHPF9LiF8aiClLWzeeblur3u5LmP68f9M1v7SAVwRSyssv4LfvBfu46voX2a1RQ3UzCmZmo4TkaZyxDpxKF+ilySSs+lflUD69HaB7t4s89XpMbpVo5pNvWoGIsj42S4k9Sqvd20nANDpMbzqH6Oo1HssszctsXBjjwIaWquWYsaWbNAshBi+gQs3mwpnh4SN807Mf+/pnnxURhpp/gVFVQCphrzk9fvXx0Z++Jdefmv29Fz3wC5eu3HHohYznc8NJPyqMWE/CX3eX8VAqYycQr8SBiA90G3mmvhUsfY609SQ/IgD1xsFxLu1Dex1aikKTIMMSUIJt0UdwPEFLtgkCLPDDR8BhOMFFlDBKvs7INnQOjWaqmPd5zzhl9TlAFm5s/BzBuwpIKQTFQAj9zk8rEaFRXHWRGEWcBDeJnM457cxR/Ssxr7+0axT6ihPTBgSJDsf198uxNSoN6/vVqQxLGL6n/d9jPggJZBbT4HdOe1rD3tipmPNCvqgLnZZattAMxeekM1g2isg6a8p63yTwSqf3KTI658S0U730VYJma3yxMfPNI0JQ6U0KAODM42uevvnnr//BAyfLr4dvOIEnFzcoo4oD7Jo//etPPfudN2/+jw88+K7LD9zXYM1ijnqqWviyxIBugCO843PqUoc6d6ycHkvX75qR2DQColZ2PMBnY0YSIGl11odnACXBG/kVECMNELcRJiJf1KWteFcHiJ3pQlvBapXfiAEQEwAfxP0J9uqdHsAC8MR94EWKEigMvq9KbqtC9Y1Ark3CPv2cQ1uiLu5e4Z7gotqAYyMs1KAs4YjWMb2b7mXIYsE+Pcj8Bs+diAcR+4ZOhXtOz+aGBWhdpJKw4EpLWGImWE3Xq7MURhZBktMsOedP+W2eM9vpDO1RmNbCt0ebQ4YtFiLonMxpvDy37/vYR7/k9m1SWGsLK+IqyT8BqGioNuTCewXXTviOZ579lrtHf/HKld++eJGAF1XzZU2k0DuqHJf8GIhMH+6Eu0c+szUpFkQFtmi0B+Bz5TB+t7cMHiiyzMba4Fi2iGroaK29foFhce8h+VEcY3o9BaYIsOT3TwkLUh7QQqZ1FLLuLHzIPwiNHPZWKDQZ+F37/BkuQO5DghFWNaydy7Bacxdcc7qwwpYQHjLU3COgGLqERDElFpIa/5jKO1Z42MZnvbm/GJPXDjbp5oDoy8cl4FFMz57wsk9nZ2LCsrq1faM1b2es7bVsiPFSSPb/wfC2g2nYFDs/e/23EPd6oBhKCNwF79/5xOOP3b5rCPkVABBVs+AGQ7ISdxohtRsLqV59QXn50b1/dnz8vQ898E8euErOAVVr3n02Lg/PWWHC8i2Wy8ZUX0GzbgsqkYoL3HsjZ8OjTzeIac2HpnkEGYac0EdrpRduQ6s26jKpxhgue/0wTid4HKcIsPhEWd0cNKSZR1M+ozHHtrvurEHDftYBPuMCPnjXFs+aAgg4BXjVCHvIUBGt6QuaudiaBlqDlulUeqW91xP5tQSUzE9xIu5xmtiFNt7qJcwSNFhvzOtFWrHpipEc+RRUL6LK+TdnE2l0ld9CJ2aQ0TmMxYeiwkM/f+eHH3/s3l1KZVWsoZLIgitMlYqqHI4JwERBC1u0gsvV+u979tbL5/LOBy/dVq8oOg2T8/t4Jui9RMJ0HCOVw/nt/l56hjhEQG94OfY/C3vVQRsZa0ZgnQjLxaGS4hH4rBNmVb9STKiVD6sRsD/AyfQyhNNMEX7yVLynmwEmYBI9WDZI6l0sdnSGaadiIxz+rBRpESCGV+/zA3exsBhdqlUm8eCVJG82wk7lIJ5eE9FMA9KhaCyhbcpHaUNVYgm4g4yyx4RXQicvhbg4LE0zZHJaiXlIsztpGDeUsmrgnkZ/5XR7bCQThb0Vi4lM3yAabOXw0Uj3zsd///X37mLhAy6ACVUAIQ0UvPvipZ+/eFFhTzj99Lk9djJ/29HxVZ54wglIFaGRoEL5rbduvHJ+/9sefslc6AgkTbxGqLJyANOWVjuJe2klMgozq6+U/ZC2yIhBGolemWCrAxi15oddpFv9F4mEMx2IwBJS92WTE/OUT+J0gl3HJ5kifOqIT5zilQ6gAUIPAeBGAZ2hCcGNEFQboLWaJ/TZA4TIY4X8YrD+EIC77/WccYdKkFUjGKmchROSqV6kkjBciJkR5AwsrZRqg5veOr5E9+hQnZeQbSGZHExwV5lAKjbCdvxLrC0T/GWmBZHdQDChds9RxMcWRtLKIUF3pWcmLX5f5gi/7cknXn/nDhCe+4OBc7i7ipn83JXL73zk2vXZhTlNqFB6T6Ak5atu3vmO28++6ngOGAkVR/pw2/myo/l3P/fU37n6UIlcQWSOQUMmKdh7/8mUw5+nwekgEmt5bxeRz+NM6RdxMKd/nw6JUDoqkGk8gA5t6pyv31r5ioFKPIHTT+L4BZF71ktPzb/nCK/YhxAgJRwtn31ajuWMhsAjWQtRWf+VHE2Nkp+v4f/hOYE770cY4bHqyuj14VQOfEnnvAZZJ3QyZ73ZzzQXleNEOsgQv7eKsLeusBdyJfYxIVdPd3RuTGNC/dYeuZm1P2w0ksvMTPWK3NPi914QkO+llKgxTH/YC+CWrzfeuvXtTz1NQAPrLWL0KsEDRv7KSx/9xQevsTKxK00L0qvAm0D581cu/Mqll33Tc7f/6+euAzB4QWCzxNN/+53jXzs8+pX9w3SzcIwSZqWZ0fzO2bH3vWfObiMhYAoxIenfhL3gpxmhA+oGDWceV9IBhkIFoubFr16UFinxjmVgBqcpI9RXFfYshJp38+6plBMUuevkRiTwAH6vVOGy16XQ8timcRNsrZ/2X9fnq2LrDx41rZ/oASPefEmvHCCUUUzslOrsNbER1751etKto2ZNV3slaInMMrF2iI0wELkWl4newIgLfRDJ73QOycwS7uTthsSJlFan3qj+Ctuqb3S5pC1esSlqr7y9cOwVY7N6DxPimqrMljiZunjkdYkOr+/95NMh3J5h99DHjvielzz6i9euFvBi3gRgEbzbaaIQmhORu8XsRx9+4Lte/FB1u6oucO9EALzj1p1YPUSm/f24ToudF3hO750cb7xJ3E4lZHLlCmZvwYWk8qneupBdodDyYtnQb1kqtHUSB30xLhJYG8lBUszUAgnzYTkBMcGjqNM8sfDB28vqQUDYcQGw6yd2wsWAVP+pI9BgZNb4yVjDyjKrt1hb/mSL+Kv1GCGvdPbqWaD6g+XadGBWuFUFmqGz4CvT3QdDEj21z30nNdJb/h0DLm2j0dBjBCwMBEwI0lNAeppDcGhRCUyBwNo4L5bLyHkuX1pPZUKrzu32OmsnkEH+YgmQsURpiZUM2lD71zYEzNGV5+Pa+uA7HyTSHusx5JT2Ne3UgTUOdHC+Dt2rFnPhvuL60y+7d6eyxhaQsozU3/Poo//ioQfh3SmMAoUpUAbxj5iBYr6EkITJP7985a8/dHXxXVucivaK4+O3370Pq36VitwyR9TLPHMuhJznGQwxa43VUuR0kt71zdSEs3CMXyUHL4ezYfErBbnaEU16kj6Z0bNnsWgcbMCvvjEQIqwM8wD/QR4vAuILAKufwwI+PIdYCGVqadg0jpcaj53X3ZyMG//ZVUeWeGxW2asBYFOetkP6qk4zyDZtGurPoIkc07gqoYSaeC4E4HAqnmahp30hNBEHKc3M0dGXlNILqS7GUcUsKBMDXnI2jXxZYqdiXFr9YGU6aiYIqsTudA5juRcx6iu2uTTuSTNkg14J4qp3SfT1e07sbxqTpc+ZSmnFIkQfQUkBpFSdf8sz189yRqwSTyJ419UH/8XDD3lTSKlwzhTmDSgMpRSEUiAixeIGY/D/25Urv78/AwgoEFKNFMEXndyhlPRmdKYm87nALXM2O+GwE2uom7rVb1mTioICAeYQEPgjPIhrlMbDrOh6xIYEStvAYg1c1UdxEWaoLLAAeVxOpmZUNFWAFex0P7rsHewpg6DVCDgVmCosfs7gvKTQVQSh11B5k6nK4kV7xi8gZCZvmIUZcTAAXqdCjjZEV70M/Eaqr2tgS3o9gRLJlBisWbk1U41yCoiIalFCTEtKEKYY1JcoKSpUNdBbb6eUdPgclASs8zfIKP7v3HQ9iZZJYqUzYmlf7ERWcTTq6tyFNNxMoNJ8w/c0u5Y4FTtTrgkyr5NnXWIgU5gZsQcTseIrnrnxqvsngC4WC033eHNv/289+hLzMxXxApiU4ikwhanNSnrRwMiSc7W5geIdoD928QoJETNqSDUa8bV3Tw2CQguBEN4JpAy37nzSGtmStdEgLPG8kS9DPI/AHG5lGlrN4vXY046IUw8TvvYTy+rEkj8tZouNOGjZYWsov3WWfOi65q1bPyMMuRthsCjiR3gEwQRzhJMTuVf5rqdO8bE5XqZCpWzchmoQTRUzLI3DwzFbz12ykji+ZS+cTB5VV/GpwOM+Z06MNVLPXGyQw3t+X8LakqoonTpPgZ8XopA9yClFKcrSqTOaQMQWleu92u3OSeuV5Kc/STQlTEejXsOFei0hIo6jaQ/3XkoScdeJnECYb7+UD6EQKTzMJLF6e+b0pr1a37J6e/WVtkI0tcArqYi85datWjov3GMd6f/mIw/f1QJyZJTCinIRWYM2o3SlM4JKEauIWzFR0fn7ilmorVGhpzg4Simw1xz7x/ecmTnhnAQLSLlo2TvMUn8jlFU6F7+m6mvbL1s06Qjg5otxAHgTV0uD1THWCMW6RMJWxF6hzmkNMOLqrVVMGM2Hp0SNprkqrCBP4PQZ+Akq3KcIsKpCiTvH8u778rLLAvHBiUDWcUjHajHCBho/c+v2DQnAdyJ/9IK85oJ98BjKRaOq6XGRGaVwGYCmB281aP903O1tTbh839kAkSTgRcyXKq6UQuEBziGFmtHN1UkJFmIUhdHUFcJECWHMlrM3wRHDT73hIdH9sF3A2PlvI1al3SvS2HFNliLRmjoHV2F4X8KhhFYnNxmDTbFqgMgYSHahPSPgSnhRmM0PS7799k1CPU0BEedpKvaxw/1fuHJNYFrO5upFLJACXqAMreV91aOFEBRA6TxLzGHyWxcueEEBR3inoJVCGOQBzCmOghMRB2ccYBKdc6yHMlWZq0K8T/ZOw6AjPQgCnymHb+EB4JU5HWaQ4bmgeVxATePB3tikGUxDzjgtPuaVkdnCifzdcv+WeRnOcJwHEznBqFy9e9+pQTzK8Ow0BJekuUob9MU2abkiyhsGrWzIyKMzpFDKsT02qwgSTKgGpg0jYgU+4+6w6bvkuErDTKec6hOlSSEyMzorSZJOBKUpbn7l3Sf/0iccDVB6eC1nfp7OPKZjf/p9jiKnMzkyQlyVVpTHtFC9ScN08qtXTYU+cXpitWn1VaYkK1Oh3xnF02WhvQiATKTdveqeGRT7b7xzO8jeFKHyyjsByR998EVKE1ipmFHoTWgkNTRnocAcUQiN9KQXqqlAKbALnDurbkVmVQRRJzBvoDioSSB6hTpIgZ7zp0HXeKYCYYJVhFUveQqAx7AnVeKvEUpsbCgZJJNnnj6nqRUeHqwHLMxQaiYg+UEeo+rZ+AKDlYGZGe4Fv3sMQKRycpco1M3P343ikwY/hdlGF2suLAiZJ4c3Ofw0BNOyalhGAlXdeC30RjoMppNuiDt/Vr96R4HXE1c621fzprA7rz+5+d037r/pCMIHfvmBi799SdS70nklurViTDitd34e440SXBSSHf2Q7FSYQ4AtE4UxO/je5oCx9a+DyNHlvZ4JcHszgwm0hOwuOmnp+rgnjTM7UaWVDvAi9Fa+7Pi4toYg1TQh/u8Hr4g4TxJzL6BKJYH3QgHoRWimEJh6LY2cEaJUCj7r6CTAuDOKQ4Xe7rk9NTU1odFILSi+2WGlr73j+Afy4dhrhGPZOb5MEAgsfROLKhKtoGrLJJMyfEdzHN4lYiXa6NhrtbDZwVdxWEzUJLdmgBPgAzwWGAUv+GBlnPeCYPakjx+DXuAMpWSxQSOEeDnIfd3rJOKtNQjd10cWPiHeehh+FxFMt2npsCq5QbfFNIBIK4tH3JSXTXPFGczZzLS08tH509/99Cd+6g/uv/kexBzx3F++DqsqM4wuZ+QYkgpJfDGtEI/5FySOVyIotg3PehmaehVhPnuUYK3QJ6jPKT/sNbIaWm+IjDKFBHGIvubNvcQPtaSdqomIf8vde7UYWHX5fdfVB+878eIpENuDUQ1CFcIL6Q3AG+6cOlcShSsdQ2/dxbH+rYP9t376S3/wwWu/dzirgQG8f69YAi86kcrEpGfAneXAsYuiWXGSfNzK18vrJG+gSx/It+Iig+pNrPoZ8PRuyRTN+gNsv0cNeG0qenaM2QSkh8j75biB8l5gsBJHLTQhhT1OmJijwPW5VQ34vOOqk42gqDSEtQxcZRkn9Nn/9G37fO0hP3QUjGc4mSLVhva5/uuS7ci3KUfL+LtXhb3c0PLXhqAqLg/XcAOv3gvFSA2NcPVMuOBLkZmWeZn3AAAgAElEQVR4d/TGu0/9g+vl1TkWfSFNcPeN9269/fblf/6ACiS0chCwkGDeiMq8pflsE5MrNVir8D60wWl8PdEMEX3dA2NNeNpcVLppYCcdlSN9S6ynk5PLIXvSzZ7R8mlLAMo0okW2R0PaKX45Xb2usI2GRSvfMoF45b6XUADjRRwC2yTO4BXyO7MZzUEgnAMkCooRCishKiJfe/PGO55+5mfuXvyhBx/+5L6KV8ILS9JRTWXvaeWPXLn893Dltcf3vvxk/u33jv7dTEAlvaCgzIVaybQFYl7hDGIMxpmeEtwiumznjIGSXzkiEu6kjiyXRAXphQ5dCrzMh5mzCV+PMd0qA/Ia7H0JDiRIkSlVdX2/5LyBsTQ759Pbska7eKwGqOqs6LMEBiKGCecJCFVgoHxYjqsOBTI55+0J+mCFGlHg43fxW3cU5RAeqRebD/BMz0PTmS63yMiXZz4rBNkpTU1evx/A6NAKgPMhrnoDanv5XsFWvvl1fu6gFvYq1V0gUUkavMLBXGC8K4NZmVFQunLvwwd6Q0Xq1DcBefavPosLVdwqRU1BXzrCgwaCe72t6HOySDm925D0t+zlXXKInJycSy+rFCNpYv4LDdqjV9eVPm2GOm9lOkqkT+PEzGSSmnEuTbzMhYC5L751tAyrVdU/+YHDixQQfpFVnJMUzgkVkYu+/K7nbhH6n925/2+e+MjX3bh5YITMiIIScoglRQFT2gf29/+nS5cee/Ejf/WBB8U8RaEkRSsfSBEvEPHiAVMJ538hLKKHo+vSEEINZqcA1AB6QIm9mPag/fiRfyeZymPqIgv2ehwUYbQw6YyES1pLeiNRfqonPw5uOwUUnR8AXvEbuP1JOV1ArskdxykajbLS9gG/VRocwGyH2Qj9fuZHNUJQNUaCN8TlNnJq0mo/rP0YhCE3j8931fPMlE6slYrxVV/1zZZh93KTCbupNhBZGaqghKkB6rxQCFMjSShMzKzw6qyQO3r1R64Spsue85DCML82f/Ybb5BU23PitTSgMCkFpdAIL7WHzhx40Tm9aQfwTIeqTDCEuHQpjTbyW9Pk+Hih1fEmBpiGwqaE11f+PGBgaWFOI51eO7HGFo0lwsO9kmIqtBreWbbEMMyFJlSKzq0QV1EInPPrb16/Mi8FwelBvv/6jZ/95BNvv3krYB86mhmlhFhJ1XA/Ap4uHB0pVrJUCCWs0OhodBXZYBSGe7v1EktYmE0uPlevAV8JlUApMCyMfAf1z0Zfockk7qLVBOLNOguaEIEQRlrqrKiDrRTk2pQKvo6lfKS9SmNVSf+tjo4pqZcD342TUJSB6ZgVTRxgLYgAyO+cggB8jQTKBijAcI5qEKvU+XCwTgYQWXhfFrNB2pcUvKATbBGQVvkMAkmDbn+Znjr93cosJB1EafQlYOIFKiSDfXApcxUPk0u/cHjxX10wEKSiAFkqANz4ruvlI0emZWlqBUAtyxlQVL2K0dElpjPupnFYDEemMRbipqZpWqj9b7tRYA5WiCG5laulq8lgDI01LOkx0L8058NYKjAfCrdno5ebjCHvzk1Uv1IDwPIGCj66f6ChSijc6q0qKZQguyKFNsOcHjBH8hEef9f120FxJVWtor7q2H7o+tP/yzNPvv70PkolVKgehSpIEZSVgRYKIZxBRErzglJhHl5QwkgTSGCIferalNqcyNmzNaUsKJQSCFdUQTFyPu6uErPom1QEFNgh5EtxsPjASzvG9ceLjVTzpavyrRWgM8NiBtEVQYoEKDCz98jpJNXtkwVYAgU1NAh43xHE48xYLxJTVzzTK55nbEcaG2hVtXYXzDo1VWeqoudZII/VicdbD/C6Qvqz5pOAXJmlQ72Plb1+DTlJtxieOHs28h5Ko4R0BnUmDM+UShIqRqEKKdf+9jUADmIoFxUHosan/9oNQalClBCULIz0EFNPq2kfe92S0pbigwBlr39BbFsxXiozUZuwkEhAJeRp2DvXk4nzej0mBvFeGOJBjyFyrsQhbq5fIAzmeALzj+8dhLKh0DFziV4FBhOlmsLoSKHS4N/x1HPheW0hZAx5PgPwZXePfubjT33PrWc+zZ8WoPNiPF2k9IyiyhOhGmcGcQKKlnB7Xk1nCkCsNCUpzupKmWYRKxGhtWYwBxMgqLmoppLheJnf53RSJJYDCHwWLn6B7YUZXnZFrf1w+ZM6LXNprUFcQG8f3liIZPbPCknSQW7RK9zv8rg6B2WKWNlNLhRL9ewCKETxndcgC9lxJ9MjQ83Th3p7yJDvWt6a2c9aiXULxaiuSsUTUuLfU367dNNjR+ttkpf/dn4Y+1Y7c5e5WOf72JAaf6q9UYiSdv9L7+mzhcxPJTRnEC8ojALhjDDR2Y2Zv3x69KaTRSVzpZM/eeXx4a8e7j25BxVQXShG0P3QQmRZ+RmblvaON3YnsbOJG01jc7F5jv0aO3Dt9Qz6U+fCbZeHWG0E8oorEbEkzfERTcOgRFownevE8CbQ6QRxECmTBpAoX37/+Avu3ausb6QK3B8/mP3G3gU6MaFQAQrmpAr4KxcOP1no2+4dBQYXoAhIFQn6avfmk/l/fnz3FvW9ezOKqIgZTfeEZuahpQasRv+2U/95p8fvn81ooAMhEDoUVgbn6JWzonOSLXypYrC8qK8EeOoopHD1/pxFgQ9Sdu42RQjgK3H5T0mxgA+GkGk9qy/si6Oxi056v14Pc/lLxpbPWdvQzymAQd+Bp25i7qZKYjlM9aUAb8/xpx7AI04k2BOM1s3ZwC/Wl2dGj8yMgTFvsSWuigYeGLyYqThghhtzedc9m+oBTETrBHLq/WsMPPWupI1C6v+qamXEoCDhX3xy6523bv+lu+6uu/DrF0pxIgqYiTlRUOBKgYBy+FsX7v0Xd/0FD0IkVBtKIcXJ64+u/tMHETyv6ehMS5ijozIuzu0EUvlIJROkJtYcm9teZNO7/kzg1S7ua/fJSefUetnQnIY56LO2ysnhpksI08buCZ4yvu9CliRIoci+8atu3QQ0TCqghLzY8ycefoDmFN5AhRkLR6PIHLP37O/91OVLL/N49fw4lOxVHXUDZSJ64O3Ljk/+xPH931f5iMzUSWGlASJKFuag3l2m/4e3bnzz/ePH/OlHVT6pM4WvqBbxstQspqtPFgSGA0jzVjgIFayKc01DqiPPrDWf35rOM+p34vIboT7w4tLQIq/gFYn8RDFKrMdfB/biELzVu3yMr8p8+pLaG3svjn8Qz/hJqq8mCrBChXxAGGIqn38Bb9pTGHMPgKWbWW6C1hoE2rIPfYBW/a85oApnMlcKrxH/tMTdcoL3hxjAStAtgwBW+5N6g7wYZxNDaVXkFlAx2ytu//l7N/7xjfI1pQAnXzTf/4mD2R0uOubOQD8TMaipCLycCI/s+EtPwkkqphQa6K8Zr+uF9x6IiAlJFQc782uIkWfdMDRGbsUAWQ4RGJvSdn1Wp/FpjM0axGMlhNux9aS7Zdc/aTQxrDXtxpJFOfuiLXrQJLFXwicMfeaiaZvTTu4qtvudaMzgxRzVi4Hkx/aK//Lm7X0rBeHsNRE8WM4/ogcfOizAPZKhbaGX0HrEC+yuK37h8OK/Odx7y7x8sCxFhFBBmEATcQCumf/q+0eP77n3a0FVVootgZWi+Jp797/u+BhwrynLbzyeX0T5u8XefYjBA0UYdQNWdlQZi4BwQEH5sfnRR8Q+IUUoooYREIoKmBFQVs6HieOq5e3zUV74AVx5QFQJgatKmJtqkCU0iTFbUvu3ZheVCKc9/BYHGk5xEOWW+TlFBPx5ufmzuFNZV6D/ZHgBYIX5XPa5JF+xp195yXqOTSa5Za3DbKPgVyahhVyyKkVZtbeuCHXQQWr2oOBfnuLx+WLydOGEjyVO3RnJ3YV42kE9navKRGCx/FoTBGh4incQX6U/MBNh1d8GEMjxN9x7+h/fmn/50eJmIhDiM8rDnz+EMsh6sTDQFwFQUP3hew7uv/2kvFoCywyIQjh/7PTBn7gocwcVE7pgx6wuhKs0fqr/KQFVY6m9BO+V4P9ycG1OrjAHn7V3YZBZf8yLP+eTTiC1pEMySayctOAgbqwTV6UbRTf5LRoEoHrnhST0yNsX37srOCOiALz15PTHr1ycawHORbQqL1QzQuhAo8MnnPtHlx94Rt3ry/KCNwn0U+iRpxDg8YP977t8ycvMw4OqYgBE9OF5+dM3b1SXAAjwC+b2tadHP7m3f6QCohRxrB6ZiQLmRYWYgyriIHOiKEw853ROiO8o53/upPyW0r8U/PcqR3BGU1UKCS+h0pwQiIcnRIlOZiuE4eVkT4bHUheOjC6gAhTgF+PwO3CwyJEaBdJMqrAuS0gCGjYs12MF9dUF1UNrcRQRlV544ArFBPK/yp1fx10AIg6YYiJneuJonpGeBPA7pWGuPQBoxMu2cDyYJa4CspWGFk0TVm+cmOAL94SFVLppkzro3+l9Y+nwme5gP8izqrFYes2rcM+qQnXqXOcIlVBSCE6FONXCQ0IltH8F5GpZ3YAkTGJx7z85Ovpjx8ICKEQcBYISUMGsYKkEoQ/+d1dWblNiAplfK5/+C7cIVdN9Smks6ARljqVk/ddGoVzagb1R4oc+f6kc9BBLhLW3cm6v+hjCqz0bmSbso/Xs6GsePMgCPnFAs5tvKjgXEecJFKbyi9eu3i72RJxVzwYKcZfnJ//w45+4WJ6SAnMCGDxMhGrwpTPxFHGQ8pcPC0cFgmmDkD7YgQL8uwcXj3SP9AHnijcAnvZX7t9f3Hn8AoXzl/b2nlXnzUgW4FKbDZ6Ig5mBe0YxM+VMYHOU4uC8XKP//tMjERr5zaU9fu/+N89PLgpIrzRhAfNezUDCC4vQ4ydBYk0wS+jDTd2E0HDrgOAL4YiaDQ/JbvfOdOlfDOVgcDWiDCqWH9+tJGs4VAC/w5MQBnhGK7wAsPoYXdZ9XT84x4kz+MhRGeGkkF+JmvapWpb+Ndyq2lRXcKeMFXEMNS+FogRKwIASYvhSx8OgUVvmPHQN6LktsNX7Pue7mX/qoEm4MFfjvKAsvKFNqB7iaEqD0UMu/tChPuPq9LRiLoLbf+0+6SGn3gwwmoMI7Hgu4iEqvPgrly7+qwtSPVNK9Xxu7uZ/dePoNXehnLMsUJw6lNaNper+TzGqJt8Hq00d5XRxTkOQGHpDvCIvgXjafxqH0uoT1evvEEOuvX2jE88GjfUgwxcjbbUVM4yNHY6zNkQgUZiVgMFEyFtu9iPXrpI+yNUBC8bub7x38s/+4IlXn8whp6G/jVGcwQvEOzoxOu/3/uyde1fmxxX1U0uz/vLFg5+7dIliBgo9UHrsedobT46/9t79kEYUQYnQIIE/cHjx7ScnP3z/+P23rr//xnM/fP/um09hjjBHigoBm8FDaSDpVchyzzB/56kPH6mImRfB952WDweIhiIY/xZeoT74RKiI0BJ3g7Cz00FXoWkxqv8qOdEh3dvECQCh0eysJ5oN7BTXHVPGVCNiqKnpiJL8XvPIsz04BR/HaYU/aZAp6tynKXJXwCBwUNw+xR8/wMv2wyzmJQHZykxn8k+DFsir/pPNibSqC8OzOmpUOsDj5TP87BE+Ua4spRMqq2gkCtOad6yhxErWD5pCvUJFAarMQDoahYU6GqEuwHstHe9i/uUnIFQKhlse4R8qZ0/r3nsPVSAQk8pcSFA4wMPEafG+vTvfeGdxrwSpUA+ifLR88Ocui0poSOLUNfr5ZIrGYssgqc2KrSGdWk0sv2aWsPuxKi/UZS6Z2cOxs1lNTlJvUHIw7W02tA1lolNhBekriUUBNQDqy/devvDSk9PXHc/lTPnnKHZlbt9w67nLpf/g3uweZtSS8KFRBDxF/JvvH/3N69cXz72UKomlIvjWqw9dD8wXncJAqijM/ud7Ry8t54SpiFGcEOC/3Zt980n5LcfHj81PD+EO6T+v9H/69PgQ8puFHkNFxEATCalGelAKJ/J6K/+H46MQFEQrQucvHh7+6tL0SwgpIQJR8QaFmagWjdtfbcawqKnMJb+3jK7Oop4sMjhCvAGH34/LAg2lgwIVeK50nU3ox6UvSSddgpn40Dov3h5pfGfuT9BfBZmpkcev4d4P4wbEJtuIcLIASwIYJUQg8rkX8IX7cUQlEbCS46SQoaniwNJF2ZRGPjZOJzSIKMSkhDhQ8Dul+3fl8sZRg/K7x1kxJRYyygnHSd27P9cZYQUKwpzNSmfV06EovYU7PFGI0NMO3rM3/xPz8iG/vMKVoKB80/zCj1/QExUvTh2hoQUWAAelYPYs7BJP3jQHTA1Uhu+evqp073UHT1wAOBMt4QWa0or1SfLTu9+LdXpXkmPlkL+JnEHG4lyvzj2Nuno7Evbm3XozpwnzMGR4hKLPTB/J/oYdg5cqsy1GNZYqjsW/Pbz4+UdHnzYvl+khAk4EwBtO5n/m5s0vOjk6mNPBLhpuOActXnR68jdv3Xx07gMjpGc9YeXvXz5818UrAqHRO1MrqFICX310+9vv3pMgp2fQoCuFLzN7yJcLabItsvZ467x8HfDTxR7glRpoMqOJOjUz2qOCh0w+yyjCUOj0QTf7bqcnqoIitAj7M758Ery3uPOKWNu+oTZFNrX8YGXgJME7MxDg+jU4/Ao5kEoBSoHWFFoJjJWDUdBq0iwRZiv8SE12IqnAl+X+gNim8/DZ2ec/hdv/J+4qFrh7UqTCtAEWJQgXQRHlIw5fvQdxXcgDA4Xngw7AcKG65AOmkYyasKSoAw1zhSMAUXff/E8dL4oDFsUUldnS7gGWiNTFWIlCuU6SJh3d04ud/QpWDopiFmqejKpCiBOFQKGQUuhEFGr6Hj39+mODgHSQoOqwwwJOiv9nT9Uqb1FaMGUQFVCc6N4HZ7e+9WZ1Ep/doFh+Di7/75f1FF60flhy3LnQVwuZAEyJmsTO6NKrMe9FvSPOkPwxxFBFbHeGGvpnGuGmPbFy2gj2IjPkmTg00o5CAGrmxdGMXiBCK/2xwy9fOvyCo5NPm3sRpVDPjFQoIo/O5287Ov6aO3e+4c7t/+bm7b9w49lvu3P3JaUBtgjt1fPbzT33rdceOjEoSiq0VDrCeJH8yefuHi7yOAINT6cSeN7qzsQVhkTxmtI/Vey9G0KBFxV41cLMvBMBnhL+zMy9T/E6ysMQEXznzP0HpxYUEWJvI378+OSbTK+rfFh5QogUS6anv2fDBNBVyEbImehTAfvLuPzZmEnlIh06Bp3ZW8SxVNqGKk0aJasRVy7JPpiVxWzlj79jYRP+fdx6D+5XjF81qhcAVv/5Fq5GoWjoX4fCybe/aNHVaISXVV6SjsiGazVolTUAyU4FZow5VBlDgAIMuXCPV83wM0d4NjS4IBc5qilkpdvhP4CtNFsziMTK+VMQZjiWIlq+1vhy8iWAV71nhDOFU1WB0YsWoOiTs9M/QnvN6cJPJmgV/OkXzPf/r729p5wF0KWONKiKUZQE5b7IBT1+45HUXGjUMH+odE/Z4X+4KIKCZ13herN77VxGJjzFcJvQNErOYbNi6cgcMDe0irAXgfU2Ws7JzSX6Z4/4t56FzDE47TVuaKlklFIKRKmGuVClZOmc0h+54pcuXfrMk+NXlKfCKmKunhvLN6w96zrCpHYO/PeXLv9/e4eqIERYUlUNhP25e3f/+OmxX1JotVuqiobOO4G4OlsbAeADTv71/p4z/6a5vcXz/aJwogzng5nN3u+KfzLTjyhOIX9rf9+RkBngL8L95PHRQ8BF8D8t/Z80vG/mPmalSH/KaFJODYJgvQdAIfbZKP4OHpwtqUiILNTJZ1buWWhpEJphNsPUjpISTTAOw1vItIEQFD+A60/KaQDuwkmwCTH8PLlBCQuBNxAKubTPX38xXrs/ZBWZ2jqupgJ7B2Yb3frQ5W15/snyWRUa/qDfctd+7E4lX4MKjJM53xaIKlRxu40ArLSuq2sNevrmU/uTfv51vnzRCQCho/iDX9s7/BsXZr95KA6GuWCmKAlVVXvx6TO/cX1RkSQwgZoaDv/lpavfdjU404Ty4KqniIYWRnr/tUdP/tIfAOJIH+yz6QG428Ur3v6K2TPFku2vo6hOkiktq6ovEGZ1Ke3KXFv7QOTYXuQosXrlX51YLR9gdS6Zz1L0Nlwa7YaVQ2il3UfR13I7DbxIT1Hx4rFolWynAifqvRVA+bXXn3vHs88aqwewBvxdeLhj4bq0vDsplB8u9t/24mtChdFX5kwloC8+LX/j+g2iFDoEtdDCofRmMfsb+8XvugNT+5J5+b33jhbrVAD/7YXDH9tzBlHIPzq5/1VHp7+4736wOPgN58xCfZhf1jwZqDRDQXg1fLO3H53PSV95fIFCfOG++03sCQxJPRyAetnpjoPwAlQIC6L8s7j6D7APaBB5GiCYE4WigPhGsidx310NLtq3JGoHPX890o0lci5i5gzJOsf5e3L6Rn7kLkxgXJ6l04NYE0wRLjKpy3T0ickbLuEN+xAPiLCAhJPAt3Rz67Spkf4k4IbRVUOG1cnNRjitBafCytGEvGvyM8cLl4uKN51Ia6ZFLBSRypAwkrSiUUVUGGr7VKALY0MBS4VKEKnDKrHZaiwPsDOkRgOvLupBLd9cnv7tk6Pvu1/+R2YX5ouvFQLzn+GPvulYPiR773cSJCIiKqQI7jk9lZP/+LSq4Anq3/+fvS+Pl+Uqq13r29V9hjskJDdBBkVJgIDPgUFAQFAjTgFJGB3AINN78FQUlIgPH6AoiCACzwnhAf70yRwIDwEB0SCoyKQoBAjwVKaQ3Iz33nNOd+1vvT/2rj59+nRV7e57Ivf6S/8O/HLOra6qrq7ae+31rW8tcnzbeuWzYfDpFTFCRgJw2cR7zKurB9f87NXNOWRZHklfFaD971+X99QBO5DKXBPO3rcXMk9tqGgyLe2uLXaXaHd3zC3kSl9Cg6EvVny3vWRHbE43/CpJyOmuNnbH5uy+Mh0+Gu1eWY0Jeu539sxnKUgO8J9X1t+xf/Xm7rcd1WDKh2bqrE0XRExmuBW227cNcIIXnn7aVyojGJM7qeR0Ol9y9PqzR5HNSGXNquaaEB5w8OB7B9WXjF8N1d9X9sbB8MH1aF24hnjSwf2vqapEyJ9fjy7a2AJwu6gLYz1U+ESFIy5xaKiRoqkz0Rbp4UzhT8cb65o8Zkbo4oH9vqppA9LdHbjp1xMFXeXch4Q2BDhhT8Xqf8FwwhWxsePr6GrnHFprNyNVoivHsszWjilYJQWNIvv4zKzGfIGcMMAvts036pqJuVdasN5k01AGOwjCU25p7i75+DExNce4OGJKaF+SLkILDpvXicrj7zJt2+w4fLDm3voPGOgOq41Jr0Gp6fYE+n6nNCJzlb8uJQBWi04ZagAeaJQTte+vRDjkiK7Q9vS6udNIRopCPMStPx0fefeR8Q+PkZ7TDJRgGAtJQ4XN521snRVFg8scrkAhKKy+cpVXV2lzNpq2in7tk4/4em1uIXVQo6IqiZRHOZXuYeboHGQJGKRrHn3N0W/LTexzIULq9p/5Y5vmZu4eOlwGOoDIbmuDlsl71o8ABQaYaHHLxCKmDB3mC20Xs9v6Ae2KLhR4WWGXO0PHpe51cJjxapq7w24GC51mEBTcQdIdCvHy4f4nfd3Nn3rm6Z9bWUk9tlPDYWp2pcNp2wQGiXesrv3jsIJT0YnkWgXA7jLa/MFjG8zr3iYRh7yqsvNOXfmUBTKQNJeDlw/CE9YOftXshw4cuCQMRYdxTf70zdonlUrnU7aO/f2RYz8Vx8TIUSWTqBS4LgLwc+iWMIcEZCL5GVYhAIrdF/9Eqg+6ZXSVR/vbY/CDXFlyiulXrUx7KBwPTdDrd7V0rt3uicOdCBPZCwDgE76xA/Rpli+5icHqokytwbQJl7Ia4jHrpGUDKk4SodtZGqkdoc+t/bE4rGY3t1RuB7/779x1m3bvs+Xvq8I/uP3TSLLkU2caAPFE/Hrnl6uMqpSG8hAojxaY1rZ35taLxjwD/CBpbm5MXOa8upVBgjG5+Ty6PvInR+pvGW8PPY3zwja5TgHwNee3qHrNKiFSYupYEseD4b9z44Gj5gsKKQbNT3cc0+oHh3nVD1Na4xtIjr6uvuGxN6RlJbejKjzruE71g28/gM5evIUUTiXVul7fh8IvDgW9hL1BNyWNgceP5nfzXh1kVUf6IQp81dEpk+8wa+itM3Yr8dtw9u73ulOKkkgPbpEB0KfWVv/kwP73rqxvGb4x1msppyCRAZYDaZqMHUXhJ04/dCQkH0wzenIXd+h11x25WXQSTUeX0SThsfsPfKham3QO1obggOuzVXjzoPo8qYT7xJ+o658YbRHG9E4IDOvCn1XVJ6qhqYYTFlxudInO+G8h/IHZdSGcW8d0Q/1yVb0jDKBIC1jcb+9rNBhmliZAggE8D+s/CmPX1KwCgdT2/tu7CLWIYIvtB1JZN2KCSGyFhi3BiJzgqjRNQwB/nVf/G+oJQMjD+4lHYZ2QWYQyYYdprR2jHrkfByaVHwMBJiULC8p/6Cn/odBewfc0r9DnEbDFiGr3lTsqpCohqrS+PHFuuO7QOjaunJWxplGkzBh5SONf8q1XbOkOHr/PV/+QPFopqZqg+SHBCElEtfXsrY1nbWAdYTslNeF2nx57Jo/B6NYIX/bhPwUZGZnqkuSo+vRavM8o3tqhtILONcjRvUerl6yGqytCZDDUMpNk4NEHXrt57hbAwdXBNoLWnBlbAQw4RYOPDIZfGaJMrd8bktOBz7Cg0G1utbG3Y7HkxAodHBYya+io8aEg+rdXhI72Pj70NRJ2UGJz94Y+b9K2d83lNee8hbm1jmINgmND9JoCrxjgr/bte9mpB983WPnn1eqz1eDqsHIqfbWWMZAm+odX1393//oHVlbMQwSESAgMgj3q6LFHbBylUcroKnUnvXb9wPm67zcAACAASURBVO+tDyhPq5XMXhOgKvdrzciqcjl4c+erN4+tK8ULevNN6/2D6n+urKCujRUYI6IREoMbrUKM42B/h/CyCmfJTpf/1HAwgglVckvtwO4nHt6iINBAfyr2fTtW5nk6Loq0mgdnMV28L9uKWLbNgqu7piYIIHoOvfYvwp/Dq4+xnqrCnagA+oQ8K5vVVxJ4za3x8GHz3eVOFM1GKi14lRfz/i9nthbackGmquNw1wHfeS0vq9Wk4ZX7N/7HA6wZLbaI5LZjIBm0L/rDPT7b4xkRzLK8wZsHg0cNKOQa4nxkIABbz45bP7eRb28JDISbECk7jHDlcHxODXhQGokrqU57OO1bT6++OpBiMJcHBQ7Eo3fdvOat14AhM4LNALL6oQOnP/Jg2BjQxkLFKKvC+JtGX3jPF9NmB96zvvp/D1z5O1cA6b282W+fevqfnoobhsHmk1VtHqHTnQEdlxF9jg9z/zKjLu9FXZPte7MR594Au4tihfcPWrTtKLP7n6vEmkFIu6ksFAcRdsitCiu86NNlo8ULvjvyyN0dMjfRAaM8MqquQJfGcJqZEGs3ysYWB9El1qG2KMDck1ODAbV7znNmhEGrqj941XWnxZGDYWqwubYa3PnUA0dQgc4oGHMtNaVREUETEZS/ZLT1qI2a9ITQaJSLwP327f8wEsAby0Ny4pQEOHxgoY5uFNzc3c8EDtMUPci3KgS3jpyDE0aAlVZ9eSCCcA6rD+rQ/taJ2VQ0e7asTMSCN5aI4rHINr1bsgN4SSIC6PnBF0G9Dkd/VF8WvYl8zf8x8cE/oaDMCfjyWfgn4O83tgV9EwF8obZ9RlPVGlmz+zT2SoPluzbzvnVA74F27eQU4V5VM5TYiYOu0C59mIyAISp1EoEeGf0JGv1+Hc+M1ig7gzR+0Mi/tZZEVW3zqIP+aG09eWMicg0AEAUMX7xy6nccPPX2p+2/19r6z68CiJl4rsGQjKI3nnbMEaN59IoMksbC6kfW979mnYjmCJoYjGHrbkeufck1cd8IMqCG8fofvf4rr/tiQ8wzfGxw4K371j+8L8jX/3L/1//QLQ/9wSEeWTHGQqukNu6khIwpmezRbmI594hpmpxE+kwUYwvpqLqZmN77p0SGNRd4dWiz2m7UhbILe2/4bl0aOpMiZy743OvTVdPMeYLKMsFI2ti9JmBmUYhZIzEaKDoiTMMIYUi4MfWO1O6e9FVBCEGR8ReOHj1NY+QI+jTsQMQvrK8cQZWoLDDHRIpJEuHmgieS2O/i9qiNWvCYlhyw1AD9xytrH6XJxBgAUyDgUsxQr6qju4DICJeZXamUgWM1QhWrwurtCTLZRWZl2Xdp/UAqPnQN/iUzUWcZruiNJQFui6bV+aLzHTN3NQnodsj+Tlstc+tNDFbxac1e8nMP8J2HGIIrNrxnflyOg53CcXw3i7Jf6qSpSvOZ2XVc5xvkD7uaWZ82gfYnUKGwjVMxqxxjumgVYFjX+FObfsYO6poGfBLr37HPEa2pbs8SLet29OM36AzXJA8QGn5qsPrIdX7GzYYCBgoR4/hov+63b8hdU0lLYpDbzc47ZfjhwWSxZ2Yi/Bb1VR8+nIfm1GpIl0CE6uow+FAIYTD6hmNbt5dBLieCGG9zz9uEK8PGt2/hlLjvffvlIZgnRYsIdObVdHBO5dTXQmk83e7tEwoNfXbzu/9jhnbqdihtI6s6HBx2d4p1w6y5iqte7qoDj7bh4xLBVlvgNzqFXChI45n9pySqAkSHMzrA2twUcjqfQ6gdRgOjIKcwBuAWLI4Ei4S5SLo7XGfFeOlVV4vRMCV0A9+xtnrhvrVoCHVWg1rKF6SSfH4EBCU7Cbz9yLF7xbEAKDktDMURwbPW1q40CJakY4bgiIaBJw+IGM7E+CsJ0UUJURy46nQmZNCUzv0Epq8mCk0KkuH1ftpDMGQpk2Rl9Q7roIT7aK2FeBnbi53sKikm2lNBiCSjwvfji+/F9RMfohlm+oR6nXgidzb/x0T6iQyAeJT6yX3al9IdlDwast/78ujKj6PTQX37XCiFkAV7U8fTQEiKun2FS8a4IhF14gl2y82dpPPKig5UDfUDG5PX0s9Lj5axoSx5BvkFVB+rpi/Y9Fwef3yrfqgrmatLBvBwWL//uv07DAMwghDcrK6PYvT4ze0snNzaLZyNldcGkqlcGRyC2bFQXWGb999qelkaZTTd16OfFTdvu+WHHBBcMIJ+4BUH199xgMTgK1X1byF9PhGkCepANjPVN0yVegut8EuUUoWV3JkTWOgovQr9bApQoDGaLk32cl0lf5ypCXb7MqDMlbQ38aZjV92zfrefVgcpOPWXvNaSJE9zVk6ncYCeD0CrALpc2cTYczaXKIhyIHgyH3L89tEbzhqPuP3t5PXIhfsPXm1GBSJmmSxdCBKJmGJ5RSHqgjj+6a2t5PbUfLeRxBNXV/4+rAguGVibhpG1cSCvQRr8dPj/29y8k/A+0zEjYIaYZWYMRN0elXMiTsNpWL+zhr+F9bCtQptME4Uxf+jbbN440CPPatshj+N8FthYWZadmohE4qsYPxvXHENEkxCwTceceHzRiVcinPRbCmnBlZZXunKLf3UMdEHmBGrzIPisq0K/t0K5UUIHU9oR+m3LfuwZTLaY6YNAMnDg/O61dKsFLbU42du7izAwXxPC3d3hcDih6Ii0IUkYNWVpAiBS9iryUwnNNGI7AsD4V71ej40SK128CqmjSYa7wpPVqsyJSKy+eJVXEB4S8W5yweJ6GL1onP1lUieKooCKtnnPzY0fqbNZV1TNVFuJK3+ytvqhdQAVAoAgCrJYEYgTJJvRoAaHh6e96JTkNgKAsswfuDJd0D7v7i4GTTs59fbw9zoLzOny86z97zWG6CgLTgwLMM/Qoc1noaN8tvu9HU4NheYOM79Oip7dFFH3Je2tWu6+PjPYrnDnaFeyF+i0bMfbXYAl3xNmS5e0z7jL2jFX96T0FqdMCt89qu+/keMK8pUUAfz6+r7LqyAJTsFiY7kTHIQLFUIELMS4bnjBsa3mED65Jy9jdbENo+qM2xBEGAbQmMGEakz9z7oGcEGs/3Vz/OSx76fkgzo4nESd7SdhFRQ9nTxFmDuOL9bpRiD1AzJ7x/tyOIQavy8tOAeVTGpT88XsrebpZ6lKTveE6IuYPuwmDgg5SKS+CuG92LgSW5qaIlXG490EsHoQiD4whoRkVYTBjo6w5e+DPXl5+73S5rml4z7PnamlqFPYns4DDwQIEcavHaQnLFUzfQLzms5jKngVwQFg8trdJZqzWcklCigI1fDJVWYrhWyHKPmhWk+Dg1FUoOhgLdHkUqxPCwCDnIrp+bTrKKIOtSQpOA37xhuvObb1nRtpcZT8sYjAfCRs/uJmXIdHBCqZsDMGyvY9ZbU6XNWoJ6DKQ52byhucBtfgmuGhR5zBo6EkVHgGssyd2nvxRy822s18ePPqfXvbyXcYOxV2wHUjubn7X/TVcVVLkNlC5ltzDzGDq9ouYJsv61yYNdd7okQT1nbOJbXIDHeipPEB33rWsSMZmSe5J8yoa4K9bHU9VfSIGvAAwYIj1hxTiR0L8BhpDx3rdGU11vRxf224ukVEIkZRLjXqKwZJxOgeNR4X68boPPx6HT+xsfUjcBPcaniIlCxIcRyDNY8wXI7BiUZipXGa8DXhPAx28jBadjI6juatolzdufOUH9/8O3+DyIh8tws0wf+WmyeqsumkBlgAPjwiVSkINRCchV+YL9UJWAjMvR1R+TyL+V5otYO4neMj0vIIEaBHArz/QHcbAMlsVF+7gSPJPZrOFAKymDN8nJGOCDhRk5JiTaM3TJBt0UVt4VJWF4dclDNvrBm49YubOitWbmklRmV3D5iECMCZ/PvSJagpBDcKQK01bLxuc3Tv8UTHRwAIaUJJNf36duPxQ0cBKXNEsNzkOLh8dd//2D91XZmK7InDTnB25drhzR58avW5QTqxDsX0bm9JdFpftmGm3rmzm/aQFOUS2ximDvqkV3t+/K8ZX9NuZq5EhN6NWeeW5LpLjR25N20SrjbOr+M8u6X33bdNB3Tu1ebPbOYGs+EFm/XtRluGFDHokpJLw2+srR2D4CEmLZTMZO41VAmVCHisGd38EMcvPnYUU4JEkoBdMhxeMmQkyGDZYMUAJ+Vek3TwueNankQFDkbCzyBfOtq4+ZgUkkBT7g5xEAFIFayWOzg+0UqEbGzP74m1+6tyyOfYpvdjKaa8yO0fTz+Lgp65tJawKOrqmHbLkZanRu9sdA+XnODHUJ+ongwnNcAy4pNjfWpccwMIQE2ElvNfrvvPCzoBfQGAsZfUWv/pCYK5FAXHuZYD2FF9Db+v/Khzu+Cbw2RcRlWxAgyqJMJI1E6DUz6Qm4gYAoDqGcMk4sj6ck/FeNa/Ok5FCTciQjCnuQLglCXjncHfraz/9Hp4YwVnqg+Oz/YbPnrN1j1HyZRHUDgcwtvWiBia7DRCgq77pWOjW9QipKGiJ+gmjtbesn7ogacNL19r4myTqCtRYXbwD085dJ9Dq58bmkupIaqvnDSXH0KfBUBbhbGE0em/d+eV0gpNL0ss6Xdjpt2HQ1m3YFvhr3XOaAeFC129DparG12hQNdViHhKJPm990x3d+ru86SwFuPjN7eSnDI1iKTX5cPBq1cHcCpE+pihcrB2iJ5oJPcaqCoF+vDFG5oqCwopnYp84WCoWEtKavoUgAiYu5lV8tFP1rqXCwipaXEiLPuVlXCVgaoYIEUzh0wRcA1Qy6sTdwomRHwPk4ofBlJeXPHATnN2P775pWA6W0zY2zuf9pybAE+lC4jUZzD+OLZOIlIonCwnSoFbwn9Ztbuup8KsfGLtisUtQCdvWXrL40TRPhGttzBVhZ9osllI4ACocMj0lhpHPCfofQ3HDvMmWNJCaku6pXAOdR50M7eBdDglFCbTeUOozSIwpEdCwUO8FuGoxe+LARQt5WgA8HO8+sDAPu9BA1kEkAuG34D6FuPVl66uPGGt+v2q+qdKtQuVweq71Ftv3vRDJAWKoF0VDp53cPXPqvGFdVxvAmiTffC6hwOq3rkqiwEk3GmUGd2+PBy+YaX6l2pwfTX4YhhevrLyocHa/1k97aLT1t6xj2OmxinAlLBhS2QyOh2npn+dfRYKTDt35zcvup9ya9MSxNZ90DauaGbjDgBREinYhl97Acfu0+gNK+xtLex21mhjNDvs5rtrsr3h1nP/PudquDbB16xWQ/Guo9omVs/GH9+/7wpbcUQ6ECrEGpTnumCsUgAP6K67+davJf0W4QxMfvG0Fw35Z41hChDlMJPLIBijwHXxTfXWulL6XsjB9tSnyKdY2DIKhKfELIoiKZpzAB8HG0ThxNPpENItUT0Pa4cQCDoi+1XnLPynqTlF0z/LurfPzn1lKpTd6bpzkwc5952OtGKmEF/HY2/UUZ48JcKT50zT69Gn4JWnATUksALGi5Nwe2Uu2hEarfJ9cplDz9lS2WYtm4brx8b22iNfQ9u15LmfyX9ApwgPA54inAMScoM5AX0C9uKA10QcC5UFFw01GJiqaxwEBF8fbV1W40yfurwEVH1yMLhrWrDSzCijEPfVYSN4Ku3BLNQxVmbj+CM6+scbk4tFGq7ym/3Q6fxcjArjCzeOvmgjSKK5nDQJpN/sB29WfXQAgMFCVKSJbmb0aKycZnLQnRZSjrVHQ3DakHBTZBXkbQBrUUdQtJuIdrcTtm1Z6BTaZhLbjdg6YnkW7WpcFMOVQLc2PNH9T90mpSiI1un+S1vZsQ0tlfhHTPoTe1FgiWAOMGgMmRDPqv1XNkfff+wIgdeu7v+Z/etADQ9unrRTjmiCozK5GxQd8MDq0huOfrOPXE17GAFVh1HfY9++r4JOg8ZptWNgDZ/EXP1cvfGcUaQRyYalsaR++LB6C71yHzMkSwgFIbpgDlFuRIywEzA7zAC3R3D9NVoFHEiTGps4ODu+6ds6Z689sAn9jzEyjUQQAD4GV7+S1+wBv3FTiXDevWT8aA2NAQMNSrfjokXAks06QH2Ppoo5ih55MbX906GpWrQEOV+DBdVAqlk7flhJf/q1+rpkydhFOMXxBOeniJcJdwQYBDC1FTpwJ/gfRlxuPAvJJBrB0qQgC2Dt2MJGNXxmAFilpEGAMgLxjmM9WlWsKljtTrlbbcfM3ekRcJdcIUD1A3EsoStZ6ioPf1udcudT9dmUDD1aecN6ddUwkhldwVMx8ro/PaI1iK6xogWgHkiKE/G+u4JgJOGkLHVEGkJNS2CtYz6bkeB0zMcLxRt3F5jatl9aI9VdjJvo6BeNcMauAOzeyOeSzzKxSO2tEnZgnd6ezbn0T0cJtaTcWYiuCvFlubP8zH4c4yR2ZAyXV9Uj963+2KkHPzsYPGetAtxzKnqd5ADmBhnhbq7oHiLJ+2zFb/axZGaNMymCUD9rZd+XbCCaoaYsVQxjlv044Gci/sbYCTin8YG9Kfglya5TA2MEzBXk8SxUFyAGBcBqkWYRJ16h0GH080QwpratBk4mpWdMhkSLN0v11P6mdVot81FROa9Fs+UF01Zpm7zoIUvTBh/GFoWTSIN10pQI8/10dc37reMbzQTl8PZeP6rine/FNlNr8xvJi8tbieZmQWiE7hDsbdRXxl+778oA6cGwv4AeDuwPoFMBjBMDFhFBQZD2iY+Q3uK8pqITpGV/wopGqB58dOAPifGMCRuEVFXQPWX/hzimwOCUOR02JJ2ApwvhOltH37mRl1swUcMPDPc9bI1Hc2w4SWwFnTka370GA1IkWvoQ64q3H69efIAhOiIRIhkQxMTAGWxMgB5gYrLjUSTNPC1DnbTuQt5u/qmbAWqLGmwjw+YGxexIKOqki7rPqoSLWii4ek+qkOX1L+wKe567QQqemguwOvyrSqiyXtfQufE+Jdk7u4mrDjTWUTadPZ8crcsYRKeZ/h+rl6+uHQlERApdcgWREpBawGBUFEhVlP2cNr5tHHNfSNMgdpXZfx9WMQsjXQykszFnoSjjy7fq23u0ZDjPwGbkfEQ1PExmjkOsbRRoDl7k9Ysc3yJ8ln6lM0onpNm33RmDF3BtkOycmC9cO1PFpags7YWfVsmuJg9yocOWuuuSzUAsQ/hrHHlhuFY6mepuJw2Dla/pmHrfGICTQIXj8u1Yos2wF+u72Pz09w0u1AXZc54uETQCHDuAVfkPwnKqS6Zt2PhRLfsFVNzxddikGsgdpfgKAO/quNTwBuiM9C91EMWY1HQAzAUwMqZz9DOAS+n76mxtEginW+2QIdQcDx47xJRhdFKW+yEf//exFOBKqWQmREmEAkVgPWz+yeaktihGu6pae+i6HzWXwAC4uwG1f86gZJTvKfMrdRlu/fDoyDOviWuRMnekNiUpwunuVJXMgTL3EB0wKcZUMYT1OkzOaKV381u7lUBtE/zut0zjqt2ESvLj3m0S0Var6m4wnHt0dPoItDFeHbQWWiTzJdxVm8p+7knuOAfOt0uY8GGTK9DGXaHTw6yNmkr4o8Q/bEayVo7D2gRn3Up8T01d9OQvBTg8ucRkZrdpYTPAoOgKhkD5KuoLj412Hi4AeObq8JiBMhcdQ6BOvi2eniP6naMe5DXYNPAq+Uzit4J9mmag+6jxRjJhfFf3X0AQcAH5927Pom7ZTPfN2GWEJQJtQoal/0jju7Fh2vduDmu+oKRlzyXOH+BgTR7TglzRuXte7h3/yyLjFvNW3JO60KKi+NmTbFR8BOr3YVy7YCcTbjlpTnS74v6BrQAaYjV7JxV2Ai562Lb7eImbcolqYPnTq+RiSVSUAIULVnSmQabggkm5kw6CLcFcCoaYNfk5OMuzfII25RFquEWNPwI+BHxX8mxOgIqRQnbeNwFuqWUvh3YTwOnuD5eDcLqPAAt1AFAHDzJ8DOEVycefjU8CBY2fWse71JECLGjgTKJIp4wexw8bj+/kU2sjrj2lCkeYRB7UWApuNIDfSGOlpiOQgHILAo89aeOG126MbhvN5jPkc9mmbhnNNO7poD06+gSxM+ClDWG0vX3uu3ZXLQurb231zW6gths6dHyQuTW+5Twj0G6dMPdadTc5oj3GsSMCEp2xNm2wr5uL6laAoVP1X84FllQnp44bSVXQCLXod0hjSENOSEwBgm+mJdG6oaa2YMGNUGVCCqV6xWgcp1wCSAJ+FfEblZH0pHmnVwiDmu6DF3jqJURUJPV04WPgXSxbATfmzE5PLLaH7WHZBIjBBdvDUZqgzFK6FhxwUxD9TIQHqULSdmSvJ/euPMESM+2FzBT2ZJLq3Hja36Hf62Fmh1ZDVATC+7EJQb4EaLsJYJXcoMkQ46O1/7u7VCvV4su9FXpvyhkLq0nzYMfKYPbQnV2Be2B22jJjeO63yO2spBjvHvW9A5LJhQqNVpTMzgmLU38MaXygJ+upvGvVWTZwAHic+CXgsU3voFvTEZOOGAlCMRtUMRUdcm+PrMJzHHC3GqgoHwVU7qgNAGXhVy1ZogQxs01uJOMvOCloKzKCdWTydocAf0Q9Ma0KqfnoXxmtBgxciQqAV6rjAdv6iaOeHVYaNJYKQxCJ4Tur6iuEs7tbrVcFNZOa3IGKZtwXO5AECvzH22bxjjiXDghSaHxQHq5XAowwT6WEAo3a0mnQ3WqwjnsA7VYXbdKrbv0WOnNy2ojPblVW+XE7TNr6LymsdkXEygBUHCd7hyAw29fB3jysjsBEj5RgYGCUGyNjhCQ9xnEHdzYueYClFLBnVisbKdNQsuCQjTmOxAWqvys9yY7QRGEK+owDMLrAQeKTRAgyhMgJsZTzHsCwl55ZguARhiS0J9ycwndj9R6EN1qXxK/ZDtsFn7m4LfKsudMZFqW1ds1cEzJywWSReZPUtjarGGY5VIEA/x1bH8NWZhx1E8C6EfBV9gS/YlPvOkZW6AcKvjgYnyskPE6Av1wsTw9fNWO5mxJpBFJyJL2k4bxKiklBMvmqJUb6ct+BCCi3dAiajEEC9BDa5cLL1KQsB2ISwZvZI7zR9FDY3QMeT1yWKXNlB3QINc8AbyfUuRK3ItUGsI6BbnX11TD8bwMkF/XMtbsJ8UFjP0/gIOGwymGo4YqBo/ukmAUEhUhA1DdRCmSQjyp5cg8bPeUGnZ6Dr1bftiqgaf7DytvWT733aWsvWcUxurVOPL2QpbBS0zYRdkOB3rmz1xV9prxV7ibVQUS1cU4dGqkSIX8Ht1QOtgo/Rfd1W5TnW4gILPyAHTdMG/hDp0tFL6fV4c4655tVJAbJTE6sFVLzTbS86Iykf51LNKtBeYpqdhAR9BjMb1vHl25tNevDZDpKuF1mfAVl2emXHs3kUWE/8HsSGcig7fPkE4nrAcD3wW6lMQFJpgCZp1wGOnIXtjka//e9lbggZ/UE5X6d8yGptryYn4StsXgG6S0ddkxt3YqU/6jaSw9ASZfF3mVbX8E4dY/eVCK8MWqEjYeJYH8ThbEhUalLFBt3/ywMgPq6Av04HURbmaoW7O9wS4MCUw0uAm4/QXznmrJVedWoHnwJhjXVZ5XVvpHpzlckgO8g3xj4BvgZZFZjKedIMgokiDeKdxMe5nhT5R+OeLnsntC/kM2SBEqCd8R9SZZh4GCMESMNQREmdwivNV5pAKk0ejIlVMX/FeOaKEutS1smmEJUs8hjZEzrw80XbGC/i26GGqLH8YU6+uSRBGMFaPVpK6eee7PqOtplwwMPOeXUC9ftM1YHNwcjCxNICt0BSsiMjupPz9zWdzJ71SpYwjyhLNSv9xHAglEnJZlCJegKnbYIJf7shcxQiVq/t3jXW3xEmcto723c23sowlSTI8OAqr6EyrapWTNQsnvE8dnRRQKVmck3yZi8sKD4e7VPFd+dzIGJP18FGB1KfhAUIseG+hHiGYnWQjQyJSReBb+teCngsBvoXyCj7FLwMYi3yDZciUJPdFeWie4pgdVMD0KOI5XdEys/ihVi4PRGeuXbtYbFXnFey+HxfIKOyoz3dR22Tm27OK3MyLVzWqop0d/ndUDSpED0kwW2VCfLiYrp+gch+j+MEA968MZoqefmKKtVq/BWO+7NFt+Yhcw8wQCIctEAczoeoPC3iKaoOtfqBDVP8iIDhCdRkqUAGDhgdkuPPwtehBRQQwUxEpkyyxf1k6afjfhLAKQoG6d6pq6HPZr+DxECGMCYKsCkHGOyApwcCFA0C3VEoGibrH4m+GvGMTvaJGGrjU8fVw8zvRokDaSb3BnMvho8G2gFKIKIh3D0H29Yfcl6vLTSt4/qJ9ajO4wJE+So1/58rfqSxyvCwXudal81EZGRqhTHsCBti5pnBMVt7W8zM9DMu6b/vrsyOCM3Rrvx5twGvbb+uN3dhbvn0UkOcUfr30yPG5taTNv57/44HRek7Yht2yzROdjB5XSAld2itzb2qEQR1d0QgAKzK/R5u5dYjPb6l/b+2rZPytySWNOleIXhrYPBA0Z1M++mTlv87mjz/JXqGAeKUBiEmtE80J61yfv4MU1qgw4p0PQm8q8RGCWaODa3iAD3rxf/wOtmvkidvU7ykPT8TKgrZZ0Kfh/YfSACjxdeB1wPj2Ql1snuC06Y9o6wmbh5EU7IyR+GUWMg2ekN+rrzvIwc8c5/VYOXNEuuzd+V9Z2MLTjfWcsUzDkznZj8ZCX+A+rIAI/bOPTkqLydPCcqAKgM0Sm8/TR8/2on8lDxH+dhGN5oaKlsYy2zz3zjWXLjYk0EXR7xw5v8TEzdzg4GeFzc4J0wJiv25H13iuMR4K8Dh9KoUEk1GLad/AS7Cv504FVgNJCZ96JM8rQ6pNvHqW/OdglJDl/dY6CPxgqrNUZkNAzEmhqSlMbJUHT0rnG8TyQNuTMxA8f1s/fzCkpj5iZTbL70aPwp5/bQlQX1TfxOjrmCDOYAD9xtX/W5FaYTpENmERtCXwAAIABJREFUNoS2aKuaYv46vDc77EPR5xc6s7dy8/eO/Xf8veT0UGYx2nHQ7l+7MdzcaXsayS3EuLQBhbnZySjwVuiumvW6opf4kXanQZfUkdEndS/ZphtKdjCspOAUKmBLqEh9Yxx/9MixyWBFSwZ4OBz4zMHwbSFcJXdU9/P6CXV9fl03X3eQokAjJN16JVytKmZBJ+Ap2xSvlx4kp+ZNwUa4JVpLCFBNpjh2J8NfKz4I4bqUJ0GHmv/fo6m8+RRQ2idxO4S3av1sJOM/NU3xtmuOsOOoQVk7SOKCSGABX1AUTS9WOO8Iege2zsNVokPYW9R7U4lwe74Gk9GkIOA90bJlyEIFwZKv1BexsFpCX7W3r7xPa+KrDMERk0kYAZxtuH/KkLckTY9LXn93KCkVCOBh4B8EHaJkAOg1SChafiqI55vfDuEVqaU7FxNlkhTzE+IA/JNZ6z55xvVhZ+TYxwEyH0qiKlLSOLCCDyJjeFrFlOuaHzmm22P89E1hy7CSRhaX1v73MChldGwrI5sqvjiBWuYA15+8b/C5IEU3AGZaSUOwUEnjNoV7BznRkdeLnSKbjsIW+prROrafWw8qlASV5AP2VvQ6zBE6SmzTr7kthOX1zd4rUKKXQrvR+bTJRW+vA9otYdvOsO0r7kV+HR+kBF1146fy7kV3I+ncchpRe7TPcnDh2tpEjeQOYwBwetRLt7Y+f3TzhmObRzaOvH1r6/y6ntp5VDK2ky4Kdg2DCKLOgdCsYbqz7Pzs0+m5o3l6pnO9j/EwKBGqgdTS4gBcui/wFsSDaAp09CDs4XCd1nKWzy1Adn9Vd4BCKrg1S1Ona36L+nLTihdPPdqL2uJyKcA9vB/Bv8IolVAIO4nqgzipxGJKasTk/mvvr31ORKCacIES/nOGjCz3ryq/k5boHCy/v3fs1tnU7xEnX6tDgPOh0KEcvBAUcRz5hKnwIzhfD1zZrPaQ1N8Cgwt4H3B74OmO6+EwJOE9IFbBBSavqSqDm/tOwqAFAb9pkpIu3T2OGXM/CmsRUbXCOCjwH1n9ZkC2qwq5TAgbP672byM1ZsrbQa1/rOxVFeHbCojshqPJx0nYa/3Fq8M/rpQLmVDqFWgGKYltSoLuMlPvtNdGhGCnSHzuvN6msEaLJ1aHMrrj9EraA+fuYbewafKXXkP2mXbLbnH69NWYi/Y67NFLsEtvb+DkFWPsLRp2gONuFqoN6WJXUyrm+WnNxUm7icDeW2KhW4iUQynGSjCwJvziEB61vnaVmfLyKmaKSSZ4qvJruy44VcSQriD/qFqNWQhUJSWPuALXq3w8ecvUKdplwEPpAbif7Ezo1vDnAYBVECYefsb7Ai9ohgYm0cNezo/WCCkg6RD8wRzk3q28+Muyr8VbvG3xOt3S4KlcCL8Y9cDmKybIxqufzW3wAdTJxDFJ5XBTFuGNirUI1wHg3Wfg7oOmulOnHGCBkpON49Nx33a8kUuBbRVMzTn5HXXAwhMwAaQ/dAtv8tR+pyVL2JZIbtCTyipcwPgGgQxCTC6ehGS8CjjLcYMlTktGuJjs15MKIau0DI8T/gjZ0TmnntIOwk+hfcn5pYHTgtWiBVGBcDUlKvczbfSRTZw+BRfdYG7vr1a/fzUDLEGE9ql+4zjeu44Um6GMCkIuApJaefLKyqv2IURzwELTBo65NbLuUlp33PL0rsyso0K3e5vdpbT02m3ENRM12FaD664ndp9boe98YbmwpHS4AFXQ/vfd3ujd+yl3mequJ3Zbty9UmJvBMRO1XPcpzfWdL/cwW/SydMj2G20WznS8KG4+YOSCW1MoTKXAqZoaXaSl/hWX9KRheLWCAww2jnUQx8GDx8eO+Qec+VqN1KXSg8DrGgiTutEBewj8damN2bNe0EWj30P2QcDoTdSY79VEq2b2kuzBGL4BgzkSFx1PQbBwGyue+rn04bRENdAsUQKiG5jrJcDfYXx/XHE0KV+Q6xEnXKZky+ukicqZ8hCXDNiq8I0B9w1ANGU7k+QBZUQnuvK5XGhBV2A5XFN58I76+bKZHarg0Nvbi4ALCnjrCHVKMBC7Iobaz5YIeWERBOky2PdSt0nmGapo6enAPuHzwEcFhQlXlIu8ltIAJRgf4viTHPCz/cGeAv4M9Hjp56EniqdF/pvxagIuWYJMhgEw4rEBr4Ie6FAAaIlXI/QNji/S/mloCQ0KrCteTJ1J/3bfJrDgIII4+PPh+o+uDP+qyoaGdKiC4ozqvAMDYUEx01xI1AF3OiBaNyTaPfn1hjEvgWl6E3LQLmyfQVTHj66wSOpzN23TUX1Dpx58UW/P7hjpXjkXCiRW0zhsuqug3Cukm2Tt/mjzd2U4Kr6+Cm8I1ReDHUE4RF8TP0W934a/s8pP+eA7ESFY7pkRyb+h/YJVdWK4vJYFQcOaawgXM64rNSBPqCtdJZxv+FKe45mZaCBA/0LcWTxHWfGe7juSX4X+slETcO9C75oJpRJ8CP4yw7cgWZRzpiLW9NOxr5DXnXvDgiLJQqk7i8Xs7JxGNflp20/TSq7G5Dl9HQb4q7D5F9jU1FL65IkiPMkYLMvLjxSHde46/2K/Gh9LgzkMqAMUUbVjXC++EIuuXRaqA3YwVXtMmBE1UOn+I7w7Tl3HxdYA2yLNXGcLVNR3AB9Mw0JQ1ocCchC4FfClLOdsmP8ARQG4JfBM4gmWEaEngTmgiqw1JcpMVBcvCtWLkuIuohqYx9SIwwOsLx3X5ygrsZhXqdWVYXDHFdtw2NDjVjATKgq6efQfUv0DtSFQ0d5rvHQQPguhcqsHvjK2rUrBDDMpTB3EUhu3hHmhgW0U1Fyg1gHsOnbbQZt1MFXd8LGDwSrPGSwBTEuDqg7M1NFg2AGh2mDETK0NnV2Kvf2JHSiqEHXNzR8scbfqKJV2n8PxcFc79kY3N4dEpwyecibyw3+m+wdHm4cmtpQMQJTsXsOVj3ntoUKsxZTYQwBPVXxecqppOOlU0/9vwB8lhj2nGuYmvjSHPw74I3JS/Sfh4puJh3SPysc90/6Ahm/HgAhI3jHz70srnRP3bLO9ZbbmH7SN2eJU+1FzgFRvsPvzmr/UhggmD7WT6nWyhT1nb0oDxOtMPzSwM5KhnIBomDxFrQRPGVOlxfmqoorD9jJlgWWPys6hbRcpDIK8AfzzMahs1LA4ujVkI/UccgPxSxVWHPeZXEfZJCvqgPC2Bv0yJ8/AgMeBrzfcF6nuHgQxJCFCCnUlAqeGQ6DC/aMPUf3VIK9qBLIyuo/gVxge6pCIAAqkOeJ+VNcaPhgIBaP7AFbTIm8Y2sdYXRyqi0N1seEjg+q6CrD0qWhAVKB5ZNK8l5M9c1vhClv2sEi7XwcJlM6hG6OUoKtuZm7m17Yku27MtDSQ6kBLvUAKi1s9lXjcl0Ci3XCn101qhnnqpsQW7Xxsqwz2XoHjRFc7xiySikGUwSPBKIqoxEjouVH3ruNMhe2Vg/AKAJW7B7AGQurrPZvxzTHhKnK7qKc3A0/Lc7pNDaRpCjDS7kyd16wZyeCSEZ8EX6ftwXcv2QHkYz0F4e6pPtiBorp4rCWwTklCc/c23JODzmO2GjfqJtBWObVFEfg46ufj+mME5FNVLDtZaKyTLexZyDIgUoe38M7arXYfGyhYYmUck+7CheKcbwS/9dbHpnDPbYdYMKZaFKCfNNxjgOWjyN0ZmzxX5CxB1PhdhMMTmE63nDITHh9w16mCIsBzAz5OvAw65MxCrJQcXzcrlghBKfZ+ksvKGgAucv/JkRSNUqAkjwji4M2Bb3GweRfgBkij3xjjTEbVcloAVbkCGRVieqZdgkk+AgCORNQ+HtAcYkBUqe11iX12yTw6d7flsphyP/G5k+gSATjdMXwLpfh1R9kUlvl6k/46vr62E0ZxDk9hG+OMGL97b3O/pt7r3w3mUOaDtRy6WogjrNwcVaTDGWgAzM08ysNqjNa0ATZu7A7YrzAAFWXEyChHpAuon+eGrKaP2rZft1/KyHuS4J7nu6TykXRPWdg2E03BqaBS93WS5O8dxKIntdfdufZIDMDU7hh737Xjp2vaKve1Pv7ZTQs2YxXNvGxwrUmpDhJgf4Gtq+GQ29TEeRLxWCdT2PP0LzkE4VKnAgzu0RJzkqyauMNtdh5Ttft+KodWPXPA7gCmDgf29ht6sbzqNmZO2XrKcYC6wGg7YnMWpjqTL3ITlwoYvzjwX4pNeF9eewByRrwwn6/fArjY9K6IOwlJv5jL7cnygQ2n1ixhsjfVRB5HIIx/w3HzgbvDI+AwinCvB0+vQg5lzm8h4bLRC+vAoUPRQYwor80YgzkYA5ks4E2shSGcZtUYjMmYUP0ui71Bb+jr2O/ebMaSAAVJeSiwdO9I+EGnx0Tbx+z1Li/MXS5pUezgVzpMFkrSZrrRIfp8E3rtIabdHDqwVHeuDjpdFbqBWu/XumgC9BLdqdO/RrmhFk0eUuNzbRYJAkdC9SQb3GtQfSAJ3gnAfjrYYathYxehoYuA18HPlT0oOYtqEm4YQPwm/NMwGJXGwB2TXQXhVrTHwpXluiYlHT3+HEzueIS32VgvN4Gl/vcHSQdTzIYnADedezv1MzdwBB0wq3yS8qW64FXwszxoE5xwR0xI12mmAOBS+DbxuD3VnTRODSdXiTBfYIPFRAVf7faAIQ4F2NjQ9IVxx9exXGWtpQ64OF+1IHRcent2VRkp1KRwW7N3mr4aU411waMmx5lK29ctSazcPk1dYDiUlaKY2HrehvgC8X2Gdwh3UFMzmywrG59PILycei7xW+IHgbOBQ+nUAye4jcIadIUGHwiqDIpkMkSp7NoKA/l9hKZlMjcp3bG2t9G+XIExxRTC3SuZKDocIXkJMhCJ3aqDaEIMCEnnV2aq2Su0mlue66jW7bZu72jxW1QLVVKtK/TwnFsqLa8Vdlzb3iLabu5tCfarm4Aph3co8+lYiBDqfXvJewtLhwtRUyXcVQmzRauBIIfBhWgWLGkZEClz6YoQ/zjYv7G6V4zHoMcPQ+0GUSA4nri0XxL9UBZpeRr3jbgSfDiwBVFV9ovPCus0L7gRF8nvm0myXEZMeq4nQjdktwDuJVdCAPhWhRdicIAGBdDbQ4vb65PsFcLPkEy925TX+0pE8dw5T7FwpiIctPTPTpgI4JN27NnY3ETktFX0SaUcPxltGna+XnAzPNWgkJPRk1ND1wdeyAltYdyjvWFNlziB1oQEwik4HbDwK4rP2SQgWpB7ag9gYpSaHoIlXg8FXp8Y9gnnpAZwpRF5Cng1TysBPR/8HeDLpiwXA2B4ruOi7e23MxQ+gcG3VyKCKFq0uoJMA6xh4/JNnj4lwwcJ8bKwcte1ADkiGdqmdpKWLHnKkFCHQ4EkM+vtAZyWTHXvv7y1sFfRNfcvHfL2ErOGbrxVIh3r2E9JL+FuMFoitGpLvFkCNMwAlLmOU92K9Q7gMsGahS4PWLDxcGkLho5uxEXh2i6bCabIhCBEejQcdJwRw2csBjX/ilq1wcJPqX6Zy5Vl7ZPdPgF4+aREQzeRYISnIYXALYAv7NTtpWidl8OfsF3b8eVm1EatXRF1NqlhXkw+A8NfUwo8NM6HIOUzcqFu3cuMG8qjb6zghGewKXt3pcY9n0orZ1HhRRg/BYdPangScLK/qsAfG8CYGtVyrVbYWT7fZjj77l9fysp2oY1vDHQ1QfazXC4RBYKWcqD19QHvEa8E4Xll1/jjYjl9VhJKfaLCt4B30jZy2vFgJcIqgAICISLw5eQDpYuBo2HSfwAjIL7bcC5wG4KkBmACXgFnuL2sCkeCKCBWCkEWWZmDhxEfkMhLUoF0e+Vg+LhB2KDLcwpA2TRfwrUUJth0U1klvYe9R0Sf31Vv82AvxmojpeYeqw0VdQOvQs6pY5vuyMK2bMdCFdFy/E13R2GHzLwXRZWfTDekKzTcasOyi6Kr0rGU2SAh5QCPhOvMLS3RzKnggKRTqDdF38fsfQCGdF6fJB63875TU/RjHprs1cAdGTzJSNhwQYYfFw6jAj0cH3fFlFcGTTqCCNwRg+ejOg15MQlYpFsrXjlOTfr0xFRCZU2fgPfZQCwheO9xnSCMCQEjKKXhMDzHjlyu8UkNTwwn++vvtvQJz41q8DxDM7nBTn5QnP5dPAzMiwRXUR36xkBXHY+OMYvAQDfcLuKBEl3MI0sachzSUjdDQrNibb/cGKMLAIMIBMkShiOY7EiByEupu0f9V8eXAwA0utTGsstkwrOV6vLCOGsfFYPot0ZUnaSsDtXmxKj2WL3awiey0iK8T8N7DVeeVNlVdCjQzK28qFFSKlpIgNK9k14Jzswh5s5tJTblu0+mLammUCbVLdtfSInVkZpXKHVqS9dp02PNWJ93pxV12OLP/Npx9douFDrb+uZu0HY+6DN8775jF/XBWqhtEGXmro5IuSl9OzbwQJkrCBWciA4XzEg+rY5nZoY8xdhHwCX+ZF5Ep3pT/pYtDYSSgLvDz4ckGRqfXoDEb7o+DYA1ZBEQqqVLQgY4I7YJfIB4AMJZiJExicWAGFDtkjH5lHZFZS3kCwmqyqeeknweL9Zj9dAWeUEuAZ5yai9T/UHfPNnhyckPsK6u8X/HyCEEadqV/Yf1GXDPD+RL+W91PWaUZWU6CZEXDni7irKpil76XwpJXRTAZRsy/zTwvLROBGSRebDwCQyj6UrwIcHvJ3wYIB1iKt1xwnOZgXThPQaAyUEhpeGkAIUoowCTkuOzAWZgLR88Zg1XsvpxDn6gso+YG+BklETRetf0JXkmbfMTOpsBO2o3JdWcubN+GxRom5jLNdols2Mh6GnbfgYGTeMGlMnSew9a0gnYHYxYskHJ1egGo73sV0kiYaFuvZygKiGfeo9ViNVm9mMIlKXBSvSxRcApF0eiyyjWqnUXt4tIByRriEmQfDP9w0x0lW8nqqT8GY8kQPvfDbflyq00ZLhC+E0EwCiENIixXoLQzyKvNN4q91qBuL34mFRwEZDl8x0aLO2CJr0zkZel6C63zvelZ5/Sag9TKTNZ9rjAS7B5clcHU4HtZP8ABPSXsotIKoogAi3CWURd9t8TswPHBFERe7L/49jeexlYAFJ0BiCSuQSoc4QLhOdn3/Vp872k/l58xcacc/484Keomysb6Ek5+SLhq18UXg5dF5NxQ7b2k0cgp9Y7BE9kV3oSlQ20EBvX+4BsTAiTZBaTJmNghH80rp9T6WiQxBBCHNcmUkRtGoizblUzOpLp8lbHljP3xszfJ8qqXozScWjsVIfMVb53bDYtA5r544w9Peb5p++WAXV9+/M+6fS7Ot4+10Or10eqnGAu2U+hQzqwQFdpoUK85J9KsPii1cNeP4vCvfWWIxfy1Jj+Y9aB5qHJ3QCAMcgG5jHKA+PTU2zHlHiIDFH+s1nPnrggNQOUJQGWyx4HfHOKJlU0BklGl+IzgGubFNe4tBQVIAIQRUCubb0pz1e4QzNeKusxgjBu1rTWB0RUyonklLivOXXSod+fIz6j3JF63AVQtPeqNpxUwc7zXie9BoskDo907gC3yhSIy/tUHkt1BbJEouQ7hVA3Erqa0//Y4kChhiGKCZKQMEK3JN4NHCYmmZqpsW+ZWc1yUyCELeM1jh9pbi02RgwCLgMeadjKTqWytOSchPakjSwHLcNwruNCYNKX2JBs4bctHvZAc8jMAJlVVBTEQB/TKKKKEoMDg20PUrRqwGd+7W3Z69i+9Rbd+euiBui703Wm9zDXYrQE4szsv+1zLSSfOv4Ndm92nGCrsEpVgpC6SdAZiLkQXYTOWOjeaiM69fIlaKz7Y/aiq5nlSuG1mnuq+fZTVrxTlJILkjudLhfeBhhx74yx0tX2JwLvRZUwTLZNsgHkWX0lOwi/BFjFdDqXkfhn4PEwMqskmDRUWKaLMOesNmOygUJ1R/G5GJyRPnEaAaD01M2E0JcuaXv/3N9pqEWCd7DIxuWGpZhJ1xFjSmv8CONzcf3GSY+v/hOI3AluIpy56t9LwE1Ubu9fDMd0jTKl0EqdyH2v0F551M+ECEqwryKTYow4g/YF6AORqEBvTBiWc31JRjPKF+oj4EONZyqtoXKyMoEzgC8SH8lrF257XE1cTnLDi8F0kPhD4DbJyiG/AxIvA58TSAeDWQ0ZGRxjN6tg0REoKRDB4E4zxeABgdBgei20NMYq+UuvgXsv/JrLck2H85SjqG6Q1A0Qu+fdbiX7oqYMc7dfCFf1UozTc/nunS/Ku5RnEZYjjF4L0CVQV+Eeej9L7zUpbOEs2SFz1w19e0hizTpEdwQa3H0c8F7Ha4BbQecAAK4BnkCMmnYZETAGz/ZWomB6lvB9U6WNiWvfo2ifhwvGHE3I7babhecjTa0Hk0+5P472iGaqbRa1ThE7pAs6vqb+RTMEdw/j3DkflXhAlICtEqn+dsiggQB/j1vvwpbdCBKc/3B48p/gAzDoXhX+Zj9hzNYDO9rylykFYlF91dJYe68CB30etwSl8lqzTJqE3OCzEQ8mPu6A0yBPWvglm5OJ1HscAdjd6P8wncwpMBBRh8Fvgm6Yfpel7txJOhhgOOi8BLofc+v0pF5A4AmBr4Q5RIPMghuSvsopoALqNKaq8cWpAAcDOSIG5ZimG4EVYrISf4e2gMLd6KG7T7CD+irBWDOYZgbM7abxdhcBZ0qQu5HTopk53WXTRSmruYiqsCK5UJhMCbdUiM92Q6h8wp6ij7u0+W2YqS1+B7s8zLqt/3tB58x3N/fr6GTUDEg6vAhAoiG418mBnaoilAcWjb8H9lLoGaaL1YwXtJSsomYYNPrZwmVoIk2nXm80PNzBVDfE1H2yvA3W5PFxkucIb8XKWQDg0Ty4AVXK1eMcDGfHMROy7Ky6t/HFT8aOewMAchgRiAjo3jz2AW3iOGq1NwGsPSWxVoiL9+sHh8RYGJqk5dnFGdP4vYJZvuwXsMQH8fnAcQYyPtPxqzF1OdcMUFx2NPEdYwrAN8IfnKTvSeupkHoInxfw9NikPhtyOithnuzL9BDi94UzUlnTJSFDJeFKhtsZjkAwcgyFOf5SifJvASgzTElvna7bFKo8UhCLxA6iz7+0G97NlA6n0VI57Gu7eii2yyonmRoHssktZDmnjm5KnVyevzinjKAfWBufddbR29+pDu53/67rqhgcMgQ3/+d/XLvuqH3pi4PLP73+lS8PYUF0RimQQorSMq/cfLdZ/8x1K5EldQO1cl5qCYvRXuZsifcuFDVYAjSXCElsvUTMiGv3vxo4tDD2JryQMFG5LdqT5x/lbwQv4MxpGKmzoc/tcbizwZJnFwA8g3y2VrgwcLGCKZvLn2HpP/lenPOOB6xFH5aWDaTC27H1EFy72Uh7bwJYX+MPkJYp/JlVvmTdBTAuFQXZotlEW76NLQiAFgVYizJb88mnPKDMsnEOGL/gOt9z2U5Lrtgmfc75CiYi7FbAF2Bubp7Le5M33AH4tE3i6yWroBrC7YHnEedrO/OGO5Zm9j3Q+4IUnVUlibO8zkIeVAtBqLadFHJUhduUIycsUvKbBpe9iGouL4WC2t8SOq25e57Mf5VjNFBqYjBQCgf21fe937X3vMcN5z3oOoOUF7sTmUlzyyQjf+Kz/zp89yWnv/u9Bz/z+RXKEYcItWCInmBbB4e9UFBMG27osDMtVLujzMKqFyHtCbrC8Tm8LwSweo1FZsiwCozT0VpJx9lMzwS+x/CeRvLujXqBxC8KL5yo4vd0TiKDEO9Cu8TtVtl71PaC/uEezeaLUmV7xGZt6992bOkGy5xj/Blu/S8dxX+K138CkXsgREqbpkcaVmCQl6Z0anGmyneJ/kq2V9kz4YvvVo1V+jxAljuW50I3Yb/xOsO7EjgTl0w2zU56oTFoAIQbjEelH2g8R/M6E0HgLYNe///Ze/d4WbK6Onyt764+9zFvGJBHeDgwgCIqjwgxomMAQYQo8hIRRREDIsmARkHEFxGJGhGjJqI/E4wKCgqiiKgYFZVBfCAPgwxvFBhl3jN37jld+7t+f+yq7up67upzBufcexs+M2e6q6urq6v2Xnt913ctJ1Z+sNK5wA8Ar6Lds26pEchK/J56CvU46HeDu0jSHL6+UWdhqXyL0blYZOQY8kuQOYAMo4E8kxAnBwm1OhxH3jtpaZFjxd4oGFUOh5LKEIJD0kKLSD3xa/7pJ378o4981NX3uNeuoUp0d9m6NaPKoaMqR0nc6vzyAV9w/ROe+Kl7ftaJT/5jccU/L0owuFth8riOj98HDpjknCZh03j+91AMUWZ3YSYOy8/eyTkDmQgVMzN/ertfm+O42kOqao25gfYG6cKK2AKqrhf7Z+IbgN2bYU5Nfdkw/Ee3RyJEFjbo3smMSSpHnsV5MItzJV+Zh6GZW9ZDZf33DeD3Y/cKlOCpUF87/ACrEhUZr3B81g4/z4RxkfsYX5UHreZBn4zbYpbOPe8wxoT5qvZDx32i/anpY04GaLtoiGph6EbJqluFwnvAbzSdXS0hyWpQ1GcLbwE/VH+FpxGvAx4ONmk2LljlzBN/BzwSfLMJbjCmXkgrKqaiw2Dl455MfJZfQMzZLbILc0NYKh8wDenl8w+vK2Ee91boMl5DxFXjg+q2qvq5VPahXITB73rRjS/9iY8+7nHXHjuWnGdTz2sVO0mxmi9ZZZCuIuQSReHERXfZe8xXX1sul+9975G9pbmIzas9XyQ+jquQ5241ieRy0MnkxpNWbTlAcBZjl8ldZU3mw55zg3vrqz+LAvg0+jfVkCsNUOlKfA7wlwgOHqSWmikSVwC+UPwJhKP0xmDMvtE4B0hMbjYzcic303A7mLUFIKuO+de49z9xI1Kl97AXCE8JgFUXBihbGB4H0Dh563JW196sLj/NPv45xTllwiCOfJ30ekEBO8TS+UbUucuzi6uhuo0WVKrM1jnQu7RrhH9fA0iu3UTxr2F6Np+ZAAAgAElEQVT/p9DnCr8HfDN5tm3sTwLd3EThO82eKX0YdcqkKqd49oisc5rjZvFSQ+mEOWxZJhGVT0Fl4kXktUB2YVDX0CufsZs81NYEv7nblIGw7ogAYBaAKOCRX37tj/3ox+9y15MGApGwmJrCUsUTAXKYWCeJi1UDGZGkHjIUokv2BQ888cD77/7Znx+/8UZr3UT5gGZ8g0wfhLnB0pPeDXNRGub4XW3xfed+7jiHOv52doa7xoVqBO5EvUo4azM7zcG/Z3ga5JWrzsHOSCJwBPgO8IsSDc9Y1840gLE0H0IdFJOEvK7A7Y5kxjGLEPjD2HsPlpWPxeEHWKeEyB0IafS9feFvPs57LaQ4OMrf7GE1vtUP4Jm7nV4OUllHpbiusj1SeOMqVGe7iyhQUQBQBJRrku1PyAdXBZzUB1298v+Ae4EpQLJOda5BHgDiLcK3E29fdQ6n4SnlrLvRctDVEMaaFUKcI73KfHJu++HWOdBDu2opsVZwZ+QThzoBZxlDTG7Ta5f6FY/45xf/yBVVLq7AyuVZybM7obIUYMaq/8s391BElKbK+zFdVtdevXjEI+5+w4m1AcDcUJeDMvzsbjC+z/yy4NyNxzebK0fbD/ocIq7GQ41aS9S1Khcu4I7ADwLfyEIqycCqZyI8QPGvCABBFg8UY6U76xHEGxEc0RBcsI2pdj9tdzeTdmpzQpId6J4zxe/6O/pDdeIT3EuRzwR0yCHW4ffBahLC10fcMfDBoW9wn0tZbYGrNP9TxIF3NcaROTVBZmI+TyKnBG64gH4P3NvytIMGRDKZmEYnKvc/AO9OyasGGikobWy4sGoPDKqzJCAwQOKnAp7jeDbwcTO6kjfK+lsQBqtXWpPoJFONhAOtG871JhgCZ1v4mua/2usM2VW6ZBp6dZ8c6tEb2MyT7T8JKdL8orvu/siPf+LYkdRzKjCAEUljlU4Dqv9Rqc64YrHqknQyIaI5PUWWGHDkmN/v/jf+we9dsLs3A1RhvkfUZNDhfv7AVmZas/AWpsJwZqGruch1xCl+giSohTuhavvXdbDXQ79luLd050pXEF4r/4kkLEmXx0HPqeeSL1DxOfXwbrRGGi4zTD5HyKS5bFP3XaPv5Wo2ybewOhDfLAD2c9z7TezWN7lOAf7nFLFpqH9H4yVH8Ic7alcJM8XsN2uyTf8bOchU5XHXnPw6NhAUVeU1GAQU/uQSv4ItbLASl2Cirx2MG02CAH4WeDpNSok9jWp7s7uw/vtHgB8CrgNQgGWlg0e9+5VYgiE1lw3BkXGgs13NrmunPsKNjWyc4301C2ZNYsqh/sGRd/U6yGd2FM7Cf1WWEhOMNsBp8fixnV/71ffd+a4nqSSzYiqNJ5CertuPfOjYhz5i73730auvsYImuCOce275OfdZXnTXk3e96y7Xt3u6bhJdCoH//b/f6mf/52cgQ1o0Fzfk4LBZhb9Mo4Qc/ft2pcb9c1eZQHO8vunJnL1vJ2z2QNf81brETwRVis5vBn6YuBC8o/DxVWiEOdwOsFBIhq9V/KUkmFC9vGzOO/0OprY/ouiAmK3moelmZcusNVcZ8O9ww/9FmZpeCBMOvZP7KQERhTXJcYx8zTl6ZOI4IhRQd6wNGy7sFyTNPKGe/WqPE8SYr9UUa9UcsRKoqT2maH+o+E3gRyAUYAnVZZWq5Xi+xyPr+/P2zo+DaRBL7ELdQFgByVQofI3hR11/UX16Yvgx2n6ZCoVND4IuLhmBRFv04o04cI6gnxGENH6QI0XAyWycueZeQ0RdjgH9LI+GJgAFQJlCpAoKbm5aOJdPeeKVz/ueK0i6V4FIgBww8M1/dM6b3nDsT/7knOtuOJpCR8kAlKLRl2ZFdBP9M25TPuwh1zzxSdfd7aKTdVxAoGIkAgjhyU++y1//7YJxEag9Q5CjwvHWi5bGrUp7wcekX/wkrNnOrGHkjZmq9rnE1dARNg1Fc3VpLhEp9nt9a7iiDmauPRe4GOGvGWs3U9t2FhjEZHcG/rfhEhC+ygCtkuk7AVAcbpWz+djFtpqROOcr73+zajZ2AioMnhY/kN6I5WN4Yleq3ctAHXqMdSqUCFfJVQBQAucZv6IACReNla4oCTfaIu79/Hiac7Vm+i/4ALPaYa04LmPXxDNVDaX6+QkWdwU+Bl0GwFmx2els2bYJhYl7EG4EPwZ9ZSr0rNrp6z9Ivlt4FvGDwj9WfT+sOC1qGEE2WZnxYt8QShh5fgQr5GCyzJ7BLZ6cpK8mj3Zuq2Nrut3Wy2NYs2WpQUVuMkhRZ529fOlPf/L4ETjdGhHd116zuPTSO7z8Z25z+QfOOrlrJOSgKnGLXLLgAOSQnzhR/O07j77ylRd86lq75MEnSaXL2NJgQZx/fvk7b7wA5i6HFlVXlasLkrpIorchADO9znO4nO3oohxAk7/Z5G+6/57BjeNBn/3VwYmdd4ErKNXxOeTW3JXWCz0oNGippxm+JY1fltoYd6Ay3Xk9cSEcmjTY+bicjrwtbCAwJ05n68PonrfaVwWRoBBfBr8Mu2tXMgJnughvAY8N9tVAXQM8doFz03ReaTVAAHHzh9/OIV1zfK0wR9HlWeMUMyHaynbFgbUAgFWPFQQsVIguAEmKeTH8z4BPADDSUhZzgDl8C6JTMKsdHPQB4unAMbL+bKMp3T9PJ74deAcgMwqJtE9BIFNJVM1EDowajo94ByCj1JWvwcrHQCP73H/M38jhDTlUDQGvoaDAuWdyyMEhVQLIRaRTAvnYx139iIddtxZbASKuu+bYU77hdn/7N8c2dRkuS8snIyIUXM7UO6hIc4nvedfxD3148ZCHXU+GVM4GAuEXXbT3668+54YTC3cjIhFBUubykSJg75mcRepsUTFEXuFv/KNbkruh6EbMEfjnmJxNoq7u18k3ddt2ZZwinVVFde2rjlNFUaQwVwIPML7YcGFtNUjALZpZjd3b4lGykVw4gWYO1oZqpq3DPJg1Is/yxn+mGgmd+jjxQi6vQtkogJwC5M+pYDS60bMbEHSV407kgwxOM1bmvZU8llur0TPfwm2g1SjaW/FVk2FT1U6an1uvBDaPJHmFOR1CIBYEgXABdSPxJpAmRVAkBAdtCw7LYMn+xcx0UvYB4WtUVwhBJ34e+Crgj2C7FAogCpZeQ/ojI2y1t5jVKzkaV4jnY5p8/VZmyCBmRiPnkFUt6/ZMpDXO1uS3C/Q+OTjXCqSiy9ItxuIF33XF7e/gFc2qxAHgCY+76wfff9QZAiB3wRlC1dgNpkwNEoYkCARolFEG8b0fXFzzKV5yyY1Q8nDxdJd89KM773nnMSBWjllaMyW9HQA5fNUQ+secJkFs5fgwLsPKLztOArX9HE93Vzm9kDrQXjKurRnIlM6CLbI/rO6iWF3HIPDtxkcBCulGtVTlQo8rKjcXhE0J/DiPlR+ivJ3pwyw2K9NGq3nMAuhczUoGQPRfQPlK3bRpt3EqACw77F+gNrdMClmUjDLHG6qxUnAhGgoggMWnxVfDB0TlQ8cfpajOI5uv8s1P9MYF3WZlV5i6chkRDEaEpWCwCC2eDXs0hJggTtJomsIWl7oQoQKku5P03zD9ulVj5Z/CH+T6FvEfViNTaQQSnSYAnkrvstwl8tAIPkkzzB3Be36jjE9vlTwmJ63xLSe3bx7AyKF2NxvRJndlyN3/7Ep5ei/szU/xqLL2XeDx43uff989wQm5kMoHv/ALF3zggyGqFGKJUkGwoJgaQSDFqD3AJLkoEU64HHJzUiz9V1556w984GgtJCAUZHjwF+3GGIECSKIf67LIvado+Luge6Lc3d3HndZzfNi7P8Q4kEJHQzYCo0c+7mZCV71XY5JedT07DnRBDtnGWlVKi7qtJ880AcHER7C4NCQyC7FO/IIAEwmyefI9jfxT449PDf456+2bm+Tw7BNYTU+2nqEiUJqK3wEKwCr7/dWsdejxyaH/Aqw6g7zGFQZH+PM9eyNSjCxBx0kgAkvOyDOeh5MOePu1x+6su2ujOZEQqz7hirUyrRlLVWnJbgIIT+8tUDyVuC2S9gYqADiXW1wnFWZLxHhKQfmvwj8RTwAfDLw9HRHS8oUVtgLoFecIAwIzbLl6J56hXJGc/eTAr3xpcFdbMxdO9W45NMvmo5zxDYY2S1ihF3Xlo8/m3mKUUMj3RKNw8d1vWA3WxmRxpde95qwIGgJcgCkaSsCjtPSUi8UQteeiQ+577qUiGMWoqBIwuH75l84FWNmUMgK45CHXMqych1OYMEfOQ+bJHwesvVtOnsDuhT3XeWHkS2Uip3E4dSD10Pxbcvv5QoSvpvd61NniQ+hWjVvRFAS7kHpqUVpqc6VCkFPEAoGNWafbjrPJNFOdfizPgFk+hbHy5ybNrPA0YFbOtJdYujo41AnAfhe7b8XJEimowVNhrUqTPAOw/oUZrKqgnrxwKnART0R/nTsEmmSEGYJAZ5ENfeayVvnMxwZl1Q+t5h3exP0TataKjbwGQ+XMGKEFCGABRoBfjfBYKoVJ1BXxbcYfODzURLAB4F+KdxdfU/9kJqbnVdvJyahUqTHAqajh67NbgRrijbqBuyOL9ZHnt2hlH5+G81moEUw5RESNMxAjux1hqiZ5nREYMfQVqmqmiqgSKO75WWVdGk61B111ZfH+D5zlEe5GUtHhEXCHJMKjyRW9ssvyap9uHhkdyVDUHPE9f1coTQNVTYYEzzpW3TXuRjiF8fM5yRSOf+UmPB05q+NnO4d67P6OI4L0SeQ0Tm7ls8sjhFy+u/1+6wtUcxxkS2QyZ+JxUAIFhwj/auIJFfVC0BADCWgJV2BtQtIoLzRwVTV61195VXrbgq/KXNV71jfsB14azILLwEOWWochJwCZAOz8JnRCDVF7I7b7DMD6F6ewKpVrcs5YXRj8gz1enu6ASARHpJBvmH6w6Gof0G3ysyZ2GHqgT+Kq6FW3nksKoEMBomTfpsXnrZyut7xCRAClVXOlV4ju+uoJSx+L1BsfauWnq0JOXmscPXP4rgZprqXuzflsklzJF9LO7Zma6+s4XhWafHLkqHrLWzkwq7vl6gx3aa1ZeCv5G1EAS8odSyI0fvMA8B3vOBoZTXDbiyqTEVasTEeNKlx0A1x1foNJgq/v99Qn/46/OZamBK1VibzbxSegZYwRpnJpUd6i6EYQQPenHP/WI0g9hxLL5HhG8Mo4vZRZ+N76Nhm6cZoxlxsx4TePzaQB6zqGTETDynj2aGwIaTX4uYZnFwBMluh/R4gisAOFxjBmKzUtN4FL3+zQs9L+NDM6ms1sZXFOkTSrb8T32+6bWNaosxr2mfzJdOiF7sVh/wL1L+7YjMHRB5d4TYHvWoDBsTQEZ9JbzMMo3Ncl3qjZDbTjZsut0EdcNY9TXWi1mkzYs6+qnbBSEKzkvWRxL/lTZd8Bj6sV/5a/S/XmJKtaN1y7bxxIvUmTP9gCaZPJRKd1ertC75GOsKY8fFI+0nVImtx568CGev57+/iaL/VaJ3Sl2V2HraFpsvcwWm8/qNm3+S6DZFRpqVpHr+fV9EtWXJYtqUK2BtECYI6lYQEX6WAgGVVSBVDWzkOCCS5P1sOCUlt+quOAcpZyU6HoCqBHMIyD1HGUPI7C53YabhHz3Lom8w2xukTvyNWb+e26O+9u4+6fHu6q+ji1RsF9ABdCiiAK8amFPtugmnhizcEmLKdQD8JaJZF7PTCzZq3SG1dQG5WOvjK1qU2W235gNjxBjKPGOLAZ++ZX5vJcEyfTAApOmbAkjgDlq33vw9hr7U1w+KnQR3j4GayRr/aGvZT3yWq41qaLgc+nlG62BcQ8dNU6bHXRlTXu86Ef3vp/fjkZLi34lVVcQfgXWDntA2/3AZTWzNFkuYZ20rQ6zLFeHOKBplm3YSHO+DQ8SYGM44DWW7pC7NbbJ6mv7pda6btHimguKtqaEUN0AgogwQiEaBHOoKoiKLq0lGKaoSJOOqSkcHeHS06JMUZy4Q6UkGjYLUEwpHjfRJoRLplER3R3yNJ+8vnCSYX7FiXUSUFhjmhv1vWTA/UwVVsfud2QoeK/WUHVzTJgw6p+VeDRwS4NCQttLnq7cSLU+iWraCGasd/B2JvarPlT0gGm7mocUs2YQBlZBbcvgFLg79TL/FMUhZyqD7pdFvXKPakkgrhHBcr2UafbF8gYE2qM3TytDsH1wTdk7OhyV817O997joKBAShRLp6p4s4AzGiH4jqZqun4yLw4d/U8SSdkH1WuVGu8kDQ0b40/v3pyBYOaRdWhS3cFxXIk7RgVcinZaWvp6Qksq/5tRjkFSuXDvnRZhj33UmIEAXM3oBB2JYKLFSBO9UHXXtqt+x64J8Ilj+Hiu59EVUMkFSABinTWEdHyvXyoNO6KnqNzz8HKmbhkvKlwHO5kiv8ymwHzRf3jR3gYCicOOGV3MTyziI6aYTLCmoYpG+LX9lNWSb0hTz2GTb6wnzLs6X+aFLnPmtoytFb7wFgVXcdEdvgv2/LPsatTwpHh9AJYJno0vs6TUwCwI0Ist8JVfrNoswZbBX1AyT64qjCtoVUYxlJM2GviBoCgHac9lIsnGYAl/LB0c1QlpDlk0qw++fG5ZJyTmIvhMFO/NclwbOHaMHe1sMW73PeIIMm9JMLfvetopTw2saqPxEc9/HoK0DIilr5LRkWHU6KXUSLhgLnD3c2SIGspEdqJcjhFf9ADbqqpbAkxVT4/fPkiySRENytq34dpd4aRrzbyE49L1kZaXyc5s5z2iMnVBfK8RicJ1/zV5sg5PCQwC18T8DDCotAwah9CDEr+8RtIqxJqpN5DWot9X3dEtS+MfhW8bzt/eQZcQx8Om8l7JMsvSUZn8frKt+HQdwuedgDLoQLSH+ziLZFmUKwDJW/ej61viTjoazVhweBzsVpCV6EBs7Bh3V7dxwG0+ie3Ue6bQGlyCN/hxUO8UEh+DYdm1Btoshshh+aSZJPL+hGbzREviVkxKTmFxbZwmMzEQ5nGDb3b9wKIsXsmNQRy4aU+/onQ+MqWzEK+7T/dkJ4JbmSQgmMJk7wq9cZY3XEk06eTlAdpGSBZec5Z+NpvOJHUZAJJA3Dl1TvXXY8ExSTGuMxHjXMhZuarGPWjykHqOWL8obePtzH2XtjYVKlPoqhx6utQVQmNwEMLPD8QgAek3KYV7GgVF1a8FQlR3RCw9Xkle0FSToF+dLLI6UOcC7NmIy1JICOMrj/T7h/wJscpTGCd0hqsksLVtFdJUMoxuNm6EjJWCdP+C0M3SQ931XS3WvHNZBNaEY02VxvldZuQz1JzFhhA3gqLb0H8jIhtmL9/OWg9ZX05i0xaiboyk9dmBafkzMGTjlPjL/UeVW+xprtBt2KYeRjdo+p1KKgYR0v5viW5uOKf9MEPHlXlre4p6OruF5Xf+fxPiQtJcLrvORTloEsC98CQ1jaACUsyuBtYUsWSkcKz/+OV97jbSSh4SmGSA/zDNx+tRVfLqliJci4SygFhvYXXEfA9+VOOGz0go1iZf92OX8b5jYc5HmyHawl/YcA3Bz835VUAgEzWglOw3pCDBsxaq6xY58Oss0M2sdbGFCNpW2FWfk1mFrGUBbMIQmUAAX8VdXVaR526KCScul+tkjfpKscjjbeS5GAxfBF4b+2Zg2NHzO5czW847rqxCxvYiM11UVdrxc2/U5CujUOrehlaY4gq84Epzfze4D8Alx3Kho7NNGh2A2Sakca9C/TMaJ3MDJzJiMPxTxl5dTxyZyTgOecTt3jLTMYxgga5EKFwxzsvH3C/vTqGMDl2+APut3f2WXt//Y5jvgukGF2RgDtIS22BZuaeVFYh6WMMJT08+UnXfft/vpapDijCLSVt/szLz7r8vYUkw0JyVU7u/XlBmQk52ColMJOFmnxLpi4q33Rqrjp+6CT07mcorP2wLOCfvtC371Q2DzVSUi+mSNEZg4jDGtF7bMfwdSzgtRFoSGwGqTU3y0yw4f62GUqq1iCnQ0XoQwg/gOU1cFHYJqroDMC6JfBzFK4iL1zgi3cGb4DR0MBZ/ak94GV66PDNuwLjikLW/xzXWq3ISY4e4MZIt6a+lBIDLWU23xt4O+JHD+UVMJJLOIJRxuOZ82HWrJ3Mgl8j09L48yNd9zk4bFbmdPaBFS4nTDLSPvlxPeXrb1QVAy2SKc79vvff+7KH7V5xnX/yY0d2dyuRsHHpCtVVKxJLIUieOvDvff/dF/3AVU97+nVcWZYw0CTg2qsW3/vCC3Z3SRoUwLKKyRzADZlC7MxEpl4v3CGjhEw01m2VzQa404W8fDlXrwHECP2JQ1YZrB5fUuglO7ggreItlUgM1CCp1Erg3IRQQgdmoZLADzjttEOjG0M/p+euzCmu83Fz3j7yFiP0U1i+lkvVtr9nGKxDN7WiSjOneIPw5KAdqxkpB1Q1blQuc2qtBCazlUkj18RP91HnLA+50fomQSUgrk1+62u0ezxh1XrSIa7WtPQUriJgzUNt3Cieao4pZFdGup1nPAq+BX5TQBXktVpw3NJXHl2+Cn1Vv5EQ5abvQ07K8lz8NJeCymS/MGB233tCxqfJucipawY29kEpV0SM8GRZdeU1i/t8drzoolXcOFZr+AtuFR/1iBPPeOY1d7ijv+NdxQ3XJ1sdmpZeR5JXjtDCq1/zqec+5+qL7lYyVdStqswkP4aX/o9zL3vLWTKHDCwBwyjE2ac2HFMpMSNOUZNoJpNbanmb5ccFbgGAus5wLeR3aGqCzQAMhjSs35Z4wU74tyYQUAG6pdBxSkPjPRsWo2tSvY0tqqVudYo2aKluhiSgpCbcWDWtMVa3IMNteSzMyZlGhTrBDQ6v0glHACdhz0f8BLxqXD91a4SnrgYrhUBrQUFvL/VLDuyZvAo2gBl3pKA1Ysh8zKlhU9m9Hq0VQX910tS5WxvjQNZwoYlliKWKCxVE0oMgyL5W9mRKkSlPhF6LvG7p3R9D8uGm3cD4jNVaf89NTxtRKGfyE72HOj47Zqqtu67l+dPqiBpplsidgmIZ5UwlQgDA93z3uVdfUy2FEo8lWj2uFxQe9pCbrvx4UenjsYxaVCJgD8nxgeQHPiDCiKR0CVWDrMuAq688+sqfOyvpryj3ygmeMc4QuSMvDqhXhjWrSRAZNmb5tNAkXM4xb0NetHP3YhiKms48tn+ZeUQFjIRJUQwwPOlI+JoQjSnNqUwBFLFOTtXE6lxtCquFtGqvUtb7Ub3PVo8h6jbD5Ji1YZrV30o1MvsctB9kZcdQ9wzKgRKIRCD9f2H5V9xNESynhqHoaQewqhBoLivd4WuXUHBa4UYEwB17YATM5USZd1V57+i5OcimRDTPg+Xed8mqS7ymnGZynNKVQZYP9vpIXiE1uisyqbHMBArFt+PoJXVql4ypa+AwULurGa7rL9or9J4kMPJ7CSfnwnxgtMX2GEhmHAJqmb1yk9tM3iOtnyCwgAriSLp4Ff0frlq88IXneCUHFEEiIrmSqnQrXv3qI3sh9RCC1b1jgIurbEF/x7uOq27/djrcCMAo4NnPPfvam1j6kipAJ2MUAKd8O5H7CLc0HpWzT9SVc0nkd05gTqJzl37LdOjIvyNuGQ8XyhXzZvIvLfDckJSBMhEh1fjSwGtrXhYjMEswooeWXpUwpEacTgW/qv+rQYE1z3+FtFZTSXWSqWyENBdjjTMFsf7uAiytx2Oyv5K9PgV7WM1unCkRHk70mJhWI8h/jLiHeO/CWYJuIlmo8nYvSM/jSCf5qrnAvyuTVxda2WZOc5d4DpMFzYbcqivf2Sg4ypwqvEJQETCKgJ1rOBvxz8HrKITkPHR4s6J6tVazpFeZOvfuf7bqkthKSj/+lu1U871bDuGzWWTDuCRI2gMhLAXSYnATdPn7dj70/qNf/hU3rq5Q1o1WpL/sJy/42IeDw4ECQlCUTHAwSgUgwk+csKd+/Ymq6CKlEiJpz/62W/3em46KRhTyPXgA3SUjx6sfs+px2FaPha0aUTP18iul11xJeyYSGg/5aa5wcq7/W8xgEQClit0dAr53gQcFoxdMXjaCEWKCCRtOOcQozOq1EWUtyWLif9giuNJLtfZk6ARudg+09e/DHz+rCDi2nxXWVCOIjakY+GroZdgtYZAEJdnJqUpinbIAy1LGU1XSdkSEMvgTA1WAFCM86VtltJEJoDFSaH/QqttY4S2ctOoQtLr63oJWm9IrZqoZ15Rzr4SodUFQsYoRSfe3mcMNpPDZ2Pmk7b5VqXIDioet+6OlvuqCkkkt/DiOyZd+b9eHiDktgUNvzG8VnJSmbXckneuzECLoRJAHMMWQ2eXv5fs/WPybB+no0VL1fQzj1Vctvvt556e1uSktA0woATODe2GMAK68snjqN15/7EgEA+t74Nuedevf+q2jUgCW8mhWgKZqEEiT2hi3l+9iMCuRJofCyQRkvSGYmVhw8qMxJ2YRebaot/ilGFjlAhKwZxzTfyxAhyxKsNR3zbqjiKPQozdSow/VN8Z8gqykIav0s7Xq1qrG7/bNaH33Yw7A2gJpDW1QCLH+cvVxAgReiPJdjLWctyL0znQRHrLHSmwY6hxCfZL4XOIeqlYBjCaKlNoNRDX6GckK6GOHBqFVtzdwo2+Rm6+GTsBCS2vFhmUKhqu8LTE7+u/uzmkTSTOHmZkouhFESIXy4oHAO6HLTRBQNBJDDyV31RR9zwJY+8RYme/d7gDyQc/WcHAc881qYauWQiRVEBHwiAgURHDgfZfba1575KK7xLvfvaycqoBX/OK5b/nTI4pQIVc0eh0uSI8FGeWWnBzvdrflve8TiRiB33/T8W/55lu/9bIiHaFpQdAr7qpwjyRAb05V+bADB2ffMLd7MdPMM98SYlbMQOsa6JYUu4DvcJkyhGodbIK+/Jh+KtiCohIbQzet0p29Vpj3kFhNNNHHWvXaGqwbpdaoi00EVFUUaXVvWvoAACAASURBVGz/ED3p730+Djm69UmYNbBBTb35GjFGEr8DvRTlTRRRVHHuhsNbCTl9ARaI2gcqUCIDdqPtBP17gBG+AE0sCauFeP3c0gQ1xBxo1QVtTRw48Xu0oFV9t471OTaPq2cSHYBWjQOS0SLcqQVCKuSbrJDpqPO20NsQrwLgh2jhsToPZoa+3sDe3rdW292nH2ONU1ZbO2lNgjNk9z9mTuTD76KUIhYSTiKNAmS7QQt33XRi5zdff+T//tGRG689cv9/vQvwpS89+6MfMQGIMaCIDlVODomNSlbtkvw2t9NDHnLTz/7MuS960Tkvf/l5V18VQEhutnDsQklDULjvAVI1mXIEefQaYu2ffJp8Nf+lTNvPLcBWF1E168gj2r7Ja+MWvlBP/77nIvzAQp+FlXcomeIHPVRRTcngpp08uFEoHFuM93llsS0OYXdzNlj5+vrU6ki6P2ndQcU5QErZjNeqJqj6gNP5imCQwouxfBsjBAMNDhrlpy6BdQqryzYu8SBEynQb8LU7+kKQAgqhLGSpjbuRYedbMVWT4Mx7AU3LcGFlXqUOulq9a7IxoWVwlfd7r6zhqw9KaM5Fo7ncKCWTd/rJH8PyhYaTiDysHSAJZnXplq5GbRLfbF0ExEjddo4vw3ZOWr1QMoegmkWSjey8fsapwm2P2pEiScLBxSr6JlmGAhD9rLPxkEtuev1vnwWXggBjDChKOKGC3BMKoCQXUXuGxdnHlwCuO2GUA5YOBDDKnUaPFdXNIC1F0HdGQgt6FeUtG4JxkNHL6IynFTWZoW7k0X4QUqY7w1xCbjwvYbWGOSwwiySkY+D3H9d/XgBe1xvSRV39qICBcfXU6ouvB+QKbjSLGT2sVeKkak6se4rUeHfaY1Ixoe2W2Dm91vkk2ziy4S3nvNo8UiMYqSATSpLSzp/hxsfBrkBJeD3PpDbhUzaL8NQ2Gm2hB+GEcIR8VJAM8JQoCzkYEvrvWlsNYpNBw5NJ1gpYe2S1d9+yYm/+TsxQsq+r831K9uEE0spey7rVSa7BFoAABRbFv3G8H3t/K5KhXuSlP9Grpzwk5FaO1H1uSW6Wi/p+nN9HXm3FEW4BBJFdTxz+jgaIDI6SsCA4nArOjW4Jo4RCLGvFScL1FETa3i4uf99RyEWQDg80lwMMZKxssBCU7jsvlnvaXTZbb9eDOuSNW6ISDSTbkRVsGuKrtvY97wKLWQXB3nLbUDsCZtqBDm3QuizHfe0ze3Jvkctwq23RFdaqoEp+/eRj+JFFqJypLJk8Vc2DFbPUqIShW/IactMZU2Wx1/mKTfpqpWxpTR49++0SVGos8plNZbGe1CblB81JR0Qgli+B/gjLcSftMwDrkD+uEB4ceMeUzrFq1KiLxZOkUM817KNSLTXxzUrJ3t8qOHDr5fghUFMz3zDlG0YZzkpuL6snCBcR7gH/G8SPNVUCVUm9wMIPw4okh1JqcTz7oZe2wDRb4Jic2t8WEG07BLaxT0QY5AktQTAxscpJhGFQkqLLxUqX0acuX7IkCoIe1ZjPXJWZe0ylRghAGTNtqkfLapMeBDkAIgdtHKyWfDvWCjPtVZt4aztDtVsKpY1UoFaNbwQUYATwoKN4yRFcqFSIrkxrVwuAXkTTiHbuESflLEHJHndRdmYCtjJ2sBI/dXykyfqQbHODIaA3tBpXdwbp3a428Eo+3vwL6EXYu+40gxunH8C6HnbM9PDUghGIWK1LxkeBwWtpPFazWQds73+kVbBVxLcpaMVxpmFjh2o8rZEroH0vEwZFIggkcaGFs1VeRl1flTZZc9Ve0eiHhruaG6eDfcvet2DIZj0/S008l/HakrdzuCxZfiR3K1N1C8CJAHCPQlQwc/fB28qUNFtV9bAqp2hVfSPqbIa7X1Q+6Skn3v62I/vBPTngIEeHnoOu5qq+MNPefZ8K9xEP+kl0dcvHWGJjIK1ywiJQ/KvCv2+BLzUg0CVLoTaVv1rjft9IGZumy9hDO7XoH2JY/L4qMhCb2zRh1iZyarT1tPXyfT4OLfzkwyDMe8EWq3s1Nf4S0I/Cfx/EqVsNnAccTuXHZ5q9fsfvHUkTlobj0p5yWKssdOWdU9yttNcmIcJQEXJ1n9oogzpTbtVqXeyvqLN/YgNoKWkEtaJz+b24/kVJvFkAZYqHlmmtDDgkMKupcydpqMRlXey1dZ0O+2sbnFIy7StOJ7/umYm3Vs0BzRgihxgTqnLgCBLbZMHkAlzB5DEEKtqqeavv/pJoBngAy9Qpn1yuomCGCy/ce/gj9r76MbsPfOBJuN3xTrfbgnyaRUdhH4YIOVW83reMVxv3GdKcfzzd/J/mqyNA+RbIYaVscRjhCaM7ge85Hn6wiG6wmKqIQqzrh43l84YffVc6pZ4uy/XGGg1HTmovDV2K9XDtfbKttGevW742fh3rva+lkVCQWfIsE5bEDiCxfJfwGMQPVsaTZwDWqfyFTXR7TuH/LTDZ56jTbjHtvJD5kpoLFY3Shq0yvfWRXiPwb4MzGCNuK1OVoYly5IIwVeeJBBUAZ9RNXx9ufFXcxG9JTXxoEFaTx6qQQUP62WXaMzHTFt2Fs+KW9388s7bBfAuuDYAlkBT91rddPurhuPd99y48XxdeUN7/gbt3vMNnABaEEjSDFI109e8/EgUCUQomybCQlm4w8Cu+6sQl/3b3SV97kqp9G6A73OH220GNfTJbk8xNjoR8COj0isTnVuj2b3C6DjfUOu9kPBvgFoyvKh3VuruIeNIxvGLBgoJbMv+T4AFUnVq2GR67Vu+pYsVaA3X3fGwM4+PjfQLWfegNzVqh2kKwFcvbCwdX2GgTAnIAZtn0eaxvfomAEwaUl9JfhpI6pfVWfY/idANYqWHBX+d8KnWfVNzyjfXDbHTlGOaKOHphssOucoqymklcadP4ZJ1mmImuVgXKEgosHGUieMQImAod/a4YP8zdt6YuTGNyEhIPkXRxNU+sHByqgOGOthd9jWC9DepNlXRXhtyNne5ukFnmm6zljRzP+BycudnkR1f/NMJLYnGX2++9+L9eg9rW8y/edhQo5HvRdogyRpgheuMO2jw5Ox5KliTdI4yOkw+43/KRj1p+zeNP3OpCB+Ay0a3aPmwNqsY5py02m1Xgy2eYWll++xF7bYHPKhSlKnFuEu3dkicGwerwSzr5oB3/rh0UyYU6uAvmIEPwCNVwjBvAaM0HD94pbaQldqad/rGzVhymJe6G4rbeW6VESU47XL3GdePjEIhLvR22CdtEEppLwSTLFazcIsDluxR+W/H0rJaddgArqa34Ien/RP7IjrBHFFLMgFYz2Kxe84UBvmr9yBHEjYjZ2YfwNpglzSOumvKvAhRKB4JAoCQKuRPhc+3IpR4/huU/pPsKRvfDuU5pdmZNTg8jJkAjNEPvW4Y8uHth1ng47hDe6k7bmdBtMuGg9TUHt48OM/nevT7XqZSz4CKuvppeLhkW7iXhQiEvEQr4Rj/aard7LAPNy3j7O+JhX3bT0765vOfFJ9Ko7lWMiFt9XECcO81PCqEywcos2JS5zf77BA+Ew+tpIWR/O+HhAljpkpHg0J0WuvQoPzcJ2itCKkGEBrpqUEItINL8L/XZX/VU85jZkNEgqzZx20pwkpgubr6l+S7axiHWP5b3UAErLq5i9b3ewIepAweS9UlJFhB/EfoIUqrQGYB1yjNYBnohOX5despJfE4QS+OIoZMP86Jj6GqzEmdDXYqm9uImFymSkwgpE11hCl2lG81goovylDwNC3DRF0/A8cvt+he57yKFZ1e+kYcTY1UyLLMW99OdcXMwVj6mmSTDMBrq1/qU8UMaOqqufnkyEmfc2Wj1vIESiIC4FCMq49xED5i0FJ1emNFiUbJkvZ5uTdjnnqV/99Abv+lp8Qv+9Y2JLqicdECrKxD1cQTmJQ1kKqW2wFUHAq0OqjVvrm/CFqdl62O7BTwMijLAcTTgGTt8IiWC7qCJnhJZI2Nwd26WAjoYKyewaIWKxqFY/7sGQNsGgKs3XIuAV6OyMH6bt4eLLagHRZBQfDf467ZbuuF0xFenH8CCw1BGgh8s9IrIH6Nklcdupb0xQZS0XqaMidl7NexCy4HXx2+YXNCjQVzVaeSYqAwO+5xwc1WyAoCp/R1c3W/V+THCj73A44dxwy9gxV5VDTUy0QmaFFEA5aFgsAC4e48nZBXpusFqjFA4I/XEcXyTM0BPbt8UmI8QYDmlw/HD7kKE1Q5Xp7FEGWDe6NNKM9LDv/yms4+fdd2Nhbm7OWXRPPnd1qO6A0bhS7705IO/uPzWb72xvmmSP6FW3VaqKAhQbN2WzaMasR3fD9rYD67CqNxqhFacW3bsQv+RybW1wWqzlnT98FUGmXouUoRatfTV6hoCvvYov3unurDkADwV18RoSP3UXRIpYayeIbaSdGqYjsIaaamuA9KnYFZj8daqGG78oU0oZ/XnNo62qcFfyWdJNop9qFVeIlxrRisZ1KEUAtk+EhnI/6Xlhx2E69Cuus8ArHl3VxQIiiV+A3qK4/PocLAAlkBQBQKS9WY5RIEO3bmp8mx5eeSZ4Gpo/cDpXWkuQ2Z97Fe3oNnQliVExbNeIH20uPH3SwJV+Em6hQUhRJRgCR2Ce6xiXMx6p7SRyS+fmhqZMic3691+5CB7kV+vt9NIpW+S2eqFcc1/Go5GJQFfZAqCJeAg9dznnvz+HzwrMpqCYkkGwt0AuVlx14v8UV++98xn3nSrC/ZQ/SjJSNvJqpsrGZ5Qq7x0I+KVV4Vf+6WdfKrpYHFVJsczSwXfbdnb4tNbnlWTHlojjYGHNAaHglSBHiVLcQFwwSh/+BF+3xGVRJGiXCrv2f6ywNg00FjzDmGsNuekgQF94CLCSidKwNUP4Po+YNWr1HLzGhm7VJkZtU8lVUh7BQkUqzwUCkIkw99o+RvmydMXhzb24wzAmnd3pbUvZfYh6P+j/6RBSQ24qHI5SDFWW3IMVDUvzARljBvrkhFDXJuDpbbACUO4alYpshfDcfOAjYgQ7oqj3+X6pJ14pxFl3X+c1oUlGaRIHCZ1Vo9SarO7sLvWH58mRxJpelVZI0Aqk3AaYa0yNxg62i49NqHZwh7hBD/0EYPKigilQD7jW68/94LyR19yzic+WcawCL4rHrn9beLDH7587ONv+IIHniQgBCF1c6XvviqA+Pp2owBceVV49S8f+cM/DW/5oyM+0GyR//W3SD7eJ2WV8659KuhzUN14E2vLA/YwRd9U+MLXZJaCI6YL5T5H8Z3HdOcACJKTK0PRDVl63aLaJbFalYQNjFX7Rg37MQydRo5hrHGiS5uB0aufmc3mdqk7M0gCIhk2Lxitib6UnUYHCsiFJWgph0rVa/EVxIe9cfg87eqEp6lNwwq5847w1xAPDEBJFaIDgjutEAhE9HujqRkOmONr1XrS5pJV7LGUG77TBu0YhjyuSHYkjmIGVcZGgzOF5f/mDd+j5cfX8VIBcIbkUnToKOKWRRY2PR16owxHcAm2MhTdbldbH0Av3TV5loYA1mbpgYDf9nb2nnd9CgrOVFtI8iyJ4ad/auctf7o451x/2JfEJzz5phTVTiXLxwBGVbKqKvStMS3wfe8rXvOrx//6neEtf3zUuQvtEHvjXeX5ucg5O9kPrspBV/tJcZ6L7UZ6FXsTElMt+BDNAUEJUhnXDBZudwQvOcJvOCoXTY01htq00Iq+wSBDmUBY13mwhqSjrlf9M8L4JamhmJ7GAa95yA5OU/sAOus9a3Uir/+Jo0CJ1FeuKt+QxGXyx4KfQFmvsxGQJ4o8A7AO93cmoEIoq2//dOP/LMQIRHgBS/myECKqOvSEd2ivfnxVTNcmGpu0ZR+b6nrWLD2IyzYzR8d/79AfXNXjM8w+ZCYiKCwZd+p2gBv/C679YfCmSnKcBKJVvJYOX65nUrv3BgW2jDTbaHgfvlP5mGkSCY082Xvw3SX1XAA3VMRMq2EAb3nrp+51N0UqAFBwRAMiEWBywHwtG1nfWSZ5cnaoljcuGC6//OivverYG9+Av/9QkZJORJeCMUJW5+FqyOZxaziV+d6byTx9O+JqLpLrc7QywA+3HUPV4BYr86ckwCKOE887Gy84QkPNbxEO0DEon1KbFtp4lW3g0s+Y6iBhVsvBYQxmaVNj74NwsDGr9KArmChzyWBCQXiaBwQ+E+XLEcGEZitbrNOP0DndHgGIVreS0sR4O8evGL5UgBnk6fJIVk6q4qgy0VWvj1Tztpi0aRsxuOrbf0/RPoxYAvccWFt0NVkTXH3r+iDFlZodCJRj5/pn7l3/swiyWGs1K0nzIeSHV3OzmbUygNNLK7UWsg3fMccgdNbzc9+Vz2ltR241rhjCZQaJX/WV5c+9/Jqkm/Jal2erykOtEGE9U3ktOKYCEEW+9a1HX/ua4s8uW7zv7wUuzJaIC9keZGSQCJRCYdRBRQ7P1XJt7aX+6YdWGA7qwYASq5qU/fDNlwGIMJhTKxBTAOW3nm0/ddzTdRPqaApo096zoRbvunf2sFnN3GT1V+0JYgKjb/xtmEZaQzBLLS28OjAL/aBwE1Rxk76yaopUtXwq6QX4f8UnWbxCWuluicM6BZwBWPO+sBKQqiNfHOTXU68waLnpKJIuDXL4cm5KqXhA0Kp/Ah64lZqbZEKrAeJKfSBsODZnXbWUapbPYAZ3h66xG56m639TITV6oWCDKD7EF08wuFqIpDcfOj9h5qCKhgce1Zyfn5Pzlkq54gG2BPC/f/HkVzzyJtTy3OoMuslife9UXUquxClDhsv+/Nirf2Px5jeGj/9T4r0WRAmZG8wRBQvBtWcIUjQr3LFdqMD+Qwn3Ca0OVkqPKX+v3i1TB2hXazWZkHjLZ6WTH3KguSDzrzpqP3eWX9jGRkEh0jfK363qnhqjJnvFVd42I+2TOo1QWR2hl4ZngB6yqiojdKucKwkZ0eaVuvisatDthVlJpixPN3+6K6XiG7H3CsQCKG3VsHkaSrBOw7DnSvq37vQAiI86PivgXulaLEA3T6myxs6Fn9oLWUc1d+mrBK0C2Azr5DCu6sUxGdE3yvkhB0HSwF5brNWQjKx5eAXMZVWHPBCMdlS4F+zd2P1YjciUAugPqxtKBZuMELoAq7lZL7TKBCvbqakOkAbLZNcyAV+zvFikuBGTiTK++U12z3vyHhdHCUYwyapsdbmyksCDRoPr/71/54dffOxF/2Xxl39x5PoTpVQQAKJQJL2tANLlThRVR+FIvSQDu2xXN5xbMczfSa8nyP6NQ4exmqfL+aBcuG5Ja2yJIMwBwL/wOH/8OO4cQKYGJ4qBUkoBYH1bT43GA9kya6+EqfVyntFoe7O+8X2dBs3+d7W6FxtMVc8b6/dyczQTK5/ECFr68pIZJeH1KF9C34M1+gatwntnANbp8dg0Oj8J7DmfmK4b1YM7etFV+yLugSZc2fyMQJx+3quVfJfBMZo2cN7E/sEAWkcRz5qNa/3R/SxrBLnXCDJZDyGsTgixuB15O9g7bO+fVZ1RrPyFjY2Tk1JpDslVoyF0lS6bnBLhyDOT3VuzoFJmLe9g8weHtox0S4qWULjK5d6R1/063/3+4ku/KB45vjp1ac1T6ddTh1f6121uHR/+8L3nXLp7h9tr5xj/8R+w3AtS6nJKHEwkFzCvipHVtOC9N9BInN8WNNWBYJ25lFWOuXy+K3114SXBtzskwcgJy4ZbOIxqLue4nuZXyzwR/Kyj+KGz9IVFBRaqgU2enKiaUKMe8FkN8I0nmyAmmSr2r3FXAyvWFjBpgY3mVMENPNSaZZqbZaTPko0xmbYxqLOFolofNAyzGhNH+koxtQ2gcjvCC2DvSKWLTUrwNMQZpy3A6jw+StyR+Pyq9yOodnvjBraYZImyLvsGdzUx9fbgQrYQD0Y9rrq6Kw5sM+mq1fosjlJlJizuAR2T/pLL6xPRlTrtWZk1rEbAQ16m5rrjGa1Ow34abH9yqP3o4reGWeiz0ZqX96xK+gruUQEoJX7wvfzJnz7+vvcubntb3elOvkJXAqkACkmPnLC5jNR9Pq/8qkft/afnLP/VHfw2t9U/fRLX3yAgkJQcCqTcPaXXOiZFwdvL22eRUvnBjpPbzDqSccfadt5RY81UBdcd5jqgrb016wIVxUqAZYDutKMXHsfjjrA2qO0hh3pvKrRt4RojrDA4IrM9jHOF2qZGfnKKzRpdi5P1fVUf3qp9ZMOYtDs/tOubGyQ9CdDJhK4SnIivoH4SvjyDKDIwwGn2eCjx28ARrYZ5qwXt3T7BXphShYVCc1sFkRXbvHpa49BqxKV9yEeUU3uwYQeKrvEEV71ewg3/Dde+CH49lTxZKky1kQ176GrzXR6r2ZE3DmsyRVcjjvDb4aQDIbSwddlR5oipgcvMAJMimShNp+wzPiM+/mvi1z7x5D0udtJTH2zdgbsyVEsWwcD6eV721qOv+jW96Xd3/vmKHbGUaAbXAgC033F+FnWUCZ72U+Pbv3PpEHPWUlyhv4vwUK5/Vjokg3nqPjWeZ/F55+J5R1div009+yqwwVuhGOmE9Izhm7J3YES9rjFJFkfV69NzSp/cqvX2IWX92phUHPy8tb/h6goJNMmd5J7wKOj3z2CJMwCr71yY/bjic1QA7mkFRKcm0RUzNOxD6Gq+mL0f8E3+oqHPG5gDX6fzWcSGYd3gd29+uwUtQg6deD6u/FHA0xhXOFWla6/E8YfQH6uLh7RZQEyNh12Lhxygsx99Vf6et9tD/oFt4EKW8GPkLs2iqq5MRRAlGGpTXwfs/vctH/Xvl0958u4FF0a4Ma0kVKFzVXVD92oSCICn6eJtf3r0995c/NYbePn7g0GU1fvcHlHNglb77PjDPlyyxrfJ1Kq37Ky6dvyHjsRaB7/UmMFEBwP8uefxR85OfmoGRng3eYY9iKcVhuMaxUDkkK2oTwCsoVzCod9h+CCH5we1d7dh5TCAsdbSeDU5+yBFGF7m9tzq8087R4YzAGsCXYkeHlDE15bFHRBJQUHsyrAmQUYOusqoBvYArOQ7ZTN/y03iSkMUFCfeq14c1kVX6/kVMjABu2ue4Vf/rKkm+AjXiu477K2FG5x528FBfbKtLRipA5GrT5Jq+8dbQzt3RKpINigMLhRIDJYHWhRdYkJFjkg7Cj/56K/WQ754+ZRvOEmHjCkeJ3UUwiqWueoxFMWq7kLgfe/beeWvFG94Y3j/5WO35v7l283fejt7z62hFeb7i3aPZxUW2aSvWsDrMHoxrABWimcpECKiVj11wDPOw8+cCzrg7OHPXT2YQ/3YpcVmtQGWBmBRDzHWny+Z2V04E2mx1b3YsshuoajW59VfuU4EVWoixMcMXxX5t1Dk6Sm4OgOwJiYGSAjPp15cZTuFgTO1bn9dFewOmLLqR1eZWqsO+bSxq3Ht1MB71cVk1vc1uVZCJuxkDi9AXcUrn6XrXiU2FmdaoY3DfD82jbIwJb0aAkyTGOvmqtzNNMGai8kaJp8RMAaDEygNAQlgm+AxcEeKooMLopQES7mEPOd4eMwT9h7/uN0v/MJlFcSeEgfpnqI2lXguA6NU+dSlloPL37t44L85vh+EtAWPhZmmWVvgvLlFxpYjQ9edoZemOszc1bquvCJHCUoFsHziefgfZ+OCWl3kqZ9QGxCH6oEj+VSW1oOhbbzU65WFCT/SLYqGk8akK+eFof1vGNa3vpW04bNqSj0DL5BebKRTdUr2mccZgLVxJqiCnxn1qwr3Z2W/s3maNv05U6bxltAqn7jCaDNC7x5C/9P9LFS3Jtg44LV9FQfQVYOWa4EwE7xAEKIEf3+46jnxhjfUxyEjKUQKp0DQ+mQdMLNKuE/Oaeuq33bWDPmJ14bgiI5oPAqXMSYbaMCq8N0gKRaEO2gFkncOXApmDtjtbq3HPrn8+ifFi+95ExxgAGOKkF1HQFdW4yAXwhLgrS4470BQwlyc1OuqsE/KahZ666WjhkzYu5sdaiOGjXUw6z9Fo3/52fayc/xuJGhQrBXrVgGChrqol5caAC59xcSWS5YyAVYPmzVpbjAGsyad39P9O5AV3dgVmwBr/fZ6XvlLFU/y+IHULXB4sinPAKxP28OqsZoenga8HAWwrOtxfQBr+gIat2Wf+gFyba56BxY7GHSFLsAaQVfYpL0XKsS4uolNOvm3uO5SXvfHSkcTVEQr4aeCAR0D5JWkthc/NQ3fsa3y/eZ7fmibudt3IVdztCUlRKGozpJTLIWCJvqSOOZYVtdXSipUnQUJRxUWYHe7m77u6/ee/OS929zaBVRhhavLVQGMFc8FXXD+eTcHnNoaJI1rvLBvr6wRofrQTnrrhjj0ax4DPYVJUBAWxuUXn4WXnovPNwBwBiLSawarSmLujIhr5LHJ93QgUZdw0iYvReUDrG0wVit2PT1pWRdyKiNwJMewCROVWlBISE5YJIxPj/7zPMSJHWcA1qfpnqTA82E/DzwWQQAQWTcvywKdQmQucTU4z+XhKsysDPZBK6Ev9GaI91Ijx3BSdFV9WfbUGVvVSQKEDNj9Y3zqebzxMtXqqwIsVxXD+hOcEwESt1weazVLrcIKsam+WlUSJ5VY+xGq9z4/GT44tJ+ROOdZ+5kBcVjSj4IuLc1Mq9ZBuSE4CPiXXFI+9jHx675ht/bNSqE6K58Bgjo/D2BtIb2aBDf7IcB6X2qrczYrfSMepC0g1Stmx2E2Zdhg2ut/F1iUtjRBKoDyQcftJef5Fy8aiKQ5NDa/crNuoV750SCPNSV771TcWiBpYDm8vvU8N1enOxlNslm9b9y08mCVrRuV+sEl0Pi6qKcC18qQmgXOQKwzAGviZn00itdjVXqoUU5Vj9A46JkirjLR1VwjhiF0hTynqzCa6TAsae+nwZpeqasBI739xO/YNc/DDe/yVENE1T2dXvQmKDnUzeGonLAhjrm9LfGkcQAAIABJREFUt7irrig+H7JszVptscF+vGGzWDEZWApGBveSDKTkJbgAXIpmO45I4dzj/u8epqc/46Z/+8AIKxxldZW6wXwIYM29tLbONBxHTrMw2XaROOjrBxxnuQ7vTbc2X09G7FXmLO59DP/1PHvkjprlrUG4o04v0BCPhZ4hc7hWuImxRkwc2uviwayeEWjVno/Ui0kHd9L+CmkejFQhOEkiOpgco/3RwG/Hqn+JXtvXnXmcAVgb8GRlL0U3WvHfhEvhUKh79xwVErDRotY+Ada4d+gWHlet9052C/Z+UBik6Nobr9BVsytlxcM5QOHEa3HNC3ny71b9JlaL3bHRwXw4eazmiJ8KzyMROi2WC/tzZNiP88J+jNr3Cbn6byILCUKRAkwSZQxwdzNLqCvFXFLm0O1u51/2ZXvPfpbf4157DhrkwgXnn7v1MWxh9X4gwAvZMvaRTMAR/4VeWuuQAqzV/aV151vtXkwkc9p7HdUPXIDH71Q+aoMNcVLfCGqoFWlNj6vhcuGQUr4P1WHca6pnJB5Svmtj1uhB7Wu7Eo4RWlp1gqudDu0CKcYAi+u3O36S+E8lLGltcUaAdQZgDZ6LQixX2YF2XxSvoX0mIlSoMqJLkXsUu3fPiKSdmz6hm3cSu+iqF2AN/VSm7vTW3y24HboKE/BRHOWuAtgScyy82LXypl/GNd/Hkx9ghMOYjI5SjOgp46HSxUxptmvaN4yoy2e1+G238XbbbG2RNQPcwIRoZpVBEQA65RKDQRJUiC6JFqGCMjcniVje/eLF133dyac8ZffCC3neeWfvE1QdFLQ62LjoSUS1EqoPhTpX1IyfQt1erPDTOr2FuOioveh8f9IxqIRZl5EapbIAqDKEb2GsKdl7hXLWxu45zFn7t+nHbSOTTuvTRxb/IxhrDT3bFUlTErQJUuWU/2HgCTH8lVMsCZrkGPNYPQOwTudHVUKWEU7S+VwWP6aVG2ZqBLfaY3roEu/1uNI+nNmRhY02ds6+zx31IO1nvKzvFu31HQ19GUEtdNUwyhKAG/4XPvWDOPmRtXKiPi9W1wp5CnQX9mKg3lLgJOTajoXaJ1Satc24TmsG5PKqxzBdNpVxgyIZRIfM5ACS7D3VE51G1XcqENy+6JKb/vhPFgcLpLaGTeNnJr8E2S2gT0qvmniri8ZOAVClipuqMk4ro0vibkfwgvP4jcdVc1G+Rg8N1mpIj7VhRrDmloRexdKURVaXtJqmstAd/2YhrUEyiZoAWO2xPH1rGFEnOEdDwHdE//EIwWCiq5osz+jcB+iJ0xxsrrXrRBDcPoLF5xB3hxAoc3gKzMQAuuJwqhRHFw6mvlDPUXTF2hWBfeiKnc/tkUklCNVAS3noCn0sV28Co5q9lgmZRWIhCjDa0c83OyZ/p5XXClhUIe0r/5hTJR90Ja5aT7GrDKbNWmEmqJpLIB2sjygO1DRr8CUrpEiaJKfX3INZVVF2aQEshYI0WVm5ujvN5BKgaP6RD+euIfcvcp/csndwmCvt6qW4mur1XgarBbZOEWjVw10RlRxBFO54DN99fvims52EM5nRVvHPVRPcShTPtathK8u5Xapb+dl2opGJRloz1rtlpwNoM2V5Uv/YX4LM9FMcmlbY+7bhQ4nGNGUkETIAFPp91w9FXIeacFg3ntoZhHUGYHXHdNFhWAgIiCJwA+NJLR6PSBkVAKfUMGvg6OiZQxKmDOlxaMUBaGV9uI15Nw67/MrmJ3a7BVfHyWF0hU1VezudnQwKoifxtwHHH8BQYPddKG9wQGRo2AifuusgVmejZXCQ06aXD5u6bf85Ic35DqK4OTsKJVDx7GN80Bfpkkvi/T7HPufzyxtvsKuvThYMdEYhMBmmeJXl64yqUqLBqNJE7Yuk3wKFzCoFtuOW+7YcaQ8cZ8s2/mjcT6eaRIartS5lgisItz+GF5xn/+Hs5PNBVimWrauB7cIC1yuejcGuGi7r/t/1+Nm4i/vCoRO60nh4cwXOhqcBNcfc/nYZTtxxA/NKX7710CFUJr+Ug9HMXN/l9javBu1QD9aGcCYnJ3P2P+1ORELmWCmxYNzx4qUonplkV1Uo+UqAtZXT1fYGVxh0EMVIcjMGw5vZgmXNPyyDuBpBV+1waIFVaVAAg+BUISvpAbjup3HtD/HaKwRfAMua9p9qSj7MhNaqDLr5UkhPzmWw8jFZL8Aa0aXeHA7vOY/jZ+s7v7O89DnLAGlV+CFe9/qjP/Fj9lfvWEAxdWiKThUUHv2Y5Zv/gDfdsKDislARC8fsVqbtwMd2+qpMtJSjZE+PlvlCk6zyGlt1RqBDP24n46UARBjMg+N2R3aef/7yWeclw/ECKAXAIVSV5YpzacczT4nQ1VCcboxMHCvY1WLw7hKi85bRiOXqFrDuNuMR0UOf2D7avsyc7smuDFCgn3Z+x1In0XbHOFMfPMNgjT9847qO4D9beKjsPLqJFecrW69e2DF82vj/CHdFzPO4Yl3r5wCuYoeIaj5jgpEGGtizPGt4MbBz+3WXSvXxtzmwANnm9zKsz1Fdl4QBpNJnHf0C6Aj23onyOqwXQuZcNfKA5MT5PGRsqa0Dd7soWRK8cuxLivhBxsjIGbSQMnHVgcCmvHc5WEgOE0ERJlx8N1321pse+pAyCYwJAIt0Id7znvGpT13+3Xv4939fJCf3dF1cdHf9/u/e+I3fFM+7ld7+trDcqxI7gBSDEoAopdNlRNzo9tqq7ynfjLS7/+5/ts5V7wYbMnZBNaKq0h4HrK2qv6VDnvgZUvQWofVQkkpRqfMo8XSy2x/nd19Qfuu5FY+5Wh1W0o4GbVNzRhs1wbpc2CwTtgblNHSyUVXcpLLW11abHOq8pVUuXNUZB1qcqnWG2gdU76u5/14ea0TEUh3A6EoNXlkpf1D6voiPnAELk+P8mVMwPQO83f0n4TSBxpRete/zPlPSnsTsq/930VUOIdnKFux+3OjV0NeZ2FNhnGiuBPrNhW/9bbj983j8Tl4DKafbZiOAxFNnaZTidbtT75p1IBySNN7n1WhrUn7CSWvjHOnPFjkqGnhsHr/J3czgIQgUjpxt/+fVJy+8DSRAgSJQOJZAgKcltv3iL5283/3cqxYKB/xOd1oC4da31nMv3X3Xe07e974yT/7vO1glIZLwWM1RjYOZ5ds+9zxjOKCm9aP3nqLWk61XV9CqF11hVLpwuB7JV0lWBZeSAQDoAQVcsgpX/Ktj/vzz+cxz2sqnVd1vdXNNDZodK5o1N7OpeGPPW4bq0mlPGpJkrV+auroG1pqdQ8pa+Yj937tzOhJUFamXOf7KQx1SdeZxBmDt5942lr9M/WYMgEOWFZMzdJXKNC1pRx+6mrzRxn1Em8fQC8usc0FQdWmv7WuFAPaiq66nw+Z7FTrWWaxT5S54lm7zAhy5CwJCKv0ARgOYHO3SknQaRx62mUPuvpoUHT04pvlk86XeyX5y+u+FSjm4oXf77bif9YNuYIxLMjn12BMff+KzL45ItmiMIoDSBCGmeVRwA77juXtioArRANzlzhCjIBC3vo3+5E9u/Lz7ltKOVIlv04elz8z3Jujimy3waw6KzZS3r266BL7H0dvWP9AtkcGCwZJYPSUuRQpBFq0EQTcRdz2OF1zAZ50TE/oCN/GCDTnfCJS4xhk1DJLYBkF1e7YGONpNjMVBjDX836uP4PQk0UNorTFW8xv1jv+tD93YvivMT8mgJhC/Jf2qs0Q8o7U6A7D2f4IIl1+J8ufBXRq47EhnkFEQXHkxjLR1DBUWB36k/tuBHdvPRAS1vBgmuC41jdo3bj3TkO4KveiKjciJ7qeHWlMQQIm3+Q+L278QRy6OUPLydrgRAYpgyoQ+peBVF+tUlgQN7iGVC1c4bEjK05xNZ5EWI5TJdkzVzDNA5y4ZHEtHhOv7v9dBOmWA3FKkOliwuoZSyBIf/ZV7F39mGVWmM3b3z3SRdBBGuAOv/Y29o8dvoi1rjAUAZgXgFf9xoN+ll2rKwWdDELl+3vt2yO5BDn3uqYKxHMmZnSFhbzFEAg7KCH3mMb3w1vYt5yU1VsCay9nAWCvcot7u7gbGqm6+TYxF1hKKBsbSBlXW4cl6ugjZQj+tSaEfHnFznthAShohqPrnjiHw1orzWA/fAmDOm4T/EfFPjXvqzGPkcUaDNU06petM70M4Kj4YBa2pg+KUBcNqs4AxaDVCX/XK5Nn33v5mEdL6bRTqHsZ+0VWPS3uLuFqzX4Jt1gKbuquKt2NH3VULHlBrGwgcu6+KW6H8f/8/e28eb9lVVouO8c19KtWkKpWqpNJVOtKQSkhCK02CNCo22IA+RH5ic68CcuVeHz5QhAgo8kMULuiPC9cL+gR91ys+RFB43qd4eYKAARO69IQkJARCGpJUVao5e33f+2POudbs1j6nkgppaq0fv3Bqn73WXmftteYcc3zjGwP7bosaVGMQXoQzw8Oj6tGca/No5HufV9haOt97I4b7eMEX726YEVTOHJYf/Vj95Zctx9mNJibwTfdKG+5UKo3u6zfzM//q/Mj/4hfNzzyrI5dg6qe2dRtw3fXuC593fsbq3VV694eDgowPlIhqvnOB/p05eZ2ARVt88zzMNjLS4aYCWsiN9ao627HeXr8FP3OEsYvjUezMi7pw6YfOniEyDN4KTARYJW9FDo1+jZWlD8TKxE8RxiWvc6QhPOsKRFkV8G9ojsXMJyGUD2xddlglVZipwfq7K0whfAvwnmWxMYJs2iYG68CvkXMmoOh7l/Rf4ePzVkFZZTftghrfQmhVBuAw4Z65kD/zVFOhu6p8EyrOKcF0rAp5TcZOfARozX4lZ9JSbVISdDUz76KiBmz+aRz7RhxxXjCZBNUgoURIxyDEeHhuvno1VsmqWYqu69IKY5NNWfDigfIx9xqOjBEqBiH3E3NnapBHnZdMcD4BB/zw385e9rI1r/nNdbfeNrPoV0d03//9c2fOsGzWXfDkTgCzZfPZOnQw+0//cV9gBHVgfQ4u74gDr5YekKDN66vy44+K2R/GESXmm4+D9TAhCvP+lnr+Bnv9MfypI0AFZk4l8pyp7iomxXgXOmPEWGwpoiQDPUGzxxRzpeWGgW8eG5bq1xMEZuNL+2xcbYJOCKIy1ZrlwvFaYXoajSVQUTGkApBPib13PyxEEU0IawJYB2HpBEXXQWG6fM1y919gmOmqa88OcCNXeUXiCqWe3VZ/kDSypm8vXuwjSsvHjYLoGgGFrm/q6dFVKay12uKhB479B3WEAg4OhpktHflcHvsmO+JJMMCZeIzFGHf1MCdNU/VVPr/Wc3Paij+GqxYgnnsHCO577Sk7QudMCVs2gqaxVZ3oAMFPv3DNC15w2J/+qbz1D/W8HYddcSViRxyfeqGuW7cPNiPd9kes+Q//Yem2byOYSaqR2HGmHHW0RSGgRnQiq5GaLdhwgE7ozd2bV6++sLVK3ePq/j1p4fjhnAFHGDpfGQT9ACyAPnED3riNz9sQ/Xu7TtT7RZv/4hP5lBX69AwAZaOeVRirwDX5UDhSLgSMhXir+sJrWEZmHzJIwUaqeixrnbZKkFfhufZeAYwKAPsvc1ztu0cMRsdJ5D4BrPs83Ul6ty1/AHjHfGl145jYIk5rxS9GFu61ggEEufjJOShfvFtZ7V9mlnpNmBS/Mt+sLx06IYzLhB3xQ3bS7+LIp6GDRh7LYzU9FMSVY4Kq0alaMw3WmB4I9znfd7Fm64CxF9XQGTEXsCNsjYXSh0Fw0avXfuiDzoc9zxQ77+GbX78u8KEmCjv9TNLEurno7L3vXXruj66DmYEU9dnsx203QFRH75kDYqHuS6vmimC3QLEat/q7SwV5q/SbfXiMwwIJSAkC6NOPxFuOlR883EwMBhWDI8S0hVnIAwIc2Q5NPVY+EFvKkPWsm/+CFjb3Gcb7Coc7YiUiagxjZVbGXOHPHRNmGaHAO4G/Xva9RypwsM4mkfsEsO7z5pfUM9+/hD3o/liWL6fEm4ugmvnymYO4yFq5cRcGjrNW/n95MW4oC1pFL6040tfmWC5R3oU+QfNOU41Cf1FnTKqTVqArL4uIqvbAV/efxXTJRpZUGeFgSp0lVJkAG5+G7W/HMc9msIRCXLkmfY99vNHDla7um/C9cYMmAz0NgJp1Zl2SHmvW6dg0H35Q650SVwmMFkiqsbCfsQkyhlfUTByAmTkAwv1Rv+tAfPzjYCjvhSn2A3/X0e/NTsDzz+/EYM588POlX8DlV0iYmOgMPP/Ry+YrikqDKKxvIxhYh9W5VKzoZbXY+WJFsFtczzEsiIW2Dgv2ekjRVdkkFfzMTDpojJvUH96Kt23DBetiy5GYWEicGPSguTrBGHksmkdCA6qIdcOktTCOvMIEY/X+pKzMl5MigwRT6r7CGDoTpal5H9HmMh2lrX8smjuEzx6OP1QMU7PGFWGlZX9W717oriD+eL/tj2tqk+7hPOZOAOs7S02L93Y3CIn9n1e+zToavfzI1FHmpgS7A3eOTtHVGHE1hsy4gkU72ilWi5Zqi5yu0kvSLC8WHyo2epILdPrFdsT52P4HcszzI2rzDJalLdjqTUiDC//DF2ZZpwHFWjp3BnO0sTpUP8JmKKe1Gj5QQc8Ce/EFCrDiFYZVvnVdp+g+/onD/LkLOgBGVSogHc0vnc3W/MmfHObdDgF71vfRvwFqJK3DNVcBKgSUHWDoxN8xRhi6vj6YskSrqQmuiL3Srs9mQ99i8gzjjYfp1tNaD+8pyedN0i+fqBY4IQXEBEZ9/jb852Px6HXC5uAVkROAsBBO829YgbeC4xl6CRtOde2ymJSDaVr7s8T10MacZiQBeTn2Sp9WK8wkKpQ2GHW1uhcXCMXiQNOfTCjFU0Dt3jbHJQYFBYx++MLJsX0CWPcVX3nJrA0xBoKlvX8J/WNPx0gXUl2o0BV7MheAiRa0ajhrclzSHh22+mq/ceH3XT8eC2Jw2ErRYZI1EXkpi/vGxMQEQfamDE3vruBhkYchzokNj+hOe5dsfzHokrEjPN7iTSMDJybzh/fdmHfqh1faAtV8Vl5x5j5Y5McCXqfmuix8c6pzgk7Ia78yv+N2F71m8bSnojMH9UHrZmZi+rmL52HsN1z41GVDFyY9DQ2DvmAkIEAJfJUCgHbxE3EfJeEprhojAhfgpwVfa8FRPXRZqHs74Gp4wJUG8W0O6BcS1CXDS7fxXcfJGUuINv2WFuYCcyMpcWXGmqIf2KY01Tk3DrU2iEki0gagE5o7dXCIZxMwcXwyiH+IcHQBbCO/SF9buGZdjLHoG6tUzGKRosN7DH8599ByrjCoej5gErlPAOs+T2l9aJWJt0JULGM38W7K1ehgM4jRDFjCrFuIqxZUBquEwZJiWGXTYo+u2EBLbXS1mpDBQtW+IroSK/eKqnZLl3C5t9YAyFIB/gzsCGzW097lTn0l1m0GITAxKjDzEyfpYHAm1If//Wyr3hoQh4u4k/T9WIWv0oEKvZv/VXNQvzKYdwqSF/2mi3MkXv6ryxvXd7Rlo4iKZ6G+8KUlNRBOia1b7RGnUW2/EXOZd5RHntlJH7lnBqoHVWNm6Pfu4hevNA+7gjlFwp8hD70Z+zoOBZglCGsngRHqm9fEHKiEbl4jLz8e79iOI2cKLmWy9HrUk1R6lbcBcmDK+qEqwU+Z4L3pdJXZkCZnb/TGbdpjrOBPk37zXGnCqdfVub2W0WKtk4sqHYax7kLPlrVpbFU6E98WIriC7j3KXQah+iNLyJTVKXNwAlj3fUUlhAvAP3SnipntvdiW30bnw0yAJZhp56rrOYaKmHTIth6wVZFeOfczmonBEbsztuwYPLRyLZevWnfV086JZUNDfMbcF2/8mkQqLn/iBTMHAN0Jb8IJr8bhJ4aOMMM8HNrMhB3UeAiJLns91oKSXAkOOl0Aj5ps02oA033Di134r1JAhb3//XLbrWIgDEdts9/93W4+80ha2Nky7Qufk9tvJdW3PvCCpyptZmYyx+mn6o6zwwxiBpKdLvU8n+OMvTf+vYJWB0ry1dc2la6j5cOe7lIZNBwCC1qbeXSiMPNAC6rsYNi+Dq85Tt98LIRQEFgOkiNZJcZCa01ZElc9xrIMY5X1u0GSVR872P3pAqi9KhcpYV00THshg41qU8MVMWICFhswq7Y8hYMSmPf2EXybdhfvNzGnAEwEVDysYmHv120yGl0ZYTkoQaNXU/obTEDDVWLHmZwPRyicUt0qNE89qJIGL2UYsXQfY7BcSG5m/vBm0KocXpKQaa6auGJuo5XaMaRuok1HhlS3INXBXQxyRt4ZkDRZ6ywUBWXTBTY7wpavlb3f8trloOw0O0TpamJlR9CeF8laitJqlNqCaLPV8DGrZ26qNyttDUhSYCB1/7K79BJ94U8TNBge9xh7xoW44Wu6e6/tuocOs070EWfY4x5jNBBu8xF83/ugzr73u+1Nv7/v5O0zL+D398c73tndcP1MGOKzSSFhphxJn7rX1dJaOH+vuzUPKUTVZnHYs+OkOJjuOEIuOs5euhVUB1oYdzQZTXLzcfbDVZr6Sp8nnhfpIgffm4KmAczMMgk5GJgmvXtM18vp6JkjstTjlOTw9HJk+dubxwJj8TksFuytyQa5e2qxK9OZgDCDUEyMRpLv6+St+2wfYDBBr/Dv93KYWKwVxudpW91FoolBQczMzdnRBKJLj8PSezk7ix11BtdZt0gwHl8YNx21FrJZxFpV66JyrzE1OrmCQJ455iszClk5/Y6gK0kOgsoi1cVxqE6bji86oovLQs5hM+DOj+Km38WtnwRNjOr9saYnvVybWqhu0EZBT/geDRDWcq5VQqXVvK0QFaUm9bSZYh+E/tuEGrj08z/Xveu/Loc7Rw3i6xV9UST0ksFoNELMlJyZzXupLwEzPOqc9dddb9apeV9Hb1Qp9GjrPoIqtDRn6V9a/LOJvTJWUg/1vncHdP55Dl3TDuietlleebw+ez1AdBS3rObAOdRBuv7KI6oSk4HOp08lTFP8LYd/YFAu9HFV6S9puduMIf11aCMxZB8w8KSlx0pxJtmwbOPTj9a6vWw8TwbsRXHNyQ9csDagoSOuhvz8Hr1YK9W8+snMefp52iaAdR83P+9rwAImDCUqAXTtL3LNfzMSc5MZ1VZCVxj1HbWRHUsM1JzVWJFnDRutPCF+FQCrRFe54ip7dnuAxZIPSyqDViy/gpl7s4gp8ZQ8gGK/TlSY6O7P48bfkps/FDoKgw98+O8EsNj3tSnMc5yNYXSEoFoMmJp8WHrYAySxlDZT2e9sSdEJln7u3+n3/cC+H/8xQ5IzYqZGJ6ah2GEu+KKBMChNzCk7AjRn7Hwg8G2386QTl9Q4823A8Lq9uWHG4T7p+6YOIMt58a9SN7L8twFfpWjskOerqoHSAMyMcz+0POdYvm6bPXotQIdoQEIjNfi7D5BCI9Lq70MrMdbQ0KcJxko6ghIMlIRWab6MLb/WCrGNY6wcLSUfYSN4KP5FBT4b/l4mkBFYBLN0dGLJBgc/mQhevN/evW8YjwklqPCMt/fH0enGXXHBMG2roKwz+GIJSrH51ZhtcfI4GrXPCZsBVKc0mngPk3H6yopSYEXhlO/2HTFs7Y7MqirnnAKxEZtmco25cCg1Jgeka9FpCcAqewbzFGemaE8y8QNdTFz16Erzi+Drp85gAV0xtq2YlyWsPUa2fJ/a3dh9OXRZIGE+DpHQJv6opMMh3ExsZgTTlLoFNcQCCgRZvN9RhtS06IrOA4ViNSYzM3JmMqdRab4y8ZnP7D/rLD8NOvYrFvqwJKaTMLO8NqXnt6gxaM794bvwTx8T/1K8syz9u7ywcmydWTQJNn+VRhs135MXCrXY8dBetZofOryAQLAUFrHhV7bW4UUnyB8eZyevC07iiP4c6bc/LOI4MJc1ld5/LL3OnM3Iej/SVNmC2T1C+GHU6r1bDMZA/5R5iCjKkSP4JwgK6zJ+U0SywjM57KXiXVvZ53vGdFhS+EedvXUZcwMgEsOerUWFTdvEYN1f184bYK45D2v+mO7xZuq1JLrMACkUskZtWayKvhngEUbQ1YI1niS3eL1Xk7tibEXmOGtVHc3qxsMCXUkicai4KzR7BpFUBlElFeYph0iGvXA0ieOkmS0RX30DvvYu2fONYSZmHAYYlpozYH7o3qUDkrDC16cuCBYgLK0z9tc80E50C+irA3EYVwVpClsDtwzlvn37PUGFYMo+rPUXptdSLfrxBo07TjpxduutrrFGb5mLrtJudOwKj0HJsVrhIc9aCUShIt7wPK0SAASOX4eXHstXH2d+EKJFX2e1BmmU/BxrhQWJFfFXao4efhv36mmttKRrTOuG5ZitaaEwEX7lNFJfvhxk85U+vir5lQAmOUh9Zxaj/Xi5MP0rxGACU4BzwMWFB4DPqb14Hy7tvBxuybg8oamJwfrOz13Or3/1Fsg9kOdCKEYFoRA32K97N+AyqqFaY60K+bpcyrgiuhoWfFW4uli24Gk6O5SLnwpd1f7vieuEjeuuUlPAgmYDc+TXa/h7dOUlAktEBx75NGzYir1flb3fMhC0KNQhYDNQPbcxbeXMkTsicgRvkUOrVbLslrb/VvJiTVY1CTNnPlhQKN3c4GCvvkiZhrvFSVDotS5E63/eo5uEkb4U90svWfOZTzbaSpuG7PVpFyesqiRFpDj/AjCteE2mamAPdmi+xuQp9aj0IUieuwmvORG/fIynvS1kZBU0EQe9ebwF4mLPkmEjZYZYGQsySA+SY2dfMdMgGraorNTXnWjeBGSZGdZr6ntaNrQMVeRZdpB+r8JisGGUxfIX5eFC4AEpXsoaW29ByCs7+4f93uuVwCS0mgDWA8Zh0WfEzK/g7DDIhX6t45aCN6kzmIQJjAW64ohr6Kg/e0kAWx3e3KSoY7df0Y1bhjJX6CrLCpQk4qYWXdWqDPuQAAAgAElEQVSqdo6o2ns30eL+860y6a+ELAgzl4wgM9IpQaewjY/mhh2qN7ud19qQWR2rAfBGWdMSrEW6JBOPoVXFG67/SKgLcvemTL2eU2JNnKEi0BkxJzCD69Re99pwGhq9hAS9f6K14RUAcM6gNSPxqlctvetdUCnlMquRmtUpkClH1QRPYyk6dbLQdO9lTAuZJLo4b4jw/Vv4O6fiOZtFIhcTh9CBh+oBVShr1avVhG4fgFHST9d35JVeUvQr4mzfvu+v7expjWE3ay3s38Oh3MiWs44Zcvi4aO5BC2Ol0wOlGvaGcdmsX/+H8qVSHIjf6+xde3yQgoSOzmmFeh8gwrTdp8tnwXdN15yKw94J+YHwnCxxpjb3T1AHK+IFF1BQNbSqJoNGZbAtnB+vDLrRin8JrdAgwMy1d2TBzI2xYjUjBZbWo4Xc3iV/kf+YmRcG0M+itvdrvP4NduOfY3lfaOFmuHiT+H0lsMWQ71iotcaE8IW6qHjPGMCqQQnJUGxUUxjpILb/nv2xpREwB+uGaZWjejoCV1xul1+Nz/6L+9BHZtd+BcScZJr7m56MZ6TSv6Jo9CtOeAxLLUBaE5xaPHR6dWC0sgOAdTO+8Bi76BSeNEOMUQItmKCZ9T53Q2NdqBWWvXvD4541A/qxU0tMU9bUCmV6ow6YAxdtgKSyXJjJwSUR5o91sFannYy41nDgyj46iStdWC70DbhGhkSe7u8VL9mPG5eXDMvx82QKdZ4A1gOErrw3on9moGt+hOveazySYjYnZkAHODiiM8SSP9teWavrFrSxSmLTjqGJrmrElqoGXPsgOFB0VQvtizbDojkx/ZWrNKMu9AIlll8mhm5G6dvB1tis4/y6N/KG/8qdN/nSg3qTAuNEYK28UCjUWvntp8UtVgCsMeqrRmzFvadCM5uZEk5FO3Pd/u7227r3/Ilc8jm97mvylWuWdu82s44G2mzUS1ZDOoqAXdfpTDgXcdrUWjVPOBVLpee5QOquqiKSZuaMobRpq1eOsS97idYZcNLh+uLj8ZpjYb63gYJOQ+4ozWuhki8/xVgRdyfthKliqQGMtAAljSNELt7aGKsYxdVaQIctbqqPZR5eL5oc0RrMF6O6Ng22+By85sxHZJvRcLvg5/bgI156pQiDJ3wH4XQz35ttKhHelwHCYHChgc0A0audm6v7XjoRhTmjMAiGxJIGmLwyyBVKe9mz1QRY4+iqPPgYuvINfatBV2N+V2xBKx4guvL9jMklsvSYkpYL6RNg6YJalUYVYMt3Y+0Jtv/r3P31aUQ4SPxWGJdHWxGLCmCT4BkDZ2sUBprNxJapRsONN+OXXjL7nx+dXXmVfePrS8v7O/NkpDEk07W2TkDTOYWdQqg6P8y5eeeAbjWBzQURtXhLa4gTorq3kN4AIYxQgz3pKHvtyXjpsbDQDwr1FJBfbJkL66iscMa+VlguPOPbrBZRRaNMFHGC/esJTzQ0/JG2WI+VFwqHqmCyUslH7cz7k8nxfTky8w1lszSZHGIk+JnByKQYpP2/BDCVwNb50fiiDu/bC5jEzmHnIRinFeoEsB6YVT+iDVNYLKheCR4He7RJ/0T723fEgHTUR7TZTr8Suiok7c1dWuiK0uLGVomuUvqK5ZEzdJVwVPlwUEGoNBMMg4dW6CVMhFyhc8yB3nwYkE2Psi1PAO7Et78sAGeAQqYBYkUgtRh8kAnNE8z5WztJ0JhjRNhekkD+jaLGmUFB+/ylXN7fGQmoemGVj3OmGnQsYURMTZ0jDOoVhp2CXE61Lk0nsANyE508qw7m8Ondlmg/tR2/dyqeuZmRrwp1qSBjUh/ZXbcqRxhiCIZ6CSrJdVpZxHICSjgwXoPnRxHGLBUGyt6wQEGf2MejFdkTRVC940kEZaE+3jRxSE10BkTWGN7YOMlcZ0KyEy9woeB9y3zzXtljof06lmYbfN20TQDrO7C17UB0H9xNTr7LlvxqDATN+eiBlnFJa6ooxewYETOxyK4pli+rc2QQg9AKiFahq0Wq9oK+Kkgsl9trSZZzH/2xqtNjIttnIiaLOCxlwoJhAIM1hgFYv43HPtfNVPdci727LV/heuWmuNAcPo0d9+7ep0gvbDczj0KEB3I9gw+qEYBTqmicVvxhBeZlUsGGx/9zBAP15iU00xBMRxnDQ2PH6X2tUmg1gar7BqUkDisi2eIU2w+3//0UedvpdtzSDKaQHmL0DS9Da3GPRyJekb4Bzyu1LYdPA+eU4w+m679+N+/kzkSfHu506wNnok+UFY3czBeLhc3VwJBZsX6F5WE6/VhPqQi2/oMsH2r9PzS2c0smqLf841LdvRJUUaf+Ml+iuGiO6+ZsRk1P2wSwHkQLs/nNNrtL3I+bEQIHWkdzC7moFAPVE0Abh7UOOEZcNeFLzzTX8T41uuqHo1oszwoFJj8M/lUBDy20vErCDYcrVjiguvwPdMgroYYZ2dEccdQzsf4E7L+Ru29Kd/FF3dp2ZtoODGRZlhbSWGysBtnQZhBTmKFTLqETdWb3Xhyqq9+15qUWFECn7T6MiZ5/cjA1zKKej085yl53svvlE80R8Jg4NvUNUGlYu2WvM4KfgQGqaoVZWw5zd4V6pZzBEaaBg+VIbEBd8eNKw3taYmSzsoeebR1NVi3qkpYBu5hjYyWFlu2eDOzehljRQV7R2T/sm8bDCWA9RLb5l23JCZ9mYuaZlVXGC+bP+KjuqhBLMX+cGmZXDUlWsFFmNTTk7zR/BGn5XaX4qbaccOnoWOuucntSyRRnSXHQBtTVs19N0weJAM6AJYRRass5cuRTzO3C3Veh6wDMBJ1flU7ODQ+KyVc6Mwd2hBDdEs1MiVIw3iOhleDOCjowS7ZVclrTdt828TELhM0AhVKwdmYvPBFvOgNPPYpi5oNXKM5Ue1sEQ1pZM1g2QFkOaxhpG6ssogp0Mow5lXyKBQRj0raarjdrd4falaqFudKg6LIQae2zbd/gXOje0CxToqwyeOm6gCB+e5+9c4/0GdvTNgGsB/mk4QjrrsCaE5fkXC3k3ovRFXN3lNoiy1UZOBhHVwuYrWb8X5XibEAWL1+AJ67gd8X0iXf5OWfoKv/cFIr1Bq3S6xKSIajHW5J8rgReDktGI5aO5gnP4Zo1uOcG7L09aDoJ6rRce8AfFO8Par6KpKbShZZ7YeLhkyioJGfOGtZchVHCBKce6K9YolOleIXV6Rv5ykfwzWfYtjUQNVCiEs78A51wPcHLITI0TH7L2v22ZL/qMbTvlkVd2msXt4dyYVtlOo6HKozFoXrIFgmW7MZEXpac4Nggv3iNkZNwcZz0kYNif7Ufb9gvu0wZxO7TqHhQ7//pEhzsC7oELPubdO1TsO494A44Exv3E887+0ZZq7Hw5n7HxWVBjJtd1Ue7F35XaJkysMLyteVVZcpgLgd8mQ9Wiq5y8sx/qJESpV0dbMmihhO49WO45u12/UcsJgBN/lgPigeGFrK6fZD6i34eJoub/nrF/egP/fae90yX+IGfYwziVzQ/eAJ/9SR8z5Gm4gGXj0QiTFRVegQSPa4GAwX/b4+/DbA8OqaHPiPmWNmbFc0g5/49DZuDwR8LddxN/v7cs2rMvqEPnS6gv1bDrdY+I/kH1Y3mfSJQbq9QJuoAl3V80X58dq/NAUydghPAeihsAqqYtxHB+hdi7Z8NTgQtCrF0EG0BmtY3txBdtexJM92VNDiwMsxnMALNJ0RJlnJjMdUy8idIxYQlu1tNdPm+QlfhzvSAzAAcQcxhSyFP2tcfMDMA6O7GF9+M69+LXd+gGSd09WAZg0gzE9BgcxvukwMfygr5lk1E1YNkSDxxPV5wAl99um0UCAhzZnNyZt3ck1zmK1SKIjQQygCcU5SzSoyVwooUckUr0cGx07LPTXw+84NoNWlq8f5qIA+1ywyNZf6fzIMUiz9nMcYqf1EN9oYCaA7EltnPLuPPdkvAmMGdaxoSJ4D1EBpaZLbhtfPDXjfeMMgGwFqcCZijqzZxNYauivxmNMqCw5m4NrrKnv7Ffleo5WJs9Az245DLR+X6dbYQXgqw/AixBq4zdWGKFQMcFAahOOh1f7X0lXcu3/jxadH2IEJXPuQG8JHN1hqgbKUhq95rAlgP9PdLZ2bPOEZfchqefzShHkIYLSAn81biYuzEfI4rAGPCDxUk1mAW2qKOUs5GoJYArOQGSQxLwwcVkCiDWWQKUyxb0iam6olLnA6iMbPEYreJsZr8ki3CWCWeG8NY1jpyvDi/tQ+/tVv7DyGc2RQ7OAGsh8A1FQOCxTuxtA0bfp9LP2MHC12lqGg1lUFUlugrAawGuhKD8r75XRVWC1UYTrZ75XpVfEoTXcVXxKAu8QlzUV/qDAIzYtdNuOKtuP7PZedt04rtgX1YZsa5mDN03jlabcHoNKzwuTLA8rPddI0fyG3rBrxwO195qh23luJnehNT0FSF4mFEF7oBPclEy+iooVBollBN5umrijpiCkcCOMt3LDBWRYMNdFebx7KKYcoBWQBY8VdWNIYPBFt6BGvfy4axbJxVYKyIFOtCJPB/7ccr9vGbHRhOVQw6LTgngPWQgllUmhh07eO57p225vFQ0qlBQoROGlQ+1mbYtmMY//KqkMHwjMoKQA2FlnyEuxqeYKnOyiFj1LKowRFVe4REZcmSUcXqyj98oK9Y8WRMzjMV4PsTmwFm7GhrNEjirvsf+Mo75YZParB4joWqoH83GRawkxXS/bXNgDnpV88SGSwsHOm9H5AhOE2bD3eLArzgySXeskimK3yQJwyDCFVJqnlxlTOoF1p5BomgCaAXHouXnsqfOs4Ip6pC69Q560Bn2gFkgjZSCRStyrTRLPLFDNABo1QsVNix1Dy1MVaix8ppsJIhw6AAi8W0UixV6pwGsGh1XW+0EIm6UjcWQZg55TaQIoEOncAMIqCCiosNL9mLz+8DbEbMzataWp86bRPAetDCK6foIHA66zA//LlY+166w6lR7e5L31IRV2Poqm8YXj266nEGVorTQUVxFT3IkiO2+oQLdIVcTVXVDVmcobTQFcpw6Kw4KBljl+UY0hBbhZIWRROwcwk4M3Dfjfalt/Ar77dd3/RD5owgOLehUuWHpTi2TtvBflKIIlx50dvVstvDYlOYAQIH62KAip9kFsRDT9u92wQARKHBM0VNIlBCiGYljlnHnzoRLz/TTl4DENpBSLXQvRARVUIvaUYm1RgrBGKlNa8Ee+VSqvpQKf+UC54SiIZMQdWQhw/H6T1PRxKdK2QWTixjy5oYq/kAaF5dsHa5MId06TDcYU6IwDoI7jb+3F7+zR6FyQyco0PiKD89LRPAeiiAK6gPyDGoV70DevjLseEtMRae1GbBboy4yu0YVt8wyIptSrmrltV7UQFkCfLYcJYXa6CiZJfs/Tl9ZVUuYRtd1WmGNbDrcVua/BOdSG0Gp9Y5+qlB15jMI9i94S9xzX/jdf80sCfefjqZ+GOb23R/3x8TtmcD6MS6uc2A+eJ9LNEg+hqIhmOJzz3x1r4GJ5xkJQcbEBsM3luhQwgdiv0iBA3PPIEvOUWed3wXAteVFLMONIGpT76JIKns0UsLf00+aQAiWu6YopmiJbCq8bFAaSW91MZYVSa0LaoVZpdNW5+yymDmqgE9pbLGPsufVGcgKWZQ70dmL9+Lt+8GMJvB5vToykVKcBrgJoD1UFmU+6Qco69tEw6buk2/hcN+BaJQaUnXF3NXLSP1JrpKn3iOHqrsGRyXtLe5qwowWVN3VZx2ga6Q2jcMZ2UtdJUxZDKOrjx9xQz5gaCoUpbMlmdEB9Cw5AscYAeb34ZL38qv/oV8+4YuZO1RA7lCwPKC7rQd3BHIlyoA0r3oF7oFhQqqM+mMAEQ6e/yTsO0oe9JTcexRAAQKE/vQB+xnf0HuuUun7+v++rriShKkxaRIGk46Ai84RV75CN18GMTi8KOdGqhCsU7NGdExoADNaZtVYKyM6RlIrAbGKl0StO4HTHdhVoBryOGbnYn9gi7zZkvFYRno6a9iqzFwRYBlNDbE9S0SK5lIejrQDO9Y5kW7cXdnQ+iQBeZxZpxjaqyeANaDn76K0it4LYLHWlAj1j4CG94G9yMUJUSLgNCFSqlR4got18QmmVThmHSxNubIMLytKf8qZO9uISiUysg0LQ6mqvZix/RTmtKrlKliDst6FZq3bncRMwmkgy7FHhoHc5Cv/b/65Xfh2g+LqYLOzAgF4aVZ0/LufmGwqDBhyhcuWElLOTl59PxjP2F/9E4evS2UYv7ln/FDP4KdO6cV+f3BOEYDOQkUjgMo7odP7F56Bp51DNDBHLgcZm81iIWGPt/HYJ14zrEUsCP0BmaIpwRY8Yn3ACuzb0gU7lVJrgGPSp17rmfSOEQlkVDZp6cYK8FGEfrkaQRWeSiMWWQViKks+TUAVvF69nHqbNZRYYL/Z5/7T/u6a/fFt2vo8ZxA1f23TU7uB5+9GjKsGLJCoyrS5t+G3Mylx9uaY6hedr1qdIWR8OYDQlf9+8eA3UhxsIwaTE4gQ12ZMIu5KQNWRFdDpHSOrtKptJFCiEx6hQin8pBpSIz9cSRmANRmkBnMiCUEle6Rp9kjn4/DjrB9t8jOWzSYlJox2iVOE/bB3WawaMIj8GVc2AJHaXrXWItzvYnnC668An/0brzoF7l+nZE86WSS/NjHpgt80Ac46wO5/fdGzM7dhl/boW9/Ak9b559Gogte/GYzMUDVXKjL0Ru2B1P1fhHWV99iNHI0D4j/x+pXvfKA/ajHKMEz6X/JPJGwMERP3tLkgTho+spPL0Yl64/W75KFcrA+PDMjeiCJc84XEDUtknxK/rv+QAQNnTNRKHFZh9fstS/s7SPIxH8boHLFmJFpmwDWg2YLzeFxFSN9zd8jjP3Xm9zOpR8FZ1C4BV1+acggm89Sy+yqBzTCUe4qRUjChr4+QVfD6dXcFdngk4BBZyzVqa4GXaWwqUB+hS+DH0YG6RUtBkIPVUiXXF4hjeqMglknEKr6UEKDCwjNQJzwJG5/mtG463os7zZPm4QLOgGsg7ppSI8MiN9LpBeFzvrJE32bp/a36L59uO6r/Mnne4aSF15gf/tRfOPm6RofdAbLIKHh45gN+Jkz7C2PsWefEL40FaIzOCMN6kiFeA9ZoW867LtHImbqV6T5t1zlBsasGSvSkpPjweJYUiThJGCOGHHvGEkxZFpmiDdrBtTQYJKa4TmskBAlw0nDirAIuCkWr5QeYy1KJyTE0Anmiv9jn/vIHosNS4NKfxapOpnSWSeA9dDhseIaLQ0pBRzEaPsvB3dj3fd7ErzRFcgkdlNWrbtCy1AKoxCnf7FAMHT53mx1OI4l4SQrv6LLL7O8KqqN/UJP8pOR3PSILSusFEsx3zFtNjSIb38SmJGkzgIBQgNmFJh5NssZhFh3FE/7Idt8BrATt10Tih3OZjbR6Qd5vjZfRmcAwLHDYAWbhtj2xMhl0QS87Ep72Uux4XA/PcvRW+39758u8UFmsAAflc4fPsUuOh+vOAdHrQF7QKC+P8RHCBqMHT0e85a/gqBJHXipDH6UuYHIc51Z4KVqeRtXQRnsSMisLEM6Mf5P/pnpMVJsN9Bt8QRqGFSFCcZwZzZRV3inpQAy+eA6g9AizRUxVnoEtPJwBHjVPr5nF/qu6Og5BjAWegWTE80EsB5aPFbdISgaupixfBlkSdZcYH794gzm/UkDbxseK2EbWkleGexzl52nmlhitRpdFe9kMsalJFONruKnZ7inRlflimv49CBgZ/J+Yd/9V7QHDqNjGomTnrZU0itXWcDTwLC21eSmt/46ACb0XQnhmvuLf+QOnPkCbNhk+2/H3d8QQxeHaAGMnn0ZrpoARukplqmguFrCF+EyGnqf3kWXzsINNcAygwVS5UlP5iN3hC/xrB3u3e+2nTsH6qWOLJjWgi5MsC53dor3MMs7meT52+zXzuMffJedfQST/n7vJRf5nfA9QXqsQxvEShEbpxCnHz1gKcZiL8fuC3Y9S2WCmvoK4McGpIUiRClDWSncSTmvfEBkCbOy5OY44qQZ5f59VnscMv24jBdnHHMtOcl6cWEALT9wQuORUIGEw7vf329v28l9sR+InpG37AmcBqoJYD3Ux7FQgPJwwvZDrzA5Ae5R8KNOLycQLDK7WuDSXpcRmZsIp8J2qfbyZUHm817+hl5Tn6Er5GiJI6YMpTI9snQDuhoJ0ilwXlE0lDiGDYRZSrRnJJmhNIInW2b0YUYwOMgMPO4C2/4MHLYku75ue+8CSQu4NZstBAKKWa/LpcO0KFz5wSBBiGAW+20NC82r2Etmkti2/p8/+Tyec/ZMoTSS+pG/5w1flX7BI5h8scoRJdZkNZLIiWybFvBVBLInH4F/90i85bvwg9utC4RwhsFSuMEUlAxVvGxAYE4HFVW/rHxm9YjHAYWwoV7KEVVBULHdOpQfhw3qtMBYLCqW5aifEFT9MQrWa0RN1ZJ8pTgsOUHLHN+cwKAwx/97v/7OXtzapRjKm9hMz8EEsB5mE0n0GvCPhd4t8lXho+ywE4UGZ2s66cQgcEqTpqQdY2XBhoqrcGTokVM8Tjl8VBbwaGKs9Gi1hKtSZbGQXhWmDBFdmSus3pMgoBHH9oSsYsCLnl1DZV7aRFfF0dIapYeSS0QHo5iYO3wrTv5+PfbRsDl23YDlvf78XdKoTY3e0vFyiE2z+cqPhVhAVNq/stLo30fSerbQRb6QG4/A+95nZioUv2j5xCdx6SWWUDKofj7Er/8gjJK4BEyuT4+33KbD9Xmn4PVPwEvOwpY1oArpmHo0EWnzf6KDj6LMxM4y02hHjDUALWQlwmB80w+hPWSRGiqllb6KPUpU7QU2Su+I0UJhi5GKgzLrckBGVQ30UqNWmBFmyNHYCMYqECAN5tBJGIgoNMMnlK/djSv3SkISC8Q4rfsmgPXwXCz6dWFQGdryN8hv2NoLYFsM7MQCh7tkZRJobceQIw9zC9FVv4SUQVVaOjKk/NZYIA8TmZSra5Ejdu1MKLFKd9XIw3HJKpIj0qte284imjD+vSmS86irUtCnNFj5SuhTIJYMHeBoBLaegkf+BI46Bct73bevNTPzGEv8u+iPYYSI95idtpU3S1gDAVhYBrXoK4uVFO8f7ttHDLR3/gEe+zgBfEc+Afzt3+LSS4Q0hr1WzOE5xIYkenUUDWI0SA+TwmUExDk8+yR97aPx6sfLIw4HpR+oPNJ1iTtTyk6lyqf0246NQEyc9YYmwaqelgCSJKq0jJpI+ae0K5A1L2Rt/gmZ+D5iLNbtfkRBSqX/X9L7JcYCRjFWrsFK/yKioMhSni9o3o2EdD0axQ3qfmOP/vNexKJpoCI5aa0mgPUwHcrUD2Sw2IhOW74GuINrnw06esZ9Bmi1WKlFV+mINYauJGuAGRiaclW2Cr8rjKQs5wu5BLJUhqIp6EmtIpKq3IBDU2jFgQxLfh4GWstxW10cLNmp9PylbzBMioNu0ELQr9PNc380Z9h6Lh7zAtu0DXvvkju/puCApRikdL3FzcSUrEyh5J1N2poVc4iPJUAZ/iuEPeaJeNIT8e534kd/gh5I+Zh1QD/8YVxyiQHiV+6TPK6Ct0IKzDuSOERjGT8fE/aU7faqx+KtF/DsLZBojk5SYXSmEIOJlOHEzBitNEQ5I2asUDB5VspSAidZAWaOCS3zhd7fZSgvNvrsLFOuV1r1ni1KmxMriFd6OoyIpVActGXBkLHe9XtQ9kSmSMsGdT4UUJkJAcGy4eW7+cF7YM6LMRjQs9ni52vaDvrwNm3foQsdyfM0VjMEsGx+BTa+WXrz40IIUDiqY9yoHQ0rUWui6ZHiYBtdScVO1QbxK9JXq3FsR5VpKBW2w0jnYFPb3uOqPNMwHna8LVFgCi6ZzYml4GboAJ0NtqXcfyc+90f48vvtxksQZWCazCCTN+lqB6DoXeSo3dyyDPImKrCcP4ABdKZKKigd1MX60nc9Hp/9t4JFnpjFYrJ2IfQm8IeOmBvx2OPwk6fzpWfbpiUYJOQNKunjbvyd3gGAaS8YCrbjZkaNX42XY6vl2YKFXXtvpC7RXzRGB1rm4R6PDH/M3sgzMWG3wV9UB8pmLBMwRCJigQFpmhgYD5IYkGIFH/bhTk99Ry0PzJE0sjk3EU3t5tsfMQylnRoEeMU9+M93+2VhiG5zQCeAisCmUWkCWA9HCgtOfTKaAUIqjRqCvTbq1tdgw6/D4AimWWwLVO0LKoPCbKklI26lycHLhMGK3zI2gVorDAd522ArbbCNrtI4Z46grmYkTmF8VaCrFCA2PyJ11Yr8mS2F7HmdAWaYBcUv1oA+p5A0M+65iRe/Ry/7a3z9srAkD3DBAKwcqzcNQBaWGWJUGKxLItCb709uZlNH6bKVhKG3+AW5eRPu2tU3l4T4vAlmtRDnjJwbnLPu7GPwY6fzpWfbcRvAgKIctDOC6qCdisjcYFCazJOGwATWQG0wTehBj5VRgCnGAhAd2xPQk0IZjWip8G2v06CrFJ0qBieJBNAg8KpCBsuonGHQs9KN3Yw5CKsxFopg6RBVXnivh7GlPn4Os6rI51jL5O/t1d/aKfcsKwnrhyASaj2YnrYJYD3MrrQDOmfS9RE6QaYTwm7XHiNHvBFrf0G96aUuisEp0VUIPmygK8iwxGugq+T4xhLrZHRRX85DLqqQgnBaNbpybS6twZA16StfHJTyJIFxgFWEQ6d/r8tbKfu/dxbiaoVUiZ2P3hVgllQ4ZsDtX8HFf8wr/8a+eeWAGFBHjk1baxDyDKi/WGoYE/TGG1/ALs5D4TtU790df+Vx1rvfgxe/iKBKWMqEnB2/pp8ufLz8PtdLaHrm0XjOGfzFs+30TZH/mMOcoFMC6GIGmMaoG+8Qp0IoLEUzI0E3WsOX/p0DGhswVsEq9e/XkuH9q34AACAASURBVOwpo2bSr1cTLopJvmGSzUyGMJ8CFeXxzIN+iVpG1lhfHjWUoCyvn2IkSZBFdDRa44fWq+zYl6DaCd63F7+xE7fsD0uXeMP3Gsfptp8A1iG3hiTUCKw7ExvfLOueYwpzBqO48IQRcQLqa1tocVe1laiESajxZaesT7qerfitzLIBSXdejdvqaqNroKvyrBiaYBrQp/Z2R6syiPHOwSqvMLPOz0uHlAjdcrmYpV5fAgqMlur9QyLabVfIJe+1L/+NffPq4BtgfXabzwPzFRQfbghNKolSrpcPIQpLegcmA9TAAx+aDEb1gNpHTRLEmWfxmqv0ULuciAFzgRTs/WF6k09/wekMXRCkqZx5tD33dPvZs93ZW3w2sydbCDOkaXUDO5WU+fo8sJ5t6qt+iipJEOkBCzTWA5oyptCY1P6y9w+ARivYoRnGGq5SA5/leK54FhXj2YKseSYgD6VOagnxxJpBhCnBlozNRfCzj4vqVL3kTAcm8sP7+Iq78ZVlf54TnJoA1rT5ynl00z3sCdj6VnfYBdZRw3AIOqifop2JLzK2c3UqsivFMcjdRAeclPni1dosQysMpwmwamF7YUCamjLUp9Sq3AWWog5vlvwT649bALCaxUFUKi5WB+/xlmTZQZQMd9odV/Cz78Nlf2c3XxZmuKEryQwmiKXhiLGiCtYOOXGET/0NeegZE3BgV8J3O/hZWAgY/rfn4QMfOPTmmMEEwaInviZXdAbO6SVrsVp61jZ9zul84bl2zmZAoYD4u9JgmqTjaS6cSop04W0JxqokUznGGmqFC8RYhTqqzHvOxFg9Cqkxlsd8OULKuCJNs5xZK7GGOyxnuDKMlZBhyYjHFo/FJju1CGOlAK4fKIw0gamJmYpQ/2U/fnUXP3cPdPDamADWBLCmLQ+o3/S92Px2me2wmcl+qqifwE2BWW4nV3BX+XdpRTmvQldtXDUWhtPMjS6aqh0allc1uioEYb3GCy1TBmBRJE5dGcR4ITIVctXa+bTy6EaO4ytZkntD9BCtA2bgklGpBO+8xj77Z/LFD+HGLytU/IzkO9mGsdeCEJ65XcEhhgd6BkuMah2N92ZgMtCgArn1Fv2ll/GDHzgU0z+yZ9JmpJkXtfmbT60vGzngnOPlOafrC8/FGRt9a6CZQgnXReIKCcbKxExx1reIe7QisVKBuab6qgxjpQBrwG3ag5WUWEoxViVdquTw4XRzNVVe8qukVEGYP4KxKiYsA5o5VstIrP5MMnIfISnNbDHGylg6ITpTMPDe5kOI3JXz7ld24x93Rs4SzqwjJ6fjCWBN30BAQg6BecfG53HLO8wdxU5sjaFjaANZUBksjBsWoKsixXkcXQ0dw0W4IasgwsXoKpVkyWi/4ain6Bi6kqZefkREn0r1WRFayIuPMk5fSeTPChToDEJR04jAhFQD7vk6/vXP8eW/lmsv9kOphWINvZus+uQh+IaoQ9BgOSkRAqCg69L+/gNAVx/4oN11Kz7yj/iH/4ldd8fC86G3WiN06GClT4FS/4NAOwImTzwRP7ZD//3ZOOZwp9aJp500ShEMvhtnAEzj5b9BTqQl1hk07JpCkKSjMCvzDaRU0r4XARaZKqsqmVfJ9KCLw18peE9k8qknWn7kpK5Xdws2eawGL1VCPSS2xFnVb4iSbpUg45WJlXODce4LH1ABjbd19st3u7/a1THHhy50eU7bBLAOaXzV0xiD8v3IX7Aj/sicEzOdUSyKpls0Uh5nlRNOY17tzGwgmmipFFGl6Kp55PQ0KlMGq2ETVqKvmkL7xfSVxGF1zFZ0QWJ0S8U1HIoGxxJfpocVb3UGOBOhdgon4oA51Hbi0+/jFz9oV/0LlvcGBZiGOk7kbw7J5WaCpKQ1Tx7AcQgqzRm62JmQ0QaHyPUMYaZRZWVdLBfOyPmaJV54sj33kfiZc7lpjalCPM5QH0QYi31dgjmsZKfqol4okKUILNkXddudJsimLimW0vi0o7DyWdBktai5VimFNZYhngzHVLsPDYkDKkqOpijcOhfrsWg56kEuxkqrlk0/iHwgN7PAencwJ6LWUV58Z/d/3p3KDML6berkmADWtKEvlns9Sv8Qbv0V2fz2MDI5kzl1CU7jmoRlxt8I21QRRahYq6oHMPuVlOfK3LKhBG3pCUgCjLDQlAEHib4qCpGFtl3QhFxD5+CYTD5kco+iKx87yKB8N/quQ6cO7CRmTa4x+cxf2aXvx5X/CzvvYNJuzlCgMR6amqGex/L3VjBsOLBnaAm67O850aBxOwRlKIPtBWO3v5kBWzfy6afiJ86xF+wAIKoqHc2MErr4PX8KFcCUoFqIL0oLhZqV4fry36CayjVbPRyJlFWldq9wW9lUqFVHYVXyq0FS2dBXUl/DEJjiudx9SmINdAxjFRCqocfKAFNL0ZXOv8mnN4y7kiHWrHOUDqSpkXj5XXj73bGj2QBSzLRPnJ9KhA/oNjm5PxggbhaRRjrvt7v/i+buwcZnuo4GYOZ1qSyYoXyWWQW6KjzTm+iq0ICPoaseQqWfJSW6apNqLjnTVCQullmuu0Q0lru6s4RKI5mDqVSfrVDncXSVoSjXxxS2vLUMMoOQgYuSSKn4NbMDlgygHXeOPPl5duoTsXa97brbdt8uIQxOtKAVD537nyGC2WIantwb3sn6vnnx2S/J1HfIkVgIGaEGyulH2/Mfg9d/D37tQpy7lX7+llAMhPmcFY05CdHfLTxJlpqy909v7m2e5gNa+sz3sYDRz4Rp7GAZeDxk1zQjcbK8Pyb/YZoP2DeTMA3pIYM7XX/0BGMN6TOZi7plwYi9uTyJhEtP4+hTX/k0cGt4tfeaH+E4WP9/YR8P/5Va70Z90S684y7MA5jyf9lMoOqzCmxKLJgA1iEPsOhHwz4uzYKhpc0x/zzU2foLQXr/meTRWx266jP7UHFFtTt8HgvdLg4OAAVZ+PRIHx8qLVQ4qyKgULLTQMtTtOxVrGXs/TXJEnKSq5F+SnR477XtSALuwnCbviLMGidjqnSAaA7mE5BImFGiRxeJmZ9j/PvNhG7rKXbes/G4H8AR26zbh9tujHTLIToeZhaQvHdKNA/U4MAueJPJoWlYLT0p9MTT5GVP1Dc+iz97Hk/dHHpSKEMU1wAMhEZSY+Ty8E1wgBs5chryBAFQPPvKMrl0wC4RY0kP0ayCFCFncsBf4efwqKWAJo/cCTdASCFk0HUxBu8EtXzyial11rD2kuLWy7EPKxSb/8FhpVUncQwgr1EwiiEdecRhMxVtGNpUKIo37+Zb7sZuDeNrzO3uoo3pIblgexDyJ9P2YN4OO0qOfK0e8TIoIHSwjkR0bfAledateekXLKhRV/m2wpHBNe4SMqFYckeGMGiNCdsL+io1ZUBZthtOoyg+NiNxmhU95mmGRbIhWvVHAX11JD1UqnaX8QqjKz0mSkpMQFYyfBeLFUtqn/wLfP7DvOLjdue3hokDWd41qRwQdpTreebBbOrKfngAo7QhLRZOw0JAjYDNiDlSc7XgawWD9E2C2zbZ00+3H96Bnzk/3iwai26+EqehGh0qgLWMfYi7yYTtqcRqrKJnmpBXDbU7EqFSmZ/T/zB0GubSqKyj0IbaXyIIw3Dklsy8EJC1a3xAH8IzUtpLTrJQpiM3ZO8dLgZSLi81DgS9P72+NdIXuUPdXI1iNCh9QBDxrt14zU7ctT98lROSmhisaTtA8EsHWnePzb8AdyTWPQZG89UnZ45UjXP+zCRTFgwsEcfQVfXOAe60g5xTj4N8mVUwSTmZVKIrJNIrNMp2TNimxdIroFUcrPBT81MyIs1H7gyfWxUHA7E3or4qMGvdFOnfk5UU48zpQNJtP9ee/Dyc+zS34Sjr9sgd3+wzYCX03fvm0sCjWTApopWRlaFCgynM+CH4tFtj8UvEljb/hSp9OJQXGwYzSUeqD2x+9Kn4uSfYG3+Av3IBzj3OW4c46TSpU/kHTHphghVs0sAMkWQk08PbzJKCWiz/JeuaEAAJAxsc0PDgWl1CC5+b1gR7YmogYpIq4XCq1S7Iiu05c1Q8g/0/LS+CJr9iVRcYCpBWrWYtyW/uBzMrmNbyK45GX/2gyrDgC8s2r+tUMTGC5vCn9+C3d/LWfRLwGieiZGKwpu0Av5gk+cYOO9WO/h1seIETUDGHwdlsLnOnAoGYYtSRwWpQlY6oRbdds3SIHKAADX19szgoVVLhmLBdcmOt1JUKiTwLSP3Th4lpRH0F5MXBsdQdxoperxtzQfjaoK/8n1ATYGP0VdMIXiCeTyA582yBrxPvxqc+aJd+hFd8wu74Ogh2ibND6mgQXyp08YeqI/xDn8EauJqIUQToENN5A1M1k2D2oTHwR47boheejh89Cz9xPtY5AdRnN7MzCE1BRJV3z0h1icdBxU5FMilxCk39FDILqyhFrzXslWVD5g6a2VbVuviFJBYWRkFb/lcgcR/t7R6aZl3WILHIyhwrD41G1QBYslkpx8bkT0BtHJ+a7RCm5sAu2kwbaGLUv9jN39xpX70n9EQRfUvHtE0Aa9oO4LsR6w0YgfVnYesbdcOPDyhEgVkcZ6ThfVAs5RrcFXJHBrRKikVwzWrQlf9caeVAF2BuzHnLIQ5DPfTJgVQTXdW2oukqcUHqjiSQpTi+C7K4Nn3lIazk9FV6hGZ9UOIrYqHJfQlO/LwXUdLXL+PFH3KX/sP8K5/jnl3mgC4x5PQGWmNAiqRNDoMP0cHY8mo9yVAjGmy9/BN7+Fo87mT84A7+8A4753igo88LUoPAozIGJBbjhwbzgm6oEoasm8GYqioU9jVBrFRPBErfUWRGDLULw4CrrDBfYLPFr+na0MJYedRg7flpGbJJMqTjl9F0tLKyC7IHYfUD1zCarz1IzRpAMKy4BJ1ZqFuYKkj8zT246C5cvmcGzsN9ckh2y04Aa9ruO4UVHxuB6Brl/k3nYsubuP4HzITiV6eGjljKd13gd4WWB1UNgDASNVhJr0q79qR+ZwVdVFteAc1Gv8C9Mc+fXlF6VUGfVXlrpexUbyrRG18tVl+F7MKWPix1fmeFrrybQ5DYmq1hSOuW0NTlD+gM3WHA5/8BF3/UXf6/uqu/iLTwERJR8nGdMYdueoAeipR1bLSnBz+BrOo9LILzAs4/yT39TH322fa9O7yMyRk6GjB3QAc6U2V0nDKNvt4FfupSPgmmPcZqA6zhbSUYymFNE2MNGc8jDqUo+Z4ic7CHKYO9OzLzrUIRlVubVlHQyB1Ei/NBmQaNyoDUrCr+1f5YIwArdbzLNF6JP5YxCOBUgE6ECnN/u6/7zTvxhT39FWuw2tM2AaxpW03FAFDM4ObokvqBbHycHv17XPvMoHh0mAHzMTfRgppCadxwYDmDqHoGW0xShq5QBvZZkxVrvbOZ6Gys0FXBGJUJ04uKg5mTqrS9RgcLiRF/h9IavlDHN+mrGBRN53vlATEhDRBnofrjiQsqRCCA7sdnPuz+7e+7Kz6FG64MQXLhb8iCoqfR9qFOYEXbYfb8RNh2nIALT+ezHoUfO8dmDjR6v3ID2XUmoM5gnXUgDF2vkjKdQxI1euSuBGboLENOCcuVcU45SEpJrOBramZmtCTM2BKjV8sCDQcAZFnOYGVw1YZuGY/lreEze6ocltUYKy32VTxWjrEKYJfv3kBpbZP3IhIHdSZ0WX8kvF2JIuxIfOwe/sZd9tndAVeJOYUBKnDxW5y2CWBN2+o2B9EwnAghhnlARMJNT8SWN9vGpzq1TogOWLIwKibFwVJ6VXm1F2xWXhmsogZd6deSAZFIn3AsD6eglDDiKdpEV2OeohiRXvn3tDRe7VBnDADL2KrlSYsYa+VGZ/SVNNis/oD0Mq/O72c2ozpDdOuYCTqaeS8jwQCwBdx/t/vUh+aXfgxXfBpfu7pcwk7FwYcwgZX1jg4rrTOPxxNP12edjR85TzauM6ovxpkBULALQdcw7y8ZHg1VCqDqK/Uz2DyDUJkSi+abFBt5gg0SK4CJVuxgm8QqqSm27NrTGmWKtEYLhUjN1vMwnAGK5URUFW6YAEctcE8+iOY50wkNhjq7cDQQOnET7fsOs96GJE7HQCOlUwPN7NP75Ne+bZ/e7dGoMMrywn8Yx5JpmwDWtK2WwkpjgJNvzBk2PlW2vFEPf2owvHbmHbRSkBTmfqsATUt4Xgnbc4AVa2EZlz1SHLQRUqrRNjhCLAWAlQvbw+TTCpC2tvg98jnFp0jL0EEShiypD9a5zkOfUK++qlmxRH0FVAArMFsWrBckdDmZg/jEXUIdYAahwGzmLW1ImM04qHD23slPfdS+9I/40mdw/ZWATz+cBBkPXYAVEk5gmJHzHdvx5NPlGTv02edy83pD56vHUVnloUkXhwdN8I0ZPAWmpgaxYAcwBrDgC1W9FbsfMrSWWCX6bs1/QKXEiiRWYMt64OXPLdWGa2KX0FNZNYlVIKda8F5grJQSS2Ecc4v2xQAr1gBSYNSIF7Qcqw2S9oQSa9BpzMFcLvnyT7GJmX56H151Fz65Ez4lx+ccxLDJ6XmfANa0HcwvLBDHR3w3t/4uDn8SnNpcANBFVkZD/9qQRTVm6Y7KnrTGQK7hNWytOh2adlyF0msBfZX29KFFX7kRwqxZHJSREuSYth150nMdO1hYt4+pr4r64ABnk2N6vCihc3Cg3FKvB5YRQBkUTtsSu71y8Udxycf1sk/justlzx4CXWyV7+8ByeQeJqTFeOlgp9QUdY1ssxCwckjjISANpekvtQRRuVc+pRffd8RaXDiBMJsBcxA0MWDdOjvnBHvKmXjGI2fPevR8vetxTI8Ycm24VZU7AF2GjXzGYIarkuqeT95alRIrBVhV12EWntMXIkffiayjUBOAlSvoa7etvtxWYp3RXWoSK1b62Bcxx4uMzLBRqQBD1lRYY6wcq5XqeABUB9eZOvqrYFCBqOfTFcZP75Nfv6P7xK7stpu2CWBN2/35jZk4YOMzdOubsP67xKiz+GsNgEMEiswuebBCwALpFZEyYamqHSPFwVoIhRI2HYj0ykBalUJYUkEFpCuoMrbSflaT65w0D5YAK/dZaJxnavfQQ6IUFDq2MqQT34dCywX06YfNNsnho81MKAL94id5yT/Zlz/Fq77AO25hnODDFUvAk/iJojcpZTSutFGl/CFtABGFyRIfsvqy9OafyUSNGTk3g89djnNkTzkIqWLYtkXOO1WffCaeeRaefDbcPPLXIQEQ8Eio8uQ0G7BRLo3yXFEmY0dTeF74NfQfFxX2uRdD1UK4UqEwqxLme/Wf26gPlqHOaZdflVGIRkRgIzp6NU2FBcBCk3zKUw7TJiLL4pUzF9PkUOnBg8uZ+vzSYCUs3q3V+Jn9fNW39f/b5UuGsTI4bRPAmrb7kcHy4zsBYPMzefTv2OFP6tdajujEjwLRiLBAV7kiPkdXDVeFg0xfNV2vmuhqNBIn8Rr1JFAB5uri4GJ0BZS5zr1YqsdJyLXwkli3uyRW1gOslL7qM/G83YMkPom5F/xQbWy1IpJV4pCUQZAmRqE5UGC3XI+LP47PfxJXXYrrr8Tee3yRMehzkj6mnouS+HqBvaYtgaQRGFn82ZI1D8mh+SDi2sRnH4DGyr+sW2dnHmePO40XnGXfcx5O2gqq89Qj5v45UDMGC6ueW6pqbakhe1SvJ7psTS2pkAnPe/YowrhAaPZ9ghosMDNCqOgHREMU3yOhMRILgClZ1f6scIsYDptAnBzuDMYTaDcG9oxafD1HZj0U0oKCamEstQLAZSRWxWONOmPltc5sEdzFm830M/vk1Xfox3fRD3IWaeZpmwDWtN3v9QmBU3QEjny6HP1GXf8UMagChxmWB0ZH4pRQ+l3VOYNIZExj3BXa0quG7got+qrZxNdEVwnOCGvDmm3CiGk7cvqqtgdr0VcH5n3VKA4aQCsqer2cq6Kvhvpg84B17yHz1MUE7QXVlwEODugEzqwTONKcqcB9/tPdFz/lvviv3TVfxA3XeAsAJZ1Z1yOABHL1FUPJqRpE0mXCXr0XOKhmecG0v5J9NZ82JPGeuZ3nn4onnMGn7NCnnOmonQnYwQiqmKmpUKOOSRN3gLRKaIE7iWAoJ5asn6cBE1Nl8oZMFNVKzmlI3ZFovAaKayVRfNOyIS/k1W6i1nLkajNSBZ90gBgrNZ0qMFYap4NWAk98G6nVg5CVQTFo25mWLFOMJWYKCjv1Sxv//s/udb9+R/fPO515zJ360E7bBLCm7f5jsILJpFc70mCy+elyzBvmGy4UmJKiUOoSZbnXP6Y2B02AJfndIGXmqJXlNlukakeFeFByaSWr1KSvmp2DPQqsOwdRKplqLm01xcGQypxUHgdF1+rVV1hYHyzoqzRXR/KTrLsRwaR0aCShpjMOB/RJGzA69pp6gMZO/u2f7Qufscs+h2u+jBu/im5esCwKnz8bgAGtXSjEoVYr9OuZ8CUp6oa/6hWZweYwOpx+gjzqZH3CGXzKmbjgUZz5op0By4BASekMmKGbRxrJq7iIzvoOwUTGXlTlIjNUAJ26SoiqktgzYX0tEiPphClg0vFD1RhrBDYVmYCJtiyvoHUtFi0vlZa9gRWGw2hMYcrL10XG2q2qUNmnv0pGykzslcEs5PJ5M2NHOOvUjxUzQwfgU3vt1bfhE7vj9xJK9pjE7BPAmrbvBH2VccUEDFu+2219Q7fxu+O62eAgSk2NG5ptg2ylRBdmWshV1cmsD4n8B1qmDMi7+bA61yu0fKoKwgwjxlcywmnV1gxtk9Kovho/TvhtE9g1s3HShkSJGjjX4sMKb60UYM1gyUUYkJzBBHAQGhy1J1AEgFIkrTwKoMn156Wftss+M7v80vk1X+L112HnXSlgCOIhEhNdlaHP9KkYXhdf/vNqns2b5NTj9bxTcf5pfNJZ9uRHClQBRtMEmME6iHfqUF+x8phDjEolFKomnt6IXX5DlRApm2VJ+nLkohCk7mY+6TkDQAOJ1c/XXRn/XFb3espHLdNIDdTUqLfCmO9oRmKhQmNWUEHI2g+rml3uv1B3IyYjWeJrBVTFvlKuXjBPGSvWwFirCNIpjgb4dksaVAyf3MPX3GH/fLdvKZX4Z0ytghPAmrbvFIkVk8gypnvjk3ncG7jhmerlTR3gVuKuakPRIh8aTfV6K20Q46YMWIWwPY3ESVXzTTxUOE0ws6pqVgwb9JW03LBqgFW4zEvD+2rAQ4vNRUNodCtahw2frWF3wGY5fYVoAyHBjJICgpo4nTpD50K5MJrj0/r7IZHYi0FvvgFf/Fdcealc+SV87St6042yZ8/Q+AZyaIg7NAf6xGk98KjBmEwJrF+DE4/FI7bLeY+YPe6M+WPP0lO3gQozByi70N0bMFAIB/QR3jOzuShg6EhRZzoXCFR72DRM4QMd1bIzyIqGCSeU9hLWbFPfeBgVUYUSKyW6GkostNoYF9tilar21MuAsVNveEMvk19Jut4QvJca+YZh6ajtQmPfCKHMBguGxhEKoBapuGLUHd4wF2+0A/Ifd+vrbrNP7RLEEZ59KJYAOsGsCWBN23douE8lMqHZ+8jH4ujf5oYfsiW4DiamlV9o+ZxL6dHAvFxoxY6Ff3rh8iAJusJC1yuX28BIHMCk2TQ3njlYcEi112jCxpWhyy1OawXvq6o4mBiiJhxVGrbjBey9vN0l6DON6ykAnOT84hjAQuoZBufHYP8pKXkp4fQCIPC4SgCG+UEkTsViztBddxUu/zde9SW7+mq5/qt68/XYeffg4HAoam17oyo4QDdv1JOPw6nHy46T9ZxHuMee2T3yRIrC2IcNB3YqwAaFmyfks8cofbNKBygUEAVMlCZdEz8JzAKH1BetvDa8yArE4OwQmheTYp/vUWMBnqwksUoQFvm7YHZluQX8gQCsYcUXcVvCjbVCoHtTCZRmEMPBFxYKS4yVQLESmaG01KoB1jD2aF4/RWXKhYwPy9woeqJLwDlA/N1uvP52+bdd4baICpC0biE2AawJYE3bA1XCILDpPDn2dbrxx2mwWSgOBGDR+UndJGndGYp3rroZCjfOnLsCWr4MMUnQ6jjnIpyn/zhpvVgbXzXV9M28GpQVxhVCDHsw1CoOFvRVW32FvHTYn6H07YHWe6UGgLXAnSGFa2mUIUr/iKTzMT+ftFuzMNCSEW1+4RbbY1ZPlt5+C6/6Eq/8kl13rV1/Lb5+Pb75Tdx1J4omxNUX2ooByFZ4acHLq3gogsjsAGwmkplsBsw3b8LxR2H7sTjtJDzyRJxzKs4+DcdtAY2BGUqER2Ga7mL9qy/b9qySJR18RQBL6hfahwl6gNVjlN5LNhxtqBJmTJJFbJQlACYAC1kLnkVA1sBqlrpxrpbEymp/Pc/UY6xc7ZTYYjF39azl9gW4YW6P3uax+p8bp1S7NqBhoDqs9PofU61Y2ldoyf06/JkMpFdnEH8CDBAbBPA3d9trb8eXdk8T2QSwpu3B/X1uPIvbftO2/NRgxdQBXvJsiXEAOGooiqpCh8w8HSjwSkaADYCJLTvTMTSArHOwFksBVSpOkVG4INS5DsZJ/8AiGGeBD6qU8UFFfRC5F3xJX60SYKX0FSuxfEJf1QBr1E8L9wpg9aRmMID1JJ/hjjtw7ZW49krccD1uuB433YRvfAO33+JRF2rFUsQYEWBlGr8S+rB3jSrRUsUJtEHVMA9b67dCVcuPFuP/jtyEo7bimKNw4nE49XicdjzOOoWnn8KtG00I6xJsAUZRVNCSo9DlaD7vDqU9xuy4gX9Kp/MAm1KA1f+5Qy9hjz8S182ilzBW3DzrYxZq602CqjYdXT3AWonEQikaw3iXYt1RWBqfIoudKcVYi5yxcueFVpZzzWNFCUaV4pxbLfQfnRo3MK8VGrwzO4ToHNyc+wlnMPz3u/HGW3H5Xj98OSNsir6ZANa0PfiKGCBmxvm6+mS+BwAAIABJREFUU3DCRdjy7wHDDDPlXABVrBFZNo3iGzRTopsEVTEBH5Apwxi6GqevmpmDjc8dMb6q6KvEQwEtnyrpmbDKur22Eh0jxmpz0RbAGpIKJQEXbHp0xR/q/sEiO6hI78Eix4dRgFW7jjGRu0kGuTw5J4IO/R9u2HMPrruON33NbroeN9/sbr65+9a3eOst/PYdese33e5d3Xx5DDzdC75K8nem+Czzoy8+cbaEww/nlk125GZs24pjt2L70e6Eo7uTT8D27e7U47u1a+EybolQLUP0+sm4K7ioDGBlJTaEsmABsDzdhdK5IGIj5mYNidR96INLUVpBKXUDWsraCbUybkD0xEp8s1abTpj/Nv056voTk4EB/yG1UG8yXonabGHJDyMROtb2pqqRGZp6rGyXJpU1jtKsJLEwmEpYhGECtffeZW+8g9fuZrg5qpDvaZsA1rQ98N+k2JJi2bcyOcPaE3jMr9u2/xhaUCwpYwG14+gidIURe0+MVwAlO3j9TqCV6IyWWfxC+soWhDqPybnGdOgYbQ9sFweb9BXG5e0r0lc94pRWwbHuRkzhZrM+2CNpyY1ka9V/epIsXU+tVw95aGVGoXK4r8RMhR7+dUiMLeLJiADfukVvuRm33ia33qa33YFv38677rQ77+TunbbzbuzajXvuwd592LMX+/dh31zmyzqfo1Nox65DNzfVcKyZozgTgZthNsOS49IaO+wwrF2DtWuxYS3Wr8cRG7l+PbZssk2buHWzbd2MbUfKti085uju6G1gl1THgiEn40SuUMC8zUI/a/rf6iB4IoNngRV1q/+fvTeNuSy7rsPW2verJnvk0OyBk8yZTbdEii0OGqxIdiIlkqMgCiAbSJDfQZAfmRAZFmlBjmTLtuQklichMAzDiBzESizFlmhTEkOKlDiTTfY8V89j9Vw9VX3v7Pw40977nPdVNTWQ3TxbULPq1Xv33Xfvffest/baa7GrxXVYdBviSV7rbSiiIp9P6sFHBFhuIG6fz0IKzUc/FZgG76idsSO3yTnYk9YcGKYjlVhuMtHzUpFhColAs67i/onCIcpmn2h9wFh9ZGggwALGOiJhMIZJa7edxQ66KQ5BSO7U/qPH+Tcf433PAkxEyVzCspp7udTBOgQvk1Ik7ABI2hJ3gDxzX3rg53n4nL7ppwVIVDlkOqZ55G+Oro6A3tYT64yA3b5W1P3du169CHQVJgftZqfoagPByWdpYUF2C823ffqLQ478sAK3Y86yywliVWpbNlB3oS1rJivLlrH3XHQIGE6NjUXau89afNWOPL+qmv+5eb2qUKP7RsGsOyiEIkAOSanILCXgsst42WWaNFEhUpiRYk+QQLaM4WzznczipH2JKniiEQmC3P1KZmaiuJzXIS9Cs/Q/A7TEMtan9i0IUlMqEhki4ZBlUax2YOqdUSo4obdN0a5kdseywIJqPGufMLnuNNnH7JmsXwg3/V+eEEw1Mjzf1TOUypFsOgGTYtNfWHYmC4bIcPCz/3r2ByHcJVNuLXVryu4kQihyjrkqjFChPTlvth9PdGdN80gBZPlSqemO1XJCGj4r+1eORj107H/wgZsvwolkeoMoVmUtyyGeCNb9VD0UHCQBRXmoil96FL90go++kMhNkWdOodshV3vw5VLbOgQvK5CVb4csN8XDZ3H6C7J7Jl30F8qar8xto74My5FhOCPrE8YGeWRzEOgi6wpWemdq6ssQQmza7jHE+HSd+1x6JXukV33pYWTmiLl1O2vrrWmSOPQxu/3psClpzJ9nm6bQsD9SEWcYJzS71N/FPiL95Ro/S9uTpnXrvT9imMosFCDdZGhZfgvWohBazDHL/qiqMqdJF7dzUdbjIwIVyXBGCMlsg6Jgtnz1sh4IbbvFiTFSM4okSCU7/7GBwjz1V1pgRVTM8nJVAYTokJdImv0wVIkkAlKz36xUxSL7al6vrX51K+0KTAtWSVZYUEO4vUwnJNZ1JtE8U9uVyN4+ayfDtKQK50i676h5jkFB5qx3PEC7EwYMsV+CWs1r7WdWuwOl1ZWvunagtF8k5udPxWUlbrye2f5k99Ec2GH/5+KCUl/i72vuaNPukjkE5lWsP27a7zr/a2X8/WkOQz0rdhdIJBHqTlSQSPzMw/hfHtmeOtW5LyCBaY0JLoC16luxRdjicQSi1AMwEadf4LNf0nSCF/4ojmE7gCQkoZQ+UeRCnEdAhQ60hpwjsjlCeiXe9aosncGQfYauDCCYOMVvjrwBByQ3Fb/Ty7PENN1wZK5zQJybWfTpvcRcmo0f97MfQfwWLAgLHw2+uxestoQO89kWpwx8lbjPztrMmAAsZyrGJpwXQjft2FTq5ybNIskCl5NiIwVSsUfl4xonkjGRQhVSF2eWk1BIjpGDy8CpI63GhGhZ0oqLPayp5tavr34ZsXncN7iiCbLLdJrWgJseNZgxQVmcQejG5ghQ/9u+KNSAlWi+pkSwmlRDt1r6kvWnUgGC9ESKYTg7h8TZzyWYPbSvpyeC4bvu/jmEVxDUv6tjgswPooaNTOZVv5FYUYLVJ/QxZYtabGxWAz0aPwLM7Io7YnMOqrOJDUgVwVg/Uhbc92+E+VTs++0RIUeeK39rTgM//SB+5SE8e9giFLZC8alUAL9ahAtgrfrWglgHYAKzwEFSvdOnQ3n6C3p4Ly78PuX5bQJ7NBQFBnTVWhJTlsiiq5FPAuI0n7lXThOdHRiy99qBBsMEzzlIwbBLcYNtQK9iBQvgxNNXRICJsJZanhlCZMjaL2scCbDA0LzrBFjdiPi3s+fCcVoej24DAeYYrAiwnBCNrkGpbNPloEC0sjKpLrRCbkpChQW7J1UUIRpJYXONtMfTMaIFCVHDBBbt6p6hlLKSPGUpFDvGVfYutxChrbHGsnqxeT6Vkc92YWZYo8ytzjz8Vb5PjDxNF1q10TYLCNSZvaEM3NHRKraBRY9dCt1mTkSBsJIRZWlJ+ithIFlYPi6VY1MajsQqYi/W9lxgeDzAokczMGzghBJjZRw7v+SeTHsjGTAdJ/eqgYAytF9HQQZS1x8EnaryXdhMzXlw1n84OOwYd4nugLBiZraRaCGAtOHh0/jvHsQ/fkiq8X82q81dwlTR1WKxFsBa9S1XY2QDqvABz12N5+/CBe/FKy5m/XWphT0qmgUBhlG7Zj6uEPoenNoWlQvsk0o/WPuGpj1qpJEYc/OxOWgX3mlwTXB74tiIDCbvhiWij17GLNcZbtpx/ghjX9LRVwJyT39waqZg2TiXzMOeF1wJPyqj3bz4ub+hYzg5PnYmsa04HmARvWVnsbJaDEfb12IHuNKmB9h7SIUTpVVNhZVV1RIMphNocACsWxKlRI70pTWrf6zeqBhEqMmh06CCollNtUjZiyi777Rv4bF/yTgwP47QKmgTIcWwgiud4J6uTlPzHPWSonpYSN98hGGbi1Sc9qUOASgN/VWwWMkBlPqGDFFdo2oxUmL0bdhOU2oQNDnQ2c5+u/wwaxS2G4XHYwzH1swwO/tky5ChCQbr+9kX0hBTlqbqAaLutiu5s6y5lapNQMBbTvGn75dfO6GlG+rmBaOT6aqXQck6BC/zUs0LbyJP/DqO/7d86g815WYTmTITwwRwQ9qShB/ctTsylVpPxFjzn5l7rjO6BcGhK4zJ02d5QasxIJi87zdy8+JZPAIvbw/HcPwVzhe/D1JcAJRHHpZ0dpvmizgSGo7wvjsIz/qS7OiftOg4a7CdCptVga3anbCDugXQZITLZl1UG9nLWQeNAyXCYbIgDosZwTmh/azo9NA1ApM0v4LIszrgBhzQ0MtOpm1sAtTyxv0cF4ZUOZERabggpB7VPTMWdVMo+MFyvFR7tDxKiRdAPBWTW0z7AUeE2Y7elTW7UX4X2niKPbIp+lvQcO4iq+o3pRwvJ4tmU85Lp1I0tTmJTz+v/839+uuPpvwDhJp0DfEvBmvVS73K7zyIgs/fzlPX6ysuwSuuoAC7akNaOj5iE4WdNGqMcw6gwcc5W8U0m7Cd2Dc5GNFJ83kaXEP7mxoazDzYF1edxft0F1AAG5u71dCjdN5Xo3dU55YcaVeJARk4M8zac64JGPRn3rB00EXZLiTHnQ+Sea9hp31OozEmPhdt4tKd+jlpNzKIGMIoMbCJZa1FaI2ZNcsDbtOS6+iiX59BKt4uOTbM3WmnOEsfOlkGHnQOxup71NJV/UhOde6WxTFRLX3/+87Tshulx5mhdadt6LcYSDg30hhlUuw6t9o5nHQV6/u4UVb3NHKPEmtoKeb+qZEuyZ7xSZqPb/kdunYq7VFqlBIjHVVJ9Kh2HwD1HgmXqtVwGaEb3b0vtgtVJCd2Q8Gt+l5t+DdP8K89IH/whBnIROtWr1oAa9VLF2DhgJJqX0Sfe4DPXY3tXJz/fmwUpnwn0Q3S0ttt5260CQ1xzoHGsGLqMMtEk0khs0BDGl4Ag5BLvDKpbgfALN/QqdeNvrt2qYwIHWNWY1NKCbtzFeuyw94fnKjENjrshWgrNQNtM7P1qOKqXSr4PuBo4N5afhz6mKJNpj2RcAEqg1QLHjz90QFW271sBGCkPG4QjG5+DOxRbmZ1I9oYZRlu83sK42bQkJkzKulwzeql6OkaB7AamNPx0+nQJewSKyJPVoLjUCatH4HtvhlPUdKxQn07DUe02T0/I1emMes0YqPbgqjI4Y/sqkD4zlwHNx0stgsoaOeNuEqN2M7uoBsndO+O2G20gGxQuztNGANUapiS8V1o5hYHjEX74yN8cPiWpT3jYJJUNFcKVQrknzyqf/1BueaZhGxyBXLTZSe6ANaql0claCcjqPrCo/Ls55UJF34/Nm5lHL7hBu+zJHNPUbesTgEKgqfogIR6lB4Dt9EnFl3s4GSAcfJeGFwSIj2jFrIAs+HE0bdzs8qYLofSqIuPqEU9LiwDbOKbN/Qy/yBUp3vHfjzpiUAOJNk2HoH2Z9fbcXK3qXCeM0T7jTJYfqwsmVWrZaTQsS9e+hTILctFjW0kx8n0i0u9HCcALMURACtsrV0FTereWUZ1LE41fKLGFq2fPXQklpeEzyVr9L93zLyhOoxi99Chl8GLQYmcWx35LQ+2nAre24RxRndVDCS0uHAO3QaNlLVs8Bgr2ECYD8IwbRBw0mRK0Uy2kGWu0DiThN+unnMmCGUCtuyMKiL6dx7Sv/kQ7n5OiTp2ihwatRqEC2CtehmUgOrXIurpZ/D0F3D4OF71Q4oDgCLQY7qZiXoV3YeuJmmDI7qyvMhoWT6+3CdJn9maYWp8hZaaZ1iohjZYuSUxrU8Ob8cw0+exkdnz6K3QqaMZubXHbJ1H0Vfq8BwNUSIG5A2HxXtouZnB3h+En68M3cARRo8n5RsDWGgAlz4ezoyAqc6Xs95oGl6FzlpG4EWr4jc4ox0diySsOB29w97X7+4h3rVjDUa42TrPcnlrJbfAm8Zl70Z5H6xxeI2DMJqRl+rWCdrQRgc3BjVFEquhRppmYvfNUtM6a55YlsSyoM17UBkTTlr91yh7YzCsMhgLju1zin9HuXFo0QZ239B+k2uinwmjR6NtIA8bJSCQBAKnN/zVe/FLj+CJ5+FBvbknrloAa9VLG14VSelWv+HZERm703jys6LHce5VeOWrSGoq6wOCsH0aCB1bdcPSG9phAy/iw2owicTBkYu99wWt2KWuCuJ/ym8+cGakr8RSZXtBYeCrHDayYikpPVbmWKEIbhyfNEl3dtiOPY46xGDbyOoOsAzkCgGCAWC5f6Vh+PozdXrMpwBLPKTfB7DGEVHLOqjfnxFgjRqshpniemeyaNT2AZv5Z+scKRhNJ12DyfQLzZPryptN12mJMbWXhBnZU8+Z0YjPwv5b1y5xjhVWN26whCOxHEOmodGmvUtId1qioag0EmtQd2n49eQ5Res7ythSLLa0prmpc/zorByc4m1ybbjGooNKexuF7vJz7lb1IEx8rRhlqOoJS+bMH8rdp/R/uAv/+EGc3tVTyPZjSfyI7KqX8eq76mVdROryYIgKgCTYFAdQEOmuX9Pb/2s89YfJjM7obDuc/lrDrDfj71QmoFAnSz7iD9c+p+Zvgx4o6LhDna4Qzz1w78E54rgNH9BPOwaG3wYa+nmsM56gvb979t2BTXtk+gwdAc3ww/1sbu56NgfqG7kmg8KdcarLjv6Z3Rny2TgMoPkIFAr373qdxtRidqqTz91+DQwZAvWZ2QysDx6KRqYibCf4IYS3dOfFbapNrungQtCU/O4ItOW/sbWi1RK1Tf+dWQdEOmChw35mBrJuME53DltXDhCnj+OZbx3DoN9wFfYfQLQpCuqPbfOrNROU9qDNxySnp8+q+ZwDmf2e7wSAfvZk+q/uxj9/uG0sAUIVlNigRCRqG5VZtRisVS/5UuP6U+Jh8xjLs7fh5Nfk2GtxwZXcahBIb2ypODPnFg9ssQ4iTRUomUDYjDAiKtk5MGEDIzK12dxq98hq20WLzFfAbaa+srDJBi3vS5UuBvfWNozsYSx0pl95sxMperXvGo9DP26c6KjGUUovwHL/OmjSyzncjJN+1TPT9UyRfTodTFTPe8XpBP8udpReo7eW+UOCc7211KlPGq7e7k70o44WCp6lvkWoZXaLAWSGsJoq0s4j9tqXV9fQZLOt176HNbDRi6F8W5BBhW2iCRVuyq+LlhRBwF19pNjjGr2VFI0QqgnIqK79B/dF8CRWI6q0c0Jqn1ZJrBZRiEGJpeq9r7DXEwv+FgMbBaQWYxliSePT2senTTEq4YnW/DNSpxmGWtK1e8pbl6rGelZrqwJeE6nFhlfI/+txfuR+/sHj+fWinbfUI37DrFoAa9XLibrcFARTvi+88ACf+rxuiRd9f7mDCjTfRqR4OELMejk6tmNwBGhaKNhgu+HKo+cFjmhC7VMCWQA0JO3Udx8dDWbuD21UcBZr6J9J+rcjnRFoX2z3iroY5e1NQySMj0yjbMaDY63nt9knlXpOxijGptPhQNGNlmaTszYHWOPUp1lGVRlih5sTeqA5nO+A9wwwpqVey8Og/mZr/JQLvBmaVsiiTpxu7Sm714AGfVgQmLMbTxg6JnYJ3QsdqrBdQrVSd3M21WnRzW7qQIfRIQkfRcgAKzHkA6JmLjRSSZP/7OV/BM2304YeMtqfli+NO0Rs6YmeQ3JTCBV+idnZaLEQQYvDdiwnPThwmJ6v0+G1/2jk5WsIV5m/bhd3fpO/+yB+/gHc+hSEUMVWLpZlx7AA1qpvLzqLkMQ8zgKSevpJPPl5vPAoX/NDcuwgqWIjEySnj8rA4uzDQ5u3MwAm03zYY9o+E3JF4gp+kC3gtiOsGajZ+EpDGKIflyNjUnKUIk3iBav6ijo3oDf5xR7rHG1e1WJ8dG6pZaVpMrB9sg9g+WPopG+cJALBm77anbSXwajBikN/1oW1WkeODp20ba7ga0XSUz4BrHihDFvnr1qUdPGWahRFqYcmdv6ObfpPVduVU1wYqJYA08p0mCt5j1MXQ97zaNBFOOmYjXkxKquejjy40jvDVc7E6WF8zoKtCLA6w2rgh6I66MOGKDOAHHdNFW8OTzLBq90AjDqzGvoT7a36PjfOLNh/dHF+mAq0MNGyZZOYQu/wTmXCDiKqFBLbM0n/6r34e/fxxHPFGN/gqoWuFsBa9e0GsWA6ByoCHL6AJz8np29N534nzn8ddspjPEbdMQzQDEzSZMmnDpxT5IHgG0YYLACOGGQb/LScldTE5NNkJPv2ooNBUpTsk6hE+8xqpmUxIjh7X+yJx+lhiOaDzF4YYdDIik37g6NTF4rovhyOMUEIcYTQyecLdB6OBsyo4z6AFWnF0WW0XzYmdk8rwvCRzyWeb2Ys6QRKOoTFIbIv7C0qt3twg4otzFojeaZd5KMETfycejsJBzgqdo1mDZyZHVRfA0PgBfTZiSjLG2HIdXZEXr1YNCjCR3uF4ujkoFLAcK4lh/j5QwL0kLeI7qM1s8Wqll+0h8KYrVpAZ3Kd1OwFLYwbBhYniiubg9ibkjAhgynLCcgkSsp1z6Sfvvvgn9yXnt8JBNQDIiHnj4lCF4O1ANaqb7MSUOtEYf4dTogyPXm9PHeNnnMZzn83dtiB2Mz92VsicURX1EhrjV5NdKDHTRRiWOOPaA6KYarCZjEZf5uZgg77H2T4e4AawjMHc9GRvnK+UzLJh6ZNEvTTjhOGb+ZGdob+4DaItiV4nmHeH9wXoe2hyl6AxaHHMjAVw/yEB/SdiIpBwn4f1Qq9TXBhnwRjNNYy+nYOlqR9X1oMDekcuTQCpOpu0HVXzs1LPV81I7E6BmimWZUkIocOoNpD3aVOgyitoQTx9qSmpTfNik4NB9OcrYknluuvaWDLHWZqR1LHZtzgvN42ol101oG4HuVbMc+B7tch5/5b7kNWag4xIUqgmgRUfOwx/ejd+O1HUtGSKYqqnQJmMftCVwtgrfo2w1dqU1uNOzUpz9ytT39BNuirP0w4C4MjDLHIvmDoHu5KtwkMmqArwmlPPAUyp69gOBsMCTw9YRqYE2mmKbYPF04PBdvgtR7BBmHqBBYoQLvN9kmdIzyPoq+O7g8ao1QdwSL2GOXvQ66bR0gcABb33WJsEJMbxM8wRgu4mWmwSqtr0kAsbTXdbzTKgQEKo3F2Gr/FDIfQvInO3VuPFmoqa8ucr6n5IK3b1GKPDZizHl1W+GU7gB2cqddO0TbyjANqEXF33rBiz6oY88r0plmqv8NYkrNt2g9jcs7EdNTNh8JSShpxWVehqxkstYRWt8Wyqvts7GWvBDJipubyQBuhEzAWfVZPxFje4R1MVIEk/IMH8HP34uon8ySQ2RUBNBEgNl0AawGsVd9elelr5FuAaB5v1uzBuQPwwuM8+Vk9dQKXfL/gFWdAV2H97r0hj64G8daE74EZQsSRzUGbDNj6UDJ7O1aGiY4BmhBmMtlDxdRpPdICzmndbkoqMTYGGooZlzNNRocIrfWk7IGDR/QHEfgnquuKegEWZpvq6IRGwrUfYE1VcbCQbmgR1m6gYszDg6esqngoci2KDoaa5N1kESqMDxGpxtjT2YeChsHKPIlMkmGAzlbQeQU4X6gqkOpvwbkHvZPMM7TevFSfYz4PpqZi/V8jMdOwmtOutXAeBsd08R03S4l5U9NoOlpJR5/pFwFZTZGI45azjxYbi+6oYnRpp9dU0eIk3efTMGIsOIwFgNSndvLRO/HL9+HBZ/M1rhs0VcuremcQze3EhbEWwFr17Qay2m/hLDfuk9wAoKdP4bHPyamb0iveLue9wdlZ7c1+pnMDt3AHs/ZiMJea0ldntGZgjB1EsFTI6CE6i3qAJUHOsUdQJYH6quZHgsmM5LQ/2BzhR2ZOnPY82KxjAFiKPROF++YH23uJGy+YnIKpP/sIZ6cAawqd3aGmVVyha63psghHBqthoOj/XqFJS6TpJ8fOBlZQ1K4E1EHOOOyWH3GJNAXQOvU0nelAIVeU1HjQ6JVe9U/SUaE6Us1FH9pIHIOEtJ8OKxKKUnc7gFkxa7PQFDt80NROQQlVDw8jaziCuRGDBvNS82QrKjdnWyeSNXpz+X0MJQ1HiJiBWF1pEDy4eNTtER5jdXD2hZP86eP6T+/HC4ft+mDKpKLY/CUdOqarFsBa9W1R06+91BXxAEwb9PGb8cyXdTtfXv1eRRcqlR7IphBilw2TNN+ydZ+wvXeasLc5tXl4JJUJE7puV+dvOLVm0G3QUQVte4M7BDeQGnOjg7pLupmjw3PNTMGgsZ4PDfMpMCC5vg+DgftmpvxmECriyA29r6PdAYuDPI4lLYVt4/kIBIDrWLfxNAWF++CVxZGzNASRZ7BSwUAeNuWDYhNdaBXcQ0qJM8QsbuFqfkQktwsVq0lfOo0BQWsRdhZFjcYraeDYaO3CVbX2rZyhQ/hZUkIXo87dqfWFTIYnrVwag3M9PQjz/6Rsll02DqjqykNacz96IxhqSIV28mBwRne+GfaqsgyYUakHr/aqytSZBWiZfKTa54eMQk5yoNFNVfuhcvxVBrs0fl3FCFQzaM5fKgXI7f94VD96Bz/1KKDHiFRRXzrqtrpqAaxVq1psIdl/Wz/7IJ78Q+hJXvzDbMHRQhBCMimOkUhKEWgiD4gkzn9BR8ao8QBDpOCEOxkChmfWo17UZTHQTJDkV/zBb0KML2hwomqJ0bBOCpiI323qDvZrp8KDcMZdc4X7eARklsBtdsnFBctokBEBFjDSVwNThZnCHUaDNdroB2xNH+nW4VE7k8ZnIUwUcvJjwVqmE+obama+rJsmqLWSNHYSZibR6KL6CFtPR6HPY0FMho6BB27ibxBAh66otxste6sG7Dfhl8/sszonBuSUP5pGD4PwKSbhNcjeT167Fgiq5sg6QI0gqAq4zVs2eGHZLFIw2Cj4TxLV7pPcaIvLSSUSIZSy51Lcz/KtDpsWeAf8/D36C8dxxzNQiBR0tWTsqxbAWnVWJWbAXRQUqihOPYOHPoPnb8OF75bzLtFD8qC4mSdQBEkyB1FHmIV7fBmGKLrB1OoMKYdbVwTNLAAsaPPxNZxrjJqruHqTqthn7Bu3P8s50TONjbxppuGYP2gt3WtPc+5hYVXkU/H+gJ8GhXvZZjmYI8CKGNe7UeCIdEJ1Fq+TZpntZ8Fp+ULHhhpAOfYBF01uvexgptKlau3J7YrsJ2E5LN5WrZUpJ7s1GyBtkKKMEMr5hjcx0piqE1qfFq/Ajd11iyyYkGnTSXStTB0g0aTZN/g1WD27bf+psVbgsI1BiXUUbtNhB4w8Ch4wtZHjrNpy6vi47TAhGM1pTc5g2apkTaoe5F+XrdOokjlJ3HRS/vs78Q/vwMlDAMcEKfl25qpVC2CtOht4JcxaElBzDAQB8omvy3NXJ1zEi/+sJkKhUkImil6WSlIEyfyY1D3oyoQ9D2CL+/VeZ6KvHNc4BNuxAAAgAElEQVSydZoBmFkz0Gfj1G9FVFNJXax8rA1jr9Crr2Y5NhynJvOnHq3tQ8fTAyBn+mqP0jiNCGPgDu5RuCNC2IkXw36AxT0cFSeODC6LsFFTjoPo9FJdXHUCQSbsmvFnb6DHeUOolyobmXl3VahtNaORt5SPNVVniZQ2GMLNORpURLNL3U8hGU7GoLqkHvx1QCDN3NPbjfq+Xs9ktB73wSaUdpIveJkRnOjlIdEwdlRfWXW5+s5gEHeZ0YFg2aB7zDswsqewictRhm8xliexPHdIUplyBzCfUaCFSVFJxa8/jI/cqR97ACqECrHT8vtsK7OmS8m+agGsVUeXmjtoUWeT0IM8l0ykp+/FY3/A9Cwu+UEc5DuTQoljKvkZkpRTAfuoqp5NBWIWxrLPenQajGPpH8s5YRY108VJnJgdmCfPWmmcmWw5+iqMH3KfE5g1hYcRYAknx2S0Y7AfcJsekMEB64gpTtkDsCyGE0+24awBVmCwWPzZrXCnsU/e2ehIgDUk2NRFvllDWgplb7ayUUrV3p+jptT3p4wbZrNFCH2oUYblKaj+6r7f7h0Hkkl1PAjFo2IfDTaI08tn6Qe5m2ypkfLFsANGOy4N1FBXPrmpwCmJ1V31YS0YPAkVrPxHgMWBnRqJuTnGctRXtngv2dhJKArdkDRb8/78cfzCXbjpSdScHy2GDKzRSEKqLoi1agGsVWfgsNraRm51CDrVe6+QODypD30Kz90i575dz309yC0nR1C5EVvRvKu9wtr9XWz3kZGVwWgWPwALeF8rDH5O4wwd96ATO/wo+xkyDo1Ll1isE3k79tpf6dRxPkJAZ1vqGKa5u70/YiE/2xqQDgArjhBO3b/gTWW3gdbqV4/GJw/O4/1fjQlTNcFqyKOqjnTkMaYAK1gqhDXWAKO2rlf1l8+wI8JPDB87IzCBiSZvx8wwBgrHwZymKvP5M+otELRxLO2vbWsanNLRPGk5wVXwBz+aP8GRedq4XiPuH8cJLf7rYT7e7ksH6weL9GZdNaeKN4jTd/5MBzjMTloSy2v6ppE77vIsR28Hypaylp3cMQnxtSf1f7xV/v5devJUhaUlr8laXlXUy6XFWrUA1qr9VaWdTu0ilarIvlmZG3/8Oj7xFT3nHLzqu0u2jHADoCpCnUbivNjMwaEv1uCU9SBlMDKNPNBeET0NqJqk+9kdEMbdbv1BOTseaNtPC3GWzLjPtlTq73oPiRzAkj6Z7jFlyy8alXB+6BJH5jfvIw4RxFXzJuOocJ8QLa0/yHJ0eEaAVePI3RNpcA+9Y7tt/PluYOeu6DJnJhosmBxAF/bX/sWooGpzz2QmsiMbTNBGyIzuWTEuc9HBmo42DXPjNExd+OWFU4HS6wjGKLFYPauco1UIw8F4KCxYcjYNhtoKSqzyeXSO8xClX8EIFH56cYwatJAX5d7BnbbhAAH/+b3ykTv0dx5WcmP8PA1mCnsAJReFtWoBrFVnYLBUCaooFYLGfZe770HLf3jufpz4/YNTj+rFH8R2LkSVhBQ7eAM4huV8lqWDge+xPzMpe14bBtCkoytioLL8G1nENkmDaU8WxhCbI2BQ2HiHQVOAVX+qywBBxkbb1K+LQ/OOk2PSnbdmCnfXq50CLDqCwZgyDNE3I8BSA+kwADLLYDlKTIcsniOnCIvJAmmz5sqaaruH9LagGrtOhZTKUqeZy6VFJ03DrmoWXFp7WjdvCC+srkopQfOvb4dEK5YzBmCd9FLvXeHRQj+etH06LyqnaRSaP+wDQ/0htVCsWrl2XCT9fe2BGmhFS0rRhx5239F6xnwjsvqLRucHxuMQYT90+Gp0dRzBHSsdyMdP42dv4y/eqXecrJ+gGNdRCXHm7Fp+galxali1agGsVbPqXL8CszR4dxM5fD49/Fl56lo992K85h1ZglJuYgdF6Z718lSYNtzgOBW6XeKbXIZYKu40U2sGGteAMYkZTTlutinQ4V3qfGL1c+qvZefGhiw/DfJ2ms9L4/vQY2ow6TxOfeGbEcM2FfjXJVwa9NGJxeieFmePQirP9Ob1042oH4G0QBlBO69NF52d62ej/369M2fauDa0Vhp9il9hj+gnA+t1qkGB7rZWTrXFgoruEVo9w+qaLWZrzUrervm9te6CCw28a/aXNEnrLkO6A1JnGDHx6FRvXkC33INtmqIzQGqaamoJLdPWjPakLpvIDQnmXmd2KtUEihGCmVy/TlL6CQGdqKw0kpr08dXe/p5G6R/yduKVANNA1C27aKVs15bHebuvFkF8/DH8zE34Z3fL87uGo9TfHHV211zc1aoFsFb98RZB6FO388RncHiIy34A2bVyg4CagE0LutqAnQqpcVxuL2iIz8GeFhUG5bXNdQ79wWkrMHTfAhU00TkxvtFAX+kgJjtKJm8B1uY+mhs5HOkrBCfVgbTbB7DQ1XZ726bYA7A8UIuHcUpV9uW/xJvUFcugDWeSDpvQHCAS7Jptw/sKT2emCAe7cBMUoz4TMW7Tkx9agwmDJXojn4Jw3kEoD++8wWbPGfQG48SErqunXwrV1jCfeu1axgAGiKg3RHWJNDr4pFuYot6NvfZJ1eAeNtG678q5cBsvEWMYBvCGHbRP6wHY8HQqBloMwZEq2G6Vf05QQgRQITVlw4eU0aL+7dvxc7fiS49jKapWLYC16lsAYHED0gtP4JFP4ekbed5b9PzXA6XNWH7Zn6Oyg27cRNPMdkGPnFkjzb1zX97O0eor0egjVX/Vlwen84n73CJGF9ARA1kUOAqwLDS0zqV52lvg9GroUq298vN9AKu4UiksISNGDzcXus1MsGRoxgQkp4yqLPWzCAY69IqUVoU4nmoC4KCP7Tnay9GpoDs35acITXOtZxjP6K44ldgwolf8IAjnPcByGDTM2Y2woIE/dRnMHhIpXJaOxSXG72Cw4KKLdByMwby4KkxeVi97dVbpHvgGn0+G2b1oc2H+rPaj+oag5+H8Z+1fpeCzGpVY7TxuQKo8ojCp5kRQJb7ylP5PN8k/ugOPv9BO+AJYqxbAWvXNxFdQavZ1T6qPXSuPf14VuOSD2HCMWdcA7KjHKIIdZtr2yKyo81uahjrvI5CEtN5XU3hk8wGxX9vkABYd8mNENrattsf+aoA4bWUTcyeXKn3Om9pyB5PRBd4fq4hQxdmTRgGWZZLCgdoHsCZpg7PHMZO9H6GIN32uMkXouD0172SSg2MesOmbxSlCjXogZzaQw/gC4TGb+/MmohESqae+Su6RpaZmiC22/gK3ZBuO5pT5OUHtbljBcAEuNgdBsBgwEGYe7j4NKPo1WMcyO5gZjqV2VZoOqvbRR3RqpmBjbfx9B44CHLzgQ2J1/p+UWDR2EKZEbtQE4FfvwV+7GZ94SKFIOCASocs7dNUCWKu+6RdQXkNT1gE9+yBO/D6evhPnv0PPex1AbLqRXQHa7vXObHMPV9SJJeoedOXoq63JidjYI9BRL47vmRJpMkAu04IyIirvzpBf0rJxIkBk9Bd19FV91Dh4ASaW2AAsHZmwYATv4wsHfssfOtlvlmFJsklCzh8NYJXmmiVgGEVLQ8POAyyHmhiRSqVGbA/RGlNF3yxPlcF7jR4BsLqyXo0raSWxggdmaRGOiM3kU/dL1dqNqiOlggwL3jlsgk50jqWC4qpE7qgDqMGBXXtAfE7OKcGLnPmIklOvdoRTOBq7m7hoOiwVG8bRscIepQFjFSlgQmJ2rlK9/hl+9Gb8vdt5z8kcKCiQVI/kMrZatQDWqm9eab5PZYssaA4qPDyFx77Kx7+scoBLvptAUojmCLx6JxRPh5wpdlBDkglnzcE5K2NSY6VPOunUYUscIODEzhQjfaWjlSi8Q2le6DaHDivAMr0MGm370WqqZv1Ay4TNcIzs6U5OrSX2tgj9Sih7mK0zAqy6YNb/HWwaKlUzYSw4AixHPKkVjHcUlPpeYczeqW2v3o607I6Oft8TXqfnJJq/NqwQ3LAYbMdNr89YcJp9mFilO5Ipbk27yAxDUI/3gzAnytslKOZm9G2HyH5i3cinOzWct2itj8agdYNLlXa9ztFI1h3Y7hshHVuFiUJCVSiJAHeqgn92Lz96k/6b+/DCISSHdKoVYi50tWoBrFXf1CpyUaRslNXu2Cfvw8Of4Mm79FVX4LyLFRDjPkobQbcvdjCYHZghxLnpZbNmsJE7Xjk069/NrDUbXNv8ikrfbQnAZY+5aPl/N1Q4cS7FmMFsnTxHp3uryremEh1yGTv7KcDaxxdKzBa0zwlwqv+TDH4cHlrNWy42KgezWT+G2Dt413JHfvj0m8GtkiFi2Yvc4/t6V1L2HwUDctJwsqIMy+9opbJSoK88/VMMLeG81zuwEKtht7vhZvSyRYZG6tFN8/nYHOvV3sYMuumpFh8uRY2K8dFDA9dZtplrcPza53HF6JFWVf+q9neMHT61xz36yDsj/ARCbnuGP3Oj/tJtuPNJEMeApJ0Aq8Z/y5p91QJYq77J+Eo1sds5t86UEqdP49Gv4MQXKeDrrtIDc986G/pqOwv6yme20LEgHitwkHMd6fwEYUywASdtON/ojNuxHlSjlVcAWBzT+tjAGUNyCj0TNh/om6XWxAfnLqyxY+uP/xxgjVOEGFuNPRfHThW4v3q44KgOZ9mwD2Bx6OW1hVbopVFG5A6D56yNRdfXK4cAZmPBELq4EWCNxu5Bj+9MvJo7/F4zKmtbMPQcNZqkz5g/Zw9mo60YOccOxbqon0NYNWfbNKCtaux8rLNOvEwHDy3C03vGHb5eSRqk7uaSCJYfwn96p37kZv3X9+DU6fy8VAj4jveFoIHAq1YtgLXqm1A6/YlXx840ESfvlQc/iWeO44J34JWXyAbNTBcBqabxpTeXkIUdB6RoiYyem8IPkck9vw8T21L6YBx6Fy74EcXWYVQ6A4WZilwl0leOLmpNySk3JmFR9/N9efIx23EF0/mwGxtsgp6hlNj92zdnDOa0yvu8GNoKF0cF69hjGAgNUYOwPljGa8O9JBkcoMNbm7RjmxzncQete1e0dw/JfdUJ3Xjh+sU4jisaTweQfWttQjCkHGrfO2u8qTZXx9sHNLldSVDwbJ6GaTbD0RgLg2Yp2h/xx9EAkhplbRMAYZif+mNCjQSwt091YKcaiVUPj834KSeO2oOiZaCZdEi2Gcw1mno9utIDqiaqRltMIJCQWma9St6/60/iI9fj796OO54olnMCqKHezQ1scVerFsBa9S1ZFZloNnHQU3jwq3j48+QpvezDUMXGcwQ7Uk5DjykSZau3z41MUCEb/JoJtOHoK8Y24tQOniME2efOQAyhgfUPe+yvJvSVGfEbWSXnX9DtrzjO7oUHscdCzCO/+ARrLYGjw4gmjFe0bCAnlvqjj2jn6pJbeoPhuzkM/S9Or5MmZk7UkcGKACtqsFAVV7Drts9o9PIj594ENuhdhwU5fbKNam7ZhR0B26dlTJY8LRe1YrDALtqBhh2O83petqThMvNgu/mLJkfcstmQeqV8jmfO8woms3oKOtUdKz/sOfFTsI6mruvqqbi0gamQohmiFsZ7Y4mUYFIINUF/9Tg+esPBv703vbA7IJMCpCjTsmNYtQDWqpdSCfNNN9+zM9LQZx7AfZ/mEzfgwjfggjfusgv0BgibcyI2pagy8/0hScazL/uMr3Bk2I6lr6YOW86n1L1v0Uhx5t4+bT4K42SifVD8CigRYOkIsPbF19g9BwYHLADUPRL42JDFmTwahDaQ0SEVRRzXbwBLSEaxD8qAn5qVvsZMWjtQG3uHIUHnaICFMDM4yqEsZWXXb2txaSBW846yphIwiYQBYI3SbDM7ad1HTYIxrC1FnSU0D/ZneojJAH3a8fHW5/AOEQb/2WMuHOCOO+amnUfXvp7H1Aww1x7UyfhhB5Q26sfaa9XdJ5G4MSUlKMVWXlXLzwslyS88jr96Lf63m3Hf00kr3ic3VV12DKsWwFr1EisteciiqlUiAUD0tD563fbAZ2R3Ui/9IOSgXIUJEPAA3BEbRag7MAuAZr7tzn/c0zNHel9pxAQcHLbm4c3eYH18i6a1CvRVZIPqDjh1FyfYDs5pwmX4jGb02xwJGabKqaZ4hLX90QDrCOA1cmae+YvNR1Vl8kNkdnav2ZZ28ogmUzks/nsBFrtUfABnTd9Nx/o0PwULYmie1URNBf2ETpb9H4329PChhC6fmAJVBySb4GwkhhCkTIi+9x2CiM/D8SxUcGHofVgLvIaJyM4GttzAQL05D9HoqM4YN8lg3zBn7Iz/QotNYunw5fTUBBWK7iiiSSmK58hfvhkfvQ6ffgCHu4PiJtMufCbhHpXDqlULYK361qwCXGpOTjaV2ZCSAKrPPar3/n985Gqcez5fdQVEeVB+bepBu6UmiETjKwwuTVJj4eA18qFd1T0XzD29eFaxfxMEYQQvphnuU4hP+oNDhg+8mN3yQL3P6AOYMUwLchbsI1OA1fT1fvFyJlhDqxT7neLPEmANrg0GaljuKvAc6uR8Ts3TO0QaWSjFmVqE1ryU0+CaMX3F8VI0OdMtpNn0/jw3NiYcOz93C7Bsp7KF2dETbxar2SREOONyda6hJu2my53U7Z91iPCmnd3WwUrJ2lZtlHaPwFSjb0SIfy5Qb84+mn0YjsaebGZjAGY6/lVrSGUmpRSi/M0H+Feu1v/9Jjx2qkHD1nvWlgR5gC0txdWqBbBWvaQQFoouAkIcALvOD+CYSnryVt7zCX3+Plx4BY69hltJd4NQEnCMklw+tHN2sNN84skheMRg+4Nifm7b4GSDZvb4F8wU4gPAct5aDXLZ75jQC5usnsk3H6foDQ5g2feKSCi8ahgRwAyu7QFYM497zFqE20BTeRzpUggjQvI0QhHGTDVYEQadVYvQkVVm8q62IxXT3fZODV3I3VygAnQwSvzGsagqBjOFoHOPbpnRqbzruzWyYk6VFUgsI4QCMMi/DJzt56I8U5zxWB828B4QZmrSebWb8UA312lJLARBv0FvTs/OgcQrbdN6gKXCLlXdQFXyzpP4uRvlF6/Vr58oP8y2AfbW/HFJ+da0atUCWKteKvgKAHBQ03dThSZCKPNfVU4/q/d/cXvos8RpfcP3UOt9/UCx02IAj0FdtM3IFYl+Tooj+4OjALzRV1tdWmw+IKJtuvqEGZ1awx84OsVp54NOPMKjSRTjvD9YiCiNllR2ER5jsGEbi57OoAdJ8xAhT+wFwLoPYMGkEAJ2Ah/RgT0wHpUvYTC0PJMGy80/OmRD174kw7ycu/DUI4QKXqzvfDcUsAiGMQMHoUVY8REZzJz2xE43w3QPsJwDfps9bFql5r055hJygqJgPCnoe37ecL4CUzFp05ibjsIc7TBqaom0KN6a+o62Z2duThRJK9pSEv/gZn7kGv6/d+gzp+ueQTKpBcXmzE6UquQSYa1aAGvVSxBmaZFI5HFobWIsQ1Do0/frnZ/A41/FK1578Oq3p+yZTWLT4vg3mGd+I/TVvghkBNd1G4cHxzA1SLe5nt0+81LXHLQ7IB5giVm5R/5sCrBkjwALR0booLcg1b7FdtYAKwxpOgbLj+hPCZ5IYrHbr2pgpOhtx1mcLYMR6NkwWM1pvV+TGtqFddJtCHsOmAluPLBSMG5/rIu6WBRCZ6XhfcZDEGEAZMGBPZutN2G8QTYk4hynGl92L96P3d2+D169ToecupwyoNscmawmRtsCYDV75rXyOJPvqP0OubCd9uNHQSJnyP/eA/wrX8E/vAH3nMy5U6mdG0I2zaET0j4DVXSJ3FctgLXqpVld0KHz23n9e8IjN8n9n0jPPIDXvgOvfI0oii2Q5EQ5ACrHoDtyU5DbRiRVEAdlvZNqjjVBP+IJpxaBzMH7CsFJi+zi9z5dqJi5n2NmlzCm9+igyo8DgMY76myt57HXAUtCDyuzLewCLPUid7u+bX6ts8tfcIXNTU0btrz5bL0+U2nVSDUrcJyUD1CN0xZhc/RO2mmOdq0lI9Yx5kxCh72asie8BVlUPT26uNNdFYgM4qr+6qIqK/MeGuixquuiBvF/xSj0NBjd6F+nAIkxrmbuOFq9QZuY0UVEc3B1r/3N6JrOqkgzjqAKpXYtvQZRv7XlaDjPJkD3PMkMCLcGhJp3e1KIZqc8YtfwPqlMxF1P4ReulV+4Vr/4kOxUZzcZ1cnNZ0mvVi2AterbA409fxL3fmF78DM4/by+6Xuxgx4oDgnhJlRSd9Um4EA1r9n5/rwRklSJbTY8KAj6LTeUNwqYrDvDRmKSoGygDIKa3j1hG+irSFB1zXsXYBlQNaOvBrH/2LCbOmBh8KyvbzFJDxw/KaJxawRlG+YphH2xr80eezCnDRqdBA624b0u4InhMDMn97DkS+RpBrVTY3zyq7QfXgfg1BsHILg8SGx6hqicYO/pGDI2trcjo5Ahrer4oXiaoqDLcmnAmK6DGD1TPku0YIi4TQ1u167us1J3hmhn910cPLEIJoGkCu8qc5aKtkC4QRNKPFcJDvwHN+AjX8Nv3K5Pv2DSQFetWgBr1apyIXIDNmD39AM4/jty35f1Fefide/JLJQCFOVWprJF2XQfm5BQzUTNvrAdzOgfzLyvNr/wiNdot0X6iHRkJ59vLxnMHWAM6J01/KQ/OJfPB4ZpDrC8A1Z7XzmqqYppFDSitZjvJOremOe8/bZUcypo8uyVanE0Lc9L6lgI4+VdNEaqHWBVGBFCi51ufZR8NejpZ/EYdq/Juox9rE5ae+piZKpgy57Zqd68wKCwe2q8stqkBVsKkLOZsGb33bzKwibzrKMBlkaI5gGWYhjAHEgpF11DFwQUyNFmSLsptDrTApDtUDfZUo6zSarYDrhLmWf+zePy01/WX7n+4IGnCx4Mx33VqgWwVn3bV2kDJjAjrfTYLbj743z0Nlx4GS56E1D6erqVtooe5EwMqkAJSdADjvKNLj+HYars8sjgj+AgCznRhkcybOoOzz0G9GI7SoN56cy+S7HfwH2e5GMsRoPBRINxU08HTKT64XjuB1iMIneJxIyHG4PSmZa7ihIhOlwQCBgrRVda0ypNZq5fJ+DGZaLQuSHYt+D4WdSGuowAC9qGA41P5gyKtQ6iM0rwVqSGnKMh+Wg07ADI/Hn7xjVMAtg+nwdYtFjKTh0aVZVBZ1q1lDY30HBpnMK2CYkVxgmlhFincg43TbuNSRUiW/PpUOHnT+jPf1l+8Zp0zSP5uKQa67TpavytWgBr1SpfQmKrt1AAp56XB6/Wu39Pnn1EL74Cxy6CADmj8FgqMugDHqgmYsvZFxYQbAPQwZ5sHJmFG3aAdcb+nQ83bOvMvD9ol+2hPyghfMYDmhH0SKBnGkFlHTqHFif8lGLwcMc3BrBwFMDqhpzN+7PYiwdBNMdAw0BxdR1PgqW1aC3RdcjM2xeAw6z1q7BMzXMwA2QeYKlhm5xXk3sm3Aweop+7w5PqLBva2F6PJVRjF9J6ccmP93nWSAdLiyaKxzA/GCBsbvxpiN+BdfYSNyKg3tEKk7cwwNSd6Np5LTuc24SiBTPrYW5D4r6T+re+pj//VXziHn3+tBAlNAI4AFP9jbAw1qoFsFat6mtRlQRDiWNAVlrg+Sflrs/wvs9g9zQvu4p6oMcSdlL6dDnRNeeR2bbXtLlmfXs6rzNk4xi+RzmdyDNbo48alCmqY/TEsu7wc+/4qNafO9QLIk0F5/RoVPx0iW4hoxrDsToaYM2eeTSD1XfAWWFpdwlXrxOnT1PptgWBejH9MuNDPpHCj4ZVCCnO6I4J3oSdCDIsGHNz9uRlKiZQDJjajRrI0mynmsJsUBLZDEF2JVZBmZ4bU8s5hYibsTfnCbxwfIaQHAOwLPqlmZ5AkOR7swnGtEE1XhVsaeIqzN3tJuw60FPKv38NPvLlg395Cx5/Xum4OIBJ1HKAq1YtgLVqVb0WNaMmKpiYV3xq9rZ58n697eN84PNpU1z6fh6rmlphfZqz8ZwM32GI25P96dHNpHofAJq+xTb19pwCOE5SBfdprTgPnzFyMQOwxIerDAk/3RnLjjfC47MpwArqsaNDciKD1U2e1PlgNb+DTkNF/OSQlF3TLU7rpuqcRLdU/GHUV0OKM9wz8+42i4dZixAjWGm8lBawQotR4N0iDOTy9lcUJNfm6oSTOr+uOjMgaHkGDmmxjCnqkOTjKJ5JQLMj0ipH5WJr6JHZYGEaBw10Du3ikwVMgEhCksZnESD+2Y3yVz6nv3oN7j+Zst5dmU9nHh8+QMlvXtHNqxbAWrXKL9BaBFUohk1FHdOtAon06HHc8js48VUcewUuvQI74KAMiG8H6EbQFkjJDF0BAXkoouUBp+acZ5jvw3wUcdIfnAAstfzWvuhlGwEksKqZiQPW1CIVfoRQhq7SPoBlHRZkOHkcpgFnU4RD8Er7M53jKKM/JwPR0k+DickL5JUFWOwpgjHYrr8k2b/AP+idGhCn+YzAC9AqGGzuEopK1RjbTDVwJXzeZGikoFIaPOe7dsycJm2ksJb9idnVzaqTmBxbbf1H9ODFybsPf1XLl3H2ZKeLZ/v+GW3+aTk40KT5F5PiN47jZ7+AX/ma3vKYGPiq2U4vH0giMf9Vs4hzoaxVC2CtWuXXjcGrJv4hncZDN+LW38HD1/O8V+N1b82aViWwaVPlCqHUfP+FAptKiR3Mrkt1wRHT+/Ou8co6+zbK21uOIQbLhrJZ7fjLeTEYB23alOj6CXsoIcJ0u0Vd7BQURw8tm80WUJc2DCCY8TeRLaNTWjO2CN2rPMAKd5aKU5knBDNY0eqZWSOVWNzCiMiVabfRVB1dBjpDMvojmKwbg+oSxgDmQVff4mDYyDFa16jRb6JdIKp2y6xQLLyp0+bbL4MFVZZCU9oo6Jb6k0VSReHumaQCoQpia44lViNvjp8NHNR2ALqTnZtQ7OAr9+Vck9qvLL4AACAASURBVJJS/bJS2YKhcaufurK0FMXlEm47VYDKTz6Qfu5L8re/qlc/gNM7+JEEIGLGOQhdtWoBrFWrXkQdvsAHvq63fRwnbsNFl+HVb8AGJJJgIrbqAsnChEnJPFZsFKlr0tZTcaP6auaurvvc1e2DYVQQJmHamXb25p0zcIfL7QH2e27NKasM8jyG889hc4IY2b7Q+AvpN77peQaAJRNyo+vcG1ipXmbODSvYKDR4oTpMEXruZHDPKtSXm0/0y3T3Z+Dga9UQkt8ZNG5HJy1Cuyd9u+aZ3pjUWzDAtOq0YxmTqYPQzbTBz5M+oO1A9gBnwkNni0xUxwOrjmoyRCBnHrTt8GZRWjogEzRV7J80EUzFyEq30ttVHKOmLGoH8LlH8De+on/ji/jsPXrqcBkvrFoAa9WqP7UiCDn1jNz75XT7v+UTd/HCN+CCy1HTPZzl+gGQVA6YJbgqJVLaNa1sQ1B6X4p7xE+Tsbup/dUowArp0cGgoYYVuskqwxt12bv164oAy2O4CQgbfLYw8RTldKIwoISzBVgW343UUQ6/M6AHwyCaRRBzgFXPzz6A1UXrtYumoxOVOijWjFHV0SK0Nps11ZBwthFREW+G/iLAqs/zl1sg1WZcF13X28rSfXdR7SCkt/QaoxJhxwYtVpNqum8Rqv0lIb2HykbvkUmh4AZVaCJEpMUdbuSuaMhEdzm04dpH+Le+hJ/7Ij51HM+cyqFai5FatQDWqlV/qlXGhZ5/mnd/Ybvl4+nkPXj15bjoUoLYdOsBdsABVVWJTSppoer6gxY6jIKq/HMbM/k5hv7gIG93TUMXLz0YUyGYLMwScmAFWI2GMc+JWYoTEGYBlk94OQv6Ct8AwHI7ZkRi6td7xPUeo0HADGCZwEETeletOCcMlkGJLnManV2zWTpk6Fs6IF2cBEq0jueQ1PtLJB0IH0aL1CEA0VFWGLqE8G5YGPTmUbTeOpjdqMs3SVvozeCA6qXuztoqGO8rG+tYI6FLIBFTOaY7FZHCcyl44wn5O1/S//mL+Le3y8kX8hnaFKl2EFetWgBr1ao/FQaropocJ63PPoHjn5Obfk+fvBuvuhwXXq6ANpQgRZml1C3zEQfZ/puOvsIRxlEOlCDo4ve7t6sljQKGIyb5gyHbZ0zI6QDLM1IB6OyxGC17GPqDgZBDFYRN6atvBGB1CFXYi+LVbmABR3VNsEja3yLsEvbMP6qODFYkvaxX1gh76glSDtZZY860JZPUeBxY5NFy/Tg3Jg3IaeKG1abxHO3UnA5U1b3EZBhWRZUOZg2NGPNSd1rYFEYBPBrrsNWYqHezVjKpioDcql0WdZMc1CgFq+m1j/B//Yr+7Of0t2/nY880jCnKxDnYXLVqAaxVq/4EIZYQCWUwu8yePf8Yjn8Ot/wunrx7u+j1uPCSjKeoVCnpOgpuCiX0WF0RrF3nbOxOrXxqv3+p0n+jeqdSTVwdJ/k8mM0nYjaiaGHcJCGnG7jTudXTKb1CY3R8L0yydPZiqbMBWFLac4M1gmIObdKEsjpji7AhqtBrG1uERvkOo2X3r3VSetrBOA5pwn2Q0Eij1E5EKh0QGzfiZGiGXxqOEB1vBDuSyXHn26dV4zRmics984naSKzGdtW0QVrL+8ALmmnNrHxnhupJq6frJror9qgJ1z4qv/xF/et/gN+6FY8/J3mjWzlgsgxDVy2AtWrVN4XAUs29g/KfDXlKW/js47jjs3rLv8Pjx+XCS/TCN/CYUqlQboRCD8A28L0N9BW899WEvpr4LOhMfTVhp6beVLbbaO2mxLiGCmKEYotOiQKsQrvsAVicvNEs+gbx7f4IAKv2qmic3PNhFHhyC5hRZC+iRXhWIncaT3MLe7IULGxHzZjdgJk0BjmPPqjW8JPcgw49+pp8EBP6HCCOG2bknkNUd+AIXDV2CTtmsvGL3Re0ya0iXVfOb46CYqqZzSlBmHLb9ssP4Ze/iJ/7Q/nYLXz8uSLsyr93VHK4pDaDlgWzVr1k16lVq16KV64KkModGQBTXZby7+ViYfDqN+L9/wm++7/Euz/E3aYCOUBCEpWUQGnsVAVYYmiqthYFf9GQb9jicbjH63zztMBW364FIw4OWK4/mNOBNo+Z9jmp1t6lAEk8sWElYkcALNEYucIxudkveUb7pdOfbsNgI4uqXQ2yqW071R6DY8FB+9fGbxmrUq1OAVJyY4qeiZqafJ5dSp8cnVauo2xdkNRyUaV2Hjbl5xc70OIxkfVV1Vqd0Lad/GRqSm23+6RkMp+6fUyFJk9xpfLfDv5S30j5gzkyXYeu5ugldgtTte9bnz++JP+5HNKWiqNUoxjTemBZj0w7/jvWnddd2jbsNOEP78O/uBa/dQfueaxeFWWnyje6uAYnLdnukc9ctWoBrFWrvmXq1Zfgyr/I7/kpvfLHmZ2RNmWiblXZwyZXUSUryFDJYdIbtqQ76fYNlnMqnlgWiIT4Z4+f2hO6PGfz43ujydbmw5glbkRhkqfbqi4ThXsBc9LdsKK3u0GNsFHTsgdgicYcngDIjBjfmH5lxXSys/0OtHWYpZHB0p0DWHVKTlXJKMDSTncaRGIE7IUnqY+rwzENXiS/YwayaE0gtvvsnSayM7s2gJIxXDeASMNmG35KBmPNYFlukhfQk5oLvLaAwrJZ1QZMK3hlPeYGUzp85iAUmjx/l3VTx4DDdswTKCll4/UdsZ0GKIqEHYrJmQLgx27TX7+Bv307HnlycVKrFsBatepldLFf8Cq+8z/gh35KP/CX8torQNqalgsEdFOyqmiywHYDU9Jj4jgtRp34BGCFEbz84MwgvgCs6kldANYYXG1lXqPWanNaHPXK/TMArP0mWBFg2dHIEWDRwDXLeLVo54auisjdpiAbgKVDnLMxdDAMVurUVNHjqd1EhwUDvDhrgJU9nAyEaqSUJu1TkMmzQe6tSR2fbMik1AX3HSA66sigzOQZPguwtEqmVD251dCY4fAsZmoIr1Fl2lX5Fc8VmlGVhPIwC6pUd9XNV6Gy8XBXedKdQlSTJKjy/7lO/88b8Mk78cQzBtSvWrUA1qpVL4MLvVhfMb3yFXj7v4er/jP84H/B7YIyykRWYkbZ7IMOKKqJlGJHDZGyoOkwPzgaNMzjmbdOAjWAoqTVWnUMdxYAazBW0E5fDQCr7CQHqmwPwIodyTMCrH0MVl6Lq/pKDQzilL4qb2sj+UzTDcnyIihhealgCIPVOotTD0VrTQ4AK+W5O+35g7Rohg1FuXdJM/FWsvyQIZyS2YEa1RywowFDbm8tDEICE4rXiJrg5xnA8nSXBVi9D+jQmOHMNDVf1vZxBDsACSJJkxzmCzZBkX+YcEdQNYE7QvjCc/yn1+1+80Z86jhOnRYgkXl8cHX9Vi2AtWrVy+RCl7JqCZhEmQC84wP4rp+QH/jLevG7FKCokhTwADhEEgg15TBpMqnBUkN/MAIsGfRMbSJs8+gqACwxuGNwwFJ6PgyjA1adExtYrijAqjuvmCnZLQ77BgAWwjSlOu4KzmjAe1+pOnrJGTc4gJWfI9ZpoUCcTgJZkASjqWJHD6WxWAAWKmpplFLjrhROR68NitF+qAiwGnxJjm3aR3c5gOVQoMNSSOqkTgH/qev9OdhkG5qeZnMCrOQBVnmw7phAd+AOUIEkpMwCq6akyA9sAr3tkfQvrsPHbsIX7u4/cba8bTHvu2rVAlirVr0MrnRiU92xqMYFSCq4/K1834/xqr+U3v2D2CrmgJZcDzS1uM2HPmv6Kjw47Q82QwQL1wb6SgMHFrqQFmAdTV9NAVZIv7H01YsCWNMWoRWcqQ/pCxl8oWnV5/cayMgPew1WC10uXULTFiSjlqhROBFgGTzUU58bdtmpI6VSAz1iaCqLZjoz5wHWwKslBxnp1OW2YRd4LNNM3NMljJutinXDis26hOPh6jQYANHdLjOt3CXlttvtDnKHMAkVQPr0XfiX1+B3bsGtJwRIIDbFrv30IPv5W7VqAaxVq14O13oZMiNVVHduZAly0WvSd/4FfNdP4of/c7J4T2fbnwRg0wNhapTFOD/ovRXig2FEMXjEG9zm9PKhOQgz0Bd8IjBIpqYq+CnAGpuD4WkvlsHifoClWnBrh1mmxeaYrbAEG+GU/atFaa1J16VOBmQ0INLZpjRosMxUXSS9/MheI7Fao82SWHtbinXqsInKG8DqqC5orRp35T6FR0Io830dOSVzhw/c2D4ZFsxYop1eNPqt2PRMmid5m4HFr30N/+oGfPp2PPo0AIEokyo36K7NAy5rq1ULYK1a9XK80LlpNjlUKTRWXliK9imJ4F3fx/f9mH7fX8br316Wqg1ow4YSAQpGf1Ez+tdexTYVGDzibYb0NjNw3+AM3Nk34r7Atn05S0KcxfV05uwMAGvW73vRLUJVjY1CsgOp0DMaOQ47xzcwWHYLhd2BUWIpXOPPjgceCbDQvKB22uFOZ3TOCmCVz5qq07qOyrAgOe9jj3XnEVqEceQwUFMWewVAlszIIRC6hFV0VRugqAJ/1C4h9FDlQBNUwEzs4bYT+uvX4rdvwBeObzsmVWb/9rERSGYKeWGsVQtgrVr1crrSi7tOXsG2VNZwKeuhCkvXKEEPLnn74Xv/fXzoP92+58d2CRuQNqh60dK8P9idqF5cf7AJrQLA4sxS60UCLJ3Cpn0AK3i4YwgiFJ8OePYtQgN6Knnjtc6B1/H/5sCNmUNEk3g7o/jWnmNd6PcwWDnG2EUBag8NHAFWn/hLFWAVdbx2gyuvrKpum5n1cQBrQHtGdOV17v2fAN0pHXiCc/mycM3KxQLAIgKjRt0/S5h3cpevjKQ7KPHxm/F/X49P34zbHslHJB/e3FXfEnYCJmzAYfluGL+rdU9atQDWqlXfvt+NV54n7/6B9L4fx5/7Kb72DXnRFmgiCzslhCbZROnSdFmcF8rgYpe3F3qAWnNvZgKsqqM6ow1EhHrdbko3Yz01ZbCmblsY4NqU0xoSdVzHE8HC1FiMwrqM2jaWBoRl7AlgMcTAYCULn6k7JaUiEnWTgEq1+Mu1CPMjdGyZGSdE061b5ql4XGiWZ5mRRvPpuplnZbA8EmpozPYfDXD0bbvk+Lxh5NDwfMYbDAkgd9AtCbLT6SF04w56cFgPXUNypO7ApIkQFWhCMZdQKHhY7BjuepL/6mv6W7fwy7fpUy+sW8WqVQtgrVr1okkvIRNU3vDOdOWP4EP/sVz1HyWy0zwCESRVkEVfQlBVxSEqANhIVRVAKGpmzKYKd8sAjQYN8GnQmDlgNTgiJh7ZUlNB74X9Hg3fAMDifpF7BUwZYQg0jQDL2XXSgLBR5O4BFpIWOirVVxYeCJOJv+Tjpa2/Q2d99gEmr/TS7hMxarbgOnQRYO19MgapuzUd9QDLvLweKO2N0XyhofrIa90O05Z7se0dU46j3jWTOAESdlCUeOaP3czf/nr63Vt4/GFJSIpNcbgYqVWrFsBatersS2qTCU0O/8pz+e4P47t+RD/8k3jTezZB2jSvVBt0lxPTMmslBesIikeTioFZLb5tEGAhKNyDVZV3gdeJA9ZZAywb7xNuBBZ7jQArPF80pBBOABZaUrVxYQBigw8GZbmGndFFFdS0n8GCJ/MsYArTdkcBLN96A4rYyAIs14W0RlyYaKE6ZvJ0V/SLD2gM3qxBo+WEB3n+mZ0FLFoxK0uTw6ay2qDaj3J3xkfKRJoCihsfkt+4Ov3erfjCcXn2hWRWjw3YYflarVq1ANaqVS+exJrgrtddru/5QXzwJ/B9P4nzzydUFQfA4WZoHAGoFGruJEpNT0SiSBEQD/ODmI0Qzg0apvTVEQk5RwCsQeE+p6/OEmAdMUXovEbZmKEIsOJ021kzWK5FaPBcA1hW9jRpEQ7CcPQkQW2AyWyky5sGEZhTmAUGq2wQBsZpmFKsLgztn2JqELuxqncK7YhNZ3b2WS92CCoTVXb14GVKTKiZxTqE8ODkqd1vXI3fvk5//1Y8/DhTlVCVkcCa/rlq1aoFsFatetFfDC3OPRomzLME563vTVf+ED784/KB/zBVqy0yqUhbgwsTRsVGUaStugJZRZSlrzAArGnkzpS+AkN29R8VYAW0dEaA1YKxYXYDmLQIDUKaMjpul86SweotQmcoMDi5T+ICMVh3TrxGB2v41sgzLgmh8Qdrc9CnDiPAwgD4jKzK5THDGygk30xs6G1ww7KvKm3SJJLF6siAX3SXzwf+3Y382Nf092/HNXdnGeFor5Db4DkGaflarVq1ANaqVS/+66HuEVEk8w8C4BXnpnd+D977wwcf+onD93xwI3fUOm9IqMrGBGN/lZfc0f5qBDSjXcJR/UHv4R6IJfHO70cCrP6mUU01Hzmk2Rm3Cg8AqzfRPDyC9T13Km9MwNCLAVjDdF4FN/tahO69jAuD1bmjCcXIFjIYSKwAsIp5RLXCOlrn7tBbf1/r516fHzwd6lH0uq4MxaR0AxVIOfWyYS8CugO/dpv+62u2371l97U78dwpIam6OwAOG7wiqJAs3Vp2C6tWLYC1atU3iq2kuoPbGTTpzIaR9wq28161e8+H5b1/IX3vX8Rbv7OIhAXQJJK7g+DoLzpaifIMACs8OIkg9F6msGp6mM7jnvnB2JQ8I8AygGwvg8W65eaw4HwK2CVQLsy4U2Nn1SJ0Giw1OTBwXcIge5oyWJb9AoIbuzFfgMkNLHDK5AySMcNn4pIVRfcTH9Gkrt+nxZu0Hcnu4NDDcAgTsKhKZsSXNux2IE4nHkBVedM9+q+/jk/cJl+6PT15UrJqsEQdssWOp3KYpeC8OuyBJcBatWoBrFWrXsQXIxj2ZI/EBhea30+GGUSqnRYh9KKL9T3fy/f/eXzgx/HWK5QENQfZ5NzojpPEqK8CgpmmRE8BVugPTgGWxWpHKNyPBlgjIBsBFgYBFrrWHgiJLnQioX74X6RNQ4vKsTYNBgk1Vfh8cO8IgGX5trgbapRSEE2JJhvRklUFpYNI6QwAi76lWPfNGVskL5waJPxTBJkddpGwo2xIhzu5/f70W9fhkzfgs7fj8adqXmdRVplfEcW/yv7YyFaiVee+WoSrVi2AtWrVn3K95lJc8UF89w/je34U73qvqCYhNmzFGFuFTFQID4BDNQCLLXJHN3BHiDgbCNZsQWtjEKxNGYysOKCokZQaXUZtl1CGG4foJOtwZLACwMomAE5Jb9NjYsyzQ1qWMTFROSVD0PmbG7hmPRfqCwW7FNFS/nPSkJaTYU9PcVYdAnMwNv40oZwE44ZVPxEHY1K/qxlsJc1euCmBGUu1HmUCCewKl8SdKqEU7hITlHlvN6Rd3uyO2BIAfP0efPw6fuom/cotfPip1eRbtWoBrFWrXsrfsYtei3ddhff/MD7wI3zPh3TDprrbWKQwBESzeEsU2ci0GTp0lNORUIE1NZraAyyjcP+jAiwOxNV+DZbOGC+H/9w9p9k1df5uT4uwEWrG8sBlF6qZIrSgzciwnAYL1XbBuG1ZgFV8DVI/mGVLuw65KsLjRGCOiT1pOR4Z0hlpvwdYxvKqslk1YMe7YSWW/D/tHlc7cEuFWC3yrx6qo1+8TX7vWv3kDfjqXXzsqZUJuGrVAlirVr3Uv2FFyFUkw+ddhHd913bln9t94IfxfT9KCDawMDDUOiG4UXfkRuyyywN7piEU3QjeCrA2TxqdDcAaLRXGmGcLsPaMHLoUQnjHecBFAJWHm1NUeyx0Ce2onZV10wOsQeQecZJtLyb/7ijRSE6E7l8S/T8jYKqNvORYs+j/PjJYe7qEDUV1p4ZsqUCBimIHaErlHKWEYlm7AxMg0B1BZdpUd9CNujtM+OQ1+PgN+PxNuPZuPPVcnRcEiHOUpxbIWrVqAaxVq16iVbMOKyOlKlXvI+/4TlzxwfS+H8SHf4SXv4lEMgAlu5xDCEKoKae+FAMIzYZcewGWn9o7CmAR4H6ABS8Fi/ahs5hni8nEbMnZuHvtVIsL9BHLxpXA25D2FqGjoKpGyJNhbmsV+nhteDWASKMx+qhzt4BpwmBV9LYnMCfo3F23sc8S9nfcAYK0y/3BnLO0QRN2qSDb08ABkqrsqv3ajnc/pp/4Gj59o3zpjnTDnfliMenLQmTybLFYq1YtgLVq1Uv4O1Zly2rwVtERMxFQldddnq64Ct/5vbjqh+SqH0wHneop2X1bxyslwJhGgxVGCE00jVqQRIeKzgpgOS6KZwuwjGZrYn9l3Rm6IVZY6a1semCw9gGsCk3ofLDgom9ixLKTw1eGCd5CfU/Lr3u4G4MrJ8PyhlsekEU3LMwSD7tjRaqd0FRPVNrpjtw0JQp0p/iD6/mZ6/QPb8LX7+YDJxQ4KCnL7WwAG1IqSCxjtjUAuGrVAlirVr1MvnKCPnIItNEtACrHzklvew+ueD/e+2F86M/jze+ESLd7SArW3l+GWZ21OmuAdYQrRJhbdP3BGcAyEns0L1MgJj1nr1Ua8AFlH5fD0B9E8MaMDNa8RTjm0gQn90BHmTm7BvOo07ScvQCrv8TYUDnnLXU0mPF6cP71Y8RNff4B0g6bIiHtQJKafWqhJJU335M+eQM/fyOuuUOuvyedOlRRaiFM1ZzAVAwjrOHIYrBWrVoAa9Wql+w3rA2xd08HSHbGTi3lkGDClg0dgTISf+Gr+M734or363d/Hz70Q7jk8gPyELXbuFGI1DpiOYIQ0cNdR17qSNstZ2HKvjXsobsiwNoncoeFJKGh1qRiNoUGXoN1ZpE7YucOnsRKDug4Zijo3H3yzMQ71I002kwbHyat9fl0/ulMekaA5Vulm6ZEEDsF9TTkxGPpU9fg8zfiy7fjhnvwxJOm49sb0M0ZtB4h83cWMLzoq1WrFsBateqlXQZdxQS38kilpcLzS732Erzzu/Ce9+F934urfoCXvRHZjMhI2v+YAdaouBp9szbXAZwALERNGCby9tDOyxVahKBDYINNgzH5PApg6S6DRQNudhb+Gf1WA17oyq2RwTrC53PKYJ0ZYJm4QzZCi7jvBD53HT5/M79yB246jgefUGIrgTjKmukoBZMad9Dm2UYktc5tNBHQq1atWgBr1apVF79O3npFes9VuPL953zg+0+/5V05dyevqIUPa7QFvXmVFmtTbWan1S4yW0WApECTQtgpD3HuXEjgNr+JaGgRGq7L8ED04TMFAHUfh46WPP6YaLAGBsvM3yGEN2PQfjF5FXwDZ0nRkRxLSHXtzcHE1Gj+v1S7sV0Epi4Dp4A/qZycIhEKTTVTqcwzpPw4kipxyz343I3bV2/Zfe12ufFunHh8EU6rVi2AtWrVqj/eL6rxjqdRL513Id7ybrzjPbjyffJdH0zf/SGc+0qXDKhGfUVQkqqAILEpDgkINmAn2fK0oodCd1T7Uyg3aoVuClCQZxgz29axk6gGdDWkIlqr8TnAAqpPFaLL6D6Ru8NbFsegIrCZW5WVsZc6zAcrW4QW4/UCuQSqoppkB6iASdnCasR5phdqalNVplTMF4j8/HKEEzSxnNakOSfguVP40g340i1y9S3ppjtx+/3b0yeRoI31XKKpVasWwFq1atWfDuRC6ycSeP2b8bZ3yjuv1D/7Pr3yKlz5/hzUK4IkOKY4beb+WtIzU0VFotgMcaXV3IFwkvkcnZL/K1X8bvN/LNyxzUGLiuItaDJPZ9DEkVOE7j42yXt2DFbfPrudOgBVFu4rz+slZpuMmkLkebW2e1XcpKlMFOpOKaXbSwXSBhXFaZK6yxb2G3SXIJISwKtv1q/eLFffkW4+zlvu1bsfLMe3TT8QXpZOVgHfqlWrFsBatWrVH7WEVB843RtsVVsjHbyQ24G+4Y142zvxznfjivfiyvfhO6/CsS14foKJIqLYsTbyDmrKb4FQdYSwDSc2WCFa2n25xyiJKkkGgMXS8jKPd8AUrQ0cF6VmkNBWmonc0cb36Eipli1o0djAYGk9Kka0XjEZM4UkquUUSGv/AUwCErpLKSdRAppQx0T1sHhxAFBKSumr1+Prt8v1t+pNd+GW+/Xu+7Hb2c+XvRXq6a6p3CRzh5IgdNkrrFq1ANaqVav+ROirgk6q5sgGUbc/V7NPHAIQ4rI38s1/Bm99l77z3bjiPXLle9Ob37LlZVt6aGDWackGqu52oADCbKuQh/6AImYvY/+E0mQDW4DVmoOp4pe+z9kJrMjS1UCibtGpjRJTN0V4dIsweLIPcM1BOkti5U+QYxKpqWMvgrv2KsEuqYKUjBrZwmqEKaUyebDLby733J+uvVWuvzPdcBzH797ueHB334OVPEwdHxNAjk9uADqPPjgKb9WqVQtgrVq16k8GVJm/ZnxT4JTtGApUi1NWSS1UzbaTSaBKVT0g9ZXn7d7wennTW/TPvF3f9g6+41244kp929uOkaelhgRSIVm03u1Jy440PTthbQH6IGH3aNDOfcGFPaObuYM9OhDWoKHeqVzLL4Kk/uSmJfcAy2it3KxfsJ4iRVU1QbKlZ2kCinKHZp21q2nNSFSUYc5DaOLxe/S6O3DTXbj1Lt51H44/gPse1ueeL9k0zjxdiERmUEVzjhMFTNjVRqHxp7XwazFYq1YtgLVq1ao/JoBF1SC+yZp36J6ll7Z7WIGJuucIzZ8PjqVLLsfll+M7vkPe/GfSd7wV73gH3vpOvPVt2Fh6kCzZiuWFAjK30CoIDAxW3xPV1hCEkU+5Zl9jsEJnUGn81uc2DYB3cofTYPlUnB6GGEXuSYCUclTRDqo5fCd3AwkAp5XEoeL4XTh+D266G3ffizvvwz0P4oFH8OAJHB5SoVCpqDMhzCi0489NW74PUIypKq7KDmoNkJnIISPJWrVq1QJYq1at+hNDXRogVyU8mhNSJXLMMm84MFqtTwRwTEJcesl28SW7y9+A178Bb3wT3vwd8pY36pvfqm96Cy44VzKfM3XAMgArooLGm3uXkgAAAxRJREFUHnlsVDCQRIDlNz1lsMo/GZE7YA3coyd7FLmXnSnUmhKqJ1/APffirntw94Ny7/3pvgdx38O472E+8qiceGyX8wBtBCOrdZY5ls4CjdocQeOdmAp1TJWMYHpd8KtWLYC1atWql/udQ3HhhXzNa/Xi1+Li1+KSS/G6S3n5pXrpZbj09bj0El5+GS69VF/5yhKhaKzbUbpvzfKqAZ+c8JIIIPtDOYbJqOMbFCNEVSV7bKaWbNhk7wj5zRbnPf88Hn4Ej5zAQ4/gwYfxyAk89Jg8ciI9cgInHsejT+DxJ/HUyXWyV61atQDWqlWr/vSquoGjupwyQdu044Hw8JXn4fwLcN7522su3J1/AV71alx4AS46nxdcyAsvSheczwsv0vPOxYUXynnn6fmv5Hnn6rnn6jnn4LxzcHAM55wjB+fosQ0iKhsyMtOE3Y5px90hT59Op17Q06fw/At44f9v716W3ISBKIB2N/n/f7UZQyovssAQHgMhlYozi3PKC0qALNubi6xqfYmuy67Pvh+6Lto2u/7HrY2ui/s9b+3weMT9Le73eHTR3pq3z9/bR/R9DkMT+e1ZsX38XNEM+TUixlJipo4AgJc8mW2LtdfioMayAtPx71Ja1JXnvGmjnsM+N0PKfTn5aWzP9fc5XlOLK2t3MFZeKL84APDipFWrHHSWn+oglv2j8FcXst1ieL/2ZZSoAID/oGKz52DGejZoHXoOlyPU+lSdJ6fDealnL+8GuIr4tDmVfzBCAIDXyNVhRU4b79RZwsqzfk7btknrQv6r41yYxz03q/8HMy8NCGCn8RUAf2mYI1TNOSYza66quStLsGyp6eZ9y/41XEhaNZ3dB7t5pOsB5Fw0fYgYplhVMRVdBwD4QNYZaDOxdGm5+rV3aeZ7l7e/H48WrZsAZj07AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FH8BFHp1DgyJgsWAAAAAElFTkSuQmCC"> + <title>png image</title> + </image> +</svg>
diff --git a/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-image-foreign-object.svg b/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-image-foreign-object.svg new file mode 100644 index 0000000..d27d1c8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-image-foreign-object.svg
@@ -0,0 +1,21 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 480 360"> + <!-- frame the view port --> + <rect id="test-frame" x="1" y="1" width="478" height="358" fill="black"/> + <!-- draw a group within the view port --> + <g shape-rendering="geometricPrecision"> + <!-- define a circular clipping path id #circle --> + <defs> + <clipPath id="circle" clipPathUnits="userSpaceOnUse"> + <circle cx="240" cy="180" r="160"/> + </clipPath> + </defs> + <!-- sub-group is a <foreignObject> (an <iframe> with a color profiled image) clipped to id #circle --> + <g clip-path="url(#circle)"> + <foreignObject x="10" y="10" width="450" height="340"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <iframe src="red-at-12-oclock-with-color-profile.png" style="width:400px; height:400px"></iframe> + </body> + </foreignObject> + </g> + </g> +</svg>
diff --git a/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-mask-image.svg b/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-mask-image.svg new file mode 100644 index 0000000..622817ba6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/images/resources/color-profile-mask-image.svg
@@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"> + <filter id="filter"> + <feGaussianBlur stdDeviation="10"></feGaussianBlur> + </filter> + <circle cx="50%" cy="50%" r="45%" fill="black" filter="url(#filter)"></circle> +</svg>
diff --git a/third_party/WebKit/LayoutTests/fast/js/webidl-type-mapping-expected.txt b/third_party/WebKit/LayoutTests/fast/js/webidl-type-mapping-expected.txt index a0f99e9..19e0897 100644 --- a/third_party/WebKit/LayoutTests/fast/js/webidl-type-mapping-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/js/webidl-type-mapping-expected.txt
@@ -1025,28 +1025,12 @@ PASS converter.testByteString is "!@#123ABCabc\u0000ÿ\r\n\t" converter.testByteString = true PASS converter.testByteString is "true" -PASS converter.testByteStringTreatReturnedNullStringAsNullAttribute is "true" -PASS converter.testByteStringTreatReturnedNullStringAsUndefinedAttribute is "true" -PASS converter.getTestByteStringTreatReturnedNullStringAsNullMethod() is "true" -PASS converter.getTestByteStringTreatReturnedNullStringAsUndefinedMethod() is "true" converter.testByteString = 123 PASS converter.testByteString is "123" -PASS converter.testByteStringTreatReturnedNullStringAsNullAttribute is "123" -PASS converter.testByteStringTreatReturnedNullStringAsUndefinedAttribute is "123" -PASS converter.getTestByteStringTreatReturnedNullStringAsNullMethod() is "123" -PASS converter.getTestByteStringTreatReturnedNullStringAsUndefinedMethod() is "123" converter.testByteString = null PASS converter.testByteString is "null" -PASS converter.testByteStringTreatReturnedNullStringAsNullAttribute is "null" -PASS converter.testByteStringTreatReturnedNullStringAsUndefinedAttribute is "null" -PASS converter.getTestByteStringTreatReturnedNullStringAsNullMethod() is "null" -PASS converter.getTestByteStringTreatReturnedNullStringAsUndefinedMethod() is "null" converter.testByteString = undefined PASS converter.testByteString is "undefined" -PASS converter.testByteStringTreatReturnedNullStringAsNullAttribute is "undefined" -PASS converter.testByteStringTreatReturnedNullStringAsUndefinedAttribute is "undefined" -PASS converter.getTestByteStringTreatReturnedNullStringAsNullMethod() is "undefined" -PASS converter.getTestByteStringTreatReturnedNullStringAsUndefinedMethod() is "undefined" PASS converter.setTestByteString('abc') did not throw exception. PASS converter.setTestByteStringDefaultNull('abc') did not throw exception. PASS converter.setTestByteString('\u0100') threw exception TypeError: Failed to execute 'setTestByteString' on 'TypeConversions': Value is not a valid ByteString.. @@ -1054,10 +1038,6 @@ PASS converter.setTestByteString() threw exception TypeError: Failed to execute 'setTestByteString' on 'TypeConversions': 1 argument required, but only 0 present.. PASS converter.setTestByteStringDefaultNull() did not throw exception. PASS converter.testByteString is "" -PASS converter.testByteStringTreatReturnedNullStringAsNullAttribute is null -PASS converter.testByteStringTreatReturnedNullStringAsUndefinedAttribute is undefined. -PASS converter.getTestByteStringTreatReturnedNullStringAsNullMethod() is null -PASS converter.getTestByteStringTreatReturnedNullStringAsUndefinedMethod() is undefined. converter.testUSVString = '!@#123ABCabc\x00\x80\xFF\r\n\t' PASS converter.testUSVString is "!@#123ABCabc\u0000ÿ\r\n\t" converter.testUSVString = '\u0100' @@ -1078,28 +1058,12 @@ PASS converter.testUSVString is "𝄞" converter.testUSVString = true PASS converter.testUSVString is "true" -PASS converter.testUSVStringTreatReturnedNullStringAsNullAttribute is "true" -PASS converter.testUSVStringTreatReturnedNullStringAsUndefinedAttribute is "true" -PASS converter.getTestUSVStringTreatReturnedNullStringAsNullMethod() is "true" -PASS converter.getTestUSVStringTreatReturnedNullStringAsUndefinedMethod() is "true" converter.testUSVString = 123 PASS converter.testUSVString is "123" -PASS converter.testUSVStringTreatReturnedNullStringAsNullAttribute is "123" -PASS converter.testUSVStringTreatReturnedNullStringAsUndefinedAttribute is "123" -PASS converter.getTestUSVStringTreatReturnedNullStringAsNullMethod() is "123" -PASS converter.getTestUSVStringTreatReturnedNullStringAsUndefinedMethod() is "123" converter.testUSVString = null PASS converter.testUSVString is "null" -PASS converter.testUSVStringTreatReturnedNullStringAsNullAttribute is "null" -PASS converter.testUSVStringTreatReturnedNullStringAsUndefinedAttribute is "null" -PASS converter.getTestUSVStringTreatReturnedNullStringAsNullMethod() is "null" -PASS converter.getTestUSVStringTreatReturnedNullStringAsUndefinedMethod() is "null" converter.testUSVString = undefined PASS converter.testUSVString is "undefined" -PASS converter.testUSVStringTreatReturnedNullStringAsNullAttribute is "undefined" -PASS converter.testUSVStringTreatReturnedNullStringAsUndefinedAttribute is "undefined" -PASS converter.getTestUSVStringTreatReturnedNullStringAsNullMethod() is "undefined" -PASS converter.getTestUSVStringTreatReturnedNullStringAsUndefinedMethod() is "undefined" PASS converter.setTestUSVString('abc') did not throw exception. PASS converter.setTestUSVStringDefaultNull('abc') did not throw exception. PASS converter.setTestUSVString('\u0100') did not throw exception. @@ -1107,10 +1071,6 @@ PASS converter.setTestUSVString() threw exception TypeError: Failed to execute 'setTestUSVString' on 'TypeConversions': 1 argument required, but only 0 present.. PASS converter.setTestUSVStringDefaultNull() did not throw exception. PASS converter.testUSVString is "" -PASS converter.testUSVStringTreatReturnedNullStringAsNullAttribute is null -PASS converter.testUSVStringTreatReturnedNullStringAsUndefinedAttribute is undefined. -PASS converter.getTestUSVStringTreatReturnedNullStringAsNullMethod() is null -PASS converter.getTestUSVStringTreatReturnedNullStringAsUndefinedMethod() is undefined. PASS successfullyParsed is true TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/fast/js/webidl-type-mapping.html b/third_party/WebKit/LayoutTests/fast/js/webidl-type-mapping.html index eaad0ff..68956e2f 100644 --- a/third_party/WebKit/LayoutTests/fast/js/webidl-type-mapping.html +++ b/third_party/WebKit/LayoutTests/fast/js/webidl-type-mapping.html
@@ -589,10 +589,6 @@ ["true", "123", "null", "undefined"].forEach(function(value) { evalAndLog("converter.testByteString = " + value); shouldBeEqualToString("converter.testByteString", value); - shouldBeEqualToString("converter.testByteStringTreatReturnedNullStringAsNullAttribute", value); - shouldBeEqualToString("converter.testByteStringTreatReturnedNullStringAsUndefinedAttribute", value); - shouldBeEqualToString("converter.getTestByteStringTreatReturnedNullStringAsNullMethod()", value); - shouldBeEqualToString("converter.getTestByteStringTreatReturnedNullStringAsUndefinedMethod()", value); }); shouldNotThrow("converter.setTestByteString('abc')"); shouldNotThrow("converter.setTestByteStringDefaultNull('abc')"); @@ -601,10 +597,6 @@ shouldThrow("converter.setTestByteString()"); shouldNotThrow("converter.setTestByteStringDefaultNull()"); shouldBeEqualToString("converter.testByteString", ""); -shouldBeNull("converter.testByteStringTreatReturnedNullStringAsNullAttribute"); -shouldBeUndefined("converter.testByteStringTreatReturnedNullStringAsUndefinedAttribute"); -shouldBeNull("converter.getTestByteStringTreatReturnedNullStringAsNullMethod()"); -shouldBeUndefined("converter.getTestByteStringTreatReturnedNullStringAsUndefinedMethod()"); evalAndLog("converter.testUSVString = '!@#123ABCabc\\x00\\x80\\xFF\\r\\n\\t'"); shouldBeEqualToString("converter.testUSVString", "!@#123ABCabc\x00\x80\xFF\r\n\t"); @@ -625,10 +617,6 @@ ["true", "123", "null", "undefined"].forEach(function(value) { evalAndLog("converter.testUSVString = " + value); shouldBeEqualToString("converter.testUSVString", value); - shouldBeEqualToString("converter.testUSVStringTreatReturnedNullStringAsNullAttribute", value); - shouldBeEqualToString("converter.testUSVStringTreatReturnedNullStringAsUndefinedAttribute", value); - shouldBeEqualToString("converter.getTestUSVStringTreatReturnedNullStringAsNullMethod()", value); - shouldBeEqualToString("converter.getTestUSVStringTreatReturnedNullStringAsUndefinedMethod()", value); }); shouldNotThrow("converter.setTestUSVString('abc')"); shouldNotThrow("converter.setTestUSVStringDefaultNull('abc')"); @@ -637,9 +625,5 @@ shouldThrow("converter.setTestUSVString()"); shouldNotThrow("converter.setTestUSVStringDefaultNull()"); shouldBeEqualToString("converter.testUSVString", ""); -shouldBeNull("converter.testUSVStringTreatReturnedNullStringAsNullAttribute"); -shouldBeUndefined("converter.testUSVStringTreatReturnedNullStringAsUndefinedAttribute"); -shouldBeNull("converter.getTestUSVStringTreatReturnedNullStringAsNullMethod()"); -shouldBeUndefined("converter.getTestUSVStringTreatReturnedNullStringAsUndefinedMethod()"); </script>
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-expected.txt b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-expected.txt index c8ab6b1..c7c9f43 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-expected.txt
@@ -3,12 +3,21 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". +PASS testExecutionOrderClosedConnection() did not throw exception. PASS pc = new webkitRTCPeerConnection(null, null); did not throw exception. +PASS error is "The RTCPeerConnection's signalingState is 'closed'." +PASS events is [1,2,3] PASS iceConnectionState is completed -PASS pc.addIceCandidate(null, addIceCandidateSuccess, addIceCandidateFailure); threw exception TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': parameter 1 is not of type 'RTCIceCandidate'.. -PASS pc.addIceCandidate(iceCandidate, null, addIceCandidateFailure); threw exception TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': The callback provided as parameter 2 is not a function.. -PASS pc.addIceCandidate(iceCandidate, addIceCandidateSuccess, null); threw exception TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': The callback provided as parameter 3 is not a function.. -PASS pc.addIceCandidate(iceCandidate, addIceCandidateSuccess, addIceCandidateFailure); did not throw exception. +PASS pc.addIceCandidate(null, addIceCandidateSuccess, unexpectedCallback).catch(expectedTypeError); did not throw exception. +PASS pc.addIceCandidate(iceCandidate, null, unexpectedCallback).catch(expectedTypeError); did not throw exception. +PASS pc.addIceCandidate(iceCandidate, addIceCandidateSuccess, null).catch(expectedTypeError); did not throw exception. +PASS pc.addIceCandidate(iceCandidate, addIceCandidateSuccess, unexpectedCallback); did not throw exception. +PASS TypeError is TypeError +PASS expectedTypeError was called. +PASS TypeError is TypeError +PASS expectedTypeError was called. +PASS TypeError is TypeError +PASS expectedTypeError was called. PASS addIceCandidateSuccess was called. PASS iceConnectionState is closed. PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-promise-expected.txt b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-promise-expected.txt new file mode 100644 index 0000000..de08478 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-promise-expected.txt
@@ -0,0 +1,17 @@ +Tests the RTCPeerConnection Ice functionality. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS pc = new webkitRTCPeerConnection(null, null); did not throw exception. +PASS iceConnectionState is completed +PASS pc.addIceCandidate(null).then(unexpectedSuccess, expectedTypeError); did not throw exception. +PASS pc.addIceCandidate(iceCandidate).then(addIceCandidateSuccess, addIceCandidateFailure); did not throw exception. +PASS error.name is "TypeError" +PASS expectedTypeError was called. +PASS addIceCandidateSuccess was called. +PASS iceConnectionState is closed. +PASS successfullyParsed is true + +TEST COMPLETE +
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-promise.html b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-promise.html new file mode 100644 index 0000000..b89bbbe4e --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice-promise.html
@@ -0,0 +1,64 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<script src="../../resources/js-test.js"></script> +</head> +<body> +<script> +description("Tests the RTCPeerConnection Ice functionality."); + +var pc = null; +var iceCandidate = null; + +function onIceChange2() +{ + if (pc.iceConnectionState === "closed") { + testPassed("iceConnectionState is closed."); + finishJSTest(); + } +} + +function addIceCandidateSuccess() +{ + testPassed("addIceCandidateSuccess was called."); + pc.oniceconnectionstatechange = onIceChange2; + pc.close(); +} + +function addIceCandidateFailure() +{ + testFailed("addIceCandidateFailure was called."); + finishJSTest(); +} + +function unexpectedSuccess() +{ + testFailed("unexpectedSuccess was called."); + finishJSTest(); +} + +function expectedTypeError(error) +{ + window.error = error; + shouldBe('error.name', '"TypeError"') + testPassed('expectedTypeError was called.') +} + +function onIceChange1() +{ + if (pc.iceConnectionState === "completed") { + testPassed("iceConnectionState is completed"); + iceCandidate = new RTCIceCandidate({candidate:"nano nano"}); + shouldNotThrow('pc.addIceCandidate(null).then(unexpectedSuccess, expectedTypeError);'); + shouldNotThrow('pc.addIceCandidate(iceCandidate).then(addIceCandidateSuccess, addIceCandidateFailure);'); + } +} + +shouldNotThrow('pc = new webkitRTCPeerConnection(null, null);'); +pc.oniceconnectionstatechange = onIceChange1; + +window.jsTestIsAsync = true; +window.successfullyParsed = true; +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice.html b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice.html index f144b9d..ef99d222 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice.html +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-ice.html
@@ -25,24 +25,50 @@ pc.close(); } -function addIceCandidateFailure() +function unexpectedCallback() { - testFailed("addIceCandidateFailue was called."); + testFailed("unexpectedCallback was called."); finishJSTest(); } +function expectedTypeError(error) +{ + shouldBe(error.name, "TypeError") + testPassed("expectedTypeError was called.") +} + function onIceChange1() { if (pc.iceConnectionState === "completed") { testPassed("iceConnectionState is completed"); iceCandidate = new RTCIceCandidate({candidate:"nano nano"}); - shouldThrow('pc.addIceCandidate(null, addIceCandidateSuccess, addIceCandidateFailure);'); - shouldThrow('pc.addIceCandidate(iceCandidate, null, addIceCandidateFailure);'); - shouldThrow('pc.addIceCandidate(iceCandidate, addIceCandidateSuccess, null);'); - shouldNotThrow('pc.addIceCandidate(iceCandidate, addIceCandidateSuccess, addIceCandidateFailure);'); + shouldNotThrow('pc.addIceCandidate(null, addIceCandidateSuccess, unexpectedCallback).catch(expectedTypeError);'); + shouldNotThrow('pc.addIceCandidate(iceCandidate, null, unexpectedCallback).catch(expectedTypeError);'); + shouldNotThrow('pc.addIceCandidate(iceCandidate, addIceCandidateSuccess, null).catch(expectedTypeError);'); + shouldNotThrow('pc.addIceCandidate(iceCandidate, addIceCandidateSuccess, unexpectedCallback);'); } } +function testExecutionOrderClosedConnection() +{ + var localPeerConnection = new webkitRTCPeerConnection(null, null); + localPeerConnection.close(); + var counter = 0; + events = []; + Promise.resolve().then(_ => events[counter++] = 1); + var iceCandidate = new RTCIceCandidate({candidate:"nano nano"}); + localPeerConnection.addIceCandidate(iceCandidate, unexpectedCallback, (error) => { + window.error = error; + shouldBe('error', '"The RTCPeerConnection\'s signalingState is \'closed\'."'); + events[counter++] = 2; + }); + Promise.resolve().then(_ => { + events[counter++] = 3; + shouldBe('events', '[1,2,3]'); + }); +} + +shouldNotThrow('testExecutionOrderClosedConnection()'); shouldNotThrow('pc = new webkitRTCPeerConnection(null, null);'); pc.oniceconnectionstatechange = onIceChange1;
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-expected.txt b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-expected.txt index 3e6b979..b7e77f7d 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-expected.txt
@@ -3,10 +3,17 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". -PASS pc.setLocalDescription(null) threw exception TypeError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': parameter 1 is not of type 'RTCSessionDescription'.. -PASS pc.setLocalDescription(sessionDescription, requestSucceeded1, requestFailed1); did not throw exception. +PASS testExecutionOrderClosedConnection() did not throw exception. +PASS pc.setLocalDescription().catch(expectedTypeError) did not throw exception. +PASS pc.setLocalDescription(null).catch(expectedInvalidSessionDescription) did not throw exception. +PASS pc.setLocalDescription(sessionDescription, requestSucceeded1, unexpectedCallback); did not throw exception. +PASS error is "The RTCPeerConnection's signalingState is 'closed'." +PASS events is [1,2,3] +PASS error.name is "TypeError" +PASS expectedTypeError was called. +PASS expectedInvalidSessionDescription was called. PASS requestSucceeded was called. -PASS pc.setLocalDescription(sessionDescription, requestSucceeded2, requestFailed2); did not throw exception. +PASS pc.setLocalDescription(sessionDescription, unexpectedCallback, requestFailed2); did not throw exception. PASS requestFailed was called. PASS pc.localDescription.type is "offer" PASS pc.localDescription.sdp is "local"
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-promise-expected.txt b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-promise-expected.txt new file mode 100644 index 0000000..1040b27d --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-promise-expected.txt
@@ -0,0 +1,23 @@ +Tests RTCPeerConnection localDescription. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS pc.setLocalDescription().catch(expectedTypeError) did not throw exception. +PASS pc.setLocalDescription(null).catch(expectedInvalidSessionDescription) did not throw exception. +PASS pc.setLocalDescription(sessionDescription).then(requestSucceeded1, requestFailed1); did not throw exception. +PASS error.name is "TypeError" +PASS expectedTypeError was called. +PASS error.name is "InvalidAccessError" +PASS expectedInvalidSessionDescription was called. +PASS requestSucceeded was called. +PASS pc.setLocalDescription(sessionDescription).then(requestSucceeded2, requestFailed2); did not throw exception. +PASS requestFailed was called. +PASS pc.localDescription.type is "offer" +PASS pc.localDescription.sdp is "local" +PASS pc.localDescription.type is "offer" +PASS pc.localDescription.sdp is "local" +PASS successfullyParsed is true + +TEST COMPLETE +
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-promise.html b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-promise.html new file mode 100644 index 0000000..16a4405 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription-promise.html
@@ -0,0 +1,69 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<script src="../../resources/js-test.js"></script> +</head> +<body> +<script> +description("Tests RTCPeerConnection localDescription."); + +var pc = null; + +function requestFailed2() +{ + testPassed('requestFailed was called.'); + + shouldBeEqualToString('pc.localDescription.type', "offer"); + shouldBeEqualToString('pc.localDescription.sdp', "local"); + pc.close(); + shouldBeEqualToString('pc.localDescription.type', "offer"); + shouldBeEqualToString('pc.localDescription.sdp', "local"); + + finishJSTest(); +} + +function requestSucceeded2() +{ + testFailed('requestSucceeded was called.'); + finishJSTest(); +} + +function requestFailed1() +{ + testFailed('requestFailed was called.'); + finishJSTest(); +} + +function requestSucceeded1() +{ + testPassed('requestSucceeded was called.'); + + sessionDescription = new RTCSessionDescription({type:"answer", sdp:"remote"}); + shouldNotThrow('pc.setLocalDescription(sessionDescription).then(requestSucceeded2, requestFailed2);'); +} + +function expectedTypeError(error) +{ + window.error = error; + shouldBe('error.name', '"TypeError"') + testPassed('expectedTypeError was called.') +} + +function expectedInvalidSessionDescription(error) +{ + window.error = error; + shouldBe('error.name', '"InvalidAccessError"') + testPassed('expectedInvalidSessionDescription was called.') +} + +pc = new webkitRTCPeerConnection(null, null); +shouldNotThrow('pc.setLocalDescription().catch(expectedTypeError)'); +shouldNotThrow('pc.setLocalDescription(null).catch(expectedInvalidSessionDescription)'); +var sessionDescription = new RTCSessionDescription({type:"offer", sdp:"local"}); +shouldNotThrow('pc.setLocalDescription(sessionDescription).then(requestSucceeded1, requestFailed1);'); + +window.jsTestIsAsync = true; +window.successfullyParsed = true; +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription.html b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription.html index a8987914..542a760 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription.html +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-localDescription.html
@@ -22,31 +22,57 @@ finishJSTest(); } -function requestSucceeded2() -{ - testFailed('requestSucceeded was called.'); - finishJSTest(); -} - -function requestFailed1() -{ - testFailed('requestFailed was called.'); - finishJSTest(); -} - function requestSucceeded1() { testPassed('requestSucceeded was called.'); sessionDescription = new RTCSessionDescription({type:"answer", sdp:"remote"}); - shouldNotThrow('pc.setLocalDescription(sessionDescription, requestSucceeded2, requestFailed2);'); + shouldNotThrow('pc.setLocalDescription(sessionDescription, unexpectedCallback, requestFailed2);'); } -pc = new webkitRTCPeerConnection(null, null); -shouldThrow('pc.setLocalDescription(null)'); -var sessionDescription = new RTCSessionDescription({type:"offer", sdp:"local"}); -shouldNotThrow('pc.setLocalDescription(sessionDescription, requestSucceeded1, requestFailed1);'); +function unexpectedCallback() +{ + testFailed('unexpectedCallback was called.'); + finishJSTest(); +} +function expectedTypeError(error) +{ + window.error = error; + shouldBe('error.name', '"TypeError"') + testPassed('expectedTypeError was called.') +} + +function expectedInvalidSessionDescription(error) +{ + testPassed('expectedInvalidSessionDescription was called.') +} + +function testExecutionOrderClosedConnection() +{ + var localPeerConnection = new webkitRTCPeerConnection(null, null); + localPeerConnection.close(); + var counter = 0; + events = []; + Promise.resolve().then(_ => events[counter++] = 1); + var sessionDescription = new RTCSessionDescription({type:"offer", sdp:"local"}); + localPeerConnection.setLocalDescription(sessionDescription, unexpectedCallback, (error) => { + window.error = error; + shouldBe('error', '"The RTCPeerConnection\'s signalingState is \'closed\'."'); + events[counter++] = 2; + }); + Promise.resolve().then(_ => { + events[counter++] = 3; + shouldBe('events', '[1,2,3]'); + }); +} + +shouldNotThrow('testExecutionOrderClosedConnection()'); +pc = new webkitRTCPeerConnection(null, null); +shouldNotThrow('pc.setLocalDescription().catch(expectedTypeError)'); +shouldNotThrow('pc.setLocalDescription(null).catch(expectedInvalidSessionDescription)'); +var sessionDescription = new RTCSessionDescription({type:"offer", sdp:"local"}); +shouldNotThrow('pc.setLocalDescription(sessionDescription, requestSucceeded1, unexpectedCallback);'); window.jsTestIsAsync = true; window.successfullyParsed = true;
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-expected.txt b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-expected.txt index 3cde3a8..6692f9b 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-expected.txt
@@ -3,10 +3,17 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". -PASS pc.setRemoteDescription(null) threw exception TypeError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': parameter 1 is not of type 'RTCSessionDescription'.. -PASS pc.setRemoteDescription(sessionDescription, requestSucceeded1, requestFailed1); did not throw exception. +PASS testExecutionOrderClosedConnection() did not throw exception. +PASS pc.setRemoteDescription().catch(expectedTypeError) did not throw exception. +PASS pc.setRemoteDescription(null).catch(expectedInvalidSessionDescription) did not throw exception. +PASS pc.setRemoteDescription(sessionDescription, requestSucceeded1, unexpectedCallback); did not throw exception. +PASS error is "The RTCPeerConnection's signalingState is 'closed'." +PASS events is [1,2,3] +PASS error.name is "TypeError" +PASS expectedTypeError was called. +PASS expectedInvalidSessionDescription was called. PASS requestSucceeded was called. -PASS pc.setRemoteDescription(sessionDescription, requestSucceeded2, requestFailed2); did not throw exception. +PASS pc.setRemoteDescription(sessionDescription, unexpectedCallback, requestFailed2); did not throw exception. PASS requestFailed was called. PASS pc.remoteDescription.type is "answer" PASS pc.remoteDescription.sdp is "remote"
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-promise-expected.txt b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-promise-expected.txt new file mode 100644 index 0000000..51b19ee --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-promise-expected.txt
@@ -0,0 +1,23 @@ +Tests RTCPeerConnection remoteDescription. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS pc.setRemoteDescription().catch(expectedTypeError) did not throw exception. +PASS pc.setRemoteDescription(null).catch(expectedInvalidSessionDescription) did not throw exception. +PASS pc.setRemoteDescription(sessionDescription).then(requestSucceeded1, requestFailed1); did not throw exception. +PASS error.name is "TypeError" +PASS expectedTypeError was called. +PASS error.name is "InvalidAccessError" +PASS expectedInvalidSessionDescription was called. +PASS requestSucceeded was called. +PASS pc.setRemoteDescription(sessionDescription).then(requestSucceeded2, requestFailed2); did not throw exception. +PASS requestFailed was called. +PASS pc.remoteDescription.type is "answer" +PASS pc.remoteDescription.sdp is "remote" +PASS pc.remoteDescription.type is "answer" +PASS pc.remoteDescription.sdp is "remote" +PASS successfullyParsed is true + +TEST COMPLETE +
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-promise.html b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-promise.html new file mode 100644 index 0000000..f526dd8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription-promise.html
@@ -0,0 +1,69 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<script src="../../resources/js-test.js"></script> +</head> +<body> +<script> +description("Tests RTCPeerConnection remoteDescription."); + +var pc = null; + +function requestFailed2() +{ + testPassed('requestFailed was called.'); + + shouldBeEqualToString('pc.remoteDescription.type', "answer"); + shouldBeEqualToString('pc.remoteDescription.sdp', "remote"); + pc.close(); + shouldBeEqualToString('pc.remoteDescription.type', "answer"); + shouldBeEqualToString('pc.remoteDescription.sdp', "remote"); + + finishJSTest(); +} + +function requestSucceeded2() +{ + testFailed('requestSucceeded was called.'); + finishJSTest(); +} + +function requestFailed1() +{ + testFailed('requestFailed was called.'); + finishJSTest(); +} + +function requestSucceeded1() +{ + testPassed('requestSucceeded was called.'); + + sessionDescription = new RTCSessionDescription({type:"offer", sdp:"local"}); + shouldNotThrow('pc.setRemoteDescription(sessionDescription).then(requestSucceeded2, requestFailed2);'); +} + +function expectedTypeError(error) +{ + window.error = error; + shouldBe('error.name', '"TypeError"') + testPassed('expectedTypeError was called.') +} + +function expectedInvalidSessionDescription(error) +{ + window.error = error; + shouldBe('error.name', '"InvalidAccessError"') + testPassed('expectedInvalidSessionDescription was called.') +} + +pc = new webkitRTCPeerConnection(null, null); +shouldNotThrow('pc.setRemoteDescription().catch(expectedTypeError)'); +shouldNotThrow('pc.setRemoteDescription(null).catch(expectedInvalidSessionDescription)'); +var sessionDescription = new RTCSessionDescription({type:"answer", sdp:"remote"}); +shouldNotThrow('pc.setRemoteDescription(sessionDescription).then(requestSucceeded1, requestFailed1);'); + +window.jsTestIsAsync = true; +window.successfullyParsed = true; +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription.html b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription.html index af224eb..19e46ec 100644 --- a/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription.html +++ b/third_party/WebKit/LayoutTests/fast/mediastream/RTCPeerConnection-remoteDescription.html
@@ -22,31 +22,57 @@ finishJSTest(); } -function requestSucceeded2() -{ - testFailed('requestSucceeded was called.'); - finishJSTest(); -} - -function requestFailed1() -{ - testFailed('requestFailed was called.'); - finishJSTest(); -} - function requestSucceeded1() { testPassed('requestSucceeded was called.'); sessionDescription = new RTCSessionDescription({type:"offer", sdp:"local"}); - shouldNotThrow('pc.setRemoteDescription(sessionDescription, requestSucceeded2, requestFailed2);'); + shouldNotThrow('pc.setRemoteDescription(sessionDescription, unexpectedCallback, requestFailed2);'); } -pc = new webkitRTCPeerConnection(null, null); -shouldThrow('pc.setRemoteDescription(null)'); -var sessionDescription = new RTCSessionDescription({type:"answer", sdp:"remote"}); -shouldNotThrow('pc.setRemoteDescription(sessionDescription, requestSucceeded1, requestFailed1);'); +function unexpectedCallback() +{ + testFailed('unexpectedCallback was called.'); + finishJSTest(); +} +function expectedTypeError(error) +{ + window.error = error; + shouldBe('error.name', '"TypeError"') + testPassed('expectedTypeError was called.') +} + +function expectedInvalidSessionDescription(error) +{ + testPassed('expectedInvalidSessionDescription was called.') +} + +function testExecutionOrderClosedConnection() +{ + var localPeerConnection = new webkitRTCPeerConnection(null, null); + localPeerConnection.close(); + var counter = 0; + events = []; + Promise.resolve().then(_ => events[counter++] = 1); + var sessionDescription = new RTCSessionDescription({type:"answer", sdp:"remote"}); + localPeerConnection.setRemoteDescription(sessionDescription, unexpectedCallback, (error) => { + window.error = error; + shouldBe('error', '"The RTCPeerConnection\'s signalingState is \'closed\'."'); + events[counter++] = 2; + }); + Promise.resolve().then(_ => { + events[counter++] = 3; + shouldBe('events', '[1,2,3]'); + }); +} + +shouldNotThrow('testExecutionOrderClosedConnection()'); +pc = new webkitRTCPeerConnection(null, null); +shouldNotThrow('pc.setRemoteDescription().catch(expectedTypeError)'); +shouldNotThrow('pc.setRemoteDescription(null).catch(expectedInvalidSessionDescription)'); +var sessionDescription = new RTCSessionDescription({type:"answer", sdp:"remote"}); +shouldNotThrow('pc.setRemoteDescription(sessionDescription, requestSucceeded1, unexpectedCallback);'); window.jsTestIsAsync = true; window.successfullyParsed = true;
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/nested-columns.html b/third_party/WebKit/LayoutTests/fast/multicol/nested-columns.html index deaa552d..3593fd3 100644 --- a/third_party/WebKit/LayoutTests/fast/multicol/nested-columns.html +++ b/third_party/WebKit/LayoutTests/fast/multicol/nested-columns.html
@@ -1,13 +1,13 @@ <!DOCTYPE html> <p>The word 'PASSED' should be seen below.</p> -<div style="width:6em; height:2em; -webkit-columns:3; -webkit-column-gap:0; column-fill:auto; line-height:2em;"> - <div style="-webkit-column-count:2; -webkit-column-gap:0;"> +<div style="width:6em; height:2em; columns:3; column-gap:0; column-fill:auto; line-height:2em;"> + <div style="column-count:2; column-gap:0;"> P<br>A </div> - <div style="-webkit-column-count:2; -webkit-column-gap:0;"> + <div style="column-count:2; column-gap:0;"> S<br>S </div> - <div style="-webkit-column-count:2; -webkit-column-gap:0;"> + <div style="column-count:2; column-gap:0;"> E<br>D </div> </div>
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/align-items-overflow-change.html b/third_party/WebKit/LayoutTests/fast/repaint/align-items-overflow-change.html index f0dc1a4..5cd5a744 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/align-items-overflow-change.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/align-items-overflow-change.html
@@ -12,7 +12,7 @@ } #container { display: grid; - grid: 200px / 150px 150px; + grid: 150px 150px / 200px; align-items: end unsafe; width: 200px; height: 300px;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/align-self-change-grid.html b/third_party/WebKit/LayoutTests/fast/repaint/align-self-change-grid.html index b4841975..e3125d4 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/align-self-change-grid.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/align-self-change-grid.html
@@ -13,7 +13,7 @@ } #container { display: grid; - grid: 100px 100px / 300px; + grid: 300px / 100px 100px; width: 200px; height: 300px; background-color: red;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/align-self-change-keeping-geometry-grid.html b/third_party/WebKit/LayoutTests/fast/repaint/align-self-change-keeping-geometry-grid.html index 7fd8fee..58eff80 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/align-self-change-keeping-geometry-grid.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/align-self-change-keeping-geometry-grid.html
@@ -12,7 +12,7 @@ } #container { display: grid; - grid: 100px / 300px; + grid: 300px / 100px; width: 200px; height: 300px; background-color: red;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/align-self-overflow-change.html b/third_party/WebKit/LayoutTests/fast/repaint/align-self-overflow-change.html index 04c5454..0cb0c08b 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/align-self-overflow-change.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/align-self-overflow-change.html
@@ -13,7 +13,7 @@ } #container { display: grid; - grid: 200px / 150px 150px; + grid: 150px 150px / 200px; width: 200px; height: 300px; background-color: red;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/justify-items-change.html b/third_party/WebKit/LayoutTests/fast/repaint/justify-items-change.html index 42b069c..4ad19e8e 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/justify-items-change.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/justify-items-change.html
@@ -12,7 +12,7 @@ } #container { display: grid; - grid: 200px / 300px; + grid: 300px / 200px; justify-items: end; width: 200px; height: 300px;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/justify-items-legacy-change.html b/third_party/WebKit/LayoutTests/fast/repaint/justify-items-legacy-change.html index f010740b..5637a51 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/justify-items-legacy-change.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/justify-items-legacy-change.html
@@ -19,7 +19,7 @@ #container { display: grid; - grid: 100px 100px / 150px 150px; + grid: 150px 150px / 100px 100px; width: 200px; height: 300px; background-color: red;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/justify-items-overflow-change.html b/third_party/WebKit/LayoutTests/fast/repaint/justify-items-overflow-change.html index 4178c7f..133ac992 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/justify-items-overflow-change.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/justify-items-overflow-change.html
@@ -12,7 +12,7 @@ } #container { display: grid; - grid: 100px 100px / 300px; + grid: 300px / 100px 100px; justify-items: end unsafe; width: 200px; height: 300px;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/justify-self-change-keeping-geometry.html b/third_party/WebKit/LayoutTests/fast/repaint/justify-self-change-keeping-geometry.html index 99d7562..9ba1345 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/justify-self-change-keeping-geometry.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/justify-self-change-keeping-geometry.html
@@ -13,7 +13,7 @@ } #container { display: grid; - grid: 100px 100px / 150px 150px; + grid: 150px 150px / 100px 100px; width: 200px; height: 300px; background-color: red;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/justify-self-change.html b/third_party/WebKit/LayoutTests/fast/repaint/justify-self-change.html index c9008050..214ef75a 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/justify-self-change.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/justify-self-change.html
@@ -13,7 +13,7 @@ } #container { display: grid; - grid: 100px 100px / 150px 150px; + grid: 150px 150px / 100px 100px; justify-items: center; width: 200px; height: 300px;
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/justify-self-overflow-change.html b/third_party/WebKit/LayoutTests/fast/repaint/justify-self-overflow-change.html index f4df8676..505e445 100644 --- a/third_party/WebKit/LayoutTests/fast/repaint/justify-self-overflow-change.html +++ b/third_party/WebKit/LayoutTests/fast/repaint/justify-self-overflow-change.html
@@ -13,7 +13,7 @@ } #container { display: grid; - grid: 100px 100px / 300px; + grid: 300px / 100px 100px; width: 200px; height: 300px; background-color: red;
diff --git a/third_party/WebKit/LayoutTests/fast/text/emoji-font-fallback-mac-expected.html b/third_party/WebKit/LayoutTests/fast/text/emoji-font-fallback-mac-expected.html new file mode 100644 index 0000000..3001bdcb --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/text/emoji-font-fallback-mac-expected.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>'GRINNING FACE WITH SMILING EYES' (U+1F601)</title> + </head> + <body> + <p style="font-family: Apple Color Emoji;">😁😁😜</p> + <p>There should be three smiling emojis above.</p> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/text/emoji-font-fallback-mac.html b/third_party/WebKit/LayoutTests/fast/text/emoji-font-fallback-mac.html new file mode 100644 index 0000000..3e330b4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/text/emoji-font-fallback-mac.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>'GRINNING FACE WITH SMILING EYES' (U+1F601)</title> + </head> + <body> + <p>😁😁😜</p> + <p>There should be three smiling emojis above.</p> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/text/emoji-font-weight-mac-expected.html b/third_party/WebKit/LayoutTests/fast/text/emoji-font-weight-mac-expected.html new file mode 100644 index 0000000..0c895cc --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/text/emoji-font-weight-mac-expected.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>'GRINNING FACE WITH SMILING EYES' (U+1F601)</title> + </head> + <body> + <h1 style="font-weight: normal; font-family: Apple Color Emoji;">😁😁😜</h1> + <p>There should be three giant smiling emojis above.</p> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/text/emoji-font-weight-mac.html b/third_party/WebKit/LayoutTests/fast/text/emoji-font-weight-mac.html new file mode 100644 index 0000000..9c590891 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/text/emoji-font-weight-mac.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>'GRINNING FACE WITH SMILING EYES' (U+1F601)</title> + </head> + <body> + <h1 style="font-weight: bold;">😁😁😜</h1> + <p>There should be three giant smiling emojis above.</p> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/fast/writing-mode/orthogonal-writing-modes-scrollbarpart-crash-expected.txt b/third_party/WebKit/LayoutTests/fast/writing-mode/orthogonal-writing-modes-scrollbarpart-crash-expected.txt new file mode 100644 index 0000000..7e24c80 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/writing-mode/orthogonal-writing-modes-scrollbarpart-crash-expected.txt
@@ -0,0 +1,3 @@ +Test passes if it does not crash. + +
diff --git a/third_party/WebKit/LayoutTests/fast/writing-mode/orthogonal-writing-modes-scrollbarpart-crash.html b/third_party/WebKit/LayoutTests/fast/writing-mode/orthogonal-writing-modes-scrollbarpart-crash.html new file mode 100644 index 0000000..2208c42 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/writing-mode/orthogonal-writing-modes-scrollbarpart-crash.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<style> +button { + overflow-y: auto; + resize: auto; +} +button::-webkit-resizer { + writing-mode: vertical-rl; +} +</style> +<p>Test passes if it does not crash.</p> +<button></button> +<script> +if (window.testRunner) + testRunner.dumpAsText(); +</script>
diff --git a/third_party/WebKit/LayoutTests/fragmentation/README.txt b/third_party/WebKit/LayoutTests/fragmentation/README.txt new file mode 100644 index 0000000..806899c4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fragmentation/README.txt
@@ -0,0 +1,7 @@ +This directory contains generic tests for fragmentation. + +Spec: https://www.w3.org/TR/2016/CR-css-break-3-20160114/ + +Actual layout tests here will typically use multicol, paged overflow or printing to test some +fragmentation behavior, but they should not test anything pertaining to a specific fragmentation +type. Such specific tests should go in printing/ , fast/pagination/ or fast/multicol/ .
diff --git a/third_party/WebKit/LayoutTests/fragmentation/break-properties-expected.txt b/third_party/WebKit/LayoutTests/fragmentation/break-properties-expected.txt new file mode 100644 index 0000000..8f4bdcf --- /dev/null +++ b/third_party/WebKit/LayoutTests/fragmentation/break-properties-expected.txt
@@ -0,0 +1,194 @@ +Test parsing and getComputedStyle behavior for break controlling properties + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +break-after:always +PASS getComputedStyle(element)['break-after'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "auto" +break-after:auto +PASS getComputedStyle(element)['break-after'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "auto" +break-after:avoid +PASS getComputedStyle(element)['break-after'] is "avoid" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "avoid" +PASS getComputedStyle(element)['page-break-after'] is "avoid" +break-after:avoid-column +PASS getComputedStyle(element)['break-after'] is "avoid-column" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "avoid" +PASS getComputedStyle(element)['page-break-after'] is "auto" +break-after:avoid-page +PASS getComputedStyle(element)['break-after'] is "avoid-page" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "avoid" +break-after:column +PASS getComputedStyle(element)['break-after'] is "column" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "always" +PASS getComputedStyle(element)['page-break-after'] is "auto" +break-after:left +PASS getComputedStyle(element)['break-after'] is "left" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "always" +break-after:page +PASS getComputedStyle(element)['break-after'] is "page" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "always" +break-after:recto +PASS getComputedStyle(element)['break-after'] is "recto" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "auto" +break-after:right +PASS getComputedStyle(element)['break-after'] is "right" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "always" +break-after:verso +PASS getComputedStyle(element)['break-after'] is "verso" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "auto" +break-before:always +PASS getComputedStyle(element)['break-before'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "auto" +break-before:auto +PASS getComputedStyle(element)['break-before'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "auto" +break-before:avoid +PASS getComputedStyle(element)['break-before'] is "avoid" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "avoid" +PASS getComputedStyle(element)['page-break-before'] is "avoid" +break-before:avoid-column +PASS getComputedStyle(element)['break-before'] is "avoid-column" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "avoid" +PASS getComputedStyle(element)['page-break-before'] is "auto" +break-before:avoid-page +PASS getComputedStyle(element)['break-before'] is "avoid-page" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "avoid" +break-before:column +PASS getComputedStyle(element)['break-before'] is "column" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "always" +PASS getComputedStyle(element)['page-break-before'] is "auto" +break-before:left +PASS getComputedStyle(element)['break-before'] is "left" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "always" +break-before:page +PASS getComputedStyle(element)['break-before'] is "page" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "always" +break-before:recto +PASS getComputedStyle(element)['break-before'] is "recto" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "auto" +break-before:right +PASS getComputedStyle(element)['break-before'] is "right" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "always" +break-before:verso +PASS getComputedStyle(element)['break-before'] is "verso" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "auto" +break-inside:auto +PASS getComputedStyle(element)['break-inside'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-inside'] is "auto" +PASS getComputedStyle(element)['page-break-inside'] is "auto" +break-inside:avoid +PASS getComputedStyle(element)['break-inside'] is "avoid" +PASS getComputedStyle(element)['-webkit-column-break-inside'] is "avoid" +PASS getComputedStyle(element)['page-break-inside'] is "avoid" +break-inside:avoid-column +PASS getComputedStyle(element)['break-inside'] is "avoid-column" +PASS getComputedStyle(element)['-webkit-column-break-inside'] is "avoid" +PASS getComputedStyle(element)['page-break-inside'] is "auto" +break-inside:avoid-page +PASS getComputedStyle(element)['break-inside'] is "avoid-page" +PASS getComputedStyle(element)['-webkit-column-break-inside'] is "auto" +PASS getComputedStyle(element)['page-break-inside'] is "avoid" +-webkit-column-break-after:always +PASS getComputedStyle(element)['break-after'] is "column" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "always" +PASS getComputedStyle(element)['page-break-after'] is "auto" +-webkit-column-break-after:avoid +PASS getComputedStyle(element)['break-after'] is "avoid-column" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "avoid" +PASS getComputedStyle(element)['page-break-after'] is "auto" +-webkit-column-break-after:column +PASS getComputedStyle(element)['break-after'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "auto" +-webkit-column-break-before:always +PASS getComputedStyle(element)['break-before'] is "column" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "always" +PASS getComputedStyle(element)['page-break-before'] is "auto" +-webkit-column-break-before:avoid +PASS getComputedStyle(element)['break-before'] is "avoid-column" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "avoid" +PASS getComputedStyle(element)['page-break-before'] is "auto" +-webkit-column-break-before:column +PASS getComputedStyle(element)['break-before'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "auto" +-webkit-column-break-inside:avoid +PASS getComputedStyle(element)['break-inside'] is "avoid-column" +PASS getComputedStyle(element)['-webkit-column-break-inside'] is "avoid" +PASS getComputedStyle(element)['page-break-inside'] is "auto" +-webkit-column-break-before:avoid-column +PASS getComputedStyle(element)['break-before'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "auto" +page-break-after:always +PASS getComputedStyle(element)['break-after'] is "page" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "always" +page-break-after:avoid +PASS getComputedStyle(element)['break-after'] is "avoid-page" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "avoid" +page-break-after:left +PASS getComputedStyle(element)['break-after'] is "left" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "always" +page-break-after:right +PASS getComputedStyle(element)['break-after'] is "right" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "always" +page-break-after:verso +PASS getComputedStyle(element)['break-after'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-after'] is "auto" +PASS getComputedStyle(element)['page-break-after'] is "auto" +page-break-before:always +PASS getComputedStyle(element)['break-before'] is "page" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "always" +page-break-before:avoid +PASS getComputedStyle(element)['break-before'] is "avoid-page" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "avoid" +page-break-before:left +PASS getComputedStyle(element)['break-before'] is "left" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "always" +page-break-before:right +PASS getComputedStyle(element)['break-before'] is "right" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "always" +page-break-before:verso +PASS getComputedStyle(element)['break-before'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "auto" +page-break-inside:always +PASS getComputedStyle(element)['break-before'] is "auto" +PASS getComputedStyle(element)['-webkit-column-break-before'] is "auto" +PASS getComputedStyle(element)['page-break-before'] is "auto" +page-break-inside:avoid +PASS getComputedStyle(element)['break-inside'] is "avoid-page" +PASS getComputedStyle(element)['-webkit-column-break-inside'] is "auto" +PASS getComputedStyle(element)['page-break-inside'] is "avoid" + +PASS successfullyParsed is true + +TEST COMPLETE +
diff --git a/third_party/WebKit/LayoutTests/fragmentation/break-properties.html b/third_party/WebKit/LayoutTests/fragmentation/break-properties.html new file mode 100644 index 0000000..c051c107 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fragmentation/break-properties.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<div id="element"></div> +<script src="../resources/js-test.js"></script> +<script> +description("Test parsing and getComputedStyle behavior for break controlling properties"); + +var tests = [["break-after:always", ["break-after", "auto"], ["-webkit-column-break-after", "auto"], ["page-break-after", "auto"]], // Invalid declaration + ["break-after:auto", ["break-after", "auto"], ["-webkit-column-break-after", "auto"], ["page-break-after", "auto"]], + ["break-after:avoid", ["break-after", "avoid"], ["-webkit-column-break-after", "avoid"], ["page-break-after", "avoid"]], + ["break-after:avoid-column", ["break-after", "avoid-column"], ["-webkit-column-break-after", "avoid"], ["page-break-after", "auto"]], + ["break-after:avoid-page", ["break-after", "avoid-page"], ["-webkit-column-break-after", "auto"], ["page-break-after", "avoid"]], + ["break-after:column", ["break-after", "column"], ["-webkit-column-break-after", "always"], ["page-break-after", "auto"]], + ["break-after:left", ["break-after", "left"], ["-webkit-column-break-after", "auto"], ["page-break-after", "always"]], + ["break-after:page", ["break-after", "page"], ["-webkit-column-break-after", "auto"], ["page-break-after", "always"]], + ["break-after:recto", ["break-after", "recto"], ["-webkit-column-break-after", "auto"], ["page-break-after", "auto"]], + ["break-after:right", ["break-after", "right"], ["-webkit-column-break-after", "auto"], ["page-break-after", "always"]], + ["break-after:verso", ["break-after", "verso"], ["-webkit-column-break-after", "auto"], ["page-break-after", "auto"]], + ["break-before:always", ["break-before", "auto"], ["-webkit-column-break-before", "auto"], ["page-break-before", "auto"]], // Invalid declaration + ["break-before:auto", ["break-before", "auto"], ["-webkit-column-break-before", "auto"], ["page-break-before", "auto"]], + ["break-before:avoid", ["break-before", "avoid"], ["-webkit-column-break-before", "avoid"], ["page-break-before", "avoid"]], + ["break-before:avoid-column", ["break-before", "avoid-column"], ["-webkit-column-break-before", "avoid"], ["page-break-before", "auto"]], + ["break-before:avoid-page", ["break-before", "avoid-page"], ["-webkit-column-break-before", "auto"], ["page-break-before", "avoid"]], + ["break-before:column", ["break-before", "column"], ["-webkit-column-break-before", "always"], ["page-break-before", "auto"]], + ["break-before:left", ["break-before", "left"], ["-webkit-column-break-before", "auto"], ["page-break-before", "always"]], + ["break-before:page", ["break-before", "page"], ["-webkit-column-break-before", "auto"], ["page-break-before", "always"]], + ["break-before:recto", ["break-before", "recto"], ["-webkit-column-break-before", "auto"], ["page-break-before", "auto"]], + ["break-before:right", ["break-before", "right"], ["-webkit-column-break-before", "auto"], ["page-break-before", "always"]], + ["break-before:verso", ["break-before", "verso"], ["-webkit-column-break-before", "auto"], ["page-break-before", "auto"]], + ["break-inside:auto", ["break-inside", "auto"], ["-webkit-column-break-inside", "auto"], ["page-break-inside", "auto"]], + ["break-inside:avoid", ["break-inside", "avoid"], ["-webkit-column-break-inside", "avoid"], ["page-break-inside", "avoid"]], + ["break-inside:avoid-column", ["break-inside", "avoid-column"], ["-webkit-column-break-inside", "avoid"], ["page-break-inside", "auto"]], + ["break-inside:avoid-page", ["break-inside", "avoid-page"], ["-webkit-column-break-inside", "auto"], ["page-break-inside", "avoid"]], + ["-webkit-column-break-after:always", ["break-after", "column"], ["-webkit-column-break-after", "always"], ["page-break-after", "auto"]], + ["-webkit-column-break-after:avoid", ["break-after", "avoid-column"], ["-webkit-column-break-after", "avoid"], ["page-break-after", "auto"]], + ["-webkit-column-break-after:column", ["break-after", "auto"], ["-webkit-column-break-after", "auto"], ["page-break-after", "auto"]], // Invalid declaration + ["-webkit-column-break-before:always", ["break-before", "column"], ["-webkit-column-break-before", "always"], ["page-break-before", "auto"]], + ["-webkit-column-break-before:avoid", ["break-before", "avoid-column"], ["-webkit-column-break-before", "avoid"], ["page-break-before", "auto"]], + ["-webkit-column-break-before:column", ["break-before", "auto"], ["-webkit-column-break-before", "auto"], ["page-break-before", "auto"]], // Invalid declaration + ["-webkit-column-break-inside:avoid", ["break-inside", "avoid-column"], ["-webkit-column-break-inside", "avoid"], ["page-break-inside", "auto"]], + ["-webkit-column-break-before:avoid-column", ["break-before", "auto"], ["-webkit-column-break-before", "auto"], ["page-break-before", "auto"]], // Invalid declaration + ["page-break-after:always", ["break-after", "page"], ["-webkit-column-break-after", "auto"], ["page-break-after", "always"]], + ["page-break-after:avoid", ["break-after", "avoid-page"], ["-webkit-column-break-after", "auto"], ["page-break-after", "avoid"]], + ["page-break-after:left", ["break-after", "left"], ["-webkit-column-break-after", "auto"], ["page-break-after", "always"]], + ["page-break-after:right", ["break-after", "right"], ["-webkit-column-break-after", "auto"], ["page-break-after", "always"]], + ["page-break-after:verso", ["break-after", "auto"], ["-webkit-column-break-after", "auto"], ["page-break-after", "auto"]], // Invalid declaration + ["page-break-before:always", ["break-before", "page"], ["-webkit-column-break-before", "auto"], ["page-break-before", "always"]], + ["page-break-before:avoid", ["break-before", "avoid-page"], ["-webkit-column-break-before", "auto"], ["page-break-before", "avoid"]], + ["page-break-before:left", ["break-before", "left"], ["-webkit-column-break-before", "auto"], ["page-break-before", "always"]], + ["page-break-before:right", ["break-before", "right"], ["-webkit-column-break-before", "auto"], ["page-break-before", "always"]], + ["page-break-before:verso", ["break-before", "auto"], ["-webkit-column-break-before", "auto"], ["page-break-before", "auto"]], // Invalid declaration + ["page-break-inside:always", ["break-before", "auto"], ["-webkit-column-break-before", "auto"], ["page-break-before", "auto"]], // Invalid declaration + ["page-break-inside:avoid", ["break-inside", "avoid-page"], ["-webkit-column-break-inside", "auto"], ["page-break-inside", "avoid"]], + [""]]; + +tests.forEach(function(test) { + debug(test[0]); + element.style.cssText = test[0]; + for (var i = 1; i < test.length; i++) + shouldBeEqualToString("getComputedStyle(element)['"+test[i][0]+"']", test[i][1]); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html index 8f24fe2e..2aad940 100644 --- a/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html +++ b/third_party/WebKit/LayoutTests/http/tests/credentialmanager/credentialscontainer-get-basics.html
@@ -49,7 +49,7 @@ t.step(function () { navigator.credentials.get({ password: true, - suppressUI: true + unmediated: true }).then( t.step_func(stubResolverUndefinedChecker.bind(t)), t.step_func(stubRejectionChecker.bind(t))); @@ -63,7 +63,7 @@ federated: { providers: [ 'https://example.com/' ] }, - suppressUI: true + unmediated: true }).then( t.step_func(stubResolverUndefinedChecker.bind(t)), t.step_func(stubRejectionChecker.bind(t))); @@ -78,7 +78,7 @@ federated: { providers: [ 'https://example.com/' ] }, - suppressUI: true + unmediated: true }).then( t.step_func(stubResolverUndefinedChecker.bind(t)), t.step_func(stubRejectionChecker.bind(t))); @@ -89,7 +89,7 @@ var t = async_test("Verify the basics of get()."); t.step(function () { navigator.credentials.get({ - suppressUI: true + unmediated: true }).then( t.step_func(stubResolverUndefinedChecker.bind(t)), t.step_func(stubRejectionChecker.bind(t)));
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/css-protocol-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/css-protocol-test.js index 86666ae..746727e 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/css-protocol-test.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/css-protocol-test.js
@@ -12,9 +12,10 @@ } } -function updateStyleSheetRange(command, styleSheetId, expectError, options, callback) +function modifyStyleSheet(command, presetStyleSheetId, styleSheetId, expectError, options, callback) { - options.styleSheetId = styleSheetId; + if (presetStyleSheetId) + options.styleSheetId = styleSheetId; if (expectError) InspectorTest.sendCommand(command, options, onResponse); else @@ -37,11 +38,15 @@ } } -InspectorTest.setPropertyText = updateStyleSheetRange.bind(null, "CSS.setPropertyText"); -InspectorTest.setRuleSelector = updateStyleSheetRange.bind(null, "CSS.setRuleSelector"); -InspectorTest.setStyleText = updateStyleSheetRange.bind(null, "CSS.setStyleText"); -InspectorTest.setMediaText = updateStyleSheetRange.bind(null, "CSS.setMediaText"); -InspectorTest.addRule = updateStyleSheetRange.bind(null, "CSS.addRule"); +InspectorTest.setPropertyText = modifyStyleSheet.bind(null, "CSS.setPropertyText", true); +InspectorTest.setRuleSelector = modifyStyleSheet.bind(null, "CSS.setRuleSelector", true); +InspectorTest.setMediaText = modifyStyleSheet.bind(null, "CSS.setMediaText", true); +InspectorTest.addRule = modifyStyleSheet.bind(null, "CSS.addRule", true); +InspectorTest.setStyleTexts = function(styleSheetId, expectError, edits, callback) +{ + var options = { edits: edits }; + modifyStyleSheet("CSS.setStyleTexts", false, styleSheetId, expectError, options, callback); +} InspectorTest.requestMainFrameId = function(callback) {
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/debugger-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/debugger-test.js index 16661d70..817cc3d5 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector/debugger-test.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector/debugger-test.js
@@ -511,7 +511,7 @@ var lineCount = source.lineEndings().length; var endLine = startLine + lineCount - 1; var endColumn = lineCount === 1 ? startColumn + source.length : source.length - source.lineEndings()[lineCount - 2]; - var hasSourceURL = !!source.match(/\/\/#\ssourceURL=\s*(\S*?)\s*$/m); + var hasSourceURL = !!source.match(/\/\/#\ssourceURL=\s*(\S*?)\s*$/m) || !!source.match(/\/\/@\ssourceURL=\s*(\S*?)\s*$/m); var script = new WebInspector.Script(debuggerModel, scriptId, url, startLine, startColumn, endLine, endColumn, 0, isContentScript, false, false, undefined, hasSourceURL); script.requestContent = function() {
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/resources/selector-line-deprecated.css b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/resources/selector-line-deprecated.css index b1c3454..6a8fc7c 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/resources/selector-line-deprecated.css +++ b/third_party/WebKit/LayoutTests/http/tests/inspector/elements/styles/resources/selector-line-deprecated.css
@@ -3,4 +3,4 @@ #container #inspected { color: green; } -/*# sourceMappingURL=selector-line.css.map */ +/*@ sourceMappingURL=selector-line.css.map */
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scripthash-handler-allowed.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scripthash-handler-allowed.html new file mode 100644 index 0000000..b73cc17 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/scripthash-handler-allowed.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> + <head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + async_test(function (t) { + window.expectSuccess = t.step_func_done(function (el) { + assert_equals(el, document.querySelector('#pass')); + }); + window.addEventListener('load', t.step_func(function () { + document.querySelector('#pass').click(); + })); + }, 'Inline event handlers whitelisted by the policy should fire.'); + + async_test(function (t) { + window.expectFailure = t.unreached_func("Handler should not execute."); + document.addEventListener('securitypolicyviolation', t.step_func_done(function (e) { + assert_equals(e.target, document); + })); + window.addEventListener('load', t.step_func(function () { + document.querySelector('#fail').click(); + })); + }, 'Inline event handlers not whitelisted by the policy should generate error events.'); + </script> + + <meta http-equiv="Content-Security-Policy" content="script-src 'sha256-nhtYaXCssBJTThiDLYewspQYue9tisulEwJ3nTJKcMI='"> + </head> + <body> + <button id="pass" onclick="expectSuccess(this)"></button> + <button id="fail" onclick="expectFailure(this)"></button> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/worklet/import.html b/third_party/WebKit/LayoutTests/http/tests/worklet/import.html new file mode 100644 index 0000000..3e44c5ff --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/worklet/import.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> + <script src="../resources/testharness.js"></script> + <script src="../resources/testharnessreport.js"></script> +</head> +<body> +<script> + promise_test(function() { + + return renderWorklet.import('resources/empty-worklet-script.js').then(function(undefined_arg) { + assert_equals(undefined_arg, undefined, 'Promise should resolve with no arguments.'); + }).catch(function(error) { + assert_unreached('unexpected rejection: ' + error); + }); + + }, 'Importing a script resolves the given promise.'); + + promise_test(function() { + + return renderWorklet.import('resources/throwing-worklet-script.js').then(function(undefined_arg) { + assert_equals(undefined_arg, undefined, 'Promise should resolve with no arguments.'); + }).catch(function(error) { + assert_unreached('unexpected rejection: ' + error); + }); + + }, 'Importing a script which throws should still resolve the given promise.'); + + promise_test(function() { + + return renderWorklet.import('non-existant-worklet-script.js').then(function() { + assert_unreached('import should fail.'); + }).catch(function(error) { + assert_equals(error.name, 'NetworkError', 'error should be a NetworkError.'); + }); + + }, 'Importing a non-existant script rejects the given promise with a NetworkError.'); + + promise_test(function() { + + return renderWorklet.import('http://invalid:123$').then(function() { + assert_unreached('import should fail.'); + }).catch(function(error) { + assert_equals(error.name, 'SyntaxError', 'error should be a SyntaxError.'); + }); + + }, 'Attempting to resolve an invalid URL should reject the given promise with a SyntaxError.'); + +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/worklet/resources/empty-worklet-script.js b/third_party/WebKit/LayoutTests/http/tests/worklet/resources/empty-worklet-script.js new file mode 100644 index 0000000..49ceb264 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/worklet/resources/empty-worklet-script.js
@@ -0,0 +1 @@ +// Do nothing.
diff --git a/third_party/WebKit/LayoutTests/http/tests/worklet/resources/throwing-worklet-script.js b/third_party/WebKit/LayoutTests/http/tests/worklet/resources/throwing-worklet-script.js new file mode 100644 index 0000000..6417d6c --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/worklet/resources/throwing-worklet-script.js
@@ -0,0 +1 @@ +throw Error();
diff --git a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-preflight-data-saver-expected.txt b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-preflight-data-saver-expected.txt new file mode 100644 index 0000000..a052ea4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-preflight-data-saver-expected.txt
@@ -0,0 +1,4 @@ +Test that 'Save-Data' header is treated as a simple header and preflight request is not created. Should print PASS. + +PASS +
diff --git a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-preflight-data-saver.html b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-preflight-data-saver.html new file mode 100644 index 0000000..e8cbda9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-preflight-data-saver.html
@@ -0,0 +1,32 @@ +<html> +<body> +<p>Test that 'Save-Data' header is treated as a simple header and preflight request is not created. Should print PASS.</p> +<div id="log"></div> +<script> +window.internals.settings.setDataSaverEnabled(true); + +function log(message) { + document.getElementById("log").innerHTML += message + "<br>"; +} + +if (window.layoutTestController) { + layoutTestController.dumpAsText(); + layoutTestController.waitUntilDone(); +} + +var xhr = new XMLHttpRequest(); +xhr.open("GET", "http://localhost:8000/xmlhttprequest/resources/access-control-preflight-request-invalid-status.php?code=400"); +xhr.onerror = function () { + log("FAIL"); + if (window.layoutTestController) + layoutTestController.notifyDone(); +}; +xhr.onload = function () { + log("PASS"); + if (window.layoutTestController) + layoutTestController.notifyDone(); +}; +xhr.send(); +</script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001a-expected.html new file mode 100644 index 0000000..c08a971 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001a-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .tealBlock { + background: teal; + width: 20px; + height: 20px; + margin-bottom: 5px; + } + </style> +</head> +<body> + <div class="tealBlock"></div> + <div class="tealBlock"></div> + <div class="tealBlock"></div> + <div class="tealBlock"></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001a.html new file mode 100644 index 0000000..6c91c67 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001a.html
@@ -0,0 +1,54 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing that "min-width", "max-width", "min-height", and "max-height" are applied on absolutely positioned children of a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#abspos-items"> + <link rel="match" href="flexbox-abspos-child-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + height: 10px; + width: 10px; + background: purple; + margin-bottom: 15px; + position: relative; + } + .absPos { + position: absolute; + background: teal; + } + .testMinWidth { + width: 10px; + height: 20px; + min-width: 20px; + } + .testMaxWidth { + width: 50px; + height: 20px; + max-width: 20px; + } + .testMinHeight { + width: 20px; + height: 10px; + min-height: 20px; + } + .testMaxHeight { + width: 20px; + height: 50px; + max-height: 20px; + } + </style> +</head> +<body> + <div class="flexContainer"><div class="absPos testMinWidth"></div></div> + <div class="flexContainer"><div class="absPos testMaxWidth"></div></div> + <div class="flexContainer"><div class="absPos testMinHeight"></div></div> + <div class="flexContainer"><div class="absPos testMaxHeight"></div></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001b-expected.html new file mode 100644 index 0000000..c08a971 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001b-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .tealBlock { + background: teal; + width: 20px; + height: 20px; + margin-bottom: 5px; + } + </style> +</head> +<body> + <div class="tealBlock"></div> + <div class="tealBlock"></div> + <div class="tealBlock"></div> + <div class="tealBlock"></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001b.html new file mode 100644 index 0000000..55ee5f4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001b.html
@@ -0,0 +1,55 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing that "min-width", "max-width", "min-height", and "max-height" are applied on absolutely positioned children of a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#abspos-items"> + <link rel="match" href="flexbox-abspos-child-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + flex-direction: column; + height: 10px; + width: 10px; + background: purple; + margin-bottom: 15px; + position: relative; + } + .absPos { + position: absolute; + background: teal; + } + .testMinWidth { + width: 10px; + height: 20px; + min-width: 20px; + } + .testMaxWidth { + width: 50px; + height: 20px; + max-width: 20px; + } + .testMinHeight { + width: 20px; + height: 10px; + min-height: 20px; + } + .testMaxHeight { + width: 20px; + height: 50px; + max-height: 20px; + } + </style> +</head> +<body> + <div class="flexContainer"><div class="absPos testMinWidth"></div></div> + <div class="flexContainer"><div class="absPos testMaxWidth"></div></div> + <div class="flexContainer"><div class="absPos testMinHeight"></div></div> + <div class="flexContainer"><div class="absPos testMaxHeight"></div></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a-expected.xhtml new file mode 100644 index 0000000..d4291caf --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a-expected.xhtml
@@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 20px; + height: 200px; + margin-right: 2px; + float: left; + background: lightgray; + } + div.a { + width: 20px; + height: 10px; + background: lightgreen; + } + div.b { + width: 20px; + height: 30px; + background: pink; + } + div.c { + width: 20px; + height: 40px; + background: orange; + } + + /* Inside of 'b': */ + div.fixedSizeChild { + width: 10px; + height: 30px; + background: purple; + } + </style> + </head> + <body> + + <!-- default (stretch) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-top:80px; height:110px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-top:40px; height: 70px"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 160px"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 120px"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 80px"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 60px"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-top: 160px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-top: 60px"><div class="fixedSizeChild"/></div> + <div class="c" style="margin-top: 60px"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 40px"/> + <div class="b" style="margin-top: 80px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 20px"/> + <div class="b" style="margin-top: 40px"><div class="fixedSizeChild"/></div> + <div class="c" style="margin-top: 40px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a.xhtml new file mode 100644 index 0000000..ca52f3a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a.xhtml
@@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of horizontal flex containers, with 1-3 flex lines, + testing each possible value of the 'align-content' property. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'align-content' in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property"/> + <link rel="match" href="flexbox-align-content-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 20px; /* Skinny, to force us to wrap */ + height: 200px; + display: flex; + flex-wrap: wrap; + margin-right: 2px; + float: left; + background: lightgray; + } + div.a { + width: 20px; + height: 10px; + flex: none; + background: lightgreen; + } + div.b { + width: 20px; + height: auto; /* height comes from contents */ + flex: none; + background: pink; + } + div.c { + width: 20px; + height: 40px; + flex: none; + background: orange; + } + + /* Inside of 'b': */ + div.fixedSizeChild { + width: 10px; + height: 30px; + background: purple; + } + </style> + </head> + <body> + + <!-- default (stretch) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b-expected.xhtml new file mode 100644 index 0000000..d4291caf --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b-expected.xhtml
@@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 20px; + height: 200px; + margin-right: 2px; + float: left; + background: lightgray; + } + div.a { + width: 20px; + height: 10px; + background: lightgreen; + } + div.b { + width: 20px; + height: 30px; + background: pink; + } + div.c { + width: 20px; + height: 40px; + background: orange; + } + + /* Inside of 'b': */ + div.fixedSizeChild { + width: 10px; + height: 30px; + background: purple; + } + </style> + </head> + <body> + + <!-- default (stretch) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-top:80px; height:110px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-top:40px; height: 70px"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 160px"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 120px"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 80px"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 60px"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-top: 160px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-top: 60px"><div class="fixedSizeChild"/></div> + <div class="c" style="margin-top: 60px"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 40px"/> + <div class="b" style="margin-top: 80px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 20px"/> + <div class="b" style="margin-top: 40px"><div class="fixedSizeChild"/></div> + <div class="c" style="margin-top: 40px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b.xhtml new file mode 100644 index 0000000..6c9a40c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b.xhtml
@@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of horizontal flex containers, with 1-3 flex lines, + testing each possible value of the 'align-content' property. Additionally, + the flex container derives its width from the "max-width" property. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'align-content' in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property"/> + <link rel="match" href="flexbox-align-content-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + max-width: 20px; /* Skinny, to force us to wrap */ + height: 200px; + display: flex; + flex-wrap: wrap; + margin-right: 2px; + float: left; + background: lightgray; + } + div.a { + height: 10px; + width: 20px; + flex: none; + background: lightgreen; + } + div.b { + height: auto; /* height comes from contents */ + width: 20px; + flex: none; + background: pink; + } + div.c { + height: 40px; + width: 20px; + flex: none; + background: orange; + } + + /* Inside of 'b': */ + div.fixedSizeChild { + width: 10px; + height: 30px; + background: purple; + } + </style> + </head> + <body> + + <!-- default (stretch) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a-expected.xhtml new file mode 100644 index 0000000..9019a512 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a-expected.xhtml
@@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + margin-bottom: 2px; + background: lightgray; + height: 20px; + clear: all; + } + div.a { + width: 10px; + height: 20px; + background: lightgreen; + float: left; + } + div.b { + width: 30px; + height: 20px; + background: pink; + float: left; + } + div.c { + width: 40px; + height: 20px; + background: orange; + float: left; + } + + /* Inside of 'b': */ + div.fixedSizeChild { + width: 30px; + height: 10px; + background: purple; + } + </style> + </head> + <body> + + <!-- default (stretch) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-left:80px; width:110px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-left: 40px; width: 70px"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 160px"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 120px"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 80px"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 60px"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-left: 160px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-left: 60px"><div class="fixedSizeChild"/></div> + <div class="c" style="margin-left: 60px"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 40px"/> + <div class="b" style="margin-left: 80px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 20px"/> + <div class="b" style="margin-left: 40px"><div class="fixedSizeChild"/></div> + <div class="c" style="margin-left: 40px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml new file mode 100644 index 0000000..ac84347 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml
@@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of vertical flex containers, with 1-3 flex lines, + testing each possible value of the 'align-content' property. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'align-content' in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property"/> + <link rel="match" href="flexbox-align-content-vert-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + height: 20px; /* Short, to force us to wrap */ + display: flex; + flex-direction: column; + flex-wrap: wrap; + margin-bottom: 2px; + background: lightgray; + } + div.a { + width: 10px; + height: 20px; + flex: none; + background: lightgreen; + } + div.b { + width: auto; /* width comes from contents */ + height: 20px; + flex: none; + background: pink; + } + div.c { + width: 40px; + height: 20px; + flex: none; + background: orange; + } + + /* Inside of 'b': */ + div.fixedSizeChild { + width: 30px; + height: 10px; + background: purple; + } + </style> + </head> + <body> + + <!-- default (stretch) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b-expected.xhtml new file mode 100644 index 0000000..9019a512 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b-expected.xhtml
@@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + margin-bottom: 2px; + background: lightgray; + height: 20px; + clear: all; + } + div.a { + width: 10px; + height: 20px; + background: lightgreen; + float: left; + } + div.b { + width: 30px; + height: 20px; + background: pink; + float: left; + } + div.c { + width: 40px; + height: 20px; + background: orange; + float: left; + } + + /* Inside of 'b': */ + div.fixedSizeChild { + width: 30px; + height: 10px; + background: purple; + } + </style> + </head> + <body> + + <!-- default (stretch) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-left:80px; width:110px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-left: 40px; width: 70px"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 160px"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 120px"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 80px"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 60px"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-left: 160px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b" style="margin-left: 60px"><div class="fixedSizeChild"/></div> + <div class="c" style="margin-left: 60px"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 40px"/> + <div class="b" style="margin-left: 80px"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 20px"/> + <div class="b" style="margin-left: 40px"><div class="fixedSizeChild"/></div> + <div class="c" style="margin-left: 40px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b.xhtml new file mode 100644 index 0000000..c76c141 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b.xhtml
@@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of vertical flex containers, with 1-3 flex lines, + testing each possible value of the 'align-content' property. Additionally, + the flex container derives its height from the "max-height" property. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'align-content' in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property"/> + <link rel="match" href="flexbox-align-content-vert-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + max-height: 20px; /* Short, to force us to wrap */ + display: flex; + flex-direction: column; + flex-wrap: wrap; + margin-bottom: 2px; + background: lightgray; + } + div.a { + width: 10px; + height: 20px; + flex: none; + background: lightgreen; + } + div.b { + width: auto; /* width comes from contents */ + height: 20px; + flex: none; + background: pink; + } + div.c { + width: 40px; + height: 20px; + flex: none; + background: orange; + } + + /* Inside of 'b': */ + div.fixedSizeChild { + width: 30px; + height: 10px; + background: purple; + } + </style> + </head> + <body> + + <!-- default (stretch) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: flex-start"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: flex-end"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: center"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: space-between"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + </div> + <div class="flexbox" style="align-content: space-around"> + <div class="a"/> + <div class="b"><div class="fixedSizeChild"/></div> + <div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a-expected.xhtml new file mode 100644 index 0000000..de5a36b1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a-expected.xhtml
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for behavior of the 'baseline' value for align-items and + align-self. + + NOTE: For multi-line 'display: block' elements in the testcase (and inline + content that gets wrapped in an anonymous block), we add an inline-table + wrapper here in the reference case, so that we get first-line baseline + alignment instead of the last-line baseline-alignment that an inline-block + would give us. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + display: block; + border: 1px dashed blue; + font: 14px sans-serif; + } + + div { display: inline-block; } + table { display: inline-table; } + + .big { + height: 100px; + font: 24px sans-serif; + margin-top: 20px; + } + .super { + vertical-align: super; + font-size: 12px; + } + .sub { + vertical-align: sub; + font-size: 12px; + } + + .lime { background: lime; } + .yellow { background: yellow; } + .orange { background: orange; } + .pink { background: pink; } + .aqua { background: aqua; } + .tan { background: tan; } + </style> + </head> + <body> + <div class="flexbox"> + <div class="lime">blk_1line</div><table cellspacing="0" cellpadding="0" + class="yellow">blk<br/>2lines</table><div class="orange"><span class="super">super</span></div><div class="pink"><span class="sub">sub</span></div><table cellspacing="0" cellpadding="0" + class="aqua big">big<br/>text<br/>3lines</table><table class="tan" cellspacing="0" cellpadding="0"> + <i>ital<br/>ic</i> + </table> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a.xhtml new file mode 100644 index 0000000..66aaf4f01 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a.xhtml
@@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for behavior of the 'baseline' value for align-items (and + align-self, implicitly). This test baseline-aligns various types of + content, and the flexbox's vertical size depends on the aggregate + post-alignment height of its children. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#baseline-participation"/> + <link rel="match" href="flexbox-align-self-baseline-horiz-001-ref.xhtml"/> + <style> + .flexbox { + display: flex; + align-items: baseline; + border: 1px dashed blue; + font: 14px sans-serif; + } + + .big { + height: 100px; + font: 24px sans-serif; + margin-top: 20px; + } + .super { + vertical-align: super; + font-size: 12px; + } + .sub { + vertical-align: sub; + font-size: 12px; + } + + .lime { background: lime; } + .yellow { background: yellow; } + .orange { background: orange; } + .pink { background: pink; } + .aqua { background: aqua; } + .tan { background: tan; } + </style> + </head> + <body> + <div class="flexbox"> + <div class="lime">blk_1line</div> + <div class="yellow">blk<br/>2lines</div> + <div class="orange"><span class="super">super</span></div> + <div class="pink"><span class="sub">sub</span></div> + <div class="aqua big">big<br/>text<br/>3lines</div> + <i class="tan">ital<br/>ic</i> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b-expected.xhtml new file mode 100644 index 0000000..de5a36b1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b-expected.xhtml
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for behavior of the 'baseline' value for align-items and + align-self. + + NOTE: For multi-line 'display: block' elements in the testcase (and inline + content that gets wrapped in an anonymous block), we add an inline-table + wrapper here in the reference case, so that we get first-line baseline + alignment instead of the last-line baseline-alignment that an inline-block + would give us. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + display: block; + border: 1px dashed blue; + font: 14px sans-serif; + } + + div { display: inline-block; } + table { display: inline-table; } + + .big { + height: 100px; + font: 24px sans-serif; + margin-top: 20px; + } + .super { + vertical-align: super; + font-size: 12px; + } + .sub { + vertical-align: sub; + font-size: 12px; + } + + .lime { background: lime; } + .yellow { background: yellow; } + .orange { background: orange; } + .pink { background: pink; } + .aqua { background: aqua; } + .tan { background: tan; } + </style> + </head> + <body> + <div class="flexbox"> + <div class="lime">blk_1line</div><table cellspacing="0" cellpadding="0" + class="yellow">blk<br/>2lines</table><div class="orange"><span class="super">super</span></div><div class="pink"><span class="sub">sub</span></div><table cellspacing="0" cellpadding="0" + class="aqua big">big<br/>text<br/>3lines</table><table class="tan" cellspacing="0" cellpadding="0"> + <i>ital<br/>ic</i> + </table> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b.xhtml new file mode 100644 index 0000000..17e91008 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b.xhtml
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for behavior of the 'baseline' value for align-items (and + align-self, implicitly). This test baseline-aligns various types of + content, and the flexbox's vertical size depends on the aggregate + post-alignment height of its children. This test also uses + "flex-wrap: wrap-reverse" to make the cross-axis bottom-to-top instead + of top-to-bottom. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' in a wrap-reverse flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#baseline-participation"/> + <link rel="match" href="flexbox-align-self-baseline-horiz-001-ref.xhtml"/> + <style> + .flexbox { + display: flex; + align-items: baseline; + flex-wrap: wrap-reverse; + border: 1px dashed blue; + font: 14px sans-serif; + } + + .big { + height: 100px; + font: 24px sans-serif; + margin-top: 20px; + } + .super { + vertical-align: super; + font-size: 12px; + } + .sub { + vertical-align: sub; + font-size: 12px; + } + + .lime { background: lime; } + .yellow { background: yellow; } + .orange { background: orange; } + .pink { background: pink; } + .aqua { background: aqua; } + .tan { background: tan; } + </style> + </head> + <body> + <div class="flexbox"> + <div class="lime">blk_1line</div> + <div class="yellow">blk<br/>2lines</div> + <div class="orange"><span class="super">super</span></div> + <div class="pink"><span class="sub">sub</span></div> + <div class="aqua big">big<br/>text<br/>3lines</div> + <i class="tan">ital<br/>ic</i> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-002-expected.xhtml new file mode 100644 index 0000000..b6f6a4a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-002-expected.xhtml
@@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + width: 40px; + height: 40px; + border: 1px solid gray; + margin: 5px; /* just for visual separation */ + float: left; + } + + .flexbox > * { + background: yellow; + border: 1px solid black; + height: 20px; + } + </style> + </head> + <body> + <!-- ZEROTH ROW: NO MARGINS --> + <!-- No margins on flex item: --> + <div class="flexbox"> + <div>a</div> + </div> + + + <!-- FIRST ROW: SETTING MARGIN-TOP: --> + <br style="clear: both"/> + + <!-- auto: --> + <div class="flexbox"> + <div style="margin-top: 18px">a</div> + </div> + + <!-- Negative: --> + <div class="flexbox"> + <div style="margin-top: -4px">a</div> + </div> + + <!-- Small: --> + <div class="flexbox"> + <div style="margin-top: 4px">a</div> + </div> + + <!-- Large (pushing us out of container): --> + <div class="flexbox"> + <div style="margin-top: 25px">a</div> + </div> + + + <!-- SECOND ROW: SETTING MARGIN-BOTTOM: --> + <br style="clear: both"/> + + <!-- auto: --> + <div class="flexbox"> + <div>a</div> + </div> + + <!-- Negative: --> + <div class="flexbox"> + <div>a</div> + </div> + + <!-- Small: --> + <div class="flexbox"> + <div>a</div> + </div> + + <!-- Large: --> + <div class="flexbox"> + <div>a</div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-002.xhtml new file mode 100644 index 0000000..09cddaa --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-002.xhtml
@@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how a baseline-aligned flex item's position is impacted by + cross-axis margins, in a fixed-size flex container. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Baseline alignment of flex items in fixed-size single-line flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#baseline-participation"/> + <link rel="match" href="flexbox-align-self-baseline-horiz-002-ref.xhtml"/> + <style> + .flexbox { + display: flex; + align-items: baseline; + width: 40px; + height: 40px; + border: 1px solid gray; + margin: 5px; /* just for visual separation */ + float: left; + } + + .flexbox > * { + background: yellow; + border: 1px solid black; + height: 20px; + flex: 1; + } + </style> + </head> + <body> + <!-- ZEROTH ROW: NO MARGINS --> + <!-- No margins on flex item: --> + <div class="flexbox"> + <div>a</div> + </div> + + + <!-- FIRST ROW: SETTING MARGIN-TOP: --> + <br style="clear: both"/> + + <!-- auto: --> + <div class="flexbox"> + <div style="margin-top: auto">a</div> + </div> + + <!-- Negative: --> + <div class="flexbox"> + <div style="margin-top: -4px">a</div> + </div> + + <!-- Small: --> + <div class="flexbox"> + <div style="margin-top: 4px">a</div> + </div> + + <!-- Large (pushing us out of container): --> + <div class="flexbox"> + <div style="margin-top: 25px">a</div> + </div> + + + <!-- SECOND ROW: SETTING MARGIN-BOTTOM: --> + <br style="clear: both"/> + + <!-- auto: --> + <div class="flexbox"> + <div style="margin-bottom: auto">a</div> + </div> + + <!-- Negative: --> + <div class="flexbox"> + <div style="margin-bottom: -4px">a</div> + </div> + + <!-- Small: --> + <div class="flexbox"> + <div style="margin-bottom: 4px">a</div> + </div> + + <!-- Large: --> + <div class="flexbox"> + <div style="margin-bottom: 25px">a</div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-003-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-003-expected.xhtml new file mode 100644 index 0000000..ca72b75 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-003-expected.xhtml
@@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + width: 40px; + height: 40px; + border: 1px solid gray; + margin: 5px; /* just for visual separation */ + float: left; + } + + .flexbox > * { + background: yellow; + border: 1px solid black; + height: 20px; + } + </style> + </head> + <body> + <!-- ZEROTH ROW: NO MARGINS --> + <!-- No margins on flex item: --> + <div class="flexbox"> + <div style="margin-top: 18px">a</div> + </div> + + + <!-- FIRST ROW: SETTING MARGIN-TOP: --> + <br style="clear: both"/> + + <!-- auto: --> + <div class="flexbox"> + <div style="margin-top: 18px">a</div> + </div> + + <!-- Negative: --> + <div class="flexbox"> + <div style="margin-top: 18px">a</div> + </div> + + <!-- Small: --> + <div class="flexbox"> + <div style="margin-top: 18px">a</div> + </div> + + <!-- Large (pushing us out of container): --> + <div class="flexbox"> + <div style="margin-top: 18px">a</div> + </div> + + + <!-- SECOND ROW: SETTING MARGIN-BOTTOM: --> + <br style="clear: both"/> + + <!-- auto: --> + <div class="flexbox"> + <div>a</div> + </div> + + <!-- Negative: --> + <div class="flexbox"> + <div style="margin-top: 22px">a</div> + </div> + + <!-- Small: --> + <div class="flexbox"> + <div style="margin-top: 14px">a</div> + </div> + + <!-- Large: --> + <div class="flexbox"> + <div style="margin-top: -7px">a</div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-003.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-003.xhtml new file mode 100644 index 0000000..c6b0c57f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-003.xhtml
@@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how a baseline-aligned flex item's position is impacted by + cross-axis margins, in a fixed-size flex container with the cross axis + reversed via "flex-wrap: wrap-reverse". + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Baseline alignment of flex items in fixed-size single-line flex container, with cross axis reversed</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#baseline-participation"/> + <link rel="match" href="flexbox-align-self-baseline-horiz-003-ref.xhtml"/> + <style> + .flexbox { + display: flex; + align-items: baseline; + flex-wrap: wrap-reverse; /* Just to flip cross-axis */ + width: 40px; + height: 40px; + border: 1px solid gray; + margin: 5px; /* just for visual separation */ + float: left; + } + + .flexbox > * { + background: yellow; + border: 1px solid black; + height: 20px; + flex: 1; + } + </style> + </head> + <body> + <!-- ZEROTH ROW: NO MARGINS --> + <!-- No margins on flex item: --> + <div class="flexbox"> + <div>a</div> + </div> + + + <!-- FIRST ROW: SETTING MARGIN-TOP: --> + <br style="clear: both"/> + + <!-- auto: --> + <div class="flexbox"> + <div style="margin-top: auto">a</div> + </div> + + <!-- Negative: --> + <div class="flexbox"> + <div style="margin-top: -4px">a</div> + </div> + + <!-- Small: --> + <div class="flexbox"> + <div style="margin-top: 4px">a</div> + </div> + + <!-- Large (pushing us out of container): --> + <div class="flexbox"> + <div style="margin-top: 25px">a</div> + </div> + + + <!-- SECOND ROW: SETTING MARGIN-BOTTOM: --> + <br style="clear: both"/> + + <!-- auto: --> + <div class="flexbox"> + <div style="margin-bottom: auto">a</div> + </div> + + <!-- Negative: --> + <div class="flexbox"> + <div style="margin-bottom: -4px">a</div> + </div> + + <!-- Small: --> + <div class="flexbox"> + <div style="margin-bottom: 4px">a</div> + </div> + + <!-- Large: --> + <div class="flexbox"> + <div style="margin-bottom: 25px">a</div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-004-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-004-expected.xhtml new file mode 100644 index 0000000..03a629fa --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-004-expected.xhtml
@@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for behavior of the 'baseline' value for align-items and + align-self, in a multi-line flex container. + + This reference case just consists of three single-line flex containers, + to match the testcase's one flex container with three flex lines. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + display: flex; + align-items: baseline; + width: 90px; + } + + .flexbox > * { + width: 28px; + background: yellow; + border: 1px solid black; + } + + .big { font: 24px sans-serif; } + .medium { font: 14px sans-serif; } + .small { font: 8px sans-serif; } + + </style> + </head> + <body> + <div class="flexbox"> + <!-- First flex line: Just 3 different sizes of text --> + <div class="big">a</div> + <div class="small">b</div> + <div class="medium">c</div> + </div> + <div class="flexbox"> + <!-- Second flex line: different margin/padding amounts on each item, + and one non-baseline-aligned item. --> + <div class="medium" style="padding-top: 10px">d</div> + <div class="medium" style="margin-bottom: 8px">e</div> + <div class="medium" style="align-self: stretch">f</div> + </div> + <div class="flexbox"> + <!-- Third flex line: other margin/padding amounts on each item --> + <div class="small" style="margin-top: 20px">g</div> + <div class="big">h</div> + <div class="medium" style="padding-bottom: 6px">i</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-004.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-004.xhtml new file mode 100644 index 0000000..b29c8d0d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-004.xhtml
@@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for behavior of the 'baseline' value for align-items (and + align-self, implicitly), in a multi-line flex container. + This test baseline-aligns variously-sized flex items, and the container's + vertical size depends on the aggregate post-alignment height of its items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' in a multi-line flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#baseline-participation"/> + <link rel="match" href="flexbox-align-self-baseline-horiz-004-ref.xhtml"/> + <style> + .flexbox { + display: flex; + flex-wrap: wrap; + align-items: baseline; + width: 90px; + } + + .flexbox > * { + width: 28px; /* 3 items per Flex Line */ + background: yellow; + border: 1px solid black; + } + + .big { font: 24px sans-serif; } + .medium { font: 14px sans-serif; } + .small { font: 8px sans-serif; } + + </style> + </head> + <body> + <div class="flexbox"> + <!-- First flex line: Just 3 different sizes of text --> + <div class="big">a</div> + <div class="small">b</div> + <div class="medium">c</div> + + <!-- Second flex line: different margin/padding amounts on each item, + and one non-baseline-aligned item. --> + <div class="medium" style="padding-top: 10px">d</div> + <div class="medium" style="margin-bottom: 8px">e</div> + <div class="medium" style="align-self: stretch">f</div> + + <!-- Third flex line: other margin/padding amounts on each item --> + <div class="small" style="margin-top: 20px">g</div> + <div class="big">h</div> + <div class="medium" style="padding-bottom: 6px">i</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-005-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-005-expected.xhtml new file mode 100644 index 0000000..29b5d7d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-005-expected.xhtml
@@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for behavior of the 'baseline' value for align-items and + align-self, in a multi-line flex container. + + This reference case just consists of three single-line flex containers, + to match the testcase's one flex container with three flex lines. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + display: flex; + align-items: baseline; + width: 90px; + } + + .flexbox > * { + width: 28px; + background: yellow; + border: 1px solid black; + } + + .big { font: 24px sans-serif; } + .medium { font: 14px sans-serif; } + .small { font: 8px sans-serif; } + + </style> + </head> + <body> + <!-- Note: The lines are reversed here with respect to the testcase, + due to the testcase's "wrap-reverse". --> + <div class="flexbox"> + <!-- Third flex line: other margin/padding amounts on each item --> + <div class="small" style="margin-top: 20px">g</div> + <div class="big">h</div> + <div class="medium" style="padding-bottom: 6px">i</div> + </div> + <div class="flexbox"> + <!-- Second flex line: different margin/padding amounts on each item, + and one non-baseline-aligned item. --> + <div class="medium" style="padding-top: 10px">d</div> + <div class="medium" style="margin-bottom: 8px">e</div> + <div class="medium" style="align-self: stretch">f</div> + </div> + <div class="flexbox"> + <!-- First flex line: Just 3 different sizes of text --> + <div class="big">a</div> + <div class="small">b</div> + <div class="medium">c</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-005.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-005.xhtml new file mode 100644 index 0000000..1a0f63d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-005.xhtml
@@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for behavior of the 'baseline' value for align-items (and + align-self, implicitly), in a wrap-reverse multi-line flex container. + This test baseline-aligns variously-sized flex items, and the container's + vertical size depends on the aggregate post-alignment height of its items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Baseline alignment of block flex items with 'baseline' value for 'align-items' / 'align-self' in a multi-line flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#baseline-participation"/> + <link rel="match" href="flexbox-align-self-baseline-horiz-005-ref.xhtml"/> + <style> + .flexbox { + display: flex; + flex-wrap: wrap-reverse; + align-items: baseline; + width: 90px; + } + + .flexbox > * { + width: 28px; /* 3 items per Flex Line */ + background: yellow; + border: 1px solid black; + } + + .big { font: 24px sans-serif; } + .medium { font: 14px sans-serif; } + .small { font: 8px sans-serif; } + + </style> + </head> + <body> + <div class="flexbox"> + <!-- First flex line: Just 3 different sizes of text --> + <div class="big">a</div> + <div class="small">b</div> + <div class="medium">c</div> + + <!-- Second flex line: different margin/padding amounts on each item, + and one non-baseline-aligned item. --> + <div class="medium" style="padding-top: 10px">d</div> + <div class="medium" style="margin-bottom: 8px">e</div> + <div class="medium" style="align-self: stretch">f</div> + + <!-- Third flex line: other margin/padding amounts on each item --> + <div class="small" style="margin-top: 20px">g</div> + <div class="big">h</div> + <div class="medium" style="padding-bottom: 6px">i</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-block-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-block-expected.xhtml new file mode 100644 index 0000000..d0eb0e9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-block-expected.xhtml
@@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using floated divs + in place of flex items and using margin-top in place of the align-items / + align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + height: 200px; + width: 560px; + font-size: 10px; + line-height: 10px; + } + + .flexbox > div { + width: 40px; + float: left; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + } + .flex-end { + background: orange; + } + .center { + background: lightblue; + } + .baseline { + background: teal; + } + .stretch { + background: pink; + } + .auto { + background: yellow; + } + .unspecified { + background: lightgreen; + } + .initial { + background: aqua; + } + .inherit { + background: violet; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end" style="margin-top: 190px">end</div> + <div class="flex-end big" style="margin-top: 100px">a b c d e f</div> + <div class="center" style="margin-top: 95px">center</div> + <div class="center big" style="margin-top: 50px">a b c d e f</div> + <!-- We use inline-blocks inside of a wrapper-block as references for the + baseline-aligned flex items, since inline-blocks get + baseline-aligned in block layout. We also need to specify the widths + manually here since the "flexbox > div" child-selector doesn't + handle these guys (since they're grandchildren). + --> + <div style="width: 80px"> + <div class="baseline" + style="width: 40px; display: inline-block">base</div><div class="baseline big" + style="width: 40px; display: inline-block">abc</div> + </div> + <div class="stretch" style="height: 100%">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="auto" style="margin-top: 95px">auto</div> + <div class="unspecified" style="margin-top: 95px">unspec</div> + <div class="initial" style="margin-top: 95px">initial</div> + <div class="inherit" style="margin-top: 190px">inherit</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-block.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-block.xhtml new file mode 100644 index 0000000..0ab118e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-block.xhtml
@@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' property values on flex items that are blocks, in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-horiz-001-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + height: 200px; + display: inline-flex; + font-size: 10px; + line-height: 10px; + + /* Any children whose align-self is 'auto' (or unspecified, or + initial) will end up taking this value from us: */ + align-items: center; + + /* Any children whose align-self is 'inherit' will end up + inheriting this value from us: */ + align-self: flex-end; + } + + .flexbox > div { + width: 40px; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + .auto { + background: yellow; + align-self: auto; + } + .unspecified { + background: lightgreen; + } + .initial { + background: aqua; + align-self: initial; + } + .inherit { + background: violet; + align-self: inherit; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="auto">auto</div> + <div class="unspecified">unspec</div> + <div class="initial">initial</div> + <div class="inherit">inherit</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-table-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-table-expected.xhtml new file mode 100644 index 0000000..d0eb0e9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-table-expected.xhtml
@@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using floated divs + in place of flex items and using margin-top in place of the align-items / + align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + height: 200px; + width: 560px; + font-size: 10px; + line-height: 10px; + } + + .flexbox > div { + width: 40px; + float: left; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + } + .flex-end { + background: orange; + } + .center { + background: lightblue; + } + .baseline { + background: teal; + } + .stretch { + background: pink; + } + .auto { + background: yellow; + } + .unspecified { + background: lightgreen; + } + .initial { + background: aqua; + } + .inherit { + background: violet; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end" style="margin-top: 190px">end</div> + <div class="flex-end big" style="margin-top: 100px">a b c d e f</div> + <div class="center" style="margin-top: 95px">center</div> + <div class="center big" style="margin-top: 50px">a b c d e f</div> + <!-- We use inline-blocks inside of a wrapper-block as references for the + baseline-aligned flex items, since inline-blocks get + baseline-aligned in block layout. We also need to specify the widths + manually here since the "flexbox > div" child-selector doesn't + handle these guys (since they're grandchildren). + --> + <div style="width: 80px"> + <div class="baseline" + style="width: 40px; display: inline-block">base</div><div class="baseline big" + style="width: 40px; display: inline-block">abc</div> + </div> + <div class="stretch" style="height: 100%">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="auto" style="margin-top: 95px">auto</div> + <div class="unspecified" style="margin-top: 95px">unspec</div> + <div class="initial" style="margin-top: 95px">initial</div> + <div class="inherit" style="margin-top: 190px">inherit</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-table.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-table.xhtml new file mode 100644 index 0000000..75829236 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-001-table.xhtml
@@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the various 'align-self' property values on flex items that are tables</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-horiz-001-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + height: 200px; + display: inline-flex; + font-size: 10px; + line-height: 10px; + + /* Any children whose align-self is 'auto' (or unspecified, or + initial) will end up taking this value from us: */ + align-items: center; + + /* Any children whose align-self is 'inherit' will end up + inheriting this value from us: */ + align-self: flex-end; + } + + .flexbox > * { + display: table; + width: 40px; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + display: block; /* XXXdholbert Hackaround for bug 799725 */ + } + .auto { + background: yellow; + align-self: auto; + } + .unspecified { + background: lightgreen; + } + .initial { + background: aqua; + align-self: initial; + } + .inherit { + background: violet; + align-self: inherit; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="auto">auto</div> + <div class="unspecified">unspec</div> + <div class="initial">initial</div> + <div class="inherit">inherit</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002-expected.xhtml new file mode 100644 index 0000000..7030a6d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002-expected.xhtml
@@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using floated divs + in place of flex items and using relative positioning in place of the + align-items / align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + height: 200px; + display: inline-block; + font-size: 10px; + line-height: 10px; + vertical-align: top; + } + + .flexbox > div { float: left } + .flex-start, .flex-end, .center, .baseline, .stretch, + .auto, .unspecified, .initial, .inherit { + width: 40px; + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + position: relative; + border-style: dotted; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + } + .flex-end { + background: orange; + } + .center { + background: lightblue; + } + .baseline { + background: teal; + } + .stretch { + background: pink; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end" style="top: 172px">end</div> + <div class="flex-end big" style="top: 82px">a b c d e f</div> + <div class="center" style="top: 86px">center</div> + <div class="center big" style="top: 41px">a b c d e f</div> + </div> + <br/> + <div class="flexbox"> + <!-- We use inline-blocks inside of a wrapper-block as references for the + baseline-aligned flex items, since inline-blocks get + baseline-aligned in block layout. We also need to specify the widths + manually here since the "flexbox > div" child-selector doesn't + handle these guys (since they're grandchildren). + --> + <div> + <div class="baseline" + style="display: inline-block">base</div><div class="baseline big" + style="display: inline-block">abc</div> + </div> + <div class="stretch" style="height: 182px">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml new file mode 100644 index 0000000..f12b628 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml
@@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with + margin/border/padding values on each item. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a horizontal flex container, with margin/padding/border on the items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-horiz-002-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + height: 200px; + display: inline-flex; + font-size: 10px; + line-height: 10px; + vertical-align: top; + } + + .flexbox > div { + width: 40px; + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + border-style: dotted; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + </div> + <br/> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-003-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-003-expected.xhtml new file mode 100644 index 0000000..21fc808 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-003-expected.xhtml
@@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using floated divs + in place of flex items and using relative positioning in place of the + align-items / align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + padding: 3px; + width: 600px; + height: 4px; + font-size: 10px; + line-height: 10px; + font-family: sans-serif; + } + + .flexbox > div { + width: 40px; + float: left; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + } + .flex-end { + background: orange; + } + .center { + background: lightblue; + } + .baseline { + background: teal; + } + .stretch { + background: pink; + } + .auto { + background: yellow; + } + .unspecified { + background: lightgreen; + } + .initial { + background: aqua; + } + .inherit { + background: violet; + } + .normal { + background: tan; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end" style="position: relative; top: -6px">end</div> + <div class="flex-end big" style="position: relative; top: -96px">a b c d e f</div> + <div class="center" style="position: relative; top: -3px">center</div> + <div class="center big" style="position: relative; top: -48px">a b c d e f</div> + <!-- We use inline-blocks inside of a wrapper-block as references for the + baseline-aligned flex items, since inline-blocks get + baseline-aligned in block layout. We also need to specify the widths + manually here since the "flexbox > div" child-selector doesn't + handle these guys (since they're grandchildren). + --> + <div style="width: 80px"> + <div class="baseline" + style="width: 40px; display: inline-block">base</div><div class="baseline big" + style="width: 40px; display: inline-block">abc</div> + </div> + <div class="stretch" style="height: 100%">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="auto" style="height: 100%">auto</div> + <div class="unspecified" style="height: 100%">unspec</div> + <div class="initial" style="height: 100%">initial</div> + <div class="inherit" style="height: 100%">inherit</div> + <div class="normal" style="height: 100%">normal</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-003.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-003.xhtml new file mode 100644 index 0000000..5e3e497 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-003.xhtml
@@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with the + flex container being shorter than its items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a horizontal flex container that's shorter than its items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-horiz-003-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + padding: 3px; + height: 4px; + display: inline-flex; + font-size: 10px; + line-height: 10px; + font-family: sans-serif; + } + + .flexbox > div { + width: 40px; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + .auto { + background: yellow; + align-self: auto; + } + .unspecified { + background: lightgreen; + } + .initial { + background: aqua; + align-self: initial; + } + .inherit { + background: violet; + align-self: inherit; + } + .normal { + background: tan; + align-self: normal; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="auto">auto</div> + <div class="unspecified">unspec</div> + <div class="initial">initial</div> + <div class="inherit">inherit</div> + <div class="normal">normal</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-004-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-004-expected.xhtml new file mode 100644 index 0000000..b7818d5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-004-expected.xhtml
@@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using floated divs + in place of flex items and using relative positioning in place of the + align-items / align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + padding: 3px; + height: 4px; + display: inline-block; + font-size: 10px; + line-height: 10px; + font-family: sans-serif; + margin-top: 20px; + margin-bottom: 120px; + vertical-align: top; + } + + .flexbox > div { float: left } + .flex-start, .flex-end, .center, .baseline, .stretch, + .auto, .unspecified, .initial, .inherit { + width: 40px; + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + position: relative; + border-style: dotted; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + } + .flex-end { + background: orange; + } + .center { + background: lightblue; + } + .baseline { + background: teal; + } + .stretch { + background: pink; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end" style="top: -24px">end</div> + <div class="flex-end big" style="top: -114px">a b c d e f</div> + <div class="center" style="top: -12px">center</div> + <div class="center big" style="top: -57px">a b c d e f</div> + </div> + <br/> + <div class="flexbox"> + <!-- We use inline-blocks inside of a wrapper-block as references for the + baseline-aligned flex items, since inline-blocks get + baseline-aligned in block layout. We also need to specify the widths + manually here since the "flexbox > div" child-selector doesn't + handle these guys (since they're grandchildren). + --> + <div> + <div class="baseline" + style="display: inline-block">base</div><div class="baseline big" + style="display: inline-block">abc</div> + </div> + <div class="stretch" style="height: 2px">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-004.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-004.xhtml new file mode 100644 index 0000000..31b93e4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-004.xhtml
@@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with the + flex container being shorter than its items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a horizontal flex container that's shorter than its items, with margin/padding/border on the items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-horiz-004-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + padding: 3px; + height: 4px; + display: inline-flex; + font-size: 10px; + line-height: 10px; + font-family: sans-serif; + margin-top: 20px; + margin-bottom: 120px; + vertical-align: top; + } + + .flexbox > div { + width: 40px; + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + border-style: dotted; + } + + .big { + height: 100px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + min-height: 2px; + align-self: stretch; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + </div> + <br/> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-005-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-005-expected.xhtml new file mode 100644 index 0000000..4616b29 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-005-expected.xhtml
@@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior with auto + margins in play. This reference case uses fixed margin-top values + in place of the testcase's auto margins. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + height: 140px; + width: 400px; + display: flex; + font-size: 10px; + line-height: 10px; + margin-bottom: 10px; + } + + .kidsAutoTop > div { margin-top: 130px; } + .kidsAutoTop > div.big { margin-top: 60px; } + .kidsAutoBoth > div { margin-top: 65px; } + .kidsAutoBoth > div.big { margin-top: 30px; } + + .flexbox > div { + width: 40px; + height: 10px; + } + + .flexbox > div.big { + height: 80px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + } + .flex-end { + background: orange; + } + .center { + background: lightblue; + } + .baseline { + background: teal; + } + .stretch { + background: pink; + } + </style> + </head> + <body> + <div class="flexbox kidsAutoTop"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">a b c d e f</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + + <div class="flexbox kidsAutoBottom"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">a b c d e f</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + + <div class="flexbox kidsAutoBoth"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">a b c d e f</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-005.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-005.xhtml new file mode 100644 index 0000000..0adec35 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-005.xhtml
@@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior with auto margins in + play (which should negate the effects of align-items / align-self, + because there won't be any available space in which to align the item + after the auto margins are resolved). --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with auto margins in play, in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#auto-margins"/> + <link rel="match" href="flexbox-align-self-horiz-005-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + height: 140px; + width: 400px; + display: flex; + font-size: 10px; + line-height: 10px; + margin-bottom: 10px; + } + + .kidsAutoTop > div { margin-top: auto; } + .kidsAutoBottom > div { margin-bottom: auto; } + .kidsAutoBoth > div { margin: auto 0; } + + .flexbox > div { + width: 40px; + } + + .flexbox > div.big { + height: 80px; + font-size: 20px; + line-height: 20px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + </style> + </head> + <body> + <div class="flexbox kidsAutoTop"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">a b c d e f</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + + <div class="flexbox kidsAutoBottom"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">a b c d e f</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + + <div class="flexbox kidsAutoBoth"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">a b c d e f</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-001-expected.html new file mode 100644 index 0000000..d2fcb7a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-001-expected.html
@@ -0,0 +1,29 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- In this reference case, we just have the testcases's text placed directly + in the outermost wrapper-block, with a lime background on that wrapper + instead of on a flex item. + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.fixedWidthWrapper { + width: 200px; + /* Small enough that 3 characters can _easily_ fit in our width */ + font-size: 12px; + background: lime; + } + </style> +</head> +<body> + <div class="fixedWidthWrapper"> + A B C + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-001.html new file mode 100644 index 0000000..de8f9bd --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-001.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing the sizing of a stretched horizontal flex container in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"> + <link rel="match" href="flexbox-align-self-stretch-vert-001-ref.html"> + <meta name="assert" content="If a stretched flex item's main size is influenced by its cross size, and the flex container has a definite cross size, then the item's cross size should be resolved early so that it can be used when determining the item's main size"> + <meta name="assert" content="https://drafts.csswg.org/css-flexbox/issues-cr-2012#issue-23"> + <meta charset="utf-8"> + <style> + div.fixedWidthWrapper { + width: 200px; + /* Small enough that 3 characters can _easily_ fit in our width */ + font-size: 12px; + } + div.vertContainer { + display: flex; + flex-direction: column; + } + div.vertItem { + background: red; + } + div.horizContainer { + display: flex; + } + div.horizItem { + flex: 1; + background: lime; + } + </style> +</head> +<body> + <div class="fixedWidthWrapper"> + <div class="vertContainer"> + <div class="vertItem"> + <div class="horizContainer"> + <div class="horizItem">A B C</div> + <div class="horizItem"></div> + </div> + </div> + </div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-002-expected.html new file mode 100644 index 0000000..38f8830 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-002-expected.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- In this reference case, we use floated fixed-sized divs to mimic the + testcases's flex items. + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.container { + width: 100px; + height: 20px; + border: 2px solid black; + } + div.item { + width: 48px; + height: 15px; + float: left; + border: 1px dotted blue; + } + </style> +</head> +<body> + <div class="container"> + <div class="item"></div> + <div class="item"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-002.html new file mode 100644 index 0000000..7f84e3f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-stretch-vert-002.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing the sizing of stretched flex items in a vertical multi-line flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"> + <link rel="match" href="flexbox-align-self-stretch-vert-002-ref.html"> + <meta name="assert" content="In a multi-line flex container, flex items should not be stretched (in the cross axis) until after wrapping has been performed."> + <meta charset="utf-8"> + <style> + div.container { + display: flex; + flex-flow: column wrap; + width: 100px; + height: 20px; + border: 2px solid black; + } + div.item { + /* Tall enough to force wrapping (since parent height is 20px): */ + min-height: 15px; + border: 1px dotted blue; + } + </style> +</head> +<body> + <div class="container"> + <div class="item"></div> + <div class="item"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-001-expected.xhtml new file mode 100644 index 0000000..80f1b17 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-001-expected.xhtml
@@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using blocks in + place of flex items and using float and width keywords to emulate the + align-items / align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 200px; + font-size: 10px; + } + + div.big { + font-size: 20px; + width: 50px; + } + + .flexbox > * { + clear: both; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + float: left; + } + .flex-end { + background: orange; + float: right; + } + .center { + background: lightblue; + margin: auto; + } + .baseline { + background: teal; + float: left; + } + .stretch { + background: pink; + width: 100%; + } + .auto { + background: yellow; + margin: auto; + } + .unspecified { + background: lightgreen; + margin: auto; + } + .initial { + background: aqua; + margin: auto; + } + .inherit { + background: violet; + float: right; + } + + <!-- We center shrinkwrapped text by putting it into an inline-block, and + then wrapping that inline-block in a helper-div that has + "text-align:center" set. --> + .centerParent { + text-align: center; + } + .centerParent > * { + display: inline-block; + text-align: left; /* Keep parent's centering from tweaking my text */ + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="centerParent"> + <div class="center">center</div> + </div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="centerParent"> + <div class="auto">auto</div> + </div> + <div class="centerParent"> + <div class="unspecified">unspec</div> + </div> + <div class="centerParent"> + <div class="initial">initial</div> + </div> + <div class="inherit">inherit</div> + <!-- Since that last div is floated right, the container doesn't include + its height by default. So we add some invisible hacky text (of the + same font) to make sure our container is tall enough. --> + <span style="visibility:hidden">hacky text</span> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-001.xhtml new file mode 100644 index 0000000..2d48348 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-001.xhtml
@@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' property values on flex items that are blocks, in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-vert-001-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 200px; + display: flex; + flex-direction: column; + font-size: 10px; + + /* Any children whose align-self is 'auto' (or unspecified, or + initial) will end up taking this value from us: */ + align-items: center; + + /* Any children whose align-self is 'inherit' will end up + inheriting this value from us: */ + align-self: flex-end; + } + + .big { + font-size: 20px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + .auto { + background: yellow; + align-self: auto; + } + .unspecified { + background: lightgreen; + } + .initial { + background: aqua; + align-self: initial; + } + .inherit { + background: violet; + align-self: inherit; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="auto">auto</div> + <div class="unspecified">unspec</div> + <div class="initial">initial</div> + <div class="inherit">inherit</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002-expected.xhtml new file mode 100644 index 0000000..1d500a14 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002-expected.xhtml
@@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using blocks in + place of flex items and using float and width keywords to emulate the + align-items / align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 200px; + float: left; + font-size: 10px; + } + + .flex-start, .flex-end, .center, .baseline, .stretch { + clear: both; + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + border-style: dotted; + } + + div.big { + font-size: 20px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + float: left; + } + .flex-end { + background: orange; + float: right; + } + <!-- We center shrinkwrapped text by putting it into an inline-block, and + then wrapping that inline-block in a helper-div that has + "text-align:center" set. --> + .centerParent { + text-align: center; + } + .center { + background: lightblue; + display: inline-block; + text-align: left; /* Keep parent's centering from tweaking my text */ + } + .baseline { + background: teal; + float: left; + } + .stretch { + background: pink; + } + .clearFloats { clear: both } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="clearFloats"></div> + <div class="centerParent"> + <div class="center">center</div> + </div> + <div class="centerParent"> + <div class="center big">a b c d e f</div> + </div> + </div> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="clearFloats"></div> + <div class="stretch">stretch</div> + <!-- Force a 3px + 1px = 4px margin between this and the previous div + (to thwart the effects of margin-collapsing). This is the only + place we need this hack, because everywhere else in this test + we use floats or inline-blocks, whose margins don't collapse. --> + <div class="stretch big" style="margin-top: 4px">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002.xhtml new file mode 100644 index 0000000..a0a892a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002.xhtml
@@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with + margin/border/padding values on each item. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a vertical flex container, with margin/padding/border on the items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-vert-002-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 200px; + display: flex; + flex-direction: column; + float: left; + font-size: 10px; + } + + .flexbox > div { + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + + border-style: dotted; + } + + div.big { + font-size: 20px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + </div> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-003-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-003-expected.xhtml new file mode 100644 index 0000000..88b4b63 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-003-expected.xhtml
@@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using blocks in + place of flex items and using float and width keywords to emulate the + align-items / align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 4px; + font-family: sans-serif; + font-size: 10px; + margin-left: 80px; + } + + div.big { + font-size: 18px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + float: left; + } + .flex-end { + background: orange; + float: right; + } + <!-- We center shrinkwrapped text by putting it into an inline-block, and + then wrapping that inline-block in a helper-div that has + "text-align:center" set. For this to work, the parent has to be at + least as wide as the centered content inside of it, so we make it + large with a negative margin such that its center aligns with the + 4px-wide container's center. --> + .centerParent { + text-align: center; + width: 100px; + margin-left: -48px; + } + .center { + background: lightblue; + display: inline-block; + text-align: left; /* Keep parent's centering from tweaking my text */ + } + .baseline { + background: teal; + float: left; + } + .stretch { + background: pink; + } + .clearFloats { clear: both } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b</div> + <div class="centerParent clearFloats"> + <div class="center">center</div> + </div> + <div class="centerParent"> + <div class="center big">a b</div> + </div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch clearFloats">stretch</div> + <div class="stretch big">a b</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-003.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-003.xhtml new file mode 100644 index 0000000..7d49e32 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-003.xhtml
@@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with the + flex container being skinnier than its items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a vertical flex container that's skinnier than its items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-vert-003-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 4px; + display: flex; + flex-direction: column; + font-family: sans-serif; + font-size: 10px; + margin-left: 80px; + } + + div.big { + font-size: 18px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b</div> + <div class="center">center</div> + <div class="center big">a b</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-004-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-004-expected.xhtml new file mode 100644 index 0000000..feaa8cd --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-004-expected.xhtml
@@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior, using blocks in + place of flex items and using float and width keywords to emulate the + align-items / align-self properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 4px; + float: left; + font-family: sans-serif; + font-size: 10px; + margin-left: 80px; + } + + .flex-start, .flex-end, .center, .baseline, .stretch { + clear: both; + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + border-style: dotted; + } + + div.big { + font-size: 18px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + float: left; + } + .flex-end { + background: orange; + float: right; + } + <!-- We center shrinkwrapped text by putting it into an inline-block, and + then wrapping that inline-block in a helper-div that has + "text-align:center" set. For this to work, the parent has to be at + least as wide as the centered content inside of it, so we make it + large with a negative margin such that its center aligns with the + 4px-wide container's center. --> + .centerParent { + text-align: center; + width: 100px; + margin-left: -48px; + } + .center { + background: lightblue; + display: inline-block; + text-align: left; /* Keep parent's centering from tweaking my text */ + } + .baseline { + background: teal; + float: left; + } + .stretch { + background: pink; + } + .clearFloats { clear: both } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b</div> + <div class="centerParent"> + <div class="center">center</div> + </div> + <div class="centerParent"> + <div class="center big">a b</div> + </div> + </div> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="clearFloats"></div> + <div class="stretch">stretch</div> + <!-- Force a 3px + 1px = 4px margin between this and the previous div + (to thwart the effects of margin-collapsing). This is the only + place we need this hack, because everywhere else in this test + we use floats or inline-blocks, whose margins don't collapse. --> + <div class="stretch big" style="margin-top: 4px">a b</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-004.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-004.xhtml new file mode 100644 index 0000000..c1e28ad3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-004.xhtml
@@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with the + flex container being skinnier than its items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a vertical flex container that's skinnier than its items, with margin/padding/border on the items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-vert-004-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 4px; + display: flex; + flex-direction: column; + float: left; + font-family: sans-serif; + font-size: 10px; + margin-left: 80px; + } + + .flexbox > div { + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + + border-style: dotted; + } + + div.big { + font-size: 18px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b</div> + <div class="center">center</div> + <div class="center big">a b</div> + </div> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-001-expected.xhtml new file mode 100644 index 0000000..30e1882 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-001-expected.xhtml
@@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior in a vertical + "direction: rtl" flex container, using blocks in place of flex items and + using float and width keywords to emulate the align-items / align-self + properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 200px; + direction: rtl; + font-family: sans-serif; + font-size: 10px; + } + + div.big { + font-size: 20px; + width: 50px; + } + + .flexbox > * { + clear: both; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + float: right; + } + .flex-end { + background: orange; + float: left; + } + .center { + background: lightblue; + margin: auto; + } + .baseline { + background: teal; + float: right; + } + .stretch { + background: pink; + width: 100%; + } + .auto { + background: yellow; + margin: auto; + } + .unspecified { + background: lightgreen; + margin: auto; + } + .initial { + background: aqua; + margin: auto; + } + .inherit { + background: violet; + float: left; + } + + <!-- We center shrinkwrapped text by putting it into an inline-block, and + then wrapping that inline-block in a helper-div that has + "text-align:center" set. --> + .centerParent { + text-align: center; + } + .centerParent > * { + display: inline-block; + text-align: left; /* Keep parent's centering from tweaking my text */ + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="centerParent"> + <div class="center">center</div> + </div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="centerParent"> + <div class="auto">auto</div> + </div> + <div class="centerParent"> + <div class="unspecified">unspec</div> + </div> + <div class="centerParent"> + <div class="initial">initial</div> + </div> + <div class="inherit">inherit</div> + <!-- Since that last div is floated right, the container doesn't include + its height by default. So we add some invisible hacky text (of the + same font) to make sure our container is tall enough. --> + <span style="visibility:hidden">hacky text</span> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-001.xhtml new file mode 100644 index 0000000..d67a97d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-001.xhtml
@@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with + "direction: rtl" to swap the horizontal (cross) axis. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' property values on flex items that are blocks, in a vertical flex container with 'direction: rtl'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-vert-rtl-001-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 200px; + display: flex; + flex-direction: column; + direction: rtl; + font-family: sans-serif; + font-size: 10px; + + /* Any children whose align-self is 'auto' (or unspecified, or + initial) will end up taking this value from us: */ + align-items: center; + + /* Any children whose align-self is 'inherit' will end up + inheriting this value from us: */ + align-self: flex-end; + } + + .big { + font-size: 20px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + .auto { + background: yellow; + align-self: auto; + } + .unspecified { + background: lightgreen; + } + .initial { + background: aqua; + align-self: initial; + } + .inherit { + background: violet; + align-self: inherit; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + <div class="auto">auto</div> + <div class="unspecified">unspec</div> + <div class="initial">initial</div> + <div class="inherit">inherit</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-002-expected.xhtml new file mode 100644 index 0000000..2a9c6644 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-002-expected.xhtml
@@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior in a vertical + "direction: rtl" flex container, using blocks in place of flex items and + using float and width keywords to emulate the align-items / align-self + properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 200px; + float: left; + direction: rtl; + font-family: sans-serif; + font-size: 10px; + } + + .flex-start, .flex-end, .center, .baseline, .stretch { + clear: both; + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + border-style: dotted; + } + + div.big { + font-size: 20px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + float: right; + } + .flex-end { + background: orange; + float: left; + } + <!-- We center shrinkwrapped text by putting it into an inline-block, and + then wrapping that inline-block in a helper-div that has + "text-align:center" set. --> + .centerParent { + text-align: center; + } + .center { + background: lightblue; + display: inline-block; + text-align: right; /* Keep parent's centering from tweaking my text */ + } + .baseline { + background: teal; + float: right; + } + .stretch { + background: pink; + } + .clearFloats { clear: both } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="clearFloats"></div> + <div class="centerParent"> + <div class="center">center</div> + </div> + <div class="centerParent"> + <div class="center big">a b c d e f</div> + </div> + </div> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="clearFloats"></div> + <div class="stretch">stretch</div> + <!-- Force a 3px + 1px = 4px margin between this and the previous div + (to thwart the effects of margin-collapsing). This is the only + place we need this hack, because everywhere else in this test + we use floats or inline-blocks, whose margins don't collapse. --> + <div class="stretch big" style="margin-top: 4px">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-002.xhtml new file mode 100644 index 0000000..3e446ac9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-002.xhtml
@@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with + margin/border/padding values on each, and with "direction: rtl" to swap + the horizontal (cross) axis item. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a vertical flex container, with margin/padding/border on the items and with 'direction: rtl'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-vert-rtl-002-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 200px; + display: flex; + flex-direction: column; + direction: rtl; + float: left; + font-family: sans-serif; + font-size: 10px; + } + + .flexbox > div { + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + + border-style: dotted; + } + + div.big { + font-size: 20px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b c d e f</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b c d e f</div> + <div class="center">center</div> + <div class="center big">a b c d e f</div> + </div> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b c d e f</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-003-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-003-expected.xhtml new file mode 100644 index 0000000..86f96eb3d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-003-expected.xhtml
@@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior in a vertical + "direction: rtl" flex container, using blocks in place of flex items and + using float and width keywords to emulate the align-items / align-self + properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 4px; + font-family: sans-serif; + direction: rtl; + font-size: 10px; + margin-left: 80px; + } + + div.big { + font-size: 18px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + float: right; + } + .flex-end { + background: orange; + float: left; + } + <!-- We center shrinkwrapped text by putting it into an inline-block, and + then wrapping that inline-block in a helper-div that has + "text-align:center" set. For this to work, the parent has to be at + least as wide as the centered content inside of it, so we make it + large with a negative margin such that its center aligns with the + 4px-wide container's center. --> + .centerParent { + text-align: center; + width: 100px; + margin-right: -48px; + } + .center { + background: lightblue; + display: inline-block; + text-align: right; /* Keep parent's centering from tweaking my text */ + } + .baseline { + background: teal; + float: right; + } + .stretch { + background: pink; + } + .clearFloats { clear: both } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b</div> + <div class="centerParent clearFloats"> + <div class="center">center</div> + </div> + <div class="centerParent"> + <div class="center big">a b</div> + </div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch clearFloats">stretch</div> + <div class="stretch big">a b</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-003.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-003.xhtml new file mode 100644 index 0000000..78e8b0fc --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-003.xhtml
@@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with the + flex container being skinnier than its items, and with "direction: rtl" to + swap the horizontal (cross) axis. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a vertical flex container that's skinnier than its items and with 'direction: rtl'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-vert-rtl-003-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 4px; + display: flex; + flex-direction: column; + direction: rtl; + font-family: sans-serif; + font-size: 10px; + margin-left: 80px; + } + + div.big { + font-size: 18px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b</div> + <div class="center">center</div> + <div class="center big">a b</div> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-004-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-004-expected.xhtml new file mode 100644 index 0000000..d7e7adc4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-004-expected.xhtml
@@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for align-items / align-self behavior in a vertical + "direction: rtl" flex container, using blocks in place of flex items and + using float and width keywords to emulate the align-items / align-self + properties. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 4px; + float: left; + direction: rtl; + font-family: sans-serif; + font-size: 10px; + margin-left: 80px; + } + + .flex-start, .flex-end, .center, .baseline, .stretch { + clear: both; + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + border-style: dotted; + } + + div.big { + font-size: 18px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + float: right; + } + .flex-end { + background: orange; + float: left; + } + <!-- We center shrinkwrapped text by putting it into an inline-block, and + then wrapping that inline-block in a helper-div that has + "text-align:center" set. For this to work, the parent has to be at + least as wide as the centered content inside of it, so we make it + large with a negative margin such that its center aligns with the + 4px-wide container's center. --> + .centerParent { + text-align: center; + width: 100px; + margin-right: -48px; + } + .center { + background: lightblue; + display: inline-block; + text-align: right; /* Keep parent's centering from tweaking my text */ + } + .baseline { + background: teal; + float: right; + } + .stretch { + background: pink; + } + .clearFloats { clear: both } + </style> + </head> + <body> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b</div> + <div class="clearFloats"></div> + <div class="stretch">stretch</div> + <!-- Force a 3px + 1px = 4px margin between this and the previous div + (to thwart the effects of margin-collapsing). This is the only + place we need this hack, because everywhere else in this test + we use floats or inline-blocks, whose margins don't collapse. --> + <div class="stretch big" style="margin-top: 4px">a b</div> + <div class="centerParent"> + <div class="center">center</div> + </div> + <div class="centerParent"> + <div class="center big">a b</div> + </div> + </div> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-004.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-004.xhtml new file mode 100644 index 0000000..6af5f5e9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-004.xhtml
@@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for align-items / align-self behavior, with all the possible + values included on different items within a flex container, and with the + flex container being skinnier than its items, and with "direction: rtl" to + swap the horizontal (cross) axis. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the behavior of 'align-self' with a vertical flex container that's skinnier than its items, with margin/padding/border on the items and with 'direction: rtl'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-items-property"/> + <link rel="match" href="flexbox-align-self-vert-rtl-004-ref.xhtml"/> + <style> + .flexbox { + border: 1px dashed blue; + width: 4px; + display: flex; + flex-direction: column; + direction: rtl; + float: left; + font-family: sans-serif; + font-size: 10px; + margin-left: 80px; + } + + .flexbox > div { + margin: 1px 2px 3px 4px; + border-width: 2px 3px 4px 5px; + padding: 3px 4px 5px 6px; + + border-style: dotted; + } + + div.big { + font-size: 18px; + width: 50px; + } + + /* Classes for each of the various align-self values */ + .flex-start { + background: lime; + align-self: flex-start; + } + .flex-end { + background: orange; + align-self: flex-end; + } + .center { + background: lightblue; + align-self: center; + } + .baseline { + background: teal; + align-self: baseline; + } + .stretch { + background: pink; + align-self: stretch; + } + </style> + </head> + <body> + <!-- (NOTE: this test has the "stretch" divs and the "flex-end" divs + swapped in the ordering, with respect to the other + flexbox-align-self-* testcases. That's because "stretch" and + "flex-end" overflow in opposite directions, and in RTL mode (with 2 + flex containers side by side), they overflow *at* each other and + overlap. If we swap them, they float away from each other and we can + still see them.) --> + <div class="flexbox"> + <div class="flex-start">start</div> + <div class="flex-start big">a b</div> + <div class="stretch">stretch</div> + <div class="stretch big">a b</div> + <div class="center">center</div> + <div class="center big">a b</div> + </div> + <div class="flexbox"> + <div class="baseline">base</div> + <div class="baseline big">abc</div> + <div class="flex-end">end</div> + <div class="flex-end big">a b</div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-anonymous-items-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-anonymous-items-001-expected.html new file mode 100644 index 0000000..83e5f50 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-anonymous-items-001-expected.html
@@ -0,0 +1,15 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> +</head> +<body> + a ab bx x +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-anonymous-items-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-anonymous-items-001.html new file mode 100644 index 0000000..b26ca94 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-anonymous-items-001.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing that we gracefully handle cases where two anonymous flex items become adjacent due to "order" reordering</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"> + <link rel="match" href="flexbox-anonymous-items-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + } + </style> +</head> +<body> + <div class="flexContainer"> + a a + <div style="order: 1">x x</div> + b b + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-horiz-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-horiz-001-expected.html new file mode 100644 index 0000000..e182196 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-horiz-001-expected.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex, and with the + unaligned children taken out of baseline-alignment with + "vertical-align:top". + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + body { + margin: 0; + font-size: 20px; + line-height: 20px; + } + .flexContainer { + display: inline-block; + background: lightblue; + vertical-align: top; + } + .hugeAndUnaligned { + font-size: 35px; + line-height: 35px; + vertical-align: top; + } + .smallFont { + font-size: 10px; + line-height: 10px; + } + .flexContainer > * { display: inline; } + </style> +</head> +<body> + a + <div class="flexContainer"> + <div class="smallFont">b</div><div>c</div><div class="hugeAndUnaligned">d</div> + </div> + <div class="flexContainer"> + <div class="hugeAndUnaligned">e</div><div>f</div><div class="smallFont">g</div> + </div> + <div class="flexContainer"> + <div class="hugeAndUnaligned">h</div><div class="smallFont">i</div><div>j</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-horiz-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-horiz-001.html new file mode 100644 index 0000000..de03a13 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-horiz-001.html
@@ -0,0 +1,62 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a horizontal flex container + with several flex items, some of which have "align-self:baseline". The + spec says this about this case: + If any of the flex items on the flex container's first line + participate in baseline alignment, the flex container's + main-axis baseline is the baseline of those flex items. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a horizontal flex container with baseline-aligned flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-align-self-baseline-horiz-001-ref.html"> + <meta charset="utf-8"> + <style> + body { + margin: 0; + font-size: 20px; + line-height: 20px; + } + .flexContainer { + display: inline-flex; + background: lightblue; + align-items: baseline; + } + .hugeAndUnaligned { + font-size: 35px; + line-height: 35px; + /* This one flex item won't be baseline-aligned, so it won't impact + the flex container's positioning */ + align-self: stretch; + } + .smallFont { + font-size: 10px; + line-height: 10px; + } + </style> +</head> +<body> + a + <div class="flexContainer"> + <div class="smallFont">b</div> + <div>c</div> + <div class="hugeAndUnaligned">d</div> + </div> + <div class="flexContainer"> + <div class="hugeAndUnaligned">e</div> + <div>f</div> + <div class="smallFont">g</div> + </div> + <div class="flexContainer"> + <div class="hugeAndUnaligned">h</div> + <div class="smallFont">i</div> + <div>j</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001-expected.html new file mode 100644 index 0000000..daa855c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001-expected.html
@@ -0,0 +1,52 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex, and with the + unaligned children taken out of baseline-alignment with + "vertical-align:top". + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + body { + margin: 0; + font: 20px Ahem; + line-height: 20px; + /* Baseline is 0.8em = 16px from top */ + } + .flexContainer { + display: inline-block; + background: lightblue; + } + .hugeAndUnaligned { + font-size: 35px; + line-height: 35px; + vertical-align: top; + } + .smallFont { + font-size: 10px; + line-height: 10px; + /* Baseline is 0.8em = 8px from top */ + } + * { vertical-align: top } + </style> +</head> +<body> + <div style="display: inline-block; margin-top: 12px">a</div> + <div class="flexContainer" style="margin-top: 20px"> + <div class="smallFont">b</div><div>c</div><div class="hugeAndUnaligned">d</div> + </div> + <div class="flexContainer"> + <div class="hugeAndUnaligned">e</div><div>f</div><div class="smallFont">g</div> + </div> + <div class="flexContainer"> + <div class="hugeAndUnaligned">h</div><div class="smallFont">i</div><div>j</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001.html new file mode 100644 index 0000000..cfd80d2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001.html
@@ -0,0 +1,66 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a vertical flex container with + several flex items, some of which have "align-self:baseline". Since we're + vertical and the items' baselines are horizontal, they do not end up + participating in baseline alignment, so their "align-self:baseline" + computed style doesn't have any special effect on the container's + baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a vertical flex container with baseline-aligned flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-align-self-baseline-vert-001-ref.html"> + <meta charset="utf-8"> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + body { + margin: 0; + font: 20px Ahem; + line-height: 20px; + /* Baseline is 0.8em = 16px from top */ + } + .flexContainer { + display: inline-flex; + flex-direction: column; + background: lightblue; + align-items: baseline; + } + .hugeAndUnaligned { + font-size: 35px; + line-height: 35px; + /* This one flex item won't be baseline-aligned, so it won't impact + the flex container's positioning */ + align-self: stretch; + } + .smallFont { + font-size: 10px; + line-height: 10px; + /* Baseline is 0.8em = 8px from top */ + } + </style> +</head> +<body> + a + <div class="flexContainer"> + <div class="smallFont">b</div> + <div>c</div> + <div class="hugeAndUnaligned">d</div> + </div> + <div class="flexContainer"> + <div class="hugeAndUnaligned">e</div> + <div>f</div> + <div class="smallFont">g</div> + </div> + <div class="flexContainer"> + <div class="hugeAndUnaligned">h</div> + <div class="smallFont">i</div> + <div>j</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001a-expected.html new file mode 100644 index 0000000..70af77b --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001a-expected.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- In this reference case, we have inline-blocks instead of inline + flex containers. We stick an Ahem whitespace character in each + inline-block, with a customized line-height to make the baseline + end up at the bottom of the inline-block's content-box. --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + body { + font: 20px Ahem; + } + .flexContainer { + display: inline-block; + height: 16px; + width: 16px; + /* Each inline-block's baseline will be the baseline of the single Ahem + character that it contains. We want to set up that char such that its + baseline is at the bottom of the container's content box (since that's + the corresponding flex container's baseline). So, we use a line-height + of 20px, which gives us a baseline of 20px * 0.8 = 16px, which is the + bottom of the container's content-box -- awesome. */ + line-height: 20px; + background: purple; + border: 0px dotted black; + /* (Elements that want a border will set their border-width.) */ + } + </style> +</head> +<body> + A + <!-- We have to include a character in the inline-blocks in order for them + to baseline-align; otherwise, they align the bottom of their + border-boxes. --> + <div class="flexContainer"> </div> + <div class="flexContainer" style="padding-bottom: 20px"> </div> + <div class="flexContainer" style="padding: 10px"> </div> + <div class="flexContainer" style="border-width: 3px"> </div> + <div class="flexContainer" style="border-bottom-width: 4px"> </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001a.html new file mode 100644 index 0000000..8da52d5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001a.html
@@ -0,0 +1,45 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a horizontal flex container + with no flex items. This is the main-axis baseline. The spec says this + about this case: + + The flex container's main-axis baseline is synthesized + from ... the flex container's content box. + + I'm taking that to mean the baseline is the bottom of the content box. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of an empty horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-empty-001-ref.html"> + <meta charset="utf-8"> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + body { + font: 20px Ahem; + } + .flexContainer { + display: inline-flex; + height: 16px; + width: 16px; + background: purple; + border: 0px dotted black; + /* (Elements that want a border will set their border-width.) */ + } + </style> +</head> +<body> + A + <div class="flexContainer"></div> + <div class="flexContainer" style="padding-bottom: 20px"></div> + <div class="flexContainer" style="padding: 10px"></div> + <div class="flexContainer" style="border-width: 3px"></div> + <div class="flexContainer" style="border-bottom-width: 4px"></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001b-expected.html new file mode 100644 index 0000000..70af77b --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001b-expected.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- In this reference case, we have inline-blocks instead of inline + flex containers. We stick an Ahem whitespace character in each + inline-block, with a customized line-height to make the baseline + end up at the bottom of the inline-block's content-box. --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + body { + font: 20px Ahem; + } + .flexContainer { + display: inline-block; + height: 16px; + width: 16px; + /* Each inline-block's baseline will be the baseline of the single Ahem + character that it contains. We want to set up that char such that its + baseline is at the bottom of the container's content box (since that's + the corresponding flex container's baseline). So, we use a line-height + of 20px, which gives us a baseline of 20px * 0.8 = 16px, which is the + bottom of the container's content-box -- awesome. */ + line-height: 20px; + background: purple; + border: 0px dotted black; + /* (Elements that want a border will set their border-width.) */ + } + </style> +</head> +<body> + A + <!-- We have to include a character in the inline-blocks in order for them + to baseline-align; otherwise, they align the bottom of their + border-boxes. --> + <div class="flexContainer"> </div> + <div class="flexContainer" style="padding-bottom: 20px"> </div> + <div class="flexContainer" style="padding: 10px"> </div> + <div class="flexContainer" style="border-width: 3px"> </div> + <div class="flexContainer" style="border-bottom-width: 4px"> </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001b.html new file mode 100644 index 0000000..84049cd --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-empty-001b.html
@@ -0,0 +1,46 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a vertical flex container + with no flex items. This is the cross-axis baseline. The spec says this + about this case: + + ...the flex container's cross-axis baseline is synthesized + from ... the flex container's content box. + + I'm taking that to mean the baseline is the bottom of the content box. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of an empty vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-empty-001-ref.html"> + <meta charset="utf-8"> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + body { + font: 20px Ahem; + } + .flexContainer { + display: inline-flex; + flex-direction: column; + height: 16px; + width: 16px; + background: purple; + border: 0px dotted black; + /* (Elements that want a border will set their border-width.) */ + } + </style> +</head> +<body> + A + <div class="flexContainer"></div> + <div class="flexContainer" style="padding-bottom: 20px"></div> + <div class="flexContainer" style="padding: 10px"></div> + <div class="flexContainer" style="border-width: 3px"></div> + <div class="flexContainer" style="border-bottom-width: 4px"></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-horiz-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-horiz-001-expected.html new file mode 100644 index 0000000..cba6a336 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-horiz-001-expected.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex, and with the + baseline-aligned items aligned using specific font-size / line-heights, + and with unaligned children taken out of baseline-alignment using + "vertical-align:top". + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-block; + background: lightblue; + } + .flexContainer > * { display: inline; } + .smallFont { + font-size: 10px; + line-height: 10px; + } + .bigFont { + font-size: 20px; + line-height: 20px; + } + .unaligned { vertical-align: top } + </style> +</head> +<body> + a + <div class="flexContainer smallFont"> + <div class="smallFont">b</div><div class="bigFont unaligned">c</div> + </div> + <div class="flexContainer bigFont"> + <div class="bigFont">d</div><div class="smallFont unaligned">e</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-horiz-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-horiz-001.html new file mode 100644 index 0000000..a220f659 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-horiz-001.html
@@ -0,0 +1,45 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a horizontal flex container + with several flex items, none of which have "align-self:baseline". The + spec says this about this case: + ...if the flex container has at least one flex item, and its + first flex item has a baseline parallel to the flex + container's main axis, the flex container's main-axis + baseline is that baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a horizontal flex container whose flex items are not baseline-aligned</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-multi-item-horiz-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + background: lightblue; + } + .smallFont { + font-size: 10px; + line-height: 10px; + } + .bigFont { + font-size: 20px; + line-height: 20px; + } + </style> +</head> +<body> + a + <div class="flexContainer"> + <div class="smallFont">b</div><div class="bigFont">c</div> + </div> + <div class="flexContainer"> + <div class="bigFont">d</div><div class="smallFont">e</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001-expected.html new file mode 100644 index 0000000..6e5a26c6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001-expected.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex, and with the + inline-blocks manually positioned with "vertical-align:top" and + margin-top. + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + body { + margin: 0; + font: 20px Ahem; + line-height: 20px; + /* Baseline is 0.8em = 16px from top */ + } + .flexContainer { + display: inline-block; + background: lightblue; + } + .smallFont { + font-size: 10px; + line-height: 10px; + /* Baseline is 0.8em = 8px from top */ + } + .bigFont { + font-size: 20px; + line-height: 20px; + /* Baseline is 0.8em = 16px from top */ + } + * { vertical-align: top } + </style> +</head> +<body> + a + <div class="flexContainer" style="margin-top: 8px"> + <div class="smallFont">b</div><div class="bigFont">c</div> + </div> + <div class="flexContainer"> + <div class="bigFont">d</div><div class="smallFont">e</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001.html new file mode 100644 index 0000000..41c27ac --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001.html
@@ -0,0 +1,55 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a vertical flex container + with several flex items, none of which have "align-self:baseline". The + spec says this about this case: + ...if the flex container has at least one flex item, and its + first flex item has a baseline parallel to the flex + container's main axis, the flex container's main-axis + baseline is that baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a vertical flex container whose flex items are not baseline-aligned</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-multi-item-vert-001-ref.html"> + <meta charset="utf-8"> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + body { + margin: 0; + font: 20px Ahem; + line-height: 20px; + /* Baseline is 0.8em = 16px from top */ + } + .flexContainer { + display: inline-flex; + flex-direction: column; + background: lightblue; + } + .smallFont { + font-size: 10px; + line-height: 10px; + /* Baseline is 0.8em = 8px from top */ + } + .bigFont { + font-size: 20px; + line-height: 20px; + /* Baseline is 0.8em = 16px from top */ + } + </style> +</head> +<body> + a + <div class="flexContainer"> + <div class="smallFont">b</div><div class="bigFont">c</div> + </div> + <div class="flexContainer"> + <div class="bigFont">d</div><div class="smallFont">e</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-001-expected.html new file mode 100644 index 0000000..1723d7f6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-001-expected.html
@@ -0,0 +1,62 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex, and with the + baseline-aligned items aligned using specific font-size / line-heights, + and with unaligned children taken out of baseline-alignment using + "vertical-align:top". + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-block; + width: 40px; + height: 40px; + background: lightblue; + } + .flexContainer > * { + width: 20px; + display: inline-block; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + .unaligned { vertical-align: top } + </style> +</head> +<body> + a + <!-- Flex containers with flex items that have a mix of baselines: --> + <div class="flexContainer medFont"> + <div class="medFont">b</div><div class="bigFont unaligned">c</div> + </div> + + <div class="flexContainer bigFont"> + <div class="bigFont">f</div><div class="smallFont unaligned">g</div> + </div> + + <!-- Flex container with second line baseline-aligned + (shouldn't make a difference) --> + <div class="flexContainer smallFont"> + <div class="smallFont">j</div><div class="bigFont unaligned">k</div> + </div> + n + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-001.html new file mode 100644 index 0000000..ca94d1b8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-001.html
@@ -0,0 +1,74 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a horizontal flex container + with several flex lines. + The spec says this about this case: + [Given that the first line has no baseline-aligned items:] + ...if the flex container has at least one flex item, and its + first flex item has a baseline parallel to the flex + container's main axis, the flex container's main-axis + baseline is that baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a horizontal flex container with multiple flex lines</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-multi-line-horiz-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + flex-wrap: wrap; + width: 40px; + height: 40px; + background: lightblue; + } + .flexContainer > * { + width: 20px; + } + + /* We'll make the second flex line not paint anything, so that the + reference case doesn't need to bother matching it. */ + .flexContainer > *:nth-child(3), + .flexContainer > *:nth-child(4) { + visibility: hidden; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + </style> +</head> +<body> + a + <!-- Flex containers with flex items that have a mix of baselines: --> + <div class="flexContainer"> + <div class="medFont">b</div><div class="bigFont">c</div><div class="bigFont">d</div><div class="medFont">e</div> + </div> + + <div class="flexContainer"> + <div class="bigFont">f</div><div class="smallFont">g</div><div class="medFont">h</div><div class="bigFont">i</div> + </div> + + <!-- Flex container with second line baseline-aligned + (shouldn't make a difference) --> + <div class="flexContainer"> + <div class="smallFont">j</div><div class="bigFont">k</div><div class="bigFont" style="align-self: baseline">l</div><div class="medFont" style="align-self: baseline">m</div> + </div> + n + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-002-expected.html new file mode 100644 index 0000000..c4823fd --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-002-expected.html
@@ -0,0 +1,66 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex, and with the + baseline-aligned items aligned using specific font-size / line-heights, + and with unaligned children taken out of baseline-alignment using + "vertical-align:top". + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-block; + width: 40px; + /* Split testcase's 40px height into 20px of padding-top and 20px of + height, to set aside space for the testcase's (invisible) second line + (which is above the first line, since this is wrap-reverse) */ + height: 20px; + padding-top: 20px; + background: lightblue; + } + .flexContainer > * { + width: 20px; + display: inline-block; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + .unaligned { vertical-align: top } + </style> +</head> +<body> + a + <!-- Flex containers with flex items that have a mix of baselines: --> + <div class="flexContainer medFont"> + <div class="medFont">b</div><div class="bigFont unaligned">c</div> + </div> + + <div class="flexContainer bigFont"> + <div class="bigFont">f</div><div class="smallFont unaligned">g</div> + </div> + + <!-- Flex container with second line baseline-aligned + (shouldn't make a difference) --> + <div class="flexContainer smallFont"> + <div class="smallFont">j</div><div class="bigFont unaligned">k</div> + </div> + n + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-002.html new file mode 100644 index 0000000..1fd6feb --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-002.html
@@ -0,0 +1,74 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a horizontal flex container + with several flex lines (wrapping in the reverse direction). + The spec says this about this case: + [Given that the first line has no baseline-aligned items:] + ...if the flex container has at least one flex item, and its + first flex item has a baseline parallel to the flex + container's main axis, the flex container's main-axis + baseline is that baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a horizontal flex container with multiple flex lines</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-multi-line-horiz-002-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + flex-wrap: wrap-reverse; + width: 40px; + height: 40px; + background: lightblue; + } + .flexContainer > * { + width: 20px; + } + + /* We'll make the second flex line not paint anything, so that the + reference case doesn't need to bother matching it. */ + .flexContainer > *:nth-child(3), + .flexContainer > *:nth-child(4) { + visibility: hidden; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + </style> +</head> +<body> + a + <!-- Flex containers with flex items that have a mix of baselines: --> + <div class="flexContainer"> + <div class="medFont">b</div><div class="bigFont">c</div><div class="bigFont">d</div><div class="medFont">e</div> + </div> + + <div class="flexContainer"> + <div class="bigFont">f</div><div class="smallFont">g</div><div class="medFont">h</div><div class="bigFont">i</div> + </div> + + <!-- Flex container with second line baseline-aligned + (shouldn't make a difference) --> + <div class="flexContainer"> + <div class="smallFont">j</div><div class="bigFont">k</div><div class="bigFont" style="align-self: baseline">l</div><div class="medFont" style="align-self: baseline">m</div> + </div> + n + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-003-expected.html new file mode 100644 index 0000000..b4ba5d7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-003-expected.html
@@ -0,0 +1,85 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + /* We use an outer vertical flex container, wrapping two single-line + flex containers, to match the testcase's multi-line flex container. */ + .outerFlexContainer { + height: 100px; + background: lightgray; + display: inline-flex; + flex-direction: column; + justify-content: center; /* to mimic testcase's "align-content:center" */ + } + .flexContainer { + display: flex; + width: 40px; + } + .flexContainer > * { + width: 20px; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + </style> +</head> +<body> + a + <!-- Flex container with second item in first line baseline-aligned + (should set the container's baseline) --> + <div class="outerFlexContainer"> + <div class="flexContainer"> + <div class="medFont">b</div> + <div class="bigFont" style="align-self: baseline">c</div> + </div> + <div class="flexContainer"> + <div class="medFont">d</div> + <div class="smallFont">e</div> + </div> + </div> + + <!-- Flex container with both items in first line baseline-aligned + (should set the container's baseline) --> + <div class="outerFlexContainer"> + <div class="flexContainer"> + <div class="smallFont" style="align-self: baseline">f</div> + <div class="medFont" style="align-self: baseline">g</div> + </div> + <div class="flexContainer"> + <div class="bigFont">h</div> + <div class="smallFont">i</div> + </div> + </div> + + <!-- Flex container with all items baseline-aligned + (only those on first line should set the container's baseline) --> + <div class="outerFlexContainer"> + <div class="flexContainer" style="align-items: baseline"> + <div class="bigFont">j</div> + <div class="smallFont" style="padding-bottom: 20px">k</div> + </div> + <div class="flexContainer" style="align-items: baseline"> + <div class="smallFont">l</div> + <div class="medFont">m</div> + </div> + </div> + n +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-003.html new file mode 100644 index 0000000..ae84841 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-003.html
@@ -0,0 +1,73 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a horizontal multi-line (wrap) flex container with baseline-aligned items on first line</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-multi-line-horiz-003-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + flex-wrap: wrap; + width: 40px; + height: 100px; + background: lightgray; + + /* Use "align-content", to test that packing space is considered when + getting container's baseline from its first FlexLine:*/ + align-content: center; + } + .flexContainer > * { + width: 20px; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + </style> +</head> +<body> + a + <!-- Flex container with second item in first line baseline-aligned + (should set the container's baseline) --> + <div class="flexContainer"> + <div class="medFont">b</div> + <div class="bigFont" style="align-self: baseline">c</div> + <div class="medFont">d</div> + <div class="smallFont">e</div> + </div> + + <!-- Flex container with both items in first line baseline-aligned + (should set the container's baseline) --> + <div class="flexContainer"> + <div class="smallFont" style="align-self: baseline">f</div> + <div class="medFont" style="align-self: baseline">g</div> + <div class="bigFont">h</div> + <div class="smallFont">i</div> + </div> + + <!-- Flex container with all items baseline-aligned, and with some padding + (only those on first line should set the container's baseline) --> + <div class="flexContainer" style="align-items: baseline"> + <div class="bigFont">j</div> + <div class="smallFont" style="padding-bottom: 20px">k</div> + <div class="smallFont">l</div> + <div class="medFont">m</div> + </div> + n +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-004-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-004-expected.html new file mode 100644 index 0000000..4417fb2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-004-expected.html
@@ -0,0 +1,85 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + /* We use an outer vertical flex container, wrapping two single-line + flex containers, to match the testcase's multi-line flex container. */ + .outerFlexContainer { + height: 100px; + background: lightgray; + display: inline-flex; + flex-direction: column-reverse; + justify-content: center; /* to mimic testcase's "align-content:center" */ + } + .flexContainer { + display: flex; + width: 40px; + } + .flexContainer > * { + width: 20px; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + </style> +</head> +<body> + a + <!-- Flex container with second item in first line baseline-aligned + (should set the container's baseline) --> + <div class="outerFlexContainer"> + <div class="flexContainer"> + <div class="medFont">b</div> + <div class="bigFont" style="align-self: baseline">c</div> + </div> + <div class="flexContainer"> + <div class="medFont">d</div> + <div class="smallFont">e</div> + </div> + </div> + + <!-- Flex container with both items in first line baseline-aligned + (should set the container's baseline) --> + <div class="outerFlexContainer"> + <div class="flexContainer"> + <div class="smallFont" style="align-self: baseline">f</div> + <div class="medFont" style="align-self: baseline">g</div> + </div> + <div class="flexContainer"> + <div class="bigFont">h</div> + <div class="smallFont">i</div> + </div> + </div> + + <!-- Flex container with all items baseline-aligned + (only those on first line should set the container's baseline) --> + <div class="outerFlexContainer"> + <div class="flexContainer" style="align-items: baseline"> + <div class="bigFont">j</div> + <div class="smallFont" style="padding-bottom: 20px">k</div> + </div> + <div class="flexContainer" style="align-items: baseline"> + <div class="smallFont">l</div> + <div class="medFont">m</div> + </div> + </div> + n +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-004.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-004.html new file mode 100644 index 0000000..49f74313 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-horiz-004.html
@@ -0,0 +1,73 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a horizontal multi-line (wrap-reverse) flex container with baseline-aligned items on first line</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-multi-line-horiz-004-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + flex-wrap: wrap-reverse; + width: 40px; + height: 100px; + background: lightgray; + + /* Use "align-content", to test that packing space is considered when + getting container's baseline from its first FlexLine:*/ + align-content: center; + } + .flexContainer > * { + width: 20px; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + </style> +</head> +<body> + a + <!-- Flex container with second item in first line baseline-aligned + (should set the container's baseline) --> + <div class="flexContainer"> + <div class="medFont">b</div> + <div class="bigFont" style="align-self: baseline">c</div> + <div class="medFont">d</div> + <div class="smallFont">e</div> + </div> + + <!-- Flex container with both items in first line baseline-aligned + (should set the container's baseline) --> + <div class="flexContainer"> + <div class="smallFont" style="align-self: baseline">f</div> + <div class="medFont" style="align-self: baseline">g</div> + <div class="bigFont">h</div> + <div class="smallFont">i</div> + </div> + + <!-- Flex container with all items baseline-aligned, and with some padding + (only those on first line should set the container's baseline) --> + <div class="flexContainer" style="align-items: baseline"> + <div class="bigFont">j</div> + <div class="smallFont" style="padding-bottom: 20px">k</div> + <div class="smallFont">l</div> + <div class="medFont">m</div> + </div> + n +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-001-expected.html new file mode 100644 index 0000000..f565034 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-001-expected.html
@@ -0,0 +1,62 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex, and with the + baseline-aligned items aligned using specific font-size / line-heights, + and with unaligned children taken out of baseline-alignment using + "float: left". + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-block; + width: 40px; + height: 40px; + background: lightblue; + } + .flexContainer > * { + height: 20px; + display: inline-block; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + .unaligned { float: left } + </style> +</head> +<body> + a + <!-- Flex containers with flex items that have a mix of baselines: --> + <div class="flexContainer medFont"> + <div class="medFont">b</div><br><div class="bigFont unaligned">c</div> + </div> + + <div class="flexContainer bigFont"> + <div class="bigFont">f</div><br><div class="smallFont unaligned">g</div> + </div> + + <!-- Flex container with second line baseline-aligned + (shouldn't make a difference) --> + <div class="flexContainer smallFont"> + <div class="smallFont">j</div><br><div class="bigFont unaligned">k</div> + </div> + n + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-001.html new file mode 100644 index 0000000..4a4fb5f3f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-001.html
@@ -0,0 +1,75 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a vertical flex container + with several flex lines. + The spec says this about this case: + [Given that the first line has no baseline-aligned items:] + ...if the flex container has at least one flex item, and its + first flex item has a baseline parallel to the flex + container's main axis, the flex container's main-axis + baseline is that baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a vertical flex container with multiple flex lines</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-multi-line-vert-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + flex-direction: column; + flex-wrap: wrap; + width: 40px; + height: 40px; + background: lightblue; + } + .flexContainer > * { + height: 20px; + } + + /* We'll make the second flex line not paint anything, so that the + reference case doesn't need to bother matching it. */ + .flexContainer > *:nth-child(3), + .flexContainer > *:nth-child(4) { + visibility: hidden; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + </style> +</head> +<body> + a + <!-- Flex containers with flex items that have a mix of baselines: --> + <div class="flexContainer"> + <div class="medFont">b</div><div class="bigFont">c</div><div class="bigFont">d</div><div class="medFont">e</div> + </div> + + <div class="flexContainer"> + <div class="bigFont">f</div><div class="smallFont">g</div><div class="medFont">h</div><div class="bigFont">i</div> + </div> + + <!-- Flex container with second line baseline-aligned + (shouldn't make a difference) --> + <div class="flexContainer"> + <div class="smallFont">j</div><div class="bigFont">k</div><div class="bigFont" style="align-self: baseline">l</div><div class="medFont" style="align-self: baseline">m</div> + </div> + n + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-002-expected.html new file mode 100644 index 0000000..b952ce3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-002-expected.html
@@ -0,0 +1,68 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex, and with the + baseline-aligned items aligned using specific font-size / line-heights, + and with unaligned children taken out of baseline-alignment using + "float: left". + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-block; + /* Split testcase's 40px width into 20px of padding-left and 20px of + width, to set aside space for the testcase's (invisible) second line + (which is to the left of the first line, since this is wrap-reverse) + */ + width: 20px; + padding-left: 20px; + height: 40px; + background: lightblue; + } + .flexContainer > * { + width: 20px; + height: 20px; + display: inline-block; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + .unaligned { float: left } + </style> +</head> +<body> + a + <!-- Flex containers with flex items that have a mix of baselines: --> + <div class="flexContainer medFont"> + <div class="medFont">b</div><br><div class="bigFont unaligned">c</div> + </div> + + <div class="flexContainer bigFont"> + <div class="bigFont">f</div><br><div class="smallFont unaligned">g</div> + </div> + + <!-- Flex container with second line baseline-aligned + (shouldn't make a difference) --> + <div class="flexContainer smallFont"> + <div class="smallFont">j</div><br><div class="bigFont unaligned">k</div> + </div> + n + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-002.html new file mode 100644 index 0000000..c985c2b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-line-vert-002.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a vertical flex container + with several flex lines (wrapping in the reverse direction). + The spec says this about this case: + [Given that the first line has no baseline-aligned items:] + ...if the flex container has at least one flex item, and its + first flex item has a baseline parallel to the flex + container's main axis, the flex container's main-axis + baseline is that baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a vertical flex container with multiple flex lines</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-multi-line-vert-002-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + flex-direction: column; + flex-wrap: wrap-reverse; + width: 40px; + height: 40px; + background: lightblue; + } + .flexContainer > * { + width: 20px; + height: 20px; + } + + /* We'll make the second flex line not paint anything, so that the + reference case doesn't need to bother matching it. */ + .flexContainer > *:nth-child(3), + .flexContainer > *:nth-child(4) { + visibility: hidden; + } + + .smallFont { + font-size: 8px; + line-height: 8px; + } + .medFont { + font-size: 12px; + line-height: 12px; + } + .bigFont { + font-size: 16px; + line-height: 16px; + } + </style> +</head> +<body> + a + <!-- Flex containers with flex items that have a mix of baselines: --> + <div class="flexContainer"> + <div class="medFont">b</div><div class="bigFont">c</div><div class="bigFont">d</div><div class="medFont">e</div> + </div> + + <div class="flexContainer"> + <div class="bigFont">f</div><div class="smallFont">g</div><div class="medFont">h</div><div class="bigFont">i</div> + </div> + + <!-- Flex container with second line baseline-aligned + (shouldn't make a difference) --> + <div class="flexContainer"> + <div class="smallFont">j</div><div class="bigFont">k</div><div class="bigFont" style="align-self: baseline">l</div><div class="medFont" style="align-self: baseline">m</div> + </div> + n + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001a-expected.html new file mode 100644 index 0000000..8bd2768 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001a-expected.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex. --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-block; + height: 16px; + width: 16px; + background: purple; + border: 0px dotted black; + /* (Elements that want a border will set their border-width.) */ + } + </style> +</head> +<body> + A + <div class="flexContainer">a</div> + <div class="flexContainer" style="padding-bottom: 20px">a</div> + <div class="flexContainer" style="padding: 10px">a</div> + <div class="flexContainer" style="border-width: 3px">a</div> + <div class="flexContainer" style="border-bottom-width: 4px">a</div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001a.html new file mode 100644 index 0000000..46e32ded --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001a.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a horizontal flex container + with one flex item. This is the main-axis baseline. The spec says this + about this case: + ...if the flex container has at least one flex item, and its + first flex item has a baseline parallel to the flex + container's main axis, the flex container's main-axis + baseline is that baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a horizontal flex container with one flex item</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-single-item-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + height: 16px; + width: 16px; + background: purple; + border: 0px dotted black; + /* (Elements that want a border will set their border-width.) */ + } + </style> +</head> +<body> + A + <div class="flexContainer">a</div> + <div class="flexContainer" style="padding-bottom: 20px">a</div> + <div class="flexContainer" style="padding: 10px">a</div> + <div class="flexContainer" style="border-width: 3px">a</div> + <div class="flexContainer" style="border-bottom-width: 4px">a</div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001b-expected.html new file mode 100644 index 0000000..8bd2768 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001b-expected.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, using inline-block instead of inline-flex. --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-block; + height: 16px; + width: 16px; + background: purple; + border: 0px dotted black; + /* (Elements that want a border will set their border-width.) */ + } + </style> +</head> +<body> + A + <div class="flexContainer">a</div> + <div class="flexContainer" style="padding-bottom: 20px">a</div> + <div class="flexContainer" style="padding: 10px">a</div> + <div class="flexContainer" style="border-width: 3px">a</div> + <div class="flexContainer" style="border-bottom-width: 4px">a</div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001b.html new file mode 100644 index 0000000..f63d18bc --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-single-item-001b.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase for how we compute the baseline of a vertical flex container + with one flex item. This is the cross-axis baseline. The spec says this + about this case: + If the flex container has at least one flex item, and its + first flex item has a baseline parallel to the flex + container's cross axis, the flex container's cross-axis + baseline is that baseline. + --> +<html> +<head> + <title>CSS Test: Testing the baseline of a vertical flex container with one flex item</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines"> + <link rel="match" href="flexbox-baseline-single-item-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: inline-flex; + flex-direction: column; + height: 16px; + width: 16px; + background: purple; + border: 0px dotted black; + /* (Elements that want a border will set their border-width.) */ + } + </style> +</head> +<body> + A + <div class="flexContainer">a</div> + <div class="flexContainer" style="padding-bottom: 20px">a</div> + <div class="flexContainer" style="padding: 10px">a</div> + <div class="flexContainer" style="border-width: 3px">a</div> + <div class="flexContainer" style="border-bottom-width: 4px">a</div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001-expected.xhtml new file mode 100644 index 0000000..433f0005 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001-expected.xhtml
@@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; } + div.flexbox { + border: 1px dashed blue; + width: 200px; + } + div.a { + display: inline-block; + background: lightgreen; + } + div.b { + display: inline-block; + background: yellow; + } + div.c { + display: inline-block; + background: orange; + } + div.auto { + display: inline-block; + background: pink; + } + div.inflex { + display: inline-block; + background: gray; + } + div.spacer { + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="width: 80px"/><div class="b" style="width: 120px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 62.5px"/><div class="c" style="width: 137.5px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 185px"/><div class="auto"> + <div class="spacer" style="width: 15px"/></div> + </div> + <div class="flexbox"> + <div class="b" style="width: 76px"/><div class="c" style="width: 124px"/> + </div> + <div class="flexbox"> + <div class="b" style="width: 170px"/><div class="auto"> + <div class="spacer" style="width: 30px"/></div> + </div> + <div class="flexbox"> + <div class="a" style="width: 45px"/><div class="b" style="width: 50px" + /><div class="inflex" style="width: 20px"/><div class="c" style="width: 85px"/> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001.xhtml new file mode 100644 index 0000000..6b770987 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-horiz-001.xhtml
@@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with blocks as flex items in a horizontal flex container, with + various "flex" values and various combinations of the items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on block flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-block-horiz-001-ref.xhtml"/> + <style> + div { height: 100px; } + div.flexbox { + border: 1px dashed blue; + width: 200px; + font-size: 10px; + display: flex; + } + div.a { + flex: 1 0 30px; + background: lightgreen; + } + div.b { + flex: 2 0 20px; + background: yellow; + } + div.c { + flex: 3 0 40px; + background: orange; + } + div.flexNone { + flex: none; + background: pink; + } + div.flexBasis { + flex: 0 0 20px; + background: gray; + } + div.spacer { + display: inline-block; + width: 15px; + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"><div class="a"></div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div class="c"/></div> + <div class="flexbox"><div class="a"/> + <div class="flexNone"><div class="spacer"/></div> + </div> + <div class="flexbox"><div class="b"/><div class="c"/></div> + <div class="flexbox"><div class="b"/> + <div class="flexNone"><div class="spacer"/><div class="spacer"/></div> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001-expected.xhtml new file mode 100644 index 0000000..0e62f74 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001-expected.xhtml
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { width: 50px; } + div.flexbox { + border: 1px dashed blue; + float: left; + } + div.a { + background: lightgreen; + } + div.b { + background: yellow; + } + div.c { + background: orange; + } + div.auto { + background: pink; + } + div.inflex { + background: gray; + } + div.spacer { + width: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="height: 80px"/><div class="b" style="height: 120px"/> + </div> + <div class="flexbox"> + <div class="a" style="height: 62.5px"/><div class="c" style="height: 137.5px"/> + </div> + <div class="flexbox"> + <div class="a" style="height: 185px"/><div class="auto"> + <div class="spacer" style="height: 15px"/></div> + </div> + <div class="flexbox"> + <div class="b" style="height: 76px"/><div class="c" style="height: 124px"/> + </div> + <div class="flexbox"> + <div class="b" style="height: 170px"/><div class="auto"> + <div class="spacer" style="height: 30px"/></div> + </div> + <div class="flexbox"> + <div class="a" style="height: 45px"/><div class="b" style="height: 50px" + /><div class="inflex" style="height: 20px"/><div class="c" style="height: 85px"/> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001.xhtml new file mode 100644 index 0000000..129d2c6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-block-vert-001.xhtml
@@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with blocks as flex items in a vertical flex container, with + various "flex" values and various combinations of the items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on block flex items in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-block-vert-001-ref.xhtml"/> + <style> + div { width: 50px; } + div.flexbox { + float: left; + border: 1px dashed blue; + height: 200px; + font-size: 10px; + display: flex; + flex-direction: column; + } + div.a { + flex: 1 0 30px; + background: lightgreen; + } + div.b { + flex: 2 0 20px; + background: yellow; + } + div.c { + flex: 3 0 40px; + background: orange; + } + div.flexNone { + flex: none; + background: pink; + } + div.flexBasis { + flex: 0 0 20px; + background: gray; + } + div.spacer { + width: 15px; + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"><div class="a"></div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div class="c"/></div> + <div class="flexbox"><div class="a"/> + <div class="flexNone"><div class="spacer"/></div> + </div> + <div class="flexbox"><div class="b"/><div class="c"/></div> + <div class="flexbox"><div class="b"/> + <div class="flexNone"><div class="spacer"/><div class="spacer"/></div> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001-expected.xhtml new file mode 100644 index 0000000..4acfd4a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001-expected.xhtml
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + margin-bottom: 5px; + line-height: 8px; + } + canvas { + width: 10px; + height: 20px; + border: 1px dotted green; + } + </style> + </head> + <body> + <div class="flexbox"> + <canvas/> + </div> + + <div class="flexbox" style="height: 22px"> + some words + <canvas style="float:right"/> + </div> + + <div class="flexbox"> + <canvas style="width: 122.5px" + /><canvas style="width: 73.5px"/> + </div> + + <div class="flexbox"> + <canvas style="width: 93px" + /><canvas style="width: 103px"/> + </div> + + <div class="flexbox"> + <canvas style="width: 114px" + /><canvas style="width: 82px"/> + </div> + + <div class="flexbox"> + <canvas style="width: 106px" + /><canvas style="width: 90px"/> + </div> + + <div class="flexbox"> + <canvas style="width: 46px" + /><canvas style="width: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001.xhtml new file mode 100644 index 0000000..d68a925 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-horiz-001.xhtml
@@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that canvas elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on canvas flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-canvas-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + margin-bottom: 5px; + line-height: 8px; + } + canvas { + min-width: 0; + width: 10px; + height: 20px; + border: 1px dotted green; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <canvas/> + </div> + + <!-- B) Text and a canvas (ensure they aren't merged into one flex item) --> + <div class="flexbox"> + some words <canvas/> + </div> + + <!-- C) Two canvas elements, getting stretched by different ratios, from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <canvas style="flex: 5"/> + <canvas style="flex: 3"/> + </div> + + <!-- D) Two canvas elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <canvas style="width: 33px; flex: 2 auto"/> + <canvas style="width: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <canvas style="width: 150px; flex: 1 4 auto"/> + <canvas style="width: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-width --> + <!-- Same as (D), except we've added a max-width on the second element. + --> + <div class="flexbox"> + <canvas style="width: 33px; flex: 2 auto"/> + <canvas style="width: 13px; max-width: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-width --> + <!-- Same as (C), except we've added a min-width on the second element. + --> + <div class="flexbox"> + <canvas style="width: 33px; flex: 2 auto"/> + <canvas style="width: 13px; min-width: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001-expected.xhtml new file mode 100644 index 0000000..7b781f2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001-expected.xhtml
@@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + width: 22px; + float: left; + margin-right: 10px; + background: lightgreen; + line-height: 0; + } + canvas { + width: 20px; + height: 10px; + border: 1px dotted green; + } + </style> + </head> + <body> + <div class="flexbox"> + <canvas/> + </div> + + <div class="flexbox"> + <div style="font: 8px monospace; height: 188px"> + a b + </div> + <canvas/> + </div> + + <div class="flexbox"> + <canvas style="height: 122.5px" + /><canvas style="height: 73.5px"/> + </div> + + <div class="flexbox"> + <canvas style="height: 93px" + /><canvas style="height: 103px"/> + </div> + + <div class="flexbox"> + <canvas style="height: 114px" + /><canvas style="height: 82px"/> + </div> + + <div class="flexbox"> + <canvas style="height: 106px" + /><canvas style="height: 90px"/> + </div> + + <div class="flexbox"> + <canvas style="height: 46px" + /><canvas style="height: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001.xhtml new file mode 100644 index 0000000..d867635c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-canvas-vert-001.xhtml
@@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that canvas elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on canvas flex items in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-canvas-vert-001-ref.xhtml"/> + <style> + div.flexbox { + height: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + flex-direction: column; + float: left; + margin-right: 10px; + font: 8px monospace; + } + canvas { + width: 20px; + height: 10px; + min-height: 0; + border: 1px dotted green; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <canvas/> + </div> + + <!-- B) Text and a canvas (ensure they aren't merged into one flex item) --> + <div class="flexbox"> + a b <canvas/> + </div> + + <!-- C) Two canvas elements, getting stretched by different ratios, from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <canvas style="flex: 5"/> + <canvas style="flex: 3"/> + </div> + + <!-- D) Two canvas elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <canvas style="height: 33px; flex: 2 auto"/> + <canvas style="height: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <canvas style="height: 150px; flex: 1 4 auto"/> + <canvas style="height: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-height --> + <!-- Same as (D), except we've added a max-height on the second element. + --> + <div class="flexbox"> + <canvas style="height: 33px; flex: 2 auto"/> + <canvas style="height: 13px; max-height: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-height --> + <!-- Same as (C), except we've added a min-height on the second element. + --> + <div class="flexbox"> + <canvas style="height: 33px; flex: 2 auto"/> + <canvas style="height: 13px; min-height: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-horiz-001-expected.xhtml new file mode 100644 index 0000000..a1d1c99 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-horiz-001-expected.xhtml
@@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + margin-bottom: 5px; + line-height: 8px; + clear: all; + height: 22px; + } + fieldset { + width: 10px; + height: 20px; + border: 1px dotted green; + margin: 0; + padding: 0; + float: left; + } + </style> + </head> + <body> + <div class="flexbox"> + <fieldset/> + </div> + + <div class="flexbox" style="height: 22px"> + some words + <fieldset style="float:right"/> + </div> + + <div class="flexbox"> + <fieldset style="width: 122.5px" + /><fieldset style="width: 73.5px"/> + </div> + + <div class="flexbox"> + <fieldset style="width: 93px" + /><fieldset style="width: 103px"/> + </div> + + <div class="flexbox"> + <fieldset style="width: 114px" + /><fieldset style="width: 82px"/> + </div> + + <div class="flexbox"> + <fieldset style="width: 106px" + /><fieldset style="width: 90px"/> + </div> + + <div class="flexbox"> + <fieldset style="width: 46px" + /><fieldset style="width: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-horiz-001.xhtml new file mode 100644 index 0000000..acdcfddb --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-horiz-001.xhtml
@@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that fieldset elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on fieldset flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-fieldset-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + margin-bottom: 5px; + line-height: 8px; + } + fieldset { + min-width: 0; + width: 10px; + height: 20px; + border: 1px dotted green; + margin: 0; + padding: 0; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <fieldset/> + </div> + + <!-- B) Text and a fieldset (ensure they aren't merged into one flex item) + --> + <div class="flexbox"> + some words <fieldset/> + </div> + + <!-- C) Two fieldset elements, getting stretched by different ratios, + from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <fieldset style="flex: 5"/> + <fieldset style="flex: 3"/> + </div> + + <!-- D) Two fieldset elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <fieldset style="width: 33px; flex: 2 auto"/> + <fieldset style="width: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <fieldset style="width: 150px; flex: 1 4 auto"/> + <fieldset style="width: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-width --> + <!-- Same as (D), except we've added a max-width on the second element. + --> + <div class="flexbox"> + <fieldset style="width: 33px; flex: 2 auto"/> + <fieldset style="width: 13px; max-width: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-width --> + <!-- Same as (C), except we've added a min-width on the second element. + --> + <div class="flexbox"> + <fieldset style="width: 33px; flex: 2 auto"/> + <fieldset style="width: 13px; min-width: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-vert-001-expected.xhtml new file mode 100644 index 0000000..d2e510c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-vert-001-expected.xhtml
@@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + width: 22px; + float: left; + margin-right: 10px; + background: lightgreen; + line-height: 0; + } + fieldset { + width: 20px; + height: 10px; + border: 1px dotted green; + margin: 0; + padding: 0; + } + </style> + </head> + <body> + <div class="flexbox"> + <fieldset/> + </div> + + <div class="flexbox"> + <div style="font: 8px monospace; height: 188px"> + a b + </div> + <fieldset/> + </div> + + <div class="flexbox"> + <fieldset style="height: 122.5px" + /><fieldset style="height: 73.5px"/> + </div> + + <div class="flexbox"> + <fieldset style="height: 93px" + /><fieldset style="height: 103px"/> + </div> + + <div class="flexbox"> + <fieldset style="height: 114px" + /><fieldset style="height: 82px"/> + </div> + + <div class="flexbox"> + <fieldset style="height: 106px" + /><fieldset style="height: 90px"/> + </div> + + <div class="flexbox"> + <fieldset style="height: 46px" + /><fieldset style="height: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-vert-001.xhtml new file mode 100644 index 0000000..ebd6d4e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-fieldset-vert-001.xhtml
@@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that fieldset elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on fieldset flex items in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-fieldset-vert-001-ref.xhtml"/> + <style> + div.flexbox { + height: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + flex-direction: column; + float: left; + margin-right: 10px; + font: 8px monospace; + } + fieldset { + width: 20px; + height: 10px; + min-height: 0; + border: 1px dotted green; + margin: 0; + padding: 0; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <fieldset/> + </div> + + <!-- B) Text and a fieldset (ensure they aren't merged into one flex item) + --> + <div class="flexbox"> + a b <fieldset/> + </div> + + <!-- C) Two fieldset elements, getting stretched by different ratios, + from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <fieldset style="flex: 5"/> + <fieldset style="flex: 3"/> + </div> + + <!-- D) Two fieldset elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <fieldset style="height: 33px; flex: 2 auto"/> + <fieldset style="height: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <fieldset style="height: 150px; flex: 1 4 auto"/> + <fieldset style="height: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-height --> + <!-- Same as (D), except we've added a max-height on the second element. + --> + <div class="flexbox"> + <fieldset style="height: 33px; flex: 2 auto"/> + <fieldset style="height: 13px; max-height: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-height --> + <!-- Same as (C), except we've added a min-height on the second element. + --> + <div class="flexbox"> + <fieldset style="height: 33px; flex: 2 auto"/> + <fieldset style="height: 13px; min-height: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-horiz-001-expected.xhtml new file mode 100644 index 0000000..f8041f0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-horiz-001-expected.xhtml
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + margin-bottom: 5px; + line-height: 8px; + } + iframe { + width: 10px; + height: 20px; + border: 1px dotted green; + } + </style> + </head> + <body> + <div class="flexbox"> + <iframe/> + </div> + + <div class="flexbox" style="height: 22px"> + some words + <iframe style="float:right"/> + </div> + + <div class="flexbox"> + <iframe style="width: 122.5px" + /><iframe style="width: 73.5px"/> + </div> + + <div class="flexbox"> + <iframe style="width: 93px" + /><iframe style="width: 103px"/> + </div> + + <div class="flexbox"> + <iframe style="width: 114px" + /><iframe style="width: 82px"/> + </div> + + <div class="flexbox"> + <iframe style="width: 106px" + /><iframe style="width: 90px"/> + </div> + + <div class="flexbox"> + <iframe style="width: 46px" + /><iframe style="width: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-horiz-001.xhtml new file mode 100644 index 0000000..1a3e2bc --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-horiz-001.xhtml
@@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that iframe elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on iframe flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-iframe-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + margin-bottom: 5px; + line-height: 8px; + } + iframe { + min-width: 0; + width: 10px; + height: 20px; + border: 1px dotted green; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <iframe/> + </div> + + <!-- B) Text and an iframe (ensure they aren't merged into one flex item) + --> + <div class="flexbox"> + some words <iframe/> + </div> + + <!-- C) Two iframe elements, getting stretched by different ratios, from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <iframe style="flex: 5"/> + <iframe style="flex: 3"/> + </div> + + <!-- D) Two iframe elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <iframe style="width: 33px; flex: 2 auto"/> + <iframe style="width: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <iframe style="width: 150px; flex: 1 4 auto"/> + <iframe style="width: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-width --> + <!-- Same as (D), except we've added a max-width on the second element. + --> + <div class="flexbox"> + <iframe style="width: 33px; flex: 2 auto"/> + <iframe style="width: 13px; max-width: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-width --> + <!-- Same as (C), except we've added a min-width on the second element. + --> + <div class="flexbox"> + <iframe style="width: 33px; flex: 2 auto"/> + <iframe style="width: 13px; min-width: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-vert-001-expected.xhtml new file mode 100644 index 0000000..a30d3bc --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-vert-001-expected.xhtml
@@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + width: 22px; + float: left; + margin-right: 10px; + background: lightgreen; + line-height: 0; + } + iframe { + width: 20px; + height: 10px; + border: 1px dotted green; + } + </style> + </head> + <body> + <div class="flexbox"> + <iframe/> + </div> + + <div class="flexbox"> + <div style="font: 8px monospace; height: 188px"> + a b + </div> + <iframe/> + </div> + + <div class="flexbox"> + <iframe style="height: 122.5px" + /><iframe style="height: 73.5px"/> + </div> + + <div class="flexbox"> + <iframe style="height: 93px" + /><iframe style="height: 103px"/> + </div> + + <div class="flexbox"> + <iframe style="height: 114px" + /><iframe style="height: 82px"/> + </div> + + <div class="flexbox"> + <iframe style="height: 106px" + /><iframe style="height: 90px"/> + </div> + + <div class="flexbox"> + <iframe style="height: 46px" + /><iframe style="height: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-vert-001.xhtml new file mode 100644 index 0000000..8ffe68572 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-iframe-vert-001.xhtml
@@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that iframe elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on iframe flex items in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-iframe-vert-001-ref.xhtml"/> + <style> + div.flexbox { + height: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + flex-direction: column; + float: left; + margin-right: 10px; + font: 8px monospace; + } + iframe { + width: 20px; + height: 10px; + min-height: 0; + border: 1px dotted green; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <iframe/> + </div> + + <!-- B) Text and an iframe (ensure they aren't merged into one flex item) + --> + <div class="flexbox"> + a b <iframe/> + </div> + + <!-- C) Two iframe elements, getting stretched by different ratios, from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <iframe style="flex: 5"/> + <iframe style="flex: 3"/> + </div> + + <!-- D) Two iframe elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <iframe style="height: 33px; flex: 2 auto"/> + <iframe style="height: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <iframe style="height: 150px; flex: 1 4 auto"/> + <iframe style="height: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-height --> + <!-- Same as (D), except we've added a max-height on the second element. + --> + <div class="flexbox"> + <iframe style="height: 33px; flex: 2 auto"/> + <iframe style="height: 13px; max-height: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-height --> + <!-- Same as (C), except we've added a min-height on the second element. + --> + <div class="flexbox"> + <iframe style="height: 33px; flex: 2 auto"/> + <iframe style="height: 13px; min-height: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-horiz-001-expected.xhtml new file mode 100644 index 0000000..94cec0a1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-horiz-001-expected.xhtml
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + margin-bottom: 5px; + line-height: 8px; + } + img { + width: 10px; + height: 20px; + border: 1px dotted green; + } + </style> + </head> + <body> + <div class="flexbox"> + <img src="support/solidblue.png"/> + </div> + + <div class="flexbox" style="height: 22px"> + some words + <img src="support/solidblue.png" style="float:right"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 122.5px" + /><img src="support/solidblue.png" style="width: 73.5px"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 93px" + /><img src="support/solidblue.png" style="width: 103px"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 114px" + /><img src="support/solidblue.png" style="width: 82px"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 106px" + /><img src="support/solidblue.png" style="width: 90px"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 46px" + /><img src="support/solidblue.png" style="width: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-horiz-001.xhtml new file mode 100644 index 0000000..db6e349 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-horiz-001.xhtml
@@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that img elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on img flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-img-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + margin-bottom: 5px; + line-height: 8px; + } + img { + min-width: 0; + width: 10px; + height: 20px; + border: 1px dotted green; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <img src="support/solidblue.png"/> + </div> + + <!-- B) Text and an img (ensure they aren't merged into one flex item) --> + <div class="flexbox"> + some words <img src="support/solidblue.png"/> + </div> + + <!-- C) Two img elements, getting stretched by different ratios, from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="flex: 5"/> + <img src="support/solidblue.png" style="flex: 3"/> + </div> + + <!-- D) Two img elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 33px; flex: 2 auto"/> + <img src="support/solidblue.png" style="width: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 150px; flex: 1 4 auto"/> + <img src="support/solidblue.png" style="width: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-width --> + <!-- Same as (D), except we've added a max-width on the second element. + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 33px; flex: 2 auto"/> + <img src="support/solidblue.png" + style="width: 13px; max-width: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-width --> + <!-- Same as (C), except we've added a min-width on the second element. + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="width: 33px; flex: 2 auto"/> + <img src="support/solidblue.png" + style="width: 13px; min-width: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-vert-001-expected.xhtml new file mode 100644 index 0000000..57be163e2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-vert-001-expected.xhtml
@@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + width: 22px; + float: left; + margin-right: 10px; + background: lightgreen; + line-height: 0; + } + img { + width: 20px; + height: 10px; + border: 1px dotted green; + } + </style> + </head> + <body> + <div class="flexbox"> + <img src="support/solidblue.png"/> + </div> + + <div class="flexbox"> + <div style="font: 8px monospace; height: 188px"> + a b + </div> + <img src="support/solidblue.png"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 122.5px" + /><img src="support/solidblue.png" style="height: 73.5px"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 93px" + /><img src="support/solidblue.png" style="height: 103px"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 114px" + /><img src="support/solidblue.png" style="height: 82px"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 106px" + /><img src="support/solidblue.png" style="height: 90px"/> + </div> + + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 46px" + /><img src="support/solidblue.png" style="height: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-vert-001.xhtml new file mode 100644 index 0000000..1777386 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-img-vert-001.xhtml
@@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that img elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on img flex items in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-img-vert-001-ref.xhtml"/> + <style> + div.flexbox { + height: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + flex-direction: column; + float: left; + margin-right: 10px; + font: 8px monospace; + } + img { + width: 20px; + height: 10px; + min-height: 0; + border: 1px dotted green; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <img src="support/solidblue.png"/> + </div> + + <!-- B) Text and an img (ensure they aren't merged into one flex item) --> + <div class="flexbox"> + a b <img src="support/solidblue.png"/> + </div> + + <!-- C) Two img elements, getting stretched by different ratios, from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="flex: 5"/> + <img src="support/solidblue.png" style="flex: 3"/> + </div> + + <!-- D) Two img elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 33px; flex: 2 auto"/> + <img src="support/solidblue.png" style="height: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 150px; flex: 1 4 auto"/> + <img src="support/solidblue.png" style="height: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-height --> + <!-- Same as (D), except we've added a max-height on the second element. + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 33px; flex: 2 auto"/> + <img src="support/solidblue.png" + style="height: 13px; max-height: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-height --> + <!-- Same as (C), except we've added a min-height on the second element. + --> + <div class="flexbox"> + <img src="support/solidblue.png" style="height: 33px; flex: 2 auto"/> + <img src="support/solidblue.png" + style="height: 13px; min-height: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-horiz-001-expected.xhtml new file mode 100644 index 0000000..638f568 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-horiz-001-expected.xhtml
@@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + margin-bottom: 5px; + line-height: 8px; + } + textarea { + width: 10px; + height: 20px; + background: white; + border-radius: 0; + border: 1px dotted green; + padding: 0; + } + </style> + </head> + <body> + <div class="flexbox"> + <textarea/> + </div> + + <div class="flexbox" style="height: 24px"> + some words + <textarea style="float:right"/> + </div> + + <div class="flexbox"> + <textarea style="width: 122.5px" + /><textarea style="width: 73.5px"/> + </div> + + <div class="flexbox"> + <textarea style="width: 93px" + /><textarea style="width: 103px"/> + </div> + + <div class="flexbox"> + <textarea style="width: 114px" + /><textarea style="width: 82px"/> + </div> + + <div class="flexbox"> + <textarea style="width: 106px" + /><textarea style="width: 90px"/> + </div> + + <div class="flexbox"> + <textarea style="width: 46px" + /><textarea style="width: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-horiz-001.xhtml new file mode 100644 index 0000000..4744cc6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-horiz-001.xhtml
@@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that textarea elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on textarea flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-textarea-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + margin-bottom: 5px; + line-height: 8px; + } + textarea { + min-width: 0; + width: 10px; + height: 20px; + background: white; + border-radius: 0; + border: 1px dotted green; + padding: 0; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <textarea/> + </div> + + <!-- B) Text and a textarea (ensure they aren't merged into one flex item) + --> + <div class="flexbox"> + some words <textarea/> + </div> + + <!-- C) Two textarea elements, getting stretched by different ratios, + from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <textarea style="flex: 5"/> + <textarea style="flex: 3"/> + </div> + + <!-- D) Two textarea elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <textarea style="width: 33px; flex: 2 auto"/> + <textarea style="width: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <textarea style="width: 150px; flex: 1 4 auto"/> + <textarea style="width: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-width --> + <!-- Same as (D), except we've added a max-width on the second element. + --> + <div class="flexbox"> + <textarea style="width: 33px; flex: 2 auto"/> + <textarea + style="width: 13px; max-width: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-width --> + <!-- Same as (C), except we've added a min-width on the second element. + --> + <div class="flexbox"> + <textarea style="width: 33px; flex: 2 auto"/> + <textarea + style="width: 13px; min-width: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-vert-001-expected.xhtml new file mode 100644 index 0000000..a4c3e00 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-vert-001-expected.xhtml
@@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + width: 22px; + float: left; + margin-right: 10px; + background: lightgreen; + line-height: 0; + } + textarea { + width: 20px; + height: 10px; + background: white; + border-radius: 0; + border: 1px dotted green; + margin: 0; + padding: 0; + } + </style> + </head> + <body> + <div class="flexbox"> + <textarea/> + </div> + + <div class="flexbox"> + <div style="font: 8px monospace; height: 188px"> + a b + </div> + <textarea/> + </div> + + <div class="flexbox"> + <textarea style="height: 122.5px" + /><textarea style="height: 73.5px"/> + </div> + + <div class="flexbox"> + <textarea style="height: 93px" + /><textarea style="height: 103px"/> + </div> + + <div class="flexbox"> + <textarea style="height: 114px" + /><textarea style="height: 82px"/> + </div> + + <div class="flexbox"> + <textarea style="height: 106px" + /><textarea style="height: 90px"/> + </div> + + <div class="flexbox"> + <textarea style="height: 46px" + /><textarea style="height: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-vert-001.xhtml new file mode 100644 index 0000000..f5d7cf33 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-textarea-vert-001.xhtml
@@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that textarea elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on textarea flex items in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-textarea-vert-001-ref.xhtml"/> + <style> + div.flexbox { + height: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + flex-direction: column; + float: left; + margin-right: 10px; + font: 8px monospace; + } + textarea { + width: 20px; + height: 10px; + min-height: 0; + background: white; + border-radius: 0; + border: 1px dotted green; + margin: 0; + padding: 0; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <textarea/> + </div> + + <!-- B) Text and a textarea (ensure they aren't merged into one flex item) + --> + <div class="flexbox"> + a b <textarea/> + </div> + + <!-- C) Two textarea elements, getting stretched by different ratios, + from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <textarea style="flex: 5"/> + <textarea style="flex: 3"/> + </div> + + <!-- D) Two textarea elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <textarea style="height: 33px; flex: 2 auto"/> + <textarea style="height: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <textarea style="height: 150px; flex: 1 4 auto"/> + <textarea style="height: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-height --> + <!-- Same as (D), except we've added a max-height on the second element. + --> + <div class="flexbox"> + <textarea style="height: 33px; flex: 2 auto"/> + <textarea + style="height: 13px; max-height: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-height --> + <!-- Same as (C), except we've added a min-height on the second element. + --> + <div class="flexbox"> + <textarea style="height: 33px; flex: 2 auto"/> + <textarea + style="height: 13px; min-height: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-horiz-001-expected.xhtml new file mode 100644 index 0000000..834e725 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-horiz-001-expected.xhtml
@@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + margin-bottom: 5px; + line-height: 8px; + } + video { + width: 10px; + height: 20px; + border: 1px dotted green; + } + </style> + </head> + <body> + <div class="flexbox"> + <video/> + </div> + + <div class="flexbox" style="height: 22px"> + some words + <video style="float:right"/> + </div> + + <div class="flexbox"> + <video style="width: 122.5px" + /><video style="width: 73.5px"/> + </div> + + <div class="flexbox"> + <video style="width: 93px" + /><video style="width: 103px"/> + </div> + + <div class="flexbox"> + <video style="width: 114px" + /><video style="width: 82px"/> + </div> + + <div class="flexbox"> + <video style="width: 106px" + /><video style="width: 90px"/> + </div> + + <div class="flexbox"> + <video style="width: 46px" + /><video style="width: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-horiz-001.xhtml new file mode 100644 index 0000000..486330f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-horiz-001.xhtml
@@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that video elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on video flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-video-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + margin-bottom: 5px; + line-height: 8px; + } + video { + min-width: 0; + width: 10px; + height: 20px; + border: 1px dotted green; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <video/> + </div> + + <!-- B) Text and a video (ensure they aren't merged into one flex item) --> + <div class="flexbox"> + some words <video/> + </div> + + <!-- C) Two video elements, getting stretched by different ratios, from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <video style="flex: 5"/> + <video style="flex: 3"/> + </div> + + <!-- D) Two video elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <video style="width: 33px; flex: 2 auto"/> + <video style="width: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <video style="width: 150px; flex: 1 4 auto"/> + <video style="width: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-width --> + <!-- Same as (D), except we've added a max-width on the second element. + --> + <div class="flexbox"> + <video style="width: 33px; flex: 2 auto"/> + <video style="width: 13px; max-width: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-width --> + <!-- Same as (C), except we've added a min-width on the second element. + --> + <div class="flexbox"> + <video style="width: 33px; flex: 2 auto"/> + <video style="width: 13px; min-width: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-vert-001-expected.xhtml new file mode 100644 index 0000000..2be52fc --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-vert-001-expected.xhtml
@@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + width: 22px; + float: left; + margin-right: 10px; + background: lightgreen; + line-height: 0; + } + video { + width: 20px; + height: 10px; + border: 1px dotted green; + } + </style> + </head> + <body> + <div class="flexbox"> + <video/> + </div> + + <div class="flexbox"> + <div style="font: 8px monospace; height: 188px"> + a b + </div> + <video/> + </div> + + <div class="flexbox"> + <video style="height: 122.5px" + /><video style="height: 73.5px"/> + </div> + + <div class="flexbox"> + <video style="height: 93px" + /><video style="height: 103px"/> + </div> + + <div class="flexbox"> + <video style="height: 114px" + /><video style="height: 82px"/> + </div> + + <div class="flexbox"> + <video style="height: 106px" + /><video style="height: 90px"/> + </div> + + <div class="flexbox"> + <video style="height: 46px" + /><video style="height: 150px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-vert-001.xhtml new file mode 100644 index 0000000..fb5692a8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-basic-video-vert-001.xhtml
@@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test checks that video elements behave correctly as flex items. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing flexbox layout algorithm property on video flex items in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-basic-video-vert-001-ref.xhtml"/> + <style> + div.flexbox { + height: 200px; + background: lightgreen; + display: flex; + justify-content: space-between; + flex-direction: column; + float: left; + margin-right: 10px; + font: 8px monospace; + } + video { + width: 20px; + height: 10px; + min-height: 0; + border: 1px dotted green; + } + </style> + </head> + <body> + + <!-- A) One flex item --> + <div class="flexbox"> + <video/> + </div> + + <!-- B) Text and a video (ensure they aren't merged into one flex item) --> + <div class="flexbox"> + a b <video/> + </div> + + <!-- C) Two video elements, getting stretched by different ratios, from 0. + + Space-to-be-distributed = 200px - borders = 200 - (1 + 1) - (1 + 1) + = 196px + + 1st element gets 5/8 of space: 5/8 * 196px = 122.5px + 1st element gets 3/8 of space: 3/8 * 196px = 73.5px + --> + <div class="flexbox"> + <video style="flex: 5"/> + <video style="flex: 3"/> + </div> + + <!-- D) Two video elements, getting stretched by different ratios, from + different flex bases. + + Space-to-be-distributed = 200px - (33 + 1 + 1) - (13 + 1 + 1) = 150px + 1st element gets 2/5 of space: 33px + 2/5 * 150px = 93px + 1st element gets 3/5 of space: 13px + 3/5 * 150px = 103px + --> + <div class="flexbox"> + <video style="height: 33px; flex: 2 auto"/> + <video style="height: 13px; flex: 3 auto"/> + </div> + + <!-- E) Two flex items, getting shrunk by different amounts. + + Space-to-be-distributed = 200px - (150 + 1 + 1) - (100 + 1 + 1) = -54px + First element scaled flex ratio = 4 * 150 = 600 + Second element scaled flex ratio = 3 * 100 = 300 + * So, total flexibility is 600 + 300 = 900 + + 1st element gets 600/900 of space: 150 + 600/900 * -54 = 114px + 2nd element gets 300/900 of space: 100 + 300/900 * -54 = 82px + --> + <div class="flexbox"> + <video style="height: 150px; flex: 1 4 auto"/> + <video style="height: 100px; flex: 1 3 auto"/> + </div> + + <!-- F) Making sure we don't grow past max-height --> + <!-- Same as (D), except we've added a max-height on the second element. + --> + <div class="flexbox"> + <video style="height: 33px; flex: 2 auto"/> + <video style="height: 13px; max-height: 90px; flex: 3 auto"/> + </div> + + <!-- G) Making sure we grow at least as large as min-height --> + <!-- Same as (C), except we've added a min-height on the second element. + --> + <div class="flexbox"> + <video style="height: 33px; flex: 2 auto"/> + <video style="height: 13px; min-height: 150px; flex: 3 auto"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a-expected.html new file mode 100644 index 0000000..5c4a2ab8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a-expected.html
@@ -0,0 +1,98 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 60px; + height: 20px; + margin: 2px; + float: left; + } + div.item { + width: 28px; + border: 1px solid blue; + background: lightblue; + } + div.fullCrossSize { + height: 18px; + } + div.halfCrossSize { + height: 8px; + } + </style> +</head> +<body> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize"></div> + <div class="item halfCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize"></div> + <div class="item halfCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize"></div> + <div class="item halfCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a.html new file mode 100644 index 0000000..55bb6de9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a.html
@@ -0,0 +1,108 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing page-break-before in horizontal multi-line flex containers</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-line-break"> + <link rel="match" href="flexbox-break-request-horiz-001-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-wrap: wrap; + border: 1px dashed black; + width: 60px; + height: 20px; + margin: 2px; + float: left; + } + div.item { + width: 28px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + <!-- page-break-before specified on first (and only) item, at a point where + we're already "breaking" (the beginning): --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-before specified on second item, at a point where breaking + would otherwise be unnecessary: --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-before specified on third item, at a point where breaking + is already necessary: --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: right"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b-expected.html new file mode 100644 index 0000000..5c4a2ab8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b-expected.html
@@ -0,0 +1,98 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 60px; + height: 20px; + margin: 2px; + float: left; + } + div.item { + width: 28px; + border: 1px solid blue; + background: lightblue; + } + div.fullCrossSize { + height: 18px; + } + div.halfCrossSize { + height: 8px; + } + </style> +</head> +<body> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize"></div> + <div class="item halfCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize"></div> + <div class="item halfCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize"></div> + <div class="item halfCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b.html new file mode 100644 index 0000000..9fc7abd4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b.html
@@ -0,0 +1,108 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing page-break-after in horizontal multi-line flex containers</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-line-break"> + <link rel="match" href="flexbox-break-request-horiz-001-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-wrap: wrap; + border: 1px dashed black; + width: 60px; + height: 20px; + margin: 2px; + float: left; + } + div.item { + width: 28px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + <!-- page-break-after specified on first (and only) item, at a point where + we're already "breaking" (the end): --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: auto"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: avoid"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: always"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: left"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-after specified on first item, at a point where breaking + would otherwise be unnecessary: --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: auto"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: avoid"></div> + <div class="item"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: always"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: left"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: right"></div> + <div class="item"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-after specified on second item, at a point where breaking + is already necessary: --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: auto"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: avoid"></div> + <div class="item"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: always"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: left"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: right"></div> + <div class="item"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002a-expected.html new file mode 100644 index 0000000..f3be469 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002a-expected.html
@@ -0,0 +1,101 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 60px; + height: 20px; + margin: 2px; + float: left; + } + div.item { + width: 28px; + border: 1px solid blue; + background: lightblue; + } + div.fullCrossSize { + height: 18px; + } + div.halfCrossSize { + height: 8px; + } + div.shrunkMainSize { + width: 18px; + } + </style> +</head> +<body> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002a.html new file mode 100644 index 0000000..9929a560 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002a.html
@@ -0,0 +1,107 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing page-break-before in horizontal single-line flex containers (should have no effect)</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-line-break"> + <link rel="match" href="flexbox-break-request-horiz-002-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + border: 1px dashed black; + width: 60px; + height: 20px; + margin: 2px; + float: left; + } + div.item { + width: 28px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + <!-- page-break-before specified on first (and only) item, at a point where + we're already "breaking" (the beginning): --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-before specified on second item, at a point where breaking + would otherwise be unnecessary: --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-before specified on third item, at a point where breaking + is already necessary: --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: right"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002b-expected.html new file mode 100644 index 0000000..f3be469 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002b-expected.html
@@ -0,0 +1,101 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 60px; + height: 20px; + margin: 2px; + float: left; + } + div.item { + width: 28px; + border: 1px solid blue; + background: lightblue; + } + div.fullCrossSize { + height: 18px; + } + div.halfCrossSize { + height: 8px; + } + div.shrunkMainSize { + width: 18px; + } + </style> +</head> +<body> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize" style="float: left"></div> + <div class="item fullCrossSize" style="float: left"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + <div class="item fullCrossSize shrunkMainSize" style="float: left"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002b.html new file mode 100644 index 0000000..8b6f4d617 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-002b.html
@@ -0,0 +1,107 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing page-break-after in horizontal single-line flex containers (should have no effect)</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-line-break"> + <link rel="match" href="flexbox-break-request-horiz-002-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + border: 1px dashed black; + width: 60px; + height: 20px; + margin: 2px; + float: left; + } + div.item { + width: 28px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + <!-- page-break-after specified on first (and only) item, at a point where + we're already "breaking" (the end): --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: auto"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: avoid"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: always"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: left"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-after specified on first item, at a point where breaking + would otherwise be unnecessary: --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: auto"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: avoid"></div> + <div class="item"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: always"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: left"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: right"></div> + <div class="item"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-after specified on second item, at a point where breaking + is already necessary: --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: auto"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: avoid"></div> + <div class="item"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: always"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: left"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: right"></div> + <div class="item"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a-expected.html new file mode 100644 index 0000000..1550d35 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a-expected.html
@@ -0,0 +1,98 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 20px; + height: 60px; + margin: 2px; + float: left; + } + div.item { + height: 28px; + border: 1px solid blue; + background: lightblue; + } + div.fullCrossSize { + width: 18px; + } + div.halfCrossSize { + width: 8px; + } + </style> +</head> +<body> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a.html new file mode 100644 index 0000000..12206b03 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a.html
@@ -0,0 +1,109 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing page-break-before in vertical multi-line flex containers</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-line-break"> + <link rel="match" href="flexbox-break-request-vert-001-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-direction: column; + flex-wrap: wrap; + border: 1px dashed black; + width: 20px; + height: 60px; + margin: 2px; + float: left; + } + div.item { + height: 28px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + <!-- page-break-before specified on first (and only) item, at a point where + we're already "breaking" (the beginning): --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-before specified on second item, at a point where breaking + would otherwise be unnecessary: --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-before specified on third item, at a point where breaking + is already necessary: --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: right"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b-expected.html new file mode 100644 index 0000000..1550d35 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b-expected.html
@@ -0,0 +1,98 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 20px; + height: 60px; + margin: 2px; + float: left; + } + div.item { + height: 28px; + border: 1px solid blue; + background: lightblue; + } + div.fullCrossSize { + width: 18px; + } + div.halfCrossSize { + width: 8px; + } + </style> +</head> +<body> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> + <div class="flexbox"> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + <div class="item halfCrossSize" style="float: left"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b.html new file mode 100644 index 0000000..b4931582 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b.html
@@ -0,0 +1,109 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing page-break-after in vertical multi-line flex containers</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-line-break"> + <link rel="match" href="flexbox-break-request-vert-001-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-direction: column; + flex-wrap: wrap; + border: 1px dashed black; + width: 20px; + height: 60px; + margin: 2px; + float: left; + } + div.item { + height: 28px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + <!-- page-break-after specified on first (and only) item, at a point where + we're already "breaking" (the end): --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: auto"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: avoid"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: always"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: left"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-after specified on first item, at a point where breaking + would otherwise be unnecessary: --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: auto"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: avoid"></div> + <div class="item"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: always"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: left"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: right"></div> + <div class="item"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-after specified on second item, at a point where breaking + is already necessary: --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: auto"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: avoid"></div> + <div class="item"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: always"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: left"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: right"></div> + <div class="item"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002a-expected.html new file mode 100644 index 0000000..9fc9cb66 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002a-expected.html
@@ -0,0 +1,101 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 20px; + height: 60px; + margin: 2px; + float: left; + } + div.item { + height: 28px; + border: 1px solid blue; + background: lightblue; + } + div.fullCrossSize { + width: 18px; + } + div.halfCrossSize { + width: 8px; + } + div.shrunkMainSize { + height: 18px; + } + </style> +</head> +<body> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002a.html new file mode 100644 index 0000000..6c9a091 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002a.html
@@ -0,0 +1,108 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing page-break-before in vertical single-line flex containers (should have no effect)</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-line-break"> + <link rel="match" href="flexbox-break-request-vert-002-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-direction: column; + border: 1px dashed black; + width: 20px; + height: 60px; + margin: 2px; + float: left; + } + div.item { + height: 28px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + <!-- page-break-before specified on first (and only) item, at a point where + we're already "breaking" (the beginning): --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-before: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-before specified on second item, at a point where breaking + would otherwise be unnecessary: --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-before: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-before specified on third item, at a point where breaking + is already necessary: --> + <!-- * With 'page-break-before: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: auto"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: avoid"></div> + </div> + <!-- * With 'page-break-before: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: always"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: left"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item"></div> + <div class="item" style="page-break-before: right"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002b-expected.html new file mode 100644 index 0000000..9fc9cb66 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002b-expected.html
@@ -0,0 +1,101 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 20px; + height: 60px; + margin: 2px; + float: left; + } + div.item { + height: 28px; + border: 1px solid blue; + background: lightblue; + } + div.fullCrossSize { + width: 18px; + } + div.halfCrossSize { + width: 8px; + } + div.shrunkMainSize { + height: 18px; + } + </style> +</head> +<body> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize"></div> + <div class="item fullCrossSize"></div> + </div> + <div style="clear: both"></div> + + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> + <div class="flexbox"> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + <div class="item fullCrossSize shrunkMainSize"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002b.html new file mode 100644 index 0000000..cf77ab9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-002b.html
@@ -0,0 +1,108 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing page-break-after in vertical single-line flex containers (should have no effect)</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-line-break"> + <link rel="match" href="flexbox-break-request-vert-002-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-direction: column; + border: 1px dashed black; + width: 20px; + height: 60px; + margin: 2px; + float: left; + } + div.item { + height: 28px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + <!-- page-break-after specified on first (and only) item, at a point where + we're already "breaking" (the end): --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: auto"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: avoid"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: always"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: left"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: right"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-after specified on first item, at a point where breaking + would otherwise be unnecessary: --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: auto"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: avoid"></div> + <div class="item"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item" style="page-break-after: always"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: left"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item" style="page-break-after: right"></div> + <div class="item"></div> + </div> + <div style="clear: both"></div> + + <!-- page-break-after specified on second item, at a point where breaking + is already necessary: --> + <!-- * With 'page-break-after: auto, avoid' (not requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: auto"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: avoid"></div> + <div class="item"></div> + </div> + <!-- * With 'page-break-after: always, left, right' (requesting a break): --> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: always"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: left"></div> + <div class="item"></div> + </div> + <div class="flexbox"> + <div class="item"></div> + <div class="item" style="page-break-after: right"></div> + <div class="item"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001-expected.html new file mode 100644 index 0000000..bd861819 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001-expected.html
@@ -0,0 +1,63 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- In this reference case, we get each container to be sized the same as + in the testcase, without any visible baseline alignemnt, by using some + hidden flex items. + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 50px; + background: yellow; + border: 1px dotted black; + margin: 5px; + align-items: flex-start; + } + .hiddenItemForSizing { + width: 0; + min-width: 0; /* disable default min-width:auto behavior */ + color: transparent; + align-self: baseline; + } + .largeFont { + font-size: 20px; + background: lightblue; + /* Our flex items get padding on opposite sides (top/bottom) so that they + produce a large combined height when baseline-aligned: */ + padding-top: 5px; + } + .smallFont { + font-size: 10px; + background: pink; + /* Our flex items get padding on opposite sides (top/bottom) so that they + produce a large combined height when baseline-aligned: */ + padding-bottom: 20px; + } + </style> +</head> +<body> + <div class="flexContainer"> + <div class="largeFont">a</div> + <!-- Hidden flex items used to determine container's cross size, + based on their baseline-aligned combined cross size: --> + <div class="largeFont hiddenItemForSizing">a</div> + <div class="smallFont hiddenItemForSizing">b</div> + </div> + + <div class="flexContainer"> + <div class="smallFont">b</div> + <!-- Hidden flex items used to determine container's cross size, + based on their baseline-aligned combined cross size: --> + <div class="largeFont hiddenItemForSizing">a</div> + <div class="smallFont hiddenItemForSizing">b</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001.html new file mode 100644 index 0000000..02e1d50 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001.html
@@ -0,0 +1,54 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing that a collapsed flex item participates in baseline alignment only for the purpose of establishing container's cross size</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-visibility"> + <link rel="match" href="flexbox-collapsed-item-baseline-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 50px; + background: yellow; + border: 1px dotted black; + margin: 5px; + align-items: baseline; + } + .collapse { + visibility: collapse; + } + .largeFont { + font-size: 20px; + background: lightblue; + /* Our flex items get padding on opposite sides (top/bottom) so that they + produce a large combined height when baseline-aligned: */ + padding-top: 5px; + } + .smallFont { + font-size: 10px; + background: pink; + /* Our flex items get padding on opposite sides (top/bottom) so that they + produce a large combined height when baseline-aligned: */ + padding-bottom: 20px; + } + </style> +</head> +<body> + <!-- Second item collapsed: --> + <div class="flexContainer"> + <div class="largeFont">a</div> + <div class="smallFont collapse">b</div> + </div> + + <!-- First item collapsed: --> + <div class="flexContainer"> + <div class="largeFont collapse">a</div> + <div class="smallFont">b</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001-expected.html new file mode 100644 index 0000000..b925559 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001-expected.html
@@ -0,0 +1,102 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- In this reference case, we have blocks in place of the testcase's + flex containers. The testcase's collapsed flex items are entirely + absent here (and the remaining content is sized using exact pixel + values). + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + background: yellow; + border: 1px dotted black; + float: left; + margin: 5px; + } + .flexContainer > * { + /* All flex items have 20px base size */ + width: 20px; + } + .collapse { + flex-basis: 0; + height: 20px; + } + .flexible { + flex: 1 auto; + } + .heightTall { + height: 40px; + background: purple; + } + .heightAuto { + background: teal; + } + .heightShort { + height: 10px; + background: pink; + } + </style> +</head> +<body> + <!-- FIRST ROW: --> + <!-- Just one (collapsed) flex item, which ends up establishing + the container's size (even though it's collapsed): --> + <div class="flexContainer"> + <div class="heightTall collapse"></div> + </div> + + <div style="clear: both"></div> + + <!-- SECOND ROW: --> + <!-- One collapsed flex item, one short flex item. + (Container ends up with collapsed item's height) --> + <div class="flexContainer"> + <div class="heightTall collapse"></div> + <div class="heightShort"></div> + </div> + <!-- (same, but with items in opposite order) --> + <div class="flexContainer"> + <div class="heightShort"></div> + <div class="heightTall collapse"></div> + </div> + + <div style="clear: both"></div> + + <!-- THIRD ROW: --> + <!-- One collapsed flex item, one stretched flex item. + (Container and stretched item end up with collapsed item's height) --> + <div class="flexContainer"> + <div class="heightTall collapse"></div> + <div class="heightAuto"></div> + </div> + <!-- (again, with items in opposite order) --> + <div class="flexContainer"> + <div class="heightAuto"></div> + <div class="heightTall collapse"></div> + </div> + + <div style="clear: both"></div> + + <!-- FOURTH ROW: --> + <!-- One collapsed flex item, one other flex item; both are flexible. + (The non-collapsed one should take all of the available width.) --> + <div class="flexContainer"> + <div class="heightAuto collapse"></div> + <div class="heightTall flexible"></div> + </div> + <!-- (again, with items in opposite order) --> + <div class="flexContainer"> + <div class="heightTall flexible"></div> + <div class="heightAuto collapse"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001.html new file mode 100644 index 0000000..d911112f3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001.html
@@ -0,0 +1,98 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing that visibility:collapse on a flex item in a single-line flex container maintains the containers's cross size, but doesn't otherwise impact flex layout</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-visibility"> + <link rel="match" href="flexbox-collapsed-item-horiz-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + background: yellow; + border: 1px dotted black; + float: left; + margin: 5px; + } + .flexContainer > * { + /* All flex items have 20px base size */ + width: 20px; + } + .collapse { + visibility: collapse; + } + .flexible { + flex: 1 auto; + } + .heightTall { + height: 40px; + background: purple; + } + .heightAuto { + background: teal; + } + .heightShort { + height: 10px; + background: pink; + } + </style> +</head> +<body> + <!-- FIRST ROW: --> + <!-- Just one (collapsed) flex item, which ends up establishing + the container's size (even though it's collapsed): --> + <div class="flexContainer"> + <div class="heightTall collapse"></div> + </div> + + <div style="clear: both"></div> + + <!-- SECOND ROW: --> + <!-- One collapsed flex item, one short flex item. + (Container ends up with collapsed item's height) --> + <div class="flexContainer"> + <div class="heightTall collapse"></div> + <div class="heightShort"></div> + </div> + <!-- (same, but with items in opposite order) --> + <div class="flexContainer"> + <div class="heightShort"></div> + <div class="heightTall collapse"></div> + </div> + + <div style="clear: both"></div> + + <!-- THIRD ROW: --> + <!-- One collapsed flex item, one stretched flex item. + (Container and stretched item end up with collapsed item's height) --> + <div class="flexContainer"> + <div class="heightTall collapse"></div> + <div class="heightAuto"></div> + </div> + <!-- (again, with items in opposite order) --> + <div class="flexContainer"> + <div class="heightAuto"></div> + <div class="heightTall collapse"></div> + </div> + + <div style="clear: both"></div> + + <!-- FOURTH ROW: --> + <!-- One collapsed flex item, one other flex item; both are flexible. + (The non-collapsed one should take all of the available width.) --> + <div class="flexContainer"> + <div class="heightAuto flexible collapse"></div> + <div class="heightTall flexible"></div> + </div> + <!-- (again, with items in opposite order) --> + <div class="flexContainer"> + <div class="heightTall flexible"></div> + <div class="heightAuto flexible collapse"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002-expected.html new file mode 100644 index 0000000..db909823 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002-expected.html
@@ -0,0 +1,108 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + flex-wrap: wrap; + + /* These let us show where each flex line begins (and hence, how tall + the flex lines end up) */ + align-content: flex-start; + align-items: flex-start; + + width: 30px; + background: yellow; + border: 1px dotted black; + float: left; + margin: 5px; + } + .collapsedItem { + width: 0; + height: 25px; + } + .halfWidthItem { + width: 15px; + height: 15px; + background: teal; + } + .fullWidthItem { + width: 30px; + height: 20px; + background: purple; + } + .veryTallItem { + width: 15px; + height: 40px; + background: olive; + } + </style> +</head> +<body> + <!-- FIRST ROW: --> + <!-- One collapsed flex item, at the beginning of a flex line, which + ends up establishing its flex line's cross size: --> + <div class="flexContainer"> + <div class="collapsedItem"></div> + <div class="halfWidthItem"></div> + <div class="fullWidthItem"></div> + </div> + <!-- ...and now with it being at the end of that flex line: --> + <div class="flexContainer"> + <div class="halfWidthItem"></div> + <div class="collapsedItem"></div> + <div class="fullWidthItem"></div> + </div> + + <div style="clear: both"></div> + + <!-- SECOND ROW: --> + <!-- One collapsed flex item, initially in its own line. It ends + up being merged into another line after it collapses, due to its + (post-collapse) zero main-size. --> + <div class="flexContainer"> + <div class="collapsedItem"></div> + <div class="fullWidthItem"></div> + <div class="fullWidthItem"></div> + </div> + <div class="flexContainer"> + <div class="fullWidthItem"></div> + <div class="collapsedItem"></div> + <div class="fullWidthItem"></div> + </div> + <div class="flexContainer"> + <div class="fullWidthItem"></div> + <div class="fullWidthItem"></div> + <div class="collapsedItem"></div> + </div> + + <div style="clear: both"></div> + + <!-- THIRD ROW: --> + <!-- One collapsed flex item, initially in a line with an even-taller item. + The collapsed item ends up shifting into another line after it + collapses, but it carries the taller item's cross size with it, as its + strut size. --> + <div class="flexContainer"> + <div class="fullWidthItem"></div> + <div class="collapsedItem" style="height: 40px"></div> + <div class="veryTallItem"></div> + </div> + <!-- ...and now with two (differently-sized) struts in first line: + (the one that's taller - due to being initially grouped with the tall + item - wins out.) --> + <div class="flexContainer"> + <div class="collapsedItem"></div> + <div class="fullWidthItem"></div> + <div class="collapsedItem" style="height: 40px"></div> + <div class="veryTallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002.html new file mode 100644 index 0000000..1b78f74 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002.html
@@ -0,0 +1,111 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing that visibility:collapse on a flex item in a multi-line flex container creates struts, and that they can migrate between lines</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-visibility"> + <link rel="match" href="flexbox-collapsed-item-horiz-002-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + flex-wrap: wrap; + + /* These let us show where each flex line begins (and hence, how tall + the flex lines end up) */ + align-content: flex-start; + align-items: flex-start; + + width: 30px; + background: yellow; + border: 1px dotted black; + float: left; + margin: 5px; + } + .collapsedItem { + visibility: collapse; + width: 15px; + height: 25px; + } + .halfWidthItem { + width: 15px; + height: 15px; + background: teal; + } + .fullWidthItem { + width: 30px; + height: 20px; + background: purple; + } + .veryTallItem { + width: 15px; + height: 40px; + background: olive; + } + </style> +</head> +<body> + <!-- FIRST ROW: --> + <!-- One collapsed flex item, at the beginning of a flex line, which + ends up establishing its flex line's cross size: --> + <div class="flexContainer"> + <div class="collapsedItem"></div> + <div class="halfWidthItem"></div> + <div class="fullWidthItem"></div> + </div> + <!-- ...and now with it being at the end of that flex line: --> + <div class="flexContainer"> + <div class="halfWidthItem"></div> + <div class="collapsedItem"></div> + <div class="fullWidthItem"></div> + </div> + + <div style="clear: both"></div> + + <!-- SECOND ROW: --> + <!-- One collapsed flex item, initially in its own line. It ends + up being merged into another line after it collapses, due to its + (post-collapse) zero main-size. --> + <div class="flexContainer"> + <div class="collapsedItem"></div> + <div class="fullWidthItem"></div> + <div class="fullWidthItem"></div> + </div> + <div class="flexContainer"> + <div class="fullWidthItem"></div> + <div class="collapsedItem"></div> + <div class="fullWidthItem"></div> + </div> + <div class="flexContainer"> + <div class="fullWidthItem"></div> + <div class="fullWidthItem"></div> + <div class="collapsedItem"></div> + </div> + + <div style="clear: both"></div> + + <!-- THIRD ROW: --> + <!-- One collapsed flex item, initially in a line with an even-taller item. + The collapsed item ends up shifting into another line after it + collapses, but it carries the taller item's cross size with it, as its + strut size. --> + <div class="flexContainer"> + <div class="fullWidthItem"></div> + <div class="collapsedItem"></div> + <div class="veryTallItem"></div> + </div> + <!-- ...and now with two (differently-sized) struts in first line: + (the one that's taller - due to being initially grouped with the tall + item - wins out.) --> + <div class="flexContainer"> + <div class="collapsedItem"></div> + <div class="fullWidthItem"></div> + <div class="collapsedItem"></div> + <div class="veryTallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003-expected.html new file mode 100644 index 0000000..6620ea7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003-expected.html
@@ -0,0 +1,46 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + flex-wrap: wrap; + align-content: stretch; /* Initial value; just here for emphasis */ + width: 25px; + height: 60px; + background: yellow; + border: 1px dotted black; + float: left; + margin: 5px; + } + .collapsedItem { + width: 0; + height: 40px; + } + .shortItem { + width: 25px; + height: 10px; + background: purple; + } + .tallItem { + width: 10px; + height: 30px; + background: olive; + } + </style> +</head> +<body> + <div class="flexContainer"> + <div class="shortItem"></div> + <div class="collapsedItem"></div> + <div class="tallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003.html new file mode 100644 index 0000000..5f254f2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing that strut formation (from visibility:collapse) happens *after* lines have been stretched</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#algo-visibility"> + <link rel="match" href="flexbox-collapsed-item-horiz-003-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + flex-wrap: wrap; + align-content: stretch; /* Initial value; just here for emphasis */ + width: 25px; + height: 60px; + background: yellow; + border: 1px dotted black; + float: left; + margin: 5px; + } + .collapsedItem { + visibility: collapse; + width: 10px; + } + .shortItem { + width: 25px; + height: 10px; + background: purple; + } + .tallItem { + width: 10px; + height: 30px; + background: olive; + } + </style> +</head> +<body> + <!-- In this testcase, the first flex line initially has a cross-size of + 10px, and the second flex line has a cross-size of 30px. Both lines are + stretched by 10px each (to hit the container's total cross-size, 60px). + Then, we handle "visibility:collapse" and convert the collapsed item + into a strut with the second line's stretched cross-size (40px), and we + restart flex layout. This strut then ends up in the *first* line (since + it has 0 main-size), which means both flex lines end up being 40px tall. + --> + <div class="flexContainer"> + <div class="shortItem"></div> + <div class="collapsedItem"></div> + <div class="tallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001-expected.html new file mode 100644 index 0000000..e6c95d935 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001-expected.html
@@ -0,0 +1,127 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + height: 60px; + width: 60px; + font: 10px sans-serif; + background: yellow; + float: left; + border: 1px solid black; + } + .flexContainer > * { + border: 1px dotted gray; + width: 28px; + height: 28px; + float: left; + } + + /* The single-line flex containers' flex items are shrunk in main axis: */ + .singleLineHoriz > * { + width: 13px; + } + .singleLineVert > * { + height: 13px; + float: none; + } + </style> +</head> +<body> + <!-- single-line (flex-wrap unspecified): --> + <div class="flexContainer singleLineHoriz"><!-- flex-flow: row --> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer singleLineHoriz"><!-- flex-flow: row-reverse --> + <div>4</div><div>3</div><div>2</div><div>1</div> + </div> + <div class="flexContainer singleLineVert"><!-- flex-flow: column --> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer singleLineVert"><!-- flex-flow: column-reverse --> + <div>4</div><div>3</div><div>2</div><div>1</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap", after flex-direction: --> + <div class="flexContainer"><!-- flex-flow: row wrap --> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer"><!-- flex-flow: row-reverse wrap --> + <div>2</div><div>1</div><div>4</div><div>3</div> + </div> + <div class="flexContainer"><!-- flex-flow: column wrap --> + <div>1</div><div>3</div><div>2</div><div>4</div> + </div> + <div class="flexContainer"><!-- flex-flow: column-reverse wrap --> + <div>2</div><div>4</div><div>1</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap", before flex-direction: --> + <div class="flexContainer"><!-- flex-flow: wrap row --> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap row-reverse --> + <div>2</div><div>1</div><div>4</div><div>3</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap column --> + <div>1</div><div>3</div><div>2</div><div>4</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap column-reverse --> + <div>2</div><div>4</div><div>1</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap-reverse", after flex-direction: --> + <div class="flexContainer"><!-- flex-flow: row wrap-reverse --> + <div>3</div><div>4</div><div>1</div><div>2</div> + </div> + <div class="flexContainer"><!-- flex-flow: row-reverse wrap-reverse --> + <div>4</div><div>3</div><div>2</div><div>1</div> + </div> + <div class="flexContainer"><!-- flex-flow: column wrap-reverse --> + <div>3</div><div>1</div><div>4</div><div>2</div> + </div> + <div class="flexContainer"><!-- flex-flow: column-reverse wrap-reverse --> + <div>4</div><div>2</div><div>3</div><div>1</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap-reverse", before flex-direction: --> + <div class="flexContainer"><!-- flex-flow: wrap-reverse row --> + <div>3</div><div>4</div><div>1</div><div>2</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap-reverse row-reverse --> + <div>4</div><div>3</div><div>2</div><div>1</div> + </div> + <div class="flexContainer"> <!-- flex-flow: wrap-reverse column --> + <div>3</div><div>1</div><div>4</div><div>2</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap-reverse column-reverse --> + <div>4</div><div>2</div><div>3</div><div>1</div> + </div> + + <div style="clear:both"></div> + + <!-- now just specifying flex-wrap (no flex-direction) --> + <div class="flexContainer"><!-- flex-flow: wrap --> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap-reverse --> + <div>3</div><div>4</div><div>1</div><div>2</div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001.html new file mode 100644 index 0000000..1cc6e9a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001.html
@@ -0,0 +1,126 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing all the values of the "flex-flow" shorthand property, with 4 flex items in each container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-flow-property"> + <link rel="match" href="flexbox-flex-flow-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + height: 60px; + width: 60px; + font: 10px sans-serif; + background: yellow; + float: left; + border: 1px solid black; + } + .flexContainer > * { + border: 1px dotted gray; + width: 28px; + height: 28px; + /* Explicitly set min-width & min-height to 0, to prevent their "auto" + value from influencing the sizes of our flex items (particularly for + the single-line chunks of this testcase, whose items may be shrunk a + little below the numerals' intrinsic sizes): */ + min-width: 0; + min-height: 0; + } + </style> +</head> +<body> + <!-- single-line (flex-wrap unspecified): --> + <div class="flexContainer" style="flex-flow: row"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: column"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap", after flex-direction: --> + <div class="flexContainer" style="flex-flow: row wrap"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: column wrap"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap", before flex-direction: --> + <div class="flexContainer" style="flex-flow: wrap row"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: wrap row-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: wrap column"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: wrap column-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap-reverse", after flex-direction: --> + <div class="flexContainer" style="flex-flow: row wrap-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: column wrap-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap-reverse", before flex-direction: --> + <div class="flexContainer" style="flex-flow: wrap-reverse row"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: wrap-reverse row-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: wrap-reverse column"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: wrap-reverse column-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + + <div style="clear:both"></div> + + <!-- now just specifying flex-wrap (no flex-direction) --> + <div class="flexContainer" style="flex-flow: wrap"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + <div class="flexContainer" style="flex-flow: wrap-reverse"> + <div>1</div><div>2</div><div>3</div><div>4</div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-002-expected.html new file mode 100644 index 0000000..f6d192c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-002-expected.html
@@ -0,0 +1,133 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + height: 60px; + width: 60px; + font: 10px sans-serif; + background: yellow; + float: left; + border: 1px solid black; + } + .flexContainer > * { + border: 1px dotted gray; + width: 28px; + height: 28px; + float: left; + } + + /* The single-line flex containers' flex items are shrunk in main axis: */ + .singleLineHoriz > * { + width: 18px; + } + .singleLineVert > * { + height: 18px; + float: none; + } + .hidden { + /* We use this to hide the "4" box in each of the multi-line chunks. + The testcase has 3 flex items in each flex container, but it's easier + to write this reference case w/ a hidden 4th box as a space-filler. */ + visibility: hidden; + } + </style> +</head> +<body> + <!-- single-line (flex-wrap unspecified): --> + <div class="flexContainer singleLineHoriz"><!-- flex-flow: row --> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer singleLineHoriz"><!-- flex-flow: row-reverse --> + <div>3</div><div>2</div><div>1</div> + </div> + <div class="flexContainer singleLineVert"><!-- flex-flow: column --> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer singleLineVert"><!-- flex-flow: column-reverse --> + <div>3</div><div>2</div><div>1</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap", after flex-direction: --> + <div class="flexContainer"><!-- flex-flow: row wrap --> + <div>1</div><div>2</div><div>3</div><div class="hidden">4</div> + </div> + <div class="flexContainer"><!-- flex-flow: row-reverse wrap --> + <div>2</div><div>1</div><div class="hidden">4</div><div>3</div> + </div> + <div class="flexContainer"><!-- flex-flow: column wrap --> + <div>1</div><div>3</div><div>2</div><div class="hidden">4</div> + </div> + <div class="flexContainer"><!-- flex-flow: column-reverse wrap --> + <div>2</div><div class="hidden">4</div><div>1</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap", before flex-direction: --> + <div class="flexContainer"><!-- flex-flow: wrap row --> + <div>1</div><div>2</div><div>3</div><div class="hidden">4</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap row-reverse --> + <div>2</div><div>1</div><div class="hidden">4</div><div>3</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap column --> + <div>1</div><div>3</div><div>2</div><div class="hidden">4</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap column-reverse --> + <div>2</div><div class="hidden">4</div><div>1</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap-reverse", after flex-direction: --> + <div class="flexContainer"><!-- flex-flow: row wrap-reverse --> + <div>3</div><div class="hidden">4</div><div>1</div><div>2</div> + </div> + <div class="flexContainer"><!-- flex-flow: row-reverse wrap-reverse --> + <div class="hidden">4</div><div>3</div><div>2</div><div>1</div> + </div> + <div class="flexContainer"><!-- flex-flow: column wrap-reverse --> + <div>3</div><div>1</div><div class="hidden">4</div><div>2</div> + </div> + <div class="flexContainer"><!-- flex-flow: column-reverse wrap-reverse --> + <div class="hidden">4</div><div>2</div><div>3</div><div>1</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap-reverse", before flex-direction: --> + <div class="flexContainer"><!-- flex-flow: wrap-reverse row --> + <div>3</div><div class="hidden">4</div><div>1</div><div>2</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap-reverse row-reverse --> + <div class="hidden">4</div><div>3</div><div>2</div><div>1</div> + </div> + <div class="flexContainer"> <!-- flex-flow: wrap-reverse column --> + <div>3</div><div>1</div><div class="hidden">4</div><div>2</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap-reverse column-reverse --> + <div class="hidden">4</div><div>2</div><div>3</div><div>1</div> + </div> + + <div style="clear:both"></div> + + <!-- now just specifying flex-wrap (no flex-direction) --> + <div class="flexContainer"><!-- flex-flow: wrap --> + <div>1</div><div>2</div><div>3</div><div class="hidden">4</div> + </div> + <div class="flexContainer"><!-- flex-flow: wrap-reverse --> + <div>3</div><div class="hidden">4</div><div>1</div><div>2</div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-002.html new file mode 100644 index 0000000..de2d1b3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-002.html
@@ -0,0 +1,126 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing all the values of the "flex-flow" shorthand property, with 3 flex items in each container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-flow-property"> + <link rel="match" href="flexbox-flex-flow-002-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + height: 60px; + width: 60px; + font: 10px sans-serif; + background: yellow; + float: left; + border: 1px solid black; + } + .flexContainer > * { + border: 1px dotted gray; + width: 28px; + height: 28px; + /* Explicitly set min-width & min-height to 0, to prevent their "auto" + value from influencing the sizes of our flex items (particularly for + the single-line chunks of this testcase, whose items may be shrunk a + little below the numerals' intrinsic sizes): */ + min-width: 0; + min-height: 0; + } + </style> +</head> +<body> + <!-- single-line (flex-wrap unspecified): --> + <div class="flexContainer" style="flex-flow: row"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: column"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap", after flex-direction: --> + <div class="flexContainer" style="flex-flow: row wrap"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: column wrap"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap"> + <div>1</div><div>2</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap", before flex-direction: --> + <div class="flexContainer" style="flex-flow: wrap row"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: wrap row-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: wrap column"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: wrap column-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap-reverse", after flex-direction: --> + <div class="flexContainer" style="flex-flow: row wrap-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: column wrap-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now using "wrap-reverse", before flex-direction: --> + <div class="flexContainer" style="flex-flow: wrap-reverse row"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: wrap-reverse row-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: wrap-reverse column"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: wrap-reverse column-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + + <div style="clear:both"></div> + + <!-- now just specifying flex-wrap (no flex-direction) --> + <div class="flexContainer" style="flex-flow: wrap"> + <div>1</div><div>2</div><div>3</div> + </div> + <div class="flexContainer" style="flex-flow: wrap-reverse"> + <div>1</div><div>2</div><div>3</div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-001-expected.html new file mode 100644 index 0000000..009162d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-001-expected.html
@@ -0,0 +1,101 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 100px; + height: 12px; + margin-bottom: 2px; + } + div.halfMainSize { + width: 48px; + border: 1px solid blue; + background: lightblue; + } + div.hugeMainSize { + width: 148px; + border: 1px solid purple; + background: fuchsia; + } + div.fullCrossSize { + height: 10px; + } + div.halfCrossSize { + height: 4px; + } + </style> +</head> +<body> + + <!-- Single small flex item --> + <div class="flexbox"> + <div class="halfMainSize fullCrossSize"></div> + </div> + + <!-- Single small flex item with 'margin-left' set to push it to protrude + from the far edge of the container (and to shrink as a result) --> + <div class="flexbox"> + <div class="halfMainSize fullCrossSize" + style="margin-left: 80px; width: 18px"></div> + </div> + + <!-- Single small inflexible flex item with 'margin-left' set to push it to + protrude from the far edge of the container --> + <div class="flexbox"> + <div class="halfMainSize fullCrossSize" + style="margin-left: 80px"></div> + </div> + + <!-- Two flex items, exactly large enough to fit in line (no wrapping): --> + <div class="flexbox"> + <div class="halfMainSize fullCrossSize" style="float: left"></div> + <div class="halfMainSize fullCrossSize" style="float: left"></div> + </div> + + <!-- and now with some margin on first item, to force second item to wrap: --> + <div class="flexbox"> + <div class="halfMainSize halfCrossSize"></div> + <div class="halfMainSize halfCrossSize"></div> + </div> + + <!-- and now with some margin on second item, again forcing it to wrap: --> + <div class="flexbox"> + <div class="halfMainSize halfCrossSize"></div> + <div class="halfMainSize halfCrossSize"></div> + </div> + + <!-- Single large flex item: overflows (but does not wrap) and is shrunk + to fit container's main-size --> + <div class="flexbox"> + <div class="hugeMainSize fullCrossSize" style="width: 98px"></div> + </div> + + <!-- Single large flex item: overflows (but does not wrap) --> + <div class="flexbox"> + <div class="hugeMainSize fullCrossSize"></div> + </div> + + <!-- Small flex item, followed by large flex item (which wraps to + its own line and then shrink to container's main-size) --> + <div class="flexbox"> + <div class="halfMainSize halfCrossSize"></div> + <div class="hugeMainSize halfCrossSize" style="width: 98px"></div> + </div> + + <!-- Small flex item, followed by large inflexible flex item (which wraps to + its own line and then shrink to container's main-size) --> + <div class="flexbox"> + <div class="halfMainSize halfCrossSize"></div> + <div class="hugeMainSize halfCrossSize"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-001.html new file mode 100644 index 0000000..be35404 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-001.html
@@ -0,0 +1,97 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing flex-wrap in horizontal flex containers</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-wrap-property"> + <link rel="match" href="flexbox-flex-wrap-horiz-001-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-wrap: wrap; + border: 1px dashed black; + width: 100px; + height: 12px; + margin-bottom: 2px; + } + div.halfMainSize { + width: 48px; + border: 1px solid blue; + background: lightblue; + } + div.hugeMainSize { + width: 148px; + border: 1px solid purple; + background: fuchsia; + } + </style> +</head> +<body> + + <!-- Single small flex item --> + <div class="flexbox"> + <div class="halfMainSize"></div> + </div> + + <!-- Single small flex item with 'margin-left' set to push it to protrude + from the far edge of the container (and to shrink as a result) --> + <div class="flexbox"> + <div class="halfMainSize" style="margin-left: 80px"></div> + </div> + + <!-- Single small inflexible flex item with 'margin-left' set to push it to + protrude from the far edge of the container --> + <div class="flexbox"> + <div class="halfMainSize" style="margin-left: 80px; flex: none"></div> + </div> + + <!-- Two flex items, exactly large enough to fit in line (no wrapping): --> + <div class="flexbox"> + <div class="halfMainSize"></div> + <div class="halfMainSize"></div> + </div> + + <!-- and now with some margin on first item, to force second item to wrap: --> + <div class="flexbox"> + <div class="halfMainSize" style="margin-right: 1px"></div> + <div class="halfMainSize"></div> + </div> + + <!-- and now with some margin on second item, again forcing it to wrap: --> + <div class="flexbox"> + <div class="halfMainSize"></div> + <div class="halfMainSize" style="margin-right: 1px"></div> + </div> + + <!-- Single large flex item: overflows (but does not wrap) and is shrunk + to fit container's main-size --> + <div class="flexbox"> + <div class="hugeMainSize"></div> + </div> + + <!-- Single large flex item: overflows (but does not wrap) --> + <div class="flexbox"> + <div class="hugeMainSize" style="flex: none"></div> + </div> + + <!-- Small flex item, followed by large flex item (which wraps to + its own line and then shrink to container's main-size) --> + <div class="flexbox"> + <div class="halfMainSize"></div> + <div class="hugeMainSize"></div> + </div> + + <!-- Small flex item, followed by large inflexible flex item (which wraps to + its own line and then shrink to container's main-size) --> + <div class="flexbox"> + <div class="halfMainSize"></div> + <div class="hugeMainSize" style="flex: none"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-002-expected.html new file mode 100644 index 0000000..2481e4e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-002-expected.html
@@ -0,0 +1,64 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + min-width: 100px; + height: 12px; + float: left; + clear: both; + margin-bottom: 2px; + } + div.smallItem { + width: 30px; + border: 1px solid blue; + background: lightblue; + float: left; + } + div.fullCrossSize { + height: 10px; + } + div.halfCrossSize { + height: 4px; + } + </style> +</head> +<body> + + <!-- No flex items --> + <div class="flexbox"> + </div> + + <!-- Single small flex item --> + <div class="flexbox"> + <div class="smallItem fullCrossSize"></div> + </div> + + <!-- Multiple flex items, larger than flex container's min-size: --> + <div class="flexbox"> + <div class="smallItem fullCrossSize"></div> + <div class="smallItem fullCrossSize"></div> + <div class="smallItem fullCrossSize"></div> + <div class="smallItem fullCrossSize"></div> + <div class="smallItem fullCrossSize"></div> + </div> + + <!-- - ...and now with flex container constrained by both min and max-size --> + <div class="flexbox" style="max-width: 120px"> + <div class="smallItem halfCrossSize"></div> + <div class="smallItem halfCrossSize"></div> + <div class="smallItem halfCrossSize"></div> + <div class="smallItem halfCrossSize"></div> + <div class="smallItem halfCrossSize"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-002.html new file mode 100644 index 0000000..3ff6ccc --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-horiz-002.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Ensure that min-width is honored for horizontal multi-line flex containers</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-wrap-property"> + <link rel="match" href="flexbox-flex-wrap-horiz-002-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-wrap: wrap; + border: 1px dashed black; + min-width: 100px; + height: 12px; + float: left; + clear: both; + margin-bottom: 2px; + } + div.smallItem { + width: 30px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + + <!-- No flex items --> + <div class="flexbox"> + </div> + + <!-- Single small flex item --> + <div class="flexbox"> + <div class="smallItem"></div> + </div> + + <!-- Multiple flex items, larger than flex container's min-size: --> + <div class="flexbox"> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + </div> + + <!-- - ...and now with flex container constrained by both min and max-size --> + <div class="flexbox" style="max-width: 120px"> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-001-expected.html new file mode 100644 index 0000000..5d4d8ea6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-001-expected.html
@@ -0,0 +1,102 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 12px; + height: 100px; + margin-right: 2px; + float: left; + } + div.halfMainSize { + height: 48px; + border: 1px solid blue; + background: lightblue; + } + div.hugeMainSize { + height: 148px; + border: 1px solid purple; + background: fuchsia; + } + div.fullCrossSize { + width: 10px; + } + div.halfCrossSize { + width: 4px; + } + </style> +</head> +<body> + + <!-- Single small flex item --> + <div class="flexbox"> + <div class="halfMainSize fullCrossSize"></div> + </div> + + <!-- Single small flex item with 'margin-left' set to push it to protrude + from the far edge of the container (and to shrink as a result) --> + <div class="flexbox"> + <div class="halfMainSize fullCrossSize" + style="margin-top: 80px; height: 18px"></div> + </div> + + <!-- Single small inflexible flex item with 'margin-left' set to push it to + protrude from the far edge of the container --> + <div class="flexbox"> + <div class="halfMainSize fullCrossSize" + style="margin-top: 80px"></div> + </div> + + <!-- Two flex items, exactly large enough to fit in line (no wrapping): --> + <div class="flexbox"> + <div class="halfMainSize fullCrossSize"></div> + <div class="halfMainSize fullCrossSize"></div> + </div> + + <!-- and now with some margin on first item, to force second item to wrap: --> + <div class="flexbox"> + <div class="halfMainSize halfCrossSize" style="float: left"></div> + <div class="halfMainSize halfCrossSize" style="float: left"></div> + </div> + + <!-- and now with some margin on second item, again forcing it to wrap: --> + <div class="flexbox"> + <div class="halfMainSize halfCrossSize" style="float: left"></div> + <div class="halfMainSize halfCrossSize" style="float: left"></div> + </div> + + <!-- Single large flex item: overflows (but does not wrap) and is shrunk + to fit container's main-size --> + <div class="flexbox"> + <div class="hugeMainSize fullCrossSize" style="height: 98px"></div> + </div> + + <!-- Single large flex item: overflows (but does not wrap) --> + <div class="flexbox"> + <div class="hugeMainSize fullCrossSize"></div> + </div> + + <!-- Small flex item, followed by large flex item (which wraps to + its own line and then shrink to container's main-size) --> + <div class="flexbox"> + <div class="halfMainSize halfCrossSize" style="float: left"></div> + <div class="hugeMainSize halfCrossSize" style="float: left; height: 98px"></div> + </div> + + <!-- Small flex item, followed by large inflexible flex item (which wraps to + its own line and then shrink to container's main-size) --> + <div class="flexbox"> + <div class="halfMainSize halfCrossSize" style="float: left"></div> + <div class="hugeMainSize halfCrossSize" style="float: left"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-001.html new file mode 100644 index 0000000..5d3541c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-001.html
@@ -0,0 +1,99 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Testing flex-wrap in vertical flex containers</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-wrap-property"> + <link rel="match" href="flexbox-flex-wrap-vert-001-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-direction: column; + flex-wrap: wrap; + border: 1px dashed black; + width: 12px; + height: 100px; + margin-right: 2px; + float: left; + } + div.halfMainSize { + height: 48px; + border: 1px solid blue; + background: lightblue; + } + div.hugeMainSize { + height: 148px; + border: 1px solid purple; + background: fuchsia; + } + </style> +</head> +<body> + + <!-- Single small flex item --> + <div class="flexbox"> + <div class="halfMainSize"></div> + </div> + + <!-- Single small flex item with 'margin-top' set to push it to protrude + from the far edge of the container (and to shrink as a result) --> + <div class="flexbox"> + <div class="halfMainSize" style="margin-top: 80px"></div> + </div> + + <!-- Single small inflexible flex item with 'margin-top' set to push it to + protrude from the far edge of the container --> + <div class="flexbox"> + <div class="halfMainSize" style="margin-top: 80px; flex: none"></div> + </div> + + <!-- Two flex items, exactly large enough to fit in line (no wrapping): --> + <div class="flexbox"> + <div class="halfMainSize"></div> + <div class="halfMainSize"></div> + </div> + + <!-- and now with some margin on first item, to force second item to wrap: --> + <div class="flexbox"> + <div class="halfMainSize" style="margin-bottom: 1px"></div> + <div class="halfMainSize"></div> + </div> + + <!-- and now with some margin on second item, again forcing it to wrap: --> + <div class="flexbox"> + <div class="halfMainSize"></div> + <div class="halfMainSize" style="margin-bottom: 1px"></div> + </div> + + <!-- Single large flex item: overflows (but does not wrap) and is shrunk + to fit container's main-size --> + <div class="flexbox"> + <div class="hugeMainSize"></div> + </div> + + <!-- Single large flex item: overflows (but does not wrap) --> + <div class="flexbox"> + <div class="hugeMainSize" style="flex: none"></div> + </div> + + <!-- Small flex item, followed by large flex item (which wraps to + its own line and then shrink to container's main-size) --> + <div class="flexbox"> + <div class="halfMainSize"></div> + <div class="hugeMainSize"></div> + </div> + + <!-- Small flex item, followed by large inflexible flex item (which wraps to + its own line and then shrink to container's main-size) --> + <div class="flexbox"> + <div class="halfMainSize"></div> + <div class="hugeMainSize" style="flex: none"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-002-expected.html new file mode 100644 index 0000000..5cf841b9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-002-expected.html
@@ -0,0 +1,59 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + div.flexbox { + border: 1px dashed black; + width: 12px; + min-height: 100px; + margin-right: 2px; + float: left; + } + div.smallItem { + height: 30px; + border: 1px solid blue; + background: lightblue; + } + div.halfCrossSize { + width: 4px; + } + </style> +</head> +<body> + + <!-- No flex items --> + <div class="flexbox"> + </div> + + <!-- Single small flex item --> + <div class="flexbox"> + <div class="smallItem"></div> + </div> + + <!-- Multiple flex items, larger than flex container's min-size: --> + <div class="flexbox"> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + </div> + + <!-- - ...and now with flex container constrained by both min and max-size --> + <div class="flexbox" style="max-height: 120px"> + <div class="smallItem halfCrossSize" style="float: left"></div> + <div class="smallItem halfCrossSize" style="float: left"></div> + <div class="smallItem halfCrossSize" style="float: left"></div> + <div class="smallItem halfCrossSize" style="float: left"></div> + <div class="smallItem halfCrossSize" style="float: left"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-002.html new file mode 100644 index 0000000..bd3fd2c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-wrap-vert-002.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Ensure that min-height is honored for vertical multi-line flex containers</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-wrap-property"> + <link rel="match" href="flexbox-flex-wrap-vert-002-ref.html"> + <meta charset="utf-8"> + <style> + div.flexbox { + display: flex; + flex-direction: column; + flex-wrap: wrap; + border: 1px dashed black; + width: 12px; + min-height: 100px; + margin-right: 2px; + float: left; + } + div.smallItem { + height: 30px; + border: 1px solid blue; + background: lightblue; + } + </style> +</head> +<body> + + <!-- No flex items --> + <div class="flexbox"> + </div> + + <!-- Single small flex item --> + <div class="flexbox"> + <div class="smallItem"></div> + </div> + + <!-- Multiple flex items, larger than flex container's min-size: --> + <div class="flexbox"> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + </div> + + <!-- - ...and now with flex container constrained by both min and max-size --> + <div class="flexbox" style="max-height: 120px"> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + <div class="smallItem"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-001-expected.xhtml new file mode 100644 index 0000000..b14ae67f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-001-expected.xhtml
@@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case, with floated elements in place of flex items, and using + "position: relative" on those elements, to make z-index work on them + outside of a flex container. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .flexbox { + width: 90px; + height: 10px; + border: 2px solid gray; + margin-bottom: 10px; + } + .a { + width: 10px; + height: 10px; + background: lightblue; + min-width: 0; + float: left; + } + .b { + width: 10px; + height: 10px; + background: pink; + min-width: 0; + margin-right: 10px; + float: left; + } + .aKid { + margin-left: 3px; + margin-top: 3px; + width: 10px; + height: 10px; + background: steelblue; + border: 1px solid blue; + } + .bKid { + margin-left: 3px; + margin-top: 6px; + width: 10px; + height: 10px; + background: violet; + border: 1px solid purple; + } + + .z0, .z1, .zn1 { position: relative; } + .z0 { z-index: 0; } + .z1 { z-index: 1; } + .zn1 { z-index: -1; } + + </style> + </head> + <body> + <!-- No "z-index" --> + <div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div> + + <!-- Various "z-index" just on the first item --> + <div class="flexbox"> + <div class="a zn1"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + + <div class="a z0"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + + <div class="a z1"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div> + + <!-- Various "z-index" just on the second item --> + <div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b zn1"><div class="bKid"/></div> + + <div class="a"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + + <div class="a"><div class="aKid"/></div> + <div class="b z1"><div class="bKid"/></div> + </div> + + <!-- Various "z-index" on the first item, w/ "z-index:0" on second item --> + <div class="flexbox"> + <div class="a zn1"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + + <div class="a z0"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + + <div class="a z1"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + </div> + + <!-- Various "z-index" on the second item, w/ "z-index:0" on first item --> + <div class="flexbox"> + <div class="a z0"><div class="aKid"/></div> + <div class="b zn1"><div class="bKid"/></div> + + <div class="a z0"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + + <div class="a z0"><div class="aKid"/></div> + <div class="b z1"><div class="bKid"/></div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-001.xhtml new file mode 100644 index 0000000..3febbfb --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-001.xhtml
@@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with flex items containing overlapping content, with + "z-index" set on some of them, which should make them create + stacking contexts. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing that 'z-index' property makes flex items form stacking contexts</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#painting"/> + <link rel="match" href="flexbox-items-as-stacking-contexts-001-ref.xhtml"/> + <style> + .flexbox { + width: 90px; + height: 10px; + border: 2px solid gray; + display: flex; + margin-bottom: 10px; + } + .a { + width: 10px; + height: 10px; + background: lightblue; + min-width: 0; + } + .b { + width: 10px; + height: 10px; + background: pink; + min-width: 0; + margin-right: 10px; + } + .aKid { + margin-left: 3px; + margin-top: 3px; + width: 10px; + height: 10px; + background: steelblue; + border: 1px solid blue; + } + .bKid { + margin-left: 3px; + margin-top: 6px; + width: 10px; + height: 10px; + background: violet; + border: 1px solid purple; + } + .z0 { z-index: 0; } + .z1 { z-index: 1; } + .zn1 { z-index: -1; } + + </style> + </head> + <body> + <!-- No "z-index" --> + <div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div> + + <!-- Various "z-index" just on the first item --> + <div class="flexbox"> + <div class="a zn1"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + + <div class="a z0"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + + <div class="a z1"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div> + + <!-- Various "z-index" just on the second item --> + <div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b zn1"><div class="bKid"/></div> + + <div class="a"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + + <div class="a"><div class="aKid"/></div> + <div class="b z1"><div class="bKid"/></div> + </div> + + <!-- Various "z-index" on the first item, w/ "z-index:0" on second item --> + <div class="flexbox"> + <div class="a zn1"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + + <div class="a z0"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + + <div class="a z1"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + </div> + + <!-- Various "z-index" on the second item, w/ "z-index:0" on first item --> + <div class="flexbox"> + <div class="a z0"><div class="aKid"/></div> + <div class="b zn1"><div class="bKid"/></div> + + <div class="a z0"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + + <div class="a z0"><div class="aKid"/></div> + <div class="b z1"><div class="bKid"/></div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-002-expected.html new file mode 100644 index 0000000..43ef91f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-002-expected.html
@@ -0,0 +1,54 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + body { font: 10px sans-serif } + .flexContainer { + background: orange; + width: 70px; + padding: 2px; + margin-bottom: 2px; + } + + .flexContainer > div:first-child { + margin-right: 10px; /* the space between the flex items, in testcase */ + } + + .item1 { + display: inline-block; + background: lightblue; + width: 30px; + } + .item2 { + display: inline-block; + background: yellow; + width: 30px; + } + </style> +</head> +<body> + <div class="flexContainer" + ><div class="item1">ThisIsALongUnbrokenString</div><div class="item2">HereIsSomeMoreLongText</div></div> + + <div class="flexContainer" + ><div class="item1" style="position:relative">ThisIsALongUnbrokenString</div><div class="item2">HereIsSomeMoreLongText</div></div> + + <div class="flexContainer" + ><div class="item1" style="position:relative">ThisIsALongUnbrokenString</div><div class="item2">HereIsSomeMoreLongText</div></div> + + <div class="flexContainer" + ><div class="item2">HereIsSomeMoreLongText</div><div class="item1">ThisIsALongUnbrokenString</div></div> + + <div class="flexContainer" + ><div class="item2">HereIsSomeMoreLongText</div><div class="item1">ThisIsALongUnbrokenString</div></div> + + <div class="flexContainer" + ><div class="item2" style="position:relative">HereIsSomeMoreLongText</div><div class="item1">ThisIsALongUnbrokenString</div></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-002.html new file mode 100644 index 0000000..e2555587 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-002.html
@@ -0,0 +1,78 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks flex items are painted atomically. In particular, + if one item has content that overflows into the region of another item, + then one item is painted "behind" the other; there shouldn't normally + any interleaving of backgrounds and content between the two items. + + This testcase also tests some special cases that will change the paint + ordering - specifically, the properties "position", "z-index", and + "order" on flex items. + --> +<!-- This was resolved by the CSSWG in April 2013: + http://krijnhoetmer.nl/irc-logs/css/20130403#l-455 --> +<html> +<head> + <title>CSS Test: Testing that flex items paint as pseudo-stacking contexts (like inline-blocks): atomically, in the absence of 'z-index' on descendants</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#painting"> + <link rel="match" href="flexbox-items-as-stacking-contexts-002-ref.html"> + <style> + body { font: 10px sans-serif } + .flexContainer { + background: orange; + display: flex; + justify-content: space-between; + width: 70px; + padding: 2px; + margin-bottom: 2px; + } + .item1 { + background: lightblue; + width: 30px; + min-width: 0; /* disable default min-width:auto behavior */ + } + .item2 { + background: yellow; + width: 30px; + min-width: 0; /* disable default min-width:auto behavior */ + } + </style> +</head> +<body> + <!-- This container has two flex items, the first of which has content + sticking out & overlapping the second. If they're painting atomically + (and in the right order), the second item's background should cover the + first item's overflowing content. --> + <div class="flexContainer" + ><div class="item1">ThisIsALongUnbrokenString</div><div class="item2">HereIsSomeMoreLongText</div></div> + + <!-- Now, the first item is relatively positioned, which should make it paint + on top of everything. --> + <div class="flexContainer" + ><div class="item1" style="position:relative">ThisIsALongUnbrokenString</div><div class="item2">HereIsSomeMoreLongText</div></div> + + <!-- Now, the first item is has "z-index" set, which should make it paint on + top of everything. --> + <div class="flexContainer" + ><div class="item1" style="z-index: 1">ThisIsALongUnbrokenString</div><div class="item2">HereIsSomeMoreLongText</div></div> + + <!-- Now, the first item has "order" set to a higher value than default, + which should make it paint on top (and at the far right) --> + <div class="flexContainer" + ><div class="item1" style="order: 1">ThisIsALongUnbrokenString</div><div class="item2">HereIsSomeMoreLongText</div></div> + + <!-- And for thoroughness, let's set "order" to a lower value than default, + on the second item. (Should render the same as previous example.) --> + <div class="flexContainer" + ><div class="item1">ThisIsALongUnbrokenString</div><div class="item2" style="order: -1">HereIsSomeMoreLongText</div></div> + + <!-- ...but if we relatively position that second item, it should paint + on top again, despite its low "order" value. --> + <div class="flexContainer" + ><div class="item1">ThisIsALongUnbrokenString</div><div class="item2" style="order: -1; position: relative">HereIsSomeMoreLongText</div></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-003-expected.html new file mode 100644 index 0000000..ba730426 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-003-expected.html
@@ -0,0 +1,66 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: orange; + width: 70px; + height: 20px; + padding: 2px; + margin-bottom: 2px; + } + .item1 { + display: inline-block; + background: lightblue; + width: 30px; + height: 16px; + padding: 2px; + margin-right: 2px; + vertical-align: top; + } + .item2 { + display: inline-block; + background: yellow; + width: 30px; + height: 16px; + padding: 2px; + vertical-align: top; + } + .grandchildA { + background: purple; + width: 80px; + height: 6px; + position: relative; + z-index: 10; + } + .grandchildB { + background: teal; + width: 80px; + height: 6px; + position: relative; + z-index: 20; + } + .grandchildC { + background: lime; + width: 20px; + height: 16px; + position: relative; + /* This z-index should interleave this content + between grandchildA and grandchildB: */ + z-index: 15; + } + </style> +</head> +<body> + <div class="flexContainer" + ><div class="item1" + ><div class="grandchildA"></div><div class="grandchildB"></div></div><div class="item2" + ><div class="grandchildC"></div></div></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-003.html new file mode 100644 index 0000000..eea1373 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-items-as-stacking-contexts-003.html
@@ -0,0 +1,75 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that flex items are painted as pseudo-stacking + contexts, instead of full stacking contexts. In other words, content + inside of one flex item should be able to iterleave between pieces of + content in another flex item, if we set appropriately interleaving + "z-index" values. --> +<!-- This was resolved by the CSSWG in April 2013: + http://krijnhoetmer.nl/irc-logs/css/20130403#l-455 --> +<html> +<head> + <title>CSS Test: Testing that flex items paint as pseudo-stacking contexts (like inline-blocks), instead of full stacking contexts: 'z-index' should let descendants interleave</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#painting"> + <link rel="match" href="flexbox-items-as-stacking-contexts-003-ref.html"> + <style> + .flexContainer { + background: orange; + display: flex; + justify-content: space-between; + width: 70px; + height: 20px; + padding: 2px; + margin-bottom: 2px; + } + .item1 { + background: lightblue; + width: 30px; + min-width: 0; /* disable default min-width:auto behavior */ + padding: 2px; + } + .item2 { + background: yellow; + width: 30px; + padding: 2px; + } + .grandchildA { + background: purple; + width: 80px; + height: 6px; + position: relative; + z-index: 10; + } + .grandchildB { + background: teal; + width: 80px; + height: 6px; + position: relative; + z-index: 20; + } + .grandchildC { + background: lime; + width: 20px; + height: 16px; + position: relative; + /* This z-index should interleave this content + between grandchildA and grandchildB: */ + z-index: 15; + } + </style> +</head> +<body> + <!-- This flex container's first flex item has content that overflows + and overlap the second flex item. The z-index values are set such + that this content should interleave; grandchildC should + paint on top of grandchildA, but underneath grandchildB. --> + <div class="flexContainer" + ><div class="item1" + ><div class="grandchildA"></div><div class="grandchildB"></div></div><div class="item2" + ><div class="grandchildC"></div></div></div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001a-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001a-expected.xhtml new file mode 100644 index 0000000..6faa52ee --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001a-expected.xhtml
@@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + margin-bottom: 2px; + line-height: 0; + } + div.flexbox > * { + display: inline-block; + } + div.a { + height: 20px; + width: 10px; + background: lightgreen; + } + div.b { + height: 20px; + width: 50px; + background: pink; + } + div.c { + height: 20px; + width: 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a" style="margin-left: 190px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 140px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 40px"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a" style="margin-left: 95px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 70px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 20px"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b" style="margin-left: 140px"/> + </div> + <div class="flexbox"> + <div class="a" + /><div class="b" style="margin-left: 20px" + /><div class="c" style="margin-left: 20px"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a" style="margin-left: 95px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 35px" + /><div class="b" style="margin-left: 70px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: calc(40px / 6)" + /><div class="b" style="margin-left: calc(40px / 3)" + /><div class="c" style="margin-left: calc(40px / 3)"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001a.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001a.xhtml new file mode 100644 index 0000000..7ce14885 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001a.xhtml
@@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of horizontal flex containers, testing each + possible value of the 'justify-content' property. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + display: flex; + margin-bottom: 2px; + } + div.a { + height: 20px; + flex: 0 10px; + background: lightgreen; + } + div.b { + height: 20px; + flex: 0 50px; + background: pink; + } + div.c { + height: 20px; + flex: 0 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001b-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001b-expected.xhtml new file mode 100644 index 0000000..6faa52ee --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001b-expected.xhtml
@@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + margin-bottom: 2px; + line-height: 0; + } + div.flexbox > * { + display: inline-block; + } + div.a { + height: 20px; + width: 10px; + background: lightgreen; + } + div.b { + height: 20px; + width: 50px; + background: pink; + } + div.c { + height: 20px; + width: 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a" style="margin-left: 190px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 140px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 40px"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a" style="margin-left: 95px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 70px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 20px"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b" style="margin-left: 140px"/> + </div> + <div class="flexbox"> + <div class="a" + /><div class="b" style="margin-left: 20px" + /><div class="c" style="margin-left: 20px"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a" style="margin-left: 95px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 35px" + /><div class="b" style="margin-left: 70px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: calc(40px / 6)" + /><div class="b" style="margin-left: calc(40px / 3)" + /><div class="c" style="margin-left: calc(40px / 3)"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001b.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001b.xhtml new file mode 100644 index 0000000..6e8c544a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001b.xhtml
@@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of horizontal flex containers, testing each + possible value of the 'justify-content' property. The flex containers' + widths are determined by their "min-width" property. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a horizontal flex container with "min-width"</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + min-width: 200px; + display: flex; + margin-bottom: 2px; + + /* Use "float" to trigger intrinsic sizing, which in this case will + make us clamp to "min-width": */ + float: left; + clear: both; + } + div.a { + height: 20px; + flex: 0 10px; + background: lightgreen; + } + div.b { + height: 20px; + flex: 0 50px; + background: pink; + } + div.c { + height: 20px; + flex: 0 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-002-expected.xhtml new file mode 100644 index 0000000..d28d317 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-002-expected.xhtml
@@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + width: 200px; + line-height: 0; + margin-bottom: 4px; + border: 1px dotted black; + } + div.flexbox > * { + vertical-align: top; + display: inline-block; + } + div.a { + height: 10px; + width: 10px; + background: lightgreen; + border-style: solid; + border-color: purple; + border-top-width: 1px; + border-right-width: 2px; + border-bottom-width: 3px; + border-left-width: 4px; + padding: 2px; + } + div.b { + height: 10px; + width: 50px; + background: pink; + padding: 4px 3px 2px 1px; + margin: 2px 3px 4px 5px; + } + div.c { + height: 10px; + width: 100px; + background: orange; + margin: 3px; + border: 2px dashed teal; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a" style="margin-left: 180px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 118px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 8px"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a" style="margin-left: 90px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 59px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 4px"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div style="margin-left: 118px"><div class="b"/></div> + </div> + <div class="flexbox"> + <div class="a" + /><div style="margin-left: 4px"><div class="b"/></div><div style="margin-left: 4px"><div class="c"/></div> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a" style="margin-left: 90px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: 29.5px" + /><div style="margin-left: 59px"><div class="b"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-left: calc(8px / 6)" + /><div style="margin-left: calc(8px / 3)"><div class="b"/></div><div style="margin-left: calc(8px / 3)"><div class="c"/></div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-002.xhtml new file mode 100644 index 0000000..d46e872 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-002.xhtml
@@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of flex containers testing each possible value of + the 'justify-content' property, with margin/border/padding on the + flex items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a horizontal flex container, with margins/border/padding on flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-horiz-002-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + display: flex; + margin-bottom: 4px; + border: 1px dotted black; + } + div.a { + height: 10px; + flex: 0 10px; + background: lightgreen; + border-style: solid; + border-color: purple; + border-top-width: 1px; + border-right-width: 2px; + border-bottom-width: 3px; + border-left-width: 4px; + padding: 2px; + } + div.b { + height: 10px; + flex: 0 50px; + background: pink; + padding: 4px 3px 2px 1px; + margin: 2px 3px 4px 5px; + } + div.c { + height: 10px; + flex: 0 100px; + background: orange; + margin: 3px; + border: 2px dashed teal; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-003-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-003-expected.xhtml new file mode 100644 index 0000000..beeec86 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-003-expected.xhtml
@@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + body { margin-left: 0px; } /* We'll apply margins w/ style attribute */ + div.flexbox { + margin-bottom: 2px; + line-height: 0; + } + div.flexbox > * { + display: inline-block; + } + div.a { + height: 20px; + width: 35px; + background: lightgreen; + } + div.b { + height: 20px; + width: 40px; + background: pink; + } + div.c { + height: 20px; + width: 45px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="margin-left: 95px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 55px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 10px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="margin-left: 97.5px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 77.5px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 55px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="margin-left: 97.5px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 77.5px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 55px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-003.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-003.xhtml new file mode 100644 index 0000000..4957fa9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-003.xhtml
@@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a variety of 'display: flex' examples testing each + possible value of the 'justify-content' property, and with each + individual flex item being wider than the flexbox itself (so that + there isn't any packing space available). + * For 'flex-start'/'space-between', we should overflow on the end + (right) side. + * For 'flex-end', we should overflow on the start (left) side. + * For 'center'/'space-around', we should overflow equally on both sides. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a horizontal flex container, and its effects on flex items that overflow</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-horiz-003-ref.xhtml"/> + <style> + body { margin-left: 100px; } /* So we can see left-overflowed stuff */ + div.flexbox { + width: 30px; + display: flex; + margin-bottom: 2px; + } + div.a { + height: 20px; + flex: 0 0 35px; + background: lightgreen; + } + div.b { + height: 20px; + flex: 0 0 40px; + background: pink; + } + div.c { + height: 20px; + flex: 0 0 45px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-004-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-004-expected.xhtml new file mode 100644 index 0000000..852c3dff --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-004-expected.xhtml
@@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + body { margin-left: 0px; } /* We'll apply margins w/ style attribute */ + div.flexbox { + line-height: 0; + margin-bottom: 4px; + } + div.flexbox > * { + vertical-align: top; + display: inline-block; + } + div.a { + height: 10px; + width: 35px; + background: lightgreen; + border-style: solid; + border-color: purple; + border-top-width: 1px; + border-right-width: 2px; + border-bottom-width: 3px; + border-left-width: 4px; + padding: 2px; + } + div.b { + height: 10px; + width: 40px; + background: pink; + padding: 4px 3px 2px 1px; + margin: 2px 3px 4px 5px; + } + div.c { + height: 10px; + width: 45px; + background: orange; + margin: 3px; + border: 2px dashed teal; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="margin-left: 85px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 33px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: -22px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="margin-left: 92.5px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 66.5px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 39px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="margin-left: 92.5px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-left: 66.5px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-left: 39px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-004.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-004.xhtml new file mode 100644 index 0000000..f311c06f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-004.xhtml
@@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a variety of 'display: flex' examples testing each + possible value of the 'justify-content' property, and with each + individual flex item being wider than the flexbox itself (so that + there isn't any packing space available). + * For 'flex-start'/'space-between', we should overflow on the end + (right) side. + * For 'flex-end', we should overflow on the start (left) side. + * For 'center'/'space-around', we should overflow equally on both sides. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a horizontal flex container, and its effects on flex items that overflow, with margins/border/padding on flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-horiz-004-ref.xhtml"/> + <style> + body { margin-left: 100px; } /* So we can see left-overflowed stuff */ + div.flexbox { + width: 30px; + display: flex; + margin-bottom: 4px; + } + div.a { + height: 10px; + flex: 0 0 35px; + background: lightgreen; + border-style: solid; + border-color: purple; + border-top-width: 1px; + border-right-width: 2px; + border-bottom-width: 3px; + border-left-width: 4px; + padding: 2px; + } + div.b { + height: 10px; + flex: 0 0 40px; + background: pink; + padding: 4px 3px 2px 1px; + margin: 2px 3px 4px 5px; + } + div.c { + height: 10px; + flex: 0 0 45px; + background: orange; + margin: 3px; + border: 2px dashed teal; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-005-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-005-expected.xhtml new file mode 100644 index 0000000..1bbe027e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-005-expected.xhtml
@@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + body { line-height: 0; } + + div.flexbox > * { + display: inline-block; + } + div.a { + height: 20px; + width: 10px; + background: lightgreen; + } + div.b { + height: 20px; + width: 50px; + background: pink; + } + div.c { + height: 20px; + width: 100px; + background: orange; + } + .centerParent { + text-align: center; + } + .center { + display: inline-block; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="float:right; clear:right;"> + <div class="a"/> + </div> + <div class="flexbox" style="float:right; clear:right;"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="float:right; clear:right;"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + <div style="clear:right;"></div> + + <!-- center --> + <div class="centerParent"> + <div class="flexbox center"> + <div class="a"/> + </div> + </div> + <div class="centerParent"> + <div class="flexbox center"> + <div class="a"/><div class="b"/> + </div> + </div> + <div class="centerParent"> + <div class="flexbox center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b" style="float: right"/> + </div> + <div class="flexbox"> + <div class="a" style="display:block; float: left"/> + <div class="c" style="float:right"/> + <!-- Use fixed-size margins to subtract space for "a" and "c", and then + use auto margins to center 'b' within the remaining space --> + <div style="display: block; margin-left: 10px; margin-right: 100px"> + <div class="b" style="margin: auto"/> + </div> + </div> + + <!-- space-around --> + <!-- Center "a" here just as we did above in the "center" case. --> + <div class="centerParent"> + <div class="flexbox center"> + <div class="a"/> + </div> + </div> + <!-- For the rest, we'll use a flex container with invisible flexible items + instead of packing space. That's simpler than trying to hack up + a width-independent reference case for "justify-content: space-around". + (There are other reftests to ensure that basic flex container + behavior is correct, so it's safe to rely on it here.) --> + <div class="flexbox" style="display: flex"> + <div style="flex: 0.5"/> + <div class="a"/> + <div style="flex: 1"/> + <div class="b"/> + <div style="flex: 0.5"/> + </div> + <div class="flexbox" style="display: flex"> + <div style="flex: 0.5"/> + <div class="a"/> + <div style="flex: 1"/> + <div class="b"/> + <div style="flex: 1"/> + <div class="c"/> + <div style="flex: 0.5"/> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-005.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-005.xhtml new file mode 100644 index 0000000..b15bf7f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-005.xhtml
@@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase to exercise each possible value of the 'justify-content' + property, in an auto-sized horizontal flex container. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in an auto-sized horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-horiz-005-ref.xhtml"/> + <style> + div.flexbox { + display: flex; + } + div.a { + height: 20px; + flex: 0 10px; + background: lightgreen; + } + div.b { + height: 20px; + flex: 0 50px; + background: pink; + } + div.c { + height: 20px; + flex: 0 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001a-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001a-expected.xhtml new file mode 100644 index 0000000..58f51060 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001a-expected.xhtml
@@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + margin-right: 2px; + float: left; + } + div.a { + width: 20px; + height: 10px; + background: lightgreen; + } + div.b { + width: 20px; + height: 50px; + background: pink; + } + div.c { + width: 20px; + height: 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a" style="margin-top: 190px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 140px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 40px"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a" style="margin-top: 95px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 70px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 20px"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b" style="margin-top: 140px"/> + </div> + <div class="flexbox"> + <div class="a" + /><div class="b" style="margin-top: 20px" + /><div class="c" style="margin-top: 20px"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a" style="margin-top: 95px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 35px" + /><div class="b" style="margin-top: 70px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: calc(40px / 6)" + /><div class="b" style="margin-top: calc(40px / 3)" + /><div class="c" style="margin-top: calc(40px / 3)"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001a.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001a.xhtml new file mode 100644 index 0000000..2772215 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001a.xhtml
@@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of vertical flex containers, testing each + possible value of the 'justify-content' property. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-vert-001-ref.xhtml"/> + <style> + div.flexbox { + height: 200px; + display: flex; + flex-direction: column; + margin-right: 2px; + float: left; + } + div.a { + width: 20px; + flex: 0 10px; + background: lightgreen; + } + div.b { + width: 20px; + flex: 0 50px; + background: pink; + } + div.c { + width: 20px; + flex: 0 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001b-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001b-expected.xhtml new file mode 100644 index 0000000..58f51060 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001b-expected.xhtml
@@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + margin-right: 2px; + float: left; + } + div.a { + width: 20px; + height: 10px; + background: lightgreen; + } + div.b { + width: 20px; + height: 50px; + background: pink; + } + div.c { + width: 20px; + height: 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a" style="margin-top: 190px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 140px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 40px"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a" style="margin-top: 95px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 70px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 20px"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b" style="margin-top: 140px"/> + </div> + <div class="flexbox"> + <div class="a" + /><div class="b" style="margin-top: 20px" + /><div class="c" style="margin-top: 20px"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a" style="margin-top: 95px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 35px" + /><div class="b" style="margin-top: 70px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: calc(40px / 6)" + /><div class="b" style="margin-top: calc(40px / 3)" + /><div class="c" style="margin-top: calc(40px / 3)"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001b.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001b.xhtml new file mode 100644 index 0000000..3ec9ed87 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-001b.xhtml
@@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of vertical flex containers, testing each + possible value of the 'justify-content' property. The flex containers' + heights are determined by their "min-height" property. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a vertical flex container with "min-height"</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-vert-001-ref.xhtml"/> + <style> + div.flexbox { + min-height: 200px; + display: flex; + flex-direction: column; + margin-right: 2px; + float: left; + } + div.a { + width: 20px; + flex: 0 10px; + background: lightgreen; + } + div.b { + width: 20px; + flex: 0 50px; + background: pink; + } + div.c { + width: 20px; + flex: 0 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-002-expected.xhtml new file mode 100644 index 0000000..90fb08c1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-002-expected.xhtml
@@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + height: 200px; + margin-right: 2px; + border: 1px dotted black; + float: left; + } + div.a { + width: 10px; + height: 10px; + background: lightgreen; + border-style: solid; + border-color: purple; + border-top-width: 4px; + border-right-width: 3px; + border-bottom-width: 2px; + border-left-width: 1px; + padding: 2px; + } + div.b { + width: 10px; + height: 50px; + background: pink; + padding: 1px 2px 3px 4px; + margin: 5px 4px 3px 2px; + } + div.c { + width: 10px; + height: 100px; + background: orange; + margin: 3px; + margin-top: 6px; /* Increased to counteract for collapsing */ + border: 2px dashed teal; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a" style="margin-top: 180px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 118px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 8px"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a" style="margin-top: 90px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 59px"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 4px"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div style="margin-top: 123px"><div class="b"/></div> + </div> + <div class="flexbox"> + <div class="a" + /><div style="margin-top: 9px"><div class="b"/></div><div style="margin-top: 10px"><div class="c"/></div> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a" style="margin-top: 90px"/> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: 29.5px" + /><div style="margin-top: 64px"><div class="b"/></div> + </div> + <div class="flexbox"> + <div class="a" style="margin-top: calc(8px / 6)" + /><div style="margin-top: calc(5px + 8px / 3)"><div class="b"/></div><div style="margin-top: calc(6px + 8px / 3)"><div class="c"/></div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-002.xhtml new file mode 100644 index 0000000..baca12f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-002.xhtml
@@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of flex containers testing each possible value of + the 'justify-content' property, with margin/border/padding on the + flex items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a vertical flex container, with margins/border/padding on flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-vert-002-ref.xhtml"/> + <style> + div.flexbox { + height: 200px; + display: flex; + flex-direction: column; + margin-right: 2px; + float: left; + border: 1px dotted black; + } + div.a { + width: 10px; + flex: 0 10px; + background: lightgreen; + border-style: solid; + border-color: purple; + border-top-width: 4px; + border-right-width: 3px; + border-bottom-width: 2px; + border-left-width: 1px; + padding: 2px; + } + div.b { + width: 10px; + flex: 0 50px; + background: pink; + padding: 1px 2px 3px 4px; + margin: 5px 4px 3px 2px; + } + div.c { + width: 10px; + flex: 0 100px; + background: orange; + margin: 3px; + border: 2px dashed teal; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-003-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-003-expected.xhtml new file mode 100644 index 0000000..63fbd19 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-003-expected.xhtml
@@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + body { margin-top: 0px; } /* We'll apply margins w/ style attribute */ + div.flexbox { + height: 200px; + margin-right: 2px; + float: left; + } + div.a { + width: 20px; + height: 35px; + background: lightgreen; + } + div.b { + width: 20px; + height: 40px; + background: pink; + } + div.c { + width: 20px; + height: 45px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="margin-top: 95px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 55px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 10px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="margin-top: 97.5px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 77.5px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 55px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="margin-top: 97.5px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 77.5px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 55px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-003.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-003.xhtml new file mode 100644 index 0000000..ca31802 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-003.xhtml
@@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of vertical flex containers testing each possible + value of the 'justify-content' property, and with each individual + flex item being larger than the flexbox itself (so that there isn't any + packing space available). + + * For 'flex-start'/'space-between', we should overflow on the end + (bottom) side. + * For 'flex-end', we should overflow on the start (top) side. + * For 'center'/'space-around', we should overflow equally on both sides. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a vertical flex container, and its effects on flex items that overflow</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-vert-003-ref.xhtml"/> + <style> + body { margin-top: 100px; } /* So we can see top-overflowed stuff */ + div.flexbox { + height: 30px; + display: flex; + flex-direction: column; + margin-right: 2px; + float: left; + } + div.a { + width: 20px; + flex: 0 0 35px; + background: lightgreen; + } + div.b { + width: 20px; + flex: 0 0 40px; + background: pink; + } + div.c { + width: 20px; + flex: 0 0 45px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-004-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-004-expected.xhtml new file mode 100644 index 0000000..0d5ef4e5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-004-expected.xhtml
@@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + body { margin-top: 0px; } /* We'll apply margins w/ style attribute */ + div.flexbox { + margin-right: 4px; + float: left; + } + div.a { + width: 10px; + height: 35px; + background: lightgreen; + border-style: solid; + border-color: purple; + border-top-width: 4px; + border-right-width: 3px; + border-bottom-width: 2px; + border-left-width: 1px; + padding: 2px; + } + div.b { + width: 10px; + height: 40px; + background: pink; + padding: 1px 2px 3px 4px; + margin: 5px 4px 3px 2px; + } + div.c { + width: 10px; + height: 45px; + background: orange; + margin: 3px; + margin-top: 6px; /* Increased to counteract for collapsing */ + border: 2px dashed teal; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="margin-top: 85px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 33px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: -22px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="margin-top: 92.5px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 66.5px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 39px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 100px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="margin-top: 92.5px"> + <div class="a"/> + </div> + <div class="flexbox" style="margin-top: 66.5px"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="margin-top: 39px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-004.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-004.xhtml new file mode 100644 index 0000000..1119feb --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-004.xhtml
@@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a series of vertical flex containers, testing each possible + value of the 'justify-content' property, and with each individual + flex item being larger than the flexbox itself (so that there isn't any + packing space available). Also, we've got margin/border/padding on the + flex items. + * For 'flex-start'/'space-between', we should overflow on the end + (bottom) side. + * For 'flex-end', we should overflow on the start (top) side. + * For 'center'/'space-around', we should overflow equally on both sides. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in a vertical flex container, and its effects on flex items that overflow, with margins/border/padding on flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-vert-004-ref.xhtml"/> + <style> + body { margin-top: 100px; } /* So we can see top-overflowed stuff */ + div.flexbox { + height: 30px; + display: flex; + flex-direction: column; + margin-right: 4px; + float: left; + } + div.a { + width: 10px; + flex: 0 0 35px; + background: lightgreen; + border-style: solid; + border-color: purple; + border-top-width: 4px; + border-right-width: 3px; + border-bottom-width: 2px; + border-left-width: 1px; + padding: 2px; + } + div.b { + width: 10px; + flex: 0 0 40px; + background: pink; + padding: 1px 2px 3px 4px; + margin: 5px 4px 3px 2px; + } + div.c { + width: 10px; + flex: 0 0 45px; + background: orange; + margin: 3px; + border: 2px dashed teal; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-005-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-005-expected.xhtml new file mode 100644 index 0000000..9e9a2c01 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-005-expected.xhtml
@@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + margin-right: 2px; + float: left; + border: 1px dotted black; + } + div.a { + width: 20px; + height: 10px; + background: lightgreen; + } + div.b { + width: 20px; + height: 50px; + background: pink; + } + div.c { + width: 20px; + height: 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-005.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-005.xhtml new file mode 100644 index 0000000..4f7dc996 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-005.xhtml
@@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase to exercise each possible value of the 'justify-content' + property, in an auto-sized vertical flex container. The flex container + should shrink-wrap its contents' heights, leaving no packing space + available. So, the "justify-content" values should have no effect + in this testcase. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing 'justify-content' in an auto-sized vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#justify-content-property"/> + <link rel="match" href="flexbox-justify-content-vert-005-ref.xhtml"/> + <style> + div.flexbox { + display: flex; + flex-direction: column; + margin-right: 2px; + float: left; + border: 1px dotted black; + } + div.a { + width: 20px; + flex: 0 10px; + background: lightgreen; + } + div.b { + width: 20px; + flex: 0 50px; + background: pink; + } + div.c { + width: 20px; + flex: 0 100px; + background: orange; + } + </style> + </head> + <body> + + <!-- default (start) --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"></div> + </div> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-start --> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-start"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- flex-end --> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: flex-end"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- center --> + <div class="flexbox" style="justify-content: center"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: center"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-between --> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-between"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- space-around --> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox" style="justify-content: space-around"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-001-expected.xhtml new file mode 100644 index 0000000..d13b155 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-001-expected.xhtml
@@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 20px; } + div.flexbox { + width: 200px; + background: lightgray; + margin-bottom: 2px; + } + div.a { + width: 20px; + background: green; + display: inline-block; + } + div.b { + width: 20px; + background: pink; + display: inline-block; + } + div.c { + width: 20px; + background: purple; + display: inline-block; + } + + <!-- These classes allow us to conveniently/concisely specify margin + values below, for exact positioning of the items on each reference + line. ("l" = "margin-_l_eft", and the number = number of pixels) --> + div.l180 { margin-left: 180px } + div.l90 { margin-left: 90px } + div.l80 { margin-left: 80px } + div.l70 { margin-left: 70px } + div.l53 { margin-left: calc(160px / 3) } <!-- == 53.33333px --> + div.l35 { margin-left: 35px } + </style> + </head> + <body> + <!-- just one item --> + <div class="flexbox"> + <div class="a l180"/> + </div> + <div class="flexbox"> + <div class="b l90"/> + </div> + <div class="flexbox"> + <div class="c"/> + </div> + + <!-- Two items --> + <div class="flexbox"> + <div class="a l53"/><div class="b l53"/> + </div> + <div class="flexbox"> + <div class="a l80"/><div class="c"/> + </div> + <div class="flexbox"> + <div class="b l53"/><div class="c l53"/> + </div> + + <!-- Three items --> + <div class="flexbox"> + <div class="a l35"/><div class="b l35"/><div class="c l35"/> + </div> + <div class="flexbox"> + <div class="a l35"/><div class="c"/><div class="b l70"/> + </div> + <div class="flexbox"> + <div class="b l35"/><div class="a l70"/><div class="c"/> + </div> + <div class="flexbox"> + <div class="b l35"/><div class="c l35"/><div class="a l70"/> + </div> + <div class="flexbox"> + <div class="c"/><div class="a l70"/><div class="b l35"/> + </div> + <div class="flexbox"> + <div class="c"/><div class="b l70"/><div class="a l70"/> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-001.xhtml new file mode 100644 index 0000000..4c5d1a39 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-001.xhtml
@@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a variety of 'display: flex' examples + with margin-left and/or margin-right set to 'auto'. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing horizontal auto margins on flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#auto-margins"/> + <link rel="match" href="flexbox-margin-auto-horiz-001-ref.xhtml"/> + <style> + div.flexbox { + width: 200px; + height: 20px; + background: lightgray; + display: flex; + margin-bottom: 2px; + } + div.a { + width: 20px; + background: green; + margin-left: auto; + } + div.b { + width: 20px; + background: pink; + margin-left: auto; + margin-right: auto; + } + div.c { + width: 20px; + background: purple; + margin-right: auto; + } + </style> + </head> + <body> + <!-- just one item --> + <div class="flexbox"> + <div class="a"/> + </div> + <div class="flexbox"> + <div class="b"/> + </div> + <div class="flexbox"> + <div class="c"/> + </div> + + <!-- Two items --> + <div class="flexbox"> + <div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="c"/> + </div> + <div class="flexbox"> + <div class="b"/><div class="c"/> + </div> + + <!-- Three items --> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + <div class="flexbox"> + <div class="a"/><div class="c"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="b"/><div class="a"/><div class="c"/> + </div> + <div class="flexbox"> + <div class="b"/><div class="c"/><div class="a"/> + </div> + <div class="flexbox"> + <div class="c"/><div class="a"/><div class="b"/> + </div> + <div class="flexbox"> + <div class="c"/><div class="b"/><div class="a"/> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-002-expected.xhtml new file mode 100644 index 0000000..f2b323d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-002-expected.xhtml
@@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a variety of 'display: flex' examples + with margin-top and/or margin-bottom set to 'auto' on flex items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + border: 2px dotted black; + display: flex; + margin-bottom: 2px; + width: 100px; + } + div.fixedSize { + width: 20px; + height: 20px; + } + div.gray { background: gray; } + div.green { background: green; } + div.pink { background: pink; } + div.blue { background: blue; } + </style> + </head> + <body> + + <!-- fixed-height flexbox, with items that have auto margins --> + <div class="flexbox" style="height: 100px"> + <div class="fixedSize green" style="margin-top: 80px"/> + <div class="fixedSize pink"/> + <div class="fixedSize blue" style="margin-top: 40px"/> + </div> + <!-- fixed-height flexbox, with items that have auto & fixed margins --> + <div class="flexbox" style="height: 100px"> + <div class="fixedSize green" style="margin-top: 70px"/> + <div class="fixedSize pink" style="margin-top: 10px"/> + </div> + + <!-- height-shrinkwrapping flexbox, sized by item w/ fixed height & margins, + with other items that have auto margins --> + <div class="flexbox" style="height: 50px"> + <div class="fixedSize green" style="margin-top: 30px"/> + <div class="fixedSize pink"/> + <div class="fixedSize blue" style="margin-top: 15px"/> + <div class="gray" style="width: 10px; height: 30px; margin-top: 10px"/> + </div> + + <!-- height-shrinkwrapping flexbox, sized by item w/ fixed height & margins, + with other items that have auto & fixed margins --> + <div class="flexbox" style="height: 50px"> + <div class="fixedSize green" style="margin-top: 20px"/> + <div class="fixedSize pink" style="margin-top: 10px"/> + <div class="gray" style="width: 10px; height: 30px; margin-top: 10px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-002.xhtml new file mode 100644 index 0000000..520f094d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-margin-auto-horiz-002.xhtml
@@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with a variety of 'display: flex' examples + with margin-top and/or margin-bottom set to 'auto' on flex items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing vertical auto margins on flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#auto-margins"/> + <link rel="match" href="flexbox-margin-auto-horiz-002-ref.xhtml"/> + <style> + div.flexbox { + border: 2px dotted black; + display: flex; + margin-bottom: 2px; + width: 100px; + } + div.fixedSize { + width: 20px; + height: 20px; + } + div.gray { background: gray; } + div.green { background: green; } + div.pink { background: pink; } + div.blue { background: blue; } + + div.autoTop { margin-top: auto; } + div.autoBottom { margin-bottom: auto; } + div.fixedTop { margin-top: 10px; } + div.fixedBottom { margin-bottom: 10px; } + </style> + </head> + <body> + + <!-- fixed-height flexbox, with items that have auto margins --> + <div class="flexbox" style="height: 100px"> + <div class="fixedSize green autoTop"/> + <div class="fixedSize pink autoBottom"/> + <div class="fixedSize blue autoTop autoBottom"/> + </div> + <!-- fixed-height flexbox, with items that have auto & fixed margins --> + <div class="flexbox" style="height: 100px"> + <div class="fixedSize green autoTop fixedBottom"/> + <div class="fixedSize pink autoBottom fixedTop"/> + </div> + + <!-- height-shrinkwrapping flexbox, sized by item w/ fixed height & margins, + with other items that have auto margins --> + <div class="flexbox"> + <div class="fixedSize green autoTop"/> + <div class="fixedSize pink autoBottom"/> + <div class="fixedSize blue autoTop autoBottom"/> + <div class="fixedTop fixedBottom gray" style="width: 10px; height: 30px"/> + </div> + + <!-- height-shrinkwrapping flexbox, sized by item w/ fixed height & margins, + with other items that have auto & fixed margins --> + <div class="flexbox"> + <div class="fixedSize green autoTop fixedBottom"/> + <div class="fixedSize pink autoBottom fixedTop"/> + <div class="fixedTop fixedBottom gray" style="width: 10px; height: 30px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-expected.xhtml new file mode 100644 index 0000000..f19ec25 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-expected.xhtml
@@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + } + div.a { + display: inline-block; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + display: inline-block; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + display: inline-block; + background: orange; + } + div.flexNone { + display: inline-block; + background: pink; + } + div.flexBasis { + display: inline-block; + background: gray; + } + div.spacer { + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="width: 74px"/><div class="b" style="width: 110px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 56.5px"/><div class="c" style="width: 137.5px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 179px"/><div class="flexNone"> + <div class="spacer" style="width: 15px"/></div> + </div> + <div class="flexbox"> + <div class="b" style="width: 66px"/><div class="c" style="width: 124px"/> + </div> + <div class="flexbox"> + <div class="b" style="width: 160px"/><div class="flexNone"> + <div class="spacer" style="width: 30px"/></div> + </div> + <div class="flexbox"> + <div class="a" style="width: 39px"/><div class="b" style="width: 40px" + /><div class="flexBasis" style="width: 20px"/><div class="c" style="width: 85px"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-reverse-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-reverse-expected.xhtml new file mode 100644 index 0000000..3213d0f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-reverse-expected.xhtml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + } + div.a { + display: inline-block; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + display: inline-block; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + display: inline-block; + background: orange; + } + div.flexNone { + display: inline-block; + background: pink; + } + div.flexBasis { + display: inline-block; + background: gray; + } + div.spacer { + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="b" style="width: 110px"/><div class="a" style="width: 74px"/> + </div> + <div class="flexbox"> + <div class="c" style="width: 137.5px"/><div class="a" style="width: 56.5px"/> + </div> + <div class="flexbox"> + <div class="flexNone"><div class="spacer" style="width: 15px"/> + </div><div class="a" style="width: 179px"/> + </div> + <div class="flexbox"> + <div class="c" style="width: 124px" + /><div class="b" style="width: 66px"/> + </div> + <div class="flexbox"> + <div class="flexNone"><div class="spacer" style="width: 30px"/> + </div><div class="b" style="width: 160px"/> + </div> + <div class="flexbox"> + <div class="c" style="width: 85px" + /><div class="flexBasis" style="width: 20px" + /><div class="b" style="width: 40px" + /><div class="a" style="width: 39px"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-reverse.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-reverse.xhtml new file mode 100644 index 0000000..92dda1e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-reverse.xhtml
@@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with borders on flex items, and "flex-direction: row-reverse" to + reverse the flex container's main axis. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing borders on flex items in a row-reverse horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-001-reverse-ref.xhtml"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + font-size: 10px; + display: flex; + flex-direction: row-reverse; + } + div.a { + flex: 1 0 24px; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + flex: 2 0 10px; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + flex: 3 0 40px; + background: orange; + } + div.flexNone { + flex: none; + background: pink; + } + div.flexBasis { + flex: 0 0 20px; + background: gray; + } + div.spacer { + display: inline-block; + width: 15px; + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"><div class="a"></div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div class="c"/></div> + <div class="flexbox"><div class="a"/> + <div class="flexNone"><div class="spacer"/></div> + </div> + <div class="flexbox"><div class="b"/><div class="c"/></div> + <div class="flexbox"><div class="b"/> + <div class="flexNone"><div class="spacer"/><div class="spacer"/></div> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-expected.xhtml new file mode 100644 index 0000000..3213d0f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-expected.xhtml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + } + div.a { + display: inline-block; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + display: inline-block; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + display: inline-block; + background: orange; + } + div.flexNone { + display: inline-block; + background: pink; + } + div.flexBasis { + display: inline-block; + background: gray; + } + div.spacer { + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="b" style="width: 110px"/><div class="a" style="width: 74px"/> + </div> + <div class="flexbox"> + <div class="c" style="width: 137.5px"/><div class="a" style="width: 56.5px"/> + </div> + <div class="flexbox"> + <div class="flexNone"><div class="spacer" style="width: 15px"/> + </div><div class="a" style="width: 179px"/> + </div> + <div class="flexbox"> + <div class="c" style="width: 124px" + /><div class="b" style="width: 66px"/> + </div> + <div class="flexbox"> + <div class="flexNone"><div class="spacer" style="width: 30px"/> + </div><div class="b" style="width: 160px"/> + </div> + <div class="flexbox"> + <div class="c" style="width: 85px" + /><div class="flexBasis" style="width: 20px" + /><div class="b" style="width: 40px" + /><div class="a" style="width: 39px"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-reverse-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-reverse-expected.xhtml new file mode 100644 index 0000000..f19ec25 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-reverse-expected.xhtml
@@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + } + div.a { + display: inline-block; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + display: inline-block; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + display: inline-block; + background: orange; + } + div.flexNone { + display: inline-block; + background: pink; + } + div.flexBasis { + display: inline-block; + background: gray; + } + div.spacer { + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="width: 74px"/><div class="b" style="width: 110px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 56.5px"/><div class="c" style="width: 137.5px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 179px"/><div class="flexNone"> + <div class="spacer" style="width: 15px"/></div> + </div> + <div class="flexbox"> + <div class="b" style="width: 66px"/><div class="c" style="width: 124px"/> + </div> + <div class="flexbox"> + <div class="b" style="width: 160px"/><div class="flexNone"> + <div class="spacer" style="width: 30px"/></div> + </div> + <div class="flexbox"> + <div class="a" style="width: 39px"/><div class="b" style="width: 40px" + /><div class="flexBasis" style="width: 20px"/><div class="c" style="width: 85px"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-reverse.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-reverse.xhtml new file mode 100644 index 0000000..1d54ece0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl-reverse.xhtml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with borders on flex items, and "direction: rtl" and + "flex-direction: row-reverse" to *doubly* reverse the flex container's + main axis. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing borders on flex items in a row-reverse horizontal flex container, with 'direction: rtl'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-001-ref.xhtml"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + font-size: 10px; + display: flex; + flex-direction: row-reverse; + direction: rtl; + } + div.a { + flex: 1 0 24px; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + flex: 2 0 10px; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + flex: 3 0 40px; + background: orange; + } + div.flexNone { + flex: none; + background: pink; + } + div.flexBasis { + flex: 0 0 20px; + background: gray; + } + div.spacer { + display: inline-block; + width: 15px; + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"><div class="a"></div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div class="c"/></div> + <div class="flexbox"><div class="a"/> + <div class="flexNone"><div class="spacer"/></div> + </div> + <div class="flexbox"><div class="b"/><div class="c"/></div> + <div class="flexbox"><div class="b"/> + <div class="flexNone"><div class="spacer"/><div class="spacer"/></div> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl.xhtml new file mode 100644 index 0000000..2185ac2c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001-rtl.xhtml
@@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with borders on flex items, and "direction: rtl" to reverse + the flex container's main axis. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing borders on flex items in a horizontal flex container with 'direction: rtl'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-001-reverse-ref.xhtml"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + font-size: 10px; + display: flex; + direction: rtl; + } + div.a { + flex: 1 0 24px; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + flex: 2 0 10px; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + flex: 3 0 40px; + background: orange; + } + div.flexNone { + flex: none; + background: pink; + } + div.flexBasis { + flex: 0 0 20px; + background: gray; + } + div.spacer { + display: inline-block; + width: 15px; + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"><div class="a"></div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div class="c"/></div> + <div class="flexbox"><div class="a"/> + <div class="flexNone"><div class="spacer"/></div> + </div> + <div class="flexbox"><div class="b"/><div class="c"/></div> + <div class="flexbox"><div class="b"/> + <div class="flexNone"><div class="spacer"/><div class="spacer"/></div> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001.xhtml new file mode 100644 index 0000000..3393451 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-001.xhtml
@@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with borders on flex items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing borders on flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-001-ref.xhtml"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + font-size: 10px; + display: flex; + } + div.a { + flex: 1 0 24px; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + flex: 2 0 10px; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + flex: 3 0 40px; + background: orange; + } + div.flexNone { + flex: none; + background: pink; + } + div.flexBasis { + flex: 0 0 20px; + background: gray; + } + div.spacer { + display: inline-block; + width: 15px; + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"><div class="a"></div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div class="c"/></div> + <div class="flexbox"><div class="a"/> + <div class="flexNone"><div class="spacer"/></div> + </div> + <div class="flexbox"><div class="b"/><div class="c"/></div> + <div class="flexbox"><div class="b"/> + <div class="flexNone"><div class="spacer"/><div class="spacer"/></div> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002a-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002a-expected.xhtml new file mode 100644 index 0000000..51fc6944 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002a-expected.xhtml
@@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + } + div.a { + display: inline-block; + background: lightgreen; + margin-left: 1px; + margin-right: 3px; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + display: inline-block; + background: yellow; + margin-left: 2px; + margin-right: 4px; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + display: inline-block; + background: orange; + } + div.flexNone { + display: inline-block; + background: pink; + } + div.flexBasis { + display: inline-block; + background: gray; + } + div.spacer { + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="width: 70px"/><div class="b" style="width: 104px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 52.5px"/><div class="c" style="width: 137.5px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 175px"/><div class="flexNone"> + <div class="spacer" style="width: 15px"/></div> + </div> + <div class="flexbox"> + <div class="b" style="width: 60px"/><div class="c" style="width: 124px"/> + </div> + <div class="flexbox"> + <div class="b" style="width: 154px"/><div class="flexNone"> + <div class="spacer" style="width: 30px"/></div> + </div> + <div class="flexbox"> + <div class="a" style="width: 35px"/><div class="b" style="width: 34px" + /><div class="flexBasis" style="width: 20px"/><div class="c" style="width: 85px"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002a.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002a.xhtml new file mode 100644 index 0000000..b0f18f8d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002a.xhtml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with margin/border on flex items. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing margins and borders on flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-002-ref.xhtml"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + font-size: 10px; + display: flex; + } + div.a { + flex: 1 0 20px; + background: lightgreen; + margin-left: 1px; + margin-right: 3px; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + flex: 2 0 4px; + background: yellow; + margin-left: 2px; + margin-right: 4px; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + flex: 3 0 40px; + background: orange; + } + div.flexNone { + flex: none; + background: pink; + } + div.flexBasis { + flex: 0 0 20px; + background: gray; + } + div.spacer { + display: inline-block; + width: 15px; + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"><div class="a"></div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div class="c"/></div> + <div class="flexbox"><div class="a"/> + <div class="flexNone"><div class="spacer"/></div> + </div> + <div class="flexbox"><div class="b"/><div class="c"/></div> + <div class="flexbox"><div class="b"/> + <div class="flexNone"><div class="spacer"/><div class="spacer"/></div> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002b-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002b-expected.xhtml new file mode 100644 index 0000000..51fc6944 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002b-expected.xhtml
@@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + } + div.a { + display: inline-block; + background: lightgreen; + margin-left: 1px; + margin-right: 3px; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.b { + display: inline-block; + background: yellow; + margin-left: 2px; + margin-right: 4px; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + div.c { + display: inline-block; + background: orange; + } + div.flexNone { + display: inline-block; + background: pink; + } + div.flexBasis { + display: inline-block; + background: gray; + } + div.spacer { + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="width: 70px"/><div class="b" style="width: 104px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 52.5px"/><div class="c" style="width: 137.5px"/> + </div> + <div class="flexbox"> + <div class="a" style="width: 175px"/><div class="flexNone"> + <div class="spacer" style="width: 15px"/></div> + </div> + <div class="flexbox"> + <div class="b" style="width: 60px"/><div class="c" style="width: 124px"/> + </div> + <div class="flexbox"> + <div class="b" style="width: 154px"/><div class="flexNone"> + <div class="spacer" style="width: 30px"/></div> + </div> + <div class="flexbox"> + <div class="a" style="width: 35px"/><div class="b" style="width: 34px" + /><div class="flexBasis" style="width: 20px"/><div class="c" style="width: 85px"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002b.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002b.xhtml new file mode 100644 index 0000000..a312d6d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-002b.xhtml
@@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with margin/border/padding on flex items. (NOTE: This renders + the same as the "-2a" variant, which lacks padding, because we've + just replaced some of the "-2a" variant's content-box area with + padding-box area in this test.) --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing margins, borders, and padding on flex items in a horizontal flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-002-ref.xhtml"/> + <style> + div { height: 100px; border: 0; } + div.flexbox { + width: 200px; + font-size: 10px; + display: flex; + } + div.a { + flex: 1 0 9px; + background: lightgreen; + margin-left: 1px; + margin-right: 3px; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + padding-left: 5px; + padding-right: 6px; + } + div.b { + flex: 2 0 1px; + background: yellow; + margin-left: 2px; + margin-right: 4px; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + padding-left: 1px; + padding-right: 2px; + } + div.c { + flex: 3 0 40px; + background: orange; + } + div.flexNone { + flex: none; + background: pink; + } + div.flexBasis { + flex: 0 0 20px; + background: gray; + } + div.spacer { + display: inline-block; + width: 15px; + height: 15px; + background: purple; + } + </style> + </head> + <body> + <div class="flexbox"><div class="a"></div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div class="c"/></div> + <div class="flexbox"><div class="a"/> + <div class="flexNone"><div class="spacer"/></div> + </div> + <div class="flexbox"><div class="b"/><div class="c"/></div> + <div class="flexbox"><div class="b"/> + <div class="flexNone"><div class="spacer"/><div class="spacer"/></div> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="flexBasis"/><div class="c"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-expected.xhtml new file mode 100644 index 0000000..c7a6a37 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-expected.xhtml
@@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 20px; border: 0; } + div.flexbox { + width: 200px; + margin-bottom: 2px; + } + + <!-- customizations for flexbox border/padding --> + .borderA { + border-style: dashed; + border-color: purple; + border-top-width: 6px; + border-right-width: 4px; + border-bottom-width: 2px; + border-left-width: 8px; + } + + .borderB { + border-style: dashed; + border-color: purple; + border-top-width: 4px; + border-right-width: 5px; + border-bottom-width: 6px; + border-left-width: 7px; + } + + .paddingA { + padding: 4px 3px 2px 1px; + } + + .paddingB { + padding: 8px 11px 14px 17px; + } + + div.child1 { + display: inline-block; + width: 74px; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.child2 { + display: inline-block; + width: 110px; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + </style> + </head> + <body> + <div class="flexbox borderA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderA paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderA paddingB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB paddingB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox paddingB" + ><div class="child1"/><div class="child2"/></div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-reverse-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-reverse-expected.xhtml new file mode 100644 index 0000000..971f5d8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-reverse-expected.xhtml
@@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 20px; border: 0; } + div.flexbox { + width: 200px; + margin-bottom: 2px; + } + + <!-- customizations for flexbox border/padding --> + .borderA { + border-style: dashed; + border-color: purple; + border-top-width: 6px; + border-right-width: 4px; + border-bottom-width: 2px; + border-left-width: 8px; + } + + .borderB { + border-style: dashed; + border-color: purple; + border-top-width: 4px; + border-right-width: 5px; + border-bottom-width: 6px; + border-left-width: 7px; + } + + .paddingA { + padding: 4px 3px 2px 1px; + } + + .paddingB { + padding: 8px 11px 14px 17px; + } + + div.child1 { + display: inline-block; + width: 74px; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.child2 { + display: inline-block; + width: 110px; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + </style> + </head> + <body> + <div class="flexbox borderA" + ><div class="child2"/><div class="child1"/></div> + <div class="flexbox borderA paddingA" + ><div class="child2"/><div class="child1"/></div> + <div class="flexbox borderA paddingB" + ><div class="child2"/><div class="child1"/></div> + <div class="flexbox borderB" + ><div class="child2"/><div class="child1"/></div> + <div class="flexbox borderB paddingA" + ><div class="child2"/><div class="child1"/></div> + <div class="flexbox borderB paddingB" + ><div class="child2"/><div class="child1"/></div> + <div class="flexbox paddingA" + ><div class="child2"/><div class="child1"/></div> + <div class="flexbox paddingB" + ><div class="child2"/><div class="child1"/></div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-reverse.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-reverse.xhtml new file mode 100644 index 0000000..f021a5e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003-reverse.xhtml
@@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with border/padding on a row-reverse flex container and on its children --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing borders and padding on a row-reverse horizontal flex container and its flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-003-reverse-ref.xhtml"/> + <style> + div { height: 20px; border: 0; } + div.flexbox { + width: 200px; + display: flex; + flex-direction: row-reverse; + margin-bottom: 2px; + } + + <!-- customizations for flex container border/padding --> + .borderA { + border-style: dashed; + border-color: purple; + border-top-width: 6px; + border-right-width: 4px; + border-bottom-width: 2px; + border-left-width: 8px; + } + + .borderB { + border-style: dashed; + border-color: purple; + border-top-width: 4px; + border-right-width: 5px; + border-bottom-width: 6px; + border-left-width: 7px; + } + + .paddingA { + padding: 4px 3px 2px 1px; + } + + .paddingB { + padding: 8px 11px 14px 17px; + } + + div.child1 { + flex: 1 0 24px; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.child2 { + flex: 2 0 10px; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + </style> + </head> + <body> + <div class="flexbox borderA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderA paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderA paddingB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB paddingB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox paddingB" + ><div class="child1"/><div class="child2"/></div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003.xhtml new file mode 100644 index 0000000..68f2ff0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-003.xhtml
@@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with border/padding on a flex container and on its children --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing borders and padding on a horizontal flex container and its flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-003-ref.xhtml"/> + <style> + div { height: 20px; border: 0; } + div.flexbox { + width: 200px; + display: flex; + margin-bottom: 2px; + } + + <!-- customizations for flex container border/padding --> + .borderA { + border-style: dashed; + border-color: purple; + border-top-width: 6px; + border-right-width: 4px; + border-bottom-width: 2px; + border-left-width: 8px; + } + + .borderB { + border-style: dashed; + border-color: purple; + border-top-width: 4px; + border-right-width: 5px; + border-bottom-width: 6px; + border-left-width: 7px; + } + + .paddingA { + padding: 4px 3px 2px 1px; + } + + .paddingB { + padding: 8px 11px 14px 17px; + } + + div.child1 { + flex: 1 0 24px; + background: lightgreen; + border-style: dotted; + border-left-width: 2px; + border-right-width: 4px; + } + div.child2 { + flex: 2 0 10px; + background: yellow; + border-style: dashed; + border-left-width: 7px; + border-right-width: 3px; + } + </style> + </head> + <body> + <div class="flexbox borderA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderA paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderA paddingB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox borderB paddingB" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox paddingA" + ><div class="child1"/><div class="child2"/></div> + <div class="flexbox paddingB" + ><div class="child1"/><div class="child2"/></div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-004-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-004-expected.xhtml new file mode 100644 index 0000000..57c9463 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-004-expected.xhtml
@@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case - identical to the testcase, but with with the flex items' + vertical margin and padding values set to 0 by default, and then set to + specific pixel values for the items that have a 50px percent-basis. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { border: 0; } + div.flexbox { + width: 200px; + display: flex; + margin-bottom: 2px; + border: 1px dotted black; + } + div.height50 { height: 50px; } + + .marginA { margin: 0 8% 0 4%; } + .marginB { margin: 0 10% 0 14%; } + .paddingA { padding: 0 6% 0 2%; } + .paddingB { padding: 0 8% 0 12%; } + + div.height50 > .marginA { + margin-top: 5px; + margin-bottom: 3px; + } + div.height50 > .marginB { + margin-top: 4px; + margin-bottom: 6px; + } + div.height50 > .paddingA { + padding-top: 4px; + padding-bottom: 2px; + } + div.height50 > .paddingB { + padding-top: 3px; + padding-bottom: 5px; + } + + div.child1 { + flex: none; + width: 10px; + height: 10px; + background: lightgreen; + } + div.child2 { + flex: none; + width: 10px; + height: 10px; + background: purple; + } + + div.filler { + /* Filler-div to fill up content-box and make padding easier to see. */ + height: 10px; + width: 100%; + background: lightgrey; + } + + </style> + </head> + <body> + <div class="flexbox" + ><div class="child1 paddingA"><div class="filler"/></div><div class="child2 paddingB"><div class="filler"/></div><div class="child1 marginA"></div><div class="child2 marginB"></div></div> + + <div class="flexbox height50" + ><div class="child1 paddingA"><div class="filler"/></div><div class="child2 paddingB"><div class="filler"/></div><div class="child1 marginA"></div><div class="child2 marginB"></div></div> + + <div class="flexbox height50" style="align-items: flex-end" + ><div class="child1 paddingA"><div class="filler"/></div><div class="child2 paddingB"><div class="filler"/></div><div class="child1 marginA"></div><div class="child2 marginB"></div></div> + + <div class="flexbox height50" + ><div class="child1 paddingA marginA"><div class="filler"/></div><div class="child2 paddingB marginB"><div class="filler"/></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-004.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-004.xhtml new file mode 100644 index 0000000..e69c4e66 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-mbp-horiz-004.xhtml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with percent-valued padding and/or margin on flex items. The spec + says that percentage values on padding/margin-top and -bottom should be + resolved against the flex container's height (not its width, as would + be the case in a block). + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing percent-valued padding and margin on flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-mbp-horiz-004-ref.xhtml"/> + <style> + div { border: 0; } + div.flexbox { + width: 200px; + display: flex; + margin-bottom: 2px; + border: 1px dotted black; + } + div.height50 { height: 50px; } + + .marginA { margin: 10% 8% 6% 4%; } + .marginB { margin: 8% 10% 12% 14%; } + .paddingA { padding: 8% 6% 4% 2%; } + .paddingB { padding: 6% 8% 10% 12%; } + + div.child1 { + flex: none; + width: 10px; + height: 10px; + background: lightgreen; + } + div.child2 { + flex: none; + width: 10px; + height: 10px; + background: purple; + } + + div.filler { + /* Filler-div to fill up content-box and make padding easier to see. */ + height: 10px; + width: 100%; + background: lightgrey; + } + + </style> + </head> + <body> + <!-- Flex container is auto-height - vertical margin and padding should + resolve to 0, since they don't have anything to resolve % against. --> + <div class="flexbox" + ><div class="child1 paddingA"><div class="filler"/></div><div class="child2 paddingB"><div class="filler"/></div><div class="child1 marginA"></div><div class="child2 marginB"></div></div> + + <!-- Flex container has height: 50px - vertical margin and padding should + resolve % values against that. --> + <div class="flexbox height50" + ><div class="child1 paddingA"><div class="filler"/></div><div class="child2 paddingB"><div class="filler"/></div><div class="child1 marginA"></div><div class="child2 marginB"></div></div> + + <!-- ...and now with align-items: flex-end, so we can see the margin-bottom + in action --> + <div class="flexbox height50" style="align-items: flex-end" + ><div class="child1 paddingA"><div class="filler"/></div><div class="child2 paddingB"><div class="filler"/></div><div class="child1 marginA"></div><div class="child2 marginB"></div></div> + + <!-- ...and finally, with margin and padding applied to the same items --> + <div class="flexbox height50" + ><div class="child1 paddingA marginA"><div class="filler"/></div><div class="child2 paddingB marginB"><div class="filler"/></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-001-expected.html new file mode 100644 index 0000000..2ee08b7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-001-expected.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .item { + /* Flex items have purple border: */ + border: 2px dotted purple; + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .small { height: 50px; } + .big { height: 80px; } + + .item > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + width: 10px; + height: 80px; + } + </style> + </head> + <body> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + + <div class="item big"><div></div></div> + <div class="item big"><div></div></div> + <div class="item big"><div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-001.html new file mode 100644 index 0000000..6e79757 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-001.html
@@ -0,0 +1,102 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-height:auto</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-height-auto-001-ref.html"> +<!-- + This testcase tests the used value of "min-height:auto" (the property's + initial value) on flex items in a vertical flex container. + + It's supposed to resolve to the smallest of: + a) The used 'flex-basis' (taken from 'height'), if 'flex-basis' is at its + initial value. + b) The computed 'max-height' property + c) If there's no intrinsic aspect ratio: the item's min-content height. + d) If there is an intrinsic aspect ratio: the item's height derived from + the ratio & constraints in the other dimension. + + We measure what the used value is by putting flex items in a small flex + container, which will shrink its items down to their min-height. + + This test checks for situations where we should resolve the min-height as + (a), (b), or (c) above. Another test will check (d). + --> + <style> + .flexbox { + display: flex; + flex-direction: column; + height: 1px; /* No available space; shrink flex items to min-height */ + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + } + + .flexbox > * > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + width: 10px; + height: 80px; + } + </style> + </head> + <body> + <!-- Check for min-height:auto resolving correctly when the smallest + candidate value is: --> + <!-- *** (a) Used 'flex-basis' (from 'height') *** --> + <!-- First, without definite max-height: --> + <div class="flexbox"> + <div style="height: 50px"><div></div></div> + </div> + <!-- ...and now with definite (& large) 'max-height': --> + <div class="flexbox"> + <div style="height: 50px; max-height: 120px;"><div></div></div> + </div> + <!-- ...and now with used 'flex-basis' being a calc expression: --> + <div class="flexbox"> + <div style="height: calc(10% + 50px)"><div></div></div> + </div> + + <!-- *** (b) The computed 'max-height' *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'height') --> + <div class="flexbox"> + <div style="height: 100px; max-height:50px"><div></div></div> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <div style="flex-basis: 100px; max-height:50px"><div></div></div> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <div style="flex-basis: 10px; max-height:50px"><div></div></div> + </div> + + <!-- *** (c) (no intrinsic aspect ratio) The min-content size *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'height') --> + <div class="flexbox"> + <div style="height: 100px"><div></div></div> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <div style="flex-basis: 100px"><div></div></div> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <div style="flex-basis: 10px"><div></div></div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a-expected.html new file mode 100644 index 0000000..315fc14e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + img { + width: 30px; + height: 30px; + float: left; + border: 2px dotted purple; + margin-right: 2px; /* (Just for spacing things out, visually) */ + } + </style> + </head> + <body> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a.html new file mode 100644 index 0000000..a575f77 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a.html
@@ -0,0 +1,69 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-height:auto</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-height-auto-002-ref.html"> +<!-- + This testcase tests the used value of "min-height:auto" (the property's + initial value) on flex items in a vertical flex container. + + It's supposed to resolve to the smallest of: + a) The used 'flex-basis' (taken from 'height'), if 'flex-basis' is at its + initial value. + b) The computed 'max-height' property + c) If there's no intrinsic aspect ratio: the item's min-content height. + d) If there is an intrinsic aspect ratio: the item's height derived from + the ratio & constraints in the other dimension. + + We measure what the used value is by putting flex items in a small flex + container, which will shrink its items down to their min-height. + + This test checks for situations where we should resolve the min-height as + (d) above, with "constraints in the other dimension" being "width". + --> + <style> + .flexbox { + display: flex; + flex-direction: column; + height: 1px; /* No available space; shrink flex items to min-height */ + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + /* Flex items have sizing constraint in cross axis: */ + width: 30px; + } + </style> + </head> + <body> + <!-- Check for min-height:auto resolving correctly when the smallest + candidate value is: --> + + <!-- *** (d) (with intrinsic aspect ratio) The height derived from ratio + and constraints in the other dimension *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'height') --> + <div class="flexbox"> + <img style="height: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 10px" src="support/solidblue.png" alt="blue square"> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b-expected.html new file mode 100644 index 0000000..315fc14e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + img { + width: 30px; + height: 30px; + float: left; + border: 2px dotted purple; + margin-right: 2px; /* (Just for spacing things out, visually) */ + } + </style> + </head> + <body> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b.html new file mode 100644 index 0000000..e51a02c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b.html
@@ -0,0 +1,69 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-height:auto</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-height-auto-002-ref.html"> +<!-- + This testcase tests the used value of "min-height:auto" (the property's + initial value) on flex items in a vertical flex container. + + It's supposed to resolve to the smallest of: + a) The used 'flex-basis' (taken from 'height'), if 'flex-basis' is at its + initial value. + b) The computed 'max-height' property + c) If there's no intrinsic aspect ratio: the item's min-content height. + d) If there is an intrinsic aspect ratio: the item's height derived from + the ratio & constraints in the other dimension. + + We measure what the used value is by putting flex items in a small flex + container, which will shrink its items down to their min-height. + + This test checks for situations where we should resolve the min-height as + (d) above, with "constraints in the other dimension" being "min-width". + --> + <style> + .flexbox { + display: flex; + flex-direction: column; + height: 1px; /* No available space; shrink flex items to min-height */ + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + /* Flex items have sizing constraint in cross axis: */ + min-width: 30px; + } + </style> + </head> + <body> + <!-- Check for min-height:auto resolving correctly when the smallest + candidate value is: --> + + <!-- *** (d) (with intrinsic aspect ratio) The height derived from ratio + and constraints in the other dimension *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'height') --> + <div class="flexbox"> + <img style="height: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 10px" src="support/solidblue.png" alt="blue square"> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c-expected.html new file mode 100644 index 0000000..315fc14e --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + img { + width: 30px; + height: 30px; + float: left; + border: 2px dotted purple; + margin-right: 2px; /* (Just for spacing things out, visually) */ + } + </style> + </head> + <body> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c.html new file mode 100644 index 0000000..64a5320 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c.html
@@ -0,0 +1,71 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-height:auto</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-height-auto-002-ref.html"> +<!-- + This testcase tests the used value of "min-height:auto" (the property's + initial value) on flex items in a vertical flex container. + + It's supposed to resolve to the smallest of: + a) The used 'flex-basis' (taken from 'height'), if 'flex-basis' is at its + initial value. + b) The computed 'max-height' property + c) If there's no intrinsic aspect ratio: the item's min-content height. + d) If there is an intrinsic aspect ratio: the item's height derived from + the ratio & constraints in the other dimension. + + We measure what the used value is by putting flex items in a small flex + container, which will shrink its items down to their min-height. + + This test checks for situations where we should resolve the min-height as + (d) above, with "constraints in the other dimension" being + max-width-clamped "width". + --> + <style> + .flexbox { + display: flex; + flex-direction: column; + height: 1px; /* No available space; shrink flex items to min-height */ + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + /* Flex items have sizing constraint in cross axis: */ + max-width: 30px; + width: 60px; + } + </style> + </head> + <body> + <!-- Check for min-height:auto resolving correctly when the smallest + candidate value is: --> + + <!-- *** (d) (with intrinsic aspect ratio) The height derived from ratio + and constraints in the other dimension *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'height') --> + <div class="flexbox"> + <img style="height: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-height:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 10px" src="support/solidblue.png" alt="blue square"> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-003-expected.html new file mode 100644 index 0000000..c347b0b --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-003-expected.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .item { + /* Flex items have purple border: */ + border: 2px dotted purple; + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .small { height: 26px; } + .big { height: 80px; } + + .item > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + width: 40px; + height: 80px; + } + + .yvisible { overflow-y: visible; } + .yhidden { overflow-y: hidden; } + .yscroll { overflow-y: scroll; } + .yauto { overflow-y: auto; } + </style> + </head> + <body> + <div class="item big yvisible"><div></div></div> + <div class="item small yhidden"><div></div></div> + <div class="item small yscroll"><div></div></div> + <div class="item small yauto"><div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-003.html new file mode 100644 index 0000000..daad89e2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-003.html
@@ -0,0 +1,57 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-height:auto & 'overflow' interaction</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-height-auto-003-ref.html"> +<!-- + This testcase checks how "overflow-y" impacts the sizing behavior of flex + items with "min-height:auto" (the new initial value for "min-height"). + + In particular, the flex-item-specific "min-height:auto" behavior is + supposed to be disabled (e.g. we end up with min-height:0) when + "overflow-y" is non-"visible". + --> + <style> + .flexbox { + display: flex; + flex-direction: column; + height: 30px; /* Shrink flex items below min-height */ + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + } + + .flexbox > * > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + width: 40px; + height: 80px; + } + + .yvisible { overflow-y: visible; } + .yhidden { overflow-y: hidden; } + .yscroll { overflow-y: scroll; } + .yauto { overflow-y: auto; } + </style> + </head> + <body> + <!-- min-height:auto should prevent shrinking below intrinsic height when + the flex item has "overflow-y: visible", but not for any other + overflow-y values. --> + <div class="flexbox"><div class="yvisible"><div></div></div></div> + <div class="flexbox"><div class="yhidden"><div></div></div></div> + <div class="flexbox"><div class="yscroll"><div></div></div></div> + <div class="flexbox"><div class="yauto"><div></div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-004-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-004-expected.html new file mode 100644 index 0000000..0b5a6fd --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-004-expected.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .item { + /* Flex items have purple border: */ + border: 2px dotted purple; + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .small { height: 26px; } + .big { height: 80px; } + + .item > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + width: 40px; + height: 80px; + } + + .xvisible { overflow-x: visible; } + .xhidden { overflow-x: hidden; } + .xscroll { overflow-x: scroll; } + .xauto { overflow-x: auto; } + </style> + </head> + <body> + <div class="item big xvisible"><div></div></div> + <div class="item small xhidden"><div></div></div> + <div class="item small xscroll"><div></div></div> + <div class="item small xauto"><div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-004.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-004.html new file mode 100644 index 0000000..109bb81 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-004.html
@@ -0,0 +1,63 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-height:auto & 'overflow' interaction</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-height-auto-004-ref.html"> +<!-- + This testcase checks how "overflow-x" indirectly impacts the sizing + behavior of flex items with "min-height:auto" (the new initial value + for "min-height"), via its influence on "overflow-y". + + In particular, the flex-item-specific "min-height:auto" behavior is + supposed to be disabled (e.g. we end up with min-height:0) when + "overflow-y" is non-"visible". Moreover, when "overflow-x" is set to a + scrolling value, it forces "overflow-y" to compute to a scrolling value + as well, as described at + http://www.w3.org/TR/css-overflow-3/#overflow-properties + So, "overflow-x" has an indirect effect (via "overflow-y") here. + --> + <style> + .flexbox { + display: flex; + flex-direction: column; + height: 30px; /* Shrink flex items below min-height */ + margin-right: 2px; /* (Just for spacing things out, visually) */ + float: left; + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + } + + .flexbox > * > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + width: 40px; + height: 80px; + } + + .xvisible { overflow-x: visible; } + .xhidden { overflow-x: hidden; } + .xscroll { overflow-x: scroll; } + .xauto { overflow-x: auto; } + </style> + </head> + <body> + <!-- min-height:auto should prevent shrinking below intrinsic height when + the flex item has "overflow-x: visible", but not for any other + overflow-x values (because of overflow-x's influence on overflow-y). + --> + <div class="flexbox"><div class="xvisible"><div></div></div></div> + <div class="flexbox"><div class="xhidden"><div></div></div></div> + <div class="flexbox"><div class="xscroll"><div></div></div></div> + <div class="flexbox"><div class="xauto"><div></div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-001-expected.html new file mode 100644 index 0000000..cabe442 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-001-expected.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .item { + /* Flex items have purple border: */ + border: 2px dotted purple; + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .small { width: 50px; } + .big { width: 80px; } + + .item > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + height: 10px; + width: 80px; + } + </style> + </head> + <body> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + <div class="item small"><div></div></div> + + <div class="item big"><div></div></div> + <div class="item big"><div></div></div> + <div class="item big"><div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-001.html new file mode 100644 index 0000000..443c478 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-001.html
@@ -0,0 +1,100 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-width:auto</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-width-auto-001-ref.html"> +<!-- + This testcase tests the used value of "min-width:auto" (the property's + initial value) on flex items in a horizontal flex container. + + It's supposed to resolve to the smallest of: + a) The used 'flex-basis' (taken from 'width'), if 'flex-basis' is at its + initial value. + b) The computed 'max-width' property + c) If there's no intrinsic aspect ratio: the item's min-content width. + d) If there is an intrinsic aspect ratio: the item's width derived from + the ratio & constraints in the other dimension. + + We measure what the used value is by putting flex items in a small flex + container, which will shrink its items down to their min-width. + + This test checks for situations where we should resolve the min-width as + (a), (b), or (c) above. Another test will check (d). + --> + <style> + .flexbox { + display: flex; + width: 1px; /* No available space; shrink flex items to min-width */ + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + } + + .flexbox > * > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + height: 10px; + width: 80px; + } + </style> + </head> + <body> + <!-- Check for min-width:auto resolving correctly when the smallest + candidate value is: --> + <!-- *** (a) Used 'flex-basis' (from 'width') *** --> + <!-- First, without definite max-width: --> + <div class="flexbox"> + <div style="width: 50px"><div></div></div> + </div> + <!-- ...and now with definite (& large) 'max-width': --> + <div class="flexbox"> + <div style="width: 50px; max-width: 120px;"><div></div></div> + </div> + <!-- ...and now with used 'flex-basis' being a calc expression: --> + <div class="flexbox"> + <div style="width: calc(10% + 50px)"><div></div></div> + </div> + + <!-- *** (b) The computed 'max-width' *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'width') --> + <div class="flexbox"> + <div style="width: 100px; max-width:50px"><div></div></div> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <div style="flex-basis: 100px; max-width:50px"><div></div></div> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <div style="flex-basis: 10px; max-width:50px"><div></div></div> + </div> + + <!-- *** (c) (no intrinsic aspect ratio) The min-content size *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'width') --> + <div class="flexbox"> + <div style="width: 100px"><div></div></div> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <div style="flex-basis: 100px"><div></div></div> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <div style="flex-basis: 10px"><div></div></div> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a-expected.html new file mode 100644 index 0000000..4ab3271 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + img { + width: 30px; + height: 30px; + display: block; + border: 2px dotted purple; + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + </style> + </head> + <body> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a.html new file mode 100644 index 0000000..a31e367 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a.html
@@ -0,0 +1,67 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-width:auto</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-width-auto-002-ref.html"> +<!-- + This testcase tests the used value of "min-width:auto" (the property's + initial value) on flex items in a horizontal flex container. + + It's supposed to resolve to the smallest of: + a) The used 'flex-basis' (taken from 'width'), if 'flex-basis' is at its + initial value. + b) The computed 'max-width' property + c) If there's no intrinsic aspect ratio: the item's min-content width. + d) If there is an intrinsic aspect ratio: the item's width derived from + the ratio & constraints in the other dimension. + + We measure what the used value is by putting flex items in a small flex + container, which will shrink its items down to their min-width. + + This test checks for situations where we should resolve the min-width as + (d) above, with "constraints in the other dimension" being "height". + --> + <style> + .flexbox { + display: flex; + width: 0px; /* No available space; shrink flex items to min-width */ + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + /* Flex items have sizing constraint in cross axis: */ + height: 30px; + } + </style> + </head> + <body> + <!-- Check for min-width:auto resolving correctly when the smallest + candidate value is: --> + + <!-- *** (d) (with intrinsic aspect ratio) The width derived from ratio + and constraints in the other dimension *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'width') --> + <div class="flexbox"> + <img style="width: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 10px" src="support/solidblue.png" alt="blue square"> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002b-expected.html new file mode 100644 index 0000000..4ab3271 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002b-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + img { + width: 30px; + height: 30px; + display: block; + border: 2px dotted purple; + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + </style> + </head> + <body> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002b.html new file mode 100644 index 0000000..97182ff03 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002b.html
@@ -0,0 +1,67 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-width:auto</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-width-auto-002-ref.html"> +<!-- + This testcase tests the used value of "min-width:auto" (the property's + initial value) on flex items in a horizontal flex container. + + It's supposed to resolve to the smallest of: + a) The used 'flex-basis' (taken from 'width'), if 'flex-basis' is at its + initial value. + b) The computed 'max-width' property + c) If there's no intrinsic aspect ratio: the item's min-content width. + d) If there is an intrinsic aspect ratio: the item's width derived from + the ratio & constraints in the other dimension. + + We measure what the used value is by putting flex items in a small flex + container, which will shrink its items down to their min-width. + + This test checks for situations where we should resolve the min-width as + (d) above, with "constraints in the other dimension" being "min-height". + --> + <style> + .flexbox { + display: flex; + width: 0px; /* No available space; shrink flex items to min-width */ + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + /* Flex items have sizing constraint in cross axis: */ + min-height: 30px; + } + </style> + </head> + <body> + <!-- Check for min-width:auto resolving correctly when the smallest + candidate value is: --> + + <!-- *** (d) (with intrinsic aspect ratio) The width derived from ratio + and constraints in the other dimension *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'width') --> + <div class="flexbox"> + <img style="width: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 10px" src="support/solidblue.png" alt="blue square"> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c-expected.html new file mode 100644 index 0000000..4ab3271 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c-expected.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + img { + width: 30px; + height: 30px; + display: block; + border: 2px dotted purple; + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + </style> + </head> + <body> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + <img src="support/solidblue.png" alt="blue square"> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c.html new file mode 100644 index 0000000..1927b49 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c.html
@@ -0,0 +1,69 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-width:auto</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-width-auto-002-ref.html"> +<!-- + This testcase tests the used value of "min-width:auto" (the property's + initial value) on flex items in a horizontal flex container. + + It's supposed to resolve to the smallest of: + a) The used 'flex-basis' (taken from 'width'), if 'flex-basis' is at its + initial value. + b) The computed 'max-width' property + c) If there's no intrinsic aspect ratio: the item's min-content width. + d) If there is an intrinsic aspect ratio: the item's width derived from + the ratio & constraints in the other dimension. + + We measure what the used value is by putting flex items in a small flex + container, which will shrink its items down to their min-width. + + This test checks for situations where we should resolve the min-width as + (d) above, with "constraints in the other dimension" being + max-height-clamped "height". + --> + <style> + .flexbox { + display: flex; + width: 0px; /* No available space; shrink flex items to min-width */ + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + /* Flex items have sizing constraint in cross axis: */ + max-height: 30px; + height: 60px; + } + </style> + </head> + <body> + <!-- Check for min-width:auto resolving correctly when the smallest + candidate value is: --> + + <!-- *** (d) (with intrinsic aspect ratio) The width derived from ratio + and constraints in the other dimension *** --> + <!-- First, with a larger candidate 'flex-basis' value (from 'width') --> + <div class="flexbox"> + <img style="width: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a larger explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 100px" src="support/solidblue.png" alt="blue square"> + </div> + <!-- ...and now with a smaller explicit 'flex-basis' value (which shouldn't + be considered for 'min-width:auto' anyway) --> + <div class="flexbox"> + <img style="flex-basis: 10px" src="support/solidblue.png" alt="blue square"> + </div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-003-expected.html new file mode 100644 index 0000000..de4ad78 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-003-expected.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .item { + /* Flex items have purple border: */ + border: 2px dotted purple; + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .small { width: 26px; } + .big { width: 80px; } + + .item > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + height: 40px; + width: 80px; + } + + .xvisible { overflow-x: visible; } + .xhidden { overflow-x: hidden; } + .xscroll { overflow-x: scroll; } + .xauto { overflow-x: auto; } + </style> + </head> + <body> + <div class="item big xvisible"><div></div></div> + <div class="item small xhidden"><div></div></div> + <div class="item small xscroll"><div></div></div> + <div class="item small xauto"><div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-003.html new file mode 100644 index 0000000..3f6ab640 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-003.html
@@ -0,0 +1,55 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-width:auto & 'overflow' interaction</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-width-auto-003-ref.html"> +<!-- + This testcase checks how "overflow-x" impacts the sizing behavior of flex + items with "min-width:auto" (the new initial value for "min-width"). + + In particular, the flex-item-specific "min-width:auto" behavior is + supposed to be disabled (e.g. we end up with min-width:0) when + "overflow-x" is non-"visible". + --> + <style> + .flexbox { + display: flex; + width: 30px; /* Shrink flex items below min-width */ + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + } + + .flexbox > * > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + height: 40px; + width: 80px; + } + + .xvisible { overflow-x: visible; } + .xhidden { overflow-x: hidden; } + .xscroll { overflow-x: scroll; } + .xauto { overflow-x: auto; } + </style> + </head> + <body> + <!-- min-width:auto should prevent shrinking below intrinsic width when + the flex item has "overflow-x: visible", but not for any other + overflow-x values. --> + <div class="flexbox"><div class="xvisible"><div></div></div></div> + <div class="flexbox"><div class="xhidden"><div></div></div></div> + <div class="flexbox"><div class="xscroll"><div></div></div></div> + <div class="flexbox"><div class="xauto"><div></div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-004-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-004-expected.html new file mode 100644 index 0000000..feaf42c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-004-expected.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .item { + /* Flex items have purple border: */ + border: 2px dotted purple; + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .small { width: 26px; } + .big { width: 80px; } + + .item > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + height: 40px; + width: 80px; + } + + .yvisible { overflow-y: visible; } + .yhidden { overflow-y: hidden; } + .yscroll { overflow-y: scroll; } + .yauto { overflow-y: auto; } + </style> + </head> + <body> + <div class="item big yvisible"><div></div></div> + <div class="item small yhidden"><div></div></div> + <div class="item small yscroll"><div></div></div> + <div class="item small yauto"><div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-004.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-004.html new file mode 100644 index 0000000..97c20d2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-004.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> + <head> + <meta charset="utf-8"> + <title>CSS Test: Testing min-width:auto & 'overflow' interaction</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto"> + <link rel="match" href="flexbox-min-width-auto-004-ref.html"> +<!-- + This testcase checks how "overflow-y" indirectly impacts the sizing + behavior of flex items with "min-width:auto" (the new initial value + for "min-width"), via its influence on "overflow-x". + + In particular, the flex-item-specific "min-width:auto" behavior is + supposed to be disabled (e.g. we end up with min-width:0) when + "overflow-x" is non-"visible". Moreover, when "overflow-y" is set to a + scrolling value, it forces "overflow-x" to compute to a scrolling value + as well, as described at + http://www.w3.org/TR/css-overflow-3/#overflow-properties + So, "overflow-y" has an indirect effect (via "overflow-x") here. + --> + <style> + .flexbox { + display: flex; + width: 30px; /* Shrink flex items below min-width */ + margin-bottom: 2px; /* (Just for spacing things out, visually) */ + } + + .flexbox > * { + /* Flex items have purple border: */ + border: 2px dotted purple; + } + + .flexbox > * > * { + /* Flex items' contents are gray & fixed-size: */ + background: gray; + height: 40px; + width: 80px; + } + + .yvisible { overflow-y: visible; } + .yhidden { overflow-y: hidden; } + .yscroll { overflow-y: scroll; } + .yauto { overflow-y: auto; } + </style> + </head> + <body> + <!-- min-width:auto should prevent shrinking below intrinsic width when + the flex item has "overflow-y: visible", but not for any other + overflow-y values (because of overflow-y's influence on overflow-x). + --> + <div class="flexbox"><div class="yvisible"><div></div></div></div> + <div class="flexbox"><div class="yhidden"><div></div></div></div> + <div class="flexbox"><div class="yscroll"><div></div></div></div> + <div class="flexbox"><div class="yauto"><div></div></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-001-expected.html new file mode 100644 index 0000000..90f427d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-001-expected.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + display: inline-block; + height: 200px; + width: 75%; + float: left; + } + .smallItem { + background: teal; + width: 25%; + /* In the testcase, we'll stretch to container's height, + minus our 10px margin-bottom. */ + height: calc(100% - 10px); + float: left; + } + .scroll { overflow: scroll } + .auto { overflow: auto } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer scroll"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer auto"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-001.html new file mode 100644 index 0000000..daf44d29 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-001.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we correctly handle the "overflow" property on + a horizontal flex container. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a horizontal flex container, with contents not overflowing</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-containers"> + <link rel="match" href="flexbox-overflow-horiz-001-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + height: 200px; + flex: 3; + } + .smallItem { + background: teal; + margin-bottom: 10px; + flex: 1; + } + .scroll { overflow: scroll } + .auto { overflow: auto } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer scroll"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer auto"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-002-expected.html new file mode 100644 index 0000000..1c1aa168 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-002-expected.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + display: flex; + align-items: center; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + height: 10px; + /* Tall border (taller than our container): */ + border: solid coral; + border-width: 50px 2px; + flex: 3; + } + .smallItem { + background: teal; + height: 20px; + flex: 1; + } + .hidden > .bigItem { + /* To match the testcase's "overflow:hidden"-cropped flex item, we + just use a smaller border that exactly fits our container (and + doesn't overflow). */ + border-width: 30px 2px; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-002.html new file mode 100644 index 0000000..6d611b3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-002.html
@@ -0,0 +1,52 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we honor "align-items" on a horizontal + flex container that has "overflow: hidden". We use a huge border on + one of the flex items, large enough that it overflows the container, + to be sure that "overflow: hidden" is actually applying. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a horizontal flex container, with 'align-items: center'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-containers"> + <link rel="match" href="flexbox-overflow-horiz-002-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + align-items: center; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + height: 10px; + /* Tall border (taller than our container): */ + border: solid coral; + border-width: 50px 2px; + flex: 3; + } + .smallItem { + background: teal; + height: 20px; + flex: 1; + } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-003-expected.html new file mode 100644 index 0000000..6712138 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-003-expected.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + display: flex; + justify-content: space-around; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + width: 20px; + height: 200px; + } + .smallItem { + background: teal; + width: 20px; + height: 20px; + } + .hidden > .bigItem { + /* To match the testcase's "overflow:hidden"-cropped flex item, we + just use a smaller height that exactly fits our container (and + doesn't overflow). */ + height: 70px; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-003.html new file mode 100644 index 0000000..6996dc0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-003.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we honor "justify-content" on a horizontal + flex container that has "overflow:hidden". We use a large flex item, + large enough that it overflows the container, to be sure that + "overflow: hidden" is actually applying. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a horizontal flex container, with 'justify-content: space-around'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-containers"> + <link rel="match" href="flexbox-overflow-horiz-003-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + justify-content: space-around; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + width: 20px; + height: 200px; + } + .smallItem { + background: teal; + width: 20px; + height: 20px; + } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-004-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-004-expected.html new file mode 100644 index 0000000..4da6efd --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-004-expected.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-wrap: wrap; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + width: 50px; + height: 200px; + } + .smallItem { + background: teal; + width: 50px; + height: 20px; + } + .hidden > .bigItem { + /* To match the testcase's "overflow:hidden"-cropped flex item, we + just use a smaller height that exactly fits the space remaining + in our container, after wrapping */ + height: 50px; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="smallItem"></div> + <div class="bigItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="smallItem"></div> + <div class="bigItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-004.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-004.html new file mode 100644 index 0000000..985898f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-004.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we honor "flex-wrap" on a horizontal + flex container that has "overflow:hidden". We use a large flex item, + large enough that it overflows the container in the cross axis, to be + sure that "overflow: hidden" is actually applying. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a horizontal flex container, with 'flex-wrap: wrap'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-wrap-property"> + <link rel="match" href="flexbox-overflow-horiz-004-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-wrap: wrap; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + width: 50px; + height: 200px; + } + .smallItem { + background: teal; + width: 50px; + height: 20px; + } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="smallItem"></div> + <div class="bigItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="smallItem"></div> + <div class="bigItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-005-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-005-expected.html new file mode 100644 index 0000000..cd7b9f22 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-005-expected.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-wrap: wrap; + align-content: space-around; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + flex: none; /* prevent shrinking (so we can intentionally overflow) */ + width: 72px; + height: 20px; + } + .smallItem { + background: teal; + width: 20px; + height: 20px; + } + .hidden > .bigItem { + /* To match the testcase's "overflow:hidden"-cropped flex item, we + just use a smaller width that exactly fits our container (and + doesn't overflow). */ + width: 70px; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-005.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-005.html new file mode 100644 index 0000000..2c7aee1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-horiz-005.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we honor "align-content" on a horizontal + flex container that has "overflow:hidden". We use a large flex item, + large enough that it overflows the container in the main axis, to be + sure that "overflow: hidden" is actually applying. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a horizontal flex container, with 'align-content: space-around'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property"> + <link rel="match" href="flexbox-overflow-horiz-005-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-wrap: wrap; + align-content: space-around; + width: 70px; + height: 70px; + float: left; + margin-right: 5px; + } + .bigItem { + background: blue; + flex: none; /* prevent shrinking (so we can intentionally overflow) */ + width: 72px; + height: 20px; + } + .smallItem { + background: teal; + width: 20px; + height: 20px; + } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-001-expected.html new file mode 100644 index 0000000..63a00e32 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-001-expected.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + width: 200px; + background: blue; + height: 75%; + } + .smallItem { + background: teal; + margin-right: 10px; + height: 25%; + } + .scroll { overflow: scroll } + .auto { overflow: auto } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer scroll"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer auto"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-001.html new file mode 100644 index 0000000..e9e4990 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-001.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we correctly handle the "overflow" property on + a vertical flex container with overflowing contents. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a vertical flex container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-containers"> + <link rel="match" href="flexbox-overflow-vert-001-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + width: 200px; + flex: 3; + } + .smallItem { + background: teal; + margin-right: 10px; + flex: 1; + } + .scroll { overflow: scroll } + .auto { overflow: auto } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer scroll"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer auto"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-002-expected.html new file mode 100644 index 0000000..bee06d6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-002-expected.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + align-items: center; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + width: 10px; + /* Tall border (taller than our container): */ + border: solid coral; + border-width: 2px 50px; + flex: 3; + } + .smallItem { + background: teal; + width: 20px; + flex: 1; + } + .hidden > .bigItem { + /* To match the testcase's "overflow:hidden"-cropped flex item, we + just use a smaller border that exactly fits our container (and + doesn't overflow). */ + border-width: 2px 30px; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-002.html new file mode 100644 index 0000000..98f4579 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-002.html
@@ -0,0 +1,52 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we honor "align-items" on a vertical + flex container that has "overflow: hidden". We use a huge border on + one of the flex items, large enough that it overflows the container, + to be sure that "overflow: hidden" is actually applying. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a vertical flex container, with 'align-items: center'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-containers"> + <link rel="match" href="flexbox-overflow-vert-002-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + align-items: center; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + width: 10px; + /* Tall border (taller than our container): */ + border: solid coral; + border-width: 2px 50px; + flex: 3; + } + .smallItem { + background: teal; + width: 20px; + flex: 1; + } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-003-expected.html new file mode 100644 index 0000000..48b6e23 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-003-expected.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + justify-content: space-around; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + width: 200px; + height: 20px; + } + .smallItem { + background: teal; + width: 20px; + height: 20px; + } + .hidden > .bigItem { + /* To match the testcase's "overflow:hidden"-cropped flex item, we + just use a smaller width that exactly fits our container (and + doesn't overflow). */ + width: 70px; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-003.html new file mode 100644 index 0000000..6f6ff7f7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-003.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we honor "justify-content" on a vertical + flex container that has "overflow:hidden". We use a large flex item, + large enough that it overflows the container, to be sure that + "overflow: hidden" is actually applying. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a vertical flex container, with 'justify-content: space-around'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-containers"> + <link rel="match" href="flexbox-overflow-vert-003-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + justify-content: space-around; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + width: 200px; + height: 20px; + } + .smallItem { + background: teal; + width: 20px; + height: 20px; + } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-004-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-004-expected.html new file mode 100644 index 0000000..6098fcd --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-004-expected.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + flex-wrap: wrap; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + width: 200px; + height: 50px; + } + .smallItem { + background: teal; + width: 20px; + height: 50px; + } + .hidden > .bigItem { + /* To match the testcase's "overflow:hidden"-cropped flex item, we + just use a smaller width that exactly fits the space remaining + in our container, after wrapping */ + width: 50px; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="smallItem"></div> + <div class="bigItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="smallItem"></div> + <div class="bigItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-004.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-004.html new file mode 100644 index 0000000..149aa97 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-004.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we honor "flex-wrap" on a vertical + flex container that has "overflow:hidden". We use a large flex item, + large enough that it overflows the container in the cross axis, to be + sure that "overflow: hidden" is actually applying. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a vertical flex container, with 'flex-wrap: wrap'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-wrap-property"> + <link rel="match" href="flexbox-overflow-vert-004-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + flex-wrap: wrap; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + width: 200px; + height: 50px; + } + .smallItem { + background: teal; + width: 20px; + height: 50px; + } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="smallItem"></div> + <div class="bigItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="smallItem"></div> + <div class="bigItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-005-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-005-expected.html new file mode 100644 index 0000000..e6a4b51 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-005-expected.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + flex-wrap: wrap; + align-content: space-around; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + flex: none; /* prevent shrinking (so we can intentionally overflow) */ + width: 20px; + height: 72px; + } + .smallItem { + background: teal; + width: 20px; + height: 20px; + } + .hidden > .bigItem { + /* To match the testcase's "overflow:hidden"-cropped flex item, we + just use a smaller height that exactly fits our container (and + doesn't overflow). */ + height: 70px; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-005.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-005.html new file mode 100644 index 0000000..b56bdc0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-overflow-vert-005.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we honor "align-content" on a vertical + flex container that has "overflow:hidden". We use a large flex item, + large enough that it overflows the container in the main axis, to be + sure that "overflow: hidden" is actually applying. --> +<html> +<head> + <title>CSS Test: Testing 'overflow' property on a vertical flex container, with 'align-content: space-around'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#align-content-property"> + <link rel="match" href="flexbox-overflow-vert-005-ref.html"> + <style> + .flexContainer { + background: purple; + display: flex; + flex-direction: column; + flex-wrap: wrap; + align-content: space-around; + width: 70px; + height: 70px; + margin-bottom: 5px; + } + .bigItem { + background: blue; + flex: none; /* prevent shrinking (so we can intentionally overflow) */ + width: 20px; + height: 72px; + } + .smallItem { + background: teal; + width: 20px; + height: 20px; + } + .hidden { overflow: hidden } + </style> +</head> +<body> + <div class="flexContainer"><!-- (overflow un-set) --> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> + <div class="flexContainer hidden"> + <div class="bigItem"></div> + <div class="smallItem"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-001-expected.xhtml new file mode 100644 index 0000000..b6565eb --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-001-expected.xhtml
@@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + .container { + width: 40px; + height: 14px; + border: 2px solid green; + margin-bottom: 2px; + } + .a { + width: 16px; + height: 10px; + background: blue; + min-width: 0; + border: 2px solid lightblue; + } + .b { + width: 16px; + height: 10px; + background: purple; + min-width: 0; + border: 2px solid slateblue; + } + .aKid { + margin-left: 10px; + margin-top: 2px; + width: 16px; + height: 6px; + background: yellow; + border: 1px solid black; + } + .a, .b { float: left; } + </style> + </head> + <body> + <!-- Just 6 copies of the same container, since they all should look the + same (except for the final "position: fixed" one, which needs to be + explicitly marked as "position: fixed" or else it paints differently + on Android.) --> + <div class="container"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + <div class="container"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + <div class="container"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + <div class="container"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + <div class="container"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + <div class="container" style="position: fixed"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-001.xhtml new file mode 100644 index 0000000..3cc71d3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-001.xhtml
@@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with flex items containing overlapping content, to test + their paint-order. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the paint-order of overlapping flex items, with varying tweaks on the container</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#painting"/> + <link rel="match" href="flexbox-paint-ordering-001-ref.xhtml"/> + <style> + body { + line-height: 0; + } + .container { + width: 40px; + height: 14px; + border: 2px solid green; + margin-bottom: 2px; + } + .a { + width: 16px; + height: 10px; + background: blue; + min-width: 0; + border: 2px solid lightblue; + } + .b { + width: 16px; + height: 10px; + background: purple; + min-width: 0; + border: 2px solid slateblue; + } + .aKid { + margin-left: 10px; + margin-top: 2px; + width: 16px; + height: 6px; + background: yellow; + border: 1px solid black; + } + </style> + </head> + <body> + <!-- inline-level flex container --> + <div class="container" style="display: inline-flex"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + <!-- block-level flex container --> + <div class="container" style="display: flex"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + <!-- floated flex container --> + <div class="container" style="display: flex; float: left"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + <!-- Helper-div to clear floats: --> + <div style="clear: both"/> + + <!-- relatively-positioned flex container --> + <div class="container" style="display: flex; position: relative"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + <!-- absolutely-positioned flex container --> + <div class="container" style="display: flex; position: absolute"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + <!-- - Spacer div, since abspos div doesn't set aside space for itself --> + <div style="height: 20px"/> + + <!-- fixed-position flex container --> + <div class="container" style="display: flex; position: fixed"> + <div class="a"><div class="aKid"/></div> + <div class="b"></div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002-expected.xhtml new file mode 100644 index 0000000..8ac0522 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002-expected.xhtml
@@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for flex items containing overlapping content. + This reference uses inline-block in place of inline-flex, with floated + children in place of flex items, and with hardcoded DOM-reordering in + place of "order" reordering. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + body { + line-height: 0; + } + + .flexbox { + display: inline-block; + width: 20px; + height: 10px; + border: 2px solid gray; + margin-bottom: 10px; + margin-right: 10px; + } + .a { + width: 10px; + height: 10px; + background: lightblue; + float: left; /* to stack horizontally, like a flex item */ + } + .b { + width: 10px; + height: 10px; + background: pink; + float: left; /* to stack horizontally, like a flex item */ + } + .aKid { + margin-left: 3px; + margin-top: 3px; + width: 10px; + height: 10px; + background: steelblue; + border: 1px solid blue; + } + .bKid { + margin-left: 3px; + margin-top: 6px; + width: 10px; + height: 10px; + background: violet; + border: 1px solid purple; + } + + /* Need to set 'position' for z-index to take effect. */ + .zn2 { z-index: -2; position: relative; } + .zn1 { z-index: -1; position: relative; } + .z0 { z-index: 0; position: relative; } + .z1 { z-index: 1; position: relative; } + + </style> + </head> + <body> + <!-- order not set: --> + <div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div> + + <br/> + + <!-- order set, but it matches content order, so it shouldn't matter: --> + <div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div> + + <br/> + + <!-- order set to reverse of content-order: --> + <div class="flexbox"> + <div class="b"><div class="bKid"/></div> + <div class="a"><div class="aKid"/></div> + </div><div class="flexbox"> + <div class="b"><div class="bKid"/></div> + <div class="a"><div class="aKid"/></div> + </div><div class="flexbox"> + <div class="b"><div class="bKid"/></div> + <div class="a"><div class="aKid"/></div> + </div><div class="flexbox"> + <div class="b"><div class="bKid"/></div> + <div class="a"><div class="aKid"/></div> + </div> + + <br/> + + <!-- order set to reverse of content-order, AND with z-index set on + one or both items, but not such that it changes the paint order --> + <div class="flexbox"> + <div class="b"><div class="bKid"/></div> + <div class="a"><div class="aKid"/></div> + </div><div class="flexbox"> + <div class="b"><div class="bKid"/></div> + <div class="a"><div class="aKid"/></div> + </div><div class="flexbox"> + <div class="b"><div class="bKid"/></div> + <div class="a"><div class="aKid"/></div> + </div><div class="flexbox"> + <div class="b"><div class="bKid"/></div> + <div class="a"><div class="aKid"/></div> + </div> + + <br/> + + <!-- order set to reverse of content-order, AND with z-index set on + one or both items, in such a way that it affects paint order --> + <div class="flexbox"> + <!-- 'a' is behind the container's border --> + <div class="b"><div class="bKid"/></div> + <div class="a zn1"><div class="aKid"/></div> + </div><div class="flexbox"> + <!-- 'a' and 'b' are both behind the container's border --> + <div class="b zn1"><div class="bKid"/></div> + <div class="a zn1"><div class="aKid"/></div> + </div><div class="flexbox"> + <!-- 'a' and 'b' are both behind the container's border, + and 'a' is behind 'b' despite coming after it in the 'order' + ordering --> + <div class="b zn1"><div class="bKid"/></div> + <div class="a zn2"><div class="aKid"/></div> + </div><div class="flexbox"> + <!-- 'a' and 'b' are both in front of the container's border, + and 'a' is behind 'b' despite coming after it in the 'order' + ordering --> + <div class="b z1"><div class="bKid"/></div> + <div class="a z0"><div class="aKid"/></div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002.xhtml new file mode 100644 index 0000000..b6e2130 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002.xhtml
@@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase with flex items containing overlapping content, with + "order" and "z-index" set on some of them, to test how that affects + paint-order. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing the paint-order of overlapping flex items with 'order' and 'z-index' set</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#painting"/> + <link rel="match" href="flexbox-paint-ordering-002-ref.xhtml"/> + <style> + body { + line-height: 0; + } + + .flexbox { + display: inline-flex; + width: 20px; + height: 10px; + border: 2px solid gray; + margin-bottom: 10px; + margin-right: 10px; + } + .a { + width: 10px; + height: 10px; + background: lightblue; + min-width: 0; + } + .b { + width: 10px; + height: 10px; + background: pink; + min-width: 0; + } + + .aKid { + margin-left: 3px; + margin-top: 3px; + width: 10px; + height: 10px; + background: steelblue; + border: 1px solid blue; + } + .bKid { + margin-left: 3px; + margin-top: 6px; + width: 10px; + height: 10px; + background: violet; + border: 1px solid purple; + } + + .on1 { order: -1; } + .o0 { order: 0; } + .o1 { order: 1; } + .o2 { order: 2; } + + .zn2 { z-index: -2; } + .zn1 { z-index: -1; } + .z0 { z-index: 0; } + .z1 { z-index: 1; } + + </style> + </head> + <body> + <!-- order not set: --> + <div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div> + + <br/> + + <!-- order set, but it matches content order, so it shouldn't matter: --> + <div class="flexbox"> + <div class="a on1"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a o0"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a o0"><div class="aKid"/></div> + <div class="b o0"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a o2"><div class="aKid"/></div> + <div class="b o2"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b o0"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b o1"><div class="bKid"/></div> + </div> + + <br/> + + <!-- order set to reverse of content-order: --> + <div class="flexbox"> + <div class="a o1"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a"><div class="aKid"/></div> + <div class="b on1"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a o0"><div class="aKid"/></div> + <div class="b on1"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a o2"><div class="aKid"/></div> + <div class="b o1"><div class="bKid"/></div> + </div> + + <br/> + + <!-- order set to reverse of content-order, AND with z-index set on + one or both items, but not such that it changes the paint order --> + <div class="flexbox"> + <div class="a o1 z0"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a o1 z1"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a o1 z0"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + </div><div class="flexbox"> + <div class="a o1 z1"><div class="aKid"/></div> + <div class="b z0"><div class="bKid"/></div> + </div> + + <br/> + + <!-- order set to reverse of content-order, AND with z-index set on + one or both items, in such a way that it affects paint order --> + <div class="flexbox"> + <!-- 'a' is behind the container's border --> + <div class="a o1 zn1"><div class="aKid"/></div> + <div class="b"><div class="bKid"/></div> + </div><div class="flexbox"> + <!-- 'a' and 'b' are both behind the container's border --> + <div class="a o1 zn1"><div class="aKid"/></div> + <div class="b zn1"><div class="bKid"/></div> + </div><div class="flexbox"> + <!-- 'a' and 'b' are both behind the container's border, + and 'a' is behind 'b' despite coming after it in the 'order' + ordering --> + <div class="a o1 zn2"><div class="aKid"/></div> + <div class="b zn1"><div class="bKid"/></div> + </div><div class="flexbox"> + <!-- 'a' and 'b' are both in front of the container's border, + and 'a' is behind 'b' despite coming after it in the 'order' + ordering --> + <div class="a o1 z0"><div class="aKid"/></div> + <div class="b z1"><div class="bKid"/></div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001a-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001a-expected.html new file mode 100644 index 0000000..7098d775 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001a-expected.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html style="display: flex; justify-content: center"> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + div { + display: flex; + justify-content: center; + } + </style> +</head> +<body> + <div>centered</div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001a.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001a.html new file mode 100644 index 0000000..009a4ac --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001a.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we correctly handle 'display:flex' property on + the root <html> element, with the <body> as the sole flex item. --> +<html style="display: flex; justify-content: center"> +<head> + <title>CSS Test: Testing 'display:flex' on root node</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-containers"> + <link rel="match" href="flexbox-root-node-001-ref.html"> + <style> + html { + display: flex; + justify-content: center; + } + </style> +</head> +<body> + centered +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001b-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001b-expected.html new file mode 100644 index 0000000..7098d775 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001b-expected.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html style="display: flex; justify-content: center"> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <style> + div { + display: flex; + justify-content: center; + } + </style> +</head> +<body> + <div>centered</div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001b.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001b.html new file mode 100644 index 0000000..6679699 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-root-node-001b.html
@@ -0,0 +1,22 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- This testcase checks that we correctly handle 'display:flex' property on + the root <html> element, with no explicit <body>. --> +<html style="display: flex; justify-content: center"> +<head> + <title>CSS Test: Testing 'display:flex' on root node</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-containers"> + <link rel="match" href="flexbox-root-node-001-ref.html"> + <style> + html { + display: flex; + justify-content: center; + } + </style> +</head> +centered +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-001-expected.xhtml new file mode 100644 index 0000000..dfe5b35c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-001-expected.xhtml
@@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 10px; } + div.flexbox { + border: 1px dashed blue; + font-size: 10px; + margin-bottom: 2px; + } + div.a, div.b, div.c { float: left } + div.a { + width: 20px; + background: lightgreen; + } + div.b { + width: 40px; + background: purple; + } + div.c { + width: 40px; + background: orange; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="width: 60px"/> + <div class="b" style="width: 60px"/> + <div class="c" style="width: 60px"/> + </div> + + <div class="flexbox"> + <div class="a" style="width: 60px"/> + <div class="b" style="width: 60px"/> + <div class="c" style="width: 60px"/> + </div> + + <div class="flexbox" style="width: 300px"> + <div class="a" style="width: 60px"/> + <div class="b" style="width: 60px"/> + <div class="c" style="width: 60px"/> + </div> + + <div class="flexbox" style="width: 70px"> + <div class="a" style="width: 10px"/> + <div class="b"/> + <div class="c" style="width: 20px"/> + </div> + + <div class="flexbox" style="width: 20px"> + <!-- We add an extra layer of <div> wrapping in this chunk, with a + fixed width, to keep the overflowing children from wrapping. --> + <div style="width: 50px"> + <div class="b"/> + <div class="c" style="width: 10px"/> + </div> + </div> + + <div class="flexbox" style="width: 58px"> + <div class="a" style="width: 6px"/> + <div class="b"/> + <div class="c" style="width: 12px"/> + </div> + + <div class="flexbox" style="width: 140px"> + <div class="a" style="width: 40px"/> + <div class="b" style="width: 40px"/> + <div class="c" style="width: 60px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-001.xhtml new file mode 100644 index 0000000..85c54b4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-001.xhtml
@@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This testcase checks how "min-width" and "max-width" affect the sizing + of horizontal flex containers that have no explicit "width" property. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing sizing of an auto-sized horizontal flex container with min-width and max-width constraints</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-sizing-horiz-001-ref.xhtml"/> + <style> + div { height: 10px; } + div.flexbox { + border: 1px dashed blue; + font-size: 10px; + display: flex; + margin-bottom: 2px; + } + div.a { + flex: 1 20px; + max-width: 60px; + background: lightgreen; + } + div.b { + flex: 1 20px; + min-width: 40px; + max-width: 60px; + background: purple; + } + div.c { + flex: 1 40px; + min-width: 10px; + max-width: 60px; + background: orange; + } + </style> + </head> + <body> + <!-- auto-sized horizontal flexbox should occupy the available width. --> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- Adding a small min-size shouldn't affect that. --> + <div class="flexbox" style="min-width: 10px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- ...but a (large) max-size will limit us to that size, instead of + our available size. --> + <div class="flexbox" style="max-width: 300px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- If we set a maximum size that's even smaller, it'll limit our + size and compress our children. --> + <div class="flexbox" style="max-width: 70px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- The max-size may be small enough that our items will overflow. --> + <div class="flexbox" style="max-width: 20px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- But if we add a min-size, it beats the max-size. Here, we use a + min-size smaller than the sum of the items' base sizes... --> + <div class="flexbox" style="min-width: 58px; max-width: 20px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- ...and here we use a min-size larger than the sum of the items' + base sizes. --> + <div class="flexbox" style="min-width: 140px; max-width: 20px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-002-expected.xhtml new file mode 100644 index 0000000..1f48736 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-002-expected.xhtml
@@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { width: 200px; } + div.flexbox { + border: 1px dashed blue; + background: lightgreen; + font-size: 10px; + margin-bottom: 5px; + } + </style> + </head> + <body> + <div class="flexbox"> + <div>text</div> + </div> + + <div class="flexbox"> + <div>text</div> + </div> + + <div class="flexbox"> + <div>text</div> + </div> + + <div class="flexbox" style="height: 30px"> + <div>text</div> + </div> + + <div class="flexbox" style="height: 6px"> + <div>text</div> + </div> + + <div class="flexbox" style="height: 30px"> + <div>text</div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-002.xhtml new file mode 100644 index 0000000..fc77063 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-horiz-002.xhtml
@@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This testcase checks how "min-height" and "max-height" affect the sizing + of horizontal flex containers that have no explicit "height" property. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing sizing of an auto-sized horizontal flex container with min-height and max-height constraints</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-sizing-horiz-002-ref.xhtml"/> + <style> + div { width: 200px; } + div.flexbox { + border: 1px dashed blue; + background: lightgreen; + font-size: 10px; + display: flex; + margin-bottom: 5px; + } + </style> + </head> + <body> + <!-- auto-height horizontal flexbox should shrinkwrap its contents. --> + <div class="flexbox"> + <div>text</div> + </div> + + <!-- Adding a small min-size shouldn't affect that. --> + <div class="flexbox" style="min-height: 2px"> + <div>text</div> + </div> + + <!-- ...nor should a large max-size. --> + <div class="flexbox" style="max-height: 300px"> + <div>text</div> + </div> + + <!-- OK. Now, if we set a minimum size that's larger than the shrinkwrap + size, we should jump up to that size. --> + <div class="flexbox" style="min-height: 30px"> + <div>text</div> + </div> + + <!-- If we set a maximum size that's smaller than the shrinkwrap size, + we should max out at that size. --> + <div class="flexbox" style="max-height: 6px"> + <div>text</div> + </div> + + <!-- But if we add a min-size, it beats the max-size. --> + <div class="flexbox" style="min-height: 30px; max-height: 5px"> + <div>text</div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-001-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-001-expected.xhtml new file mode 100644 index 0000000..a034254 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-001-expected.xhtml
@@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { width: 10px; } + div.flexbox { + float: left; + border: 1px dashed blue; + font-size: 10px; + margin-right: 2px; + } + div.a { + height: 20px; + background: lightgreen; + } + div.b { + height: 40px; + background: purple; + } + div.c { + height: 40px; + background: orange; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <div class="flexbox"> + <div class="a" style="height: 30px"/> + <div class="b"/> + <div class="c" style="height: 50px"/> + </div> + + <div class="flexbox" style="height: 200px"> + <div class="a" style="height: 60px"/> + <div class="b" style="height: 60px"/> + <div class="c" style="height: 60px"/> + </div> + + <div class="flexbox"> + <div class="a" style="height: 10px"/> + <div class="b"/> + <div class="c" style="height: 20px"/> + </div> + + <div class="flexbox" style="height: 20px"> + <div class="b"/> + <div class="c" style="height: 10px"/> + </div> + + <div class="flexbox"> + <div class="a" style="height: 6px"/> + <div class="b"/> + <div class="c" style="height: 12px"/> + </div> + + <div class="flexbox"> + <div class="a" style="height: 40px"/> + <div class="b" style="height: 40px"/> + <div class="c" style="height: 60px"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-001.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-001.xhtml new file mode 100644 index 0000000..46fb1da3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-001.xhtml
@@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This testcase checks how "min-height" and "max-height" affect the sizing + of vertical flex containers that have no explicit "height" property. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing sizing of an auto-sized vertical flex container with min-height and max-height constraints</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-sizing-vert-001-ref.xhtml"/> + <style> + div { width: 10px; } + div.flexbox { + float: left; + border: 1px dashed blue; + font-size: 10px; + display: flex; + flex-direction: column; + margin-right: 2px; + } + div.a { + flex: 1 20px; + max-height: 60px; + background: lightgreen; + } + div.b { + flex: 1 20px; + min-height: 40px; + max-height: 60px; + background: purple; + } + div.c { + flex: 1 40px; + min-height: 10px; + max-height: 60px; + background: orange; + } + </style> + </head> + <body> + <!-- floated auto-sized vertical flexbox should shrinkwrap its flex items' + hypothetical main sizes (their flex base sizes, clamped to min/max) --> + <div class="flexbox"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- Adding a small min-size shouldn't affect that. --> + <div class="flexbox" style="min-height: 10px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- ...nor should a large max-size. --> + <div class="flexbox" style="max-height: 300px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- OK. Now, if we set a minimum size that's larger than the shrinkwrap + size, we should jump up to that size. --> + <div class="flexbox" style="min-height: 120px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- ...even if it's large enough that our items won't occupy all the + space. --> + <div class="flexbox" style="min-height: 200px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- If we set a maximum size that's smaller than the shrinkwrap size, + we should max out at that size. --> + <div class="flexbox" style="max-height: 70px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- The max-size may be small enough that our items will overflow. --> + <div class="flexbox" style="max-height: 20px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- But if we add a min-size, it beats the max-size. Here, we use a + min-size smaller than the sum of the items' base sizes... --> + <div class="flexbox" style="min-height: 58px; max-height: 20px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + <!-- ...and here we use a min-size larger than the sum of the items' + base sizes. --> + <div class="flexbox" style="min-height: 140px; max-height: 20px"> + <div class="a"/><div class="b"/><div class="c"/> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-002-expected.xhtml new file mode 100644 index 0000000..e2b9ed0b --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-002-expected.xhtml
@@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; } + div.flexbox { + float: left; + border: 1px dashed blue; + background: lightgreen; + font-size: 10px; + margin-right: 10px; + } + </style> + </head> + <body> + <div class="flexbox"> + <div>AB</div> + </div> + + <div class="flexbox"> + <div>AB</div> + </div> + + <div class="flexbox"> + <div>AB</div> + </div> + + <div class="flexbox" style="width: 30px"> + <div>AB</div> + </div> + + <div class="flexbox" style="width: 3px"> + <div>AB</div> + </div> + + <div class="flexbox" style="width: 30px"> + <div>AB</div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-002.xhtml new file mode 100644 index 0000000..fac421b --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-sizing-vert-002.xhtml
@@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This testcase checks how "min-width" and "max-width" affect the sizing + of vertical flex containers that have no explicit "width" property. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing sizing of an auto-sized vertical flex container with min-width and max-width constraints</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"/> + <link rel="match" href="flexbox-sizing-vert-002-ref.xhtml"/> + <style> + div { height: 100px; } + div.flexbox { + float: left; + border: 1px dashed blue; + background: lightgreen; + font-size: 10px; + display: flex; + margin-right: 10px; + } + </style> + </head> + <body> + <!-- floated auto-width vertical flexbox should shrinkwrap its contents. --> + <div class="flexbox"> + <div>AB</div> + </div> + + <!-- Adding a small min-size shouldn't affect that. --> + <div class="flexbox" style="min-width: 2px"> + <div>AB</div> + </div> + + <!-- ...nor should a large max-size. --> + <div class="flexbox" style="max-width: 300px"> + <div>AB</div> + </div> + + <!-- OK. Now, if we set a minimum size that's larger than the shrinkwrap + size, we should jump up to that size. --> + <div class="flexbox" style="min-width: 30px"> + <div>AB</div> + </div> + + <!-- If we set a maximum size that's smaller than the shrinkwrap size, + we should max out at that size. --> + <div class="flexbox" style="max-width: 3px"> + <div>AB</div> + </div> + + <!-- But if we add a min-size, it beats the max-size. --> + <div class="flexbox" style="min-width: 30px; max-width: 5px"> + <div>AB</div> + </div> + + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001a-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001a-expected.xhtml new file mode 100644 index 0000000..7adbbb7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001a-expected.xhtml
@@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for table-fixup on table parts inside of a + flex container. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + border: 1px dashed blue; + } + + <!-- NOTE: table-fixup pads each td element by 1px on each side. We + override that for top & bottom, for simplicity. So the td makes us + generate a box that's 2px wider than its contents. --> + td { + padding-top: 0px; + padding-bottom: 0px; + } + + .a { + background: lightgreen; + width: 48px; + } + + .b { + background: yellow; + width: 48px; + } + + .c { + background: pink; + width: 48px; + } + </style> + </head> + <body> + <!-- Just 2 adjacent table cells (they end up in the same table) --> + <div class="flexbox" style="padding-left: 50px; width: 150px" + ><td class="a">cell1</td><td class="b">cell2</td></div> + + <!-- Table cell followed by tbody (they end up in the same table) --> + <div class="flexbox" style="padding-left: 75px; width: 125px" + ><td class="a">cell1</td><tbody class="b">t</tbody></div> + + <!-- Empty table cell (ends up occupying 2px of width), followed by div, + followed by nonempty table cell. (3 flex items). --> + <div class="flexbox" style="padding-left: 52px; width: 148px" + ><div style="display: inline-block;" class="c">div</div><div style="display: inline-table; margin-left: 50px"><td class="b">cell1</td></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001a.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001a.xhtml new file mode 100644 index 0000000..b87151ad --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001a.xhtml
@@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + Testcase with table parts inside of a flex container, triggering + table-fixup. We use justify-content:space-between to stick packing + space between flex items, so that we can verify that e.g. a contiguous + run of <td>s will end up in the same flex item (wrapped in a table). + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing that table cells in a flex container get an anonymous table wrapper that forms the flex item</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"/> + <link rel="match" href="flexbox-table-fixup-001-ref.xhtml"/> + <style> + div.flexbox { + border: 1px dashed blue; + width: 200px; + display: flex; + justify-content: space-around; + } + + <!-- NOTE: table-fixup pads each td element by 1px on each side. We + override that for top & bottom, for simplicity. So the td makes us + generate a box that's 2px wider than its contents. --> + td { + padding-top: 0px; + padding-bottom: 0px; + } + + .a { + background: lightgreen; + width: 48px; + } + + .b { + background: yellow; + width: 48px; + } + + .c { + background: pink; + width: 48px; + } + </style> + </head> + <body> + <!-- Just 2 adjacent table cells (they end up in the same table) --> + <div class="flexbox" + ><td class="a">cell1</td><td class="b">cell2</td></div> + + <!-- Table cell followed by tbody (they end up in the same table) --> + <div class="flexbox" + ><td class="a">cell1</td><tbody class="b">t</tbody></div> + + <!-- Empty table cell (ends up occupying 2px of width), followed by div, + followed by nonempty table cell. (3 flex items). --> + <!-- Note: We use "space-between" (instead of "space-around") here because + it makes the math cleaner. (100px split 2 ways instead of 3 ways.) --> + <div class="flexbox" style="justify-content: space-between" + ><td></td><div class="c">div</div><td class="b">cell1</td></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001b-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001b-expected.xhtml new file mode 100644 index 0000000..7adbbb7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001b-expected.xhtml
@@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case for table-fixup on table parts inside of a + flex container. --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div.flexbox { + border: 1px dashed blue; + } + + <!-- NOTE: table-fixup pads each td element by 1px on each side. We + override that for top & bottom, for simplicity. So the td makes us + generate a box that's 2px wider than its contents. --> + td { + padding-top: 0px; + padding-bottom: 0px; + } + + .a { + background: lightgreen; + width: 48px; + } + + .b { + background: yellow; + width: 48px; + } + + .c { + background: pink; + width: 48px; + } + </style> + </head> + <body> + <!-- Just 2 adjacent table cells (they end up in the same table) --> + <div class="flexbox" style="padding-left: 50px; width: 150px" + ><td class="a">cell1</td><td class="b">cell2</td></div> + + <!-- Table cell followed by tbody (they end up in the same table) --> + <div class="flexbox" style="padding-left: 75px; width: 125px" + ><td class="a">cell1</td><tbody class="b">t</tbody></div> + + <!-- Empty table cell (ends up occupying 2px of width), followed by div, + followed by nonempty table cell. (3 flex items). --> + <div class="flexbox" style="padding-left: 52px; width: 148px" + ><div style="display: inline-block;" class="c">div</div><div style="display: inline-table; margin-left: 50px"><td class="b">cell1</td></div></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001b.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001b.xhtml new file mode 100644 index 0000000..ed82d5a --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-table-fixup-001b.xhtml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + Testcase with table parts inside of a flex container, triggering + table-fixup. We use justify-content:space-between to stick packing + space between flex items, so that we can verify that e.g. a contiguous + run of <td>s will end up in the same flex item (wrapped in a table). + + In this variant of the test, we also assign 'flex' values to the + table parts - these values should have no effect, since these children + don't themselves form flex items. The flex property _is_ honored on + the <div class="c">, though, because _its_ box _is_ a direct child of a + flexbox, so it _is_ a flex item. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Testing that the 'flex' shorthand has no effect on table cells in a flex container, since they aren't flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"/> + <link rel="match" href="flexbox-table-fixup-001-ref.xhtml"/> + <style> + div.flexbox { + border: 1px dashed blue; + width: 200px; + display: flex; + justify-content: space-around; + } + + <!-- NOTE: table-fixup pads each td element by 1px on each side. We + override that for top & bottom, for simplicity. So the td makes us + generate a box that's 2px wider than its contents. --> + td { + padding-top: 0px; + padding-bottom: 0px; + } + + .a { + background: lightgreen; + width: 48px; + flex: 5 3 100px; + } + + .b { + background: yellow; + width: 48px; + flex: 1 2 3px; + } + + .c { + background: pink; + flex: 0 0 48px; + } + </style> + </head> + <body> + <!-- Just 2 adjacent table cells (they end up in the same table) --> + <div class="flexbox" + ><td class="a">cell1</td><td class="b">cell2</td></div> + + <!-- Table cell followed by tbody (they end up in the same table) --> + <div class="flexbox" + ><td class="a">cell1</td><tbody class="b">t</tbody></div> + + <!-- Empty table cell (ends up occupying 2px of width), followed by div, + followed by nonempty table cell. (3 flex items). --> + <!-- Note: We use "space-between" (instead of "space-around") here because + it makes the math cleaner. (100px split 2 ways instead of 3 ways.) --> + <div class="flexbox" style="justify-content: space-between" + ><td></td><div class="c">div</div><td class="b">cell1</td></div> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001a-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001a-expected.xhtml new file mode 100644 index 0000000..66db4bf --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001a-expected.xhtml
@@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + Reference case, with inline-blocks instead of flexbox/flex items, + with positioning done using margins. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; } + div.flexbox { + border: 1px dashed blue; + width: 200px; + } + div.a { + width: 30px; + background: lightgreen; + display: inline-block; + } + div.b { + width: 20px; + background: lightblue; + display: inline-block; + } + img { + width: 40px; + height: 100%; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="margin-left: 85px"/> + </div> + + <div class="flexbox"> + <div class="a" style="margin-left: 37.5px" + /><div class="b" style="margin-left: 75px"/> + </div> + + <div class="flexbox"> + <img src="solidblue.png" style="margin-left: 30px" + /><img src="solidblue.png" style="margin-left: 60px"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001a.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001a.xhtml new file mode 100644 index 0000000..0ab6f6d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001a.xhtml
@@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test verifies that we don't create anonymous flex items around + runs of inline content that are _purely_ whitespace. + + The test uses "white-space: pre", to try to tempt us into honoring + that whitespace. (but we should resist that temptation). + + The test also uses 'justify-content: space-around' to be sure we can + tell whether anonymous flex items are being created around the whitespace + (since they'd claim a share of the packing space if they were there). + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Test that anonymous flex items aren't created for pure-whitespace inline content</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"/> + <link rel="match" href="flexbox-whitespace-handling-001-ref.xhtml"/> + <style> + div { height: 100px; } + div.flexbox { + white-space: pre; + border: 1px dashed blue; + width: 200px; + display: flex; + justify-content: space-around; + } + div.a { + flex: none; + width: 30px; + background: lightgreen; + } + div.b { + flex: none; + width: 20px; + background: lightblue; + } + img { width: 40px; } + </style> + </head> + <body> + <!-- whitespace & tab after flex item --> + <div class="flexbox"><div class="a"/> </div> + + <!-- 2 spaces before flex item --> + <div class="flexbox"> <div class="a"/><div class="b"/></div> + + <!-- newline & whitespace between flex items --> + <div class="flexbox"><img src="solidblue.png"/> + <img src="solidblue.png"/></div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001b-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001b-expected.xhtml new file mode 100644 index 0000000..66db4bf --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001b-expected.xhtml
@@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + Reference case, with inline-blocks instead of flexbox/flex items, + with positioning done using margins. + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <style> + div { height: 100px; } + div.flexbox { + border: 1px dashed blue; + width: 200px; + } + div.a { + width: 30px; + background: lightgreen; + display: inline-block; + } + div.b { + width: 20px; + background: lightblue; + display: inline-block; + } + img { + width: 40px; + height: 100%; + } + </style> + </head> + <body> + <div class="flexbox"> + <div class="a" style="margin-left: 85px"/> + </div> + + <div class="flexbox"> + <div class="a" style="margin-left: 37.5px" + /><div class="b" style="margin-left: 75px"/> + </div> + + <div class="flexbox"> + <img src="solidblue.png" style="margin-left: 30px" + /><img src="solidblue.png" style="margin-left: 60px"/> + </div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001b.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001b.xhtml new file mode 100644 index 0000000..db03de8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-001b.xhtml
@@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test is like the -1a variant, but with the whitespace removed between + flex items (since that whitespace should be ignored anyway, if we're + doing things right). + --> +<!-- XXXdholbert Does this testcase add value? + (Maybe it should be an alternate reference case.) --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Test that flex items are created correctly</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"/> + <link rel="match" href="flexbox-whitespace-handling-001-ref.xhtml"/> + <style> + div { height: 100px; } + div.flexbox { + border: 1px dashed blue; + width: 200px; + display: flex; + justify-content: space-around; + } + div.a { + width: 30px; + background: lightgreen; + } + div.b { + width: 20px; + background: lightblue; + } + img { width: 40px; } + </style> + </head> + <body> + <div class="flexbox"><div class="a"/></div> + + <div class="flexbox"><div class="a"/><div class="b"/></div> + + <div class="flexbox" + ><img src="solidblue.png" + /><img src="solidblue.png"/></div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-002-expected.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-002-expected.xhtml new file mode 100644 index 0000000..e1925c72 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-002-expected.xhtml
@@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This reference case is the same as the testcase, but with an explicit <div> + around each run of content that we expect to turn into an anonymous + flex item (to ensure that the whitespace is included). + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + div { height: 100px; } + div.flexbox { + white-space: pre; + border: 1px dashed blue; + width: 200px; + display: flex; + justify-content: space-around; + } + div.a { + width: 30px; + background: lightgreen; + } + div.b { + width: 20px; + background: lightblue; + } + </style> + </head> + <body> + <!-- spaces around inline content at the beginning of flexbox --> + <div class="flexbox"><div> abc</div><div class="a"/></div> + <div class="flexbox"><div>abc </div><div class="a"/></div> + <div class="flexbox"><div> abc </div><div class="a"/></div> + + <!-- spaces around inline content at the end of flexbox --> + <div class="flexbox"><div class="a"/><div> abc</div></div> + <div class="flexbox"><div class="a"/><div>abc </div></div> + <div class="flexbox"><div class="a"/><div> abc </div></div> + + <!-- whitespace around inline content in between flex items --> + <div class="flexbox"><div class="a"/><div> abc</div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div>abc </div><div class="b"/></div> + <div class="flexbox"><div class="a"/><div> abc </div><div class="b"/></div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-002.xhtml b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-002.xhtml new file mode 100644 index 0000000..cce7445 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-whitespace-handling-002.xhtml
@@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- + This test verifies that we preserve whitespace at the beginning & end of + anonymous flex items (using "white-space: pre" so that it actually + occupies space and affects the rendering). + --> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Test that whitespace is preserved at the edges of anonymous flex items if 'white-space: pre' is set</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"/> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"/> + <link rel="match" href="flexbox-whitespace-handling-002-ref.xhtml"/> + <link rel="stylesheet" type="text/css" href="support/ahem.css" /> + <style> + div { height: 100px; } + div.flexbox { + white-space: pre; + border: 1px dashed blue; + width: 200px; + display: flex; + justify-content: space-around; + } + div.a { + width: 30px; + background: lightgreen; + } + div.b { + width: 20px; + background: lightblue; + } + </style> + </head> + <body> + <!-- spaces around inline content at the beginning of flexbox --> + <div class="flexbox"> abc<div class="a"/></div> + <div class="flexbox">abc <div class="a"/></div> + <div class="flexbox"> abc <div class="a"/></div> + + <!-- spaces around inline content at the end of flexbox --> + <div class="flexbox"><div class="a"/> abc</div> + <div class="flexbox"><div class="a"/>abc </div> + <div class="flexbox"><div class="a"/> abc </div> + + <!-- whitespace around inline content in between flex items --> + <div class="flexbox"><div class="a"/> abc<div class="b"/></div> + <div class="flexbox"><div class="a"/>abc <div class="b"/></div> + <div class="flexbox"><div class="a"/> abc <div class="b"/></div> + </body> +</html> +
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-001-expected.html new file mode 100644 index 0000000..0825015 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-001-expected.html
@@ -0,0 +1,59 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case where we've swapped in actual divs (fakeBefore/fakeAfter) + for the testcase's ::before and ::after generated content. + + fakeBefore div is always the first child; fakeAfter is always the last. + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + align-items: flex-end; + justify-content: space-between; + height: 50px; + width: 300px; + margin-bottom: 2px; + background: lightgray; + } + .fakeBefore { + align-self: center; + content: 'b'; + background: yellow; + } + .fakeAfter { + align-self: center; + content: 'a'; + background: lightblue; + } + </style> +</head> +<body> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + x + <div>y</div> + z + </div> + <div class="flexContainer"> + x + <div>y</div> + z + <div class="fakeAfter">a</div> + </div> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + x + <div>y</div> + z + <div class="fakeAfter">a</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-001.html new file mode 100644 index 0000000..3560c891 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-001.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase to ensure we handle ::before and ::after pseudo-elements on + a flex container and treat them as flex items (e.g. honoring "align-self", + and not merging them into anonymous flex items formed around text). + --> +<html> +<head> + <title>CSS Test: Testing that generated content nodes are treated as a flex items</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"> + <link rel="match" href="flexbox-with-pseudo-elements-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + align-items: flex-end; + justify-content: space-between; + height: 50px; + width: 300px; + margin-bottom: 2px; + background: lightgray; + } + div.withBefore::before { + align-self: center; + content: 'b'; + background: yellow; + } + div.withAfter::after { + align-self: center; + content: 'a'; + background: lightblue; + } + </style> +</head> +<body> + <div class="flexContainer withBefore"> + x + <div>y</div> + z + </div> + <div class="flexContainer withAfter"> + x + <div>y</div> + z + </div> + <div class="flexContainer withBefore withAfter"> + x + <div>y</div> + z + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-002-expected.html new file mode 100644 index 0000000..3d55562 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-002-expected.html
@@ -0,0 +1,85 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case where we've swapped in actual divs (fakeBefore/fakeAfter) + for the testcase's ::before and ::after generated content. + + fakeBefore div is always the first child; fakeAfter is always the last. + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + margin-bottom: 2px; + background: lightgray; + } + .fakeBefore { + background: yellow; + /* This 'order' value should place us after the other elements, visually, + even though we're ::before. */ + order: 1; + } + .fakeAfter { + background: lightblue; + order: -1; + } + </style> +</head> +<body> + <!-- 'b' should be at end, due to its high 'order' value: --> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + <div>I</div> + </div> + + <!-- 'b' should be at beginning, since it's '::before' and the other item has + a matching 'order' value: --> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + <div style="order: 1">I</div> + </div> + + <!-- 'a' should be at beginning, due to its low 'order' value: --> + <div class="flexContainer"> + <div>I</div> + <div class="fakeAfter">a</div> + </div> + + <!-- 'a' should be at end, since it's '::after' and the other item has + a matching 'order' value: --> + <div class="flexContainer"> + <div style="order: -1">I</div> + <div class="fakeAfter">a</div> + </div> + + <!-- As above, the ::after 'a' should be at beginning, and the ::before 'b' + should be at end, due to their 'order' values --> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + <div>I</div> + <div class="fakeAfter">a</div> + </div> + + <!-- ...but now the normal item "I" has its order increased, so it'll go + at the end. --> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + <div style="order: 1">I</div> + <div class="fakeAfter">a</div> + </div> + + <!-- ...and now the normal item "I" has its order reduced, so it'll go + at the beginning. --> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + <div style="order: -1">I</div> + <div class="fakeAfter">a</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-002.html new file mode 100644 index 0000000..8539a0c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-002.html
@@ -0,0 +1,79 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase to ensure we handle ::before and ::after pseudo-elements on + a flex container and treat them as flex items (e.g. honoring "order"). + --> +<html> +<head> + <title>CSS Test: Testing that generated content nodes are treated as a flex items, and honor 'order'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"> + <link rel="match" href="flexbox-with-pseudo-elements-002-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + margin-bottom: 2px; + background: lightgray; + } + .withBefore::before { + content: 'b'; + background: yellow; + /* This 'order' value should place us after the other elements, visually, + even though we're ::before. */ + order: 1; + } + .withAfter::after { + content: 'a'; + background: lightblue; + /* This 'order' value should place us before the other elements, visually, + even though we're ::after. */ + order: -1; + } + </style> +</head> +<body> + <!-- 'b' should be at end, due to its high 'order' value: --> + <div class="flexContainer withBefore"> + <div>I</div> + </div> + + <!-- 'b' should be at beginning, since it's '::before' and the other item has + a matching 'order' value: --> + <div class="flexContainer withBefore"> + <div style="order: 1">I</div> + </div> + + <!-- 'a' should be at beginning, due to its low 'order' value: --> + <div class="flexContainer withAfter"> + <div>I</div> + </div> + + <!-- 'b' should be at beginning, since it's '::after' and the other item has + a matching 'order' value: --> + <div class="flexContainer withAfter"> + <div style="order: -1">I</div> + </div> + + <!-- As above, the ::after 'a' should be at beginning, and the ::before 'b' + should be at end, due to their 'order' values --> + <div class="flexContainer withBefore withAfter"> + <div>I</div> + </div> + + <!-- ...but now the normal item "I" has its order increased, so it'll go + at the end. --> + <div class="flexContainer withBefore withAfter"> + <div style="order: 1">I</div> + </div> + + <!-- ...and now the normal item "I" has its order reduced, so it'll go + at the beginning. --> + <div class="flexContainer withBefore withAfter"> + <div style="order: -1">I</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-003-expected.html new file mode 100644 index 0000000..e47b7303 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-003-expected.html
@@ -0,0 +1,57 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Reference case where we've swapped in actual divs (fakeBefore/fakeAfter) + for the testcase's ::before and ::after generated content. + + fakeBefore div is always the first child; fakeAfter is always the last. + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + align-items: flex-end; + justify-content: space-between; + height: 50px; + width: 300px; + margin-bottom: 2px; + background: lightgray; + } + .fakeBefore { + content: 'b'; + background: yellow; + } + .fakeAfter { + content: 'a'; + background: lightblue; + } + </style> +</head> +<body> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + x + <div>y</div> + z + </div> + <div class="flexContainer"> + x + <div>y</div> + z + <div class="fakeAfter">a</div> + </div> + <div class="flexContainer"> + <div class="fakeBefore">b</div> + x + <div>y</div> + z + <div class="fakeAfter">a</div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-003.html new file mode 100644 index 0000000..f50f943 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-with-pseudo-elements-003.html
@@ -0,0 +1,64 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<!-- Testcase to ensure we handle ::before and ::after pseudo-elements on a + flex container, specifically when they've got display:table-row or + table-cell. + + Note that we *don't* treat the table row or cell frames themselves as flex + items, because they get wrapped in an anonymous table box, and *that* is + the flex item. So, "align-self" and "order" have no effect on the + row/cell. --> +<html> +<head> + <title>CSS Test: Testing that generated content nodes with table-part display types are wrapped with an anonymous table, which forms a flex item</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-items"> + <link rel="match" href="flexbox-with-pseudo-elements-003-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + align-items: flex-end; + justify-content: space-between; + height: 50px; + width: 300px; + margin-bottom: 2px; + background: lightgray; + } + div.withBefore::before { + display: table-row; + content: 'b'; + background: yellow; + align-self: center; /* should have no effect */ + order: 1; /* should have no effect */ + } + div.withAfter::after { + display: table-cell; + content: 'a'; + background: lightblue; + align-self: center; /* should have no effect */ + order: -1; /* should have no effect */ + } + </style> +</head> +<body> + <div class="flexContainer withBefore"> + x + <div>y</div> + z + </div> + <div class="flexContainer withAfter"> + x + <div>y</div> + z + </div> + <div class="flexContainer withBefore withAfter"> + x + <div>y</div> + z + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-001-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-001-expected.html new file mode 100644 index 0000000..830e4d87 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-001-expected.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + /* Testcase has direction: ltr; */ + /* Testcase has writing-mode: horizontal-tb; */ + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- row wrap --> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- row wrap-reverse --> + <div class="item3"></div><div class="item4"></div> + <div class="item1"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap --> + <div class="item2"></div><div class="item1"></div> + <div class="item4"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap-reverse --> + <div class="item4"></div><div class="item3"></div> + <div class="item2"></div><div class="item1"></div> + </div> + + <div class="flexContainer"><!-- column wrap --> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- column wrap-reverse --> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap --> + <div class="item2"></div><div class="item4"></div> + <div class="item1"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap-reverse --> + <div class="item4"></div><div class="item2"></div> + <div class="item3"></div><div class="item1"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-001.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-001.html new file mode 100644 index 0000000..49078e0d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-001.html
@@ -0,0 +1,78 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: horizontal-tb'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#propdef-flex-direction"> + <link rel="match" href="flexbox-writing-mode-001-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + direction: ltr; + writing-mode: horizontal-tb; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer" style="flex-flow: row wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + + <div class="flexContainer" style="flex-flow: column wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-002-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-002-expected.html new file mode 100644 index 0000000..40a537f1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-002-expected.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + /* Testcase has direction: ltr; */ + /* Testcase has writing-mode: vertical-rl; */ + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- row wrap --> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- row wrap-reverse --> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap --> + <div class="item4"></div><div class="item2"></div> + <div class="item3"></div><div class="item1"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap-reverse --> + <div class="item2"></div><div class="item4"></div> + <div class="item1"></div><div class="item3"></div> + </div> + + <div class="flexContainer"><!-- column wrap --> + <div class="item2"></div><div class="item1"></div> + <div class="item4"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- column wrap-reverse --> + <div class="item4"></div><div class="item3"></div> + <div class="item2"></div><div class="item1"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap --> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap-reverse --> + <div class="item3"></div><div class="item4"></div> + <div class="item1"></div><div class="item2"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-002.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-002.html new file mode 100644 index 0000000..0c61e39d --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-002.html
@@ -0,0 +1,78 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: vertical-rl'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#propdef-flex-direction"> + <link rel="match" href="flexbox-writing-mode-002-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + direction: ltr; + writing-mode: vertical-rl; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer" style="flex-flow: row wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + + <div class="flexContainer" style="flex-flow: column wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-003-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-003-expected.html new file mode 100644 index 0000000..4035e8c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-003-expected.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + /* Testcase has direction: ltr; */ + /* Testcase has writing-mode: vertical-lr; */ + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- row wrap --> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- row wrap-reverse --> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap --> + <div class="item2"></div><div class="item4"></div> + <div class="item1"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap-reverse --> + <div class="item4"></div><div class="item2"></div> + <div class="item3"></div><div class="item1"></div> + </div> + + <div class="flexContainer"><!-- column wrap --> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- column wrap-reverse --> + <div class="item3"></div><div class="item4"></div> + <div class="item1"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap --> + <div class="item2"></div><div class="item1"></div> + <div class="item4"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap-reverse --> + <div class="item4"></div><div class="item3"></div> + <div class="item2"></div><div class="item1"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-003.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-003.html new file mode 100644 index 0000000..791997c2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-003.html
@@ -0,0 +1,78 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Try various flex-flow values, with 'direction: ltr' and 'writing-mode: vertical-lr'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#propdef-flex-direction"> + <link rel="match" href="flexbox-writing-mode-003-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + direction: ltr; + writing-mode: vertical-lr; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer" style="flex-flow: row wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + + <div class="flexContainer" style="flex-flow: column wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-004-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-004-expected.html new file mode 100644 index 0000000..f50c241f3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-004-expected.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + /* Testcase has direction: rtl; */ + /* Testcase has writing-mode: horizontal-tb; */ + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- row wrap --> + <div class="item2"></div><div class="item1"></div> + <div class="item4"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- row wrap-reverse --> + <div class="item4"></div><div class="item3"></div> + <div class="item2"></div><div class="item1"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap --> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap-reverse --> + <div class="item3"></div><div class="item4"></div> + <div class="item1"></div><div class="item2"></div> + </div> + + <div class="flexContainer"><!-- column wrap --> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- column wrap-reverse --> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap --> + <div class="item4"></div><div class="item2"></div> + <div class="item3"></div><div class="item1"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap-reverse --> + <div class="item2"></div><div class="item4"></div> + <div class="item1"></div><div class="item3"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-004.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-004.html new file mode 100644 index 0000000..e437b09 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-004.html
@@ -0,0 +1,78 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: horizontal-tb'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#propdef-flex-direction"> + <link rel="match" href="flexbox-writing-mode-004-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + direction: rtl; + writing-mode: horizontal-tb; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer" style="flex-flow: row wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + + <div class="flexContainer" style="flex-flow: column wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-005-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-005-expected.html new file mode 100644 index 0000000..b7efee4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-005-expected.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + /* Testcase has direction: rtl; */ + /* Testcase has writing-mode: vertical-rl; */ + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- row wrap --> + <div class="item4"></div><div class="item2"></div> + <div class="item3"></div><div class="item1"></div> + </div> + <div class="flexContainer"><!-- row wrap-reverse --> + <div class="item2"></div><div class="item4"></div> + <div class="item1"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap --> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap-reverse --> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + + <div class="flexContainer"><!-- column wrap --> + <div class="item4"></div><div class="item3"></div> + <div class="item2"></div><div class="item1"></div> + </div> + <div class="flexContainer"><!-- column wrap-reverse --> + <div class="item2"></div><div class="item1"></div> + <div class="item4"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap --> + <div class="item3"></div><div class="item4"></div> + <div class="item1"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap-reverse --> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-005.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-005.html new file mode 100644 index 0000000..d137274 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-005.html
@@ -0,0 +1,78 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: vertical-rl'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#propdef-flex-direction"> + <link rel="match" href="flexbox-writing-mode-005-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + direction: rtl; + writing-mode: vertical-rl; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer" style="flex-flow: row wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + + <div class="flexContainer" style="flex-flow: column wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-006-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-006-expected.html new file mode 100644 index 0000000..865fc39b --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-006-expected.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + /* Testcase has direction: rtl; */ + /* Testcase has writing-mode: vertical-lr; */ + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"><!-- row wrap --> + <div class="item2"></div><div class="item4"></div> + <div class="item1"></div><div class="item3"></div> + </div> + <div class="flexContainer"><!-- row wrap-reverse --> + <div class="item4"></div><div class="item2"></div> + <div class="item3"></div><div class="item1"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap --> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- row-reverse wrap-reverse --> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + + <div class="flexContainer"><!-- column wrap --> + <div class="item3"></div><div class="item4"></div> + <div class="item1"></div><div class="item2"></div> + </div> + <div class="flexContainer"><!-- column wrap-reverse --> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap --> + <div class="item4"></div><div class="item3"></div> + <div class="item2"></div><div class="item1"></div> + </div> + <div class="flexContainer"><!-- column-reverse wrap-reverse --> + <div class="item2"></div><div class="item1"></div> + <div class="item4"></div><div class="item3"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-006.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-006.html new file mode 100644 index 0000000..b5e00b3c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-006.html
@@ -0,0 +1,78 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Try various flex-flow values, with 'direction: rtl' and 'writing-mode: vertical-lr'</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#propdef-flex-direction"> + <link rel="match" href="flexbox-writing-mode-006-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + direction: rtl; + writing-mode: vertical-lr; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer" style="flex-flow: row wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: row-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + + <div class="flexContainer" style="flex-flow: column wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer" style="flex-flow: column-reverse wrap-reverse"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-007-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-007-expected.html new file mode 100644 index 0000000..a8157cb --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-007-expected.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-007.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-007.html new file mode 100644 index 0000000..4f1bb4c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-007.html
@@ -0,0 +1,75 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Verify that explicit sizes are honored on flex items whose writing-mode may differ from the flex container's writing-mode</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"> + <link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode"> + <link rel="match" href="flexbox-writing-mode-007-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + writing-mode: horizontal-tb; + flex-flow: row wrap; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + + /* Classes applied to flex container, to customize its children + * (which should not affect their sizing): + */ + .kids_horizontal_tb > * { + writing-mode: horizontal-tb; + } + .kids_vertical_lr > * { + writing-mode: vertical-lr; + } + .kids_vertical_rl > * { + writing-mode: vertical-rl; + } + + </style> +</head> +<body> + <div class="flexContainer"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_horizontal_tb"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_vertical_lr"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_vertical_rl"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-008-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-008-expected.html new file mode 100644 index 0000000..18f1c87ca --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-008-expected.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + <div class="flexContainer"> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + <div class="flexContainer"> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> + <div class="flexContainer"> + <div class="item1"></div><div class="item3"></div> + <div class="item2"></div><div class="item4"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-008.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-008.html new file mode 100644 index 0000000..efe7f7c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-008.html
@@ -0,0 +1,75 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Verify that explicit sizes are honored on flex items whose writing-mode may differ from the flex container's writing-mode</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"> + <link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode"> + <link rel="match" href="flexbox-writing-mode-008-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + writing-mode: vertical-lr; + flex-flow: row wrap; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + + /* Classes applied to flex container, to customize its children + * (which should not affect their sizing): + */ + .kids_horizontal_tb > * { + writing-mode: horizontal-tb; + } + .kids_vertical_lr > * { + writing-mode: vertical-lr; + } + .kids_vertical_rl > * { + writing-mode: vertical-rl; + } + + </style> +</head> +<body> + <div class="flexContainer"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_horizontal_tb"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_vertical_lr"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_vertical_rl"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-009-expected.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-009-expected.html new file mode 100644 index 0000000..7380c11 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-009-expected.html
@@ -0,0 +1,56 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Reftest Reference</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <meta charset="utf-8"> + <style> + .flexContainer { + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + } + .flexContainer > * { + width: 20px; + height: 15px; + float: left; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + </style> +</head> +<body> + <div class="flexContainer"> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + <div class="flexContainer"> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + <div class="flexContainer"> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> + <div class="flexContainer"> + <div class="item3"></div><div class="item1"></div> + <div class="item4"></div><div class="item2"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-009.html b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-009.html new file mode 100644 index 0000000..ec19a88 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-009.html
@@ -0,0 +1,75 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +<html> +<head> + <title>CSS Test: Verify that explicit sizes are honored on flex items whose writing-mode may differ from the flex container's writing-mode</title> + <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com"> + <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#layout-algorithm"> + <link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#propdef-writing-mode"> + <link rel="match" href="flexbox-writing-mode-009-ref.html"> + <meta charset="utf-8"> + <style> + .flexContainer { + display: flex; + width: 40px; + height: 30px; + border: 1px solid gray; + margin-bottom: 5px; + writing-mode: vertical-rl; + flex-flow: row wrap; + } + .flexContainer > * { + width: 20px; + height: 15px; + } + .item1 { + /* Note: flex items are ordered as "CMYK": cyan, magenta, yellow, black */ + background: cyan; + } + .item2 { + background: magenta; + } + .item3 { + background: yellow; + } + .item4 { + background: black; + } + + /* Classes applied to flex container, to customize its children + * (which should not affect their sizing): + */ + .kids_horizontal_tb > * { + writing-mode: horizontal-tb; + } + .kids_vertical_lr > * { + writing-mode: vertical-lr; + } + .kids_vertical_rl > * { + writing-mode: vertical-rl; + } + + </style> +</head> +<body> + <div class="flexContainer"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_horizontal_tb"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_vertical_lr"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> + <div class="flexContainer kids_vertical_rl"> + <div class="item1"></div><div class="item2"></div> + <div class="item3"></div><div class="item4"></div> + </div> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/Ahem.ttf b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/Ahem.ttf new file mode 100644 index 0000000..ac81cb0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/Ahem.ttf Binary files differ
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/ahem.css b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/ahem.css new file mode 100644 index 0000000..82ee4667 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/ahem.css
@@ -0,0 +1,4 @@ +@font-face { + font-family: "Ahem"; + src: url(./Ahem.ttf); +}
diff --git a/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/solidblue.png b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/solidblue.png new file mode 100644 index 0000000..a64b6a4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/csswg-test/vendor-imports/mozilla/mozilla-central-reftests/flexbox/support/solidblue.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/imported/web-platform-tests/dom/historical-expected.txt b/third_party/WebKit/LayoutTests/imported/web-platform-tests/dom/historical-expected.txt index 598c203b..08d34fe 100644 --- a/third_party/WebKit/LayoutTests/imported/web-platform-tests/dom/historical-expected.txt +++ b/third_party/WebKit/LayoutTests/imported/web-platform-tests/dom/historical-expected.txt
@@ -1,4 +1,3 @@ -CONSOLE WARNING: 'Document.defaultCharset' is deprecated and will be removed in M50, around April 2016. See https://www.chromestatus.com/features/6217124578066432 for more details. This is a testharness.js-based test. FAIL Historical DOM features must be removed: CDATASection assert_equals: expected (undefined) undefined but got (function) function "function CDATASection() { [native code] }" PASS Historical DOM features must be removed: DOMConfiguration @@ -27,7 +26,7 @@ PASS Historical DOM features must be removed: domConfig PASS Historical DOM features must be removed: normalizeDocument PASS Historical DOM features must be removed: renameNode -FAIL Historical DOM features must be removed: defaultCharset assert_equals: expected (undefined) undefined but got (string) "ISO-8859-1" +PASS Historical DOM features must be removed: defaultCharset PASS Historical DOM features must be removed: height PASS Historical DOM features must be removed: width PASS DOMImplementation.getFeature() must be nuked.
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-style-text-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-style-text-expected.txt index 2e946ac8..2af3a30e 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-style-text-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-style-text-expected.txt
@@ -1,4 +1,4 @@ -The test verifies functionality of protocol method CSS.setStyleText for inline elements. +The test verifies functionality of protocol method CSS.setStyleTexts for inline elements. ==== Initial style sheet text ==== color: red; @@ -9,7 +9,7 @@ Stylesheet text: content: 'EDITED' Running test: testSetStylePoorContent -Expected protocol error: SyntaxError Style text is not valid. +Expected protocol error: Failed applying edit #0: SyntaxError Style text is not valid. Stylesheet text: color: red; Running test: testDeleteStyleBody @@ -18,6 +18,6 @@ Stylesheet text: Running test: testSetStyleOpenComment -Expected protocol error: SyntaxError Style text is not valid. +Expected protocol error: Failed applying edit #0: SyntaxError Style text is not valid. Stylesheet text: color: red;
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-style-text.html b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-style-text.html index dbbc96e..948d1c20 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-style-text.html +++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-inline-style-text.html
@@ -9,9 +9,8 @@ { var styleSheetId; var documentNodeId; - var setStyleText; + var setStyleTexts; var verifyProtocolError; - var dumpStyleSheet; InspectorTest.sendCommandOrDie("DOM.enable", {}); InspectorTest.sendCommandOrDie("CSS.enable", {}, cssWasEnabled); @@ -35,9 +34,8 @@ function onGotInlineStyles(result) { styleSheetId = result.inlineStyle.styleSheetId; - setStyleText = InspectorTest.setStyleText.bind(InspectorTest, styleSheetId, false); - verifyProtocolError = InspectorTest.setStyleText.bind(InspectorTest, styleSheetId, true); - dumpStyleSheet = InspectorTest.dumpStyleSheetText.bind(null, styleSheetId); + setStyleTexts = InspectorTest.setStyleTexts.bind(InspectorTest, styleSheetId, false); + verifyProtocolError = InspectorTest.setStyleTexts.bind(InspectorTest, styleSheetId, true); InspectorTest.sendCommandOrDie("CSS.getStyleSheetText", { styleSheetId: styleSheetId }, onInitialStyleSheetText); } @@ -65,38 +63,38 @@ var testSuite = [ function testBasicSetStyle(next) { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 0, endLine: 0, endColumn: 11 }, text: "content: 'EDITED'", - }, dumpAndNext(next)); + }], dumpAndNext(next)); }, function testSetStylePoorContent(next) { - verifyProtocolError({ + verifyProtocolError([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 0, endLine: 0, endColumn: 11 }, text: "}", - }, dumpAndNext(next)); + }], dumpAndNext(next)); }, function testDeleteStyleBody(next) { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 0, endLine: 0, endColumn: 11 }, text: "", - }, dumpAndNext(next)); + }], dumpAndNext(next)); }, function testSetStyleOpenComment(next) { - verifyProtocolError({ + verifyProtocolError([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 0, endLine: 0, endColumn: 11 }, text: "/*", - }, dumpAndNext(next)); + }], dumpAndNext(next)); } ]; } @@ -104,7 +102,7 @@ </script> </head> <body onload="runTest();"> -<p>The test verifies functionality of protocol method CSS.setStyleText for inline elements.</p> +<p>The test verifies functionality of protocol method CSS.setStyleTexts for inline elements.</p> <div id="inliner" style="color: red;"> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-multiple-style-texts-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-multiple-style-texts-expected.txt new file mode 100644 index 0000000..85f2ea8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-multiple-style-texts-expected.txt
@@ -0,0 +1,117 @@ +The test verifies functionality of protocol method CSS.setStyleTexts. + +==== Initial style sheet text ==== +#test { + box-sizing: border-box; +} + +#test { + /* resetting some properties */ + line-height: 1; + font-family: "Arial"; + color: blue; + display: flex; /* flex FTW! */ +} + +@media (min-width: 1px) { + #test { + font-size: 200%; + } + + #test { + } +} + + + +Running test: testMalformedArguments1 +Expected protocol error: Could not parse styleSheetId for edit #2 of 2 + +Running test: testMalformedArguments2 +Expected protocol error: Could not parse range object for edit #1 of 3 + +Running test: testMalformedArguments3 +Expected protocol error: range.startLine must be a non-negative integer + +Running test: testFirstEditDoesNotApply +Expected protocol error: Failed applying edit #0: SyntaxError Style text is not valid. + +Running test: testSecondEditDoesNotApply +Expected protocol error: Failed applying edit #1: SyntaxError Style text is not valid. + +Running test: testBasicSetStyle +==== Style sheet text ==== +#test { + content: 'EDITED'; +} + +#test { + /* resetting some properties */ + line-height: 1; + font-family: "Arial"; + color: blue; + display: flex; /* flex FTW! */ +} + +@media (min-width: 1px) { + #test { + font-size: 200%; + } + + #test { + } +} + + + +Running test: testMultipleStyleTexts1 +==== Style sheet text ==== +#test { + content: 'EDITED2'; +} + +#test { + /* resetting some properties */ + line-height: 1; + font-family: "Arial"; + color: blue; + display: flex; /* flex FTW! */ +} + +@media (min-width: 1px) { + #test { + content: 'EDITED1'; +} + + #test { + } +} + + + +Running test: testMultipleStyleTexts2 +==== Style sheet text ==== +#test { + content: 'EDITED3'; +} + +#test { + /* resetting some properties */ + line-height: 1; + font-family: "Arial"; + color: blue; + display: flex; /* flex FTW! */ +} + +@media (min-width: 1px) { + #test { + content: 'EDITED4'; +} + + #test { + content: 'EDITED5'; +} +} + + +
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-multiple-style-texts.html b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-multiple-style-texts.html new file mode 100644 index 0000000..78cec58 --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-multiple-style-texts.html
@@ -0,0 +1,177 @@ +<html> +<head> +<link rel="stylesheet" href="resources/set-style-text.css"/> +<script type="text/javascript" src="../../http/tests/inspector-protocol/inspector-protocol-test.js"></script> +<script type="text/javascript" src="../../http/tests/inspector-protocol/css-protocol-test.js"></script> +<script type="text/javascript" src="../../http/tests/inspector-protocol/dom-protocol-test.js"></script> +<script type="text/javascript"> + +function test() +{ + var setStyleTexts; + var verifyProtocolError; + var styleSheetId; + var undoAndNext = InspectorTest.undoAndNext; + + InspectorTest.requestDocumentNodeId(onDocumentNodeId); + + function onDocumentNodeId(nodeId) + { + InspectorTest.eventHandler["CSS.styleSheetAdded"] = styleSheetAdded; + InspectorTest.sendCommandOrDie("CSS.enable", {}); + } + + function styleSheetAdded(result) + { + styleSheetId = result.params.header.styleSheetId; + setStyleTexts = InspectorTest.setStyleTexts.bind(InspectorTest, styleSheetId, false); + verifyProtocolError = InspectorTest.setStyleTexts.bind(InspectorTest, styleSheetId, true); + InspectorTest.sendCommandOrDie("CSS.getStyleSheetText", { styleSheetId: styleSheetId }, onInitialStyleSheetText); + } + + function onInitialStyleSheetText(result) + { + InspectorTest.log("==== Initial style sheet text ===="); + InspectorTest.log(result.text); + InspectorTest.runTestSuite(testSuite); + } + + var testSuite = [ + function testMalformedArguments1(next) + { + verifyProtocolError([ + { + styleSheetId: styleSheetId, + range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, + text: "\n content: 'EDITED';\n", + }, + { + range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, + text: "\n content: 'EDITED';\n", + }, + ], next); + }, + + function testMalformedArguments2(next) + { + verifyProtocolError([ + { + styleSheetId: styleSheetId, + }, + { + styleSheetId: styleSheetId, + range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, + text: "\n content: 'EDITED';\n", + }, + { + range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, + text: "\n content: 'EDITED';\n", + }, + ], next); + }, + + function testMalformedArguments3(next) + { + verifyProtocolError([ + { + styleSheetId: styleSheetId, + range: { startLine: "STRING INSTEAD OF NUMBER", startColumn: 11, endLine: 15, endColumn: 4 }, + text: "\n content: 'EDITED';\n", + }, + { + styleSheetId: styleSheetId, + range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, + text: "\n content: 'EDITED';\n", + }, + ], next); + }, + + function testFirstEditDoesNotApply(next) + { + verifyProtocolError([ + { + styleSheetId: styleSheetId, + range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, + text: "\n content: 'EDITED';/*\n", + }, + { + styleSheetId: styleSheetId, + range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, + text: "\n content: 'EDITED';\n", + }, + ], next); + }, + + function testSecondEditDoesNotApply(next) + { + verifyProtocolError([ + { + styleSheetId: styleSheetId, + range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, + text: "\n content: 'EDITED';\n", + }, + { + styleSheetId: styleSheetId, + range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, + text: "\n content: 'EDITED';/*\n", + }, + ], next); + }, + + function testBasicSetStyle(next) + { + setStyleTexts([ + { + styleSheetId: styleSheetId, + range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, + text: "\n content: 'EDITED';\n" + }, + ], undoAndNext(next)); + }, + + function testMultipleStyleTexts1(next) + { + setStyleTexts([ + { + styleSheetId: styleSheetId, + range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, + text: "\n content: 'EDITED1';\n", + }, + { + styleSheetId: styleSheetId, + range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, + text: "\n content: 'EDITED2';\n", + }, + ], undoAndNext(next)); + }, + + function testMultipleStyleTexts2(next) + { + setStyleTexts([ + { + styleSheetId: styleSheetId, + range: { startLine: 17, startColumn: 11, endLine: 18, endColumn: 4 }, + text: "\n content: 'EDITED5';\n", + }, + { + styleSheetId: styleSheetId, + range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, + text: "\n content: 'EDITED4';\n", + }, + { + styleSheetId: styleSheetId, + range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, + text: "\n content: 'EDITED3';\n", + }, + ], undoAndNext(next)); + }, + ]; +} + +</script> +</head> +<body onload="runTest();"> +<p>The test verifies functionality of protocol method CSS.setStyleTexts.</p> +<article id="test"></article> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-style-text-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-style-text-expected.txt index a38b2e6..def76856 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-style-text-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-style-text-expected.txt
@@ -1,4 +1,4 @@ -The test verifies functionality of protocol method CSS.setStyleText. +The test verifies functionality of protocol method CSS.setStyleTexts. ==== Initial style sheet text ==== #test { @@ -217,7 +217,7 @@ Dumping inherited rules: Running test: testSetStylePoorContent -Expected protocol error: SyntaxError Style text is not valid. +Expected protocol error: Failed applying edit #0: SyntaxError Style text is not valid. Dumping matched rules: *#test* { regular box-sizing: border-box; @@ -345,7 +345,7 @@ Dumping inherited rules: Running test: testSetStyleOpenComment -Expected protocol error: SyntaxError Style text is not valid. +Expected protocol error: Failed applying edit #0: SyntaxError Style text is not valid. Dumping matched rules: *#test* { regular box-sizing: border-box; @@ -366,5 +366,5 @@ Dumping inherited rules: Running test: testSetStyleOfRemovedRule -ERROR: NotFoundError Source range didn't match existing style source range +ERROR: Failed applying edit #0: NotFoundError Source range didn't match existing style source range
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-style-text.html b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-style-text.html index d2aefcf..47feeb5 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-style-text.html +++ b/third_party/WebKit/LayoutTests/inspector-protocol/css/css-set-style-text.html
@@ -13,9 +13,8 @@ function test() { - var setStyleText; + var setStyleTexts; var verifyProtocolError; - var dumpStyleSheet; var documentNodeId; var styleSheetId; @@ -31,9 +30,8 @@ function styleSheetAdded(result) { styleSheetId = result.params.header.styleSheetId; - setStyleText = InspectorTest.setStyleText.bind(InspectorTest, styleSheetId, false); - verifyProtocolError = InspectorTest.setStyleText.bind(InspectorTest, styleSheetId, true); - dumpStyleSheet = InspectorTest.dumpStyleSheetText.bind(null, styleSheetId); + setStyleTexts = InspectorTest.setStyleTexts.bind(InspectorTest, styleSheetId, false); + verifyProtocolError = InspectorTest.setStyleTexts.bind(InspectorTest, styleSheetId, true); InspectorTest.sendCommandOrDie("CSS.getStyleSheetText", { styleSheetId: styleSheetId }, onInitialStyleSheetText); } @@ -57,83 +55,83 @@ var testSuite = [ function testBasicSetStyle(next) { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, text: "\n content: 'EDITED';\n", - }, dumpUndoAndNext(next)); + }], dumpUndoAndNext(next)); }, function testSetStyleTwice(next) { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, text: "\n color: green;\n padding: 0 4px;\n cursor: pointer\n", - }, dumpAndCall(step2)); + }], dumpAndCall(step2)); function step2() { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 7, endLine: 4, endColumn: 0 }, text: "\n color: green;\n padding: 0 6px;\n cursor: pointer\n", - }, dumpAndCall(step3)); + }], dumpAndCall(step3)); } function step3() { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 7, endLine: 4, endColumn: 0 }, text: "\n color: green;\n padding: 0 8px;\n cursor: pointer\n", - }, dumpUndoAndNext(next)); + }], dumpUndoAndNext(next)); } }, function testSetStylePoorContent(next) { - verifyProtocolError({ + verifyProtocolError([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, text: "}", - }, dumpUndoAndNext(next)); + }], dumpUndoAndNext(next)); }, function testSetStyleInMedia(next) { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, text: "\n content: 'EDITED';\n color: red;\n /** foo */\n ", - }, dumpUndoAndNext(next)); + }], dumpUndoAndNext(next)); }, function testDeleteStyleBody(next) { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, text: "", - }, dumpUndoAndNext(next)); + }], dumpUndoAndNext(next)); }, function testSetStylePoorRange(next) { - verifyProtocolError({ + verifyProtocolError([{ styleSheetId: styleSheetId, range: { startLine: 11, startColumn: 11, endLine: 15, endColumn: 4 }, text: "\n content: 'EDITED';\n", - }, dumpUndoAndNext(next)); + }], dumpUndoAndNext(next)); }, function testSetStyleOpenComment(next) { - verifyProtocolError({ + verifyProtocolError([{ styleSheetId: styleSheetId, range: { startLine: 13, startColumn: 11, endLine: 15, endColumn: 4 }, text: "\n content: 'EDITED'/* ;\n", - }, dumpUndoAndNext(next)); + }], dumpUndoAndNext(next)); }, function testSetStyleOfRemovedRule(next) @@ -141,11 +139,11 @@ InspectorTest.sendCommandOrDie("Runtime.evaluate", {expression: "removeRule()"}, mutateRule); function mutateRule() { - setStyleText({ + setStyleTexts([{ styleSheetId: styleSheetId, range: { startLine: 0, startColumn: 7, endLine: 2, endColumn: 0 }, text: "\n content: 'EDITED';\n", - }, dumpUndoAndNext(next)); + }], dumpUndoAndNext(next)); } } ]; @@ -154,7 +152,7 @@ </script> </head> <body onload="runTest();"> -<p>The test verifies functionality of protocol method CSS.setStyleText.</p> +<p>The test verifies functionality of protocol method CSS.setStyleTexts.</p> <article id="test"></article> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline-expected.txt b/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline-expected.txt index 30666fb3..662f3e7e 100644 --- a/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline-expected.txt
@@ -2,7 +2,7 @@ >>>> Animation with start delay only WebAnimation - +testId <g style="cursor: -webkit-grab; transform: translateX(200px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="407.00" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0 5 L 400.00 5 L 400.00 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black; cursor: ew-resize;"></circle><circle class="animation-endpoint" cx="407.00" cy="26" r="3.5" style="stroke: black; fill: black; cursor: ew-resize;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="207.00" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(600px);"></line> >>>> Animation with start and end delay WebAnimation
diff --git a/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline.html b/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline.html index 86927b57..363acbe 100644 --- a/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline.html +++ b/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline.html
@@ -25,7 +25,7 @@ function startAnimationWithDelay() { - player = node.animate([{ width: "100px" }, { width: "200px" }], { duration: 200, delay: 100 }); + player = node.animate([{ width: "100px" }, { width: "200px" }], { duration: 200, delay: 100, id: "testId" }); } function startAnimationWithEndDelay()
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline-expected.txt index 95db061..95e6693fe 100644 --- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline-expected.txt
@@ -63,6 +63,12 @@ } ] selectorText : "div" + styleRange : { + endColumn : 17 + endLine : 19 + startColumn : 5 + startLine : 19 + } } { columnNumber : 0 @@ -137,6 +143,12 @@ } ] selectorText : "#fluffy" + styleRange : { + endColumn : 0 + endLine : 24 + startColumn : 9 + startLine : 20 + } } { columnNumber : 0 @@ -166,6 +178,12 @@ } ] selectorText : "input:-moz-placeholder" + styleRange : { + endColumn : 50 + endLine : 25 + startColumn : 24 + startLine : 25 + } } { columnNumber : 0 @@ -218,6 +236,12 @@ } ] selectorText : ".class-name, p /* style all paragraphs as well */" + styleRange : { + endColumn : 0 + endLine : 29 + startColumn : 51 + startLine : 26 + } } { atRule : "@keyframes identifier" @@ -252,6 +276,12 @@ } ] selectorText : "svg|a" + styleRange : { + endColumn : 0 + endLine : 40 + startColumn : 7 + startLine : 38 + } } { atRule : "@media (max-width:500px)" @@ -332,5 +362,11 @@ } ] selectorText : "span" + styleRange : { + endColumn : 4 + endLine : 47 + startColumn : 10 + startLine : 43 + } }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-new-API.html b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-new-API.html index 016f3ff3..edf35506 100644 --- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-new-API.html +++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/styles-new-API.html
@@ -191,12 +191,18 @@ InspectorTest.addResult("error: " + error); return; } - InspectorTest.CSSAgent.setStyleText(rule.style.styleSheetId, { - startLine: rule.style.range.startLine, - startColumn: rule.style.range.startColumn, - endLine: rule.style.range.startLine, - endColumn: rule.style.range.startColumn - }, "font-family: serif;", didSetStyleText); + InspectorTest.CSSAgent.setStyleTexts([ + { + styleSheetId: rule.style.styleSheetId, + range: { + startLine: rule.style.range.startLine, + startColumn: rule.style.range.startColumn, + endLine: rule.style.range.startLine, + endColumn: rule.style.range.startColumn + }, + text: "font-family: serif;", + }, + ], didSetStyleText); } function viaInspectorStyleSheetCreated(error, styleSheetId)
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/stylesheet-source-url-comment-expected.txt b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/stylesheet-source-url-comment-expected.txt index 41fe5f0..829e258 100644 --- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/stylesheet-source-url-comment-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/stylesheet-source-url-comment-expected.txt
@@ -6,6 +6,9 @@ Running: testSourceURLComment body { color: black; } +Running: testDeprecatedSourceURLComment +body { color: black; } + Running: testNonRelativeURL body { color: red; }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/stylesheet-source-url-comment.html b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/stylesheet-source-url-comment.html index d094c48..4b248f6 100644 --- a/third_party/WebKit/LayoutTests/inspector/elements/styles-4/stylesheet-source-url-comment.html +++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-4/stylesheet-source-url-comment.html
@@ -31,6 +31,13 @@ document.head.appendChild(styleElement); } +function addInlineStyleSheetDeprecated() +{ + var styleElement = document.createElement("style"); + styleElement.textContent = "body { color: black; }\n/*@ sourceURL=css/addedInlineStylesheetDeprecated.css */"; + document.head.appendChild(styleElement); +} + function test() { function forEachHeaderMatchingURL(url, handler) @@ -77,6 +84,19 @@ } }, + function testDeprecatedSourceURLComment(next) + { + InspectorTest.showScriptSource("css/addedInlineStylesheetDeprecated.css", didShowSource); + InspectorTest.evaluateInPage("setTimeout(addInlineStyleSheetDeprecated, 0)"); + + function didShowSource(sourceFrame) + { + InspectorTest.addResult(sourceFrame.textEditor.text()); + forEachHeaderMatchingURL("addedInlineStylesheetDeprecated", checkHeaderHasSourceURL); + next(); + } + }, + function testNonRelativeURL(next) { InspectorTest.showScriptSource("/css/nonRelativeInlineStylesheet.css", didShowSource);
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/editing-test-suite.js b/third_party/WebKit/LayoutTests/inspector/sass/editing-test-suite.js index 880d38b..723e4e4 100644 --- a/third_party/WebKit/LayoutTests/inspector/sass/editing-test-suite.js +++ b/third_party/WebKit/LayoutTests/inspector/sass/editing-test-suite.js
@@ -62,7 +62,7 @@ var clone = cssAST.clone(); var rule = clone.rules[0]; var anchor = rule.properties[0]; - rule.insertProperty("NEW-NAME", "NEW-VALUE", false, anchor, true); + rule.insertProperties(["NEW-NAME"], ["NEW-VALUE"], [false], anchor, true); InspectorTest.addResult(clone.document.newText()); next(); }, @@ -72,7 +72,7 @@ var clone = cssAST.clone(); var rule = clone.rules[0]; var anchor = rule.properties[rule.properties.length - 1]; - rule.insertProperty("NEW-NAME", "NEW-VALUE", false, anchor, false); + rule.insertProperties(["NEW-NAME"], ["NEW-VALUE"], [false], anchor, false); InspectorTest.addResult(clone.document.newText()); next(); }, @@ -82,7 +82,7 @@ var clone = cssAST.clone(); var rule = clone.rules[0]; var anchor = rule.properties[1]; - rule.insertProperty("NEW-NAME", "NEW-VALUE", true, anchor, true); + rule.insertProperties(["NEW-NAME"], ["NEW-VALUE"], [true], anchor, true); InspectorTest.addResult(clone.document.newText()); next(); }, @@ -92,10 +92,23 @@ var clone = cssAST.clone(); var rule = clone.rules[0]; var anchor = rule.properties[rule.properties.length - 1]; - rule.insertProperty("TRAILING-4", "VALUE", false, anchor, false); - rule.insertProperty("TRAILING-3", "VALUE", false, anchor, false); - rule.insertProperty("TRAILING-2", "VALUE", false, anchor, false); - rule.insertProperty("TRAILING-1", "VALUE", false, anchor, false); + rule.insertProperties(["TRAILING-4", "TRAILING-3", "TRAILING-2", "TRAILING-1"], + ["VALUE", "VALUE", "VALUE", "VALUE"], + [false, false, false, false], + anchor, false); + InspectorTest.addResult(clone.document.newText()); + next(); + }, + + function testPrependMultipleProperties(next) + { + var clone = cssAST.clone(); + var rule = clone.rules[0]; + var anchor = rule.properties[0]; + rule.insertProperties(["TRAILING-1", "TRAILING-2", "TRAILING-3", "TRAILING-4"], + ["VALUE", "VALUE", "VALUE", "VALUE"], + [false, false, false, false], + anchor, true); InspectorTest.addResult(clone.document.newText()); next(); }, @@ -105,7 +118,7 @@ var clone = cssAST.clone(); var rule = clone.rules[0]; var anchor = rule.properties[rule.properties.length - 1]; - rule.insertProperty("NEW-NAME", "NEW-VALUE", false, anchor, false); + rule.insertProperties(["NEW-NAME"], ["NEW-VALUE"], [false], anchor, false); anchor.remove(); InspectorTest.addResult(clone.document.newText()); next(); @@ -116,7 +129,7 @@ var clone = cssAST.clone(); var rule = clone.rules[0]; var lastProperty = rule.properties[rule.properties.length - 1]; - rule.insertProperty("NEW-NAME", "NEW-VALUE", false, lastProperty, false); + rule.insertProperties(["NEW-NAME"], ["NEW-VALUE"], [false], lastProperty, false); lastProperty.name.setText("CHANGED"); rule.properties[0].value.setText("CHANGED"); rule.properties[1].setDisabled(true);
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/_reset.scss b/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/_reset.scss new file mode 100644 index 0000000..d8d986e --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/_reset.scss
@@ -0,0 +1,7 @@ +html, +body, +ul, +ol { + margin: 0; + padding: 0; +}
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.css b/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.css new file mode 100644 index 0000000..592b7815 --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.css
@@ -0,0 +1,12 @@ +html, +body, +ul, +ol { + margin: 0; + padding: 0; } + +body { + font: 100% Helvetica, sans-serif; + background-color: #efefef; } + +/*# sourceMappingURL=base.css.map */
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.css.map b/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.css.map new file mode 100644 index 0000000..5434277 --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.css.map
@@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAAA;;;EAGG;EACC,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;;ACHd,IAAK;EACD,IAAI,EAAE,0BAA0B;EAChC,gBAAgB,EAAE,OAAO", +"sources": ["_reset.scss","base.scss"], +"names": [], +"file": "base.css" +}
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.scss b/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.scss new file mode 100644 index 0000000..25255be4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector/sass/resources/test-mapping-many-scss/base.scss
@@ -0,0 +1,6 @@ +@import 'reset'; + +body { + font: 100% Helvetica, sans-serif; + background-color: #efefef; +}
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/sass-test.js b/third_party/WebKit/LayoutTests/inspector/sass/sass-test.js index 96eb57f..a98f1ec4 100644 --- a/third_party/WebKit/LayoutTests/inspector/sass/sass-test.js +++ b/third_party/WebKit/LayoutTests/inspector/sass/sass-test.js
@@ -2,26 +2,18 @@ InspectorTest.preloadModule("sass"); -var sassWorkspaceAdapter = null; -InspectorTest.sassWorkspaceAdapter = function() -{ - if (!sassWorkspaceAdapter) - sassWorkspaceAdapter = new WebInspector.SASSWorkspaceAdapter(InspectorTest.cssModel, WebInspector.workspace, WebInspector.networkMapping); - return sassWorkspaceAdapter; -} +var cssParserService = null; -var cssParser = null; - -InspectorTest.cssParser = function() +InspectorTest.cssParserService = function() { - if (!cssParser) - cssParser = new WebInspector.CSSParser(); - return cssParser; + if (!cssParserService) + cssParserService = new WebInspector.CSSParserService(); + return cssParserService; } InspectorTest.parseCSS = function(url, text) { - return WebInspector.SASSSupport.parseCSS(InspectorTest.cssParser(), url, text); + return WebInspector.SASSSupport.parseCSS(InspectorTest.cssParserService(), url, text); } InspectorTest.parseSCSS = function(url, text) @@ -31,37 +23,22 @@ function onTokenizer(tokenizer) { - return WebInspector.SASSSupport.parseSCSS(url, text, tokenizer); + return WebInspector.SASSSupport.parseSCSS(tokenizer, url, text); } } InspectorTest.loadASTMapping = function(header, callback) { - console.assert(header.cssModel() === InspectorTest.sassWorkspaceAdapter()._cssModel, "The header could not be processed by main target workspaceAdapter"); - var tokenizerFactory = null; - var sourceMap = null; var completeSourceMapURL = WebInspector.ParsedURL.completeURL(header.sourceURL, header.sourceMapURL); WebInspector.SourceMap.load(completeSourceMapURL, header.sourceURL, onSourceMapLoaded); - self.runtime.instancePromise(WebInspector.TokenizerFactory) - .then(tf => tokenizerFactory = tf) - .then(maybeStartLoading); - - function onSourceMapLoaded(sm) + function onSourceMapLoaded(sourceMap) { - sourceMap = sm; - maybeStartLoading(); - } - - function maybeStartLoading() - { - if (!sourceMap || !tokenizerFactory) - return; - var client = InspectorTest.sassWorkspaceAdapter().trackSources(sourceMap); - WebInspector.SASSLiveSourceMap._loadMapping(client, InspectorTest.cssParser(), tokenizerFactory, sourceMap) - .then(callback) - .then(() => client.dispose()) + var astService = new WebInspector.ASTService(); + WebInspector.ASTSourceMap.fromSourceMap(astService, header.cssModel(), sourceMap) + .then(mapping => callback(mapping)) + .then(() => astService.dispose()); } }
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-ast-editing-1-expected.txt b/third_party/WebKit/LayoutTests/inspector/sass/test-ast-editing-1-expected.txt index 647fea6a..4d14a1b 100644 --- a/third_party/WebKit/LayoutTests/inspector/sass/test-ast-editing-1-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/sass/test-ast-editing-1-expected.txt
@@ -99,6 +99,19 @@ TRAILING-1: VALUE;} +Running: testPrependMultipleProperties +div { + /* This is a regular comment */ + TRAILING-1: VALUE; + TRAILING-2: VALUE; + TRAILING-3: VALUE; + TRAILING-4: VALUE; + color: red; + /* position: absolute; */ + display: block +;} + + Running: testAppendAndRemoveLastProperty div { /* This is a regular comment */
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-ast-editing-2-expected.txt b/third_party/WebKit/LayoutTests/inspector/sass/test-ast-editing-2-expected.txt index c64227f..8421888 100644 --- a/third_party/WebKit/LayoutTests/inspector/sass/test-ast-editing-2-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/sass/test-ast-editing-2-expected.txt
@@ -39,6 +39,13 @@ TRAILING-2: VALUE; TRAILING-1: VALUE;} +Running: testPrependMultipleProperties +div{TRAILING-1: VALUE; +TRAILING-2: VALUE; +TRAILING-3: VALUE; +TRAILING-4: VALUE; +color:red;/*display:block;*/margin:0;} + Running: testAppendAndRemoveLastProperty div{color:red;/*display:block;*/; NEW-NAME: NEW-VALUE;}
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-ast-scss-6-expected.txt b/third_party/WebKit/LayoutTests/inspector/sass/test-ast-scss-6-expected.txt new file mode 100644 index 0000000..55b8741 --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector/sass/test-ast-scss-6-expected.txt
@@ -0,0 +1,29 @@ +Verifies AST of SCSS with @for control statement. + +@for $i from 1 through 3 { + .box#{$i} { + color: $color; + margin: 10px; + } +} +=== AST === +rule 0: "variables" +rule 1: "properties" + property 0 + name: "color" + range: {"startLine":2,"startColumn":8,"endLine":2,"endColumn":13} + value: " $color" + range: {"startLine":2,"startColumn":14,"endLine":2,"endColumn":21} + range: {"startLine":2,"startColumn":8,"endLine":2,"endColumn":22} + disabled: false + property 1 + name: "margin" + range: {"startLine":3,"startColumn":8,"endLine":3,"endColumn":14} + value: " 10px" + range: {"startLine":3,"startColumn":15,"endLine":3,"endColumn":20} + range: {"startLine":3,"startColumn":8,"endLine":3,"endColumn":21} + disabled: false +rule 2: "mixins" +====== +Ranges OK. +
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-ast-scss-6.html b/third_party/WebKit/LayoutTests/inspector/sass/test-ast-scss-6.html new file mode 100644 index 0000000..9d823666 --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector/sass/test-ast-scss-6.html
@@ -0,0 +1,48 @@ +<html> +<head> +<script src="../../http/tests/inspector/inspector-test.js"></script> +<script src="../../http/tests/inspector/elements-test.js"></script> +<script src="./sass-test.js"></script> +<style> +pre { + font-family: monospace; +} +</style> +<script> + +function test() +{ + InspectorTest.evaluateInPage("getSASS()", onSASS); + + function onSASS(result) + { + InspectorTest.parseSCSS("", result.value) + .then(InspectorTest.dumpAST) + .then(InspectorTest.validateASTRanges) + .catch(console.error.bind(console)) + .then(InspectorTest.completeTest); + } +} + +function getSASS() +{ + return document.querySelector(".snippet").textContent; +} + +</script> +</head> + +<body onload="runTest()"> +<p> +Verifies AST of SCSS with @for control statement. +</p> +<pre class="snippet"> +@for $i from 1 through 3 { + .box#{$i} { + color: $color; + margin: 10px; + } +} +</pre> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-good-expected.txt b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-good-expected.txt index 813a825..0015063d2 100644 --- a/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-good-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-good-expected.txt
@@ -1,4 +1,4 @@ -Verify CSSToSASSMapping rebaselines. +Verify WI.ASTSourceMap rebaselines. Mapped CSS: 12 Mapped SCSS: 12
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-good.html b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-good.html index 2a8390f..44b6401 100644 --- a/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-good.html +++ b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-good.html
@@ -67,6 +67,6 @@ </head> <body onload="runTest()"> -<p>Verify CSSToSASSMapping rebaselines.</p> +<p>Verify WI.ASTSourceMap rebaselines.</p> </body> </html>
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-many-scss-expected.txt b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-many-scss-expected.txt new file mode 100644 index 0000000..fb4f331a --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-many-scss-expected.txt
@@ -0,0 +1,6 @@ +Verifies mapping correctly loads multiple SCSS files. + +Mapped CSS: 8 +Mapped SCSS: 8 +No stale entries found. +
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-many-scss.html b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-many-scss.html new file mode 100644 index 0000000..2bf5d3a --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector/sass/test-mapping-many-scss.html
@@ -0,0 +1,38 @@ +<html> +<head> + +<link rel="stylesheet" href="resources/test-mapping-many-scss/base.css"> + +<script src="../../http/tests/inspector/inspector-test.js"></script> +<script src="../../http/tests/inspector/debugger-test.js"></script> +<script src="./sass-test.js"></script> +<script> + +function test() +{ + var mapping; + + var header = InspectorTest.cssModel.styleSheetHeaders().find(header => !!header.sourceMapURL); + InspectorTest.loadASTMapping(header, onMappingLoaded); + + function onMappingLoaded(map) + { + mapping = map; + if (!mapping.isValid()) { + InspectorTest.addResult("ERROR: mapping is not valid."); + InspectorTest.completeTest(); + return; + } + InspectorTest.validateMapping(mapping); + InspectorTest.completeTest(); + } +} + +</script> + +</head> + +<body onload="runTest()"> +<p>Verifies mapping correctly loads multiple SCSS files.</p> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-workspace-adapter-expected.txt b/third_party/WebKit/LayoutTests/inspector/sass/test-workspace-adapter-expected.txt deleted file mode 100644 index b230107..0000000 --- a/third_party/WebKit/LayoutTests/inspector/sass/test-workspace-adapter-expected.txt +++ /dev/null
@@ -1,73 +0,0 @@ -Verify SourceMapTracker functionality. - - -Running: loadFileSystemResources - -Running: loadCSSResources - -Running: mutateCSS - -- TRACKER 1 -- SourceChanged event: all.css - -- TRACKER 1 -- SourceChanged event: all.css -== Outdated == -TRACKER 1: true -TRACKER 2: false - -- TRACKER 2 -- SourceChanged event: print.css -== Outdated == -TRACKER 1: true -TRACKER 2: true -== Contents == -content of print.css: <all.css update 1> -content of print.css: <print.css update 1> -== Outdated == -TRACKER 1: false -TRACKER 2: false - -Running: mutateSASS - -- TRACKER 1 -- SourceChanged event: file:///var/www/a.scss -== Outdated == -TRACKER 1: true -TRACKER 2: false - -- TRACKER 2 -- SourceChanged event: file:///var/www/b.scss -== Outdated == -TRACKER 1: true -TRACKER 2: true -== Contents == -content of file:///var/www/b.scss: <a.scss update 1> -content of file:///var/www/b.scss: <b.scss update 1> -== Outdated == -TRACKER 1: false -TRACKER 2: false - -Running: mutateSASSCommon - -- TRACKER 1 -- SourceChanged event: file:///var/www/common.scss - -- TRACKER 2 -- SourceChanged event: file:///var/www/common.scss -== Outdated == -TRACKER 1: true -TRACKER 2: true -== Contents == -content of file:///var/www/common.scss: <common.scss update 1> -content of file:///var/www/common.scss: <common.scss update 1> -== Outdated == -TRACKER 1: false -TRACKER 2: false - -Running: trackerSetCSS -tracker1.setCSSText result: true -tracker2.setCSSText result: true -== Contents == -content of print.css: <all.css TRACKER 1> -content of print.css: <print.css TRACKER 2> - -Running: trackerSetSASSCommon -tracker1.setSASSText result: true - -- TRACKER 2 -- SourceChanged event: file:///var/www/common.scss -== Outdated == -TRACKER 1: false -TRACKER 2: true - -Running: removeFirstTrackerStyleSheets - -- TRACKER 1 -- TrackingStopped - -Running: removeSecondTrackerStyleSheets - -- TRACKER 2 -- TrackingStopped -
diff --git a/third_party/WebKit/LayoutTests/inspector/sass/test-workspace-adapter.html b/third_party/WebKit/LayoutTests/inspector/sass/test-workspace-adapter.html deleted file mode 100644 index 9243eedc..0000000 --- a/third_party/WebKit/LayoutTests/inspector/sass/test-workspace-adapter.html +++ /dev/null
@@ -1,189 +0,0 @@ -<html> -<head> - -<script src="../../http/tests/inspector/inspector-test.js"></script> -<script src="../../http/tests/inspector/debugger-test.js"></script> -<script src="../../http/tests/inspector/isolated-filesystem-test.js"></script> -<script src="./sass-test.js"></script> -<script> - -var styles = []; -function addStyleSheets() -{ - for (var i = 0; i < 2; ++i) { - var style = document.createElement("style"); - style.textContent = "<all.css content>\n/*# sourceURL=all.css */"; - document.head.appendChild(style); - styles.push(style); - } - - var style = document.createElement("style"); - style.textContent = "<print.css content>\n/*# sourceURL=print.css */"; - document.head.appendChild(style); - styles.push(style); -} - -function removeStyles(amount) -{ - for (var i = 0; i < amount; ++i) - styles.shift().remove(); -} - -function test() -{ - function createTracker(trackerName, cssURL, sassURLs) - { - var sources = sassURLs; - var fakeSourceMap = { - sources: () => sources, - compiledURL: () => cssURL - }; - var tracker = InspectorTest.sassWorkspaceAdapter().trackSources(fakeSourceMap); - tracker.__NAME = trackerName; - tracker.addEventListener(WebInspector.SourceMapTracker.Events.SourceChanged, onSourceChanged); - tracker.addEventListener(WebInspector.SourceMapTracker.Events.TrackingStopped, onTrackingStopped); - return tracker; - - function onSourceChanged(event) - { - InspectorTest.addResult(" -- " + event.target.__NAME + " -- SourceChanged event: " + event.data); - } - - function onTrackingStopped(event) - { - InspectorTest.addResult(" -- " + event.target.__NAME + " -- TrackingStopped"); - } - } - - var fileSystemPrefix = "file:///var/www"; - var css1 = "all.css"; - var css2 = "print.css"; - var sass1 = fileSystemPrefix + "/a.scss"; - var sass2 = fileSystemPrefix + "/b.scss"; - var sassCommon = fileSystemPrefix + "/common.scss"; - var tracker1 = createTracker("TRACKER 1", css1, [sass1, sassCommon]); - var tracker2 = createTracker("TRACKER 2", css2, [sass2, sassCommon]); - - InspectorTest.runTestSuite([ - function loadFileSystemResources(next) - { - var fs = new InspectorTest.TestFileSystem(fileSystemPrefix); - fs.addFileMapping(fileSystemPrefix, "/"); - fs.root.addFile("common.scss", "<common.scss content>"); - fs.root.addFile("a.scss", "<a.scss content>"); - fs.root.addFile("b.scss", "<b.scss content>"); - fs.reportCreated(next); - }, - - function loadCSSResources(next) - { - InspectorTest.evaluateInPage("addStyleSheets()", next); - }, - - function mutateCSS(next) - { - InspectorTest.updateCSSText(css1, "<all.css update 1>") - .then(checkOutdated) - .then(() => InspectorTest.updateCSSText(css2, "<print.css update 1>")) - .then(checkOutdated) - .then(() => requestContent([css1], [css2])) - .then(checkOutdated) - .then(next); - }, - - function mutateSASS(next) - { - InspectorTest.updateSASSText(sass1, "<a.scss update 1>"); - checkOutdated(); - InspectorTest.updateSASSText(sass2, "<b.scss update 1>"); - checkOutdated(); - requestContent([sass1], [sass2]) - .then(checkOutdated) - .then(next); - }, - - function mutateSASSCommon(next) - { - InspectorTest.updateSASSText(sassCommon, "<common.scss update 1>"); - checkOutdated(); - requestContent([sassCommon], [sassCommon]) - .then(checkOutdated) - .then(next); - }, - - function trackerSetCSS(next) - { - var result = tracker1.setCSSText("<all.css TRACKER 1>", []); - InspectorTest.addResult("tracker1.setCSSText result: " + result); - var result = tracker2.setCSSText("<print.css TRACKER 2>", []); - InspectorTest.addResult("tracker2.setCSSText result: " + result); - requestContent([css1], [css2]) - .then(next); - }, - - function trackerSetSASSCommon(next) - { - var result = tracker1.setSASSText(sassCommon, "<common.scss TRACKER 1>"); - InspectorTest.addResult("tracker1.setSASSText result: " + result); - awaitSourceChanges(tracker2, sassCommon) - .then(checkOutdated) - .then(next); - }, - - function removeFirstTrackerStyleSheets(next) - { - InspectorTest.evaluateInPage("removeStyles(2)", next); - }, - - function removeSecondTrackerStyleSheets(next) - { - InspectorTest.evaluateInPage("removeStyles(1)", next); - }, - ]); - - function awaitSourceChanges(tracker, url) - { - var callback; - var promise = new Promise(fulfill => callback = fulfill); - tracker.addEventListener(WebInspector.SourceMapTracker.Events.SourceChanged, onSourceChanged); - return promise; - - function onSourceChanged(event) - { - if (event.data !== url) - return; - tracker.removeEventListener(WebInspector.SourceMapTracker.Events.SourceChanged, onSourceChanged); - callback(); - } - } - - function checkOutdated() - { - InspectorTest.addResult("== Outdated =="); - InspectorTest.addResult(tracker1.__NAME + ": " + tracker1.isOutdated()); - InspectorTest.addResult(tracker2.__NAME + ": " + tracker2.isOutdated()); - } - - function requestContent(urls1, urls2) - { - InspectorTest.addResult("== Contents =="); - urls1 = urls1 || []; - urls2 = urls2 || []; - var onContent = (url, content) => InspectorTest.addResult("content of " + url + ": " + content); - var promises = []; - for (var url of urls1) - promises.push(tracker1.content(url).then(text => onContent(url, text))); - for (var url of urls2) - promises.push(tracker2.content(url).then(text => onContent(url, text))); - return Promise.all(promises); - } -} - -</script> - -</head> - -<body onload="runTest()"> -<p>Verify SourceMapTracker functionality.</p> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/script-snippet-model-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/script-snippet-model-expected.txt index cbd7950fc..155411e 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/script-snippet-model-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/script-snippet-model-expected.txt
@@ -58,6 +58,8 @@ } Last evaluation source url for snippet: snippets:///1_3 Snippet execution result: undefined +Last evaluation source url for snippet: snippets:///3_4 +Snippet execution result: 1 Running: testEvaluateEditReload Snippet execution result: undefined
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/script-snippet-model.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/script-snippet-model.html index 1786ee9..82678cd 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/script-snippet-model.html +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger-ui/script-snippet-model.html
@@ -147,14 +147,14 @@ InspectorTest.addResult("Snippet3 created."); WebInspector.scriptSnippetModel.project().deleteFile(uiSourceCode3.url()); InspectorTest.addResult("Snippet3 deleted."); - + InspectorTest.addResult("Number of uiSourceCodes in workspace: " + workspace.uiSourceCodes().filter(filterSnippet).length); var storageSnippetsCount = WebInspector.scriptSnippetModel._snippetStorage.snippets().length; InspectorTest.addResult("Number of snippets in the storage: " + storageSnippetsCount); - + workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, uiSourceCodeAdded); workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, uiSourceCodeRemoved); - + next(); } }, @@ -163,13 +163,14 @@ { var uiSourceCode1; var uiSourceCode2; + var uiSoruceCode3; var context = WebInspector.context.flavor(WebInspector.ExecutionContext); resetSnippetsSettings(); var snippetScriptMapping = WebInspector.scriptSnippetModel.snippetScriptMapping(WebInspector.targetManager.targets()[0]); WebInspector.scriptSnippetModel.project().createFile("", null, "", step2.bind(this)); - + function step2(uiSourceCode) { uiSourceCode1 = uiSourceCode; @@ -180,7 +181,7 @@ uiSourceCode1.setWorkingCopy(content); WebInspector.scriptSnippetModel.project().createFile("", null, "", step3.bind(this)); } - + function step3(uiSourceCode) { uiSourceCode2 = uiSourceCode; @@ -192,17 +193,33 @@ content += "};\n"; content += "doesNothing;\n"; uiSourceCode2.setWorkingCopy(content); - evaluateSnippetAndDumpEvaluationDetails(uiSourceCode1, context, step4); + WebInspector.scriptSnippetModel.project().createFile("", null, "", step4.bind(this)); } - function step4() + function step4(uiSourceCode) { - evaluateSnippetAndDumpEvaluationDetails(uiSourceCode2, context, step5); + uiSourceCode3 = uiSourceCode; + uiSourceCode3.rename("Snippet3", function() { }); + content = ""; + content += "// This snippet uses Command Line API.\n"; + content += "$$(\"p\").length"; + uiSourceCode3.setWorkingCopy(content); + evaluateSnippetAndDumpEvaluationDetails(uiSourceCode1, context, step5); } function step5() { - evaluateSnippetAndDumpEvaluationDetails(uiSourceCode1, context, next); + evaluateSnippetAndDumpEvaluationDetails(uiSourceCode2, context, step6); + } + + function step6() + { + evaluateSnippetAndDumpEvaluationDetails(uiSourceCode1, context, step7); + } + + function step7() + { + evaluateSnippetAndDumpEvaluationDetails(uiSourceCode3, context, next); } }, @@ -226,7 +243,7 @@ var snippetScriptMapping = WebInspector.scriptSnippetModel.snippetScriptMapping(WebInspector.targetManager.targets()[0]); WebInspector.scriptSnippetModel.project().createFile("", null, "", step3.bind(this)); - + function step3(uiSourceCode) { var uiSourceCode1 = uiSourceCode;
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-aggregated-details-expected.txt b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-aggregated-details-expected.txt index 0fb2784a..701860b 100644 --- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-aggregated-details-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-aggregated-details-expected.txt
@@ -1,37 +1,37 @@ CallTree Group by: None - a: 0.000 8.380 - b: 0.000 8.380 - c: 7.476 7.476 - e: 0.000 0.905 - g: 0.905 0.905 - x: 0.000 0.882 - y: 0.482 0.882 + a: 0.000 11.125 + b: 0.000 11.125 + c: 8.250 8.250 + e: 0.000 2.875 + g: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + a: 9.900 10.000 + Layout: 0.100 0.100 + x: 0.000 1.000 + y: 0.600 1.000 z: 0.000 0.200 w: 0.200 0.200 w: 0.200 0.200 - f: 0.000 0.833 - l: 0.000 0.833 - a: 0.733 0.833 - Layout: 0.100 0.100 recursive_a: 0.101 0.215 recursive_b: 0.102 0.114 recursive_a: 0.004 0.012 recursive_b: 0.008 0.008 BottomUp Group by: None - c: 7.476 7.476 - b: 7.476 7.476 - a: 7.476 7.476 - g: 0.905 0.905 - e: 0.905 0.905 - b: 0.905 0.905 - a: 0.905 0.905 - a: 0.733 9.213 - l: 0.733 0.833 - f: 0.733 0.833 - y: 0.482 0.882 - x: 0.482 0.882 + a: 9.900 21.125 + l: 9.900 10.000 + f: 9.900 10.000 + c: 8.250 8.250 + b: 8.250 8.250 + a: 8.250 8.250 + g: 2.875 2.875 + e: 2.875 2.875 + b: 2.875 2.875 + a: 2.875 2.875 + y: 0.600 1.000 + x: 0.600 1.000 w: 0.400 0.400 z: 0.200 0.200 y: 0.200 0.200 @@ -51,18 +51,18 @@ f: 0.100 0.100 CallTree Group by: Category - Scripting: 0.101 10.310 - a: 0.000 8.380 - b: 0.000 8.380 - c: 7.476 7.476 - e: 0.000 0.905 - g: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - a: 0.733 0.833 + Scripting: 0.101 22.340 + a: 0.000 11.125 + b: 0.000 11.125 + c: 8.250 8.250 + e: 0.000 2.875 + g: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + a: 9.900 10.000 Layout: 0.100 0.100 - x: 0.000 0.882 - y: 0.482 0.882 + x: 0.000 1.000 + y: 0.600 1.000 z: 0.000 0.200 w: 0.200 0.200 w: 0.200 0.200 @@ -72,28 +72,28 @@ recursive_b: 0.008 0.008 BottomUp Group by: Category - Scripting: 10.210 10.210 - a: 0.733 9.213 - l: 0.733 0.833 - f: 0.733 0.833 - b: 0.000 8.380 - a: 0.000 8.380 - c: 7.476 7.476 - b: 7.476 7.476 - a: 7.476 7.476 - e: 0.000 0.905 - b: 0.000 0.905 - a: 0.000 0.905 - g: 0.905 0.905 - e: 0.905 0.905 - b: 0.905 0.905 - a: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - f: 0.000 0.833 - x: 0.000 0.882 - y: 0.482 0.882 - x: 0.482 0.882 + Scripting: 22.240 22.240 + a: 9.900 21.125 + l: 9.900 10.000 + f: 9.900 10.000 + b: 0.000 11.125 + a: 0.000 11.125 + c: 8.250 8.250 + b: 8.250 8.250 + a: 8.250 8.250 + e: 0.000 2.875 + b: 0.000 2.875 + a: 0.000 2.875 + g: 2.875 2.875 + e: 2.875 2.875 + b: 2.875 2.875 + a: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + f: 0.000 10.000 + x: 0.000 1.000 + y: 0.600 1.000 + x: 0.600 1.000 z: 0.000 0.200 y: 0.000 0.200 x: 0.000 0.200 @@ -117,18 +117,18 @@ f: 0.100 0.100 CallTree Group by: Domain - unattributed: 0.000 10.095 - a: 0.000 8.380 - b: 0.000 8.380 - c: 7.476 7.476 - e: 0.000 0.905 - g: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - a: 0.733 0.833 + unattributed: 0.000 22.125 + a: 0.000 11.125 + b: 0.000 11.125 + c: 8.250 8.250 + e: 0.000 2.875 + g: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + a: 9.900 10.000 Layout: 0.100 0.100 - x: 0.000 0.882 - y: 0.482 0.882 + x: 0.000 1.000 + y: 0.600 1.000 z: 0.000 0.200 w: 0.200 0.200 w: 0.200 0.200 @@ -139,32 +139,32 @@ recursive_b: 0.008 0.008 BottomUp Group by: Domain - unattributed: 10.095 10.095 - a: 0.733 9.213 - l: 0.733 0.833 - f: 0.733 0.833 - b: 0.000 8.380 - a: 0.000 8.380 - c: 7.476 7.476 - b: 7.476 7.476 - a: 7.476 7.476 - e: 0.000 0.905 - b: 0.000 0.905 - a: 0.000 0.905 - g: 0.905 0.905 - e: 0.905 0.905 - b: 0.905 0.905 - a: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - f: 0.000 0.833 + unattributed: 22.125 22.125 + a: 9.900 21.125 + l: 9.900 10.000 + f: 9.900 10.000 + b: 0.000 11.125 + a: 0.000 11.125 + c: 8.250 8.250 + b: 8.250 8.250 + a: 8.250 8.250 + e: 0.000 2.875 + b: 0.000 2.875 + a: 0.000 2.875 + g: 2.875 2.875 + e: 2.875 2.875 + b: 2.875 2.875 + a: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + f: 0.000 10.000 Layout: 0.100 0.100 a: 0.100 0.100 l: 0.100 0.100 f: 0.100 0.100 - x: 0.000 0.882 - y: 0.482 0.882 - x: 0.482 0.882 + x: 0.000 1.000 + y: 0.600 1.000 + x: 0.600 1.000 z: 0.000 0.200 y: 0.000 0.200 x: 0.000 0.200 @@ -184,18 +184,18 @@ recursive_a: 0.008 0.008 CallTree Group by: Subdomain - unattributed: 0.000 10.095 - a: 0.000 8.380 - b: 0.000 8.380 - c: 7.476 7.476 - e: 0.000 0.905 - g: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - a: 0.733 0.833 + unattributed: 0.000 22.125 + a: 0.000 11.125 + b: 0.000 11.125 + c: 8.250 8.250 + e: 0.000 2.875 + g: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + a: 9.900 10.000 Layout: 0.100 0.100 - x: 0.000 0.882 - y: 0.482 0.882 + x: 0.000 1.000 + y: 0.600 1.000 z: 0.000 0.200 w: 0.200 0.200 w: 0.200 0.200 @@ -206,32 +206,32 @@ recursive_b: 0.008 0.008 BottomUp Group by: Subdomain - unattributed: 10.095 10.095 - a: 0.733 9.213 - l: 0.733 0.833 - f: 0.733 0.833 - b: 0.000 8.380 - a: 0.000 8.380 - c: 7.476 7.476 - b: 7.476 7.476 - a: 7.476 7.476 - e: 0.000 0.905 - b: 0.000 0.905 - a: 0.000 0.905 - g: 0.905 0.905 - e: 0.905 0.905 - b: 0.905 0.905 - a: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - f: 0.000 0.833 + unattributed: 22.125 22.125 + a: 9.900 21.125 + l: 9.900 10.000 + f: 9.900 10.000 + b: 0.000 11.125 + a: 0.000 11.125 + c: 8.250 8.250 + b: 8.250 8.250 + a: 8.250 8.250 + e: 0.000 2.875 + b: 0.000 2.875 + a: 0.000 2.875 + g: 2.875 2.875 + e: 2.875 2.875 + b: 2.875 2.875 + a: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + f: 0.000 10.000 Layout: 0.100 0.100 a: 0.100 0.100 l: 0.100 0.100 f: 0.100 0.100 - x: 0.000 0.882 - y: 0.482 0.882 - x: 0.482 0.882 + x: 0.000 1.000 + y: 0.600 1.000 + x: 0.600 1.000 z: 0.000 0.200 y: 0.000 0.200 x: 0.000 0.200 @@ -251,18 +251,18 @@ recursive_a: 0.008 0.008 CallTree Group by: URL - unattributed: 0.000 10.095 - a: 0.000 8.380 - b: 0.000 8.380 - c: 7.476 7.476 - e: 0.000 0.905 - g: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - a: 0.733 0.833 + unattributed: 0.000 22.125 + a: 0.000 11.125 + b: 0.000 11.125 + c: 8.250 8.250 + e: 0.000 2.875 + g: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + a: 9.900 10.000 Layout: 0.100 0.100 - x: 0.000 0.882 - y: 0.482 0.882 + x: 0.000 1.000 + y: 0.600 1.000 z: 0.000 0.200 w: 0.200 0.200 w: 0.200 0.200 @@ -273,32 +273,32 @@ recursive_b: 0.008 0.008 BottomUp Group by: URL - unattributed: 10.095 10.095 - a: 0.733 9.213 - l: 0.733 0.833 - f: 0.733 0.833 - b: 0.000 8.380 - a: 0.000 8.380 - c: 7.476 7.476 - b: 7.476 7.476 - a: 7.476 7.476 - e: 0.000 0.905 - b: 0.000 0.905 - a: 0.000 0.905 - g: 0.905 0.905 - e: 0.905 0.905 - b: 0.905 0.905 - a: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - f: 0.000 0.833 + unattributed: 22.125 22.125 + a: 9.900 21.125 + l: 9.900 10.000 + f: 9.900 10.000 + b: 0.000 11.125 + a: 0.000 11.125 + c: 8.250 8.250 + b: 8.250 8.250 + a: 8.250 8.250 + e: 0.000 2.875 + b: 0.000 2.875 + a: 0.000 2.875 + g: 2.875 2.875 + e: 2.875 2.875 + b: 2.875 2.875 + a: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + f: 0.000 10.000 Layout: 0.100 0.100 a: 0.100 0.100 l: 0.100 0.100 f: 0.100 0.100 - x: 0.000 0.882 - y: 0.482 0.882 - x: 0.482 0.882 + x: 0.000 1.000 + y: 0.600 1.000 + x: 0.600 1.000 z: 0.000 0.200 y: 0.000 0.200 x: 0.000 0.200 @@ -318,24 +318,24 @@ recursive_a: 0.008 0.008 Events - Function Call: 20.787 30.000 - a: 0.000 0.226 - b: 0.000 0.226 - c: 0.226 0.226 - a: 0.000 8.155 - b: 0.000 8.155 + Function Call: 8.875 30.000 + a: 0.000 1.000 + b: 0.000 1.000 + c: 1.000 1.000 + a: 0.000 10.125 + b: 0.000 10.125 c: 7.250 7.250 - e: 0.000 0.905 - g: 0.905 0.905 - f: 0.000 0.833 - l: 0.000 0.833 - a: 0.733 0.833 + e: 0.000 2.875 + g: 2.875 2.875 + f: 0.000 10.000 + l: 0.000 10.000 + a: 9.900 10.000 Layout: 0.100 0.100 Install Timer: 0.000 0.000 Function Call: 8.785 10.000 - Function Call: 0.118 1.000 - x: 0.000 0.882 - y: 0.482 0.882 + Function Call: 0.000 1.000 + x: 0.000 1.000 + y: 0.600 1.000 z: 0.000 0.200 w: 0.200 0.200 w: 0.200 0.200
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js-callstacks-expected.txt b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js-callstacks-expected.txt index 613635b..87469cac 100644 --- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js-callstacks-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js-callstacks-expected.txt
@@ -1,23 +1,23 @@ Program: 200.000 / 300.000 FunctionCall: 210.000 / 30.000 -JSFrame: 211.000 / 0.226 a -JSFrame: 211.000 / 0.226 b -JSFrame: 211.000 / 0.226 c -JSFrame: 219.875 / 8.155 a -JSFrame: 219.875 / 8.155 b +JSFrame: 211.000 / 1.000 a +JSFrame: 211.000 / 1.000 b +JSFrame: 211.000 / 1.000 c +JSFrame: 219.875 / 10.125 a +JSFrame: 219.875 / 10.125 b JSFrame: 219.875 / 7.250 c InvalidateLayout: 220.000 / 7.000 InvalidateLayout: 221.000 / 3.000 -JSFrame: 227.125 / 0.905 e -JSFrame: 227.125 / 0.905 g -JSFrame: 230.000 / 0.833 f -JSFrame: 230.000 / 0.833 l -JSFrame: 230.000 / 0.833 a +JSFrame: 227.125 / 2.875 e +JSFrame: 227.125 / 2.875 g +JSFrame: 230.000 / 10.000 f +JSFrame: 230.000 / 10.000 l +JSFrame: 230.000 / 10.000 a Layout: 230.010 / 0.100 FunctionCall: 250.000 / 10.000 FunctionCall: 251.000 / 1.000 -JSFrame: 251.000 / 0.882 x -JSFrame: 251.000 / 0.882 y +JSFrame: 251.000 / 1.000 x +JSFrame: 251.000 / 1.000 y JSFrame: 251.000 / 0.200 z JSFrame: 251.000 / 0.200 w JSFrame: 251.200 / 0.200 w
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js-samping-tracing-expected.txt b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js-samping-tracing-expected.txt index aabb3dd..392ade47 100644 --- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js-samping-tracing-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js-samping-tracing-expected.txt
@@ -6,7 +6,7 @@ JSFrame: 220.000 / 8.125 c JSFrame: 228.125 / 1.875 e JSFrame: 228.125 / 1.875 g -JSFrame: 230.000 / 0.808 f -JSFrame: 230.000 / 0.808 l -JSFrame: 230.000 / 0.808 a +JSFrame: 230.000 / 10.000 f +JSFrame: 230.000 / 10.000 l +JSFrame: 230.000 / 10.000 a
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/trace-event-self-time-expected.txt b/third_party/WebKit/LayoutTests/inspector/tracing/trace-event-self-time-expected.txt index 7bbd875..02cedfe8 100644 --- a/third_party/WebKit/LayoutTests/inspector/tracing/trace-event-self-time-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/tracing/trace-event-self-time-expected.txt
@@ -1,19 +1,21 @@ +process_name: 0 0.00/0.00 +thread_name: 0 0.00/0.00 TracingStartedInPage: 100 0.00/0.00 Program: 200 50.00/100.00 InvalidateLayout: 210 0.00/0.00 Layout: 220 50.00/50.00 Program: 400 40.00/100.00 EventDispatch: 410 40.00/60.00 -FunctionCall: 420 1.69/10.00 -JSFrame: 421 4.79/8.31 +FunctionCall: 420 1.00/10.00 JSSample: 421 0.00/0.00 +JSFrame: 421 4.00/9.00 JSSample: 422 0.00/0.00 -JSFrame: 423 2.23/2.23 +JSFrame: 423 3.00/3.00 JSSample: 423 0.00/0.00 JSSample: 424 0.00/0.00 JSSample: 425 0.00/0.00 -JSFrame: 426 1.28/1.28 JSSample: 426 0.00/0.00 +JSFrame: 426 2.00/2.00 JSSample: 427 0.00/0.00 JSSample: 428 0.00/0.00 JSSample: 429 0.00/0.00
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-expected.txt b/third_party/WebKit/LayoutTests/media/color-profile-video-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image-expected.txt b/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image.html b/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image.html new file mode 100644 index 0000000..66806fc7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-poster-image.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html> +<head> +<style> + video { + outline: 15px solid blue; + border: 15px solid green; + background: orange; + margin: 20px; + padding: 15px; + width: 300px; + } +</style> +</head> + +<body> + <!-- The blue sector of the video poster image should be at 12 o'clock. --> + <video poster="../fast/images/resources/red-at-12-oclock-with-color-profile.jpg" /> +</body> + +<script> +window.onload = function() { + if (window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); +}; + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-expected.txt b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter-expected.txt b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter.html b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter.html new file mode 100644 index 0000000..708b7d3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-filter.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html> +<head> +<script src="media-file.js"></script> +<style> + .filter { + -webkit-filter: blur(2px); + } + + video { + outline: 15px solid blue; + border: 15px solid green; + background: orange; + padding: 15px; + margin: 20px; + width: 300px; + } +</style> +</head> + +<body> + <!-- This test passes if + 1) the video content and its surrounding borders are blurred + 2) the final colored blocks in the series of colored blocks in the + video content box area are respectively: cyan, blue, green + 3) the video seek point is near 00:00:00.24 --> + <video/> +</body> + +<script> +window.onload = function() { + var video = document.querySelector('video'); + + video.addEventListener('seeked', function() { + video.classList.add('filter'); + if (window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); + video.pause(); + }, false); + + setSrcByTagName('video', findMediaFile('video', 'content/test')); + seek(video, 1.0); +}; + +function seek(video, time) { + video.currentTime = time; +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit-expected.txt b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit-expected.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit-expected.txt
@@ -0,0 +1 @@ +
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit.html b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit.html new file mode 100644 index 0000000..846171b --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek-object-fit.html
@@ -0,0 +1,60 @@ +<!DOCTYPE html> +<html> +<head> +<script src="media-file.js"></script> +<style> + .test { + object-fit: contain; + height: 200px; + } + + video { + outline: 15px solid blue; + border: 15px solid green; + background: orange; + padding: 15px; + margin: 20px; + width: 300px; + } +</style> +</head> + +<body> + <!-- This test passes if: + 1) the final colored blocks in the series of colored blocks in the + video content box area are respectively: cyan, blue, green + 2) the video seek point is near 00:00:03.19 --> + <video class="test"/> +</body> + +<script> +window.onload = function() { + var video = document.querySelector('video'); + + video.addEventListener('seeked', function() { + if (window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); + video.pause(); + }, false); + + setSrcByTagName('video', findMediaFile('video', 'content/test')); + seek(video, 3.8); +}; + +function seek(video, time) { + video.currentTime = time; +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video-seek.html b/third_party/WebKit/LayoutTests/media/color-profile-video-seek.html new file mode 100644 index 0000000..b6a14af79 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video-seek.html
@@ -0,0 +1,55 @@ +<!DOCTYPE html> +<html> +<head> +<script src="media-file.js"></script> +<style> + video { + outline: 15px solid blue; + border: 15px solid green; + background: orange; + padding: 15px; + margin: 20px; + width: 300px; + } +</style> +</head> + +<body> + <!-- This test passes if: + 1) the final colored blocks in the series of colored blocks in the + video content box area are respectively: cyan, blue, green + 2) the video seek point is near 00:00:03.19 --> + <video/> +</body> + +<script> +window.onload = function() { + var video = document.querySelector('video'); + + video.addEventListener('seeked', function() { + if (window.testRunner) + setTimeout(function() { testRunner.setColorProfile('whacked', done) }, 100); + video.pause(); + }, false); + + setSrcByTagName('video', findMediaFile('video', 'content/test')); + seek(video, 3.8); +}; + +function seek(video, time) { + video.currentTime = time; +} + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} + +if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/media/color-profile-video.html b/third_party/WebKit/LayoutTests/media/color-profile-video.html new file mode 100644 index 0000000..beb531f5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/media/color-profile-video.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> +<head> +<script src="media-file.js"></script> +<style> + video { + outline: 15px solid blue; + border: 15px solid green; + background: orange; + padding: 15px; + margin: 20px; + width: 300px; + } +</style> +</head> + +<body> + <!-- This test passes if: + 1) the final colored blocks in the series of colored blocks in the + video content box area are respectively: cyan, blue, green + 2) the video seek point is near 00:000:00.00 --> + <video/> +</body> + +<script> +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.waitUntilDone(); +} + +window.onload = function() { + if (window.internals) + internals.settings.setImageColorProfilesEnabled(true); + + if (window.testRunner) { + document.querySelector('video').oncanplaythrough = function() { + testRunner.setColorProfile('whacked', done); + }; + } + + setSrcByTagName('video', findMediaFile('video', 'content/test')); +}; + +function done() { + setTimeout(function() { testRunner.notifyDone() }, 0); +} +</script> +</html>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/text-selection-focus-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/text-selection-focus-expected.txt new file mode 100644 index 0000000..c92ca87 --- /dev/null +++ b/third_party/WebKit/LayoutTests/paint/invalidation/text-selection-focus-expected.txt
@@ -0,0 +1,10 @@ +If this test passes, a frame was scheduled when focusing the window. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS scheduled is true +PASS successfullyParsed is true + +TEST COMPLETE +Selected area
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/text-selection-focus.html b/third_party/WebKit/LayoutTests/paint/invalidation/text-selection-focus.html new file mode 100644 index 0000000..8f22091 --- /dev/null +++ b/third_party/WebKit/LayoutTests/paint/invalidation/text-selection-focus.html
@@ -0,0 +1,31 @@ +<!doctype HTML> +<script src="../../resources/run-after-layout-and-paint.js"></script> +<script src="../selection/resources/selection.js"></script> +<script src="../../resources/js-test.js"></script> +<div id="container"> + Selected area +</div> +<script> +var jsTestIsAsync = true; +onload = function() { + description('If this test passes, a frame was scheduled when focusing the window.'); + + selectRange(container, 0, container, 1); + if (window.internals) + window.internals.setFocused(false); + + if (window.testRunner) { + window.testRunner.waitUntilDone(); + window.testRunner.dumpAsText(); + runAfterLayoutAndPaint(function() { + window.internals.setFocused(true); + window.scheduled = window.testRunner.animationScheduled(); + shouldBe('scheduled', 'true'); + finishJSTest(); + window.testRunner.notifyDone(); + + }); + } + +} +</script> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-expected.png b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-expected.png new file mode 100644 index 0000000..c5560be --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-expected.txt new file mode 100644 index 0000000..c633b9ab --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-expected.txt
@@ -0,0 +1,14 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x411 + LayoutBlockFlow {html} at (0,0) size 800x411 + LayoutBlockFlow {body} at (0,0) size 800x411 + LayoutSVGRoot {svg} at (0,0) size 406x406 + LayoutSVGContainer {g} at (303,303) size 100x100 + LayoutSVGEllipse {circle} at (303,303) size 100x100 [fill={[type=SOLID] [color=#008000]}] [cx=350.00] [cy=350.00] [r=50.00] + LayoutSVGText {text} at (56,35) size 288x19 contains 1 chunk(s) + LayoutSVGInlineText {#text} at (0,0) size 288x19 + chunk 1 (middle anchor) text run 1 at (56.00,50.00) startOffset 0 endOffset 49 width 288.00: "The circle should stay in the bottom-right corner" + LayoutText {#text} at (0,0) size 0x0 + LayoutText {#text} at (0,0) size 0x0 +caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-standalone-expected.png b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-standalone-expected.png new file mode 100644 index 0000000..c5560be --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-standalone-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-standalone-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-standalone-expected.txt new file mode 100644 index 0000000..47c44f0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-container-standalone-expected.txt
@@ -0,0 +1,10 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 406x406 + LayoutSVGRoot {svg} at (0,0) size 406x406 + LayoutSVGContainer {g} at (303,303) size 100x100 + LayoutSVGEllipse {circle} at (303,303) size 100x100 [fill={[type=SOLID] [color=#008000]}] [cx=350.00] [cy=350.00] [r=50.00] + LayoutSVGText {text} at (56,35) size 288x19 contains 1 chunk(s) + LayoutSVGInlineText {#text} at (0,0) size 288x19 + chunk 1 (middle anchor) text run 1 at (56.00,50.00) startOffset 0 endOffset 49 width 288.00: "The circle should stay in the bottom-right corner" +caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-root-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-root-expected.txt new file mode 100644 index 0000000..b2701ba --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-root-expected.txt
@@ -0,0 +1,13 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x411 + LayoutBlockFlow {html} at (0,0) size 800x411 + LayoutBlockFlow {body} at (0,0) size 800x411 + LayoutSVGRoot {svg} at (0,0) size 406x406 + LayoutSVGEllipse {circle} at (150,150) size 100x100 [fill={[type=SOLID] [color=#008000]}] [cx=197.00] [cy=197.00] [r=50.00] + LayoutSVGText {text} at (99,35) size 202x19 contains 1 chunk(s) + LayoutSVGInlineText {#text} at (0,0) size 201x19 + chunk 1 (middle anchor) text run 1 at (99.50,50.00) startOffset 0 endOffset 34 width 201.00: "The circle should be in the middle" + LayoutText {#text} at (0,0) size 0x0 + LayoutText {#text} at (0,0) size 0x0 +caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-root-standalone-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-root-standalone-expected.txt new file mode 100644 index 0000000..a2bbbc6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/custom/mouse-move-on-svg-root-standalone-expected.txt
@@ -0,0 +1,9 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 406x406 + LayoutSVGRoot {svg} at (0,0) size 406x406 + LayoutSVGEllipse {circle} at (150,150) size 100x100 [fill={[type=SOLID] [color=#008000]}] [cx=197.00] [cy=197.00] [r=50.00] + LayoutSVGText {text} at (99,35) size 202x19 contains 1 chunk(s) + LayoutSVGInlineText {#text} at (0,0) size 201x19 + chunk 1 (middle anchor) text run 1 at (99.50,50.00) startOffset 0 endOffset 34 width 201.00: "The circle should be in the middle" +caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/custom/text-hit-test-expected.txt b/third_party/WebKit/LayoutTests/platform/android/svg/custom/text-hit-test-expected.txt new file mode 100644 index 0000000..154a33b --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/android/svg/custom/text-hit-test-expected.txt
@@ -0,0 +1,15 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x600 + LayoutSVGRoot {svg} at (20,112) size 246x156 + LayoutSVGContainer {g} at (20,112) size 246x156 [transform={m=((2.00,0.00)(0.00,2.00)) t=(0.00,0.00)}] + LayoutSVGText {text} at (70,56) size 57x18 contains 1 chunk(s) + LayoutSVGInlineText {#text} at (0,0) size 57x18 + chunk 1 text run 1 at (70.00,70.00) startOffset 0 endOffset 8 width 57.00: "Click me" + LayoutSVGText {text} at (10,116) size 43x18 contains 1 chunk(s) + LayoutSVGInlineText {#text} at (0,0) size 43x18 + chunk 1 text run 1 at (10.00,130.00) startOffset 0 endOffset 7 width 42.50: "Status:" + LayoutSVGText {text} at (90,115) size 43x19 contains 1 chunk(s) + LayoutSVGInlineText {#text} at (0,0) size 43x19 + chunk 1 text run 1 at (90.00,130.00) startOffset 0 endOffset 6 width 43.00: "Passed" +caret: position 0 of child 0 {#text} of child 1 {text} of child 3 {g} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/replaced-min-max-001-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/replaced-min-max-001-expected.png index cc48bf6..6fed7cf 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/replaced-min-max-001-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/css2.1/20110323/replaced-min-max-001-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/background-color-image-border-radius-bleed-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/background-color-image-border-radius-bleed-expected.txt new file mode 100644 index 0000000..e34b0ca2b --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/background-color-image-border-radius-bleed-expected.txt
@@ -0,0 +1,7 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x121 + LayoutBlockFlow {HTML} at (0,0) size 800x121 + LayoutBlockFlow {BODY} at (8,8) size 784x105 + LayoutBlockFlow {SPAN} at (0,0) size 100x100 [bgcolor=#000000] + LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/background-multi-image-border-radius-bleed-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/background-multi-image-border-radius-bleed-expected.txt new file mode 100644 index 0000000..c1b28497 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/background-multi-image-border-radius-bleed-expected.txt
@@ -0,0 +1,7 @@ +layer at (0,0) size 800x600 + LayoutView at (0,0) size 800x600 +layer at (0,0) size 800x121 + LayoutBlockFlow {HTML} at (0,0) size 800x121 + LayoutBlockFlow {BODY} at (8,8) size 784x105 + LayoutBlockFlow {SPAN} at (0,0) size 100x100 + LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-repeat-round-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-repeat-round-expected.png index 42e5867..102ecd8 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-repeat-round-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-repeat-round-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-side-reduction-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-side-reduction-expected.png index 349f52c9..b85cdf0f 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-side-reduction-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-image-side-reduction-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-expected.png index 83d743e..d86da4e 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png index 4697d4f..5301c05 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/input-appearance-height-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/input-appearance-height-expected.png index fcffbb2..fb8c5a3d 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/input-appearance-height-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/input-appearance-height-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-animate-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-animate-expected.png new file mode 100644 index 0000000..dd97881a --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-animate-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-animate-rotate-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-animate-rotate-expected.png new file mode 100644 index 0000000..b2be355 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-animate-rotate-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-clip-text-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-clip-text-expected.png new file mode 100644 index 0000000..64097a35 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-clip-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cover-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cover-expected.png new file mode 100644 index 0000000..28c936c --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cover-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cross-fade-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cross-fade-expected.png new file mode 100644 index 0000000..4c4e0fb --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cross-fade-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cross-fade-png-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cross-fade-png-expected.png new file mode 100644 index 0000000..4c4e0fb --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-cross-fade-png-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-repeat-expected.png new file mode 100644 index 0000000..d67f0f4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-repeat-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-space-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-space-expected.png new file mode 100644 index 0000000..ecb909e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-background-image-space-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-fade-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-fade-expected.png new file mode 100644 index 0000000..8f7fd07 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-fade-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-image-expected.png new file mode 100644 index 0000000..dae1449 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-image-source-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-image-source-expected.png new file mode 100644 index 0000000..455f42d --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-image-source-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-radius-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-radius-expected.png new file mode 100644 index 0000000..67783ac --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-border-radius-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-clip-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-clip-expected.png new file mode 100644 index 0000000..dd3cfc5a --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-clip-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-drag-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-drag-image-expected.png new file mode 100644 index 0000000..cd0faac --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-drag-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-filter-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-filter-expected.png new file mode 100644 index 0000000..c6245c9e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-group-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-group-expected.png new file mode 100644 index 0000000..32f875f0f --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-group-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-iframe-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-iframe-expected.png new file mode 100644 index 0000000..bfc5e71 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-iframe-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-expected.png new file mode 100644 index 0000000..ac8b2a6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-pattern-expected.png new file mode 100644 index 0000000..3382565e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-pattern-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-svg-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-svg-expected.png new file mode 100644 index 0000000..179026a --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-canvas-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-expected.png new file mode 100644 index 0000000..138e066 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-filter-all-expected.png new file mode 100644 index 0000000..16146d0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-filter-all-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-object-fit-expected.png new file mode 100644 index 0000000..d66b9ae --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-object-fit-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-pseudo-content-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-pseudo-content-expected.png new file mode 100644 index 0000000..4df84c0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-pseudo-content-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-shape-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-shape-expected.png new file mode 100644 index 0000000..f40fd1f8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-shape-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-svg-resource-url-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-svg-resource-url-expected.png new file mode 100644 index 0000000..6ac448e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-image-svg-resource-url-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-layer-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-layer-expected.png new file mode 100644 index 0000000..34c5782 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-layer-filter-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-layer-filter-expected.png new file mode 100644 index 0000000..3ba7a33 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-layer-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-mask-image-svg-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-mask-image-svg-expected.png new file mode 100644 index 0000000..ebf444b6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-mask-image-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-object-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-object-expected.png new file mode 100644 index 0000000..4e0c25d --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-object-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-reflection-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-reflection-expected.png new file mode 100644 index 0000000..9ae76eb --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-reflection-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-expected.png new file mode 100644 index 0000000..942337e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-fill-text-expected.png new file mode 100644 index 0000000..6a012ac --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-fill-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-foreign-object-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-foreign-object-expected.png new file mode 100644 index 0000000..30096df --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/color-profile-svg-foreign-object-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-expected.png new file mode 100644 index 0000000..5f54aa7f --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-poster-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-poster-image-expected.png new file mode 100644 index 0000000..85390ec --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-poster-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-expected.png new file mode 100644 index 0000000..a7bde8c --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-filter-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-filter-expected.png new file mode 100644 index 0000000..04b88b21 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-object-fit-expected.png new file mode 100644 index 0000000..e1caf9ce --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/linux/media/color-profile-video-seek-object-fit-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png index cc6f0b1..8b29c39 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png index 67c9e4f9..6709f94 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png index f3edc41a..ae95f3e7 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-expected.png index c5560be..19ac5a6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-expected.txt index c633b9ab..9eec541 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-expected.txt
@@ -11,4 +11,5 @@ chunk 1 (middle anchor) text run 1 at (56.00,50.00) startOffset 0 endOffset 49 width 288.00: "The circle should stay in the bottom-right corner" LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body +selection start: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body +selection end: position 25 of child 0 {#text} of child 3 {text} of child 1 {svg} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-standalone-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-standalone-expected.png index c5560be..19ac5a6 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-standalone-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-standalone-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-standalone-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-standalone-expected.txt index 47c44f0..c55becd 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-standalone-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-container-standalone-expected.txt
@@ -7,4 +7,5 @@ LayoutSVGText {text} at (56,35) size 288x19 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 288x19 chunk 1 (middle anchor) text run 1 at (56.00,50.00) startOffset 0 endOffset 49 width 288.00: "The circle should stay in the bottom-right corner" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +selection start: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +selection end: position 25 of child 0 {#text} of child 3 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-root-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-root-expected.txt index b2701ba..4f59493 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-root-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-root-expected.txt
@@ -10,4 +10,4 @@ chunk 1 (middle anchor) text run 1 at (99.50,50.00) startOffset 0 endOffset 34 width 201.00: "The circle should be in the middle" LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body +caret: position 16 of child 0 {#text} of child 3 {text} of child 1 {svg} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-root-standalone-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-root-standalone-expected.txt index a2bbbc6..f79a8212 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-root-standalone-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/mouse-move-on-svg-root-standalone-expected.txt
@@ -6,4 +6,4 @@ LayoutSVGText {text} at (99,35) size 202x19 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 201x19 chunk 1 (middle anchor) text run 1 at (99.50,50.00) startOffset 0 endOffset 34 width 201.00: "The circle should be in the middle" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 16 of child 0 {#text} of child 3 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-image-css-transform-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-image-css-transform-expected.txt index 63417574..d5da100 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-image-css-transform-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-image-css-transform-expected.txt
@@ -359,4 +359,4 @@ LayoutSVGText {text} at (504,548) size 26x19 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 26x19 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 26.00: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-image-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-image-expected.txt index 51c8ff45..c746d385 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-image-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-image-expected.txt
@@ -359,4 +359,4 @@ LayoutSVGText {text} at (504,548) size 26x19 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 26x19 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 26.00: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-text-css-transform-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-text-css-transform-expected.txt index 5eb266a..5b5d5e5 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-text-css-transform-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-text-css-transform-expected.txt
@@ -519,4 +519,4 @@ LayoutSVGText {text} at (504,548) size 26x19 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 26x19 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 26.00: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-text-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-text-expected.txt index be68998..a322add 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-text-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pointer-events-text-expected.txt
@@ -519,4 +519,4 @@ LayoutSVGText {text} at (504,548) size 26x19 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 26x19 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 26.00: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/text-hit-test-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/text-hit-test-expected.txt index 154a33b..ebe63124 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/text-hit-test-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/text-hit-test-expected.txt
@@ -12,4 +12,4 @@ LayoutSVGText {text} at (90,115) size 43x19 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 43x19 chunk 1 text run 1 at (90.00,130.00) startOffset 0 endOffset 6 width 43.00: "Passed" -caret: position 0 of child 0 {#text} of child 1 {text} of child 3 {g} of child 1 {svg} of document +caret: position 3 of child 0 {#text} of child 1 {text} of child 3 {g} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1188-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1188-expected.png index 1b59f536..4ffcaf2 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1188-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1188-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1296-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1296-expected.png index f0121177..c414416 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1296-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1296-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1430-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1430-expected.png index e1f4967..e414d5b 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1430-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug1430-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4093-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4093-expected.png index d031f676..37308ab 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4093-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4093-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4427-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4427-expected.png index 480d43c0..e28d35c 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4427-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4427-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4523-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4523-expected.png index 407d3cd..b3b7f5505 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4523-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug4523-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug56563-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug56563-expected.png index 81be9c3..028bde2 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug56563-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug56563-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug625-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug625-expected.png index dbbf7fe..48467eb 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug625-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug625-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/bloomberg-expected.png index 9f3a679..f10f5d5 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/bloomberg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/bloomberg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/misc-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/misc-expected.png index e631e3d..f12ee875 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/misc-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/core/misc-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug6933-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug6933-expected.png index a6e1497..d1cd6368 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug6933-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug6933-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/forms/input-appearance-height-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/forms/input-appearance-height-expected.png index 668a858..e3ffd6b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/fast/forms/input-appearance-height-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/fast/forms/input-appearance-height-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/tables/mozilla/bugs/bug1188-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/tables/mozilla/bugs/bug1188-expected.png index 32f0e59..e8ee9f9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/tables/mozilla/bugs/bug1188-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/tables/mozilla/bugs/bug1188-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-lion/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/mac-lion/tables/mozilla/core/bloomberg-expected.png index c8838ba..44c97b7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-lion/tables/mozilla/core/bloomberg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-lion/tables/mozilla/core/bloomberg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/compositing/plugins/composited-plugin-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/compositing/plugins/composited-plugin-expected.png deleted file mode 100644 index 89146934a..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/compositing/plugins/composited-plugin-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/forms/input-appearance-height-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/forms/input-appearance-height-expected.png index 3594f70..b6ff0f2d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/forms/input-appearance-height-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/forms/input-appearance-height-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/images/color-profile-background-clip-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/images/color-profile-background-clip-text-expected.png new file mode 100644 index 0000000..1fb4adc4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/images/color-profile-background-clip-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/images/color-profile-svg-fill-text-expected.png new file mode 100644 index 0000000..8153fce --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/fast/images/color-profile-svg-fill-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/tables/mozilla/bugs/bug1188-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/tables/mozilla/bugs/bug1188-expected.png index 6d5d304..f435d93 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/tables/mozilla/bugs/bug1188-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/tables/mozilla/bugs/bug1188-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mavericks/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mavericks/tables/mozilla/core/bloomberg-expected.png index 3654291..5e587be9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mavericks/tables/mozilla/core/bloomberg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mavericks/tables/mozilla/core/bloomberg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mountainlion/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mountainlion/tables/mozilla/core/bloomberg-expected.png index dc293983e..e0811421 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mountainlion/tables/mozilla/core/bloomberg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mountainlion/tables/mozilla/core/bloomberg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/forms/input-appearance-height-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/forms/input-appearance-height-expected.png index 3f365e1..2e6a059 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/forms/input-appearance-height-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/fast/forms/input-appearance-height-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/scrollbars/listbox-scrollbar-combinations-expected.png index e9b8fd0b..e1d8380b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/tables/mozilla/bugs/bug1188-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/tables/mozilla/bugs/bug1188-expected.png index 6310a2d..6c7b91e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/tables/mozilla/bugs/bug1188-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/tables/mozilla/bugs/bug1188-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/tables/mozilla/core/bloomberg-expected.png index 7fe7f64..653164f7 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/tables/mozilla/core/bloomberg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/tables/mozilla/core/bloomberg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png index e9b8fd0b..e1d8380b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/virtual/rootlayerscrolls/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/virtual/rootlayerscrolls/scrollbars/listbox-scrollbar-combinations-expected.png index e9b8fd0b..e1d8380b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-snowleopard/virtual/rootlayerscrolls/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-snowleopard/virtual/rootlayerscrolls/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/1x1-composited-plugin-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/1x1-composited-plugin-expected.txt deleted file mode 100644 index 7b029bf..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/1x1-composited-plugin-expected.txt +++ /dev/null
@@ -1,2 +0,0 @@ - -{ "bounds": [800, 600], "children": [ { "bounds": [800, 600], "contentsOpaque": true, "drawsContent": true } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/composited-plugin-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/composited-plugin-expected.png deleted file mode 100644 index c2224dd..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/composited-plugin-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/composited-plugin-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/composited-plugin-expected.txt deleted file mode 100644 index bebca87..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/composited-plugin-expected.txt +++ /dev/null
@@ -1,8 +0,0 @@ -layer at (0,0) size 800x600 - LayoutView at (0,0) size 800x600 -layer at (0,0) size 800x170 - LayoutBlockFlow {HTML} at (0,0) size 800x170 - LayoutBlockFlow {BODY} at (8,8) size 784x154 - LayoutText {#text} at (0,0) size 0x0 -layer at (8,8) size 300x150 - LayoutEmbeddedObject {EMBED} at (0,0) size 300x150
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/large-to-small-composited-plugin-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/large-to-small-composited-plugin-expected.txt deleted file mode 100644 index 25aad9cd21..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/large-to-small-composited-plugin-expected.txt +++ /dev/null
@@ -1,2 +0,0 @@ - -{ "bounds": [800, 600], "children": [ { "bounds": [800, 600], "contentsOpaque": true, "drawsContent": true } ] }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/small-to-large-composited-plugin-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/small-to-large-composited-plugin-expected.txt deleted file mode 100644 index 9f9c614a9..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/compositing/plugins/small-to-large-composited-plugin-expected.txt +++ /dev/null
@@ -1,12 +0,0 @@ - -{ - "bounds": [800, 600], - "children": [ - { - "bounds": [800, 600], - "contentsOpaque": true, - "drawsContent": true - } - ] -} -
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/replaced-min-max-001-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/replaced-min-max-001-expected.png index 61bff28..e5402364 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/replaced-min-max-001-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css2.1/20110323/replaced-min-max-001-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-gradient-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-gradient-image-expected.png index 95aef897..352c8c5 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-gradient-image-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-gradient-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-color-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-color-expected.png index 262d345..a4e41b5 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-color-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-color-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-image-expected.png index a8078a0ed..ceea6eeb 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-image-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-svg-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-svg-expected.png index 2a58c7b..21ca715 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-svg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-image-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-tiled-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-tiled-gradient-expected.png index 3e1c5d3..dc41334 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-tiled-gradient-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/background-blend-mode-tiled-gradient-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-expected.png index 1b15ad2..983a9c2b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-stacking-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-stacking-expected.png index 66bd07e..0f47f00 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-stacking-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-stacking-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-tiled-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-tiled-expected.png index 7814edf..2d2441b9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-tiled-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/effect-background-blend-mode-tiled-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/inline-mask-overlay-image-outset-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/inline-mask-overlay-image-outset-expected.png index ee6894b..2fe6dd9 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/inline-mask-overlay-image-outset-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/inline-mask-overlay-image-outset-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png index 27ca2cc2..502b84af 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/input-appearance-height-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/input-appearance-height-expected.png index 726d693..ff12e79 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/input-appearance-height-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/input-appearance-height-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-animate-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-animate-expected.png new file mode 100644 index 0000000..dba42dd --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-animate-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-animate-rotate-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-animate-rotate-expected.png new file mode 100644 index 0000000..d883f43 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-animate-rotate-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-clip-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-clip-text-expected.png new file mode 100644 index 0000000..84ee67ac --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-clip-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cover-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cover-expected.png new file mode 100644 index 0000000..e3a18b57 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cover-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cross-fade-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cross-fade-expected.png new file mode 100644 index 0000000..748dbf80 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cross-fade-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cross-fade-png-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cross-fade-png-expected.png new file mode 100644 index 0000000..748dbf80 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-cross-fade-png-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-repeat-expected.png new file mode 100644 index 0000000..f7f4010 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-repeat-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-space-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-space-expected.png new file mode 100644 index 0000000..78a2f6b2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-background-image-space-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-fade-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-fade-expected.png new file mode 100644 index 0000000..4a29e65 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-fade-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-image-expected.png new file mode 100644 index 0000000..e3b1b1a --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-image-source-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-image-source-expected.png new file mode 100644 index 0000000..885fd1a --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-image-source-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-radius-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-radius-expected.png new file mode 100644 index 0000000..bee10c7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-border-radius-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-clip-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-clip-expected.png new file mode 100644 index 0000000..b81c491 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-clip-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-drag-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-drag-image-expected.png new file mode 100644 index 0000000..0f613502 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-drag-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-filter-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-filter-expected.png new file mode 100644 index 0000000..bb4a79d --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-group-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-group-expected.png new file mode 100644 index 0000000..44891249 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-group-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-iframe-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-iframe-expected.png new file mode 100644 index 0000000..163c2db --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-iframe-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-expected.png new file mode 100644 index 0000000..984597e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-pattern-expected.png new file mode 100644 index 0000000..1e95b59 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-pattern-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-svg-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-svg-expected.png new file mode 100644 index 0000000..2e2006d --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-canvas-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-expected.png new file mode 100644 index 0000000..86a33c7b --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-filter-all-expected.png new file mode 100644 index 0000000..8eb47624 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-filter-all-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-object-fit-expected.png new file mode 100644 index 0000000..1e5c4b8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-object-fit-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-profile-match-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-profile-match-expected.png new file mode 100644 index 0000000..80785a90 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-profile-match-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-pseudo-content-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-pseudo-content-expected.png new file mode 100644 index 0000000..d7289f14 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-pseudo-content-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-shape-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-shape-expected.png new file mode 100644 index 0000000..a468227 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-shape-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-svg-resource-url-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-svg-resource-url-expected.png new file mode 100644 index 0000000..5bf8720 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-image-svg-resource-url-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-layer-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-layer-expected.png new file mode 100644 index 0000000..122f3fe --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-layer-filter-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-layer-filter-expected.png new file mode 100644 index 0000000..f18c1add --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-layer-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-mask-image-svg-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-mask-image-svg-expected.png new file mode 100644 index 0000000..707c080 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-mask-image-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-object-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-object-expected.png new file mode 100644 index 0000000..e274945 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-object-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-reflection-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-reflection-expected.png new file mode 100644 index 0000000..ff28759 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-reflection-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-expected.png new file mode 100644 index 0000000..6dfe691 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-fill-text-expected.png new file mode 100644 index 0000000..3750cc5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-fill-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-foreign-object-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-foreign-object-expected.png new file mode 100644 index 0000000..ef9ed38 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/color-profile-svg-foreign-object-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-expected.png b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-expected.png new file mode 100644 index 0000000..5f54aa7f --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-poster-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-poster-image-expected.png new file mode 100644 index 0000000..d06619e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-poster-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-expected.png b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-expected.png new file mode 100644 index 0000000..a7bde8c --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-filter-expected.png b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-filter-expected.png new file mode 100644 index 0000000..04b88b21 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-object-fit-expected.png new file mode 100644 index 0000000..e1caf9ce --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-object-fit-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/WebKit/LayoutTests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png index b357b64..e1d8025 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png index caa0e8c..bdbf02f6 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png index 64ccb86..2e4a4ed 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png index 4b8252c6a..5365a41 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-container-expected.png index 35d69c4..2a1fbfb 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-container-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-container-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-container-standalone-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-container-standalone-expected.png index 35d69c4..2a1fbfb 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-container-standalone-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-container-standalone-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-root-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-root-expected.txt index 255dcc4c..c59fb6c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-root-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-root-expected.txt
@@ -10,4 +10,4 @@ chunk 1 (middle anchor) text run 1 at (90.70,50.00) startOffset 0 endOffset 34 width 218.60: "The circle should be in the middle" LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body +caret: position 16 of child 0 {#text} of child 3 {text} of child 1 {svg} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-root-standalone-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-root-standalone-expected.txt index 4f10974..7de61b7af 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-root-standalone-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/mouse-move-on-svg-root-standalone-expected.txt
@@ -6,4 +6,4 @@ LayoutSVGText {text} at (90,36) size 220x18 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 219x18 chunk 1 (middle anchor) text run 1 at (90.70,50.00) startOffset 0 endOffset 34 width 218.60: "The circle should be in the middle" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 16 of child 0 {#text} of child 3 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-image-css-transform-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-image-css-transform-expected.txt index b0c53bc..c01bdad 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-image-css-transform-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-image-css-transform-expected.txt
@@ -359,4 +359,4 @@ LayoutSVGText {text} at (504,549) size 30x18 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 30x18 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 29.34: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-image-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-image-expected.txt index 949f4f5..5212269 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-image-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-image-expected.txt
@@ -359,4 +359,4 @@ LayoutSVGText {text} at (504,549) size 30x18 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 30x18 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 29.34: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-text-css-transform-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-text-css-transform-expected.txt index df8b7eb..da3e1c8 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-text-css-transform-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-text-css-transform-expected.txt
@@ -519,4 +519,4 @@ LayoutSVGText {text} at (504,549) size 30x18 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 30x18 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 29.34: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-text-expected.txt index 701c327..a67ded69 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-text-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pointer-events-text-expected.txt
@@ -519,4 +519,4 @@ LayoutSVGText {text} at (504,549) size 30x18 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 30x18 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 29.34: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/text-hit-test-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/text-hit-test-expected.txt index faf9d8ed..9da5c24 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/text-hit-test-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/text-hit-test-expected.txt
@@ -12,4 +12,4 @@ LayoutSVGText {text} at (90,116) size 44x18 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 44x18 chunk 1 text run 1 at (90.00,130.00) startOffset 0 endOffset 6 width 43.55: "Passed" -caret: position 0 of child 0 {#text} of child 1 {text} of child 3 {g} of child 1 {svg} of document +caret: position 3 of child 0 {#text} of child 1 {text} of child 3 {g} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1188-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1188-expected.png index 951735c..9804770f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1188-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1188-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1296-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1296-expected.png index b49c86e..f94a311 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1296-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1296-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1430-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1430-expected.png index efa242b2..1e1191d 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1430-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug1430-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4093-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4093-expected.png index 79edcd87..b48f8ce 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4093-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4093-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4427-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4427-expected.png index 8d89591..cda97f4 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4427-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4427-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4523-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4523-expected.png index 8e00775..7646e424 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4523-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug4523-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug56563-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug56563-expected.png index 6bf1d385..dd59f22 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug56563-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug56563-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug625-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug625-expected.png index 1f6d03da..df8487a 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug625-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug625-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/bloomberg-expected.png index 0bba729..c854467 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/bloomberg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/bloomberg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/misc-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/misc-expected.png index cf96df07..2506c4f 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/misc-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/core/misc-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug6933-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug6933-expected.png index cc01609c..23242d37 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug6933-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug6933-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png index b357b64..e1d8025 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/rootlayerscrolls/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/rootlayerscrolls/scrollbars/listbox-scrollbar-combinations-expected.png index b357b64..e1d8025 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/virtual/rootlayerscrolls/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/rootlayerscrolls/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/replaced-min-max-001-expected.png b/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/replaced-min-max-001-expected.png index 85580d3..1ba4d7c5 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/replaced-min-max-001-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css2.1/20110323/replaced-min-max-001-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-gradient-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-gradient-image-expected.png index 850b5fa..320a7c0 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-gradient-image-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-gradient-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-color-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-color-expected.png index dac1218..5b09550 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-color-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-color-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-image-expected.png index e58849f..78c3a5a 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-image-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-svg-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-svg-expected.png index bedcc3f..34bda46 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-svg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-image-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-tiled-gradient-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-tiled-gradient-expected.png index b823c89e7..d9c54c36 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-tiled-gradient-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/background-blend-mode-tiled-gradient-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-expected.png index 4501b27d9..4c315bf 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-stacking-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-stacking-expected.png index d1eda7f..2fe59b8f 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-stacking-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-stacking-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-tiled-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-tiled-expected.png index 2d7fac4..6e5bbbc 100644 --- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-tiled-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/effect-background-blend-mode-tiled-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-expected.png index 1f9c6b2f..fb1cae99 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png index c03b345b..0101f08 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-vertical-rl-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/input-appearance-height-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/forms/input-appearance-height-expected.png index 179fef3..3caaacc 100644 --- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/input-appearance-height-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/input-appearance-height-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-animate-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-animate-expected.png new file mode 100644 index 0000000..f84991a --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-animate-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-animate-rotate-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-animate-rotate-expected.png new file mode 100644 index 0000000..a4bdbd9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-animate-rotate-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-clip-text-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-clip-text-expected.png new file mode 100644 index 0000000..ed0d4c8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-clip-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cover-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cover-expected.png new file mode 100644 index 0000000..69cd422 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cover-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cross-fade-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cross-fade-expected.png new file mode 100644 index 0000000..6b85c6f --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cross-fade-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cross-fade-png-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cross-fade-png-expected.png new file mode 100644 index 0000000..6b85c6f --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-cross-fade-png-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-repeat-expected.png new file mode 100644 index 0000000..4d503f9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-repeat-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-space-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-space-expected.png new file mode 100644 index 0000000..026b025 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-background-image-space-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-fade-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-fade-expected.png new file mode 100644 index 0000000..519a01e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-fade-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-image-expected.png new file mode 100644 index 0000000..250dabd5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-image-source-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-image-source-expected.png new file mode 100644 index 0000000..d6c4262e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-image-source-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-radius-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-radius-expected.png new file mode 100644 index 0000000..09f6a0d --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-border-radius-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-clip-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-clip-expected.png new file mode 100644 index 0000000..56c1bba --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-clip-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-drag-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-drag-image-expected.png new file mode 100644 index 0000000..b76adc4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-drag-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-filter-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-filter-expected.png new file mode 100644 index 0000000..d5579e9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-group-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-group-expected.png new file mode 100644 index 0000000..aaa76c13 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-group-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-iframe-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-iframe-expected.png new file mode 100644 index 0000000..42b30fb --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-iframe-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-expected.png new file mode 100644 index 0000000..77f79a1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-pattern-expected.png new file mode 100644 index 0000000..f38f9ee2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-pattern-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-svg-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-svg-expected.png new file mode 100644 index 0000000..1178172 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-canvas-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-expected.png new file mode 100644 index 0000000..1ce10f2e --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-filter-all-expected.png new file mode 100644 index 0000000..d85af35 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-filter-all-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-object-fit-expected.png new file mode 100644 index 0000000..1ae93cd --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-object-fit-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-profile-match-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-profile-match-expected.png new file mode 100644 index 0000000..ed764d06 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-profile-match-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-pseudo-content-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-pseudo-content-expected.png new file mode 100644 index 0000000..b9e1f91 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-pseudo-content-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-shape-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-shape-expected.png new file mode 100644 index 0000000..be1763b --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-shape-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-svg-resource-url-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-svg-resource-url-expected.png new file mode 100644 index 0000000..fa73d3b --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-image-svg-resource-url-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-layer-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-layer-expected.png new file mode 100644 index 0000000..218b032 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-layer-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-layer-filter-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-layer-filter-expected.png new file mode 100644 index 0000000..3632fd5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-layer-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-mask-image-svg-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-mask-image-svg-expected.png new file mode 100644 index 0000000..479d5ac5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-mask-image-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-object-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-object-expected.png new file mode 100644 index 0000000..c384be8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-object-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-reflection-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-reflection-expected.png new file mode 100644 index 0000000..9b42565 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-reflection-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-expected.png new file mode 100644 index 0000000..e12935c --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-fill-text-expected.png new file mode 100644 index 0000000..ec4ada56 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-fill-text-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-foreign-object-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-foreign-object-expected.png new file mode 100644 index 0000000..ad20470f --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/color-profile-svg-foreign-object-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-expected.png b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-expected.png new file mode 100644 index 0000000..a074936 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-poster-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-poster-image-expected.png new file mode 100644 index 0000000..14099e454 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-poster-image-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-expected.png b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-expected.png new file mode 100644 index 0000000..bc89584 --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-filter-expected.png b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-filter-expected.png new file mode 100644 index 0000000..1b3394df --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-filter-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-object-fit-expected.png new file mode 100644 index 0000000..98b8648d --- /dev/null +++ b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-object-fit-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png index 0f52fc6..aaa4c472 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-30-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png index 4548006..4c0e9ad 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-39-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png index 1ceff7e..a95a99d 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/animate-elem-40-t-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-container-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-container-expected.png index e0ff38b..26b97d35 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-container-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-container-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-container-standalone-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-container-standalone-expected.png index e0ff38b..26b97d35 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-container-standalone-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-container-standalone-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-root-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-root-expected.txt index 03f153a..f8f1713 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-root-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-root-expected.txt
@@ -10,4 +10,4 @@ chunk 1 (middle anchor) text run 1 at (90.70,50.00) startOffset 0 endOffset 34 width 218.60: "The circle should be in the middle" LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body +caret: position 16 of child 0 {#text} of child 3 {text} of child 1 {svg} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-root-standalone-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-root-standalone-expected.txt index 8f134fce..b38a490 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-root-standalone-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/mouse-move-on-svg-root-standalone-expected.txt
@@ -6,4 +6,4 @@ LayoutSVGText {text} at (90,36) size 220x17 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 219x17 chunk 1 (middle anchor) text run 1 at (90.70,50.00) startOffset 0 endOffset 34 width 218.60: "The circle should be in the middle" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 16 of child 0 {#text} of child 3 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-image-css-transform-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-image-css-transform-expected.txt index 8a539c1..15a4963 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-image-css-transform-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-image-css-transform-expected.txt
@@ -359,4 +359,4 @@ LayoutSVGText {text} at (504,549) size 30x17 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 30x17 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 29.34: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-image-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-image-expected.txt index 9e33652..b9bb86a1 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-image-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-image-expected.txt
@@ -359,4 +359,4 @@ LayoutSVGText {text} at (504,549) size 30x17 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 30x17 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 29.34: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-text-css-transform-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-text-css-transform-expected.txt index 3e06e47..e1c56d2 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-text-css-transform-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-text-css-transform-expected.txt
@@ -519,4 +519,4 @@ LayoutSVGText {text} at (504,549) size 30x17 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 30x17 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 29.34: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-text-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-text-expected.txt index e8572c8..2cd6d251 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-text-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pointer-events-text-expected.txt
@@ -519,4 +519,4 @@ LayoutSVGText {text} at (504,549) size 30x17 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 30x17 chunk 1 text run 1 at (504.00,563.00) startOffset 0 endOffset 4 width 29.34: "miss" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +caret: position 18 of child 0 {#text} of child 5 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/text-hit-test-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/text-hit-test-expected.txt index 42acf8d5..3137a68 100644 --- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/text-hit-test-expected.txt +++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/text-hit-test-expected.txt
@@ -12,4 +12,4 @@ LayoutSVGText {text} at (90,116) size 44x17 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 44x17 chunk 1 text run 1 at (90.00,130.00) startOffset 0 endOffset 6 width 43.55: "Passed" -caret: position 0 of child 0 {#text} of child 1 {text} of child 3 {g} of child 1 {svg} of document +caret: position 3 of child 0 {#text} of child 1 {text} of child 3 {g} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1188-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1188-expected.png index 9a1b1af..a755cd1 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1188-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1188-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1296-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1296-expected.png index e84733d..e5a2c21 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1296-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1296-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1430-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1430-expected.png index 540bc29..440a920 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1430-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug1430-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4093-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4093-expected.png index 697bc71..d6a7d36 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4093-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4093-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4427-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4427-expected.png index ffa74e8e..4713461 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4427-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4427-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4523-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4523-expected.png index d811fb1..b3c0ea7 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4523-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug4523-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug56563-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug56563-expected.png index 603429b..9ded1d88 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug56563-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug56563-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug625-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug625-expected.png index f391224..239c461 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug625-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug625-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/bloomberg-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/bloomberg-expected.png index 401cd07..2f9f4be4 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/bloomberg-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/bloomberg-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/misc-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/misc-expected.png index 59fcd91..ccbf0a69 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/misc-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/core/misc-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug6933-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug6933-expected.png index 19da758e..d0505f8 100644 --- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug6933-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug6933-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/scrollbars/reconstruct-coordinated-scrollbar-expected.html b/third_party/WebKit/LayoutTests/scrollbars/reconstruct-coordinated-scrollbar-expected.html new file mode 100644 index 0000000..ed25ac7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/scrollbars/reconstruct-coordinated-scrollbar-expected.html
@@ -0,0 +1,28 @@ +<!DOCTYPE html> +<script src="../resources/run-after-layout-and-paint.js"></script> +<style> +.box { + display: inline-block; + position: relative; + width: 200px; + height: 200px; + overflow: auto; + will-change: transform; +} +.space { + width: 800px; + height: 800px; +} +</style> + +This test verifies that composited scrollbars update correctly when removed and +reinserted with a different content size.<br><br> + +It passes if both boxes have identical scrollbars.<br><br> + +<div class="box"> + <div class="space"></div> +</div> +<div class="box"> + <div class="space"></div> +</div>
diff --git a/third_party/WebKit/LayoutTests/scrollbars/reconstruct-coordinated-scrollbar.html b/third_party/WebKit/LayoutTests/scrollbars/reconstruct-coordinated-scrollbar.html new file mode 100644 index 0000000..a964927 --- /dev/null +++ b/third_party/WebKit/LayoutTests/scrollbars/reconstruct-coordinated-scrollbar.html
@@ -0,0 +1,54 @@ +<!DOCTYPE html> +<script src="../resources/run-after-layout-and-paint.js"></script> +<style> +.box { + display: inline-block; + position: relative; + width: 200px; + height: 200px; + overflow: auto; + will-change: transform; +} +#space1 { + width: 300px; + height: 300px; +} +#space2 { + width: 800px; + height: 800px; +} +</style> + +This test verifies that composited scrollbars update correctly when removed and +reinserted with a different content size.<br><br> + +It passes if both boxes have identical scrollbars.<br><br> + +<div class="box"> + <div id="space1"></div> +</div> +<div class="box"> + <div id="space2"></div> +</div> +<script> + +if (window.testRunner) + testRunner.waitUntilDone(); + +runAfterLayoutAndPaint(function() { + var space = document.querySelector("#space1"); + + space.style.height = "0"; + space.style.width = "0"; + + // Force layout (but not compositing update). + // Scrollbars are destroyed. + space.clientWidth; + + space.style.height = "800px"; + space.style.width = "800px"; + + if (window.testRunner) + testRunner.notifyDone(); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/svg/css/getComputedStyle-listing-expected.txt b/third_party/WebKit/LayoutTests/svg/css/getComputedStyle-listing-expected.txt index d40eea9..9a57df3 100644 --- a/third_party/WebKit/LayoutTests/svg/css/getComputedStyle-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/svg/css/getComputedStyle-listing-expected.txt
@@ -22,16 +22,6 @@ -webkit-box-pack: start -webkit-box-reflect: none -webkit-clip-path: none --webkit-column-break-after: auto --webkit-column-break-before: auto --webkit-column-break-inside: auto --webkit-column-count: auto --webkit-column-gap: normal --webkit-column-rule-color: rgb(0, 0, 0) --webkit-column-rule-style: none --webkit-column-rule-width: 0px --webkit-column-span: none --webkit-column-width: auto -webkit-filter: none -webkit-font-smoothing: auto -webkit-highlight: none @@ -124,6 +114,9 @@ bottom: auto box-shadow: none box-sizing: content-box +break-after: auto +break-before: auto +break-inside: auto buffered-rendering: auto caption-side: top clear: none @@ -134,6 +127,13 @@ color-interpolation: sRGB color-interpolation-filters: linearRGB color-rendering: auto +column-count: auto +column-gap: normal +column-rule-color: rgb(0, 0, 0) +column-rule-style: none +column-rule-width: 0px +column-span: none +column-width: auto content: cursor: auto cx: 0px @@ -224,9 +224,6 @@ padding-left: 0px padding-right: 0px padding-top: 0px -page-break-after: auto -page-break-before: auto -page-break-inside: auto paint-order: fill stroke markers perspective: none perspective-origin: 0px 0px
diff --git a/third_party/WebKit/LayoutTests/svg/custom/mouse-move-on-svg-container-expected.txt b/third_party/WebKit/LayoutTests/svg/custom/mouse-move-on-svg-container-expected.txt index 1736a9d..4394c8c 100644 --- a/third_party/WebKit/LayoutTests/svg/custom/mouse-move-on-svg-container-expected.txt +++ b/third_party/WebKit/LayoutTests/svg/custom/mouse-move-on-svg-container-expected.txt
@@ -11,4 +11,5 @@ chunk 1 (middle anchor) text run 1 at (44.71,50.00) startOffset 0 endOffset 49 width 310.58: "The circle should stay in the bottom-right corner" LayoutText {#text} at (0,0) size 0x0 LayoutText {#text} at (0,0) size 0x0 -caret: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body +selection start: position 0 of child 0 {#text} of child 3 {text} of child 1 {svg} of body +selection end: position 24 of child 0 {#text} of child 3 {text} of child 1 {svg} of body
diff --git a/third_party/WebKit/LayoutTests/svg/custom/mouse-move-on-svg-container-standalone-expected.txt b/third_party/WebKit/LayoutTests/svg/custom/mouse-move-on-svg-container-standalone-expected.txt index 9450d02..111492c2 100644 --- a/third_party/WebKit/LayoutTests/svg/custom/mouse-move-on-svg-container-standalone-expected.txt +++ b/third_party/WebKit/LayoutTests/svg/custom/mouse-move-on-svg-container-standalone-expected.txt
@@ -7,4 +7,5 @@ LayoutSVGText {text} at (44,36) size 312x18 contains 1 chunk(s) LayoutSVGInlineText {#text} at (0,0) size 311x18 chunk 1 (middle anchor) text run 1 at (44.71,50.00) startOffset 0 endOffset 49 width 310.58: "The circle should stay in the bottom-right corner" -caret: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +selection start: position 0 of child 0 {#text} of child 3 {text} of child 0 {svg} of document +selection end: position 24 of child 0 {#text} of child 3 {text} of child 0 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/svg/custom/svg-fonts-in-html.html b/third_party/WebKit/LayoutTests/svg/custom/svg-fonts-in-html.html index dacb66f..ab78652 100644 --- a/third_party/WebKit/LayoutTests/svg/custom/svg-fonts-in-html.html +++ b/third_party/WebKit/LayoutTests/svg/custom/svg-fonts-in-html.html
@@ -78,8 +78,6 @@ a, acronym { text-decoration: none; text-transform: none; border: none } #quickSummary, #supportingText, #linkList { display: none } - - #preamble { columns: 2 } </style> </head>
diff --git a/third_party/WebKit/LayoutTests/svg/custom/use-nested-disallowed-target-on-inner-expected.svg b/third_party/WebKit/LayoutTests/svg/custom/use-nested-disallowed-target-on-inner-expected.svg new file mode 100644 index 0000000..c8d8b9b --- /dev/null +++ b/third_party/WebKit/LayoutTests/svg/custom/use-nested-disallowed-target-on-inner-expected.svg
@@ -0,0 +1,2 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg"></svg>
diff --git a/third_party/WebKit/LayoutTests/svg/custom/use-nested-disallowed-target-on-inner.svg b/third_party/WebKit/LayoutTests/svg/custom/use-nested-disallowed-target-on-inner.svg new file mode 100644 index 0000000..a47c3701 --- /dev/null +++ b/third_party/WebKit/LayoutTests/svg/custom/use-nested-disallowed-target-on-inner.svg
@@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <use xlink:href="#use"> + <use xlink:href="#use2" id="use"> + <dd> + <aa id="use2"> + <aaa></aaa> + </aa> + </dd> + </use> + </use> +</svg>
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug97383-expected.png b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug97383-expected.png index c3343fb786..c628a382 100644 --- a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug97383-expected.png +++ b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug97383-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt index 068579d..c41d545 100644 --- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt +++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -66,6 +66,9 @@ bottom boxShadow boxSizing +breakAfter +breakBefore +breakInside bufferedRendering captionSide clear @@ -76,6 +79,16 @@ colorInterpolation colorInterpolationFilters colorRendering +columnCount +columnFill +columnGap +columnRule +columnRuleColor +columnRuleStyle +columnRuleWidth +columnSpan +columnWidth +columns content counterIncrement counterReset @@ -272,15 +285,6 @@ webkitColumnBreakAfter webkitColumnBreakBefore webkitColumnBreakInside -webkitColumnCount -webkitColumnGap -webkitColumnRule -webkitColumnRuleColor -webkitColumnRuleStyle -webkitColumnRuleWidth -webkitColumnSpan -webkitColumnWidth -webkitColumns webkitFilter webkitFontSizeDelta webkitFontSmoothing
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt index 4d16860..0d18d71 100644 --- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -647,7 +647,6 @@ getter contentType getter cookie getter currentScript - getter defaultCharset getter defaultView getter designMode getter dir @@ -4590,6 +4589,22 @@ getter workerStart method constructor setter onerror +interface SourceBuffer : EventTarget + getter appendWindowEnd + getter appendWindowStart + getter buffered + getter timestampOffset + getter updating + method abort + method appendBuffer + method constructor + method remove + setter appendWindowEnd + setter appendWindowStart + setter timestampOffset +interface SourceBufferList : EventTarget + getter length + method constructor interface SpeechSynthesisEvent : Event getter charIndex getter elapsedTime @@ -5838,9 +5853,9 @@ attribute BluetoothAdvertisingData attribute BluetoothCharacteristicProperties attribute BluetoothDevice - attribute BluetoothGATTCharacteristic - attribute BluetoothGATTRemoteServer - attribute BluetoothGATTService + attribute BluetoothRemoteGATTCharacteristic + attribute BluetoothRemoteGATTServer + attribute BluetoothRemoteGATTService attribute BluetoothUUID attribute GCController attribute accessibilityController
diff --git a/third_party/WebKit/LayoutTests/web-animations-api/animation-id.html b/third_party/WebKit/LayoutTests/web-animations-api/animation-id.html new file mode 100644 index 0000000..7a85fecf --- /dev/null +++ b/third_party/WebKit/LayoutTests/web-animations-api/animation-id.html
@@ -0,0 +1,9 @@ +<!DOCTYPE html> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<script> +test(function() { + var animation = document.documentElement.animate([], { id: 'testId' }); + assert_equals(animation.id, 'testId'); +}, 'Tests that animation id is set when specified from keyframe options'); +</script>
diff --git a/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-byte-data-expected.txt b/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-byte-data-expected.txt new file mode 100644 index 0000000..c906b223 --- /dev/null +++ b/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-byte-data-expected.txt
@@ -0,0 +1,14 @@ +Test AnalyserNode getByteTimeDomainData + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS Index of first sample greater than +1 is greater than or equal to 0. +PASS Index of first sample less than -1 is greater than or equal to 0. +PASS Clip 1.01149: byteData[43] is equal to 255. +PASS Clip -1.42637: byteData[56] is equal to 0. +PASS Byte data is identical to the array [128,130,135,136,140,143,145,149,152,153,159,160,163,167,168,173...]. +PASS successfullyParsed is true + +TEST COMPLETE +
diff --git a/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-byte-data.html b/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-byte-data.html new file mode 100644 index 0000000..270c5f9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/webaudio/realtimeanalyser-byte-data.html
@@ -0,0 +1,101 @@ +<!doctype html> +<html> + <head> + <script src="../resources/js-test.js"></script> + <script src="resources/compatibility.js"></script> + <script src="resources/audio-testing.js"></script> + <title>Test Analyser.getByteTimeDomainData()</title> + </head> + + <body> + <script> + description("Test AnalyserNode getByteTimeDomainData"); + window.jsTestIsAsync = true; + + var sampleRate = 48000; + // The size of the analyser frame. Anything larger than 128 is ok, but should be long enough + // to capture the peaks of the oscillator waveform. + var fftSize = 256; + // Number of frames to render. Should be greater than the fftSize, but is otherwise + // arbitrary. + var renderFrames = 2 * fftSize; + + var audit = Audit.createTaskRunner(); + + // Test that getByteTimeDomainData returns the correct values. This test depends on + // getFloatTimeDomainData returning the correct data (for which there is already a test). + audit.defineTask("byte-data", function (done) { + var context = new OfflineAudioContext(1, renderFrames, sampleRate); + + // Create a sawtooth as the signal under test. A sine wave or triangle wave would probably + // also work. + var src = context.createOscillator(); + src.type = "sawtooth"; + // Choose a frequency high enough that we get at least a full period in one analyser fftSize + // frame. Otherwise, the frequency is arbitrary. + src.frequency.value = 440; + + // Gain node to make sure the signal goes somewhat above 1, for testing clipping. + var gain = context.createGain(); + gain.gain.value = 1.5; + + // The analyser node to test + var analyser = context.createAnalyser(); + analyser.fftSize = fftSize; + + // Connect the graph. + src.connect(gain); + gain.connect(analyser); + analyser.connect(context.destination); + + // Stop rendering after one analyser frame so we can grab the data. + context.suspend(fftSize / sampleRate).then(function () { + var floatData = new Float32Array(fftSize); + var byteData = new Uint8Array(fftSize); + + analyser.getFloatTimeDomainData(floatData); + analyser.getByteTimeDomainData(byteData); + + // Use the float data to compute the expected value for the byte data. + var expected = new Float32Array(fftSize); + for (var k = 0; k < fftSize; ++k) { + // It's important to do Math.fround to match the single-precision float in the + // implementation! + var value = Math.fround(128 * Math.fround(1 + floatData[k])); + // Clip the result to lie in the range [0, 255]. + expected[k] = Math.floor(Math.min(255, Math.max(0, value))); + } + + // Find the first index of the first sample that exceeds +1 or -1. The test MUST have at + // least one such value. + var indexMax = floatData.findIndex(function (x) { return x > 1; }); + var indexMin = floatData.findIndex(function (x) { return x < -1; }); + + Should("Index of first sample greater than +1", indexMax).beGreaterThanOrEqualTo(0); + Should("Index of first sample less than -1", indexMin).beGreaterThanOrEqualTo(0); + + // Verify explicitly that clipping happened correctly at the above indices. + Should("Clip " + floatData[indexMax].toPrecision(6) + ": byteData[" + indexMax + "]", + byteData[indexMax]).beEqualTo(255); + Should("Clip " + floatData[indexMin].toPrecision(6) + ": byteData[" + indexMin + "]", + byteData[indexMin]).beEqualTo(0); + + // Verify that all other samples are computed correctly. + Should("Byte data", byteData, { + verbose: true + }).beEqualToArray(expected); + }).then(context.resume.bind(context)) + + src.start(); + context.startRendering().then(done); + }); + + audit.defineTask("finish", function (done) { + finishJSTest(); + done(); + }); + + audit.runTasks(); + </script> + </body> +</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/audio-testing.js b/third_party/WebKit/LayoutTests/webaudio/resources/audio-testing.js index f2f6182..fecd719 100644 --- a/third_party/WebKit/LayoutTests/webaudio/resources/audio-testing.js +++ b/third_party/WebKit/LayoutTests/webaudio/resources/audio-testing.js
@@ -756,8 +756,12 @@ } else { var counter = 0; var failureMessage = 'is not equal to the array [' + arrStr + ']'; + if (this.verbose) + failureMessage += '\nindex\tActual\t\tExpected'; for (var index in mismatches) { failureMessage += '\n[' + index + '] : ' + mismatches[index]; + if (this.verbose) + failureMessage += '\t' + array[index]; if (++counter >= this.NUM_ERRORS_LOG) { failureMessage += '\nand ' + (numberOfmismatches - counter) + ' more differences...';
diff --git a/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt index 95ff9e05..a943b04 100644 --- a/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/css-properties-as-js-properties-expected.txt
@@ -67,6 +67,9 @@ bottom boxShadow boxSizing +breakAfter +breakBefore +breakInside bufferedRendering captionSide clear @@ -77,7 +80,16 @@ colorInterpolation colorInterpolationFilters colorRendering +columnCount columnFill +columnGap +columnRule +columnRuleColor +columnRuleStyle +columnRuleWidth +columnSpan +columnWidth +columns contain content counterIncrement @@ -315,15 +327,6 @@ webkitColumnBreakAfter webkitColumnBreakBefore webkitColumnBreakInside -webkitColumnCount -webkitColumnGap -webkitColumnRule -webkitColumnRuleColor -webkitColumnRuleStyle -webkitColumnRuleWidth -webkitColumnSpan -webkitColumnWidth -webkitColumns webkitFilter webkitFontSizeDelta webkitFontSmoothing
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 5006df5..31a9c9f 100644 --- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt +++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -321,7 +321,7 @@ method connectGATT method constructor setter ongattserverdisconnected -interface BluetoothGATTCharacteristic : EventTarget +interface BluetoothRemoteGATTCharacteristic : EventTarget getter oncharacteristicvaluechanged getter properties getter uuid @@ -332,14 +332,14 @@ method stopNotifications method writeValue setter oncharacteristicvaluechanged -interface BluetoothGATTRemoteServer +interface BluetoothRemoteGATTServer getter connected getter device method connect method constructor method disconnect method getPrimaryService -interface BluetoothGATTService +interface BluetoothRemoteGATTService getter isPrimary getter uuid method constructor @@ -966,7 +966,6 @@ getter contentType getter cookie getter currentScript - getter defaultCharset getter defaultView getter designMode getter dir @@ -5237,6 +5236,27 @@ getter ax getter ay method constructor +interface SourceBuffer : EventTarget + getter appendWindowEnd + getter appendWindowStart + getter buffered + getter mode + getter timestampOffset + getter trackDefaults + getter updating + method abort + method appendBuffer + method appendStream + method constructor + method remove + setter appendWindowEnd + setter appendWindowStart + setter mode + setter timestampOffset + setter trackDefaults +interface SourceBufferList : EventTarget + getter length + method constructor interface SpeechSynthesisEvent : Event getter charIndex getter elapsedTime @@ -6336,6 +6356,9 @@ method terminate setter onerror setter onmessage +interface Worklet + method constructor + method import interface XMLDocument : Document method constructor interface XMLHttpRequest : XMLHttpRequestEventTarget @@ -6850,6 +6873,7 @@ attribute performance attribute personalbar attribute propertyNamesInGlobal + attribute renderWorklet attribute screen attribute screenLeft attribute screenTop
diff --git a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt index 19f1292..36c5ed80 100644 --- a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt +++ b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.txt
@@ -93,7 +93,6 @@ SetWrapperReferenceTo=* SetterCallWith=ExecutionContext|ScriptArguments|ActiveWindow|FirstWindow TreatNullAs=NullString|EmptyString -TreatReturnedNullStringAs=Null|Undefined TreatUndefinedAs=NullString URL Unforgeable
diff --git a/third_party/WebKit/Source/bindings/core/v8/DictionaryHelperForCore.cpp b/third_party/WebKit/Source/bindings/core/v8/DictionaryHelperForCore.cpp index c728205d7..a7e2596 100644 --- a/third_party/WebKit/Source/bindings/core/v8/DictionaryHelperForCore.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/DictionaryHelperForCore.cpp
@@ -312,98 +312,4 @@ template CORE_EXPORT bool DictionaryHelper::get(const Dictionary&, const String& key, RefPtr<DOMUint8Array>& value); -template <typename T> -struct IntegralTypeTraits { -}; - -template <> -struct IntegralTypeTraits<uint8_t> { - static inline uint8_t toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toUInt8(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "UInt8"; } -}; - -template <> -struct IntegralTypeTraits<int8_t> { - static inline int8_t toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toInt8(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "Int8"; } -}; - -template <> -struct IntegralTypeTraits<unsigned short> { - static inline uint16_t toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toUInt16(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "UInt16"; } -}; - -template <> -struct IntegralTypeTraits<short> { - static inline int16_t toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toInt16(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "Int16"; } -}; - -template <> -struct IntegralTypeTraits<unsigned> { - static inline uint32_t toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toUInt32(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "UInt32"; } -}; - -template <> -struct IntegralTypeTraits<unsigned long> { - static inline uint32_t toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toUInt32(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "UInt32"; } -}; - -template <> -struct IntegralTypeTraits<int> { - static inline int32_t toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toInt32(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "Int32"; } -}; - -template <> -struct IntegralTypeTraits<long> { - static inline int32_t toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toInt32(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "Int32"; } -}; - -template <> -struct IntegralTypeTraits<unsigned long long> { - static inline unsigned long long toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toUInt64(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "UInt64"; } -}; - -template <> -struct IntegralTypeTraits<long long> { - static inline long long toIntegral(v8::Isolate* isolate, v8::Local<v8::Value> value, IntegerConversionConfiguration configuration, ExceptionState& exceptionState) - { - return toInt64(isolate, value, configuration, exceptionState); - } - static const String typeName() { return "Int64"; } -}; - } // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h index 7cad8c0..75c6ba90 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h +++ b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
@@ -184,16 +184,6 @@ } template<typename CallbackInfo> -inline void v8SetReturnValueStringOrUndefined(const CallbackInfo& info, const String& string, v8::Isolate* isolate) -{ - if (string.isNull()) { - v8SetReturnValueUndefined(info); - return; - } - V8PerIsolateData::from(isolate)->stringCache()->setReturnValueFromString(info.GetReturnValue(), string.impl()); -} - -template<typename CallbackInfo> inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ScriptWrappable* impl, v8::Local<v8::Object> creationContext) { if (UNLIKELY(!impl)) {
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8LazyEventListener.h b/third_party/WebKit/Source/bindings/core/v8/V8LazyEventListener.h index 3ea1c8c..b3a11bf 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8LazyEventListener.h +++ b/third_party/WebKit/Source/bindings/core/v8/V8LazyEventListener.h
@@ -56,6 +56,8 @@ V8AbstractEventListener::trace(visitor); } + const String& code() const { return m_code; } + protected: void prepareListenerObject(ExecutionContext*) override;
diff --git a/third_party/WebKit/Source/bindings/core/v8/custom/V8WindowCustom.cpp b/third_party/WebKit/Source/bindings/core/v8/custom/V8WindowCustom.cpp index 70dc3e3..ff53c3dc 100644 --- a/third_party/WebKit/Source/bindings/core/v8/custom/V8WindowCustom.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/custom/V8WindowCustom.cpp
@@ -411,7 +411,7 @@ return BindingSecurity::shouldAllowAccessTo(isolate, callingDOMWindow(isolate), targetWindow, DoNotReportSecurityError); } -bool V8Window::securityCheckCustom(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject) +bool V8Window::securityCheckCustom(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject, v8::Local<v8::Value> data) { // TODO(jochen): Take accessingContext into account. return securityCheck(accessedObject);
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_types.py b/third_party/WebKit/Source/bindings/scripts/v8_types.py index 939dda0..04739d2 100644 --- a/third_party/WebKit/Source/bindings/scripts/v8_types.py +++ b/third_party/WebKit/Source/bindings/scripts/v8_types.py
@@ -770,14 +770,7 @@ if idl_type.is_string_type: if idl_type.is_nullable: return 'StringOrNull' - if 'TreatReturnedNullStringAs' not in extended_attributes: - return base_idl_type - treat_returned_null_string_as = extended_attributes['TreatReturnedNullStringAs'] - if treat_returned_null_string_as == 'Null': - return 'StringOrNull' - if treat_returned_null_string_as == 'Undefined': - return 'StringOrUndefined' - raise 'Unrecognized TreatReturnedNullStringAs value: "%s"' % treat_returned_null_string_as + return base_idl_type if idl_type.is_basic_type or base_idl_type == 'ScriptValue': return base_idl_type # Generic dictionary type @@ -801,9 +794,7 @@ 'DOMString': 'v8SetReturnValueString(info, {cpp_value}, info.GetIsolate())', 'ByteString': 'v8SetReturnValueString(info, {cpp_value}, info.GetIsolate())', 'USVString': 'v8SetReturnValueString(info, {cpp_value}, info.GetIsolate())', - # [TreatReturnedNullStringAs] 'StringOrNull': 'v8SetReturnValueStringOrNull(info, {cpp_value}, info.GetIsolate())', - 'StringOrUndefined': 'v8SetReturnValueStringOrUndefined(info, {cpp_value}, info.GetIsolate())', 'void': '', # No special v8SetReturnValue* function (set value directly) 'float': 'v8SetReturnValue(info, {cpp_value})', @@ -890,9 +881,7 @@ 'double': 'v8::Number::New({isolate}, {cpp_value})', 'unrestricted double': 'v8::Number::New({isolate}, {cpp_value})', 'void': 'v8Undefined()', - # [TreatReturnedNullStringAs] 'StringOrNull': '{cpp_value}.isNull() ? v8::Local<v8::Value>(v8::Null({isolate})) : v8String({isolate}, {cpp_value})', - 'StringOrUndefined': '{cpp_value}.isNull() ? v8Undefined() : v8String({isolate}, {cpp_value})', # Special cases 'Dictionary': '{cpp_value}.v8Value()', 'EventHandler': '{cpp_value} ? v8::Local<v8::Value>(V8AbstractEventListener::cast({cpp_value})->getListenerObject(impl->executionContext())) : v8::Local<v8::Value>(v8::Null({isolate}))',
diff --git a/third_party/WebKit/Source/bindings/templates/interface.h b/third_party/WebKit/Source/bindings/templates/interface.h index 70a37b8d..091bb29 100644 --- a/third_party/WebKit/Source/bindings/templates/interface.h +++ b/third_party/WebKit/Source/bindings/templates/interface.h
@@ -157,7 +157,7 @@ static const int internalFieldCount = v8DefaultWrapperInternalFieldCount + {{custom_internal_field_counter}}; {# End custom internal fields #} {% if interface_name == 'Window' %} - static bool securityCheckCustom(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject); + static bool securityCheckCustom(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject, v8::Local<v8::Value> value); {% endif %} static void installConditionallyEnabledProperties(v8::Local<v8::Object>, v8::Isolate*){% if has_conditional_attributes %}; {% else %} { }
diff --git a/third_party/WebKit/Source/bindings/templates/interface_base.cpp b/third_party/WebKit/Source/bindings/templates/interface_base.cpp index f890f49..69ea7a8 100644 --- a/third_party/WebKit/Source/bindings/templates/interface_base.cpp +++ b/third_party/WebKit/Source/bindings/templates/interface_base.cpp
@@ -124,7 +124,7 @@ {##############################################################################} {% block security_check_functions %} {% if has_access_check_callbacks %} -bool securityCheck(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject) +bool securityCheck(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject, v8::Local<v8::Value> data) { // TODO(jochen): Take accessingContext into account. {{cpp_class}}* impl = {{v8_class}}::toImpl(accessedObject);
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl index caa59e28..4e37b827 100644 --- a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl +++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
@@ -250,13 +250,6 @@ [SetterCallWith=ExecutionContext] attribute DOMString setterCallWithExecutionContextStringAttribute; [TreatNullAs=EmptyString] attribute DOMString treatNullAsEmptyStringStringAttribute; [TreatNullAs=NullString] attribute DOMString treatNullAsNullStringStringAttribute; - [TreatReturnedNullStringAs=Null] attribute DOMString treatReturnedNullStringAsNullStringAttribute; - [TreatReturnedNullStringAs=Undefined] attribute DOMString treatReturnedNullStringAsUndefinedStringAttribute; - [TreatReturnedNullStringAs=Undefined, CachedAttribute=isStringDirty] attribute DOMString cachedTreatReturnedNullStringAsUndefinedStringAttribute; - [TreatReturnedNullStringAs=Null] attribute ByteString treatReturnedNullStringAsNullByteStringAttribute; - [TreatReturnedNullStringAs=Undefined] attribute ByteString treatReturnedNullStringAsUndefinedByteStringAttribute; - [TreatReturnedNullStringAs=Null] attribute USVString treatReturnedNullStringAsNullUSVStringAttribute; - [TreatReturnedNullStringAs=Undefined] attribute USVString treatReturnedNullStringAsUndefinedUSVStringAttribute; [LegacyInterfaceTypeChecking] attribute float legacyInterfaceTypeCheckingFloatAttribute; // nop for non-interface types [LegacyInterfaceTypeChecking] attribute TestInterface legacyInterfaceTypeCheckingTestInterfaceAttribute; [LegacyInterfaceTypeChecking] attribute TestInterface? legacyInterfaceTypeCheckingTestInterfaceOrNullAttribute; @@ -578,12 +571,6 @@ [RuntimeEnabled=FeatureName2] void partiallyRuntimeEnabledOverloadedVoidMethod(TestInterface testInterfaceArg); void partiallyRuntimeEnabledOverloadedVoidMethod(long longArg, DOMString stringArg); [RuntimeEnabled=FeatureName3] void partiallyRuntimeEnabledOverloadedVoidMethod(long longArg, DOMString stringArg, TestInterface testInterfaceArg); - [TreatReturnedNullStringAs=Null] DOMString treatReturnedNullStringAsNullStringMethod(); - [TreatReturnedNullStringAs=Undefined] DOMString treatReturnedNullStringAsUndefinedStringMethod(); - [TreatReturnedNullStringAs=Null] ByteString treatReturnedNullStringAsNullByteStringMethod(); - [TreatReturnedNullStringAs=Undefined] ByteString treatReturnedNullStringAsUndefinedByteStringMethod(); - [TreatReturnedNullStringAs=Null] USVString treatReturnedNullStringAsNullUSVStringMethod(); - [TreatReturnedNullStringAs=Undefined] USVString treatReturnedNullStringAsUndefinedUSVStringMethod(); [LegacyInterfaceTypeChecking] void legacyInterfaceTypeCheckingVoidMethodTestInterfaceEmptyArg(TestInterfaceEmpty testInterfaceEmptyArg); [LegacyInterfaceTypeChecking] void legacyInterfaceTypeCheckingVoidMethodTestInterfaceEmptyVariadicArg(TestInterfaceEmpty... testInterfaceEmptyArg); // Avoid redundant type checking
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp index 745fc95..0c12c1a 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
@@ -200,7 +200,7 @@ TestInterfaceCheckSecurityV8Internal::doNotCheckSecurityReplaceableReadonlyLongAttributeAttributeSetter(v8Value, info); } -bool securityCheck(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject) +bool securityCheck(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject, v8::Local<v8::Value> data) { // TODO(jochen): Take accessingContext into account. TestInterfaceCheckSecurity* impl = V8TestInterfaceCheckSecurity::toImpl(accessedObject);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp index 0d7656e..88f60b5 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -4325,218 +4325,6 @@ TestObjectV8Internal::treatNullAsNullStringStringAttributeAttributeSetter(v8Value, info); } -static void treatReturnedNullStringAsNullStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - v8SetReturnValueStringOrNull(info, impl->treatReturnedNullStringAsNullStringAttribute(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsNullStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsNullStringAttributeAttributeGetter(info); -} - -static void treatReturnedNullStringAsNullStringAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - V8StringResource<> cppValue = v8Value; - if (!cppValue.prepare()) - return; - impl->setTreatReturnedNullStringAsNullStringAttribute(cppValue); -} - -static void treatReturnedNullStringAsNullStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Value> v8Value = info[0]; - TestObjectV8Internal::treatReturnedNullStringAsNullStringAttributeAttributeSetter(v8Value, info); -} - -static void treatReturnedNullStringAsUndefinedStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - v8SetReturnValueStringOrUndefined(info, impl->treatReturnedNullStringAsUndefinedStringAttribute(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsUndefinedStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsUndefinedStringAttributeAttributeGetter(info); -} - -static void treatReturnedNullStringAsUndefinedStringAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - V8StringResource<> cppValue = v8Value; - if (!cppValue.prepare()) - return; - impl->setTreatReturnedNullStringAsUndefinedStringAttribute(cppValue); -} - -static void treatReturnedNullStringAsUndefinedStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Value> v8Value = info[0]; - TestObjectV8Internal::treatReturnedNullStringAsUndefinedStringAttributeAttributeSetter(v8Value, info); -} - -static void cachedTreatReturnedNullStringAsUndefinedStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - v8::Local<v8::String> propertyName = v8AtomicString(info.GetIsolate(), "cachedTreatReturnedNullStringAsUndefinedStringAttribute"); - TestObject* impl = V8TestObject::toImpl(holder); - if (!impl->isStringDirty()) { - v8::Local<v8::Value> v8Value = V8HiddenValue::getHiddenValue(ScriptState::current(info.GetIsolate()), holder, propertyName); - if (!v8Value.IsEmpty() && !v8Value->IsUndefined()) { - v8SetReturnValue(info, v8Value); - return; - } - } - String cppValue(impl->cachedTreatReturnedNullStringAsUndefinedStringAttribute()); - v8::Local<v8::Value> v8Value(cppValue.isNull() ? v8Undefined() : v8String(info.GetIsolate(), cppValue)); - V8HiddenValue::setHiddenValue(ScriptState::current(info.GetIsolate()), holder, propertyName, v8Value); - v8SetReturnValue(info, v8Value); -} - -static void cachedTreatReturnedNullStringAsUndefinedStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::cachedTreatReturnedNullStringAsUndefinedStringAttributeAttributeGetter(info); -} - -static void cachedTreatReturnedNullStringAsUndefinedStringAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - V8StringResource<> cppValue = v8Value; - if (!cppValue.prepare()) - return; - impl->setCachedTreatReturnedNullStringAsUndefinedStringAttribute(cppValue); - V8HiddenValue::deleteHiddenValue(ScriptState::current(info.GetIsolate()), holder, v8AtomicString(info.GetIsolate(), "cachedTreatReturnedNullStringAsUndefinedStringAttribute")); // Invalidate the cached value. -} - -static void cachedTreatReturnedNullStringAsUndefinedStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Value> v8Value = info[0]; - TestObjectV8Internal::cachedTreatReturnedNullStringAsUndefinedStringAttributeAttributeSetter(v8Value, info); -} - -static void treatReturnedNullStringAsNullByteStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - v8SetReturnValueStringOrNull(info, impl->treatReturnedNullStringAsNullByteStringAttribute(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsNullByteStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsNullByteStringAttributeAttributeGetter(info); -} - -static void treatReturnedNullStringAsNullByteStringAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - ExceptionState exceptionState(ExceptionState::SetterContext, "treatReturnedNullStringAsNullByteStringAttribute", "TestObject", holder, info.GetIsolate()); - TestObject* impl = V8TestObject::toImpl(holder); - V8StringResource<> cppValue = toByteString(info.GetIsolate(), v8Value, exceptionState); - if (exceptionState.throwIfNeeded()) - return; - impl->setTreatReturnedNullStringAsNullByteStringAttribute(cppValue); -} - -static void treatReturnedNullStringAsNullByteStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Value> v8Value = info[0]; - TestObjectV8Internal::treatReturnedNullStringAsNullByteStringAttributeAttributeSetter(v8Value, info); -} - -static void treatReturnedNullStringAsUndefinedByteStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - v8SetReturnValueStringOrUndefined(info, impl->treatReturnedNullStringAsUndefinedByteStringAttribute(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsUndefinedByteStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsUndefinedByteStringAttributeAttributeGetter(info); -} - -static void treatReturnedNullStringAsUndefinedByteStringAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - ExceptionState exceptionState(ExceptionState::SetterContext, "treatReturnedNullStringAsUndefinedByteStringAttribute", "TestObject", holder, info.GetIsolate()); - TestObject* impl = V8TestObject::toImpl(holder); - V8StringResource<> cppValue = toByteString(info.GetIsolate(), v8Value, exceptionState); - if (exceptionState.throwIfNeeded()) - return; - impl->setTreatReturnedNullStringAsUndefinedByteStringAttribute(cppValue); -} - -static void treatReturnedNullStringAsUndefinedByteStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Value> v8Value = info[0]; - TestObjectV8Internal::treatReturnedNullStringAsUndefinedByteStringAttributeAttributeSetter(v8Value, info); -} - -static void treatReturnedNullStringAsNullUSVStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - v8SetReturnValueStringOrNull(info, impl->treatReturnedNullStringAsNullUSVStringAttribute(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsNullUSVStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsNullUSVStringAttributeAttributeGetter(info); -} - -static void treatReturnedNullStringAsNullUSVStringAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - ExceptionState exceptionState(ExceptionState::SetterContext, "treatReturnedNullStringAsNullUSVStringAttribute", "TestObject", holder, info.GetIsolate()); - TestObject* impl = V8TestObject::toImpl(holder); - V8StringResource<> cppValue = toUSVString(info.GetIsolate(), v8Value, exceptionState); - if (exceptionState.throwIfNeeded()) - return; - impl->setTreatReturnedNullStringAsNullUSVStringAttribute(cppValue); -} - -static void treatReturnedNullStringAsNullUSVStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Value> v8Value = info[0]; - TestObjectV8Internal::treatReturnedNullStringAsNullUSVStringAttributeAttributeSetter(v8Value, info); -} - -static void treatReturnedNullStringAsUndefinedUSVStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - TestObject* impl = V8TestObject::toImpl(holder); - v8SetReturnValueStringOrUndefined(info, impl->treatReturnedNullStringAsUndefinedUSVStringAttribute(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsUndefinedUSVStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsUndefinedUSVStringAttributeAttributeGetter(info); -} - -static void treatReturnedNullStringAsUndefinedUSVStringAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Object> holder = info.Holder(); - ExceptionState exceptionState(ExceptionState::SetterContext, "treatReturnedNullStringAsUndefinedUSVStringAttribute", "TestObject", holder, info.GetIsolate()); - TestObject* impl = V8TestObject::toImpl(holder); - V8StringResource<> cppValue = toUSVString(info.GetIsolate(), v8Value, exceptionState); - if (exceptionState.throwIfNeeded()) - return; - impl->setTreatReturnedNullStringAsUndefinedUSVStringAttribute(cppValue); -} - -static void treatReturnedNullStringAsUndefinedUSVStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - v8::Local<v8::Value> v8Value = info[0]; - TestObjectV8Internal::treatReturnedNullStringAsUndefinedUSVStringAttributeAttributeSetter(v8Value, info); -} - static void legacyInterfaceTypeCheckingFloatAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Local<v8::Object> holder = info.Holder(); @@ -10948,72 +10736,6 @@ TestObjectV8Internal::partiallyRuntimeEnabledOverloadedVoidMethodMethod(info); } -static void treatReturnedNullStringAsNullStringMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObject* impl = V8TestObject::toImpl(info.Holder()); - v8SetReturnValueStringOrNull(info, impl->treatReturnedNullStringAsNullStringMethod(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsNullStringMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsNullStringMethodMethod(info); -} - -static void treatReturnedNullStringAsUndefinedStringMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObject* impl = V8TestObject::toImpl(info.Holder()); - v8SetReturnValueStringOrUndefined(info, impl->treatReturnedNullStringAsUndefinedStringMethod(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsUndefinedStringMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsUndefinedStringMethodMethod(info); -} - -static void treatReturnedNullStringAsNullByteStringMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObject* impl = V8TestObject::toImpl(info.Holder()); - v8SetReturnValueStringOrNull(info, impl->treatReturnedNullStringAsNullByteStringMethod(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsNullByteStringMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsNullByteStringMethodMethod(info); -} - -static void treatReturnedNullStringAsUndefinedByteStringMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObject* impl = V8TestObject::toImpl(info.Holder()); - v8SetReturnValueStringOrUndefined(info, impl->treatReturnedNullStringAsUndefinedByteStringMethod(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsUndefinedByteStringMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsUndefinedByteStringMethodMethod(info); -} - -static void treatReturnedNullStringAsNullUSVStringMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObject* impl = V8TestObject::toImpl(info.Holder()); - v8SetReturnValueStringOrNull(info, impl->treatReturnedNullStringAsNullUSVStringMethod(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsNullUSVStringMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsNullUSVStringMethodMethod(info); -} - -static void treatReturnedNullStringAsUndefinedUSVStringMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObject* impl = V8TestObject::toImpl(info.Holder()); - v8SetReturnValueStringOrUndefined(info, impl->treatReturnedNullStringAsUndefinedUSVStringMethod(), info.GetIsolate()); -} - -static void treatReturnedNullStringAsUndefinedUSVStringMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - TestObjectV8Internal::treatReturnedNullStringAsUndefinedUSVStringMethodMethod(info); -} - static void legacyInterfaceTypeCheckingVoidMethodTestInterfaceEmptyArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { if (UNLIKELY(info.Length() < 1)) { @@ -12015,13 +11737,6 @@ {"setterCallWithExecutionContextStringAttribute", TestObjectV8Internal::setterCallWithExecutionContextStringAttributeAttributeGetterCallback, TestObjectV8Internal::setterCallWithExecutionContextStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, {"treatNullAsEmptyStringStringAttribute", TestObjectV8Internal::treatNullAsEmptyStringStringAttributeAttributeGetterCallback, TestObjectV8Internal::treatNullAsEmptyStringStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, {"treatNullAsNullStringStringAttribute", TestObjectV8Internal::treatNullAsNullStringStringAttributeAttributeGetterCallback, TestObjectV8Internal::treatNullAsNullStringStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, - {"treatReturnedNullStringAsNullStringAttribute", TestObjectV8Internal::treatReturnedNullStringAsNullStringAttributeAttributeGetterCallback, TestObjectV8Internal::treatReturnedNullStringAsNullStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, - {"treatReturnedNullStringAsUndefinedStringAttribute", TestObjectV8Internal::treatReturnedNullStringAsUndefinedStringAttributeAttributeGetterCallback, TestObjectV8Internal::treatReturnedNullStringAsUndefinedStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, - {"cachedTreatReturnedNullStringAsUndefinedStringAttribute", TestObjectV8Internal::cachedTreatReturnedNullStringAsUndefinedStringAttributeAttributeGetterCallback, TestObjectV8Internal::cachedTreatReturnedNullStringAsUndefinedStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, - {"treatReturnedNullStringAsNullByteStringAttribute", TestObjectV8Internal::treatReturnedNullStringAsNullByteStringAttributeAttributeGetterCallback, TestObjectV8Internal::treatReturnedNullStringAsNullByteStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, - {"treatReturnedNullStringAsUndefinedByteStringAttribute", TestObjectV8Internal::treatReturnedNullStringAsUndefinedByteStringAttributeAttributeGetterCallback, TestObjectV8Internal::treatReturnedNullStringAsUndefinedByteStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, - {"treatReturnedNullStringAsNullUSVStringAttribute", TestObjectV8Internal::treatReturnedNullStringAsNullUSVStringAttributeAttributeGetterCallback, TestObjectV8Internal::treatReturnedNullStringAsNullUSVStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, - {"treatReturnedNullStringAsUndefinedUSVStringAttribute", TestObjectV8Internal::treatReturnedNullStringAsUndefinedUSVStringAttributeAttributeGetterCallback, TestObjectV8Internal::treatReturnedNullStringAsUndefinedUSVStringAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, {"legacyInterfaceTypeCheckingFloatAttribute", TestObjectV8Internal::legacyInterfaceTypeCheckingFloatAttributeAttributeGetterCallback, TestObjectV8Internal::legacyInterfaceTypeCheckingFloatAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, {"legacyInterfaceTypeCheckingTestInterfaceAttribute", TestObjectV8Internal::legacyInterfaceTypeCheckingTestInterfaceAttributeAttributeGetterCallback, TestObjectV8Internal::legacyInterfaceTypeCheckingTestInterfaceAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, {"legacyInterfaceTypeCheckingTestInterfaceOrNullAttribute", TestObjectV8Internal::legacyInterfaceTypeCheckingTestInterfaceOrNullAttributeAttributeGetterCallback, TestObjectV8Internal::legacyInterfaceTypeCheckingTestInterfaceOrNullAttributeAttributeSetterCallback, 0, 0, 0, v8::DEFAULT, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype, V8DOMConfiguration::CheckHolder}, @@ -12261,12 +11976,6 @@ {"raisesExceptionTestInterfaceEmptyVoidMethod", TestObjectV8Internal::raisesExceptionTestInterfaceEmptyVoidMethodMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, {"raisesExceptionXPathNSResolverVoidMethod", TestObjectV8Internal::raisesExceptionXPathNSResolverVoidMethodMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, {"callWithExecutionContextRaisesExceptionVoidMethodLongArg", TestObjectV8Internal::callWithExecutionContextRaisesExceptionVoidMethodLongArgMethodCallback, 0, 1, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, - {"treatReturnedNullStringAsNullStringMethod", TestObjectV8Internal::treatReturnedNullStringAsNullStringMethodMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, - {"treatReturnedNullStringAsUndefinedStringMethod", TestObjectV8Internal::treatReturnedNullStringAsUndefinedStringMethodMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, - {"treatReturnedNullStringAsNullByteStringMethod", TestObjectV8Internal::treatReturnedNullStringAsNullByteStringMethodMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, - {"treatReturnedNullStringAsUndefinedByteStringMethod", TestObjectV8Internal::treatReturnedNullStringAsUndefinedByteStringMethodMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, - {"treatReturnedNullStringAsNullUSVStringMethod", TestObjectV8Internal::treatReturnedNullStringAsNullUSVStringMethodMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, - {"treatReturnedNullStringAsUndefinedUSVStringMethod", TestObjectV8Internal::treatReturnedNullStringAsUndefinedUSVStringMethodMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, {"legacyInterfaceTypeCheckingVoidMethodTestInterfaceEmptyArg", TestObjectV8Internal::legacyInterfaceTypeCheckingVoidMethodTestInterfaceEmptyArgMethodCallback, 0, 1, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, {"legacyInterfaceTypeCheckingVoidMethodTestInterfaceEmptyVariadicArg", TestObjectV8Internal::legacyInterfaceTypeCheckingVoidMethodTestInterfaceEmptyVariadicArgMethodCallback, 0, 0, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype}, {"useToImpl4ArgumentsCheckingIfPossibleWithOptionalArg", TestObjectV8Internal::useToImpl4ArgumentsCheckingIfPossibleWithOptionalArgMethodCallback, 0, 1, v8::None, V8DOMConfiguration::ExposedToAllScripts, V8DOMConfiguration::OnPrototype},
diff --git a/third_party/WebKit/Source/build/scripts/templates/StyleBuilderFunctions.cpp.tmpl b/third_party/WebKit/Source/build/scripts/templates/StyleBuilderFunctions.cpp.tmpl index f6bf5bf..af9d877 100644 --- a/third_party/WebKit/Source/build/scripts/templates/StyleBuilderFunctions.cpp.tmpl +++ b/third_party/WebKit/Source/build/scripts/templates/StyleBuilderFunctions.cpp.tmpl
@@ -148,9 +148,9 @@ {% endmacro %} {{apply_auto('CSSPropertyClip')}} {{apply_auto('CSSPropertyOrphans')}} -{{apply_auto('CSSPropertyWebkitColumnCount')}} -{{apply_auto('CSSPropertyWebkitColumnGap', auto_getter='hasNormalColumnGap', auto_setter='setHasNormalColumnGap', auto_identity='CSSValueNormal')}} -{{apply_auto('CSSPropertyWebkitColumnWidth')}} +{{apply_auto('CSSPropertyColumnCount')}} +{{apply_auto('CSSPropertyColumnGap', auto_getter='hasNormalColumnGap', auto_setter='setHasNormalColumnGap', auto_identity='CSSValueNormal')}} +{{apply_auto('CSSPropertyColumnWidth')}} {{apply_auto('CSSPropertyWidows')}} {{apply_auto('CSSPropertyZIndex')}} @@ -304,7 +304,7 @@ {{apply_color('CSSPropertyBorderTopColor')}} {{apply_color('CSSPropertyOutlineColor')}} {{apply_color('CSSPropertyTextDecorationColor')}} -{{apply_color('CSSPropertyWebkitColumnRuleColor')}} +{{apply_color('CSSPropertyColumnRuleColor')}} {{apply_color('CSSPropertyWebkitTextEmphasisColor')}} {{apply_color('CSSPropertyWebkitTextFillColor')}} {{apply_color('CSSPropertyWebkitTextStrokeColor')}}
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn index 586a241..f2c0082 100644 --- a/third_party/WebKit/Source/core/BUILD.gn +++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -173,7 +173,7 @@ ":svg", ] - if (is_win && is_debug) { + if (is_win && is_debug && is_component_build) { # Incremental linking doesn't work on this target in debug mode, even # with symbol_level=1. configs -= [ "//build/config/win:default_incremental_linking" ]
diff --git a/third_party/WebKit/Source/core/animation/Animation.h b/third_party/WebKit/Source/core/animation/Animation.h index a75cb18..ab164c92 100644 --- a/third_party/WebKit/Source/core/animation/Animation.h +++ b/third_party/WebKit/Source/core/animation/Animation.h
@@ -133,6 +133,9 @@ AnimationEffect* effect() { return m_content.get(); } void setEffect(AnimationEffect*); + void setId(const String& id) { m_id = id; } + const String& id() const { return m_id; } + // Pausing via this method is not reflected in the value returned by // paused() and must never overlap with pausing via pause(). void pauseForTesting(double pauseTime); @@ -206,6 +209,8 @@ void notifyAnimationFinished(double monotonicTime, int group) override { } void notifyAnimationAborted(double monotonicTime, int group) override { } + String m_id; + AnimationPlayState m_playState; double m_playbackRate; double m_startTime;
diff --git a/third_party/WebKit/Source/core/animation/Animation.idl b/third_party/WebKit/Source/core/animation/Animation.idl index dbb6db0a..3e8c758 100644 --- a/third_party/WebKit/Source/core/animation/Animation.idl +++ b/third_party/WebKit/Source/core/animation/Animation.idl
@@ -46,6 +46,7 @@ [Measure] void play(); [Measure] void pause(); [Measure] void reverse(); + [Measure] attribute DOMString id; [Measure] void cancel(); [Measure] attribute EventHandler onfinish;
diff --git a/third_party/WebKit/Source/core/animation/AnimationEffect.h b/third_party/WebKit/Source/core/animation/AnimationEffect.h index 1a6cbc4..821964d 100644 --- a/third_party/WebKit/Source/core/animation/AnimationEffect.h +++ b/third_party/WebKit/Source/core/animation/AnimationEffect.h
@@ -109,9 +109,6 @@ void computedTiming(ComputedTimingProperties&); ComputedTimingProperties computedTiming(); - void setName(const String& name) { m_name = name; } - const String& name() const { return m_name; } - DECLARE_VIRTUAL_TRACE(); protected:
diff --git a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp index d245e36..70300df4 100644 --- a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp +++ b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
@@ -19,7 +19,7 @@ Currentcolor, WebkitActivelink, WebkitLink, - WebkitText, + QuirkInherit, InterpolableColorIndexCount, }; @@ -42,7 +42,7 @@ list->set(Currentcolor, InterpolableNumber::create(0)); list->set(WebkitActivelink, InterpolableNumber::create(0)); list->set(WebkitLink, InterpolableNumber::create(0)); - list->set(WebkitText, InterpolableNumber::create(0)); + list->set(QuirkInherit, InterpolableNumber::create(0)); return list.release(); } @@ -55,8 +55,8 @@ return createInterpolableColorForIndex(WebkitActivelink); case CSSValueWebkitLink: return createInterpolableColorForIndex(WebkitLink); - case CSSValueWebkitText: - return createInterpolableColorForIndex(WebkitText); + case CSSValueInternalQuirkInherit: + return createInterpolableColorForIndex(QuirkInherit); case CSSValueWebkitFocusRingColor: return createInterpolableColor(LayoutTheme::theme().focusRingColor()); default: @@ -119,8 +119,8 @@ addPremultipliedColor(red, green, blue, alpha, webkitActivelinkFraction, colors.activeLinkColor()); if (double webkitLinkFraction = toInterpolableNumber(list.get(WebkitLink))->value()) addPremultipliedColor(red, green, blue, alpha, webkitLinkFraction, isVisited ? colors.visitedLinkColor() : colors.linkColor()); - if (double webkitTextFraction = toInterpolableNumber(list.get(WebkitText))->value()) - addPremultipliedColor(red, green, blue, alpha, webkitTextFraction, colors.textColor()); + if (double quirkInheritFraction = toInterpolableNumber(list.get(QuirkInherit))->value()) + addPremultipliedColor(red, green, blue, alpha, quirkInheritFraction, colors.textColor()); alpha = clampTo<double>(alpha, 0, 255); if (alpha == 0)
diff --git a/third_party/WebKit/Source/core/animation/CSSMotionRotationInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSMotionRotationInterpolationType.cpp new file mode 100644 index 0000000..0a93d378 --- /dev/null +++ b/third_party/WebKit/Source/core/animation/CSSMotionRotationInterpolationType.cpp
@@ -0,0 +1,145 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/animation/CSSMotionRotationInterpolationType.h" + +#include "core/css/resolver/StyleBuilderConverter.h" +#include "core/style/StyleMotionRotation.h" + +namespace blink { + +class CSSMotionRotationNonInterpolableValue : public NonInterpolableValue { +public: + ~CSSMotionRotationNonInterpolableValue() {} + + static PassRefPtr<CSSMotionRotationNonInterpolableValue> create(MotionRotationType rotationType) + { + return adoptRef(new CSSMotionRotationNonInterpolableValue(rotationType)); + } + + MotionRotationType rotationType() const { return m_rotationType; } + + DECLARE_NON_INTERPOLABLE_VALUE_TYPE(); + +private: + CSSMotionRotationNonInterpolableValue(MotionRotationType rotationType) + : m_rotationType(rotationType) + { } + + MotionRotationType m_rotationType; +}; + +DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSMotionRotationNonInterpolableValue); +DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSMotionRotationNonInterpolableValue); + +namespace { + +class UnderlyingRotationTypeChecker : public InterpolationType::ConversionChecker { +public: + static PassOwnPtr<UnderlyingRotationTypeChecker> create(MotionRotationType underlyingRotationType) + { + return adoptPtr(new UnderlyingRotationTypeChecker(underlyingRotationType)); + } + + bool isValid(const InterpolationEnvironment&, const InterpolationValue& underlying) const final + { + return m_underlyingRotationType == toCSSMotionRotationNonInterpolableValue(*underlying.nonInterpolableValue).rotationType(); + } + +private: + UnderlyingRotationTypeChecker(MotionRotationType underlyingRotationType) + : m_underlyingRotationType(underlyingRotationType) + { } + + MotionRotationType m_underlyingRotationType; +}; + +class InheritedRotationTypeChecker : public InterpolationType::ConversionChecker { +public: + static PassOwnPtr<InheritedRotationTypeChecker> create(MotionRotationType inheritedRotationType) + { + return adoptPtr(new InheritedRotationTypeChecker(inheritedRotationType)); + } + + bool isValid(const InterpolationEnvironment& environment, const InterpolationValue& underlying) const final + { + return m_inheritedRotationType == environment.state().parentStyle()->motionRotation().type; + } + +private: + InheritedRotationTypeChecker(MotionRotationType inheritedRotationType) + : m_inheritedRotationType(inheritedRotationType) + { } + + MotionRotationType m_inheritedRotationType; +}; + +InterpolationValue convertMotionRotation(const StyleMotionRotation& rotation) +{ + return InterpolationValue( + InterpolableNumber::create(rotation.angle), + CSSMotionRotationNonInterpolableValue::create(rotation.type)); +} + +} // namespace + +InterpolationValue CSSMotionRotationInterpolationType::maybeConvertNeutral(const InterpolationValue& underlying, ConversionCheckers& conversionCheckers) const +{ + MotionRotationType underlyingRotationType = toCSSMotionRotationNonInterpolableValue(*underlying.nonInterpolableValue).rotationType(); + conversionCheckers.append(UnderlyingRotationTypeChecker::create(underlyingRotationType)); + return convertMotionRotation(StyleMotionRotation(0, underlyingRotationType)); +} + +InterpolationValue CSSMotionRotationInterpolationType::maybeConvertInitial() const +{ + return convertMotionRotation(StyleMotionRotation(0, MotionRotationAuto)); +} + +InterpolationValue CSSMotionRotationInterpolationType::maybeConvertInherit(const StyleResolverState& state, ConversionCheckers& conversionCheckers) const +{ + MotionRotationType inheritedRotationType = state.parentStyle()->motionRotation().type; + conversionCheckers.append(InheritedRotationTypeChecker::create(inheritedRotationType)); + return convertMotionRotation(state.parentStyle()->motionRotation()); +} + +InterpolationValue CSSMotionRotationInterpolationType::maybeConvertValue(const CSSValue& value, const StyleResolverState&, ConversionCheckers&) const +{ + return convertMotionRotation(StyleBuilderConverter::convertMotionRotation(value)); +} + +PairwiseInterpolationValue CSSMotionRotationInterpolationType::mergeSingleConversions(InterpolationValue& start, InterpolationValue& end) const +{ + const MotionRotationType& startType = toCSSMotionRotationNonInterpolableValue(*start.nonInterpolableValue).rotationType(); + const MotionRotationType& endType = toCSSMotionRotationNonInterpolableValue(*end.nonInterpolableValue).rotationType(); + if (startType != endType) + return nullptr; + return PairwiseInterpolationValue( + start.interpolableValue.release(), + end.interpolableValue.release(), + start.nonInterpolableValue.release()); +} + +InterpolationValue CSSMotionRotationInterpolationType::maybeConvertUnderlyingValue(const InterpolationEnvironment& environment) const +{ + return convertMotionRotation(environment.state().style()->motionRotation()); +} + +void CSSMotionRotationInterpolationType::composite(UnderlyingValueOwner& underlyingValueOwner, double underlyingFraction, const InterpolationValue& value) const +{ + const MotionRotationType& underlyingType = toCSSMotionRotationNonInterpolableValue(*underlyingValueOwner.value().nonInterpolableValue).rotationType(); + const MotionRotationType& rotationType = toCSSMotionRotationNonInterpolableValue(*value.nonInterpolableValue).rotationType(); + if (underlyingType == rotationType) + underlyingValueOwner.mutableValue().interpolableValue->scaleAndAdd(underlyingFraction, *value.interpolableValue); + else + underlyingValueOwner.set(*this, value); +} + +void CSSMotionRotationInterpolationType::apply(const InterpolableValue& interpolableValue, const NonInterpolableValue* nonInterpolableValue, InterpolationEnvironment& environment) const +{ + environment.state().style()->setMotionRotation(StyleMotionRotation( + toInterpolableNumber(interpolableValue).value(), + toCSSMotionRotationNonInterpolableValue(*nonInterpolableValue).rotationType())); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/animation/CSSMotionRotationInterpolationType.h b/third_party/WebKit/Source/core/animation/CSSMotionRotationInterpolationType.h new file mode 100644 index 0000000..dbeec445 --- /dev/null +++ b/third_party/WebKit/Source/core/animation/CSSMotionRotationInterpolationType.h
@@ -0,0 +1,34 @@ +// 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 CSSMotionRotationInterpolationType_h +#define CSSMotionRotationInterpolationType_h + +#include "core/animation/CSSInterpolationType.h" + +namespace blink { + +class CSSMotionRotationInterpolationType : public CSSInterpolationType { +public: + CSSMotionRotationInterpolationType(CSSPropertyID property) + : CSSInterpolationType(property) + { + ASSERT(property == CSSPropertyMotionRotation); + } + + InterpolationValue maybeConvertUnderlyingValue(const InterpolationEnvironment&) const final; + void composite(UnderlyingValueOwner&, double underlyingFraction, const InterpolationValue&) const final; + void apply(const InterpolableValue&, const NonInterpolableValue*, InterpolationEnvironment&) const final; + +private: + InterpolationValue maybeConvertNeutral(const InterpolationValue& underlying, ConversionCheckers&) const final; + InterpolationValue maybeConvertInitial() const final; + InterpolationValue maybeConvertInherit(const StyleResolverState&, ConversionCheckers&) const final; + InterpolationValue maybeConvertValue(const CSSValue&, const StyleResolverState&, ConversionCheckers&) const final; + PairwiseInterpolationValue mergeSingleConversions(InterpolationValue& start, InterpolationValue& end) const final; +}; + +} // namespace blink + +#endif // CSSMotionRotationInterpolationType_h
diff --git a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp index 40cc56d..8fbd12a8 100644 --- a/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp +++ b/third_party/WebKit/Source/core/animation/ColorPropertyFunctions.cpp
@@ -30,7 +30,7 @@ return style.color(); case CSSPropertyOutlineColor: return style.outlineColor(); - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: return style.columnRuleColor(); case CSSPropertyWebkitTextEmphasisColor: return style.textEmphasisColor(); @@ -71,7 +71,7 @@ return style.visitedLinkColor(); case CSSPropertyOutlineColor: return style.visitedLinkOutlineColor(); - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: return style.visitedLinkColumnRuleColor(); case CSSPropertyWebkitTextEmphasisColor: return style.visitedLinkTextEmphasisColor(); @@ -131,7 +131,7 @@ case CSSPropertyTextDecorationColor: style.setTextDecorationColor(color); return; - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: style.setColumnRuleColor(color); return; case CSSPropertyWebkitTextStrokeColor: @@ -179,7 +179,7 @@ case CSSPropertyTextDecorationColor: style.setVisitedLinkTextDecorationColor(color); return; - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: style.setVisitedLinkColumnRuleColor(color); return; case CSSPropertyWebkitTextStrokeColor:
diff --git a/third_party/WebKit/Source/core/animation/ElementAnimation.h b/third_party/WebKit/Source/core/animation/ElementAnimation.h index abe45a66..a60b1eb 100644 --- a/third_party/WebKit/Source/core/animation/ElementAnimation.h +++ b/third_party/WebKit/Source/core/animation/ElementAnimation.h
@@ -36,13 +36,13 @@ #include "core/animation/EffectInput.h" #include "core/animation/ElementAnimations.h" #include "core/animation/KeyframeEffect.h" +#include "core/animation/KeyframeEffectOptions.h" #include "core/animation/TimingInput.h" #include "core/dom/Document.h" #include "core/dom/Element.h" #include "platform/RuntimeEnabledFeatures.h" #include "wtf/Allocator.h" - namespace blink { class Dictionary; @@ -58,12 +58,15 @@ return animateInternal(element, effect, TimingInput::convert(duration)); } - static Animation* animate(Element& element, const EffectModelOrDictionarySequenceOrDictionary& effectInput, const KeyframeEffectOptions& timingInput, ExceptionState& exceptionState) + static Animation* animate(Element& element, const EffectModelOrDictionarySequenceOrDictionary& effectInput, const KeyframeEffectOptions& options, ExceptionState& exceptionState) { EffectModel* effect = EffectInput::convert(&element, effectInput, exceptionState); if (exceptionState.hadException()) return 0; - return animateInternal(element, effect, TimingInput::convert(timingInput)); + + Animation* animation = animateInternal(element, effect, TimingInput::convert(options)); + animation->setId(options.id()); + return animation; } static Animation* animate(Element& element, const EffectModelOrDictionarySequenceOrDictionary& effectInput, ExceptionState& exceptionState) @@ -76,17 +79,17 @@ static HeapVector<Member<Animation>> getAnimations(Element& element) { - HeapVector<Member<Animation>> animationss; + HeapVector<Member<Animation>> animations; if (!element.hasAnimations()) - return animationss; + return animations; for (const auto& animation : element.document().timeline().getAnimations()) { ASSERT(animation->effect()); if (toKeyframeEffect(animation->effect())->target() == element && (animation->effect()->isCurrent() || animation->effect()->isInEffect())) - animationss.append(animation); + animations.append(animation); } - return animationss; + return animations; } private:
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffectOptions.idl b/third_party/WebKit/Source/core/animation/KeyframeEffectOptions.idl index 235551eb..fbf78adb 100644 --- a/third_party/WebKit/Source/core/animation/KeyframeEffectOptions.idl +++ b/third_party/WebKit/Source/core/animation/KeyframeEffectOptions.idl
@@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// https://w3c.github.io/web-animations/#the-animationeffecttimingproperties-dictionary +// https://w3c.github.io/web-animations/#the-keyframeeffectoptions-dictionary enum FillMode { "none", "forwards", "backwards", "both", "auto" }; enum PlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" }; -// TODO(dstockwell): Rename this to AnimationEffectTimingProperties. dictionary KeyframeEffectOptions { double delay = 0; double endDelay = 0; @@ -19,4 +18,5 @@ double playbackRate = 1.0; PlaybackDirection direction = "normal"; DOMString easing = "linear"; + DOMString id = ""; };
diff --git a/third_party/WebKit/Source/core/animation/LengthPropertyFunctions.cpp b/third_party/WebKit/Source/core/animation/LengthPropertyFunctions.cpp index 4d35ecf..c1786ab 100644 --- a/third_party/WebKit/Source/core/animation/LengthPropertyFunctions.cpp +++ b/third_party/WebKit/Source/core/animation/LengthPropertyFunctions.cpp
@@ -37,8 +37,8 @@ case CSSPropertyStrokeWidth: case CSSPropertyWebkitBorderHorizontalSpacing: case CSSPropertyWebkitBorderVerticalSpacing: - case CSSPropertyWebkitColumnGap: - case CSSPropertyWebkitColumnWidth: + case CSSPropertyColumnGap: + case CSSPropertyColumnWidth: case CSSPropertyWidth: return ValueRangeNonNegative; default: @@ -64,7 +64,7 @@ case CSSPropertyBorderLeftWidth: case CSSPropertyBorderRightWidth: case CSSPropertyBorderTopWidth: - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: case CSSPropertyOutlineWidth: if (valueID == CSSValueThin) { result = 1; @@ -102,7 +102,7 @@ // The computed value of "initial" for the following properties is 0px if the associated *-style property resolves to "none" or "hidden". // border-width: https://drafts.csswg.org/css-backgrounds-3/#the-border-width // outline-width: https://drafts.csswg.org/css-ui-3/#outline-width - // -webkit-column-rule-width: https://drafts.csswg.org/css-multicol-1/#crw + // column-rule-width: https://drafts.csswg.org/css-multicol-1/#crw // We ignore this value adjustment for animations and use the wrong value for hidden widths to avoid // having to restart our animations based on the computed *-style values. // This is acceptable since animations running on hidden widths are unobservable to the user, even via getComputedStyle(). @@ -115,7 +115,7 @@ case CSSPropertyOutlineWidth: result = lengthFromUnsigned(ComputedStyle::initialOutlineWidth()); return true; - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: result = lengthFromUnsigned(ComputedStyle::initialColumnRuleWidth()); return true; @@ -257,10 +257,10 @@ case CSSPropertyWebkitBorderVerticalSpacing: result = Length(style.verticalBorderSpacing(), Fixed); return true; - case CSSPropertyWebkitColumnGap: + case CSSPropertyColumnGap: result = Length(style.columnGap(), Fixed); return true; - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: result = Length(style.columnRuleWidth(), Fixed); return true; case CSSPropertyWebkitTransformOriginZ: @@ -295,7 +295,7 @@ return false; result = style.verticalAlignLength(); return true; - case CSSPropertyWebkitColumnWidth: + case CSSPropertyColumnWidth: if (style.hasAutoColumnWidth()) return false; result = Length(style.columnWidth(), Fixed); @@ -432,9 +432,9 @@ case CSSPropertyVerticalAlign: case CSSPropertyWebkitBorderHorizontalSpacing: case CSSPropertyWebkitBorderVerticalSpacing: - case CSSPropertyWebkitColumnGap: - case CSSPropertyWebkitColumnRuleWidth: - case CSSPropertyWebkitColumnWidth: + case CSSPropertyColumnGap: + case CSSPropertyColumnRuleWidth: + case CSSPropertyColumnWidth: case CSSPropertyWebkitTransformOriginZ: case CSSPropertyWordSpacing: return false;
diff --git a/third_party/WebKit/Source/core/animation/NumberPropertyFunctions.cpp b/third_party/WebKit/Source/core/animation/NumberPropertyFunctions.cpp index b3a6b43..b6612811 100644 --- a/third_party/WebKit/Source/core/animation/NumberPropertyFunctions.cpp +++ b/third_party/WebKit/Source/core/animation/NumberPropertyFunctions.cpp
@@ -55,7 +55,7 @@ return false; result = style.fontSizeAdjust(); return true; - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: if (style.hasAutoColumnCount()) return false; result = style.columnCount(); @@ -110,10 +110,10 @@ case CSSPropertyWidows: return clampTo<short>(round(value), 1); - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: return clampTo<unsigned short>(round(value), 1); - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: return clampTo<unsigned short>(round(value)); case CSSPropertyZIndex: @@ -162,7 +162,7 @@ case CSSPropertyStrokeOpacity: style.setStrokeOpacity(value); return true; - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: style.setColumnCount(value); return true; case CSSPropertyWidows:
diff --git a/third_party/WebKit/Source/core/animation/PropertyInterpolationTypesMapping.cpp b/third_party/WebKit/Source/core/animation/PropertyInterpolationTypesMapping.cpp index 612dcf9..52bee750 100644 --- a/third_party/WebKit/Source/core/animation/PropertyInterpolationTypesMapping.cpp +++ b/third_party/WebKit/Source/core/animation/PropertyInterpolationTypesMapping.cpp
@@ -12,6 +12,7 @@ #include "core/animation/CSSImageListInterpolationType.h" #include "core/animation/CSSLengthInterpolationType.h" #include "core/animation/CSSLengthListInterpolationType.h" +#include "core/animation/CSSMotionRotationInterpolationType.h" #include "core/animation/CSSNumberInterpolationType.h" #include "core/animation/CSSPaintInterpolationType.h" #include "core/animation/CSSPathInterpolationType.h" @@ -89,9 +90,9 @@ case CSSPropertyVerticalAlign: case CSSPropertyWebkitBorderHorizontalSpacing: case CSSPropertyWebkitBorderVerticalSpacing: - case CSSPropertyWebkitColumnGap: - case CSSPropertyWebkitColumnRuleWidth: - case CSSPropertyWebkitColumnWidth: + case CSSPropertyColumnGap: + case CSSPropertyColumnRuleWidth: + case CSSPropertyColumnWidth: case CSSPropertyWebkitPerspectiveOriginX: case CSSPropertyWebkitPerspectiveOriginY: case CSSPropertyWebkitTransformOriginX: @@ -114,7 +115,7 @@ case CSSPropertyStopOpacity: case CSSPropertyStrokeMiterlimit: case CSSPropertyStrokeOpacity: - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: case CSSPropertyWidows: case CSSPropertyZIndex: applicableTypes->append(adoptPtr(new CSSNumberInterpolationType(cssProperty))); @@ -134,7 +135,7 @@ case CSSPropertyOutlineColor: case CSSPropertyStopColor: case CSSPropertyTextDecorationColor: - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: case CSSPropertyWebkitTextStrokeColor: applicableTypes->append(adoptPtr(new CSSColorInterpolationType(cssProperty))); break; @@ -170,6 +171,9 @@ case CSSPropertyClip: applicableTypes->append(adoptPtr(new CSSClipInterpolationType(cssProperty))); break; + case CSSPropertyMotionRotation: + applicableTypes->append(adoptPtr(new CSSMotionRotationInterpolationType(cssProperty))); + break; default: // TODO(alancutter): Support all interpolable CSS properties here so we can stop falling back to the old StyleInterpolation implementation. if (CSSPropertyMetadata::isInterpolableProperty(cssProperty))
diff --git a/third_party/WebKit/Source/core/animation/StringKeyframe.cpp b/third_party/WebKit/Source/core/animation/StringKeyframe.cpp index fede64b..df0a03a 100644 --- a/third_party/WebKit/Source/core/animation/StringKeyframe.cpp +++ b/third_party/WebKit/Source/core/animation/StringKeyframe.cpp
@@ -175,13 +175,6 @@ break; - case CSSPropertyMotionRotation: { - RefPtr<Interpolation> interpolation = DoubleStyleInterpolation::maybeCreateFromMotionRotation(*fromCSSValue, *toCSSValue, property); - if (interpolation) - return interpolation.release(); - break; - } - case CSSPropertyBorderBottomLeftRadius: case CSSPropertyBorderBottomRightRadius: case CSSPropertyBorderTopLeftRadius:
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp index b2c4c89..90cd914 100644 --- a/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp +++ b/third_party/WebKit/Source/core/animation/css/CSSAnimatableValueFactory.cpp
@@ -488,17 +488,17 @@ if (ClipPathOperation* operation = style.clipPath()) return AnimatableClipPathOperation::create(operation); return AnimatableUnknown::create(CSSValueNone); - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: if (style.hasAutoColumnCount()) return AnimatableUnknown::create(CSSValueAuto); return createFromDouble(style.columnCount()); - case CSSPropertyWebkitColumnGap: + case CSSPropertyColumnGap: return createFromDouble(style.columnGap()); - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: return createFromColor(property, style); - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: return createFromDouble(style.columnRuleWidth()); - case CSSPropertyWebkitColumnWidth: + case CSSPropertyColumnWidth: if (style.hasAutoColumnWidth()) return AnimatableUnknown::create(CSSValueAuto); return createFromDouble(style.columnWidth());
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp index 5beb28da..660a9ba 100644 --- a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp +++ b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
@@ -402,8 +402,8 @@ const InertEffect* inertAnimation = entry.effect.get(); AnimationEventDelegate* eventDelegate = new AnimationEventDelegate(element, entry.name); KeyframeEffect* effect = KeyframeEffect::create(element, inertAnimation->model(), inertAnimation->specifiedTiming(), KeyframeEffect::DefaultPriority, eventDelegate); - effect->setName(entry.name); Animation* animation = element->document().timeline().play(effect); + animation->setId(entry.name); if (inertAnimation->paused()) animation->pause(); animation->update(TimingUpdateOnDemand); @@ -483,8 +483,8 @@ } KeyframeEffect* transition = KeyframeEffect::create(element, model, inertAnimation->specifiedTiming(), KeyframeEffect::TransitionPriority, eventDelegate); - transition->setName(getPropertyName(newTransition.id)); Animation* animation = element->document().timeline().play(transition); + animation->setId(getPropertyName(newTransition.id)); // Set the current time as the start time for retargeted transitions if (retargetedCompositorTransitions.contains(id)) animation->setStartTime(element->document().timeline().currentTime());
diff --git a/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.cpp b/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.cpp index b14ff6e..2aeaf71 100644 --- a/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.cpp +++ b/third_party/WebKit/Source/core/animation/css/CSSPropertyEquality.cpp
@@ -249,16 +249,16 @@ return a.verticalBorderSpacing() == b.verticalBorderSpacing(); case CSSPropertyWebkitClipPath: return dataEquivalent(a.clipPath(), b.clipPath()); - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: return a.columnCount() == b.columnCount(); - case CSSPropertyWebkitColumnGap: + case CSSPropertyColumnGap: return a.columnGap() == b.columnGap(); - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: return a.columnRuleColor() == b.columnRuleColor() && a.visitedLinkColumnRuleColor() == b.visitedLinkColumnRuleColor(); - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: return a.columnRuleWidth() == b.columnRuleWidth(); - case CSSPropertyWebkitColumnWidth: + case CSSPropertyColumnWidth: return a.columnWidth() == b.columnWidth(); case CSSPropertyWebkitFilter: return a.filter() == b.filter();
diff --git a/third_party/WebKit/Source/core/core.gypi b/third_party/WebKit/Source/core/core.gypi index 032adbe1..43fb270 100644 --- a/third_party/WebKit/Source/core/core.gypi +++ b/third_party/WebKit/Source/core/core.gypi
@@ -502,6 +502,7 @@ ], 'webcore_rendering_files': [ 'layout/api/HitTestAction.h', + 'layout/api/LayoutBoxModel.h', 'layout/api/LayoutItem.h', 'layout/api/LineLayoutAPIShim.h', 'layout/api/LineLayoutBlockFlow.h', @@ -866,6 +867,8 @@ 'animation/CSSLengthListInterpolationType.h', 'animation/CSSNumberInterpolationType.cpp', 'animation/CSSNumberInterpolationType.h', + 'animation/CSSMotionRotationInterpolationType.cpp', + 'animation/CSSMotionRotationInterpolationType.h', 'animation/CSSPaintInterpolationType.cpp', 'animation/CSSPaintInterpolationType.h', 'animation/CSSPathInterpolationType.cpp', @@ -1903,6 +1906,8 @@ 'page/DragData.cpp', 'page/EventSource.cpp', 'page/EventSource.h', + 'page/EventSourceParser.cpp', + 'page/EventSourceParser.h', 'page/EventWithHitTestResults.h', 'page/FocusController.cpp', 'page/FrameTree.cpp', @@ -3994,7 +3999,7 @@ 'origin_trials/OriginTrialContextTest.cpp', 'page/ChromeClientTest.cpp', 'page/ContextMenuControllerTest.cpp', - 'page/EventSourceTest.cpp', + 'page/EventSourceParserTest.cpp', 'page/NetworkStateNotifierTest.cpp', 'page/PagePopupClientTest.cpp', 'page/PrintContextTest.cpp',
diff --git a/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp b/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp index 934a62c..cd02c7f 100644 --- a/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp +++ b/third_party/WebKit/Source/core/css/CSSComputedStyleDeclaration.cpp
@@ -88,6 +88,9 @@ CSSPropertyBottom, CSSPropertyBoxShadow, CSSPropertyBoxSizing, + CSSPropertyBreakAfter, + CSSPropertyBreakBefore, + CSSPropertyBreakInside, CSSPropertyCaptionSide, CSSPropertyClear, CSSPropertyClip, @@ -146,9 +149,6 @@ CSSPropertyPaddingLeft, CSSPropertyPaddingRight, CSSPropertyPaddingTop, - CSSPropertyPageBreakAfter, - CSSPropertyPageBreakBefore, - CSSPropertyPageBreakInside, CSSPropertyPointerEvents, CSSPropertyPosition, CSSPropertyResize, @@ -209,16 +209,13 @@ CSSPropertyWebkitBoxPack, CSSPropertyWebkitBoxReflect, CSSPropertyWebkitClipPath, - CSSPropertyWebkitColumnBreakAfter, - CSSPropertyWebkitColumnBreakBefore, - CSSPropertyWebkitColumnBreakInside, - CSSPropertyWebkitColumnCount, - CSSPropertyWebkitColumnGap, - CSSPropertyWebkitColumnRuleColor, - CSSPropertyWebkitColumnRuleStyle, - CSSPropertyWebkitColumnRuleWidth, - CSSPropertyWebkitColumnSpan, - CSSPropertyWebkitColumnWidth, + CSSPropertyColumnCount, + CSSPropertyColumnGap, + CSSPropertyColumnRuleColor, + CSSPropertyColumnRuleStyle, + CSSPropertyColumnRuleWidth, + CSSPropertyColumnSpan, + CSSPropertyColumnWidth, CSSPropertyWebkitFilter, CSSPropertyBackdropFilter, CSSPropertyAlignContent,
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValue.cpp b/third_party/WebKit/Source/core/css/CSSPrimitiveValue.cpp index 6cf70967..3c6eaf0 100644 --- a/third_party/WebKit/Source/core/css/CSSPrimitiveValue.cpp +++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValue.cpp
@@ -144,7 +144,7 @@ { int valueID = getValueID(); switch (valueID) { - case CSSValueWebkitText: + case CSSValueInternalQuirkInherit: case CSSValueWebkitLink: case CSSValueWebkitActivelink: case CSSValueCurrentcolor:
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h index be6100e..a80af4da 100644 --- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h +++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -636,7 +636,6 @@ case CSSValueContentBox: return ContentFillBox; case CSSValueText: - case CSSValueWebkitText: return TextFillBox; default: break; @@ -1755,41 +1754,78 @@ return OVISIBLE; } -template<> inline CSSPrimitiveValue::CSSPrimitiveValue(EPageBreak e) +template<> inline CSSPrimitiveValue::CSSPrimitiveValue(EBreak e) : CSSValue(PrimitiveClass) { init(UnitType::ValueID); switch (e) { - case PBAUTO: + default: + ASSERT_NOT_REACHED(); + case BreakAuto: m_value.valueID = CSSValueAuto; break; - case PBALWAYS: + case BreakAlways: m_value.valueID = CSSValueAlways; break; - case PBAVOID: + case BreakAvoid: m_value.valueID = CSSValueAvoid; break; + case BreakAvoidPage: + m_value.valueID = CSSValueAvoidPage; + break; + case BreakPage: + m_value.valueID = CSSValuePage; + break; + case BreakLeft: + m_value.valueID = CSSValueLeft; + break; + case BreakRight: + m_value.valueID = CSSValueRight; + break; + case BreakRecto: + m_value.valueID = CSSValueRecto; + break; + case BreakVerso: + m_value.valueID = CSSValueVerso; + break; + case BreakAvoidColumn: + m_value.valueID = CSSValueAvoidColumn; + break; + case BreakColumn: + m_value.valueID = CSSValueColumn; + break; } } -template<> inline EPageBreak CSSPrimitiveValue::convertTo() const +template<> inline EBreak CSSPrimitiveValue::convertTo() const { ASSERT(isValueID()); switch (m_value.valueID) { - case CSSValueAuto: - return PBAUTO; - case CSSValueLeft: - case CSSValueRight: - case CSSValueAlways: - return PBALWAYS; // CSS2.1: "Conforming user agents may map left/right to always." - case CSSValueAvoid: - return PBAVOID; default: - break; + ASSERT_NOT_REACHED(); + case CSSValueAuto: + return BreakAuto; + case CSSValueAvoid: + return BreakAvoid; + case CSSValueAlways: + return BreakAlways; + case CSSValueAvoidPage: + return BreakAvoidPage; + case CSSValuePage: + return BreakPage; + case CSSValueLeft: + return BreakLeft; + case CSSValueRight: + return BreakRight; + case CSSValueRecto: + return BreakRecto; + case CSSValueVerso: + return BreakVerso; + case CSSValueAvoidColumn: + return BreakAvoidColumn; + case CSSValueColumn: + return BreakColumn; } - - ASSERT_NOT_REACHED(); - return PBAUTO; } template<> inline CSSPrimitiveValue::CSSPrimitiveValue(EPosition e)
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.in b/third_party/WebKit/Source/core/css/CSSProperties.in index 1472523..1e236971 100644 --- a/third_party/WebKit/Source/core/css/CSSProperties.in +++ b/third_party/WebKit/Source/core/css/CSSProperties.in
@@ -153,6 +153,9 @@ bottom interpolable, initial=initialOffset, converter=convertLengthOrAuto box-shadow interpolable, converter=convertShadow box-sizing +break-after type_name=EBreak +break-before type_name=EBreak +break-inside type_name=EBreak buffered-rendering svg caption-side inherited clear @@ -246,9 +249,6 @@ padding-left interpolable, initial=initialPadding, converter=convertLength padding-right interpolable, initial=initialPadding, converter=convertLength padding-top interpolable, initial=initialPadding, converter=convertLength -page-break-after type_name=EPageBreak, initial=initialPageBreak -page-break-before type_name=EPageBreak, initial=initialPageBreak -page-break-inside type_name=EPageBreak, initial=initialPageBreak paint-order inherited, svg, converter=convertPaintOrder perspective interpolable, converter=convertPerspective perspective-origin interpolable, converter=convertPosition @@ -337,16 +337,13 @@ -webkit-box-pack -webkit-box-reflect converter=convertBoxReflect -webkit-clip-path interpolable, custom_value --webkit-column-break-after type_name=EPageBreak, initial=initialPageBreak --webkit-column-break-before type_name=EPageBreak, initial=initialPageBreak --webkit-column-break-inside type_name=EPageBreak, initial=initialPageBreak --webkit-column-count interpolable, type_name=unsigned short, custom_all --webkit-column-gap interpolable, converter=convertComputedLength<float>, custom_all --webkit-column-rule-color interpolable, custom_all --webkit-column-rule-style type_name=EBorderStyle, initial=initialBorderStyle --webkit-column-rule-width interpolable, converter=convertLineWidth<unsigned short> --webkit-column-span type_name=ColumnSpan --webkit-column-width interpolable, converter=convertComputedLength<float>, custom_all +column-count interpolable, type_name=unsigned short, custom_all +column-gap interpolable, converter=convertComputedLength<float>, custom_all +column-rule-color interpolable, custom_all +column-rule-style type_name=EBorderStyle, initial=initialBorderStyle +column-rule-width interpolable, converter=convertLineWidth<unsigned short> +column-span type_name=ColumnSpan +column-width interpolable, converter=convertComputedLength<float>, custom_all -webkit-filter interpolable, converter=convertFilterOperations -webkit-highlight inherited, converter=convertString<CSSValueNone> -webkit-hyphenate-character inherited, name_for_methods=HyphenationString, converter=convertString<CSSValueAuto> @@ -477,13 +474,19 @@ outline longhands=outline-color;outline-style;outline-width overflow longhands=overflow-x;overflow-y padding longhands=padding-top;padding-right;padding-bottom;padding-left +page-break-after longhands=break-after +page-break-before longhands=break-before +page-break-inside longhands=break-inside transition longhands=transition-property;transition-duration;transition-timing-function;transition-delay -webkit-border-after longhands=-webkit-border-after-width;-webkit-border-after-style;-webkit-border-after-color -webkit-border-before longhands=-webkit-border-before-width;-webkit-border-before-style;-webkit-border-before-color -webkit-border-end longhands=-webkit-border-end-width;-webkit-border-end-style;-webkit-border-end-color -webkit-border-start longhands=-webkit-border-start-width;-webkit-border-start-style;-webkit-border-start-color --webkit-column-rule longhands=-webkit-column-rule-width;-webkit-column-rule-style;-webkit-column-rule-color --webkit-columns longhands=-webkit-column-width;-webkit-column-count +-webkit-column-break-after longhands=break-after +-webkit-column-break-before longhands=break-before +-webkit-column-break-inside longhands=break-inside +column-rule longhands=column-rule-width;column-rule-style;column-rule-color +columns longhands=column-width;column-count -webkit-margin-collapse longhands=-webkit-margin-before-collapse;-webkit-margin-after-collapse -webkit-mask longhands=-webkit-mask-image;-webkit-mask-position-x;-webkit-mask-position-y;-webkit-mask-size;-webkit-mask-repeat-x;-webkit-mask-repeat-y;-webkit-mask-origin;-webkit-mask-clip -webkit-mask-box-image longhands=-webkit-mask-box-image-source;-webkit-mask-box-image-slice;-webkit-mask-box-image-width;-webkit-mask-box-image-outset;-webkit-mask-box-image-repeat @@ -526,6 +529,15 @@ -webkit-border-top-right-radius alias_for=border-top-right-radius -webkit-box-shadow alias_for=box-shadow -webkit-box-sizing alias_for=box-sizing +-webkit-column-count alias_for=column-count +-webkit-column-gap alias_for=column-gap +-webkit-column-rule alias_for=column-rule +-webkit-column-rule-color alias_for=column-rule-color +-webkit-column-rule-style alias_for=column-rule-style +-webkit-column-rule-width alias_for=column-rule-width +-webkit-column-span alias_for=column-span +-webkit-column-width alias_for=column-width +-webkit-columns alias_for=columns -webkit-flex alias_for=flex -webkit-flex-basis alias_for=flex-basis -webkit-flex-direction alias_for=flex-direction
diff --git a/third_party/WebKit/Source/core/css/CSSValueKeywords.in b/third_party/WebKit/Source/core/css/CSSValueKeywords.in index 772f0178c..404f3755 100644 --- a/third_party/WebKit/Source/core/css/CSSValueKeywords.in +++ b/third_party/WebKit/Source/core/css/CSSValueKeywords.in
@@ -188,8 +188,9 @@ currentcolor grey // -// colors in non strict mode --webkit-text mode=QuirksOrUASheet +// Value used to implement the behavior in: +// https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk +-internal-quirk-inherit // // background-repeat // @@ -1069,3 +1070,10 @@ var -internal-variable-value + +// break-before, break-after, break-inside +avoid-page +page +recto +verso +avoid-column
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp index 2656aff..4566ed63 100644 --- a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp +++ b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
@@ -1323,6 +1323,45 @@ return list.release(); } +static EBreak mapToPageBreakValue(EBreak genericBreakValue) +{ + switch (genericBreakValue) { + case BreakAvoidColumn: + case BreakColumn: + case BreakRecto: + case BreakVerso: + return BreakAuto; + case BreakLeft: + case BreakRight: + // TODO(mstensho): "left" and "right" should simply be mapped to that, not "always", according to spec. + case BreakPage: + return BreakAlways; + case BreakAvoidPage: + return BreakAvoid; + default: + return genericBreakValue; + } +} + +static EBreak mapToColumnBreakValue(EBreak genericBreakValue) +{ + switch (genericBreakValue) { + case BreakAvoidPage: + case BreakLeft: + case BreakPage: + case BreakRecto: + case BreakRight: + case BreakVerso: + return BreakAuto; + case BreakColumn: + return BreakAlways; + case BreakAvoidColumn: + return BreakAvoid; + default: + return genericBreakValue; + } +} + PassRefPtrWillBeRawPtr<CSSValue> ComputedStyleCSSValueMapping::get(const AtomicString customPropertyName, const ComputedStyle& style) { StyleVariableData* variables = style.variables(); @@ -1517,32 +1556,32 @@ return cssValuePool().createColorValue(allowVisitedStyle ? style.visitedDependentColor(CSSPropertyColor).rgb() : style.color().rgb()); case CSSPropertyWebkitPrintColorAdjust: return cssValuePool().createValue(style.printColorAdjust()); - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: if (style.hasAutoColumnCount()) return cssValuePool().createIdentifierValue(CSSValueAuto); return cssValuePool().createValue(style.columnCount(), CSSPrimitiveValue::UnitType::Number); case CSSPropertyColumnFill: ASSERT(RuntimeEnabledFeatures::columnFillEnabled()); return cssValuePool().createValue(style.columnFill()); - case CSSPropertyWebkitColumnGap: + case CSSPropertyColumnGap: if (style.hasNormalColumnGap()) return cssValuePool().createIdentifierValue(CSSValueNormal); return zoomAdjustedPixelValue(style.columnGap(), style); - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: return allowVisitedStyle ? cssValuePool().createColorValue(style.visitedDependentColor(CSSPropertyOutlineColor).rgb()) : currentColorOrValidColor(style, style.columnRuleColor()); - case CSSPropertyWebkitColumnRuleStyle: + case CSSPropertyColumnRuleStyle: return cssValuePool().createValue(style.columnRuleStyle()); - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: return zoomAdjustedPixelValue(style.columnRuleWidth(), style); - case CSSPropertyWebkitColumnSpan: + case CSSPropertyColumnSpan: return cssValuePool().createIdentifierValue(style.columnSpan() ? CSSValueAll : CSSValueNone); case CSSPropertyWebkitColumnBreakAfter: - return cssValuePool().createValue(style.columnBreakAfter()); + return cssValuePool().createValue(mapToColumnBreakValue(style.breakAfter())); case CSSPropertyWebkitColumnBreakBefore: - return cssValuePool().createValue(style.columnBreakBefore()); + return cssValuePool().createValue(mapToColumnBreakValue(style.breakBefore())); case CSSPropertyWebkitColumnBreakInside: - return cssValuePool().createValue(style.columnBreakInside()); - case CSSPropertyWebkitColumnWidth: + return cssValuePool().createValue(mapToColumnBreakValue(style.breakInside())); + case CSSPropertyColumnWidth: if (style.hasAutoColumnWidth()) return cssValuePool().createIdentifierValue(CSSValueAuto); return zoomAdjustedPixelValue(style.columnWidth(), style); @@ -1884,17 +1923,18 @@ return zoomAdjustedPixelValueForLength(paddingLeft, style); return zoomAdjustedPixelValue(toLayoutBox(layoutObject)->computedCSSPaddingLeft(), style); } + case CSSPropertyBreakAfter: + return cssValuePool().createValue(style.breakAfter()); + case CSSPropertyBreakBefore: + return cssValuePool().createValue(style.breakBefore()); + case CSSPropertyBreakInside: + return cssValuePool().createValue(style.breakInside()); case CSSPropertyPageBreakAfter: - return cssValuePool().createValue(style.pageBreakAfter()); + return cssValuePool().createValue(mapToPageBreakValue(style.breakAfter())); case CSSPropertyPageBreakBefore: - return cssValuePool().createValue(style.pageBreakBefore()); - case CSSPropertyPageBreakInside: { - EPageBreak pageBreak = style.pageBreakInside(); - ASSERT(pageBreak != PBALWAYS); - if (pageBreak == PBALWAYS) - return nullptr; - return cssValuePool().createValue(style.pageBreakInside()); - } + return cssValuePool().createValue(mapToPageBreakValue(style.breakBefore())); + case CSSPropertyPageBreakInside: + return cssValuePool().createValue(mapToPageBreakValue(style.breakInside())); case CSSPropertyPosition: return cssValuePool().createValue(style.position()); case CSSPropertyQuotes: @@ -2405,10 +2445,10 @@ return valuesForShorthandProperty(borderTopShorthand(), style, layoutObject, styledNode, allowVisitedStyle); case CSSPropertyBorderWidth: return valuesForSidesShorthand(borderWidthShorthand(), style, layoutObject, styledNode, allowVisitedStyle); - case CSSPropertyWebkitColumnRule: - return valuesForShorthandProperty(webkitColumnRuleShorthand(), style, layoutObject, styledNode, allowVisitedStyle); - case CSSPropertyWebkitColumns: - return valuesForShorthandProperty(webkitColumnsShorthand(), style, layoutObject, styledNode, allowVisitedStyle); + case CSSPropertyColumnRule: + return valuesForShorthandProperty(columnRuleShorthand(), style, layoutObject, styledNode, allowVisitedStyle); + case CSSPropertyColumns: + return valuesForShorthandProperty(columnsShorthand(), style, layoutObject, styledNode, allowVisitedStyle); case CSSPropertyListStyle: return valuesForShorthandProperty(listStyleShorthand(), style, layoutObject, styledNode, allowVisitedStyle); case CSSPropertyMargin:
diff --git a/third_party/WebKit/Source/core/css/StylePropertySerializer.cpp b/third_party/WebKit/Source/core/css/StylePropertySerializer.cpp index d9af51ef..82dda72 100644 --- a/third_party/WebKit/Source/core/css/StylePropertySerializer.cpp +++ b/third_party/WebKit/Source/core/css/StylePropertySerializer.cpp
@@ -184,7 +184,7 @@ result.append(' '); const CSSCustomPropertyDeclaration* value = toCSSCustomPropertyDeclaration(property.value()); result.append(value->name()); - result.appendLiteral(": "); + result.append(':'); result.append(value->customCSSText()); if (property.isImportant()) result.appendLiteral(" !important"); @@ -435,10 +435,10 @@ return get4Values(borderWidthShorthand()); case CSSPropertyBorderStyle: return get4Values(borderStyleShorthand()); - case CSSPropertyWebkitColumnRule: - return getShorthandValue(webkitColumnRuleShorthand()); - case CSSPropertyWebkitColumns: - return getShorthandValue(webkitColumnsShorthand()); + case CSSPropertyColumnRule: + return getShorthandValue(columnRuleShorthand()); + case CSSPropertyColumns: + return getShorthandValue(columnsShorthand()); case CSSPropertyFlex: return getShorthandValue(flexShorthand()); case CSSPropertyFlexFlow:
diff --git a/third_party/WebKit/Source/core/css/StyleSheetContents.cpp b/third_party/WebKit/Source/core/css/StyleSheetContents.cpp index 48bbc4c..e76b0389 100644 --- a/third_party/WebKit/Source/core/css/StyleSheetContents.cpp +++ b/third_party/WebKit/Source/core/css/StyleSheetContents.cpp
@@ -34,7 +34,6 @@ #include "core/inspector/InspectorTraceEvents.h" #include "platform/TraceEvent.h" #include "platform/weborigin/SecurityOrigin.h" -#include "wtf/Deque.h" namespace blink {
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp index 9104ecc..c15a16a9 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSParserFastPaths.cpp
@@ -141,7 +141,7 @@ case CSSPropertyWebkitBorderBeforeColor: case CSSPropertyWebkitBorderEndColor: case CSSPropertyWebkitBorderStartColor: - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: case CSSPropertyWebkitTextEmphasisColor: case CSSPropertyWebkitTextFillColor: case CSSPropertyWebkitTextStrokeColor: @@ -490,7 +490,7 @@ case CSSPropertyWebkitBorderBeforeStyle: case CSSPropertyWebkitBorderEndStyle: case CSSPropertyWebkitBorderStartStyle: - case CSSPropertyWebkitColumnRuleStyle: + case CSSPropertyColumnRuleStyle: return valueID >= CSSValueNone && valueID <= CSSValueDouble; case CSSPropertyBoxSizing: return valueID == CSSValueBorderBox || valueID == CSSValueContentBox; @@ -558,14 +558,11 @@ return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay; case CSSPropertyOverflowY: // visible | hidden | scroll | auto | overlay | -webkit-paged-x | -webkit-paged-y return valueID == CSSValueVisible || valueID == CSSValueHidden || valueID == CSSValueScroll || valueID == CSSValueAuto || valueID == CSSValueOverlay || valueID == CSSValueWebkitPagedX || valueID == CSSValueWebkitPagedY; - case CSSPropertyPageBreakAfter: // auto | always | avoid | left | right - case CSSPropertyPageBreakBefore: - case CSSPropertyWebkitColumnBreakAfter: - case CSSPropertyWebkitColumnBreakBefore: - return valueID == CSSValueAuto || valueID == CSSValueAlways || valueID == CSSValueAvoid || valueID == CSSValueLeft || valueID == CSSValueRight; - case CSSPropertyPageBreakInside: // avoid | auto - case CSSPropertyWebkitColumnBreakInside: - return valueID == CSSValueAuto || valueID == CSSValueAvoid; + case CSSPropertyBreakAfter: + case CSSPropertyBreakBefore: + return valueID == CSSValueAuto || valueID == CSSValueAvoid || valueID == CSSValueAvoidPage || valueID == CSSValuePage || valueID == CSSValueLeft || valueID == CSSValueRight || valueID == CSSValueRecto || valueID == CSSValueVerso || valueID == CSSValueAvoidColumn || valueID == CSSValueColumn; + case CSSPropertyBreakInside: + return valueID == CSSValueAuto || valueID == CSSValueAvoid || valueID == CSSValueAvoidPage || valueID == CSSValueAvoidColumn; case CSSPropertyPointerEvents: // none | visiblePainted | visibleFill | visibleStroke | visible | // painted | fill | stroke | auto | all | bounding-box @@ -756,9 +753,9 @@ case CSSPropertyOverflowWrap: case CSSPropertyOverflowX: case CSSPropertyOverflowY: - case CSSPropertyPageBreakAfter: - case CSSPropertyPageBreakBefore: - case CSSPropertyPageBreakInside: + case CSSPropertyBreakAfter: + case CSSPropertyBreakBefore: + case CSSPropertyBreakInside: case CSSPropertyPointerEvents: case CSSPropertyPosition: case CSSPropertyResize: @@ -795,11 +792,8 @@ case CSSPropertyWebkitBoxLines: case CSSPropertyWebkitBoxOrient: case CSSPropertyWebkitBoxPack: - case CSSPropertyWebkitColumnBreakAfter: - case CSSPropertyWebkitColumnBreakBefore: - case CSSPropertyWebkitColumnBreakInside: case CSSPropertyColumnFill: - case CSSPropertyWebkitColumnRuleStyle: + case CSSPropertyColumnRuleStyle: case CSSPropertyFlexDirection: case CSSPropertyFlexWrap: case CSSPropertyFontKerning:
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp index 8dc3606..c1b512d 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -155,9 +155,9 @@ // '-internal-inactive-list-box-selection' // '-internal-inactive-list-box-selection-text' // '-webkit-focus-ring-color' - // '-webkit-text' + // '-internal-quirk-inherit' // - return (id >= CSSValueAqua && id <= CSSValueWebkitText) + return (id >= CSSValueAqua && id <= CSSValueInternalQuirkInherit) || (id >= CSSValueAliceblue && id <= CSSValueYellowgreen) || id == CSSValueMenu; } @@ -3501,13 +3501,13 @@ case CSSPropertyWebkitHyphenateCharacter: case CSSPropertyWebkitLocale: return consumeLocale(m_range); - case CSSPropertyWebkitColumnWidth: + case CSSPropertyColumnWidth: return consumeColumnWidth(m_range); - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: return consumeColumnCount(m_range); - case CSSPropertyWebkitColumnGap: + case CSSPropertyColumnGap: return consumeColumnGap(m_range, m_context.mode()); - case CSSPropertyWebkitColumnSpan: + case CSSPropertyColumnSpan: return consumeColumnSpan(m_range, m_context.mode()); case CSSPropertyZoom: return consumeZoom(m_range, m_context); @@ -3552,7 +3552,7 @@ case CSSPropertyStopColor: case CSSPropertyFloodColor: case CSSPropertyLightingColor: - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: return consumeColor(m_range, m_context.mode()); case CSSPropertyColor: return consumeColor(m_range, m_context.mode(), inQuirksMode()); @@ -3637,7 +3637,7 @@ return consumeNumber(m_range, ValueRangeNonNegative); case CSSPropertyStrokeDasharray: return consumeStrokeDasharray(m_range); - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: return consumeColumnRuleWidth(m_range, m_context.mode()); case CSSPropertyStrokeOpacity: case CSSPropertyFillOpacity: @@ -4088,8 +4088,8 @@ columnWidth = cssValuePool().createIdentifierValue(CSSValueAuto); if (!columnCount) columnCount = cssValuePool().createIdentifierValue(CSSValueAuto); - addProperty(CSSPropertyWebkitColumnWidth, columnWidth.release(), important); - addProperty(CSSPropertyWebkitColumnCount, columnCount.release(), important); + addProperty(CSSPropertyColumnWidth, columnWidth.release(), important); + addProperty(CSSPropertyColumnCount, columnCount.release(), important); return true; } @@ -4273,6 +4273,99 @@ return false; } +static inline CSSValueID mapFromPageBreakBetween(CSSValueID value) +{ + if (value == CSSValueAlways) + return CSSValuePage; + // TODO(mstensho): "avoid" should just return "avoid", not "avoid-page", according to the spec. + if (value == CSSValueAvoid) + return CSSValueAvoidPage; + if (value == CSSValueAuto || value == CSSValueLeft || value == CSSValueRight) + return value; + return CSSValueInvalid; +} + +static inline CSSValueID mapFromColumnBreakBetween(CSSValueID value) +{ + if (value == CSSValueAlways) + return CSSValueColumn; + // TODO(mstensho): "avoid" should just return "avoid", not "avoid-column". + if (value == CSSValueAvoid) + return CSSValueAvoidColumn; + // TODO(mstensho): column break properties shouldn't take 'left' and 'right' values (but + // allowing it for now, since that's what we've always done). + if (value == CSSValueAuto || value == CSSValueLeft || value == CSSValueRight) + return value; + return CSSValueInvalid; +} + +static inline CSSValueID mapFromPageBreakInside(CSSValueID value) +{ + // TODO(mstensho): "avoid" should just return "avoid", not "avoid-page", according to the spec. + if (value == CSSValueAvoid) + return CSSValueAvoidPage; + if (value == CSSValueAuto) + return value; + return CSSValueInvalid; +} + +static inline CSSValueID mapFromColumnBreakInside(CSSValueID value) +{ + // TODO(mstensho): "avoid" should just return "avoid", not "avoid-column". + if (value == CSSValueAvoid) + return CSSValueAvoidColumn; + if (value == CSSValueAuto) + return value; + return CSSValueInvalid; +} + +static inline CSSPropertyID mapFromLegacyBreakProperty(CSSPropertyID property) +{ + if (property == CSSPropertyPageBreakAfter || property == CSSPropertyWebkitColumnBreakAfter) + return CSSPropertyBreakAfter; + if (property == CSSPropertyPageBreakBefore || property == CSSPropertyWebkitColumnBreakBefore) + return CSSPropertyBreakBefore; + ASSERT(property == CSSPropertyPageBreakInside || property == CSSPropertyWebkitColumnBreakInside); + return CSSPropertyBreakInside; +} + +bool CSSPropertyParser::consumeLegacyBreakProperty(CSSPropertyID property, bool important) +{ + // The fragmentation spec says that page-break-(after|before|inside) are to be treated as + // shorthands for their break-(after|before|inside) counterparts. We'll do the same for the + // non-standard properties -webkit-column-break-(after|before|inside). + RefPtrWillBeRawPtr<CSSPrimitiveValue> keyword = consumeIdent(m_range); + if (!keyword) + return false; + if (!m_range.atEnd()) + return false; + CSSValueID value = keyword->getValueID(); + switch (property) { + case CSSPropertyPageBreakAfter: + case CSSPropertyPageBreakBefore: + value = mapFromPageBreakBetween(value); + break; + case CSSPropertyWebkitColumnBreakAfter: + case CSSPropertyWebkitColumnBreakBefore: + value = mapFromColumnBreakBetween(value); + break; + case CSSPropertyPageBreakInside: + value = mapFromPageBreakInside(value); + break; + case CSSPropertyWebkitColumnBreakInside: + value = mapFromColumnBreakInside(value); + break; + default: + ASSERT_NOT_REACHED(); + } + if (value == CSSValueInvalid) + return false; + + CSSPropertyID genericBreakProperty = mapFromLegacyBreakProperty(property); + addProperty(genericBreakProperty, cssValuePool().createIdentifierValue(value), important); + return true; +} + bool CSSPropertyParser::parseShorthand(CSSPropertyID unresolvedProperty, bool important) { CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty); @@ -4327,7 +4420,7 @@ } case CSSPropertyBorderSpacing: return consumeBorderSpacing(important); - case CSSPropertyWebkitColumns: { + case CSSPropertyColumns: { // TODO(rwlbuis): investigate if this shorthand hack can be removed. m_currentShorthand = oldShorthand; return consumeColumns(important); @@ -4373,8 +4466,8 @@ return consumeFlex(important); case CSSPropertyFlexFlow: return consumeShorthandGreedily(flexFlowShorthand(), important); - case CSSPropertyWebkitColumnRule: - return consumeShorthandGreedily(webkitColumnRuleShorthand(), important); + case CSSPropertyColumnRule: + return consumeShorthandGreedily(columnRuleShorthand(), important); case CSSPropertyListStyle: return consumeShorthandGreedily(listStyleShorthand(), important); case CSSPropertyBorderRadius: { @@ -4407,6 +4500,13 @@ case CSSPropertyBorderImage: case CSSPropertyWebkitMaskBoxImage: return consumeBorderImage(property, important); + case CSSPropertyPageBreakAfter: + case CSSPropertyPageBreakBefore: + case CSSPropertyPageBreakInside: + case CSSPropertyWebkitColumnBreakAfter: + case CSSPropertyWebkitColumnBreakBefore: + case CSSPropertyWebkitColumnBreakInside: + return consumeLegacyBreakProperty(property, important); default: m_currentShorthand = oldShorthand; CSSParserValueList valueList(m_range);
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.h b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.h index 76d203b6..a406cea 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.h +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.h
@@ -158,7 +158,8 @@ PassRefPtrWillBeRawPtr<CSSValue> parseGridPosition(); bool parseIntegerOrCustomIdentFromGridPosition(RefPtrWillBeRawPtr<CSSPrimitiveValue>& numericValue, RefPtrWillBeRawPtr<CSSCustomIdentValue>& gridLineName); bool parseGridItemPositionShorthand(CSSPropertyID, bool important); - bool parseGridTemplateRowsAndAreas(PassRefPtrWillBeRawPtr<CSSValue>, bool important); + PassRefPtrWillBeRawPtr<CSSValue> parseGridTemplateColumns(bool important); + bool parseGridTemplateRowsAndAreasAndColumns(bool important); bool parseGridTemplateShorthand(bool important); bool parseGridShorthand(bool important); bool parseGridAreaShorthand(bool important); @@ -191,6 +192,8 @@ bool consumeFlex(bool important); + bool consumeLegacyBreakProperty(CSSPropertyID, bool important); + // Image generators bool parseDeprecatedGradient(CSSParserValueList*, RefPtrWillBeRawPtr<CSSValue>&); bool parseDeprecatedLinearGradient(CSSParserValueList*, RefPtrWillBeRawPtr<CSSValue>&, CSSGradientRepeat repeating);
diff --git a/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp index f9359994..647911c 100644 --- a/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp
@@ -456,7 +456,7 @@ static bool parseBackgroundClip(CSSParserValue* parserValue, RefPtrWillBeRawPtr<CSSValue>& cssValue) { if (parserValue->id == CSSValueBorderBox || parserValue->id == CSSValuePaddingBox - || parserValue->id == CSSValueContentBox || parserValue->id == CSSValueWebkitText) { + || parserValue->id == CSSValueContentBox) { cssValue = cssValuePool().createIdentifierValue(parserValue->id); return true; } @@ -624,8 +624,6 @@ if (isColorKeyword(id)) { if (!isValueAllowedInMode(id, m_context.mode())) return nullptr; - if (id == CSSValueWebkitText && m_context.useCounter()) - m_context.useCounter()->count(UseCounter::WebkitTextInColorProperty); return cssValuePool().createIdentifierValue(id); } RGBA32 c = Color::transparent; @@ -1149,19 +1147,14 @@ if (val->id == CSSValueBorder || val->id == CSSValuePadding || val->id == CSSValueContent || val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox || ((propId == CSSPropertyWebkitBackgroundClip || propId == CSSPropertyWebkitMaskClip) - && (val->id == CSSValueText || val->id == CSSValueWebkitText))) { - if (val->id == CSSValueWebkitText && m_context.useCounter()) - m_context.useCounter()->count(UseCounter::WebkitTextInClipProperty); + && val->id == CSSValueText)) { currValue = cssValuePool().createIdentifierValue(val->id); m_valueList->next(); } break; case CSSPropertyBackgroundClip: - if (parseBackgroundClip(val, currValue)) { - if (val->id == CSSValueWebkitText && m_context.useCounter()) - m_context.useCounter()->count(UseCounter::WebkitTextInClipProperty); + if (parseBackgroundClip(val, currValue)) m_valueList->next(); - } break; case CSSPropertyBackgroundOrigin: if (val->id == CSSValueBorderBox || val->id == CSSValuePaddingBox || val->id == CSSValueContentBox) { @@ -1449,7 +1442,20 @@ return true; } -bool CSSPropertyParser::parseGridTemplateRowsAndAreas(PassRefPtrWillBeRawPtr<CSSValue> templateColumns, bool important) +PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseGridTemplateColumns(bool important) +{ + if (!(m_valueList->current() && isForwardSlashOperator(m_valueList->current()) && m_valueList->next())) + return nullptr; + if (RefPtrWillBeRawPtr<CSSValue> columnsValue = parseGridTrackList()) { + if (m_valueList->current()) + return nullptr; + return columnsValue; + } + + return nullptr; +} + +bool CSSPropertyParser::parseGridTemplateRowsAndAreasAndColumns(bool important) { NamedGridAreaMap gridAreaMap; size_t rowCount = 0; @@ -1458,10 +1464,10 @@ RefPtrWillBeRawPtr<CSSValueList> templateRows = CSSValueList::createSpaceSeparated(); // At least template-areas strings must be defined. - if (!m_valueList->current()) + if (!m_valueList->current() || isForwardSlashOperator(m_valueList->current())) return false; - while (m_valueList->current()) { + while (m_valueList->current() && !isForwardSlashOperator(m_valueList->current())) { // Handle leading <custom-ident>*. if (!parseGridLineNames(*m_valueList, *templateRows, trailingIdentWasAdded ? toCSSGridLineNamesValue(templateRows->item(templateRows->length() - 1)) : nullptr)) return false; @@ -1472,7 +1478,7 @@ ++rowCount; // Handle template-rows's track-size. - if (m_valueList->current() && m_valueList->current()->m_unit != CSSParserValue::String) { + if (m_valueList->current() && !isForwardSlashOperator(m_valueList->current()) && m_valueList->current()->m_unit != CSSParserValue::String) { RefPtrWillBeRawPtr<CSSValue> value = parseGridTrackSize(*m_valueList); if (!value) return false; @@ -1487,16 +1493,22 @@ trailingIdentWasAdded = templateRows->item(templateRows->length() - 1)->isGridLineNamesValue(); } - // [<track-list> /]? - if (templateColumns) - addProperty(CSSPropertyGridTemplateColumns, templateColumns, important); - else - addProperty(CSSPropertyGridTemplateColumns, cssValuePool().createIdentifierValue(CSSValueNone), important); + RefPtrWillBeRawPtr<CSSValue> columnsValue = nullptr; + if (m_valueList->current()) { + ASSERT(isForwardSlashOperator(m_valueList->current())); + columnsValue = parseGridTemplateColumns(important); + if (!columnsValue) + return false; + // The template-columns <track-list> can't be 'none'. + if (columnsValue->isPrimitiveValue() && toCSSPrimitiveValue(*columnsValue).getValueID() == CSSValueNone) + return false; + } - // [<line-names>? <string> [<track-size> <line-names>]? ]+ + addProperty(CSSPropertyGridTemplateRows, templateRows.release(), important); + addProperty(CSSPropertyGridTemplateColumns, columnsValue ? columnsValue.release() : cssValuePool().createIdentifierValue(CSSValueNone), important); + RefPtrWillBeRawPtr<CSSValue> templateAreas = CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount); addProperty(CSSPropertyGridTemplateAreas, templateAreas.release(), important); - addProperty(CSSPropertyGridTemplateRows, templateRows.release(), important); return true; } @@ -1523,37 +1535,29 @@ return true; } - unsigned index = 0; - RefPtrWillBeRawPtr<CSSValue> columnsValue = nullptr; + // 2- <grid-template-rows> / <grid-template-columns> + RefPtrWillBeRawPtr<CSSValue> rowsValue = nullptr; if (firstValueIsNone) { - columnsValue = cssValuePool().createIdentifierValue(CSSValueNone); + rowsValue = cssValuePool().createIdentifierValue(CSSValueNone); } else { - columnsValue = parseGridTrackList(); + rowsValue = parseGridTrackList(); } - // 2- <grid-template-columns> / <grid-template-columns> syntax. - if (columnsValue) { - if (!(m_valueList->current() && isForwardSlashOperator(m_valueList->current()) && m_valueList->next())) + if (rowsValue) { + RefPtrWillBeRawPtr<CSSValue> columnsValue = parseGridTemplateColumns(important); + if (!columnsValue) return false; - index = m_valueList->currentIndex(); - if (RefPtrWillBeRawPtr<CSSValue> rowsValue = parseGridTrackList()) { - if (m_valueList->current()) - return false; - addProperty(CSSPropertyGridTemplateColumns, columnsValue, important); - addProperty(CSSPropertyGridTemplateRows, rowsValue, important); - addProperty(CSSPropertyGridTemplateAreas, cssValuePool().createIdentifierValue(CSSValueNone), important); - return true; - } + + addProperty(CSSPropertyGridTemplateRows, rowsValue.release(), important); + addProperty(CSSPropertyGridTemplateColumns, columnsValue.release(), important); + addProperty(CSSPropertyGridTemplateAreas, cssValuePool().createIdentifierValue(CSSValueNone), important); + return true; } - - // 3- [<track-list> /]? [<line-names>? <string> [<track-size> <line-names>]? ]+ syntax. - // The template-columns <track-list> can't be 'none'. - if (firstValueIsNone) - return false; + // 3- [<line-names>? <string> [<track-size> <line-names>]? ]+ syntax. // It requires to rewind parsing due to previous syntax failures. - m_valueList->setCurrentIndex(index); - return parseGridTemplateRowsAndAreas(columnsValue, important); + m_valueList->setCurrentIndex(0); + return parseGridTemplateRowsAndAreasAndColumns(important); } bool CSSPropertyParser::parseGridShorthand(bool important)
diff --git a/third_party/WebKit/Source/core/css/quirks.css b/third_party/WebKit/Source/core/css/quirks.css index 38e3ac83..90f654c 100644 --- a/third_party/WebKit/Source/core/css/quirks.css +++ b/third_party/WebKit/Source/core/css/quirks.css
@@ -38,7 +38,7 @@ font-size: medium; font-variant: normal; font-style: normal; - color: -webkit-text; + color: -internal-quirk-inherit; text-align: start; }
diff --git a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp index ef9d714..b396723 100644 --- a/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp +++ b/third_party/WebKit/Source/core/css/resolver/AnimatedStyleBuilder.cpp
@@ -545,20 +545,20 @@ case CSSPropertyWebkitClipPath: style->setClipPath(toAnimatableClipPathOperation(value)->clipPathOperation()); return; - case CSSPropertyWebkitColumnCount: + case CSSPropertyColumnCount: style->setColumnCount(clampTo<unsigned short>(round(toAnimatableDouble(value)->toDouble()), 1)); return; - case CSSPropertyWebkitColumnGap: + case CSSPropertyColumnGap: style->setColumnGap(clampTo(toAnimatableDouble(value)->toDouble(), 0)); return; - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: style->setColumnRuleColor(toAnimatableColor(value)->color()); style->setVisitedLinkColumnRuleColor(toAnimatableColor(value)->visitedLinkColor()); return; - case CSSPropertyWebkitColumnWidth: + case CSSPropertyColumnWidth: style->setColumnWidth(clampTo(toAnimatableDouble(value)->toDouble(), std::numeric_limits<float>::epsilon())); return; - case CSSPropertyWebkitColumnRuleWidth: + case CSSPropertyColumnRuleWidth: style->setColumnRuleWidth(animatableLineWidthClamp<unsigned short>(value)); return; case CSSPropertyWebkitFilter:
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.cpp b/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.cpp index b91b835..75fd9d03 100644 --- a/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.cpp +++ b/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.cpp
@@ -630,6 +630,11 @@ StyleMotionRotation StyleBuilderConverter::convertMotionRotation(StyleResolverState&, const CSSValue& value) { + return convertMotionRotation(value); +} + +StyleMotionRotation StyleBuilderConverter::convertMotionRotation(const CSSValue& value) +{ StyleMotionRotation result(0, MotionRotationFixed); const CSSValueList& list = toCSSValueList(value);
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.h b/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.h index 130ece80..101b00f 100644 --- a/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.h +++ b/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.h
@@ -109,6 +109,7 @@ static RespectImageOrientationEnum convertImageOrientation(StyleResolverState&, const CSSValue&); static PassRefPtr<StylePath> convertPath(StyleResolverState&, const CSSValue&); static PassRefPtr<StylePath> convertPathOrNone(StyleResolverState&, const CSSValue&); + static StyleMotionRotation convertMotionRotation(const CSSValue&); }; template <typename T>
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp b/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp index 620d6d5..31d335f 100644 --- a/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp +++ b/third_party/WebKit/Source/core/css/resolver/StyleBuilderCustom.cpp
@@ -95,7 +95,7 @@ case CSSPropertyOutlineColor: case CSSPropertyStroke: case CSSPropertyTextDecorationColor: - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: case CSSPropertyWebkitTextEmphasisColor: case CSSPropertyWebkitTextFillColor: case CSSPropertyWebkitTextStrokeColor:
diff --git a/third_party/WebKit/Source/core/dom/AXObjectCache.h b/third_party/WebKit/Source/core/dom/AXObjectCache.h index c8df706..b39d30c 100644 --- a/third_party/WebKit/Source/core/dom/AXObjectCache.h +++ b/third_party/WebKit/Source/core/dom/AXObjectCache.h
@@ -46,7 +46,7 @@ public: static AXObjectCache* create(Document&); - static AXObject* focusedUIElementForPage(const Page*); + static AXObject* focusedObject(); virtual ~AXObjectCache(); DEFINE_INLINE_VIRTUAL_TRACE() { }
diff --git a/third_party/WebKit/Source/core/dom/DOMTokenList.h b/third_party/WebKit/Source/core/dom/DOMTokenList.h index f0d3661..e93e20c4 100644 --- a/third_party/WebKit/Source/core/dom/DOMTokenList.h +++ b/third_party/WebKit/Source/core/dom/DOMTokenList.h
@@ -59,6 +59,11 @@ virtual ~DOMTokenList() { } +#if !ENABLE(OILPAN) + virtual void ref() { RefCounted<DOMTokenList>::ref(); } + virtual void deref() { RefCounted<DOMTokenList>::deref(); } +#endif + virtual unsigned length() const { return m_tokens.size(); } virtual const AtomicString item(unsigned index) const;
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp index 52379a8..757f46e1 100644 --- a/third_party/WebKit/Source/core/dom/Document.cpp +++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -637,7 +637,7 @@ void Document::mediaQueryAffectingValueChanged() { - styleResolverChanged(); + styleEngine().resolverChanged(FullStyleUpdate); m_evaluateMediaQueriesOnStyleRecalc = true; styleEngine().clearMediaQueryRuleSetStyleSheets(); InspectorInstrumentation::mediaQueryResultChanged(this); @@ -1107,13 +1107,6 @@ return AtomicString(encoding().name()); } -String Document::defaultCharset() const -{ - if (Settings* settings = this->settings()) - return settings->defaultTextEncodingName(); - return String(); -} - void Document::setContentLanguage(const AtomicString& language) { if (m_contentLanguage == language) @@ -2015,7 +2008,7 @@ HTMLElement* bodyElement = body(); if (bodyElement && !bodyElement->layoutObject() && m_pendingSheetLayout == NoLayoutWithPendingSheets) { m_pendingSheetLayout = DidLayoutWithPendingSheets; - styleResolverChanged(); + styleEngine().resolverChanged(FullStyleUpdate); } else if (m_hasNodesWithPlaceholderStyle) { // If new nodes have been added or style recalc has been done with style sheets still // pending, some nodes may not have had their real style calculated yet. Normally this @@ -3447,7 +3440,7 @@ void Document::setSelectedStylesheetSet(const String& aString) { styleEngine().setSelectedStylesheetSetName(aString); - styleResolverChanged(); + styleEngine().resolverChanged(FullStyleUpdate); } void Document::evaluateMediaQueryListIfNeeded() @@ -3474,9 +3467,9 @@ setNeedsStyleRecalcForViewportUnits(); } -void Document::styleResolverChanged(StyleResolverUpdateMode updateMode) +void Document::styleResolverMayHaveChanged() { - styleEngine().resolverChanged(updateMode); + styleEngine().resolverChanged(hasNodesWithPlaceholderStyle() ? FullStyleUpdate : AnalyzedStyleUpdate); if (didLayoutWithPendingStylesheets() && !styleEngine().hasPendingSheets()) { // We need to manually repaint because we avoid doing all repaints in layout or style @@ -3489,11 +3482,6 @@ } } -void Document::styleResolverMayHaveChanged() -{ - styleResolverChanged(hasNodesWithPlaceholderStyle() ? FullStyleUpdate : AnalyzedStyleUpdate); -} - void Document::setHoverNode(PassRefPtrWillBeRawPtr<Node> newHoverNode) { m_hoverNode = newHoverNode; @@ -4989,7 +4977,8 @@ bool Document::allowInlineEventHandlers(Node* node, EventListener* listener, const String& contextURL, const WTF::OrdinalNumber& contextLine) { - if (!ContentSecurityPolicy::shouldBypassMainWorld(this) && !contentSecurityPolicy()->allowInlineEventHandlers(contextURL, contextLine)) + bool allowedByHash = contentSecurityPolicy()->experimentalFeaturesEnabled() && contentSecurityPolicy()->allowScriptWithHash(listener->code()); + if (!ContentSecurityPolicy::shouldBypassMainWorld(this) && !allowedByHash && !contentSecurityPolicy()->allowInlineEventHandlers(contextURL, contextLine)) return false; // HTML says that inline script needs browsing context to create its execution environment. @@ -5686,21 +5675,31 @@ void Document::removedStyleSheet(StyleSheet* sheet, StyleResolverUpdateMode updateMode) { // If we're in document teardown, then we don't need this notification of our sheet's removal. - // styleResolverChanged() is needed even when the document is inactive so that - // imported docuements (which is inactive) notifies the change to the master document. + // resolverChanged() is needed even when the document is inactive so that imported documents + // (which are inactive) notify the change to the master document. if (isActive()) styleEngine().modifiedStyleSheet(sheet); - styleResolverChanged(updateMode); + styleEngine().resolverChanged(updateMode); +} + +void Document::addedStyleSheet(StyleSheet*) +{ + styleEngine().resolverChanged(FullStyleUpdate); } void Document::modifiedStyleSheet(StyleSheet* sheet, StyleResolverUpdateMode updateMode) { // If we're in document teardown, then we don't need this notification of our sheet's removal. - // styleResolverChanged() is needed even when the document is inactive so that - // imported docuements (which is inactive) notifies the change to the master document. + // resolverChanged() is needed even when the document is inactive so that imported documents + // (which are inactive) notify the change to the master document. if (isActive()) styleEngine().modifiedStyleSheet(sheet); - styleResolverChanged(updateMode); + styleEngine().resolverChanged(updateMode); +} + +void Document::changedSelectorWatch() +{ + styleEngine().resolverChanged(FullStyleUpdate); } TextAutosizer* Document::textAutosizer()
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h index 584327a..3b581fb 100644 --- a/third_party/WebKit/Source/core/dom/Document.h +++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -303,8 +303,6 @@ String readyState() const; - String defaultCharset() const; - AtomicString characterSet() const { return Document::encodingName(); } AtomicString encodingName() const; @@ -387,16 +385,12 @@ bool gotoAnchorNeededAfterStylesheetsLoad() { return m_gotoAnchorNeededAfterStylesheetsLoad; } void setGotoAnchorNeededAfterStylesheetsLoad(bool b) { m_gotoAnchorNeededAfterStylesheetsLoad = b; } - // Called when one or more stylesheets in the document may have been added, removed, or changed. - void styleResolverChanged(StyleResolverUpdateMode = FullStyleUpdate); - void styleResolverMayHaveChanged(); - - // FIXME: Switch all callers of styleResolverChanged to these or better ones and then make them + // FIXME: Switch all callers of resolverChanged to these or better ones and then make them // do something smarter. void removedStyleSheet(StyleSheet*, StyleResolverUpdateMode = FullStyleUpdate); - void addedStyleSheet(StyleSheet*) { styleResolverChanged(); } + void addedStyleSheet(StyleSheet*); void modifiedStyleSheet(StyleSheet*, StyleResolverUpdateMode = FullStyleUpdate); - void changedSelectorWatch() { styleResolverChanged(); } + void changedSelectorWatch(); void scheduleUseShadowTreeUpdate(SVGUseElement&); void unscheduleUseShadowTreeUpdate(SVGUseElement&); @@ -1160,6 +1154,7 @@ void clearFocusedElementTimerFired(Timer<Document>*); bool haveStylesheetsLoaded() const; + void styleResolverMayHaveChanged(); void setHoverNode(PassRefPtrWillBeRawPtr<Node>);
diff --git a/third_party/WebKit/Source/core/dom/Document.idl b/third_party/WebKit/Source/core/dom/Document.idl index 2a864291..aafcdaf 100644 --- a/third_party/WebKit/Source/core/dom/Document.idl +++ b/third_party/WebKit/Source/core/dom/Document.idl
@@ -195,7 +195,6 @@ readonly attribute VisibilityState visibilityState; // Non-standard APIs - [DeprecateAs=DocumentDefaultCharset, TreatReturnedNullStringAs=Undefined] readonly attribute DOMString defaultCharset; [MeasureAs=DocumentCaretRangeFromPoint] Range caretRangeFromPoint([Default=Undefined] optional long x, [Default=Undefined] optional long y); // Deprecated prefixed page visibility API.
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp index bcabb7c6..8533007 100644 --- a/third_party/WebKit/Source/core/dom/Element.cpp +++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -2361,9 +2361,9 @@ return; // Slide the focus to its inner node. - Element* next = document().page()->focusController().findFocusableElement(WebFocusTypeForward, *this); - if (next && containsIncludingShadowDOM(next)) { - next->focus(FocusParams(SelectionBehaviorOnFocus::Reset, WebFocusTypeForward, nullptr)); + Element* found = document().page()->focusController().findFocusableElementInShadowHost(*this); + if (found && containsIncludingShadowDOM(found)) { + found->focus(FocusParams(SelectionBehaviorOnFocus::Reset, WebFocusTypeForward, nullptr)); return; } }
diff --git a/third_party/WebKit/Source/core/dom/ElementRareData.h b/third_party/WebKit/Source/core/dom/ElementRareData.h index d09bff66..f243985 100644 --- a/third_party/WebKit/Source/core/dom/ElementRareData.h +++ b/third_party/WebKit/Source/core/dom/ElementRareData.h
@@ -88,7 +88,7 @@ void clearComputedStyle() { m_computedStyle = nullptr; } ClassList* classList() const { return m_classList.get(); } - void setClassList(PassRefPtrWillBeRawPtr<ClassList> classList) { m_classList = classList; } + void setClassList(PassRefPtrWillBeRawPtr<ClassList> classList) { m_classList = classList.leakRef(); } void clearClassListValueForQuirksMode() { if (!m_classList) @@ -146,7 +146,7 @@ OwnPtrWillBeMember<DatasetDOMStringMap> m_dataset; OwnPtrWillBeMember<ElementShadow> m_shadow; - RefPtrWillBeMember<ClassList> m_classList; + RawPtrWillBeMember<ClassList> m_classList; OwnPtrWillBeMember<NamedNodeMap> m_attributeMap; OwnPtrWillBeMember<AttrNodeList> m_attrNodeList; OwnPtrWillBeMember<InlineCSSStyleDeclaration> m_cssomWrapper; @@ -175,6 +175,7 @@ : NodeRareData(layoutObject) , m_tabindex(0) , m_minimumSizeForResizing(defaultMinimumSizeForResizing()) + , m_classList(nullptr) { m_isElementRareData = true; }
diff --git a/third_party/WebKit/Source/core/dom/Node.cpp b/third_party/WebKit/Source/core/dom/Node.cpp index f1caf2f..7e548db 100644 --- a/third_party/WebKit/Source/core/dom/Node.cpp +++ b/third_party/WebKit/Source/core/dom/Node.cpp
@@ -86,6 +86,7 @@ #include "core/page/Page.h" #include "core/svg/graphics/SVGImage.h" #include "platform/EventDispatchForbiddenScope.h" +#include "platform/RuntimeEnabledFeatures.h" #include "platform/TraceEvent.h" #include "platform/TracedValue.h" #include "wtf/HashSet.h" @@ -691,7 +692,7 @@ void Node::markAncestorsWithChildNeedsDistributionRecalc() { ScriptForbiddenScope forbidScriptDuringRawIteration; - if (inDocument() && !document().childNeedsDistributionRecalc()) { + if (RuntimeEnabledFeatures::shadowDOMV1Enabled() && inDocument() && !document().childNeedsDistributionRecalc()) { // TODO(hayato): Support a non-document composed tree. // TODO(hayato): Enqueue a task only if a 'slotchange' event listner is registered in the document composed tree. Microtask::enqueueMicrotask(WTF::bind(&Document::updateDistribution, PassRefPtrWillBeRawPtr<Document>(&document())));
diff --git a/third_party/WebKit/Source/core/dom/Range.cpp b/third_party/WebKit/Source/core/dom/Range.cpp index 322ab36a..257d7cf 100644 --- a/third_party/WebKit/Source/core/dom/Range.cpp +++ b/third_party/WebKit/Source/core/dom/Range.cpp
@@ -1635,8 +1635,16 @@ getBorderAndTextQuads(quads); FloatRect result; - for (const FloatQuad& quad : quads) - result.unite(quad.boundingBox()); + // As per section 10 in https://www.w3.org/TR/cssom-view/ + // "Return a static DOMRect object describing the smallest rectangle that + // includes the first rectangle in list and all of the remaining rectangles + // of which the height or width is not zero." + for (const FloatQuad& quad : quads) { + if (result.isEmpty()) + result.uniteIfNonZero(quad.boundingBox()); + else + result.unite(quad.boundingBox()); // Skips empty rects. + } return result; }
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp index b43eaed..a40a516 100644 --- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp +++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -439,7 +439,7 @@ { if (!isMaster()) { if (Document* master = this->master()) - master->styleResolverChanged(mode); + master->styleEngine().resolverChanged(mode); return; }
diff --git a/third_party/WebKit/Source/core/dom/TextLinkColors.cpp b/third_party/WebKit/Source/core/dom/TextLinkColors.cpp index c2c7ea31..0bf89d10 100644 --- a/third_party/WebKit/Source/core/dom/TextLinkColors.cpp +++ b/third_party/WebKit/Source/core/dom/TextLinkColors.cpp
@@ -67,7 +67,7 @@ switch (valueID) { case 0: return Color(); - case CSSValueWebkitText: + case CSSValueInternalQuirkInherit: return textColor(); case CSSValueWebkitLink: return forVisitedLink ? visitedLinkColor() : linkColor();
diff --git a/third_party/WebKit/Source/core/editing/EditingStyle.cpp b/third_party/WebKit/Source/core/editing/EditingStyle.cpp index 4d54ad90..fa9326c 100644 --- a/third_party/WebKit/Source/core/editing/EditingStyle.cpp +++ b/third_party/WebKit/Source/core/editing/EditingStyle.cpp
@@ -603,17 +603,20 @@ // This is the list of CSS properties that apply specially to block-level elements. static const CSSPropertyID staticBlockProperties[] = { + CSSPropertyBreakAfter, + CSSPropertyBreakBefore, + CSSPropertyBreakInside, CSSPropertyOrphans, CSSPropertyOverflow, // This can be also be applied to replaced elements - CSSPropertyWebkitColumnCount, - CSSPropertyWebkitColumnGap, - CSSPropertyWebkitColumnRuleColor, - CSSPropertyWebkitColumnRuleStyle, - CSSPropertyWebkitColumnRuleWidth, + CSSPropertyColumnCount, + CSSPropertyColumnGap, + CSSPropertyColumnRuleColor, + CSSPropertyColumnRuleStyle, + CSSPropertyColumnRuleWidth, CSSPropertyWebkitColumnBreakBefore, CSSPropertyWebkitColumnBreakAfter, CSSPropertyWebkitColumnBreakInside, - CSSPropertyWebkitColumnWidth, + CSSPropertyColumnWidth, CSSPropertyPageBreakAfter, CSSPropertyPageBreakBefore, CSSPropertyPageBreakInside,
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp index 8383712..b013bcc4 100644 --- a/third_party/WebKit/Source/core/editing/Editor.cpp +++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -830,9 +830,7 @@ VisiblePosition caret = frame().selection().selection().visibleStart(); bool alignToEdge = isEndOfEditableOrNonEditableContent(caret); ASSERT(frame().document()); - EditingState editingState; - TypingCommand::insertLineBreak(*frame().document(), 0, &editingState); - if (editingState.isAborted()) + if (!TypingCommand::insertLineBreak(*frame().document(), 0)) return false; revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded); @@ -851,8 +849,7 @@ bool alignToEdge = isEndOfEditableOrNonEditableContent(caret); ASSERT(frame().document()); EditingState editingState; - TypingCommand::insertParagraphSeparator(*frame().document(), 0, &editingState); - if (editingState.isAborted()) + if (!TypingCommand::insertParagraphSeparator(*frame().document(), 0)) return false; revealSelectionAfterEditingOperation(alignToEdge ? ScrollAlignment::alignToEdgeIfNeeded : ScrollAlignment::alignCenterIfNeeded);
diff --git a/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp b/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp index 012881a0..382f304 100644 --- a/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp
@@ -177,7 +177,7 @@ ASSERT(isTopLevelCommand() || !m_composition); } -void CompositeEditCommand::apply() +bool CompositeEditCommand::apply() { if (!endingSelection().isContentRichlyEditable()) { switch (editingAction()) { @@ -190,7 +190,7 @@ break; default: ASSERT_NOT_REACHED(); - return; + return false; } } ensureComposition(); @@ -202,9 +202,9 @@ LocalFrame* frame = document().frame(); ASSERT(frame); + EditingState editingState; { EventQueueScope eventQueueScope; - EditingState editingState; doApply(&editingState); } @@ -213,6 +213,7 @@ if (!isTypingCommand()) frame->editor().appliedEditing(this); setShouldRetainAutocorrectionIndicator(false); + return !editingState.isAborted(); } EditCommandComposition* CompositeEditCommand::ensureComposition() @@ -320,7 +321,7 @@ void CompositeEditCommand::insertNodeBefore(PassRefPtrWillBeRawPtr<Node> insertChild, PassRefPtrWillBeRawPtr<Node> refChild, EditingState* editingState, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) { ASSERT(document().body() != refChild); - ASSERT_IN_EDITING_COMMAND(refChild->parentNode()->hasEditableStyle() || !refChild->parentNode()->inActiveDocument()); + ABORT_EDITING_COMMAND_IF(!refChild->parentNode()->hasEditableStyle() && refChild->parentNode()->inActiveDocument()); applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable), editingState); } @@ -342,7 +343,7 @@ void CompositeEditCommand::insertNodeAt(PassRefPtrWillBeRawPtr<Node> insertChild, const Position& editingPosition, EditingState* editingState) { - ASSERT_IN_EDITING_COMMAND(isEditablePosition(editingPosition, ContentIsEditable, DoNotUpdateStyle)); + ABORT_EDITING_COMMAND_IF(!isEditablePosition(editingPosition, ContentIsEditable, DoNotUpdateStyle)); // For editing positions like [table, 0], insert before the table, // likewise for replaced elements, brs, etc. Position p = editingPosition.parentAnchoredEquivalent(); @@ -377,14 +378,14 @@ // of an OBJECT element, the ASSERT below may fire since the return // value of canHaveChildrenForEditing is not reliable until the layout // object of the OBJECT is created. Hence we ignore this check for OBJECTs. - // TODO(yosin): We should move following |ASSERT_IN_EDITING_COMMAND|s to + // TODO(yosin): We should move following |ABORT_EDITING_COMMAND_IF|s to // |AppendNodeCommand|. // TODO(yosin): We should get rid of |canHaveChildrenForEditing()|, since // |cloneParagraphUnderNewElement()| attempt to clone non-well-formed HTML, // produced by JavaScript. - ASSERT_IN_EDITING_COMMAND(canHaveChildrenForEditing(parent.get()) - || (parent->isElementNode() && toElement(parent.get())->tagQName() == objectTag)); - ASSERT_IN_EDITING_COMMAND(parent->hasEditableStyle() || !parent->inActiveDocument()); + ABORT_EDITING_COMMAND_IF(!canHaveChildrenForEditing(parent.get()) + && !(parent->isElementNode() && toElement(parent.get())->tagQName() == objectTag)); + ABORT_EDITING_COMMAND_IF(!parent->hasEditableStyle() && parent->inActiveDocument()); applyCommandToComposite(AppendNodeCommand::create(parent, node), editingState); } @@ -407,13 +408,13 @@ { if (!node || !node->nonShadowBoundaryParentNode()) return; - ASSERT_IN_EDITING_COMMAND(node->document().frame()); + ABORT_EDITING_COMMAND_IF(!node->document().frame()); applyCommandToComposite(RemoveNodeCommand::create(node, shouldAssumeContentIsAlwaysEditable), editingState); } void CompositeEditCommand::removeNodePreservingChildren(PassRefPtrWillBeRawPtr<Node> node, EditingState* editingState, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) { - ASSERT_IN_EDITING_COMMAND(node->document().frame()); + ABORT_EDITING_COMMAND_IF(!node->document().frame()); applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable), editingState); }
diff --git a/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.h b/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.h index 083381c..d3875cc6 100644 --- a/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.h +++ b/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.h
@@ -76,7 +76,8 @@ public: ~CompositeEditCommand() override; - void apply(); + // Returns |false| if the command failed. e.g. It's aborted. + bool apply(); bool isFirstCommand(EditCommand* command) { return !m_commands.isEmpty() && m_commands.first() == command; } EditCommandComposition* composition() { return m_composition.get(); } EditCommandComposition* ensureComposition();
diff --git a/third_party/WebKit/Source/core/editing/commands/EditingState.h b/third_party/WebKit/Source/core/editing/commands/EditingState.h index c4da581..ab20a87 100644 --- a/third_party/WebKit/Source/core/editing/commands/EditingState.h +++ b/third_party/WebKit/Source/core/editing/commands/EditingState.h
@@ -52,17 +52,14 @@ EditingState m_editingState; }; -// All assertion in "core/editing/commands" should use -// |ASSERT_IN_EDITING_COMMAND()| instead of |ASSERT()|. -// TODO(tkent): Rename this because this works without ENABLE(ASSERT). -#define ASSERT_IN_EDITING_COMMAND(expr) \ +// Abort the editing command if the specified expression is true. +#define ABORT_EDITING_COMMAND_IF(expr) \ do { \ - if (!(expr)) { \ + if (expr) { \ editingState->abort(); \ return; \ } \ - break; \ - } while (true) + } while (false) #if ENABLE(ASSERT) // This class is inspired by |NoExceptionStateAssertionChecker|.
diff --git a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp index 5dcaaa3..d7b2d693 100644 --- a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
@@ -198,8 +198,7 @@ static bool executeInsertFragment(LocalFrame& frame, PassRefPtrWillBeRawPtr<DocumentFragment> fragment) { ASSERT(frame.document()); - ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionUnspecified)->apply(); - return true; + return ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionUnspecified)->apply(); } static bool executeInsertElement(LocalFrame& frame, PassRefPtrWillBeRawPtr<HTMLElement> content) @@ -324,8 +323,7 @@ if (value.isEmpty()) return false; ASSERT(frame.document()); - CreateLinkCommand::create(*frame.document(), value)->apply(); - return true; + return CreateLinkCommand::create(*frame.document(), value)->apply(); } static bool executeCut(LocalFrame& frame, Event*, EditorCommandSource source, const String&) @@ -518,8 +516,7 @@ static bool executeIndent(LocalFrame& frame, Event*, EditorCommandSource, const String&) { ASSERT(frame.document()); - IndentOutdentCommand::create(*frame.document(), IndentOutdentCommand::Indent)->apply(); - return true; + return IndentOutdentCommand::create(*frame.document(), IndentOutdentCommand::Indent)->apply(); } static bool executeInsertBacktab(LocalFrame& frame, Event* event, EditorCommandSource, const String&) @@ -561,9 +558,7 @@ // InsertLineBreak is not implemented in IE or Firefox, so this behavior is only needed for // backward compatibility with ourselves, and for consistency with other commands. ASSERT(frame.document()); - EditingState editingState; - TypingCommand::insertLineBreak(*frame.document(), 0, &editingState); - return !editingState.isAborted(); + return TypingCommand::insertLineBreak(*frame.document(), 0); } ASSERT_NOT_REACHED(); return false; @@ -578,24 +573,19 @@ static bool executeInsertNewlineInQuotedContent(LocalFrame& frame, Event*, EditorCommandSource, const String&) { ASSERT(frame.document()); - EditingState editingState; - TypingCommand::insertParagraphSeparatorInQuotedContent(*frame.document(), &editingState); - return !editingState.isAborted(); + return TypingCommand::insertParagraphSeparatorInQuotedContent(*frame.document()); } static bool executeInsertOrderedList(LocalFrame& frame, Event*, EditorCommandSource, const String&) { ASSERT(frame.document()); - InsertListCommand::create(*frame.document(), InsertListCommand::OrderedList)->apply(); - return true; + return InsertListCommand::create(*frame.document(), InsertListCommand::OrderedList)->apply(); } static bool executeInsertParagraph(LocalFrame& frame, Event*, EditorCommandSource, const String&) { ASSERT(frame.document()); - EditingState editingState; - TypingCommand::insertParagraphSeparator(*frame.document(), 0, &editingState); - return !editingState.isAborted(); + return TypingCommand::insertParagraphSeparator(*frame.document(), 0); } static bool executeInsertTab(LocalFrame& frame, Event* event, EditorCommandSource, const String&) @@ -613,8 +603,7 @@ static bool executeInsertUnorderedList(LocalFrame& frame, Event*, EditorCommandSource, const String&) { ASSERT(frame.document()); - InsertListCommand::create(*frame.document(), InsertListCommand::UnorderedList)->apply(); - return true; + return InsertListCommand::create(*frame.document(), InsertListCommand::UnorderedList)->apply(); } static bool executeJustifyCenter(LocalFrame& frame, Event*, EditorCommandSource source, const String&) @@ -962,8 +951,7 @@ static bool executeOutdent(LocalFrame& frame, Event*, EditorCommandSource, const String&) { ASSERT(frame.document()); - IndentOutdentCommand::create(*frame.document(), IndentOutdentCommand::Outdent)->apply(); - return true; + return IndentOutdentCommand::create(*frame.document(), IndentOutdentCommand::Outdent)->apply(); } static bool executeToggleOverwrite(LocalFrame& frame, Event*, EditorCommandSource, const String&) @@ -1183,8 +1171,7 @@ static bool executeUnlink(LocalFrame& frame, Event*, EditorCommandSource, const String&) { ASSERT(frame.document()); - UnlinkCommand::create(*frame.document())->apply(); - return true; + return UnlinkCommand::create(*frame.document())->apply(); } static bool executeUnscript(LocalFrame& frame, Event*, EditorCommandSource source, const String&)
diff --git a/third_party/WebKit/Source/core/editing/commands/RemoveNodeCommand.cpp b/third_party/WebKit/Source/core/editing/commands/RemoveNodeCommand.cpp index ddc352d..636dd916 100644 --- a/third_party/WebKit/Source/core/editing/commands/RemoveNodeCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/RemoveNodeCommand.cpp
@@ -56,8 +56,8 @@ // Node::remove dispatch synchronous events such as IFRAME unload events, // and event handlers may break the document. We check the document state // here in order to prevent further processing in bad situation. - ASSERT_IN_EDITING_COMMAND(m_node->document().frame()); - ASSERT_IN_EDITING_COMMAND(m_node->document().documentElement()); + ABORT_EDITING_COMMAND_IF(!m_node->document().frame()); + ABORT_EDITING_COMMAND_IF(!m_node->document().documentElement()); } void RemoveNodeCommand::doUnapply()
diff --git a/third_party/WebKit/Source/core/editing/commands/RemoveNodePreservingChildrenCommand.cpp b/third_party/WebKit/Source/core/editing/commands/RemoveNodePreservingChildrenCommand.cpp index 9568a68d..3a1d33c 100644 --- a/third_party/WebKit/Source/core/editing/commands/RemoveNodePreservingChildrenCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/RemoveNodePreservingChildrenCommand.cpp
@@ -40,8 +40,8 @@ void RemoveNodePreservingChildrenCommand::doApply(EditingState* editingState) { - ASSERT_IN_EDITING_COMMAND(m_node->parentNode()); - ASSERT_IN_EDITING_COMMAND(m_node->parentNode()->hasEditableStyle()); + ABORT_EDITING_COMMAND_IF(!m_node->parentNode()); + ABORT_EDITING_COMMAND_IF(!m_node->parentNode()->hasEditableStyle()); if (m_node->isContainerNode()) { NodeVector children; getChildNodes(toContainerNode(*m_node), children);
diff --git a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp index 2c7c4c7..d75b458 100644 --- a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
@@ -208,40 +208,39 @@ applyTextInsertionCommand(frame.get(), cmd, selectionForInsertion, currentSelection); } -void TypingCommand::insertLineBreak(Document& document, Options options, EditingState* editingState) +bool TypingCommand::insertLineBreak(Document& document, Options options) { if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) { lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator); - lastTypingCommand->insertLineBreak(editingState); - return; + EditingState editingState; + lastTypingCommand->insertLineBreak(&editingState); + return !editingState.isAborted(); } - TypingCommand::create(document, InsertLineBreak, "", options)->apply(); + return TypingCommand::create(document, InsertLineBreak, "", options)->apply(); } -void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document, EditingState* editingState) +bool TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document) { if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) { - lastTypingCommand->insertParagraphSeparatorInQuotedContent(editingState); - return; + EditingState editingState; + lastTypingCommand->insertParagraphSeparatorInQuotedContent(&editingState); + return !editingState.isAborted(); } - // TODO(tkent): apply() should take an EditingState argument, or return a - // bool value. - TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent)->apply(); + return TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent)->apply(); } -void TypingCommand::insertParagraphSeparator(Document& document, Options options, EditingState* editingState) +bool TypingCommand::insertParagraphSeparator(Document& document, Options options) { if (RefPtrWillBeRawPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) { lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator); - lastTypingCommand->insertParagraphSeparator(editingState); - return; + EditingState editingState; + lastTypingCommand->insertParagraphSeparator(&editingState); + return !editingState.isAborted(); } - // TODO(tkent): apply() should take an EditingState argument, or return a - // bool value. - TypingCommand::create(document, InsertParagraphSeparator, "", options)->apply(); + return TypingCommand::create(document, InsertParagraphSeparator, "", options)->apply(); } PassRefPtrWillBeRawPtr<TypingCommand> TypingCommand::lastTypingCommandIfStillOpenForTyping(LocalFrame* frame)
diff --git a/third_party/WebKit/Source/core/editing/commands/TypingCommand.h b/third_party/WebKit/Source/core/editing/commands/TypingCommand.h index 1463240..ea175263 100644 --- a/third_party/WebKit/Source/core/editing/commands/TypingCommand.h +++ b/third_party/WebKit/Source/core/editing/commands/TypingCommand.h
@@ -62,10 +62,10 @@ static void forwardDeleteKeyPressed(Document&, EditingState*, Options = 0, TextGranularity = CharacterGranularity); static void insertText(Document&, const String&, Options, TextCompositionType = TextCompositionNone); static void insertText(Document&, const String&, const VisibleSelection&, Options, TextCompositionType = TextCompositionNone); - static void insertLineBreak(Document&, Options, EditingState*); + static bool insertLineBreak(Document&, Options); // TODO(tkent): |Options| argument should be removed. It's always 0. - static void insertParagraphSeparator(Document&, Options, EditingState*); - static void insertParagraphSeparatorInQuotedContent(Document&, EditingState*); + static bool insertParagraphSeparator(Document&, Options); + static bool insertParagraphSeparatorInQuotedContent(Document&); static void closeTyping(LocalFrame*); void insertText(const String &text, bool selectInsertedText, EditingState*);
diff --git a/third_party/WebKit/Source/core/events/EventListener.h b/third_party/WebKit/Source/core/events/EventListener.h index 1b60bd0..d7b46a83 100644 --- a/third_party/WebKit/Source/core/events/EventListener.h +++ b/third_party/WebKit/Source/core/events/EventListener.h
@@ -24,6 +24,7 @@ #include "core/CoreExport.h" #include "platform/heap/Handle.h" #include "wtf/RefCounted.h" +#include "wtf/text/WTFString.h" namespace blink { @@ -43,6 +44,7 @@ virtual ~EventListener() { } virtual bool operator==(const EventListener&) const = 0; virtual void handleEvent(ExecutionContext*, Event*) = 0; + virtual const String& code() const { return emptyString(); } virtual bool wasCreatedFromMarkup() const { return false; } virtual bool belongsToTheCurrentWorld() const { return false; }
diff --git a/third_party/WebKit/Source/core/fetch/FetchUtils.cpp b/third_party/WebKit/Source/core/fetch/FetchUtils.cpp index 1b314a1..658f3ad 100644 --- a/third_party/WebKit/Source/core/fetch/FetchUtils.cpp +++ b/third_party/WebKit/Source/core/fetch/FetchUtils.cpp
@@ -90,10 +90,13 @@ // `Content-Type` and value, once parsed, is one of // `application/x-www-form-urlencoded`, `multipart/form-data`, and // `text/plain`." + // Treat 'Save-Data' as a simple header, since it is added by Chrome when + // Data Saver feature is enabled. if (equalIgnoringCase(name, "accept") || equalIgnoringCase(name, "accept-language") - || equalIgnoringCase(name, "content-language")) + || equalIgnoringCase(name, "content-language") + || equalIgnoringCase(name, "save-data")) return true; if (equalIgnoringCase(name, "content-type")) {
diff --git a/third_party/WebKit/Source/core/fetch/ImageResource.h b/third_party/WebKit/Source/core/fetch/ImageResource.h index 2ca6294..d176d05 100644 --- a/third_party/WebKit/Source/core/fetch/ImageResource.h +++ b/third_party/WebKit/Source/core/fetch/ImageResource.h
@@ -61,7 +61,6 @@ return adoptRefWillBeNoop(new ImageResource(request, image)); } - ~ImageResource() override; void load(ResourceFetcher*, const ResourceLoaderOptions&) override;
diff --git a/third_party/WebKit/Source/core/frame/Deprecation.cpp b/third_party/WebKit/Source/core/frame/Deprecation.cpp index a364cad..3d40162 100644 --- a/third_party/WebKit/Source/core/frame/Deprecation.cpp +++ b/third_party/WebKit/Source/core/frame/Deprecation.cpp
@@ -327,9 +327,6 @@ case UseCounter::MediaStreamTrackGetSources: return "MediaStreamTrack.getSources is deprecated. See https://www.chromestatus.com/feature/4765305641369600 for more details."; - case UseCounter::DocumentDefaultCharset: - return willBeRemoved("'Document.defaultCharset'", 50, "6217124578066432"); - case UseCounter::V8TouchEvent_InitTouchEvent_Method: return replacedWillBeRemoved("'TouchEvent.initTouchEvent'", "the TouchEvent constructor", 53, "5730982598541312");
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp index 342bc4b..6fad532 100644 --- a/third_party/WebKit/Source/core/frame/FrameView.cpp +++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -64,6 +64,7 @@ #include "core/layout/ScrollAlignment.h" #include "core/layout/TextAutosizer.h" #include "core/layout/TracedLayoutObject.h" +#include "core/layout/api/LayoutBoxModel.h" #include "core/layout/api/LayoutItem.h" #include "core/layout/compositing/CompositedLayerMapping.h" #include "core/layout/compositing/CompositedSelection.h" @@ -274,9 +275,6 @@ scrollAnimator->cancelAnimation(); cancelProgrammaticScrollAnimation(); - if (RuntimeEnabledFeatures::scrollAnchoringEnabled()) - m_scrollAnchor.clear(); - detachScrollbars(); // When the view is no longer associated with a frame, it needs to be removed from the ax object cache @@ -1363,9 +1361,10 @@ { for (const auto& viewportConstrainedObject : *m_viewportConstrainedObjects) { LayoutObject* layoutObject = viewportConstrainedObject; - ASSERT(layoutObject->style()->hasViewportConstrainedPosition()); - ASSERT(layoutObject->hasLayer()); - PaintLayer* layer = toLayoutBoxModelObject(layoutObject)->layer(); + LayoutItem layoutItem = LayoutItem(layoutObject); + ASSERT(layoutItem.style()->hasViewportConstrainedPosition()); + ASSERT(layoutItem.hasLayer()); + PaintLayer* layer = LayoutBoxModel(layoutItem).layer(); if (layer->isPaintInvalidationContainer()) continue; @@ -1385,7 +1384,7 @@ "data", InspectorScrollInvalidationTrackingEvent::data(*layoutObject)); - layoutObject->setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); + layoutItem.setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); } return true; } @@ -2364,6 +2363,7 @@ if (cornerStyle) { if (!m_scrollCorner) m_scrollCorner = LayoutScrollbarPart::createAnonymous(doc); + m_scrollCorner->adjustStyleBeforeSet(cornerStyle.get()); m_scrollCorner->setStyle(cornerStyle.release()); setScrollCornerNeedsPaintInvalidation(); } else if (m_scrollCorner) {
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.cpp b/third_party/WebKit/Source/core/frame/UseCounter.cpp index 39c1e51c..15eb00c 100644 --- a/third_party/WebKit/Source/core/frame/UseCounter.cpp +++ b/third_party/WebKit/Source/core/frame/UseCounter.cpp
@@ -257,16 +257,16 @@ case CSSPropertyWebkitColumnBreakAfter: return 215; case CSSPropertyWebkitColumnBreakBefore: return 216; case CSSPropertyWebkitColumnBreakInside: return 217; - case CSSPropertyWebkitColumnCount: return 218; - case CSSPropertyWebkitColumnGap: return 219; + case CSSPropertyAliasWebkitColumnCount: return 218; + case CSSPropertyAliasWebkitColumnGap: return 219; // CSSPropertyWebkitColumnProgression was 220 - case CSSPropertyWebkitColumnRule: return 221; - case CSSPropertyWebkitColumnRuleColor: return 222; - case CSSPropertyWebkitColumnRuleStyle: return 223; - case CSSPropertyWebkitColumnRuleWidth: return 224; - case CSSPropertyWebkitColumnSpan: return 225; - case CSSPropertyWebkitColumnWidth: return 226; - case CSSPropertyWebkitColumns: return 227; + case CSSPropertyAliasWebkitColumnRule: return 221; + case CSSPropertyAliasWebkitColumnRuleColor: return 222; + case CSSPropertyAliasWebkitColumnRuleStyle: return 223; + case CSSPropertyAliasWebkitColumnRuleWidth: return 224; + case CSSPropertyAliasWebkitColumnSpan: return 225; + case CSSPropertyAliasWebkitColumnWidth: return 226; + case CSSPropertyAliasWebkitColumns: return 227; // 228 was CSSPropertyWebkitBoxDecorationBreak (duplicated due to #ifdef). // 229 was CSSPropertyWebkitFilter (duplicated due to #ifdef). case CSSPropertyAlignContent: return 230; @@ -552,6 +552,18 @@ case CSSPropertyContain: return 517; case CSSPropertyD: return 518; case CSSPropertySnapHeight: return 519; + case CSSPropertyBreakAfter: return 520; + case CSSPropertyBreakBefore: return 521; + case CSSPropertyBreakInside: return 522; + case CSSPropertyColumnCount: return 523; + case CSSPropertyColumnGap: return 524; + case CSSPropertyColumnRule: return 525; + case CSSPropertyColumnRuleColor: return 526; + case CSSPropertyColumnRuleStyle: return 527; + case CSSPropertyColumnRuleWidth: return 528; + case CSSPropertyColumnSpan: return 529; + case CSSPropertyColumnWidth: return 530; + case CSSPropertyColumns: return 531; // 1. Add new features above this line (don't change the assigned numbers of the existing // items). @@ -568,7 +580,7 @@ return 0; } -static int maximumCSSSampleId() { return 519; } +static int maximumCSSSampleId() { return 531; } static EnumerationHistogram& featureObserverHistogram() {
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h index df3d360..5526acd 100644 --- a/third_party/WebKit/Source/core/frame/UseCounter.h +++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -352,7 +352,6 @@ DocumentPointerLockElement = 422, PrefixedCursorZoomIn = 424, PrefixedCursorZoomOut = 425, - DocumentDefaultCharset = 428, TextEncoderConstructor = 429, TextEncoderEncode = 430, TextDecoderConstructor = 431, @@ -787,8 +786,6 @@ // The above items are available in M46 branch. HTMLImportsHasStyleSheets = 940, - WebkitTextInClipProperty = 941, - WebkitTextInColorProperty = 942, ClipPathOfPositionedElement = 944, ClipCssOfPositionedElement = 945, NetInfoType = 946, @@ -1089,6 +1086,9 @@ V8HTMLComment = 1236, V8SloppyModeBlockScopedFunctionRedefinition = 1237, V8ForInInitializer = 1238, + V8Animation_Id_AttributeGetter = 1239, + V8Animation_Id_AttributeSetter = 1240, + MediaStreamOnEnded = 1241, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/html/ClassList.cpp b/third_party/WebKit/Source/core/html/ClassList.cpp index 1637c65..c37b6bf 100644 --- a/third_party/WebKit/Source/core/html/ClassList.cpp +++ b/third_party/WebKit/Source/core/html/ClassList.cpp
@@ -32,6 +32,20 @@ ClassList::ClassList(Element* element) : DOMTokenList(nullptr), m_element(element) { } +#if !ENABLE(OILPAN) +void ClassList::ref() +{ + m_element->ref(); + DOMTokenList::ref(); +} + +void ClassList::deref() +{ + m_element->deref(); + DOMTokenList::deref(); +} +#endif + unsigned ClassList::length() const { return m_element->hasClass() ? classNames().size() : 0;
diff --git a/third_party/WebKit/Source/core/html/ClassList.h b/third_party/WebKit/Source/core/html/ClassList.h index 146550b4..e4c16618 100644 --- a/third_party/WebKit/Source/core/html/ClassList.h +++ b/third_party/WebKit/Source/core/html/ClassList.h
@@ -44,6 +44,11 @@ return adoptRefWillBeNoop(new ClassList(element)); } +#if !ENABLE(OILPAN) + void ref() override; + void deref() override; +#endif + unsigned length() const override; const AtomicString item(unsigned index) const override;
diff --git a/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp b/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp index c5886a7..5f7c5da 100644 --- a/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp
@@ -149,7 +149,7 @@ : HTMLElement(linkTag, document) , m_linkLoader(LinkLoader::create(this)) , m_sizes(DOMTokenList::create(this)) - , m_relList(RelList::create(this)) + , m_relList(RelList::create(this).leakRef()) , m_createdByParser(createdByParser) , m_isInShadowTree(false) { @@ -515,7 +515,7 @@ // See the comment in PendingScript.cpp about why this check is necessary // here, instead of in the resource fetcher. https://crbug.com/500701. - if (!cachedStyleSheet->errorOccurred() && cachedStyleSheet->resourceBuffer() && !SubresourceIntegrity::CheckSubresourceIntegrity(*m_owner, cachedStyleSheet->resourceBuffer()->data(), cachedStyleSheet->resourceBuffer()->size(), KURL(baseURL, href), *cachedStyleSheet)) { + if (!cachedStyleSheet->errorOccurred() && m_owner->fastHasAttribute(HTMLNames::integrityAttr) && cachedStyleSheet->resourceBuffer() && !SubresourceIntegrity::CheckSubresourceIntegrity(*m_owner, cachedStyleSheet->resourceBuffer()->data(), cachedStyleSheet->resourceBuffer()->size(), KURL(baseURL, href), *cachedStyleSheet)) { m_loading = false; removePendingSheet(); notifyLoadedSheetAndAllCriticalSubresources(Node::ErrorOccurredLoadingSubresource); @@ -646,7 +646,7 @@ // Document::removePendingSheet() triggers the style selector recalc for blocking sheets. // FIXME: We don't have enough knowledge at this point to know if we're adding or removing a sheet // so we can't call addedStyleSheet() or removedStyleSheet(). - m_owner->document().styleResolverChanged(); + m_owner->document().styleEngine().resolverChanged(FullStyleUpdate); return; } @@ -690,7 +690,7 @@ process(); } else { // FIXME: We don't have enough knowledge here to know if we should call addedStyleSheet() or removedStyleSheet(). - m_owner->document().styleResolverChanged(); + m_owner->document().styleEngine().resolverChanged(FullStyleUpdate); } } }
diff --git a/third_party/WebKit/Source/core/html/HTMLLinkElement.h b/third_party/WebKit/Source/core/html/HTMLLinkElement.h index fddcd51..d42bf50 100644 --- a/third_party/WebKit/Source/core/html/HTMLLinkElement.h +++ b/third_party/WebKit/Source/core/html/HTMLLinkElement.h
@@ -225,7 +225,7 @@ String m_media; RefPtrWillBeMember<DOMTokenList> m_sizes; Vector<IntSize> m_iconSizes; - RefPtrWillBeMember<RelList> m_relList; + RawPtrWillBeMember<RelList> m_relList; LinkRelAttribute m_relAttribute; bool m_createdByParser;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp index efeacead..94a7462 100644 --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -87,25 +87,6 @@ #include "wtf/text/CString.h" #include <limits> - -namespace blink { - -#if !LOG_DISABLED -static String urlForLoggingMedia(const KURL& url) -{ - static const unsigned maximumURLLengthForLogging = 128; - - if (url.string().length() < maximumURLLengthForLogging) - return url.string(); - return url.string().substring(0, maximumURLLengthForLogging) + "..."; -} - -static const char* boolString(bool val) -{ - return val ? "true" : "false"; -} -#endif - #ifndef LOG_MEDIA_EVENTS // Default to not logging events because so many are generated they can overwhelm the rest of // the logging. @@ -118,20 +99,41 @@ #define LOG_CACHED_TIME_WARNINGS 0 #endif -// URL protocol used to signal that the media source API is being used. -static const char mediaSourceBlobProtocol[] = "blob"; +namespace blink { using namespace HTMLNames; -typedef WillBeHeapHashSet<RawPtrWillBeWeakMember<HTMLMediaElement>> WeakMediaElementSet; -typedef WillBeHeapHashMap<RawPtrWillBeWeakMember<Document>, WeakMediaElementSet> DocumentElementSetMap; -static DocumentElementSetMap& documentToElementSetMap() +using WeakMediaElementSet = WillBeHeapHashSet<RawPtrWillBeWeakMember<HTMLMediaElement>>; +using DocumentElementSetMap = WillBeHeapHashMap<RawPtrWillBeWeakMember<Document>, WeakMediaElementSet>; + +namespace { + +// URL protocol used to signal that the media source API is being used. +const char mediaSourceBlobProtocol[] = "blob"; + +#if !LOG_DISABLED +String urlForLoggingMedia(const KURL& url) +{ + static const unsigned maximumURLLengthForLogging = 128; + + if (url.string().length() < maximumURLLengthForLogging) + return url.string(); + return url.string().substring(0, maximumURLLengthForLogging) + "..."; +} + +const char* boolString(bool val) +{ + return val ? "true" : "false"; +} +#endif + +DocumentElementSetMap& documentToElementSetMap() { DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<DocumentElementSetMap>, map, (adoptPtrWillBeNoop(new DocumentElementSetMap()))); return *map; } -static void addElementToDocumentMap(HTMLMediaElement* element, Document* document) +void addElementToDocumentMap(HTMLMediaElement* element, Document* document) { DocumentElementSetMap& map = documentToElementSetMap(); WeakMediaElementSet set = map.take(document); @@ -139,7 +141,7 @@ map.add(document, set); } -static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document) +void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document) { DocumentElementSetMap& map = documentToElementSetMap(); WeakMediaElementSet set = map.take(document); @@ -167,7 +169,7 @@ Member<AudioSourceProviderClient> m_client; }; -static const AtomicString& AudioKindToString(WebMediaPlayerClient::AudioTrackKind kind) +const AtomicString& AudioKindToString(WebMediaPlayerClient::AudioTrackKind kind) { switch (kind) { case WebMediaPlayerClient::AudioTrackKindNone: @@ -190,7 +192,7 @@ return emptyAtom; } -static const AtomicString& VideoKindToString(WebMediaPlayerClient::VideoTrackKind kind) +const AtomicString& VideoKindToString(WebMediaPlayerClient::VideoTrackKind kind) { switch (kind) { case WebMediaPlayerClient::VideoTrackKindNone: @@ -213,7 +215,7 @@ return emptyAtom; } -static bool canLoadURL(const KURL& url, const ContentType& contentType, const String& keySystem) +bool canLoadURL(const KURL& url, const ContentType& contentType, const String& keySystem) { DEFINE_STATIC_LOCAL(const String, codecs, ("codecs")); @@ -241,6 +243,8 @@ return false; } +} // anonymous namespace + void HTMLMediaElement::recordAutoplayMetric(AutoplayMetrics metric) { DEFINE_STATIC_LOCAL(EnumerationHistogram, autoplayHistogram, ("Blink.MediaElement.Autoplay", NumberOfAutoplayMetrics));
diff --git a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp index 2fa0a8d..b694c01 100644 --- a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp
@@ -45,6 +45,7 @@ inline HTMLSlotElement::HTMLSlotElement(Document& document) : HTMLElement(slotTag, document) { + setHasCustomStyleCallbacks(); } DEFINE_NODE_FACTORY(HTMLSlotElement); @@ -214,6 +215,15 @@ HTMLElement::removedFrom(insertionPoint); } +void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) +{ + if (change < Inherit && styleChangeType() < SubtreeStyleChange) + return; + + for (auto& node : m_distributedNodes) + node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); +} + void HTMLSlotElement::updateDistributedNodesWithFallback() { if (!m_distributedNodes.isEmpty())
diff --git a/third_party/WebKit/Source/core/html/HTMLSlotElement.h b/third_party/WebKit/Source/core/html/HTMLSlotElement.h index b72bd375..991d2fd 100644 --- a/third_party/WebKit/Source/core/html/HTMLSlotElement.h +++ b/third_party/WebKit/Source/core/html/HTMLSlotElement.h
@@ -63,19 +63,20 @@ void updateDistributedNodesWithFallback(); void didUpdateDistribution(); - void attach(const AttachContext& = AttachContext()) override; - void detach(const AttachContext& = AttachContext()) override; + void attach(const AttachContext& = AttachContext()) final; + void detach(const AttachContext& = AttachContext()) final; - void attributeChanged(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason = ModifiedDirectly) override; + void attributeChanged(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason = ModifiedDirectly) final; DECLARE_VIRTUAL_TRACE(); private: HTMLSlotElement(Document&); - void childrenChanged(const ChildrenChange&) override; - InsertionNotificationRequest insertedInto(ContainerNode*) override; - void removedFrom(ContainerNode*) override; + void childrenChanged(const ChildrenChange&) final; + InsertionNotificationRequest insertedInto(ContainerNode*) final; + void removedFrom(ContainerNode*) final; + void willRecalcStyle(StyleRecalcChange) final; void dispatchSlotChangeEvent();
diff --git a/third_party/WebKit/Source/core/html/RelList.cpp b/third_party/WebKit/Source/core/html/RelList.cpp index 8410446..18a2ecda 100644 --- a/third_party/WebKit/Source/core/html/RelList.cpp +++ b/third_party/WebKit/Source/core/html/RelList.cpp
@@ -14,6 +14,20 @@ RelList::RelList(Element* element) : DOMTokenList(nullptr), m_element(element) { } +#if !ENABLE(OILPAN) +void RelList::ref() +{ + m_element->ref(); + DOMTokenList::ref(); +} + +void RelList::deref() +{ + m_element->deref(); + DOMTokenList::deref(); +} +#endif + unsigned RelList::length() const { return !m_element->fastGetAttribute(relAttr).isEmpty() ? m_relValues.size() : 0;
diff --git a/third_party/WebKit/Source/core/html/RelList.h b/third_party/WebKit/Source/core/html/RelList.h index 2f0825bd..a34a18e 100644 --- a/third_party/WebKit/Source/core/html/RelList.h +++ b/third_party/WebKit/Source/core/html/RelList.h
@@ -19,6 +19,11 @@ return adoptRefWillBeNoop(new RelList(element)); } +#if !ENABLE(OILPAN) + void ref() override; + void deref() override; +#endif + unsigned length() const override; const AtomicString item(unsigned index) const override;
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp index d75e004..d0c082a 100644 --- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp +++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
@@ -9,6 +9,7 @@ #include "platform/ThreadSafeFunctional.h" #include "platform/graphics/ImageBuffer.h" #include "platform/heap/Handle.h" +#include "platform/image-encoders/skia/JPEGImageEncoder.h" #include "platform/image-encoders/skia/PNGImageEncoder.h" #include "platform/threading/BackgroundTaskRunner.h" #include "public/platform/Platform.h" @@ -69,18 +70,32 @@ // TODO(xlai): Progressive encoding on jpeg and webp image formats (crbug.com/571398, crbug.com/571399) if (canUseIdlePeriodScheduling) { ASSERT(m_mimeType == "image/png"); - Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, WTF::bind<double>(&CanvasAsyncBlobCreator::initiatePngEncoding, this)); + Platform::current()->mainThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, bind<double>(&CanvasAsyncBlobCreator::initiatePngEncoding, this)); + } else if (m_mimeType == "image/jpeg") { + Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::initiateJpegEncoding, this, quality)); } else { BackgroundTaskRunner::TaskSize taskSize = (m_size.height() * m_size.width() >= LongTaskImageSizeThreshold) ? BackgroundTaskRunner::TaskSizeLongRunningTask : BackgroundTaskRunner::TaskSizeShortRunningTask; BackgroundTaskRunner::postOnBackgroundThread(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, AllowCrossThreadAccess(this), quality), taskSize); } } +void CanvasAsyncBlobCreator::initiateJpegEncoding(const double& quality) +{ + m_jpegEncoderState = JPEGImageEncoderState::create(m_size, quality, m_encodedImage.get()); + if (!m_jpegEncoderState) { + Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr)); + m_selfRef.clear(); + return; + } + BackgroundTaskRunner::TaskSize taskSize = (m_size.height() * m_size.width() >= LongTaskImageSizeThreshold) ? BackgroundTaskRunner::TaskSizeLongRunningTask : BackgroundTaskRunner::TaskSizeShortRunningTask; + BackgroundTaskRunner::postOnBackgroundThread(BLINK_FROM_HERE, threadSafeBind(&CanvasAsyncBlobCreator::encodeImageOnEncoderThread, AllowCrossThreadAccess(this), quality), taskSize); +} + void CanvasAsyncBlobCreator::initiatePngEncoding(double deadlineSeconds) { ASSERT(isMainThread()); - m_encoderState = PNGImageEncoderState::create(m_size, m_encodedImage.get()); - if (!m_encoderState) { + m_pngEncoderState = PNGImageEncoderState::create(m_size, m_encodedImage.get()); + if (!m_pngEncoderState) { Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&BlobCallback::handleEvent, m_callback, nullptr)); m_selfRef.clear(); return; @@ -105,11 +120,11 @@ CanvasAsyncBlobCreator::scheduleIdleEncodeRowsPng(); return; } - PNGImageEncoder::writeOneRowToPng(inputPixels, m_encoderState.get()); + PNGImageEncoder::writeOneRowToPng(inputPixels, m_pngEncoderState.get()); inputPixels += m_pixelRowStride; } m_numRowsCompleted = m_size.height(); - PNGImageEncoder::finalizePng(m_encoderState.get()); + PNGImageEncoder::finalizePng(m_pngEncoderState.get()); if (isDeadlineNearOrPassed(deadlineSeconds)) { Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, bind(&CanvasAsyncBlobCreator::createBlobAndCall, this)); @@ -130,11 +145,13 @@ { ASSERT(!isMainThread()); - if (ImageDataBuffer(m_size, m_data->data()).encodeImage(m_mimeType, quality, m_encodedImage.get())) { - scheduleCreateBlobAndCallOnMainThread(); - } else { + if (m_mimeType == "image/jpeg") { + JPEGImageEncoder::encodeWithPreInitializedState(m_jpegEncoderState.get(), m_data->data()); + } else if (!ImageDataBuffer(m_size, m_data->data()).encodeImage(m_mimeType, quality, m_encodedImage.get())) { scheduleCreateNullptrAndCallOnMainThread(); } + + scheduleCreateBlobAndCallOnMainThread(); } void CanvasAsyncBlobCreator::clearSelfReference()
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.h b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.h index 71980f9..391a9ce 100644 --- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.h +++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.h
@@ -15,6 +15,7 @@ namespace blink { class PNGImageEncoderState; +class JPEGImageEncoderState; class CORE_EXPORT CanvasAsyncBlobCreator : public RefCounted<CanvasAsyncBlobCreator> { @@ -28,7 +29,8 @@ void scheduleCreateBlobAndCallOnMainThread(); void scheduleCreateNullptrAndCallOnMainThread(); - OwnPtr<PNGImageEncoderState> m_encoderState; + OwnPtr<PNGImageEncoderState> m_pngEncoderState; + OwnPtr<JPEGImageEncoderState> m_jpegEncoderState; RefPtr<DOMUint8ClampedArray> m_data; OwnPtr<Vector<unsigned char>> m_encodedImage; int m_numRowsCompleted; @@ -45,6 +47,8 @@ void scheduleIdleEncodeRowsPng(); void idleEncodeRowsPng(double deadlineSeconds); + void initiateJpegEncoding(const double& quality); + void createBlobAndCall(); void encodeImageOnEncoderThread(double quality);
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp index afc59cb..edc0e40 100644 --- a/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp +++ b/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp
@@ -32,6 +32,7 @@ #include "core/css/StyleSheetList.h" #include "core/dom/Document.h" +#include "core/dom/StyleEngine.h" #include "core/dom/custom/CustomElement.h" #include "core/dom/custom/CustomElementMicrotaskImportStep.h" #include "core/dom/custom/CustomElementSyncMicrotaskQueue.h" @@ -66,7 +67,7 @@ { if (!m_loader->isDone()) return; - root()->document()->styleResolverChanged(); + root()->document()->styleEngine().resolverChanged(FullStyleUpdate); } void HTMLImportChild::didShareLoader()
diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp index 188d4c4f..e1d4146 100644 --- a/third_party/WebKit/Source/core/input/EventHandler.cpp +++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
@@ -1716,10 +1716,10 @@ // If the host has a focusable inner element, focus it. Otherwise, the host takes focus. Page* page = m_frame->page(); ASSERT(page); - Element* next = page->focusController().findFocusableElement(WebFocusTypeForward, *element.authorShadowRoot()); - if (next && element.containsIncludingShadowDOM(next)) { + Element* found = page->focusController().findFocusableElementInShadowHost(element); + if (found && element.containsIncludingShadowDOM(found)) { // Use WebFocusTypeForward instead of WebFocusTypeMouse here to mean the focus has slided. - next->focus(FocusParams(SelectionBehaviorOnFocus::Reset, WebFocusTypeForward, nullptr)); + found->focus(FocusParams(SelectionBehaviorOnFocus::Reset, WebFocusTypeForward, nullptr)); return true; } }
diff --git a/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp index 53501282..dd843c40 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorAnimationAgent.cpp
@@ -120,7 +120,6 @@ .setDuration(duration) .setDirection(computedTiming.direction()) .setFill(computedTiming.fill()) - .setName(effect->name()) .setBackendNodeId(DOMNodeIds::idForNode(effect->target())) .setEasing(easing); return animationObject.release(); @@ -184,6 +183,7 @@ RefPtr<protocol::TypeBuilder::Animation::Animation> animationObject = protocol::TypeBuilder::Animation::Animation::create() .setId(id) + .setName(animation.id()) .setPausedState(animation.paused()) .setPlayState(animation.playState()) .setPlaybackRate(animation.playbackRate()) @@ -430,14 +430,14 @@ } else { for (CSSPropertyID property : transitionProperties) cssProperties.append(property); - cssProperties.append(cssPropertyID(effect->name())); + cssProperties.append(cssPropertyID(animation.id())); } Element* element = effect->target(); WillBeHeapVector<RefPtrWillBeMember<CSSStyleDeclaration>> styles = m_cssAgent->matchingStyles(element); OwnPtr<WebCryptoDigestor> digestor = createDigestor(HashAlgorithmSha1); addStringToDigestor(digestor.get(), String::number(type)); - addStringToDigestor(digestor.get(), effect->name()); + addStringToDigestor(digestor.get(), animation.id()); for (CSSPropertyID property : cssProperties) { RefPtrWillBeRawPtr<CSSStyleDeclaration> style = m_cssAgent->findEffectiveDeclaration(property, styles); // Ignore inline styles.
diff --git a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp index 9b07d06..8c9b5bba 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp
@@ -311,6 +311,11 @@ : InspectorHistory::Action(name) { } + + virtual PassRefPtr<protocol::TypeBuilder::CSS::CSSStyle> takeSerializedStyle() + { + return nullptr; + } }; class InspectorCSSAgent::SetStyleSheetTextAction final : public InspectorCSSAgent::StyleSheetAction { @@ -435,6 +440,18 @@ return result; } + PassRefPtr<protocol::TypeBuilder::CSS::CSSStyle> takeSerializedStyle() override + { + if (m_type != SetStyleText) + return nullptr; + RefPtrWillBeRawPtr<CSSRule> rule = takeRule(); + if (rule->type() == CSSRule::STYLE_RULE) + return m_styleSheet->buildObjectForStyle(toCSSStyleRule(rule.get())->style()); + if (rule->type() == CSSRule::KEYFRAME_RULE) + return m_styleSheet->buildObjectForStyle(toCSSKeyframeRule(rule.get())->style()); + return nullptr; + } + DEFINE_INLINE_VIRTUAL_TRACE() { visitor->trace(m_styleSheet); @@ -509,6 +526,11 @@ return String::format("SetElementStyleAction:%s", m_styleSheet->id().utf8().data()); } + PassRefPtr<protocol::TypeBuilder::CSS::CSSStyle> takeSerializedStyle() override + { + return m_styleSheet->buildObjectForStyle(m_styleSheet->inlineStyle()); + } + void merge(PassRefPtrWillBeRawPtr<Action> action) override { ASSERT(action->mergeId() == mergeId()); @@ -1204,21 +1226,90 @@ *errorString = InspectorDOMAgent::toErrorString(exceptionState); } -void InspectorCSSAgent::setStyleText(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& text, RefPtr<protocol::TypeBuilder::CSS::CSSStyle>& result) +bool InspectorCSSAgent::multipleStyleTextsActions(ErrorString* errorString, const RefPtr<JSONArray>& edits, HeapVector<RefPtrWillBeMember<StyleSheetAction>>* actions) +{ + int n = edits->length(); + if (n == 0) { + *errorString = "Edits should not be empty"; + return false; + } + + for (int i = 0; i < n; ++i) { + RefPtr<JSONObject> edit = edits->get(i)->asObject(); + String styleSheetId; + bool success = edit->getString("styleSheetId", &styleSheetId); + if (!success) { + *errorString = String::format("Could not parse styleSheetId for edit #%d of %d", i + 1, n); + return false; + } + InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId); + if (!inspectorStyleSheet) { + *errorString = String::format("StyleSheet not found for edit #%d of %d", i + 1 , n); + return false; + } + + RefPtr<JSONObject> rangeObject = edit->getObject("range"); + if (!rangeObject) { + *errorString = String::format("Could not parse range object for edit #%d of %d", i + 1, n); + return false; + } + + SourceRange range; + if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, rangeObject, &range)) + return false; + + String text; + success = edit->getString("text", &text); + if (!success) { + *errorString = String::format("Could not parse text for edit #%d of %d", i + 1, n); + return false; + } + + if (inspectorStyleSheet->isInlineStyle()) { + InspectorStyleSheetForInlineStyle* inlineStyleSheet = static_cast<InspectorStyleSheetForInlineStyle*>(inspectorStyleSheet); + RefPtrWillBeRawPtr<SetElementStyleAction> action = adoptRefWillBeNoop(new SetElementStyleAction(inlineStyleSheet, text)); + actions->append(action); + } else { + RefPtrWillBeRawPtr<ModifyRuleAction> action = adoptRefWillBeNoop(new ModifyRuleAction(ModifyRuleAction::SetStyleText, static_cast<InspectorStyleSheet*>(inspectorStyleSheet), range, text)); + actions->append(action); + } + } + return true; +} + +void InspectorCSSAgent::setStyleTexts(ErrorString* errorString, const RefPtr<JSONArray>& edits, RefPtr<protocol::TypeBuilder::Array<protocol::TypeBuilder::CSS::CSSStyle>>& result) { FrontendOperationScope scope; - InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId); - if (!inspectorStyleSheet) { - *errorString = "Stylesheet not found"; - return; - } - SourceRange selectorRange; - if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &selectorRange)) + HeapVector<RefPtrWillBeMember<StyleSheetAction>> actions; + if (!multipleStyleTextsActions(errorString, edits, &actions)) return; - CSSStyleDeclaration* style = setStyleText(errorString, inspectorStyleSheet, selectorRange, text); - if (style) - result = inspectorStyleSheet->buildObjectForStyle(style); + TrackExceptionState exceptionState; + + int n = actions.size(); + for (int i = 0; i < n; ++i) { + RefPtrWillBeMember<StyleSheetAction> action = actions.at(i); + bool success = action->perform(exceptionState); + if (!success) { + for (int j = i - 1; j >= 0; --j) { + RefPtrWillBeMember<StyleSheetAction> revert = actions.at(j); + TrackExceptionState undoExceptionState; + revert->undo(undoExceptionState); + ASSERT(!undoExceptionState.hadException()); + } + *errorString = String::format("Failed applying edit #%d: %s", i, InspectorDOMAgent::toErrorString(exceptionState).utf8().data()); + return; + } + } + + result = protocol::TypeBuilder::Array<protocol::TypeBuilder::CSS::CSSStyle>::create(); + for (size_t i = 0; i < actions.size(); ++i) { + RefPtrWillBeMember<StyleSheetAction> action = actions.at(i); + RefPtr<protocol::TypeBuilder::CSS::CSSStyle> stylePayload = action->takeSerializedStyle(); + ASSERT(stylePayload); + result->addItem(stylePayload); + m_domAgent->history()->appendPerformedAction(action); + } } CSSStyleDeclaration* InspectorCSSAgent::setStyleText(ErrorString* errorString, InspectorStyleSheetBase* inspectorStyleSheet, const SourceRange& range, const String& text)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.h b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.h index d8f92da..69689fd 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.h +++ b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.h
@@ -136,7 +136,7 @@ void setStyleSheetText(ErrorString*, const String& styleSheetId, const String& text, protocol::TypeBuilder::OptOutput<String>* sourceMapURL) override; void setRuleSelector(ErrorString*, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& selector, RefPtr<protocol::TypeBuilder::CSS::SelectorList>& result) override; void setKeyframeKey(ErrorString*, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& keyText, RefPtr<protocol::TypeBuilder::CSS::Value>& result) override; - void setStyleText(ErrorString*, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& text, RefPtr<protocol::TypeBuilder::CSS::CSSStyle>& result) override; + void setStyleTexts(ErrorString*, const RefPtr<JSONArray>& edits, RefPtr<protocol::TypeBuilder::Array<protocol::TypeBuilder::CSS::CSSStyle>>& result); void setMediaText(ErrorString*, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& text, RefPtr<protocol::TypeBuilder::CSS::CSSMedia>& result) override; void createStyleSheet(ErrorString*, const String& frameId, protocol::TypeBuilder::CSS::StyleSheetId* outStyleSheetId) override; void addRule(ErrorString*, const String& styleSheetId, const String& ruleText, const RefPtr<JSONObject>& location, RefPtr<protocol::TypeBuilder::CSS::CSSRule>& result) override; @@ -181,6 +181,7 @@ void updateActiveStyleSheets(Document*, StyleSheetsUpdateType); void setActiveStyleSheets(Document*, const WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet>>&, StyleSheetsUpdateType); CSSStyleDeclaration* setStyleText(ErrorString*, InspectorStyleSheetBase*, const SourceRange&, const String&); + bool multipleStyleTextsActions(ErrorString*, const RefPtr<JSONArray>& edits, HeapVector<RefPtrWillBeMember<StyleSheetAction>>* actions); PassRefPtr<protocol::TypeBuilder::Array<protocol::TypeBuilder::CSS::CSSKeyframesRule>> animationsForNode(Element*);
diff --git a/third_party/WebKit/Source/core/inspector/InspectorHistory.cpp b/third_party/WebKit/Source/core/inspector/InspectorHistory.cpp index bf12383d..3f672a5 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorHistory.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorHistory.cpp
@@ -90,7 +90,12 @@ { if (!action->perform(exceptionState)) return false; + appendPerformedAction(action); + return true; +} +void InspectorHistory::appendPerformedAction(PassRefPtrWillBeRawPtr<Action> action) +{ if (!action->mergeId().isEmpty() && m_afterLastActionIndex > 0 && action->mergeId() == m_history[m_afterLastActionIndex - 1]->mergeId()) { m_history[m_afterLastActionIndex - 1]->merge(action); if (m_history[m_afterLastActionIndex - 1]->isNoop()) @@ -101,7 +106,6 @@ m_history.append(action); ++m_afterLastActionIndex; } - return true; } void InspectorHistory::markUndoableState()
diff --git a/third_party/WebKit/Source/core/inspector/InspectorHistory.h b/third_party/WebKit/Source/core/inspector/InspectorHistory.h index 78d02ed..ab9a568 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorHistory.h +++ b/third_party/WebKit/Source/core/inspector/InspectorHistory.h
@@ -71,6 +71,7 @@ DECLARE_TRACE(); bool perform(PassRefPtrWillBeRawPtr<Action>, ExceptionState&); + void appendPerformedAction(PassRefPtrWillBeRawPtr<Action>); void markUndoableState(); bool undo(ExceptionState&);
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl index 704b333..260db42 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl +++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.idl
@@ -255,7 +255,7 @@ void willSendEventSourceRequest(ExecutionContext*, ThreadableLoaderClient* eventSource); [Resource] - void willDispachEventSourceEvent(ExecutionContext*, ThreadableLoaderClient* eventSource, const AtomicString& eventName, const AtomicString& eventId, const Vector<UChar>& data); + void willDispatchEventSourceEvent(ExecutionContext*, ThreadableLoaderClient* eventSource, const AtomicString& eventName, const AtomicString& eventId, const String& data); [Resource] void didFinishEventSourceRequest(ExecutionContext*, ThreadableLoaderClient* eventSource);
diff --git a/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.cpp index b0e59f40..4e7f7ee5 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.cpp
@@ -726,12 +726,12 @@ m_pendingRequestType = InspectorPageAgent::EventSourceResource; } -void InspectorResourceAgent::willDispachEventSourceEvent(ThreadableLoaderClient* eventSource, const AtomicString& eventName, const AtomicString& eventId, const Vector<UChar>& data) +void InspectorResourceAgent::willDispatchEventSourceEvent(ThreadableLoaderClient* eventSource, const AtomicString& eventName, const AtomicString& eventId, const String& data) { ThreadableLoaderClientRequestIdMap::iterator it = m_knownRequestIdMap.find(eventSource); if (it == m_knownRequestIdMap.end()) return; - frontend()->eventSourceMessageReceived(IdentifiersFactory::requestId(it->value), monotonicallyIncreasingTime(), eventName.string(), eventId.string(), String(data)); + frontend()->eventSourceMessageReceived(IdentifiersFactory::requestId(it->value), monotonicallyIncreasingTime(), eventName.string(), eventId.string(), data); } void InspectorResourceAgent::didFinishEventSourceRequest(ThreadableLoaderClient* eventSource)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.h b/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.h index 1ecfded..d4a99cb 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.h +++ b/third_party/WebKit/Source/core/inspector/InspectorResourceAgent.h
@@ -105,7 +105,7 @@ void didFinishFetch(ExecutionContext*, ThreadableLoaderClient*, const AtomicString& method, const String& url); void willSendEventSourceRequest(ThreadableLoaderClient*); - void willDispachEventSourceEvent(ThreadableLoaderClient*, const AtomicString& eventName, const AtomicString& eventId, const Vector<UChar>& data); + void willDispatchEventSourceEvent(ThreadableLoaderClient*, const AtomicString& eventName, const AtomicString& eventId, const String& data); void didFinishEventSourceRequest(ThreadableLoaderClient*); void removedResourceFromMemoryCache(Resource*);
diff --git a/third_party/WebKit/Source/core/inspector/InspectorRuntimeAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorRuntimeAgent.cpp index 9e0467e..f52d617 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorRuntimeAgent.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorRuntimeAgent.cpp
@@ -146,12 +146,12 @@ m_v8RuntimeAgent->compileScript(errorString, inExpression, inSourceURL, inPersistScript, inExecutionContextId, optOutScriptId, optOutExceptionDetails); } -void InspectorRuntimeAgent::runScript(ErrorString* errorString, const String& inScriptId, int inExecutionContextId, const String* inObjectGroup, const bool* inDoNotPauseOnExceptionsAndMuteConsole, RefPtr<protocol::TypeBuilder::Runtime::RemoteObject>& outResult, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>& optOutExceptionDetails) +void InspectorRuntimeAgent::runScript(ErrorString* errorString, const String& inScriptId, int inExecutionContextId, const String* inObjectGroup, const bool* inDoNotPauseOnExceptionsAndMuteConsole, const bool* includeCommandLineAPI, RefPtr<protocol::TypeBuilder::Runtime::RemoteObject>& outResult, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>& optOutExceptionDetails) { MuteConsoleScope<InspectorRuntimeAgent> muteScope; if (asBool(inDoNotPauseOnExceptionsAndMuteConsole)) muteScope.enter(this); - m_v8RuntimeAgent->runScript(errorString, inScriptId, inExecutionContextId, inObjectGroup, inDoNotPauseOnExceptionsAndMuteConsole, outResult, optOutExceptionDetails); + m_v8RuntimeAgent->runScript(errorString, inScriptId, inExecutionContextId, inObjectGroup, inDoNotPauseOnExceptionsAndMuteConsole, includeCommandLineAPI, outResult, optOutExceptionDetails); } void InspectorRuntimeAgent::enable(ErrorString* errorString)
diff --git a/third_party/WebKit/Source/core/inspector/InspectorRuntimeAgent.h b/third_party/WebKit/Source/core/inspector/InspectorRuntimeAgent.h index 2af8f52..36b9d30 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorRuntimeAgent.h +++ b/third_party/WebKit/Source/core/inspector/InspectorRuntimeAgent.h
@@ -99,7 +99,7 @@ void isRunRequired(ErrorString*, bool* out_result) override; void setCustomObjectFormatterEnabled(ErrorString*, bool) final; void compileScript(ErrorString*, const String& inExpression, const String& inSourceURL, bool inPersistScript, int inExecutionContextId, protocol::TypeBuilder::OptOutput<protocol::TypeBuilder::Runtime::ScriptId>* optOutScriptId, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>& optOutExceptionDetails) override; - void runScript(ErrorString*, const String& inScriptId, int inExecutionContextId, const String* inObjectGroup, const bool* inDoNotPauseOnExceptionsAndMuteConsole, RefPtr<protocol::TypeBuilder::Runtime::RemoteObject>& outResult, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>& optOutExceptionDetails) override; + void runScript(ErrorString*, const String& inScriptId, int inExecutionContextId, const String* inObjectGroup, const bool* inDoNotPauseOnExceptionsAndMuteConsole, const bool* includeCommandLineAPI, RefPtr<protocol::TypeBuilder::Runtime::RemoteObject>& outResult, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>& optOutExceptionDetails) override; virtual void muteConsole() = 0; virtual void unmuteConsole() = 0;
diff --git a/third_party/WebKit/Source/core/inspector/InspectorStyleSheet.cpp b/third_party/WebKit/Source/core/inspector/InspectorStyleSheet.cpp index 94283acdb..8fb9bb2 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorStyleSheet.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorStyleSheet.cpp
@@ -44,6 +44,7 @@ #include "core/dom/DOMNodeIds.h" #include "core/dom/Document.h" #include "core/dom/Element.h" +#include "core/dom/StyleEngine.h" #include "core/html/HTMLStyleElement.h" #include "core/html/parser/HTMLParserIdioms.h" #include "core/inspector/IdentifiersFactory.h" @@ -986,7 +987,7 @@ if (listener()) listener()->didReparseStyleSheet(); onStyleSheetTextChanged(); - m_pageStyleSheet->ownerDocument()->styleResolverChanged(FullStyleUpdate); + m_pageStyleSheet->ownerDocument()->styleEngine().resolverChanged(FullStyleUpdate); return true; } @@ -1489,7 +1490,8 @@ String styleSheetText; bool success = getText(&styleSheetText); if (success) { - String commentValue = V8ContentSearchUtil::findSourceURL(styleSheetText, true); + bool deprecated = false; + String commentValue = V8ContentSearchUtil::findSourceURL(styleSheetText, true, &deprecated); if (!commentValue.isEmpty()) { m_sourceURL = commentValue; return commentValue; @@ -1540,7 +1542,8 @@ String styleSheetText; bool success = getText(&styleSheetText); if (success) { - String commentValue = V8ContentSearchUtil::findSourceMapURL(styleSheetText, true); + bool deprecated = false; + String commentValue = V8ContentSearchUtil::findSourceMapURL(styleSheetText, true, &deprecated); if (!commentValue.isEmpty()) return commentValue; }
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp index 232a18b..d60b9d4 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
@@ -798,13 +798,13 @@ return value.release(); } -PassRefPtr<TracedValue> InspectorAnimationEvent::data(const Animation& player) +PassRefPtr<TracedValue> InspectorAnimationEvent::data(const Animation& animation) { RefPtr<TracedValue> value = TracedValue::create(); - value->setString("id", String::number(player.sequenceNumber())); - value->setString("state", player.playState()); - if (const AnimationEffect* effect = player.effect()) { - value->setString("name", effect->name()); + value->setString("id", String::number(animation.sequenceNumber())); + value->setString("state", animation.playState()); + if (const AnimationEffect* effect = animation.effect()) { + value->setString("name", animation.id()); if (effect->isKeyframeEffect()) { if (Element* target = toKeyframeEffect(effect)->target()) setNodeInfo(value.get(), target, "nodeId", "nodeName"); @@ -813,10 +813,10 @@ return value.release(); } -PassRefPtr<TracedValue> InspectorAnimationStateEvent::data(const Animation& player) +PassRefPtr<TracedValue> InspectorAnimationStateEvent::data(const Animation& animation) { RefPtr<TracedValue> value = TracedValue::create(); - value->setString("state", player.playState()); + value->setString("state", animation.playState()); return value.release(); }
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp index 52b6ac3..5eb6a702 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -243,7 +243,7 @@ removeFromPercentHeightContainer(); if (oldHorizontalWritingMode != isHorizontalWritingMode()) { - if (parent()) { + if (oldStyle) { if (isOrthogonalWritingModeRoot()) markOrthogonalWritingModeRoot(); else @@ -1867,8 +1867,8 @@ LayoutFlowThread* flowThread = flowThreadContainingBlock(); bool checkColumnBreaks = flowThread; bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->pageLogicalHeight(); // TODO(mstensho): Once columns can print, we have to check this. - bool checkBeforeAlways = (checkColumnBreaks && style()->columnBreakBefore() == PBALWAYS) - || (checkPageBreaks && style()->pageBreakBefore() == PBALWAYS); + bool checkBeforeAlways = (checkColumnBreaks && style()->breakBefore() == BreakColumn) + || (checkPageBreaks && style()->breakBefore() == BreakPage); return checkBeforeAlways && isForcedBreakAllowed(this); } @@ -1877,8 +1877,8 @@ LayoutFlowThread* flowThread = flowThreadContainingBlock(); bool checkColumnBreaks = flowThread; bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->pageLogicalHeight(); // TODO(mstensho): Once columns can print, we have to check this. - bool checkAfterAlways = (checkColumnBreaks && style()->columnBreakAfter() == PBALWAYS) - || (checkPageBreaks && style()->pageBreakAfter() == PBALWAYS); + bool checkAfterAlways = (checkColumnBreaks && style()->breakAfter() == BreakColumn) + || (checkPageBreaks && style()->breakAfter() == BreakPage); return checkAfterAlways && isForcedBreakAllowed(this); } @@ -4184,8 +4184,9 @@ bool checkColumnBreaks = flowThreadContainingBlock(); bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->pageLogicalHeight(); - bool isUnsplittable = (checkColumnBreaks && style()->columnBreakInside() == PBAVOID) - || (checkPageBreaks && style()->pageBreakInside() == PBAVOID); + EBreak breakInside = style()->breakInside(); + bool isUnsplittable = (checkColumnBreaks && (breakInside == BreakAvoid || breakInside == BreakAvoidColumn)) + || (checkPageBreaks && (breakInside == BreakAvoid || breakInside == BreakAvoidPage)); if (isUnsplittable) return AvoidBreaks; return AllowAnyBreaks;
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThreadTest.cpp b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThreadTest.cpp index e687e827..dda940e 100644 --- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThreadTest.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThreadTest.cpp
@@ -58,8 +58,8 @@ { const char* style = "<style>" - " #mc { -webkit-columns:2; }" - " .s, #spanner, #spanner1, #spanner2 { -webkit-column-span:all; }" + " #mc { columns:2; }" + " .s, #spanner, #spanner1, #spanner2 { column-span:all; }" "</style>"; setBodyInnerHTML(style + html); }
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp index 53ae6f1cd..5365263 100644 --- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -2684,7 +2684,9 @@ static bool findReferencingScrollAnchors(LayoutObject* layoutObject, FindReferencingScrollAnchorsBehavior behavior) { - PaintLayer* layer = layoutObject->enclosingLayer(); + PaintLayer* layer = nullptr; + if (LayoutObject* parent = layoutObject->parent()) + layer = parent->enclosingLayer(); bool found = false; // Walk up the layer tree to clear any scroll anchors that reference us. @@ -3373,9 +3375,9 @@ { if (!canUpdateSelectionOnRootLineBoxes()) return; - m_bitfields.setShouldInvalidateSelection(true); markContainerChainForPaintInvalidation(); + frameView()->scheduleVisualUpdateForPaintInvalidationIfNeeded(); } void LayoutObject::setShouldDoFullPaintInvalidation(PaintInvalidationReason reason)
diff --git a/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp b/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp index 768b3c05..4b0058a8 100644 --- a/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutObjectChildList.cpp
@@ -73,19 +73,21 @@ if (oldChild->isBox()) toLayoutBox(oldChild)->deleteLineBoxWrapper(); - // If oldChild is the start or end of the selection, then clear the selection to - // avoid problems of invalid pointers. - // FIXME: The FrameSelection should be responsible for this when it - // is notified of DOM mutations. - if (!owner->documentBeingDestroyed() && oldChild->isSelectionBorder()) - owner->view()->clearSelection(); - if (!owner->documentBeingDestroyed()) + if (!owner->documentBeingDestroyed()) { + // If oldChild is the start or end of the selection, then clear the selection to + // avoid problems of invalid pointers. + // FIXME: The FrameSelection should be responsible for this when it + // is notified of DOM mutations. + if (oldChild->isSelectionBorder()) + owner->view()->clearSelection(); + owner->notifyOfSubtreeChange(); - if (!owner->documentBeingDestroyed() && notifyLayoutObject) { - LayoutCounter::layoutObjectSubtreeWillBeDetached(oldChild); - oldChild->willBeRemovedFromTree(); + if (notifyLayoutObject) { + LayoutCounter::layoutObjectSubtreeWillBeDetached(oldChild); + oldChild->willBeRemovedFromTree(); + } } // WARNING: There should be no code running between willBeRemovedFromTree and the actual removal below.
diff --git a/third_party/WebKit/Source/core/layout/LayoutScrollbar.cpp b/third_party/WebKit/Source/core/layout/LayoutScrollbar.cpp index 033560b..d4e88f8a 100644 --- a/third_party/WebKit/Source/core/layout/LayoutScrollbar.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutScrollbar.cpp
@@ -272,8 +272,10 @@ partLayoutObject = 0; } - if (partLayoutObject) + if (partLayoutObject) { + partLayoutObject->adjustStyleBeforeSet(partStyle.get()); partLayoutObject->setStyle(partStyle.release()); + } } IntRect LayoutScrollbar::buttonRect(ScrollbarPart partType) const
diff --git a/third_party/WebKit/Source/core/layout/LayoutScrollbarPart.cpp b/third_party/WebKit/Source/core/layout/LayoutScrollbarPart.cpp index d21d838..e04f2c3 100644 --- a/third_party/WebKit/Source/core/layout/LayoutScrollbarPart.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutScrollbarPart.cpp
@@ -166,6 +166,15 @@ clearPreferredLogicalWidthsDirty(); } +void LayoutScrollbarPart::adjustStyleBeforeSet(ComputedStyle* newStyle) +{ + // LayoutScrollbarPart cannot be an orthogonal writing-mode root because + // FrameView calls layout() for all orthogonal writing-mode roots, but + // LayoutScrollbarPart::layout() will crash if m_scrollbar is nullptr. + if (parent()) + newStyle->setWritingMode(parent()->styleRef().writingMode()); +} + void LayoutScrollbarPart::styleWillChange(StyleDifference diff, const ComputedStyle& newStyle) { LayoutBlock::styleWillChange(diff, newStyle); @@ -175,6 +184,8 @@ void LayoutScrollbarPart::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle) { LayoutBlock::styleDidChange(diff, oldStyle); + // See adjustStyleBeforeSet() above. + ASSERT(!isOrthogonalWritingModeRoot()); setInline(false); clearPositionedState(); setFloating(false);
diff --git a/third_party/WebKit/Source/core/layout/LayoutScrollbarPart.h b/third_party/WebKit/Source/core/layout/LayoutScrollbarPart.h index eec2dce7..741026a 100644 --- a/third_party/WebKit/Source/core/layout/LayoutScrollbarPart.h +++ b/third_party/WebKit/Source/core/layout/LayoutScrollbarPart.h
@@ -74,6 +74,8 @@ bool isOfType(LayoutObjectType type) const override { return type == LayoutObjectLayoutScrollbarPart || LayoutBlock::isOfType(type); } LayoutObject* layoutObjectOwningScrollbar() const; + void adjustStyleBeforeSet(ComputedStyle*); + protected: void styleWillChange(StyleDifference, const ComputedStyle& newStyle) override; void styleDidChange(StyleDifference, const ComputedStyle* oldStyle) override;
diff --git a/third_party/WebKit/Source/core/layout/ScrollAnchorTest.cpp b/third_party/WebKit/Source/core/layout/ScrollAnchorTest.cpp index d431ffe..c4531afd 100644 --- a/third_party/WebKit/Source/core/layout/ScrollAnchorTest.cpp +++ b/third_party/WebKit/Source/core/layout/ScrollAnchorTest.cpp
@@ -4,7 +4,9 @@ #include "core/layout/ScrollAnchor.h" +#include "core/layout/LayoutBox.h" #include "core/layout/LayoutTestHelper.h" +#include "core/paint/PaintLayerScrollableArea.h" namespace blink { @@ -28,4 +30,35 @@ EXPECT_EQ(250, viewport->scrollPosition().y()); } +TEST_F(ScrollAnchorTest, AnchorWithLayerInScrollingDiv) +{ + setBodyInnerHTML( + "<style>" + " #scroller { overflow: scroll; width: 500px; height: 400px; }" + " div { height: 100px }" + " #block2 { overflow: hidden }" + " #space { height: 1000px; }" + "</style>" + "<div id='scroller'><div id='space'>" + "<div id='block1'>abc</div>" + "<div id='block2'>def</div>" + "</div></div>"); + + PaintLayerScrollableArea* scroller = toLayoutBox( + document().getElementById("scroller")->layoutObject())->scrollableArea(); + scroller->scrollBy(DoubleSize(0, 150), UserScroll); + document().getElementById("block1")->setAttribute(HTMLNames::styleAttr, "height: 200px"); + + // In this layout pass we will anchor to #block2 which has its own PaintLayer. + document().view()->updateAllLifecyclePhases(); + EXPECT_EQ(250, scroller->scrollPosition().y()); + EXPECT_EQ(document().getElementById("block2")->layoutObject(), + scroller->scrollAnchor().anchorObject()); + + // Test that the anchor object can be destroyed without affecting the scroll position. + document().getElementById("block2")->remove(); + document().view()->updateAllLifecyclePhases(); + EXPECT_EQ(250, scroller->scrollPosition().y()); +} + }
diff --git a/third_party/WebKit/Source/core/layout/api/LayoutBoxModel.h b/third_party/WebKit/Source/core/layout/api/LayoutBoxModel.h new file mode 100644 index 0000000..478d2f66 --- /dev/null +++ b/third_party/WebKit/Source/core/layout/api/LayoutBoxModel.h
@@ -0,0 +1,44 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LayoutBoxModel_h +#define LayoutBoxModel_h + +#include "core/layout/LayoutBoxModelObject.h" +#include "core/layout/api/LayoutItem.h" + +namespace blink { + +class LayoutBoxModelObject; + +class LayoutBoxModel : public LayoutItem { +public: + explicit LayoutBoxModel(LayoutBoxModelObject* layoutBox) + : LayoutItem(layoutBox) + { + } + + explicit LayoutBoxModel(const LayoutItem& item) + : LayoutItem(item) + { + ASSERT(!item || item.isBoxModelObject()); + } + + explicit LayoutBoxModel(std::nullptr_t) : LayoutItem(nullptr) { } + + LayoutBoxModel() { } + + PaintLayer* layer() const + { + return toBoxModel()->layer(); + } + +private: + LayoutBoxModelObject* toBoxModel() { return toLayoutBoxModelObject(layoutObject()); } + const LayoutBoxModelObject* toBoxModel() const { return toLayoutBoxModelObject(layoutObject()); } +}; + +} // namespace blink + +#endif // LayoutBoxModel_h
diff --git a/third_party/WebKit/Source/core/layout/api/LayoutItem.h b/third_party/WebKit/Source/core/layout/api/LayoutItem.h index ad01821c..e478bb8b 100644 --- a/third_party/WebKit/Source/core/layout/api/LayoutItem.h +++ b/third_party/WebKit/Source/core/layout/api/LayoutItem.h
@@ -31,12 +31,49 @@ // https://crbug.com/499321 operator LayoutObject*() const { return m_layoutObject; } - bool needsLayout() { return m_layoutObject->needsLayout(); } - void layout() { m_layoutObject->layout(); } + bool isBoxModelObject() const + { + return m_layoutObject->isBoxModelObject(); + } - LayoutItem container() const { return LayoutItem(m_layoutObject->container()); } + bool needsLayout() + { + return m_layoutObject->needsLayout(); + } - void setMayNeedPaintInvalidation() { m_layoutObject->setMayNeedPaintInvalidation(); } + void layout() + { + m_layoutObject->layout(); + } + + LayoutItem container() const + { + return LayoutItem(m_layoutObject->container()); + } + + void setMayNeedPaintInvalidation() + { + m_layoutObject->setMayNeedPaintInvalidation(); + } + + const ComputedStyle* style() const + { + return m_layoutObject->style(); + } + + bool hasLayer() const + { + return m_layoutObject->hasLayer(); + } + + void setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants() + { + m_layoutObject->setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); + } + +protected: + LayoutObject* layoutObject() { return m_layoutObject; } + const LayoutObject* layoutObject() const { return m_layoutObject; } private: LayoutObject* m_layoutObject;
diff --git a/third_party/WebKit/Source/core/layout/api/LineLayoutBlockFlow.h b/third_party/WebKit/Source/core/layout/api/LineLayoutBlockFlow.h index 9c80eb0..a88ab1b 100644 --- a/third_party/WebKit/Source/core/layout/api/LineLayoutBlockFlow.h +++ b/third_party/WebKit/Source/core/layout/api/LineLayoutBlockFlow.h
@@ -60,7 +60,7 @@ LayoutUnit logicalWidthForChild(LineLayoutBox child) const { - return toBlockFlow()->logicalWidthForChild(*toLayoutBox(child)); + return toBlockFlow()->logicalWidthForChild(*toLayoutBox(child.layoutObject())); } LayoutUnit marginStartForChild(const LayoutBoxModelObject& child) const @@ -70,7 +70,7 @@ LayoutUnit marginStartForChild(LineLayoutBox child) const { - return toBlockFlow()->marginStartForChild(*toLayoutBoxModelObject(child)); + return toBlockFlow()->marginStartForChild(*toLayoutBoxModelObject(child.layoutObject())); } LayoutUnit marginEndForChild(const LayoutBoxModelObject& child) const @@ -80,7 +80,7 @@ LayoutUnit marginEndForChild(LineLayoutBox child) const { - return toBlockFlow()->marginEndForChild(*toLayoutBoxModelObject(child)); + return toBlockFlow()->marginEndForChild(*toLayoutBoxModelObject(child.layoutObject())); } LayoutUnit marginBeforeForChild(const LayoutBoxModelObject& child) const @@ -105,12 +105,12 @@ void setStaticInlinePositionForChild(LineLayoutBox box, LayoutUnit inlinePosition) { - toBlockFlow()->setStaticInlinePositionForChild(*toLayoutBox(box), inlinePosition); + toBlockFlow()->setStaticInlinePositionForChild(*toLayoutBox(box.layoutObject()), inlinePosition); } void updateStaticInlinePositionForChild(LineLayoutBox box, LayoutUnit logicalTop, IndentTextOrNot indentText = DoNotIndentText) { - toBlockFlow()->updateStaticInlinePositionForChild(*toLayoutBox(box), logicalTop, indentText); + toBlockFlow()->updateStaticInlinePositionForChild(*toLayoutBox(box.layoutObject()), logicalTop, indentText); } FloatingObject* insertFloatingObject(LayoutBox& box) @@ -120,7 +120,7 @@ FloatingObject* insertFloatingObject(LineLayoutBox box) { - return toBlockFlow()->insertFloatingObject(*toLayoutBox(box)); + return toBlockFlow()->insertFloatingObject(*toLayoutBox(box.layoutObject())); } bool positionNewFloats(LineWidth* width)
diff --git a/third_party/WebKit/Source/core/layout/api/LineLayoutItem.h b/third_party/WebKit/Source/core/layout/api/LineLayoutItem.h index 4c4abbde..7730e03 100644 --- a/third_party/WebKit/Source/core/layout/api/LineLayoutItem.h +++ b/third_party/WebKit/Source/core/layout/api/LineLayoutItem.h
@@ -419,6 +419,7 @@ LayoutObject* m_layoutObject; friend class LineLayoutAPIShim; + friend class LineLayoutBlockFlow; // For layoutObject(). }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp index ec4a6e8..3cacebf 100644 --- a/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp +++ b/third_party/WebKit/Source/core/layout/compositing/CompositedLayerMapping.cpp
@@ -1301,17 +1301,35 @@ m_backgroundLayerPaintsFixedRootBackground = backgroundLayerPaintsFixedRootBackground; } -// Only a member function so it can call createGraphicsLayer. bool CompositedLayerMapping::toggleScrollbarLayerIfNeeded(OwnPtr<GraphicsLayer>& layer, bool needsLayer, CompositingReasons reason) { if (needsLayer == !!layer) return false; layer = needsLayer ? createGraphicsLayer(reason) : nullptr; + + if (PaintLayerScrollableArea* scrollableArea = m_owningLayer.scrollableArea()) { + if (ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer)) { + if (reason == CompositingReasonLayerForHorizontalScrollbar) + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(scrollableArea, HorizontalScrollbar); + else if (reason == CompositingReasonLayerForVerticalScrollbar) + scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(scrollableArea, VerticalScrollbar); + } + } return true; } bool CompositedLayerMapping::updateOverflowControlsLayers(bool needsHorizontalScrollbarLayer, bool needsVerticalScrollbarLayer, bool needsScrollCornerLayer, bool needsAncestorClip) { + if (PaintLayerScrollableArea* scrollableArea = m_owningLayer.scrollableArea()) { + // If the scrollable area is marked as needing a new scrollbar layer, + // destroy the layer now so that it will be created again below. + if (m_layerForHorizontalScrollbar && needsHorizontalScrollbarLayer && scrollableArea->shouldRebuildHorizontalScrollbarLayer()) + toggleScrollbarLayerIfNeeded(m_layerForHorizontalScrollbar, false, CompositingReasonLayerForHorizontalScrollbar); + if (m_layerForVerticalScrollbar && needsVerticalScrollbarLayer && scrollableArea->shouldRebuildVerticalScrollbarLayer()) + toggleScrollbarLayerIfNeeded(m_layerForVerticalScrollbar, false, CompositingReasonLayerForVerticalScrollbar); + scrollableArea->resetRebuildScrollbarLayerFlags(); + } + // If the subtree is invisible, we don't actually need scrollbar layers. bool invisible = m_owningLayer.subtreeIsInvisible(); needsHorizontalScrollbarLayer &= !invisible; @@ -1327,13 +1345,6 @@ bool needsOverflowAncestorClipLayer = needsOverflowControlsHostLayer && needsAncestorClip; toggleScrollbarLayerIfNeeded(m_overflowControlsAncestorClippingLayer, needsOverflowAncestorClipLayer, CompositingReasonLayerForOverflowControlsHost); - if (ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer)) { - if (horizontalScrollbarLayerChanged) - scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_owningLayer.scrollableArea(), HorizontalScrollbar); - if (verticalScrollbarLayerChanged) - scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_owningLayer.scrollableArea(), VerticalScrollbar); - } - return horizontalScrollbarLayerChanged || verticalScrollbarLayerChanged || scrollCornerLayerChanged; }
diff --git a/third_party/WebKit/Source/core/layout/line/RootInlineBox.cpp b/third_party/WebKit/Source/core/layout/line/RootInlineBox.cpp index 4f369da..42205c2 100644 --- a/third_party/WebKit/Source/core/layout/line/RootInlineBox.cpp +++ b/third_party/WebKit/Source/core/layout/line/RootInlineBox.cpp
@@ -191,6 +191,33 @@ } } +static inline void snapHeight(int& maxAscent, int& maxDescent, const ComputedStyle& style) +{ + // If position is 0, add spaces to over/under equally. + // https://drafts.csswg.org/css-snap-size/#snap-height + int unit = style.snapHeightUnit(); + ASSERT(unit); + int position = style.snapHeightPosition(); + if (!position) { + int space = unit - ((maxAscent + maxDescent) % unit); + maxDescent += space / 2; + maxAscent += space - space / 2; + return; + } + + // Match the baseline to the specified position. + // https://drafts.csswg.org/css-snap-size/#snap-baseline + ASSERT(position > 0 && position <= 100); + position = position * unit / 100; + int spaceOver = position - maxAscent % unit; + if (spaceOver < 0) { + spaceOver += unit; + ASSERT(spaceOver >= 0); + } + maxAscent += spaceOver; + maxDescent += unit - (maxAscent + maxDescent) % unit; +} + LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) { // SVG will handle vertical alignment on its own. @@ -214,6 +241,9 @@ if (maxAscent + maxDescent < std::max(maxPositionTop, maxPositionBottom)) adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); + if (lineLayoutItem().styleRef().snapHeightUnit()) + snapHeight(maxAscent, maxDescent, lineLayoutItem().styleRef()); + LayoutUnit maxHeight = LayoutUnit(maxAscent + maxDescent); LayoutUnit lineTop = heightOfBlock; LayoutUnit lineBottom = heightOfBlock;
diff --git a/third_party/WebKit/Source/core/loader/HttpEquiv.cpp b/third_party/WebKit/Source/core/loader/HttpEquiv.cpp index f2ce04c..34b9cd8e 100644 --- a/third_party/WebKit/Source/core/loader/HttpEquiv.cpp +++ b/third_party/WebKit/Source/core/loader/HttpEquiv.cpp
@@ -74,7 +74,7 @@ // -dwh document.styleEngine().setSelectedStylesheetSetName(content); document.styleEngine().setPreferredStylesheetSetName(content); - document.styleResolverChanged(); + document.styleEngine().resolverChanged(FullStyleUpdate); } void HttpEquiv::processHttpEquivRefresh(Document& document, const AtomicString& content)
diff --git a/third_party/WebKit/Source/core/page/EventSource.cpp b/third_party/WebKit/Source/core/page/EventSource.cpp index b13242df..0fb02c69 100644 --- a/third_party/WebKit/Source/core/page/EventSource.cpp +++ b/third_party/WebKit/Source/core/page/EventSource.cpp
@@ -44,7 +44,6 @@ #include "core/frame/LocalDOMWindow.h" #include "core/frame/LocalFrame.h" #include "core/frame/csp/ContentSecurityPolicy.h" -#include "core/html/parser/TextResourceDecoder.h" #include "core/inspector/ConsoleMessage.h" #include "core/inspector/InspectorInstrumentation.h" #include "core/loader/ThreadableLoader.h" @@ -55,7 +54,6 @@ #include "platform/network/ResourceResponse.h" #include "platform/weborigin/SecurityOrigin.h" #include "public/platform/WebURLRequest.h" -#include "wtf/ASCIICType.h" #include "wtf/text/StringBuilder.h" namespace blink { @@ -67,9 +65,7 @@ , m_url(url) , m_withCredentials(eventSourceInit.withCredentials()) , m_state(CONNECTING) - , m_decoder(TextResourceDecoder::create("text/plain", "UTF-8")) , m_connectTimer(this, &EventSource::connectTimerFired) - , m_discardTrailingNewline(false) , m_reconnectDelay(defaultReconnectDelay) { } @@ -127,10 +123,10 @@ request.setHTTPHeaderField(HTTPNames::Accept, "text/event-stream"); request.setHTTPHeaderField(HTTPNames::Cache_Control, "no-cache"); request.setRequestContext(WebURLRequest::RequestContextEventSource); - if (!m_lastEventId.isEmpty()) { + if (m_parser && !m_parser->lastEventId().isEmpty()) { // HTTP headers are Latin-1 byte strings, but the Last-Event-ID header is encoded as UTF-8. // TODO(davidben): This should be captured in the type of setHTTPHeaderField's arguments. - CString lastEventIdUtf8 = m_lastEventId.utf8(); + CString lastEventIdUtf8 = m_parser->lastEventId().utf8(); request.setHTTPHeaderField(HTTPNames::Last_Event_ID, AtomicString(reinterpret_cast<const LChar*>(lastEventIdUtf8.data()), lastEventIdUtf8.length())); } @@ -196,6 +192,8 @@ ASSERT(!m_loader); return; } + if (m_parser) + m_parser->stop(); // Stop trying to reconnect if EventSource was explicitly closed or if ActiveDOMObject::stop() was called. if (m_connectTimer.isActive()) { @@ -256,6 +254,12 @@ if (responseIsValid) { m_state = OPEN; + AtomicString lastEventId; + if (m_parser) { + // The new parser takes over the event ID. + lastEventId = m_parser->lastEventId(); + } + m_parser = new EventSourceParser(lastEventId, this); dispatchEvent(Event::create(EventTypeNames::open)); } else { m_loader->cancel(); @@ -267,9 +271,9 @@ { ASSERT(m_state == OPEN); ASSERT(m_loader); + ASSERT(m_parser); - append(m_receiveBuf, m_decoder->decode(data, length)); - parseEventStream(); + m_parser->addBytes(data, length); } void EventSource::didFinishLoading(unsigned long, double) @@ -277,15 +281,6 @@ ASSERT(m_state == OPEN); ASSERT(m_loader); - if (m_receiveBuf.size() > 0 || m_data.size() > 0) { - parseEventStream(); - - // Discard everything that has not been dispatched by now. - m_receiveBuf.clear(); - m_data.clear(); - m_eventName = emptyAtom; - m_currentlyParsedEventId = nullAtom; - } networkRequestEnded(); } @@ -316,6 +311,20 @@ abortConnectionAttempt(); } +void EventSource::onMessageEvent(const AtomicString& eventType, const String& data, const AtomicString& lastEventId) +{ + RefPtrWillBeRawPtr<MessageEvent> e = MessageEvent::create(); + e->initMessageEvent(eventType, false, false, SerializedScriptValueFactory::instance().create(data), m_eventStreamOrigin, lastEventId, 0, nullptr); + + InspectorInstrumentation::willDispatchEventSourceEvent(executionContext(), this, eventType, lastEventId, data); + dispatchEvent(e); +} + +void EventSource::onReconnectionTimeSet(unsigned long long reconnectionTime) +{ + m_reconnectDelay = reconnectionTime; +} + void EventSource::abortConnectionAttempt() { ASSERT(m_state == CONNECTING); @@ -327,105 +336,6 @@ dispatchEvent(Event::create(EventTypeNames::error)); } -void EventSource::parseEventStream() -{ - unsigned bufPos = 0; - unsigned bufSize = m_receiveBuf.size(); - while (bufPos < bufSize) { - if (m_discardTrailingNewline) { - if (m_receiveBuf[bufPos] == '\n') - bufPos++; - m_discardTrailingNewline = false; - } - - int lineLength = -1; - int fieldLength = -1; - for (unsigned i = bufPos; lineLength < 0 && i < bufSize; i++) { - switch (m_receiveBuf[i]) { - case ':': - if (fieldLength < 0) - fieldLength = i - bufPos; - break; - case '\r': - m_discardTrailingNewline = true; - case '\n': - lineLength = i - bufPos; - break; - } - } - - if (lineLength < 0) - break; - - parseEventStreamLine(bufPos, fieldLength, lineLength); - bufPos += lineLength + 1; - - // EventSource.close() might've been called by one of the message event handlers. - // Per spec, no further messages should be fired after that. - if (m_state == CLOSED) - break; - } - - if (bufPos == bufSize) - m_receiveBuf.clear(); - else if (bufPos) - m_receiveBuf.remove(0, bufPos); -} - -void EventSource::parseEventStreamLine(unsigned bufPos, int fieldLength, int lineLength) -{ - if (!lineLength) { - if (!m_data.isEmpty()) { - m_data.removeLast(); - if (!m_currentlyParsedEventId.isNull()) { - m_lastEventId = m_currentlyParsedEventId; - m_currentlyParsedEventId = nullAtom; - } - InspectorInstrumentation::willDispachEventSourceEvent(executionContext(), this, m_eventName.isEmpty() ? EventTypeNames::message : m_eventName, m_lastEventId, m_data); - dispatchEvent(createMessageEvent()); - } - if (!m_eventName.isEmpty()) - m_eventName = emptyAtom; - } else if (fieldLength) { - bool noValue = fieldLength < 0; - - String field(&m_receiveBuf[bufPos], noValue ? lineLength : fieldLength); - int step; - if (noValue) - step = lineLength; - else if (m_receiveBuf[bufPos + fieldLength + 1] != ' ') - step = fieldLength + 1; - else - step = fieldLength + 2; - bufPos += step; - int valueLength = lineLength - step; - - if (field == "data") { - if (valueLength) - m_data.append(&m_receiveBuf[bufPos], valueLength); - m_data.append('\n'); - } else if (field == "event") { - m_eventName = valueLength ? AtomicString(&m_receiveBuf[bufPos], valueLength) : ""; - } else if (field == "id") { - m_currentlyParsedEventId = valueLength ? AtomicString(&m_receiveBuf[bufPos], valueLength) : ""; - } else if (field == "retry") { - bool hasOnlyDigits = true; - for (int i = 0; i < valueLength && hasOnlyDigits; ++i) { - hasOnlyDigits = isASCIIDigit(m_receiveBuf[bufPos + i]); - } - if (!valueLength) { - m_reconnectDelay = defaultReconnectDelay; - } else if (hasOnlyDigits) { - String value(&m_receiveBuf[bufPos], valueLength); - bool ok; - unsigned long long retry = value.toUInt64(&ok); - if (ok) - m_reconnectDelay = retry; - } - } - } -} - void EventSource::stop() { close(); @@ -436,18 +346,12 @@ return m_state != CLOSED; } -PassRefPtrWillBeRawPtr<MessageEvent> EventSource::createMessageEvent() -{ - RefPtrWillBeRawPtr<MessageEvent> event = MessageEvent::create(); - event->initMessageEvent(m_eventName.isEmpty() ? EventTypeNames::message : m_eventName, false, false, SerializedScriptValueFactory::instance().create(String(m_data)), m_eventStreamOrigin, m_lastEventId, 0, nullptr); - m_data.clear(); - return event.release(); -} - DEFINE_TRACE(EventSource) { + visitor->trace(m_parser); RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor); ActiveDOMObject::trace(visitor); + EventSourceParser::Client::trace(visitor); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/page/EventSource.h b/third_party/WebKit/Source/core/page/EventSource.h index 23eddfc7..5e12eba 100644 --- a/third_party/WebKit/Source/core/page/EventSource.h +++ b/third_party/WebKit/Source/core/page/EventSource.h
@@ -36,24 +36,23 @@ #include "core/events/EventTarget.h" #include "core/loader/ThreadableLoader.h" #include "core/loader/ThreadableLoaderClient.h" +#include "core/page/EventSourceParser.h" #include "platform/Timer.h" #include "platform/heap/Handle.h" #include "platform/weborigin/KURL.h" +#include "wtf/Forward.h" #include "wtf/RefPtr.h" -#include "wtf/Vector.h" namespace blink { class EventSourceInit; class ExceptionState; -class MessageEvent; class ResourceResponse; -class TextResourceDecoder; -class CORE_EXPORT EventSource final : public RefCountedGarbageCollectedEventTargetWithInlineData<EventSource>, private ThreadableLoaderClient, public ActiveDOMObject { +class CORE_EXPORT EventSource final : public RefCountedGarbageCollectedEventTargetWithInlineData<EventSource>, private ThreadableLoaderClient, public ActiveDOMObject, public EventSourceParser::Client { DEFINE_WRAPPERTYPEINFO(); REFCOUNTED_GARBAGE_COLLECTED_EVENT_TARGET(EventSource); - WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(EventSource); + USING_GARBAGE_COLLECTED_MIXIN(EventSource); public: static EventSource* create(ExecutionContext*, const String& url, const EventSourceInit&, ExceptionState&); ~EventSource() override; @@ -63,7 +62,7 @@ String url() const; bool withCredentials() const; - enum State : unsigned short { + enum State : short { CONNECTING = 0, OPEN = 1, CLOSED = 2 @@ -92,11 +91,6 @@ DECLARE_VIRTUAL_TRACE(); - void setStateForTest(State state) { m_state = state; } - void setThreadableLoaderForTest(PassRefPtr<ThreadableLoader> loader) { m_loader = loader; } - ThreadableLoaderClient* asThreadableLoaderClientForTest() { return this; } - unsigned long long reconnectDelayForTest() const { return m_reconnectDelay; } - private: EventSource(ExecutionContext*, const KURL&, const EventSourceInit&); @@ -107,30 +101,24 @@ void didFailAccessControlCheck(const ResourceError&) override; void didFailRedirectCheck() override; + void onMessageEvent(const AtomicString& event, const String& data, const AtomicString& id) override; + void onReconnectionTimeSet(unsigned long long reconnectionTime) override; + void scheduleInitialConnect(); void connect(); void networkRequestEnded(); void scheduleReconnect(); void connectTimerFired(Timer<EventSource>*); void abortConnectionAttempt(); - void parseEventStream(); - void parseEventStreamLine(unsigned pos, int fieldLength, int lineLength); - PassRefPtrWillBeRawPtr<MessageEvent> createMessageEvent(); KURL m_url; bool m_withCredentials; State m_state; - OwnPtr<TextResourceDecoder> m_decoder; + Member<EventSourceParser> m_parser; RefPtr<ThreadableLoader> m_loader; Timer<EventSource> m_connectTimer; - Vector<UChar> m_receiveBuf; - bool m_discardTrailingNewline; - AtomicString m_eventName; - Vector<UChar> m_data; - AtomicString m_currentlyParsedEventId; - AtomicString m_lastEventId; unsigned long long m_reconnectDelay; String m_eventStreamOrigin; };
diff --git a/third_party/WebKit/Source/core/page/EventSourceParser.cpp b/third_party/WebKit/Source/core/page/EventSourceParser.cpp new file mode 100644 index 0000000..861bb6d --- /dev/null +++ b/third_party/WebKit/Source/core/page/EventSourceParser.cpp
@@ -0,0 +1,135 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/page/EventSourceParser.h" + +#include "core/EventTypeNames.h" +#include "core/page/EventSource.h" +#include "wtf/ASCIICType.h" +#include "wtf/Assertions.h" +#include "wtf/NotFound.h" +#include "wtf/StdLibExtras.h" +#include "wtf/text/TextEncoding.h" +#include "wtf/text/TextEncodingRegistry.h" + +namespace blink { + +EventSourceParser::EventSourceParser(const AtomicString& lastEventId, Client* client) + : m_id(lastEventId) + , m_lastEventId(lastEventId) + , m_client(client) + , m_codec(newTextCodec(UTF8Encoding())) +{ +} + +void EventSourceParser::addBytes(const char* bytes, size_t size) +{ + // A line consists of |m_line| followed by + // |bytes[start..(next line break)]|. + size_t start = 0; + const unsigned char kBOM[] = {0xef, 0xbb, 0xbf}; + for (size_t i = 0; i < size && !m_isStopped; ++i) { + // As kBOM contains neither CR nor LF, we can think BOM and the line + // break separately. + if (m_isRecognizingBOM && m_line.size() + (i - start) == WTF_ARRAY_LENGTH(kBOM)) { + Vector<char> line = m_line; + line.append(&bytes[start], i - start); + ASSERT(line.size() == WTF_ARRAY_LENGTH(kBOM)); + m_isRecognizingBOM = false; + if (memcmp(line.data(), kBOM, sizeof(kBOM)) == 0) { + start = i; + m_line.clear(); + continue; + } + } + if (m_isRecognizingCRLF && bytes[i] == '\n') { + // This is the latter part of "\r\n". + m_isRecognizingCRLF = false; + ++start; + continue; + } + m_isRecognizingCRLF = false; + if (bytes[i] == '\r' || bytes[i] == '\n') { + m_line.append(&bytes[start], i - start); + parseLine(); + m_line.clear(); + start = i + 1; + m_isRecognizingCRLF = bytes[i] == '\r'; + m_isRecognizingBOM = false; + } + } + if (m_isStopped) + return; + m_line.append(&bytes[start], size - start); +} + +void EventSourceParser::parseLine() +{ + if (m_line.size() == 0) { + m_lastEventId = m_id; + // We dispatch an event when seeing an empty line. + if (!m_data.isEmpty()) { + ASSERT(m_data[m_data.size() - 1] == '\n'); + String data = fromUTF8(m_data.data(), m_data.size() - 1); + m_client->onMessageEvent(m_eventType.isEmpty() ? EventTypeNames::message : m_eventType, data, m_lastEventId); + m_data.clear(); + } + m_eventType = nullAtom; + return; + } + size_t fieldNameEnd = m_line.find(':'); + size_t fieldValueStart; + if (fieldNameEnd == WTF::kNotFound) { + fieldNameEnd = m_line.size(); + fieldValueStart = fieldNameEnd; + } else { + fieldValueStart = fieldNameEnd + 1; + if (fieldValueStart < m_line.size() && m_line[fieldValueStart] == ' ') { + ++fieldValueStart; + } + } + size_t fieldValueSize = m_line.size() - fieldValueStart; + String fieldName = fromUTF8(m_line.data(), fieldNameEnd); + if (fieldName == "event") { + m_eventType = AtomicString(fromUTF8(m_line.data() + fieldValueStart, fieldValueSize)); + return; + } + if (fieldName == "data") { + m_data.append(m_line.data() + fieldValueStart, fieldValueSize); + m_data.append('\n'); + return; + } + if (fieldName == "id") { + m_id = AtomicString(fromUTF8(m_line.data() + fieldValueStart, fieldValueSize)); + return; + } + if (fieldName == "retry") { + bool hasOnlyDigits = true; + for (size_t i = fieldValueStart; i < m_line.size() && hasOnlyDigits; ++i) + hasOnlyDigits = isASCIIDigit(m_line[i]); + if (fieldValueStart == m_line.size()) { + m_client->onReconnectionTimeSet(EventSource::defaultReconnectDelay); + } else if (hasOnlyDigits) { + bool ok; + auto reconnectionTime = fromUTF8(m_line.data() + fieldValueStart, fieldValueSize).toUInt64Strict(&ok); + if (ok) + m_client->onReconnectionTimeSet(reconnectionTime); + } + return; + } + // Unrecognized field name. Ignore! +} + +String EventSourceParser::fromUTF8(const char* bytes, size_t size) +{ + return m_codec->decode(bytes, size, WTF::DataEOF); +} + +DEFINE_TRACE(EventSourceParser) +{ + visitor->trace(m_client); +} + +} // namespace blink +
diff --git a/third_party/WebKit/Source/core/page/EventSourceParser.h b/third_party/WebKit/Source/core/page/EventSourceParser.h new file mode 100644 index 0000000..7629b68 --- /dev/null +++ b/third_party/WebKit/Source/core/page/EventSourceParser.h
@@ -0,0 +1,61 @@ +// 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 EventSourceParser_h +#define EventSourceParser_h + +#include "core/CoreExport.h" +#include "platform/heap/Handle.h" +#include "wtf/OwnPtr.h" +#include "wtf/Vector.h" +#include "wtf/text/AtomicString.h" +#include "wtf/text/TextCodec.h" +#include "wtf/text/WTFString.h" + +namespace blink { + +class CORE_EXPORT EventSourceParser final : public GarbageCollectedFinalized<EventSourceParser> { +public: + class CORE_EXPORT Client : public GarbageCollectedMixin { + public: + virtual ~Client() {} + virtual void onMessageEvent(const AtomicString& type, const String& data, const AtomicString& lastEventId) = 0; + virtual void onReconnectionTimeSet(unsigned long long reconnectionTime) = 0; + DEFINE_INLINE_VIRTUAL_TRACE() {} + }; + + EventSourceParser(const AtomicString& lastEventId, Client*); + + void addBytes(const char*, size_t); + const AtomicString& lastEventId() const { return m_lastEventId; } + // Stop parsing. This can be called from Client::onMessageEvent. + void stop() { m_isStopped = true; } + DECLARE_TRACE(); + +private: + void parseLine(); + String fromUTF8(const char* bytes, size_t); + + Vector<char> m_line; + + AtomicString m_eventType; + Vector<char> m_data; + // This variable corresponds to "last event ID buffer" in the spec. The + // value can be discarded when a connection is disconnected while + // parsing an event. + AtomicString m_id; + // This variable corresponds to "last event ID string" in the spec. + AtomicString m_lastEventId; + + Member<Client> m_client; + OwnPtr<TextCodec> m_codec; + + bool m_isRecognizingCRLF = false; + bool m_isRecognizingBOM = true; + bool m_isStopped = false; +}; + +} // namespace blink + +#endif // EventSourceParser_h
diff --git a/third_party/WebKit/Source/core/page/EventSourceParserTest.cpp b/third_party/WebKit/Source/core/page/EventSourceParserTest.cpp new file mode 100644 index 0000000..d9db5532 --- /dev/null +++ b/third_party/WebKit/Source/core/page/EventSourceParserTest.cpp
@@ -0,0 +1,407 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/page/EventSourceParser.h" + +#include "core/page/EventSource.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "wtf/text/CharacterNames.h" + +#include <string.h> + +namespace blink { + +namespace { + +struct EventOrReconnectionTimeSetting { + enum Type { + Event, + ReconnectionTimeSetting, + }; + + EventOrReconnectionTimeSetting(const AtomicString& event, const String& data, const AtomicString& id) + : type(Event) + , event(event) + , data(data) + , id(id) + , reconnectionTime(0) + { + } + explicit EventOrReconnectionTimeSetting(unsigned long long reconnectionTime) + : type(ReconnectionTimeSetting) + , reconnectionTime(reconnectionTime) + { + } + + const Type type; + const AtomicString event; + const String data; + const AtomicString id; + const unsigned long long reconnectionTime; +}; + +class Client : public GarbageCollectedFinalized<Client>, public EventSourceParser::Client { + USING_GARBAGE_COLLECTED_MIXIN(Client); +public: + ~Client() override {} + const Vector<EventOrReconnectionTimeSetting>& events() const { return m_events; } + void onMessageEvent(const AtomicString& event, const String& data, const AtomicString& id) override + { + m_events.append(EventOrReconnectionTimeSetting(event, data, id)); + } + void onReconnectionTimeSet(unsigned long long reconnectionTime) override + { + m_events.append(EventOrReconnectionTimeSetting(reconnectionTime)); + } + +private: + Vector<EventOrReconnectionTimeSetting> m_events; +}; + +class StoppingClient : public GarbageCollectedFinalized<StoppingClient>, public EventSourceParser::Client { + USING_GARBAGE_COLLECTED_MIXIN(StoppingClient); +public: + ~StoppingClient() override {} + const Vector<EventOrReconnectionTimeSetting>& events() const { return m_events; } + void setParser(EventSourceParser* parser) { m_parser = parser; } + void onMessageEvent(const AtomicString& event, const String& data, const AtomicString& id) override + { + m_parser->stop(); + m_events.append(EventOrReconnectionTimeSetting(event, data, id)); + } + void onReconnectionTimeSet(unsigned long long reconnectionTime) override + { + m_events.append(EventOrReconnectionTimeSetting(reconnectionTime)); + } + + DEFINE_INLINE_VIRTUAL_TRACE() + { + visitor->trace(m_parser); + EventSourceParser::Client::trace(visitor); + } + +private: + Member<EventSourceParser> m_parser; + Vector<EventOrReconnectionTimeSetting> m_events; +}; + +class EventSourceParserTest : public ::testing::Test { +protected: + using Type = EventOrReconnectionTimeSetting::Type; + EventSourceParserTest() + : m_client(new Client) + , m_parser(new EventSourceParser(AtomicString(), m_client)) + { + } + ~EventSourceParserTest() override {} + + void enqueue(const char* data) { m_parser->addBytes(data, strlen(data)); } + void enqueueOneByOne(const char* data) + { + const char*p = data; + while (*p != '\0') + m_parser->addBytes(p++, 1); + } + + const Vector<EventOrReconnectionTimeSetting>& events() { return m_client->events(); } + + EventSourceParser* parser() { return m_parser; } + + Persistent<Client> m_client; + Persistent<EventSourceParser> m_parser; +}; + +TEST_F(EventSourceParserTest, EmptyMessageEventShouldNotBeDispatched) +{ + enqueue("\n"); + + EXPECT_EQ(0u, events().size()); + EXPECT_EQ(String(), parser()->lastEventId()); +} + +TEST_F(EventSourceParserTest, DispatchSimpleMessageEvent) +{ + enqueue("data:hello\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); + EXPECT_EQ(String(), events()[0].id); + EXPECT_EQ(AtomicString(), parser()->lastEventId()); +} + +TEST_F(EventSourceParserTest, ConstructWithLastEventId) +{ + m_parser = new EventSourceParser("hoge", m_client); + EXPECT_EQ("hoge", parser()->lastEventId()); + + enqueue("data:hello\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); + EXPECT_EQ("hoge", events()[0].id); + EXPECT_EQ("hoge", parser()->lastEventId()); +} + +TEST_F(EventSourceParserTest, DispatchMessageEventWithLastEventId) +{ + enqueue("id:99\ndata:hello\n"); + EXPECT_EQ(String(), parser()->lastEventId()); + + enqueue("\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); + EXPECT_EQ("99", events()[0].id); + EXPECT_EQ("99", parser()->lastEventId()); +} + +TEST_F(EventSourceParserTest, LastEventIdCanBeUpdatedEvenWhenDataIsEmpty) +{ + enqueue("id:99\n"); + EXPECT_EQ(String(), parser()->lastEventId()); + + enqueue("\n"); + + ASSERT_EQ(0u, events().size()); + EXPECT_EQ("99", parser()->lastEventId()); +} + +TEST_F(EventSourceParserTest, DispatchMessageEventWithCustomEventType) +{ + enqueue("event:foo\ndata:hello\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("foo", events()[0].event); + EXPECT_EQ("hello", events()[0].data); +} + +TEST_F(EventSourceParserTest, RetryTakesEffectEvenWhenNotDispatching) +{ + enqueue("retry:999\n"); + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::ReconnectionTimeSetting, events()[0].type); + ASSERT_EQ(999u, events()[0].reconnectionTime); +} + +TEST_F(EventSourceParserTest, EventTypeShouldBeReset) +{ + enqueue("event:foo\ndata:hello\n\ndata:bye\n\n"); + + ASSERT_EQ(2u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("foo", events()[0].event); + EXPECT_EQ("hello", events()[0].data); + + ASSERT_EQ(Type::Event, events()[1].type); + EXPECT_EQ("message", events()[1].event); + EXPECT_EQ("bye", events()[1].data); +} + +TEST_F(EventSourceParserTest, DataShouldBeReset) +{ + enqueue("data:hello\n\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); +} + +TEST_F(EventSourceParserTest, LastEventIdShouldNotBeReset) +{ + enqueue("id:99\ndata:hello\n\ndata:bye\n\n"); + + EXPECT_EQ("99", parser()->lastEventId()); + ASSERT_EQ(2u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); + EXPECT_EQ("99", events()[0].id); + + ASSERT_EQ(Type::Event, events()[1].type); + EXPECT_EQ("message", events()[1].event); + EXPECT_EQ("bye", events()[1].data); + EXPECT_EQ("99", events()[1].id); +} + +TEST_F(EventSourceParserTest, VariousNewLinesShouldBeAllowed) +{ + enqueueOneByOne("data:hello\r\n\rdata:bye\r\r"); + + ASSERT_EQ(2u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); + + ASSERT_EQ(Type::Event, events()[1].type); + EXPECT_EQ("message", events()[1].event); + EXPECT_EQ("bye", events()[1].data); +} + +TEST_F(EventSourceParserTest, RetryWithEmptyValueShouldRestoreDefaultValue) +{ + // TODO(yhirano): This is unspecified in the spec. We need to update + // the implementation or the spec. See https://crbug.com/587980. + enqueue("retry\n"); + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::ReconnectionTimeSetting, events()[0].type); + EXPECT_EQ(EventSource::defaultReconnectDelay, events()[0].reconnectionTime); +} + +TEST_F(EventSourceParserTest, NonDigitRetryShouldBeIgnored) +{ + enqueue("retry:a0\n"); + enqueue("retry:xi\n"); + enqueue("retry:2a\n"); + enqueue("retry:09a\n"); + enqueue("retry:1\b\n"); + enqueue("retry: 1234\n"); + enqueue("retry:456 \n"); + + EXPECT_EQ(0u, events().size()); +} + +TEST_F(EventSourceParserTest, UnrecognizedFieldShouldBeIgnored) +{ + enqueue("data:hello\nhoge:fuga\npiyo\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); +} + +TEST_F(EventSourceParserTest, CommentShouldBeIgnored) +{ + enqueue("data:hello\n:event:a\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); +} + +TEST_F(EventSourceParserTest, BOMShouldBeIgnored) +{ + // This line is recognized because "\xef\xbb\xbf" is a BOM. + enqueue("\xef\xbb\xbf" "data:hello\n"); + // This line is ignored because "\xef\xbb\xbf" is part of the field name. + enqueue("\xef\xbb\xbf" "data:bye\n"); + enqueue("\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); +} + +TEST_F(EventSourceParserTest, BOMShouldBeIgnored_OneByOne) +{ + // This line is recognized because "\xef\xbb\xbf" is a BOM. + enqueueOneByOne("\xef\xbb\xbf" "data:hello\n"); + // This line is ignored because "\xef\xbb\xbf" is part of the field name. + enqueueOneByOne("\xef\xbb\xbf" "data:bye\n"); + enqueueOneByOne("\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); +} + +TEST_F(EventSourceParserTest, ColonlessLineShouldBeTreatedAsNameOnlyField) +{ + enqueue("data:hello\nevent:a\nevent\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("hello", events()[0].data); +} + +TEST_F(EventSourceParserTest, AtMostOneLeadingSpaceCanBeSkipped) +{ + enqueue("data: hello \nevent: type \n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ(" type ", events()[0].event); + EXPECT_EQ(" hello ", events()[0].data); +} + +TEST_F(EventSourceParserTest, DataShouldAccumulate) +{ + enqueue("data\ndata:hello\ndata: world\ndata\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + EXPECT_EQ("\nhello\nworld\n", events()[0].data); +} + +TEST_F(EventSourceParserTest, EventShouldNotAccumulate) +{ + enqueue("data:hello\nevent:a\nevent:b\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("b", events()[0].event); + EXPECT_EQ("hello", events()[0].data); +} + +TEST_F(EventSourceParserTest, FeedDataOneByOne) +{ + enqueueOneByOne("data:hello\r\ndata:world\revent:a\revent:b\nid:4\n\nid:8\ndata:bye\r\n\r"); + + ASSERT_EQ(2u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("b", events()[0].event); + EXPECT_EQ("hello\nworld", events()[0].data); + EXPECT_EQ("4", events()[0].id); + + ASSERT_EQ(Type::Event, events()[1].type); + EXPECT_EQ("message", events()[1].event); + EXPECT_EQ("bye", events()[1].data); + EXPECT_EQ("8", events()[1].id); +} + +TEST_F(EventSourceParserTest, InvalidUTF8Sequence) +{ + enqueue("data:\xffhello\xc2\ndata:bye\n\n"); + + ASSERT_EQ(1u, events().size()); + ASSERT_EQ(Type::Event, events()[0].type); + EXPECT_EQ("message", events()[0].event); + String expected = String() + replacementCharacter + "hello" + replacementCharacter + "\nbye"; + EXPECT_EQ(expected, events()[0].data); +} + +TEST(EventSourceParserStoppingTest, StopWhileParsing) +{ + StoppingClient* client = new StoppingClient(); + EventSourceParser* parser = new EventSourceParser(AtomicString(), client); + client->setParser(parser); + + const char input[] = "data:hello\nid:99\n\nid:44\ndata:bye\n\n"; + parser->addBytes(input, strlen(input)); + + const auto& events = client->events(); + + ASSERT_EQ(1u, events.size()); + ASSERT_EQ(EventOrReconnectionTimeSetting::Type::Event, events[0].type); + EXPECT_EQ("message", events[0].event); + EXPECT_EQ("hello", events[0].data); + EXPECT_EQ("99", parser->lastEventId()); +} + +} // namespace + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/page/EventSourceTest.cpp b/third_party/WebKit/Source/core/page/EventSourceTest.cpp index 93f437e..221a463 100644 --- a/third_party/WebKit/Source/core/page/EventSourceTest.cpp +++ b/third_party/WebKit/Source/core/page/EventSourceTest.cpp
@@ -18,6 +18,7 @@ #include "core/page/EventSourceInit.h" #include "core/testing/DummyPageHolder.h" #include "testing/gtest/include/gtest/gtest.h" +#include "wtf/text/CharacterNames.h" #include <string.h> #include <v8.h> @@ -69,6 +70,8 @@ { source()->setStateForTest(EventSource::OPEN); source()->setThreadableLoaderForTest(MockThreadableLoader::create()); + source()->setParser(new EventSourceParser("https://localhost/", source())); + source()->setRequestInFlightForTest(true); } ~EventSourceTest() override { @@ -423,6 +426,21 @@ EXPECT_EQ("8", messageEvent->lastEventId()); } +TEST_F(EventSourceTest, InvalidUTF8Sequence) +{ + RefPtrWillBeRawPtr<FakeEventListener> listener = FakeEventListener::create(); + + source()->addEventListener("message", listener); + enqueue("data:\xffhello\xc2\n\n"); + + ASSERT_EQ(1u, listener->events().size()); + ASSERT_EQ("message", listener->events()[0]->type()); + RefPtrWillBeRawPtr<MessageEvent> event = static_cast<MessageEvent*>(listener->events()[0].get()); + ASSERT_EQ(MessageEvent::DataTypeSerializedScriptValue, event->dataType()); + String expected = String() + replacementCharacter + "hello" + replacementCharacter; + EXPECT_EQ(expected, dataAsString(scriptState(), event.get())); +} + } // namespace } // namespace blink
diff --git a/third_party/WebKit/Source/core/page/FocusController.cpp b/third_party/WebKit/Source/core/page/FocusController.cpp index 9f9b583..00331f9 100644 --- a/third_party/WebKit/Source/core/page/FocusController.cpp +++ b/third_party/WebKit/Source/core/page/FocusController.cpp
@@ -74,7 +74,7 @@ public: Node* rootNode() const; Element* owner() const; - static FocusNavigationScope focusNavigationScopeOf(const Node&); + static FocusNavigationScope focusNavigationScopeOf(const Element&); static FocusNavigationScope ownedByNonFocusableFocusScopeOwner(Element&); static FocusNavigationScope ownedByShadowHost(const Element&); static FocusNavigationScope ownedByShadowInsertionPoint(HTMLShadowElement&); @@ -109,9 +109,9 @@ return nullptr; } -FocusNavigationScope FocusNavigationScope::focusNavigationScopeOf(const Node& node) +FocusNavigationScope FocusNavigationScope::focusNavigationScopeOf(const Element& element) { - return FocusNavigationScope(&node.treeScope()); + return FocusNavigationScope(&element.treeScope()); } FocusNavigationScope FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(Element& element) @@ -529,6 +529,16 @@ findFocusableElementAcrossFocusScopesBackward(scope, currentNode); } +inline Element* adjustToElement(Node* node, WebFocusType type) +{ + ASSERT(type == WebFocusTypeForward || type == WebFocusTypeBackward); + if (!node) + return nullptr; + if (node->isElementNode()) + return toElement(node); + return (type == WebFocusTypeForward) ? ElementTraversal::next(*node) : ElementTraversal::previous(*node); +} + } // anonymous namespace FocusController::FocusController(Page* page) @@ -714,33 +724,34 @@ // child frame has no more focusable elements, and we should continue // looking for focusable elements in the parent, starting from the <iframe> // element of the child frame. - Node* startingNode = nullptr; + Element* start = nullptr; if (from->tree().parent() == to) { ASSERT(from->owner()->isLocal()); - startingNode = toHTMLFrameOwnerElement(from->owner()); + start = toHTMLFrameOwnerElement(from->owner()); } - return advanceFocusInDocumentOrder(to, startingNode, type, false, sourceCapabilities); + return advanceFocusInDocumentOrder(to, start, type, false, sourceCapabilities); } -bool FocusController::advanceFocusInDocumentOrder(LocalFrame* frame, Node* startingNode, WebFocusType type, bool initialFocus, InputDeviceCapabilities* sourceCapabilities) +bool FocusController::advanceFocusInDocumentOrder(LocalFrame* frame, Element* start, WebFocusType type, bool initialFocus, InputDeviceCapabilities* sourceCapabilities) { ASSERT(frame); Document* document = frame->document(); + ASSERT(document->documentElement()); - Node* currentNode = startingNode; - if (!currentNode) - currentNode = document->focusedElement(); + Element* current = start; + if (!current) + current = document->focusedElement(); // FIXME: Not quite correct when it comes to focus transitions leaving/entering the WebView itself bool caretBrowsing = frame->settings() && frame->settings()->caretBrowsingEnabled(); - if (caretBrowsing && !currentNode) - currentNode = frame->selection().start().anchorNode(); + if (caretBrowsing && !current) + current = adjustToElement(frame->selection().start().anchorNode(), type); document->updateLayoutIgnorePendingStylesheets(); - RefPtrWillBeRawPtr<Element> element = findFocusableElementAcrossFocusScopes(type, FocusNavigationScope::focusNavigationScopeOf(currentNode ? *currentNode : *document), currentNode); + RefPtrWillBeRawPtr<Element> element = findFocusableElementAcrossFocusScopes(type, FocusNavigationScope::focusNavigationScopeOf(current ? *current : *document->documentElement()), current); if (!element) { // If there's a RemoteFrame on the ancestor chain, we need to continue @@ -760,7 +771,7 @@ } // Chrome doesn't want focus, so we should wrap focus. - element = findFocusableElementRecursively(type, FocusNavigationScope::focusNavigationScopeOf(*toLocalFrame(m_page->mainFrame())->document()), nullptr); + element = findFocusableElementRecursively(type, FocusNavigationScope::focusNavigationScopeOf(*toLocalFrame(m_page->mainFrame())->document()->documentElement()), nullptr); element = findFocusableElementDescendingDownIntoFrameDocument(type, element.get()); if (!element) @@ -816,12 +827,17 @@ return true; } -Element* FocusController::findFocusableElement(WebFocusType type, Node& node) +Element* FocusController::findFocusableElement(WebFocusType type, Element& element) { // FIXME: No spacial navigation code yet. ASSERT(type == WebFocusTypeForward || type == WebFocusTypeBackward); - Element* found = findFocusableElementAcrossFocusScopes(type, FocusNavigationScope::focusNavigationScopeOf(node), &node); - return found; + return findFocusableElementAcrossFocusScopes(type, FocusNavigationScope::focusNavigationScopeOf(element), &element); +} + +Element* FocusController::findFocusableElementInShadowHost(const Element& shadowHost) +{ + ASSERT(shadowHost.authorShadowRoot()); + return findFocusableElementAcrossFocusScopes(WebFocusTypeForward, FocusNavigationScope::ownedByShadowHost(shadowHost), nullptr); } static bool relinquishesEditingFocus(const Element& element)
diff --git a/third_party/WebKit/Source/core/page/FocusController.h b/third_party/WebKit/Source/core/page/FocusController.h index 02be6b3..1754787e 100644 --- a/third_party/WebKit/Source/core/page/FocusController.h +++ b/third_party/WebKit/Source/core/page/FocusController.h
@@ -70,7 +70,7 @@ bool setInitialFocus(WebFocusType); bool advanceFocus(WebFocusType type, InputDeviceCapabilities* sourceCapabilities = nullptr) { return advanceFocus(type, false, sourceCapabilities); } bool advanceFocusAcrossFrames(WebFocusType, RemoteFrame* from, LocalFrame* to, InputDeviceCapabilities* sourceCapabilities = nullptr); - Element* findFocusableElement(WebFocusType, Node&); + Element* findFocusableElementInShadowHost(const Element& shadowHost); bool setFocusedElement(Element*, PassRefPtrWillBeRawPtr<Frame>, const FocusParams&); // |setFocusedElement| variant with SelectionBehaviorOnFocus::None, @@ -88,9 +88,11 @@ private: explicit FocusController(Page*); + Element* findFocusableElement(WebFocusType, Element&); + bool advanceFocus(WebFocusType, bool initialFocus, InputDeviceCapabilities* sourceCapabilities = nullptr); bool advanceFocusDirectionally(WebFocusType); - bool advanceFocusInDocumentOrder(LocalFrame*, Node* startingNode, WebFocusType, bool initialFocus, InputDeviceCapabilities* sourceCapabilities); + bool advanceFocusInDocumentOrder(LocalFrame*, Element* start, WebFocusType, bool initialFocus, InputDeviceCapabilities* sourceCapabilities); bool advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, WebFocusType); void findFocusCandidateInContainer(Node& container, const LayoutRect& startingRect, WebFocusType, FocusCandidate& closest);
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp index 98307266..474628f9 100644 --- a/third_party/WebKit/Source/core/page/Page.cpp +++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -241,7 +241,7 @@ { for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) { if (frame->isLocalFrame()) - toLocalFrame(frame)->document()->styleResolverChanged(); + toLocalFrame(frame)->document()->styleEngine().resolverChanged(FullStyleUpdate); } }
diff --git a/third_party/WebKit/Source/core/paint/BlockPainter.cpp b/third_party/WebKit/Source/core/paint/BlockPainter.cpp index 9e0b7da..624d9f10f 100644 --- a/third_party/WebKit/Source/core/paint/BlockPainter.cpp +++ b/third_party/WebKit/Source/core/paint/BlockPainter.cpp
@@ -241,8 +241,8 @@ void BlockPainter::paintContents(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC. - // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverChanged() on the Document - // will do a full paint invalidation. + // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverMayHaveChanged() + // on Document will trigger a full paint invalidation. if (m_layoutBlock.document().didLayoutWithPendingStylesheets() && !m_layoutBlock.isLayoutView()) return;
diff --git a/third_party/WebKit/Source/core/paint/BoxDecorationData.cpp b/third_party/WebKit/Source/core/paint/BoxDecorationData.cpp index ef35fb1..dafa508 100644 --- a/third_party/WebKit/Source/core/paint/BoxDecorationData.cpp +++ b/third_party/WebKit/Source/core/paint/BoxDecorationData.cpp
@@ -5,6 +5,7 @@ #include "core/paint/BoxDecorationData.h" #include "core/layout/LayoutBox.h" +#include "core/paint/BoxPainter.h" #include "core/style/BorderEdge.h" #include "core/style/ComputedStyle.h" #include "platform/RuntimeEnabledFeatures.h" @@ -47,13 +48,25 @@ if (!hasBackground) return BackgroundBleedNone; - if (!hasBorderDecoration || !layoutBox.style()->hasBorderRadius() || layoutBox.canRenderBorderImage()) { + const ComputedStyle& boxStyle = layoutBox.styleRef(); + const bool hasBorderRadius = boxStyle.hasBorderRadius(); + if (!hasBorderDecoration || !hasBorderRadius || layoutBox.canRenderBorderImage()) { if (layoutBox.backgroundShouldAlwaysBeClipped()) return BackgroundBleedClipOnly; + // Border radius clipping may require layer bleed avoidance if we are going to draw + // an image over something else, because we do not want the antialiasing to lead to bleeding + if (boxStyle.hasBackgroundImage() && hasBorderRadius) { + // But if the top layer is opaque for the purposes of background painting, we do not + // need the bleed avoidance because we will not paint anything behind the top layer. + // But only if we need to draw something underneath. + const FillLayer& fillLayer = layoutBox.style()->backgroundLayers(); + if ((backgroundColor.alpha() || fillLayer.next()) && !BoxPainter::isFillLayerOpaque(fillLayer, layoutBox)) + return BackgroundBleedClipLayer; + } return BackgroundBleedNone; } - if (borderObscuresBackgroundEdge(*layoutBox.style())) + if (borderObscuresBackgroundEdge(boxStyle)) return BackgroundBleedShrinkBackground; return BackgroundBleedClipLayer;
diff --git a/third_party/WebKit/Source/core/paint/BoxPainter.cpp b/third_party/WebKit/Source/core/paint/BoxPainter.cpp index 8e4236f..3d6d0f9 100644 --- a/third_party/WebKit/Source/core/paint/BoxPainter.cpp +++ b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
@@ -92,7 +92,6 @@ GraphicsContextStateSaver stateSaver(paintInfo.context, false); if (bleedAvoidanceIsClipping(boxDecorationData.bleedAvoidance)) { - stateSaver.save(); FloatRoundedRect border = style.getRoundedBorderFor(paintRect); paintInfo.context.clipRoundedRect(border); @@ -135,7 +134,7 @@ paintFillLayers(paintInfo, backgroundColor, m_layoutBox.style()->backgroundLayers(), paintRect, bleedAvoidance); } -static bool isFillLayerOpaque(const FillLayer& layer, const LayoutObject& imageClient) +bool BoxPainter::isFillLayerOpaque(const FillLayer& layer, const LayoutObject& imageClient) { return layer.hasOpaqueImage(&imageClient) && layer.image()->canRender()
diff --git a/third_party/WebKit/Source/core/paint/BoxPainter.h b/third_party/WebKit/Source/core/paint/BoxPainter.h index b2d3484..93247a2 100644 --- a/third_party/WebKit/Source/core/paint/BoxPainter.h +++ b/third_party/WebKit/Source/core/paint/BoxPainter.h
@@ -34,6 +34,9 @@ // Note that the output list will be in top-bottom order. bool calculateFillLayerOcclusionCulling(FillLayerOcclusionOutputList &reversedPaintList, const FillLayer&); + // Returns true if the fill layer will certainly occlude anything painted behind it. + static bool isFillLayerOpaque(const FillLayer&, const LayoutObject&); + void paintFillLayers(const PaintInfo&, const Color&, const FillLayer&, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone, SkXfermode::Mode = SkXfermode::kSrcOver_Mode, const LayoutObject* backgroundObject = nullptr); void paintMaskImages(const PaintInfo&, const LayoutRect&); void paintBoxDecorationBackgroundWithRect(const PaintInfo&, const LayoutPoint&, const LayoutRect&);
diff --git a/third_party/WebKit/Source/core/paint/MultiColumnSetPainter.cpp b/third_party/WebKit/Source/core/paint/MultiColumnSetPainter.cpp index e67d81b..8c21945 100644 --- a/third_party/WebKit/Source/core/paint/MultiColumnSetPainter.cpp +++ b/third_party/WebKit/Source/core/paint/MultiColumnSetPainter.cpp
@@ -37,7 +37,7 @@ return; const ComputedStyle& blockStyle = m_layoutMultiColumnSet.multiColumnBlockFlow()->styleRef(); - const Color& ruleColor = m_layoutMultiColumnSet.resolveColor(blockStyle, CSSPropertyWebkitColumnRuleColor); + const Color& ruleColor = m_layoutMultiColumnSet.resolveColor(blockStyle, CSSPropertyColumnRuleColor); bool ruleTransparent = blockStyle.columnRuleIsTransparent(); EBorderStyle ruleStyle = blockStyle.columnRuleStyle(); LayoutUnit ruleThickness(blockStyle.columnRuleWidth());
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp index b5b1f61f..439349d 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp +++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -204,9 +204,6 @@ clearCompositedLayerMapping(true); - if (PaintLayerReflectionInfo* reflectionInfo = this->reflectionInfo()) - reflectionInfo->destroy(); - if (m_scrollableArea) m_scrollableArea->dispose(); } @@ -1427,7 +1424,6 @@ m_rareData->reflectionInfo = adoptPtr(new PaintLayerReflectionInfo(*layoutBox())); m_rareData->reflectionInfo->updateAfterStyleChange(oldStyle); } else if (m_rareData && m_rareData->reflectionInfo) { - m_rareData->reflectionInfo->destroy(); m_rareData->reflectionInfo = nullptr; } }
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerReflectionInfo.cpp b/third_party/WebKit/Source/core/paint/PaintLayerReflectionInfo.cpp index db2a9b8..7859231c 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerReflectionInfo.cpp +++ b/third_party/WebKit/Source/core/paint/PaintLayerReflectionInfo.cpp
@@ -65,7 +65,7 @@ m_reflection->setDangerousOneWayParent(m_box); } -void PaintLayerReflectionInfo::destroy() +PaintLayerReflectionInfo::~PaintLayerReflectionInfo() { if (!m_reflection->documentBeingDestroyed()) m_reflection->removeLayers(box().layer());
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerReflectionInfo.h b/third_party/WebKit/Source/core/paint/PaintLayerReflectionInfo.h index 7aabe5c7..b68733d4 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerReflectionInfo.h +++ b/third_party/WebKit/Source/core/paint/PaintLayerReflectionInfo.h
@@ -72,7 +72,7 @@ WTF_MAKE_NONCOPYABLE(PaintLayerReflectionInfo); public: explicit PaintLayerReflectionInfo(LayoutBox&); - void destroy(); + ~PaintLayerReflectionInfo(); LayoutReplica* reflection() const { return m_reflection; } PaintLayer* reflectionLayer() const;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp index 5d54131..feba660 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp +++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -90,6 +90,8 @@ , m_nextTopmostScrollChild(0) , m_topmostScrollChild(0) , m_needsCompositedScrolling(false) + , m_rebuildHorizontalScrollbarLayer(false) + , m_rebuildVerticalScrollbarLayer(false) , m_scrollbarManager(*this) , m_scrollCorner(nullptr) , m_resizer(nullptr) @@ -155,7 +157,10 @@ clearScrollAnimators(); - if (RuntimeEnabledFeatures::scrollAnchoringEnabled()) + // Note: it is not safe to call ScrollAnchor::clear if the document is being destroyed, + // because LayoutObjectChildList::removeChildNode skips the call to willBeRemovedFromTree, + // leaving the ScrollAnchor with a stale LayoutObject pointer. + if (RuntimeEnabledFeatures::scrollAnchoringEnabled() && !box().documentBeingDestroyed()) m_scrollAnchor.clear(); #if ENABLE(ASSERT) @@ -1075,6 +1080,7 @@ m_scrollCorner = LayoutScrollbarPart::createAnonymous(&box().document()); m_scrollCorner->setDangerousOneWayParent(&box()); } + m_scrollCorner->adjustStyleBeforeSet(corner.get()); m_scrollCorner->setStyle(corner.release()); } else if (m_scrollCorner) { m_scrollCorner->destroy(); @@ -1203,6 +1209,7 @@ m_resizer = LayoutScrollbarPart::createAnonymous(&box().document()); m_resizer->setDangerousOneWayParent(&box()); } + m_resizer->adjustStyleBeforeSet(resizer.get()); m_resizer->setStyle(resizer.release()); } else if (m_resizer) { m_resizer->destroy(); @@ -1436,6 +1443,12 @@ return box().frame()->view(); } +void PaintLayerScrollableArea::resetRebuildScrollbarLayerFlags() +{ + m_rebuildHorizontalScrollbarLayer = false; + m_rebuildVerticalScrollbarLayer = false; +} + PaintLayerScrollableArea::ScrollbarManager::ScrollbarManager(PaintLayerScrollableArea& scrollableArea) : m_scrollableArea(&scrollableArea) , m_canDetachScrollbars(0) @@ -1524,6 +1537,10 @@ return; m_scrollableArea->setScrollbarNeedsPaintInvalidation(orientation); + if (orientation == HorizontalScrollbar) + m_scrollableArea->m_rebuildHorizontalScrollbarLayer = true; + else + m_scrollableArea->m_rebuildVerticalScrollbarLayer = true; if (!scrollbar->isCustomScrollbar()) m_scrollableArea->willRemoveScrollbar(*scrollbar, orientation);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h index 9f7419c..3a8fb51 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h +++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
@@ -334,6 +334,10 @@ ScrollAnchor& scrollAnchor() { return m_scrollAnchor; } bool isPaintLayerScrollableArea() const override { return true; } + bool shouldRebuildHorizontalScrollbarLayer() const { return m_rebuildHorizontalScrollbarLayer; } + bool shouldRebuildVerticalScrollbarLayer() const { return m_rebuildVerticalScrollbarLayer; } + void resetRebuildScrollbarLayerFlags(); + DECLARE_VIRTUAL_TRACE(); private: @@ -388,6 +392,12 @@ // no longer need this bit. unsigned m_needsCompositedScrolling : 1; + // Set to indicate that a scrollbar layer, if present, needs to be rebuilt + // in the next compositing update because the underlying blink::Scrollbar + // instance has been reconstructed. + unsigned m_rebuildHorizontalScrollbarLayer : 1; + unsigned m_rebuildVerticalScrollbarLayer : 1; + // The width/height of our scrolled area. // This is OverflowModel's layout overflow translated to physical // coordinates. See OverflowModel for the different overflow and
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp index 7bc15c0..2b68b35 100644 --- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp +++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -67,7 +67,7 @@ } inherited_flags; struct NonInheritedFlags { - unsigned m_bitfields[2]; + unsigned m_bitfields[3]; } noninherited_flags; }; @@ -109,7 +109,7 @@ { setBitDefaults(); // Would it be faster to copy this from the default style? static_assert((sizeof(InheritedFlags) <= 8), "InheritedFlags should not grow"); - static_assert((sizeof(NonInheritedFlags) <= 8), "NonInheritedFlags should not grow"); + static_assert((sizeof(NonInheritedFlags) <= 12), "NonInheritedFlags should not grow"); } ALWAYS_INLINE ComputedStyle::ComputedStyle(InitialStyleTag) @@ -290,9 +290,9 @@ noninherited_flags.tableLayout = other.noninherited_flags.tableLayout; noninherited_flags.unicodeBidi = other.noninherited_flags.unicodeBidi; noninherited_flags.hasViewportUnits = other.noninherited_flags.hasViewportUnits; - noninherited_flags.pageBreakBefore = other.noninherited_flags.pageBreakBefore; - noninherited_flags.pageBreakAfter = other.noninherited_flags.pageBreakAfter; - noninherited_flags.pageBreakInside = other.noninherited_flags.pageBreakInside; + noninherited_flags.breakBefore = other.noninherited_flags.breakBefore; + noninherited_flags.breakAfter = other.noninherited_flags.breakAfter; + noninherited_flags.breakInside = other.noninherited_flags.breakInside; noninherited_flags.hasRemUnits = other.noninherited_flags.hasRemUnits; // Correctly set during selector matching: @@ -1534,7 +1534,7 @@ case CSSPropertyOutlineColor: result = visitedLink ? visitedLinkOutlineColor() : outlineColor(); break; - case CSSPropertyWebkitColumnRuleColor: + case CSSPropertyColumnRuleColor: result = visitedLink ? visitedLinkColumnRuleColor() : columnRuleColor(); break; case CSSPropertyWebkitTextEmphasisColor: @@ -1738,7 +1738,7 @@ { return columnRuleStyle() == otherStyle->columnRuleStyle() && columnRuleWidth() == otherStyle->columnRuleWidth() - && visitedDependentColor(CSSPropertyWebkitColumnRuleColor) == otherStyle->visitedDependentColor(CSSPropertyWebkitColumnRuleColor); + && visitedDependentColor(CSSPropertyColumnRuleColor) == otherStyle->visitedDependentColor(CSSPropertyColumnRuleColor); } TextEmphasisMark ComputedStyle::textEmphasisMark() const
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h index 01814ea8..b666a15f 100644 --- a/third_party/WebKit/Source/core/style/ComputedStyle.h +++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -237,9 +237,9 @@ && tableLayout == other.tableLayout && unicodeBidi == other.unicodeBidi // hasViewportUnits - && pageBreakBefore == other.pageBreakBefore - && pageBreakAfter == other.pageBreakAfter - && pageBreakInside == other.pageBreakInside; + && breakBefore == other.breakBefore + && breakAfter == other.breakAfter + && breakInside == other.breakInside; // styleType // pseudoBits // explicitInheritance @@ -271,9 +271,9 @@ // 32 bits - unsigned pageBreakBefore : 2; // EPageBreak - unsigned pageBreakAfter : 2; // EPageBreak - unsigned pageBreakInside : 2; // EPageBreak + unsigned breakBefore : 4; // EBreak + unsigned breakAfter : 4; // EBreak + unsigned breakInside : 2; // EBreak unsigned styleType : 6; // PseudoId unsigned pseudoBits : 8; @@ -288,11 +288,13 @@ unsigned affectedByActive : 1; unsigned affectedByDrag : 1; + // 64 bits + unsigned isLink : 1; mutable unsigned hasRemUnits : 1; // If you add more style bits here, you will also need to update ComputedStyle::copyNonInheritedFromCached() - // 62 bits + // 66 bits } noninherited_flags; // !END SYNC! @@ -328,9 +330,9 @@ noninherited_flags.floating = initialFloating(); noninherited_flags.tableLayout = initialTableLayout(); noninherited_flags.unicodeBidi = initialUnicodeBidi(); - noninherited_flags.pageBreakBefore = initialPageBreak(); - noninherited_flags.pageBreakAfter = initialPageBreak(); - noninherited_flags.pageBreakInside = initialPageBreak(); + noninherited_flags.breakBefore = initialBreakBefore(); + noninherited_flags.breakAfter = initialBreakAfter(); + noninherited_flags.breakInside = initialBreakInside(); noninherited_flags.styleType = NOPSEUDO; noninherited_flags.pseudoBits = 0; noninherited_flags.explicitInheritance = false; @@ -750,9 +752,9 @@ short orphans() const { return rareInheritedData->orphans; } bool hasAutoWidows() const { return rareInheritedData->widows == 1; } bool hasAutoOrphans() const { return rareInheritedData->m_hasAutoOrphans; } - EPageBreak pageBreakInside() const { return static_cast<EPageBreak>(noninherited_flags.pageBreakInside); } - EPageBreak pageBreakBefore() const { return static_cast<EPageBreak>(noninherited_flags.pageBreakBefore); } - EPageBreak pageBreakAfter() const { return static_cast<EPageBreak>(noninherited_flags.pageBreakAfter); } + EBreak breakAfter() const { return static_cast<EBreak>(noninherited_flags.breakAfter); } + EBreak breakBefore() const { return static_cast<EBreak>(noninherited_flags.breakBefore); } + EBreak breakInside() const { return static_cast<EBreak>(noninherited_flags.breakInside); } // CSS3 Getter Methods @@ -883,9 +885,6 @@ bool columnRuleIsTransparent() const { return rareNonInheritedData->m_multiCol->m_rule.isTransparent(); } bool columnRuleEquivalent(const ComputedStyle* otherStyle) const; ColumnSpan columnSpan() const { return static_cast<ColumnSpan>(rareNonInheritedData->m_multiCol->m_columnSpan); } - EPageBreak columnBreakBefore() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakBefore); } - EPageBreak columnBreakInside() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakInside); } - EPageBreak columnBreakAfter() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakAfter); } bool hasInlineTransform() const { return rareNonInheritedData->m_hasInlineTransform; } bool hasCompositorProxy() const { return rareNonInheritedData->m_hasCompositorProxy; } const TransformOperations& transform() const { return rareNonInheritedData->m_transform->m_operations; } @@ -1306,10 +1305,9 @@ void setHasAutoOrphans() { SET_VAR(rareInheritedData, m_hasAutoOrphans, true); SET_VAR(rareInheritedData, orphans, initialOrphans()); } void setOrphans(short o) { SET_VAR(rareInheritedData, m_hasAutoOrphans, false); SET_VAR(rareInheritedData, orphans, o); } - // For valid values of page-break-inside see http://www.w3.org/TR/CSS21/page.html#page-break-props - void setPageBreakInside(EPageBreak b) { ASSERT(b == PBAUTO || b == PBAVOID); noninherited_flags.pageBreakInside = b; } - void setPageBreakBefore(EPageBreak b) { noninherited_flags.pageBreakBefore = b; } - void setPageBreakAfter(EPageBreak b) { noninherited_flags.pageBreakAfter = b; } + void setBreakAfter(EBreak b) { ASSERT(b <= BreakValueLastAllowedForBreakAfterAndBefore); noninherited_flags.breakAfter = b; } + void setBreakBefore(EBreak b) { ASSERT(b <= BreakValueLastAllowedForBreakAfterAndBefore); noninherited_flags.breakBefore = b; } + void setBreakInside(EBreak b) { ASSERT(b <= BreakValueLastAllowedForBreakInside); noninherited_flags.breakInside = b; } // CSS3 Setters void setOutlineOffset(int v) { SET_VAR(m_background, m_outline.m_offset, v); } @@ -1405,10 +1403,6 @@ void setColumnRuleStyle(EBorderStyle b) { SET_NESTED_VAR(rareNonInheritedData, m_multiCol, m_rule.m_style, b); } void setColumnRuleWidth(unsigned short w) { SET_NESTED_VAR(rareNonInheritedData, m_multiCol, m_rule.m_width, w); } void setColumnSpan(ColumnSpan columnSpan) { SET_NESTED_VAR(rareNonInheritedData, m_multiCol, m_columnSpan, columnSpan); } - void setColumnBreakBefore(EPageBreak p) { SET_NESTED_VAR(rareNonInheritedData, m_multiCol, m_breakBefore, p); } - // For valid values of column-break-inside see http://www.w3.org/TR/css3-multicol/#break-before-break-after-break-inside - void setColumnBreakInside(EPageBreak p) { ASSERT(p == PBAUTO || p == PBAVOID); SET_NESTED_VAR(rareNonInheritedData, m_multiCol, m_breakInside, p); } - void setColumnBreakAfter(EPageBreak p) { SET_NESTED_VAR(rareNonInheritedData, m_multiCol, m_breakAfter, p); } void inheritColumnPropertiesFrom(const ComputedStyle& parent) { rareNonInheritedData.access()->m_multiCol = parent.rareNonInheritedData->m_multiCol; } void setHasInlineTransform(bool b) { SET_VAR(rareNonInheritedData, m_hasInlineTransform, b); } void setHasCompositorProxy(bool b) { SET_VAR(rareNonInheritedData, m_hasCompositorProxy, b); } @@ -1671,7 +1665,9 @@ static EListStyleType initialListStyleType() { return Disc; } static EOverflow initialOverflowX() { return OVISIBLE; } static EOverflow initialOverflowY() { return OVISIBLE; } - static EPageBreak initialPageBreak() { return PBAUTO; } + static EBreak initialBreakAfter() { return BreakAuto; } + static EBreak initialBreakBefore() { return BreakAuto; } + static EBreak initialBreakInside() { return BreakAuto; } static EPosition initialPosition() { return StaticPosition; } static ETableLayout initialTableLayout() { return TAUTO; } static EUnicodeBidi initialUnicodeBidi() { return UBNormal; }
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h index 7755393d..8c110c0 100644 --- a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h +++ b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
@@ -321,8 +321,22 @@ TextUnderlinePositionUnder }; -enum EPageBreak { - PBAUTO, PBALWAYS, PBAVOID +enum EBreak { + BreakAuto, + BreakAvoid, + BreakAvoidColumn, + BreakAvoidPage, + // Values below are only allowed for break-after and break-before. Values above are also + // allowed for break-inside (in addition to break-after and break-before). + BreakValueLastAllowedForBreakInside = BreakAvoidPage, + BreakColumn, + BreakLeft, + BreakPage, + BreakRecto, + BreakRight, + BreakVerso, + BreakValueLastAllowedForBreakAfterAndBefore = BreakVerso, + BreakAlways // Only needed by {page,-webkit-column}-break-{after,before} shorthands. }; enum EEmptyCell {
diff --git a/third_party/WebKit/Source/core/style/StyleMultiColData.cpp b/third_party/WebKit/Source/core/style/StyleMultiColData.cpp index ed854927..275b3c7 100644 --- a/third_party/WebKit/Source/core/style/StyleMultiColData.cpp +++ b/third_party/WebKit/Source/core/style/StyleMultiColData.cpp
@@ -35,9 +35,6 @@ , m_normalGap(true) , m_fill(ComputedStyle::initialColumnFill()) , m_columnSpan(false) - , m_breakBefore(ComputedStyle::initialPageBreak()) - , m_breakAfter(ComputedStyle::initialPageBreak()) - , m_breakInside(ComputedStyle::initialPageBreak()) { } @@ -53,19 +50,15 @@ , m_normalGap(o.m_normalGap) , m_fill(o.m_fill) , m_columnSpan(o.m_columnSpan) - , m_breakBefore(o.m_breakBefore) - , m_breakAfter(o.m_breakAfter) - , m_breakInside(o.m_breakInside) { } bool StyleMultiColData::operator==(const StyleMultiColData& o) const { return m_width == o.m_width && m_count == o.m_count && m_gap == o.m_gap - && m_rule == o.m_rule && m_visitedLinkColumnRuleColor == o.m_visitedLinkColumnRuleColor && m_breakBefore == o.m_breakBefore + && m_rule == o.m_rule && m_visitedLinkColumnRuleColor == o.m_visitedLinkColumnRuleColor && m_autoWidth == o.m_autoWidth && m_autoCount == o.m_autoCount && m_normalGap == o.m_normalGap - && m_fill == o.m_fill && m_columnSpan == o.m_columnSpan - && m_breakAfter == o.m_breakAfter && m_breakInside == o.m_breakInside; + && m_fill == o.m_fill && m_columnSpan == o.m_columnSpan; } } // namespace blink
diff --git a/third_party/WebKit/Source/core/style/StyleMultiColData.h b/third_party/WebKit/Source/core/style/StyleMultiColData.h index 300842a..3e8d13b 100644 --- a/third_party/WebKit/Source/core/style/StyleMultiColData.h +++ b/third_party/WebKit/Source/core/style/StyleMultiColData.h
@@ -64,9 +64,6 @@ unsigned m_normalGap : 1; unsigned m_fill : 1; // ColumnFill unsigned m_columnSpan : 1; - unsigned m_breakBefore : 2; // EPageBreak - unsigned m_breakAfter : 2; // EPageBreak - unsigned m_breakInside : 2; // EPageBreak private: StyleMultiColData();
diff --git a/third_party/WebKit/Source/core/testing/TypeConversions.idl b/third_party/WebKit/Source/core/testing/TypeConversions.idl index fbe1673d..a937e5e 100644 --- a/third_party/WebKit/Source/core/testing/TypeConversions.idl +++ b/third_party/WebKit/Source/core/testing/TypeConversions.idl
@@ -47,18 +47,10 @@ [EnforceRange, ImplementedAs=testUnsignedShort] attribute unsigned short testEnforceRangeUnsignedShort; attribute ByteString testByteString; - [ImplementedAs=testByteString, TreatReturnedNullStringAs=Null] attribute ByteString testByteStringTreatReturnedNullStringAsNullAttribute; - [ImplementedAs=testByteString, TreatReturnedNullStringAs=Undefined] attribute ByteString testByteStringTreatReturnedNullStringAsUndefinedAttribute; - [ImplementedAs=testByteString] ByteString? getTestByteStringTreatReturnedNullStringAsNullMethod(); - [ImplementedAs=testByteString, TreatReturnedNullStringAs=Undefined] ByteString getTestByteStringTreatReturnedNullStringAsUndefinedMethod(); void setTestByteString(ByteString byteString); [ImplementedAs=setTestByteString] void setTestByteStringDefaultNull(optional ByteString byteString = null); attribute USVString testUSVString; - [ImplementedAs=testUSVString, TreatReturnedNullStringAs=Null] attribute USVString testUSVStringTreatReturnedNullStringAsNullAttribute; - [ImplementedAs=testUSVString, TreatReturnedNullStringAs=Undefined] attribute USVString testUSVStringTreatReturnedNullStringAsUndefinedAttribute; - [ImplementedAs=testUSVString] USVString? getTestUSVStringTreatReturnedNullStringAsNullMethod(); - [ImplementedAs=testUSVString, TreatReturnedNullStringAs=Undefined] USVString getTestUSVStringTreatReturnedNullStringAsUndefinedMethod(); void setTestUSVString(USVString usvString); [ImplementedAs=setTestUSVString] void setTestUSVStringDefaultNull(optional USVString usvString = null); };
diff --git a/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp b/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp index 84c25a9..2d87cd5 100644 --- a/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp +++ b/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp
@@ -39,6 +39,7 @@ #include "core/dom/DocumentType.h" #include "core/dom/ProcessingInstruction.h" #include "core/dom/ScriptLoader.h" +#include "core/dom/StyleEngine.h" #include "core/dom/TransformSource.h" #include "core/fetch/FetchInitiatorTypeNames.h" #include "core/fetch/RawResource.h" @@ -448,7 +449,7 @@ updateLeafTextNode(); // Do not bail out if in a stopped state, but notify document that // parsing has finished. - document()->styleResolverChanged(); + document()->styleEngine().resolverChanged(FullStyleUpdate); } if (isParsing()) @@ -1541,9 +1542,9 @@ document()->setTransformSource(adoptPtr(new TransformSource(doc))); // Make the document think it's done, so it will apply XSL stylesheets. document()->setParsingState(Document::FinishedParsing); - document()->styleResolverChanged(); + document()->styleEngine().resolverChanged(FullStyleUpdate); - // styleResolverChanged() call can detach the parser and null out its + // resolverChanged() call can detach the parser and null out its // document. In that case, we just bail out. if (isDetached()) return;
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn index a70c74e..5c7c122 100644 --- a/third_party/WebKit/Source/devtools/BUILD.gn +++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -43,6 +43,7 @@ gypi_values.devtools_profiler_js_files + gypi_values.devtools_promises_js_files + gypi_values.devtools_resources_js_files + + gypi_values.devtools_sass_js_files + gypi_values.devtools_security_js_files + gypi_values.devtools_screencast_js_files + gypi_values.devtools_script_formatter_worker_js_files + @@ -150,6 +151,7 @@ resources_out_dir + "profiler_module.js", resources_out_dir + "promises_module.js", resources_out_dir + "resources_module.js", + resources_out_dir + "sass_module.js", resources_out_dir + "security_module.js", resources_out_dir + "script_formatter_worker_module.js", resources_out_dir + "settings_module.js", @@ -281,6 +283,7 @@ resources_out_dir + "profiler_module.js", resources_out_dir + "promises_module.js", resources_out_dir + "resources_module.js", + resources_out_dir + "sass_module.js", resources_out_dir + "security_module.js", resources_out_dir + "screencast_module.js", resources_out_dir + "script_formatter_worker_module.js",
diff --git a/third_party/WebKit/Source/devtools/devtools.gyp b/third_party/WebKit/Source/devtools/devtools.gyp index fa1a131..33505c0 100644 --- a/third_party/WebKit/Source/devtools/devtools.gyp +++ b/third_party/WebKit/Source/devtools/devtools.gyp
@@ -116,6 +116,7 @@ '<(PRODUCT_DIR)/resources/inspector/profiler_module.js', '<(PRODUCT_DIR)/resources/inspector/promises_module.js', '<(PRODUCT_DIR)/resources/inspector/resources_module.js', + '<(PRODUCT_DIR)/resources/inspector/sass_module.js', '<(PRODUCT_DIR)/resources/inspector/security_module.js', '<(PRODUCT_DIR)/resources/inspector/script_formatter_worker_module.js', '<(PRODUCT_DIR)/resources/inspector/settings_module.js', @@ -282,6 +283,7 @@ '<(_output_path)/profiler_module.js', '<(_output_path)/promises_module.js', '<(_output_path)/resources_module.js', + '<(_output_path)/sass_module.js', '<(_output_path)/security_module.js', '<(_output_path)/screencast_module.js', '<(_output_path)/script_formatter_worker_module.js',
diff --git a/third_party/WebKit/Source/devtools/devtools.gypi b/third_party/WebKit/Source/devtools/devtools.gypi index 7b940d2..b2b2199 100644 --- a/third_party/WebKit/Source/devtools/devtools.gypi +++ b/third_party/WebKit/Source/devtools/devtools.gypi
@@ -43,7 +43,6 @@ '<@(devtools_host_js_files)', '<@(devtools_main_js_files)', '<@(devtools_platform_js_files)', - '<@(devtools_sass_js_files)', '<@(devtools_sdk_js_files)', '<@(devtools_toolbox_bootstrap_js_files)', '<@(devtools_ui_js_files)', @@ -118,8 +117,8 @@ 'front_end/host/UserMetrics.js', ], 'devtools_sass_js_files': [ - 'front_end/sass/SASSLiveSourceMap.js', - 'front_end/sass/SASSWorkspaceAdapter.js', + 'front_end/sass/ASTService.js', + 'front_end/sass/ASTSourceMap.js', 'front_end/sass/SASSSupport.js', ], 'devtools_screencast_js_files': [ @@ -755,6 +754,7 @@ '<@(devtools_profiler_js_files)', '<@(devtools_promises_js_files)', '<@(devtools_resources_js_files)', + '<@(devtools_sass_js_files)', '<@(devtools_security_js_files)', '<@(devtools_screencast_js_files)', '<@(devtools_script_formatter_worker_js_files)',
diff --git a/third_party/WebKit/Source/devtools/front_end/animation/AnimationModel.js b/third_party/WebKit/Source/devtools/front_end/animation/AnimationModel.js index f79c518..ca45d51 100644 --- a/third_party/WebKit/Source/devtools/front_end/animation/AnimationModel.js +++ b/third_party/WebKit/Source/devtools/front_end/animation/AnimationModel.js
@@ -264,7 +264,7 @@ */ name: function() { - return this.source().name(); + return this._payload.name; }, /** @@ -514,14 +514,6 @@ }, /** - * @return {string} - */ - name: function() - { - return this._payload.name; - }, - - /** * @return {!Promise.<!WebInspector.DOMNode>} */ node: function()
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/BlackboxManager.js b/third_party/WebKit/Source/devtools/front_end/bindings/BlackboxManager.js index 87519392..f542c592 100644 --- a/third_party/WebKit/Source/devtools/front_end/bindings/BlackboxManager.js +++ b/third_party/WebKit/Source/devtools/front_end/bindings/BlackboxManager.js
@@ -24,7 +24,6 @@ } WebInspector.BlackboxManager.prototype = { - /** * @param {function(!WebInspector.Event)} listener * @param {!Object=} thisObject @@ -221,9 +220,8 @@ var promises = []; for (var debuggerModel of WebInspector.DebuggerModel.instances()) { for (var scriptId in debuggerModel.scripts) { - let script = debuggerModel.scripts[scriptId]; + var script = debuggerModel.scripts[scriptId]; promises.push(this._addScript(script) - .then(() => this._debuggerWorkspaceBinding.sourceMapForScript(script)) .then(loadSourceMap.bind(this, script))); } } @@ -231,13 +229,12 @@ /** * @param {!WebInspector.Script} script - * @param {?WebInspector.SourceMap} sourceMap * @return {!Promise<undefined>} * @this {WebInspector.BlackboxManager} */ - function loadSourceMap(script, sourceMap) + function loadSourceMap(script) { - return this.sourceMapLoaded(script, sourceMap); + return this.sourceMapLoaded(script, this._debuggerWorkspaceBinding.sourceMapForScript(script)); } }, @@ -310,10 +307,12 @@ */ function updateState(success) { - if (success) + if (success) { this._scriptIdToPositions.set(script.scriptId, positions); - else if (!this._scriptIdToPositions.has(script.scriptId)) + this._debuggerWorkspaceBinding.updateLocations(script); + } else if (!this._scriptIdToPositions.has(script.scriptId)) { this._scriptIdToPositions.set(script.scriptId, []); + } } },
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/BreakpointManager.js b/third_party/WebKit/Source/devtools/front_end/bindings/BreakpointManager.js index 4e665e0..53446c5b 100644 --- a/third_party/WebKit/Source/devtools/front_end/bindings/BreakpointManager.js +++ b/third_party/WebKit/Source/devtools/front_end/bindings/BreakpointManager.js
@@ -919,10 +919,13 @@ /** * @param {!WebInspector.DebuggerModel.Location} location - * @param {!WebInspector.UILocation} uiLocation + * @param {!WebInspector.LiveLocation} liveLocation */ - _locationUpdated: function(location, uiLocation) + _locationUpdated: function(location, liveLocation) { + var uiLocation = liveLocation.uiLocation(); + if (!uiLocation) + return; var oldUILocation = this._uiLocations[location.id()] || null; this._uiLocations[location.id()] = uiLocation; this._breakpoint._replaceUILocation(oldUILocation, uiLocation);
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/CSSWorkspaceBinding.js b/third_party/WebKit/Source/devtools/front_end/bindings/CSSWorkspaceBinding.js index 1502aa0..62835f6 100644 --- a/third_party/WebKit/Source/devtools/front_end/bindings/CSSWorkspaceBinding.js +++ b/third_party/WebKit/Source/devtools/front_end/bindings/CSSWorkspaceBinding.js
@@ -100,7 +100,7 @@ /** * @param {!WebInspector.CSSLocation} rawLocation - * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate + * @param {function(!WebInspector.LiveLocation)} updateDelegate * @return {!WebInspector.CSSWorkspaceBinding.LiveLocation} */ createLiveLocation: function(rawLocation, updateDelegate) @@ -314,7 +314,7 @@ * @param {?WebInspector.CSSStyleSheetHeader} header * @param {!WebInspector.CSSLocation} rawLocation * @param {!WebInspector.CSSWorkspaceBinding} binding - * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate + * @param {function(!WebInspector.LiveLocation)} updateDelegate */ WebInspector.CSSWorkspaceBinding.LiveLocation = function(cssModel, header, rawLocation, binding, updateDelegate) { @@ -397,6 +397,15 @@ this._cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this); }, + /** + * @override + * @return {boolean} + */ + isBlackboxed: function() + { + return false; + }, + __proto__: WebInspector.LiveLocation.prototype }
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js b/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js index 0e90c6f..6d22ee7 100644 --- a/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js +++ b/third_party/WebKit/Source/devtools/front_end/bindings/DebuggerWorkspaceBinding.js
@@ -121,7 +121,7 @@ /** * @param {!WebInspector.DebuggerModel.Location} rawLocation - * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate + * @param {function(!WebInspector.LiveLocation)} updateDelegate * @return {!WebInspector.DebuggerWorkspaceBinding.Location} */ createLiveLocation: function(rawLocation, updateDelegate) @@ -135,7 +135,7 @@ /** * @param {!WebInspector.DebuggerModel.CallFrame} callFrame - * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate + * @param {function(!WebInspector.LiveLocation)} updateDelegate * @return {!WebInspector.DebuggerWorkspaceBinding.Location} */ createCallFrameLiveLocation: function(callFrame, updateDelegate) @@ -469,7 +469,7 @@ }, /** - * @param {!WebInspector.LiveLocation} location + * @param {!WebInspector.DebuggerWorkspaceBinding.Location} location */ _addLocation: function(location) { @@ -478,7 +478,7 @@ }, /** - * @param {!WebInspector.LiveLocation} location + * @param {!WebInspector.DebuggerWorkspaceBinding.Location} location */ _removeLocation: function(location) { @@ -512,7 +512,7 @@ * @param {!WebInspector.Script} script * @param {!WebInspector.DebuggerModel.Location} rawLocation * @param {!WebInspector.DebuggerWorkspaceBinding} binding - * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate + * @param {function(!WebInspector.LiveLocation)} updateDelegate */ WebInspector.DebuggerWorkspaceBinding.Location = function(script, rawLocation, binding, updateDelegate) { @@ -539,6 +539,15 @@ this._binding._removeLiveLocation(this); }, + /** + * @override + * @return {boolean} + */ + isBlackboxed: function() + { + return WebInspector.blackboxManager.isBlackboxedRawLocation(this._rawLocation); + }, + __proto__: WebInspector.LiveLocation.prototype }
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/LiveLocation.js b/third_party/WebKit/Source/devtools/front_end/bindings/LiveLocation.js index 2cc65c3..50bd67c 100644 --- a/third_party/WebKit/Source/devtools/front_end/bindings/LiveLocation.js +++ b/third_party/WebKit/Source/devtools/front_end/bindings/LiveLocation.js
@@ -4,7 +4,7 @@ /** * @constructor - * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate + * @param {function(!WebInspector.LiveLocation)} updateDelegate */ WebInspector.LiveLocation = function(updateDelegate) { @@ -14,11 +14,7 @@ WebInspector.LiveLocation.prototype = { update: function() { - var uiLocation = this.uiLocation(); - if (!uiLocation) - return; - if (this._updateDelegate(uiLocation)) - this.dispose(); + this._updateDelegate(this); }, /** @@ -32,5 +28,13 @@ dispose: function() { // Overridden by subclasses. + }, + + /** + * @return {boolean} + */ + isBlackboxed: function() + { + throw "Not implemented"; } }
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/PresentationConsoleMessageHelper.js b/third_party/WebKit/Source/devtools/front_end/bindings/PresentationConsoleMessageHelper.js index 0f66b56..7d54c70 100644 --- a/third_party/WebKit/Source/devtools/front_end/bindings/PresentationConsoleMessageHelper.js +++ b/third_party/WebKit/Source/devtools/front_end/bindings/PresentationConsoleMessageHelper.js
@@ -173,12 +173,15 @@ WebInspector.PresentationConsoleMessage.prototype = { /** - * @param {!WebInspector.UILocation} uiLocation + * @param {!WebInspector.LiveLocation} liveLocation */ - _updateLocation: function(uiLocation) + _updateLocation: function(liveLocation) { if (this._uiMessage) this._uiMessage.remove(); + var uiLocation = liveLocation.uiLocation(); + if (!uiLocation) + return; this._uiMessage = uiLocation.uiSourceCode.addLineMessage(this._level, this._text, uiLocation.lineNumber, uiLocation.columnNumber); },
diff --git a/third_party/WebKit/Source/devtools/front_end/common/TestBase.js b/third_party/WebKit/Source/devtools/front_end/common/TestBase.js index 5b9cb4bef..48fecf7b 100644 --- a/third_party/WebKit/Source/devtools/front_end/common/TestBase.js +++ b/third_party/WebKit/Source/devtools/front_end/common/TestBase.js
@@ -79,6 +79,7 @@ clearTimeout(this.timerId_); this.timerId_ = -1; } + this.controlTaken_ = false; this.reportOk_(); }; @@ -107,7 +108,7 @@ /** * Run specified test on a fresh instance of the test suite. - * @param {string} name Name of a test method from implementation class. + * @param {Array<string>} args method name followed by its parameters. */ WebInspector.TestBase.prototype.dispatch = function(args) { @@ -123,6 +124,19 @@ /** + * Wrap an async method with TestBase.{takeControl(), releaseControl()} + * and invoke TestBase.reportOk_ upon completion. + * @param {Array<string>} args method name followed by its parameters. + */ +WebInspector.TestBase.prototype.waitForAsync = function(var_args) +{ + var args = Array.prototype.slice.call(arguments); + this.takeControl(); + args.push(this.releaseControl.bind(this)); + this.dispatch(args); +}; + +/** * Overrides the method with specified name until it's called first time. * @param {!Object} receiver An object whose method to override. * @param {string} methodName Name of the method to override.
diff --git a/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js b/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js index f7804d5..ef8b82740 100644 --- a/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js +++ b/third_party/WebKit/Source/devtools/front_end/components/Linkifier.js
@@ -39,8 +39,9 @@ /** * @param {!Element} anchor * @param {!WebInspector.UILocation} uiLocation + * @param {boolean} isBlackboxed */ - formatLiveAnchor: function(anchor, uiLocation) { } + formatLiveAnchor: function(anchor, uiLocation, isBlackboxed) { } } /** @@ -202,16 +203,7 @@ // FIXME(62725): console stack trace line/column numbers are one-based. var lineNumber = callFrame.lineNumber ? callFrame.lineNumber - 1 : 0; var columnNumber = callFrame.columnNumber ? callFrame.columnNumber - 1 : 0; - var anchor = this.linkifyScriptLocation(target, callFrame.scriptId, callFrame.url, lineNumber, columnNumber, classes); - var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); - var location = debuggerModel && debuggerModel.createRawLocationByScriptId(callFrame.scriptId, callFrame.lineNumber, callFrame.columnNumber); - var blackboxed = location ? - WebInspector.blackboxManager.isBlackboxedRawLocation(location) : - WebInspector.blackboxManager.isBlackboxedURL(callFrame.url); - if (blackboxed) - anchor.classList.add("webkit-html-blackbox-link"); - - return anchor; + return this.linkifyScriptLocation(target, callFrame.scriptId, callFrame.url, lineNumber, columnNumber, classes); }, /** @@ -290,12 +282,15 @@ /** * @param {!Element} anchor - * @param {!WebInspector.UILocation} uiLocation + * @param {!WebInspector.LiveLocation} liveLocation */ - _updateAnchor: function(anchor, uiLocation) + _updateAnchor: function(anchor, liveLocation) { + var uiLocation = liveLocation.uiLocation(); + if (!uiLocation) + return; anchor[WebInspector.Linkifier._uiLocationSymbol] = uiLocation; - this._formatter.formatLiveAnchor(anchor, uiLocation); + this._formatter.formatLiveAnchor(anchor, uiLocation, liveLocation.isBlackboxed()); } } @@ -323,8 +318,9 @@ * @override * @param {!Element} anchor * @param {!WebInspector.UILocation} uiLocation + * @param {boolean} isBlackboxed */ - formatLiveAnchor: function(anchor, uiLocation) + formatLiveAnchor: function(anchor, uiLocation, isBlackboxed) { var text = uiLocation.linkText(); text = text.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g, "$1\u2026"); @@ -336,6 +332,8 @@ if (typeof uiLocation.lineNumber === "number") titleText += ":" + (uiLocation.lineNumber + 1); anchor.title = titleText; + + anchor.classList.toggle("webkit-html-blackbox-link", isBlackboxed); } } @@ -355,10 +353,11 @@ * @override * @param {!Element} anchor * @param {!WebInspector.UILocation} uiLocation + * @param {boolean} isBlackboxed */ - formatLiveAnchor: function(anchor, uiLocation) + formatLiveAnchor: function(anchor, uiLocation, isBlackboxed) { - WebInspector.Linkifier.DefaultFormatter.prototype.formatLiveAnchor.call(this, anchor, uiLocation); + WebInspector.Linkifier.DefaultFormatter.prototype.formatLiveAnchor.call(this, anchor, uiLocation, isBlackboxed); anchor.classList.add("webkit-html-resource-link"); anchor.setAttribute("data-uncopyable", anchor.textContent); anchor.textContent = "";
diff --git a/third_party/WebKit/Source/devtools/front_end/console/consoleView.css b/third_party/WebKit/Source/devtools/front_end/console/consoleView.css index 1aa3b97..773921b 100644 --- a/third_party/WebKit/Source/devtools/front_end/console/consoleView.css +++ b/third_party/WebKit/Source/devtools/front_end/console/consoleView.css
@@ -404,7 +404,7 @@ flex-shrink: 0; } -.console-message-text { +.console-message-text .object-value-string { white-space: pre-wrap; }
diff --git a/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeModel.js b/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeModel.js index 2a31e21..14ed4df3 100644 --- a/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeModel.js +++ b/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeModel.js
@@ -17,7 +17,8 @@ this._initialized = false; this._deviceMetricsThrottler = new WebInspector.Throttler(0); this._appliedDeviceSize = new Size(1, 1); - this._currentDeviceScaleFactor = window.devicePixelRatio; + this._appliedDeviceScaleFactor = window.devicePixelRatio; + this._appliedUserAgentType = WebInspector.DeviceModeModel.UA.Desktop; this._scaleSetting = WebInspector.settings.createSetting("emulation.deviceScale", 1); // We've used to allow zero before. @@ -76,6 +77,7 @@ /** @enum {string} */ WebInspector.DeviceModeModel.UA = { Mobile: "Mobile", + MobileNoTouch: "MobileNoTouch", Desktop: "Desktop", DesktopTouch: "DesktopTouch" } @@ -107,7 +109,7 @@ WebInspector.DeviceModeModel._touchEventsScriptIdSymbol = Symbol("DeviceModeModel.touchEventsScriptIdSymbol"); WebInspector.DeviceModeModel._defaultMobileUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36"; -WebInspector.DeviceModeModel._defaultMobileScaleFactor = 2; +WebInspector.DeviceModeModel.defaultMobileScaleFactor = 2; WebInspector.DeviceModeModel.prototype = { /** @@ -273,6 +275,22 @@ }, /** + * @return {number} + */ + appliedDeviceScaleFactor: function() + { + return this._appliedDeviceScaleFactor; + }, + + /** + * @return {!WebInspector.DeviceModeModel.UA} + */ + appliedUserAgentType: function() + { + return this._appliedUserAgentType; + }, + + /** * @return {boolean} */ isFullHeight: function() @@ -304,19 +322,6 @@ return this._deviceScaleFactorSetting; }, - /** - * @return {number} - */ - defaultDeviceScaleFactor: function() - { - if (this._type === WebInspector.DeviceModeModel.Type.Responsive) - return this._uaSetting.get() === WebInspector.DeviceModeModel.UA.Mobile ? WebInspector.DeviceModeModel._defaultMobileScaleFactor : this._currentDeviceScaleFactor; - else if (this._type === WebInspector.DeviceModeModel.Type.Device) - return this._device.deviceScaleFactor; - else - return this._currentDeviceScaleFactor; - }, - reset: function() { this._deviceScaleFactorSetting.set(0); @@ -413,11 +418,16 @@ if (this._type === WebInspector.DeviceModeModel.Type.Device) { var orientation = this._device.orientationByName(this._mode.orientation); this._fitScale = this._calculateFitScale(orientation.width, orientation.height); + if (this._device.mobile()) + this._appliedUserAgentType = this._device.touch() ? WebInspector.DeviceModeModel.UA.Mobile : WebInspector.DeviceModeModel.UA.MobileNoTouch; + else + this._appliedUserAgentType = this._device.touch() ? WebInspector.DeviceModeModel.UA.DesktopTouch : WebInspector.DeviceModeModel.UA.Desktop; this._applyDeviceMetrics(new Size(orientation.width, orientation.height), this._mode.insets, this._scaleSetting.get(), this._device.deviceScaleFactor, this._device.mobile(), resetPageScaleFactor); this._applyUserAgent(this._device.userAgent); this._applyScreenOrientation(this._mode.orientation == WebInspector.EmulatedDevice.Horizontal ? "landscapePrimary" : "portraitPrimary"); } else if (this._type === WebInspector.DeviceModeModel.Type.None) { this._fitScale = this._calculateFitScale(this._availableSize.width, this._availableSize.height); + this._appliedUserAgentType = WebInspector.DeviceModeModel.UA.Desktop; this._applyDeviceMetrics(this._availableSize, new Insets(0, 0, 0, 0), 1, 0, false, resetPageScaleFactor); this._applyUserAgent(""); this._applyScreenOrientation(""); @@ -428,9 +438,10 @@ var screenHeight = this._heightSetting.get(); if (!screenHeight || screenHeight > this._preferredScaledHeight()) screenHeight = this._preferredScaledHeight(); - var mobile = this._uaSetting.get() === WebInspector.DeviceModeModel.UA.Mobile; - var defaultDeviceScaleFactor = mobile ? WebInspector.DeviceModeModel._defaultMobileScaleFactor : 0; + var mobile = this._uaSetting.get() === WebInspector.DeviceModeModel.UA.Mobile || this._uaSetting.get() === WebInspector.DeviceModeModel.UA.MobileNoTouch; + var defaultDeviceScaleFactor = mobile ? WebInspector.DeviceModeModel.defaultMobileScaleFactor : 0; this._fitScale = this._calculateFitScale(this._widthSetting.get(), this._heightSetting.get()); + this._appliedUserAgentType = this._uaSetting.get(); this._applyDeviceMetrics(new Size(screenWidth, screenHeight), new Insets(0, 0, 0, 0), this._scaleSetting.get(), this._deviceScaleFactorSetting.get() || defaultDeviceScaleFactor, mobile, resetPageScaleFactor); this._applyUserAgent(mobile ? WebInspector.DeviceModeModel._defaultMobileUserAgent : ""); this._applyScreenOrientation(screenHeight >= screenWidth ? "portraitPrimary" : "landscapePrimary"); @@ -474,7 +485,7 @@ else if (this._type === WebInspector.DeviceModeModel.Type.None) this._applyTouch(false, false); else if (this._type === WebInspector.DeviceModeModel.Type.Responsive) - this._applyTouch(this._uaSetting.get() !== WebInspector.DeviceModeModel.UA.Desktop, this._uaSetting.get() === WebInspector.DeviceModeModel.UA.Mobile); + this._applyTouch(this._uaSetting.get() === WebInspector.DeviceModeModel.UA.DesktopTouch || this._uaSetting.get() === WebInspector.DeviceModeModel.UA.Mobile, this._uaSetting.get() === WebInspector.DeviceModeModel.UA.Mobile); }, /** @@ -504,6 +515,7 @@ var positionY = insets.top; this._appliedDeviceSize = screenSize; + this._appliedDeviceScaleFactor = deviceScaleFactor || window.devicePixelRatio; this._screenRect = new WebInspector.Rect( Math.max(0, (this._availableSize.width - screenSize.width * scale) / 2), 0,
diff --git a/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeToolbar.js b/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeToolbar.js index a71ce0b..372ef3e26 100644 --- a/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeToolbar.js +++ b/third_party/WebKit/Source/devtools/front_end/emulation/DeviceModeToolbar.js
@@ -13,6 +13,13 @@ this._model = model; this._showMediaInspectorSetting = showMediaInspectorSetting; this._showRulersSetting = showRulersSetting; + + this._showDeviceScaleFactorSetting = WebInspector.settings.createSetting("emulation.showDeviceScaleFactor", false); + this._showDeviceScaleFactorSetting.addChangeListener(this._updateDeviceScaleFactorVisibility, this); + + this._showUserAgentTypeSetting = WebInspector.settings.createSetting("emulation.showUserAgentType", false); + this._showUserAgentTypeSetting.addChangeListener(this._updateUserAgentTypeVisibility, this); + /** @type {!Map<!WebInspector.EmulatedDevice, !WebInspector.EmulatedDevice.Mode>} */ this._lastMode = new Map(); @@ -137,6 +144,24 @@ _fillModeToolbar: function(toolbar) { toolbar.appendToolbarItem(this._wrapToolbarItem(createElementWithClass("div", "device-mode-empty-toolbar-element"))); + this._deviceScaleItem = new WebInspector.ToolbarMenuButton(this._appendDeviceScaleMenuItems.bind(this)); + this._deviceScaleItem.setVisible(this._showDeviceScaleFactorSetting.get()); + this._deviceScaleItem.setTitle(WebInspector.UIString("Device pixel ratio")); + this._deviceScaleItem.setGlyph(""); + this._deviceScaleItem.turnIntoSelect(); + this._deviceScaleItem.element.style.padding = "0 5px"; + toolbar.appendToolbarItem(this._deviceScaleItem); + + toolbar.appendToolbarItem(this._wrapToolbarItem(createElementWithClass("div", "device-mode-empty-toolbar-element"))); + this._uaItem = new WebInspector.ToolbarMenuButton(this._appendUserAgentMenuItems.bind(this)); + this._uaItem.setVisible(this._showUserAgentTypeSetting.get()); + this._uaItem.setTitle(WebInspector.UIString("Device type")); + this._uaItem.setGlyph(""); + this._uaItem.turnIntoSelect(); + this._uaItem.element.style.padding = "0 5px"; + toolbar.appendToolbarItem(this._uaItem); + + toolbar.appendToolbarItem(this._wrapToolbarItem(createElementWithClass("div", "device-mode-empty-toolbar-element"))); this._modeButton = new WebInspector.ToolbarButton("", "rotate-screen-toolbar-item"); this._modeButton.addEventListener("click", this._modeMenuClicked, this); toolbar.appendToolbarItem(this._modeButton); @@ -147,18 +172,6 @@ */ _fillOptionsToolbar: function(toolbar) { - this._uaItem = new WebInspector.ToolbarText(); - this._uaItem.setVisible(false); - this._uaItem.setTitle(WebInspector.UIString("User agent type")); - this._uaItem.element.style.opacity = "0.5"; - toolbar.appendToolbarItem(this._uaItem); - - this._deviceScaleItem = new WebInspector.ToolbarText(); - this._deviceScaleItem.setVisible(false); - this._deviceScaleItem.setTitle(WebInspector.UIString("Device pixel ratio")); - this._deviceScaleItem.element.style.opacity = "0.5"; - toolbar.appendToolbarItem(this._deviceScaleItem); - var moreOptionsButton = new WebInspector.ToolbarMenuButton(this._appendOptionsMenuItems.bind(this)); moreOptionsButton.setTitle(WebInspector.UIString("More options")); toolbar.appendToolbarItem(moreOptionsButton); @@ -196,35 +209,12 @@ /** * @param {!WebInspector.ContextMenu} contextMenu */ - _appendOptionsMenuItems: function(contextMenu) + _appendDeviceScaleMenuItems: function(contextMenu) { - var uaDisabled = this._model.type() !== WebInspector.DeviceModeModel.Type.Responsive; - var uaSetting = this._model.uaSetting(); - var uaSubmenu = contextMenu.appendSubMenuItem(WebInspector.UIString("User agent type"), false); - var uaValue = this._model.uaSetting().get(); - if (this._model.type() === WebInspector.DeviceModeModel.Type.None) - uaValue = WebInspector.DeviceModeModel.UA.Desktop; - if (this._model.type() === WebInspector.DeviceModeModel.Type.Device) - uaValue = this._model.device().mobile() ? WebInspector.DeviceModeModel.UA.Mobile : this._model.device().touch() ? WebInspector.DeviceModeModel.UA.DesktopTouch : WebInspector.DeviceModeModel.UA.Desktop; - appendUAItem(WebInspector.UIString("Mobile (default)"), WebInspector.DeviceModeModel.UA.Mobile); - appendUAItem(WebInspector.UIString("Desktop"), WebInspector.DeviceModeModel.UA.Desktop); - appendUAItem(WebInspector.UIString("Desktop with touch"), WebInspector.DeviceModeModel.UA.DesktopTouch); - - /** - * @param {string} title - * @param {!WebInspector.DeviceModeModel.UA} value - */ - function appendUAItem(title, value) - { - uaSubmenu.appendCheckboxItem(title, uaSetting.set.bind(uaSetting, value), uaValue === value, uaDisabled); - } - - var deviceScaleFactorDisabled = this._model.type() !== WebInspector.DeviceModeModel.Type.Responsive; - var deviceScaleFactorSubmenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Device pixel ratio"), false); var deviceScaleFactorSetting = this._model.deviceScaleFactorSetting(); - var deviceScaleFactorValue = deviceScaleFactorDisabled ? 0 : deviceScaleFactorSetting.get(); - appendDeviceScaleFactorItem(WebInspector.UIString("Default: %f", this._model.defaultDeviceScaleFactor()), 0); - deviceScaleFactorSubmenu.appendSeparator(); + var defaultValue = this._model.uaSetting().get() === WebInspector.DeviceModeModel.UA.Mobile || this._model.uaSetting().get() === WebInspector.DeviceModeModel.UA.MobileNoTouch ? WebInspector.DeviceModeModel.defaultMobileScaleFactor : window.devicePixelRatio; + appendDeviceScaleFactorItem(WebInspector.UIString("Default: %.1f", defaultValue), 0); + contextMenu.appendSeparator(); appendDeviceScaleFactorItem(WebInspector.UIString("1"), 1); appendDeviceScaleFactorItem(WebInspector.UIString("2"), 2); appendDeviceScaleFactorItem(WebInspector.UIString("3"), 3); @@ -235,16 +225,55 @@ */ function appendDeviceScaleFactorItem(title, value) { - deviceScaleFactorSubmenu.appendCheckboxItem(title, deviceScaleFactorSetting.set.bind(deviceScaleFactorSetting, value), deviceScaleFactorValue === value, deviceScaleFactorDisabled); + contextMenu.appendCheckboxItem(title, deviceScaleFactorSetting.set.bind(deviceScaleFactorSetting, value), deviceScaleFactorSetting.get() === value); } + }, - contextMenu.appendItem(WebInspector.UIString("Reset to defaults"), this._model.reset.bind(this._model), this._model.type() !== WebInspector.DeviceModeModel.Type.Responsive); - contextMenu.appendSeparator(); + /** + * @param {!WebInspector.ContextMenu} contextMenu + */ + _appendUserAgentMenuItems: function(contextMenu) + { + var uaSetting = this._model.uaSetting(); + appendUAItem(WebInspector.UIString("Mobile"), WebInspector.DeviceModeModel.UA.Mobile); + appendUAItem(WebInspector.UIString("Mobile (no touch)"), WebInspector.DeviceModeModel.UA.MobileNoTouch); + appendUAItem(WebInspector.UIString("Desktop"), WebInspector.DeviceModeModel.UA.Desktop); + appendUAItem(WebInspector.UIString("Desktop (touch)"), WebInspector.DeviceModeModel.UA.DesktopTouch); + /** + * @param {string} title + * @param {!WebInspector.DeviceModeModel.UA} value + */ + function appendUAItem(title, value) + { + contextMenu.appendCheckboxItem(title, uaSetting.set.bind(uaSetting, value), uaSetting.get() === value); + } + }, + + /** + * @param {!WebInspector.ContextMenu} contextMenu + */ + _appendOptionsMenuItems: function(contextMenu) + { + contextMenu.appendCheckboxItem(WebInspector.UIString("Show device pixel ratio"), this._toggleDeviceScaleFactor.bind(this), this._showDeviceScaleFactorSetting.get(), this._model.type() === WebInspector.DeviceModeModel.Type.None); + contextMenu.appendCheckboxItem(WebInspector.UIString("Show device type"), this._toggleUserAgentType.bind(this), this._showUserAgentTypeSetting.get(), this._model.type() === WebInspector.DeviceModeModel.Type.None); contextMenu.appendCheckboxItem(WebInspector.UIString("Show media queries"), this._toggleMediaInspector.bind(this), this._showMediaInspectorSetting.get(), this._model.type() === WebInspector.DeviceModeModel.Type.None); contextMenu.appendCheckboxItem(WebInspector.UIString("Show rulers"), this._toggleRulers.bind(this), this._showRulersSetting.get(), this._model.type() === WebInspector.DeviceModeModel.Type.None); + contextMenu.appendSeparator(); contextMenu.appendItem(WebInspector.UIString("Configure network\u2026"), this._openNetworkConfig.bind(this), false); contextMenu.appendItemsAtLocation("deviceModeMenu"); + contextMenu.appendSeparator(); + contextMenu.appendItem(WebInspector.UIString("Reset to defaults"), this._reset.bind(this)); + }, + + _toggleDeviceScaleFactor: function() + { + this._showDeviceScaleFactorSetting.set(!this._showDeviceScaleFactorSetting.get()); + }, + + _toggleUserAgentType: function() + { + this._showUserAgentTypeSetting.set(!this._showUserAgentTypeSetting.get()); }, _toggleMediaInspector: function() @@ -264,6 +293,15 @@ WebInspector.actionRegistry.action("network.show-config").execute(); }, + _reset: function() + { + this._showDeviceScaleFactorSetting.set(false); + this._showUserAgentTypeSetting.set(false); + this._showMediaInspectorSetting.set(false); + this._showRulersSetting.set(false); + this._model.reset(); + }, + /** * @param {!Element} element * @return {!WebInspector.ToolbarItem} @@ -366,6 +404,16 @@ } }, + _updateDeviceScaleFactorVisibility: function() + { + this._deviceScaleItem.setVisible(this._showDeviceScaleFactorSetting.get()); + }, + + _updateUserAgentTypeVisibility: function() + { + this._uaItem.setVisible(this._showUserAgentTypeSetting.get()); + }, + /** * @param {!WebInspector.Event} event */ @@ -436,6 +484,8 @@ this._cachedModelType = this._model.type(); this._widthInput.disabled = this._model.type() !== WebInspector.DeviceModeModel.Type.Responsive; this._heightInput.disabled = this._model.type() !== WebInspector.DeviceModeModel.Type.Responsive; + this._deviceScaleItem.setEnabled(this._model.type() === WebInspector.DeviceModeModel.Type.Responsive); + this._uaItem.setEnabled(this._model.type() === WebInspector.DeviceModeModel.Type.Responsive); } var size = this._model.appliedDeviceSize(); @@ -445,21 +495,25 @@ if (this._model.scale() !== this._cachedScale) { this._scaleItem.setText(WebInspector.UIString("%.0f%%", this._model.scale() * 100)); - this._scaleItem.setState(this._model.scale() === 1 ? "off" : "on"); this._cachedScale = this._model.scale(); } - var deviceScale = this._model.deviceScaleFactorSetting().get(); - this._deviceScaleItem.setVisible(this._model.type() === WebInspector.DeviceModeModel.Type.Responsive && !!deviceScale); + var deviceScale = this._model.appliedDeviceScaleFactor(); if (deviceScale !== this._cachedDeviceScale) { this._deviceScaleItem.setText(WebInspector.UIString("DPR: %.1f", deviceScale)); this._cachedDeviceScale = deviceScale; } - var uaType = this._model.type() === WebInspector.DeviceModeModel.Type.Responsive ? this._model.uaSetting().get() : WebInspector.DeviceModeModel.UA.Mobile; - this._uaItem.setVisible(this._model.type() === WebInspector.DeviceModeModel.Type.Responsive && uaType !== WebInspector.DeviceModeModel.UA.Mobile); + var uaType = this._model.appliedUserAgentType(); if (uaType !== this._cachedUaType) { - this._uaItem.setText(uaType === WebInspector.DeviceModeModel.UA.Desktop ? WebInspector.UIString("Desktop") : WebInspector.UIString("Touch")); + var uaTitle = WebInspector.UIString("Desktop"); + if (uaType === WebInspector.DeviceModeModel.UA.Mobile) + uaTitle = WebInspector.UIString("Mobile"); + if (uaType === WebInspector.DeviceModeModel.UA.MobileNoTouch) + uaTitle = WebInspector.UIString("Mobile (no touch)"); + if (uaType === WebInspector.DeviceModeModel.UA.DesktopTouch) + uaTitle = WebInspector.UIString("Desktop (touch)"); + this._uaItem.setText(uaTitle); this._cachedUaType = uaType; }
diff --git a/third_party/WebKit/Source/devtools/front_end/externs.js b/third_party/WebKit/Source/devtools/front_end/externs.js index 773a9fd..7906eca06 100644 --- a/third_party/WebKit/Source/devtools/front_end/externs.js +++ b/third_party/WebKit/Source/devtools/front_end/externs.js
@@ -42,6 +42,9 @@ /** @type {string} */ Event.prototype.code; +/** @type {function():!Array<!EventTarget>|undefined} */ +Event.prototype.deepPath; + /** * @type {number} */
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Tests.js b/third_party/WebKit/Source/devtools/front_end/main/Tests.js index e63ad970..4e3588f 100644 --- a/third_party/WebKit/Source/devtools/front_end/main/Tests.js +++ b/third_party/WebKit/Source/devtools/front_end/main/Tests.js
@@ -47,6 +47,7 @@ function TestSuite() { WebInspector.TestBase.call(this, domAutomationController); + this._asyncInvocationId = 0; }; TestSuite.prototype = { @@ -651,10 +652,9 @@ this.takeControl(); }; -TestSuite.prototype.invokeAsyncWithTimeline_ = function(functionName, callback) +TestSuite.prototype.startTimeline = function(callback) { - var test = this; - test.showPanel("timeline").then(function() { + this.showPanel("timeline").then(function() { WebInspector.panels.timeline._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStarted, onRecordingStarted); WebInspector.panels.timeline._toggleRecording(); }); @@ -662,32 +662,77 @@ function onRecordingStarted() { WebInspector.panels.timeline._model.removeEventListener(WebInspector.TimelineModel.Events.RecordingStarted, onRecordingStarted); - test.evaluateInConsole_(functionName + "(function() { console.log('DONE'); });", function() {}); - WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage); + callback(); } +} - function onConsoleMessage(event) - { - var text = event.data.messageText; - if (text === "DONE") { - WebInspector.multitargetConsoleModel.removeEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage); - pageActionsDone(); - } - } - - function pageActionsDone() - { - WebInspector.panels.timeline._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStopped, onRecordingStopped); - WebInspector.panels.timeline._toggleRecording(); - } - +TestSuite.prototype.stopTimeline = function(callback) +{ + WebInspector.panels.timeline._model.addEventListener(WebInspector.TimelineModel.Events.RecordingStopped, onRecordingStopped); + WebInspector.panels.timeline._toggleRecording(); function onRecordingStopped() { WebInspector.panels.timeline._model.removeEventListener(WebInspector.TimelineModel.Events.RecordingStopped, onRecordingStopped); callback(); } +} + +TestSuite.prototype.invokePageFunctionAsync = function(functionName, opt_args, callback_is_always_last) +{ + var callback = arguments[arguments.length - 1]; + var doneMessage = `DONE: ${functionName}.${++this._asyncInvocationId}`; + var argsString = arguments.length < 3 ? "" : Array.prototype.slice.call(arguments, 1, -1).map(arg => JSON.stringify(arg)).join(",") + ","; + this.evaluateInConsole_(`${functionName}(${argsString} function() { console.log('${doneMessage}'); });`, function() {}); + WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage); + + function onConsoleMessage(event) + { + var text = event.data.messageText; + if (text === doneMessage) { + WebInspector.multitargetConsoleModel.removeEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage); + callback(); + } + } +} + +TestSuite.prototype.invokeAsyncWithTimeline_ = function(functionName, callback) +{ + var test = this; + + this.startTimeline(onRecordingStarted); + + function onRecordingStarted() + { + test.invokePageFunctionAsync(functionName, pageActionsDone); + } + + function pageActionsDone() + { + test.stopTimeline(callback); + } }; +TestSuite.prototype.enableExperiment = function(name) +{ + Runtime.experiments.enableForTest(name); +} + +TestSuite.prototype.checkInputEventsPresent = function() +{ + var expectedEvents = new Set(arguments); + var model = WebInspector.panels.timeline._model; + var asyncEvents = model.mainThreadAsyncEvents(); + var input = asyncEvents.get(WebInspector.TimelineUIUtils.asyncEventGroups().input) || []; + var prefix = "InputLatency::"; + for (var e of input) { + if (!e.name.startsWith(prefix)) + continue; + expectedEvents.delete(e.name.substr(prefix.length)); + } + if (expectedEvents.size) + throw "Some expected events are not found: " + Array.from(expectedEvents.keys()).join(","); +} + /** * Serializes array of uiSourceCodes to string. * @param {!Array.<!WebInspectorUISourceCode>} uiSourceCodes
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileFlameChart.js b/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileFlameChart.js index 47a0dfc2..727a2d9 100644 --- a/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileFlameChart.js +++ b/third_party/WebKit/Source/devtools/front_end/profiler/CPUProfileFlameChart.js
@@ -385,7 +385,7 @@ this._overviewPane = new WebInspector.CPUProfileFlameChart.OverviewPane(dataProvider); this._overviewPane.show(this.element); - this._mainPane = new WebInspector.FlameChart(dataProvider, this._overviewPane, true); + this._mainPane = new WebInspector.FlameChart(dataProvider, this._overviewPane); this._mainPane.show(this.element); this._mainPane.addEventListener(WebInspector.FlameChart.Events.EntrySelected, this._onEntrySelected, this); this._overviewPane.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/ASTService.js b/third_party/WebKit/Source/devtools/front_end/sass/ASTService.js new file mode 100644 index 0000000..4d6e2bae --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/sass/ASTService.js
@@ -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. + +/** + * @constructor + */ +WebInspector.ASTService = function() +{ + this._cssParserService = new WebInspector.CSSParserService(); + this._sassInitPromise = self.runtime.instancePromise(WebInspector.TokenizerFactory); + this._terminated = false; +} + +WebInspector.ASTService.prototype = { + /** + * @param {string} url + * @param {string} text + * @return {!Promise<!WebInspector.SASSSupport.AST>} + */ + parseCSS: function(url, text) + { + console.assert(!this._terminated, "Illegal call parseCSS on terminated ASTService."); + return WebInspector.SASSSupport.parseCSS(this._cssParserService, url, text); + }, + + /** + * @param {string} url + * @param {string} text + * @return {!Promise<!WebInspector.SASSSupport.AST>} + */ + parseSCSS: function(url, text) + { + console.assert(!this._terminated, "Illegal call parseSCSS on terminated ASTService."); + return this._sassInitPromise.then(tokenizer => WebInspector.SASSSupport.parseSCSS(tokenizer, url, text)); + }, + + dispose: function() + { + if (this._terminated) + return; + this._terminated = true; + this._cssParserService.dispose(); + }, +}
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/ASTSourceMap.js b/third_party/WebKit/Source/devtools/front_end/sass/ASTSourceMap.js new file mode 100644 index 0000000..787cb2e --- /dev/null +++ b/third_party/WebKit/Source/devtools/front_end/sass/ASTSourceMap.js
@@ -0,0 +1,225 @@ +// 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. + +/** + * @constructor + * @param {!WebInspector.SASSSupport.AST} cssAST + * @param {!Map<string, !WebInspector.SASSSupport.AST>} sassModels + */ +WebInspector.ASTSourceMap = function(cssAST, sassModels) +{ + this._cssAST = cssAST; + this._sassModels = sassModels; + /** @type {!Map<!WebInspector.SASSSupport.TextNode, !WebInspector.SASSSupport.TextNode>} */ + this._cssToSass = new Map(); + /** @type {!Multimap<!WebInspector.SASSSupport.TextNode, !WebInspector.SASSSupport.TextNode>} */ + this._sassToCss = new Multimap(); +} + +/** + * @param {!WebInspector.ASTService} astService + * @param {!WebInspector.CSSStyleModel} cssModel + * @param {!WebInspector.SourceMap} sourceMap + * @return {!Promise<?WebInspector.ASTSourceMap>} + */ +WebInspector.ASTSourceMap.fromSourceMap = function(astService, cssModel, sourceMap) +{ + var headerIds = cssModel.styleSheetIdsForURL(sourceMap.compiledURL()); + if (!headerIds || !headerIds.length) + return Promise.resolve(/** @type {?WebInspector.ASTSourceMap} */(null)); + var header = cssModel.styleSheetHeaderForId(headerIds[0]); + + var sassModels = new Map(); + var cssAST = null; + var promises = []; + for (var url of sourceMap.sources()) { + var contentProvider = sourceMap.sourceContentProvider(url, WebInspector.resourceTypes.SourceMapStyleSheet); + var sassPromise = contentProvider.requestContent() + .then(onSCSSText.bind(null, url)) + .then(ast => sassModels.set(ast.document.url, ast)); + promises.push(sassPromise); + } + var cssPromise = header.requestContent() + .then(text => astService.parseCSS(sourceMap.compiledURL(), text || "")) + .then(ast => cssAST = ast); + promises.push(cssPromise); + + return Promise.all(promises) + .then(() => onParsed(cssAST, sassModels, sourceMap)) + .catchException(/** @type {?WebInspector.ASTSourceMap} */(null)); + + /** + * @param {string} url + * @param {?string} text + * @return {!Promise<!WebInspector.SASSSupport.AST>} + */ + function onSCSSText(url, text) + { + return astService.parseSCSS(url, text || ""); + } + + /** + * @param {!WebInspector.SASSSupport.AST} cssAST + * @param {!Map<string, !WebInspector.SASSSupport.AST>} sassModels + * @param {!WebInspector.SourceMap} sourceMap + */ + function onParsed(cssAST, sassModels, sourceMap) + { + var mapping = new WebInspector.ASTSourceMap(cssAST, sassModels); + //FIXME: this works O(N^2). + cssAST.visit(map); + return mapping; + + /** + * @param {!WebInspector.SASSSupport.Node} cssNode + */ + function map(cssNode) + { + if (!(cssNode instanceof WebInspector.SASSSupport.TextNode)) + return; + var entry = sourceMap.findEntry(cssNode.range.endLine, cssNode.range.endColumn); + if (!entry || !entry.sourceURL || typeof entry.sourceLineNumber === "undefined" || typeof entry.sourceColumnNumber === "undefined") + return; + var sassAST = sassModels.get(entry.sourceURL); + if (!sassAST) + return; + var sassNode = sassAST.findNodeForPosition(entry.sourceLineNumber, entry.sourceColumnNumber); + if (sassNode) + mapping.mapCssToSass(cssNode, sassNode); + } + } +} + +WebInspector.ASTSourceMap.prototype = { + /** + * @return {!WebInspector.SASSSupport.AST} + */ + cssAST: function() + { + return this._cssAST; + }, + + /** + * @return {!Map<string, !WebInspector.SASSSupport.AST>} + */ + sassModels: function() + { + return this._sassModels; + }, + + /** + * @param {!WebInspector.SASSSupport.TextNode} css + * @param {!WebInspector.SASSSupport.TextNode} sass + */ + mapCssToSass: function(css, sass) + { + this._cssToSass.set(css, sass); + this._sassToCss.set(sass, css); + }, + + /** + * @param {!WebInspector.SASSSupport.TextNode} css + * @param {!WebInspector.SASSSupport.TextNode} sass + */ + unmapCssFromSass: function(css, sass) + { + this._cssToSass.delete(css); + this._sassToCss.remove(sass, css); + }, + + /** + * @param {!WebInspector.SASSSupport.TextNode} css + * @return {?WebInspector.SASSSupport.TextNode} + */ + toSASSNode: function(css) + { + return this._cssToSass.get(css) || null; + }, + + /** + * @param {!WebInspector.SASSSupport.TextNode} sass + * @return {!Array<!WebInspector.SASSSupport.TextNode>} + */ + toCSSNodes: function(sass) + { + var cssNodes = this._sassToCss.get(sass); + return cssNodes ? cssNodes.valuesArray() : []; + }, + + /** + * @param {!WebInspector.SASSSupport.Property} cssProperty + * @return {?WebInspector.SASSSupport.Property} + */ + toSASSProperty: function(cssProperty) + { + var sassName = this._cssToSass.get(cssProperty.name); + return sassName ? sassName.parent : null; + }, + + /** + * @param {!WebInspector.SASSSupport.Property} sassProperty + * @return {!Array<!WebInspector.SASSSupport.Property>} + */ + toCSSProperties: function(sassProperty) + { + return this.toCSSNodes(sassProperty.name).map(name => name.parent); + }, + + /** + * @param {!WebInspector.SASSSupport.ASTDiff} cssDiff + * @return {!WebInspector.ASTSourceMap} + */ + rebaseForCSSDiff: function(cssDiff) + { + var newMapping = new WebInspector.ASTSourceMap(cssDiff.newAST, this._sassModels); + var cssNodes = this._cssToSass.keysArray(); + for (var i = 0; i < cssNodes.length; ++i) { + var cssNode = cssNodes[i]; + var sassNode = this._cssToSass.get(cssNode); + var mappedNode = cssDiff.mapping.get(cssNode); + if (mappedNode && sassNode) + newMapping.mapCssToSass(mappedNode, sassNode); + } + return newMapping; + }, + + /** + * @param {!WebInspector.SASSSupport.ASTDiff} sassDiff + * @return {!WebInspector.ASTSourceMap} + */ + rebaseForSASSDiff: function(sassDiff) + { + var sassModels = new Map(this._sassModels); + sassModels.set(sassDiff.url, sassDiff.newAST); + var newMapping = new WebInspector.ASTSourceMap(this._cssAST, sassModels); + var cssNodes = this._cssToSass.keysArray(); + for (var i = 0; i < cssNodes.length; ++i) { + var cssNode = cssNodes[i]; + var sassNode = this._cssToSass.get(cssNode); + var mappedNode = sassNode.document.url === sassDiff.url ? sassDiff.mapping.get(sassNode) : sassNode; + if (mappedNode) + newMapping.mapCssToSass(cssNode, mappedNode); + } + return newMapping; + }, + + /** + * @return {boolean} + */ + isValid: function() + { + var cssNodes = this._cssToSass.keysArray(); + for (var i = 0; i < cssNodes.length; ++i) { + var cssNode = cssNodes[i]; + if (!cssNode.parent || !(cssNode.parent instanceof WebInspector.SASSSupport.Property)) + continue; + if (cssNode !== cssNode.parent.name) + continue; + var sassNode = this._cssToSass.get(cssNode); + if (sassNode && cssNode.text.trim() !== sassNode.text.trim()) + return false; + } + return true; + } +}
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/SASSLiveSourceMap.js b/third_party/WebKit/Source/devtools/front_end/sass/SASSLiveSourceMap.js deleted file mode 100644 index 6c49db3c..0000000 --- a/third_party/WebKit/Source/devtools/front_end/sass/SASSLiveSourceMap.js +++ /dev/null
@@ -1,212 +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. - -WebInspector.SASSLiveSourceMap = {} - -/** - * @param {!WebInspector.SourceMapTracker} tracker - * @param {!WebInspector.CSSParser} cssParser - * @param {!WebInspector.TokenizerFactory} tokenizer - * @return {!Promise<?WebInspector.SASSLiveSourceMap.CSSToSASSMapping>} - */ -WebInspector.SASSLiveSourceMap._loadMapping = function(tracker, cssParser, tokenizer) -{ - var sassModels = new Map(); - var cssAST = null; - var promises = []; - for (var url of tracker.sassURLs()) { - var sassPromise = tracker.content(url) - .then(text => WebInspector.SASSSupport.parseSCSS(url, text, tokenizer)) - .then(ast => sassModels.set(url, ast)); - promises.push(sassPromise); - } - var cssPromise = tracker.content(tracker.cssURL()) - .then(text => WebInspector.SASSSupport.parseCSS(cssParser, tracker.cssURL(), text)) - .then(ast => cssAST = ast); - promises.push(cssPromise); - - return Promise.all(promises) - .then(() => WebInspector.SASSLiveSourceMap.CSSToSASSMapping.fromSourceMap(tracker.sourceMap(), cssAST, sassModels)) - .catchException(/** @type {?WebInspector.SASSLiveSourceMap.CSSToSASSMapping} */(null)); -} - -/** - * @constructor - * @param {!WebInspector.SASSSupport.AST} cssAST - * @param {!Map<string, !WebInspector.SASSSupport.AST>} sassModels - */ -WebInspector.SASSLiveSourceMap.CSSToSASSMapping = function(cssAST, sassModels) -{ - this._cssAST = cssAST; - this._sassModels = sassModels; - /** @type {!Map<!WebInspector.SASSSupport.TextNode, !WebInspector.SASSSupport.TextNode>} */ - this._cssToSass = new Map(); - /** @type {!Multimap<!WebInspector.SASSSupport.TextNode, !WebInspector.SASSSupport.TextNode>} */ - this._sassToCss = new Multimap(); -} - -/** - * @param {!WebInspector.SourceMap} sourceMap - * @param {!WebInspector.SASSSupport.AST} cssAST - * @param {!Map<string, !WebInspector.SASSSupport.AST>} sassModels - * @return {!WebInspector.SASSLiveSourceMap.CSSToSASSMapping} - */ -WebInspector.SASSLiveSourceMap.CSSToSASSMapping.fromSourceMap = function(sourceMap, cssAST, sassModels) -{ - var mapping = new WebInspector.SASSLiveSourceMap.CSSToSASSMapping(cssAST, sassModels); - //FIXME: this works O(N^2). - cssAST.visit(map); - return mapping; - - /** - * @param {!WebInspector.SASSSupport.Node} cssNode - */ - function map(cssNode) - { - if (!(cssNode instanceof WebInspector.SASSSupport.TextNode)) - return; - var entry = sourceMap.findEntry(cssNode.range.endLine, cssNode.range.endColumn); - if (!entry || !entry.sourceURL || typeof entry.sourceLineNumber === "undefined" || typeof entry.sourceColumnNumber === "undefined") - return; - var sassAST = sassModels.get(entry.sourceURL); - if (!sassAST) - return; - var sassNode = sassAST.findNodeForPosition(entry.sourceLineNumber, entry.sourceColumnNumber); - if (sassNode) - mapping.mapCssToSass(cssNode, sassNode); - } -} - -WebInspector.SASSLiveSourceMap.CSSToSASSMapping.prototype = { - /** - * @return {!WebInspector.SASSSupport.AST} - */ - cssAST: function() - { - return this._cssAST; - }, - - /** - * @return {!Map<string, !WebInspector.SASSSupport.AST>} - */ - sassModels: function() - { - return this._sassModels; - }, - - /** - * @param {!WebInspector.SASSSupport.TextNode} css - * @param {!WebInspector.SASSSupport.TextNode} sass - */ - mapCssToSass: function(css, sass) - { - this._cssToSass.set(css, sass); - this._sassToCss.set(sass, css); - }, - - /** - * @param {!WebInspector.SASSSupport.TextNode} css - * @param {!WebInspector.SASSSupport.TextNode} sass - */ - unmapCssFromSass: function(css, sass) - { - this._cssToSass.delete(css); - this._sassToCss.remove(sass, css); - }, - - /** - * @param {!WebInspector.SASSSupport.TextNode} css - * @return {?WebInspector.SASSSupport.TextNode} - */ - toSASSNode: function(css) - { - return this._cssToSass.get(css) || null; - }, - - /** - * @param {!WebInspector.SASSSupport.TextNode} sass - * @return {!Array<!WebInspector.SASSSupport.TextNode>} - */ - toCSSNodes: function(sass) - { - var cssNodes = this._sassToCss.get(sass); - return cssNodes ? cssNodes.valuesArray() : []; - }, - - /** - * @param {!WebInspector.SASSSupport.Property} cssProperty - * @return {?WebInspector.SASSSupport.Property} - */ - toSASSProperty: function(cssProperty) - { - var sassName = this._cssToSass.get(cssProperty.name); - return sassName ? sassName.parent : null; - }, - - /** - * @param {!WebInspector.SASSSupport.Property} sassProperty - * @return {!Array<!WebInspector.SASSSupport.Property>} - */ - toCSSProperties: function(sassProperty) - { - return this.toCSSNodes(sassProperty.name).map(name => name.parent); - }, - - /** - * @param {!WebInspector.SASSSupport.ASTDiff} cssDiff - * @return {!WebInspector.SASSLiveSourceMap.CSSToSASSMapping} - */ - rebaseForCSSDiff: function(cssDiff) - { - var newMapping = new WebInspector.SASSLiveSourceMap.CSSToSASSMapping(cssDiff.newAST, this._sassModels); - var cssNodes = this._cssToSass.keysArray(); - for (var i = 0; i < cssNodes.length; ++i) { - var cssNode = cssNodes[i]; - var sassNode = this._cssToSass.get(cssNode); - var mappedNode = cssDiff.mapping.get(cssNode); - if (mappedNode && sassNode) - newMapping.mapCssToSass(mappedNode, sassNode); - } - return newMapping; - }, - - /** - * @param {!WebInspector.SASSSupport.ASTDiff} sassDiff - * @return {!WebInspector.SASSLiveSourceMap.CSSToSASSMapping} - */ - rebaseForSASSDiff: function(sassDiff) - { - var sassModels = new Map(this._sassModels); - sassModels.set(sassDiff.url, sassDiff.newAST); - var newMapping = new WebInspector.SASSLiveSourceMap.CSSToSASSMapping(this._cssAST, sassModels); - var cssNodes = this._cssToSass.keysArray(); - for (var i = 0; i < cssNodes.length; ++i) { - var cssNode = cssNodes[i]; - var sassNode = this._cssToSass.get(cssNode); - var mappedNode = sassNode.document.url === sassDiff.url ? sassDiff.mapping.get(sassNode) : sassNode; - if (mappedNode) - newMapping.mapCssToSass(cssNode, mappedNode); - } - return newMapping; - }, - - /** - * @return {boolean} - */ - isValid: function() - { - var cssNodes = this._cssToSass.keysArray(); - for (var i = 0; i < cssNodes.length; ++i) { - var cssNode = cssNodes[i]; - if (!cssNode.parent || !(cssNode.parent instanceof WebInspector.SASSSupport.Property)) - continue; - if (cssNode !== cssNode.parent.name) - continue; - var sassNode = this._cssToSass.get(cssNode); - if (sassNode && cssNode.text.trim() !== sassNode.text.trim()) - return false; - } - return true; - } -}
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js b/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js index 0ed1943..d6744a5 100644 --- a/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js +++ b/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js
@@ -5,14 +5,14 @@ WebInspector.SASSSupport = {} /** - * @param {!WebInspector.CSSParser} parser + * @param {!WebInspector.CSSParserService} cssParserService * @param {string} url * @param {string} text * @return {!Promise<!WebInspector.SASSSupport.AST>} */ -WebInspector.SASSSupport.parseCSS = function(parser, url, text) +WebInspector.SASSSupport.parseCSS = function(cssParserService, url, text) { - return parser.parsePromise(text) + return cssParserService.parseCSS(text) .then(onParsed); /** @@ -35,27 +35,27 @@ var property = new WebInspector.SASSSupport.Property(document, name, value, WebInspector.TextRange.fromObject(cssProperty.range), !!cssProperty.disabled); properties.push(property); } - rules.push(new WebInspector.SASSSupport.Rule(document, rule.selectorText, properties)); + rules.push(new WebInspector.SASSSupport.Rule(document, rule.selectorText, WebInspector.TextRange.fromObject(rule.styleRange), properties)); } return new WebInspector.SASSSupport.AST(document, rules); } } /** + * @param {!WebInspector.TokenizerFactory} tokenizerFactory * @param {string} url * @param {string} text - * @param {!WebInspector.TokenizerFactory} tokenizerFactory * @return {!WebInspector.SASSSupport.AST} */ -WebInspector.SASSSupport.parseSCSS = function(url, text, tokenizerFactory) +WebInspector.SASSSupport.parseSCSS = function(tokenizerFactory, url, text) { var document = new WebInspector.SASSSupport.ASTDocument(url, text); var result = WebInspector.SASSSupport._innerParseSCSS(document, tokenizerFactory); var rules = [ - new WebInspector.SASSSupport.Rule(document, "variables", result.variables), - new WebInspector.SASSSupport.Rule(document, "properties", result.properties), - new WebInspector.SASSSupport.Rule(document, "mixins", result.mixins) + new WebInspector.SASSSupport.Rule(document, "variables", WebInspector.TextRange.createFromLocation(0, 0), result.variables), + new WebInspector.SASSSupport.Rule(document, "properties", WebInspector.TextRange.createFromLocation(0, 0), result.properties), + new WebInspector.SASSSupport.Rule(document, "mixins", WebInspector.TextRange.createFromLocation(0, 0), result.mixins) ]; return new WebInspector.SASSSupport.AST(document, rules); @@ -70,7 +70,7 @@ VariableValue: "VariableValue", MixinName: "MixinName", MixinValue: "MixinValue", - Media: "Media", + Media: "Media" } /** @@ -139,7 +139,9 @@ } break; case States.VariableName: - if (tokenValue === ")" && tokenType === UndefTokenType) { + if (tokenValue === "}" && tokenType === UndefTokenType) { + state = States.Initial; + } else if (tokenValue === ")" && tokenType === UndefTokenType) { state = States.Initial; } else if (tokenValue === ":" && tokenType === UndefTokenType) { state = States.VariableValue; @@ -416,13 +418,15 @@ * @extends {WebInspector.SASSSupport.Node} * @param {!WebInspector.SASSSupport.ASTDocument} document * @param {string} selector + * @param {!WebInspector.TextRange} styleRange * @param {!Array<!WebInspector.SASSSupport.Property>} properties */ -WebInspector.SASSSupport.Rule = function(document, selector, properties) +WebInspector.SASSSupport.Rule = function(document, selector, styleRange, properties) { WebInspector.SASSSupport.Node.call(this, document); this.selector = selector; this.properties = properties; + this.styleRange = styleRange; for (var i = 0; i < this.properties.length; ++i) this.properties[i].parent = this; @@ -439,7 +443,7 @@ var properties = []; for (var i = 0; i < this.properties.length; ++i) properties.push(this.properties[i].clone(document)); - return new WebInspector.SASSSupport.Rule(document, this.selector, properties); + return new WebInspector.SASSSupport.Rule(document, this.selector, this.styleRange.clone(), properties); }, /** @@ -461,27 +465,49 @@ }, /** + * @param {!Array<string>} nameTexts + * @param {!Array<string>} valueTexts + * @param {!Array<boolean>} disabledStates + * @param {!WebInspector.SASSSupport.Property} anchorProperty + * @param {boolean} insertBefore + * @return {!Array<!WebInspector.SASSSupport.Property>} + */ + insertProperties: function(nameTexts, valueTexts, disabledStates, anchorProperty, insertBefore) + { + console.assert(this.properties.length, "Cannot insert in empty rule."); + console.assert(nameTexts.length === valueTexts.length && valueTexts.length === disabledStates.length, "Input array should be of the same size."); + + this._addTrailingSemicolon(); + var newProperties = []; + var index = this.properties.indexOf(anchorProperty); + for (var i = 0; i < nameTexts.length; ++i) { + var nameText = nameTexts[i]; + var valueText = valueTexts[i]; + var disabled = disabledStates[i]; + this.document.edits.push(this._insertPropertyEdit(nameText, valueText, disabled, anchorProperty, insertBefore)); + + var name = new WebInspector.SASSSupport.TextNode(this.document, nameText, WebInspector.TextRange.createFromLocation(0, 0)); + var value = new WebInspector.SASSSupport.TextNode(this.document, valueText, WebInspector.TextRange.createFromLocation(0, 0)); + var newProperty = new WebInspector.SASSSupport.Property(this.document, name, value, WebInspector.TextRange.createFromLocation(0, 0), disabled); + + this.properties.splice(insertBefore ? index + i : index + i + 1, 0, newProperty); + newProperty.parent = this; + + newProperties.push(newProperty); + } + return newProperties; + }, + + /** * @param {string} nameText * @param {string} valueText * @param {boolean} disabled * @param {!WebInspector.SASSSupport.Property} anchorProperty * @param {boolean} insertBefore - * @return {!WebInspector.SASSSupport.Property} + * @return {!WebInspector.SourceEdit} */ - insertProperty: function(nameText, valueText, disabled, anchorProperty, insertBefore) + _insertPropertyEdit: function(nameText, valueText, disabled, anchorProperty, insertBefore) { - console.assert(this.properties.length, "Cannot insert in empty rule."); - - this._addTrailingSemicolon(); - - var name = new WebInspector.SASSSupport.TextNode(this.document, nameText, WebInspector.TextRange.createFromLocation(10, 0)); - var value = new WebInspector.SASSSupport.TextNode(this.document, valueText, WebInspector.TextRange.createFromLocation(10, 0)); - var newProperty = new WebInspector.SASSSupport.Property(this.document, name, value, WebInspector.TextRange.createFromLocation(10, 0), disabled); - - var index = this.properties.indexOf(anchorProperty); - this.properties.splice(insertBefore ? index : index + 1, 0, newProperty); - newProperty.parent = this; - var oldRange = insertBefore ? anchorProperty.range.collapseToStart() : anchorProperty.range.collapseToEnd(); var indent = (new WebInspector.TextRange(anchorProperty.range.startLine, 0, anchorProperty.range.startLine, anchorProperty.range.startColumn)).extract(this.document.text); if (!/^\s+$/.test(indent)) indent = ""; @@ -491,12 +517,11 @@ var rightComment = disabled ? " */" : ""; if (insertBefore) { - newText = String.sprintf("%s%s: %s;%s\n%s", leftComment, newProperty.name.text, newProperty.value.text, rightComment, indent); + newText = String.sprintf("%s%s: %s;%s\n%s", leftComment, nameText, valueText, rightComment, indent); } else { - newText = String.sprintf("\n%s%s%s: %s;%s", indent, leftComment, newProperty.name.text, newProperty.value.text, rightComment); + newText = String.sprintf("\n%s%s%s: %s;%s", indent, leftComment, nameText, valueText, rightComment); } - this.document.edits.push(new WebInspector.SourceEdit(this.document.url, oldRange, "", newText)); - return newProperty; + return new WebInspector.SourceEdit(this.document.url, oldRange, "", newText); }, __proto__: WebInspector.SASSSupport.Node.prototype
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/SASSWorkspaceAdapter.js b/third_party/WebKit/Source/devtools/front_end/sass/SASSWorkspaceAdapter.js deleted file mode 100644 index 5dc3292..0000000 --- a/third_party/WebKit/Source/devtools/front_end/sass/SASSWorkspaceAdapter.js +++ /dev/null
@@ -1,461 +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. - -/** - * @constructor - * @param {!WebInspector.CSSStyleModel} cssModel - * @param {!WebInspector.Workspace} workspace - * @param {!WebInspector.NetworkMapping} networkMapping - */ -WebInspector.SASSWorkspaceAdapter = function(cssModel, workspace, networkMapping) -{ - this._workspace = workspace; - this._networkMapping = networkMapping; - this._cssModel = cssModel; - - /** @type {!Map<string, number>} */ - this._versions = new Map(); - /** @type {!Map<string, !Promise<boolean>>} */ - this._awaitingPromises = new Map(); - /** @type {!Map<string, function(boolean)>} */ - this._awaitingFulfills = new Map(); - - /** @type {!Multimap<string, !WebInspector.SourceMapTracker>} */ - this._urlToTrackers = new Multimap(); - /** @type {!Set<string>} */ - this._cssURLs = new Set(); - - this._eventListeners = [ - this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this), - this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this), - this._workspace.addEventListener(WebInspector.Workspace.Events.WorkingCopyChanged, this._uiSourceCodeChanged, this), - this._workspace.addEventListener(WebInspector.Workspace.Events.WorkingCopyCommitted, this._uiSourceCodeChanged, this), - this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetAdded, this), - this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetRemoved, this), - this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this) - ]; -} - -/** - * @constructor - * @param {string} url - * @param {number} version - * @param {string} text - */ -WebInspector.SASSWorkspaceAdapter.ContentResponse = function(url, version, text) -{ - this.url = url; - this.version = version; - this.text = text; -} - -WebInspector.SASSWorkspaceAdapter.prototype = { - /** - * @param {!WebInspector.SourceMap} sourceMap - * @return {!WebInspector.SourceMapTracker} - */ - trackSources: function(sourceMap) - { - var cssURL = sourceMap.compiledURL(); - this._cssURLs.add(cssURL); - - var allSources = new Set(sourceMap.sources().concat(cssURL)); - for (var sourceURL of allSources) { - if (this._versions.has(sourceURL)) - continue; - this._versions.set(sourceURL, 1); - var promise = new Promise(fulfill => this._awaitingFulfills.set(sourceURL, fulfill)); - this._awaitingPromises.set(sourceURL, promise); - var contentProvider = sourceURL === cssURL ? this._headersForURL(sourceURL).peekLast() : this._sassUISourceCode(sourceURL); - if (contentProvider) - this._contentProviderAdded(sourceURL); - } - - var tracker = new WebInspector.SourceMapTracker(this, sourceMap); - for (var sourceURL of tracker.allURLs()) - this._urlToTrackers.set(sourceURL, tracker); - return tracker; - }, - - /** - * @param {!WebInspector.SourceMapTracker} tracker - */ - _stopTrackSources: function(tracker) - { - for (var sourceURL of tracker.allURLs()) { - this._urlToTrackers.remove(sourceURL, tracker); - if (!this._urlToTrackers.has(sourceURL)) { - this._awaitingFulfills.get(sourceURL).call(null, false); - this._awaitingFulfills.delete(sourceURL); - this._awaitingPromises.delete(sourceURL); - this._versions.delete(sourceURL); - this._cssURLs.delete(sourceURL); - } - } - }, - - /** - * @param {string} url - * @return {?WebInspector.UISourceCode} - */ - _sassUISourceCode: function(url) - { - return this._networkMapping.uiSourceCodeForURLForAnyTarget(url); - }, - - /** - * @param {string} url - * @return {!Array<!WebInspector.CSSStyleSheetHeader>} - */ - _headersForURL: function(url) - { - return this._cssModel.styleSheetIdsForURL(url) - .map(styleSheetId => this._cssModel.styleSheetHeaderForId(styleSheetId)); - }, - - /** - * @param {string} url - */ - _contentProviderAdded: function(url) - { - this._awaitingFulfills.get(url).call(null, true); - }, - - /** - * @param {string} url - */ - _contentProviderRemoved: function(url) - { - var trackers = new Set(this._urlToTrackers.get(url)); - for (var tracker of trackers) - tracker.dispose(); - }, - - /** - * @param {string} url - * @return {boolean} - */ - _isSASSURL: function(url) - { - return this._versions.has(url) && !this._cssURLs.has(url); - }, - - /** - * @param {!WebInspector.Event} event - */ - _uiSourceCodeAdded: function(event) - { - var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data); - var url = this._networkMapping.networkURL(uiSourceCode); - if (!this._isSASSURL(url)) - return; - this._contentProviderAdded(url); - }, - - /** - * @param {!WebInspector.Event} event - */ - _uiSourceCodeRemoved: function(event) - { - var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data); - var url = this._networkMapping.networkURL(uiSourceCode); - if (!this._isSASSURL(url)) - return; - this._contentProviderRemoved(url); - }, - - /** - * @param {!WebInspector.Event} event - */ - _styleSheetAdded: function(event) - { - var styleSheetHeader = /** @type {!WebInspector.CSSStyleSheetHeader} */(event.data); - var url = styleSheetHeader.sourceURL; - if (!this._cssURLs.has(url)) - return; - this._contentProviderAdded(url); - }, - - /** - * @param {!WebInspector.Event} event - */ - _styleSheetRemoved: function(event) - { - var styleSheetHeader = /** @type {!WebInspector.CSSStyleSheetHeader} */(event.data); - var url = styleSheetHeader.sourceURL; - if (!this._cssURLs.has(url)) - return; - var headers = this._headersForURL(url); - if (headers.length) - return; - this._contentProviderRemoved(url); - }, - - /** - * @param {!WebInspector.Event} event - */ - _uiSourceCodeChanged: function(event) - { - var uiSourceCode = /** @type {!WebInspector.UISourceCode} */(event.data.uiSourceCode); - var url = this._networkMapping.networkURL(uiSourceCode); - if (!this._isSASSURL(url)) - return; - this._newContentAvailable(url); - }, - - /** - * @param {!WebInspector.Event} event - */ - _styleSheetChanged: function(event) - { - var styleSheetId = /** @type {!CSSAgent.StyleSheetId} */(event.data.styleSheetId); - var styleSheetHeader = this._cssModel.styleSheetHeaderForId(styleSheetId); - var url = styleSheetHeader.sourceURL; - if (!this._cssURLs.has(url)) - return; - this._newContentAvailable(url); - }, - - /** - * @param {string} url - */ - _newContentAvailable: function(url) - { - console.assert(this._versions.has(url), "The '" + url + "' is not tracked.") - var newVersion = this._versions.get(url) + 1; - this._versions.set(url, newVersion); - for (var tracker of this._urlToTrackers.get(url)) - tracker._newContentAvailable(url, newVersion); - }, - - /** - * @param {string} url - * @return {number} - */ - _urlVersion: function(url) - { - var version = this._versions.get(url); - console.assert(version, "The '" + url + "' is not tracked.") - return version || 0; - }, - - /** - * @param {string} url - * @return {!Promise<?WebInspector.SASSWorkspaceAdapter.ContentResponse>} - */ - _getContent: function(url) - { - console.assert(this._awaitingPromises.has(url), "The '" + url + "' is not tracked.") - return this._awaitingPromises.get(url) - .then(onContentProviderResolved.bind(this)); - - /** - * @param {boolean} success - * @return {!Promise<?WebInspector.SASSWorkspaceAdapter.ContentResponse>} - * @this {WebInspector.SASSWorkspaceAdapter} - */ - function onContentProviderResolved(success) - { - if (!success) - return Promise.resolve(/** @type {?WebInspector.SASSWorkspaceAdapter.ContentResponse} */(null)); - var contentProvider = this._cssURLs.has(url) ? this._headersForURL(url).peekLast() : this._sassUISourceCode(url); - if (!contentProvider) - return Promise.resolve(/** @type {?WebInspector.SASSWorkspaceAdapter.ContentResponse} */(null)); - return contentProvider.requestContent() - .then(text => new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, /** @type {number} */(this._versions.get(url)), text || "")); - } - }, - - /** - * @param {string} url - * @param {string} text - * @return {?WebInspector.SASSWorkspaceAdapter.ContentResponse} - */ - _setSASSText: function(url, text) - { - console.assert(this._isSASSURL(url), "The url '" + url + "' should be a tracked SASS url"); - var uiSourceCode = this._sassUISourceCode(url); - if (!uiSourceCode) - return null; - setImmediate(() => uiSourceCode.addRevision(text)); - var futureVersion = this._versions.get(url) + 1; - return new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, futureVersion, text); - }, - - /** - * @param {string} url - * @param {string} text - * @param {!Array<!WebInspector.SourceEdit>} cssEdits - * @return {?WebInspector.SASSWorkspaceAdapter.ContentResponse} - */ - _setCSSText: function(url, text, cssEdits) - { - console.assert(this._cssURLs.has(url), "The url '" + url + "' should be a tracked CSS url"); - var headers = this._headersForURL(url); - if (!headers.length) - return null; - for (var i = 0; i < headers.length; ++i) - this._cssModel.setStyleSheetText(headers[i].id, text, true); - for (var i = cssEdits.length - 1; i >= 0; --i) { - var edit = cssEdits[i]; - var oldRange = edit.oldRange; - var newRange = edit.newRange(); - for (var j = 0; j < headers.length; ++j) { - this._cssModel.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.ExternalRangeEdit, { - styleSheetId: headers[j].id, - oldRange: oldRange, - newRange: newRange - }); - } - } - var futureVersion = this._versions.get(url) + headers.length; - return new WebInspector.SASSWorkspaceAdapter.ContentResponse(url, futureVersion, text); - } -} - -/** - * @constructor - * @extends {WebInspector.Object} - * @param {!WebInspector.SASSWorkspaceAdapter} adapter - * @param {!WebInspector.SourceMap} sourceMap - */ -WebInspector.SourceMapTracker = function(adapter, sourceMap) -{ - WebInspector.Object.call(this); - this._adapter = adapter; - this._sourceMap = sourceMap; - this._cssURL = sourceMap.compiledURL(); - this._sassURLs = sourceMap.sources().slice(); - this._allURLs = this._sassURLs.concat(this._cssURL); - this._terminated = false; - this._versions = new Map(); - for (var url of this._allURLs) - this._versions.set(url, adapter._urlVersion(url)); -} - -/** @enum {string} */ -WebInspector.SourceMapTracker.Events = { - SourceChanged: "SourceChanged", - TrackingStopped: "TrackingStopped" -} - -WebInspector.SourceMapTracker.prototype = { - /** - * @return {!WebInspector.SourceMap} - */ - sourceMap: function() - { - return this._sourceMap; - }, - - /** - * @return {!Array<string>} - */ - allURLs: function() - { - return this._allURLs; - }, - - /** - * @return {string} - */ - cssURL: function() - { - return this._cssURL; - }, - - /** - * @return {!Array<string>} - */ - sassURLs: function() - { - return this._sassURLs; - }, - - /** - * @return {boolean} - */ - isOutdated: function() - { - if (this._terminated) - return true; - for (var url of this._allURLs) { - if (this._adapter._urlVersion(url) > this._versions.get(url)) - return true; - } - return false; - }, - - /** - * @param {string} text - * @param {!Array<!WebInspector.SourceEdit>} edits - * @return {boolean} - */ - setCSSText: function(text, edits) - { - if (this._terminated || this.isOutdated()) - return false; - var result = this._adapter._setCSSText(this._cssURL, text, edits); - this._handleContentResponse(result); - return !!result; - }, - - /** - * @param {string} url - * @param {string} text - * @return {boolean} - */ - setSASSText: function(url, text) - { - if (this._terminated || this.isOutdated()) - return false; - var result = this._adapter._setSASSText(url, text); - this._handleContentResponse(result); - return !!result; - }, - - /** - * @param {?WebInspector.SASSWorkspaceAdapter.ContentResponse} contentResponse - * @return {?string} - */ - _handleContentResponse: function(contentResponse) - { - if (!contentResponse) - return null; - this._versions.set(contentResponse.url, contentResponse.version); - return contentResponse.text; - }, - - /** - * @param {string} url - * @return {!Promise<string>} - */ - content: function(url) - { - return this._adapter._getContent(url) - .then(this._handleContentResponse.bind(this)) - .then(text => text || ""); - }, - - dispose: function() - { - if (this._terminated) - return; - this._terminated = true; - this._adapter._stopTrackSources(this); - this.dispatchEventToListeners(WebInspector.SourceMapTracker.Events.TrackingStopped); - }, - - /** - * @param {string} url - * @param {number} newVersion - */ - _newContentAvailable: function(url, newVersion) - { - if (this._versions.get(url) < newVersion) - this.dispatchEventToListeners(WebInspector.SourceMapTracker.Events.SourceChanged, url); - }, - - __proto__: WebInspector.Object.prototype -}
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/module.json b/third_party/WebKit/Source/devtools/front_end/sass/module.json index d4704ba5..2c9da98 100644 --- a/third_party/WebKit/Source/devtools/front_end/sass/module.json +++ b/third_party/WebKit/Source/devtools/front_end/sass/module.json
@@ -1,8 +1,8 @@ { - "dependencies": ["platform", "common", "diff", "sdk", "workspace", "bindings"], + "dependencies": ["platform", "common", "diff", "sdk"], "scripts": [ "SASSSupport.js", - "SASSWorkspaceAdapter.js", - "SASSLiveSourceMap.js" + "ASTService.js", + "ASTSourceMap.js" ] }
diff --git a/third_party/WebKit/Source/devtools/front_end/script_formatter_worker/ScriptFormatterWorker.js b/third_party/WebKit/Source/devtools/front_end/script_formatter_worker/ScriptFormatterWorker.js index 9af72ef5f..df53144 100644 --- a/third_party/WebKit/Source/devtools/front_end/script_formatter_worker/ScriptFormatterWorker.js +++ b/third_party/WebKit/Source/devtools/front_end/script_formatter_worker/ScriptFormatterWorker.js
@@ -230,6 +230,7 @@ case FormatterWorker.CSSParserStates.Selector: if (tokenValue === "{" && tokenType === UndefTokenType) { rule.selectorText = rule.selectorText.trim(); + rule.styleRange = createRange(lineNumber, newColumn); state = FormatterWorker.CSSParserStates.Style; } else { rule.selectorText += tokenValue; @@ -254,6 +255,8 @@ }; state = FormatterWorker.CSSParserStates.PropertyName; } else if (tokenValue === "}" && tokenType === UndefTokenType) { + rule.styleRange.endLine = lineNumber; + rule.styleRange.endColumn = column; rules.push(rule); state = FormatterWorker.CSSParserStates.Initial; } else if (tokenType["comment"]) { @@ -304,6 +307,8 @@ property.range.endColumn = tokenValue === ";" ? newColumn : column; rule.properties.push(property); if (tokenValue === "}") { + rule.styleRange.endLine = lineNumber; + rule.styleRange.endColumn = column; rules.push(rule); state = FormatterWorker.CSSParserStates.Initial; } else {
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/CSSParser.js b/third_party/WebKit/Source/devtools/front_end/sdk/CSSParser.js index 63376821d..3096a7e 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/CSSParser.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/CSSParser.js
@@ -66,6 +66,7 @@ if (this._worker) { this._worker.terminate(); delete this._worker; + this._runFinishedCallback([]); } }, @@ -115,8 +116,18 @@ _onFinishedParsing: function() { this._unlock(); - if (this._finishedCallback) - this._finishedCallback(this._rules); + this._runFinishedCallback(this._rules); + }, + + /** + * @param {!Array<!WebInspector.CSSRule>} rules + */ + _runFinishedCallback: function(rules) + { + var callback = this._finishedCallback; + delete this._finishedCallback; + if (callback) + callback.call(null, rules); }, __proto__: WebInspector.Object.prototype, @@ -128,9 +139,21 @@ WebInspector.CSSParser.DataChunk; /** - * @typedef {{selectorText: string, lineNumber: number, columnNumber: number, properties: !Array.<!WebInspector.CSSParser.Property>}} + * @constructor */ -WebInspector.CSSParser.StyleRule; +WebInspector.CSSParser.StyleRule = function() +{ + /** @type {string} */ + this.selectorText; + /** @type {!WebInspector.CSSParser.Range} */ + this.styleRange; + /** @type {number} */ + this.lineNumber; + /** @type {number} */ + this.columnNumber; + /** @type {!Array.<!WebInspector.CSSParser.Property>} */ + this.properties; +} /** * @typedef {{atRule: string, lineNumber: number, columnNumber: number}} @@ -165,3 +188,76 @@ /** @type {(boolean|undefined)} */ this.disabled; } + +/** + * @constructor + */ +WebInspector.CSSParserService = function() +{ + this._cssParser = null; + this._cssRequests = []; + this._terminated = false; +} + +WebInspector.CSSParserService.prototype = { + /** + * @param {string} text + * @return {!Promise<!Array.<!WebInspector.CSSParser.Rule>>} + */ + parseCSS: function(text) + { + console.assert(!this._terminated, "Illegal call parseCSS on terminated CSSParserService."); + if (!this._cssParser) + this._cssParser = new WebInspector.CSSParser(); + var request = new WebInspector.CSSParserService.ParseRequest(text); + this._cssRequests.push(request); + this._maybeParseCSS(); + return request.parsedPromise; + }, + + _maybeParseCSS: function() + { + if (this._terminated || this._isParsingCSS || !this._cssRequests.length) + return; + this._isParsingCSS = true; + var request = this._cssRequests.shift(); + this._cssParser.parsePromise(request.text) + .catchException(/** @type {!Array.<!WebInspector.CSSParser.Rule>} */([])) + .then(onCSSParsed.bind(this)); + + /** + * @param {!Array.<!WebInspector.CSSParser.Rule>} rules + * @this {WebInspector.CSSParserService} + */ + function onCSSParsed(rules) + { + request.parsedCallback.call(null, rules); + this._isParsingCSS = false; + this._maybeParseCSS(); + } + }, + + dispose: function() + { + if (this._terminated) + return; + this._terminated = true; + if (this._cssParser) + this._cssParser.dispose(); + for (var request of this._cssRequests) + request.parsedCallback.call(null, /** @type {!Array.<!WebInspector.CSSParser.Rule>} */([])); + this._cssRequests = []; + }, +} + +/** + * @constructor + * @param {string} text + */ +WebInspector.CSSParserService.ParseRequest = function(text) +{ + this.text = text; + /** @type {function(!Array.<!WebInspector.CSSParser.Rule>)} */ + this.parsedCallback; + this.parsedPromise = new Promise(fulfill => this.parsedCallback = fulfill); +}
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js index 06759431..cc46da3 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/CSSStyleModel.js
@@ -70,8 +70,7 @@ PseudoStateForced: "PseudoStateForced", StyleSheetAdded: "StyleSheetAdded", StyleSheetChanged: "StyleSheetChanged", - StyleSheetRemoved: "StyleSheetRemoved", - ExternalRangeEdit: "ExternalRangeEdit" + StyleSheetRemoved: "StyleSheetRemoved" } WebInspector.CSSStyleModel.MediaTypes = ["all", "braille", "embossed", "handheld", "print", "projection", "screen", "speech", "tty", "tv"]; @@ -80,6 +79,48 @@ WebInspector.CSSStyleModel.prototype = { /** + * @param {!Array<!CSSAgent.StyleSheetId>} styleSheetIds + * @param {!Array<!WebInspector.TextRange>} ranges + * @param {!Array<string>} texts + * @param {boolean} majorChange + * @return {!Promise<?Array<!CSSAgent.CSSStyle>>} + */ + setStyleTexts: function(styleSheetIds, ranges, texts, majorChange) + { + /** + * @param {?Protocol.Error} error + * @param {?Array<!CSSAgent.CSSStyle>} stylePayloads + * @return {?Array<!CSSAgent.CSSStyle>} + * @this {WebInspector.CSSStyleModel} + */ + function parsePayload(error, stylePayloads) + { + if (error || !stylePayloads || !stylePayloads.length) + return null; + + if (majorChange) + this._domModel.markUndoableState(); + var uniqueIDs = new Set(styleSheetIds); + for (var styleSheetId of uniqueIDs) + this._fireStyleSheetChanged(styleSheetId); + return stylePayloads; + } + + console.assert(styleSheetIds.length === ranges.length && ranges.length === texts.length, "Array lengths must be equal"); + var edits = []; + for (var i = 0; i < styleSheetIds.length; ++i) { + edits.push({ + styleSheetId: styleSheetIds[i], + range: ranges[i].serializeToObject(), + text: texts[i] + }); + } + + return this._agent.setStyleTexts(edits, parsePayload.bind(this)) + .catchException(/** @type {?Array<!CSSAgent.CSSStyle>} */(null)); + }, + + /** * @return {!Promise.<!Array.<!WebInspector.CSSMedia>>} */ mediaQueriesPromise: function() @@ -946,28 +987,21 @@ */ setText: function(text, majorChange) { - if (!this.styleSheetId) - return Promise.resolve(false); - /** - * @param {?Protocol.Error} error - * @param {?CSSAgent.CSSStyle} stylePayload + * @param {?Array<!CSSAgent.CSSStyle>} stylePayloads * @return {boolean} * @this {WebInspector.CSSStyleDeclaration} */ - function parsePayload(error, stylePayload) + function onPayload(stylePayloads) { - if (error || !stylePayload) + if (!stylePayloads) return false; - - if (majorChange) - this._cssModel._domModel.markUndoableState(); - this._reinitialize(stylePayload); - this._cssModel._fireStyleSheetChanged(this.styleSheetId); + this._reinitialize(stylePayloads[0]); return true; } - return this._cssModel._agent.setStyleText(this.styleSheetId, this.range.serializeToObject(), text, parsePayload.bind(this)) + return this._cssModel.setStyleTexts([this.styleSheetId], [this.range], [text], majorChange) + .then(onPayload.bind(this)) .catchException(false); }, @@ -1992,13 +2026,16 @@ _trimSourceURL: function(text) { var sourceURLIndex = text.lastIndexOf("/*# sourceURL="); - if (sourceURLIndex === -1) - return text; + if (sourceURLIndex === -1) { + sourceURLIndex = text.lastIndexOf("/*@ sourceURL="); + if (sourceURLIndex === -1) + return text; + } var sourceURLLineIndex = text.lastIndexOf("\n", sourceURLIndex); if (sourceURLLineIndex === -1) return text; var sourceURLLine = text.substr(sourceURLLineIndex + 1).split("\n", 1)[0]; - var sourceURLRegex = /[\040\t]*\/\*# sourceURL=[\040\t]*([^\s]*)[\040\t]*\*\/[\040\t]*$/; + var sourceURLRegex = /[\040\t]*\/\*[#@] sourceURL=[\040\t]*([^\s]*)[\040\t]*\*\/[\040\t]*$/; if (sourceURLLine.search(sourceURLRegex) === -1) return text; return text.substr(0, sourceURLLineIndex) + text.substr(sourceURLLineIndex + sourceURLLine.length + 1);
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js index 91933f12..f5b6c16 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js
@@ -192,11 +192,12 @@ * @param {number} executionContextId * @param {string=} objectGroup * @param {boolean=} doNotPauseOnExceptionsAndMuteConsole + * @param {boolean=} includeCommandLineAPI * @param {function(?RuntimeAgent.RemoteObject, ?RuntimeAgent.ExceptionDetails=)=} callback */ - runScript: function(scriptId, executionContextId, objectGroup, doNotPauseOnExceptionsAndMuteConsole, callback) + runScript: function(scriptId, executionContextId, objectGroup, doNotPauseOnExceptionsAndMuteConsole, includeCommandLineAPI, callback) { - this._agent.runScript(scriptId, executionContextId, objectGroup, doNotPauseOnExceptionsAndMuteConsole, innerCallback); + this._agent.runScript(scriptId, executionContextId, objectGroup, doNotPauseOnExceptionsAndMuteConsole, includeCommandLineAPI, innerCallback); /** * @param {?Protocol.Error} error
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/Script.js b/third_party/WebKit/Source/devtools/front_end/sdk/Script.js index 2bb24730..43a003d 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/Script.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/Script.js
@@ -64,7 +64,7 @@ SourceMapURLAdded: "SourceMapURLAdded" } -WebInspector.Script.sourceURLRegex = /^[\040\t]*\/\/# sourceURL=\s*(\S*?)\s*$/m; +WebInspector.Script.sourceURLRegex = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m; /** * @param {string} source @@ -73,8 +73,11 @@ WebInspector.Script._trimSourceURLComment = function(source) { var sourceURLIndex = source.lastIndexOf("//# sourceURL="); - if (sourceURLIndex === -1) - return source; + if (sourceURLIndex === -1) { + sourceURLIndex = source.lastIndexOf("//@ sourceURL="); + if (sourceURLIndex === -1) + return source; + } var sourceURLLineIndex = source.lastIndexOf("\n", sourceURLIndex); if (sourceURLLineIndex === -1) return source;
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js index ff3d0ff0..667dc1a 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js
@@ -227,27 +227,28 @@ else this._backingStorage.appendString(stringPayload); - if (payload.ph !== WebInspector.TracingModel.Phase.Metadata) { - var timestamp = payload.ts / 1000; - // We do allow records for unrelated threads to arrive out-of-order, - // so there's a chance we're getting records from the past. - if (timestamp && (!this._minimumRecordTime || timestamp < this._minimumRecordTime)) - this._minimumRecordTime = timestamp; - var endTimeStamp = (payload.ts + (payload.dur || 0)) / 1000; - this._maximumRecordTime = Math.max(this._maximumRecordTime, endTimeStamp); - var event = process._addEvent(payload); - if (!event) - return; - // Build async event when we've got events from all threads & processes, so we can sort them and process in the - // chronological order. However, also add individual async events to the thread flow (above), so we can easily - // display them on the same chart as other events, should we choose so. - if (WebInspector.TracingModel.isAsyncPhase(payload.ph)) - this._asyncEvents.push(event); - event._setBackingStorage(backingStorage); - if (event.hasCategory(WebInspector.TracingModel.DevToolsMetadataEventCategory)) - this._devToolsMetadataEvents.push(event); + var timestamp = payload.ts / 1000; + // We do allow records for unrelated threads to arrive out-of-order, + // so there's a chance we're getting records from the past. + if (timestamp && (!this._minimumRecordTime || timestamp < this._minimumRecordTime)) + this._minimumRecordTime = timestamp; + var endTimeStamp = (payload.ts + (payload.dur || 0)) / 1000; + this._maximumRecordTime = Math.max(this._maximumRecordTime, endTimeStamp); + var event = process._addEvent(payload); + if (!event) return; - } + // Build async event when we've got events from all threads & processes, so we can sort them and process in the + // chronological order. However, also add individual async events to the thread flow (above), so we can easily + // display them on the same chart as other events, should we choose so. + if (WebInspector.TracingModel.isAsyncPhase(payload.ph)) + this._asyncEvents.push(event); + event._setBackingStorage(backingStorage); + if (event.hasCategory(WebInspector.TracingModel.DevToolsMetadataEventCategory)) + this._devToolsMetadataEvents.push(event); + + if (payload.ph !== WebInspector.TracingModel.Phase.Metadata) + return; + switch (payload.name) { case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex: process._setSortIndex(payload.args["sort_index"]);
diff --git a/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js b/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js index ed412cc5..9c350e3 100644 --- a/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js +++ b/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js
@@ -260,7 +260,7 @@ _runScript: function(scriptId, executionContext, sourceURL) { var target = executionContext.target(); - target.runtimeModel.runScript(scriptId, executionContext.id, "console", false, runCallback.bind(this, target)); + target.runtimeModel.runScript(scriptId, executionContext.id, "console", false, true, runCallback.bind(this, target)); /** * @param {!WebInspector.Target} target
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js index 23ddc26..588a449d 100644 --- a/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js +++ b/third_party/WebKit/Source/devtools/front_end/sources/CallStackSidebarPane.js
@@ -425,10 +425,13 @@ WebInspector.CallStackSidebarPane.CallFrame.prototype = { /** - * @param {!WebInspector.UILocation} uiLocation + * @param {!WebInspector.LiveLocation} liveLocation */ - _update: function(uiLocation) + _update: function(liveLocation) { + var uiLocation = liveLocation.uiLocation(); + if (!uiLocation) + return; var text = uiLocation.linkText(); this.setSubtitle(text.trimMiddle(30)); this.subtitleElement.title = text;
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js index b99dda4..efc8026 100644 --- a/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js +++ b/third_party/WebKit/Source/devtools/front_end/sources/SourcesPanel.js
@@ -242,11 +242,14 @@ } /** - * @param {!WebInspector.UILocation} uiLocation + * @param {!WebInspector.LiveLocation} liveLocation * @this {WebInspector.SourcesPanel} */ - function didGetUILocation(uiLocation) + function didGetUILocation(liveLocation) { + var uiLocation = liveLocation.uiLocation(); + if (!uiLocation) + return; var breakpoint = WebInspector.breakpointManager.findBreakpointOnLine(uiLocation.uiSourceCode, uiLocation.lineNumber); if (!breakpoint) return; @@ -388,10 +391,13 @@ }, /** - * @param {!WebInspector.UILocation} uiLocation + * @param {!WebInspector.LiveLocation} liveLocation */ - _executionLineChanged: function(uiLocation) + _executionLineChanged: function(liveLocation) { + var uiLocation = liveLocation.uiLocation(); + if (!uiLocation) + return; this._sourcesView.clearCurrentExecutionLine(); this._sourcesView.setExecutionLocation(uiLocation); if (window.performance.now() - this._lastModificationTime < WebInspector.SourcesPanel._lastModificationTimeout)
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js index 34e507b..245e93b 100644 --- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js +++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
@@ -1231,10 +1231,10 @@ this._splitWidget = new WebInspector.SplitWidget(false, false, "timelineFlamechartMainView", 150); this._dataProvider = new WebInspector.TimelineFlameChartDataProvider(this._model, frameModel, irModel); - this._mainView = new WebInspector.FlameChart(this._dataProvider, this, true); + this._mainView = new WebInspector.FlameChart(this._dataProvider, this); this._networkDataProvider = new WebInspector.TimelineFlameChartNetworkDataProvider(this._model); - this._networkView = new WebInspector.FlameChart(this._networkDataProvider, this, true); + this._networkView = new WebInspector.FlameChart(this._networkDataProvider, this); if (Runtime.experiments.isEnabled("networkRequestsOnTimeline")) { this._splitWidget.setMainWidget(this._mainView);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineJSProfile.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineJSProfile.js index 3d74497..ea1e23a 100644 --- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineJSProfile.js +++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineJSProfile.js
@@ -86,32 +86,12 @@ var jsFrameEvents = []; var jsFramesStack = []; var lockedJsStackDepth = []; - var currentSamplingIntervalMs = 0.1; - var lastStackSampleTime = 0; var ordinal = 0; var filterNativeFunctions = !WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get(); /** * @param {!WebInspector.TracingModel.Event} e */ - function updateSamplingInterval(e) - { - if (e.name !== WebInspector.TimelineModel.RecordType.JSSample) - return; - var time = e.startTime; - var interval = time - lastStackSampleTime; - lastStackSampleTime = time; - // Do not take into account intervals longer than 10ms. - if (!interval || interval > 10) - return; - // Use exponential moving average with a smoothing factor of 0.1 - var alpha = 0.1; - currentSamplingIntervalMs += alpha * (interval - currentSamplingIntervalMs); - } - - /** - * @param {!WebInspector.TracingModel.Event} e - */ function onStartEvent(e) { e.ordinal = ++ordinal; @@ -127,7 +107,6 @@ function onInstantEvent(e, parent) { e.ordinal = ++ordinal; - updateSamplingInterval(e); if (parent && isJSInvocationEvent(parent)) extractStackTrace(e); } @@ -157,11 +136,8 @@ console.error("Trying to truncate higher than the current stack size at " + time); depth = jsFramesStack.length; } - var minFrameDurationMs = currentSamplingIntervalMs / 2; - for (var k = 0; k < depth; ++k) + for (var k = 0; k < jsFramesStack.length; ++k) jsFramesStack[k].setEndTime(time); - for (var k = depth; k < jsFramesStack.length; ++k) - jsFramesStack[k].setEndTime(Math.min(eventEndTime(jsFramesStack[k]) + minFrameDurationMs, time)); jsFramesStack.length = depth; }
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js index 68f328c..d10b7c77 100644 --- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js +++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
@@ -403,8 +403,17 @@ */ WebInspector.TimelineTreeView.eventURL = function(event) { + var data = event.args["data"] || event.args["beginData"]; + if (data && data["url"]) + return data["url"]; var frame = WebInspector.TimelineTreeView.eventStackFrame(event); - return frame && frame["url"] || null; + while (frame) { + var url = frame["url"]; + if (url) + return url; + frame = frame.parent; + } + return null; } /**
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Toolbar.js b/third_party/WebKit/Source/devtools/front_end/ui/Toolbar.js index 64b1078..25c8230d 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/Toolbar.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/Toolbar.js
@@ -637,7 +637,7 @@ * @override * @param {!Event} event */ - _clicked: function(event) + _mouseDown: function(event) { var contextMenu = new WebInspector.ContextMenu(event, this._useSoftMenu, @@ -647,6 +647,14 @@ contextMenu.show(); }, + /** + * @override + * @param {!Event} event + */ + _clicked: function(event) + { + }, + __proto__: WebInspector.ToolbarButton.prototype }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js b/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js index 9dd3547b..26e24423 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/Tooltip.js
@@ -33,7 +33,7 @@ */ _mouseMove: function(event) { - var path = event.deepPath() ? event.deepPath() : event.path; + var path = event.path; if (!path || event.buttons !== 0) return;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/toolbar.css b/third_party/WebKit/Source/devtools/front_end/ui/toolbar.css index 15f9ba3b..45d3c61 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/toolbar.css +++ b/third_party/WebKit/Source/devtools/front_end/ui/toolbar.css
@@ -128,7 +128,7 @@ /* Button */ -.toolbar-button:disabled .toolbar-glyph { +.toolbar-button:disabled { opacity: 0.5; }
diff --git a/third_party/WebKit/Source/devtools/front_end/ui_lazy/FlameChart.js b/third_party/WebKit/Source/devtools/front_end/ui_lazy/FlameChart.js index 3697986..5d7c9606 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui_lazy/FlameChart.js +++ b/third_party/WebKit/Source/devtools/front_end/ui_lazy/FlameChart.js
@@ -54,15 +54,13 @@ * @extends {WebInspector.HBox} * @param {!WebInspector.FlameChartDataProvider} dataProvider * @param {!WebInspector.FlameChartDelegate} flameChartDelegate - * @param {boolean} isTopDown */ -WebInspector.FlameChart = function(dataProvider, flameChartDelegate, isTopDown) +WebInspector.FlameChart = function(dataProvider, flameChartDelegate) { WebInspector.HBox.call(this, true); this.registerRequiredCSS("ui_lazy/flameChart.css"); this.contentElement.classList.add("flame-chart-main-pane"); this._flameChartDelegate = flameChartDelegate; - this._isTopDown = isTopDown; this._calculator = new WebInspector.FlameChart.Calculator(); @@ -93,19 +91,18 @@ this._windowLeft = 0.0; this._windowRight = 1.0; - this._windowWidth = 1.0; this._timeWindowLeft = 0; this._timeWindowRight = Infinity; this._barHeight = dataProvider.barHeight(); - this._barHeightDelta = this._isTopDown ? -this._barHeight : this._barHeight; this._paddingLeft = this._dataProvider.paddingLeft(); - this._markerPadding = 2; - this._markerRadius = this._barHeight / 2 - this._markerPadding; + var markerPadding = 2; + this._markerRadius = this._barHeight / 2 - markerPadding; this._highlightedMarkerIndex = -1; this._highlightedEntryIndex = -1; this._selectedEntryIndex = -1; this._rawTimelineDataLength = 0; - this._textWidth = {}; + /** @type {!Map<string,!Map<string,number>>} */ + this._textWidth = new Map(); this._lastMouseOffsetX = 0; } @@ -545,10 +542,10 @@ var minEntryTimeWindow = Math.min(entryTotalTime, timeRight - timeLeft); var y = this._levelToHeight(timelineData.entryLevels[entryIndex]); - if (y < this._vScrollElement.scrollTop) + if (this._vScrollElement.scrollTop > y) this._vScrollElement.scrollTop = y; - else if (y > this._vScrollElement.scrollTop + this._offsetHeight + this._barHeightDelta) - this._vScrollElement.scrollTop = y - this._offsetHeight - this._barHeightDelta; + else if (this._vScrollElement.scrollTop < y - this._offsetHeight + this._barHeight) + this._vScrollElement.scrollTop = y - this._offsetHeight + this._barHeight; if (timeLeft > entryEndTime) { var delta = timeLeft - entryEndTime + minEntryTimeWindow; @@ -901,12 +898,9 @@ return; } if (e.keyCode === keys.Up.code || e.keyCode === keys.Down.code) { - var level = timelineData.entryLevels[this._selectedEntryIndex]; - var delta = e.keyCode === keys.Up.code ? 1 : -1; e.consume(true); - if (this._isTopDown) - delta = -delta; - level += delta; + var level = timelineData.entryLevels[this._selectedEntryIndex]; + level += e.keyCode === keys.Up.code ? -1 : 1; if (level < 0 || level >= this._timelineLevels.length) return; var entryTime = timelineData.entryStartTimes[this._selectedEntryIndex] + timelineData.entryTotalTimes[this._selectedEntryIndex] / 2; @@ -1016,15 +1010,8 @@ if (!timelineData) return -1; var cursorTime = this._cursorTime(x); - var cursorLevel; - var offsetFromLevel; - if (this._isTopDown) { - cursorLevel = Math.floor((y - WebInspector.FlameChart.DividersBarHeight) / this._barHeight); - offsetFromLevel = y - WebInspector.FlameChart.DividersBarHeight - cursorLevel * this._barHeight; - } else { - cursorLevel = Math.floor((this._canvas.height / window.devicePixelRatio - y) / this._barHeight); - offsetFromLevel = this._canvas.height / window.devicePixelRatio - cursorLevel * this._barHeight; - } + var cursorLevel = Math.floor((y - WebInspector.FlameChart.DividersBarHeight) / this._barHeight); + var offsetFromLevel = y - WebInspector.FlameChart.DividersBarHeight - cursorLevel * this._barHeight; var entryStartTimes = timelineData.entryStartTimes; var entryTotalTimes = timelineData.entryTotalTimes; var entryIndexes = this._timelineLevels[cursorLevel]; @@ -1133,7 +1120,7 @@ context.scale(ratio, ratio); var timeWindowRight = this._timeWindowRight; - var timeWindowLeft = this._timeWindowLeft - this._paddingLeftTime; + var timeWindowLeft = this._timeWindowLeft - this._paddingLeft / this._timeToPixel; var entryTotalTimes = timelineData.entryTotalTimes; var entryStartTimes = timelineData.entryStartTimes; var entryLevels = timelineData.entryLevels; @@ -1150,8 +1137,8 @@ var barHeight = this._barHeight; var colorBuckets = {}; - var minVisibleBarLevel = Math.max(Math.floor((this._scrollTop - this._baseHeight) / barHeight), 0); - var maxVisibleBarLevel = Math.min(Math.floor((this._scrollTop - this._baseHeight + height) / barHeight), this._dataProvider.maxStackDepth()); + var minVisibleBarLevel = Math.max(Math.floor((this._scrollTop - WebInspector.FlameChart.DividersBarHeight) / barHeight), 0); + var maxVisibleBarLevel = Math.min(Math.floor((this._scrollTop - WebInspector.FlameChart.DividersBarHeight + height) / barHeight), this._dataProvider.maxStackDepth()); context.translate(0, -this._scrollTop); @@ -1459,7 +1446,7 @@ */ _levelToHeight: function(level) { - return this._baseHeight - level * this._barHeightDelta; + return WebInspector.FlameChart.DividersBarHeight + level * this._barHeight; }, /** @@ -1528,15 +1515,15 @@ return context.measureText(text).width; var font = context.font; - var textWidths = this._textWidth[font]; + var textWidths = this._textWidth.get(font); if (!textWidths) { - textWidths = {}; - this._textWidth[font] = textWidths; + textWidths = new Map(); + this._textWidth.set(font, textWidths); } - var width = textWidths[text]; + var width = textWidths.get(text); if (!width) { width = context.measureText(text).width; - textWidths[text] = width; + textWidths.set(text, width); } return width; }, @@ -1546,29 +1533,24 @@ this._totalTime = this._dataProvider.totalTime(); this._minimumBoundary = this._dataProvider.minimumBoundary(); + var windowWidth = 1; if (this._timeWindowRight !== Infinity) { this._windowLeft = (this._timeWindowLeft - this._minimumBoundary) / this._totalTime; this._windowRight = (this._timeWindowRight - this._minimumBoundary) / this._totalTime; - this._windowWidth = this._windowRight - this._windowLeft; + windowWidth = this._windowRight - this._windowLeft; } else if (this._timeWindowLeft === Infinity) { this._windowLeft = Infinity; this._windowRight = Infinity; - this._windowWidth = 1; } else { this._windowLeft = 0; this._windowRight = 1; - this._windowWidth = 1; } - this._pixelWindowWidth = this._offsetWidth - this._paddingLeft; - this._totalPixels = Math.floor(this._pixelWindowWidth / this._windowWidth); - this._pixelWindowLeft = Math.floor(this._totalPixels * this._windowLeft); + var totalPixels = Math.floor((this._offsetWidth - this._paddingLeft) / windowWidth); + this._pixelWindowLeft = Math.floor(totalPixels * this._windowLeft); - this._timeToPixel = this._totalPixels / this._totalTime; - this._pixelToTime = this._totalTime / this._totalPixels; - this._paddingLeftTime = this._paddingLeft / this._timeToPixel; - - this._baseHeight = this._isTopDown ? WebInspector.FlameChart.DividersBarHeight : this._offsetHeight - this._barHeight; + this._timeToPixel = totalPixels / this._totalTime; + this._pixelToTime = this._totalTime / totalPixels; this._totalHeight = this._levelToHeight(this._dataProvider.maxStackDepth()); this._vScrollContent.style.height = this._totalHeight + "px"; @@ -1631,7 +1613,8 @@ this._selectedEntryIndex = -1; this._rangeSelectionStart = 0; this._rangeSelectionEnd = 0; - this._textWidth = {}; + /** @type {!Map<string,!Map<string,number>>} */ + this._textWidth = new Map(); this.update(); },
diff --git a/third_party/WebKit/Source/devtools/protocol.json b/third_party/WebKit/Source/devtools/protocol.json index 2bfc10d..1baa4757 100644 --- a/third_party/WebKit/Source/devtools/protocol.json +++ b/third_party/WebKit/Source/devtools/protocol.json
@@ -1014,7 +1014,8 @@ { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." }, { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." } + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." } ], "returns": [ { "name": "result", "$ref": "RemoteObject", "description": "Run result." }, @@ -3174,6 +3175,16 @@ { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." } ], "description": "CSS keyframe rule representation." + }, + { + "id": "StyleDeclarationEdit", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The css style sheet identifier." }, + { "name": "range", "$ref": "SourceRange", "description": "The range of the style text in the enclosing stylesheet." }, + { "name": "text", "type": "string", "description": "New style text."} + ], + "description": "A descriptor of operation to mutate style declaration text." } ], "commands": [ @@ -3279,16 +3290,14 @@ "description": "Modifies the keyframe rule key text." }, { - "name": "setStyleText", + "name": "setStyleTexts", "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" }, - { "name": "range", "$ref": "SourceRange" }, - { "name": "text", "type": "string" } + { "name": "edits", "type": "array", "items": { "$ref": "StyleDeclarationEdit" }} ], "returns": [ - { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the selector modification." } + { "name": "styles", "type": "array", "items": { "$ref": "CSSStyle" }, "description": "The resulting styles after modification." } ], - "description": "Modifies the style text." + "description": "Applies specified style edits one after another in the given order." }, { "name": "setMediaText", @@ -5016,6 +5025,7 @@ "hidden": true, "properties": [ { "name": "id", "type": "string", "description": "<code>Animation</code>'s id." }, + { "name": "name", "type": "string", "description": "<code>Animation</code>'s name." }, { "name": "pausedState", "type": "boolean", "hidden": "true", "description": "<code>Animation</code>'s internal paused state." }, { "name": "playState", "type": "string", "description": "<code>Animation</code>'s play state." }, { "name": "playbackRate", "type": "number", "description": "<code>Animation</code>'s playback rate." }, @@ -5040,7 +5050,6 @@ { "name": "duration", "type": "number", "description": "<code>AnimationEffect</code>'s iteration duration." }, { "name": "direction", "type": "string", "description": "<code>AnimationEffect</code>'s playback direction." }, { "name": "fill", "type": "string", "description": "<code>AnimationEffect</code>'s fill mode." }, - { "name": "name", "type": "string", "description": "<code>AnimationEffect</code>'s name." }, { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "description": "<code>AnimationEffect</code>'s target node." }, { "name": "keyframesRule", "$ref": "KeyframesRule", "optional": true, "description": "<code>AnimationEffect</code>'s keyframes." }, { "name": "easing", "type": "string", "description": "<code>AnimationEffect</code>'s timing function." }
diff --git a/third_party/WebKit/Source/modules/EventTargetModulesFactory.in b/third_party/WebKit/Source/modules/EventTargetModulesFactory.in index 61414d0d..7b35999 100644 --- a/third_party/WebKit/Source/modules/EventTargetModulesFactory.in +++ b/third_party/WebKit/Source/modules/EventTargetModulesFactory.in
@@ -3,7 +3,7 @@ modules/battery/BatteryManager modules/bluetooth/BluetoothDevice -modules/bluetooth/BluetoothGATTCharacteristic +modules/bluetooth/BluetoothRemoteGATTCharacteristic modules/compositorworker/CompositorWorker modules/compositorworker/CompositorWorkerGlobalScope modules/encryptedmedia/MediaKeySession
diff --git a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp index 02d3e1a..b77dd508 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp +++ b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
@@ -2214,7 +2214,7 @@ // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB // focus inside of it. - AXObject* focusedElement = focusedUIElement(); + AXObject* focusedElement = axObjectCache().focusedObject(); if (!focusedElement) return false;
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp index 76188bf..2cf10aa 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp +++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
@@ -1182,19 +1182,6 @@ m_haveChildren = false; } -AXObject* AXObject::focusedUIElement() const -{ - Document* doc = document(); - if (!doc) - return 0; - - Page* page = doc->page(); - if (!page) - return 0; - - return axObjectCache().focusedUIElementForPage(page); -} - Document* AXObject::document() const { FrameView* frameView = documentFrameView();
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.h b/third_party/WebKit/Source/modules/accessibility/AXObject.h index 289ea532..d6e4de9 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXObject.h +++ b/third_party/WebKit/Source/modules/accessibility/AXObject.h
@@ -821,7 +821,6 @@ // Properties of the object's owning document or page. virtual double estimatedLoadingProgress() const { return 0; } - AXObject* focusedUIElement() const; // DOM and layout tree access. virtual Node* node() const { return 0; }
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp index d02caab..73d29f6a 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp +++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
@@ -149,24 +149,38 @@ return 0; } -AXObject* AXObjectCacheImpl::focusedUIElementForPage(const Page* page) +AXObject* AXObjectCacheImpl::focusedObject() { - if (!page->settings().accessibilityEnabled()) + if (!accessibilityEnabled()) return 0; - // Cross-process accessibility is not yet implemented. - if (!page->focusController().focusedOrMainFrame()->isLocalFrame()) + // We don't have to return anything if the focused frame is not local; + // the remote frame will have its own AXObjectCacheImpl and the focused + // object will be sorted out by the browser process. + Page* page = m_document->page(); + if (!page->focusController().focusedFrame()) return 0; - // get the focused node in the page - Document* focusedDocument = toLocalFrame(page->focusController().focusedOrMainFrame())->document(); + // Get the focused node in the page. + Document* focusedDocument = page->focusController().focusedFrame()->document(); Node* focusedNode = focusedDocument->focusedElement(); if (!focusedNode) focusedNode = focusedDocument; - if (isHTMLAreaElement(*focusedNode)) + // If it's an image map, get the focused link within the image map. + if (isHTMLAreaElement(focusedNode)) return focusedImageMapUIElement(toHTMLAreaElement(focusedNode)); + // See if there's a page popup, for example a calendar picker. + Element* adjustedFocusedElement = focusedDocument->adjustedFocusedElement(); + if (isHTMLInputElement(adjustedFocusedElement)) { + if (AXObject* axPopup = toHTMLInputElement(adjustedFocusedElement)->popupRootAXObject()) { + if (Element* focusedElementInPopup = axPopup->document()->focusedElement()) + focusedNode = focusedElementInPopup; + } + + } + AXObject* obj = getOrCreate(focusedNode); if (!obj) return 0; @@ -1120,7 +1134,7 @@ if (!page) return; - AXObject* focusedObject = focusedUIElementForPage(page); + AXObject* focusedObject = this->focusedObject(); if (!focusedObject) return;
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h index 6e8d77c..c6eff56 100644 --- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h +++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h
@@ -54,7 +54,7 @@ ~AXObjectCacheImpl(); DECLARE_VIRTUAL_TRACE(); - AXObject* focusedUIElementForPage(const Page*); + AXObject* focusedObject(); void dispose() override;
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothCharacteristicProperties.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothCharacteristicProperties.h index 7012f044..baf26f0d 100644 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothCharacteristicProperties.h +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothCharacteristicProperties.h
@@ -10,7 +10,7 @@ namespace blink { -// Each BluetoothGATTCharacteristic exposes its characteristic properties +// Each BluetoothRemoteGATTCharacteristic exposes its characteristic properties // through a BluetoothCharacteristicProperties object. These properties express // what operations are valid on the characteristic. class BluetoothCharacteristicProperties final
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp index 0b5ac39..b763a3b6 100644 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.cpp
@@ -13,7 +13,7 @@ #include "core/events/Event.h" #include "core/page/PageVisibilityState.h" #include "modules/bluetooth/BluetoothError.h" -#include "modules/bluetooth/BluetoothGATTRemoteServer.h" +#include "modules/bluetooth/BluetoothRemoteGATTServer.h" #include "modules/bluetooth/BluetoothSupplement.h" #include "public/platform/modules/bluetooth/WebBluetooth.h" @@ -25,7 +25,7 @@ , m_webDevice(webDevice) , m_adData(BluetoothAdvertisingData::create(m_webDevice->txPower, m_webDevice->rssi)) - , m_gatt(BluetoothGATTRemoteServer::create(this)) + , m_gatt(BluetoothRemoteGATTServer::create(this)) { // See example in Source/platform/heap/ThreadState.h ThreadState::current()->registerPreFinalizer(this);
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h index e83e0f3..bd38f71 100644 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.h
@@ -10,7 +10,7 @@ #include "core/page/PageLifecycleObserver.h" #include "modules/EventTargetModules.h" #include "modules/bluetooth/BluetoothAdvertisingData.h" -#include "modules/bluetooth/BluetoothGATTRemoteServer.h" +#include "modules/bluetooth/BluetoothRemoteGATTServer.h" #include "platform/heap/Heap.h" #include "public/platform/modules/bluetooth/WebBluetoothDevice.h" #include "wtf/OwnPtr.h" @@ -19,7 +19,7 @@ namespace blink { -class BluetoothGATTRemoteServer; +class BluetoothRemoteGATTServer; class ScriptPromise; class ScriptPromiseResolver; class ScriptState; @@ -90,7 +90,7 @@ unsigned vendorID(bool& isNull); unsigned productID(bool& isNull); unsigned productVersion(bool& isNull); - BluetoothGATTRemoteServer* gatt() { return m_gatt; } + BluetoothRemoteGATTServer* gatt() { return m_gatt; } Vector<String> uuids(); // TODO(ortuno): Remove connectGATT // http://crbug.com/582292 @@ -101,7 +101,7 @@ private: OwnPtr<WebBluetoothDevice> m_webDevice; Member<BluetoothAdvertisingData> m_adData; - Member<BluetoothGATTRemoteServer> m_gatt; + Member<BluetoothRemoteGATTServer> m_gatt; }; } // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.idl b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.idl index 9753608b..2bb26ce8 100644 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.idl +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothDevice.idl
@@ -30,9 +30,9 @@ readonly attribute unsigned long? vendorID; readonly attribute unsigned long? productID; readonly attribute unsigned long? productVersion; - readonly attribute BluetoothGATTRemoteServer gatt; + readonly attribute BluetoothRemoteGATTServer gatt; readonly attribute UUID[] uuids; - [CallWith=ScriptState, DeprecateAs=BluetoothDeviceConnectGATT] Promise<BluetoothGATTRemoteServer> connectGATT (); + [CallWith=ScriptState, DeprecateAs=BluetoothDeviceConnectGATT] Promise<BluetoothRemoteGATTServer> connectGATT (); attribute EventHandler ongattserverdisconnected; };
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.cpp deleted file mode 100644 index 27bcfc1c..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.cpp +++ /dev/null
@@ -1,223 +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. - -#include "modules/bluetooth/BluetoothGATTCharacteristic.h" - -#include "bindings/core/v8/CallbackPromiseAdapter.h" -#include "bindings/core/v8/ScriptPromise.h" -#include "bindings/core/v8/ScriptPromiseResolver.h" -#include "core/dom/DOMDataView.h" -#include "core/dom/DOMException.h" -#include "core/dom/ExceptionCode.h" -#include "core/events/Event.h" -#include "modules/bluetooth/BluetoothCharacteristicProperties.h" -#include "modules/bluetooth/BluetoothError.h" -#include "modules/bluetooth/BluetoothSupplement.h" -#include "public/platform/modules/bluetooth/WebBluetooth.h" - -namespace blink { - -namespace { - -PassRefPtr<DOMDataView> ConvertWebVectorToDataView( - const WebVector<uint8_t>& webVector) -{ - static_assert(sizeof(*webVector.data()) == 1, "uint8_t should be a single byte"); - RefPtr<DOMArrayBuffer> domBuffer = DOMArrayBuffer::create(webVector.data(), webVector.size()); - RefPtr<DOMDataView> domDataView = DOMDataView::create(domBuffer, 0, webVector.size()); - return domDataView; -} - -} // anonymous namespace - -BluetoothGATTCharacteristic::BluetoothGATTCharacteristic(ExecutionContext* context, PassOwnPtr<WebBluetoothGATTCharacteristicInit> webCharacteristic) - : ActiveDOMObject(context) - , m_webCharacteristic(webCharacteristic) - , m_stopped(false) -{ - m_properties = BluetoothCharacteristicProperties::create(m_webCharacteristic->characteristicProperties); - // See example in Source/platform/heap/ThreadState.h - ThreadState::current()->registerPreFinalizer(this); -} - -BluetoothGATTCharacteristic* BluetoothGATTCharacteristic::take(ScriptPromiseResolver* resolver, PassOwnPtr<WebBluetoothGATTCharacteristicInit> webCharacteristic) -{ - if (!webCharacteristic) { - return nullptr; - } - BluetoothGATTCharacteristic* characteristic = new BluetoothGATTCharacteristic(resolver->executionContext(), webCharacteristic); - // See note in ActiveDOMObject about suspendIfNeeded. - characteristic->suspendIfNeeded(); - return characteristic; -} - -void BluetoothGATTCharacteristic::setValue( - const PassRefPtr<DOMDataView>& domDataView) -{ - m_value = domDataView; -} - -void BluetoothGATTCharacteristic::dispatchCharacteristicValueChanged( - const WebVector<uint8_t>& value) -{ - RefPtr<DOMDataView> domDataView = ConvertWebVectorToDataView(value); - this->setValue(domDataView); - dispatchEvent(Event::create(EventTypeNames::characteristicvaluechanged)); -} - -void BluetoothGATTCharacteristic::stop() -{ - notifyCharacteristicObjectRemoved(); -} - -void BluetoothGATTCharacteristic::dispose() -{ - notifyCharacteristicObjectRemoved(); -} - -void BluetoothGATTCharacteristic::notifyCharacteristicObjectRemoved() -{ - if (!m_stopped) { - m_stopped = true; - WebBluetooth* webbluetooth = BluetoothSupplement::fromExecutionContext(ActiveDOMObject::executionContext()); - webbluetooth->characteristicObjectRemoved(m_webCharacteristic->characteristicInstanceID, this); - } -} - -const WTF::AtomicString& BluetoothGATTCharacteristic::interfaceName() const -{ - return EventTargetNames::BluetoothGATTCharacteristic; -} - -ExecutionContext* BluetoothGATTCharacteristic::executionContext() const -{ - return ActiveDOMObject::executionContext(); -} - -bool BluetoothGATTCharacteristic::addEventListenerInternal(const AtomicString& eventType, PassRefPtrWillBeRawPtr<EventListener> listener, const EventListenerOptions& options) -{ - // We will also need to unregister a characteristic once all the event - // listeners have been removed. See http://crbug.com/541390 - if (eventType == EventTypeNames::characteristicvaluechanged) { - WebBluetooth* webbluetooth = BluetoothSupplement::fromExecutionContext(executionContext()); - webbluetooth->registerCharacteristicObject(m_webCharacteristic->characteristicInstanceID, this); - } - return EventTarget::addEventListenerInternal(eventType, listener, options); -} - -class ReadValueCallback : public WebBluetoothReadValueCallbacks { -public: - ReadValueCallback(BluetoothGATTCharacteristic* characteristic, ScriptPromiseResolver* resolver) : m_webCharacteristic(characteristic), m_resolver(resolver) {} - - void onSuccess(const WebVector<uint8_t>& value) override - { - if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) - return; - - RefPtr<DOMDataView> domDataView = ConvertWebVectorToDataView(value); - if (m_webCharacteristic) { - m_webCharacteristic->setValue(domDataView); - } - m_resolver->resolve(domDataView); - } - - void onError(const WebBluetoothError& e) override - { - if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) - return; - m_resolver->reject(BluetoothError::take(m_resolver, e)); - } - -private: - WeakPersistent<BluetoothGATTCharacteristic> m_webCharacteristic; - Persistent<ScriptPromiseResolver> m_resolver; -}; - -ScriptPromise BluetoothGATTCharacteristic::readValue(ScriptState* scriptState) -{ - WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); - - ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); - ScriptPromise promise = resolver->promise(); - webbluetooth->readValue(m_webCharacteristic->characteristicInstanceID, new ReadValueCallback(this, resolver)); - - return promise; -} - -class WriteValueCallback : public WebBluetoothWriteValueCallbacks { -public: - WriteValueCallback(BluetoothGATTCharacteristic* characteristic, ScriptPromiseResolver* resolver) : m_webCharacteristic(characteristic), m_resolver(resolver) {} - - void onSuccess(const WebVector<uint8_t>& value) override - { - if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) - return; - - if (m_webCharacteristic) { - m_webCharacteristic->setValue(ConvertWebVectorToDataView(value)); - } - m_resolver->resolve(); - } - - void onError(const WebBluetoothError& e) override - { - if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) - return; - m_resolver->reject(BluetoothError::take(m_resolver, e)); - } - -private: - WeakPersistent<BluetoothGATTCharacteristic> m_webCharacteristic; - Persistent<ScriptPromiseResolver> m_resolver; -}; - -ScriptPromise BluetoothGATTCharacteristic::writeValue(ScriptState* scriptState, const DOMArrayPiece& value) -{ - WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); - // Partial implementation of writeValue algorithm: - // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothgattcharacteristic-writevalue - - // If bytes is more than 512 bytes long (the maximum length of an attribute - // value, per Long Attribute Values) return a promise rejected with an - // InvalidModificationError and abort. - if (value.byteLength() > 512) - return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidModificationError, "Value can't exceed 512 bytes.")); - - // Let valueVector be a copy of the bytes held by value. - WebVector<uint8_t> valueVector(value.bytes(), value.byteLength()); - - ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); - - ScriptPromise promise = resolver->promise(); - webbluetooth->writeValue(m_webCharacteristic->characteristicInstanceID, valueVector, new WriteValueCallback(this, resolver)); - - return promise; -} - -ScriptPromise BluetoothGATTCharacteristic::startNotifications(ScriptState* scriptState) -{ - WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); - ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); - ScriptPromise promise = resolver->promise(); - webbluetooth->startNotifications(m_webCharacteristic->characteristicInstanceID, this, new CallbackPromiseAdapter<void, BluetoothError>(resolver)); - return promise; -} - -ScriptPromise BluetoothGATTCharacteristic::stopNotifications(ScriptState* scriptState) -{ - WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); - ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); - ScriptPromise promise = resolver->promise(); - webbluetooth->stopNotifications(m_webCharacteristic->characteristicInstanceID, this, new CallbackPromiseAdapter<void, BluetoothError>(resolver)); - return promise; -} - -DEFINE_TRACE(BluetoothGATTCharacteristic) -{ - RefCountedGarbageCollectedEventTargetWithInlineData<BluetoothGATTCharacteristic>::trace(visitor); - ActiveDOMObject::trace(visitor); - visitor->trace(m_properties); -} - -} // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.h deleted file mode 100644 index 0ff5428..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.h +++ /dev/null
@@ -1,100 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BluetoothGATTCharacteristic_h -#define BluetoothGATTCharacteristic_h - -#include "bindings/core/v8/ScriptWrappable.h" -#include "core/dom/ActiveDOMObject.h" -#include "core/dom/DOMArrayPiece.h" -#include "core/dom/DOMDataView.h" -#include "modules/EventTargetModules.h" -#include "platform/heap/Handle.h" -#include "public/platform/modules/bluetooth/WebBluetoothGATTCharacteristic.h" -#include "public/platform/modules/bluetooth/WebBluetoothGATTCharacteristicInit.h" -#include "wtf/OwnPtr.h" -#include "wtf/PassOwnPtr.h" -#include "wtf/text/WTFString.h" - -namespace blink { - -class BluetoothCharacteristicProperties; -class ExecutionContext; -class ScriptPromise; -class ScriptPromiseResolver; -class ScriptState; - -// BluetoothGATTCharacteristic represents a GATT Characteristic, which is a -// basic data element that provides further information about a peripheral's -// service. -// -// Callbacks providing WebBluetoothGATTCharacteristicInit objects are handled by -// CallbackPromiseAdapter templatized with this class. See this class's -// "Interface required by CallbackPromiseAdapter" section and the -// CallbackPromiseAdapter class comments. -class BluetoothGATTCharacteristic final - : public RefCountedGarbageCollectedEventTargetWithInlineData<BluetoothGATTCharacteristic> - , public ActiveDOMObject - , public WebBluetoothGATTCharacteristic { - USING_PRE_FINALIZER(BluetoothGATTCharacteristic, dispose); - DEFINE_WRAPPERTYPEINFO(); - REFCOUNTED_GARBAGE_COLLECTED_EVENT_TARGET(BluetoothGATTCharacteristic); - WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(BluetoothGATTCharacteristic); -public: - explicit BluetoothGATTCharacteristic(ExecutionContext*, PassOwnPtr<WebBluetoothGATTCharacteristicInit>); - - // Interface required by CallbackPromiseAdapter. - using WebType = OwnPtr<WebBluetoothGATTCharacteristicInit>; - static BluetoothGATTCharacteristic* take(ScriptPromiseResolver*, PassOwnPtr<WebBluetoothGATTCharacteristicInit>); - - // Save value. - void setValue(const PassRefPtr<DOMDataView>&); - - // WebBluetoothGATTCharacteristic interface: - void dispatchCharacteristicValueChanged(const WebVector<uint8_t>&) override; - - // ActiveDOMObject interface. - void stop() override; - - // USING_PRE_FINALIZER interface. - // Called before the object gets garbage collected. - void dispose(); - - // Notify our embedder that we should stop any notifications. - // The function only notifies the embedder once. - void notifyCharacteristicObjectRemoved(); - - // EventTarget methods: - const AtomicString& interfaceName() const override; - ExecutionContext* executionContext() const; - - // Interface required by garbage collection. - DECLARE_VIRTUAL_TRACE(); - - // IDL exposed interface: - String uuid() { return m_webCharacteristic->uuid; } - - BluetoothCharacteristicProperties* properties() { return m_properties; } - PassRefPtr<DOMDataView> value() const { return m_value; } - ScriptPromise readValue(ScriptState*); - ScriptPromise writeValue(ScriptState*, const DOMArrayPiece&); - ScriptPromise startNotifications(ScriptState*); - ScriptPromise stopNotifications(ScriptState*); - - DEFINE_ATTRIBUTE_EVENT_LISTENER(characteristicvaluechanged); - -protected: - // EventTarget overrides. - bool addEventListenerInternal(const AtomicString& eventType, PassRefPtrWillBeRawPtr<EventListener>, const EventListenerOptions&) override; - -private: - OwnPtr<WebBluetoothGATTCharacteristicInit> m_webCharacteristic; - bool m_stopped; - Member<BluetoothCharacteristicProperties> m_properties; - RefPtr<DOMDataView> m_value; -}; - -} // namespace blink - -#endif // BluetoothGATTCharacteristic_h
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.idl b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.idl deleted file mode 100644 index ebdd2566..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTCharacteristic.idl +++ /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. - -// https://webbluetoothchrome.github.io/web-bluetooth/#idl-def-bluetoothgattcharacteristic - -// Implement BluetoothGATTCharacteristic interface: https://crbug.com/483344 - -[ - GarbageCollected, - ActiveDOMObject, - RuntimeEnabled=WebBluetooth, - OriginTrialEnabled=WebBluetooth, -] interface BluetoothGATTCharacteristic : EventTarget {//: CharacteristicEventHandlers { - // TODO(ortuno): Add test to make sure service matches the service - // used to call getCharacteristic. - // readonly attribute BluetoothGATTService service; - readonly attribute UUID uuid; - readonly attribute BluetoothCharacteristicProperties properties; - readonly attribute DataView? value; - // Promise<BluetoothGATTDescriptor> getDescriptor(BluetoothDescriptorUUID descriptor); - // Promise<sequence<BluetoothGATTDescriptor>> getDescriptors(optional BluetoothDescriptorUUID descriptor); - [CallWith=ScriptState] Promise<DataView> readValue(); - [CallWith=ScriptState] Promise<void> writeValue(BufferSource value); - [CallWith=ScriptState] Promise<void> startNotifications(); - [CallWith=ScriptState] Promise<void> stopNotifications(); - - // TODO(ortuno): Move this to CharacteristicEventHandlers. - // http://crbug.com/537459 - attribute EventHandler oncharacteristicvaluechanged; -};
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.cpp deleted file mode 100644 index 4a107fe..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.cpp +++ /dev/null
@@ -1,120 +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. - -#include "modules/bluetooth/BluetoothGATTRemoteServer.h" - -#include "bindings/core/v8/CallbackPromiseAdapter.h" -#include "bindings/core/v8/ScriptPromise.h" -#include "bindings/core/v8/ScriptPromiseResolver.h" -#include "core/dom/DOMException.h" -#include "core/dom/Document.h" -#include "core/dom/ExceptionCode.h" -#include "core/page/Page.h" -#include "modules/bluetooth/BluetoothError.h" -#include "modules/bluetooth/BluetoothGATTService.h" -#include "modules/bluetooth/BluetoothSupplement.h" -#include "modules/bluetooth/BluetoothUUID.h" -#include "public/platform/modules/bluetooth/WebBluetooth.h" -#include "wtf/OwnPtr.h" - -namespace blink { -namespace { - -const char kPageHiddenError[] = "Connection is only allowed while the page is visible. This is a temporary measure until we are able to effectively communicate to the user that a page is connected to a device."; - -} - -BluetoothGATTRemoteServer::BluetoothGATTRemoteServer(BluetoothDevice* device) - : m_device(device) - , m_connected(false) -{ -} - -BluetoothGATTRemoteServer* BluetoothGATTRemoteServer::create(BluetoothDevice* device) -{ - return new BluetoothGATTRemoteServer(device); -} - -DEFINE_TRACE(BluetoothGATTRemoteServer) -{ - visitor->trace(m_device); -} - -class ConnectCallback : public WebBluetoothGATTServerConnectCallbacks { -public: - ConnectCallback(BluetoothDevice* device, ScriptPromiseResolver* resolver) - : m_device(device) - , m_resolver(resolver) {} - - void onSuccess() override - { - if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) - return; - m_device->gatt()->setConnected(true); - if (!m_device->page()->isPageVisible()) { - // TODO(ortuno): Allow connections when the tab is in the background. - // This is a short term solution instead of implementing a tab indicator - // for bluetooth connections. - // https://crbug.com/579746 - m_device->disconnectGATTIfConnected(); - m_resolver->reject(DOMException::create(SecurityError, kPageHiddenError)); - } else { - m_resolver->resolve(m_device->gatt()); - } - } - - void onError(const WebBluetoothError& e) override - { - if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) - return; - m_resolver->reject(BluetoothError::take(m_resolver, e)); - } -private: - Persistent<BluetoothDevice> m_device; - Persistent<ScriptPromiseResolver> m_resolver; -}; - -ScriptPromise BluetoothGATTRemoteServer::connect(ScriptState* scriptState) -{ - // TODO(ortuno): Allow connections when the tab is in the background. - // This is a short term solution instead of implementing a tab indicator - // for bluetooth connections. - // https://crbug.com/579746 - if (!toDocument(scriptState->executionContext())->page()->isPageVisible()) { - return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, kPageHiddenError)); - } - - WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); - if (!webbluetooth) - return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError)); - - ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); - ScriptPromise promise = resolver->promise(); - webbluetooth->connect(device()->id(), new ConnectCallback(device(), resolver)); - return promise; -} - -void BluetoothGATTRemoteServer::disconnect(ScriptState* scriptState) -{ - m_connected = false; - WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); - webbluetooth->disconnect(device()->id()); -} - -ScriptPromise BluetoothGATTRemoteServer::getPrimaryService(ScriptState* scriptState, const StringOrUnsignedLong& service, ExceptionState& exceptionState) -{ - WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); - - String serviceUUID = BluetoothUUID::getService(service, exceptionState); - if (exceptionState.hadException()) - return exceptionState.reject(scriptState); - - ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); - ScriptPromise promise = resolver->promise(); - webbluetooth->getPrimaryService(device()->id(), serviceUUID, new CallbackPromiseAdapter<BluetoothGATTService, BluetoothError>(resolver)); - - return promise; -} - -} // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.h deleted file mode 100644 index 6683586..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.h +++ /dev/null
@@ -1,52 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BluetoothGATTRemoteServer_h -#define BluetoothGATTRemoteServer_h - -#include "bindings/core/v8/ScriptWrappable.h" -#include "bindings/modules/v8/UnionTypesModules.h" -#include "modules/bluetooth/BluetoothDevice.h" -#include "platform/heap/Heap.h" -#include "wtf/OwnPtr.h" -#include "wtf/PassOwnPtr.h" -#include "wtf/text/WTFString.h" - -namespace blink { - -class BluetoothDevice; -class ScriptPromise; -class ScriptPromiseResolver; -class ScriptState; - -// BluetoothGATTRemoteServer provides a way to interact with a connected bluetooth peripheral. -class BluetoothGATTRemoteServer final - : public GarbageCollectedFinalized<BluetoothGATTRemoteServer> - , public ScriptWrappable { - DEFINE_WRAPPERTYPEINFO(); -public: - BluetoothGATTRemoteServer(BluetoothDevice*); - - static BluetoothGATTRemoteServer* create(BluetoothDevice*); - - void setConnected(bool connected) { m_connected = connected; } - - // Interface required by Garbage Collectoin: - DECLARE_VIRTUAL_TRACE(); - - // IDL exposed interface: - BluetoothDevice* device() { return m_device; } - bool connected() { return m_connected; } - ScriptPromise connect(ScriptState*); - void disconnect(ScriptState*); - ScriptPromise getPrimaryService(ScriptState*, const StringOrUnsignedLong& service, ExceptionState&); - -private: - Member<BluetoothDevice> m_device; - bool m_connected; -}; - -} // namespace blink - -#endif // BluetoothDevice_h
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.idl b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.idl deleted file mode 100644 index 3c609df..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTRemoteServer.idl +++ /dev/null
@@ -1,21 +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. - -// https://webbluetoothchrome.github.io/web-bluetooth/#idl-def-bluetoothgattremoteserver - -// Implement BluetoothGATTRemoteServer interface: https://crbug.com/476735 - -[ - GarbageCollected, - RuntimeEnabled=WebBluetooth, - OriginTrialEnabled=WebBluetooth, -] interface BluetoothGATTRemoteServer -{ - readonly attribute BluetoothDevice device; - readonly attribute boolean connected; - [CallWith=ScriptState] Promise<BluetoothGATTRemoteServer> connect(); - [CallWith=ScriptState] void disconnect(); - [CallWith=ScriptState, RaisesException] Promise<BluetoothGATTService> getPrimaryService (BluetoothServiceUUID service); - // Promise<sequence<BluetoothGATTService>> getPrimaryServices (optional BluetoothServiceUUID service); -};
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.cpp deleted file mode 100644 index 13d2fee..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.cpp +++ /dev/null
@@ -1,49 +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. - -#include "modules/bluetooth/BluetoothGATTService.h" - -#include "bindings/core/v8/CallbackPromiseAdapter.h" -#include "bindings/core/v8/ScriptPromise.h" -#include "bindings/core/v8/ScriptPromiseResolver.h" -#include "core/dom/DOMException.h" -#include "core/dom/ExceptionCode.h" -#include "modules/bluetooth/BluetoothError.h" -#include "modules/bluetooth/BluetoothGATTCharacteristic.h" -#include "modules/bluetooth/BluetoothSupplement.h" -#include "modules/bluetooth/BluetoothUUID.h" -#include "public/platform/modules/bluetooth/WebBluetooth.h" - -namespace blink { - -BluetoothGATTService::BluetoothGATTService(PassOwnPtr<WebBluetoothGATTService> webService) - : m_webService(webService) -{ -} - -BluetoothGATTService* BluetoothGATTService::take(ScriptPromiseResolver*, PassOwnPtr<WebBluetoothGATTService> webService) -{ - if (!webService) { - return nullptr; - } - return new BluetoothGATTService(webService); -} - -ScriptPromise BluetoothGATTService::getCharacteristic(ScriptState* scriptState, - const StringOrUnsignedLong& characteristic, ExceptionState& exceptionState) -{ - WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); - - String characteristicUUID = BluetoothUUID::getCharacteristic(characteristic, exceptionState); - if (exceptionState.hadException()) - return exceptionState.reject(scriptState); - - ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); - ScriptPromise promise = resolver->promise(); - webbluetooth->getCharacteristic(m_webService->serviceInstanceID, characteristicUUID, new CallbackPromiseAdapter<BluetoothGATTCharacteristic, BluetoothError>(resolver)); - - return promise; -} - -} // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.h deleted file mode 100644 index f547dc8..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.h +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BluetoothGATTService_h -#define BluetoothGATTService_h - -#include "bindings/core/v8/ScriptWrappable.h" -#include "bindings/modules/v8/UnionTypesModules.h" -#include "platform/heap/Handle.h" -#include "public/platform/modules/bluetooth/WebBluetoothGATTService.h" -#include "wtf/OwnPtr.h" -#include "wtf/PassOwnPtr.h" -#include "wtf/text/WTFString.h" - -namespace blink { - -class ScriptPromise; -class ScriptPromiseResolver; -class ScriptState; - -// Represents a GATT Service within a Bluetooth Peripheral, a collection of -// characteristics and relationships to other services that encapsulate the -// behavior of part of a device. -// -// Callbacks providing WebBluetoothGATTService objects are handled by -// CallbackPromiseAdapter templatized with this class. See this class's -// "Interface required by CallbackPromiseAdapter" section and the -// CallbackPromiseAdapter class comments. -class BluetoothGATTService final - : public GarbageCollectedFinalized<BluetoothGATTService> - , public ScriptWrappable { - DEFINE_WRAPPERTYPEINFO(); -public: - explicit BluetoothGATTService(PassOwnPtr<WebBluetoothGATTService>); - - // Interface required by CallbackPromiseAdapter: - using WebType = OwnPtr<WebBluetoothGATTService>; - static BluetoothGATTService* take(ScriptPromiseResolver*, PassOwnPtr<WebBluetoothGATTService>); - - // Interface required by garbage collection. - DEFINE_INLINE_TRACE() { } - - // IDL exposed interface: - String uuid() { return m_webService->uuid; } - bool isPrimary() { return m_webService->isPrimary; } - ScriptPromise getCharacteristic(ScriptState*, const StringOrUnsignedLong& characteristic, ExceptionState&); - -private: - OwnPtr<WebBluetoothGATTService> m_webService; -}; - -} // namespace blink - -#endif // BluetoothGATTService_h
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.idl b/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.idl deleted file mode 100644 index 5ff18bc..0000000 --- a/third_party/WebKit/Source/modules/bluetooth/BluetoothGATTService.idl +++ /dev/null
@@ -1,23 +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. - -// https://webbluetoothchrome.github.io/web-bluetooth/#idl-def-bluetoothgattservice - -// Implement BluetoothGATTService interface: https://crbug.com/483342 - -[ - GarbageCollected, - RuntimeEnabled=WebBluetooth, - OriginTrialEnabled=WebBluetooth, -] interface BluetoothGATTService { // : ServiceEventHandlers { - readonly attribute UUID uuid; - readonly attribute boolean isPrimary; - // TODO(ortuno): Once device is implemented test that it matches - // the original device. - // readonly attribute BluetoothDevice device; - [RaisesException, CallWith=ScriptState] Promise<BluetoothGATTCharacteristic> getCharacteristic(BluetoothCharacteristicUUID characteristic); - // Promise<sequence<BluetoothGATTCharacteristic>> getCharacteristics(optional BluetoothCharacteristicUUID characteristic); - // Promise<BluetoothGATTService> getIncludedService(BluetoothServiceUUID service); - // Promise<sequence<BluetoothGATTService>> getIncludedServices(optional BluetoothServiceUUID service); -};
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp new file mode 100644 index 0000000..d4577022 --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp
@@ -0,0 +1,223 @@ +// 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. + +#include "modules/bluetooth/BluetoothRemoteGATTCharacteristic.h" + +#include "bindings/core/v8/CallbackPromiseAdapter.h" +#include "bindings/core/v8/ScriptPromise.h" +#include "bindings/core/v8/ScriptPromiseResolver.h" +#include "core/dom/DOMDataView.h" +#include "core/dom/DOMException.h" +#include "core/dom/ExceptionCode.h" +#include "core/events/Event.h" +#include "modules/bluetooth/BluetoothCharacteristicProperties.h" +#include "modules/bluetooth/BluetoothError.h" +#include "modules/bluetooth/BluetoothSupplement.h" +#include "public/platform/modules/bluetooth/WebBluetooth.h" + +namespace blink { + +namespace { + +PassRefPtr<DOMDataView> ConvertWebVectorToDataView( + const WebVector<uint8_t>& webVector) +{ + static_assert(sizeof(*webVector.data()) == 1, "uint8_t should be a single byte"); + RefPtr<DOMArrayBuffer> domBuffer = DOMArrayBuffer::create(webVector.data(), webVector.size()); + RefPtr<DOMDataView> domDataView = DOMDataView::create(domBuffer, 0, webVector.size()); + return domDataView; +} + +} // anonymous namespace + +BluetoothRemoteGATTCharacteristic::BluetoothRemoteGATTCharacteristic(ExecutionContext* context, PassOwnPtr<WebBluetoothRemoteGATTCharacteristicInit> webCharacteristic) + : ActiveDOMObject(context) + , m_webCharacteristic(webCharacteristic) + , m_stopped(false) +{ + m_properties = BluetoothCharacteristicProperties::create(m_webCharacteristic->characteristicProperties); + // See example in Source/platform/heap/ThreadState.h + ThreadState::current()->registerPreFinalizer(this); +} + +BluetoothRemoteGATTCharacteristic* BluetoothRemoteGATTCharacteristic::take(ScriptPromiseResolver* resolver, PassOwnPtr<WebBluetoothRemoteGATTCharacteristicInit> webCharacteristic) +{ + if (!webCharacteristic) { + return nullptr; + } + BluetoothRemoteGATTCharacteristic* characteristic = new BluetoothRemoteGATTCharacteristic(resolver->executionContext(), webCharacteristic); + // See note in ActiveDOMObject about suspendIfNeeded. + characteristic->suspendIfNeeded(); + return characteristic; +} + +void BluetoothRemoteGATTCharacteristic::setValue( + const PassRefPtr<DOMDataView>& domDataView) +{ + m_value = domDataView; +} + +void BluetoothRemoteGATTCharacteristic::dispatchCharacteristicValueChanged( + const WebVector<uint8_t>& value) +{ + RefPtr<DOMDataView> domDataView = ConvertWebVectorToDataView(value); + this->setValue(domDataView); + dispatchEvent(Event::create(EventTypeNames::characteristicvaluechanged)); +} + +void BluetoothRemoteGATTCharacteristic::stop() +{ + notifyCharacteristicObjectRemoved(); +} + +void BluetoothRemoteGATTCharacteristic::dispose() +{ + notifyCharacteristicObjectRemoved(); +} + +void BluetoothRemoteGATTCharacteristic::notifyCharacteristicObjectRemoved() +{ + if (!m_stopped) { + m_stopped = true; + WebBluetooth* webbluetooth = BluetoothSupplement::fromExecutionContext(ActiveDOMObject::executionContext()); + webbluetooth->characteristicObjectRemoved(m_webCharacteristic->characteristicInstanceID, this); + } +} + +const WTF::AtomicString& BluetoothRemoteGATTCharacteristic::interfaceName() const +{ + return EventTargetNames::BluetoothRemoteGATTCharacteristic; +} + +ExecutionContext* BluetoothRemoteGATTCharacteristic::executionContext() const +{ + return ActiveDOMObject::executionContext(); +} + +bool BluetoothRemoteGATTCharacteristic::addEventListenerInternal(const AtomicString& eventType, PassRefPtrWillBeRawPtr<EventListener> listener, const EventListenerOptions& options) +{ + // We will also need to unregister a characteristic once all the event + // listeners have been removed. See http://crbug.com/541390 + if (eventType == EventTypeNames::characteristicvaluechanged) { + WebBluetooth* webbluetooth = BluetoothSupplement::fromExecutionContext(executionContext()); + webbluetooth->registerCharacteristicObject(m_webCharacteristic->characteristicInstanceID, this); + } + return EventTarget::addEventListenerInternal(eventType, listener, options); +} + +class ReadValueCallback : public WebBluetoothReadValueCallbacks { +public: + ReadValueCallback(BluetoothRemoteGATTCharacteristic* characteristic, ScriptPromiseResolver* resolver) : m_webCharacteristic(characteristic), m_resolver(resolver) {} + + void onSuccess(const WebVector<uint8_t>& value) override + { + if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) + return; + + RefPtr<DOMDataView> domDataView = ConvertWebVectorToDataView(value); + if (m_webCharacteristic) { + m_webCharacteristic->setValue(domDataView); + } + m_resolver->resolve(domDataView); + } + + void onError(const WebBluetoothError& e) override + { + if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) + return; + m_resolver->reject(BluetoothError::take(m_resolver, e)); + } + +private: + WeakPersistent<BluetoothRemoteGATTCharacteristic> m_webCharacteristic; + Persistent<ScriptPromiseResolver> m_resolver; +}; + +ScriptPromise BluetoothRemoteGATTCharacteristic::readValue(ScriptState* scriptState) +{ + WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + webbluetooth->readValue(m_webCharacteristic->characteristicInstanceID, new ReadValueCallback(this, resolver)); + + return promise; +} + +class WriteValueCallback : public WebBluetoothWriteValueCallbacks { +public: + WriteValueCallback(BluetoothRemoteGATTCharacteristic* characteristic, ScriptPromiseResolver* resolver) : m_webCharacteristic(characteristic), m_resolver(resolver) {} + + void onSuccess(const WebVector<uint8_t>& value) override + { + if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) + return; + + if (m_webCharacteristic) { + m_webCharacteristic->setValue(ConvertWebVectorToDataView(value)); + } + m_resolver->resolve(); + } + + void onError(const WebBluetoothError& e) override + { + if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) + return; + m_resolver->reject(BluetoothError::take(m_resolver, e)); + } + +private: + WeakPersistent<BluetoothRemoteGATTCharacteristic> m_webCharacteristic; + Persistent<ScriptPromiseResolver> m_resolver; +}; + +ScriptPromise BluetoothRemoteGATTCharacteristic::writeValue(ScriptState* scriptState, const DOMArrayPiece& value) +{ + WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); + // Partial implementation of writeValue algorithm: + // https://webbluetoothchrome.github.io/web-bluetooth/#dom-bluetoothgattcharacteristic-writevalue + + // If bytes is more than 512 bytes long (the maximum length of an attribute + // value, per Long Attribute Values) return a promise rejected with an + // InvalidModificationError and abort. + if (value.byteLength() > 512) + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidModificationError, "Value can't exceed 512 bytes.")); + + // Let valueVector be a copy of the bytes held by value. + WebVector<uint8_t> valueVector(value.bytes(), value.byteLength()); + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + + ScriptPromise promise = resolver->promise(); + webbluetooth->writeValue(m_webCharacteristic->characteristicInstanceID, valueVector, new WriteValueCallback(this, resolver)); + + return promise; +} + +ScriptPromise BluetoothRemoteGATTCharacteristic::startNotifications(ScriptState* scriptState) +{ + WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + webbluetooth->startNotifications(m_webCharacteristic->characteristicInstanceID, this, new CallbackPromiseAdapter<void, BluetoothError>(resolver)); + return promise; +} + +ScriptPromise BluetoothRemoteGATTCharacteristic::stopNotifications(ScriptState* scriptState) +{ + WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + webbluetooth->stopNotifications(m_webCharacteristic->characteristicInstanceID, this, new CallbackPromiseAdapter<void, BluetoothError>(resolver)); + return promise; +} + +DEFINE_TRACE(BluetoothRemoteGATTCharacteristic) +{ + RefCountedGarbageCollectedEventTargetWithInlineData<BluetoothRemoteGATTCharacteristic>::trace(visitor); + ActiveDOMObject::trace(visitor); + visitor->trace(m_properties); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h new file mode 100644 index 0000000..3d7d20b --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h
@@ -0,0 +1,100 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BluetoothRemoteGATTCharacteristic_h +#define BluetoothRemoteGATTCharacteristic_h + +#include "bindings/core/v8/ScriptWrappable.h" +#include "core/dom/ActiveDOMObject.h" +#include "core/dom/DOMArrayPiece.h" +#include "core/dom/DOMDataView.h" +#include "modules/EventTargetModules.h" +#include "platform/heap/Handle.h" +#include "public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristic.h" +#include "public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h" +#include "wtf/OwnPtr.h" +#include "wtf/PassOwnPtr.h" +#include "wtf/text/WTFString.h" + +namespace blink { + +class BluetoothCharacteristicProperties; +class ExecutionContext; +class ScriptPromise; +class ScriptPromiseResolver; +class ScriptState; + +// BluetoothRemoteGATTCharacteristic represents a GATT Characteristic, which is a +// basic data element that provides further information about a peripheral's +// service. +// +// Callbacks providing WebBluetoothRemoteGATTCharacteristicInit objects are handled by +// CallbackPromiseAdapter templatized with this class. See this class's +// "Interface required by CallbackPromiseAdapter" section and the +// CallbackPromiseAdapter class comments. +class BluetoothRemoteGATTCharacteristic final + : public RefCountedGarbageCollectedEventTargetWithInlineData<BluetoothRemoteGATTCharacteristic> + , public ActiveDOMObject + , public WebBluetoothRemoteGATTCharacteristic { + USING_PRE_FINALIZER(BluetoothRemoteGATTCharacteristic, dispose); + DEFINE_WRAPPERTYPEINFO(); + REFCOUNTED_GARBAGE_COLLECTED_EVENT_TARGET(BluetoothRemoteGATTCharacteristic); + WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(BluetoothRemoteGATTCharacteristic); +public: + explicit BluetoothRemoteGATTCharacteristic(ExecutionContext*, PassOwnPtr<WebBluetoothRemoteGATTCharacteristicInit>); + + // Interface required by CallbackPromiseAdapter. + using WebType = OwnPtr<WebBluetoothRemoteGATTCharacteristicInit>; + static BluetoothRemoteGATTCharacteristic* take(ScriptPromiseResolver*, PassOwnPtr<WebBluetoothRemoteGATTCharacteristicInit>); + + // Save value. + void setValue(const PassRefPtr<DOMDataView>&); + + // WebBluetoothRemoteGATTCharacteristic interface: + void dispatchCharacteristicValueChanged(const WebVector<uint8_t>&) override; + + // ActiveDOMObject interface. + void stop() override; + + // USING_PRE_FINALIZER interface. + // Called before the object gets garbage collected. + void dispose(); + + // Notify our embedder that we should stop any notifications. + // The function only notifies the embedder once. + void notifyCharacteristicObjectRemoved(); + + // EventTarget methods: + const AtomicString& interfaceName() const override; + ExecutionContext* executionContext() const; + + // Interface required by garbage collection. + DECLARE_VIRTUAL_TRACE(); + + // IDL exposed interface: + String uuid() { return m_webCharacteristic->uuid; } + + BluetoothCharacteristicProperties* properties() { return m_properties; } + PassRefPtr<DOMDataView> value() const { return m_value; } + ScriptPromise readValue(ScriptState*); + ScriptPromise writeValue(ScriptState*, const DOMArrayPiece&); + ScriptPromise startNotifications(ScriptState*); + ScriptPromise stopNotifications(ScriptState*); + + DEFINE_ATTRIBUTE_EVENT_LISTENER(characteristicvaluechanged); + +protected: + // EventTarget overrides. + bool addEventListenerInternal(const AtomicString& eventType, PassRefPtrWillBeRawPtr<EventListener>, const EventListenerOptions&) override; + +private: + OwnPtr<WebBluetoothRemoteGATTCharacteristicInit> m_webCharacteristic; + bool m_stopped; + Member<BluetoothCharacteristicProperties> m_properties; + RefPtr<DOMDataView> m_value; +}; + +} // namespace blink + +#endif // BluetoothRemoteGATTCharacteristic_h
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.idl b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.idl new file mode 100644 index 0000000..dad100f --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.idl
@@ -0,0 +1,31 @@ +// 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. + +// https://webbluetoothchrome.github.io/web-bluetooth/#idl-def-bluetoothgattcharacteristic + +// Implement BluetoothRemoteGATTCharacteristic interface: https://crbug.com/483344 + +[ + GarbageCollected, + ActiveDOMObject, + RuntimeEnabled=WebBluetooth, + OriginTrialEnabled=WebBluetooth, +] interface BluetoothRemoteGATTCharacteristic : EventTarget {//: CharacteristicEventHandlers { + // TODO(ortuno): Add test to make sure service matches the service + // used to call getCharacteristic. + // readonly attribute BluetoothRemoteGATTService service; + readonly attribute UUID uuid; + readonly attribute BluetoothCharacteristicProperties properties; + readonly attribute DataView? value; + // Promise<BluetoothRemoteGATTDescriptor> getDescriptor(BluetoothDescriptorUUID descriptor); + // Promise<sequence<BluetoothRemoteGATTDescriptor>> getDescriptors(optional BluetoothDescriptorUUID descriptor); + [CallWith=ScriptState] Promise<DataView> readValue(); + [CallWith=ScriptState] Promise<void> writeValue(BufferSource value); + [CallWith=ScriptState] Promise<void> startNotifications(); + [CallWith=ScriptState] Promise<void> stopNotifications(); + + // TODO(ortuno): Move this to CharacteristicEventHandlers. + // http://crbug.com/537459 + attribute EventHandler oncharacteristicvaluechanged; +};
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp new file mode 100644 index 0000000..9ca7aad1 --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp
@@ -0,0 +1,120 @@ +// 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. + +#include "modules/bluetooth/BluetoothRemoteGATTServer.h" + +#include "bindings/core/v8/CallbackPromiseAdapter.h" +#include "bindings/core/v8/ScriptPromise.h" +#include "bindings/core/v8/ScriptPromiseResolver.h" +#include "core/dom/DOMException.h" +#include "core/dom/Document.h" +#include "core/dom/ExceptionCode.h" +#include "core/page/Page.h" +#include "modules/bluetooth/BluetoothError.h" +#include "modules/bluetooth/BluetoothRemoteGATTService.h" +#include "modules/bluetooth/BluetoothSupplement.h" +#include "modules/bluetooth/BluetoothUUID.h" +#include "public/platform/modules/bluetooth/WebBluetooth.h" +#include "wtf/OwnPtr.h" + +namespace blink { +namespace { + +const char kPageHiddenError[] = "Connection is only allowed while the page is visible. This is a temporary measure until we are able to effectively communicate to the user that a page is connected to a device."; + +} + +BluetoothRemoteGATTServer::BluetoothRemoteGATTServer(BluetoothDevice* device) + : m_device(device) + , m_connected(false) +{ +} + +BluetoothRemoteGATTServer* BluetoothRemoteGATTServer::create(BluetoothDevice* device) +{ + return new BluetoothRemoteGATTServer(device); +} + +DEFINE_TRACE(BluetoothRemoteGATTServer) +{ + visitor->trace(m_device); +} + +class ConnectCallback : public WebBluetoothRemoteGATTServerConnectCallbacks { +public: + ConnectCallback(BluetoothDevice* device, ScriptPromiseResolver* resolver) + : m_device(device) + , m_resolver(resolver) {} + + void onSuccess() override + { + if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) + return; + m_device->gatt()->setConnected(true); + if (!m_device->page()->isPageVisible()) { + // TODO(ortuno): Allow connections when the tab is in the background. + // This is a short term solution instead of implementing a tab indicator + // for bluetooth connections. + // https://crbug.com/579746 + m_device->disconnectGATTIfConnected(); + m_resolver->reject(DOMException::create(SecurityError, kPageHiddenError)); + } else { + m_resolver->resolve(m_device->gatt()); + } + } + + void onError(const WebBluetoothError& e) override + { + if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped()) + return; + m_resolver->reject(BluetoothError::take(m_resolver, e)); + } +private: + Persistent<BluetoothDevice> m_device; + Persistent<ScriptPromiseResolver> m_resolver; +}; + +ScriptPromise BluetoothRemoteGATTServer::connect(ScriptState* scriptState) +{ + // TODO(ortuno): Allow connections when the tab is in the background. + // This is a short term solution instead of implementing a tab indicator + // for bluetooth connections. + // https://crbug.com/579746 + if (!toDocument(scriptState->executionContext())->page()->isPageVisible()) { + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SecurityError, kPageHiddenError)); + } + + WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); + if (!webbluetooth) + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError)); + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + webbluetooth->connect(device()->id(), new ConnectCallback(device(), resolver)); + return promise; +} + +void BluetoothRemoteGATTServer::disconnect(ScriptState* scriptState) +{ + m_connected = false; + WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); + webbluetooth->disconnect(device()->id()); +} + +ScriptPromise BluetoothRemoteGATTServer::getPrimaryService(ScriptState* scriptState, const StringOrUnsignedLong& service, ExceptionState& exceptionState) +{ + WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); + + String serviceUUID = BluetoothUUID::getService(service, exceptionState); + if (exceptionState.hadException()) + return exceptionState.reject(scriptState); + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + webbluetooth->getPrimaryService(device()->id(), serviceUUID, new CallbackPromiseAdapter<BluetoothRemoteGATTService, BluetoothError>(resolver)); + + return promise; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.h new file mode 100644 index 0000000..417f2e5 --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.h
@@ -0,0 +1,52 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BluetoothRemoteGATTServer_h +#define BluetoothRemoteGATTServer_h + +#include "bindings/core/v8/ScriptWrappable.h" +#include "bindings/modules/v8/UnionTypesModules.h" +#include "modules/bluetooth/BluetoothDevice.h" +#include "platform/heap/Heap.h" +#include "wtf/OwnPtr.h" +#include "wtf/PassOwnPtr.h" +#include "wtf/text/WTFString.h" + +namespace blink { + +class BluetoothDevice; +class ScriptPromise; +class ScriptPromiseResolver; +class ScriptState; + +// BluetoothRemoteGATTServer provides a way to interact with a connected bluetooth peripheral. +class BluetoothRemoteGATTServer final + : public GarbageCollectedFinalized<BluetoothRemoteGATTServer> + , public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); +public: + BluetoothRemoteGATTServer(BluetoothDevice*); + + static BluetoothRemoteGATTServer* create(BluetoothDevice*); + + void setConnected(bool connected) { m_connected = connected; } + + // Interface required by Garbage Collectoin: + DECLARE_VIRTUAL_TRACE(); + + // IDL exposed interface: + BluetoothDevice* device() { return m_device; } + bool connected() { return m_connected; } + ScriptPromise connect(ScriptState*); + void disconnect(ScriptState*); + ScriptPromise getPrimaryService(ScriptState*, const StringOrUnsignedLong& service, ExceptionState&); + +private: + Member<BluetoothDevice> m_device; + bool m_connected; +}; + +} // namespace blink + +#endif // BluetoothDevice_h
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.idl b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.idl new file mode 100644 index 0000000..fee90a2b --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.idl
@@ -0,0 +1,21 @@ +// 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. + +// https://webbluetoothchrome.github.io/web-bluetooth/#idl-def-bluetoothgattremoteserver + +// Implement BluetoothGATTRemoteServer interface: https://crbug.com/476735 + +[ + GarbageCollected, + RuntimeEnabled=WebBluetooth, + OriginTrialEnabled=WebBluetooth, +] interface BluetoothRemoteGATTServer +{ + readonly attribute BluetoothDevice device; + readonly attribute boolean connected; + [CallWith=ScriptState] Promise<BluetoothRemoteGATTServer> connect(); + [CallWith=ScriptState] void disconnect(); + [CallWith=ScriptState, RaisesException] Promise<BluetoothRemoteGATTService> getPrimaryService (BluetoothServiceUUID service); + // Promise<sequence<BluetoothRemoteGATTService>> getPrimaryServices (optional BluetoothServiceUUID service); +};
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp new file mode 100644 index 0000000..9c71c57 --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp
@@ -0,0 +1,49 @@ +// 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. + +#include "modules/bluetooth/BluetoothRemoteGATTService.h" + +#include "bindings/core/v8/CallbackPromiseAdapter.h" +#include "bindings/core/v8/ScriptPromise.h" +#include "bindings/core/v8/ScriptPromiseResolver.h" +#include "core/dom/DOMException.h" +#include "core/dom/ExceptionCode.h" +#include "modules/bluetooth/BluetoothError.h" +#include "modules/bluetooth/BluetoothRemoteGATTCharacteristic.h" +#include "modules/bluetooth/BluetoothSupplement.h" +#include "modules/bluetooth/BluetoothUUID.h" +#include "public/platform/modules/bluetooth/WebBluetooth.h" + +namespace blink { + +BluetoothRemoteGATTService::BluetoothRemoteGATTService(PassOwnPtr<WebBluetoothRemoteGATTService> webService) + : m_webService(webService) +{ +} + +BluetoothRemoteGATTService* BluetoothRemoteGATTService::take(ScriptPromiseResolver*, PassOwnPtr<WebBluetoothRemoteGATTService> webService) +{ + if (!webService) { + return nullptr; + } + return new BluetoothRemoteGATTService(webService); +} + +ScriptPromise BluetoothRemoteGATTService::getCharacteristic(ScriptState* scriptState, + const StringOrUnsignedLong& characteristic, ExceptionState& exceptionState) +{ + WebBluetooth* webbluetooth = BluetoothSupplement::fromScriptState(scriptState); + + String characteristicUUID = BluetoothUUID::getCharacteristic(characteristic, exceptionState); + if (exceptionState.hadException()) + return exceptionState.reject(scriptState); + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + webbluetooth->getCharacteristic(m_webService->serviceInstanceID, characteristicUUID, new CallbackPromiseAdapter<BluetoothRemoteGATTCharacteristic, BluetoothError>(resolver)); + + return promise; +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h new file mode 100644 index 0000000..55d64d0 --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h
@@ -0,0 +1,55 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BluetoothRemoteGATTService_h +#define BluetoothRemoteGATTService_h + +#include "bindings/core/v8/ScriptWrappable.h" +#include "bindings/modules/v8/UnionTypesModules.h" +#include "platform/heap/Handle.h" +#include "public/platform/modules/bluetooth/WebBluetoothRemoteGATTService.h" +#include "wtf/OwnPtr.h" +#include "wtf/PassOwnPtr.h" +#include "wtf/text/WTFString.h" + +namespace blink { + +class ScriptPromise; +class ScriptPromiseResolver; +class ScriptState; + +// Represents a GATT Service within a Bluetooth Peripheral, a collection of +// characteristics and relationships to other services that encapsulate the +// behavior of part of a device. +// +// Callbacks providing WebBluetoothRemoteGATTService objects are handled by +// CallbackPromiseAdapter templatized with this class. See this class's +// "Interface required by CallbackPromiseAdapter" section and the +// CallbackPromiseAdapter class comments. +class BluetoothRemoteGATTService final + : public GarbageCollectedFinalized<BluetoothRemoteGATTService> + , public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); +public: + explicit BluetoothRemoteGATTService(PassOwnPtr<WebBluetoothRemoteGATTService>); + + // Interface required by CallbackPromiseAdapter: + using WebType = OwnPtr<WebBluetoothRemoteGATTService>; + static BluetoothRemoteGATTService* take(ScriptPromiseResolver*, PassOwnPtr<WebBluetoothRemoteGATTService>); + + // Interface required by garbage collection. + DEFINE_INLINE_TRACE() { } + + // IDL exposed interface: + String uuid() { return m_webService->uuid; } + bool isPrimary() { return m_webService->isPrimary; } + ScriptPromise getCharacteristic(ScriptState*, const StringOrUnsignedLong& characteristic, ExceptionState&); + +private: + OwnPtr<WebBluetoothRemoteGATTService> m_webService; +}; + +} // namespace blink + +#endif // BluetoothRemoteGATTService_h
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.idl b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.idl new file mode 100644 index 0000000..6579a6c --- /dev/null +++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.idl
@@ -0,0 +1,23 @@ +// 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. + +// https://webbluetoothchrome.github.io/web-bluetooth/#idl-def-bluetoothgattservice + +// Implement BluetoothRemoteGATTService interface: https://crbug.com/483342 + +[ + GarbageCollected, + RuntimeEnabled=WebBluetooth, + OriginTrialEnabled=WebBluetooth, +] interface BluetoothRemoteGATTService { // : ServiceEventHandlers { + readonly attribute UUID uuid; + readonly attribute boolean isPrimary; + // TODO(ortuno): Once device is implemented test that it matches + // the original device. + // readonly attribute BluetoothDevice device; + [RaisesException, CallWith=ScriptState] Promise<BluetoothRemoteGATTCharacteristic> getCharacteristic(BluetoothCharacteristicUUID characteristic); + // Promise<sequence<BluetoothRemoteGATTCharacteristic>> getCharacteristics(optional BluetoothCharacteristicUUID characteristic); + // Promise<BluetoothRemoteGATTService> getIncludedService(BluetoothServiceUUID service); + // Promise<sequence<BluetoothRemoteGATTService>> getIncludedServices(optional BluetoothServiceUUID service); +};
diff --git a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp index 7fe1240..ee6b0908 100644 --- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp +++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
@@ -414,6 +414,7 @@ modifiableState().setStrokeStyle(canvasStyle); modifiableState().setUnparsedStrokeColor(colorString); + modifiableState().clearResolvedFilter(); } void CanvasRenderingContext2D::fillStyle(StringOrCanvasGradientOrCanvasPattern& returnValue) const @@ -454,6 +455,7 @@ ASSERT(canvasStyle); modifiableState().setFillStyle(canvasStyle); modifiableState().setUnparsedFillColor(colorString); + modifiableState().clearResolvedFilter(); } double CanvasRenderingContext2D::lineWidth() const @@ -1985,6 +1987,9 @@ canvas()->disableDeferral(DisableDeferralReasonSubPixelTextAntiAliasingSupport); const Font& font = accessFont(); + if (!font.primaryFont()) + return; + const FontMetrics& fontMetrics = font.fontMetrics(); // FIXME: Need to turn off font smoothing.
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialRequestOptions.idl b/third_party/WebKit/Source/modules/credentialmanager/CredentialRequestOptions.idl index 2a70c7f..0e5902aa 100644 --- a/third_party/WebKit/Source/modules/credentialmanager/CredentialRequestOptions.idl +++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialRequestOptions.idl
@@ -8,5 +8,5 @@ FederatedCredentialRequestOptions federated; boolean password = false; - boolean suppressUI = false; + boolean unmediated = false; };
diff --git a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp index 5a257e9..bf0330af 100644 --- a/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp +++ b/third_party/WebKit/Source/modules/credentialmanager/CredentialsContainer.cpp
@@ -139,11 +139,9 @@ } } - UseCounter::count(scriptState->executionContext(), - options.suppressUI() ? UseCounter::CredentialManagerGetWithoutUI - : UseCounter::CredentialManagerGetWithUI); + UseCounter::count(scriptState->executionContext(), options.unmediated() ? UseCounter::CredentialManagerGetWithoutUI : UseCounter::CredentialManagerGetWithUI); - CredentialManagerClient::from(scriptState->executionContext())->dispatchGet(options.suppressUI(), options.password(), providers, new RequestCallbacks(resolver)); + CredentialManagerClient::from(scriptState->executionContext())->dispatchGet(options.unmediated(), options.password(), providers, new RequestCallbacks(resolver)); return promise; }
diff --git a/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.h b/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.h index e0eb8f59..197f882e 100644 --- a/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.h +++ b/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.h
@@ -25,6 +25,8 @@ class MODULES_EXPORT BodyStreamBuffer final : public GarbageCollectedFinalized<BodyStreamBuffer>, public UnderlyingSource, public WebDataConsumerHandle::Client { WTF_MAKE_NONCOPYABLE(BodyStreamBuffer); USING_GARBAGE_COLLECTED_MIXIN(BodyStreamBuffer); + // Needed because we have to release |m_reader| promptly. + EAGERLY_FINALIZE(); public: // |handle| cannot be null and cannot be locked. explicit BodyStreamBuffer(PassOwnPtr<FetchDataConsumerHandle> /* handle */);
diff --git a/third_party/WebKit/Source/modules/geolocation/GeoNotifier.cpp b/third_party/WebKit/Source/modules/geolocation/GeoNotifier.cpp index 7100668..1a0c8a7 100644 --- a/third_party/WebKit/Source/modules/geolocation/GeoNotifier.cpp +++ b/third_party/WebKit/Source/modules/geolocation/GeoNotifier.cpp
@@ -7,6 +7,7 @@ #include "modules/geolocation/Geolocation.h" #include "modules/geolocation/PositionError.h" #include "modules/geolocation/PositionOptions.h" +#include "platform/Histogram.h" namespace blink { @@ -20,6 +21,9 @@ { ASSERT(m_geolocation); ASSERT(m_successCallback); + + DEFINE_STATIC_LOCAL(CustomCountHistogram, timeoutHistogram, ("Geolocation.Timeout", 0, 1000 * 60 * 10 /* 10 minute max */, 20 /* buckets */)); + timeoutHistogram.count(m_options.timeout()); } DEFINE_TRACE(GeoNotifier) @@ -94,6 +98,10 @@ if (m_errorCallback) m_errorCallback->handleEvent(PositionError::create(PositionError::TIMEOUT, "Timeout expired")); + + DEFINE_STATIC_LOCAL(CustomCountHistogram, timeoutExpiredHistogram, ("Geolocation.TimeoutExpired", 0, 1000 * 60 * 10 /* 10 minute max */, 20 /* buckets */)); + timeoutExpiredHistogram.count(m_options.timeout()); + m_geolocation->requestTimedOut(this); }
diff --git a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.idl b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.idl index 083c3cf..346eba8 100644 --- a/third_party/WebKit/Source/modules/mediasource/SourceBuffer.idl +++ b/third_party/WebKit/Source/modules/mediasource/SourceBuffer.idl
@@ -37,7 +37,6 @@ [ ActiveDOMObject, - NoInterfaceObject, RuntimeEnabled=MediaSource, ] interface SourceBuffer : EventTarget {
diff --git a/third_party/WebKit/Source/modules/mediasource/SourceBufferList.idl b/third_party/WebKit/Source/modules/mediasource/SourceBufferList.idl index eb4db73..2cd26cc 100644 --- a/third_party/WebKit/Source/modules/mediasource/SourceBufferList.idl +++ b/third_party/WebKit/Source/modules/mediasource/SourceBufferList.idl
@@ -30,7 +30,6 @@ [ GarbageCollected, - NoInterfaceObject, RuntimeEnabled=MediaSource, ] interface SourceBufferList : EventTarget { readonly attribute unsigned long length;
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaConstraintsTest.cpp b/third_party/WebKit/Source/modules/mediastream/MediaConstraintsTest.cpp index 43dcac1..adc9554 100644 --- a/third_party/WebKit/Source/modules/mediastream/MediaConstraintsTest.cpp +++ b/third_party/WebKit/Source/modules/mediastream/MediaConstraintsTest.cpp
@@ -90,3 +90,27 @@ EXPECT_TRUE(theSet.hasMandatoryOutsideSet({ "width" }, foundName)); EXPECT_EQ("googPayloadPadding", foundName); } + +TEST(MediaTrackConstraintsTest, SetToString) +{ + blink::WebMediaTrackConstraintSet theSet; + EXPECT_EQ("", theSet.toString()); + theSet.width.setMax(240); + EXPECT_EQ("width: {max: 240}", theSet.toString().utf8()); + theSet.echoCancellation.setIdeal(true); + EXPECT_EQ("width: {max: 240}, echoCancellation: {ideal: true}", theSet.toString().utf8()); +} + +TEST(MediaTrackConstraintsTest, ConstraintsToString) +{ + blink::WebMediaConstraints theConstraints; + blink::WebMediaTrackConstraintSet basic; + blink::WebVector<blink::WebMediaTrackConstraintSet> advanced(static_cast<size_t>(1)); + basic.width.setMax(240); + advanced[0].echoCancellation.setExact(true); + theConstraints.initialize(basic, advanced); + EXPECT_EQ("{width: {max: 240}, advanced: [{echoCancellation: {exact: true}}]}", theConstraints.toString().utf8()); + + blink::WebMediaConstraints nullConstraints; + EXPECT_EQ("", nullConstraints.toString().utf8()); +}
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaErrorState.cpp b/third_party/WebKit/Source/modules/mediastream/MediaErrorState.cpp index e52cb02..1480b0e 100644 --- a/third_party/WebKit/Source/modules/mediastream/MediaErrorState.cpp +++ b/third_party/WebKit/Source/modules/mediastream/MediaErrorState.cpp
@@ -49,7 +49,7 @@ void MediaErrorState::throwDOMException(const ExceptionCode& code, const String& message) { - m_errorType = DOMError; + m_errorType = DOMException; m_code = code; m_message = message; } @@ -73,7 +73,7 @@ bool MediaErrorState::canGenerateException() { - return m_errorType == TypeError || m_errorType == DOMError; + return m_errorType == TypeError || m_errorType == DOMException; } void MediaErrorState::raiseException(ExceptionState& target) @@ -85,7 +85,7 @@ case TypeError: target.throwTypeError(m_message); break; - case DOMError: + case DOMException: target.throwDOMException(m_code, m_message); break; case ConstraintError: @@ -96,9 +96,34 @@ // TODO(hta): Remove this code. https://crbug.com/576581 target.throwDOMException(NotSupportedError, "Unsatisfiable constraint " + m_constraint); break; + default: + ASSERT_NOT_REACHED(); } } +String MediaErrorState::getErrorMessage() +{ + switch (m_errorType) { + case NoError: + ASSERT_NOT_REACHED(); + break; + case TypeError: + case DOMException: + return m_message; + case ConstraintError: + // This is for the cases where we can't pass back a + // NavigatorUserMediaError. + // So far, we have this in the constructor of RTCPeerConnection, + // which is due to be deprecated. + // TODO(hta): Remove this code. https://crbug.com/576581 + return "Unsatisfiable constraint " + m_constraint; + default: + ASSERT_NOT_REACHED(); + } + + return String(); +} + NavigatorUserMediaError* MediaErrorState::createError() { ASSERT(m_errorType == ConstraintError);
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaErrorState.h b/third_party/WebKit/Source/modules/mediastream/MediaErrorState.h index cd07d67..eaf782f 100644 --- a/third_party/WebKit/Source/modules/mediastream/MediaErrorState.h +++ b/third_party/WebKit/Source/modules/mediastream/MediaErrorState.h
@@ -39,7 +39,7 @@ // A class that is able to be used like ExceptionState for carrying // information about an error up the stack, but it is up to the higher -// level code whether it produces a DOMerror or a NavigatorUserMediaError. +// level code whether it produces a DOMException or a NavigatorUserMediaError. class MediaErrorState { public: MediaErrorState(); @@ -51,12 +51,13 @@ bool hadException(); bool canGenerateException(); void raiseException(ExceptionState&); + String getErrorMessage(); NavigatorUserMediaError* createError(); private: enum ErrorType { NoError, TypeError, - DOMError, + DOMException, ConstraintError }; ErrorType m_errorType;
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStream.idl b/third_party/WebKit/Source/modules/mediastream/MediaStream.idl index 96d250da..146dff17 100644 --- a/third_party/WebKit/Source/modules/mediastream/MediaStream.idl +++ b/third_party/WebKit/Source/modules/mediastream/MediaStream.idl
@@ -49,7 +49,7 @@ attribute EventHandler onactive; attribute EventHandler oninactive; // DEPRECATED - attribute EventHandler onended; + [MeasureAs=MediaStreamOnEnded] attribute EventHandler onended; attribute EventHandler onaddtrack; attribute EventHandler onremovetrack;
diff --git a/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.cpp b/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.cpp index d97557f..e9d8a83 100644 --- a/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.cpp +++ b/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.cpp
@@ -35,10 +35,15 @@ #include "bindings/core/v8/ExceptionState.h" #include "bindings/core/v8/Nullable.h" #include "bindings/core/v8/ScriptPromiseResolver.h" +#include "bindings/core/v8/ScriptState.h" +#include "bindings/core/v8/ScriptValue.h" +#include "bindings/core/v8/V8ThrowException.h" +#include "bindings/modules/v8/UnionTypesModules.h" #include "bindings/modules/v8/V8RTCCertificate.h" #include "core/dom/Document.h" #include "core/dom/ExceptionCode.h" #include "core/dom/ExecutionContext.h" +#include "core/dom/Microtask.h" #include "core/frame/Deprecation.h" #include "core/frame/LocalFrame.h" #include "core/html/VoidCallback.h" @@ -54,10 +59,12 @@ #include "modules/mediastream/RTCIceCandidateEvent.h" #include "modules/mediastream/RTCSessionDescription.h" #include "modules/mediastream/RTCSessionDescriptionCallback.h" +#include "modules/mediastream/RTCSessionDescriptionInit.h" #include "modules/mediastream/RTCSessionDescriptionRequestImpl.h" #include "modules/mediastream/RTCStatsCallback.h" #include "modules/mediastream/RTCStatsRequestImpl.h" #include "modules/mediastream/RTCVoidRequestImpl.h" +#include "modules/mediastream/RTCVoidRequestPromiseImpl.h" #include "platform/mediastream/RTCConfiguration.h" #include "platform/mediastream/RTCOfferOptions.h" #include "public/platform/Platform.h" @@ -81,16 +88,84 @@ namespace { -static bool throwExceptionIfSignalingStateClosed(RTCPeerConnection::SignalingState state, ExceptionState& exceptionState) +const char kSignalingStateClosedMessage[] = "The RTCPeerConnection's signalingState is 'closed'."; + +bool throwExceptionIfSignalingStateClosed(RTCPeerConnection::SignalingState state, ExceptionState& exceptionState) { if (state == RTCPeerConnection::SignalingStateClosed) { - exceptionState.throwDOMException(InvalidStateError, "The RTCPeerConnection's signalingState is 'closed'."); + exceptionState.throwDOMException(InvalidStateError, kSignalingStateClosedMessage); return true; } return false; } +// Helper class for running error callbacks asynchronously +class ErrorCallbackTask : public WebTaskRunner::Task { +public: + static PassOwnPtr<ErrorCallbackTask> create(RTCErrorCallback* errorCallback, const String& errorMessage) + { + return adoptPtr(new ErrorCallbackTask(errorCallback, errorMessage)); + } + + ~ErrorCallbackTask() override = default; + + void run() override + { + m_errorCallback->handleEvent(m_errorMessage); + } + +private: + ErrorCallbackTask(RTCErrorCallback* errorCallback, const String& errorMessage) + : m_errorCallback(errorCallback) + , m_errorMessage(errorMessage) + { + ASSERT(errorCallback); + } + + Persistent<RTCErrorCallback> m_errorCallback; + String m_errorMessage; +}; + +void asyncCallErrorCallback(RTCErrorCallback* errorCallback, const String& errorMessage) +{ + Microtask::enqueueMicrotask(ErrorCallbackTask::create(errorCallback, errorMessage)); +} + +bool callErrorCallbackIfSignalingStateClosed(RTCPeerConnection::SignalingState state, RTCErrorCallback* errorCallback) +{ + if (state == RTCPeerConnection::SignalingStateClosed) { + if (errorCallback) + asyncCallErrorCallback(errorCallback, kSignalingStateClosedMessage); + + return true; + } + + return false; +} + +bool isIceCandidateMissingSdp(const RTCIceCandidateInitOrRTCIceCandidate& candidate) +{ + if (candidate.isRTCIceCandidateInit()) { + const RTCIceCandidateInit& iceCandidateInit = candidate.getAsRTCIceCandidateInit(); + return !iceCandidateInit.hasSdpMid() && !iceCandidateInit.hasSdpMLineIndex(); + } + + ASSERT(candidate.isRTCIceCandidate()); + return false; +} + +WebRTCICECandidate convertToWebRTCIceCandidate(const RTCIceCandidateInitOrRTCIceCandidate& candidate) +{ + if (candidate.isRTCIceCandidateInit()) { + const RTCIceCandidateInit& iceCandidateInit = candidate.getAsRTCIceCandidateInit(); + return WebRTCICECandidate(iceCandidateInit.candidate(), iceCandidateInit.sdpMid(), iceCandidateInit.sdpMLineIndex()); + } + + ASSERT(candidate.isRTCIceCandidate()); + return candidate.getAsRTCIceCandidate()->webCandidate(); +} + // Helper class for RTCPeerConnection::generateCertificate. class WebRTCCertificateObserver : public WebCallbacks<WebRTCCertificate*, void> { public: @@ -460,8 +535,21 @@ m_peerHandler->createAnswer(request, constraints); } -void RTCPeerConnection::setLocalDescription(ExecutionContext* context, RTCSessionDescription* sessionDescription, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) +ScriptPromise RTCPeerConnection::setLocalDescription(ScriptState* scriptState, const RTCSessionDescriptionInit& sessionDescriptionInit) { + if (m_signalingState == SignalingStateClosed) + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, kSignalingStateClosedMessage)); + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + RTCVoidRequest* request = RTCVoidRequestPromiseImpl::create(this, resolver, InvalidAccessError); + m_peerHandler->setLocalDescription(request, WebRTCSessionDescription(sessionDescriptionInit.type(), sessionDescriptionInit.sdp())); + return promise; +} + +ScriptPromise RTCPeerConnection::setLocalDescription(ScriptState* scriptState, RTCSessionDescription* sessionDescription, VoidCallback* successCallback, RTCErrorCallback* errorCallback) +{ + ExecutionContext* context = scriptState->executionContext(); if (successCallback && errorCallback) { UseCounter::count(context, UseCounter::RTCPeerConnectionSetLocalDescriptionLegacyCompliant); } else { @@ -471,13 +559,14 @@ UseCounter::count(context, UseCounter::RTCPeerConnectionSetLocalDescriptionLegacyNoFailureCallback); } - if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) - return; + if (callErrorCallbackIfSignalingStateClosed(m_signalingState, errorCallback)) + return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate())); ASSERT(sessionDescription); RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); m_peerHandler->setLocalDescription(request, sessionDescription->webSessionDescription()); + return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate())); } RTCSessionDescription* RTCPeerConnection::localDescription() @@ -489,8 +578,21 @@ return RTCSessionDescription::create(webSessionDescription); } -void RTCPeerConnection::setRemoteDescription(ExecutionContext* context, RTCSessionDescription* sessionDescription, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) +ScriptPromise RTCPeerConnection::setRemoteDescription(ScriptState* scriptState, const RTCSessionDescriptionInit& sessionDescriptionInit) { + if (m_signalingState == SignalingStateClosed) + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, kSignalingStateClosedMessage)); + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + RTCVoidRequest* request = RTCVoidRequestPromiseImpl::create(this, resolver, InvalidAccessError); + m_peerHandler->setRemoteDescription(request, WebRTCSessionDescription(sessionDescriptionInit.type(), sessionDescriptionInit.sdp())); + return promise; +} + +ScriptPromise RTCPeerConnection::setRemoteDescription(ScriptState* scriptState, RTCSessionDescription* sessionDescription, VoidCallback* successCallback, RTCErrorCallback* errorCallback) +{ + ExecutionContext* context = scriptState->executionContext(); if (successCallback && errorCallback) { UseCounter::count(context, UseCounter::RTCPeerConnectionSetRemoteDescriptionLegacyCompliant); } else { @@ -500,13 +602,14 @@ UseCounter::count(context, UseCounter::RTCPeerConnectionSetRemoteDescriptionLegacyNoFailureCallback); } - if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) - return; + if (callErrorCallbackIfSignalingStateClosed(m_signalingState, errorCallback)) + return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate())); ASSERT(sessionDescription); RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); m_peerHandler->setRemoteDescription(request, sessionDescription->webSessionDescription()); + return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate())); } RTCSessionDescription* RTCPeerConnection::remoteDescription() @@ -608,33 +711,41 @@ return promise; } -void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, ExceptionState& exceptionState) +ScriptPromise RTCPeerConnection::addIceCandidate(ScriptState* scriptState, const RTCIceCandidateInitOrRTCIceCandidate& candidate) { - if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) - return; + if (m_signalingState == SignalingStateClosed) + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, kSignalingStateClosedMessage)); - ASSERT(iceCandidate); + if (isIceCandidateMissingSdp(candidate)) + return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError(scriptState->isolate(), "Candidate missing values for both sdpMid and sdpMLineIndex")); - bool valid = m_peerHandler->addICECandidate(iceCandidate->webCandidate()); - if (!valid) - exceptionState.throwDOMException(SyntaxError, "The ICE candidate could not be added."); + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + ScriptPromise promise = resolver->promise(); + RTCVoidRequest* request = RTCVoidRequestPromiseImpl::create(this, resolver, OperationError); + WebRTCICECandidate webCandidate = convertToWebRTCIceCandidate(candidate); + bool implemented = m_peerHandler->addICECandidate(request, webCandidate); + // TODO(guidou): replace NotSupportedError when error handling in the spec is finalized. crbug.com/585621 + if (!implemented) + resolver->reject(DOMException::create(NotSupportedError, "This method is not yet implemented.")); + + return promise; } -void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) +ScriptPromise RTCPeerConnection::addIceCandidate(ScriptState* scriptState, RTCIceCandidate* iceCandidate, VoidCallback* successCallback, RTCErrorCallback* errorCallback) { - if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) - return; - ASSERT(iceCandidate); ASSERT(successCallback); ASSERT(errorCallback); - RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); + if (callErrorCallbackIfSignalingStateClosed(m_signalingState, errorCallback)) + return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate())); + RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); bool implemented = m_peerHandler->addICECandidate(request, iceCandidate->webCandidate()); - if (!implemented) { - exceptionState.throwDOMException(NotSupportedError, "This method is not yet implemented."); - } + if (!implemented) + asyncCallErrorCallback(errorCallback, "This method is not yet implemented."); + + return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate())); } String RTCPeerConnection::signalingState() const
diff --git a/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.h b/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.h index b641e08..06a9e787 100644 --- a/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.h +++ b/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.h
@@ -50,10 +50,13 @@ class RTCDTMFSender; class RTCDataChannel; class RTCErrorCallback; +class RTCIceCandidateInitOrRTCIceCandidate; class RTCOfferOptions; class RTCSessionDescription; class RTCSessionDescriptionCallback; +class RTCSessionDescriptionInit; class RTCStatsCallback; +class ScriptState; class VoidCallback; class RTCPeerConnection final @@ -72,10 +75,12 @@ void createAnswer(ExecutionContext*, RTCSessionDescriptionCallback*, RTCErrorCallback*, const Dictionary&, ExceptionState&); - void setLocalDescription(ExecutionContext*, RTCSessionDescription*, VoidCallback*, RTCErrorCallback*, ExceptionState&); + ScriptPromise setLocalDescription(ScriptState*, const RTCSessionDescriptionInit&); + ScriptPromise setLocalDescription(ScriptState*, RTCSessionDescription*, VoidCallback*, RTCErrorCallback*); RTCSessionDescription* localDescription(); - void setRemoteDescription(ExecutionContext*, RTCSessionDescription*, VoidCallback*, RTCErrorCallback*, ExceptionState&); + ScriptPromise setRemoteDescription(ScriptState*, const RTCSessionDescriptionInit&); + ScriptPromise setRemoteDescription(ScriptState*, RTCSessionDescription*, VoidCallback*, RTCErrorCallback*); RTCSessionDescription* remoteDescription(); String signalingState() const; @@ -86,10 +91,8 @@ // http://w3c.github.io/webrtc-pc/#sec.cert-mgmt static ScriptPromise generateCertificate(ScriptState*, const AlgorithmIdentifier& keygenAlgorithm, ExceptionState&); - // DEPRECATED - void addIceCandidate(RTCIceCandidate*, ExceptionState&); - - void addIceCandidate(RTCIceCandidate*, VoidCallback*, RTCErrorCallback*, ExceptionState&); + ScriptPromise addIceCandidate(ScriptState*, const RTCIceCandidateInitOrRTCIceCandidate&); + ScriptPromise addIceCandidate(ScriptState*, RTCIceCandidate*, VoidCallback*, RTCErrorCallback*); String iceGatheringState() const;
diff --git a/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.idl b/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.idl index c9d1435a..2908a9c 100644 --- a/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.idl +++ b/third_party/WebKit/Source/modules/mediastream/RTCPeerConnection.idl
@@ -73,16 +73,15 @@ ] interface RTCPeerConnection : EventTarget { // Promise<RTCSessionDescription> createOffer(optional RTCOfferOptions options); // Promise<RTCSessionDescription> createAnswer(optional RTCAnswerOptions options); - // Promise<void> setLocalDescription(RTCSessionDescription description); + [CallWith=ScriptState] Promise<void> setLocalDescription(RTCSessionDescriptionInit description); readonly attribute RTCSessionDescription? localDescription; // readonly attribute RTCSessionDescription? currentLocalDescription; // readonly attribute RTCSessionDescription? pendingLocalDescription; - // Promise<void> setRemoteDescription(RTCSessionDescription description); + [CallWith=ScriptState] Promise<void> setRemoteDescription(RTCSessionDescriptionInit description); readonly attribute RTCSessionDescription? remoteDescription; // readonly attribute RTCSessionDescription? currentRemoteDescription; // readonly attribute RTCSessionDescription? pendingRemoteDescription; - // TODO(guidou): addIceCandidate() should return a Promise. - [RaisesException] void addIceCandidate(RTCIceCandidate candidate); + [CallWith=ScriptState] Promise<void> addIceCandidate ((RTCIceCandidateInit or RTCIceCandidate) candidate); readonly attribute RTCSignalingState signalingState; readonly attribute RTCIceGatheringState iceGatheringState; readonly attribute RTCIceConnectionState iceConnectionState; @@ -98,17 +97,15 @@ // attribute EventHandler onicegatheringstatechange; // https://w3c.github.io/webrtc-pc/#legacy-interface-extensions + // These methods return or will be changed to return Promise<void> because + // having Promise-based versions requires that all overloads return Promises. + [CallWith=ExecutionContext, RaisesException] void createOffer(RTCSessionDescriptionCallback successCallback, RTCErrorCallback failureCallback, optional Dictionary rtcOfferOptions); + // TODO(guidou): There should be no mediaConstraints argument. + [CallWith=ExecutionContext, RaisesException] void createAnswer(RTCSessionDescriptionCallback successCallback, RTCErrorCallback failureCallback, optional Dictionary mediaConstraints); + [CallWith=ScriptState] Promise<void> setLocalDescription(RTCSessionDescription description, VoidCallback successCallback, [Default=Undefined] optional RTCErrorCallback failureCallback); // TODO(guidou): The failureCallback argument should be non-optional. - [CallWith=ExecutionContext, RaisesException] void createOffer(RTCSessionDescriptionCallback successCallback, [Default=Undefined] optional RTCErrorCallback failureCallback, optional Dictionary rtcOfferOptions); - // TODO(guidou): None of the arguments should be optional. - [CallWith=ExecutionContext, RaisesException] void setLocalDescription(RTCSessionDescription description, [Default=Undefined] optional VoidCallback successCallback, [Default=Undefined] optional RTCErrorCallback failureCallback); - // TODO(guidou): The failureCallback argument should be non-optional, and - // there should be no mediaConstraints argument. - [CallWith=ExecutionContext, RaisesException] void createAnswer(RTCSessionDescriptionCallback successCallback, [Default=Undefined] optional RTCErrorCallback failureCallback, optional Dictionary mediaConstraints); - // TODO(guidou): The successCallback and failureCallback arguments should be - // non-optional. - [CallWith=ExecutionContext, RaisesException] void setRemoteDescription(RTCSessionDescription description, [Default=Undefined] optional VoidCallback successCallback, [Default=Undefined] optional RTCErrorCallback failureCallback); - [RaisesException] void addIceCandidate(RTCIceCandidate candidate, VoidCallback successCallback, RTCErrorCallback failureCallback); + [CallWith=ScriptState] Promise<void> setRemoteDescription(RTCSessionDescription description, VoidCallback successCallback, [Default=Undefined] optional RTCErrorCallback failureCallback); + [CallWith=ScriptState] Promise<void> addIceCandidate(RTCIceCandidate candidate, VoidCallback successCallback, RTCErrorCallback failureCallback); // TODO(guidou): The selector argument should the first (nullable, // non-optional) argument, and there should be a third failureCallback // argument.
diff --git a/third_party/WebKit/Source/modules/mediastream/RTCVoidRequestPromiseImpl.cpp b/third_party/WebKit/Source/modules/mediastream/RTCVoidRequestPromiseImpl.cpp new file mode 100644 index 0000000..2618c96 --- /dev/null +++ b/third_party/WebKit/Source/modules/mediastream/RTCVoidRequestPromiseImpl.cpp
@@ -0,0 +1,69 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "modules/mediastream/RTCVoidRequestPromiseImpl.h" + +#include "bindings/core/v8/ScriptPromiseResolver.h" +#include "core/dom/DOMException.h" +#include "modules/mediastream/RTCPeerConnection.h" + +namespace blink { + +RTCVoidRequestPromiseImpl* RTCVoidRequestPromiseImpl::create(RTCPeerConnection* requester, ScriptPromiseResolver* resolver, ExceptionCode exceptionCode) +{ + return new RTCVoidRequestPromiseImpl(requester, resolver, exceptionCode); +} + +RTCVoidRequestPromiseImpl::RTCVoidRequestPromiseImpl(RTCPeerConnection* requester, ScriptPromiseResolver* resolver, ExceptionCode exceptionCode) + : m_requester(requester) + , m_resolver(resolver) + , m_exceptionCode(exceptionCode) +{ + ASSERT(m_requester); + ASSERT(m_resolver); +} + +RTCVoidRequestPromiseImpl::~RTCVoidRequestPromiseImpl() +{ +} + +void RTCVoidRequestPromiseImpl::requestSucceeded() +{ + if (m_requester && m_requester->shouldFireDefaultCallbacks()) { + m_resolver->resolve(); + } else { + // This is needed to have the resolver release its internal resources + // while leaving the associated promise pending as specified. + m_resolver->detach(); + } + + clear(); +} + +void RTCVoidRequestPromiseImpl::requestFailed(const String& error) +{ + if (m_requester && m_requester->shouldFireDefaultCallbacks()) { + m_resolver->reject(DOMException::create(m_exceptionCode, error)); + } else { + // This is needed to have the resolver release its internal resources + // while leaving the associated promise pending as specified. + m_resolver->detach(); + } + + clear(); +} + +void RTCVoidRequestPromiseImpl::clear() +{ + m_requester.clear(); +} + +DEFINE_TRACE(RTCVoidRequestPromiseImpl) +{ + visitor->trace(m_resolver); + visitor->trace(m_requester); + RTCVoidRequest::trace(visitor); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/modules/mediastream/RTCVoidRequestPromiseImpl.h b/third_party/WebKit/Source/modules/mediastream/RTCVoidRequestPromiseImpl.h new file mode 100644 index 0000000..ca058d44 --- /dev/null +++ b/third_party/WebKit/Source/modules/mediastream/RTCVoidRequestPromiseImpl.h
@@ -0,0 +1,40 @@ +// 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 RTCVoidRequestPromiseImpl_h +#define RTCVoidRequestPromiseImpl_h + +#include "core/dom/ExceptionCode.h" +#include "platform/mediastream/RTCVoidRequest.h" +#include "wtf/text/WTFString.h" + +namespace blink { + +class ScriptPromiseResolver; +class RTCPeerConnection; + +class RTCVoidRequestPromiseImpl final : public RTCVoidRequest { +public: + static RTCVoidRequestPromiseImpl* create(RTCPeerConnection*, ScriptPromiseResolver*, ExceptionCode); + ~RTCVoidRequestPromiseImpl() override; + + // RTCVoidRequest + void requestSucceeded() override; + void requestFailed(const String& error) override; + + DECLARE_VIRTUAL_TRACE(); + +private: + RTCVoidRequestPromiseImpl(RTCPeerConnection*, ScriptPromiseResolver*, ExceptionCode); + + void clear(); + + Member<RTCPeerConnection> m_requester; + Member<ScriptPromiseResolver> m_resolver; + ExceptionCode m_exceptionCode; +}; + +} // namespace blink + +#endif // RTCVoidRequestPromiseImpl_h
diff --git a/third_party/WebKit/Source/modules/modules.gypi b/third_party/WebKit/Source/modules/modules.gypi index 08d52ce6..b4a7ee5f 100644 --- a/third_party/WebKit/Source/modules/modules.gypi +++ b/third_party/WebKit/Source/modules/modules.gypi
@@ -23,9 +23,9 @@ 'bluetooth/BluetoothAdvertisingData.idl', 'bluetooth/BluetoothDevice.idl', 'bluetooth/BluetoothCharacteristicProperties.idl', - 'bluetooth/BluetoothGATTCharacteristic.idl', - 'bluetooth/BluetoothGATTRemoteServer.idl', - 'bluetooth/BluetoothGATTService.idl', + 'bluetooth/BluetoothRemoteGATTCharacteristic.idl', + 'bluetooth/BluetoothRemoteGATTServer.idl', + 'bluetooth/BluetoothRemoteGATTService.idl', 'bluetooth/BluetoothUUID.idl', 'cachestorage/Cache.idl', 'cachestorage/CacheStorage.idl', @@ -808,12 +808,12 @@ 'bluetooth/BluetoothDevice.h', 'bluetooth/BluetoothError.cpp', 'bluetooth/BluetoothError.h', - 'bluetooth/BluetoothGATTCharacteristic.cpp', - 'bluetooth/BluetoothGATTCharacteristic.h', - 'bluetooth/BluetoothGATTRemoteServer.cpp', - 'bluetooth/BluetoothGATTRemoteServer.h', - 'bluetooth/BluetoothGATTService.cpp', - 'bluetooth/BluetoothGATTService.h', + 'bluetooth/BluetoothRemoteGATTCharacteristic.cpp', + 'bluetooth/BluetoothRemoteGATTCharacteristic.h', + 'bluetooth/BluetoothRemoteGATTServer.cpp', + 'bluetooth/BluetoothRemoteGATTServer.h', + 'bluetooth/BluetoothRemoteGATTService.cpp', + 'bluetooth/BluetoothRemoteGATTService.h', 'bluetooth/BluetoothSupplement.cpp', 'bluetooth/BluetoothSupplement.h', 'bluetooth/BluetoothUUID.cpp', @@ -1245,6 +1245,8 @@ 'mediastream/RTCStatsResponse.h', 'mediastream/RTCVoidRequestImpl.cpp', 'mediastream/RTCVoidRequestImpl.h', + 'mediastream/RTCVoidRequestPromiseImpl.cpp', + 'mediastream/RTCVoidRequestPromiseImpl.h', 'mediastream/SourceInfo.cpp', 'mediastream/SourceInfo.h', 'mediastream/URLMediaStream.cpp',
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp index 8b80148..7570d6779 100644 --- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp +++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -1170,7 +1170,7 @@ clearMask |= GL_DEPTH_BUFFER_BIT; webContext()->depthMask(true); } - if (contextAttributes.get().stencil()) { + if (contextAttributes.get().stencil() || drawingBuffer()->hasImplicitStencilBuffer()) { if (combinedClear && (mask & GL_STENCIL_BUFFER_BIT)) webContext()->clearStencil(m_clearStencil & m_stencilMask); else @@ -1685,8 +1685,16 @@ synthesizeGLError(GL_INVALID_FRAMEBUFFER_OPERATION, "clear", reason); return; } - if (clearIfComposited(mask) != CombinedClear) + if (clearIfComposited(mask) != CombinedClear) { + // If clearing the default back buffer's depth buffer, also clear the stencil buffer, if one + // was allocated implicitly. This avoids performance problems on some GPUs. + if (!m_framebufferBinding && drawingBuffer()->hasImplicitStencilBuffer() && (mask & GL_DEPTH_BUFFER_BIT)) { + // It shouldn't matter what value it's cleared to, since in other queries in the API, we + // claim that the stencil buffer doesn't exist. + mask |= GL_STENCIL_BUFFER_BIT; + } webContext()->clear(mask); + } markContextChanged(CanvasChanged); }
diff --git a/third_party/WebKit/Source/modules/worklet/Worklet.cpp b/third_party/WebKit/Source/modules/worklet/Worklet.cpp index 3fd3be7..bbab7dc 100644 --- a/third_party/WebKit/Source/modules/worklet/Worklet.cpp +++ b/third_party/WebKit/Source/modules/worklet/Worklet.cpp
@@ -4,24 +4,98 @@ #include "modules/worklet/Worklet.h" +#include "bindings/core/v8/ScriptPromiseResolver.h" +#include "bindings/core/v8/ScriptSourceCode.h" #include "bindings/core/v8/V8Binding.h" +#include "bindings/core/v8/WorkerOrWorkletScriptController.h" +#include "core/dom/DOMException.h" +#include "core/dom/ExceptionCode.h" namespace blink { // static Worklet* Worklet::create(ExecutionContext* executionContext) { - return new Worklet(executionContext); + Worklet* worklet = new Worklet(executionContext); + worklet->suspendIfNeeded(); + return worklet; } Worklet::Worklet(ExecutionContext* executionContext) - : m_workletGlobalScope(WorkletGlobalScope::create(executionContext->url(), executionContext->userAgent(), toIsolate(executionContext))) + : ActiveDOMObject(executionContext) + , m_workletGlobalScope(WorkletGlobalScope::create(executionContext->url(), executionContext->userAgent(), executionContext->securityOrigin(), toIsolate(executionContext))) { } +ScriptPromise Worklet::import(ScriptState* scriptState, const String& url) +{ + KURL scriptURL = executionContext()->completeURL(url); + if (!scriptURL.isValid()) { + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(SyntaxError, "'" + url + "' is not a valid URL.")); + } + + // TODO(ikilpatrick): Perform upfront CSP checks once we decide on a + // CSP-policy for worklets. + + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); + m_resolvers.append(resolver); + + ScriptPromise promise = resolver->promise(); + + // TODO(ikilpatrick): WorkerScriptLoader will need to be extended to allow + // module loading support. For now just fetch a 'classic' script. + + // NOTE: WorkerScriptLoader may synchronously invoke its callbacks + // (resolving the promise) before we return it. + m_scriptLoaders.append(WorkerScriptLoader::create()); + m_scriptLoaders.last()->loadAsynchronously(*executionContext(), scriptURL, DenyCrossOriginRequests, + bind(&Worklet::onResponse, this), + bind(&Worklet::onFinished, this, m_scriptLoaders.last().get(), resolver)); + + return promise; +} + +void Worklet::onResponse() +{ + // TODO(ikilpatrick): Add devtools instrumentation on worklet script + // resource loading. +} + +void Worklet::onFinished(WorkerScriptLoader* scriptLoader, ScriptPromiseResolver* resolver) +{ + if (scriptLoader->failed()) { + resolver->reject(DOMException::create(NetworkError)); + } else { + // TODO(ikilpatrick): Worklets don't have the same error behaviour + // as workers, etc. For a SyntaxError we should reject, however if + // the script throws a normal error, resolve. For now just resolve. + m_workletGlobalScope->script()->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->url())); + resolver->resolve(); + } + + size_t index = m_scriptLoaders.find(scriptLoader); + + ASSERT(index != kNotFound); + ASSERT(m_resolvers[index] == resolver); + + m_scriptLoaders.remove(index); + m_resolvers.remove(index); +} + +void Worklet::stop() +{ + m_workletGlobalScope->script()->willScheduleExecutionTermination(); + + for (auto scriptLoader : m_scriptLoaders) { + scriptLoader->cancel(); + } +} + DEFINE_TRACE(Worklet) { + visitor->trace(m_resolvers); visitor->trace(m_workletGlobalScope); + ActiveDOMObject::trace(visitor); } } // namespace blink
diff --git a/third_party/WebKit/Source/modules/worklet/Worklet.h b/third_party/WebKit/Source/modules/worklet/Worklet.h index 7af0832..91f7f2e 100644 --- a/third_party/WebKit/Source/modules/worklet/Worklet.h +++ b/third_party/WebKit/Source/modules/worklet/Worklet.h
@@ -5,28 +5,45 @@ #ifndef Worklet_h #define Worklet_h +#include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptWrappable.h" +#include "core/dom/ActiveDOMObject.h" +#include "core/frame/csp/ContentSecurityPolicy.h" +#include "core/workers/WorkerScriptLoader.h" #include "modules/worklet/WorkletGlobalScope.h" #include "platform/heap/Handle.h" namespace blink { class ExecutionContext; +class ScriptPromiseResolver; +class WorkerScriptLoader; -class Worklet final : public GarbageCollectedFinalized<Worklet>, public ScriptWrappable { - WTF_MAKE_NONCOPYABLE(Worklet); +class Worklet final : public GarbageCollectedFinalized<Worklet>, public ScriptWrappable, public ActiveDOMObject { DEFINE_WRAPPERTYPEINFO(); + WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(Worklet); + WTF_MAKE_NONCOPYABLE(Worklet); public: // The ExecutionContext argument is the parent document of the Worklet. The // Worklet inherits the url and userAgent, from the document. static Worklet* create(ExecutionContext*); + ScriptPromise import(ScriptState*, const String& url); + + // ActiveDOMObject + void stop() final; + DECLARE_TRACE(); private: explicit Worklet(ExecutionContext*); + void onResponse(); + void onFinished(WorkerScriptLoader*, ScriptPromiseResolver*); + RefPtrWillBeMember<WorkletGlobalScope> m_workletGlobalScope; + Vector<RefPtr<WorkerScriptLoader>> m_scriptLoaders; + HeapVector<Member<ScriptPromiseResolver>> m_resolvers; }; } // namespace blink
diff --git a/third_party/WebKit/Source/modules/worklet/Worklet.idl b/third_party/WebKit/Source/modules/worklet/Worklet.idl index e4ad4f1..f04dd1d 100644 --- a/third_party/WebKit/Source/modules/worklet/Worklet.idl +++ b/third_party/WebKit/Source/modules/worklet/Worklet.idl
@@ -5,7 +5,9 @@ // https://drafts.css-houdini.org/worklets/#worklet [ + ActiveDOMObject, GarbageCollected, RuntimeEnabled=Worklet, ] interface Worklet { + [CallWith=ScriptState] Promise<void> import(DOMString url); };
diff --git a/third_party/WebKit/Source/modules/worklet/WorkletGlobalScope.cpp b/third_party/WebKit/Source/modules/worklet/WorkletGlobalScope.cpp index 15819a4..9be0628 100644 --- a/third_party/WebKit/Source/modules/worklet/WorkletGlobalScope.cpp +++ b/third_party/WebKit/Source/modules/worklet/WorkletGlobalScope.cpp
@@ -9,16 +9,19 @@ namespace blink { // static -PassRefPtrWillBeRawPtr<WorkletGlobalScope> WorkletGlobalScope::create(const KURL& url, const String& userAgent, v8::Isolate* isolate) +PassRefPtrWillBeRawPtr<WorkletGlobalScope> WorkletGlobalScope::create(const KURL& url, const String& userAgent, PassRefPtr<SecurityOrigin> securityOrigin, v8::Isolate* isolate) { - RefPtrWillBeRawPtr<WorkletGlobalScope> workletGlobalScope = adoptRefWillBeNoop(new WorkletGlobalScope(url, userAgent, isolate)); + RefPtrWillBeRawPtr<WorkletGlobalScope> workletGlobalScope = adoptRefWillBeNoop(new WorkletGlobalScope(url, userAgent, securityOrigin, isolate)); workletGlobalScope->script()->initializeContextIfNeeded(); return workletGlobalScope.release(); } -WorkletGlobalScope::WorkletGlobalScope(const KURL& url, const String& userAgent, v8::Isolate* isolate) - : m_script(WorkerOrWorkletScriptController::create(this, isolate)) +WorkletGlobalScope::WorkletGlobalScope(const KURL& url, const String& userAgent, PassRefPtr<SecurityOrigin> securityOrigin, v8::Isolate* isolate) + : m_url(url) + , m_userAgent(userAgent) + , m_script(WorkerOrWorkletScriptController::create(this, isolate)) { + setSecurityOrigin(securityOrigin); } WorkletGlobalScope::~WorkletGlobalScope()
diff --git a/third_party/WebKit/Source/modules/worklet/WorkletGlobalScope.h b/third_party/WebKit/Source/modules/worklet/WorkletGlobalScope.h index e26977f..a9498de7 100644 --- a/third_party/WebKit/Source/modules/worklet/WorkletGlobalScope.h +++ b/third_party/WebKit/Source/modules/worklet/WorkletGlobalScope.h
@@ -28,9 +28,9 @@ using RefCounted<WorkletGlobalScope>::deref; #endif - // The url and userAgent arguments are inherited from the parent - // ExecutionContext for Worklets. - static PassRefPtrWillBeRawPtr<WorkletGlobalScope> create(const KURL&, const String& userAgent, v8::Isolate*); + // The url, userAgent and securityOrigin arguments are inherited from the + // parent ExecutionContext for Worklets. + static PassRefPtrWillBeRawPtr<WorkletGlobalScope> create(const KURL&, const String& userAgent, PassRefPtr<SecurityOrigin>, v8::Isolate*); ~WorkletGlobalScope() override; bool isWorkletGlobalScope() const final { return true; } @@ -74,7 +74,7 @@ void derefExecutionContext() final { deref(); } #endif - WorkletGlobalScope(const KURL&, const String& userAgent, v8::Isolate*); + WorkletGlobalScope(const KURL&, const String& userAgent, PassRefPtr<SecurityOrigin>, v8::Isolate*); const KURL& virtualURL() const final { return m_url; } KURL virtualCompleteURL(const String&) const final;
diff --git a/third_party/WebKit/Source/platform/PlatformTouchPoint.h b/third_party/WebKit/Source/platform/PlatformTouchPoint.h index e806327..b9071f1 100644 --- a/third_party/WebKit/Source/platform/PlatformTouchPoint.h +++ b/third_party/WebKit/Source/platform/PlatformTouchPoint.h
@@ -49,7 +49,7 @@ FloatPoint pos() const { return m_pos; } FloatSize radius() const { return m_radius; } float rotationAngle() const { return m_rotationAngle; } - float force() const { ASSERT(!isnan(pointerProperties().force)); return pointerProperties().force; } + float force() const { ASSERT(!std::isnan(pointerProperties().force)); return pointerProperties().force; } protected: WebPointerProperties m_pointerProperties;
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in index 0884e81..2d9b2d0 100644 --- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in +++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in
@@ -31,7 +31,7 @@ BackgroundSync status=stable CacheIgnoreSearchOption status=experimental ClientHints status=stable -ColumnFill status=experimental +ColumnFill status=stable CompositedSelectionUpdate CompositorWorker status=experimental // Unified Chrome Compositor and Blink Animations engine (Project Heaviside). crbug.com/394772 @@ -216,4 +216,4 @@ WakeLock status=experimental WebFontsIntervention status=experimental WebFontsInterventionTrigger -Worklet +Worklet status=test
diff --git a/third_party/WebKit/Source/platform/exported/WebMediaConstraints.cpp b/third_party/WebKit/Source/platform/exported/WebMediaConstraints.cpp index 079fb3c..328125b 100644 --- a/third_party/WebKit/Source/platform/exported/WebMediaConstraints.cpp +++ b/third_party/WebKit/Source/platform/exported/WebMediaConstraints.cpp
@@ -32,10 +32,42 @@ #include "wtf/PassRefPtr.h" #include "wtf/RefCounted.h" +#include "wtf/text/StringBuilder.h" +#include "wtf/text/WTFString.h" #include <math.h> namespace blink { +namespace { + +template <typename T> +void maybeEmitNamedValue(StringBuilder& builder, bool emit, const char* name, T value) +{ + if (!emit) + return; + if (builder.length() > 1) + builder.appendLiteral(", "); + builder.append(name); + builder.appendLiteral(": "); + builder.appendNumber(value); +} + +void maybeEmitNamedBoolean(StringBuilder& builder, bool emit, const char* name, bool value) +{ + if (!emit) + return; + if (builder.length() > 1) + builder.appendLiteral(", "); + builder.append(name); + builder.appendLiteral(": "); + if (value) + builder.appendLiteral("true"); + else + builder.appendLiteral("false"); +} + +} // namespace + class WebMediaConstraintsPrivate final : public RefCounted<WebMediaConstraintsPrivate> { public: static PassRefPtr<WebMediaConstraintsPrivate> create(); @@ -49,6 +81,7 @@ bool getOptionalConstraintValue(const WebString& name, WebString& value); const WebMediaTrackConstraintSet& basic() const; const WebVector<WebMediaTrackConstraintSet>& advanced() const; + const String toString() const; private: WebMediaConstraintsPrivate(const WebVector<WebMediaConstraint>& optional, const WebVector<WebMediaConstraint>& mandatory, const WebMediaTrackConstraintSet& basic, const WebVector<WebMediaTrackConstraintSet>& advanced); @@ -143,6 +176,32 @@ return m_advanced; } +const String WebMediaConstraintsPrivate::toString() const +{ + StringBuilder builder; + if (!isEmpty()) { + builder.append('{'); + builder.append(basic().toString()); + if (!advanced().isEmpty()) { + if (builder.length() > 1) + builder.appendLiteral(", "); + builder.appendLiteral("advanced: ["); + bool first = true; + for (const auto& constraintSet : advanced()) { + if (!first) + builder.appendLiteral(", "); + builder.append('{'); + builder.append(constraintSet.toString()); + builder.append('}'); + first = false; + } + builder.append(']'); + } + builder.append('}'); + } + return builder.toString(); +} + // *Constraints BaseConstraint::BaseConstraint(const char* name) @@ -191,6 +250,18 @@ return m_hasMin || m_hasMax || m_hasExact; } +WebString LongConstraint::toString() const +{ + StringBuilder builder; + builder.append('{'); + maybeEmitNamedValue(builder, m_hasMin, "min", m_min); + maybeEmitNamedValue(builder, m_hasMax, "max", m_max); + maybeEmitNamedValue(builder, m_hasExact, "exact", m_exact); + maybeEmitNamedValue(builder, m_hasIdeal, "ideal", m_ideal); + builder.append('}'); + return builder.toString(); +} + const double DoubleConstraint::kConstraintEpsilon = 0.00001; DoubleConstraint::DoubleConstraint(const char* name) @@ -230,6 +301,18 @@ return m_hasMin || m_hasMax || m_hasExact; } +WebString DoubleConstraint::toString() const +{ + StringBuilder builder; + builder.append('{'); + maybeEmitNamedValue(builder, m_hasMin, "min", m_min); + maybeEmitNamedValue(builder, m_hasMax, "max", m_max); + maybeEmitNamedValue(builder, m_hasExact, "exact", m_exact); + maybeEmitNamedValue(builder, m_hasIdeal, "ideal", m_ideal); + builder.append('}'); + return builder.toString(); +} + StringConstraint::StringConstraint(const char* name) : BaseConstraint(name) , m_exact() @@ -270,6 +353,41 @@ return m_ideal; } +WebString StringConstraint::toString() const +{ + StringBuilder builder; + builder.append('{'); + if (!m_ideal.isEmpty()) { + builder.appendLiteral("ideal: ["); + bool first = true; + for (const auto& iter : m_ideal) { + if (!first) + builder.appendLiteral(", "); + builder.append('"'); + builder.append(iter); + builder.append('"'); + first = false; + } + builder.append(']'); + } + if (!m_exact.isEmpty()) { + if (builder.length() > 1) + builder.appendLiteral(", "); + builder.appendLiteral("exact: ["); + bool first = true; + for (const auto& iter : m_exact) { + if (!first) + builder.appendLiteral(", "); + builder.append('"'); + builder.append(iter); + builder.append('"'); + } + builder.append(']'); + } + builder.append('}'); + return builder.toString(); +} + BooleanConstraint::BooleanConstraint(const char* name) : BaseConstraint(name) , m_ideal(false) @@ -297,6 +415,16 @@ return m_hasExact; } +WebString BooleanConstraint::toString() const +{ + StringBuilder builder; + builder.append('{'); + maybeEmitNamedBoolean(builder, m_hasExact, "exact", exact()); + maybeEmitNamedBoolean(builder, m_hasIdeal, "ideal", ideal()); + builder.append('}'); + return builder.toString(); +} + WebMediaTrackConstraintSet::WebMediaTrackConstraintSet() : width("width") , height("height") @@ -326,7 +454,6 @@ , googArrayGeometry("googArrayGeometry") , googAudioMirroring("googAudioMirroring") , googDAEchoCancellation("googDAEchoCancellation") - , googAecDump("googAecDump") , googNoiseReduction("googNoiseReduction") , offerToReceiveAudio("offerToReceiveAudio") , offerToReceiveVideo("offerToReceiveVideo") @@ -364,7 +491,7 @@ &googHighpassFilter, &googTypingNoiseDetection, &googExperimentalNoiseSuppression, &googBeamforming, &googArrayGeometry, &googAudioMirroring, &googDAEchoCancellation, - &googAecDump, &googNoiseReduction, &offerToReceiveAudio, + &googNoiseReduction, &offerToReceiveAudio, &offerToReceiveVideo, &voiceActivityDetection, &iceRestart, &googUseRtpMux, &enableDtlsSrtp, &enableRtpDataChannels, &enableDscp, &enableIPv6, &googEnableVideoSuspendBelowMinBitrate, @@ -407,6 +534,23 @@ return hasMandatoryOutsideSet(std::vector<std::string>(), dummyString); } +WebString WebMediaTrackConstraintSet::toString() const +{ + StringBuilder builder; + bool first = true; + for (const auto& constraint : allConstraints()) { + if (!constraint->isEmpty()) { + if (!first) + builder.appendLiteral(", "); + builder.append(constraint->name()); + builder.appendLiteral(": "); + builder.append(constraint->toString()); + first = false; + } + } + return builder.toString(); +} + // WebMediaConstraints void WebMediaConstraints::assign(const WebMediaConstraints& other) @@ -478,4 +622,11 @@ return m_private->advanced(); } +const WebString WebMediaConstraints::toString() const +{ + if (isNull()) + return WebString(""); + return m_private->toString(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/platform/exported/WebRTCOfferOptions.cpp b/third_party/WebKit/Source/platform/exported/WebRTCOfferOptions.cpp index 6402ddd..647688c 100644 --- a/third_party/WebKit/Source/platform/exported/WebRTCOfferOptions.cpp +++ b/third_party/WebKit/Source/platform/exported/WebRTCOfferOptions.cpp
@@ -13,6 +13,16 @@ { } +WebRTCOfferOptions::WebRTCOfferOptions(int32_t offerToReceiveAudio, + int32_t offerToReceiveVideo, bool voiceActivityDetection, + bool iceRestart) + : m_private(RTCOfferOptions::create(offerToReceiveAudio, + offerToReceiveVideo, + voiceActivityDetection, + iceRestart)) +{ +} + void WebRTCOfferOptions::assign(const WebRTCOfferOptions& other) { m_private = other.m_private;
diff --git a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm index 629b7eb..28dacdb 100644 --- a/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm +++ b/third_party/WebKit/Source/platform/fonts/mac/FontCacheMac.mm
@@ -167,8 +167,14 @@ substituteFontTraits = [fontManager traitsOfFont:substituteFont]; substituteFontWeight = [fontManager weightOfFont:substituteFont]; + // TODO(eae): Remove once skia supports bold emoji. See https://bugs.chromium.org/p/skia/issues/detail?id=4904 + // Bold emoji look the same as normal emoji, so syntheticBold isn't needed. + bool syntheticBold = isAppKitFontWeightBold(weight) && + !isAppKitFontWeightBold(substituteFontWeight) && + ![substituteFont.familyName isEqual:@"Apple Color Emoji"]; + FontPlatformData alternateFont(substituteFont, platformData.size(), - isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight), + syntheticBold, (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait), platformData.orientation()); @@ -211,7 +217,12 @@ NSFont *platformFont = useHinting() ? [nsFont screenFont] : [nsFont printerFont]; NSInteger appKitWeight = toAppKitFontWeight(fontDescription.weight()); - bool syntheticBold = (isAppKitFontWeightBold(appKitWeight) && !isAppKitFontWeightBold(actualWeight)) || fontDescription.isSyntheticBold(); + + // TODO(eae): Remove once skia supports bold emoji. See https://bugs.chromium.org/p/skia/issues/detail?id=4904 + // Bold emoji look the same as normal emoji, so syntheticBold isn't needed. + bool syntheticBold = [platformFont.familyName isEqual:@"Apple Color Emoji"] ? false : + (isAppKitFontWeightBold(appKitWeight) && !isAppKitFontWeightBold(actualWeight)) || fontDescription.isSyntheticBold(); + bool syntheticItalic = ((traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait)) || fontDescription.isSyntheticItalic(); // FontPlatformData::typeface() is null in the case of Chromium out-of-process font loading failing.
diff --git a/third_party/WebKit/Source/platform/graphics/CompositorMutableStateTest.cpp b/third_party/WebKit/Source/platform/graphics/CompositorMutableStateTest.cpp index 66959f5..ec33486 100644 --- a/third_party/WebKit/Source/platform/graphics/CompositorMutableStateTest.cpp +++ b/third_party/WebKit/Source/platform/graphics/CompositorMutableStateTest.cpp
@@ -36,7 +36,6 @@ { LayerTreeSettings settings; settings.layer_transforms_should_scale_layer_contents = true; - settings.verify_property_trees = true; m_hostImpl.reset(new FakeLayerTreeHostImpl(settings, &m_taskRunnerProvider, &m_sharedBitmapManager, &m_taskGraphRunner)); m_hostImpl->SetVisible(true); EXPECT_TRUE(m_hostImpl->InitializeRenderer(m_outputSurface.get()));
diff --git a/third_party/WebKit/Source/platform/graphics/ContentLayerDelegate.cpp b/third_party/WebKit/Source/platform/graphics/ContentLayerDelegate.cpp index e4edd3f..040e0280 100644 --- a/third_party/WebKit/Source/platform/graphics/ContentLayerDelegate.cpp +++ b/third_party/WebKit/Source/platform/graphics/ContentLayerDelegate.cpp
@@ -49,8 +49,7 @@ { } -static void paintArtifactToWebDisplayItemList(WebDisplayItemList* list, const GraphicsLayer* graphicsLayer, - const PaintArtifact& artifact, const gfx::Rect& bounds) +static void paintArtifactToWebDisplayItemList(WebDisplayItemList* list, const PaintArtifact& artifact, const gfx::Rect& bounds) { if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { // This is a temporary path to paint the artifact using the paint chunk @@ -62,7 +61,7 @@ list->appendDrawingItem(WebRect(bounds.x(), bounds.y(), bounds.width(), bounds.height()), picture.get()); return; } - artifact.appendToWebDisplayItemList(list, graphicsLayer); + artifact.appendToWebDisplayItemList(list); } gfx::Rect ContentLayerDelegate::paintableRegion() @@ -100,7 +99,7 @@ if (paintingControl != PaintDefaultBehavior) m_graphicsLayer->paint(nullptr, disabledMode); - paintArtifactToWebDisplayItemList(webDisplayItemList, m_graphicsLayer, paintController.paintArtifact(), paintableRegion()); + paintArtifactToWebDisplayItemList(webDisplayItemList, paintController.paintArtifact(), paintableRegion()); paintController.setDisplayItemConstructionIsDisabled(false); paintController.setSubsequenceCachingIsDisabled(false);
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp index 968a7c8..56f5a202 100644 --- a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp +++ b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
@@ -375,10 +375,7 @@ bool ImageDataBuffer::encodeImage(const String& mimeType, const double& quality, Vector<unsigned char>* encodedImage) const { if (mimeType == "image/jpeg") { - int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; - if (quality >= 0.0 && quality <= 1.0) - compressionQuality = static_cast<int>(quality * 100 + 0.5); - if (!JPEGImageEncoder::encode(*this, compressionQuality, encodedImage)) + if (!JPEGImageEncoder::encode(*this, quality, encodedImage)) return false; } else if (mimeType == "image/webp") { int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality;
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp index 763fa94..6592c1a 100644 --- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp +++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
@@ -65,22 +65,16 @@ PaintArtifactCompositor::PaintArtifactCompositor() { + if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled()) + return; + m_rootLayer = cc::Layer::Create(cc::LayerSettings()); + m_webLayer = adoptPtr(Platform::current()->compositorSupport()->createLayerFromCCLayer(m_rootLayer.get())); } PaintArtifactCompositor::~PaintArtifactCompositor() { } -void PaintArtifactCompositor::initializeIfNeeded() -{ - ASSERT(RuntimeEnabledFeatures::slimmingPaintV2Enabled()); - if (m_rootLayer) - return; - - m_rootLayer = cc::Layer::Create(cc::LayerSettings()); - m_webLayer = adoptPtr(Platform::current()->compositorSupport()->createLayerFromCCLayer(m_rootLayer.get())); -} - namespace { static void appendDisplayItemToCcDisplayItemList(const DisplayItem& displayItem, cc::DisplayItemList* list)
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h index bbcfd9c..358c3be5 100644 --- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h +++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h
@@ -33,11 +33,6 @@ PaintArtifactCompositor(); ~PaintArtifactCompositor(); - // Creates at least a root layer to be managed by this controller. Can't be - // done in the constructor, since RuntimeEnabledFeatures may not be - // initialized yet. - void initializeIfNeeded(); - // Updates the layer tree to match the provided paint artifact. // Creates the root layer if not already done. void update(const PaintArtifact&);
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp index 426de69..5def30a 100644 --- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp +++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -29,7 +29,9 @@ void SetUp() override { RuntimeEnabledFeatures::setSlimmingPaintV2Enabled(true); - m_paintArtifactCompositor.initializeIfNeeded(); + + // Delay constructing the compositor until after the feature is set. + m_paintArtifactCompositor = adoptPtr(new PaintArtifactCompositor); } void TearDown() override @@ -37,13 +39,13 @@ m_featuresBackup.restore(); } - PaintArtifactCompositor& paintArtifactCompositor() { return m_paintArtifactCompositor; } - cc::Layer* rootLayer() { return m_paintArtifactCompositor.rootLayer(); } - void update(const PaintArtifact& artifact) { m_paintArtifactCompositor.update(artifact); } + PaintArtifactCompositor& paintArtifactCompositor() { return *m_paintArtifactCompositor; } + cc::Layer* rootLayer() { return m_paintArtifactCompositor->rootLayer(); } + void update(const PaintArtifact& artifact) { m_paintArtifactCompositor->update(artifact); } private: RuntimeEnabledFeatures::Backup m_featuresBackup; - PaintArtifactCompositor m_paintArtifactCompositor; + OwnPtr<PaintArtifactCompositor> m_paintArtifactCompositor; }; TEST_F(PaintArtifactCompositorTest, EmptyPaintArtifact)
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp index df28ac3c..9d2600b 100644 --- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp +++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
@@ -118,7 +118,7 @@ if (discardFramebufferSupported) extensionsUtil->ensureExtensionEnabled("GL_EXT_discard_framebuffer"); - RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(std::move(context), extensionsUtil.release(), multisampleSupported, true, discardFramebufferSupported, preserve, requestedAttributes)); + RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(std::move(context), extensionsUtil.release(), multisampleSupported, discardFramebufferSupported, preserve, requestedAttributes)); if (!drawingBuffer->initialize(size)) { drawingBuffer->beginDestruction(); return PassRefPtr<DrawingBuffer>(); @@ -134,7 +134,6 @@ DrawingBuffer::DrawingBuffer(PassOwnPtr<WebGraphicsContext3D> context, PassOwnPtr<Extensions3DUtil> extensionsUtil, bool multisampleExtensionSupported, - bool packedDepthStencilExtensionSupported, bool discardFramebufferSupported, PreserveDrawingBuffer preserve, WebGraphicsContext3D::Attributes requestedAttributes) @@ -149,12 +148,9 @@ , m_size(-1, -1) , m_requestedAttributes(requestedAttributes) , m_multisampleExtensionSupported(multisampleExtensionSupported) - , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported) , m_discardFramebufferSupported(discardFramebufferSupported) , m_fbo(0) , m_depthStencilBuffer(0) - , m_depthBuffer(0) - , m_stencilBuffer(0) , m_multisampleFBO(0) , m_multisampleColorBuffer(0) , m_contentsChanged(true) @@ -610,12 +606,6 @@ if (m_depthStencilBuffer) m_context->deleteRenderbuffer(m_depthStencilBuffer); - if (m_depthBuffer) - m_context->deleteRenderbuffer(m_depthBuffer); - - if (m_stencilBuffer) - m_context->deleteRenderbuffer(m_stencilBuffer); - if (m_colorBuffer.textureId) { deleteChromiumImageForTexture(&m_colorBuffer); m_context->deleteTexture(m_colorBuffer.textureId); @@ -627,8 +617,6 @@ m_frontColorBuffer = FrontBufferInfo(); m_multisampleColorBuffer = 0; m_depthStencilBuffer = 0; - m_depthBuffer = 0; - m_stencilBuffer = 0; m_multisampleFBO = 0; m_fbo = 0; @@ -697,43 +685,16 @@ if (!m_requestedAttributes.depth && !m_requestedAttributes.stencil) return; - if (m_packedDepthStencilExtensionSupported) { - if (!m_depthStencilBuffer) - m_depthStencilBuffer = m_context->createRenderbuffer(); - m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); - if (m_antiAliasingMode == MSAAImplicitResolve) - m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height()); - else if (m_antiAliasingMode == MSAAExplicitResolve) - m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height()); - else - m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, size.width(), size.height()); - m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); - } else { - if (m_requestedAttributes.depth) { - if (!m_depthBuffer) - m_depthBuffer = m_context->createRenderbuffer(); - m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); - if (m_antiAliasingMode == MSAAImplicitResolve) - m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height()); - else if (m_antiAliasingMode == MSAAExplicitResolve) - m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH_COMPONENT16, size.width(), size.height()); - else - m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height()); - m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer); - } - if (m_requestedAttributes.stencil) { - if (!m_stencilBuffer) - m_stencilBuffer = m_context->createRenderbuffer(); - m_context->bindRenderbuffer(GL_RENDERBUFFER, m_stencilBuffer); - if (m_antiAliasingMode == MSAAImplicitResolve) - m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height()); - else if (m_antiAliasingMode == MSAAExplicitResolve) - m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_STENCIL_INDEX8, size.width(), size.height()); - else - m_context->renderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, size.width(), size.height()); - m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer); - } - } + if (!m_depthStencilBuffer) + m_depthStencilBuffer = m_context->createRenderbuffer(); + m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthStencilBuffer); + if (m_antiAliasingMode == MSAAImplicitResolve) + m_context->renderbufferStorageMultisampleEXT(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height()); + else if (m_antiAliasingMode == MSAAExplicitResolve) + m_context->renderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, m_sampleCount, GL_DEPTH24_STENCIL8_OES, size.width(), size.height()); + else + m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, size.width(), size.height()); + m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depthStencilBuffer); m_context->bindRenderbuffer(GL_RENDERBUFFER, 0); } @@ -751,6 +712,11 @@ m_context->clear(clearMask); } +bool DrawingBuffer::hasImplicitStencilBuffer() const +{ + return m_depthStencilBuffer && m_requestedAttributes.depth && !m_requestedAttributes.stencil; +} + void DrawingBuffer::setSize(const IntSize& size) { if (m_size == size)
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h index 87a2789..327b27e 100644 --- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h +++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h
@@ -83,6 +83,12 @@ // making the context current and setting the clear values and masks. Modifies the framebuffer binding. void clearFramebuffers(GLbitfield clearMask); + // Indicates whether the DrawingBuffer internally allocated a packed depth-stencil renderbuffer + // in the situation where the end user only asked for a depth buffer. In this case, we need to + // upgrade clears of the depth buffer to clears of the depth and stencil buffers in order to + // avoid performance problems on some GPUs. + bool hasImplicitStencilBuffer() const; + // Given the desired buffer size, provides the largest dimensions that will fit in the pixel budget. static IntSize adjustSize(const IntSize& desiredSize, const IntSize& curSize, int maxTextureSize); bool reset(const IntSize&); @@ -171,7 +177,6 @@ PassOwnPtr<WebGraphicsContext3D>, PassOwnPtr<Extensions3DUtil>, bool multisampleExtensionSupported, - bool packedDepthStencilExtensionSupported, bool discardFramebufferSupported, PreserveDrawingBuffer, WebGraphicsContext3D::Attributes requestedAttributes); @@ -298,7 +303,6 @@ IntSize m_size; WebGraphicsContext3D::Attributes m_requestedAttributes; bool m_multisampleExtensionSupported; - bool m_packedDepthStencilExtensionSupported; bool m_discardFramebufferSupported; Platform3DObject m_fbo; // DrawingBuffer's output is double-buffered. m_colorBuffer is the back buffer. @@ -311,13 +315,9 @@ OwnPtr<Closure> m_newMailboxCallback; - // This is used when we have OES_packed_depth_stencil. + // This is used when the user requests either a depth or stencil buffer. Platform3DObject m_depthStencilBuffer; - // These are used when we don't. - Platform3DObject m_depthBuffer; - Platform3DObject m_stencilBuffer; - // For multisampling. Platform3DObject m_multisampleFBO; Platform3DObject m_multisampleColorBuffer;
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp index 0056f6c..7e9dc9f3 100644 --- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp +++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
@@ -223,8 +223,7 @@ DrawingBufferForTests(PassOwnPtr<WebGraphicsContext3D> context, PassOwnPtr<Extensions3DUtil> extensionsUtil, PreserveDrawingBuffer preserve) - : DrawingBuffer(context, extensionsUtil, false /* multisampleExtensionSupported */, - false /* packedDepthStencilExtensionSupported */, false /* discardFramebufferSupported */, preserve, WebGraphicsContext3D::Attributes()) + : DrawingBuffer(context, extensionsUtil, false /* multisampleExtensionSupported */, false /* discardFramebufferSupported */, preserve, WebGraphicsContext3D::Attributes()) , m_live(0) { }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.cpp index 733401c..8bcee319 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.cpp
@@ -48,16 +48,26 @@ { } -PaintArtifact::PaintArtifact(DisplayItemList displayItems, Vector<PaintChunk> paintChunks) +static void appendVisualRectForDisplayItem(const DisplayItem& displayItem, const LayoutSize& offsetFromLayoutObject, Vector<IntRect>& visualRects) +{ + LayoutRect visualRect = displayItem.client().visualRect(); + visualRect.move(-offsetFromLayoutObject); + visualRects.append(enclosingIntRect(visualRect)); +} + +PaintArtifact::PaintArtifact(DisplayItemList displayItems, Vector<PaintChunk> paintChunks, const LayoutSize& offsetFromLayoutObject) : m_displayItemList(std::move(displayItems)) , m_paintChunks(std::move(paintChunks)) { + for (const DisplayItem& displayItem : m_displayItemList) + appendVisualRectForDisplayItem(displayItem, offsetFromLayoutObject, m_visualRects); computeChunkBoundsAndOpaqueness(m_displayItemList, m_paintChunks); } PaintArtifact::PaintArtifact(PaintArtifact&& source) : m_displayItemList(std::move(source.m_displayItemList)) , m_paintChunks(std::move(source.m_paintChunks)) + , m_visualRects(std::move(source.m_visualRects)) { } @@ -69,6 +79,7 @@ { m_displayItemList = std::move(source.m_displayItemList); m_paintChunks = std::move(source.m_paintChunks); + m_visualRects = std::move(source.m_visualRects); return *this; } @@ -91,16 +102,17 @@ displayItem.replay(graphicsContext); } -void PaintArtifact::appendToWebDisplayItemList(WebDisplayItemList* list, const GraphicsLayer* graphicsLayer) const +void PaintArtifact::appendToWebDisplayItemList(WebDisplayItemList* list) const { TRACE_EVENT0("blink,benchmark", "PaintArtifact::appendToWebDisplayItemList"); #if ENABLE(ASSERT) m_displayItemList.assertDisplayItemClientsAreAlive(); #endif + ASSERT(m_displayItemList.size() == m_visualRects.size()); + unsigned visualRectIndex = 0; for (const DisplayItem& displayItem : m_displayItemList) { - LayoutRect visualRect = displayItem.client().visualRect(); - visualRect.move(-graphicsLayer->offsetFromLayoutObjectWithSubpixelAccumulation()); - displayItem.appendToWebDisplayItemList(enclosingIntRect(visualRect), list); + displayItem.appendToWebDisplayItemList(m_visualRects[visualRectIndex], list); + visualRectIndex++; } }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.h b/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.h index 42084c7e..4b8e1a7 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.h +++ b/third_party/WebKit/Source/platform/graphics/paint/PaintArtifact.h
@@ -35,7 +35,7 @@ WTF_MAKE_NONCOPYABLE(PaintArtifact); public: PaintArtifact(); - PaintArtifact(DisplayItemList, Vector<PaintChunk>); + PaintArtifact(DisplayItemList, Vector<PaintChunk>, const LayoutSize& offsetFromLayoutObject); PaintArtifact(PaintArtifact&&); ~PaintArtifact(); @@ -60,11 +60,16 @@ void replay(GraphicsContext&) const; // Writes the paint artifact into a WebDisplayItemList. - void appendToWebDisplayItemList(WebDisplayItemList*, const GraphicsLayer*) const; + void appendToWebDisplayItemList(WebDisplayItemList*) const; private: + IntRect visualRect(unsigned index) const { return m_visualRects[index]; } + DisplayItemList m_displayItemList; Vector<PaintChunk> m_paintChunks; + Vector<IntRect> m_visualRects; + + friend class PaintControllerTest; }; } // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp index bc42f6a..d98ac276 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
@@ -254,12 +254,12 @@ } while (!endSubsequenceId.matches(updatedList.last())); } -void PaintController::commitNewDisplayItems() +void PaintController::commitNewDisplayItems(const LayoutSize& offsetFromLayoutObject) { #if ENABLE(ASSERT) m_newDisplayItemList.assertDisplayItemClientsAreAlive(); #endif - commitNewDisplayItemsInternal(); + commitNewDisplayItemsInternal(offsetFromLayoutObject); #if ENABLE(ASSERT) m_currentPaintArtifact.displayItemList().assertDisplayItemClientsAreAlive(); #endif @@ -276,7 +276,7 @@ // Coefficients are related to the ratio of out-of-order CachedDisplayItems // and the average number of (Drawing|Subsequence)DisplayItems per client. // -void PaintController::commitNewDisplayItemsInternal() +void PaintController::commitNewDisplayItemsInternal(const LayoutSize& offsetFromLayoutObject) { TRACE_EVENT2("blink,benchmark", "PaintController::commitNewDisplayItems", "current_display_list_size", (int)m_currentPaintArtifact.displayItemList().size(), @@ -302,7 +302,8 @@ for (const auto& item : m_newDisplayItemList) ASSERT(!item.isCached()); #endif - m_currentPaintArtifact = PaintArtifact(std::move(m_newDisplayItemList), m_newPaintChunks.releasePaintChunks()); + + m_currentPaintArtifact = PaintArtifact(std::move(m_newDisplayItemList), m_newPaintChunks.releasePaintChunks(), offsetFromLayoutObject); m_newDisplayItemList = DisplayItemList(kInitialDisplayItemListCapacityBytes); m_validlyCachedClientsDirty = true; return; @@ -384,7 +385,8 @@ // TODO(jbroman): When subsequence caching applies to SPv2, we'll need to // merge the paint chunks as well. - m_currentPaintArtifact = PaintArtifact(std::move(updatedList), m_newPaintChunks.releasePaintChunks()); + m_currentPaintArtifact = PaintArtifact(std::move(updatedList), m_newPaintChunks.releasePaintChunks(), offsetFromLayoutObject); + m_newDisplayItemList = DisplayItemList(kInitialDisplayItemListCapacityBytes); m_validlyCachedClientsDirty = true; }
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h index e7718a2..d1fdd98 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.h +++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.h
@@ -107,7 +107,9 @@ bool skippingCache() const { return m_skippingCacheCount; } // Must be called when a painting is finished. - void commitNewDisplayItems(); + // offsetFromLayoutObject is the offset between the space of the GraphicsLayer which owns this + // PaintController and the coordinate space of the owning LayoutObject. + void commitNewDisplayItems(const LayoutSize& offsetFromLayoutObject = LayoutSize()); // Returns the approximate memory usage, excluding memory likely to be // shared with the embedder after copying to WebPaintController. @@ -216,7 +218,7 @@ void checkNoRemainingCachedDisplayItems(); #endif - void commitNewDisplayItemsInternal(); + void commitNewDisplayItemsInternal(const LayoutSize& offsetFromLayoutObject); // The last complete paint artifact. // In SPv2, this includes paint chunks as well as display items.
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp index 4284fd9..1df44824 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
@@ -22,6 +22,11 @@ : m_paintController(PaintController::create()) , m_originalSlimmingPaintV2Enabled(RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { } + IntRect visualRect(const PaintArtifact& paintArtifact, unsigned index) + { + return paintArtifact.visualRect(index); + } + protected: PaintController& paintController() { return *m_paintController; } @@ -41,15 +46,16 @@ class TestDisplayItemClient : public DisplayItemClient { public: - TestDisplayItemClient(const String& name) - : m_name(name) + TestDisplayItemClient(const String& name, LayoutRect visualRect = LayoutRect()) + : m_name(name), m_visualRect(visualRect) { } String debugName() const final { return m_name; } - LayoutRect visualRect() const override { return LayoutRect(); } + LayoutRect visualRect() const override { return m_visualRect; } private: String m_name; + LayoutRect m_visualRect; }; class TestDisplayItem final : public DisplayItem { @@ -779,4 +785,26 @@ EXPECT_EQ(1u, paintChunks[0].endIndex); } +#define EXPECT_RECT_EQ(expected, actual) \ + do { \ + const IntRect& actualRect = actual; \ + EXPECT_EQ(expected.x(), actualRect.x()); \ + EXPECT_EQ(expected.y(), actualRect.y()); \ + EXPECT_EQ(expected.width(), actualRect.width()); \ + EXPECT_EQ(expected.height(), actualRect.height()); \ + } while (false) + +TEST_F(PaintControllerTest, PaintArtifactWithVisualRects) +{ + TestDisplayItemClient client("test client", LayoutRect(0, 0, 200, 100)); + + GraphicsContext context(paintController()); + drawRect(context, client, backgroundDrawingType, FloatRect(0, 0, 100, 100)); + + paintController().commitNewDisplayItems(LayoutSize(20, 30)); + const auto& paintArtifact = paintController().paintArtifact(); + ASSERT_EQ(1u, paintArtifact.displayItemList().size()); + EXPECT_RECT_EQ(IntRect(-20, -30, 200, 100), visualRect(paintArtifact, 0)); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/platform/heap/BlinkGCMemoryDumpProvider.cpp b/third_party/WebKit/Source/platform/heap/BlinkGCMemoryDumpProvider.cpp index 6f924c1..abd9b811 100644 --- a/third_party/WebKit/Source/platform/heap/BlinkGCMemoryDumpProvider.cpp +++ b/third_party/WebKit/Source/platform/heap/BlinkGCMemoryDumpProvider.cpp
@@ -31,9 +31,9 @@ objectsDump->addScalar("size", "bytes", Heap::allocatedObjectSize() + Heap::markedObjectSize()); } -void reportAllocation(Address address, size_t size) +void reportAllocation(Address address, size_t size, const char* typeName) { - BlinkGCMemoryDumpProvider::instance()->insert(address, size); + BlinkGCMemoryDumpProvider::instance()->insert(address, size, typeName); } void reportFree(Address address) @@ -114,11 +114,10 @@ { } -void BlinkGCMemoryDumpProvider::insert(Address address, size_t size) +void BlinkGCMemoryDumpProvider::insert(Address address, size_t size, const char* typeName) { base::trace_event::AllocationContext context = base::trace_event::AllocationContextTracker::GetContextSnapshot(); - // TODO(hajimehoshi): Implement to use a correct type name. - context.type_name = ""; + context.type_name = typeName; MutexLocker locker(m_allocationRegisterMutex); if (m_allocationRegister) m_allocationRegister->Insert(address, size, context);
diff --git a/third_party/WebKit/Source/platform/heap/BlinkGCMemoryDumpProvider.h b/third_party/WebKit/Source/platform/heap/BlinkGCMemoryDumpProvider.h index e0166d88..e6de7a33 100644 --- a/third_party/WebKit/Source/platform/heap/BlinkGCMemoryDumpProvider.h +++ b/third_party/WebKit/Source/platform/heap/BlinkGCMemoryDumpProvider.h
@@ -46,7 +46,7 @@ WebProcessMemoryDump* currentProcessMemoryDump() { return m_currentProcessMemoryDump.get(); } - void insert(Address, size_t); + void insert(Address, size_t, const char*); void remove(Address); private:
diff --git a/third_party/WebKit/Source/platform/heap/Heap.h b/third_party/WebKit/Source/platform/heap/Heap.h index ece92ca..eb9fcd65 100644 --- a/third_party/WebKit/Source/platform/heap/Heap.h +++ b/third_party/WebKit/Source/platform/heap/Heap.h
@@ -48,17 +48,17 @@ class PLATFORM_EXPORT HeapAllocHooks { public: // TODO(hajimehoshi): Pass a type name of the allocated object. - typedef void AllocationHook(Address, size_t); + typedef void AllocationHook(Address, size_t, const char*); typedef void FreeHook(Address); static void setAllocationHook(AllocationHook* hook) { m_allocationHook = hook; } static void setFreeHook(FreeHook* hook) { m_freeHook = hook; } - static void allocationHookIfEnabled(Address address, size_t size) + static void allocationHookIfEnabled(Address address, size_t size, const char* typeName) { AllocationHook* allocationHook = m_allocationHook; if (UNLIKELY(!!allocationHook)) - allocationHook(address, size); + allocationHook(address, size, typeName); } static void freeHookIfEnabled(Address address) @@ -68,14 +68,14 @@ freeHook(address); } - static void reallocHookIfEnabled(Address oldAddress, Address newAddress, size_t size) + static void reallocHookIfEnabled(Address oldAddress, Address newAddress, size_t size, const char* typeName) { // Report a reallocation as a free followed by an allocation. AllocationHook* allocationHook = m_allocationHook; FreeHook* freeHook = m_freeHook; if (UNLIKELY(allocationHook && freeHook)) { freeHook(oldAddress); - allocationHook(newAddress, size); + allocationHook(newAddress, size, typeName); } } @@ -496,7 +496,8 @@ { ThreadState* state = ThreadStateFor<ThreadingTrait<T>::Affinity>::state(); Address address = Heap::allocateOnHeapIndex(state, size, eagerlySweep ? BlinkGC::EagerSweepHeapIndex : Heap::heapIndexForObjectSize(size), GCInfoTrait<T>::index()); - HeapAllocHooks::allocationHookIfEnabled(address, size); + const char* typeName = WTF_HEAP_PROFILER_TYPE_NAME(T); + HeapAllocHooks::allocationHookIfEnabled(address, size, typeName); return address; } @@ -530,7 +531,8 @@ if (copySize > size) copySize = size; memcpy(address, previous, copySize); - HeapAllocHooks::reallocHookIfEnabled(static_cast<Address>(previous), address, size); + const char* typeName = WTF_HEAP_PROFILER_TYPE_NAME(T); + HeapAllocHooks::reallocHookIfEnabled(static_cast<Address>(previous), address, size, typeName); return address; }
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.cpp b/third_party/WebKit/Source/platform/heap/ThreadState.cpp index 3d9554d..ba95db76 100644 --- a/third_party/WebKit/Source/platform/heap/ThreadState.cpp +++ b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
@@ -564,7 +564,8 @@ bool ThreadState::shouldSchedulePageNavigationGC(float estimatedRemovalRatio) { - return judgeGCThreshold(1024 * 1024, 1.5 * (1 - estimatedRemovalRatio)); + // TODO(keishi): Temporarily disabled to observe impact. crbug.com/588029 + return false; } bool ThreadState::shouldForceConservativeGC()
diff --git a/third_party/WebKit/Source/platform/image-encoders/skia/JPEGImageEncoder.cpp b/third_party/WebKit/Source/platform/image-encoders/skia/JPEGImageEncoder.cpp index 5f360eb4..7880c3d9 100644 --- a/third_party/WebKit/Source/platform/image-encoders/skia/JPEGImageEncoder.cpp +++ b/third_party/WebKit/Source/platform/image-encoders/skia/JPEGImageEncoder.cpp
@@ -43,11 +43,30 @@ namespace blink { struct JPEGOutputBuffer : public jpeg_destination_mgr { - DISALLOW_NEW(); + USING_FAST_MALLOC(JPEGOutputBuffer); + WTF_MAKE_NONCOPYABLE(JPEGOutputBuffer); +public: + JPEGOutputBuffer() : jpeg_destination_mgr() {} Vector<unsigned char>* output; Vector<unsigned char> buffer; }; +class JPEGImageEncoderStateImpl : public JPEGImageEncoderState { +public: + JPEGImageEncoderStateImpl(jpeg_compress_struct* jpeg, JPEGOutputBuffer* destination, jpeg_error_mgr* error) + : m_jpeg(jpeg) + , m_destination(destination) + , m_error(error) {} + ~JPEGImageEncoderStateImpl() override; + jpeg_compress_struct* jpeg() { ASSERT(m_jpeg); return m_jpeg; } + JPEGOutputBuffer* outputBuffer() { return m_destination; } + jpeg_error_mgr* error() { return m_error; } +private: + jpeg_compress_struct* m_jpeg; + JPEGOutputBuffer* m_destination; + jpeg_error_mgr* m_error; +}; + static void prepareOutput(j_compress_ptr cinfo) { JPEGOutputBuffer* out = static_cast<JPEGOutputBuffer*>(cinfo->dest); @@ -108,64 +127,94 @@ } } -static bool encodePixels(IntSize imageSize, const unsigned char* inputPixels, int quality, Vector<unsigned char>* output) +PassOwnPtr<JPEGImageEncoderState> JPEGImageEncoderState::create(const IntSize& imageSize, const double& quality, Vector<unsigned char>* output) { if (imageSize.width() <= 0 || imageSize.height() <= 0) - return false; + return nullptr; - JPEGOutputBuffer destination; - destination.output = output; - Vector<JSAMPLE> row; + int compressionQuality = JPEGImageEncoder::computeCompressionQuality(quality); + JPEGOutputBuffer* destination = new JPEGOutputBuffer; + destination->output = output; - jpeg_compress_struct cinfo; - jpeg_error_mgr error; - cinfo.err = jpeg_std_error(&error); - error.error_exit = handleError; + jpeg_compress_struct* cinfo = new jpeg_compress_struct; + jpeg_error_mgr* error = new jpeg_error_mgr; + + cinfo->err = jpeg_std_error(error); + error->error_exit = handleError; jmp_buf jumpBuffer; - cinfo.client_data = &jumpBuffer; + cinfo->client_data = &jumpBuffer; if (setjmp(jumpBuffer)) { - jpeg_destroy_compress(&cinfo); - return false; + jpeg_destroy_compress(cinfo); + return nullptr; } - jpeg_create_compress(&cinfo); - cinfo.dest = &destination; - cinfo.dest->init_destination = prepareOutput; - cinfo.dest->empty_output_buffer = writeOutput; - cinfo.dest->term_destination = finishOutput; + jpeg_create_compress(cinfo); + cinfo->dest = destination; + cinfo->dest->init_destination = prepareOutput; + cinfo->dest->empty_output_buffer = writeOutput; + cinfo->dest->term_destination = finishOutput; - cinfo.image_height = imageSize.height(); - cinfo.image_width = imageSize.width(); - cinfo.in_color_space = JCS_RGB; - cinfo.input_components = 3; - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - disableSubsamplingForHighQuality(&cinfo, quality); - jpeg_start_compress(&cinfo, TRUE); + cinfo->image_height = imageSize.height(); + cinfo->image_width = imageSize.width(); + cinfo->in_color_space = JCS_RGB; + cinfo->input_components = 3; + jpeg_set_defaults(cinfo); + jpeg_set_quality(cinfo, compressionQuality, TRUE); + disableSubsamplingForHighQuality(cinfo, compressionQuality); + jpeg_start_compress(cinfo, TRUE); + + return adoptPtr(new JPEGImageEncoderStateImpl(cinfo, destination, error)); +} + +JPEGImageEncoderStateImpl::~JPEGImageEncoderStateImpl() +{ + jpeg_destroy_compress(m_jpeg); +} + +int JPEGImageEncoder::computeCompressionQuality(const double& quality) +{ + int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; + if (quality >= 0.0 && quality <= 1.0) + compressionQuality = static_cast<int>(quality * 100 + 0.5); + return compressionQuality; +} + +void JPEGImageEncoder::encodeWithPreInitializedState(JPEGImageEncoderState* encoderState, const unsigned char* inputPixels) +{ + JPEGImageEncoderStateImpl* encoderStateImpl = static_cast<JPEGImageEncoderStateImpl *>(encoderState); + + Vector<JSAMPLE> row; unsigned char* pixels = const_cast<unsigned char*>(inputPixels); - row.resize(cinfo.image_width * cinfo.input_components); - const size_t pixelRowStride = cinfo.image_width * 4; - while (cinfo.next_scanline < cinfo.image_height) { + + row.resize(encoderStateImpl->jpeg()->image_width * encoderStateImpl->jpeg()->input_components); + + const size_t pixelRowStride = encoderStateImpl->jpeg()->image_width * 4; + while (encoderStateImpl->jpeg()->next_scanline < encoderStateImpl->jpeg()->image_height) { JSAMPLE* rowData = row.data(); - RGBAtoRGB(pixels, cinfo.image_width, rowData); - jpeg_write_scanlines(&cinfo, &rowData, 1); + RGBAtoRGB(pixels, encoderStateImpl->jpeg()->image_width, rowData); + jpeg_write_scanlines(encoderStateImpl->jpeg(), &rowData, 1); pixels += pixelRowStride; } - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - return true; + jpeg_finish_compress(encoderStateImpl->jpeg()); } -bool JPEGImageEncoder::encode(const ImageDataBuffer& imageData, int quality, Vector<unsigned char>* output) +bool JPEGImageEncoder::encode(const ImageDataBuffer& imageData, const double& quality, Vector<unsigned char>* output) { if (!imageData.pixels()) return false; + IntSize imageSize(imageData.width(), imageData.height()); + const unsigned char* inputPixels = imageData.pixels(); - return encodePixels(IntSize(imageData.width(), imageData.height()), imageData.pixels(), quality, output); + OwnPtr<JPEGImageEncoderState> encoderState = JPEGImageEncoderState::create(imageSize, quality, output); + if (!encoderState.get()) + return false; + + JPEGImageEncoder::encodeWithPreInitializedState(encoderState.get(), inputPixels); + return true; } } // namespace blink
diff --git a/third_party/WebKit/Source/platform/image-encoders/skia/JPEGImageEncoder.h b/third_party/WebKit/Source/platform/image-encoders/skia/JPEGImageEncoder.h index e59a8e8f..45f4aa98 100644 --- a/third_party/WebKit/Source/platform/image-encoders/skia/JPEGImageEncoder.h +++ b/third_party/WebKit/Source/platform/image-encoders/skia/JPEGImageEncoder.h
@@ -31,19 +31,38 @@ #ifndef JPEGImageEncoder_h #define JPEGImageEncoder_h +#include "platform/geometry/IntSize.h" #include "wtf/Allocator.h" +#include "wtf/PassOwnPtr.h" #include "wtf/Vector.h" namespace blink { struct ImageDataBuffer; -class JPEGImageEncoder { +class PLATFORM_EXPORT JPEGImageEncoderState { + USING_FAST_MALLOC(JPEGImageEncoderState); + WTF_MAKE_NONCOPYABLE(JPEGImageEncoderState); +public: + static PassOwnPtr<JPEGImageEncoderState> create(const IntSize& imageSize, const double& quality, Vector<unsigned char>* output); + JPEGImageEncoderState() {} + virtual ~JPEGImageEncoderState() {} +}; + +class PLATFORM_EXPORT JPEGImageEncoder { STATIC_ONLY(JPEGImageEncoder); public: // Encode the image data with a compression quality in [0-100]. - static bool encode(const ImageDataBuffer&, int quality, Vector<unsigned char>*); + // Warning: Calling this method off the main thread may result in data race + // problems; instead, call JPEGImageEncoderState::create on main thread + // first followed by encodeWithPreInitializedState off the main thread will + // be safer. + static bool encode(const ImageDataBuffer&, const double& quality, Vector<unsigned char>*); + static void encodeWithPreInitializedState(JPEGImageEncoderState*, const unsigned char*); + static int computeCompressionQuality(const double& quality); + +private: // For callers: provide a reasonable compression quality default. enum Quality { DefaultCompressionQuality = 92 }; };
diff --git a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.h b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.h index cd4e8d2..b132940 100644 --- a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.h +++ b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.h
@@ -31,7 +31,9 @@ #include "platform/geometry/FloatSize.h" #include "platform/geometry/IntRect.h" #include "platform/heap/Handle.h" +#include "platform/scheduler/CancellableTaskFactory.h" #include "platform/scroll/ScrollAnimatorBase.h" +#include "public/platform/WebTaskRunner.h" #include "wtf/RetainPtr.h" OBJC_CLASS BlinkScrollAnimationHelperDelegate; @@ -81,11 +83,12 @@ RetainPtr<BlinkScrollbarPainterDelegate> m_horizontalScrollbarPainterDelegate; RetainPtr<BlinkScrollbarPainterDelegate> m_verticalScrollbarPainterDelegate; - void initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*); - Timer<ScrollAnimatorMac> m_initialScrollbarPaintTimer; + void initialScrollbarPaintTask(); + OwnPtr<CancellableTaskFactory> m_initialScrollbarPaintTaskFactory; - void sendContentAreaScrolledTimerFired(Timer<ScrollAnimatorMac>*); - Timer<ScrollAnimatorMac> m_sendContentAreaScrolledTimer; + void sendContentAreaScrolledTask(); + OwnPtr<CancellableTaskFactory> m_sendContentAreaScrolledTaskFactory; + OwnPtr<WebTaskRunner> m_taskRunner; FloatSize m_contentAreaScrolledTimerScrollDelta; ScrollResultOneDimensional userScroll(ScrollbarOrientation, ScrollGranularity, float step, float delta) override;
diff --git a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm index 6f940cb..e26dfd2 100644 --- a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm +++ b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm
@@ -37,6 +37,8 @@ #include "platform/scroll/ScrollbarTheme.h" #include "platform/scroll/ScrollbarThemeMacCommon.h" #include "platform/scroll/ScrollbarThemeMacOverlayAPI.h" +#include "public/platform/Platform.h" +#include "public/platform/WebScheduler.h" #include "wtf/MainThread.h" #include "wtf/MathExtras.h" #include "wtf/PassOwnPtr.h" @@ -684,8 +686,9 @@ ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea) : ScrollAnimatorBase(scrollableArea) - , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired) - , m_sendContentAreaScrolledTimer(this, &ScrollAnimatorMac::sendContentAreaScrolledTimerFired) + , m_initialScrollbarPaintTaskFactory(CancellableTaskFactory::create(this, &ScrollAnimatorMac::initialScrollbarPaintTask)) + , m_sendContentAreaScrolledTaskFactory(CancellableTaskFactory::create(this, &ScrollAnimatorMac::sendContentAreaScrolledTask)) + , m_taskRunner(adoptPtr(Platform::current()->currentThread()->scheduler()->timerTaskRunner()->clone())) , m_haveScrolledSincePageLoad(false) , m_needsScrollerStyleUpdate(false) { @@ -722,8 +725,8 @@ [m_scrollAnimationHelperDelegate.get() invalidate]; END_BLOCK_OBJC_EXCEPTIONS; } - m_initialScrollbarPaintTimer.stop(); - m_sendContentAreaScrolledTimer.stop(); + m_initialScrollbarPaintTaskFactory->cancel(); + m_sendContentAreaScrolledTaskFactory->cancel(); } ScrollResultOneDimensional ScrollAnimatorMac::userScroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta) @@ -1128,20 +1131,20 @@ void ScrollAnimatorMac::startScrollbarPaintTimer() { - m_initialScrollbarPaintTimer.startOneShot(0.1, BLINK_FROM_HERE); + m_taskRunner->postDelayedTask(BLINK_FROM_HERE, m_initialScrollbarPaintTaskFactory->cancelAndCreate(), 0.1); } bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const { - return m_initialScrollbarPaintTimer.isActive(); + return m_initialScrollbarPaintTaskFactory->isPending(); } void ScrollAnimatorMac::stopScrollbarPaintTimer() { - m_initialScrollbarPaintTimer.stop(); + m_initialScrollbarPaintTaskFactory->cancel(); } -void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*) +void ScrollAnimatorMac::initialScrollbarPaintTask() { if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) { // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController @@ -1155,11 +1158,11 @@ { m_contentAreaScrolledTimerScrollDelta = delta; - if (!m_sendContentAreaScrolledTimer.isActive()) - m_sendContentAreaScrolledTimer.startOneShot(0, BLINK_FROM_HERE); + if (!m_sendContentAreaScrolledTaskFactory->isPending()) + m_taskRunner->postTask(BLINK_FROM_HERE, m_sendContentAreaScrolledTaskFactory->cancelAndCreate()); } -void ScrollAnimatorMac::sendContentAreaScrolledTimerFired(Timer<ScrollAnimatorMac>*) +void ScrollAnimatorMac::sendContentAreaScrolledTask() { if (supportsContentAreaScrolledInDirection()) { [m_scrollbarPainterController.get() contentAreaScrolledInDirection:NSMakePoint(m_contentAreaScrolledTimerScrollDelta.width(), m_contentAreaScrolledTimerScrollDelta.height())];
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp b/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp index d599b4d..11d2da15 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp +++ b/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp
@@ -177,6 +177,9 @@ graphicsLayer.resetTrackedPaintInvalidations(); scrollbar->setNeedsPaintInvalidation(NoPart); EXPECT_TRUE(graphicsLayer.hasTrackedPaintInvalidations()); + + // Forced GC in order to finalize objects depending on the mock object. + Heap::collectAllGarbage(); } TEST_F(ScrollableAreaTest, InvalidatesNonCompositedScrollbarsWhenThumbMoves)
diff --git a/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp b/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp index ea199b5..7de22860b 100644 --- a/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp +++ b/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp
@@ -84,7 +84,7 @@ if (!m_paintChunks.isEmpty()) m_paintChunks.last().endIndex = m_displayItemList.size(); - m_paintArtifact = PaintArtifact(std::move(m_displayItemList), std::move(m_paintChunks)); + m_paintArtifact = PaintArtifact(std::move(m_displayItemList), std::move(m_paintChunks), LayoutSize()); m_built = true; return m_paintArtifact; }
diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp index 94c2e7c..810f287d 100644 --- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp +++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp
@@ -127,8 +127,7 @@ WebTaskRunner* clone() override { - ASSERT_NOT_REACHED(); - return nullptr; + return new TestingPlatformMockWebTaskRunner(m_tasks); } private:
diff --git a/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp b/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp index 3b155d37..3704ccb2 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp +++ b/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp
@@ -342,6 +342,25 @@ m_native->unbind(boundId); } +v8::MaybeLocal<v8::Value> InjectedScript::runCompiledScript(v8::Local<v8::Script> script, bool includeCommandLineAPI) +{ + v8::Local<v8::Symbol> commandLineAPISymbolValue = V8Debugger::commandLineAPISymbol(m_isolate); + v8::Local<v8::Object> global = context()->Global(); + if (includeCommandLineAPI) { + V8FunctionCall function(m_client, context(), v8Value(), "commandLineAPI"); + bool hadException = false; + v8::Local<v8::Value> commandLineAPI = function.call(hadException, false); + if (!hadException) + global->Set(commandLineAPISymbolValue, commandLineAPI); + } + + v8::MaybeLocal<v8::Value> maybeValue = m_client->runCompiledScript(context(), script); + if (includeCommandLineAPI) + global->Delete(context(), commandLineAPISymbolValue); + + return maybeValue; +} + PassRefPtr<Array<CallFrame>> InjectedScript::wrapCallFrames(v8::Local<v8::Object> callFrames, int asyncOrdinal) { v8::HandleScope handles(m_isolate); @@ -509,6 +528,8 @@ void InjectedScript::makeCallWithExceptionDetails(V8FunctionCall& function, RefPtr<JSONValue>* result, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>* exceptionDetails) { + v8::HandleScope handles(m_isolate); + v8::Context::Scope scope(context()); v8::TryCatch tryCatch(m_isolate); v8::Local<v8::Value> resultValue = function.callWithoutExceptionHandling(); if (tryCatch.HasCaught()) {
diff --git a/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.h b/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.h index d54b52a..5cb37c13 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.h +++ b/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.h
@@ -94,6 +94,7 @@ void getProperties(ErrorString*, const String& objectId, bool ownProperties, bool accessorPropertiesOnly, bool generatePreview, RefPtr<protocol::TypeBuilder::Array<protocol::TypeBuilder::Runtime::PropertyDescriptor>>* result, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>*); void getInternalProperties(ErrorString*, const String& objectId, RefPtr<protocol::TypeBuilder::Array<protocol::TypeBuilder::Runtime::InternalPropertyDescriptor>>* result, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>*); void releaseObject(const String& objectId); + v8::MaybeLocal<v8::Value> runCompiledScript(v8::Local<v8::Script>, bool includeCommandLineAPI); PassRefPtr<protocol::TypeBuilder::Array<protocol::TypeBuilder::Debugger::CallFrame>> wrapCallFrames(v8::Local<v8::Object>, int asyncOrdinal);
diff --git a/third_party/WebKit/Source/platform/v8_inspector/InjectedScriptSource.js b/third_party/WebKit/Source/platform/v8_inspector/InjectedScriptSource.js index 5aab32c..f6b73730f 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/InjectedScriptSource.js +++ b/third_party/WebKit/Source/platform/v8_inspector/InjectedScriptSource.js
@@ -986,6 +986,14 @@ }, /** + * @return {!CommandLineAPI} + */ + commandLineAPI: function() + { + return new CommandLineAPI(this._commandLineAPIImpl, null); + }, + + /** * @param {!JavaScriptCallFrame} topCallFrame * @param {string} callFrameId * @return {?JavaScriptCallFrame}
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp index dcb45e6..e6981b8 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp +++ b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp
@@ -215,7 +215,7 @@ *scriptId = scriptValueId; } -void V8RuntimeAgentImpl::runScript(ErrorString* errorString, const ScriptId& scriptId, int executionContextId, const String* const objectGroup, const bool* const doNotPauseOnExceptionsAndMuteConsole, RefPtr<RemoteObject>& result, RefPtr<ExceptionDetails>& exceptionDetails) +void V8RuntimeAgentImpl::runScript(ErrorString* errorString, const ScriptId& scriptId, int executionContextId, const String* const objectGroup, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* includeCommandLineAPI, RefPtr<RemoteObject>& result, RefPtr<ExceptionDetails>& exceptionDetails) { if (!m_enabled) { *errorString = "Runtime agent is not enabled"; @@ -248,8 +248,9 @@ return; } v8::TryCatch tryCatch(isolate); + v8::Local<v8::Value> value; - v8::MaybeLocal<v8::Value> maybeValue = m_debugger->client()->runCompiledScript(context, script); + v8::MaybeLocal<v8::Value> maybeValue = injectedScript->runCompiledScript(script, asBool(includeCommandLineAPI)); if (maybeValue.IsEmpty()) { value = tryCatch.Exception(); v8::Local<v8::Message> message = tryCatch.Message();
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h index cbbe8bd..a6c184b 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h +++ b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h
@@ -86,7 +86,7 @@ void isRunRequired(ErrorString*, bool* out_result) override; void setCustomObjectFormatterEnabled(ErrorString*, bool) override; void compileScript(ErrorString*, const String& expression, const String& sourceURL, bool persistScript, int executionContextId, protocol::TypeBuilder::OptOutput<protocol::TypeBuilder::Runtime::ScriptId>*, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>&) override; - void runScript(ErrorString*, const protocol::TypeBuilder::Runtime::ScriptId&, int executionContextId, const String* objectGroup, const bool* doNotPauseOnExceptionsAndMuteConsole, RefPtr<protocol::TypeBuilder::Runtime::RemoteObject>& result, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>&) override; + void runScript(ErrorString*, const protocol::TypeBuilder::Runtime::ScriptId&, int executionContextId, const String* objectGroup, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* includeCommandLineAPI, RefPtr<protocol::TypeBuilder::Runtime::RemoteObject>& result, RefPtr<protocol::TypeBuilder::Runtime::ExceptionDetails>&) override; V8DebuggerImpl* debugger() { return m_debugger; } InjectedScriptManager* injectedScriptManager() { return m_injectedScriptManager.get(); }
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8StringUtil.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8StringUtil.cpp index 7cc1995..344ef49 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8StringUtil.cpp +++ b/third_party/WebKit/Source/platform/v8_inspector/V8StringUtil.cpp
@@ -14,9 +14,11 @@ namespace { -String findMagicComment(const String& content, const String& name, bool multiline) +String findMagicComment(const String& content, const String& name, bool multiline, bool* deprecated) { ASSERT(name.find("=") == kNotFound); + if (deprecated) + *deprecated = false; unsigned length = content.length(); unsigned nameLength = name.length(); @@ -38,7 +40,7 @@ if ((content[pos + 1] != '/' || multiline) && (content[pos + 1] != '*' || !multiline)) continue; - if (content[pos + 2] != '#') + if (content[pos + 2] != '#' && content[pos + 2] != '@') continue; if (content[pos + 3] != ' ' && content[pos + 3] != '\t') continue; @@ -54,6 +56,9 @@ break; } + if (deprecated && content[pos + 2] == '@') + *deprecated = true; + ASSERT(equalSignPos); ASSERT(!multiline || closingCommentPos); size_t urlPos = equalSignPos + 1; @@ -158,14 +163,14 @@ namespace V8ContentSearchUtil { -String findSourceURL(const String& content, bool multiline) +String findSourceURL(const String& content, bool multiline, bool* deprecated) { - return findMagicComment(content, "sourceURL", multiline); + return findMagicComment(content, "sourceURL", multiline, deprecated); } -String findSourceMapURL(const String& content, bool multiline) +String findSourceMapURL(const String& content, bool multiline, bool* deprecated) { - return findMagicComment(content, "sourceMappingURL", multiline); + return findMagicComment(content, "sourceMappingURL", multiline, deprecated); } PassRefPtr<protocol::TypeBuilder::Array<protocol::TypeBuilder::Debugger::SearchMatch>> searchInTextByLines(V8Debugger* debugger, const String& text, const String& query, const bool caseSensitive, const bool isRegex)
diff --git a/third_party/WebKit/Source/platform/v8_inspector/public/V8ContentSearchUtil.h b/third_party/WebKit/Source/platform/v8_inspector/public/V8ContentSearchUtil.h index 512c651..55658fc0 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/public/V8ContentSearchUtil.h +++ b/third_party/WebKit/Source/platform/v8_inspector/public/V8ContentSearchUtil.h
@@ -15,8 +15,8 @@ namespace V8ContentSearchUtil { -PLATFORM_EXPORT String findSourceURL(const String& content, bool multiline); -PLATFORM_EXPORT String findSourceMapURL(const String& content, bool multiline); +PLATFORM_EXPORT String findSourceURL(const String& content, bool multiline, bool* deprecated = nullptr); +PLATFORM_EXPORT String findSourceMapURL(const String& content, bool multiline, bool* deprecated = nullptr); PLATFORM_EXPORT PassRefPtr<protocol::TypeBuilder::Array<protocol::TypeBuilder::Debugger::SearchMatch>> searchInTextByLines(V8Debugger*, const String& text, const String& query, const bool caseSensitive, const bool isRegex); }
diff --git a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp index 8489a85..491c651 100644 --- a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp +++ b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp
@@ -194,6 +194,18 @@ return secureContextBypassingSchemes; } +static URLSchemesSet& allowedInReferrerSchemes() +{ + DEFINE_STATIC_LOCAL_WITH_LOCK(URLSchemesSet, allowedInReferrerSchemes, ()); + + if (allowedInReferrerSchemes.isEmpty()) { + allowedInReferrerSchemes.add("http"); + allowedInReferrerSchemes.add("https"); + } + + return allowedInReferrerSchemes; +} + bool SchemeRegistry::shouldTreatURLSchemeAsLocal(const String& scheme) { if (scheme.isEmpty()) @@ -392,6 +404,26 @@ return firstPartyWhenTopLevelSchemes().contains(scheme); } +void SchemeRegistry::registerURLSchemeAsAllowedForReferrer(const String& scheme) +{ + MutexLocker locker(mutex()); + allowedInReferrerSchemes().add(scheme); +} + +void SchemeRegistry::removeURLSchemeAsAllowedForReferrer(const String& scheme) +{ + MutexLocker locker(mutex()); + allowedInReferrerSchemes().remove(scheme); +} + +bool SchemeRegistry::shouldTreatURLSchemeAsAllowedForReferrer(const String& scheme) +{ + if (scheme.isEmpty()) + return false; + MutexLocker locker(mutex()); + return allowedInReferrerSchemes().contains(scheme); +} + void SchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy(const String& scheme, PolicyAreas policyAreas) { MutexLocker locker(mutex());
diff --git a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h index 153cf4a..a234c9b 100644 --- a/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h +++ b/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h
@@ -101,6 +101,11 @@ static void registerURLSchemeAsFirstPartyWhenTopLevel(const String& scheme); static bool shouldTreatURLSchemeAsFirstPartyWhenTopLevel(const String& scheme); + // Schemes that can be used in a referrer. + static void registerURLSchemeAsAllowedForReferrer(const String& scheme); + static void removeURLSchemeAsAllowedForReferrer(const String& scheme); + static bool shouldTreatURLSchemeAsAllowedForReferrer(const String& scheme); + // Allow resources from some schemes to load on a page, regardless of its // Content Security Policy. // This enum should be kept in sync with public/web/WebSecurityPolicy.h.
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp index af82e512..3eb154a 100644 --- a/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp +++ b/third_party/WebKit/Source/platform/weborigin/SecurityPolicy.cpp
@@ -31,6 +31,7 @@ #include "platform/RuntimeEnabledFeatures.h" #include "platform/weborigin/KURL.h" #include "platform/weborigin/OriginAccessEntry.h" +#include "platform/weborigin/SchemeRegistry.h" #include "platform/weborigin/SecurityOrigin.h" #include "wtf/HashMap.h" #include "wtf/HashSet.h" @@ -67,9 +68,10 @@ bool SecurityPolicy::shouldHideReferrer(const KURL& url, const String& referrer) { bool referrerIsSecureURL = protocolIs(referrer, "https"); - bool referrerIsWebURL = referrerIsSecureURL || protocolIs(referrer, "http"); + String scheme = KURL(KURL(), referrer).protocol(); + bool schemeIsAllowed = SchemeRegistry::shouldTreatURLSchemeAsAllowedForReferrer(scheme); - if (!referrerIsWebURL) + if (!schemeIsAllowed) return true; if (!referrerIsSecureURL) @@ -85,7 +87,8 @@ if (referrer.isEmpty()) return Referrer(String(), referrerPolicy); - if (!(protocolIs(referrer, "https") || protocolIs(referrer, "http"))) + String scheme = KURL(KURL(), referrer).protocol(); + if (!SchemeRegistry::shouldTreatURLSchemeAsAllowedForReferrer(scheme)) return Referrer(String(), referrerPolicy); if (SecurityOrigin::shouldUseInnerURL(url))
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityPolicyTest.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityPolicyTest.cpp index 59a3603d..e6ed820 100644 --- a/third_party/WebKit/Source/platform/weborigin/SecurityPolicyTest.cpp +++ b/third_party/WebKit/Source/platform/weborigin/SecurityPolicyTest.cpp
@@ -31,14 +31,40 @@ #include "platform/weborigin/SecurityPolicy.h" #include "platform/weborigin/KURL.h" +#include "platform/weborigin/SchemeRegistry.h" #include "platform/weborigin/SecurityOrigin.h" #include "testing/gtest/include/gtest/gtest.h" namespace blink { -TEST(SecurityPolicyTest, ReferrerIsAlwaysAWebURL) +TEST(SecurityPolicyTest, EmptyReferrerForUnauthorizedScheme) { - EXPECT_TRUE(String() == SecurityPolicy::generateReferrer(ReferrerPolicyAlways, KURL(ParsedURLString, "http://example.com/"), String::fromUTF8("chrome://somepage/")).referrer); + const KURL exampleHttpUrl = KURL(ParsedURLString, "http://example.com/"); + EXPECT_TRUE(String() == SecurityPolicy::generateReferrer(ReferrerPolicyAlways, exampleHttpUrl, String::fromUTF8("chrome://somepage/")).referrer); +} + +TEST(SecurityPolicyTest, GenerateReferrerRespectsReferrerSchemesRegistry) +{ + const KURL exampleHttpUrl = KURL(ParsedURLString, "http://example.com/"); + const String foobarURL = String::fromUTF8("foobar://somepage/"); + const String foobarScheme = String::fromUTF8("foobar"); + + EXPECT_EQ(String(), SecurityPolicy::generateReferrer(ReferrerPolicyAlways, exampleHttpUrl, foobarURL).referrer); + SchemeRegistry::registerURLSchemeAsAllowedForReferrer(foobarScheme); + EXPECT_EQ(foobarURL, SecurityPolicy::generateReferrer(ReferrerPolicyAlways, exampleHttpUrl, foobarURL).referrer); + SchemeRegistry::removeURLSchemeAsAllowedForReferrer(foobarScheme); +} + +TEST(SecurityPolicyTest, ShouldHideReferrerRespectsReferrerSchemesRegistry) +{ + const KURL exampleHttpUrl = KURL(ParsedURLString, "http://example.com/"); + const String foobarURL = String::fromUTF8("foobar://somepage/"); + const String foobarScheme = String::fromUTF8("foobar"); + + EXPECT_TRUE(SecurityPolicy::shouldHideReferrer(exampleHttpUrl, foobarScheme)); + SchemeRegistry::registerURLSchemeAsAllowedForReferrer(foobarScheme); + EXPECT_FALSE(SecurityPolicy::shouldHideReferrer(exampleHttpUrl, foobarURL)); + SchemeRegistry::removeURLSchemeAsAllowedForReferrer(foobarScheme); } TEST(SecurityPolicyTest, GenerateReferrer)
diff --git a/third_party/WebKit/Source/web/TextFinder.cpp b/third_party/WebKit/Source/web/TextFinder.cpp index 6ecd7d07..bdaf155 100644 --- a/third_party/WebKit/Source/web/TextFinder.cpp +++ b/third_party/WebKit/Source/web/TextFinder.cpp
@@ -209,6 +209,7 @@ ownerFrame().frame()->document()->markers().removeMarkers(DocumentMarker::TextMatch); ownerFrame().frame()->editor().setMarkedTextMatchesAreHighlighted(false); clearFindMatchesCache(); + resetActiveMatch(); // Let the frame know that we don't want tickmarks anymore. ownerFrame().frameView()->invalidatePaintForTickmarks();
diff --git a/third_party/WebKit/Source/web/WebDocument.cpp b/third_party/WebKit/Source/web/WebDocument.cpp index 1f4822f2..acd1576d 100644 --- a/third_party/WebKit/Source/web/WebDocument.cpp +++ b/third_party/WebKit/Source/web/WebDocument.cpp
@@ -266,6 +266,13 @@ return cache ? WebAXObject(cache->objectFromAXID(axID)) : WebAXObject(); } +WebAXObject WebDocument::focusedAccessibilityObject() const +{ + const Document* document = constUnwrap<Document>(); + AXObjectCacheImpl* cache = toAXObjectCacheImpl(document->axObjectCache()); + return cache ? WebAXObject(cache->focusedObject()) : WebAXObject(); +} + WebVector<WebDraggableRegion> WebDocument::draggableRegions() const { WebVector<WebDraggableRegion> draggableRegions;
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp b/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp index 13eff21c..80db122d 100644 --- a/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp +++ b/third_party/WebKit/Source/web/WebEmbeddedWorkerImplTest.cpp
@@ -177,7 +177,7 @@ #define MAYBE_DontPauseAfterDownload DontPauseAfterDownload #endif -TEST_F(WebEmbeddedWorkerImplTest, DontPauseAfterDownload) +TEST_F(WebEmbeddedWorkerImplTest, MAYBE_DontPauseAfterDownload) { EXPECT_CALL(*m_mockClient, workerReadyForInspection()) .Times(1); @@ -206,7 +206,7 @@ #define MAYBE_PauseAfterDownload PauseAfterDownload #endif -TEST_F(WebEmbeddedWorkerImplTest, PauseAfterDownload) +TEST_F(WebEmbeddedWorkerImplTest, MAYBE_PauseAfterDownload) { EXPECT_CALL(*m_mockClient, workerReadyForInspection()) .Times(1);
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp index eb621366..a73bea7 100644 --- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp +++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -374,7 +374,7 @@ // DisplayItemClient methods String debugName() const final { return "ChromePrintContext"; } - LayoutRect visualRect() const override { ASSERT_NOT_REACHED(); return LayoutRect(); } + LayoutRect visualRect() const override { return LayoutRect(); } protected: // Spools the printed page, a subrect of frame(). Skip the scale step.
diff --git a/third_party/WebKit/Source/web/WebSecurityPolicy.cpp b/third_party/WebKit/Source/web/WebSecurityPolicy.cpp index ea67c1db..2c1ac0f 100644 --- a/third_party/WebKit/Source/web/WebSecurityPolicy.cpp +++ b/third_party/WebKit/Source/web/WebSecurityPolicy.cpp
@@ -152,4 +152,9 @@ SchemeRegistry::registerURLSchemeAsNotAllowingJavascriptURLs(scheme); } +void WebSecurityPolicy::registerURLSchemeAsAllowedForReferrer(const WebString& scheme) +{ + SchemeRegistry::registerURLSchemeAsAllowedForReferrer(scheme); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp index e390f65c..a1c1a2c 100644 --- a/third_party/WebKit/Source/web/WebViewImpl.cpp +++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -638,6 +638,10 @@ WebInputEventResult WebViewImpl::handleMouseWheel(LocalFrame& mainFrame, const WebMouseWheelEvent& event) { + // Halt an in-progress fling on a wheel tick. + if (!event.hasPreciseScrollingDeltas) + endActiveFlingAnimation(); + hidePopups(); return PageWidgetEventHandler::handleMouseWheel(mainFrame, event); } @@ -4549,7 +4553,6 @@ // TODO(jbroman): This should probably have hookups for overlays, visual // viewport, etc. - m_paintArtifactCompositor.initializeIfNeeded(); WebLayer* rootLayer = m_paintArtifactCompositor.webLayer(); ASSERT(rootLayer); m_layerTreeView->setRootLayer(*rootLayer);
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h index c8dfebd7..7d95760 100644 --- a/third_party/WebKit/Source/web/WebViewImpl.h +++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -272,6 +272,7 @@ WebPageImportanceSignals* pageImportanceSignals() override; void transferActiveWheelFlingAnimation(const WebActiveWheelFlingParameters&) override; bool endActiveFlingAnimation() override; + bool isFlinging() const override { return !!m_gestureAnimation.get(); } void setShowPaintRects(bool) override; void setShowDebugBorders(bool); void setShowFPSCounter(bool) override;
diff --git a/third_party/WebKit/Source/web/WebViewImplPaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/web/WebViewImplPaintArtifactCompositorTest.cpp index 4256840..fe594f13 100644 --- a/third_party/WebKit/Source/web/WebViewImplPaintArtifactCompositorTest.cpp +++ b/third_party/WebKit/Source/web/WebViewImplPaintArtifactCompositorTest.cpp
@@ -68,7 +68,6 @@ TEST_F(WebViewImplPaintArtifactCompositorTest, AttachAndDetach) { - paintArtifactCompositor().initializeIfNeeded(); cc::Layer* rootLayer = paintArtifactCompositor().rootLayer(); ASSERT_TRUE(rootLayer);
diff --git a/third_party/WebKit/Source/web/resources/calendarPicker.js b/third_party/WebKit/Source/web/resources/calendarPicker.js index 0ba0d41d..89571bf 100644 --- a/third_party/WebKit/Source/web/resources/calendarPicker.js +++ b/third_party/WebKit/Source/web/resources/calendarPicker.js
@@ -2438,7 +2438,7 @@ if (this.highlightedMonth && row === this.highlightedMonth.year - 1) { var monthButton = cell.monthButtons[this.highlightedMonth.month]; monthButton.classList.add(YearListCell.ClassNameHighlighted); - // aira-activedescendant assumes both elements have layoutObjects, and + // aria-activedescendant assumes both elements have layoutObjects, and // |monthButton| might have no layoutObject yet. var element = this.element; setTimeout(function() {
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp index ead7d815..8dc7f3e9 100644 --- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp +++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
@@ -56,7 +56,7 @@ // dance. Since the parser is threaded, simply spinning the run loop once is not // enough to ensure completion of a load. Instead, the following pattern is // used to ensure that tests see the final state: -// 1. Post a task to trigger a load (LoadTask/LoadHTMLStringTask/ReloadTask). +// 1. Starts a load. // 2. Enter the run loop. // 3. Posted task triggers the load, and starts pumping pending resource // requests using ServeAsyncRequestsTask. @@ -98,84 +98,6 @@ testing::enterRunLoop(); } -class LoadTask : public WebTaskRunner::Task { -public: - LoadTask(WebFrame* frame, const WebURLRequest& request) - : m_frame(frame) - , m_request(request) - { - } - - void run() override - { - m_frame->loadRequest(m_request); - } - -private: - WebFrame* const m_frame; - const WebURLRequest m_request; -}; - -class LoadHTMLStringTask : public WebTaskRunner::Task { -public: - LoadHTMLStringTask(WebFrame* frame, const std::string& html, const WebURL& baseURL) - : m_frame(frame) - , m_html(html) - , m_baseURL(baseURL) - { - } - - void run() override - { - m_frame->loadHTMLString(WebData(m_html.data(), m_html.size()), m_baseURL); - } - -private: - WebFrame* const m_frame; - const std::string m_html; - const WebURL m_baseURL; -}; - -class LoadHistoryItemTask : public WebTaskRunner::Task { -public: - LoadHistoryItemTask(WebFrame* frame, const WebHistoryItem& item, WebHistoryLoadType loadType, WebURLRequest::CachePolicy cachePolicy) - : m_frame(frame) - , m_item(item) - , m_loadType(loadType) - , m_cachePolicy(cachePolicy) - { - } - - void run() override - { - m_frame->loadHistoryItem(m_item, m_loadType, m_cachePolicy); - } - -private: - WebFrame* const m_frame; - const WebHistoryItem m_item; - const WebHistoryLoadType m_loadType; - const WebURLRequest::CachePolicy m_cachePolicy; -}; - -class ReloadTask : public WebTaskRunner::Task { -public: - ReloadTask(WebFrame* frame, bool ignoreCache) - : m_frame(frame) - , m_ignoreCache(ignoreCache) - { - } - - void run() override - { - m_frame->reload(m_ignoreCache); - } - -private: - WebFrame* const m_frame; - const bool m_ignoreCache; -}; - TestWebFrameClient* defaultWebFrameClient() { DEFINE_STATIC_LOCAL(TestWebFrameClient, client, ()); @@ -195,32 +117,31 @@ WebURLRequest urlRequest; urlRequest.initialize(); urlRequest.setURL(URLTestHelpers::toKURL(url)); - - Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_HERE, new LoadTask(frame, urlRequest)); + frame->loadRequest(urlRequest); pumpPendingRequests(frame); } void loadHTMLString(WebFrame* frame, const std::string& html, const WebURL& baseURL) { - Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_HERE, new LoadHTMLStringTask(frame, html, baseURL)); + frame->loadHTMLString(WebData(html.data(), html.size()), baseURL); pumpPendingRequests(frame); } void loadHistoryItem(WebFrame* frame, const WebHistoryItem& item, WebHistoryLoadType loadType, WebURLRequest::CachePolicy cachePolicy) { - Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_HERE, new LoadHistoryItemTask(frame, item, loadType, cachePolicy)); + frame->loadHistoryItem(item, loadType, cachePolicy); pumpPendingRequests(frame); } void reloadFrame(WebFrame* frame) { - Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_HERE, new ReloadTask(frame, false)); + frame->reload(false); pumpPendingRequests(frame); } void reloadFrameIgnoringCache(WebFrame* frame) { - Platform::current()->currentThread()->taskRunner()->postTask(BLINK_FROM_HERE, new ReloadTask(frame, true)); + frame->reload(true); pumpPendingRequests(frame); }
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp index a9fbdd44..7242f22 100644 --- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp +++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -3716,6 +3716,7 @@ FindUpdateWebFrameClient() : m_findResultsAreReady(false) , m_count(-1) + , m_activeIndex(-1) { } @@ -3726,12 +3727,19 @@ m_findResultsAreReady = true; } + void reportFindInPageSelection(int, int activeMatchOrdinal, const WebRect&) override + { + m_activeIndex = activeMatchOrdinal; + } + bool findResultsAreReady() const { return m_findResultsAreReady; } int count() const { return m_count; } + int activeIndex() const { return m_activeIndex; } private: bool m_findResultsAreReady; int m_count; + int m_activeIndex; }; TEST_P(ParameterizedWebFrameTest, FindInPageMatchRects) @@ -3854,6 +3862,54 @@ EXPECT_TRUE(mainFrame->findMatchMarkersVersion() != rectsVersion); } +TEST_F(WebFrameTest, FindInPageActiveIndex) +{ + registerMockedHttpURLLoad("find_match_count.html"); + + FindUpdateWebFrameClient client; + FrameTestHelpers::WebViewHelper webViewHelper; + webViewHelper.initializeAndLoad(m_baseURL + "find_match_count.html", true, &client); + webViewHelper.webView()->resize(WebSize(640, 480)); + runPendingTasks(); + + const char* kFindString = "a"; + const int kFindIdentifier = 7777; + const int kActiveIndex = 1; + + WebFindOptions options; + WebString searchText = WebString::fromUTF8(kFindString); + WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webViewHelper.webView()->mainFrame()); + EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchText, options, false, 0)); + mainFrame->resetMatchCount(); + + for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false)) + frame->toWebLocalFrame()->scopeStringMatches(kFindIdentifier, searchText, options, true); + + runPendingTasks(); + EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchText, options, false, 0)); + mainFrame->stopFinding(true); + + for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false)) + frame->toWebLocalFrame()->scopeStringMatches(kFindIdentifier, searchText, options, true); + + runPendingTasks(); + EXPECT_TRUE(client.findResultsAreReady()); + EXPECT_EQ(kActiveIndex, client.activeIndex()); + + const char* kFindStringNew = "e"; + WebString searchTextNew = WebString::fromUTF8(kFindStringNew); + + EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchTextNew, options, false, 0)); + mainFrame->resetMatchCount(); + + for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false)) + frame->toWebLocalFrame()->scopeStringMatches(kFindIdentifier, searchTextNew, options, true); + + runPendingTasks(); + EXPECT_TRUE(client.findResultsAreReady()); + EXPECT_EQ(kActiveIndex, client.activeIndex()); +} + TEST_P(ParameterizedWebFrameTest, FindInPageSkipsHiddenFrames) { registerMockedHttpURLLoad("find_in_hidden_frame.html");
diff --git a/third_party/WebKit/Source/web/tests/data/find_match_count.html b/third_party/WebKit/Source/web/tests/data/find_match_count.html new file mode 100644 index 0000000..2628f07 --- /dev/null +++ b/third_party/WebKit/Source/web/tests/data/find_match_count.html
@@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> +<body> +This is a simple page with multiple 'a' and 'e' characters in the specified text. +</body> +</html>
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py index 59f4657..46041d6 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py
@@ -196,7 +196,7 @@ return method(source, *args) raise AssertionError('unknown message %s received from %s, args=%s' % (name, source, repr(args))) - def _handle_started_test(self, worker_name, test_input, test_timeout_sec): + def _handle_started_test(self, worker_name, test_input): self._printer.print_started_test(test_input.test_name) def _handle_finished_test_list(self, worker_name, list_name): @@ -279,12 +279,11 @@ stop_when_done = True self._update_test_input(test_input) - test_timeout_sec = self._timeout(test_input) start = time.time() device_failed = False _log.debug("%s %s started" % (self._name, test_input.test_name)) - self._caller.post('started_test', test_input, test_timeout_sec) + self._caller.post('started_test', test_input) result = single_test_runner.run_single_test( self._port, self._options, self._results_directory, self._name, self._primary_driver, self._secondary_driver, test_input, @@ -304,18 +303,6 @@ self._kill_driver(self._primary_driver, "primary") self._kill_driver(self._secondary_driver, "secondary") - def _timeout(self, test_input): - """Compute the appropriate timeout value for a test.""" - # The driver watchdog uses 2.5x the timeout; we want to be - # larger than that. We also add a little more padding if we're - # running tests in a separate thread. - # - # Note that we need to convert the test timeout from a - # string value in milliseconds to a float for Python. - - # FIXME: Can we just return the test_input.timeout now? - driver_timeout_sec = 3.0 * float(test_input.timeout) / 1000.0 - def _kill_driver(self, driver, label): # Be careful about how and when we kill the driver; if driver.stop() # raises an exception, this routine may get re-entered via __del__.
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py index ab51899..e27855b7 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
@@ -60,12 +60,6 @@ host = Host() try: - # TODO: crbug.com/539509 - Drop this check and make --target work properly. - if options.configuration not in (None, 'Debug', 'Release', 'Debug_x64', 'Release_x64'): - raise NotImplementedError('--target must be either "Debug" or "Release"; other values do not work\n.' - 'Use multiple //out directories for multiple builds if necessary.\n' - 'See crbug.com/539509.') - port = host.port_factory.get(options.platform, options) except NotImplementedError, e: # FIXME: is this the best way to handle unsupported port names?
diff --git a/third_party/WebKit/public/blink_headers.gypi b/third_party/WebKit/public/blink_headers.gypi index 42912c5..80c4ade 100644 --- a/third_party/WebKit/public/blink_headers.gypi +++ b/third_party/WebKit/public/blink_headers.gypi
@@ -233,9 +233,9 @@ "platform/modules/bluetooth/WebBluetooth.h", "platform/modules/bluetooth/WebBluetoothDevice.h", "platform/modules/bluetooth/WebBluetoothError.h", - "platform/modules/bluetooth/WebBluetoothGATTCharacteristic.h", - "platform/modules/bluetooth/WebBluetoothGATTCharacteristicInit.h", - "platform/modules/bluetooth/WebBluetoothGATTService.h", + "platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristic.h", + "platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h", + "platform/modules/bluetooth/WebBluetoothRemoteGATTService.h", "platform/modules/bluetooth/WebRequestDeviceOptions.h", "platform/modules/device_orientation/WebDeviceMotionData.h", "platform/modules/device_orientation/WebDeviceMotionListener.h",
diff --git a/third_party/WebKit/public/platform/WebMediaConstraints.h b/third_party/WebKit/public/platform/WebMediaConstraints.h index b0ef2b96..b53fbce 100644 --- a/third_party/WebKit/public/platform/WebMediaConstraints.h +++ b/third_party/WebKit/public/platform/WebMediaConstraints.h
@@ -53,6 +53,8 @@ { return m_name; } + virtual WebString toString() const = 0; + private: const char* m_name; }; @@ -88,6 +90,7 @@ bool matches(long value) const; bool isEmpty() const override; bool hasMandatory() const override; + WebString toString() const override; bool hasMin() const { return m_hasMin; } long min() const { return m_min; } bool hasMax() const { return m_hasMax; } @@ -140,6 +143,7 @@ bool matches(double value) const; bool isEmpty() const override; bool hasMandatory() const override; + WebString toString() const override; bool hasMin() const { return m_hasMin; } double min() const { return m_min; } bool hasMax() const { return m_hasMax; } @@ -181,6 +185,7 @@ bool matches(WebString value) const; bool isEmpty() const override; bool hasMandatory() const override; + WebString toString() const override; const WebVector<WebString>& exact() const; const WebVector<WebString>& ideal() const; @@ -193,6 +198,8 @@ public: explicit BooleanConstraint(const char* name); + bool exact() const { return m_exact; } + bool ideal() const { return m_ideal; } void setIdeal(bool value) { m_ideal = value; @@ -208,6 +215,7 @@ bool matches(bool value) const; bool isEmpty() const override; bool hasMandatory() const override; + WebString toString() const override; private: unsigned m_ideal : 1; @@ -250,7 +258,6 @@ StringConstraint googArrayGeometry; BooleanConstraint googAudioMirroring; BooleanConstraint googDAEchoCancellation; - BooleanConstraint googAecDump; BooleanConstraint googNoiseReduction; StringConstraint offerToReceiveAudio; StringConstraint offerToReceiveVideo; @@ -277,6 +284,7 @@ BLINK_PLATFORM_EXPORT bool isEmpty() const; BLINK_PLATFORM_EXPORT bool hasMandatory() const; BLINK_PLATFORM_EXPORT bool hasMandatoryOutsideSet(const std::vector<std::string>&, std::string&) const; + BLINK_PLATFORM_EXPORT WebString toString() const; private: std::vector<const BaseConstraint*> allConstraints() const; @@ -335,6 +343,8 @@ BLINK_PLATFORM_EXPORT const WebMediaTrackConstraintSet& basic() const; BLINK_PLATFORM_EXPORT const WebVector<WebMediaTrackConstraintSet>& advanced() const; + BLINK_PLATFORM_EXPORT const WebString toString() const; + private: WebPrivatePtr<WebMediaConstraintsPrivate> m_private; };
diff --git a/third_party/WebKit/public/platform/WebRTCOfferOptions.h b/third_party/WebKit/public/platform/WebRTCOfferOptions.h index d9559e8..9246428 100644 --- a/third_party/WebKit/public/platform/WebRTCOfferOptions.h +++ b/third_party/WebKit/public/platform/WebRTCOfferOptions.h
@@ -13,9 +13,11 @@ class RTCOfferOptions; -class WebRTCOfferOptions { +class BLINK_PLATFORM_EXPORT WebRTCOfferOptions { public: - WebRTCOfferOptions() { } + WebRTCOfferOptions(int32_t offerToReceiveAudio, + int32_t offerToReceiveVideo, bool voiceActivityDetection, + bool iceRestart); WebRTCOfferOptions(const WebRTCOfferOptions& other) { assign(other); } ~WebRTCOfferOptions() { reset(); } @@ -25,18 +27,18 @@ return *this; } - BLINK_PLATFORM_EXPORT void assign(const WebRTCOfferOptions&); + void assign(const WebRTCOfferOptions&); - BLINK_PLATFORM_EXPORT void reset(); + void reset(); bool isNull() const { return m_private.isNull(); } - BLINK_PLATFORM_EXPORT int32_t offerToReceiveVideo() const; - BLINK_PLATFORM_EXPORT int32_t offerToReceiveAudio() const; - BLINK_PLATFORM_EXPORT bool voiceActivityDetection() const; - BLINK_PLATFORM_EXPORT bool iceRestart() const; + int32_t offerToReceiveVideo() const; + int32_t offerToReceiveAudio() const; + bool voiceActivityDetection() const; + bool iceRestart() const; #if INSIDE_BLINK - BLINK_PLATFORM_EXPORT WebRTCOfferOptions(RTCOfferOptions*); + WebRTCOfferOptions(RTCOfferOptions*); #endif private:
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetooth.h b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetooth.h index b750d27..090e5c65 100644 --- a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetooth.h +++ b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetooth.h
@@ -13,24 +13,24 @@ namespace blink { -class WebBluetoothGATTCharacteristic; +class WebBluetoothRemoteGATTCharacteristic; struct WebBluetoothDevice; -struct WebBluetoothGATTCharacteristicInit; -struct WebBluetoothGATTService; +struct WebBluetoothRemoteGATTCharacteristicInit; +struct WebBluetoothRemoteGATTService; struct WebRequestDeviceOptions; // Success and failure callbacks for requestDevice. using WebBluetoothRequestDeviceCallbacks = WebCallbacks<WebPassOwnPtr<WebBluetoothDevice>, const WebBluetoothError&>; // Success and failure callbacks for connectGATT. -using WebBluetoothGATTServerConnectCallbacks = WebCallbacks<void, const WebBluetoothError&>; +using WebBluetoothRemoteGATTServerConnectCallbacks = WebCallbacks<void, const WebBluetoothError&>; // Success and failure callbacks for getPrimaryService. -using WebBluetoothGetPrimaryServiceCallbacks = WebCallbacks<WebPassOwnPtr<WebBluetoothGATTService>, const WebBluetoothError&>; +using WebBluetoothGetPrimaryServiceCallbacks = WebCallbacks<WebPassOwnPtr<WebBluetoothRemoteGATTService>, const WebBluetoothError&>; // Success and failure callbacks for getCharacteristic. -using WebBluetoothGetCharacteristicCallbacks = WebCallbacks<WebPassOwnPtr<WebBluetoothGATTCharacteristicInit>, const WebBluetoothError&>; +using WebBluetoothGetCharacteristicCallbacks = WebCallbacks<WebPassOwnPtr<WebBluetoothRemoteGATTCharacteristicInit>, const WebBluetoothError&>; // Success and failure callbacks for readValue. using WebBluetoothReadValueCallbacks = WebCallbacks<const WebVector<uint8_t>&, const WebBluetoothError&>; @@ -53,23 +53,23 @@ // BluetoothDevice methods: - // BluetoothGATTRemoteServer methods: + // BluetoothRemoteGATTServer methods: // See https://webbluetoothchrome.github.io/web-bluetooth/#idl-def-bluetoothgattremoteserver virtual void connect(const WebString& deviceId, - WebBluetoothGATTServerConnectCallbacks*) { } + WebBluetoothRemoteGATTServerConnectCallbacks*) { } virtual void disconnect(const WebString& deviceId) = 0; virtual void getPrimaryService(const WebString& deviceId, const WebString& serviceUUID, WebBluetoothGetPrimaryServiceCallbacks*) { } // virtual void getPrimaryServices() { } - // BluetoothGATTService methods: + // BluetoothRemoteGATTService methods: // See https://webbluetoothchrome.github.io/web-bluetooth/#idl-def-bluetoothgattservice virtual void getCharacteristic(const WebString& serviceInstanceID, const WebString& characteristicUUID, WebBluetoothGetCharacteristicCallbacks*) { } - // BluetoothGATTCharacteristic methods: + // BluetoothRemoteGATTCharacteristic methods: // See https://webbluetoothchrome.github.io/web-bluetooth/#bluetoothgattcharacteristic virtual void readValue(const WebString& characteristicInstanceID, WebBluetoothReadValueCallbacks*) { } @@ -77,19 +77,19 @@ const WebVector<uint8_t>& value, WebBluetoothWriteValueCallbacks*) {} virtual void startNotifications(const WebString& characteristicInstanceID, - WebBluetoothGATTCharacteristic*, + WebBluetoothRemoteGATTCharacteristic*, WebBluetoothNotificationsCallbacks*) {} virtual void stopNotifications(const WebString& characteristicInstanceID, - WebBluetoothGATTCharacteristic*, + WebBluetoothRemoteGATTCharacteristic*, WebBluetoothNotificationsCallbacks*) {} // Called when addEventListener is called on a characteristic. virtual void registerCharacteristicObject( const WebString& characteristicInstanceID, - WebBluetoothGATTCharacteristic*) = 0; + WebBluetoothRemoteGATTCharacteristic*) = 0; virtual void characteristicObjectRemoved( const WebString& characteristicInstanceID, - WebBluetoothGATTCharacteristic*) {} + WebBluetoothRemoteGATTCharacteristic*) {} }; } // namespace blink
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristic.h b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristic.h deleted file mode 100644 index 2f4154cd..0000000 --- a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristic.h +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef WebBluetoothGATTCharacteristic_h -#define WebBluetoothGATTCharacteristic_h - -#include "public/platform/WebVector.h" - -namespace blink { - -// An object through which the embedder can trigger events on a Document-bound -// Web Bluetooth GATT Characteristic object. -class WebBluetoothGATTCharacteristic { -public: - // Used to notify blink that the characteristic's value changed. - virtual void dispatchCharacteristicValueChanged(const WebVector<uint8_t>&) = 0; -}; - -} // namespace blink - -#endif // WebBluetoothGATTCharacteristicDelegate_h
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristicInit.h b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristicInit.h deleted file mode 100644 index cc31db0..0000000 --- a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristicInit.h +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef WebBluetoothGATTCharacteristicInit_h -#define WebBluetoothGATTCharacteristicInit_h - -#include "public/platform/WebString.h" -#include "public/platform/WebVector.h" - -namespace blink { - -// Contains members corresponding to BluetoothGATTCharacteristic attributes as -// specified in the IDL. -struct WebBluetoothGATTCharacteristicInit { - WebBluetoothGATTCharacteristicInit(const WebString& characteristicInstanceID, - const WebString& serviceInstanceID, - const WebString& uuid, - uint32_t characteristicProperties) - : characteristicInstanceID(characteristicInstanceID) - , serviceInstanceID(serviceInstanceID) - , uuid(uuid) - , characteristicProperties(characteristicProperties) - { - } - - const WebString characteristicInstanceID; - const WebString serviceInstanceID; - const WebString uuid; - const uint32_t characteristicProperties; - const WebVector<uint8_t> value; -}; - -} // namespace blink - -#endif // WebBluetoothGATTCharacteristicInit_h
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTService.h b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTService.h deleted file mode 100644 index 4171faa..0000000 --- a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTService.h +++ /dev/null
@@ -1,34 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef WebBluetoothGATTService_h -#define WebBluetoothGATTService_h - -#include "public/platform/WebString.h" - -namespace blink { - -struct WebBluetoothGATTService { - WebBluetoothGATTService(const WebString& serviceInstanceID, - const WebString& uuid, - bool isPrimary, - const WebString& deviceInstanceID) - : serviceInstanceID(serviceInstanceID) - , uuid(uuid) - , isPrimary(isPrimary) - , deviceInstanceID(deviceInstanceID) - { - } - - // Members corresponding to BluetoothGATTService attributes as - // specified in the IDL. - const WebString serviceInstanceID; - const WebString uuid; - const bool isPrimary; - const WebString deviceInstanceID; -}; - -} // namespace blink - -#endif // WebBluetoothGATTService_h
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristic.h b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristic.h new file mode 100644 index 0000000..d7a02fa --- /dev/null +++ b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristic.h
@@ -0,0 +1,22 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebBluetoothRemoteGATTCharacteristic_h +#define WebBluetoothRemoteGATTCharacteristic_h + +#include "public/platform/WebVector.h" + +namespace blink { + +// An object through which the embedder can trigger events on a Document-bound +// Web Bluetooth GATT Characteristic object. +class WebBluetoothRemoteGATTCharacteristic { +public: + // Used to notify blink that the characteristic's value changed. + virtual void dispatchCharacteristicValueChanged(const WebVector<uint8_t>&) = 0; +}; + +} // namespace blink + +#endif // WebBluetoothRemoteGATTCharacteristicDelegate_h
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h new file mode 100644 index 0000000..a07f917 --- /dev/null +++ b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h
@@ -0,0 +1,36 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebBluetoothRemoteGATTCharacteristicInit_h +#define WebBluetoothRemoteGATTCharacteristicInit_h + +#include "public/platform/WebString.h" +#include "public/platform/WebVector.h" + +namespace blink { + +// Contains members corresponding to BluetoothRemoteGATTCharacteristic attributes as +// specified in the IDL. +struct WebBluetoothRemoteGATTCharacteristicInit { + WebBluetoothRemoteGATTCharacteristicInit(const WebString& characteristicInstanceID, + const WebString& serviceInstanceID, + const WebString& uuid, + uint32_t characteristicProperties) + : characteristicInstanceID(characteristicInstanceID) + , serviceInstanceID(serviceInstanceID) + , uuid(uuid) + , characteristicProperties(characteristicProperties) + { + } + + const WebString characteristicInstanceID; + const WebString serviceInstanceID; + const WebString uuid; + const uint32_t characteristicProperties; + const WebVector<uint8_t> value; +}; + +} // namespace blink + +#endif // WebBluetoothRemoteGATTCharacteristicInit_h
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTService.h b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTService.h new file mode 100644 index 0000000..723babd1 --- /dev/null +++ b/third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothRemoteGATTService.h
@@ -0,0 +1,34 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebBluetoothRemoteGATTService_h +#define WebBluetoothRemoteGATTService_h + +#include "public/platform/WebString.h" + +namespace blink { + +struct WebBluetoothRemoteGATTService { + WebBluetoothRemoteGATTService(const WebString& serviceInstanceID, + const WebString& uuid, + bool isPrimary, + const WebString& deviceInstanceID) + : serviceInstanceID(serviceInstanceID) + , uuid(uuid) + , isPrimary(isPrimary) + , deviceInstanceID(deviceInstanceID) + { + } + + // Members corresponding to BluetoothRemoteGATTService attributes as + // specified in the IDL. + const WebString serviceInstanceID; + const WebString uuid; + const bool isPrimary; + const WebString deviceInstanceID; +}; + +} // namespace blink + +#endif // WebBluetoothRemoteGATTService_h
diff --git a/third_party/WebKit/public/web/WebDocument.h b/third_party/WebKit/public/web/WebDocument.h index b81c119..4c34f91f 100644 --- a/third_party/WebKit/public/web/WebDocument.h +++ b/third_party/WebKit/public/web/WebDocument.h
@@ -121,6 +121,10 @@ // Gets the accessibility object for an object on this page by ID. BLINK_EXPORT WebAXObject accessibilityObjectFromID(int axID) const; + + // Gets the accessibility object that has focus. + BLINK_EXPORT WebAXObject focusedAccessibilityObject() const; + // Inserts the given CSS source code as a stylesheet in the document. BLINK_EXPORT void insertStyleSheet(const WebString& sourceCode);
diff --git a/third_party/WebKit/public/web/WebInputEvent.h b/third_party/WebKit/public/web/WebInputEvent.h index b67617a..3be5b18e 100644 --- a/third_party/WebKit/public/web/WebInputEvent.h +++ b/third_party/WebKit/public/web/WebInputEvent.h
@@ -533,6 +533,12 @@ } scrollUpdate; struct { + // The original delta units the scrollBegin and scrollUpdates + // were sent as. + ScrollUnits deltaUnits; + } scrollEnd; + + struct { float velocityX; float velocityY; // If true, this event will skip hit testing to find a scroll
diff --git a/third_party/WebKit/public/web/WebSecurityPolicy.h b/third_party/WebKit/public/web/WebSecurityPolicy.h index fbe011d..eb2858f 100644 --- a/third_party/WebKit/public/web/WebSecurityPolicy.h +++ b/third_party/WebKit/public/web/WebSecurityPolicy.h
@@ -125,6 +125,9 @@ // by bookmarklets or javascript: URLs typed in the omnibox. BLINK_EXPORT static void registerURLSchemeAsNotAllowingJavascriptURLs(const WebString&); + // Registers an URL scheme as allowed in referrers. + BLINK_EXPORT static void registerURLSchemeAsAllowedForReferrer(const WebString&); + private: WebSecurityPolicy(); };
diff --git a/third_party/WebKit/public/web/WebView.h b/third_party/WebKit/public/web/WebView.h index 18b2e9d..0f417ca 100644 --- a/third_party/WebKit/public/web/WebView.h +++ b/third_party/WebKit/public/web/WebView.h
@@ -447,6 +447,9 @@ // Cancels an active fling, returning true if a fling was active. virtual bool endActiveFlingAnimation() = 0; + // Returns true if there's an active fling animation. + virtual bool isFlinging() const = 0; + virtual void setShowPaintRects(bool) = 0; virtual void setShowFPSCounter(bool) = 0; virtual void setShowScrollBottleneckRects(bool) = 0;
diff --git a/third_party/boringssl/boringssl.gypi b/third_party/boringssl/boringssl.gypi index b9f8413..530dbf7 100644 --- a/third_party/boringssl/boringssl.gypi +++ b/third_party/boringssl/boringssl.gypi
@@ -112,6 +112,7 @@ 'src/crypto/bn/shift.c', 'src/crypto/bn/sqrt.c', 'src/crypto/buf/buf.c', + 'src/crypto/bytestring/asn1_compat.c', 'src/crypto/bytestring/ber.c', 'src/crypto/bytestring/cbb.c', 'src/crypto/bytestring/cbs.c',
diff --git a/third_party/boringssl/err_data.c b/third_party/boringssl/err_data.c index fe7979b..d4cc08bd 100644 --- a/third_party/boringssl/err_data.c +++ b/third_party/boringssl/err_data.c
@@ -73,159 +73,163 @@ 0xc3a08c6, 0xc3a87b8, 0xc3b00b0, - 0x10321469, - 0x10329475, - 0x1033148e, - 0x103394a1, - 0x10340dd2, - 0x103494c0, - 0x103514d5, - 0x10359507, - 0x10361520, - 0x10369535, - 0x10371553, - 0x10379562, - 0x1038157e, - 0x10389599, - 0x103915a8, - 0x103995c4, - 0x103a15df, - 0x103a95f6, - 0x103b1607, - 0x103b961b, - 0x103c163a, - 0x103c9649, - 0x103d1660, - 0x103d9673, + 0x10321484, + 0x10329490, + 0x103314a9, + 0x103394bc, + 0x10340ded, + 0x103494cf, + 0x103514e4, + 0x10359516, + 0x1036152f, + 0x10369544, + 0x10371562, + 0x10379571, + 0x1038158d, + 0x103895a8, + 0x103915b7, + 0x103995d3, + 0x103a15ee, + 0x103a9605, + 0x103b1616, + 0x103b962a, + 0x103c1649, + 0x103c9658, + 0x103d166f, + 0x103d9682, 0x103e0b5d, - 0x103e96a4, - 0x103f16b7, - 0x103f96d1, - 0x104016e1, - 0x104096f5, - 0x1041170b, - 0x10419723, - 0x10421738, - 0x1042974c, - 0x1043175e, + 0x103e96b3, + 0x103f16c6, + 0x103f96e0, + 0x104016f0, + 0x10409704, + 0x1041171a, + 0x10419732, + 0x10421747, + 0x1042975b, + 0x1043176d, 0x104385c1, 0x104408b2, - 0x10449773, - 0x1045178a, - 0x1045979f, - 0x104617ad, - 0x10469686, - 0x104714e8, + 0x10449782, + 0x10451799, + 0x104597ae, + 0x104617bc, + 0x10469695, + 0x104714f7, 0x104787b8, 0x104800b0, - 0x104894b4, + 0x10488b8c, 0x14320b40, 0x14328b4e, 0x14330b5d, 0x14338b6f, 0x18320083, - 0x18328e38, - 0x18340e66, - 0x18348e7a, - 0x18358eb1, - 0x18368ede, - 0x18370ef1, - 0x18378f05, - 0x18380f29, - 0x18388f37, - 0x18390f4d, - 0x18398f61, - 0x183a0f71, - 0x183b0f81, - 0x183b8f96, - 0x183c8fc1, - 0x183d0fd5, - 0x183d8fe5, - 0x183e0b8c, - 0x183e8ff2, - 0x183f1004, - 0x183f900f, - 0x1840101f, - 0x18409030, - 0x18411041, - 0x18419053, - 0x1842107c, - 0x184290ae, - 0x184310bd, - 0x18451126, - 0x1845913c, - 0x18461157, - 0x18468ec9, + 0x18328e53, + 0x18340e81, + 0x18348e95, + 0x18358ecc, + 0x18368ef9, + 0x18370f0c, + 0x18378f20, + 0x18380f44, + 0x18388f52, + 0x18390f68, + 0x18398f7c, + 0x183a0f8c, + 0x183b0f9c, + 0x183b8fb1, + 0x183c8fdc, + 0x183d0ff0, + 0x183d9000, + 0x183e0b98, + 0x183e900d, + 0x183f101f, + 0x183f902a, + 0x1840103a, + 0x1840904b, + 0x1841105c, + 0x1841906e, + 0x18421097, + 0x184290c9, + 0x184310d8, + 0x18451141, + 0x18459157, + 0x18461172, + 0x18468ee4, 0x184709ca, 0x18478094, - 0x18480fad, - 0x184890f2, - 0x18490e4e, - 0x18498e8f, - 0x184a118d, - 0x184a910a, - 0x184b10d1, - 0x184b8e28, - 0x184c1095, + 0x18480fc8, + 0x1848910d, + 0x18490e69, + 0x18498eaa, + 0x184a11a8, + 0x184a9125, + 0x184b10ec, + 0x184b8e43, + 0x184c10b0, 0x184c865c, - 0x184d1172, - 0x203211b4, - 0x243211c0, + 0x184d118d, + 0x184d80b0, + 0x203211cf, + 0x243211db, 0x243288f8, - 0x243311d2, - 0x243391df, - 0x243411ec, - 0x243491fe, - 0x2435120d, - 0x2435922a, - 0x24361237, - 0x24369245, - 0x24371253, - 0x24379261, - 0x2438126a, - 0x24389277, - 0x2439128a, + 0x243311ed, + 0x243391fa, + 0x24341207, + 0x24349219, + 0x24351228, + 0x24359245, + 0x24361252, + 0x24369260, + 0x2437126e, + 0x2437927c, + 0x24381285, + 0x24389292, + 0x243912a5, 0x28320b80, - 0x28328b8c, + 0x28328b98, 0x28330b5d, - 0x28338b9f, - 0x2c32280e, - 0x2c32a81c, - 0x2c33282e, - 0x2c33a840, - 0x2c342854, - 0x2c34a866, - 0x2c352881, - 0x2c35a893, - 0x2c3628a6, + 0x28338bab, + 0x28340b8c, + 0x28348094, + 0x283500b0, + 0x2c32281d, + 0x2c32a82b, + 0x2c33283d, + 0x2c33a84f, + 0x2c342863, + 0x2c34a875, + 0x2c352890, + 0x2c35a8a2, + 0x2c3628b5, 0x2c3682f3, - 0x2c3728b3, - 0x2c37a8c5, - 0x2c3828d8, - 0x2c38a8e6, - 0x2c3928f6, - 0x2c39a908, - 0x2c3a291c, - 0x2c3aa92d, - 0x2c3b134a, - 0x2c3ba93e, - 0x2c3c2952, - 0x2c3ca968, - 0x2c3d2981, - 0x2c3da9af, - 0x2c3e29bd, - 0x2c3ea9d5, - 0x2c3f29ed, - 0x2c3fa9fa, - 0x2c402a1d, - 0x2c40aa3c, - 0x2c4111b4, - 0x2c41aa4d, - 0x2c422a60, - 0x2c429126, - 0x2c432a71, + 0x2c3728c2, + 0x2c37a8d4, + 0x2c3828e7, + 0x2c38a8f5, + 0x2c392905, + 0x2c39a917, + 0x2c3a292b, + 0x2c3aa93c, + 0x2c3b1365, + 0x2c3ba94d, + 0x2c3c2961, + 0x2c3ca977, + 0x2c3d2990, + 0x2c3da9be, + 0x2c3e29cc, + 0x2c3ea9e4, + 0x2c3f29fc, + 0x2c3faa09, + 0x2c402a2c, + 0x2c40aa4b, + 0x2c4111cf, + 0x2c41aa5c, + 0x2c422a6f, + 0x2c429141, + 0x2c432a80, 0x2c438693, - 0x2c44299e, + 0x2c4429ad, 0x30320000, 0x30328015, 0x3033001f, @@ -320,213 +324,216 @@ 0x34340b0d, 0x34348b2a, 0x3c320083, - 0x3c328bc9, - 0x3c330be2, - 0x3c338bfd, - 0x3c340c1a, - 0x3c348c35, - 0x3c350c50, - 0x3c358c65, - 0x3c360c7e, - 0x3c368c96, - 0x3c370ca7, - 0x3c378cb5, - 0x3c380cc2, - 0x3c388cd6, - 0x3c390b8c, - 0x3c398cea, - 0x3c3a0cfe, + 0x3c328bd5, + 0x3c330bee, + 0x3c338c09, + 0x3c340c26, + 0x3c348c50, + 0x3c350c6b, + 0x3c358c80, + 0x3c360c99, + 0x3c368cb1, + 0x3c370cc2, + 0x3c378cd0, + 0x3c380cdd, + 0x3c388cf1, + 0x3c390b98, + 0x3c398d05, + 0x3c3a0d19, 0x3c3a8872, - 0x3c3b0d0e, - 0x3c3b8d29, - 0x3c3c0d3b, - 0x3c3c8d51, - 0x3c3d0d5b, - 0x3c3d8d6f, - 0x3c3e0d7d, - 0x3c3e8da2, - 0x3c3f0bb5, - 0x3c3f8d8b, - 0x403217c4, - 0x403297da, - 0x40331808, - 0x40339812, - 0x40341829, - 0x40349847, - 0x40351857, - 0x40359869, - 0x40361876, - 0x40369882, - 0x40371897, - 0x403798a9, - 0x403818b4, - 0x403898c6, - 0x40390dd2, - 0x403998d6, - 0x403a18e9, - 0x403a990a, - 0x403b191b, - 0x403b992b, + 0x3c3b0d29, + 0x3c3b8d44, + 0x3c3c0d56, + 0x3c3c8d6c, + 0x3c3d0d76, + 0x3c3d8d8a, + 0x3c3e0d98, + 0x3c3e8dbd, + 0x3c3f0bc1, + 0x3c3f8da6, + 0x3c400094, + 0x3c4080b0, + 0x3c410c41, + 0x403217d3, + 0x403297e9, + 0x40331817, + 0x40339821, + 0x40341838, + 0x40349856, + 0x40351866, + 0x40359878, + 0x40361885, + 0x40369891, + 0x403718a6, + 0x403798b8, + 0x403818c3, + 0x403898d5, + 0x40390ded, + 0x403998e5, + 0x403a18f8, + 0x403a9919, + 0x403b192a, + 0x403b993a, 0x403c0064, 0x403c8083, - 0x403d1937, - 0x403d994d, - 0x403e195c, - 0x403e996f, - 0x403f1989, - 0x403f9997, - 0x404019ac, - 0x404099c0, - 0x404119dd, - 0x404199f8, - 0x40421a11, - 0x40429a24, - 0x40431a38, - 0x40439a50, - 0x40441a67, + 0x403d1946, + 0x403d995c, + 0x403e196b, + 0x403e997e, + 0x403f1998, + 0x403f99a6, + 0x404019bb, + 0x404099cf, + 0x404119ec, + 0x40419a07, + 0x40421a20, + 0x40429a33, + 0x40431a47, + 0x40439a5f, + 0x40441a76, 0x40448094, - 0x40451a7c, - 0x40459a8e, - 0x40461ab2, - 0x40469ad2, - 0x40471ae0, - 0x40479af4, - 0x40481b09, - 0x40489b22, - 0x40491b39, - 0x40499b53, - 0x404a1b6a, - 0x404a9b88, - 0x404b1ba0, - 0x404b9bb7, - 0x404c1bcd, - 0x404c9bdf, - 0x404d1c00, - 0x404d9c22, - 0x404e1c36, - 0x404e9c43, - 0x404f1c5a, - 0x404f9c6a, - 0x40501c7a, - 0x40509c8e, - 0x40511ca9, - 0x40519cb9, - 0x40521cd0, - 0x40529ce2, - 0x40531cfa, - 0x40539d0d, - 0x40541d22, - 0x40549d45, - 0x40551d53, - 0x40559d70, - 0x40561d7d, - 0x40569d96, - 0x40571dae, - 0x40579dc1, - 0x40581dd6, - 0x40589de8, - 0x40591df8, - 0x40599e11, - 0x405a1e25, - 0x405a9e35, - 0x405b1e4d, - 0x405b9e5e, - 0x405c1e71, - 0x405c9e82, - 0x405d1e8f, - 0x405d9ea6, - 0x405e1ec6, + 0x40451a8b, + 0x40459a9d, + 0x40461ac1, + 0x40469ae1, + 0x40471aef, + 0x40479b03, + 0x40481b18, + 0x40489b31, + 0x40491b48, + 0x40499b62, + 0x404a1b79, + 0x404a9b97, + 0x404b1baf, + 0x404b9bc6, + 0x404c1bdc, + 0x404c9bee, + 0x404d1c0f, + 0x404d9c31, + 0x404e1c45, + 0x404e9c52, + 0x404f1c69, + 0x404f9c79, + 0x40501c89, + 0x40509c9d, + 0x40511cb8, + 0x40519cc8, + 0x40521cdf, + 0x40529cf1, + 0x40531d09, + 0x40539d1c, + 0x40541d31, + 0x40549d54, + 0x40551d62, + 0x40559d7f, + 0x40561d8c, + 0x40569da5, + 0x40571dbd, + 0x40579dd0, + 0x40581de5, + 0x40589df7, + 0x40591e07, + 0x40599e20, + 0x405a1e34, + 0x405a9e44, + 0x405b1e5c, + 0x405b9e6d, + 0x405c1e80, + 0x405c9e91, + 0x405d1e9e, + 0x405d9eb5, + 0x405e1ed5, 0x405e8a08, - 0x405f1ee7, - 0x405f9ef4, - 0x40601f02, - 0x40609f24, - 0x40611f4c, - 0x40619f61, - 0x40621f78, - 0x40629f89, - 0x40631f9a, - 0x40639faf, - 0x40641fc6, - 0x40649fd7, - 0x40651ff2, - 0x4065a009, - 0x40662021, - 0x4066a04b, - 0x40672076, - 0x4067a097, - 0x406820aa, - 0x4068a0cb, - 0x406920e6, - 0x4069a114, - 0x406a2135, - 0x406aa155, - 0x406b22dd, - 0x406ba300, - 0x406c2316, - 0x406ca542, - 0x406d2571, - 0x406da599, - 0x406e25b2, - 0x406ea5ca, - 0x406f25e9, - 0x406fa5fe, - 0x40702611, - 0x4070a62e, + 0x405f1ef6, + 0x405f9f03, + 0x40601f11, + 0x40609f33, + 0x40611f5b, + 0x40619f70, + 0x40621f87, + 0x40629f98, + 0x40631fa9, + 0x40639fbe, + 0x40641fd5, + 0x40649fe6, + 0x40652001, + 0x4065a018, + 0x40662030, + 0x4066a05a, + 0x40672085, + 0x4067a0a6, + 0x406820b9, + 0x4068a0da, + 0x406920f5, + 0x4069a123, + 0x406a2144, + 0x406aa164, + 0x406b22ec, + 0x406ba30f, + 0x406c2325, + 0x406ca551, + 0x406d2580, + 0x406da5a8, + 0x406e25c1, + 0x406ea5d9, + 0x406f25f8, + 0x406fa60d, + 0x40702620, + 0x4070a63d, 0x40710773, - 0x4071a640, - 0x40722653, - 0x4072a66c, - 0x40732684, - 0x407390ae, - 0x40742698, - 0x4074a6b2, - 0x407526c3, - 0x4075a6d7, - 0x407626e5, - 0x40769277, - 0x4077270a, - 0x4077a72c, - 0x40782747, - 0x4078a75c, - 0x40792773, - 0x4079a789, - 0x407a2795, - 0x407aa7a8, - 0x407b27bd, - 0x407ba7cf, - 0x407c27e4, - 0x407ca7ed, - 0x41f42208, - 0x41f9229a, - 0x41fe218d, - 0x41fea369, - 0x41ff245a, - 0x42032221, - 0x42082243, - 0x4208a27f, - 0x42092171, - 0x4209a2b9, - 0x420a21c8, - 0x420aa1a8, - 0x420b21e8, - 0x420ba261, - 0x420c2476, - 0x420ca336, - 0x420d2350, - 0x420da387, - 0x421223a1, - 0x4217243d, - 0x4217a3e3, - 0x421c2405, - 0x421f23c0, - 0x4221248d, - 0x42262420, - 0x422b2526, - 0x422ba4ef, - 0x422c250e, - 0x422ca4c9, - 0x422d24a8, + 0x4071a64f, + 0x40722662, + 0x4072a67b, + 0x40732693, + 0x407390c9, + 0x407426a7, + 0x4074a6c1, + 0x407526d2, + 0x4075a6e6, + 0x407626f4, + 0x40769292, + 0x40772719, + 0x4077a73b, + 0x40782756, + 0x4078a76b, + 0x40792782, + 0x4079a798, + 0x407a27a4, + 0x407aa7b7, + 0x407b27cc, + 0x407ba7de, + 0x407c27f3, + 0x407ca7fc, + 0x41f42217, + 0x41f922a9, + 0x41fe219c, + 0x41fea378, + 0x41ff2469, + 0x42032230, + 0x42082252, + 0x4208a28e, + 0x42092180, + 0x4209a2c8, + 0x420a21d7, + 0x420aa1b7, + 0x420b21f7, + 0x420ba270, + 0x420c2485, + 0x420ca345, + 0x420d235f, + 0x420da396, + 0x421223b0, + 0x4217244c, + 0x4217a3f2, + 0x421c2414, + 0x421f23cf, + 0x4221249c, + 0x4226242f, + 0x422b2535, + 0x422ba4fe, + 0x422c251d, + 0x422ca4d8, + 0x422d24b7, 0x4432069e, 0x443286ad, 0x443306b9, @@ -544,104 +551,104 @@ 0x44390773, 0x44398781, 0x443a0794, - 0x4c3212a1, - 0x4c3292b1, - 0x4c3312c4, - 0x4c3392e4, + 0x4c3212bc, + 0x4c3292cc, + 0x4c3312df, + 0x4c3392ff, 0x4c340094, 0x4c3480b0, - 0x4c3512f0, - 0x4c3592fe, - 0x4c36131a, - 0x4c36932d, - 0x4c37133c, - 0x4c37934a, - 0x4c38135f, - 0x4c38936b, - 0x4c39138b, - 0x4c3993b5, - 0x4c3a13ce, - 0x4c3a93e7, + 0x4c35130b, + 0x4c359319, + 0x4c361335, + 0x4c369348, + 0x4c371357, + 0x4c379365, + 0x4c38137a, + 0x4c389386, + 0x4c3913a6, + 0x4c3993d0, + 0x4c3a13e9, + 0x4c3a9402, 0x4c3b05c1, - 0x4c3b9400, - 0x4c3c1412, - 0x4c3c9421, - 0x4c3d10ae, - 0x4c3d943a, - 0x4c3e1447, - 0x50322a83, - 0x5032aa92, - 0x50332a9d, - 0x5033aaad, - 0x50342ac6, - 0x5034aae0, - 0x50352aee, - 0x5035ab04, - 0x50362b16, - 0x5036ab2c, - 0x50372b45, - 0x5037ab58, - 0x50382b70, - 0x5038ab81, - 0x50392b96, - 0x5039abaa, - 0x503a2bca, - 0x503aabe0, - 0x503b2bf8, - 0x503bac0a, - 0x503c2c26, - 0x503cac3d, - 0x503d2c56, - 0x503dac6c, - 0x503e2c79, - 0x503eac8f, - 0x503f2ca1, + 0x4c3b941b, + 0x4c3c142d, + 0x4c3c943c, + 0x4c3d10c9, + 0x4c3d9455, + 0x4c3e1462, + 0x50322a92, + 0x5032aaa1, + 0x50332aac, + 0x5033aabc, + 0x50342ad5, + 0x5034aaef, + 0x50352afd, + 0x5035ab13, + 0x50362b25, + 0x5036ab3b, + 0x50372b54, + 0x5037ab67, + 0x50382b7f, + 0x5038ab90, + 0x50392ba5, + 0x5039abb9, + 0x503a2bd9, + 0x503aabef, + 0x503b2c07, + 0x503bac19, + 0x503c2c35, + 0x503cac4c, + 0x503d2c65, + 0x503dac7b, + 0x503e2c88, + 0x503eac9e, + 0x503f2cb0, 0x503f8348, - 0x50402cb4, - 0x5040acc4, - 0x50412cde, - 0x5041aced, - 0x50422d07, - 0x5042ad24, - 0x50432d34, - 0x5043ad44, - 0x50442d53, + 0x50402cc3, + 0x5040acd3, + 0x50412ced, + 0x5041acfc, + 0x50422d16, + 0x5042ad33, + 0x50432d43, + 0x5043ad53, + 0x50442d62, 0x50448405, - 0x50452d67, - 0x5045ad85, - 0x50462d98, - 0x5046adae, - 0x50472dc0, - 0x5047add5, - 0x50482dfb, - 0x5048ae09, - 0x50492e1c, - 0x5049ae31, - 0x504a2e47, - 0x504aae57, - 0x504b2e77, - 0x504bae8a, - 0x504c2ead, - 0x504caedb, - 0x504d2eed, - 0x504daf0a, - 0x504e2f25, - 0x504eaf41, - 0x504f2f53, - 0x504faf6a, - 0x50502f79, + 0x50452d76, + 0x5045ad94, + 0x50462da7, + 0x5046adbd, + 0x50472dcf, + 0x5047ade4, + 0x50482e0a, + 0x5048ae18, + 0x50492e2b, + 0x5049ae40, + 0x504a2e56, + 0x504aae66, + 0x504b2e86, + 0x504bae99, + 0x504c2ebc, + 0x504caeea, + 0x504d2efc, + 0x504daf19, + 0x504e2f34, + 0x504eaf50, + 0x504f2f62, + 0x504faf79, + 0x50502f88, 0x50508678, - 0x50512f8c, - 0x58320e10, - 0x68320dd2, - 0x68328b8c, - 0x68330b9f, - 0x68338de0, - 0x68340df0, + 0x50512f9b, + 0x58320e2b, + 0x68320ded, + 0x68328b98, + 0x68330bab, + 0x68338dfb, + 0x68340e0b, 0x683480b0, - 0x6c320dae, + 0x6c320dc9, 0x6c328b6f, - 0x6c330db9, + 0x6c330dd4, 0x7432097e, 0x783208e3, 0x783288f8, @@ -668,7 +675,7 @@ 0x783d0a8c, 0x783d8aa1, 0x783e09f7, - 0x7c3211a3, + 0x7c3211be, }; const size_t kOpenSSLReasonValuesLen = sizeof(kOpenSSLReasonValues) / sizeof(kOpenSSLReasonValues[0]); @@ -831,6 +838,7 @@ "MODULUS_TOO_LARGE\0" "NO_PRIVATE_VALUE\0" "BAD_Q_VALUE\0" + "BAD_VERSION\0" "MISSING_PARAMETERS\0" "NEED_NEW_SETUP_VALUES\0" "BIGNUM_OUT_OF_RANGE\0" @@ -838,6 +846,7 @@ "D2I_ECPKPARAMETERS_FAILURE\0" "EC_GROUP_NEW_BY_NAME_FAILURE\0" "GROUP2PKPARAMETERS_FAILURE\0" + "GROUP_MISMATCH\0" "I2D_ECPKPARAMETERS_FAILURE\0" "INCOMPATIBLE_OBJECTS\0" "INVALID_COMPRESSED_POINT\0" @@ -946,7 +955,6 @@ "BAD_FIXED_HEADER_DECRYPT\0" "BAD_PAD_BYTE_COUNT\0" "BAD_RSA_PARAMETERS\0" - "BAD_VERSION\0" "BLOCK_TYPE_IS_NOT_01\0" "BN_NOT_INITIALIZED\0" "CANNOT_RECOVER_MULTI_PRIME_KEY\0"
diff --git a/third_party/brotli/BUILD.gn b/third_party/brotli/BUILD.gn index 00b1daf..e25cf2b 100644 --- a/third_party/brotli/BUILD.gn +++ b/third_party/brotli/BUILD.gn
@@ -17,8 +17,6 @@ "dec/prefix.h", "dec/state.c", "dec/state.h", - "dec/streams.c", - "dec/streams.h", "dec/transform.h", "dec/types.h", ]
diff --git a/third_party/brotli/README.chromium b/third_party/brotli/README.chromium index 8908727..63f8a24 100644 --- a/third_party/brotli/README.chromium +++ b/third_party/brotli/README.chromium
@@ -1,6 +1,6 @@ Name: Brotli URL: https://github.com/google/brotli -Version: 24ffa78414663b545b66be392caff7eb5574a62c +Version: 722f8996b05bdec7c375462f9139e72ed33e3c85 License: MIT License File: LICENSE Security Critical: yes @@ -16,4 +16,3 @@ - .gitignore: Added. - BUILD.gn: Added. - brotli.gyp: Added. -- Cherry-picked 37a320dd81db8d546cd24a45b4c61d87b45dcade (04 Feb 2016) \ No newline at end of file
diff --git a/third_party/brotli/brotli.gyp b/third_party/brotli/brotli.gyp index f7b3aa1..08fa2d6 100644 --- a/third_party/brotli/brotli.gyp +++ b/third_party/brotli/brotli.gyp
@@ -24,8 +24,6 @@ 'dec/prefix.h', 'dec/state.c', 'dec/state.h', - 'dec/streams.c', - 'dec/streams.h', 'dec/transform.h', 'dec/types.h', ],
diff --git a/third_party/brotli/dec/Makefile b/third_party/brotli/dec/Makefile index fa2fc3d..4d11ed1 100644 --- a/third_party/brotli/dec/Makefile +++ b/third_party/brotli/dec/Makefile
@@ -4,7 +4,7 @@ CFLAGS += -Wall -OBJS = bit_reader.o decode.o dictionary.o huffman.o state.o streams.o +OBJS = bit_reader.o decode.o dictionary.o huffman.o state.o all : $(OBJS)
diff --git a/third_party/brotli/dec/bit_reader.c b/third_party/brotli/dec/bit_reader.c index fc814d0..cde90af 100644 --- a/third_party/brotli/dec/bit_reader.c +++ b/third_party/brotli/dec/bit_reader.c
@@ -6,10 +6,10 @@ /* Bit reading helpers */ -#include <stdlib.h> - #include "./bit_reader.h" + #include "./port.h" +#include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -44,5 +44,5 @@ } #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif
diff --git a/third_party/brotli/dec/bit_reader.h b/third_party/brotli/dec/bit_reader.h index 0301a48..e77d114 100644 --- a/third_party/brotli/dec/bit_reader.h +++ b/third_party/brotli/dec/bit_reader.h
@@ -9,11 +9,15 @@ #ifndef BROTLI_DEC_BIT_READER_H_ #define BROTLI_DEC_BIT_READER_H_ -#include <stdio.h> -#include <string.h> +#include <string.h> /* memcpy */ + #include "./port.h" #include "./types.h" +#ifdef BROTLI_DECODE_DEBUG +#include <stdio.h> +#endif + #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif @@ -48,17 +52,17 @@ } typedef struct { - reg_t val_; /* pre-fetched bits */ - uint32_t bit_pos_; /* current bit-reading position in val_ */ - const uint8_t* next_in; /* the byte we're reading from */ - size_t avail_in; + reg_t val_; /* pre-fetched bits */ + uint32_t bit_pos_; /* current bit-reading position in val_ */ + const uint8_t* next_in; /* the byte we're reading from */ + size_t avail_in; } BrotliBitReader; typedef struct { - reg_t val_; + reg_t val_; uint32_t bit_pos_; const uint8_t* next_in; - size_t avail_in; + size_t avail_in; } BrotliBitReaderState; /* Initializes the bitreader fields. */ @@ -66,7 +70,7 @@ /* Ensures that accumulator is not empty. May consume one byte of input. Returns 0 if data is required but there is no input available. - For BROTLI_BUILD_PORTABLE this function also prepares bit reader for aligned + For BROTLI_ALIGNED_READ this function also prepares bit reader for aligned reading. */ int BrotliWarmupBitReader(BrotliBitReader* const br); @@ -109,9 +113,7 @@ return *((const uint16_t*)in); } else if (BROTLI_BIG_ENDIAN) { uint16_t value = *((const uint16_t*)in); - return (uint16_t)( - ((value & 0xFFU) << 8) | - ((value & 0xFF00U) >> 8)); + return (uint16_t)(((value & 0xFFU) << 8) | ((value & 0xFF00U) >> 8)); } else { return (uint16_t)(in[0] | (in[1] << 8)); } @@ -228,9 +230,9 @@ } br->val_ >>= 8; #if (BROTLI_64_BITS) - br->val_ |= ((uint64_t)*br->next_in) << 56; + br->val_ |= ((uint64_t)*br->next_in) << 56; #else - br->val_ |= ((uint32_t)*br->next_in) << 24; + br->val_ |= ((uint32_t)*br->next_in) << 24; #endif br->bit_pos_ -= 8; --br->avail_in; @@ -262,7 +264,7 @@ /* Tries to peek the specified amount of bits. Returns 0, if there is not enough input. */ static BROTLI_INLINE int BrotliSafeGetBits( - BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) { + BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) { while (BrotliGetAvailableBits(br) < n_bits) { if (!BrotliPullByte(br)) { return 0; @@ -283,7 +285,11 @@ uint32_t unused_bits = unused_bytes << 3; br->avail_in += unused_bytes; br->next_in -= unused_bytes; - br->val_ <<= unused_bits; + if (unused_bits == sizeof(br->val_) << 3) { + br->val_ = 0; + } else { + br->val_ <<= unused_bits; + } br->bit_pos_ += unused_bits; } @@ -322,7 +328,7 @@ /* Tries to read the specified amount of bits. Returns 0, if there is not enough input. n_bits MUST be positive. */ static BROTLI_INLINE int BrotliSafeReadBits( - BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) { + BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) { while (BrotliGetAvailableBits(br) < n_bits) { if (!BrotliPullByte(br)) { return 0; @@ -349,9 +355,7 @@ static BROTLI_INLINE int BrotliPeekByte(BrotliBitReader* br, size_t offset) { uint32_t available_bits = BrotliGetAvailableBits(br); size_t bytes_left = available_bits >> 3; - if (available_bits & 7) { - return -1; - } + BROTLI_DCHECK((available_bits & 7) == 0); if (offset < bytes_left) { return (BrotliGetBitsUnmasked(br) >> (unsigned)(offset << 3)) & 0xFF; } @@ -379,7 +383,7 @@ } #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif #endif /* BROTLI_DEC_BIT_READER_H_ */
diff --git a/third_party/brotli/dec/context.h b/third_party/brotli/dec/context.h index b71f9a3..37ebe6a 100644 --- a/third_party/brotli/dec/context.h +++ b/third_party/brotli/dec/context.h
@@ -102,10 +102,10 @@ #include "./types.h" enum ContextType { - CONTEXT_LSB6 = 0, - CONTEXT_MSB6 = 1, - CONTEXT_UTF8 = 2, - CONTEXT_SIGNED = 3 + CONTEXT_LSB6 = 0, + CONTEXT_MSB6 = 1, + CONTEXT_UTF8 = 2, + CONTEXT_SIGNED = 3 }; /* Common context lookup table for all context modes. */
diff --git a/third_party/brotli/dec/decode.c b/third_party/brotli/dec/decode.c index 401dd15..a81706b0 100644 --- a/third_party/brotli/dec/decode.c +++ b/third_party/brotli/dec/decode.c
@@ -4,31 +4,47 @@ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include "./bit_reader.h" -#include "./context.h" #include "./decode.h" -#include "./dictionary.h" -#include "./port.h" -#include "./transform.h" -#include "./huffman.h" -#include "./prefix.h" #ifdef __ARM_NEON__ #include <arm_neon.h> #endif +#include <stdio.h> /* printf (debug output) */ +#include <stdlib.h> /* free, malloc */ +#include <string.h> /* memcpy, memset */ + +#include "./bit_reader.h" +#include "./context.h" +#include "./dictionary.h" +#include "./huffman.h" +#include "./port.h" +#include "./prefix.h" +#include "./transform.h" + #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif +/* BROTLI_FAILURE macro unwraps to BROTLI_RESULT_ERROR in non-debug build. */ +/* In debug build it dumps file name, line and pretty function name. */ +#if defined(_MSC_VER) || \ + (!defined(BROTLI_DEBUG) && !defined(BROTLI_DECODE_DEBUG)) +#define BROTLI_FAILURE() BROTLI_RESULT_ERROR +#else +#define BROTLI_FAILURE() BrotliFailure(__FILE__, __LINE__, __PRETTY_FUNCTION__) +static inline BrotliResult BrotliFailure(const char* f, int l, const char* fn) { + fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn); + fflush(stderr); + return BROTLI_RESULT_ERROR; +} +#endif + #ifdef BROTLI_DECODE_DEBUG -#define BROTLI_LOG_UINT(name) \ +#define BROTLI_LOG_UINT(name) \ printf("[%s] %s = %lu\n", __func__, #name, (unsigned long)(name)) -#define BROTLI_LOG_ARRAY_INDEX(array_name, idx) \ - printf("[%s] %s[%lu] = %lu\n", __func__, #array_name, \ +#define BROTLI_LOG_ARRAY_INDEX(array_name, idx) \ + printf("[%s] %s[%lu] = %lu\n", __func__, #array_name, \ (unsigned long)(idx), (unsigned long)array_name[idx]) #define BROTLI_LOG(x) printf x #else @@ -45,8 +61,8 @@ static const int kLiteralContextBits = 6; static const int kDistanceContextBits = 2; -#define HUFFMAN_TABLE_BITS 8U -#define HUFFMAN_TABLE_MASK 0xff +#define HUFFMAN_TABLE_BITS 8U +#define HUFFMAN_TABLE_MASK 0xff #define CODE_LENGTH_CODES 18 static const uint8_t kCodeLengthCodeOrder[CODE_LENGTH_CODES] = { @@ -111,24 +127,13 @@ return 17; } -static BROTLI_INLINE BROTLI_NO_ASAN void memmove16( - uint8_t* dst, uint8_t* src) { -#if BROTLI_SAFE_MEMMOVE - /* For x86 this compiles to the same binary as signle memcpy. - On ARM memcpy is not inlined, so it works slower. - This implementation makes decompression 1% slower than regular one, - and 2% slower than NEON implementation. - */ +static BROTLI_INLINE void memmove16(uint8_t* dst, uint8_t* src) { +#if defined(__ARM_NEON__) + vst1q_u8(dst, vld1q_u8(src)); +#else uint32_t buffer[4]; memcpy(buffer, src, 16); memcpy(dst, buffer, 16); -#elif defined(__ARM_NEON__) - vst1q_u8(dst, vld1q_u8(src)); -#else - /* memcpy is unsafe for overlapping regions and ASAN detects this. - But, because of optimizations, it works exactly as memmove: - copies data to registers first, and then stores them to dst. */ - memcpy(dst, src, 16); #endif } @@ -239,7 +244,7 @@ /* No break, transit to the next state. */ case BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED: - if (!s->is_last_metablock && !s->is_metadata) { + if (!s->is_last_metablock) { if (!BrotliSafeReadBits(br, 1, &bits)) { return BROTLI_RESULT_NEEDS_MORE_INPUT; } @@ -283,9 +288,9 @@ } s->meta_block_remaining_len |= (int)(bits << (i * 8)); } - s->substate_metablock_header = - BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED; - break; + ++s->meta_block_remaining_len; + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; + return BROTLI_RESULT_SUCCESS; default: return BROTLI_FAILURE(); @@ -473,6 +478,7 @@ *prev_code_len = code_len; *space -= 32768U >> code_len; code_length_histo[code_len]++; + BROTLI_LOG(("[ReadHuffmanCode] code_length[%d] = %d\n", *symbol, code_len)); } (*symbol)++; } @@ -513,6 +519,8 @@ *space = 0xFFFFF; return; } + BROTLI_LOG(("[ReadHuffmanCode] code_length[%d..%d] = %d\n", + *symbol, *symbol + repeat_delta - 1, *repeat_code_len)); if (*repeat_code_len != 0) { unsigned last = *symbol + repeat_delta; int next = next_symbol[*repeat_code_len]; @@ -522,8 +530,8 @@ } while (++(*symbol) != last); next_symbol[*repeat_code_len] = next; *space -= repeat_delta << (15 - *repeat_code_len); - code_length_histo[*repeat_code_len] = (uint16_t) - (code_length_histo[*repeat_code_len] + repeat_delta); + code_length_histo[*repeat_code_len] = + (uint16_t)(code_length_histo[*repeat_code_len] + repeat_delta); } else { *symbol += repeat_delta; } @@ -558,8 +566,8 @@ BrotliFillBitWindow16(br); p += BrotliGetBitsUnmasked(br) & BitMask(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH); - BrotliDropBits(br, p->bits); /* Use 1..5 bits */ - code_len = p->value; /* code_len == 0..17 */ + BrotliDropBits(br, p->bits); /* Use 1..5 bits */ + code_len = p->value; /* code_len == 0..17 */ if (code_len < kCodeLengthRepeatCode) { ProcessSingleCodeLength(code_len, &symbol, &repeat, &space, &prev_code_len, symbol_lists, code_length_histo, next_symbol); @@ -775,8 +783,8 @@ BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", s->space)); return BROTLI_FAILURE(); } - table_size = BrotliBuildHuffmanTable(table, HUFFMAN_TABLE_BITS, - s->symbol_lists, s->code_length_histo); + table_size = BrotliBuildHuffmanTable( + table, HUFFMAN_TABLE_BITS, s->symbol_lists, s->code_length_histo); if (opt_table_size) { *opt_table_size = table_size; } @@ -916,7 +924,7 @@ BrotliBitReader* br = &s->br; BrotliResult result = BROTLI_RESULT_SUCCESS; - switch((int)s->substate_context_map) { + switch ((int)s->substate_context_map) { case BROTLI_STATE_CONTEXT_MAP_NONE: result = DecodeVarLenUint8(s, br, num_htrees); if (result != BROTLI_RESULT_SUCCESS) { @@ -1018,9 +1026,9 @@ s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE; return BROTLI_RESULT_SUCCESS; } + default: + return BROTLI_FAILURE(); } - - return BROTLI_FAILURE(); } /* Decodes a command or literal and updates block type ringbuffer. @@ -1028,9 +1036,10 @@ static BROTLI_INLINE int DecodeBlockTypeAndLength(int safe, BrotliState* s, int tree_type) { uint32_t max_block_type = s->num_block_types[tree_type]; - int tree_offset = tree_type * BROTLI_HUFFMAN_MAX_TABLE_SIZE; - const HuffmanCode* type_tree = &s->block_type_trees[tree_offset]; - const HuffmanCode* len_tree = &s->block_len_trees[tree_offset]; + const HuffmanCode* type_tree = &s->block_type_trees[ + tree_type * BROTLI_HUFFMAN_MAX_SIZE_258]; + const HuffmanCode* len_tree = &s->block_len_trees[ + tree_type * BROTLI_HUFFMAN_MAX_SIZE_26]; BrotliBitReader* br = &s->br; uint32_t* ringbuffer = &s->block_type_rb[tree_type * 2]; uint32_t block_type; @@ -1133,12 +1142,11 @@ static BrotliResult WriteRingBuffer(size_t* available_out, uint8_t** next_out, size_t* total_out, BrotliState* s) { - size_t pos = (s->pos > s->ringbuffer_size) ? - (size_t)s->ringbuffer_size : (size_t)(s->pos); - uint8_t* start = s->ringbuffer - + (s->partial_pos_out & (size_t)s->ringbuffer_mask); - size_t partial_pos_rb = - (s->rb_roundtrips * (size_t)s->ringbuffer_size) + pos; + size_t pos = (s->pos > s->ringbuffer_size) ? (size_t)s->ringbuffer_size + : (size_t)(s->pos); + uint8_t* start = + s->ringbuffer + (s->partial_pos_out & (size_t)s->ringbuffer_mask); + size_t partial_pos_rb = (s->rb_roundtrips * (size_t)s->ringbuffer_size) + pos; size_t to_write = (partial_pos_rb - s->partial_pos_out); size_t num_written = *available_out; if (num_written > to_write) { @@ -1152,7 +1160,7 @@ *available_out -= num_written; BROTLI_LOG_UINT(to_write); BROTLI_LOG_UINT(num_written); - s->partial_pos_out += (size_t)num_written; + s->partial_pos_out += num_written; *total_out = s->partial_pos_out; if (num_written < to_write) { return BROTLI_RESULT_NEEDS_MORE_OUTPUT; @@ -1160,9 +1168,48 @@ return BROTLI_RESULT_SUCCESS; } +/* Allocates ringbuffer. + + s->ringbuffer_size MUST be updated by BrotliCalculateRingBufferSize before + this function is called. + + Last two bytes of ringbuffer are initialized to 0, so context calculation + could be done uniformly for the first two and all other positions. + + Custom dictionary, if any, is copied to the end of ringbuffer. +*/ +static int BROTLI_NOINLINE BrotliAllocateRingBuffer(BrotliState* s) { + /* We need the slack region for the following reasons: + - doing up to two 16-byte copies for fast backward copying + - inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */ + static const int kRingBufferWriteAheadSlack = 42; + s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->ringbuffer_size + + kRingBufferWriteAheadSlack)); + if (s->ringbuffer == 0) { + return 0; + } + + s->ringbuffer_end = s->ringbuffer + s->ringbuffer_size; + + s->ringbuffer[s->ringbuffer_size - 2] = 0; + s->ringbuffer[s->ringbuffer_size - 1] = 0; + + if (s->custom_dict) { + memcpy(&s->ringbuffer[(-s->custom_dict_size) & s->ringbuffer_mask], + s->custom_dict, (size_t)s->custom_dict_size); + } + + return 1; +} + static BrotliResult BROTLI_NOINLINE CopyUncompressedBlockToOutput( size_t* available_out, uint8_t** next_out, size_t* total_out, BrotliState* s) { + /* TODO: avoid allocation for single uncompressed block. */ + if (!s->ringbuffer && !BrotliAllocateRingBuffer(s)) { + return BROTLI_FAILURE(); + } + /* State machine */ for (;;) { switch (s->substate_uncompressed) { @@ -1185,12 +1232,11 @@ return BROTLI_RESULT_NEEDS_MORE_INPUT; } s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE; - /*s->partial_pos_rb += (size_t)s->ringbuffer_size;*/ /* No break, continue to next state */ } case BROTLI_STATE_UNCOMPRESSED_WRITE: { - BrotliResult result = WriteRingBuffer( - available_out, next_out, total_out, s); + BrotliResult result = + WriteRingBuffer(available_out, next_out, total_out, s); if (result != BROTLI_RESULT_SUCCESS) { return result; } @@ -1231,29 +1277,23 @@ return (next_block_header != -1) && ((next_block_header & 3) == 3); } -/* Allocates the smallest feasible ring buffer. +/* Calculates the smallest feasible ring buffer. If we know the data size is small, do not allocate more ringbuffer size than needed to reduce memory usage. - This method is called before the first non-empty non-metadata block is - processed. When this method is called, metablock size and flags MUST be - decoded. + When this method is called, metablock size and flags MUST be decoded. */ -static int BROTLI_NOINLINE BrotliAllocateRingBuffer(BrotliState* s, +static void BROTLI_NOINLINE BrotliCalculateRingBufferSize(BrotliState* s, BrotliBitReader* br) { - /* We need the slack region for the following reasons: - - doing up to two 16-byte copies for fast backward copying - - inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */ - static const int kRingBufferWriteAheadSlack = 42; int is_last = s->is_last_metablock; s->ringbuffer_size = 1 << s->window_bits; if (s->is_uncompressed) { - int next_block_header = BrotliPeekByte(br, - (size_t)s->meta_block_remaining_len); - if (next_block_header != -1) { /* Peek succeeded */ - if ((next_block_header & 3) == 3) { /* ISLAST and ISEMPTY */ + int next_block_header = + BrotliPeekByte(br, (size_t)s->meta_block_remaining_len); + if (next_block_header != -1) { /* Peek succeeded */ + if ((next_block_header & 3) == 3) { /* ISLAST and ISEMPTY */ is_last = 1; } } @@ -1262,8 +1302,8 @@ /* We need at least 2 bytes of ring buffer size to get the last two bytes for context from there */ if (is_last) { - while (s->ringbuffer_size >= s->meta_block_remaining_len * 2 - && s->ringbuffer_size > 32) { + while (s->ringbuffer_size >= s->meta_block_remaining_len * 2 && + s->ringbuffer_size > 32) { s->ringbuffer_size >>= 1; } } @@ -1274,20 +1314,6 @@ } s->ringbuffer_mask = s->ringbuffer_size - 1; - s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->ringbuffer_size + - kRingBufferWriteAheadSlack + kBrotliMaxDictionaryWordLength)); - if (s->ringbuffer == 0) { - return 0; - } - s->ringbuffer_end = s->ringbuffer + s->ringbuffer_size; - s->ringbuffer[s->ringbuffer_size - 2] = 0; - s->ringbuffer[s->ringbuffer_size - 1] = 0; - if (s->custom_dict) { - memcpy(&s->ringbuffer[(-s->custom_dict_size) & s->ringbuffer_mask], - s->custom_dict, (size_t)s->custom_dict_size); - } - - return 1; } /* Reads 1..256 2-bit context modes. */ @@ -1378,8 +1404,8 @@ if (!safe && (s->distance_postfix_bits == 0)) { nbits = ((uint32_t)distval >> 1) + 1; offset = ((2 + (distval & 1)) << nbits) - 4; - s->distance_code = (int)s->num_direct_distance_codes + - offset + (int)BrotliReadBits(br, nbits); + s->distance_code = (int)s->num_direct_distance_codes + offset + + (int)BrotliReadBits(br, nbits); } else { /* This branch also works well when s->distance_postfix_bits == 0 */ uint32_t bits; @@ -1461,13 +1487,6 @@ return ReadCommandInternal(1, s, br, insert_length); } -static BROTLI_INLINE int WarmupBitReader(int safe, BrotliBitReader* const br) { - if (safe) { - return 1; - } - return BrotliWarmupBitReader(br); -} - static BROTLI_INLINE int CheckInputAmount(int safe, BrotliBitReader* const br, size_t num) { if (safe) { @@ -1476,16 +1495,17 @@ return BrotliCheckInputAmount(br, num); } -#define BROTLI_SAFE(METHOD) { \ - if (safe) { \ - if (! Safe ## METHOD ) { \ - result = BROTLI_RESULT_NEEDS_MORE_INPUT; \ - goto saveStateAndReturn; \ - } \ - } else { \ - METHOD ; \ - } \ -} +#define BROTLI_SAFE(METHOD) \ + { \ + if (safe) { \ + if (!Safe##METHOD) { \ + result = BROTLI_RESULT_NEEDS_MORE_INPUT; \ + goto saveStateAndReturn; \ + } \ + } else { \ + METHOD; \ + } \ + } static BROTLI_INLINE BrotliResult ProcessCommandsInternal(int safe, BrotliState* s) { @@ -1494,10 +1514,13 @@ BrotliResult result = BROTLI_RESULT_SUCCESS; BrotliBitReader* br = &s->br; - if (!CheckInputAmount(safe, br, 28) || !WarmupBitReader(safe, br)) { + if (!CheckInputAmount(safe, br, 28)) { result = BROTLI_RESULT_NEEDS_MORE_INPUT; goto saveStateAndReturn; } + if (!safe) { + BROTLI_UNUSED(BrotliWarmupBitReader(br)); + } /* Jump into state machine. */ if (s->state == BROTLI_STATE_COMMAND_BEGIN) { @@ -1527,9 +1550,8 @@ } /* Read the insert/copy length in the command */ BROTLI_SAFE(ReadCommand(s, br, &i)); - BROTLI_LOG_UINT(i); - BROTLI_LOG_UINT(s->copy_length); - BROTLI_LOG_UINT(s->distance_code); + BROTLI_LOG(("[ProcessCommandsInternal] pos = %d insert = %d copy = %d\n", + pos, i, s->copy_length)); if (i == 0) { goto CommandPostDecodeLiterals; } @@ -1555,8 +1577,8 @@ PreloadSymbol(safe, s->literal_htree, br, &bits, &value); } if (!safe) { - s->ringbuffer[pos] = (uint8_t)ReadPreloadedSymbol( - s->literal_htree, br, &bits, &value); + s->ringbuffer[pos] = + (uint8_t)ReadPreloadedSymbol(s->literal_htree, br, &bits, &value); } else { uint32_t literal; if (!SafeReadSymbol(s->literal_htree, br, &literal)) { @@ -1615,6 +1637,7 @@ } } while (--i != 0); } + BROTLI_LOG_UINT(s->meta_block_remaining_len); if (s->meta_block_remaining_len <= 0) { s->state = BROTLI_STATE_METABLOCK_DONE; goto saveStateAndReturn; @@ -1635,7 +1658,8 @@ } BROTLI_SAFE(ReadDistance(s, br)); postReadDistance: - BROTLI_LOG_UINT(s->distance_code); + BROTLI_LOG(("[ProcessCommandsInternal] pos = %d distance = %d\n", + pos, s->distance_code)); if (s->max_distance != s->max_backward_distance) { if (pos < s->max_backward_distance_minus_custom_dict_size) { s->max_distance = pos + s->custom_dict_size; @@ -1649,7 +1673,7 @@ if (s->distance_code > s->max_distance) { if (i >= kBrotliMinDictionaryWordLength && i <= kBrotliMaxDictionaryWordLength) { - int offset = kBrotliDictionaryOffsetsByLength[i]; + int offset = (int)kBrotliDictionaryOffsetsByLength[i]; int word_id = s->distance_code - s->max_distance - 1; uint32_t shift = kBrotliDictionarySizeBitsByLength[i]; int mask = (int)BitMask(shift); @@ -1674,58 +1698,44 @@ } } else { BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " - "len: %d bytes left: %d\n", - pos, s->distance_code, i, - s->meta_block_remaining_len)); + "len: %d bytes left: %d\n", + pos, s->distance_code, i, s->meta_block_remaining_len)); return BROTLI_FAILURE(); } } else { BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " - "len: %d bytes left: %d\n", pos, s->distance_code, i, - s->meta_block_remaining_len)); + "len: %d bytes left: %d\n", + pos, s->distance_code, i, s->meta_block_remaining_len)); return BROTLI_FAILURE(); } } else { - const uint8_t *ringbuffer_end_minus_copy_length = - s->ringbuffer_end - i; - uint8_t* copy_src = &s->ringbuffer[ - (pos - s->distance_code) & s->ringbuffer_mask]; + int src_start = (pos - s->distance_code) & s->ringbuffer_mask; uint8_t* copy_dst = &s->ringbuffer[pos]; - /* Check for possible underflow and clamp the pointer to 0. */ - if (PREDICT_FALSE(s->ringbuffer_end < (const uint8_t*)0 + i)) { - ringbuffer_end_minus_copy_length = 0; - } + uint8_t* copy_src = &s->ringbuffer[src_start]; + int dst_end = pos + i; + int src_end = src_start + i; /* update the recent distances cache */ s->dist_rb[s->dist_rb_idx & 3] = s->distance_code; ++s->dist_rb_idx; s->meta_block_remaining_len -= i; if (PREDICT_FALSE(s->meta_block_remaining_len < 0)) { BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d " - "len: %d bytes left: %d\n", pos, s->distance_code, i, - s->meta_block_remaining_len)); + "len: %d bytes left: %d\n", + pos, s->distance_code, i, s->meta_block_remaining_len)); return BROTLI_FAILURE(); } - /* There is 128+ bytes of slack in the ringbuffer allocation. + /* There are 32+ bytes of slack in the ringbuffer allocation. Also, we have 16 short codes, that make these 16 bytes irrelevant in the ringbuffer. Let's copy over them as a first guess. */ memmove16(copy_dst, copy_src); - /* Now check if the copy extends over the ringbuffer end, - or if the copy overlaps with itself, if yes, do wrap-copy. */ - if (copy_src < copy_dst) { - if (copy_dst >= ringbuffer_end_minus_copy_length) { - goto CommandPostWrapCopy; - } - if (copy_src + i > copy_dst) { - goto postSelfintersecting; - } - } else { - if (copy_src >= ringbuffer_end_minus_copy_length) { - goto CommandPostWrapCopy; - } - if (copy_dst + i > copy_src) { - goto postSelfintersecting; - } + if (src_end > pos && dst_end > src_start) { + /* Regions intersect. */ + goto CommandPostWrapCopy; + } + if (dst_end >= s->ringbuffer_size || src_end >= s->ringbuffer_size) { + /* At least one region wraps. */ + goto CommandPostWrapCopy; } pos += i; if (i > 16) { @@ -1738,6 +1748,7 @@ } } } + BROTLI_LOG_UINT(s->meta_block_remaining_len); if (s->meta_block_remaining_len <= 0) { /* Next metablock, if any */ s->state = BROTLI_STATE_METABLOCK_DONE; @@ -1745,30 +1756,17 @@ } else { goto CommandBegin; } -postSelfintersecting: - while (--i >= 0) { - s->ringbuffer[pos] = - s->ringbuffer[(pos - s->distance_code) & s->ringbuffer_mask]; - ++pos; - } - if (s->meta_block_remaining_len <= 0) { - /* Next metablock, if any */ - s->state = BROTLI_STATE_METABLOCK_DONE; - goto saveStateAndReturn; - } else { - goto CommandBegin; - } - CommandPostWrapCopy: - s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY; - while (--i >= 0) { - s->ringbuffer[pos] = - s->ringbuffer[(pos - s->distance_code) & s->ringbuffer_mask]; - ++pos; - if (pos == s->ringbuffer_size) { - /*s->partial_pos_rb += (size_t)s->ringbuffer_size;*/ - s->state = BROTLI_STATE_COMMAND_POST_WRITE_2; - goto saveStateAndReturn; + { + int wrap_guard = s->ringbuffer_size - pos; + while (--i >= 0) { + s->ringbuffer[pos] = + s->ringbuffer[(pos - s->distance_code) & s->ringbuffer_mask]; + ++pos; + if (PREDICT_FALSE(--wrap_guard == 0)) { + s->state = BROTLI_STATE_COMMAND_POST_WRITE_2; + goto saveStateAndReturn; + } } } if (s->meta_block_remaining_len <= 0) { @@ -1817,113 +1815,6 @@ return result; } -BrotliResult BrotliDecompress(BrotliInput input, BrotliOutput output) { - BrotliState s; - BrotliResult result; - BrotliStateInit(&s); - result = BrotliDecompressStreaming(input, output, 1, &s); - if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) { - /* Not ok: it didn't finish even though this is a non-streaming function. */ - result = BROTLI_FAILURE(); - } - BrotliStateCleanup(&s); - return result; -} - -BrotliResult BrotliDecompressBufferStreaming(size_t* available_in, - const uint8_t** next_in, - int finish, - size_t* available_out, - uint8_t** next_out, - size_t* total_out, - BrotliState* s) { - BrotliResult result = BrotliDecompressStream(available_in, next_in, - available_out, next_out, total_out, s); - if (finish && result == BROTLI_RESULT_NEEDS_MORE_INPUT) { - result = BROTLI_FAILURE(); - } - return result; -} - -BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, - int finish, BrotliState* s) { - const size_t kBufferSize = 65536; - BrotliResult result; - uint8_t* input_buffer; - uint8_t* output_buffer; - size_t avail_in; - const uint8_t* next_in; - size_t total_out; - - if (s->legacy_input_buffer == 0) { - s->legacy_input_buffer = (uint8_t*)BROTLI_ALLOC(s, kBufferSize); - } - if (s->legacy_output_buffer == 0) { - s->legacy_output_buffer = (uint8_t*)BROTLI_ALLOC(s, kBufferSize); - } - if (s->legacy_input_buffer == 0 || s->legacy_output_buffer == 0) { - return BROTLI_FAILURE(); - } - input_buffer = s->legacy_input_buffer; - output_buffer = s->legacy_output_buffer; - - /* Push remaining output. */ - if (s->legacy_output_len > s->legacy_output_pos) { - size_t to_write = s->legacy_output_len - s->legacy_output_pos; - int num_written = BrotliWrite( - output, output_buffer + s->legacy_output_pos, to_write); - if (num_written < 0) { - return BROTLI_FAILURE(); - } - s->legacy_output_pos += (size_t)num_written; - if ((size_t)num_written < to_write) { - return BROTLI_RESULT_NEEDS_MORE_OUTPUT; - } - } - s->legacy_output_pos = 0; - - avail_in = s->legacy_input_len - s->legacy_input_pos; - next_in = input_buffer + s->legacy_input_pos; - - while (1) { - size_t to_write; - int num_written; - size_t avail_out = kBufferSize; - uint8_t* next_out = output_buffer; - result = BrotliDecompressStream(&avail_in, &next_in, - &avail_out, &next_out, &total_out, s); - s->legacy_input_pos = (size_t)(next_out - input_buffer); - to_write = (size_t)(next_out - output_buffer); - num_written = BrotliWrite(output, output_buffer, to_write); - if (num_written < 0) { - return BROTLI_FAILURE(); - } - if ((size_t)num_written < to_write) { - s->legacy_output_len = to_write; - s->legacy_output_pos = (size_t)num_written; - return BROTLI_RESULT_NEEDS_MORE_OUTPUT; - } - if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) { - int num_read = BrotliRead(input, input_buffer, kBufferSize); - if (num_read < 0 || (num_read == 0 && finish)) { - return BROTLI_FAILURE(); - } - if (num_read == 0) { - s->legacy_input_len = 0; - s->legacy_input_pos = 0; - return BROTLI_RESULT_NEEDS_MORE_INPUT; - } - avail_in = (size_t)num_read; - next_in = input_buffer; - s->legacy_input_len = avail_in; - s->legacy_input_pos = 0; - } else if (result != BROTLI_RESULT_NEEDS_MORE_OUTPUT) { - /* Success or failure. */ - return result; - } - } -} - /* Invariant: input stream is never overconsumed: * invalid input implies that the whole stream is invalid -> any amount of input could be read and discarded @@ -2034,13 +1925,14 @@ /* Allocate memory for both block_type_trees and block_len_trees. */ s->block_type_trees = (HuffmanCode*)BROTLI_ALLOC(s, - 6 * BROTLI_HUFFMAN_MAX_TABLE_SIZE * sizeof(HuffmanCode)); + sizeof(HuffmanCode) * 3 * + (BROTLI_HUFFMAN_MAX_SIZE_258 + BROTLI_HUFFMAN_MAX_SIZE_26)); if (s->block_type_trees == 0) { result = BROTLI_FAILURE(); break; } - s->block_len_trees = s->block_type_trees + - 3 * BROTLI_HUFFMAN_MAX_TABLE_SIZE; + s->block_len_trees = + s->block_type_trees + 3 * BROTLI_HUFFMAN_MAX_SIZE_258; s->state = BROTLI_STATE_METABLOCK_BEGIN; /* No break, continue to next state */ @@ -2073,10 +1965,7 @@ break; } if (!s->ringbuffer) { - if (!BrotliAllocateRingBuffer(s, br)) { - result = BROTLI_FAILURE(); - break; - } + BrotliCalculateRingBufferSize(s, br); } if (s->is_uncompressed) { s->state = BROTLI_STATE_UNCOMPRESSED; @@ -2128,7 +2017,7 @@ s->state = BROTLI_STATE_HUFFMAN_CODE_1; /* No break, continue to next state */ case BROTLI_STATE_HUFFMAN_CODE_1: { - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_TABLE_SIZE; + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_258; result = ReadHuffmanCode(s->num_block_types[s->loop_counter] + 2, &s->block_type_trees[tree_offset], NULL, s); if (result != BROTLI_RESULT_SUCCESS) break; @@ -2136,7 +2025,7 @@ /* No break, continue to next state */ } case BROTLI_STATE_HUFFMAN_CODE_2: { - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_TABLE_SIZE; + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26; result = ReadHuffmanCode(kNumBlockLengthCodes, &s->block_len_trees[tree_offset], NULL, s); if (result != BROTLI_RESULT_SUCCESS) break; @@ -2144,7 +2033,7 @@ /* No break, continue to next state */ } case BROTLI_STATE_HUFFMAN_CODE_3: { - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_TABLE_SIZE; + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26; if (!SafeReadBlockLength(s, &s->block_length[s->loop_counter], &s->block_len_trees[tree_offset], br)) { result = BROTLI_RESULT_NEEDS_MORE_INPUT; @@ -2163,8 +2052,8 @@ } s->distance_postfix_bits = bits & BitMask(2); bits >>= 2; - s->num_direct_distance_codes = NUM_DISTANCE_SHORT_CODES + - (bits << s->distance_postfix_bits); + s->num_direct_distance_codes = + NUM_DISTANCE_SHORT_CODES + (bits << s->distance_postfix_bits); BROTLI_LOG_UINT(s->num_direct_distance_codes); BROTLI_LOG_UINT(s->distance_postfix_bits); s->distance_postfix_mask = (int)BitMask(s->distance_postfix_bits); @@ -2216,7 +2105,7 @@ s->num_literal_htrees); BrotliHuffmanTreeGroupInit(s, &s->insert_copy_hgroup, kNumInsertAndCopyCodes, - s->num_block_types[1]); + s->num_block_types[1]); BrotliHuffmanTreeGroupInit(s, &s->distance_hgroup, num_distance_codes, s->num_dist_htrees); if (s->literal_hgroup.codes == 0 || @@ -2241,6 +2130,8 @@ case 2: hgroup = &s->distance_hgroup; break; + default: + return BROTLI_FAILURE(); } result = HuffmanTreeGroupDecode(hgroup, s); } @@ -2256,6 +2147,10 @@ &kContextLookup[kContextLookupOffsets[context_mode + 1]]; s->htree_command = s->insert_copy_hgroup.htrees[0]; s->literal_htree = s->literal_hgroup.htrees[s->literal_htree_index]; + if (!s->ringbuffer && !BrotliAllocateRingBuffer(s)) { + result = BROTLI_FAILURE(); + break; + } s->state = BROTLI_STATE_COMMAND_BEGIN; } break; @@ -2280,7 +2175,7 @@ s->max_distance = s->max_backward_distance; if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_1) { memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)s->pos); - if (s->meta_block_remaining_len <= 0) { + if (s->meta_block_remaining_len == 0) { /* Next metablock, if any */ s->state = BROTLI_STATE_METABLOCK_DONE; } else { @@ -2291,7 +2186,7 @@ s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY; } else { /* BROTLI_STATE_COMMAND_INNER_WRITE */ if (s->loop_counter == 0) { - if (s->meta_block_remaining_len <= 0) { + if (s->meta_block_remaining_len == 0) { s->state = BROTLI_STATE_METABLOCK_DONE; } else { s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS; @@ -2333,10 +2228,10 @@ void BrotliSetCustomDictionary( size_t size, const uint8_t* dict, BrotliState* s) { s->custom_dict = dict; - s->custom_dict_size = (int) size; + s->custom_dict_size = (int)size; } #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif
diff --git a/third_party/brotli/dec/decode.h b/third_party/brotli/dec/decode.h index af3c0b52..074b2f5 100644 --- a/third_party/brotli/dec/decode.h +++ b/third_party/brotli/dec/decode.h
@@ -10,7 +10,6 @@ #define BROTLI_DEC_DECODE_H_ #include "./state.h" -#include "./streams.h" #include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) @@ -18,126 +17,57 @@ #endif typedef enum { - /* Decoding error, e.g. corrupt input or no memory */ + /* Decoding error, e.g. corrupt input or memory allocation problem */ BROTLI_RESULT_ERROR = 0, - /* Successfully completely done */ + /* Decoding successfully completed */ BROTLI_RESULT_SUCCESS = 1, - /* Partially done, but must be called again with more input */ + /* Partially done; should be called again with more input */ BROTLI_RESULT_NEEDS_MORE_INPUT = 2, - /* Partially done, but must be called again with more output */ + /* Partially done; should be called again with more output */ BROTLI_RESULT_NEEDS_MORE_OUTPUT = 3 } BrotliResult; -/* BROTLI_FAILURE macro unwraps to BROTLI_RESULT_ERROR in non-debug build. */ -/* In debug build it dumps file name, line and pretty function name. */ -#if defined(_MSC_VER) || !defined(BROTLI_DEBUG) -#define BROTLI_FAILURE() BROTLI_RESULT_ERROR -#else -#define BROTLI_FAILURE() \ - BrotliFailure(__FILE__, __LINE__, __PRETTY_FUNCTION__) -static inline BrotliResult BrotliFailure(const char *f, int l, const char *fn) { - fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn); - fflush(stderr); - return BROTLI_RESULT_ERROR; -} -#endif - -/* Creates the instance of BrotliState and initializes it. alloc_func and - free_func MUST be both zero or both non-zero. In the case they are both zero, - default memory allocators are used. opaque parameter is passed to alloc_func - and free_func when they are called. */ +/* Creates the instance of BrotliState and initializes it. |alloc_func| and + |free_func| MUST be both zero or both non-zero. In the case they are both + zero, default memory allocators are used. |opaque| is passed to |alloc_func| + and |free_func| when they are called. */ BrotliState* BrotliCreateState( brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); /* Deinitializes and frees BrotliState instance. */ void BrotliDestroyState(BrotliState* state); -/* Sets *decoded_size to the decompressed size of the given encoded stream. */ -/* This function only works if the encoded buffer has a single meta block, */ -/* or if it has two meta-blocks, where the first is uncompressed and the */ -/* second is empty. */ -/* Returns 1 on success, 0 on failure. */ +/* Sets |*decoded_size| to the decompressed size of the given encoded stream. + This function only works if the encoded buffer has a single meta block, + or if it has two meta-blocks, where the first is uncompressed and the + second is empty. + Returns 1 on success, 0 on failure. */ int BrotliDecompressedSize(size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size); -/* Decompresses the data in encoded_buffer into decoded_buffer, and sets */ -/* *decoded_size to the decompressed length. */ +/* Decompresses the data in |encoded_buffer| into |decoded_buffer|, and sets + |*decoded_size| to the decompressed length. */ BrotliResult BrotliDecompressBuffer(size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size, uint8_t* decoded_buffer); -/* Same as above, but uses the specified input and output callbacks instead */ -/* of reading from and writing to pre-allocated memory buffers. */ -/* DEPRECATED */ -BrotliResult BrotliDecompress(BrotliInput input, BrotliOutput output); +/* Decompresses the data. Supports partial input and output. -/* Same as above, but supports the caller to call the decoder repeatedly with - partial data to support streaming. The state must be initialized with - BrotliStateInit and reused with every call for the same stream. - Return values: - 0: failure. - 1: success, and done. - 2: success so far, end not reached so should call again with more input. - The finish parameter is used as follows, for a series of calls with the - same state: - 0: Every call except the last one must be called with finish set to 0. The - last call may have finish set to either 0 or 1. Only if finish is 0, can - the function return 2. It may also return 0 or 1, in that case no more - calls (even with finish 1) may be made. - 1: Only the last call may have finish set to 1. It's ok to give empty input - if all input was already given to previous calls. It is also ok to have - only one single call in total, with finish 1, and with all input - available immediately. That matches the non-streaming case. If finish is - 1, the function can only return 0 or 1, never 2. After a finish, no more - calls may be done. - After everything is done, the state must be cleaned with BrotliStateCleanup - to free allocated resources. - The given BrotliOutput must always accept all output and make enough space, - it returning a smaller value than the amount of bytes to write always results - in an error. -*/ -/* DEPRECATED */ -BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, - int finish, BrotliState* s); + Must be called with an allocated input buffer in |*next_in| and an allocated + output buffer in |*next_out|. The values |*available_in| and |*available_out| + must specify the allocated size in |*next_in| and |*next_out| respectively. -/* Same as above, but with memory buffers. - Must be called with an allocated input buffer in *next_in and an allocated - output buffer in *next_out. The values *available_in and *available_out - must specify the allocated size in *next_in and *next_out respectively. - The value *total_out must be 0 initially, and will be summed with the - amount of output bytes written after each call, so that at the end it - gives the complete decoded size. - After each call, *available_in will be decremented by the amount of input - bytes consumed, and the *next_in pointer will be incremented by that amount. - Similarly, *available_out will be decremented by the amount of output - bytes written, and the *next_out pointer will be incremented by that - amount. + After each call, |*available_in| will be decremented by the amount of input + bytes consumed, and the |*next_in| pointer will be incremented by that + amount. Similarly, |*available_out| will be decremented by the amount of + output bytes written, and the |*next_out| pointer will be incremented by that + amount. |total_out| will be set to the number of bytes decompressed since + last state initialization. - The input may be partial. With each next function call, *next_in and - *available_in must be updated to point to a next part of the compressed - input. The current implementation will always consume all input unless - an error occurs, so normally *available_in will always be 0 after - calling this function and the next adjacent part of input is desired. - - In the current implementation, the function requires that there is enough - output buffer size to write all currently processed input, so - *available_out must be large enough. Since the function updates *next_out - each time, as long as the output buffer is large enough you can keep - reusing this variable. It is also possible to update *next_out and - *available_out yourself before a next call, e.g. to point to a new larger - buffer. -*/ -/* DEPRECATED */ -BrotliResult BrotliDecompressBufferStreaming(size_t* available_in, - const uint8_t** next_in, - int finish, - size_t* available_out, - uint8_t** next_out, - size_t* total_out, - BrotliState* s); - + Input is never overconsumed, so |next_in| and |available_in| could be passed + to the next consumer after decoding is complete. */ BrotliResult BrotliDecompressStream(size_t* available_in, const uint8_t** next_in, size_t* available_out, @@ -150,10 +80,10 @@ Not to be confused with the built-in transformable dictionary of Brotli. The dictionary must exist in memory until decoding is done and is owned by the caller. To use: - -initialize state with BrotliStateInit - -use BrotliSetCustomDictionary - -use BrotliDecompressBufferStreaming - -clean up with BrotliStateCleanup + 1) initialize state with BrotliStateInit + 2) use BrotliSetCustomDictionary + 3) use BrotliDecompressStream + 4) clean up with BrotliStateCleanup */ void BrotliSetCustomDictionary( size_t size, const uint8_t* dict, BrotliState* s);
diff --git a/third_party/brotli/dec/dictionary.c b/third_party/brotli/dec/dictionary.c index d87f2776..f8f5857 100644 --- a/third_party/brotli/dec/dictionary.c +++ b/third_party/brotli/dec/dictionary.c
@@ -9462,5 +9462,5 @@ }; #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif
diff --git a/third_party/brotli/dec/dictionary.h b/third_party/brotli/dec/dictionary.h index b327fd6..ae250c2 100644 --- a/third_party/brotli/dec/dictionary.h +++ b/third_party/brotli/dec/dictionary.h
@@ -17,23 +17,22 @@ extern const uint8_t kBrotliDictionary[122784]; -static const int kBrotliDictionaryOffsetsByLength[] = { - 0, 0, 0, 0, 0, 4096, 9216, 21504, 35840, 44032, - 53248, 63488, 74752, 87040, 93696, 100864, 104704, 106752, 108928, 113536, - 115968, 118528, 119872, 121280, 122016, +static const uint32_t kBrotliDictionaryOffsetsByLength[] = { + 0, 0, 0, 0, 0, 4096, 9216, 21504, 35840, 44032, 53248, 63488, 74752, 87040, + 93696, 100864, 104704, 106752, 108928, 113536, 115968, 118528, 119872, 121280, + 122016 }; static const uint8_t kBrotliDictionarySizeBitsByLength[] = { - 0, 0, 0, 0, 10, 10, 11, 11, 10, 10, - 10, 10, 10, 9, 9, 8, 7, 7, 8, 7, - 7, 6, 6, 5, 5, + 0, 0, 0, 0, 10, 10, 11, 11, 10, 10, 10, 10, 10, + 9, 9, 8, 7, 7, 8, 7, 7, 6, 6, 5, 5, }; static const int kBrotliMinDictionaryWordLength = 4; static const int kBrotliMaxDictionaryWordLength = 24; #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif #endif /* BROTLI_DEC_DICTIONARY_H_ */
diff --git a/third_party/brotli/dec/huffman.c b/third_party/brotli/dec/huffman.c index 291f0a7d..159ac1c 100644 --- a/third_party/brotli/dec/huffman.c +++ b/third_party/brotli/dec/huffman.c
@@ -6,11 +6,12 @@ /* Utilities for building Huffman decoding tables. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> #include "./huffman.h" + +#include <string.h> /* memcpy, memset */ + #include "./port.h" +#include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -23,43 +24,43 @@ #else #define BROTLI_REVERSE_BITS_BASE 0 static uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, - 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, - 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, - 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, - 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, - 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, - 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, - 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, - 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, - 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, - 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, - 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF }; -#endif /* BROTLI_RBIT */ +#endif /* BROTLI_RBIT */ #define BROTLI_REVERSE_BITS_LOWEST \ - (1U << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE)) + (1U << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE)) /* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX), where reverse(value, len) is the bit-wise reversal of the len least @@ -101,20 +102,20 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table, const uint8_t* const code_lengths, - uint16_t *count) { - HuffmanCode code; /* current table entry */ - int symbol; /* symbol index in original or sorted table */ - uint32_t key; /* prefix code */ - uint32_t key_step; /* prefix code addend */ - int step; /* step size to replicate values in current table */ - int table_size; /* size of current table */ - int sorted[18]; /* symbols sorted by code length */ + uint16_t* count) { + HuffmanCode code; /* current table entry */ + int symbol; /* symbol index in original or sorted table */ + uint32_t key; /* prefix code */ + uint32_t key_step; /* prefix code addend */ + int step; /* step size to replicate values in current table */ + int table_size; /* size of current table */ + int sorted[18]; /* symbols sorted by code length */ /* offsets in sorted table for each length */ int offset[BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1]; int bits; int bits_count; - BROTLI_DCHECK( - BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH <= BROTLI_REVERSE_BITS_MAX); + BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH <= + BROTLI_REVERSE_BITS_MAX); /* generate offsets into sorted symbol table by code length */ symbol = -1; @@ -169,26 +170,26 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table, int root_bits, const uint16_t* const symbol_lists, - uint16_t *count) { - HuffmanCode code; /* current table entry */ - HuffmanCode* table; /* next available space in table */ - int len; /* current code length */ - int symbol; /* symbol index in original or sorted table */ - uint32_t key; /* prefix code */ - uint32_t key_step; /* prefix code addend */ - uint32_t sub_key; /* 2nd level table prefix code */ - uint32_t sub_key_step;/* 2nd level table prefix code addend */ - int step; /* step size to replicate values in current table */ - int table_bits; /* key length of current table */ - int table_size; /* size of current table */ - int total_size; /* sum of root table size and 2nd level table sizes */ + uint16_t* count) { + HuffmanCode code; /* current table entry */ + HuffmanCode* table; /* next available space in table */ + int len; /* current code length */ + int symbol; /* symbol index in original or sorted table */ + uint32_t key; /* prefix code */ + uint32_t key_step; /* prefix code addend */ + uint32_t sub_key; /* 2nd level table prefix code */ + uint32_t sub_key_step; /* 2nd level table prefix code addend */ + int step; /* step size to replicate values in current table */ + int table_bits; /* key length of current table */ + int table_size; /* size of current table */ + int total_size; /* sum of root table size and 2nd level table sizes */ int max_length = -1; int bits; int bits_count; BROTLI_DCHECK(root_bits <= BROTLI_REVERSE_BITS_MAX); - BROTLI_DCHECK( - BROTLI_HUFFMAN_MAX_CODE_LENGTH - root_bits <= BROTLI_REVERSE_BITS_MAX); + BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH - root_bits <= + BROTLI_REVERSE_BITS_MAX); while (symbol_lists[max_length] == 0xFFFF) max_length--; max_length += BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1; @@ -237,7 +238,7 @@ for (len = root_bits + 1, step = 2; len <= max_length; ++len) { symbol = len - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1); for (; count[len] != 0; --count[len]) { - if (sub_key == (uint32_t)(BROTLI_REVERSE_BITS_LOWEST << 1)) { + if (sub_key == (BROTLI_REVERSE_BITS_LOWEST << 1U)) { table += table_size; table_bits = NextTableBitSize(count, len, root_bits); table_size = 1 << table_bits; @@ -245,8 +246,8 @@ sub_key = BrotliReverseBits(key); key += key_step; root_table[sub_key].bits = (uint8_t)(table_bits + root_bits); - root_table[sub_key].value = (uint16_t)( - ((size_t)(table - root_table)) - sub_key); + root_table[sub_key].value = + (uint16_t)(((size_t)(table - root_table)) - sub_key); sub_key = 0; } code.bits = (uint8_t)(len - root_bits); @@ -264,7 +265,7 @@ uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table, int root_bits, - uint16_t *val, + uint16_t* val, uint32_t num_symbols) { uint32_t table_size = 1; const uint32_t goal_size = 1U << root_bits; @@ -301,49 +302,47 @@ table[3].bits = 2; table_size = 4; break; - case 3: - { - int i, k; - for (i = 0; i < 3; ++i) { - for (k = i + 1; k < 4; ++k) { - if (val[k] < val[i]) { - uint16_t t = val[k]; - val[k] = val[i]; - val[i] = t; - } + case 3: { + int i, k; + for (i = 0; i < 3; ++i) { + for (k = i + 1; k < 4; ++k) { + if (val[k] < val[i]) { + uint16_t t = val[k]; + val[k] = val[i]; + val[i] = t; } } - for (i = 0; i < 4; ++i) { - table[i].bits = 2; - } - table[0].value = val[0]; - table[2].value = val[1]; - table[1].value = val[2]; - table[3].value = val[3]; - table_size = 4; } - break; - case 4: - { - int i; - if (val[3] < val[2]) { - uint16_t t = val[3]; - val[3] = val[2]; - val[2] = t; - } - for (i = 0; i < 7; ++i) { - table[i].value = val[0]; - table[i].bits = (uint8_t)(1 + (i & 1)); - } - table[1].value = val[1]; - table[3].value = val[2]; - table[5].value = val[1]; - table[7].value = val[3]; - table[3].bits = 3; - table[7].bits = 3; - table_size = 8; + for (i = 0; i < 4; ++i) { + table[i].bits = 2; } + table[0].value = val[0]; + table[2].value = val[1]; + table[1].value = val[2]; + table[3].value = val[3]; + table_size = 4; break; + } + case 4: { + int i; + if (val[3] < val[2]) { + uint16_t t = val[3]; + val[3] = val[2]; + val[2] = t; + } + for (i = 0; i < 7; ++i) { + table[i].value = val[0]; + table[i].bits = (uint8_t)(1 + (i & 1)); + } + table[1].value = val[1]; + table[3].value = val[2]; + table[5].value = val[1]; + table[7].value = val[3]; + table[3].bits = 3; + table[7].bits = 3; + table_size = 8; + break; + } } while (table_size != goal_size) { memcpy(&table[table_size], &table[0], @@ -354,5 +353,5 @@ } #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif
diff --git a/third_party/brotli/dec/huffman.h b/third_party/brotli/dec/huffman.h index e8481f00..7cbec80 100644 --- a/third_party/brotli/dec/huffman.h +++ b/third_party/brotli/dec/huffman.h
@@ -20,29 +20,34 @@ /* For current format this constant equals to kNumInsertAndCopyCodes */ #define BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE 704 -/* Maximum possible Huffman table size for an alphabet size of 704, max code - * length 15 and root table bits 8. */ -#define BROTLI_HUFFMAN_MAX_TABLE_SIZE 1080 +/* Maximum possible Huffman table size for an alphabet size of (index * 32), + * max code length 15 and root table bits 8. */ +static const uint16_t kMaxHuffmanTableSize[] = { + 256, 402, 436, 468, 500, 534, 566, 598, 630, 662, 694, 726, 758, 790, 822, + 854, 886, 920, 952, 984, 1016, 1048, 1080}; +#define BROTLI_HUFFMAN_MAX_SIZE_26 396 +#define BROTLI_HUFFMAN_MAX_SIZE_258 632 +#define BROTLI_HUFFMAN_MAX_SIZE_272 646 #define BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH 5 typedef struct { - uint8_t bits; /* number of bits used for this symbol */ - uint16_t value; /* symbol value or table offset */ + uint8_t bits; /* number of bits used for this symbol */ + uint16_t value; /* symbol value or table offset */ } HuffmanCode; /* Builds Huffman lookup table assuming code lengths are in symbol order. */ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_table, const uint8_t* const code_lengths, - uint16_t *count); + uint16_t* count); /* Builds Huffman lookup table assuming code lengths are in symbol order. */ /* Returns size of resulting table. */ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table, int root_bits, const uint16_t* const symbol_lists, - uint16_t *count_arg); + uint16_t* count_arg); /* Builds a simple Huffman table. The num_symbols parameter is to be */ /* interpreted as follows: 0 means 1 symbol, 1 means 2 symbols, 2 means 3 */ @@ -50,7 +55,7 @@ /* lengths 1,2,3,3. */ uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table, int root_bits, - uint16_t *symbols, + uint16_t* symbols, uint32_t num_symbols); /* Contains a collection of Huffman trees with the same alphabet size. */ @@ -62,7 +67,7 @@ } HuffmanTreeGroup; #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif #endif /* BROTLI_DEC_HUFFMAN_H_ */
diff --git a/third_party/brotli/dec/port.h b/third_party/brotli/dec/port.h index f80e9573..6cbe498 100644 --- a/third_party/brotli/dec/port.h +++ b/third_party/brotli/dec/port.h
@@ -24,7 +24,7 @@ #ifndef BROTLI_DEC_PORT_H_ #define BROTLI_DEC_PORT_H_ -#include<assert.h> +#include <assert.h> /* Compatibility with non-clang compilers. */ #ifndef __has_builtin @@ -39,10 +39,6 @@ #define __has_feature(x) 0 #endif -#if defined(__sparc) -#define BROTLI_TARGET_SPARC -#endif - #if defined(__arm__) || defined(__thumb__) || \ defined(_M_ARM) || defined(_M_ARMT) #define BROTLI_TARGET_ARM @@ -55,6 +51,10 @@ #endif /* ARMv8 */ #endif /* ARM */ +#if defined(__i386) || defined(_M_IX86) +#define BROTLI_TARGET_X86 +#endif + #if defined(__x86_64__) || defined(_M_X64) #define BROTLI_TARGET_X64 #endif @@ -83,25 +83,15 @@ #define BROTLI_MODERN_COMPILER 0 #endif -/* SPARC and ARMv6 don't support unaligned read. - Choose portable build for them. */ -#if !defined(BROTLI_BUILD_PORTABLE) -#if defined(BROTLI_TARGET_SPARC) || \ - (defined(BROTLI_TARGET_ARM) && !defined(BROTLI_TARGET_ARMV7)) -#define BROTLI_BUILD_PORTABLE -#endif /* SPARK or ARMv6 */ -#endif /* portable build */ - #ifdef BROTLI_BUILD_PORTABLE #define BROTLI_ALIGNED_READ 1 -#define BROTLI_SAFE_MEMMOVE 1 +#elif defined(BROTLI_TARGET_X86) || defined(BROTLI_TARGET_X64) || \ + defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8) +#define BROTLI_ALIGNED_READ 0 /* Allow unaligned access on whitelisted CPUs. */ #else -#define BROTLI_ALIGNED_READ 0 -#define BROTLI_SAFE_MEMMOVE 0 +#define BROTLI_ALIGNED_READ 1 #endif -#define BROTLI_ASAN_BUILD __has_feature(address_sanitizer) - /* Define "PREDICT_TRUE" and "PREDICT_FALSE" macros for capable compilers. To apply compiler hint, enclose the branching condition into macros, like this: @@ -141,8 +131,8 @@ #endif #ifndef _MSC_VER -#if defined(__cplusplus) || !defined(__STRICT_ANSI__) \ - || __STDC_VERSION__ >= 199901L +#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \ + __STDC_VERSION__ >= 199901L #define BROTLI_INLINE inline ATTRIBUTE_ALWAYS_INLINE #else #define BROTLI_INLINE @@ -194,20 +184,14 @@ #endif #if BROTLI_MODERN_COMPILER || __has_attribute(noinline) -#define BROTLI_NOINLINE __attribute__ ((noinline)) +#define BROTLI_NOINLINE __attribute__((noinline)) #else #define BROTLI_NOINLINE #endif -#if BROTLI_ASAN_BUILD && !defined(BROTLI_BUILD_PORTABLE) -#define BROTLI_NO_ASAN __attribute__((no_sanitize("address"))) BROTLI_NOINLINE -#else -#define BROTLI_NO_ASAN -#endif - -#define BROTLI_REPEAT(N, X) { \ - if ((N & 1) != 0) {X;} \ - if ((N & 2) != 0) {X; X;} \ +#define BROTLI_REPEAT(N, X) { \ + if ((N & 1) != 0) {X;} \ + if ((N & 2) != 0) {X; X;} \ if ((N & 4) != 0) {X; X; X; X;} \ } @@ -230,9 +214,9 @@ #define BROTLI_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L) -#define BROTLI_FREE(S, X) { \ +#define BROTLI_FREE(S, X) { \ S->free_func(S->memory_manager_opaque, X); \ - X = NULL; \ + X = NULL; \ } #define BROTLI_UNUSED(X) (void)(X)
diff --git a/third_party/brotli/dec/prefix.h b/third_party/brotli/dec/prefix.h index 41263fdc..eaae37f0 100644 --- a/third_party/brotli/dec/prefix.h +++ b/third_party/brotli/dec/prefix.h
@@ -11,6 +11,8 @@ #ifndef BROTLI_DEC_PREFIX_H_ #define BROTLI_DEC_PREFIX_H_ +#include "./types.h" + /* Represents the range of values belonging to a prefix code: */ /* [offset, offset + 2^nbits) */ struct PrefixCodeRange {
diff --git a/third_party/brotli/dec/state.c b/third_party/brotli/dec/state.c index d5815e0..52c0e6c 100644 --- a/third_party/brotli/dec/state.c +++ b/third_party/brotli/dec/state.c
@@ -4,11 +4,12 @@ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ -#include "./huffman.h" #include "./state.h" -#include <stdlib.h> -#include <string.h> +#include <stdlib.h> /* free, malloc */ + +#include "./huffman.h" +#include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -94,13 +95,6 @@ s->symbol_lists = &s->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1]; s->mtf_upper_bound = 255; - - s->legacy_input_buffer = 0; - s->legacy_output_buffer = 0; - s->legacy_input_len = 0; - s->legacy_output_len = 0; - s->legacy_input_pos = 0; - s->legacy_output_pos = 0; } void BrotliStateMetablockBegin(BrotliState* s) { @@ -150,8 +144,6 @@ BROTLI_FREE(s, s->ringbuffer); BROTLI_FREE(s, s->block_type_trees); - BROTLI_FREE(s, s->legacy_input_buffer); - BROTLI_FREE(s, s->legacy_output_buffer); } int BrotliStateIsStreamStart(const BrotliState* s) { @@ -166,10 +158,10 @@ void BrotliHuffmanTreeGroupInit(BrotliState* s, HuffmanTreeGroup* group, uint32_t alphabet_size, uint32_t ntrees) { /* Pack two allocations into one */ - const size_t code_size = - sizeof(HuffmanCode) * (size_t)(ntrees * BROTLI_HUFFMAN_MAX_TABLE_SIZE); - const size_t htree_size = sizeof(HuffmanCode*) * (size_t)ntrees; - char *p = (char*)BROTLI_ALLOC(s, code_size + htree_size); + const size_t max_table_size = kMaxHuffmanTableSize[(alphabet_size + 31) >> 5]; + const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size; + const size_t htree_size = sizeof(HuffmanCode*) * ntrees; + char* p = (char*)BROTLI_ALLOC(s, code_size + htree_size); group->alphabet_size = (uint16_t)alphabet_size; group->num_htrees = (uint16_t)ntrees; group->codes = (HuffmanCode*)p; @@ -182,5 +174,5 @@ } #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif
diff --git a/third_party/brotli/dec/state.h b/third_party/brotli/dec/state.h index f569f58b..925fa149 100644 --- a/third_party/brotli/dec/state.h +++ b/third_party/brotli/dec/state.h
@@ -9,7 +9,6 @@ #ifndef BROTLI_DEC_STATE_H_ #define BROTLI_DEC_STATE_H_ -#include <stdio.h> #include "./bit_reader.h" #include "./huffman.h" #include "./types.h" @@ -95,6 +94,10 @@ struct BrotliStateStruct { BrotliRunningState state; + + /* This counter is reused for several disjoint loops. */ + int loop_counter; + BrotliBitReader br; brotli_alloc_func alloc_func; @@ -108,8 +111,6 @@ } buffer; uint32_t buffer_length; - /* This counter is reused for several disjoint loops. */ - int loop_counter; int pos; int max_backward_distance; int max_backward_distance_minus_custom_dict_size; @@ -149,7 +150,7 @@ int distance_postfix_mask; uint32_t num_dist_htrees; uint8_t* dist_context_map; - HuffmanCode *literal_htree; + HuffmanCode* literal_htree; uint8_t literal_htree_index; uint8_t dist_htree_index; uint32_t repeat_code_len; @@ -173,7 +174,7 @@ uint16_t* symbol_lists; /* Storage from symbol_lists. */ uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 + - BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE]; + BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE]; /* Tails of symbol chains. */ int next_symbol[32]; uint8_t code_length_code_lengths[18]; @@ -188,7 +189,7 @@ uint32_t context_index; uint32_t max_run_length_prefix; uint32_t code; - HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_TABLE_SIZE]; + HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272]; /* For InverseMoveToFrontTransform */ uint32_t mtf_upper_bound; @@ -217,13 +218,6 @@ uint32_t num_literal_htrees; uint8_t* context_map; uint8_t* context_modes; - - uint8_t* legacy_input_buffer; - uint8_t* legacy_output_buffer; - size_t legacy_input_len; - size_t legacy_output_len; - size_t legacy_input_pos; - size_t legacy_output_pos; }; typedef struct BrotliStateStruct BrotliState; @@ -248,9 +242,8 @@ produced all of the output, and 0 otherwise. */ int BrotliStateIsStreamEnd(const BrotliState* s); - #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif #endif /* BROTLI_DEC_STATE_H_ */
diff --git a/third_party/brotli/dec/streams.c b/third_party/brotli/dec/streams.c deleted file mode 100644 index 0c781a7..0000000 --- a/third_party/brotli/dec/streams.c +++ /dev/null
@@ -1,102 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Functions for streaming input and output. */ - -#include <string.h> -#ifndef _WIN32 -#include <unistd.h> -#endif -#include "./streams.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -int BrotliMemInputFunction(void* data, uint8_t* buf, size_t count) { - BrotliMemInput* input = (BrotliMemInput*)data; - if (input->pos > input->length) { - return -1; - } - if (input->pos + count > input->length) { - count = input->length - input->pos; - } - memcpy(buf, input->buffer + input->pos, count); - input->pos += count; - return (int)count; -} - -BrotliInput BrotliInitMemInput(const uint8_t* buffer, size_t length, - BrotliMemInput* mem_input) { - BrotliInput input; - mem_input->buffer = buffer; - mem_input->length = length; - mem_input->pos = 0; - input.cb_ = &BrotliMemInputFunction; - input.data_ = mem_input; - return input; -} - -int BrotliMemOutputFunction(void* data, const uint8_t* buf, size_t count) { - BrotliMemOutput* output = (BrotliMemOutput*)data; - size_t limit = output->length - output->pos; - if (count > limit) { - count = limit; - } - memcpy(output->buffer + output->pos, buf, count); - output->pos += count; - return (int)count; -} - -BrotliOutput BrotliInitMemOutput(uint8_t* buffer, size_t length, - BrotliMemOutput* mem_output) { - BrotliOutput output; - mem_output->buffer = buffer; - mem_output->length = length; - mem_output->pos = 0; - output.cb_ = &BrotliMemOutputFunction; - output.data_ = mem_output; - return output; -} - -int BrotliFileInputFunction(void* data, uint8_t* buf, size_t count) { - return (int)fread(buf, 1, count, (FILE*)data); -} - -BrotliInput BrotliFileInput(FILE* f) { - BrotliInput in; - in.cb_ = BrotliFileInputFunction; - in.data_ = f; - return in; -} - -int BrotliFileOutputFunction(void* data, const uint8_t* buf, size_t count) { - return (int)fwrite(buf, 1, count, (FILE*)data); -} - -BrotliOutput BrotliFileOutput(FILE* f) { - BrotliOutput out; - out.cb_ = BrotliFileOutputFunction; - out.data_ = f; - return out; -} - -int BrotliNullOutputFunction(void* data , const uint8_t* buf, size_t count) { - BROTLI_UNUSED(data); - BROTLI_UNUSED(buf); - return (int)count; -} - -BrotliOutput BrotliNullOutput(void) { - BrotliOutput out; - out.cb_ = BrotliNullOutputFunction; - out.data_ = NULL; - return out; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif
diff --git a/third_party/brotli/dec/streams.h b/third_party/brotli/dec/streams.h deleted file mode 100644 index a849432..0000000 --- a/third_party/brotli/dec/streams.h +++ /dev/null
@@ -1,95 +0,0 @@ -/* Copyright 2013 Google Inc. All Rights Reserved. - - Distributed under MIT license. - See file LICENSE for detail or copy at https://opensource.org/licenses/MIT -*/ - -/* Functions for streaming input and output. */ - -#ifndef BROTLI_DEC_STREAMS_H_ -#define BROTLI_DEC_STREAMS_H_ - -#include <stdio.h> -#include "./port.h" -#include "./types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -/* Function pointer type used to read len bytes into buf. Returns the */ -/* number of bytes read or -1 on error. */ -typedef int (*BrotliInputFunction)(void* data, uint8_t* buf, size_t len); - -/* Input callback function with associated data. */ -typedef struct { - BrotliInputFunction cb_; - void* data_; -} BrotliInput; - -/* Reads len bytes into buf, using the in callback. */ -static BROTLI_INLINE int BrotliRead(BrotliInput in, uint8_t* buf, size_t len) { - return in.cb_(in.data_, buf, len); -} - -/* Function pointer type used to write len bytes into buf. Returns the */ -/* number of bytes written or -1 on error. */ -typedef int (*BrotliOutputFunction)(void* data, const uint8_t* buf, size_t len); - -/* Output callback function with associated data. */ -typedef struct { - BrotliOutputFunction cb_; - void* data_; -} BrotliOutput; - -/* Writes len bytes into buf, using the out callback. */ -static BROTLI_INLINE int BrotliWrite(BrotliOutput out, - const uint8_t* buf, size_t len) { - return out.cb_(out.data_, buf, len); -} - -/* Memory region with position. */ -typedef struct { - const uint8_t* buffer; - size_t length; - size_t pos; -} BrotliMemInput; - -/* Input callback where *data is a BrotliMemInput struct. */ -int BrotliMemInputFunction(void* data, uint8_t* buf, size_t count); - -/* Returns an input callback that wraps the given memory region. */ -BrotliInput BrotliInitMemInput(const uint8_t* buffer, size_t length, - BrotliMemInput* mem_input); - -/* Output buffer with position. */ -typedef struct { - uint8_t* buffer; - size_t length; - size_t pos; -} BrotliMemOutput; - -/* Output callback where *data is a BrotliMemOutput struct. */ -int BrotliMemOutputFunction(void* data, const uint8_t* buf, size_t count); - -/* Returns an output callback that wraps the given memory region. */ -BrotliOutput BrotliInitMemOutput(uint8_t* buffer, size_t length, - BrotliMemOutput* mem_output); - -/* Input callback that reads from a file. */ -int BrotliFileInputFunction(void* data, uint8_t* buf, size_t count); -BrotliInput BrotliFileInput(FILE* f); - -/* Output callback that writes to a file. */ -int BrotliFileOutputFunction(void* data, const uint8_t* buf, size_t count); -BrotliOutput BrotliFileOutput(FILE* f); - -/* Output callback that does nothing, always consumes the whole input. */ -int BrotliNullOutputFunction(void* data, const uint8_t* buf, size_t count); -BrotliOutput BrotliNullOutput(void); - -#if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ -#endif - -#endif /* BROTLI_DEC_STREAMS_H_ */
diff --git a/third_party/brotli/dec/transform.h b/third_party/brotli/dec/transform.h index f47eeb4..8c08f3f 100644 --- a/third_party/brotli/dec/transform.h +++ b/third_party/brotli/dec/transform.h
@@ -9,8 +9,6 @@ #ifndef BROTLI_DEC_TRANSFORM_H_ #define BROTLI_DEC_TRANSFORM_H_ -#include <stdio.h> -#include <ctype.h> #include "./port.h" #include "./types.h" @@ -19,27 +17,27 @@ #endif enum WordTransformType { - kIdentity = 0, - kOmitLast1 = 1, - kOmitLast2 = 2, - kOmitLast3 = 3, - kOmitLast4 = 4, - kOmitLast5 = 5, - kOmitLast6 = 6, - kOmitLast7 = 7, - kOmitLast8 = 8, - kOmitLast9 = 9, + kIdentity = 0, + kOmitLast1 = 1, + kOmitLast2 = 2, + kOmitLast3 = 3, + kOmitLast4 = 4, + kOmitLast5 = 5, + kOmitLast6 = 6, + kOmitLast7 = 7, + kOmitLast8 = 8, + kOmitLast9 = 9, kUppercaseFirst = 10, - kUppercaseAll = 11, - kOmitFirst1 = 12, - kOmitFirst2 = 13, - kOmitFirst3 = 14, - kOmitFirst4 = 15, - kOmitFirst5 = 16, - kOmitFirst6 = 17, - kOmitFirst7 = 18, - kOmitFirst8 = 19, - kOmitFirst9 = 20 + kUppercaseAll = 11, + kOmitFirst1 = 12, + kOmitFirst2 = 13, + kOmitFirst3 = 14, + kOmitFirst4 = 15, + kOmitFirst5 = 16, + kOmitFirst6 = 17, + kOmitFirst7 = 18, + kOmitFirst8 = 19, + kOmitFirst9 = 20 }; typedef struct { @@ -68,15 +66,15 @@ kPFix_SP = 1, kPFix_COMMASP = 3, kPFix_SPofSPtheSP = 6, - kPFix_SPtheSP = 9, + kPFix_SPtheSP = 9, kPFix_eSP = 12, - kPFix_SPofSP = 15, + kPFix_SPofSP = 15, kPFix_sSP = 20, kPFix_DOT = 23, kPFix_SPandSP = 25, kPFix_SPinSP = 31, kPFix_DQUOT = 36, - kPFix_SPtoSP = 38, + kPFix_SPtoSP = 38, kPFix_DQUOTGT = 43, kPFix_NEWLINE = 46, kPFix_DOTSP = 48, @@ -86,20 +84,20 @@ kPFix_SPthatSP = 63, kPFix_SQUOT = 70, kPFix_SPwithSP = 72, - kPFix_SPfromSP = 79, - kPFix_SPbySP = 86, + kPFix_SPfromSP = 79, + kPFix_SPbySP = 86, kPFix_OPEN = 91, kPFix_DOTSPTheSP = 93, - kPFix_SPonSP = 100, - kPFix_SPasSP = 105, - kPFix_SPisSP = 110, + kPFix_SPonSP = 100, + kPFix_SPasSP = 105, + kPFix_SPisSP = 110, kPFix_ingSP = 115, kPFix_NEWLINETAB = 120, kPFix_COLON = 123, kPFix_edSP = 125, kPFix_EQDQUOT = 129, kPFix_SPatSP = 132, - kPFix_lySP = 137, + kPFix_lySP = 137, kPFix_COMMA = 141, kPFix_EQSQUOT = 143, kPFix_DOTcomSLASH = 146, @@ -116,7 +114,6 @@ kPFix_ousSP = 203 }; - static const Transform kTransforms[] = { { kPFix_EMPTY, kIdentity, kPFix_EMPTY }, { kPFix_EMPTY, kIdentity, kPFix_SP }, @@ -243,7 +240,7 @@ static const int kNumTransforms = sizeof(kTransforms) / sizeof(kTransforms[0]); -static int ToUpperCase(uint8_t *p) { +static int ToUpperCase(uint8_t* p) { if (p[0] < 0xc0) { if (p[0] >= 'a' && p[0] <= 'z') { p[0] ^= 32; @@ -269,22 +266,19 @@ } { const int t = kTransforms[transform].transform; - int skip = t < kOmitFirst1 ? 0 : t - (kOmitFirst1 - 1); int i = 0; - uint8_t* uppercase; - if (skip > len) { - skip = len; - } - word += skip; - len -= skip; - if (t <= kOmitLast9) { + int skip = t - (kOmitFirst1 - 1); + if (skip > 0) { + word += skip; + len -= skip; + } else if (t <= kOmitLast9) { len -= t; } while (i < len) { dst[idx++] = word[i++]; } - uppercase = &dst[idx - len]; if (t == kUppercaseFirst) { - ToUpperCase(uppercase); + ToUpperCase(&dst[idx - len]); } else if (t == kUppercaseAll) { + uint8_t* uppercase = &dst[idx - len]; while (len > 0) { int step = ToUpperCase(uppercase); uppercase += step; @@ -300,7 +294,7 @@ } #if defined(__cplusplus) || defined(c_plusplus) -} /* extern "C" */ +} /* extern "C" */ #endif #endif /* BROTLI_DEC_TRANSFORM_H_ */
diff --git a/third_party/brotli/dec/types.h b/third_party/brotli/dec/types.h index 56fde60..0f7a021 100644 --- a/third_party/brotli/dec/types.h +++ b/third_party/brotli/dec/types.h
@@ -29,10 +29,10 @@ size length. Neither items nor size are allowed to be 0. opaque argument is a pointer provided by client and could be used to bind function to specific object (memory pool). */ -typedef void* (*brotli_alloc_func) (void* opaque, size_t size); +typedef void* (*brotli_alloc_func)(void* opaque, size_t size); /* Deallocating function pointer. Function SHOULD be no-op in the case the address is 0. */ -typedef void (*brotli_free_func) (void* opaque, void* address); +typedef void (*brotli_free_func)(void* opaque, void* address); #endif /* BROTLI_DEC_TYPES_H_ */
diff --git a/third_party/google_toolbox_for_mac/BUILD.gn b/third_party/google_toolbox_for_mac/BUILD.gn index 1977a06..7b207b3 100644 --- a/third_party/google_toolbox_for_mac/BUILD.gn +++ b/third_party/google_toolbox_for_mac/BUILD.gn
@@ -10,6 +10,11 @@ "src/DebugUtils", "src/Foundation", ] + + # TODO(crbug.com/569158): Suppresses warnings that are treated as errors + # when minimum iOS version support is increased to iOS 9 and up. + # This should be removed once all deprecation violations have been fixed. + cflags = [ "-Wno-deprecated-declarations" ] } component("google_toolbox_for_mac") {
diff --git a/third_party/libvpx_new/README.chromium b/third_party/libvpx_new/README.chromium index db35fe33..b7cff7e 100644 --- a/third_party/libvpx_new/README.chromium +++ b/third_party/libvpx_new/README.chromium
@@ -5,9 +5,9 @@ License File: source/libvpx/LICENSE Security Critical: yes -Date: Tuesday February 09 2016 +Date: Tuesday February 16 2016 Branch: master -Commit: f288c943c4d6c3fb03266dee821df797fb99bde0 +Commit: 89cc68252846478fa7f2d570d96ff93776cefac6 Description: Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx_new/source/config/vpx_version.h b/third_party/libvpx_new/source/config/vpx_version.h index 6a48824..7fac6a4 100644 --- a/third_party/libvpx_new/source/config/vpx_version.h +++ b/third_party/libvpx_new/source/config/vpx_version.h
@@ -1,7 +1,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 5 #define VERSION_PATCH 0 -#define VERSION_EXTRA "459-gf288c94" +#define VERSION_EXTRA "483-g89cc682" #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH)) -#define VERSION_STRING_NOSP "v1.5.0-459-gf288c94" -#define VERSION_STRING " v1.5.0-459-gf288c94" +#define VERSION_STRING_NOSP "v1.5.0-483-g89cc682" +#define VERSION_STRING " v1.5.0-483-g89cc682"
diff --git a/third_party/wayland-protocols/BUILD.gn b/third_party/wayland-protocols/BUILD.gn index 921184a..b9aad46 100644 --- a/third_party/wayland-protocols/BUILD.gn +++ b/third_party/wayland-protocols/BUILD.gn
@@ -22,3 +22,24 @@ public_configs = [ ":xdg_shell_protocol_config" ] } + +config("scaler_protocol_config") { + include_dirs = [ "include/protocol" ] +} + +source_set("scaler_protocol") { + sources = [ + "include/protocol/scaler-client-protocol.h", + "include/protocol/scaler-server-protocol.h", + "protocol/scaler-protocol.c", + ] + + deps = [ + "//third_party/wayland:wayland_util", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + public_configs = [ ":scaler_protocol_config" ] +}
diff --git a/third_party/wayland-protocols/README.chromium b/third_party/wayland-protocols/README.chromium index 964598fe..30bde1e2 100644 --- a/third_party/wayland-protocols/README.chromium +++ b/third_party/wayland-protocols/README.chromium
@@ -13,7 +13,7 @@ wayland-protocols. Modifications: -- None +- Added src/unstable/scaler/scaler.xml To import a new snapshot of wayland-protocols: - Checkout the latest release tag: git checkout 1.1 @@ -22,4 +22,7 @@ wayland-scanner code < src/*/xdg-shell/xdg-shell*.xml > protocol/xdg-shell-protocol.c wayland-scanner server-header < src/unstable/xdg-shell/xdg-shell-unstable-v5.xml > include/protocol/xdg-shell-unstable-v5-server-protocol.h wayland-scanner client-header < src/unstable/xdg-shell/xdg-shell-unstable-v5.xml > include/protocol/xdg-shell-unstable-v5-client-protocol.h + wayland-scanner code < src/*/scaler/scaler.xml > protocol/scaler-protocol.c + wayland-scanner server-header < src/unstable/scaler/scaler.xml > include/protocol/scaler-server-protocol.h + wayland-scanner client-header < src/unstable/scaler/scaler.xml > include/protocol/scaler-client-protocol.h - Update this README to reflect the new version number.
diff --git a/third_party/wayland-protocols/include/protocol/scaler-client-protocol.h b/third_party/wayland-protocols/include/protocol/scaler-client-protocol.h new file mode 100644 index 0000000..45941cf --- /dev/null +++ b/third_party/wayland-protocols/include/protocol/scaler-client-protocol.h
@@ -0,0 +1,144 @@ +/* + * Copyright © 2013-2014 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef SCALER_CLIENT_PROTOCOL_H +#define SCALER_CLIENT_PROTOCOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +struct wl_client; +struct wl_resource; + +struct wl_scaler; +struct wl_viewport; + +extern const struct wl_interface wl_scaler_interface; +extern const struct wl_interface wl_viewport_interface; + +#ifndef WL_SCALER_ERROR_ENUM +#define WL_SCALER_ERROR_ENUM +enum wl_scaler_error { + WL_SCALER_ERROR_VIEWPORT_EXISTS = 0, +}; +#endif /* WL_SCALER_ERROR_ENUM */ + +#define WL_SCALER_DESTROY 0 +#define WL_SCALER_GET_VIEWPORT 1 + +static inline void +wl_scaler_set_user_data(struct wl_scaler *wl_scaler, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wl_scaler, user_data); +} + +static inline void * +wl_scaler_get_user_data(struct wl_scaler *wl_scaler) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wl_scaler); +} + +static inline void +wl_scaler_destroy(struct wl_scaler *wl_scaler) +{ + wl_proxy_marshal((struct wl_proxy *) wl_scaler, + WL_SCALER_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) wl_scaler); +} + +static inline struct wl_viewport * +wl_scaler_get_viewport(struct wl_scaler *wl_scaler, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_scaler, + WL_SCALER_GET_VIEWPORT, &wl_viewport_interface, NULL, surface); + + return (struct wl_viewport *) id; +} + +#ifndef WL_VIEWPORT_ERROR_ENUM +#define WL_VIEWPORT_ERROR_ENUM +enum wl_viewport_error { + WL_VIEWPORT_ERROR_BAD_VALUE = 0, +}; +#endif /* WL_VIEWPORT_ERROR_ENUM */ + +#define WL_VIEWPORT_DESTROY 0 +#define WL_VIEWPORT_SET 1 +#define WL_VIEWPORT_SET_SOURCE 2 +#define WL_VIEWPORT_SET_DESTINATION 3 + +static inline void +wl_viewport_set_user_data(struct wl_viewport *wl_viewport, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wl_viewport, user_data); +} + +static inline void * +wl_viewport_get_user_data(struct wl_viewport *wl_viewport) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wl_viewport); +} + +static inline void +wl_viewport_destroy(struct wl_viewport *wl_viewport) +{ + wl_proxy_marshal((struct wl_proxy *) wl_viewport, + WL_VIEWPORT_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) wl_viewport); +} + +static inline void +wl_viewport_set(struct wl_viewport *wl_viewport, wl_fixed_t src_x, wl_fixed_t src_y, wl_fixed_t src_width, wl_fixed_t src_height, int32_t dst_width, int32_t dst_height) +{ + wl_proxy_marshal((struct wl_proxy *) wl_viewport, + WL_VIEWPORT_SET, src_x, src_y, src_width, src_height, dst_width, dst_height); +} + +static inline void +wl_viewport_set_source(struct wl_viewport *wl_viewport, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) +{ + wl_proxy_marshal((struct wl_proxy *) wl_viewport, + WL_VIEWPORT_SET_SOURCE, x, y, width, height); +} + +static inline void +wl_viewport_set_destination(struct wl_viewport *wl_viewport, int32_t width, int32_t height) +{ + wl_proxy_marshal((struct wl_proxy *) wl_viewport, + WL_VIEWPORT_SET_DESTINATION, width, height); +} + +#ifdef __cplusplus +} +#endif + +#endif
diff --git a/third_party/wayland-protocols/include/protocol/scaler-server-protocol.h b/third_party/wayland-protocols/include/protocol/scaler-server-protocol.h new file mode 100644 index 0000000..0684fb1a --- /dev/null +++ b/third_party/wayland-protocols/include/protocol/scaler-server-protocol.h
@@ -0,0 +1,254 @@ +/* + * Copyright © 2013-2014 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef SCALER_SERVER_PROTOCOL_H +#define SCALER_SERVER_PROTOCOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stddef.h> +#include "wayland-util.h" + +struct wl_client; +struct wl_resource; + +struct wl_scaler; +struct wl_viewport; + +extern const struct wl_interface wl_scaler_interface; +extern const struct wl_interface wl_viewport_interface; + +#ifndef WL_SCALER_ERROR_ENUM +#define WL_SCALER_ERROR_ENUM +enum wl_scaler_error { + WL_SCALER_ERROR_VIEWPORT_EXISTS = 0, +}; +#endif /* WL_SCALER_ERROR_ENUM */ + +/** + * wl_scaler - surface cropping and scaling + * @destroy: unbind from the cropping and scaling interface + * @get_viewport: extend surface interface for crop and scale + * + * The global interface exposing surface cropping and scaling + * capabilities is used to instantiate an interface extension for a + * wl_surface object. This extended interface will then allow cropping and + * scaling the surface contents, effectively disconnecting the direct + * relationship between the buffer and the surface size. + */ +struct wl_scaler_interface { + /** + * destroy - unbind from the cropping and scaling interface + * + * Informs the server that the client will not be using this + * protocol object anymore. This does not affect any other objects, + * wl_viewport objects included. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * get_viewport - extend surface interface for crop and scale + * @id: the new viewport interface id + * @surface: the surface + * + * Instantiate an interface extension for the given wl_surface to + * crop and scale its content. If the given wl_surface already has + * a wl_viewport object associated, the viewport_exists protocol + * error is raised. + */ + void (*get_viewport)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface); +}; + +#ifndef WL_VIEWPORT_ERROR_ENUM +#define WL_VIEWPORT_ERROR_ENUM +enum wl_viewport_error { + WL_VIEWPORT_ERROR_BAD_VALUE = 0, +}; +#endif /* WL_VIEWPORT_ERROR_ENUM */ + +/** + * wl_viewport - crop and scale interface to a wl_surface + * @destroy: remove scaling and cropping from the surface + * @set: set the crop and scale state + * @set_source: set the source rectangle for cropping + * @set_destination: set the surface size for scaling + * + * An additional interface to a wl_surface object, which allows the + * client to specify the cropping and scaling of the surface contents. + * + * This interface allows to define the source rectangle (src_x, src_y, + * src_width, src_height) from where to take the wl_buffer contents, and + * scale that to destination size (dst_width, dst_height). This state is + * double-buffered, and is applied on the next wl_surface.commit. + * + * The two parts of crop and scale state are independent: the source + * rectangle, and the destination size. Initially both are unset, that is, + * no scaling is applied. The whole of the current wl_buffer is used as the + * source, and the surface size is as defined in wl_surface.attach. + * + * If the destination size is set, it causes the surface size to become + * dst_width, dst_height. The source (rectangle) is scaled to exactly this + * size. This overrides whatever the attached wl_buffer size is, unless the + * wl_buffer is NULL. If the wl_buffer is NULL, the surface has no content + * and therefore no size. Otherwise, the size is always at least 1x1 in + * surface coordinates. + * + * If the source rectangle is set, it defines what area of the wl_buffer is + * taken as the source. If the source rectangle is set and the destination + * size is not set, the surface size becomes the source rectangle size + * rounded up to the nearest integer. If the source size is already exactly + * integers, this results in cropping without scaling. + * + * The coordinate transformations from buffer pixel coordinates up to the + * surface-local coordinates happen in the following order: 1. + * buffer_transform (wl_surface.set_buffer_transform) 2. buffer_scale + * (wl_surface.set_buffer_scale) 3. crop and scale (wl_viewport.set*) This + * means, that the source rectangle coordinates of crop and scale are given + * in the coordinates after the buffer transform and scale, i.e. in the + * coordinates that would be the surface-local coordinates if the crop and + * scale was not applied. + * + * If the source rectangle is partially or completely outside of the + * wl_buffer, then the surface contents are undefined (not void), and the + * surface size is still dst_width, dst_height. + * + * The x, y arguments of wl_surface.attach are applied as normal to the + * surface. They indicate how many pixels to remove from the surface size + * from the left and the top. In other words, they are still in the + * surface-local coordinate system, just like dst_width and dst_height are. + * + * If the wl_surface associated with the wl_viewport is destroyed, the + * wl_viewport object becomes inert. + * + * If the wl_viewport object is destroyed, the crop and scale state is + * removed from the wl_surface. The change will be applied on the next + * wl_surface.commit. + */ +struct wl_viewport_interface { + /** + * destroy - remove scaling and cropping from the surface + * + * The associated wl_surface's crop and scale state is removed. + * The change is applied on the next wl_surface.commit. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * set - set the crop and scale state + * @src_x: source rectangle x + * @src_y: source rectangle y + * @src_width: source rectangle width + * @src_height: source rectangle height + * @dst_width: surface width + * @dst_height: surface height + * + * Set both source rectangle and destination size of the + * associated wl_surface. See wl_viewport for the description, and + * relation to the wl_buffer size. + * + * The bad_value protocol error is raised if src_width or + * src_height is negative, or if dst_width or dst_height is not + * positive. + * + * The crop and scale state is double-buffered state, and will be + * applied on the next wl_surface.commit. + * + * Arguments dst_x and dst_y do not exist here, use the x and y + * arguments to wl_surface.attach. The x, y, dst_width, and + * dst_height define the surface-local coordinate system + * irrespective of the attached wl_buffer size. + */ + void (*set)(struct wl_client *client, + struct wl_resource *resource, + wl_fixed_t src_x, + wl_fixed_t src_y, + wl_fixed_t src_width, + wl_fixed_t src_height, + int32_t dst_width, + int32_t dst_height); + /** + * set_source - set the source rectangle for cropping + * @x: source rectangle x + * @y: source rectangle y + * @width: source rectangle width + * @height: source rectangle height + * + * Set the source rectangle of the associated wl_surface. See + * wl_viewport for the description, and relation to the wl_buffer + * size. + * + * If width is -1.0 and height is -1.0, the source rectangle is + * unset instead. Any other pair of values for width and height + * that contains zero or negative values raises the bad_value + * protocol error. + * + * The crop and scale state is double-buffered state, and will be + * applied on the next wl_surface.commit. + * @since: 2 + */ + void (*set_source)(struct wl_client *client, + struct wl_resource *resource, + wl_fixed_t x, + wl_fixed_t y, + wl_fixed_t width, + wl_fixed_t height); + /** + * set_destination - set the surface size for scaling + * @width: surface width + * @height: surface height + * + * Set the destination size of the associated wl_surface. See + * wl_viewport for the description, and relation to the wl_buffer + * size. + * + * If width is -1 and height is -1, the destination size is unset + * instead. Any other pair of values for width and height that + * contains zero or negative values raises the bad_value protocol + * error. + * + * The crop and scale state is double-buffered state, and will be + * applied on the next wl_surface.commit. + * + * Arguments x and y do not exist here, use the x and y arguments + * to wl_surface.attach. The x, y, width, and height define the + * surface-local coordinate system irrespective of the attached + * wl_buffer size. + * @since: 2 + */ + void (*set_destination)(struct wl_client *client, + struct wl_resource *resource, + int32_t width, + int32_t height); +}; + +#ifdef __cplusplus +} +#endif + +#endif
diff --git a/third_party/wayland-protocols/protocol/scaler-protocol.c b/third_party/wayland-protocols/protocol/scaler-protocol.c new file mode 100644 index 0000000..54473261 --- /dev/null +++ b/third_party/wayland-protocols/protocol/scaler-protocol.c
@@ -0,0 +1,65 @@ +/* + * Copyright © 2013-2014 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +extern const struct wl_interface wl_viewport_interface; +extern const struct wl_interface wl_surface_interface; + +static const struct wl_interface *types[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &wl_viewport_interface, + &wl_surface_interface, +}; + +static const struct wl_message wl_scaler_requests[] = { + { "destroy", "", types + 0 }, + { "get_viewport", "no", types + 6 }, +}; + +WL_EXPORT const struct wl_interface wl_scaler_interface = { + "wl_scaler", 2, + 2, wl_scaler_requests, + 0, NULL, +}; + +static const struct wl_message wl_viewport_requests[] = { + { "destroy", "", types + 0 }, + { "set", "ffffii", types + 0 }, + { "set_source", "2ffff", types + 0 }, + { "set_destination", "2ii", types + 0 }, +}; + +WL_EXPORT const struct wl_interface wl_viewport_interface = { + "wl_viewport", 2, + 4, wl_viewport_requests, + 0, NULL, +}; +
diff --git a/third_party/wayland-protocols/wayland-protocols.gyp b/third_party/wayland-protocols/wayland-protocols.gyp index ddde484..8c765e3 100644 --- a/third_party/wayland-protocols/wayland-protocols.gyp +++ b/third_party/wayland-protocols/wayland-protocols.gyp
@@ -24,5 +24,25 @@ ], }, }, + { + 'target_name': 'scaler_protocol', + 'type': 'static_library', + 'dependencies' : [ + '../wayland/wayland.gyp:wayland_util', + ], + 'sources': [ + 'include/protocol/scaler-client-protocol.h', + 'include/protocol/scaler-server-protocol.h', + 'protocol/scaler-protocol.c', + ], + 'include_dirs': [ + 'include/protocol', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'include/protocol', + ], + }, + }, ], }
diff --git a/tools/android/loading/activity_lens.py b/tools/android/loading/activity_lens.py index 257fdc3..c99d47a9 100644 --- a/tools/android/loading/activity_lens.py +++ b/tools/android/loading/activity_lens.py
@@ -17,6 +17,9 @@ class ActivityLens(object): """Reconstructs the activity of the main renderer thread between requests.""" + _SCRIPT_EVENT_NAMES = ('EvaluateScript', 'FunctionCall') + _PARSING_EVENT_NAMES = ('ParseHTML', 'ParseAuthorStyleSheet') + def __init__(self, trace): """Initializes an instance of ActivityLens. @@ -103,8 +106,8 @@ script_to_duration = collections.defaultdict(float) script_events = [e for e in events if ('devtools.timeline' in e.tracing_event['cat'] - and e.tracing_event['name'] in ( - 'EvaluateScript', 'FunctionCall'))] + and (e.tracing_event['name'] + in cls._SCRIPT_EVENT_NAMES))] for event in script_events: clamped_duration = cls._ClampedDuration(event, start_msec, end_msec) script_url = event.args['data'].get('scriptName', None) @@ -112,6 +115,16 @@ return dict(script_to_duration) @classmethod + def _FullyIncludedEvents(cls, events, event): + """Return a list of events wholly included in the |event| span.""" + (start, end) = (event.start_msec, event.end_msec) + result = [] + for event in events: + if start <= event.start_msec < end and start <= event.end_msec < end: + result.append(event) + return result + + @classmethod def _Parsing(cls, events, start_msec, end_msec): """Returns the HTML/CSS parsing time within an interval. @@ -127,16 +140,25 @@ url_to_duration = collections.defaultdict(float) parsing_events = [e for e in events if ('devtools.timeline' in e.tracing_event['cat'] - and e.tracing_event['name'] in ( - 'ParseHTML', 'ParseAuthorStyleSheet'))] + and (e.tracing_event['name'] + in cls._PARSING_EVENT_NAMES))] for event in parsing_events: + # Parsing events can contain nested script execution events, avoid + # double-counting by discounting these. + nested_events = cls._FullyIncludedEvents(events, event) + events_tree = _EventsTree(event, nested_events) + js_events = events_tree.DominatingEventsWithNames(cls._SCRIPT_EVENT_NAMES) + duration_to_subtract = sum( + cls._ClampedDuration(e, start_msec, end_msec) for e in js_events) tracing_event = event.tracing_event clamped_duration = cls._ClampedDuration(event, start_msec, end_msec) if tracing_event['name'] == 'ParseAuthorStyleSheet': url = tracing_event['args']['data']['styleSheetUrl'] else: url = tracing_event['args']['beginData']['url'] - url_to_duration[url] += clamped_duration + parsing_duration = clamped_duration - duration_to_subtract + assert parsing_duration >= 0 + url_to_duration[url] += parsing_duration return dict(url_to_duration) def GenerateEdgeActivity(self, dep): @@ -170,28 +192,69 @@ RequestDependencyLens.GetRequestDependencies(). Returns: - {'script': float, 'parsing': float, 'other': float, 'unknown': float} + {'script': float, 'parsing': float, 'other_url': float, + 'unknown_url': float, 'unrelated_work': float} where the values are durations in ms: + - idle: The renderer main thread was idle. - script: The initiating file was executing. - parsing: The initiating file was being parsed. - - other: Other scripts and/or parsing activities. - - unknown: Activity which is not associated with a URL. + - other_url: Other scripts and/or parsing activities. + - unknown_url: Activity which is not associated with a URL. + - unrelated_work: Activity unrelated to scripts or parsing. """ activity = self.GenerateEdgeActivity(dep) - related = {'script': 0, 'parsing': 0, 'other_url': 0, 'unknown_url': 0} + breakdown = {'unrelated_work': activity['busy'], + 'idle': activity['edge_cost'] - activity['busy'], + 'script': 0, 'parsing': 0, + 'other_url': 0, 'unknown_url': 0} for kind in ('script', 'parsing'): for (script_name, duration_ms) in activity[kind].items(): if not script_name: - related['unknown_url'] += duration_ms + breakdown['unknown_url'] += duration_ms elif script_name == dep[0].url: - related[kind] += duration_ms + breakdown[kind] += duration_ms else: - # A lot of "ParseHTML" tasks are mostly about executing - # scripts. Don't double-count. - # TODO(lizeb): Better handle TraceEvents nesting. - if kind == 'script': - related['other_url'] += duration_ms - return related + breakdown['other_url'] += duration_ms + breakdown['unrelated_work'] -= sum( + breakdown[x] for x in ('script', 'parsing', 'other_url', 'unknown_url')) + return breakdown + + +class _EventsTree(object): + """Builds the hierarchy of events from a list of fully nested events.""" + def __init__(self, root_event, events): + """Creates the tree. + + Args: + root_event: (Event) Event held by the tree root. + events: ([Event]) List of events that are fully included in |root_event|. + """ + self.event = root_event + self.start_msec = root_event.start_msec + self.end_msec = root_event.end_msec + self.children = [] + events.sort(key=operator.attrgetter('start_msec')) + if not events: + return + current_child = (events[0], []) + for event in events[1:]: + if event.end_msec < current_child[0].end_msec: + current_child[1].append(event) + else: + self.children.append(_EventsTree(current_child[0], current_child[1])) + current_child = (event, []) + self.children.append(_EventsTree(current_child[0], current_child[1])) + + def DominatingEventsWithNames(self, names): + """Returns a list of the top-most events in the tree with a matching name. + """ + if self.event.name in names: + return [self.event] + else: + result = [] + for child in self.children: + result += child.DominatingEventsWithNames(names) + return result if __name__ == '__main__':
diff --git a/tools/android/loading/activity_lens_unittest.py b/tools/android/loading/activity_lens_unittest.py index f96bbd4..5abc172 100644 --- a/tools/android/loading/activity_lens_unittest.py +++ b/tools/android/loading/activity_lens_unittest.py
@@ -2,9 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import collections +import copy import unittest -from activity_lens import ActivityLens +from activity_lens import (ActivityLens, _EventsTree) import test_utils import tracing @@ -202,23 +204,33 @@ u'ph': u'X', u'pid': 1, u'tid': 1, + u'ts': 0}, + {u'cat': u'toplevel', + u'dur': 100 * 1000, + u'name': u'MessageLoop::RunTask', + u'ph': u'X', + u'pid': 1, + u'tid': 1, u'ts': 0}] activity = self._ActivityLens(requests, raw_events) dep = (requests[0], requests[1], 'parser') self.assertEquals( - {'script': 0, 'parsing': 12, 'other_url': 0, 'unknown_url': 0}, + {'unrelated_work': 18, 'idle': 0, 'script': 0, 'parsing': 12, + 'other_url': 0, 'unknown_url': 0}, activity.BreakdownEdgeActivityByInitiator(dep)) dep = (requests[0], requests[1], 'other') - # Truncating the event from the parent xrequest end. + # Truncating the event from the parent request end. self.assertEquals( - {'script': 0, 'parsing': 7, 'other_url': 0, 'unknown_url': 0}, + {'unrelated_work': 13, 'idle': 0, 'script': 0, 'parsing': 7, + 'other_url': 0, 'unknown_url': 0}, activity.BreakdownEdgeActivityByInitiator(dep)) # Unknown URL raw_events[0]['args']['beginData']['url'] = None activity = self._ActivityLens(requests, raw_events) dep = (requests[0], requests[1], 'parser') self.assertEquals( - {'script': 0, 'parsing': 0, 'other_url': 0, 'unknown_url': 12}, + {'unrelated_work': 18, 'idle': 0, 'script': 0, 'parsing': 0, + 'other_url': 0, 'unknown_url': 12}, activity.BreakdownEdgeActivityByInitiator(dep)) # Script raw_events[1]['ts'] = 40 * 1000 @@ -226,13 +238,15 @@ activity = self._ActivityLens(requests, raw_events) dep = (requests[0], requests[1], 'script') self.assertEquals( - {'script': 6, 'parsing': 0, 'other_url': 0, 'unknown_url': 7}, + {'unrelated_work': 7, 'idle': 0, 'script': 6, 'parsing': 0, + 'other_url': 0, 'unknown_url': 7}, activity.BreakdownEdgeActivityByInitiator(dep)) # Other URL raw_events[1]['args']['data']['scriptName'] = 'http://other.com/url' activity = self._ActivityLens(requests, raw_events) self.assertEquals( - {'script': 0., 'parsing': 0., 'other_url': 6., 'unknown_url': 7.}, + {'unrelated_work': 7, 'idle': 0, 'script': 0., 'parsing': 0., + 'other_url': 6., 'unknown_url': 7.}, activity.BreakdownEdgeActivityByInitiator(dep)) def _ActivityLens(self, requests, raw_events): @@ -241,5 +255,37 @@ return ActivityLens(loading_trace) +class EventsTreeTestCase(unittest.TestCase): + FakeEvent = collections.namedtuple( + 'FakeEvent', ('name', 'start_msec', 'end_msec')) + _ROOT_EVENT = FakeEvent('-1', 0, 20) + _EVENTS = [ + FakeEvent('0', 2, 4), FakeEvent('1', 1, 5), + FakeEvent('2', 6, 9), + FakeEvent('3', 13, 14), FakeEvent('4', 14, 17), FakeEvent('5', 12, 18)] + + def setUp(self): + self.tree = _EventsTree(self._ROOT_EVENT, copy.deepcopy(self._EVENTS)) + + def testEventsTreeConstruction(self): + self.assertEquals(self._ROOT_EVENT, self.tree.event) + self.assertEquals(3, len(self.tree.children)) + self.assertEquals(self._EVENTS[1], self.tree.children[0].event) + self.assertEquals(self._EVENTS[0], self.tree.children[0].children[0].event) + self.assertEquals(self._EVENTS[2], self.tree.children[1].event) + self.assertEquals([], self.tree.children[1].children) + self.assertEquals(self._EVENTS[5], self.tree.children[2].event) + self.assertEquals(2, len(self.tree.children[2].children)) + + def testDominatingEventsWithNames(self): + self.assertListEqual( + [self._ROOT_EVENT], self.tree.DominatingEventsWithNames(('-1'))) + self.assertListEqual( + [self._ROOT_EVENT], self.tree.DominatingEventsWithNames(('-1', '0'))) + self.assertListEqual( + [self._EVENTS[1], self._EVENTS[5]], + self.tree.DominatingEventsWithNames(('1', '5'))) + + if __name__ == '__main__': unittest.main()
diff --git a/tools/android/loading/loading_model.py b/tools/android/loading/loading_model.py index 5deec03..f9905a05 100644 --- a/tools/android/loading/loading_model.py +++ b/tools/android/loading/loading_model.py
@@ -31,6 +31,10 @@ See model parameters in Set(). """ + # The lens to build request dependencies. Exposed here for subclasses in + # unittesting. + REQUEST_LENS = request_dependencies_lens.RequestDependencyLens + EDGE_KIND_KEY = 'edge_kind' EDGE_KINDS = request_track.Request.INITIATORS + ( 'script_inferred', 'after-load', 'before-load', 'timing') @@ -477,8 +481,7 @@ self._nodes.append(node) self._node_info.append(node_info) - dependencies = request_dependencies_lens.RequestDependencyLens( - trace).GetRequestDependencies() + dependencies = self.REQUEST_LENS(trace).GetRequestDependencies() for dep in dependencies: (parent_rq, child_rq, reason) = dep parent = self._node_info[index_by_request[parent_rq]]
diff --git a/tools/android/loading/loading_model_unittest.py b/tools/android/loading/loading_model_unittest.py index 44dcbae3..ee5e84d 100644 --- a/tools/android/loading/loading_model_unittest.py +++ b/tools/android/loading/loading_model_unittest.py
@@ -13,36 +13,7 @@ import test_utils -class SimpleLens(object): - def __init__(self, trace): - self._trace = trace - - def GetRequestDependencies(self): - url_to_rq = {} - deps = [] - for rq in self._trace.request_track.GetEvents(): - assert rq.url not in url_to_rq - url_to_rq[rq.url] = rq - for rq in self._trace.request_track.GetEvents(): - initiating_url = rq.initiator['url'] - if initiating_url in url_to_rq: - deps.append((url_to_rq[initiating_url], rq, rq.initiator['type'])) - return deps - - class LoadingModelTestCase(unittest.TestCase): - - def setUp(self): - self.old_lens = request_dependencies_lens.RequestDependencyLens - request_dependencies_lens.RequestDependencyLens = SimpleLens - - def tearDown(self): - request_dependencies_lens.RequestDependencyLens = self.old_lens - - def MakeGraph(self, requests): - return loading_model.ResourceGraph( - test_utils.LoadingTraceFromEvents(requests)) - def SortedIndicies(self, graph): return [n.Index() for n in dag.TopologicalSort(graph._nodes)] @@ -50,7 +21,7 @@ return [c.Index() for c in node.SortedSuccessors()] def test_DictConstruction(self): - graph = loading_model.ResourceGraph( + graph = test_utils.TestResourceGraph( {'request_track': { 'events': [ test_utils.MakeRequest(0, 'null', 100, 100.5, 101).ToJsonDict(), @@ -77,7 +48,7 @@ test_utils.MakeRequest(4, 3, 127, 127.5, 128), test_utils.MakeRequest(5, 'null', 100, 103, 105), test_utils.MakeRequest(6, 5, 105, 107, 110)] - graph = self.MakeGraph(requests) + graph = test_utils.TestResourceGraph.FromRequestList(requests) self.assertEqual(self.SuccessorIndicies(graph._nodes[0]), [1, 2]) self.assertEqual(self.SuccessorIndicies(graph._nodes[1]), [3]) self.assertEqual(self.SuccessorIndicies(graph._nodes[2]), []) @@ -98,7 +69,7 @@ test_utils.MakeRequest(4, 3, 127, 128, 129), test_utils.MakeRequest(5, 'null', 100, 105, 106), test_utils.MakeRequest(6, 5, 105, 110, 111)] - graph = self.MakeGraph(requests) + graph = test_utils.TestResourceGraph.FromRequestList(requests) path_list = [] self.assertEqual(29, graph.Cost(path_list)) self.assertEqual([0, 1, 3, 4], [n.Index() for n in path_list]) @@ -119,7 +90,7 @@ magic_content_type=True), test_utils.MakeRequest(4, 2, 122, 126, 126), test_utils.MakeRequest(5, 2, 122, 126, 126)] - graph = self.MakeGraph(requests) + graph = test_utils.TestResourceGraph.FromRequestList(requests) self.assertEqual(self.SuccessorIndicies(graph._nodes[0]), [1, 3]) self.assertEqual(self.SuccessorIndicies(graph._nodes[1]), [2]) self.assertEqual(self.SuccessorIndicies(graph._nodes[2]), [4, 5]) @@ -131,7 +102,7 @@ # Change node 1 so it is a parent of 3, which becomes the parent of 2. requests[1] = test_utils.MakeRequest( 1, 0, 110, 111, 111, magic_content_type=True) - graph = self.MakeGraph(requests) + graph = test_utils.TestResourceGraph.FromRequestList(requests) self.assertEqual(self.SuccessorIndicies(graph._nodes[0]), [1]) self.assertEqual(self.SuccessorIndicies(graph._nodes[1]), [3]) self.assertEqual(self.SuccessorIndicies(graph._nodes[2]), [4, 5]) @@ -144,12 +115,12 @@ requests[1] = test_utils.MakeRequest( 1, 0, 110, 111, 111, magic_content_type=True) requests.append(test_utils.MakeRequest(6, 1, 111, 112, 112)) - graph = self.MakeGraph(requests) + graph = test_utils.TestResourceGraph.FromRequestList(requests) # Check it doesn't change until we change the content type of 6. self.assertEqual(self.SuccessorIndicies(graph._nodes[6]), []) requests[6] = test_utils.MakeRequest(6, 1, 111, 112, 112, magic_content_type=True) - graph = self.MakeGraph(requests) + graph = test_utils.TestResourceGraph.FromRequestList(requests) self.assertEqual(self.SuccessorIndicies(graph._nodes[0]), [1]) self.assertEqual(self.SuccessorIndicies(graph._nodes[1]), [6]) self.assertEqual(self.SuccessorIndicies(graph._nodes[2]), [4, 5]) @@ -169,7 +140,7 @@ test_utils.MakeRequest(5, 2, 122, 126, 126)] for r in requests: r.response_headers['Content-Type'] = 'image/gif' - graph = self.MakeGraph(requests) + graph = test_utils.TestResourceGraph.FromRequestList(requests) self.assertEqual(self.SuccessorIndicies(graph._nodes[0]), [1, 2, 3]) self.assertEqual(self.SuccessorIndicies(graph._nodes[1]), []) self.assertEqual(self.SuccessorIndicies(graph._nodes[2]), [4, 5])
diff --git a/tools/android/loading/model_graph.py b/tools/android/loading/model_graph.py index 591bd7f..eaded4aa 100644 --- a/tools/android/loading/model_graph.py +++ b/tools/android/loading/model_graph.py
@@ -37,6 +37,8 @@ 'gif': 'grey', 'image': 'orange', 'jpeg': 'orange', + 'ping': 'cyan', # Empty response + 'redirect': 'forestgreen', 'png': 'orange', 'plain': 'brown3', 'octet-stream': 'brown3', @@ -54,8 +56,8 @@ } _ACTIVITY_TYPE_LABEL = ( - ('script', 'S'), ('parsing', 'P'), ('other_url', 'O'), - ('unknown_url', 'U')) + ('idle', 'I'), ('unrelated_work', 'W'), ('script', 'S'), + ('parsing', 'P'), ('other_url', 'O'), ('unknown_url', 'U')) def __init__(self, graph): """Initialize.
diff --git a/tools/android/loading/request_track.py b/tools/android/loading/request_track.py index 2c94703..685ab0d 100644 --- a/tools/android/loading/request_track.py +++ b/tools/android/loading/request_track.py
@@ -179,19 +179,36 @@ result.timing = TimingFromDict({'requestTime': result.timestamp}) return result + def GetHTTPResponseHeader(self, header_name): + """Gets the value of a HTTP response header. + + Does a case-insensitive search for the header name in the HTTP response + headers, in order to support servers that use a wrong capitalization. + """ + lower_case_name = header_name.lower() + result = None + for name, value in self.response_headers.iteritems(): + if name.lower() == lower_case_name: + result = value + break + return result + def GetContentType(self): """Returns the content type, or None.""" + # Check for redirects. Use the "Location" header, because the HTTP status is + # not reliable. + if self.GetHTTPResponseHeader('Location') is not None: + return 'redirect' + + # Check if the response is empty. + if (self.GetHTTPResponseHeader('Content-Length') == '0' or + self.status == 204): + return 'ping' + if self.mime_type: return self.mime_type - # Case-insensitive search because servers sometimes use a wrong - # capitalization. - content_type = None - for header, value in self.response_headers.iteritems(): - if header.lower() == 'content-type': - content_type = value - break - + content_type = self.GetHTTPResponseHeader('Content-Type') if not content_type or ';' not in content_type: return content_type else: @@ -207,14 +224,7 @@ if not self.response_headers: return -1 - # Case-insensitive search because servers sometimes use a wrong - # capitalization. - cache_control_str = None - for header, value in self.response_headers.iteritems(): - if header.lower() == 'cache-control': - cache_control_str = value - break - + cache_control_str = self.GetHTTPResponseHeader('Cache-Control') if cache_control_str is not None: directives = [s.strip() for s in cache_control_str.split(',')] for directive in directives: @@ -481,12 +491,14 @@ # network stack. ('requestHeaders', 'request_headers'), ('headers', 'response_headers'))) - # data URLs don't have a timing dict. timing_dict = {} - if r.protocol != 'data': - timing_dict = response['timing'] - else: + # data URLs don't have a timing dict, and timings for cached requests are + # stale. + # TODO(droger): the timestamp is inacurate, get the real timings instead. + if r.protocol == 'data' or r.served_from_cache: timing_dict = {'requestTime': r.timestamp} + else: + timing_dict = response['timing'] r.timing = TimingFromDict(timing_dict) self._requests_in_flight[request_id] = (r, RequestTrack._STATUS_RESPONSE) self._request_id_to_response_received[request_id] = params
diff --git a/tools/android/loading/request_track_unittest.py b/tools/android/loading/request_track_unittest.py index 7a85f07..6e54208 100644 --- a/tools/android/loading/request_track_unittest.py +++ b/tools/android/loading/request_track_unittest.py
@@ -47,10 +47,31 @@ # Parameters are filtered out. r.response_headers = {'Content-Type': 'application/javascript;bla'} self.assertEquals('application/javascript', r.GetContentType()) - # MIME type takes precedence over headers. + # MIME type takes precedence over 'Content-Type' header. r.mime_type = 'image/webp' self.assertEquals('image/webp', r.GetContentType()) r.mime_type = None + # Test for 'ping' type. + r.status = 204 + self.assertEquals('ping', r.GetContentType()) + r.status = None + r.response_headers = {'Content-Type': 'application/javascript', + 'content-length': '0'} + self.assertEquals('ping', r.GetContentType()) + # Test for 'redirect' type. + r.response_headers = {'Content-Type': 'application/javascript', + 'location': 'http://foo', + 'content-length': '0'} + self.assertEquals('redirect', r.GetContentType()) + + def testGetHTTPResponseHeader(self): + r = Request() + r.response_headers = {} + self.assertEquals(None, r.GetHTTPResponseHeader('Foo')) + r.response_headers = {'Foo': 'Bar', 'Baz': 'Foo'} + self.assertEquals('Bar', r.GetHTTPResponseHeader('Foo')) + r.response_headers = {'foo': 'Bar', 'Baz': 'Foo'} + self.assertEquals('Bar', r.GetHTTPResponseHeader('Foo')) class RequestTrackTestCase(unittest.TestCase):
diff --git a/tools/android/loading/test_utils.py b/tools/android/loading/test_utils.py index 654e94bd..aea73a7 100644 --- a/tools/android/loading/test_utils.py +++ b/tools/android/loading/test_utils.py
@@ -5,6 +5,7 @@ """Common utilities used in unit tests, within this directory.""" import devtools_monitor +import loading_model import loading_trace import page_track import request_track @@ -80,3 +81,33 @@ tracing_track = None return loading_trace.LoadingTrace( None, None, page_event_track, request, tracing_track) + + +class SimpleLens(object): + """A simple replacement for RequestDependencyLens. + + Uses only the initiator url of a request for determining a dependency. + """ + def __init__(self, trace): + self._trace = trace + + def GetRequestDependencies(self): + url_to_rq = {} + deps = [] + for rq in self._trace.request_track.GetEvents(): + assert rq.url not in url_to_rq + url_to_rq[rq.url] = rq + for rq in self._trace.request_track.GetEvents(): + initiating_url = rq.initiator['url'] + if initiating_url in url_to_rq: + deps.append((url_to_rq[initiating_url], rq, rq.initiator['type'])) + return deps + + +class TestResourceGraph(loading_model.ResourceGraph): + """Replace the default request lens in a ResourceGraph with our SimpleLens.""" + REQUEST_LENS = SimpleLens + + @classmethod + def FromRequestList(cls, requests, page_events=None, trace_events=None): + return cls(LoadingTraceFromEvents(requests, page_events, trace_events))
diff --git a/tools/android/loading/trace_test/README.md b/tools/android/loading/trace_test/README.md new file mode 100644 index 0000000..c16e472 --- /dev/null +++ b/tools/android/loading/trace_test/README.md
@@ -0,0 +1,8 @@ +Trace Integration Tests + +This directory defines integration tests which verify traces in various corners +of the HTML/JS/CSS world. + +The unittests in this directory are run as part of +tools/android/loading/run_tests. The integration tests are only run +manually. See webserver_test.py for details.
diff --git a/tools/android/loading/trace_test/webserver_test.py b/tools/android/loading/trace_test/webserver_test.py index f6724c0..2b413a8 100755 --- a/tools/android/loading/trace_test/webserver_test.py +++ b/tools/android/loading/trace_test/webserver_test.py
@@ -10,11 +10,23 @@ how you have chrome set up). When debugging or adding tests, setting --failed_trace_dir could be useful. -Spawns a local http server to serve web pages. The trace generated by each -file in tests/*.html will be compared with the corresponding results/*.result. +The integration test spawns a local http server to serve web pages. The trace +generated by each file in tests/*.html will be compared with the corresponding +results/*.result. Each test should have a detailed comment explaining its +organization and what the important part of the test result is. By default this will use a release version of chrome built in this same code tree (out/Release/chrome), see --local_binary to override. + +See InitiatorSequence for what the integration tests verify. The idea is to +capture a sketch of the initiator and call stack relationship. The output is +human-readable. To create a new test, first run test_server.py locally with +--source_dir pointing to tests/, and verify that the test page works as expected +by pointing a browser to localhost:XXX/your_new_test.html (with XXX the port +reported in the console output of test_server.py). Then run this +webserver_test.py with --failed_trace_dir set. Verify that the actual output is +what you expect it to be, then copy it to results/. If your test is 7.html, you +should copy to results/7.result. """ import argparse
diff --git a/tools/android/loading/trace_test/webserver_unittest.py b/tools/android/loading/trace_test/webserver_unittest.py index 2716492..2daa2710 100644 --- a/tools/android/loading/trace_test/webserver_unittest.py +++ b/tools/android/loading/trace_test/webserver_unittest.py
@@ -8,7 +8,7 @@ import unittest _SRC_DIR = os.path.abspath(os.path.join( - os.path.dirname(__file__), '..', '..', '..')) + os.path.dirname(__file__), '..', '..', '..', '..')) sys.path.append(os.path.join(_SRC_DIR, 'tools', 'android', 'loading')) import options @@ -43,8 +43,11 @@ sock.connect((host, int(port))) sock.sendall('GET test.html HTTP/1.1\n\n') data = sock.recv(4096) - print '%%% ' + data self.assertTrue('HTTP/1.0 200 OK' in data) sock.close() self.assertTrue(server.Stop()) + + +if __name__ == '__main__': + unittest.main()
diff --git a/tools/android/loading/tracing.py b/tools/android/loading/tracing.py index 70710117..cf799086 100644 --- a/tools/android/loading/tracing.py +++ b/tools/android/loading/tracing.py
@@ -312,6 +312,10 @@ return self._tracing_event.get('id') @property + def name(self): + return self._tracing_event['name'] + + @property def tracing_event(self): return self._tracing_event @@ -413,6 +417,7 @@ return _IntervalTree(start, end, filtered_events) def OverlappingEvents(self, start, end): + """Returns a set of events overlapping with [start, end).""" if min(end, self.end) - max(start, self.start) <= 0: return set() elif self._IsLeaf():
diff --git a/tools/clang/plugins/ChromeClassTester.cpp b/tools/clang/plugins/ChromeClassTester.cpp index 2b22ed95..5383a91 100644 --- a/tools/clang/plugins/ChromeClassTester.cpp +++ b/tools/clang/plugins/ChromeClassTester.cpp
@@ -224,15 +224,9 @@ banned_directories_.emplace("/breakpad/"); banned_directories_.emplace("/courgette/"); banned_directories_.emplace("/ppapi/"); - banned_directories_.emplace("/usr/include/"); - banned_directories_.emplace("/usr/lib/"); - banned_directories_.emplace("/usr/local/include/"); - banned_directories_.emplace("/usr/local/lib/"); banned_directories_.emplace("/testing/"); banned_directories_.emplace("/v8/"); - banned_directories_.emplace("/dart/"); banned_directories_.emplace("/sdch/"); - banned_directories_.emplace("/icu4c/"); banned_directories_.emplace("/frameworks/"); // Don't check autogenerated headers.
diff --git a/tools/clang/scripts/package.py b/tools/clang/scripts/package.py index 9371110..944acee 100755 --- a/tools/clang/scripts/package.py +++ b/tools/clang/scripts/package.py
@@ -17,6 +17,7 @@ # Path constants. THIS_DIR = os.path.dirname(__file__) +CHROMIUM_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..', '..')) THIRD_PARTY_DIR = os.path.join(THIS_DIR, '..', '..', '..', 'third_party') LLVM_DIR = os.path.join(THIRD_PARTY_DIR, 'llvm') LLVM_BOOTSTRAP_DIR = os.path.join(THIRD_PARTY_DIR, 'llvm-bootstrap') @@ -57,6 +58,57 @@ return tarinfo +def GetExpectedStamp(): + rev_cmd = [sys.executable, os.path.join(THIS_DIR, 'update.py'), + '--print-revision'] + return subprocess.check_output(rev_cmd).rstrip() + + +def GetGsutilPath(): + if not 'find_depot_tools' in sys.modules: + sys.path.insert(0, os.path.join(CHROMIUM_DIR, 'build')) + global find_depot_tools + import find_depot_tools + depot_path = find_depot_tools.add_depot_tools_to_path() + if depot_path is None: + print ('depot_tools are not found in PATH. ' + 'Follow the instructions in this document ' + 'http://dev.chromium.org/developers/how-tos/install-depot-tools' + ' to install depot_tools and then try again.') + sys.exit(1) + gsutil_path = os.path.join(depot_path, 'gsutil.py') + return gsutil_path + + +def RunGsutil(args): + return subprocess.call([sys.executable, GetGsutilPath()] + args) + + +def GsutilArchiveExists(archive_name, platform): + gsutil_args = ['-q', 'stat', + 'gs://chromium-browser-clang/%s/%s.tgz' % + (platform, archive_name)] + return RunGsutil(gsutil_args) == 0 + + +def MaybeUpload(args, archive_name, platform): + # We don't want to rewrite the file, if it already exists on the server, + # so -n option to gsutil is used. It will warn, if the upload was aborted. + gsutil_args = ['cp', '-n', '-a', 'public-read', + '%s.tgz' % archive_name, + 'gs://chromium-browser-clang/%s/%s.tgz' % + (platform, archive_name)] + if args.upload: + print 'Uploading %s to Google Cloud Storage...' % archive_name + exit_code = RunGsutil(gsutil_args) + if exit_code != 0: + print "gsutil failed, exit_code: %s" % exit_code + os.exit(exit_code) + else: + print 'To upload, run:' + print ('gsutil %s' % ' '.join(gsutil_args)) + + def main(): if sys.platform == 'win32': try: @@ -66,8 +118,40 @@ return 1 parser = argparse.ArgumentParser(description='build and package clang') + parser.add_argument('--upload', action='store_true', + help='Upload the target archive to Google Cloud Storage.') args = parser.parse_args() + # Check that the script is not going to upload a toolchain built from HEAD. + use_head_revision = 'LLVM_FORCE_HEAD_REVISION' in os.environ + if args.upload and use_head_revision: + print ("--upload and LLVM_FORCE_HEAD_REVISION could not be used " + "at the same time.") + return 1 + + expected_stamp = GetExpectedStamp() + pdir = 'clang-' + expected_stamp + golddir = 'llvmgold-' + expected_stamp + print pdir + + if sys.platform == 'darwin': + platform = 'Mac' + elif sys.platform == 'win32': + platform = 'Win' + else: + platform = 'Linux_x64' + + # Check if Google Cloud Storage already has the artifacts we want to build. + if (args.upload and GsutilArchiveExists(pdir, platform) and + not sys.platform.startswith('linux') or + GsutilArchiveExists(golddir, platform)): + print ('Desired toolchain revision %s is already available ' + 'in Google Cloud Storage:') % expected_stamp + print 'gs://chromium-browser-clang/%s/%s.tgz' % (platform, pdir) + if sys.platform.startswith('linux'): + print 'gs://chromium-browser-clang/%s/%s.tgz' % (platform, golddir) + return 0 + with open('buildlog.txt', 'w') as log: Tee('Diff in llvm:\n', log) TeeCmd(['svn', 'stat', LLVM_DIR], log, fail_hard=False) @@ -103,8 +187,10 @@ TeeCmd(build_cmd, log) stamp = open(STAMP_FILE).read().rstrip() - pdir = 'clang-' + stamp - print pdir + if stamp != expected_stamp: + print 'Actual stamp (%s) != expected stamp (%s).' % (stamp, expected_stamp) + return 1 + shutil.rmtree(pdir, ignore_errors=True) # Copy a whitelist of files to the directory we're going to tar up. @@ -191,20 +277,10 @@ for entry in tar_entries: tar.add(os.path.join(pdir, entry), arcname=entry, filter=PrintTarProgress) - if sys.platform == 'darwin': - platform = 'Mac' - elif sys.platform == 'win32': - platform = 'Win' - else: - platform = 'Linux_x64' - - print 'To upload, run:' - print ('gsutil cp -a public-read %s.tgz ' - 'gs://chromium-browser-clang/%s/%s.tgz') % (pdir, platform, pdir) + MaybeUpload(args, pdir, platform) # Zip up gold plugin on Linux. if sys.platform.startswith('linux'): - golddir = 'llvmgold-' + stamp shutil.rmtree(golddir, ignore_errors=True) os.makedirs(os.path.join(golddir, 'lib')) shutil.copy(os.path.join(LLVM_RELEASE_DIR, 'lib', 'LLVMgold.so'), @@ -212,9 +288,7 @@ with tarfile.open(golddir + '.tgz', 'w:gz') as tar: tar.add(os.path.join(golddir, 'lib'), arcname='lib', filter=PrintTarProgress) - print ('gsutil cp -a public-read %s.tgz ' - 'gs://chromium-browser-clang/%s/%s.tgz') % (golddir, platform, - golddir) + MaybeUpload(args, golddir, platform) # FIXME: Warn if the file already exists on the server.
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn index eb60e08..683c8eb 100644 --- a/tools/gn/BUILD.gn +++ b/tools/gn/BUILD.gn
@@ -50,6 +50,8 @@ "copy_target_generator.h", "deps_iterator.cc", "deps_iterator.h", + "eclipse_writer.cc", + "eclipse_writer.h", "err.cc", "err.h", "escape.cc",
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc index b4faaa5..7ba7789 100644 --- a/tools/gn/command_gen.cc +++ b/tools/gn/command_gen.cc
@@ -10,6 +10,7 @@ #include "base/timer/elapsed_timer.h" #include "tools/gn/build_settings.h" #include "tools/gn/commands.h" +#include "tools/gn/eclipse_writer.h" #include "tools/gn/ninja_target_writer.h" #include "tools/gn/ninja_writer.h" #include "tools/gn/runtime_deps.h" @@ -26,6 +27,7 @@ const char kSwitchCheck[] = "check"; const char kSwitchIde[] = "ide"; +const char kSwitchIdeValueEclipse[] = "eclipse"; const char kSwitchIdeValueVs[] = "vs"; // Called on worker thread to write the ninja file. @@ -151,8 +153,16 @@ const BuildSettings* build_settings, Builder* builder, Err* err) { - if (ide == kSwitchIdeValueVs) { - base::ElapsedTimer timer; + base::ElapsedTimer timer; + if (ide == kSwitchIdeValueEclipse) { + bool res = EclipseWriter::RunAndWriteFile(build_settings, builder, err); + if (res) { + OutputString("Generating Eclipse settings took " + + base::Int64ToString(timer.Elapsed().InMilliseconds()) + + "ms\n"); + } + return res; + } else if (ide == kSwitchIdeValueVs) { bool res = VisualStudioWriter::RunAndWriteFiles(build_settings, builder, err); if (res && @@ -189,8 +199,20 @@ " --ide=<ide_name>\n" " Also generate files for an IDE. Currently supported values:\n" " 'vs' - Visual Studio project/solution files.\n" + " 'eclipse' - Eclipse CDT settings file.\n" "\n" - " See \"gn help switches\" for the common command-line switches.\n"; + " See \"gn help switches\" for the common command-line switches.\n" + "\n" + "Eclipse IDE Support\n" + "\n" + " GN DOES NOT generate Eclipse CDT projects. Instead, it generates a\n" + " settings file which can be imported into an Eclipse CDT project. The\n" + " XML file contains a list of include paths and defines. Because GN does\n" + " not generate a full .cproject definition, it is not possible to\n" + " properly define includes/defines for each file individually.\n" + " Instead, one set of includes/defines is generated for the entire\n" + " project. This works fairly well but may still result in a few indexer\n" + " issues here and there.\n"; int RunGen(const std::vector<std::string>& args) { base::ElapsedTimer timer;
diff --git a/tools/gn/eclipse_writer.cc b/tools/gn/eclipse_writer.cc new file mode 100644 index 0000000..e38e247 --- /dev/null +++ b/tools/gn/eclipse_writer.cc
@@ -0,0 +1,172 @@ +// 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 "tools/gn/eclipse_writer.h" + +#include <fstream> + +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "tools/gn/builder.h" +#include "tools/gn/config_values_extractors.h" +#include "tools/gn/filesystem_utils.h" +#include "tools/gn/loader.h" +#include "tools/gn/xml_element_writer.h" + +namespace { + +// Escapes |unescaped| for use in XML element content. +std::string EscapeForXML(const std::string& unescaped) { + std::string result; + result.reserve(unescaped.length()); + for (const char c : unescaped) { + if (c == '<') + result += "<"; + else if (c == '>') + result += ">"; + else if (c == '&') + result += "&"; + else + result.push_back(c); + } + return result; +} + +} // namespace + +EclipseWriter::EclipseWriter(const BuildSettings* build_settings, + const Builder* builder, + std::ostream& out) + : build_settings_(build_settings), builder_(builder), out_(out) { + languages_.push_back("C++ Source File"); + languages_.push_back("C Source File"); + languages_.push_back("Assembly Source File"); + languages_.push_back("GNU C++"); + languages_.push_back("GNU C"); + languages_.push_back("Assembly"); +} + +EclipseWriter::~EclipseWriter() {} + +// static +bool EclipseWriter::RunAndWriteFile( + const BuildSettings* build_settings, + const Builder* builder, + Err* err) { + base::FilePath file = build_settings->GetFullPath(build_settings->build_dir()) + .AppendASCII("eclipse-cdt-settings.xml"); + std::ofstream file_out; + file_out.open(FilePathToUTF8(file).c_str(), + std::ios_base::out | std::ios_base::binary); + if (file_out.fail()) { + *err = + Err(Location(), "Couldn't open eclipse-cdt-settings.xml for writing"); + return false; + } + + EclipseWriter gen(build_settings, builder, file_out); + gen.Run(); + return true; +} + +void EclipseWriter::Run() { + GetAllIncludeDirs(); + GetAllDefines(); + WriteCDTSettings(); +} + +void EclipseWriter::GetAllIncludeDirs() { + std::vector<const Target*> targets = builder_->GetAllResolvedTargets(); + for (const Target* target : targets) { + if (!UsesDefaultToolchain(target)) + continue; + + for (ConfigValuesIterator it(target); !it.done(); it.Next()) { + for (const SourceDir& include_dir : it.cur().include_dirs()) { + include_dirs_.insert( + FilePathToUTF8(build_settings_->GetFullPath(include_dir))); + } + } + } +} + +void EclipseWriter::GetAllDefines() { + std::vector<const Target*> targets = builder_->GetAllResolvedTargets(); + for (const Target* target : targets) { + if (!UsesDefaultToolchain(target)) + continue; + + for (ConfigValuesIterator it(target); !it.done(); it.Next()) { + for (const std::string& define : it.cur().defines()) { + size_t equal_pos = define.find('='); + std::string define_key; + std::string define_value; + if (equal_pos == std::string::npos) { + define_key = define; + } else { + define_key = define.substr(0, equal_pos); + define_value = define.substr(equal_pos + 1); + } + defines_[define_key] = define_value; + } + } + } +} + +bool EclipseWriter::UsesDefaultToolchain(const Target* target) const { + return target->toolchain()->label() == + builder_->loader()->GetDefaultToolchain(); +} + +void EclipseWriter::WriteCDTSettings() { + out_ << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; + XmlElementWriter cdt_properties_element(out_, "cdtprojectproperties", + XmlAttributes()); + + { + const char* kIncludesSectionName = + "org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths"; + scoped_ptr<XmlElementWriter> section_element = + cdt_properties_element.SubElement( + "section", XmlAttributes("name", kIncludesSectionName)); + + section_element->SubElement( + "language", XmlAttributes("name", "holder for library settings")); + + for (const std::string& language : languages_) { + scoped_ptr<XmlElementWriter> language_element = + section_element->SubElement("language", + XmlAttributes("name", language)); + for (const std::string& include_dir : include_dirs_) { + language_element + ->SubElement("includepath", + XmlAttributes("workspace_path", "false")) + ->Text(EscapeForXML(include_dir)); + } + } + } + + { + const char* kMacrosSectionName = + "org.eclipse.cdt.internal.ui.wizards.settingswizards.Macros"; + scoped_ptr<XmlElementWriter> section_element = + cdt_properties_element.SubElement( + "section", XmlAttributes("name", kMacrosSectionName)); + + section_element->SubElement( + "language", XmlAttributes("name", "holder for library settings")); + + for (const std::string& language : languages_) { + scoped_ptr<XmlElementWriter> language_element = + section_element->SubElement("language", + XmlAttributes("name", language)); + for (const auto& key_val : defines_) { + scoped_ptr<XmlElementWriter> macro_element = + language_element->SubElement("macro"); + macro_element->SubElement("name")->Text(EscapeForXML(key_val.first)); + macro_element->SubElement("value")->Text(EscapeForXML(key_val.second)); + } + } + } +}
diff --git a/tools/gn/eclipse_writer.h b/tools/gn/eclipse_writer.h new file mode 100644 index 0000000..560b872 --- /dev/null +++ b/tools/gn/eclipse_writer.h
@@ -0,0 +1,67 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_GN_ECLIPSE_WRITER_H_ +#define TOOLS_GN_ECLIPSE_WRITER_H_ + +#include <iosfwd> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/macros.h" + +class BuildSettings; +class Builder; +class Err; +class Target; + +class EclipseWriter { + public: + static bool RunAndWriteFile(const BuildSettings* build_settings, + const Builder* builder, + Err* err); + + private: + EclipseWriter(const BuildSettings* build_settings, + const Builder* builder, + std::ostream& out); + ~EclipseWriter(); + + void Run(); + + // Populates |include_dirs_| with the include dirs of all the targets for the + // default toolchain. + void GetAllIncludeDirs(); + + // Populates |defines_| with the defines of all the targets for the default + // toolchain. + void GetAllDefines(); + + // Returns true if |target| uses the default toolchain. + bool UsesDefaultToolchain(const Target* target) const; + + // Writes the XML settings file. + void WriteCDTSettings(); + + const BuildSettings* build_settings_; + const Builder* builder_; + + // The output stream for the settings file. + std::ostream& out_; + + // Eclipse languages for which the include dirs and defines apply. + std::vector<std::string> languages_; + + // The include dirs of all the targets which use the default toolchain. + std::set<std::string> include_dirs_; + + // The defines of all the targets which use the default toolchain. + std::map<std::string, std::string> defines_; + + DISALLOW_COPY_AND_ASSIGN(EclipseWriter); +}; + +#endif // TOOLS_GN_ECLIPSE_WRITER_H_
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc index 5905cdb..2ceca816 100644 --- a/tools/gn/filesystem_utils.cc +++ b/tools/gn/filesystem_utils.cc
@@ -687,6 +687,86 @@ return toolchain_label.name() + "/"; } +bool ContentsEqual(const base::FilePath& file_path, const std::string& data) { + // Compare file and stream sizes first. Quick and will save us some time if + // they are different sizes. + int64_t file_size; + if (!base::GetFileSize(file_path, &file_size) || + static_cast<size_t>(file_size) != data.size()) { + return false; + } + + std::string file_data; + file_data.resize(file_size); + if (!base::ReadFileToString(file_path, &file_data)) + return false; + + return file_data == data; +} + +bool WriteFileIfChanged(const base::FilePath& file_path, + const std::string& data, + Err* err) { + if (ContentsEqual(file_path, data)) + return true; + + // Create the directory if necessary. + if (!base::CreateDirectory(file_path.DirName())) { + if (err) { + *err = + Err(Location(), "Unable to create directory.", + "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\"."); + } + return false; + } + + int size = static_cast<int>(data.size()); + bool write_success = false; + +#if defined(OS_WIN) + // On Windows, provide a custom implementation of base::WriteFile. Sometimes + // the base version fails, especially on the bots. The guess is that Windows + // Defender or other antivirus programs still have the file open (after + // checking for the read) when the write happens immediately after. This + // version opens with FILE_SHARE_READ (normally not what you want when + // replacing the entire contents of the file) which lets us continue even if + // another program has the file open for reading. See http://crbug.com/468437 + base::win::ScopedHandle file(::CreateFile(file_path.value().c_str(), + GENERIC_WRITE, FILE_SHARE_READ, + NULL, CREATE_ALWAYS, 0, NULL)); + if (file.IsValid()) { + DWORD written; + BOOL result = ::WriteFile(file.Get(), data.c_str(), size, &written, NULL); + if (result) { + if (static_cast<int>(written) == size) { + write_success = true; + } else { + // Didn't write all the bytes. + LOG(ERROR) << "wrote" << written << " bytes to " + << base::UTF16ToUTF8(file_path.value()) << " expected " + << size; + } + } else { + // WriteFile failed. + PLOG(ERROR) << "writing file " << base::UTF16ToUTF8(file_path.value()) + << " failed"; + } + } else { + PLOG(ERROR) << "CreateFile failed for path " + << base::UTF16ToUTF8(file_path.value()); + } +#else + write_success = base::WriteFile(file_path, data.c_str(), size) == size; +#endif + + if (!write_success && err) { + *err = Err(Location(), "Unable to write file.", + "I was writing \"" + FilePathToUTF8(file_path) + "\"."); + } + + return write_success; +} + SourceDir GetToolchainOutputDir(const Settings* settings) { return settings->toolchain_output_subdir().AsSourceDir( settings->build_settings());
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h index f64c00d..aaa08bab 100644 --- a/tools/gn/filesystem_utils.h +++ b/tools/gn/filesystem_utils.h
@@ -163,6 +163,18 @@ // go in the root build directory. Otherwise, the result will end in a slash. std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default); +// Returns true if the contents of the file and stream given are equal, false +// otherwise. +bool ContentsEqual(const base::FilePath& file_path, const std::string& data); + +// Writes given stream contents to the given file if it differs from existing +// file contents. Returns true if new contents was successfully written or +// existing file contents doesn't need updating, false on write error. |err| is +// set on write error if not nullptr. +bool WriteFileIfChanged(const base::FilePath& file_path, + const std::string& data, + Err* err); + // ----------------------------------------------------------------------------- // These functions return the various flavors of output and gen directories.
diff --git a/tools/gn/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc index 27ccab2..d5638900 100644 --- a/tools/gn/filesystem_utils_unittest.cc +++ b/tools/gn/filesystem_utils_unittest.cc
@@ -3,8 +3,11 @@ // found in the LICENSE file. #include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/platform_thread.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "tools/gn/filesystem_utils.h" @@ -554,6 +557,60 @@ #endif } +TEST(FilesystemUtils, ContentsEqual) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + std::string data = "foo"; + + base::FilePath file_path = temp_dir.path().AppendASCII("foo.txt"); + base::WriteFile(file_path, data.c_str(), static_cast<int>(data.size())); + + EXPECT_TRUE(ContentsEqual(file_path, data)); + + // Different length and contents. + data += "bar"; + EXPECT_FALSE(ContentsEqual(file_path, data)); + + // The same length, different contents. + EXPECT_FALSE(ContentsEqual(file_path, "bar")); +} + +TEST(FilesystemUtils, WriteFileIfChanged) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + std::string data = "foo"; + + // Write if file doesn't exist. Create also directory. + base::FilePath file_path = + temp_dir.path().AppendASCII("bar").AppendASCII("foo.txt"); + EXPECT_TRUE(WriteFileIfChanged(file_path, data, nullptr)); + + base::File::Info file_info; + ASSERT_TRUE(base::GetFileInfo(file_path, &file_info)); + base::Time last_modified = file_info.last_modified; + +#if defined(OS_MACOSX) + // Modification times are in seconds in HFS on Mac. + base::TimeDelta sleep_time = base::TimeDelta::FromSeconds(1); +#else + base::TimeDelta sleep_time = base::TimeDelta::FromMilliseconds(1); +#endif + base::PlatformThread::Sleep(sleep_time); + + // Don't write if contents is the same. + EXPECT_TRUE(WriteFileIfChanged(file_path, data, nullptr)); + ASSERT_TRUE(base::GetFileInfo(file_path, &file_info)); + EXPECT_EQ(last_modified, file_info.last_modified); + + // Write if contents changed. + EXPECT_TRUE(WriteFileIfChanged(file_path, "bar", nullptr)); + std::string file_data; + ASSERT_TRUE(base::ReadFileToString(file_path, &file_data)); + EXPECT_EQ("bar", file_data); +} + TEST(FilesystemUtils, GetToolchainDirs) { BuildSettings build_settings; build_settings.SetBuildDir(SourceDir("//out/Debug/"));
diff --git a/tools/gn/function_write_file.cc b/tools/gn/function_write_file.cc index 9159975..feb1004c3 100644 --- a/tools/gn/function_write_file.cc +++ b/tools/gn/function_write_file.cc
@@ -19,55 +19,6 @@ namespace functions { -namespace { - -// On Windows, provide a custom implementation of base::WriteFile. Sometimes -// the base version fails, especially on the bots. The guess is that Windows -// Defender or other antivirus programs still have the file open (after -// checking for the read) when the write happens immediately after. This -// version opens with FILE_SHARE_READ (normally not what you want when -// replacing the entire contents of the file) which lets us continue even if -// another program has the file open for reading. See http://crbug.com/468437 -#if defined(OS_WIN) -int DoWriteFile(const base::FilePath& filename, const char* data, int size) { - base::win::ScopedHandle file(::CreateFile( - filename.value().c_str(), - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - CREATE_ALWAYS, - 0, - NULL)); - if (!file.IsValid()) { - PLOG(ERROR) << "CreateFile failed for path " - << base::UTF16ToUTF8(filename.value()); - return -1; - } - - DWORD written; - BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); - if (result && static_cast<int>(written) == size) - return written; - - if (!result) { - // WriteFile failed. - PLOG(ERROR) << "writing file " << base::UTF16ToUTF8(filename.value()) - << " failed"; - } else { - // Didn't write all the bytes. - LOG(ERROR) << "wrote" << written << " bytes to " - << base::UTF16ToUTF8(filename.value()) << " expected " << size; - } - return -1; -} -#else -int DoWriteFile(const base::FilePath& filename, const char* data, int size) { - return base::WriteFile(filename, data, size); -} -#endif - -} // namespace - const char kWriteFile[] = "write_file"; const char kWriteFile_HelpShort[] = "write_file: Write a file to disk."; @@ -138,30 +89,14 @@ } else { contents << args[1].ToString(false); } - const std::string& new_contents = contents.str(); + base::FilePath file_path = scope->settings()->build_settings()->GetFullPath(source_file); // Make sure we're not replacing the same contents. - std::string existing_contents; - if (base::ReadFileToString(file_path, &existing_contents) && - existing_contents == new_contents) - return Value(); // Nothing to do. + if (!WriteFileIfChanged(file_path, contents.str(), err)) + *err = Err(function->function(), err->message(), err->help_text()); - // Write file, creating the directory if necessary. - if (!base::CreateDirectory(file_path.DirName())) { - *err = Err(function->function(), "Unable to create directory.", - "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\"."); - return Value(); - } - - int int_size = static_cast<int>(new_contents.size()); - if (DoWriteFile(file_path, new_contents.c_str(), int_size) - != int_size) { - *err = Err(function->function(), "Unable to write file.", - "I was writing \"" + FilePathToUTF8(file_path) + "\"."); - return Value(); - } return Value(); }
diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp index 24cff713..85754e08 100644 --- a/tools/gn/gn.gyp +++ b/tools/gn/gn.gyp
@@ -50,6 +50,8 @@ 'copy_target_generator.h', 'deps_iterator.cc', 'deps_iterator.h', + 'eclipse_writer.cc', + 'eclipse_writer.h', 'err.cc', 'err.h', 'escape.cc',
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc index 7d9c08a..9f19b2b0 100644 --- a/tools/gn/ninja_target_writer.cc +++ b/tools/gn/ninja_target_writer.cc
@@ -74,9 +74,7 @@ CHECK(0) << "Output type of target not handled."; } - std::string contents = file.str(); - base::WriteFile(ninja_file, contents.c_str(), - static_cast<int>(contents.size())); + WriteFileIfChanged(ninja_file, file.str(), nullptr); } void NinjaTargetWriter::WriteEscapedSubstitution(SubstitutionType type) {
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc index 79814e6..bad4b7b 100644 --- a/tools/gn/setup.cc +++ b/tools/gn/setup.cc
@@ -226,6 +226,21 @@ } #endif +// Expands all ./, ../, and symbolic links in the given path. +bool NormalizePath(const base::FilePath& path, base::FilePath* out) { +#if defined(OS_POSIX) + char buf[PATH_MAX]; + if (!realpath(path.value().c_str(), buf)) { + return false; + } + *out = base::FilePath(buf); +#else + // Do nothing on a non-POSIX system. + *out = path; +#endif + return true; +} + } // namespace const char Setup::kBuildArgFileName[] = "args.gn"; @@ -513,9 +528,16 @@ root_path = dotfile_name_.DirName(); } + base::FilePath root_path_normalized; + if (!NormalizePath(root_path, &root_path_normalized)) { + Err(Location(), "Can't normalize the root path.", + "I could not normalize the path \"" + FilePathToUTF8(root_path) + "\".") + .PrintToStdout(); + return false; + } if (scheduler_.verbose_logging()) - scheduler_.Log("Using source root", FilePathToUTF8(root_path)); - build_settings_.SetRootPath(root_path); + scheduler_.Log("Using source root", FilePathToUTF8(root_path_normalized)); + build_settings_.SetRootPath(root_path_normalized); return true; } @@ -531,11 +553,21 @@ return false; } + base::FilePath build_dir_path = build_settings_.GetFullPath(resolved); + base::FilePath build_dir_path_normalized; + if (!NormalizePath(build_dir_path, &build_dir_path_normalized)) { + Err(Location(), "Can't normalize the root path.", + "I could not normalize the path \"" + FilePathToUTF8(build_dir_path) + + "\".").PrintToStdout(); + return false; + } + resolved = SourceDirForPath(build_settings_.root_path(), + build_dir_path_normalized); + if (scheduler_.verbose_logging()) scheduler_.Log("Using build dir", resolved.value()); if (require_exists) { - base::FilePath build_dir_path = build_settings_.GetFullPath(resolved); if (!base::PathExists(build_dir_path.Append( FILE_PATH_LITERAL("build.ninja")))) { Err(Location(), "Not a build directory.",
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc index 44a3f114..dec08375 100644 --- a/tools/gn/visual_studio_writer.cc +++ b/tools/gn/visual_studio_writer.cc
@@ -9,7 +9,6 @@ #include <set> #include <string> -#include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_util.h" @@ -153,26 +152,6 @@ return base::StringPiece(); } -bool HasSameContent(std::stringstream& data_1, const base::FilePath& data_2) { - // Compare file sizes first. Quick and will save us some time if they are - // different sizes. - int64_t data_1_len = data_1.tellp(); - - int64_t data_2_len; - if (!base::GetFileSize(data_2, &data_2_len) || data_1_len != data_2_len) - return false; - - std::string data_2_data; - data_2_data.resize(data_2_len); - if (!base::ReadFileToString(data_2, &data_2_data)) - return false; - - std::string data_1_data; - data_1_data = data_1.str(); - - return data_1_data == data_2_data; -} - } // namespace VisualStudioWriter::SolutionEntry::SolutionEntry(const std::string& _name, @@ -289,32 +268,13 @@ // Only write the content to the file if it's different. That is // both a performance optimization and more importantly, prevents // Visual Studio from reloading the projects. - if (!HasSameContent(vcxproj_string_out, vcxproj_path)) { - std::string content = vcxproj_string_out.str(); - int size = static_cast<int>(content.size()); - if (base::WriteFile(vcxproj_path, content.c_str(), size) != size) { - *err = Err(Location(), "Couldn't open " + target->label().name() + - ".vcxproj for writing"); - return false; - } - } + if (!WriteFileIfChanged(vcxproj_path, vcxproj_string_out.str(), err)) + return false; base::FilePath filters_path = UTF8ToFilePath(vcxproj_path_str + ".filters"); - std::stringstream filters_string_out; WriteFiltersFileContents(filters_string_out, target); - - if (!HasSameContent(filters_string_out, filters_path)) { - std::string content = filters_string_out.str(); - int size = static_cast<int>(content.size()); - if (base::WriteFile(filters_path, content.c_str(), size) != size) { - *err = Err(Location(), "Couldn't open " + target->label().name() + - ".vcxproj.filters for writing"); - return false; - } - } - - return true; + return WriteFileIfChanged(filters_path, filters_string_out.str(), err); } bool VisualStudioWriter::WriteProjectFileContents( @@ -612,17 +572,7 @@ // Only write the content to the file if it's different. That is // both a performance optimization and more importantly, prevents // Visual Studio from reloading the projects. - if (HasSameContent(string_out, sln_path)) - return true; - - std::string content = string_out.str(); - int size = static_cast<int>(content.size()); - if (base::WriteFile(sln_path, content.c_str(), size) != size) { - *err = Err(Location(), "Couldn't open all.sln for writing"); - return false; - } - - return true; + return WriteFileIfChanged(sln_path, string_out.str(), err); } void VisualStudioWriter::WriteSolutionFileContents(
diff --git a/tools/gn/xml_element_writer.cc b/tools/gn/xml_element_writer.cc index 51e0488b..a608ee3 100644 --- a/tools/gn/xml_element_writer.cc +++ b/tools/gn/xml_element_writer.cc
@@ -38,7 +38,9 @@ XmlElementWriter::~XmlElementWriter() { if (!opening_tag_finished_) { - out_ << "/>" << std::endl; + // The XML spec does not require a space before the closing slash. However, + // Eclipse is unable to parse XML settings files if there is no space. + out_ << " />" << std::endl; } else { if (!one_line_) out_ << std::string(indent_, ' ');
diff --git a/tools/gn/xml_element_writer_unittest.cc b/tools/gn/xml_element_writer_unittest.cc index 6702061d..93dfd47 100644 --- a/tools/gn/xml_element_writer_unittest.cc +++ b/tools/gn/xml_element_writer_unittest.cc
@@ -24,27 +24,27 @@ TEST(XmlElementWriter, EmptyElement) { std::ostringstream out; { XmlElementWriter writer(out, "foo", XmlAttributes()); } - EXPECT_EQ("<foo/>\n", out.str()); + EXPECT_EQ("<foo />\n", out.str()); std::ostringstream out_attr; { XmlElementWriter writer(out_attr, "foo", XmlAttributes("bar", "abc").add("baz", "123")); } - EXPECT_EQ("<foo bar=\"abc\" baz=\"123\"/>\n", out_attr.str()); + EXPECT_EQ("<foo bar=\"abc\" baz=\"123\" />\n", out_attr.str()); std::ostringstream out_indent; { XmlElementWriter writer(out_indent, "foo", XmlAttributes("bar", "baz"), 2); } - EXPECT_EQ(" <foo bar=\"baz\"/>\n", out_indent.str()); + EXPECT_EQ(" <foo bar=\"baz\" />\n", out_indent.str()); std::ostringstream out_writer; { XmlElementWriter writer(out_writer, "foo", "bar", MockValueWriter("baz"), 2); } - EXPECT_EQ(" <foo bar=\"baz\"/>\n", out_writer.str()); + EXPECT_EQ(" <foo bar=\"baz\" />\n", out_writer.str()); } TEST(XmlElementWriter, ElementWithText) { @@ -67,10 +67,10 @@ } std::string expected = "<root aaa=\"000\">\n" - " <foo/>\n" + " <foo />\n" " <bar bbb=\"111\">hello</bar>\n" " <baz ccc=\"222\">\n" - " <grandchild/>\n" + " <grandchild />\n" " </baz>\n" "</root>\n"; EXPECT_EQ(expected, out.str());
diff --git a/tools/gritsettings/translation_expectations.pyl b/tools/gritsettings/translation_expectations.pyl index 13e271f..8247c969 100644 --- a/tools/gritsettings/translation_expectations.pyl +++ b/tools/gritsettings/translation_expectations.pyl
@@ -67,8 +67,6 @@ "chrome/app/settings_chromium_strings.grd": "Work in progress; to be localized later in development (late 2015)", "chrome/app/settings_google_chrome_strings.grd": "Work in progress; to be localized later in development (late 2015)", "chromecast/app/resources/chromecast_settings.grd": "Not UI strings; localized separately", - "cloud_print/service/win/service_resources.grd": "Separate release process", - "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": "Separate release process", "components/components_locale_settings.grd": "Not UI strings; localized separately", "tools/grit/grit/testdata/buildinfo.grd": "Test data", "tools/grit/grit/testdata/chrome/app/generated_resources.grd": "Test data",
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 8de5675e..3facfbb 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -751,7 +751,7 @@ 'linux_chromium_compile_dbg_32_ng': 'swarming_gyp_debug_trybot_x86', 'linux_chromium_dbg_32_ng': 'swarming_gyp_debug_trybot_x86', 'linux_chromium_archive_rel_ng': 'noswarming_gn_release_bot', - 'linux_chromium_clobber_rel_ng': 'gyp_release_trybot', + 'linux_chromium_clobber_rel_ng': 'gn_release_trybot', 'linux_chromium_gn_upload': 'gn_linux_upload', 'linux_chromium_clang_upload': 'gn_release_bot', 'cast_shell_linux': 'cast_gn_release_trybot',
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index a438240..03d73bd4 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -136,6 +136,7 @@ <description> User flag to disable smooth scroll animations on wheel and keyboard input. </description> + <obsolete>This flag no longer exists.</obsolete> </action> <action name="AboutFlags_disable-views-rect-based-targeting"> @@ -299,6 +300,13 @@ <description>Please enter the description of this user action.</description> </action> +<action name="AboutFlags_smooth-scrolling"> + <owner>skobes@chromium.org</owner> + <description> + User flag to disable smooth scroll animations on wheel and keyboard input. + </description> +</action> + <action name="AboutFlags_snap-start"> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index ccf3c6ca1..cf050a1 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -14915,6 +14915,49 @@ </summary> </histogram> +<histogram name="GCM.Crypto.CreateKeySuccessRate" enum="BooleanSuccess"> + <owner>peter@chromium.org</owner> + <summary> + Success rate of writing new keying material to the GCM key store. + </summary> +</histogram> + +<histogram name="GCM.Crypto.DecryptMessageResult" enum="GCMDecryptionResult"> + <owner>peter@chromium.org</owner> + <summary> + Result of decryption on a received GCM message, which includes unencrypted + messages, successfully decrypted messages and decryption failures. + </summary> +</histogram> + +<histogram name="GCM.Crypto.GetKeySuccessRate" enum="BooleanSuccess"> + <owner>peter@chromium.org</owner> + <summary> + Success rate of reading keying material from the GCM key store. + </summary> +</histogram> + +<histogram name="GCM.Crypto.InitKeyStoreSuccessRate" enum="BooleanSuccess"> + <owner>peter@chromium.org</owner> + <summary> + Success rate of initializing the LevelDB back-end of the GCM key store. + </summary> +</histogram> + +<histogram name="GCM.Crypto.LoadKeyStoreSuccessRate" enum="BooleanSuccess"> + <owner>peter@chromium.org</owner> + <summary> + Success rate of loading existing keying material in the GCM key store. + </summary> +</histogram> + +<histogram name="GCM.Crypto.RemoveKeySuccessRate" enum="BooleanSuccess"> + <owner>peter@chromium.org</owner> + <summary> + Success rate of removing keying material from the GCM key store. + </summary> +</histogram> + <histogram name="GCM.Database.Open" enum="LevelDBStatus"> <owner>cmumford@chromium.org</owner> <summary>The result of a database open attempt by the GCM store.</summary> @@ -15244,6 +15287,25 @@ <summary>Http response codes in NetworkLocationRequest.</summary> </histogram> +<histogram name="Geolocation.Timeout"> + <owner>mvanouwerkerk@chromium.org</owner> + <summary> + Counts Geolocation request timeout values, bucketing by timeout duration. + This is recorded for all requests upon creation, see + Geolocation.TimeoutExpired for timeouts that actually expired. + </summary> +</histogram> + +<histogram name="Geolocation.TimeoutExpired"> + <owner>mvanouwerkerk@chromium.org</owner> + <summary> + Counts Geolocation request timeout expirations, bucketing by timeout + duration. This means no position was received within the allowed time from + the browser process due to e.g. a slow network or an unresponsive system + location provider. + </summary> +</histogram> + <histogram name="GoogleNow.Card.Button.Clicked0" enum="GoogleNowCardTypeId"> <owner>robliao@chromium.org</owner> <owner>skare@chromium.org</owner> @@ -17852,6 +17914,36 @@ </summary> </histogram> +<histogram name="JSDialogs.CountOfJSDialogMessageCharacters"> + <owner>avi@chromium.org</owner> + <summary> + The count of the number of characters in JavaScript dialog messages. + </summary> +</histogram> + +<histogram name="JSDialogs.CountOfJSDialogMessageNewlines"> + <owner>avi@chromium.org</owner> + <summary> + The count of the number of newlines in JavaScript dialog messages. (This + does not count breaks inserted by the UI toolkit in wrapping the messages.) + </summary> +</histogram> + +<histogram name="JSDialogs.CountOfOnBeforeUnloadMessageCharacters"> + <owner>avi@chromium.org</owner> + <summary> + The count of the number of characters in onbeforeunload messages. + </summary> +</histogram> + +<histogram name="JSDialogs.CountOfOnBeforeUnloadMessageNewlines"> + <owner>avi@chromium.org</owner> + <summary> + The count of the number of newlines in onbeforeunload messages. (This does + not count breaks inserted by the UI toolkit in wrapping the messages.) + </summary> +</histogram> + <histogram name="JSDialogs.FineTiming.TimeBetweenDialogClosedAndNextDialogCreated" units="ms"> @@ -18743,6 +18835,11 @@ </summary> </histogram> +<histogram name="Media.Android.MediaPlayerSuccess" enum="MediaPlayerExitStatus"> + <owner>timav@chromium.org</owner> + <summary>Android: Whether MediaPlayer exited without errors.</summary> +</histogram> + <histogram name="Media.Android.NumMediaServerCrashes"> <owner>qinmin@chromium.org</owner> <summary> @@ -22260,6 +22357,14 @@ </summary> </histogram> +<histogram name="Navigation.FrameHasEmbeddedCredentials" enum="Boolean"> + <owner>palmer@chromium.org</owner> + <owner>cbentzel@chromium.org</owner> + <summary> + Whether the navigation was to a URL that had embedded credentials. + </summary> +</histogram> + <histogram name="Navigation.IsMobileOptimized" enum="BooleanIsMobileOptimized"> <owner>cjhopman@chromium.org</owner> <owner>nyquist@chromium.org</owner> @@ -22269,6 +22374,15 @@ </summary> </histogram> +<histogram name="Navigation.MainFrameHasEmbeddedCredentials" enum="Boolean"> + <owner>palmer@chromium.org</owner> + <owner>cbentzel@chromium.org</owner> + <summary> + Whether the main-frame navigation was to a URL that had embedded + credentials. + </summary> +</histogram> + <histogram name="Navigation.MainFrameScheme" enum="NavigationScheme"> <owner>cbentzel@chromium.org</owner> <owner>davidben@chromium.org</owner> @@ -30659,6 +30773,9 @@ </histogram> <histogram name="NewTabPage.HoverTimeClicked"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> Histogram of the time, in milliseconds, users have the cursor over a most @@ -30667,6 +30784,9 @@ </histogram> <histogram name="NewTabPage.HoverTimeNotClicked"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> Histogram of the time, in milliseconds, users have the cursor over a most @@ -30775,6 +30895,9 @@ </histogram> <histogram name="NewTabPage.MostVisitedAction" enum="NtpFollowAction"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>beaudoin@chromium.org</owner> <owner>justincohen@chromium.org</owner> <owner>newt@chromium.org</owner> @@ -30787,6 +30910,9 @@ <histogram name="NewTabPage.MostVisitedTilePlacementExperiment" enum="NtpTileExperimentActions"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>beaudoin@chromium.org</owner> <owner>justincohen@chromium.org</owner> <owner>newt@chromium.org</owner> @@ -30807,6 +30933,9 @@ </histogram> <histogram name="NewTabPage.NonVisibleScreenshots"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> The number of screenshots that were cached for the non-visible but ranked @@ -30815,6 +30944,9 @@ </histogram> <histogram name="NewTabPage.NonVisibleSuggestedSiteRank"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> Given that the user has typed a URL, and given that that specific URL was @@ -30940,11 +31072,17 @@ </histogram> <histogram name="NewTabPage.Promo.Bubble" enum="NtpPromoAction"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary>Histogram for NTP bubble promo activity.</summary> </histogram> <histogram name="NewTabPage.Promo.Notification" enum="NtpPromoAction"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary>Histogram for NTP notification promo activity.</summary> </histogram> @@ -30962,6 +31100,9 @@ </histogram> <histogram name="NewTabPage.SearchURLs.Total"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary>TBD.</summary> </histogram> @@ -30972,6 +31113,9 @@ </histogram> <histogram name="NewTabPage.SessionRestore"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> Histogram for user clicks of the Recently Closed items. The value is the @@ -30980,6 +31124,9 @@ </histogram> <histogram name="NewTabPage.SingleSessionPageSwitches"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> Histogram to track how many times a user switched pages in a single NTP @@ -31037,6 +31184,9 @@ </histogram> <histogram name="NewTabPage.SuggestedSite"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> Histogram for user clicks of the suggested site thumbnails. The value is @@ -31045,16 +31195,25 @@ </histogram> <histogram name="NewTabPage.SuggestedSitesAction" enum="NtpFollowAction"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary>Action taken by the user on the Suggested Sites NTP pane.</summary> </histogram> <histogram name="NewTabPage.SuggestedSitesLoadTime"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary>Time to load the Suggested Sites NTP pane, in milliseconds.</summary> </histogram> <histogram name="NewTabPage.SuggestedSitesViewTime"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary>Time spent on the Suggested Sites NTP pane, in seconds.</summary> </histogram> @@ -31143,6 +31302,9 @@ </histogram> <histogram name="NewTabPage.VisibleScreenshots"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> The number of screenshots that were cached for the visible suggestions on @@ -31151,6 +31313,9 @@ </histogram> <histogram name="NewTabPage.VisibleSuggestedSiteRank"> + <obsolete> + Deprecated 2016-02 (and not recorded for some time before that). + </obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary> Given that the user has typed a URL, and given that that specific URL was @@ -31454,6 +31619,23 @@ <summary>The pane selected when the user switches panes in the NTP.</summary> </histogram> +<histogram name="OAuth2Login.AccountRevoked.IsEmailId" enum="Boolean"> + <owner>knn@chromium.org</owner> + <summary> + This histogram records whether the account ID is actually an email if we + detect an account that has an account ID but not email. + </summary> +</histogram> + +<histogram name="OAuth2Login.AccountRevoked.MigrationState" + enum="OAuth2LoginAccountRevokedMigrationState"> + <owner>knn@chromium.org</owner> + <summary> + This histogram records the account ID migration status from email to GAIA ID + if we detect an account that has an account ID but not email. + </summary> +</histogram> + <histogram name="OAuth2Login.GetOAuth2AccessTokenFailure" enum="GoogleServiceAuthError"> <owner>zelidrag@chromium.org</owner> @@ -43399,6 +43581,18 @@ </summary> </histogram> +<histogram name="Scheduling.MainAndImplFrameTimeDelta" units="microseconds"> + <owner>brianderson@chromium.org</owner> + <summary> + Recorded every time the compositor draws with a new active tree. A value of + 0 indicates the main-side started and finished within the same frame + interval as the impl-side. Positive values correspond to how old any + main-side updates are compared to the impl-side updates. If there are no + mid-frame updates, this metric is a good proxy for how well the main and + impl threads are synchronized. + </summary> +</histogram> + <histogram name="Scheduling.PrepareTilesDuration" units="microseconds"> <owner>brianderson@chromium.org</owner> <summary> @@ -52386,6 +52580,15 @@ </summary> </histogram> +<histogram name="UMA.FileMetricsProvider.AccessResult" + enum="FileMetricsProviderAccessResult"> + <owner>asvitkine@chromium.org</owner> + <owner>bcwhite@chromium.org</owner> + <summary> + Records attempts to access a file for the purpose of extracting metrics. + </summary> +</histogram> + <histogram name="UMA.GeneratedLowEntropySource" enum="Boolean"> <obsolete> Deprecated as of August 2015. No longer tracked. @@ -66785,6 +66988,9 @@ <int value="1236" label="V8HTMLComment"/> <int value="1237" label="V8SloppyModeBlockScopedFunctionRedefinition"/> <int value="1238" label="V8ForInInitializer"/> + <int value="1239" label="V8Animation_Id_AttributeGetter"/> + <int value="1240" label="V8Animation_Id_AttributeSetter"/> + <int value="1241" label="MediaStreamOnEnded"/> </enum> <enum name="FetchRequestMode" type="int"> @@ -67608,6 +67814,15 @@ <int value="5" label="MTP (Media Transfer Protocol) Device"/> </enum> +<enum name="FileMetricsProviderAccessResult" type="int"> + <int value="0" label="File was mapped."/> + <int value="1" label="File doesn't exist."/> + <int value="2" label="File not modified."/> + <int value="3" label="File is invalid (e.g. directory or zero-size)."/> + <int value="4" label="File could not be memory-mapped by system."/> + <int value="5" label="File has invalid contents."/> +</enum> + <enum name="FileSystemDatabaseInitResult" type="int"> <int value="0" label="OK"/> <int value="1" label="Corruption"/> @@ -67991,6 +68206,16 @@ <int value="5" label="New heartbeat interval"/> </enum> +<enum name="GCMDecryptionResult" type="int"> + <int value="0" label="Success (message unencrypted)"/> + <int value="1" label="Success (message decrypted)"/> + <int value="2" label="Failure (invalid Encryption HTTP header)"/> + <int value="3" label="Failure (invalid Crypto-Key HTTP header)"/> + <int value="4" label="Failure (no keying material available)"/> + <int value="5" label="Failure (unable to compute the shared secret)"/> + <int value="6" label="Failure (unable to decrypt using AES-GCM)"/> +</enum> + <enum name="GCMEndpoints" type="int"> <int value="0" label="mtalk.google.com:5228"/> <int value="1" label="mtalk.google.com:443"/> @@ -71107,6 +71332,7 @@ <int value="121858954" label="enable-supervised-user-safesites"/> <int value="125934378" label="enable-password-link"/> <int value="147373243" label="enable-deferred-image-decoding"/> + <int value="157217034" label="enable-tab-for-desktop-share"/> <int value="178337215" label="enable-md-history"/> <int value="180074362" label="memory-pressure-thresholds"/> <int value="203776499" label="enable-virtual-keyboard-overscroll"/> @@ -72404,6 +72630,11 @@ <int value="2" label="All external links protected"/> </enum> +<enum name="MediaPlayerExitStatus" type="int"> + <int value="0" label="No errors"/> + <int value="1" label="Has errors"/> +</enum> + <enum name="MediaRouteProviderWakeReason" type="int"> <int value="0" label="CreateRoute"/> <int value="1" label="JoinRoute"/> @@ -73788,6 +74019,12 @@ <int value="3" label="Too few URLs, didn't flip tiles 1 and 4"/> </enum> +<enum name="OAuth2LoginAccountRevokedMigrationState" type="int"> + <int value="0" label="Account ID migration not started"/> + <int value="1" label="Account ID migration in progress"/> + <int value="2" label="Account ID migration done"/> +</enum> + <enum name="OAuth2LoginSeedState" type="int"> <int value="0" label="Account was seeded before FireRefreshTokenRevoked"/> <int value="1" label="Account was seeded before FireRefreshTokenAvailable"/> @@ -76016,10 +76253,10 @@ <enum name="PrintPreviewFailureType" type="int"> <int value="0" label="No error"/> <int value="1" label="Bad settings from print preview tab"/> - <int value="2" label="Copy metadata failed"/> + <int value="2" label="Copy metadata failed (Deprecated)"/> <int value="3" label="Metafile init failed"/> <int value="4" label="0-page preview"/> - <int value="5" label="Mac draft metafile init failed"/> + <int value="5" label="Mac draft metafile init failed (Deprecated)"/> <int value="6" label="PreviewPageRendered with no metafile"/> <int value="7" label="UpdatePrintSettings failed"/> <int value="8" label="Received bad printer settings"/> @@ -77531,6 +77768,9 @@ <int value="32" label="FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_READ"/> <int value="33" label="FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_WRITE"/> <int value="34" label="FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_DELETE"/> + <int value="35" label="FAILURE_RESOURCE_BLACKLIST_UPDATE_BEGIN"/> + <int value="36" label="FAILURE_RESOURCE_BLACKLIST_UPDATE_FINISH"/> + <int value="37" label="FAILURE_RESOURCE_BLACKLIST_DELETE"/> </enum> <enum name="SB2DownloadChecks" type="int"> @@ -82120,6 +82360,9 @@ label="Chooser insta-closed because Chrome isn't allowed to ask for permission to scan for BT devices"/> <int value="11" label="Blacklisted service UUID in filter"/> + <int value="12" label="Bluetooth Overview Help link pressed."/> + <int value="13" label="Adapter Off Help link pressed."/> + <int value="14" label="Need Location Help link pressed."/> </enum> <enum name="WebCertVerifyAgreement" type="int"> @@ -83087,6 +83330,7 @@ <affected-histogram name="Scheduling.BeginMainFrameToCommitDuration"/> <affected-histogram name="Scheduling.CommitToReadyToActivateDuration"/> <affected-histogram name="Scheduling.DrawDuration"/> + <affected-histogram name="Scheduling.MainAndImplFrameTimeDelta"/> <affected-histogram name="Scheduling.PrepareTilesDuration"/> <affected-histogram name="Scheduling.SwapToAckLatency"/> </histogram_suffixes> @@ -87315,6 +87559,7 @@ </suffix> <suffix name="IPBlacklist" label="IPBlacklist"/> <suffix name="UnwantedSoftware" label="UnwantedSoftware"/> + <suffix name="ResourceBlacklist" label="ResourceBlacklist"/> <affected-histogram name="SB2.DatabaseSizeKilobytes"/> <affected-histogram name="SB2.PrefixSetSizeKilobytes"/> </histogram_suffixes>
diff --git a/tools/perf/benchmarks/memory_infra.py b/tools/perf/benchmarks/memory_infra.py index cd70bf9..737f9287 100644 --- a/tools/perf/benchmarks/memory_infra.py +++ b/tools/perf/benchmarks/memory_infra.py
@@ -55,7 +55,7 @@ """Timeline based benchmark for the Memory Health Plan.""" _PREFIX_WHITELIST = ('memory_allocator_', 'memory_android_memtrack_', - 'memory_mmaps_') + 'memory_mmaps_', 'process_count_') page_set = page_sets.MemoryHealthStory
diff --git a/tools/valgrind/gtest_exclude/base_unittests.gtest_mac.txt b/tools/valgrind/gtest_exclude/base_unittests.gtest_mac.txt index 5c579a4..7920daa 100644 --- a/tools/valgrind/gtest_exclude/base_unittests.gtest_mac.txt +++ b/tools/valgrind/gtest_exclude/base_unittests.gtest_mac.txt
@@ -11,9 +11,6 @@ # Fails on Valgrind/Mac, see http://crbug.com/93722 ProcessMemoryTest.MacTerminateOnHeapCorruption -# Fails on Valgrind/Mac, see http://crbug.com/122080 -ProcessMemoryTest.MacMallocFailureDoesNotTerminate - # Times out on Valgrind/Mac, see http://crbug.com/172044 MessageLoopTestTypeUI.RecursivePosts MessageLoopTestTypeIO.RecursivePosts
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt index c80b25d..20626402 100644 --- a/tools/valgrind/memcheck/suppressions.txt +++ b/tools/valgrind/memcheck/suppressions.txt
@@ -2561,30 +2561,6 @@ fun:_ZN7content17UnitTestTestSuiteC1EPN4base9TestSuiteE } { - bug_381156 - Memcheck:Uninitialized - ... - fun:_ZN*14SkTDynamicHashI10SkFlatData* - fun:_ZN16SkFlatDictionaryI7SkPaintNS0_16FlatteningTraitsEE24findAndReturnMutableFlatERKS0_ - fun:_ZN16SkFlatDictionaryI7SkPaintNS0_16FlatteningTraitsEE17findAndReturnFlatERKS0_ - fun:_ZN15SkPictureRecord16getFlatPaintDataERK7SkPaint - fun:_ZN15SkPictureRecord11addPaintPtrEPK7SkPaint - fun:_ZN15SkPictureRecord8addPaintERK7SkPaint - fun:_ZN15SkPictureRecord8drawPathERK6SkPathRK7SkPaint - fun:_ZN12SkBBoxRecord8drawPathERK6SkPathRK7SkPaint - fun:_ZN5blink15GraphicsContext8drawPathERK6SkPathRK7SkPaint - fun:_ZN5blink15GraphicsContext10strokePathERKNS_4PathE - fun:_ZNK7blink14RenderSVGShape11strokeShapeEPNS_15GraphicsContextE - fun:_ZNK7blink13RenderSVGPath11strokeShapeEPNS_15GraphicsContextE - fun:_ZN5blink27RenderSVGResourceSolidColor17postApplyResourceEPNS_12RenderObjectERPNS_15GraphicsContextEtPKNS_4PathEPKNS_14RenderSVGShapeE - fun:_ZN5blink14RenderSVGShape11strokeShapeEPNS_11RenderStyleEPNS_15GraphicsContextE - fun:_ZN5blink14RenderSVGShape5paintERNS_9PaintInfoERKNS_11LayoutPointE - fun:_ZN5blink9RenderBox5paintERNS_9PaintInfoERKNS_11LayoutPointE - fun:_ZN5blink13RenderSVGRoot13paintReplacedERNS_9PaintInfoERKNS_11LayoutPointE - fun:_ZN5blink14RenderReplaced5paintERNS_9PaintInfoERKNS_11LayoutPointE - fun:_ZN5blink11RenderBlock18paintAsInlineBlockEPNS_12RenderObjectERNS_9PaintInfoERKNS_11LayoutPointE -} -{ bug_385381 Memcheck:Unaddressable fun:_ZN5blink23FrameLoaderStateMachine9advanceToENS0_5StateE @@ -3169,11 +3145,10 @@ fun:_ZN7content5Shell15LoadURLForFrameERK4GURLRKSs } { - bug_542543a + bug_542543 Memcheck:Leak fun:_Znw* ... - fun:_ZN8IOThread34ConstructProxyScriptFetcherContextEPNS_7GlobalsEPN3net6NetLogE fun:_ZN8IOThread4InitEv fun:_ZN7content17BrowserThreadImpl4InitEv fun:_ZN7content21TestBrowserThreadImpl4InitEv @@ -3303,16 +3278,6 @@ fun:_ZN5blink9FrameView6layoutEv } { - bug_542543b - Memcheck:Leak - fun:_Znw* - fun:_ZN8IOThread4InitEv - fun:_ZN7content17BrowserThreadImpl4InitEv - fun:_ZN7content21TestBrowserThreadImpl4InitEv - fun:_ZN4base6Thread10ThreadMainEv - fun:_ZN4base12_GLOBAL__N_110ThreadFuncEPv -} -{ bug_571272 Memcheck:Overlap fun:memcpy* @@ -3320,20 +3285,6 @@ fun:_ZN3net17QuicPacketCreator15SerializePacketEPcm } { - bug_571278 - Memcheck:Leak - fun:_Znw* - fun:_ZN3net13DnsSocketPool10CreateNullEPNS_19ClientSocketFactoryE - fun:_ZN3net12_GLOBAL__N_113DnsClientImpl9SetConfigERKNS_9DnsConfigE - fun:_ZN18chrome_browser_net15DnsProbeService32SetPublicClientToGooglePublicDnsEv - fun:_ZN18chrome_browser_net15DnsProbeServiceC1Ev - fun:_ZN8IOThread4InitEv - fun:_ZN7content17BrowserThreadImpl4InitEv - fun:_ZN7content21TestBrowserThreadImpl4InitEv - fun:_ZN4base6Thread10ThreadMainEv - fun:_ZN4base12_GLOBAL__N_110ThreadFuncEPv -} -{ bug_571543 Memcheck:Leak ...
diff --git a/ui/accessibility/ax_enums.idl b/ui/accessibility/ax_enums.idl index bf85883..4583da7 100644 --- a/ui/accessibility/ax_enums.idl +++ b/ui/accessibility/ax_enums.idl
@@ -206,7 +206,6 @@ enabled, // content only expanded, focusable, - focused, haspopup, horizontal, hovered,
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc index fe1b3db..3738db14 100644 --- a/ui/accessibility/ax_node_data.cc +++ b/ui/accessibility/ax_node_data.cc
@@ -273,8 +273,6 @@ result += " EXPANDED"; if (state & (1 << AX_STATE_FOCUSABLE)) result += " FOCUSABLE"; - if (state & (1 << AX_STATE_FOCUSED)) - result += " FOCUSED"; if (state & (1 << AX_STATE_HASPOPUP)) result += " HASPOPUP"; if (state & (1 << AX_STATE_HOVERED))
diff --git a/ui/accessibility/ax_tree_data.cc b/ui/accessibility/ax_tree_data.cc index 9475a59..5094c3b 100644 --- a/ui/accessibility/ax_tree_data.cc +++ b/ui/accessibility/ax_tree_data.cc
@@ -21,6 +21,7 @@ parent_tree_id(-1), loaded(false), loading_progress(0.0), + focus_id(-1), sel_anchor_object_id(-1), sel_anchor_offset(-1), sel_focus_object_id(-1), @@ -54,6 +55,9 @@ if (loading_progress != 0.0) result += " loading_progress=" + DoubleToString(loading_progress); + if (focus_id != -1) + result += " focus_id=" + IntToString(focus_id); + if (sel_anchor_object_id != -1) { result += " sel_anchor_object_id=" + IntToString(sel_anchor_object_id); result += " sel_anchor_offset=" + IntToString(sel_anchor_offset); @@ -75,6 +79,7 @@ lhs.doctype == rhs.doctype && lhs.loaded == rhs.loaded && lhs.loading_progress == rhs.loading_progress && + lhs.focus_id == rhs.focus_id && lhs.sel_anchor_object_id == rhs.sel_anchor_object_id && lhs.sel_anchor_offset == rhs.sel_anchor_offset && lhs.sel_focus_object_id == rhs.sel_focus_object_id &&
diff --git a/ui/accessibility/ax_tree_data.h b/ui/accessibility/ax_tree_data.h index 2922a183..25e0db3d 100644 --- a/ui/accessibility/ax_tree_data.h +++ b/ui/accessibility/ax_tree_data.h
@@ -45,6 +45,10 @@ bool loaded; float loading_progress; + // The node with keyboard focus within this tree, if any, or -1 if no node + // in this tree has focus. + int32_t focus_id; + // The current text selection within this tree, if any, expressed as the // node ID and character offset of the anchor (selection start) and focus // (selection end).
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc index 16706e7..94fc9b8 100644 --- a/ui/accessibility/ax_tree_unittest.cc +++ b/ui/accessibility/ax_tree_unittest.cc
@@ -99,7 +99,7 @@ AXNodeData root; root.id = 1; root.role = AX_ROLE_ROOT_WEB_AREA; - root.state = (1 << AX_STATE_FOCUSABLE) | (1 << AX_STATE_FOCUSED); + root.state = 1 << AX_STATE_FOCUSABLE; root.location = gfx::Rect(0, 0, 800, 600); root.child_ids.push_back(2); root.child_ids.push_back(3); @@ -151,7 +151,7 @@ EXPECT_EQ( "AXTree title=Title\n" - "id=1 rootWebArea FOCUSABLE FOCUSED (0, 0)-(800, 600) child_ids=2,3\n" + "id=1 rootWebArea FOCUSABLE (0, 0)-(800, 600) child_ids=2,3\n" " id=2 button (20, 20)-(200, 30)\n" " id=3 checkBox (20, 50)-(200, 30)\n", dst_tree.ToString());
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc index 801c347..04e96c02 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -448,14 +448,15 @@ atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDED); if (state & (1 << ui::AX_STATE_FOCUSABLE)) atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSABLE); - if (state & (1 << ui::AX_STATE_FOCUSED)) - atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSED); if (state & (1 << ui::AX_STATE_PRESSED)) atk_state_set_add_state(atk_state_set, ATK_STATE_PRESSED); if (state & (1 << ui::AX_STATE_SELECTABLE)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTABLE); if (state & (1 << ui::AX_STATE_SELECTED)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTED); + + if (delegate_->GetFocus() == GetNativeViewAccessible()) + atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSED); } void AXPlatformNodeAuraLinux::GetAtkRelations(AtkRelationSet* atk_relation_set)
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc index ee7a4f9..574ffab9 100644 --- a/ui/accessibility/platform/ax_platform_node_win.cc +++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -982,8 +982,6 @@ msaa_state |= STATE_SYSTEM_EXPANDED; if (state & (1 << ui::AX_STATE_FOCUSABLE)) msaa_state |= STATE_SYSTEM_FOCUSABLE; - if (state & (1 << ui::AX_STATE_FOCUSED)) - msaa_state |= STATE_SYSTEM_FOCUSED; if (state & (1 << ui::AX_STATE_HASPOPUP)) msaa_state |= STATE_SYSTEM_HASPOPUP; if (state & (1 << ui::AX_STATE_HOVERED)) @@ -1007,6 +1005,15 @@ if (state & (1 << ui::AX_STATE_DISABLED)) msaa_state |= STATE_SYSTEM_UNAVAILABLE; + gfx::NativeViewAccessible focus = delegate_->GetFocus(); + if (focus == GetNativeViewAccessible()) + msaa_state |= STATE_SYSTEM_FOCUSED; + + // On Windows, the "focus" bit should be set on certain containers, like + // menu bars, when one of their children has focus. + if (GetData().role == ui::AX_ROLE_MENU_BAR && focus) + msaa_state |= STATE_SYSTEM_FOCUSED; + return msaa_state; }
diff --git a/ui/android/ui_android.gyp b/ui/android/ui_android.gyp index a296833f..d98f1d2 100644 --- a/ui/android/ui_android.gyp +++ b/ui/android/ui_android.gyp
@@ -182,6 +182,7 @@ 'junit/', ], 'test_type': 'junit', + 'wrapper_script_name': 'helper/<(_target_name)', }, 'includes': [ '../../build/android/test_runner.gypi',
diff --git a/ui/app_list/views/apps_container_view.cc b/ui/app_list/views/apps_container_view.cc index c47d3b74..1b72f2d0 100644 --- a/ui/app_list/views/apps_container_view.cc +++ b/ui/app_list/views/apps_container_view.cc
@@ -22,9 +22,7 @@ AppsContainerView::AppsContainerView(AppListMainView* app_list_main_view, AppListModel* model) - : model_(model), - show_state_(SHOW_NONE), - top_icon_animation_pending_count_(0) { + : show_state_(SHOW_NONE), top_icon_animation_pending_count_(0) { apps_grid_view_ = new AppsGridView(app_list_main_view); int cols; int rows; @@ -50,8 +48,8 @@ app_list_folder_view_->SetVisible(false); AddChildView(app_list_folder_view_); - apps_grid_view_->SetModel(model_); - apps_grid_view_->SetItemList(model_->top_level_item_list()); + apps_grid_view_->SetModel(model); + apps_grid_view_->SetItemList(model->top_level_item_list()); SetShowState(SHOW_APPS, false); }
diff --git a/ui/app_list/views/apps_container_view.h b/ui/app_list/views/apps_container_view.h index eb4cdd07..792158a 100644 --- a/ui/app_list/views/apps_container_view.h +++ b/ui/app_list/views/apps_container_view.h
@@ -107,7 +107,6 @@ void PrepareToShowApps(AppListFolderItem* folder_item); - AppListModel* model_; AppsGridView* apps_grid_view_; // Owned by views hierarchy. AppListFolderView* app_list_folder_view_; // Owned by views hierarchy. FolderBackgroundView* folder_background_view_; // Owned by views hierarchy.
diff --git a/ui/app_list/views/search_result_actions_view.cc b/ui/app_list/views/search_result_actions_view.cc index da9d8604..5d8cc09b 100644 --- a/ui/app_list/views/search_result_actions_view.cc +++ b/ui/app_list/views/search_result_actions_view.cc
@@ -54,8 +54,8 @@ SchedulePaint(); if (IsValidActionIndex(selected_action_)) { - child_at(selected_action_)->NotifyAccessibilityEvent( - ui::AX_EVENT_FOCUS, true); + child_at(selected_action_) + ->NotifyAccessibilityEvent(ui::AX_EVENT_HOVER, true); } }
diff --git a/ui/arc/notification/arc_notification_manager.cc b/ui/arc/notification/arc_notification_manager.cc index a98db941..b7dc4a1 100644 --- a/ui/arc/notification/arc_notification_manager.cc +++ b/ui/arc/notification/arc_notification_manager.cc
@@ -68,7 +68,9 @@ return; } - items_.erase(it); + // The removed ArcNotificationItem needs to live in this scope, since the + // given argument |key| may be a part of the removed item. + scoped_ptr<ArcNotificationItem> item(items_.take_and_erase(it)); auto notifications_instance = arc_bridge_service()->notifications_instance();
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc index 2a26699..313e12c7 100644 --- a/ui/compositor/compositor.cc +++ b/ui/compositor/compositor.cc
@@ -144,8 +144,6 @@ settings.initial_debug_state.SetRecordRenderingStats( command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking)); - if (command_line->HasSwitch(cc::switches::kDisableCompositorPropertyTrees)) - settings.use_property_trees = false; settings.use_zero_copy = IsUIZeroCopyEnabled(); settings.renderer_settings.use_rgba_4444_textures =
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn index 779d14d8..53ca55e 100644 --- a/ui/events/BUILD.gn +++ b/ui/events/BUILD.gn
@@ -360,6 +360,7 @@ "latency_info_unittest.cc", "platform/platform_event_source_unittest.cc", "scoped_target_handler_unittest.cc", + "win/event_utils_win_unittest.cc", ] deps = [
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc index 17de4c6..2a191b8 100644 --- a/ui/events/blink/input_handler_proxy.cc +++ b/ui/events/blink/input_handler_proxy.cc
@@ -35,6 +35,8 @@ namespace { +const int32_t kEventDispositionUndefined = -1; + // Maximum time between a fling event's timestamp and the first |Animate| call // for the fling curve to use the fling timestamp as the initial animation time. // Two frames allows a minor delay between event creation and the first animate. @@ -227,7 +229,9 @@ disallow_vertical_fling_scroll_(false), has_fling_animation_started_(false), smooth_scroll_enabled_(false), - uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()) { + uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()), + use_gesture_events_for_mouse_wheel_(true), + touch_start_result_(kEventDispositionUndefined) { DCHECK(client); input_handler_->BindToClient(this); cc::ScrollElasticityHelper* scroll_elasticity_helper = @@ -343,6 +347,12 @@ case WebInputEvent::TouchStart: return HandleTouchStart(static_cast<const WebTouchEvent&>(event)); + case WebInputEvent::TouchMove: + return HandleTouchMove(static_cast<const WebTouchEvent&>(event)); + + case WebInputEvent::TouchEnd: + return HandleTouchEnd(static_cast<const WebTouchEvent&>(event)); + case WebInputEvent::MouseMove: { const WebMouseEvent& mouse_event = static_cast<const WebMouseEvent&>(event); @@ -426,6 +436,33 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel( const WebMouseWheelEvent& wheel_event) { + // Only call |CancelCurrentFling()| if a fling was active, as it will + // otherwise disrupt an in-progress touch scroll. + if (!wheel_event.hasPreciseScrollingDeltas && fling_curve_) + CancelCurrentFling(); + + if (use_gesture_events_for_mouse_wheel_) { + cc::EventListenerProperties properties = + input_handler_->GetEventListenerProperties( + cc::EventListenerClass::kMouseWheel); + switch (properties) { + case cc::EventListenerProperties::kPassive: + return DID_HANDLE_NON_BLOCKING; + case cc::EventListenerProperties::kBlockingAndPassive: + case cc::EventListenerProperties::kBlocking: + return DID_NOT_HANDLE; + case cc::EventListenerProperties::kNone: + return DROP_EVENT; + default: + NOTREACHED(); + return DROP_EVENT; + } + } + return ScrollByMouseWheel(wheel_event); +} + +InputHandlerProxy::EventDisposition InputHandlerProxy::ScrollByMouseWheel( + const WebMouseWheelEvent& wheel_event) { InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE; cc::InputHandlerScrollResult scroll_result; @@ -734,18 +771,67 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart( const blink::WebTouchEvent& touch_event) { + EventDisposition result = DROP_EVENT; for (size_t i = 0; i < touch_event.touchesLength; ++i) { if (touch_event.touches[i].state != WebTouchPoint::StatePressed) continue; if (input_handler_->DoTouchEventsBlockScrollAt( gfx::Point(touch_event.touches[i].position.x, touch_event.touches[i].position.y))) { - // TODO(rbyers): We should consider still sending the touch events to - // main asynchronously (crbug.com/455539). - return DID_NOT_HANDLE; + result = DID_NOT_HANDLE; + break; } } - return DROP_EVENT; + + // If |result| is DROP_EVENT it wasn't processed above. + if (result == DROP_EVENT) { + switch (input_handler_->GetEventListenerProperties( + cc::EventListenerClass::kTouch)) { + case cc::EventListenerProperties::kPassive: + result = DID_HANDLE_NON_BLOCKING; + break; + case cc::EventListenerProperties::kBlocking: + // The touch area rects above already have checked whether it hits + // a blocking region. Since it does not the event can be dropped. + result = DROP_EVENT; + break; + case cc::EventListenerProperties::kBlockingAndPassive: + // There is at least one passive listener that needs to possibly + // be notified so it can't be dropped. + result = DID_HANDLE_NON_BLOCKING; + break; + case cc::EventListenerProperties::kNone: + result = DROP_EVENT; + break; + default: + NOTREACHED(); + result = DROP_EVENT; + break; + } + } + + // Merge |touch_start_result_| and |result| so the result has the highest + // priority value according to the sequence; (DROP_EVENT, + // DID_HANDLE_NON_BLOCKING, DID_NOT_HANDLE). + if (touch_start_result_ == kEventDispositionUndefined || + touch_start_result_ == DROP_EVENT || result == DID_NOT_HANDLE) + touch_start_result_ = result; + + return result; +} + +InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchMove( + const blink::WebTouchEvent& touch_event) { + if (touch_start_result_ != kEventDispositionUndefined) + return static_cast<EventDisposition>(touch_start_result_); + return DID_NOT_HANDLE; +} + +InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchEnd( + const blink::WebTouchEvent& touch_event) { + if (touch_event.touchesLength == 1) + touch_start_result_ = kEventDispositionUndefined; + return DID_NOT_HANDLE; } bool InputHandlerProxy::FilterInputEventForFlingBoosting( @@ -1089,25 +1175,47 @@ bool InputHandlerProxy::TouchpadFlingScroll( const WebFloatSize& increment) { - WebMouseWheelEvent synthetic_wheel; - synthetic_wheel.type = WebInputEvent::MouseWheel; - synthetic_wheel.timeStampSeconds = InSecondsF(base::TimeTicks::Now()); - synthetic_wheel.deltaX = increment.width; - synthetic_wheel.deltaY = increment.height; - synthetic_wheel.hasPreciseScrollingDeltas = true; - synthetic_wheel.x = fling_parameters_.point.x; - synthetic_wheel.y = fling_parameters_.point.y; - synthetic_wheel.globalX = fling_parameters_.globalPoint.x; - synthetic_wheel.globalY = fling_parameters_.globalPoint.y; - synthetic_wheel.modifiers = fling_parameters_.modifiers; + InputHandlerProxy::EventDisposition disposition; + cc::EventListenerProperties properties = + input_handler_->GetEventListenerProperties( + cc::EventListenerClass::kMouseWheel); + switch (properties) { + case cc::EventListenerProperties::kPassive: + disposition = DID_HANDLE_NON_BLOCKING; + break; + case cc::EventListenerProperties::kBlocking: + disposition = DID_NOT_HANDLE; + break; + case cc::EventListenerProperties::kNone: { + WebMouseWheelEvent synthetic_wheel; + synthetic_wheel.type = WebInputEvent::MouseWheel; + synthetic_wheel.timeStampSeconds = InSecondsF(base::TimeTicks::Now()); + synthetic_wheel.deltaX = increment.width; + synthetic_wheel.deltaY = increment.height; + synthetic_wheel.hasPreciseScrollingDeltas = true; + synthetic_wheel.x = fling_parameters_.point.x; + synthetic_wheel.y = fling_parameters_.point.y; + synthetic_wheel.globalX = fling_parameters_.globalPoint.x; + synthetic_wheel.globalY = fling_parameters_.globalPoint.y; + synthetic_wheel.modifiers = fling_parameters_.modifiers; - InputHandlerProxy::EventDisposition disposition = - HandleInputEvent(synthetic_wheel); + disposition = ScrollByMouseWheel(synthetic_wheel); + break; + } + default: + NOTREACHED(); + return false; + } + switch (disposition) { case DID_HANDLE: return true; case DROP_EVENT: break; + case DID_HANDLE_NON_BLOCKING: + // TODO(dtapuska): Process the fling on the compositor thread + // but post the events to the main thread; for now just pass it to the + // main thread. case DID_NOT_HANDLE: TRACE_EVENT_INSTANT0("input", "InputHandlerProxy::scrollBy::AbortFling",
diff --git a/ui/events/blink/input_handler_proxy.h b/ui/events/blink/input_handler_proxy.h index e96b31bf..32c0efc 100644 --- a/ui/events/blink/input_handler_proxy.h +++ b/ui/events/blink/input_handler_proxy.h
@@ -46,10 +46,14 @@ } void set_smooth_scroll_enabled(bool value) { smooth_scroll_enabled_ = value; } + void set_use_gesture_events_for_mouse_wheel(bool value) { + use_gesture_events_for_mouse_wheel_ = value; + } enum EventDisposition { DID_HANDLE, DID_NOT_HANDLE, + DID_HANDLE_NON_BLOCKING, DROP_EVENT }; EventDisposition HandleInputEventWithLatencyInfo( @@ -91,6 +95,7 @@ // Helper functions for handling more complicated input events. EventDisposition HandleMouseWheel( const blink::WebMouseWheelEvent& event); + EventDisposition ScrollByMouseWheel(const blink::WebMouseWheelEvent& event); EventDisposition HandleGestureScrollBegin( const blink::WebGestureEvent& event); EventDisposition HandleGestureScrollUpdate( @@ -99,8 +104,9 @@ const blink::WebGestureEvent& event); EventDisposition HandleGestureFlingStart( const blink::WebGestureEvent& event); - EventDisposition HandleTouchStart( - const blink::WebTouchEvent& event); + EventDisposition HandleTouchStart(const blink::WebTouchEvent& event); + EventDisposition HandleTouchMove(const blink::WebTouchEvent& event); + EventDisposition HandleTouchEnd(const blink::WebTouchEvent& event); // Returns true if the event should be suppressed due to to an active, // boost-enabled fling, in which case further processing should cease. @@ -184,8 +190,13 @@ scoped_ptr<InputScrollElasticityController> scroll_elasticity_controller_; bool smooth_scroll_enabled_; - bool uma_latency_reporting_enabled_; + bool use_gesture_events_for_mouse_wheel_; + + // The merged result of the last touch start with previous touch starts. + // This value will get returned for subsequent TouchMove events to allow + // passive events not to block scrolling. + int32_t touch_start_result_; base::TimeTicks last_fling_animate_time_;
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc index 8106b12..08358efb 100644 --- a/ui/events/blink/input_handler_proxy_unittest.cc +++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -369,6 +369,10 @@ input_handler_->smooth_scroll_enabled_ = value; } + void SetMouseWheelGesturesOn(bool value) { + input_handler_->set_use_gesture_events_for_mouse_wheel(value); + } + protected: const bool synchronous_root_scroll_; const bool install_synchronous_handler_; @@ -385,6 +389,7 @@ TEST_P(InputHandlerProxyTest, MouseWheelByPageMainThread) { expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + SetMouseWheelGesturesOn(false); WebMouseWheelEvent wheel; wheel.type = WebInputEvent::MouseWheel; wheel.scrollByPage = true; @@ -395,6 +400,7 @@ TEST_P(InputHandlerProxyTest, MouseWheelWithCtrlNotScroll) { expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + SetMouseWheelGesturesOn(false); WebMouseWheelEvent wheel; wheel.type = WebInputEvent::MouseWheel; wheel.modifiers = WebInputEvent::ControlKey; @@ -403,6 +409,45 @@ VERIFY_AND_RESET_MOCKS(); } +TEST_P(InputHandlerProxyTest, MouseWheelNoListener) { + expected_disposition_ = InputHandlerProxy::DROP_EVENT; + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); + + WebMouseWheelEvent wheel; + wheel.type = WebInputEvent::MouseWheel; + wheel.modifiers = WebInputEvent::ControlKey; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); + VERIFY_AND_RESET_MOCKS(); +} + +TEST_P(InputHandlerProxyTest, MouseWheelPassiveListener) { + expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); + + WebMouseWheelEvent wheel; + wheel.type = WebInputEvent::MouseWheel; + wheel.modifiers = WebInputEvent::ControlKey; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); + VERIFY_AND_RESET_MOCKS(); +} + +TEST_P(InputHandlerProxyTest, MouseWheelBlockingListener) { + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); + + WebMouseWheelEvent wheel; + wheel.type = WebInputEvent::MouseWheel; + wheel.modifiers = WebInputEvent::ControlKey; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); + VERIFY_AND_RESET_MOCKS(); +} + // Mac does not smooth scroll wheel events (crbug.com/574283). #if !defined(OS_MACOSX) TEST_P(InputHandlerProxyTest, MouseWheelWithPreciseScrollingDeltas) { @@ -410,6 +455,7 @@ TEST_P(InputHandlerProxyTest, DISABLED_MouseWheelWithPreciseScrollingDeltas) { #endif SetSmoothScrollEnabled(true); + SetMouseWheelGesturesOn(false); expected_disposition_ = InputHandlerProxy::DID_HANDLE; WebMouseWheelEvent wheel; wheel.type = WebInputEvent::MouseWheel; @@ -443,6 +489,7 @@ TEST_P(InputHandlerProxyTest, DISABLED_MouseWheelScrollIgnored) { #endif SetSmoothScrollEnabled(true); + SetMouseWheelGesturesOn(false); expected_disposition_ = InputHandlerProxy::DROP_EVENT; WebMouseWheelEvent wheel; wheel.type = WebInputEvent::MouseWheel; @@ -877,6 +924,7 @@ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); + // The first animate call should let us pick up an animation start time, but // we shouldn't actually move anywhere just yet. The first frame after the // fling start will typically include the last scroll from the gesture that @@ -892,6 +940,9 @@ // The second call should start scrolling in the -X direction. EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( @@ -909,6 +960,9 @@ // We also should pass the current fling parameters out to the client so the // rest of the fling can be // transferred to the main thread. + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kMainThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); @@ -959,6 +1013,97 @@ VERIFY_AND_RESET_MOCKS(); } +TEST_P(InputHandlerProxyTest, GestureFlingPassiveListener) { + // We shouldn't send any events to the widget for this gesture. + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + VERIFY_AND_RESET_MOCKS(); + + // On the fling start, we should schedule an animation but not actually start + // scrolling. + gesture_.type = WebInputEvent::GestureFlingStart; + WebFloatPoint fling_delta = WebFloatPoint(1000, 0); + WebPoint fling_point = WebPoint(7, 13); + WebPoint fling_global_point = WebPoint(17, 23); + // Note that for trackpad, wheel events with the Control modifier are + // special (reserved for zoom), so don't set that here. + int modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey; + gesture_ = CreateFling(blink::WebGestureDeviceTouchpad, fling_delta, + fling_point, fling_global_point, modifiers); + EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); + + // The first animate call should let us pick up an animation start time, but + // we shouldn't actually move anywhere just yet. The first frame after the + // fling start will typically include the last scroll from the gesture that + // lead to the scroll (either wheel or gesture scroll), so there should be no + // visible hitch. + EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .Times(0); + base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); + Animate(time); + + VERIFY_AND_RESET_MOCKS(); + + // The second call should punt the fling to the main thread + // because of a passive event listener. + EXPECT_SET_NEEDS_ANIMATE_INPUT(0); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .Times(0); + EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); + // Expected wheel fling animation parameters: + // *) fling_delta and fling_point should match the original GestureFlingStart + // event + // *) startTime should be 10 to match the time parameter of the first + // Animate() call after the GestureFlingStart + EXPECT_CALL( + mock_client_, + TransferActiveWheelFlingAnimation(testing::AllOf( + testing::Field(&WebActiveWheelFlingParameters::delta, + testing::Eq(fling_delta)), + testing::Field(&WebActiveWheelFlingParameters::point, + testing::Eq(fling_point)), + testing::Field(&WebActiveWheelFlingParameters::globalPoint, + testing::Eq(fling_global_point)), + testing::Field(&WebActiveWheelFlingParameters::modifiers, + testing::Eq(modifiers)), + testing::Field(&WebActiveWheelFlingParameters::startTime, + testing::Eq(10)), + testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll, + testing::_)))); + time += base::TimeDelta::FromMilliseconds(100); + Animate(time); + + VERIFY_AND_RESET_MOCKS(); + + // Since we've aborted the fling, the next animation should be a no-op and + // should not result in another + // frame being requested. + EXPECT_SET_NEEDS_ANIMATE_INPUT(0); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .Times(0); + time += base::TimeDelta::FromMilliseconds(100); + Animate(time); + + // Since we've transferred the fling to the main thread, we need to pass the + // next GestureFlingCancel to the main + // thread as well. + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + gesture_.type = WebInputEvent::GestureFlingCancel; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); +} + TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE; @@ -981,7 +1126,6 @@ .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - VERIFY_AND_RESET_MOCKS(); // Start the fling animation at time 10. This shouldn't actually scroll, just @@ -996,6 +1140,9 @@ // The second call should start scrolling in the -X direction. EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( @@ -1013,6 +1160,9 @@ // We also should pass the current fling parameters out to the client so the // rest of the fling can be // transferred to the main thread. + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kMainThreadScrollState)); @@ -1097,6 +1247,9 @@ // Tick the second fling once normally. EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( @@ -1110,6 +1263,9 @@ VERIFY_AND_RESET_MOCKS(); // Then abort the second fling. + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kMainThreadScrollState)); EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); @@ -1569,6 +1725,9 @@ VERIFY_AND_RESET_MOCKS(); // The second animate starts scrolling in the positive X and Y directions. + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( @@ -1588,6 +1747,9 @@ overscroll.did_overscroll_root = true; overscroll.accumulated_root_overscroll = gfx::Vector2dF(0, 100); overscroll.unused_scroll_delta = gfx::Vector2dF(0, 10); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( @@ -1609,6 +1771,9 @@ // The next call to animate will no longer scroll vertically. EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kImplThreadScrollState)); EXPECT_CALL( @@ -1807,8 +1972,9 @@ VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, - DoTouchEventsBlockScrollAt( - testing::Property(&gfx::Point::x, testing::Gt(0)))) + GetEventListenerProperties(cc::EventListenerClass::kTouch)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); + EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt(testing::_)) .WillOnce(testing::Return(false)); EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt( @@ -1856,6 +2022,30 @@ VERIFY_AND_RESET_MOCKS(); } +TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPassivePositive) { + // One of the touch points is on a touch-region. So the event should be sent + // to the main thread. + expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kTouch)) + .WillRepeatedly(testing::Return(cc::EventListenerProperties::kPassive)); + EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt(testing::_)) + .WillRepeatedly(testing::Return(false)); + + WebTouchEvent touch; + touch.type = WebInputEvent::TouchStart; + + touch.touchesLength = 3; + touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 0, 0); + touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 10, 10); + touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::StatePressed, -10, 10); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch)); + + VERIFY_AND_RESET_MOCKS(); +} + TEST_P(InputHandlerProxyTest, GestureFlingCancelledByKeyboardEvent) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE; @@ -1908,6 +2098,71 @@ EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); } +TEST_P(InputHandlerProxyTest, GestureFlingCancelledByWheelEvent) { + // We shouldn't send any events to the widget for this gesture. + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + + gesture_.type = WebInputEvent::GestureScrollBegin; + gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen; + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); + VERIFY_AND_RESET_MOCKS(); + + // Wheel events received during a scroll shouldn't cancel the fling, but will + // cause scrolling. + cc::InputHandlerScrollResult result; + + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); + + WebMouseWheelEvent wheel_event; + wheel_event.type = WebInputEvent::MouseWheel; + input_handler_->HandleInputEvent(wheel_event); + EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); + VERIFY_AND_RESET_MOCKS(); + + // On the fling start, animation should be scheduled, but no scrolling occurs. + gesture_.type = WebInputEvent::GestureFlingStart; + WebFloatPoint fling_delta = WebFloatPoint(100, 100); + gesture_.data.flingStart.velocityX = fling_delta.x; + gesture_.data.flingStart.velocityY = fling_delta.y; + EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) + .WillOnce(testing::Return(kImplThreadScrollState)); + EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); + VERIFY_AND_RESET_MOCKS(); + + // Wheel events received during a fling should cancel the active fling, and + // cause a scroll. + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); + + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); + + + input_handler_->HandleInputEvent(wheel_event); + EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); + VERIFY_AND_RESET_MOCKS(); + + // The call to animate should have no effect, as the fling was cancelled. + base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); + Animate(time); + VERIFY_AND_RESET_MOCKS(); + + // A fling cancel should be dropped, as there is nothing to cancel. + gesture_.type = WebInputEvent::GestureFlingCancel; + EXPECT_EQ(InputHandlerProxy::DROP_EVENT, + input_handler_->HandleInputEvent(gesture_)); + EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); +} + TEST_P(InputHandlerProxyTest, GestureFlingWithNegativeTimeDelta) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE;
diff --git a/ui/events/events_unittests.gyp b/ui/events/events_unittests.gyp index e0f2a01..f5a5d01c 100644 --- a/ui/events/events_unittests.gyp +++ b/ui/events/events_unittests.gyp
@@ -61,6 +61,7 @@ 'latency_info_unittest.cc', 'platform/platform_event_source_unittest.cc', 'scoped_target_handler_unittest.cc', + 'win/event_utils_win_unittest.cc', 'x/events_x_unittest.cc', ], 'include_dirs': [
diff --git a/ui/events/keycodes/platform_key_map_win.cc b/ui/events/keycodes/platform_key_map_win.cc index 52a44d5..6a95fd7 100644 --- a/ui/events/keycodes/platform_key_map_win.cc +++ b/ui/events/keycodes/platform_key_map_win.cc
@@ -163,6 +163,10 @@ if (layout == keyboard_layout_) return; + BYTE keyboard_state_to_restore[256]; + if (!::GetKeyboardState(keyboard_state_to_restore)) + return; + // TODO(chongz): Optimize layout switching (see crbug.com/587147). keyboard_layout_ = layout; code_to_key_.clear(); @@ -170,9 +174,6 @@ // US: 428, French: 554, Persian: 434, Vietnamese: 1388 code_to_key_.reserve(500); - BYTE keyboard_state_to_restore[256]; - ::GetKeyboardState(keyboard_state_to_restore); - for (int eindex = 0; eindex <= kModifierFlagsCombinations; ++eindex) { BYTE keyboard_state[256]; memset(keyboard_state, 0, sizeof(keyboard_state));
diff --git a/ui/events/win/event_utils_win_unittest.cc b/ui/events/win/event_utils_win_unittest.cc new file mode 100644 index 0000000..a4efce4f --- /dev/null +++ b/ui/events/win/event_utils_win_unittest.cc
@@ -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. + +#include "ui/events/event_utils.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/win/window_impl.h" + +namespace ui { + +namespace { + +class TestWindow : public gfx::WindowImpl { + public: + TestWindow() {} + ~TestWindow() override {} + + BOOL ProcessWindowMessage(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT& result, + DWORD msg_map_id = 0) override { + return true; + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestWindow); +}; + +MSG CreateEvent(UINT type, WORD x, WORD y, HWND hwnd) { + MSG event; + event.message = type; + event.hwnd = hwnd; + event.lParam = MAKELPARAM(x, y); + return event; +} + +TEST(EventWinTest, EventSystemLocationFromNative) { + TestWindow test_window; + const WORD x_coord = 10; + const WORD y_coord = 20; + const WORD x_window_offset = 100; + const WORD y_window_offset = 100; + test_window.Init(nullptr, + gfx::Rect(x_window_offset, y_window_offset, 100, 100)); + EXPECT_TRUE(test_window.hwnd() != nullptr); + + { + MSG event = + CreateEvent(WM_MOUSEWHEEL, x_coord, y_coord, test_window.hwnd()); + // Mouse wheel events already have screen coordinates so they should not be + // converted. + EXPECT_EQ(gfx::Point(x_coord, y_coord), + EventSystemLocationFromNative(event)); + } + + MSG event = CreateEvent(WM_LBUTTONDOWN, x_coord, y_coord, test_window.hwnd()); + EXPECT_EQ(gfx::Point(x_coord + x_window_offset, y_coord + y_window_offset), + EventSystemLocationFromNative(event)); +} + +} // namespace + +} // namespace ui
diff --git a/ui/events/win/events_win.cc b/ui/events/win/events_win.cc index e4573312..49b67400 100644 --- a/ui/events/win/events_win.cc +++ b/ui/events/win/events_win.cc
@@ -260,7 +260,9 @@ const base::NativeEvent& native_event) { POINT global_point = { static_cast<short>(LOWORD(native_event.lParam)), static_cast<short>(HIWORD(native_event.lParam)) }; - ClientToScreen(native_event.hwnd, &global_point); + // Wheel events have position in screen coordinates. + if (!IsMouseWheelEvent(native_event)) + ClientToScreen(native_event.hwnd, &global_point); return gfx::Point(global_point); }
diff --git a/ui/file_manager/file_manager/foreground/css/common.css b/ui/file_manager/file_manager/foreground/css/common.css index 6b146732..42d67c44 100644 --- a/ui/file_manager/file_manager/foreground/css/common.css +++ b/ui/file_manager/file_manager/foreground/css/common.css
@@ -320,6 +320,16 @@ opacity: 1; } +.files-alert-dialog .cr-dialog-close, +.files-confirm-dialog .cr-dialog-close { + display: none; +} + +.files-alert-dialog .cr-dialog-text, +.files-confirm-dialog .cr-dialog-text { + font-size: 14px; +} + /* Minor tweak of vertical position for texts which need to be vertically * aligned with corresponding file-type icons. */ .tree-row > .label,
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css index 839c16e7..45f806f 100644 --- a/ui/file_manager/file_manager/foreground/css/file_manager.css +++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -497,7 +497,7 @@ top: 42px; /* Positioned just overlapping the top of the toolbar */ transition: all 0.2s ease; width: 270px; - z-index: 5; + z-index: 550; } /* These horizontal positions for #cloud-import-details are fallback value.
diff --git a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp index 0417450..38abceb 100644 --- a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp +++ b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
@@ -133,6 +133,8 @@ './ui/file_manager_ui.js', './ui/file_table.js', './ui/file_table_list.js', + './ui/files_alert_dialog.js', + './ui/files_confirm_dialog.js', './ui/files_menu.js', './ui/gear_menu.js', './ui/list_container.js',
diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js index e61c262..e826ffd 100644 --- a/ui/file_manager/file_manager/foreground/js/main_scripts.js +++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js
@@ -150,6 +150,8 @@ //<include src="ui/file_list_selection_model.js"> //<include src="ui/file_table.js"> //<include src="ui/file_table_list.js"> +//<include src="ui/files_alert_dialog.js"> +//<include src="ui/files_confirm_dialog.js"> //<include src="ui/files_menu.js"> //<include src="ui/gear_menu.js"> //<include src="ui/list_container.js">
diff --git a/ui/file_manager/file_manager/foreground/js/mouse_inactivity_watcher.js b/ui/file_manager/file_manager/foreground/js/mouse_inactivity_watcher.js index ceac19d..fb9e1f6 100644 --- a/ui/file_manager/file_manager/foreground/js/mouse_inactivity_watcher.js +++ b/ui/file_manager/file_manager/foreground/js/mouse_inactivity_watcher.js
@@ -30,16 +30,6 @@ * @private **/ this.disabled_ = false; - this.__defineSetter__('disabled', function(value) { - this.disabled_ = value; - if (value) - this.kick(); - else - this.check(); - }); - this.__defineGetter__('disabled', function() { - return this.disabled_; - }); this.container_.addEventListener('mousemove', this.onMouseMove_.bind(this)); var tools = this.container_.querySelector('.tool'); @@ -71,6 +61,29 @@ MouseInactivityWatcher.DEFAULT_TIMEOUT = 3000; /** + * Defines getter/setter for disabled property to update inactivity state. + */ +MouseInactivityWatcher.prototype = { + /** + * @return {boolean} + */ + get disabled() { + return this.disabled_; + }, + + /** + * @param {boolean} value + */ + set disabled(value) { + this.disabled_ = value; + if (value) + this.kick(); + else + this.check(); + } +}; + +/** * @param {boolean} on True if show, false if hide. */ MouseInactivityWatcher.prototype.showTools = function(on) {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js index cecc345..8f4db8a 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js +++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -42,24 +42,24 @@ /** * Alert dialog. - * @type {!cr.ui.dialogs.AlertDialog} + * @type {!FilesAlertDialog} * @const */ - this.alertDialog = new cr.ui.dialogs.AlertDialog(this.element); + this.alertDialog = new FilesAlertDialog(this.element); /** * Confirm dialog. - * @type {!cr.ui.dialogs.ConfirmDialog} + * @type {!FilesConfirmDialog} * @const */ - this.confirmDialog = new cr.ui.dialogs.ConfirmDialog(this.element); + this.confirmDialog = new FilesConfirmDialog(this.element); /** * Confirm dialog for delete. - * @type {!cr.ui.dialogs.ConfirmDialog} + * @type {!FilesConfirmDialog} * @const */ - this.deleteConfirmDialog = new cr.ui.dialogs.ConfirmDialog(this.element); + this.deleteConfirmDialog = new FilesConfirmDialog(this.element); this.deleteConfirmDialog.setOkLabel(str('DELETE_BUTTON_LABEL')); /**
diff --git a/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js new file mode 100644 index 0000000..3c1d404 --- /dev/null +++ b/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js
@@ -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. + +/** + * Alert dialog. + * @param {!HTMLElement} parentNode + * @constructor + * @extends {cr.ui.dialogs.AlertDialog} + */ +var FilesAlertDialog = function(parentNode) { + cr.ui.dialogs.AlertDialog.call(this, parentNode); +}; + +FilesAlertDialog.prototype.__proto__ = cr.ui.dialogs.AlertDialog.prototype; + +/** + * @private + * @override + */ +FilesAlertDialog.prototype.initDom_ = function() { + cr.ui.dialogs.AlertDialog.prototype.initDom_.call(this); + this.frame_.classList.add('files-alert-dialog'); +};
diff --git a/ui/file_manager/file_manager/foreground/js/ui/files_confirm_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/files_confirm_dialog.js new file mode 100644 index 0000000..85e03460 --- /dev/null +++ b/ui/file_manager/file_manager/foreground/js/ui/files_confirm_dialog.js
@@ -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. + +/** + * Confirm dialog. + * @param {!HTMLElement} parentNode + * @constructor + * @extends {cr.ui.dialogs.ConfirmDialog} + */ +var FilesConfirmDialog = function(parentNode) { + cr.ui.dialogs.ConfirmDialog.call(this, parentNode); +}; + +FilesConfirmDialog.prototype.__proto__ = cr.ui.dialogs.ConfirmDialog.prototype; + +/** + * @private + * @override + */ +FilesConfirmDialog.prototype.initDom_ = function() { + cr.ui.dialogs.ConfirmDialog.prototype.initDom_.call(this); + this.frame_.classList.add('files-confirm-dialog'); +};
diff --git a/ui/file_manager/gallery/js/compiled_resources.gyp b/ui/file_manager/gallery/js/compiled_resources.gyp index 6a32842..781e4dd 100644 --- a/ui/file_manager/gallery/js/compiled_resources.gyp +++ b/ui/file_manager/gallery/js/compiled_resources.gyp
@@ -104,6 +104,7 @@ '../../file_manager/foreground/js/share_client.js', '../../file_manager/foreground/js/thumbnail_loader.js', '../../file_manager/foreground/js/ui/file_manager_dialog_base.js', + '../../file_manager/foreground/js/ui/files_confirm_dialog.js', '../../file_manager/foreground/js/ui/share_dialog.js', '../../file_manager/foreground/js/volume_manager_wrapper.js', '../../file_manager/background/js/volume_manager.js',
diff --git a/ui/file_manager/gallery/js/gallery.js b/ui/file_manager/gallery/js/gallery.js index ae0f21f..3447acc3 100644 --- a/ui/file_manager/gallery/js/gallery.js +++ b/ui/file_manager/gallery/js/gallery.js
@@ -660,8 +660,7 @@ this.document_.body.addEventListener('keydown', this.keyDownBound_); }.bind(this); - - var confirm = new cr.ui.dialogs.ConfirmDialog(this.container_); + var confirm = new FilesConfirmDialog(this.container_); confirm.setOkLabel(str('DELETE_BUTTON_LABEL')); confirm.show(strf(plural ? 'GALLERY_CONFIRM_DELETE_SOME' : 'GALLERY_CONFIRM_DELETE_ONE', param),
diff --git a/ui/file_manager/gallery/js/gallery_scripts.js b/ui/file_manager/gallery/js/gallery_scripts.js index 4871df5..ccdd008 100644 --- a/ui/file_manager/gallery/js/gallery_scripts.js +++ b/ui/file_manager/gallery/js/gallery_scripts.js
@@ -57,6 +57,7 @@ //<include src="../../file_manager/foreground/js/share_client.js"> //<include src="../../file_manager/foreground/js/thumbnail_loader.js"> //<include src="../../file_manager/foreground/js/ui/file_manager_dialog_base.js"> +//<include src="../../file_manager/foreground/js/ui/files_confirm_dialog.js"> //<include src="../../file_manager/foreground/js/ui/share_dialog.js"> //<include src="../../file_manager/foreground/js/volume_manager_wrapper.js">
diff --git a/ui/file_manager/video_player/js/cast/caster.js b/ui/file_manager/video_player/js/cast/caster.js index 2dd9e9b..70bc28ca 100644 --- a/ui/file_manager/video_player/js/cast/caster.js +++ b/ui/file_manager/video_player/js/cast/caster.js
@@ -6,7 +6,9 @@ // TODO(yoshiki): Remove this once the cast extension supports Chrome apps. // Although localStorage in Chrome app is not supported, but it's used in the // cast extension. This line prevents an exception on using localStorage. -window.__defineGetter__('localStorage', function() { return {}; }); +Object.defineProperty(window, 'localStorage', { + get: function() { return {}; } +}); /** * @type {string}
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn index d014bd7a..8fd50bd3b 100644 --- a/ui/gfx/BUILD.gn +++ b/ui/gfx/BUILD.gn
@@ -103,8 +103,6 @@ "generic_shared_memory_id.h", "gfx_paths.cc", "gfx_paths.h", - "hud_font.cc", - "hud_font.h", "icon_util.cc", "icon_util.h", "image/image.cc",
diff --git a/ui/gfx/gfx.gyp b/ui/gfx/gfx.gyp index 4519db9e..0d8d2d4 100644 --- a/ui/gfx/gfx.gyp +++ b/ui/gfx/gfx.gyp
@@ -185,8 +185,6 @@ 'gfx_paths.h', 'harfbuzz_font_skia.cc', 'harfbuzz_font_skia.h', - 'hud_font.cc', - 'hud_font.h', 'image/canvas_image_source.cc', 'image/canvas_image_source.h', 'image/image.cc',
diff --git a/ui/gfx/hud_font.cc b/ui/gfx/hud_font.cc deleted file mode 100644 index 815177885..0000000 --- a/ui/gfx/hud_font.cc +++ /dev/null
@@ -1,25 +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. - -#include "ui/gfx/hud_font.h" - -#include "base/lazy_instance.h" -#include "third_party/skia/include/core/SkTypeface.h" - -namespace gfx { - -namespace { -base::LazyInstance<skia::RefPtr<SkTypeface>> g_hud_typeface; -} // namespace - -void SetHudTypeface(skia::RefPtr<SkTypeface> typeface) { - g_hud_typeface.Get() = typeface; -} - -skia::RefPtr<SkTypeface> GetHudTypeface() { - // nullptr is fine; caller will create its own in that case. - return g_hud_typeface.Get(); -} - -} // namespace gfx
diff --git a/ui/gfx/hud_font.h b/ui/gfx/hud_font.h deleted file mode 100644 index f72a050..0000000 --- a/ui/gfx/hud_font.h +++ /dev/null
@@ -1,20 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_GFX_HUD_FONT_H_ -#define UI_GFX_HUD_FONT_H_ - -#include "skia/ext/refptr.h" -#include "ui/gfx/gfx_export.h" - -class SkTypeface; - -namespace gfx { - -GFX_EXPORT void SetHudTypeface(skia::RefPtr<SkTypeface> typeface); -GFX_EXPORT skia::RefPtr<SkTypeface> GetHudTypeface(); - -} // namespace gfx - -#endif // UI_GFX_HUD_FONT_H_
diff --git a/ui/gl/gl_context_egl.cc b/ui/gl/gl_context_egl.cc index 8a9e4e4..569c2cc 100644 --- a/ui/gl/gl_context_egl.cc +++ b/ui/gl/gl_context_egl.cc
@@ -70,6 +70,13 @@ context_attributes = kContextAttributes; } + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + LOG(ERROR) << "eglBindApi failed with error " + << GetLastEGLErrorString(); + return false; + } + + context_ = eglCreateContext( display_, config_,
diff --git a/ui/gl/gl_surface_win.cc b/ui/gl/gl_surface_win.cc index 66061d2c..abff8df1 100644 --- a/ui/gl/gl_surface_win.cc +++ b/ui/gl/gl_surface_win.cc
@@ -55,6 +55,8 @@ // Helper routine that does one-off initialization like determining the // pixel format. bool GLSurface::InitializeOneOffInternal() { + VSyncProviderWin::InitializeOneOff(); + switch (GetGLImplementation()) { case kGLImplementationDesktopGL: if (!GLSurfaceWGL::InitializeOneOff()) {
diff --git a/ui/gl/gpu_timing.cc b/ui/gl/gpu_timing.cc index c6e574a..d8c0127 100644 --- a/ui/gl/gpu_timing.cc +++ b/ui/gl/gpu_timing.cc
@@ -317,9 +317,16 @@ DCHECK(context); const GLVersionInfo* version_info = context->GetVersionInfo(); DCHECK(version_info); - if (version_info->is_es3 && // glGetInteger64v is supported under ES3. - context->HasExtension("GL_EXT_disjoint_timer_query")) { + if (context->HasExtension("GL_EXT_disjoint_timer_query")) { timer_type_ = GPUTiming::kTimerTypeDisjoint; + if (!version_info->is_es3) { + // Due to a bug in the specification, glGetInteger64v is only supported + // under ES3. Since it is only used for timestamps, we workaround this by + // emulating timestamps on ES2 so WebGL 1.0 will still have access to the + // extension + force_time_elapsed_query_ = true; + timestamp_bit_count_gl_ = 0; + } } else if (context->HasExtension("GL_ARB_timer_query")) { timer_type_ = GPUTiming::kTimerTypeARB; } else if (context->HasExtension("GL_EXT_timer_query")) {
diff --git a/ui/gl/vsync_provider_win.cc b/ui/gl/vsync_provider_win.cc index 2152af0..ab76a966 100644 --- a/ui/gl/vsync_provider_win.cc +++ b/ui/gl/vsync_provider_win.cc
@@ -13,13 +13,30 @@ namespace gfx { +namespace { +bool g_use_dwm_vsync; +} // namespace + VSyncProviderWin::VSyncProviderWin(gfx::AcceleratedWidget window) : window_(window) { - use_dwm_ = (base::win::GetVersion() >= base::win::VERSION_WIN7); } VSyncProviderWin::~VSyncProviderWin() {} +// static +void VSyncProviderWin::InitializeOneOff() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + g_use_dwm_vsync = (base::win::GetVersion() >= base::win::VERSION_WIN7); + + if (g_use_dwm_vsync) { + // Prewarm sandbox + ::LoadLibrary(L"dwmapi.dll"); + } +} + void VSyncProviderWin::GetVSyncParameters(const UpdateVSyncCallback& callback) { TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters"); @@ -29,7 +46,7 @@ // Query the DWM timing info first if available. This will provide the most // precise values. - if (use_dwm_) { + if (g_use_dwm_vsync) { DWM_TIMING_INFO timing_info; timing_info.cbSize = sizeof(timing_info); HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
diff --git a/ui/gl/vsync_provider_win.h b/ui/gl/vsync_provider_win.h index f54bad437..6b7e64ca 100644 --- a/ui/gl/vsync_provider_win.h +++ b/ui/gl/vsync_provider_win.h
@@ -13,12 +13,13 @@ explicit VSyncProviderWin(gfx::AcceleratedWidget window); ~VSyncProviderWin() override; + static void InitializeOneOff(); + // VSyncProvider overrides; void GetVSyncParameters(const UpdateVSyncCallback& callback) override; private: gfx::AcceleratedWidget window_; - bool use_dwm_; DISALLOW_COPY_AND_ASSIGN(VSyncProviderWin); };
diff --git a/ui/keyboard/resources/OWNERS b/ui/keyboard/resources/OWNERS index 7a6ff670..d1ba0b0 100644 --- a/ui/keyboard/resources/OWNERS +++ b/ui/keyboard/resources/OWNERS
@@ -1,3 +1,2 @@ -bryeung@chromium.org bshe@chromium.org -kevers@chromium.org +rsadam@chromium.org
diff --git a/ui/views/accessibility/ax_aura_obj_cache.cc b/ui/views/accessibility/ax_aura_obj_cache.cc index 47964bee..72abde7 100644 --- a/ui/views/accessibility/ax_aura_obj_cache.cc +++ b/ui/views/accessibility/ax_aura_obj_cache.cc
@@ -6,6 +6,7 @@ #include "base/memory/singleton.h" #include "base/stl_util.h" +#include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" #include "ui/views/accessibility/ax_aura_obj_wrapper.h" #include "ui/views/accessibility/ax_view_obj_wrapper.h" @@ -30,6 +31,14 @@ } AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(aura::Window* window) { + if (!focus_client_) { + aura::Window* root_window = window->GetRootWindow(); + if (root_window) { + focus_client_ = aura::client::GetFocusClient(root_window); + if (focus_client_) + focus_client_->AddObserver(this); + } + } return CreateInternal<AXWindowObjWrapper>(window, window_to_id_map_); } @@ -97,7 +106,17 @@ } } -AXAuraObjCache::AXAuraObjCache() : current_id_(1), is_destroying_(false) { +AXAuraObjWrapper* AXAuraObjCache::GetFocus() { + View* focused_view = GetFocusedView(); + if (focused_view) + return GetOrCreate(focused_view); + return nullptr; +} + +AXAuraObjCache::AXAuraObjCache() + : current_id_(1), + focus_client_(nullptr), + is_destroying_(false) { } AXAuraObjCache::~AXAuraObjCache() { @@ -106,6 +125,40 @@ cache_.clear(); } +View* AXAuraObjCache::GetFocusedView() { + if (!focus_client_) + return nullptr; + + aura::Window* focused_window = focus_client_->GetFocusedWindow(); + if (!focused_window) + return nullptr; + + Widget* focused_widget = Widget::GetWidgetForNativeView(focused_window); + while (!focused_widget) { + focused_window = focused_window->parent(); + if (!focused_window) + break; + + focused_widget = Widget::GetWidgetForNativeView(focused_window); + } + + if (!focused_widget) + return nullptr; + + FocusManager* focus_manager = focused_widget->GetFocusManager(); + if (!focus_manager) + return nullptr; + + return focus_manager->GetFocusedView(); +} + +void AXAuraObjCache::OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) { + View* view = GetFocusedView(); + if (view) + view->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); +} + template <typename AuraViewWrapper, typename AuraView> AXAuraObjWrapper* AXAuraObjCache::CreateInternal( AuraView* aura_view,
diff --git a/ui/views/accessibility/ax_aura_obj_cache.h b/ui/views/accessibility/ax_aura_obj_cache.h index 1129c1d..1e54de78 100644 --- a/ui/views/accessibility/ax_aura_obj_cache.h +++ b/ui/views/accessibility/ax_aura_obj_cache.h
@@ -11,6 +11,7 @@ #include <vector> #include "base/macros.h" +#include "ui/aura/client/focus_change_observer.h" #include "ui/views/views_export.h" namespace base { @@ -18,6 +19,9 @@ } namespace aura { +namespace client { +class FocusClient; +} class Window; } // namespace aura @@ -27,7 +31,8 @@ class Widget; // A cache responsible for assigning id's to a set of interesting Aura views. -class VIEWS_EXPORT AXAuraObjCache { +class VIEWS_EXPORT AXAuraObjCache + : public aura::client::FocusChangeObserver { public: // Get the single instance of this class. static AXAuraObjCache* GetInstance(); @@ -63,6 +68,9 @@ // Get all top level windows this cache knows about. void GetTopLevelWindows(std::vector<AXAuraObjWrapper*>* children); + // Get the object that has focus. + AXAuraObjWrapper* GetFocus(); + // Indicates if this object's currently being destroyed. bool is_destroying() { return is_destroying_; } @@ -70,7 +78,13 @@ friend struct base::DefaultSingletonTraits<AXAuraObjCache>; AXAuraObjCache(); - virtual ~AXAuraObjCache(); + ~AXAuraObjCache() override; + + View* GetFocusedView(); + + // aura::client::FocusChangeObserver override. + void OnWindowFocused(aura::Window* gained_focus, + aura::Window* lost_focus) override; template <typename AuraViewWrapper, typename AuraView> AXAuraObjWrapper* CreateInternal( @@ -93,6 +107,8 @@ std::map<int32_t, AXAuraObjWrapper*> cache_; int32_t current_id_; + aura::client::FocusClient* focus_client_; + // True immediately when entering this object's destructor. bool is_destroying_;
diff --git a/ui/views/accessibility/ax_view_obj_wrapper.cc b/ui/views/accessibility/ax_view_obj_wrapper.cc index d4a2c43..3ea1e04 100644 --- a/ui/views/accessibility/ax_view_obj_wrapper.cc +++ b/ui/views/accessibility/ax_view_obj_wrapper.cc
@@ -53,8 +53,6 @@ out_node_data->role = view_data.role; out_node_data->state = view_data.state(); - if (view_->HasFocus()) - out_node_data->state |= 1 << ui::AX_STATE_FOCUSED; if (view_->IsFocusable()) out_node_data->state |= 1 << ui::AX_STATE_FOCUSABLE; if (!view_->visible())
diff --git a/ui/views/accessibility/native_view_accessibility.cc b/ui/views/accessibility/native_view_accessibility.cc index 07406e5..ab86be1 100644 --- a/ui/views/accessibility/native_view_accessibility.cc +++ b/ui/views/accessibility/native_view_accessibility.cc
@@ -74,9 +74,6 @@ if (!view_->visible()) data_.state |= (1 << ui::AX_STATE_INVISIBLE); - if (view_->HasFocus()) - data_.state |= (1 << ui::AX_STATE_FOCUSED); - return data_; }
diff --git a/ui/views/animation/button_ink_drop_delegate.cc b/ui/views/animation/button_ink_drop_delegate.cc index 40172aba..44a24ad 100644 --- a/ui/views/animation/button_ink_drop_delegate.cc +++ b/ui/views/animation/button_ink_drop_delegate.cc
@@ -63,7 +63,7 @@ void ButtonInkDropDelegate::OnGestureEvent(ui::GestureEvent* event) { InkDropState current_ink_drop_state = - ink_drop_animation_controller_->GetInkDropState(); + ink_drop_animation_controller_->GetTargetInkDropState(); InkDropState ink_drop_state = InkDropState::HIDDEN; switch (event->type()) {
diff --git a/ui/views/animation/ink_drop_animation.cc b/ui/views/animation/ink_drop_animation.cc index 8fb1edcb..a7a65a6 100644 --- a/ui/views/animation/ink_drop_animation.cc +++ b/ui/views/animation/ink_drop_animation.cc
@@ -4,214 +4,14 @@ #include "ui/views/animation/ink_drop_animation.h" -#include <algorithm> - -#include "base/command_line.h" -#include "base/logging.h" -#include "third_party/skia/include/core/SkColor.h" -#include "ui/base/ui_base_switches.h" -#include "ui/compositor/callback_layer_animation_observer.h" -#include "ui/compositor/layer.h" -#include "ui/compositor/layer_animation_sequence.h" -#include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/gfx/geometry/point3_f.h" -#include "ui/gfx/geometry/point_conversions.h" -#include "ui/gfx/geometry/point_f.h" -#include "ui/gfx/geometry/vector3d_f.h" -#include "ui/gfx/transform_util.h" -#include "ui/views/animation/ink_drop_animation_observer.h" -#include "ui/views/animation/ink_drop_painted_layer_delegates.h" -#include "ui/views/view.h" - -namespace { - -// The minimum scale factor to use when scaling rectangle layers. Smaller values -// were causing visual anomalies. -const float kMinimumRectScale = 0.0001f; - -// The minimum scale factor to use when scaling circle layers. Smaller values -// were causing visual anomalies. -const float kMinimumCircleScale = 0.001f; - -// The ink drop color. -const SkColor kInkDropColor = SK_ColorBLACK; - -// All the sub animations that are used to animate each of the InkDropStates. -// These are used to get time durations with -// GetAnimationDuration(InkDropSubAnimations). Note that in general a sub -// animation defines the duration for either a transformation animation or an -// opacity animation but there are some exceptions where an entire InkDropState -// animation consists of only 1 sub animation and it defines the duration for -// both the transformation and opacity animations. -enum InkDropSubAnimations { - // HIDDEN sub animations. - - // The HIDDEN sub animation that is fading out to a hidden opacity. - HIDDEN_FADE_OUT, - - // The HIDDEN sub animation that transforms the shape to a |small_size_| - // circle. - HIDDEN_TRANSFORM, - - // ACTION_PENDING sub animations. - - // The ACTION_PENDING sub animation that fades in to the visible opacity. - ACTION_PENDING_FADE_IN, - - // The ACTION_PENDING sub animation that transforms the shape to a - // |large_size_| circle. - ACTION_PENDING_TRANSFORM, - - // QUICK_ACTION sub animations. - - // The QUICK_ACTION sub animation that is fading out to a hidden opacity. - QUICK_ACTION_FADE_OUT, - - // The QUICK_ACTION sub animation that transforms the shape to a |large_size_| - // circle. - QUICK_ACTION_TRANSFORM, - - // SLOW_ACTION_PENDING sub animations. - - // The SLOW_ACTION_PENDING animation has only one sub animation which animates - // to a |small_size_| rounded rectangle at visible opacity. - SLOW_ACTION_PENDING, - - // SLOW_ACTION sub animations. - - // The SLOW_ACTION sub animation that is fading out to a hidden opacity. - SLOW_ACTION_FADE_OUT, - - // The SLOW_ACTION sub animation that transforms the shape to a |large_size_| - // rounded rectangle. - SLOW_ACTION_TRANSFORM, - - // ACTIVATED sub animations. - - // The ACTIVATED sub animation that transforms the shape to a |large_size_| - // circle. This is used when the ink drop is in a HIDDEN state prior to - // animating to the ACTIVATED state. - ACTIVATED_CIRCLE_TRANSFORM, - - // The ACTIVATED sub animation that transforms the shape to a |small_size_| - // rounded rectangle. - ACTIVATED_RECT_TRANSFORM, - - // DEACTIVATED sub animations. - - // The DEACTIVATED sub animation that is fading out to a hidden opacity. - DEACTIVATED_FADE_OUT, - - // The DEACTIVATED sub animation that transforms the shape to a |large_size_| - // rounded rectangle. - DEACTIVATED_TRANSFORM, -}; - -// The scale factor used to burst the QUICK_ACTION bubble as it fades out. -const float kQuickActionBurstScale = 1.3f; - -// A multiplicative factor used to slow down InkDropState animations. -const int kSlowAnimationDurationFactor = 3; - -// Checks CommandLine switches to determine if the visual feedback should have -// a fast animations speed. -bool UseFastAnimations() { - static bool fast = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - (::switches::kMaterialDesignInkDropAnimationSpeed)) != - ::switches::kMaterialDesignInkDropAnimationSpeedSlow; - return fast; -} - -// Duration constants for InkDropStateSubAnimations. See the -// InkDropStateSubAnimations enum documentation for more info. -int kAnimationDurationInMs[] = { - 150, // HIDDEN_FADE_OUT - 200, // HIDDEN_TRANSFORM - 0, // ACTION_PENDING_FADE_IN - 160, // ACTION_PENDING_TRANSFORM - 150, // QUICK_ACTION_FADE_OUT - 160, // QUICK_ACTION_TRANSFORM - 200, // SLOW_ACTION_PENDING - 150, // SLOW_ACTION_FADE_OUT - 200, // SLOW_ACTION_TRANSFORM - 200, // ACTIVATED_CIRCLE_TRANSFORM - 160, // ACTIVATED_RECT_TRANSFORM - 150, // DEACTIVATED_FADE_OUT - 200, // DEACTIVATED_TRANSFORM -}; - -// Returns the InkDropState sub animation duration for the given |state|. -base::TimeDelta GetAnimationDuration(InkDropSubAnimations state) { - return base::TimeDelta::FromMilliseconds( - (UseFastAnimations() ? 1 : kSlowAnimationDurationFactor) * - kAnimationDurationInMs[state]); -} - -// Calculates a Transform for a circle layer. The transform will be set up to -// translate the |drawn_center_point| to the origin, scale, and then translate -// to the target point defined by |target_center_x| and |target_center_y|. -gfx::Transform CalculateCircleTransform(const gfx::Point& drawn_center_point, - float scale, - float target_center_x, - float target_center_y) { - gfx::Transform transform; - transform.Translate(target_center_x, target_center_y); - transform.Scale(scale, scale); - transform.Translate(-drawn_center_point.x(), -drawn_center_point.y()); - return transform; -} - -// Calculates a Transform for a rectangle layer. The transform will be set up to -// translate the |drawn_center_point| to the origin and then scale by the -// |x_scale| and |y_scale| factors. -gfx::Transform CalculateRectTransform(const gfx::Point& drawn_center_point, - float x_scale, - float y_scale) { - gfx::Transform transform; - transform.Scale(x_scale, y_scale); - transform.Translate(-drawn_center_point.x(), -drawn_center_point.y()); - return transform; -} - -} // namespace - namespace views { +const float InkDropAnimation::kHiddenOpacity = 0.f; const float InkDropAnimation::kVisibleOpacity = 0.11f; -const float InkDropAnimation::kHiddenOpacity = 0.0f; -InkDropAnimation::InkDropAnimation(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius) - : large_size_(large_size), - large_corner_radius_(large_corner_radius), - small_size_(small_size), - small_corner_radius_(small_corner_radius), - circle_layer_delegate_(new CircleLayerDelegate( - kInkDropColor, - std::min(large_size_.width(), large_size_.height()) / 2)), - rect_layer_delegate_( - new RectangleLayerDelegate(kInkDropColor, large_size_)), - root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)), - ink_drop_state_(InkDropState::HIDDEN) { - root_layer_->set_name("InkDropAnimation:ROOT_LAYER"); +InkDropAnimation::InkDropAnimation() {} - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) - AddPaintLayer(static_cast<PaintedShape>(i)); - - root_layer_->SetMasksToBounds(false); - root_layer_->SetBounds(gfx::Rect(large_size_)); - - SetStateToHidden(); -} - -InkDropAnimation::~InkDropAnimation() { - // Explicitly aborting all the animations ensures all callbacks are invoked - // while this instance still exists. - AbortAllAnimations(); -} +InkDropAnimation::~InkDropAnimation() {} void InkDropAnimation::AddObserver(InkDropAnimationObserver* observer) { observers_.AddObserver(observer); @@ -221,381 +21,16 @@ observers_.RemoveObserver(observer); } -void InkDropAnimation::AnimateToState(InkDropState ink_drop_state) { - // |animation_observer| will be deleted when AnimationEndedCallback() returns - // true. - ui::CallbackLayerAnimationObserver* animation_observer = - new ui::CallbackLayerAnimationObserver( - base::Bind(&InkDropAnimation::AnimationStartedCallback, - base::Unretained(this), ink_drop_state), - base::Bind(&InkDropAnimation::AnimationEndedCallback, - base::Unretained(this), ink_drop_state)); - AnimateToStateInternal(ink_drop_state, animation_observer); - animation_observer->SetActive(); -} - -void InkDropAnimation::SetCenterPoint(const gfx::Point& center_point) { - gfx::Transform transform; - transform.Translate(center_point.x(), center_point.y()); - root_layer_->SetTransform(transform); -} - -void InkDropAnimation::HideImmediately() { - AbortAllAnimations(); - SetStateToHidden(); - ink_drop_state_ = InkDropState::HIDDEN; -} - -std::string InkDropAnimation::ToLayerName(PaintedShape painted_shape) { - switch (painted_shape) { - case TOP_LEFT_CIRCLE: - return "TOP_LEFT_CIRCLE"; - case TOP_RIGHT_CIRCLE: - return "TOP_RIGHT_CIRCLE"; - case BOTTOM_RIGHT_CIRCLE: - return "BOTTOM_RIGHT_CIRCLE"; - case BOTTOM_LEFT_CIRCLE: - return "BOTTOM_LEFT_CIRCLE"; - case HORIZONTAL_RECT: - return "HORIZONTAL_RECT"; - case VERTICAL_RECT: - return "VERTICAL_RECT"; - case PAINTED_SHAPE_COUNT: - NOTREACHED() << "The PAINTED_SHAPE_COUNT value should never be used."; - return "PAINTED_SHAPE_COUNT"; - } - return "UNKNOWN"; -} - -void InkDropAnimation::AnimateToStateInternal( - InkDropState ink_drop_state, - ui::LayerAnimationObserver* animation_observer) { - InkDropState previous_ink_drop_state = ink_drop_state_; - ink_drop_state_ = ink_drop_state; - - InkDropTransforms transforms; - root_layer_->SetVisible(true); - - switch (ink_drop_state_) { - case InkDropState::HIDDEN: - if (GetCurrentOpacity() == kHiddenOpacity) { - AbortAllAnimations(); - break; - } else { - AnimateToOpacity(kHiddenOpacity, GetAnimationDuration(HIDDEN_FADE_OUT), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - CalculateCircleTransforms(small_size_, &transforms); - AnimateToTransforms( - transforms, GetAnimationDuration(HIDDEN_TRANSFORM), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - } - break; - case InkDropState::ACTION_PENDING: - DCHECK(previous_ink_drop_state == InkDropState::HIDDEN); - AnimateToOpacity(kVisibleOpacity, - GetAnimationDuration(ACTION_PENDING_FADE_IN), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN, animation_observer); - AnimateToOpacity(kVisibleOpacity, - GetAnimationDuration(ACTION_PENDING_TRANSFORM), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN, animation_observer); - CalculateCircleTransforms(large_size_, &transforms); - AnimateToTransforms(transforms, - GetAnimationDuration(ACTION_PENDING_TRANSFORM), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - break; - case InkDropState::QUICK_ACTION: { - DCHECK(previous_ink_drop_state == InkDropState::HIDDEN || - previous_ink_drop_state == InkDropState::ACTION_PENDING); - AnimateToOpacity(kHiddenOpacity, - GetAnimationDuration(QUICK_ACTION_FADE_OUT), - ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, - gfx::Tween::EASE_IN_OUT, animation_observer); - gfx::Size s = ScaleToRoundedSize(large_size_, kQuickActionBurstScale); - CalculateCircleTransforms(s, &transforms); - AnimateToTransforms(transforms, - GetAnimationDuration(QUICK_ACTION_TRANSFORM), - ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, - gfx::Tween::EASE_IN_OUT, animation_observer); - break; - } - case InkDropState::SLOW_ACTION_PENDING: - DCHECK(previous_ink_drop_state == InkDropState::ACTION_PENDING); - AnimateToOpacity(kVisibleOpacity, - GetAnimationDuration(SLOW_ACTION_PENDING), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN, animation_observer); - CalculateRectTransforms(small_size_, small_corner_radius_, &transforms); - AnimateToTransforms(transforms, GetAnimationDuration(SLOW_ACTION_PENDING), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - break; - case InkDropState::SLOW_ACTION: { - DCHECK(previous_ink_drop_state == InkDropState::SLOW_ACTION_PENDING); - base::TimeDelta visible_duration = - GetAnimationDuration(SLOW_ACTION_TRANSFORM) - - GetAnimationDuration(SLOW_ACTION_FADE_OUT); - AnimateToOpacity(kVisibleOpacity, visible_duration, - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - AnimateToOpacity(kHiddenOpacity, - GetAnimationDuration(SLOW_ACTION_FADE_OUT), - ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, - gfx::Tween::EASE_IN_OUT, animation_observer); - CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); - AnimateToTransforms(transforms, - GetAnimationDuration(SLOW_ACTION_TRANSFORM), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - break; - } - case InkDropState::ACTIVATED: { - // Animate the opacity so that it cancels any opacity animations already - // in progress. - AnimateToOpacity(kVisibleOpacity, base::TimeDelta(), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - - ui::LayerAnimator::PreemptionStrategy rect_transform_preemption_strategy = - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET; - if (previous_ink_drop_state == InkDropState::HIDDEN) { - rect_transform_preemption_strategy = - ui::LayerAnimator::ENQUEUE_NEW_ANIMATION; - CalculateCircleTransforms(large_size_, &transforms); - AnimateToTransforms( - transforms, GetAnimationDuration(ACTIVATED_CIRCLE_TRANSFORM), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - } else if (previous_ink_drop_state == InkDropState::ACTION_PENDING) { - rect_transform_preemption_strategy = - ui::LayerAnimator::ENQUEUE_NEW_ANIMATION; - } - - CalculateRectTransforms(small_size_, small_corner_radius_, &transforms); - AnimateToTransforms(transforms, - GetAnimationDuration(ACTIVATED_RECT_TRANSFORM), - rect_transform_preemption_strategy, - gfx::Tween::EASE_IN_OUT, animation_observer); - break; - } - case InkDropState::DEACTIVATED: { - base::TimeDelta visible_duration = - GetAnimationDuration(DEACTIVATED_TRANSFORM) - - GetAnimationDuration(DEACTIVATED_FADE_OUT); - AnimateToOpacity(kVisibleOpacity, visible_duration, - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); - AnimateToOpacity(kHiddenOpacity, - GetAnimationDuration(DEACTIVATED_FADE_OUT), - ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, - gfx::Tween::EASE_IN_OUT, animation_observer); - CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); - AnimateToTransforms(transforms, - GetAnimationDuration(DEACTIVATED_TRANSFORM), - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, - gfx::Tween::EASE_IN_OUT, animation_observer); - break; - } - } -} - -void InkDropAnimation::AnimateToTransforms( - const InkDropTransforms transforms, - base::TimeDelta duration, - ui::LayerAnimator::PreemptionStrategy preemption_strategy, - gfx::Tween::Type tween, - ui::LayerAnimationObserver* animation_observer) { - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) { - ui::LayerAnimator* animator = painted_layers_[i]->GetAnimator(); - ui::ScopedLayerAnimationSettings animation(animator); - animation.SetPreemptionStrategy(preemption_strategy); - animation.SetTweenType(tween); - ui::LayerAnimationElement* element = - ui::LayerAnimationElement::CreateTransformElement(transforms[i], - duration); - ui::LayerAnimationSequence* sequence = - new ui::LayerAnimationSequence(element); - - if (animation_observer) - sequence->AddObserver(animation_observer); - - animator->StartAnimation(sequence); - } -} - -void InkDropAnimation::SetStateToHidden() { - InkDropTransforms transforms; - // Using a size of 0x0 creates visual anomalies. - CalculateCircleTransforms(gfx::Size(1, 1), &transforms); - SetTransforms(transforms); - SetOpacity(kHiddenOpacity); - root_layer_->SetVisible(false); -} - -void InkDropAnimation::SetTransforms(const InkDropTransforms transforms) { - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) - painted_layers_[i]->SetTransform(transforms[i]); -} - -float InkDropAnimation::GetCurrentOpacity() const { - return root_layer_->opacity(); -} - -void InkDropAnimation::SetOpacity(float opacity) { - root_layer_->SetOpacity(opacity); -} - -void InkDropAnimation::AnimateToOpacity( - float opacity, - base::TimeDelta duration, - ui::LayerAnimator::PreemptionStrategy preemption_strategy, - gfx::Tween::Type tween, - ui::LayerAnimationObserver* animation_observer) { - ui::LayerAnimator* animator = root_layer_->GetAnimator(); - ui::ScopedLayerAnimationSettings animation_settings(animator); - animation_settings.SetPreemptionStrategy(preemption_strategy); - animation_settings.SetTweenType(tween); - ui::LayerAnimationElement* animation_element = - ui::LayerAnimationElement::CreateOpacityElement(opacity, duration); - ui::LayerAnimationSequence* animation_sequence = - new ui::LayerAnimationSequence(animation_element); - - if (animation_observer) - animation_sequence->AddObserver(animation_observer); - - animator->StartAnimation(animation_sequence); -} - -void InkDropAnimation::CalculateCircleTransforms( - const gfx::Size& size, - InkDropTransforms* transforms_out) const { - CalculateRectTransforms(size, std::min(size.width(), size.height()) / 2.0f, - transforms_out); -} - -void InkDropAnimation::CalculateRectTransforms( - const gfx::Size& size, - float corner_radius, - InkDropTransforms* transforms_out) const { - DCHECK_GE(size.width() / 2.0f, corner_radius) - << "The circle's diameter should not be greater than the total width."; - DCHECK_GE(size.height() / 2.0f, corner_radius) - << "The circle's diameter should not be greater than the total height."; - - // The shapes are drawn such that their center points are not at the origin. - // Thus we use the CalculateCircleTransform() and CalculateRectTransform() - // methods to calculate the complex Transforms. - - const float circle_scale = std::max( - kMinimumCircleScale, - corner_radius / static_cast<float>(circle_layer_delegate_->radius())); - - const float circle_target_x_offset = size.width() / 2.0f - corner_radius; - const float circle_target_y_offset = size.height() / 2.0f - corner_radius; - - (*transforms_out)[TOP_LEFT_CIRCLE] = CalculateCircleTransform( - ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, - -circle_target_x_offset, -circle_target_y_offset); - - (*transforms_out)[TOP_RIGHT_CIRCLE] = CalculateCircleTransform( - ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, - circle_target_x_offset, -circle_target_y_offset); - - (*transforms_out)[BOTTOM_RIGHT_CIRCLE] = CalculateCircleTransform( - ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, - circle_target_x_offset, circle_target_y_offset); - - (*transforms_out)[BOTTOM_LEFT_CIRCLE] = CalculateCircleTransform( - ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, - -circle_target_x_offset, circle_target_y_offset); - - const float rect_delegate_width = - static_cast<float>(rect_layer_delegate_->size().width()); - const float rect_delegate_height = - static_cast<float>(rect_layer_delegate_->size().height()); - - (*transforms_out)[HORIZONTAL_RECT] = CalculateRectTransform( - ToRoundedPoint(rect_layer_delegate_->GetCenterPoint()), - std::max(kMinimumRectScale, size.width() / rect_delegate_width), - std::max(kMinimumRectScale, - (size.height() - 2.0f * corner_radius) / rect_delegate_height)); - - (*transforms_out)[VERTICAL_RECT] = CalculateRectTransform( - ToRoundedPoint(rect_layer_delegate_->GetCenterPoint()), - std::max(kMinimumRectScale, - (size.width() - 2.0f * corner_radius) / rect_delegate_width), - std::max(kMinimumRectScale, size.height() / rect_delegate_height)); -} - -void InkDropAnimation::GetCurrentTransforms( - InkDropTransforms* transforms_out) const { - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) - (*transforms_out)[i] = painted_layers_[i]->transform(); -} - -void InkDropAnimation::AddPaintLayer(PaintedShape painted_shape) { - ui::LayerDelegate* delegate = nullptr; - switch (painted_shape) { - case TOP_LEFT_CIRCLE: - case TOP_RIGHT_CIRCLE: - case BOTTOM_RIGHT_CIRCLE: - case BOTTOM_LEFT_CIRCLE: - delegate = circle_layer_delegate_.get(); - break; - case HORIZONTAL_RECT: - case VERTICAL_RECT: - delegate = rect_layer_delegate_.get(); - break; - case PAINTED_SHAPE_COUNT: - NOTREACHED() << "PAINTED_SHAPE_COUNT is not an actual shape type."; - break; - } - - ui::Layer* layer = new ui::Layer(); - root_layer_->Add(layer); - - layer->SetBounds(gfx::Rect(large_size_)); - layer->SetFillsBoundsOpaquely(false); - layer->set_delegate(delegate); - layer->SetVisible(true); - layer->SetOpacity(1.0); - layer->SetMasksToBounds(false); - layer->set_name("PAINTED_SHAPE_COUNT:" + ToLayerName(painted_shape)); - - painted_layers_[painted_shape].reset(layer); -} - -void InkDropAnimation::AbortAllAnimations() { - root_layer_->GetAnimator()->AbortAllAnimations(); - for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) - painted_layers_[i]->GetAnimator()->AbortAllAnimations(); -} - -void InkDropAnimation::AnimationStartedCallback( - InkDropState ink_drop_state, - const ui::CallbackLayerAnimationObserver& observer) { +void InkDropAnimation::NotifyAnimationStarted(InkDropState ink_drop_state) { FOR_EACH_OBSERVER(InkDropAnimationObserver, observers_, InkDropAnimationStarted(ink_drop_state)); } -bool InkDropAnimation::AnimationEndedCallback( +void InkDropAnimation::NotifyAnimationEnded( InkDropState ink_drop_state, - const ui::CallbackLayerAnimationObserver& observer) { - if (ink_drop_state == InkDropState::HIDDEN) - SetStateToHidden(); - - FOR_EACH_OBSERVER( - InkDropAnimationObserver, observers_, - InkDropAnimationEnded(ink_drop_state, - observer.aborted_count() - ? InkDropAnimationObserver::PRE_EMPTED - : InkDropAnimationObserver::SUCCESS)); - return true; + InkDropAnimationObserver::InkDropAnimationEndedReason reason) { + FOR_EACH_OBSERVER(InkDropAnimationObserver, observers_, + InkDropAnimationEnded(ink_drop_state, reason)); } } // namespace views
diff --git a/ui/views/animation/ink_drop_animation.h b/ui/views/animation/ink_drop_animation.h index 39b16354..450c7ad5b 100644 --- a/ui/views/animation/ink_drop_animation.h +++ b/ui/views/animation/ink_drop_animation.h
@@ -1,220 +1,80 @@ -// Copyright 2015 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 UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_H_ #define UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_H_ -#include <string> - #include "base/macros.h" -#include "base/memory/scoped_ptr.h" #include "base/observer_list.h" -#include "base/time/time.h" -#include "ui/compositor/layer_animator.h" -#include "ui/gfx/animation/tween.h" #include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/transform.h" +#include "ui/views/animation/ink_drop_animation_observer.h" #include "ui/views/animation/ink_drop_state.h" #include "ui/views/views_export.h" namespace ui { -class CallbackLayerAnimationObserver; class Layer; -class LayerAnimationObserver; -class LayerDelegate; } // namespace ui namespace views { -class CircleLayerDelegate; -class InkDropAnimationObserver; -class RectangleLayerDelegate; -namespace test { -class InkDropAnimationTestApi; -} // namespace test - -// An ink drop animation that smoothly animates between a circle and a rounded -// rectangle of different sizes for each of the different InkDropStates. The -// final frame for each InkDropState will be bounded by either a |large_size_| -// rectangle or a |small_size_| rectangle. -// -// The valid InkDropState transitions are defined below: -// -// {All InkDropStates} => HIDDEN -// HIDDEN => ACTION_PENDING -// HIDDEN, ACTION_PENDING => QUICK_ACTION -// ACTION_PENDING => SLOW_ACTION_PENDING -// SLOW_ACTION_PENDING => SLOW_ACTION -// {All InkDropStates} => ACTIVATED -// {All InkDropStates} => DEACTIVATED +// Simple base class for animations that provide visual feedback for View state. +// Manages the attached InkDropAnimationObservers. // // TODO(bruthig): Document the ink drop ripple on chromium.org and add a link to -// it. +// the doc here. class VIEWS_EXPORT InkDropAnimation { public: - // The opacity of the ink drop when it is visible. - static const float kVisibleOpacity; - // The opacity of the ink drop when it is not visible. static const float kHiddenOpacity; - InkDropAnimation(const gfx::Size& large_size, - int large_corner_radius, - const gfx::Size& small_size, - int small_corner_radius); - ~InkDropAnimation(); + // The opacity of the ink drop when it is visible. + static const float kVisibleOpacity; - // The root Layer that can be added in to a Layer tree. - ui::Layer* root_layer() { return root_layer_.get(); } - - InkDropState ink_drop_state() const { return ink_drop_state_; } + InkDropAnimation(); + virtual ~InkDropAnimation(); void AddObserver(InkDropAnimationObserver* observer); void RemoveObserver(InkDropAnimationObserver* observer); - // Animates from the current |ink_drop_state_| to a new |ink_drop_state|. - void AnimateToState(InkDropState ink_drop_state); + // Gets the target InkDropState, i.e. the last |ink_drop_state| passed to + // AnimateToState() or set by HideImmediately(). + virtual InkDropState GetTargetInkDropState() const = 0; + + // Animates from the current InkDropState to the new |ink_drop_state|. + // + // NOTE: GetTargetInkDropState() should return the new |ink_drop_state| value + // to any observers being notified as a result of the call. + virtual void AnimateToState(InkDropState ink_drop_state) = 0; + + // The root Layer that can be added in to a Layer tree. + virtual ui::Layer* GetRootLayer() = 0; + + // Returns true when the ripple is visible. This is different from checking if + // the ink_drop_state() == HIDDEN because the ripple may be visible while it + // animates to the target HIDDEN state. + virtual bool IsVisible() const = 0; // Sets the |center_point| of the ink drop layer relative to its parent Layer. - void SetCenterPoint(const gfx::Point& center_point); + virtual void SetCenterPoint(const gfx::Point& center_point) = 0; // Immediately aborts all in-progress animations and hides the ink drop. + // // NOTE: This will NOT raise InkDropAnimation(Started|Ended) events for the // state transition to HIDDEN! - void HideImmediately(); + virtual void HideImmediately() = 0; + + protected: + // Notify the |observers_| that an animation has started. + void NotifyAnimationStarted(InkDropState ink_drop_state); + + // Notify the |observers_| that an animation has ended. + void NotifyAnimationEnded( + InkDropState ink_drop_state, + InkDropAnimationObserver::InkDropAnimationEndedReason reason); private: - friend class test::InkDropAnimationTestApi; - - // Enumeration of the different shapes that compose the ink drop. - enum PaintedShape { - TOP_LEFT_CIRCLE = 0, - TOP_RIGHT_CIRCLE, - BOTTOM_RIGHT_CIRCLE, - BOTTOM_LEFT_CIRCLE, - HORIZONTAL_RECT, - VERTICAL_RECT, - // The total number of shapes, not an actual shape. - PAINTED_SHAPE_COUNT - }; - - // Returns a human readable string for the |painted_shape| value. - static std::string ToLayerName(PaintedShape painted_shape); - - // Type that contains a gfx::Tansform for each of the layers required by the - // ink drop. - typedef gfx::Transform InkDropTransforms[PAINTED_SHAPE_COUNT]; - - // Animates the ripple to |ink_drop_state|. |observer| is added to all - // LayerAnimationSequence's used if not null. - void AnimateToStateInternal(InkDropState ink_drop_state, - ui::LayerAnimationObserver* observer); - - // Animates all of the painted shape layers to the specified |transforms|. The - // animation will be configured with the given |duration|, |tween|, and - // |preemption_strategy| values. The |observer| will be added to all - // LayerAnimationSequences if not null. - void AnimateToTransforms( - const InkDropTransforms transforms, - base::TimeDelta duration, - ui::LayerAnimator::PreemptionStrategy preemption_strategy, - gfx::Tween::Type tween, - ui::LayerAnimationObserver* observer); - - // Updates the Transforms and opacity to the HIDDEN state. - void SetStateToHidden(); - - // Sets the |transforms| on all of the shape layers. Note that this does not - // perform any animation. - void SetTransforms(const InkDropTransforms transforms); - - // Gets the opacity of the ink drop. - float GetCurrentOpacity() const; - - // Sets the opacity of the ink drop. - void SetOpacity(float opacity); - - // Animates all of the painted shape layers to the specified |opacity|. The - // animation will be configured with the given |duration|, |tween|, and - // |preemption_strategy| values. The |observer| will be added to all - // LayerAnimationSequences if not null. - void AnimateToOpacity( - float opacity, - base::TimeDelta duration, - ui::LayerAnimator::PreemptionStrategy preemption_strategy, - gfx::Tween::Type tween, - ui::LayerAnimationObserver* observer); - - // Updates all of the Transforms in |transforms_out| for a circle of the given - // |size|. - void CalculateCircleTransforms(const gfx::Size& size, - InkDropTransforms* transforms_out) const; - - // Updates all of the Transforms in |transforms_out| for a rounded rectangle - // of the given |size| and |corner_radius|. - void CalculateRectTransforms(const gfx::Size& size, - float corner_radius, - InkDropTransforms* transforms_out) const; - - // Updates all of the Transforms in |transforms_out| to the current Transforms - // of the painted shape Layers. - void GetCurrentTransforms(InkDropTransforms* transforms_out) const; - - // Adds and configures a new |painted_shape| layer to |painted_layers_|. - void AddPaintLayer(PaintedShape painted_shape); - - void AbortAllAnimations(); - - // The Callback invoked when all of the animation sequences for the specific - // |ink_drop_state| animation have started. |observer| is the - // ui::CallbackLayerAnimationObserver which is notifying the callback. - void AnimationStartedCallback( - InkDropState ink_drop_state, - const ui::CallbackLayerAnimationObserver& observer); - - // The Callback invoked when all of the animation sequences for the specific - // |ink_drop_state| animation have finished. |observer| is the - // ui::CallbackLayerAnimationObserver which is notifying the callback. - bool AnimationEndedCallback( - InkDropState ink_drop_state, - const ui::CallbackLayerAnimationObserver& observer); - - // Maximum size that an ink drop will be drawn to for any InkDropState whose - // final frame should be large. - gfx::Size large_size_; - - // Corner radius used to draw the rounded rectangles corner for any - // InkDropState whose final frame should be large. - int large_corner_radius_; - - // Maximum size that an ink drop will be drawn to for any InkDropState whose - // final frame should be small. - gfx::Size small_size_; - - // Corner radius used to draw the rounded rectangles corner for any - // InkDropState whose final frame should be small. - int small_corner_radius_; - - // ui::LayerDelegate to paint circles for all the circle layers. - scoped_ptr<CircleLayerDelegate> circle_layer_delegate_; - - // ui::LayerDelegate to paint rectangles for all the rectangle layers. - scoped_ptr<RectangleLayerDelegate> rect_layer_delegate_; - - // The root layer that parents the animating layers. The root layer is used to - // manipulate opacity and location, and its children are used to manipulate - // the different painted shapes that compose the ink drop. - scoped_ptr<ui::Layer> root_layer_; - - // ui::Layers for all of the painted shape layers that compose the ink drop. - scoped_ptr<ui::Layer> painted_layers_[PAINTED_SHAPE_COUNT]; - - // The current ink drop state. - InkDropState ink_drop_state_; - - // List of observers to notify when animations have finished. + // List of observers to notify when animations have started and finished. base::ObserverList<InkDropAnimationObserver> observers_; DISALLOW_COPY_AND_ASSIGN(InkDropAnimation);
diff --git a/ui/views/animation/ink_drop_animation_controller.h b/ui/views/animation/ink_drop_animation_controller.h index 993c852..082a8bc 100644 --- a/ui/views/animation/ink_drop_animation_controller.h +++ b/ui/views/animation/ink_drop_animation_controller.h
@@ -27,8 +27,12 @@ public: virtual ~InkDropAnimationController() {} - // Gets the current state of the ink drop. - virtual InkDropState GetInkDropState() const = 0; + // Gets the target state of the ink drop. + virtual InkDropState GetTargetInkDropState() const = 0; + + // Returns true when the ripple is visible, including when animating to + // HIDDEN. + virtual bool IsVisible() const = 0; // Animates from the current InkDropState to |ink_drop_state|. virtual void AnimateToState(InkDropState ink_drop_state) = 0;
diff --git a/ui/views/animation/ink_drop_animation_controller_factory.cc b/ui/views/animation/ink_drop_animation_controller_factory.cc index f1f7825..377d996c 100644 --- a/ui/views/animation/ink_drop_animation_controller_factory.cc +++ b/ui/views/animation/ink_drop_animation_controller_factory.cc
@@ -25,7 +25,8 @@ ~InkDropAnimationControllerStub() override; // InkDropAnimationController: - InkDropState GetInkDropState() const override; + InkDropState GetTargetInkDropState() const override; + bool IsVisible() const override; void AnimateToState(InkDropState state) override; void SetHovered(bool is_hovered) override; bool IsHovered() const override; @@ -49,12 +50,15 @@ InkDropAnimationControllerStub::~InkDropAnimationControllerStub() {} -InkDropState InkDropAnimationControllerStub::GetInkDropState() const { +InkDropState InkDropAnimationControllerStub::GetTargetInkDropState() const { return InkDropState::HIDDEN; } +bool InkDropAnimationControllerStub::IsVisible() const { + return false; +} + void InkDropAnimationControllerStub::AnimateToState(InkDropState state) { - SetHovered(false); } void InkDropAnimationControllerStub::SetHovered(bool is_hovered) {
diff --git a/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc b/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc index f13f162..c94f78f 100644 --- a/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc +++ b/ui/views/animation/ink_drop_animation_controller_factory_unittest.cc
@@ -107,28 +107,28 @@ TEST_P(InkDropAnimationControllerFactoryTest, StateIsHiddenInitially) { EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, HoveredStateAfterAnimateToState) { ink_drop_animation_controller_->SetHovered(true); ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); - EXPECT_FALSE(ink_drop_animation_controller_->IsHovered()); + EXPECT_TRUE(ink_drop_animation_controller_->IsHovered()); } TEST_P(InkDropAnimationControllerFactoryTest, TypicalQuickAction) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); ink_drop_animation_controller_->AnimateToState(InkDropState::QUICK_ACTION); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, CancelQuickAction) { ink_drop_animation_controller_->AnimateToState(InkDropState::ACTION_PENDING); ink_drop_animation_controller_->AnimateToState(InkDropState::HIDDEN); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, TypicalSlowAction) { @@ -137,7 +137,7 @@ InkDropState::SLOW_ACTION_PENDING); ink_drop_animation_controller_->AnimateToState(InkDropState::SLOW_ACTION); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, CancelSlowAction) { @@ -146,7 +146,7 @@ InkDropState::SLOW_ACTION_PENDING); ink_drop_animation_controller_->AnimateToState(InkDropState::HIDDEN); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, TypicalQuickActivated) { @@ -154,7 +154,7 @@ ink_drop_animation_controller_->AnimateToState(InkDropState::ACTIVATED); ink_drop_animation_controller_->AnimateToState(InkDropState::DEACTIVATED); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } TEST_P(InkDropAnimationControllerFactoryTest, TypicalSlowActivated) { @@ -164,7 +164,7 @@ ink_drop_animation_controller_->AnimateToState(InkDropState::ACTIVATED); ink_drop_animation_controller_->AnimateToState(InkDropState::DEACTIVATED); EXPECT_EQ(InkDropState::HIDDEN, - ink_drop_animation_controller_->GetInkDropState()); + ink_drop_animation_controller_->GetTargetInkDropState()); } } // namespace views
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.cc b/ui/views/animation/ink_drop_animation_controller_impl.cc index 486a174c..a8c8cf4e 100644 --- a/ui/views/animation/ink_drop_animation_controller_impl.cc +++ b/ui/views/animation/ink_drop_animation_controller_impl.cc
@@ -7,9 +7,9 @@ #include "base/auto_reset.h" #include "base/timer/timer.h" #include "ui/compositor/layer.h" -#include "ui/views/animation/ink_drop_animation.h" #include "ui/views/animation/ink_drop_host.h" #include "ui/views/animation/ink_drop_hover.h" +#include "ui/views/animation/square_ink_drop_animation.h" namespace views { @@ -56,6 +56,7 @@ ink_drop_large_corner_radius_(0), ink_drop_small_corner_radius_(0), root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)), + is_hovered_(false), can_destroy_after_hidden_animation_(true), hover_after_animation_timer_(nullptr) { root_layer_->set_name("InkDropAnimationControllerImpl:RootLayer"); @@ -69,10 +70,14 @@ ink_drop_host_->RemoveInkDropLayer(root_layer_.get()); } -InkDropState InkDropAnimationControllerImpl::GetInkDropState() const { +InkDropState InkDropAnimationControllerImpl::GetTargetInkDropState() const { if (!ink_drop_animation_) return InkDropState::HIDDEN; - return ink_drop_animation_->ink_drop_state(); + return ink_drop_animation_->GetTargetInkDropState(); +} + +bool InkDropAnimationControllerImpl::IsVisible() const { + return ink_drop_animation_ && ink_drop_animation_->IsVisible(); } void InkDropAnimationControllerImpl::AnimateToState( @@ -94,14 +99,15 @@ // Make sure the ink drop starts from the HIDDEN state it was going to auto // transition to it. - if (ink_drop_animation_->ink_drop_state() == InkDropState::HIDDEN || - ShouldAnimateToHidden(ink_drop_animation_->ink_drop_state())) { + if (ink_drop_animation_->GetTargetInkDropState() == InkDropState::HIDDEN || + ShouldAnimateToHidden(ink_drop_animation_->GetTargetInkDropState())) { ink_drop_animation_->HideImmediately(); } ink_drop_animation_->AnimateToState(ink_drop_state); } void InkDropAnimationControllerImpl::SetHovered(bool is_hovered) { + is_hovered_ = is_hovered; SetHoveredInternal(is_hovered, is_hovered ? base::TimeDelta::FromMilliseconds( kHoverFadeInFromUserInputDurationInMs) @@ -110,7 +116,7 @@ } bool InkDropAnimationControllerImpl::IsHovered() const { - return hover_ && hover_->IsVisible(); + return is_hovered_; } gfx::Size InkDropAnimationControllerImpl::GetInkDropLargeSize() const { @@ -147,19 +153,19 @@ void InkDropAnimationControllerImpl::CreateInkDropAnimation() { DestroyInkDropAnimation(); - ink_drop_animation_.reset(new InkDropAnimation( + ink_drop_animation_.reset(new SquareInkDropAnimation( ink_drop_large_size_, ink_drop_large_corner_radius_, ink_drop_small_size_, ink_drop_small_corner_radius_)); ink_drop_animation_->AddObserver(this); ink_drop_animation_->SetCenterPoint(ink_drop_center_); - root_layer_->Add(ink_drop_animation_->root_layer()); + root_layer_->Add(ink_drop_animation_->GetRootLayer()); } void InkDropAnimationControllerImpl::DestroyInkDropAnimation() { if (!ink_drop_animation_) return; - root_layer_->Remove(ink_drop_animation_->root_layer()); + root_layer_->Remove(ink_drop_animation_->GetRootLayer()); ink_drop_animation_->RemoveObserver(this); ink_drop_animation_.reset(); } @@ -180,12 +186,12 @@ hover_.reset(); } +bool InkDropAnimationControllerImpl::IsHoverFadingInOrVisible() const { + return hover_ && hover_->IsFadingInOrVisible(); +} + void InkDropAnimationControllerImpl::InkDropAnimationStarted( InkDropState ink_drop_state) { - if (IsHovered() && ink_drop_state != views::InkDropState::HIDDEN) { - SetHoveredInternal(false, base::TimeDelta::FromMilliseconds( - kHoverFadeOutBeforeAnimationDurationInMs)); - } } void InkDropAnimationControllerImpl::InkDropAnimationEnded( @@ -211,15 +217,14 @@ base::TimeDelta animation_duration) { StopHoverAfterAnimationTimer(); - if (IsHovered() == is_hovered) + if (IsHoverFadingInOrVisible() == is_hovered) return; if (is_hovered) { if (!hover_) CreateInkDropHover(); - if (GetInkDropState() == views::InkDropState::HIDDEN) { + if (!IsVisible()) hover_->FadeIn(animation_duration); - } } else { hover_->FadeOut(animation_duration); }
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.h b/ui/views/animation/ink_drop_animation_controller_impl.h index 40eb0ba4..07d138d 100644 --- a/ui/views/animation/ink_drop_animation_controller_impl.h +++ b/ui/views/animation/ink_drop_animation_controller_impl.h
@@ -34,7 +34,8 @@ ~InkDropAnimationControllerImpl() override; // InkDropAnimationController: - InkDropState GetInkDropState() const override; + InkDropState GetTargetInkDropState() const override; + bool IsVisible() const override; void AnimateToState(InkDropState ink_drop_state) override; void SetHovered(bool is_hovered) override; bool IsHovered() const override; @@ -64,6 +65,10 @@ // Destroys the current |hover_|. void DestroyInkDropHover(); + // Returns true if the hover animation is in the process of fading in or + // is visible. + bool IsHoverFadingInOrVisible() const; + // views::InkDropAnimationObserver: void InkDropAnimationStarted(InkDropState ink_drop_state) override; void InkDropAnimationEnded(InkDropState ink_drop_state, @@ -111,6 +116,9 @@ // The current InkDropHover. Lazily created using CreateInkDropHover(); scoped_ptr<InkDropHover> hover_; + // The logical hover state of |this|. + bool is_hovered_; + // The current InkDropAnimation. Created on demand using // CreateInkDropAnimation(). scoped_ptr<InkDropAnimation> ink_drop_animation_;
diff --git a/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc b/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc index 78fd2b6..1f0862cb 100644 --- a/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc +++ b/ui/views/animation/ink_drop_animation_controller_impl_unittest.cc
@@ -22,6 +22,10 @@ protected: TestInkDropHost ink_drop_host_; + bool is_hover_fading_in_or_visible() { + return ink_drop_animation_controller_.IsHoverFadingInOrVisible(); + } + // The test target. InkDropAnimationControllerImpl ink_drop_animation_controller_; @@ -60,6 +64,16 @@ EXPECT_FALSE(ink_drop_animation_controller_.IsHovered()); } +TEST_F(InkDropAnimationControllerImplTest, SetHoveredIsFadingInOrVisible) { + ink_drop_host_.set_should_show_hover(true); + + ink_drop_animation_controller_.SetHovered(true); + EXPECT_TRUE(is_hover_fading_in_or_visible()); + + ink_drop_animation_controller_.SetHovered(false); + EXPECT_FALSE(is_hover_fading_in_or_visible()); +} + TEST_F(InkDropAnimationControllerImplTest, HoveredStateAfterHoverTimerFiresWhenHostIsHovered) { ink_drop_host_.set_should_show_hover(true); @@ -69,7 +83,7 @@ task_runner_->RunPendingTasks(); - EXPECT_TRUE(ink_drop_animation_controller_.IsHovered()); + EXPECT_TRUE(is_hover_fading_in_or_visible()); } TEST_F(InkDropAnimationControllerImplTest, @@ -81,7 +95,18 @@ task_runner_->RunPendingTasks(); - EXPECT_FALSE(ink_drop_animation_controller_.IsHovered()); + EXPECT_FALSE(is_hover_fading_in_or_visible()); +} + +TEST_F(InkDropAnimationControllerImplTest, + HoveredStateNotVisibleOrFadingInAfterAnimateToState) { + ink_drop_host_.set_should_show_hover(true); + + ink_drop_animation_controller_.SetHovered(true); + EXPECT_TRUE(is_hover_fading_in_or_visible()); + + ink_drop_animation_controller_.AnimateToState(InkDropState::QUICK_ACTION); + EXPECT_FALSE(is_hover_fading_in_or_visible()); } } // namespace views
diff --git a/ui/views/animation/ink_drop_animation_unittest.cc b/ui/views/animation/ink_drop_animation_unittest.cc index cce5aca..5ac7757 100644 --- a/ui/views/animation/ink_drop_animation_unittest.cc +++ b/ui/views/animation/ink_drop_animation_unittest.cc
@@ -9,112 +9,31 @@ #include "base/memory/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/size.h" -#include "ui/gfx/geometry/size_f.h" #include "ui/views/animation/ink_drop_animation.h" #include "ui/views/animation/ink_drop_animation_observer.h" #include "ui/views/animation/ink_drop_state.h" +#include "ui/views/animation/square_ink_drop_animation.h" #include "ui/views/animation/test/ink_drop_animation_test_api.h" +#include "ui/views/animation/test/square_ink_drop_animation_test_api.h" +#include "ui/views/animation/test/test_ink_drop_animation_observer.h" namespace views { namespace test { -namespace { +// Represents all the derivatives of the InkDropAnimation class. To be used with +// the InkDropAnimationTest fixture to test all derviatives. +enum InkDropAnimationTestTypes { SQUARE_INK_DROP_ANIMATION }; -// Transforms a copy of |point| with |transform| and returns it. -gfx::Point TransformPoint(const gfx::Transform& transform, - const gfx::Point& point) { - gfx::Point transformed_point = point; - transform.TransformPoint(&transformed_point); - return transformed_point; -} - -class TestInkDropAnimationObserver : public InkDropAnimationObserver { - public: - TestInkDropAnimationObserver(); - ~TestInkDropAnimationObserver() override; - - // Resets all cached observation data. - void ResetObservations(); - - bool animation_started() const { return animation_started_; } - - bool animation_ended() const { return animation_ended_; } - - InkDropState last_animation_state_started() const { - return last_animation_state_started_; - } - - InkDropState last_animation_state_ended() const { - return last_animation_state_ended_; - } - - InkDropAnimationEndedReason last_animation_ended_reason() const { - return last_animation_ended_reason_; - } - - // InkDropAnimation: - void InkDropAnimationStarted(InkDropState ink_drop_state) override; - void InkDropAnimationEnded(InkDropState ink_drop_state, - InkDropAnimationEndedReason reason) override; - - private: - // True if InkDropAnimationStarted() has been invoked. - bool animation_started_; - - // True if InkDropAnimationEnded() has been invoked. - bool animation_ended_; - - // The |ink_drop_state| parameter used for the last invocation of - // InkDropAnimationStarted(). Only valid if |animation_started_| is true. - InkDropState last_animation_state_started_; - - // The |ink_drop_state| parameter used for the last invocation of - // InkDropAnimationEnded(). Only valid if |animation_ended_| is true. - InkDropState last_animation_state_ended_; - - // The |reason| parameter used for the last invocation of - // InkDropAnimationEnded(). Only valid if |animation_ended_| is true. - InkDropAnimationEndedReason last_animation_ended_reason_; - - DISALLOW_COPY_AND_ASSIGN(TestInkDropAnimationObserver); -}; - -TestInkDropAnimationObserver::TestInkDropAnimationObserver() - : animation_started_(false), - animation_ended_(false), - last_animation_state_started_(InkDropState::HIDDEN), - last_animation_state_ended_(InkDropState::HIDDEN), - last_animation_ended_reason_(InkDropAnimationEndedReason::SUCCESS) { - ResetObservations(); -} - -TestInkDropAnimationObserver::~TestInkDropAnimationObserver() {} - -void TestInkDropAnimationObserver::ResetObservations() { - animation_started_ = false; - animation_ended_ = false; - last_animation_state_ended_ = InkDropState::HIDDEN; - last_animation_state_started_ = InkDropState::HIDDEN; - last_animation_ended_reason_ = InkDropAnimationEndedReason::SUCCESS; -} - -void TestInkDropAnimationObserver::InkDropAnimationStarted( - InkDropState ink_drop_state) { - animation_started_ = true; - last_animation_state_started_ = ink_drop_state; -} - -void TestInkDropAnimationObserver::InkDropAnimationEnded( - InkDropState ink_drop_state, - InkDropAnimationEndedReason reason) { - animation_ended_ = true; - last_animation_state_ended_ = ink_drop_state; - last_animation_ended_reason_ = reason; -} - -} // namespace - -class InkDropAnimationTest : public testing::Test { +// Test fixture for all InkDropAnimation class derivatives. +// +// To add a new derivative: +// 1. Add a value to the InkDropAnimationTestTypes enum. +// 2. Implement set up and tear down code for the new enum value in +// InkDropAnimationTest() and +// ~InkDropAnimationTest(). +// 3. Add the new enum value to the INSTANTIATE_TEST_CASE_P) Values list. +class InkDropAnimationTest + : public testing::TestWithParam<InkDropAnimationTestTypes> { public: InkDropAnimationTest(); ~InkDropAnimationTest() override; @@ -122,369 +41,246 @@ protected: TestInkDropAnimationObserver observer_; - InkDropAnimation ink_drop_animation_; + scoped_ptr<InkDropAnimation> ink_drop_animation_; - InkDropAnimationTestApi test_api_; + scoped_ptr<InkDropAnimationTestApi> test_api_; private: DISALLOW_COPY_AND_ASSIGN(InkDropAnimationTest); }; -InkDropAnimationTest::InkDropAnimationTest() - : ink_drop_animation_(gfx::Size(10, 10), 2, gfx::Size(8, 8), 1), - test_api_(&ink_drop_animation_) { - ink_drop_animation_.AddObserver(&observer_); - test_api_.SetDisableAnimationTimers(true); +InkDropAnimationTest::InkDropAnimationTest() { + switch (GetParam()) { + case SQUARE_INK_DROP_ANIMATION: { + SquareInkDropAnimation* typed_ink_drop_animation = + new SquareInkDropAnimation(gfx::Size(10, 10), 2, gfx::Size(8, 8), 1); + ink_drop_animation_.reset(typed_ink_drop_animation); + test_api_.reset( + new SquareInkDropAnimationTestApi(typed_ink_drop_animation)); + break; + } + } + ink_drop_animation_->AddObserver(&observer_); + observer_.set_ink_drop_animation(ink_drop_animation_.get()); + test_api_->SetDisableAnimationTimers(true); } InkDropAnimationTest::~InkDropAnimationTest() {} -TEST_F(InkDropAnimationTest, InitialStateAfterConstruction) { - EXPECT_EQ(views::InkDropState::HIDDEN, ink_drop_animation_.ink_drop_state()); +// Note: First argument is optional and intentionally left blank. +// (it's a prefix for the generated test cases) +INSTANTIATE_TEST_CASE_P(, + InkDropAnimationTest, + testing::Values(SQUARE_INK_DROP_ANIMATION)); + +TEST_P(InkDropAnimationTest, InitialStateAfterConstruction) { + EXPECT_EQ(views::InkDropState::HIDDEN, + ink_drop_animation_->GetTargetInkDropState()); } -TEST_F(InkDropAnimationTest, VerifyObserversAreNotified) { - ink_drop_animation_.AnimateToState(InkDropState::ACTION_PENDING); +// Verify no animations are used when animating from HIDDEN to HIDDEN. +TEST_P(InkDropAnimationTest, AnimateToHiddenFromInvisibleState) { + EXPECT_EQ(InkDropState::HIDDEN, ink_drop_animation_->GetTargetInkDropState()); - ASSERT_TRUE(test_api_.HasActiveAnimations()); - EXPECT_TRUE(observer_.animation_started()); - EXPECT_EQ(InkDropState::ACTION_PENDING, - observer_.last_animation_state_started()); - EXPECT_FALSE(observer_.animation_ended()); + ink_drop_animation_->AnimateToState(InkDropState::HIDDEN); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); + EXPECT_FALSE(ink_drop_animation_->IsVisible()); +} - observer_.ResetObservations(); - test_api_.CompleteAnimations(); +TEST_P(InkDropAnimationTest, AnimateToHiddenFromVisibleState) { + ink_drop_animation_->AnimateToState(InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); - ASSERT_FALSE(test_api_.HasActiveAnimations()); - EXPECT_FALSE(observer_.animation_started()); - EXPECT_TRUE(observer_.animation_ended()); - EXPECT_EQ(InkDropState::ACTION_PENDING, + EXPECT_NE(InkDropState::HIDDEN, ink_drop_animation_->GetTargetInkDropState()); + + ink_drop_animation_->AnimateToState(InkDropState::HIDDEN); + test_api_->CompleteAnimations(); + + EXPECT_EQ(3, observer_.last_animation_started_ordinal()); + EXPECT_EQ(4, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, ActionPendingOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, QuickActionOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::QUICK_ACTION); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, SlowActionPendingOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::SLOW_ACTION_PENDING); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, SlowActionOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::SLOW_ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::SLOW_ACTION); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, ActivatedOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTIVATED); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_->GetCurrentOpacity()); +} + +TEST_P(InkDropAnimationTest, DeactivatedOpacity) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTIVATED); + ink_drop_animation_->AnimateToState(views::InkDropState::DEACTIVATED); + test_api_->CompleteAnimations(); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); +} + +// Verify animations are aborted during deletion and the +// InkDropAnimationObservers are notified. +TEST_P(InkDropAnimationTest, AnimationsAbortedDuringDeletion) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_.reset(); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(views::InkDropState::ACTION_PENDING, observer_.last_animation_state_ended()); -} - -TEST_F(InkDropAnimationTest, VerifyObserversAreNotifiedOfSuccessfulAnimations) { - ink_drop_animation_.AnimateToState(InkDropState::ACTION_PENDING); - test_api_.CompleteAnimations(); - - ASSERT_TRUE(observer_.animation_ended()); - EXPECT_EQ(InkDropAnimationObserver::InkDropAnimationEndedReason::SUCCESS, - observer_.last_animation_ended_reason()); -} - -TEST_F(InkDropAnimationTest, VerifyObserversAreNotifiedOfPreemptedAnimations) { - ink_drop_animation_.AnimateToState(InkDropState::ACTION_PENDING); - observer_.ResetObservations(); - - ink_drop_animation_.AnimateToState(InkDropState::SLOW_ACTION_PENDING); - - ASSERT_TRUE(observer_.animation_ended()); EXPECT_EQ(InkDropAnimationObserver::InkDropAnimationEndedReason::PRE_EMPTED, observer_.last_animation_ended_reason()); } -TEST_F(InkDropAnimationTest, AnimateToHiddenFromInvisibleState) { - ASSERT_EQ(InkDropState::HIDDEN, ink_drop_animation_.ink_drop_state()); +TEST_P(InkDropAnimationTest, VerifyObserversAreNotified) { + ink_drop_animation_->AnimateToState(InkDropState::ACTION_PENDING); - ink_drop_animation_.AnimateToState(InkDropState::HIDDEN); - EXPECT_TRUE(observer_.animation_started()); - EXPECT_TRUE(observer_.animation_ended()); + EXPECT_TRUE(test_api_->HasActiveAnimations()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_TRUE(observer_.AnimationHasNotEnded()); + EXPECT_EQ(InkDropState::ACTION_PENDING, + observer_.last_animation_state_started()); + + test_api_->CompleteAnimations(); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropState::ACTION_PENDING, + observer_.last_animation_state_ended()); } -TEST_F(InkDropAnimationTest, AnimateToHiddenFromVisibleState) { - ink_drop_animation_.AnimateToState(InkDropState::ACTION_PENDING); - test_api_.CompleteAnimations(); +TEST_P(InkDropAnimationTest, VerifyObserversAreNotifiedOfSuccessfulAnimations) { + ink_drop_animation_->AnimateToState(InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); - observer_.ResetObservations(); - - ASSERT_NE(InkDropState::HIDDEN, ink_drop_animation_.ink_drop_state()); - - ink_drop_animation_.AnimateToState(InkDropState::HIDDEN); - - EXPECT_TRUE(observer_.animation_started()); - EXPECT_FALSE(observer_.animation_ended()); - - test_api_.CompleteAnimations(); - - EXPECT_TRUE(observer_.animation_started()); - EXPECT_TRUE(observer_.animation_ended()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimationObserver::InkDropAnimationEndedReason::SUCCESS, + observer_.last_animation_ended_reason()); } -TEST_F(InkDropAnimationTest, AnimateToActionPending) { - ink_drop_animation_.AnimateToState(views::InkDropState::ACTION_PENDING); - test_api_.CompleteAnimations(); +TEST_P(InkDropAnimationTest, VerifyObserversAreNotifiedOfPreemptedAnimations) { + ink_drop_animation_->AnimateToState(InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(InkDropState::SLOW_ACTION_PENDING); - EXPECT_EQ(views::InkDropState::ACTION_PENDING, - ink_drop_animation_.ink_drop_state()); - EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_.GetCurrentOpacity()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropAnimationObserver::InkDropAnimationEndedReason::PRE_EMPTED, + observer_.last_animation_ended_reason()); } -TEST_F(InkDropAnimationTest, AnimateToQuickAction) { - ink_drop_animation_.AnimateToState(views::InkDropState::ACTION_PENDING); - ink_drop_animation_.AnimateToState(views::InkDropState::QUICK_ACTION); - test_api_.CompleteAnimations(); - - EXPECT_EQ(views::InkDropState::QUICK_ACTION, - ink_drop_animation_.ink_drop_state()); - EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_.GetCurrentOpacity()); -} - -TEST_F(InkDropAnimationTest, AnimateToSlowActionPending) { - ink_drop_animation_.AnimateToState(views::InkDropState::ACTION_PENDING); - ink_drop_animation_.AnimateToState(views::InkDropState::SLOW_ACTION_PENDING); - test_api_.CompleteAnimations(); - - EXPECT_EQ(views::InkDropState::SLOW_ACTION_PENDING, - ink_drop_animation_.ink_drop_state()); - EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_.GetCurrentOpacity()); -} - -TEST_F(InkDropAnimationTest, AnimateToSlowAction) { - ink_drop_animation_.AnimateToState(views::InkDropState::ACTION_PENDING); - ink_drop_animation_.AnimateToState(views::InkDropState::SLOW_ACTION_PENDING); - ink_drop_animation_.AnimateToState(views::InkDropState::SLOW_ACTION); - test_api_.CompleteAnimations(); - - EXPECT_EQ(views::InkDropState::SLOW_ACTION, - ink_drop_animation_.ink_drop_state()); - EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_.GetCurrentOpacity()); -} - -TEST_F(InkDropAnimationTest, AnimateToActivated) { - ink_drop_animation_.AnimateToState(views::InkDropState::ACTIVATED); - test_api_.CompleteAnimations(); - +TEST_P(InkDropAnimationTest, InkDropStatesPersistWhenCallingAnimateToState) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::ACTIVATED); EXPECT_EQ(views::InkDropState::ACTIVATED, - ink_drop_animation_.ink_drop_state()); - EXPECT_EQ(InkDropAnimation::kVisibleOpacity, test_api_.GetCurrentOpacity()); + ink_drop_animation_->GetTargetInkDropState()); } -TEST_F(InkDropAnimationTest, AnimateToDeactivated) { - ink_drop_animation_.AnimateToState(views::InkDropState::ACTIVATED); - ink_drop_animation_.AnimateToState(views::InkDropState::DEACTIVATED); - test_api_.CompleteAnimations(); +TEST_P(InkDropAnimationTest, HideImmediatelyWithoutActiveAnimations) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + test_api_->CompleteAnimations(); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); - EXPECT_EQ(views::InkDropState::DEACTIVATED, - ink_drop_animation_.ink_drop_state()); - EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_.GetCurrentOpacity()); + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_NE(InkDropState::HIDDEN, ink_drop_animation_->GetTargetInkDropState()); + + ink_drop_animation_->HideImmediately(); + + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_EQ(views::InkDropState::HIDDEN, + ink_drop_animation_->GetTargetInkDropState()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); + EXPECT_FALSE(ink_drop_animation_->IsVisible()); } // Verifies all active animations are aborted and the InkDropState is set to // HIDDEN after invoking HideImmediately(). -TEST_F(InkDropAnimationTest, HideImmediately) { - ink_drop_animation_.AnimateToState(views::InkDropState::ACTION_PENDING); - ASSERT_TRUE(test_api_.HasActiveAnimations()); - ASSERT_NE(InkDropState::HIDDEN, ink_drop_animation_.ink_drop_state()); - ASSERT_TRUE(observer_.animation_started()); +TEST_P(InkDropAnimationTest, HideImmediatelyWithActiveAnimations) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + EXPECT_TRUE(test_api_->HasActiveAnimations()); + EXPECT_NE(InkDropState::HIDDEN, ink_drop_animation_->GetTargetInkDropState()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); - observer_.ResetObservations(); - ink_drop_animation_.HideImmediately(); + ink_drop_animation_->HideImmediately(); - EXPECT_FALSE(test_api_.HasActiveAnimations()); - EXPECT_EQ(views::InkDropState::HIDDEN, ink_drop_animation_.ink_drop_state()); - ASSERT_FALSE(observer_.animation_started()); + EXPECT_FALSE(test_api_->HasActiveAnimations()); + EXPECT_EQ(views::InkDropState::HIDDEN, + ink_drop_animation_->GetTargetInkDropState()); + EXPECT_EQ(1, observer_.last_animation_started_ordinal()); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(InkDropState::ACTION_PENDING, + observer_.last_animation_state_ended()); + EXPECT_EQ(InkDropAnimationObserver::InkDropAnimationEndedReason::PRE_EMPTED, + observer_.last_animation_ended_reason()); + + EXPECT_EQ(InkDropAnimation::kHiddenOpacity, test_api_->GetCurrentOpacity()); + EXPECT_FALSE(ink_drop_animation_->IsVisible()); } -TEST(InkDropAnimationTransformTest, - TransformedPointsUsingTransformsFromCalculateCircleTransforms) { - const int kHalfDrawnSize = 5; - const int kDrawnSize = 2 * kHalfDrawnSize; - - const int kHalfTransformedSize = 100; - const int kTransformedSize = 2 * kHalfTransformedSize; - - // Constant points in the drawn space that will be transformed. - const gfx::Point center(kHalfDrawnSize, kHalfDrawnSize); - const gfx::Point mid_left(0, kHalfDrawnSize); - const gfx::Point mid_right(kDrawnSize, kHalfDrawnSize); - const gfx::Point top_mid(kHalfDrawnSize, 0); - const gfx::Point bottom_mid(kHalfDrawnSize, kDrawnSize); - - scoped_ptr<InkDropAnimation> ink_drop_animation( - new InkDropAnimation(gfx::Size(kDrawnSize, kDrawnSize), 2, - gfx::Size(kHalfDrawnSize, kHalfDrawnSize), 1)); - InkDropAnimationTestApi test_api(ink_drop_animation.get()); - - InkDropAnimationTestApi::InkDropTransforms transforms; - test_api.CalculateCircleTransforms( - gfx::Size(kTransformedSize, kTransformedSize), &transforms); - - // Transform variables to reduce verbosity of actual verification code. - const gfx::Transform kTopLeftTransform = - transforms[InkDropAnimationTestApi::PaintedShape::TOP_LEFT_CIRCLE]; - const gfx::Transform kTopRightTransform = - transforms[InkDropAnimationTestApi::PaintedShape::TOP_RIGHT_CIRCLE]; - const gfx::Transform kBottomRightTransform = - transforms[InkDropAnimationTestApi::PaintedShape::BOTTOM_RIGHT_CIRCLE]; - const gfx::Transform kBottomLeftTransform = - transforms[InkDropAnimationTestApi::PaintedShape::BOTTOM_LEFT_CIRCLE]; - const gfx::Transform kHorizontalTransform = - transforms[InkDropAnimationTestApi::PaintedShape::HORIZONTAL_RECT]; - const gfx::Transform kVerticalTransform = - transforms[InkDropAnimationTestApi::PaintedShape::VERTICAL_RECT]; - - // Top left circle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kTopLeftTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kTopLeftTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kTopLeftTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kTopLeftTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kTopLeftTransform, bottom_mid)); - - // Top right circle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kTopRightTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kTopRightTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kTopRightTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kTopRightTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kTopRightTransform, bottom_mid)); - - // Bottom right circle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kBottomRightTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kBottomRightTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kBottomRightTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kBottomRightTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kBottomRightTransform, bottom_mid)); - - // Bottom left circle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kBottomLeftTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kBottomLeftTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kBottomLeftTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kBottomLeftTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kBottomLeftTransform, bottom_mid)); - - // Horizontal rectangle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kHorizontalTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedSize, 0), - TransformPoint(kHorizontalTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedSize, 0), - TransformPoint(kHorizontalTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kHorizontalTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kHorizontalTransform, bottom_mid)); - - // Vertical rectangle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kVerticalTransform, center)); - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kVerticalTransform, mid_left)); - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kVerticalTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedSize), - TransformPoint(kVerticalTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedSize), - TransformPoint(kVerticalTransform, bottom_mid)); +TEST_P(InkDropAnimationTest, AnimateToVisibleFromHidden) { + EXPECT_EQ(InkDropState::HIDDEN, ink_drop_animation_->GetTargetInkDropState()); + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + EXPECT_TRUE(ink_drop_animation_->IsVisible()); } -TEST(InkDropAnimationTransformTest, - TransformedPointsUsingTransformsFromCalculateRectTransforms) { - const int kHalfDrawnSize = 5; - const int kDrawnSize = 2 * kHalfDrawnSize; +// Verifies that the value of InkDropAnimation::GetTargetInkDropState() returns +// the most recent value passed to AnimateToState() when notifying observers +// that an animation has started within the AnimateToState() function call. +TEST_P(InkDropAnimationTest, TargetInkDropStateOnAnimationStarted) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::HIDDEN); - const int kTransformedRadius = 10; + EXPECT_EQ(3, observer_.last_animation_started_ordinal()); + EXPECT_EQ(views::InkDropState::HIDDEN, + observer_.target_state_at_last_animation_started()); +} - const int kHalfTransformedWidth = 100; - const int kTransformedWidth = 2 * kHalfTransformedWidth; +// Verifies that the value of InkDropAnimation::GetTargetInkDropState() returns +// the most recent value passed to AnimateToState() when notifying observers +// that an animation has ended within the AnimateToState() function call. +TEST_P(InkDropAnimationTest, TargetInkDropStateOnAnimationEnded) { + ink_drop_animation_->AnimateToState(views::InkDropState::ACTION_PENDING); + ink_drop_animation_->AnimateToState(views::InkDropState::HIDDEN); - const int kHalfTransformedHeight = 100; - const int kTransformedHeight = 2 * kHalfTransformedHeight; - - // Constant points in the drawn space that will be transformed. - const gfx::Point center(kHalfDrawnSize, kHalfDrawnSize); - const gfx::Point mid_left(0, kHalfDrawnSize); - const gfx::Point mid_right(kDrawnSize, kHalfDrawnSize); - const gfx::Point top_mid(kHalfDrawnSize, 0); - const gfx::Point bottom_mid(kHalfDrawnSize, kDrawnSize); - - scoped_ptr<InkDropAnimation> ink_drop_animation( - new InkDropAnimation(gfx::Size(kDrawnSize, kDrawnSize), 2, - gfx::Size(kHalfDrawnSize, kHalfDrawnSize), 1)); - InkDropAnimationTestApi test_api(ink_drop_animation.get()); - - InkDropAnimationTestApi::InkDropTransforms transforms; - test_api.CalculateRectTransforms( - gfx::Size(kTransformedWidth, kTransformedHeight), kTransformedRadius, - &transforms); - - // Transform variables to reduce verbosity of actual verification code. - const gfx::Transform kTopLeftTransform = - transforms[InkDropAnimationTestApi::PaintedShape::TOP_LEFT_CIRCLE]; - const gfx::Transform kTopRightTransform = - transforms[InkDropAnimationTestApi::PaintedShape::TOP_RIGHT_CIRCLE]; - const gfx::Transform kBottomRightTransform = - transforms[InkDropAnimationTestApi::PaintedShape::BOTTOM_RIGHT_CIRCLE]; - const gfx::Transform kBottomLeftTransform = - transforms[InkDropAnimationTestApi::PaintedShape::BOTTOM_LEFT_CIRCLE]; - const gfx::Transform kHorizontalTransform = - transforms[InkDropAnimationTestApi::PaintedShape::HORIZONTAL_RECT]; - const gfx::Transform kVerticalTransform = - transforms[InkDropAnimationTestApi::PaintedShape::VERTICAL_RECT]; - - const int x_offset = kHalfTransformedWidth - kTransformedRadius; - const int y_offset = kHalfTransformedWidth - kTransformedRadius; - - // Top left circle - EXPECT_EQ(gfx::Point(-x_offset, -y_offset), - TransformPoint(kTopLeftTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedWidth, -y_offset), - TransformPoint(kTopLeftTransform, mid_left)); - EXPECT_EQ(gfx::Point(-x_offset, -kHalfTransformedHeight), - TransformPoint(kTopLeftTransform, top_mid)); - - // Top right circle - EXPECT_EQ(gfx::Point(x_offset, -y_offset), - TransformPoint(kTopRightTransform, center)); - EXPECT_EQ(gfx::Point(kHalfTransformedWidth, -y_offset), - TransformPoint(kTopRightTransform, mid_right)); - EXPECT_EQ(gfx::Point(x_offset, -kHalfTransformedHeight), - TransformPoint(kTopRightTransform, top_mid)); - - // Bottom right circle - EXPECT_EQ(gfx::Point(x_offset, y_offset), - TransformPoint(kBottomRightTransform, center)); - EXPECT_EQ(gfx::Point(kHalfTransformedWidth, y_offset), - TransformPoint(kBottomRightTransform, mid_right)); - EXPECT_EQ(gfx::Point(x_offset, kHalfTransformedHeight), - TransformPoint(kBottomRightTransform, bottom_mid)); - - // Bottom left circle - EXPECT_EQ(gfx::Point(-x_offset, y_offset), - TransformPoint(kBottomLeftTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedWidth, y_offset), - TransformPoint(kBottomLeftTransform, mid_left)); - EXPECT_EQ(gfx::Point(-x_offset, kHalfTransformedHeight), - TransformPoint(kBottomLeftTransform, bottom_mid)); - - // Horizontal rectangle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kHorizontalTransform, center)); - EXPECT_EQ(gfx::Point(-kHalfTransformedWidth, 0), - TransformPoint(kHorizontalTransform, mid_left)); - EXPECT_EQ(gfx::Point(kHalfTransformedWidth, 0), - TransformPoint(kHorizontalTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -y_offset), - TransformPoint(kHorizontalTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, y_offset), - TransformPoint(kHorizontalTransform, bottom_mid)); - - // Vertical rectangle - EXPECT_EQ(gfx::Point(0, 0), TransformPoint(kVerticalTransform, center)); - EXPECT_EQ(gfx::Point(-x_offset, 0), - TransformPoint(kVerticalTransform, mid_left)); - EXPECT_EQ(gfx::Point(x_offset, 0), - TransformPoint(kVerticalTransform, mid_right)); - EXPECT_EQ(gfx::Point(0, -kHalfTransformedHeight), - TransformPoint(kVerticalTransform, top_mid)); - EXPECT_EQ(gfx::Point(0, kHalfTransformedHeight), - TransformPoint(kVerticalTransform, bottom_mid)); + EXPECT_EQ(2, observer_.last_animation_ended_ordinal()); + EXPECT_EQ(views::InkDropState::HIDDEN, + observer_.target_state_at_last_animation_ended()); } } // namespace test
diff --git a/ui/views/animation/ink_drop_hover.cc b/ui/views/animation/ink_drop_hover.cc index 439ee24..311ddaf 100644 --- a/ui/views/animation/ink_drop_hover.cc +++ b/ui/views/animation/ink_drop_hover.cc
@@ -27,7 +27,8 @@ } // namespace InkDropHover::InkDropHover(const gfx::Size& size, int corner_radius) - : layer_delegate_( + : last_animation_initiated_was_fade_in_(false), + layer_delegate_( new RoundedRectangleLayerDelegate(kHoverColor, size, corner_radius)), layer_(new ui::Layer()) { layer_->SetBounds(gfx::Rect(size)); @@ -42,8 +43,8 @@ InkDropHover::~InkDropHover() {} -bool InkDropHover::IsVisible() const { - return layer_->visible(); +bool InkDropHover::IsFadingInOrVisible() const { + return last_animation_initiated_was_fade_in_; } void InkDropHover::FadeIn(const base::TimeDelta& duration) { @@ -58,6 +59,8 @@ void InkDropHover::AnimateFade(HoverAnimationType animation_type, const base::TimeDelta& duration) { + last_animation_initiated_was_fade_in_ = animation_type == FADE_IN; + // The |animation_observer| will be destroyed when the // AnimationStartedCallback() returns true. ui::CallbackLayerAnimationObserver* animation_observer =
diff --git a/ui/views/animation/ink_drop_hover.h b/ui/views/animation/ink_drop_hover.h index 462bbbe..33736695 100644 --- a/ui/views/animation/ink_drop_hover.h +++ b/ui/views/animation/ink_drop_hover.h
@@ -27,8 +27,9 @@ InkDropHover(const gfx::Size& size, int corner_radius); ~InkDropHover(); - // Returns true if the hover layer is visible. - bool IsVisible() const; + // Returns true if the hover animation is either in the process of fading + // in or is fully visible. + bool IsFadingInOrVisible() const; // Fades in the hover visual over the given |duration|. void FadeIn(const base::TimeDelta& duration); @@ -55,6 +56,10 @@ HoverAnimationType animation_type, const ui::CallbackLayerAnimationObserver& observer); + // True if the last animation to be initiated was a FADE_IN, and false + // otherwise. + bool last_animation_initiated_was_fade_in_; + // The LayerDelegate that paints the hover |layer_|. scoped_ptr<RoundedRectangleLayerDelegate> layer_delegate_;
diff --git a/ui/views/animation/ink_drop_hover_unittest.cc b/ui/views/animation/ink_drop_hover_unittest.cc index 08ee8b9..21c6326 100644 --- a/ui/views/animation/ink_drop_hover_unittest.cc +++ b/ui/views/animation/ink_drop_hover_unittest.cc
@@ -41,17 +41,17 @@ TEST_F(InkDropHoverTest, InitialStateAfterConstruction) { scoped_ptr<InkDropHover> ink_drop_hover = CreateInkDropHover(); - EXPECT_FALSE(ink_drop_hover->IsVisible()); + EXPECT_FALSE(ink_drop_hover->IsFadingInOrVisible()); } TEST_F(InkDropHoverTest, IsHoveredStateTransitions) { scoped_ptr<InkDropHover> ink_drop_hover = CreateInkDropHover(); ink_drop_hover->FadeIn(base::TimeDelta::FromMilliseconds(0)); - EXPECT_TRUE(ink_drop_hover->IsVisible()); + EXPECT_TRUE(ink_drop_hover->IsFadingInOrVisible()); ink_drop_hover->FadeOut(base::TimeDelta::FromMilliseconds(0)); - EXPECT_FALSE(ink_drop_hover->IsVisible()); + EXPECT_FALSE(ink_drop_hover->IsFadingInOrVisible()); } } // namespace test
diff --git a/ui/views/animation/square_ink_drop_animation.cc b/ui/views/animation/square_ink_drop_animation.cc new file mode 100644 index 0000000..3b1a41e0 --- /dev/null +++ b/ui/views/animation/square_ink_drop_animation.cc
@@ -0,0 +1,615 @@ +// 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 "ui/views/animation/square_ink_drop_animation.h" + +#include <algorithm> + +#include "base/command_line.h" +#include "base/logging.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/base/ui_base_switches.h" +#include "ui/compositor/callback_layer_animation_observer.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animation_sequence.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/geometry/point3_f.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/vector3d_f.h" +#include "ui/gfx/transform_util.h" +#include "ui/views/animation/ink_drop_painted_layer_delegates.h" +#include "ui/views/view.h" + +namespace { + +// The minimum scale factor to use when scaling rectangle layers. Smaller values +// were causing visual anomalies. +const float kMinimumRectScale = 0.0001f; + +// The minimum scale factor to use when scaling circle layers. Smaller values +// were causing visual anomalies. +const float kMinimumCircleScale = 0.001f; + +// The ink drop color. +const SkColor kInkDropColor = SK_ColorBLACK; + +// All the sub animations that are used to animate each of the InkDropStates. +// These are used to get time durations with +// GetAnimationDuration(InkDropSubAnimations). Note that in general a sub +// animation defines the duration for either a transformation animation or an +// opacity animation but there are some exceptions where an entire InkDropState +// animation consists of only 1 sub animation and it defines the duration for +// both the transformation and opacity animations. +enum InkDropSubAnimations { + // HIDDEN sub animations. + + // The HIDDEN sub animation that is fading out to a hidden opacity. + HIDDEN_FADE_OUT, + + // The HIDDEN sub animation that transforms the shape to a |small_size_| + // circle. + HIDDEN_TRANSFORM, + + // ACTION_PENDING sub animations. + + // The ACTION_PENDING sub animation that fades in to the visible opacity. + ACTION_PENDING_FADE_IN, + + // The ACTION_PENDING sub animation that transforms the shape to a + // |large_size_| circle. + ACTION_PENDING_TRANSFORM, + + // QUICK_ACTION sub animations. + + // The QUICK_ACTION sub animation that is fading out to a hidden opacity. + QUICK_ACTION_FADE_OUT, + + // The QUICK_ACTION sub animation that transforms the shape to a |large_size_| + // circle. + QUICK_ACTION_TRANSFORM, + + // SLOW_ACTION_PENDING sub animations. + + // The SLOW_ACTION_PENDING animation has only one sub animation which animates + // to a |small_size_| rounded rectangle at visible opacity. + SLOW_ACTION_PENDING, + + // SLOW_ACTION sub animations. + + // The SLOW_ACTION sub animation that is fading out to a hidden opacity. + SLOW_ACTION_FADE_OUT, + + // The SLOW_ACTION sub animation that transforms the shape to a |large_size_| + // rounded rectangle. + SLOW_ACTION_TRANSFORM, + + // ACTIVATED sub animations. + + // The ACTIVATED sub animation that transforms the shape to a |large_size_| + // circle. This is used when the ink drop is in a HIDDEN state prior to + // animating to the ACTIVATED state. + ACTIVATED_CIRCLE_TRANSFORM, + + // The ACTIVATED sub animation that transforms the shape to a |small_size_| + // rounded rectangle. + ACTIVATED_RECT_TRANSFORM, + + // DEACTIVATED sub animations. + + // The DEACTIVATED sub animation that is fading out to a hidden opacity. + DEACTIVATED_FADE_OUT, + + // The DEACTIVATED sub animation that transforms the shape to a |large_size_| + // rounded rectangle. + DEACTIVATED_TRANSFORM, +}; + +// The scale factor used to burst the QUICK_ACTION bubble as it fades out. +const float kQuickActionBurstScale = 1.3f; + +// A multiplicative factor used to slow down InkDropState animations. +const int kSlowAnimationDurationFactor = 3; + +// Checks CommandLine switches to determine if the visual feedback should have +// a fast animations speed. +bool UseFastAnimations() { + static bool fast = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + (::switches::kMaterialDesignInkDropAnimationSpeed)) != + ::switches::kMaterialDesignInkDropAnimationSpeedSlow; + return fast; +} + +// Duration constants for InkDropStateSubAnimations. See the +// InkDropStateSubAnimations enum documentation for more info. +int kAnimationDurationInMs[] = { + 150, // HIDDEN_FADE_OUT + 200, // HIDDEN_TRANSFORM + 0, // ACTION_PENDING_FADE_IN + 160, // ACTION_PENDING_TRANSFORM + 150, // QUICK_ACTION_FADE_OUT + 160, // QUICK_ACTION_TRANSFORM + 200, // SLOW_ACTION_PENDING + 150, // SLOW_ACTION_FADE_OUT + 200, // SLOW_ACTION_TRANSFORM + 200, // ACTIVATED_CIRCLE_TRANSFORM + 160, // ACTIVATED_RECT_TRANSFORM + 150, // DEACTIVATED_FADE_OUT + 200, // DEACTIVATED_TRANSFORM +}; + +// Returns the InkDropState sub animation duration for the given |state|. +base::TimeDelta GetAnimationDuration(InkDropSubAnimations state) { + return base::TimeDelta::FromMilliseconds( + (UseFastAnimations() ? 1 : kSlowAnimationDurationFactor) * + kAnimationDurationInMs[state]); +} + +// Calculates a Transform for a circle layer. The transform will be set up to +// translate the |drawn_center_point| to the origin, scale, and then translate +// to the target point defined by |target_center_x| and |target_center_y|. +gfx::Transform CalculateCircleTransform(const gfx::Point& drawn_center_point, + float scale, + float target_center_x, + float target_center_y) { + gfx::Transform transform; + transform.Translate(target_center_x, target_center_y); + transform.Scale(scale, scale); + transform.Translate(-drawn_center_point.x(), -drawn_center_point.y()); + return transform; +} + +// Calculates a Transform for a rectangle layer. The transform will be set up to +// translate the |drawn_center_point| to the origin and then scale by the +// |x_scale| and |y_scale| factors. +gfx::Transform CalculateRectTransform(const gfx::Point& drawn_center_point, + float x_scale, + float y_scale) { + gfx::Transform transform; + transform.Scale(x_scale, y_scale); + transform.Translate(-drawn_center_point.x(), -drawn_center_point.y()); + return transform; +} + +} // namespace + +namespace views { + +SquareInkDropAnimation::SquareInkDropAnimation(const gfx::Size& large_size, + int large_corner_radius, + const gfx::Size& small_size, + int small_corner_radius) + : large_size_(large_size), + large_corner_radius_(large_corner_radius), + small_size_(small_size), + small_corner_radius_(small_corner_radius), + circle_layer_delegate_(new CircleLayerDelegate( + kInkDropColor, + std::min(large_size_.width(), large_size_.height()) / 2)), + rect_layer_delegate_( + new RectangleLayerDelegate(kInkDropColor, large_size_)), + root_layer_(ui::LAYER_NOT_DRAWN), + ink_drop_state_(InkDropState::HIDDEN) { + root_layer_.set_name("SquareInkDropAnimation:ROOT_LAYER"); + + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) + AddPaintLayer(static_cast<PaintedShape>(i)); + + root_layer_.SetMasksToBounds(false); + root_layer_.SetBounds(gfx::Rect(large_size_)); + + SetStateToHidden(); +} + +SquareInkDropAnimation::~SquareInkDropAnimation() { + // Explicitly aborting all the animations ensures all callbacks are invoked + // while this instance still exists. + AbortAllAnimations(); +} + +ui::Layer* SquareInkDropAnimation::GetRootLayer() { + return &root_layer_; +} + +InkDropState SquareInkDropAnimation::GetTargetInkDropState() const { + return ink_drop_state_; +} + +bool SquareInkDropAnimation::IsVisible() const { + return root_layer_.visible(); +} + +float SquareInkDropAnimation::GetCurrentOpacity() const { + return root_layer_.opacity(); +} + +void SquareInkDropAnimation::AnimateToState(InkDropState ink_drop_state) { + // Does not return early if |ink_drop_state_| == |ink_drop_state| for two + // reasons. + // 1. The attached observers must be notified of all animations started and + // ended. + // 2. Not all state transitions is are valid, especially no-op transitions, + // and these should be detected by DCHECKs in AnimateStateChange(). + + // |animation_observer| will be deleted when AnimationEndedCallback() returns + // true. + // TODO(bruthig): Implement a safer ownership model for the + // |animation_observer|. + ui::CallbackLayerAnimationObserver* animation_observer = + new ui::CallbackLayerAnimationObserver( + base::Bind(&SquareInkDropAnimation::AnimationStartedCallback, + base::Unretained(this), ink_drop_state), + base::Bind(&SquareInkDropAnimation::AnimationEndedCallback, + base::Unretained(this), ink_drop_state)); + + InkDropState old_ink_drop_state = ink_drop_state_; + // Assign to |ink_drop_state_| before calling AnimateStateChange() so that any + // observers notified as a side effect of the AnimateStateChange() will get + // the target InkDropState when calling GetInkDropState(). + ink_drop_state_ = ink_drop_state; + + if (old_ink_drop_state == InkDropState::HIDDEN && + ink_drop_state_ != InkDropState::HIDDEN) { + root_layer_.SetVisible(true); + } + + AnimateStateChange(old_ink_drop_state, ink_drop_state_, animation_observer); + animation_observer->SetActive(); +} + +void SquareInkDropAnimation::SetCenterPoint(const gfx::Point& center_point) { + gfx::Transform transform; + transform.Translate(center_point.x(), center_point.y()); + root_layer_.SetTransform(transform); +} + +void SquareInkDropAnimation::HideImmediately() { + AbortAllAnimations(); + SetStateToHidden(); + ink_drop_state_ = InkDropState::HIDDEN; +} + +std::string SquareInkDropAnimation::ToLayerName(PaintedShape painted_shape) { + switch (painted_shape) { + case TOP_LEFT_CIRCLE: + return "TOP_LEFT_CIRCLE"; + case TOP_RIGHT_CIRCLE: + return "TOP_RIGHT_CIRCLE"; + case BOTTOM_RIGHT_CIRCLE: + return "BOTTOM_RIGHT_CIRCLE"; + case BOTTOM_LEFT_CIRCLE: + return "BOTTOM_LEFT_CIRCLE"; + case HORIZONTAL_RECT: + return "HORIZONTAL_RECT"; + case VERTICAL_RECT: + return "VERTICAL_RECT"; + case PAINTED_SHAPE_COUNT: + NOTREACHED() << "The PAINTED_SHAPE_COUNT value should never be used."; + return "PAINTED_SHAPE_COUNT"; + } + return "UNKNOWN"; +} + +void SquareInkDropAnimation::AnimateStateChange( + InkDropState old_ink_drop_state, + InkDropState new_ink_drop_state, + ui::LayerAnimationObserver* animation_observer) { + InkDropTransforms transforms; + + switch (new_ink_drop_state) { + case InkDropState::HIDDEN: + if (!IsVisible()) { + SetStateToHidden(); + break; + } else { + AnimateToOpacity(kHiddenOpacity, GetAnimationDuration(HIDDEN_FADE_OUT), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + CalculateCircleTransforms(small_size_, &transforms); + AnimateToTransforms( + transforms, GetAnimationDuration(HIDDEN_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + } + break; + case InkDropState::ACTION_PENDING: + DCHECK(old_ink_drop_state == InkDropState::HIDDEN); + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(ACTION_PENDING_FADE_IN), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(ACTION_PENDING_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + CalculateCircleTransforms(large_size_, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(ACTION_PENDING_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + case InkDropState::QUICK_ACTION: { + DCHECK(old_ink_drop_state == InkDropState::HIDDEN || + old_ink_drop_state == InkDropState::ACTION_PENDING); + AnimateToOpacity(kHiddenOpacity, + GetAnimationDuration(QUICK_ACTION_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + gfx::Size s = ScaleToRoundedSize(large_size_, kQuickActionBurstScale); + CalculateCircleTransforms(s, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(QUICK_ACTION_TRANSFORM), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::SLOW_ACTION_PENDING: + DCHECK(old_ink_drop_state == InkDropState::ACTION_PENDING); + AnimateToOpacity(kVisibleOpacity, + GetAnimationDuration(SLOW_ACTION_PENDING), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN, animation_observer); + CalculateRectTransforms(small_size_, small_corner_radius_, &transforms); + AnimateToTransforms(transforms, GetAnimationDuration(SLOW_ACTION_PENDING), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + case InkDropState::SLOW_ACTION: { + DCHECK(old_ink_drop_state == InkDropState::SLOW_ACTION_PENDING); + base::TimeDelta visible_duration = + GetAnimationDuration(SLOW_ACTION_TRANSFORM) - + GetAnimationDuration(SLOW_ACTION_FADE_OUT); + AnimateToOpacity(kVisibleOpacity, visible_duration, + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + AnimateToOpacity(kHiddenOpacity, + GetAnimationDuration(SLOW_ACTION_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(SLOW_ACTION_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::ACTIVATED: { + // Animate the opacity so that it cancels any opacity animations already + // in progress. + AnimateToOpacity(kVisibleOpacity, base::TimeDelta(), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + + ui::LayerAnimator::PreemptionStrategy rect_transform_preemption_strategy = + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET; + if (old_ink_drop_state == InkDropState::HIDDEN) { + rect_transform_preemption_strategy = + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION; + CalculateCircleTransforms(large_size_, &transforms); + AnimateToTransforms( + transforms, GetAnimationDuration(ACTIVATED_CIRCLE_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + } else if (old_ink_drop_state == InkDropState::ACTION_PENDING) { + rect_transform_preemption_strategy = + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION; + } + + CalculateRectTransforms(small_size_, small_corner_radius_, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(ACTIVATED_RECT_TRANSFORM), + rect_transform_preemption_strategy, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + case InkDropState::DEACTIVATED: { + base::TimeDelta visible_duration = + GetAnimationDuration(DEACTIVATED_TRANSFORM) - + GetAnimationDuration(DEACTIVATED_FADE_OUT); + AnimateToOpacity(kVisibleOpacity, visible_duration, + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); + AnimateToOpacity(kHiddenOpacity, + GetAnimationDuration(DEACTIVATED_FADE_OUT), + ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, + gfx::Tween::EASE_IN_OUT, animation_observer); + CalculateRectTransforms(large_size_, large_corner_radius_, &transforms); + AnimateToTransforms(transforms, + GetAnimationDuration(DEACTIVATED_TRANSFORM), + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, + gfx::Tween::EASE_IN_OUT, animation_observer); + break; + } + } +} + +void SquareInkDropAnimation::AnimateToTransforms( + const InkDropTransforms transforms, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* animation_observer) { + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) { + ui::LayerAnimator* animator = painted_layers_[i]->GetAnimator(); + ui::ScopedLayerAnimationSettings animation(animator); + animation.SetPreemptionStrategy(preemption_strategy); + animation.SetTweenType(tween); + ui::LayerAnimationElement* element = + ui::LayerAnimationElement::CreateTransformElement(transforms[i], + duration); + ui::LayerAnimationSequence* sequence = + new ui::LayerAnimationSequence(element); + + if (animation_observer) + sequence->AddObserver(animation_observer); + + animator->StartAnimation(sequence); + } +} + +void SquareInkDropAnimation::SetStateToHidden() { + InkDropTransforms transforms; + // Use non-zero size to avoid visual anomalies. + CalculateCircleTransforms(gfx::Size(1, 1), &transforms); + SetTransforms(transforms); + root_layer_.SetOpacity(InkDropAnimation::kHiddenOpacity); + root_layer_.SetVisible(false); +} + +void SquareInkDropAnimation::SetTransforms(const InkDropTransforms transforms) { + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) + painted_layers_[i]->SetTransform(transforms[i]); +} + +void SquareInkDropAnimation::SetOpacity(float opacity) { + root_layer_.SetOpacity(opacity); +} + +void SquareInkDropAnimation::AnimateToOpacity( + float opacity, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* animation_observer) { + ui::LayerAnimator* animator = root_layer_.GetAnimator(); + ui::ScopedLayerAnimationSettings animation_settings(animator); + animation_settings.SetPreemptionStrategy(preemption_strategy); + animation_settings.SetTweenType(tween); + ui::LayerAnimationElement* animation_element = + ui::LayerAnimationElement::CreateOpacityElement(opacity, duration); + ui::LayerAnimationSequence* animation_sequence = + new ui::LayerAnimationSequence(animation_element); + + if (animation_observer) + animation_sequence->AddObserver(animation_observer); + + animator->StartAnimation(animation_sequence); +} + +void SquareInkDropAnimation::CalculateCircleTransforms( + const gfx::Size& size, + InkDropTransforms* transforms_out) const { + CalculateRectTransforms(size, std::min(size.width(), size.height()) / 2.0f, + transforms_out); +} + +void SquareInkDropAnimation::CalculateRectTransforms( + const gfx::Size& size, + float corner_radius, + InkDropTransforms* transforms_out) const { + DCHECK_GE(size.width() / 2.0f, corner_radius) + << "The circle's diameter should not be greater than the total width."; + DCHECK_GE(size.height() / 2.0f, corner_radius) + << "The circle's diameter should not be greater than the total height."; + + // The shapes are drawn such that their center points are not at the origin. + // Thus we use the CalculateCircleTransform() and CalculateRectTransform() + // methods to calculate the complex Transforms. + + const float circle_scale = std::max( + kMinimumCircleScale, + corner_radius / static_cast<float>(circle_layer_delegate_->radius())); + + const float circle_target_x_offset = size.width() / 2.0f - corner_radius; + const float circle_target_y_offset = size.height() / 2.0f - corner_radius; + + (*transforms_out)[TOP_LEFT_CIRCLE] = CalculateCircleTransform( + ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, + -circle_target_x_offset, -circle_target_y_offset); + + (*transforms_out)[TOP_RIGHT_CIRCLE] = CalculateCircleTransform( + ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, + circle_target_x_offset, -circle_target_y_offset); + + (*transforms_out)[BOTTOM_RIGHT_CIRCLE] = CalculateCircleTransform( + ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, + circle_target_x_offset, circle_target_y_offset); + + (*transforms_out)[BOTTOM_LEFT_CIRCLE] = CalculateCircleTransform( + ToRoundedPoint(circle_layer_delegate_->GetCenterPoint()), circle_scale, + -circle_target_x_offset, circle_target_y_offset); + + const float rect_delegate_width = + static_cast<float>(rect_layer_delegate_->size().width()); + const float rect_delegate_height = + static_cast<float>(rect_layer_delegate_->size().height()); + + (*transforms_out)[HORIZONTAL_RECT] = CalculateRectTransform( + ToRoundedPoint(rect_layer_delegate_->GetCenterPoint()), + std::max(kMinimumRectScale, size.width() / rect_delegate_width), + std::max(kMinimumRectScale, + (size.height() - 2.0f * corner_radius) / rect_delegate_height)); + + (*transforms_out)[VERTICAL_RECT] = CalculateRectTransform( + ToRoundedPoint(rect_layer_delegate_->GetCenterPoint()), + std::max(kMinimumRectScale, + (size.width() - 2.0f * corner_radius) / rect_delegate_width), + std::max(kMinimumRectScale, size.height() / rect_delegate_height)); +} + +void SquareInkDropAnimation::GetCurrentTransforms( + InkDropTransforms* transforms_out) const { + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) + (*transforms_out)[i] = painted_layers_[i]->transform(); +} + +void SquareInkDropAnimation::AddPaintLayer(PaintedShape painted_shape) { + ui::LayerDelegate* delegate = nullptr; + switch (painted_shape) { + case TOP_LEFT_CIRCLE: + case TOP_RIGHT_CIRCLE: + case BOTTOM_RIGHT_CIRCLE: + case BOTTOM_LEFT_CIRCLE: + delegate = circle_layer_delegate_.get(); + break; + case HORIZONTAL_RECT: + case VERTICAL_RECT: + delegate = rect_layer_delegate_.get(); + break; + case PAINTED_SHAPE_COUNT: + NOTREACHED() << "PAINTED_SHAPE_COUNT is not an actual shape type."; + break; + } + + ui::Layer* layer = new ui::Layer(); + root_layer_.Add(layer); + + layer->SetBounds(gfx::Rect(large_size_)); + layer->SetFillsBoundsOpaquely(false); + layer->set_delegate(delegate); + layer->SetVisible(true); + layer->SetOpacity(1.0); + layer->SetMasksToBounds(false); + layer->set_name("PAINTED_SHAPE_COUNT:" + ToLayerName(painted_shape)); + + painted_layers_[painted_shape].reset(layer); +} + +void SquareInkDropAnimation::AbortAllAnimations() { + root_layer_.GetAnimator()->AbortAllAnimations(); + for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i) + painted_layers_[i]->GetAnimator()->AbortAllAnimations(); +} + +void SquareInkDropAnimation::AnimationStartedCallback( + InkDropState ink_drop_state, + const ui::CallbackLayerAnimationObserver& observer) { + NotifyAnimationStarted(ink_drop_state); +} + +bool SquareInkDropAnimation::AnimationEndedCallback( + InkDropState ink_drop_state, + const ui::CallbackLayerAnimationObserver& observer) { + if (ink_drop_state == InkDropState::HIDDEN) + SetStateToHidden(); + NotifyAnimationEnded(ink_drop_state, + observer.aborted_count() + ? InkDropAnimationObserver::PRE_EMPTED + : InkDropAnimationObserver::SUCCESS); + return true; +} + +} // namespace views
diff --git a/ui/views/animation/square_ink_drop_animation.h b/ui/views/animation/square_ink_drop_animation.h new file mode 100644 index 0000000..1e62b3d --- /dev/null +++ b/ui/views/animation/square_ink_drop_animation.h
@@ -0,0 +1,203 @@ +// 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 UI_VIEWS_ANIMATION_SQUARE_INK_DROP_ANIMATION_H_ +#define UI_VIEWS_ANIMATION_SQUARE_INK_DROP_ANIMATION_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animator.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/transform.h" +#include "ui/views/animation/ink_drop_animation.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/views_export.h" + +namespace ui { +class CallbackLayerAnimationObserver; +class Layer; +class LayerAnimationObserver; +} // namespace ui + +namespace views { +class CircleLayerDelegate; +class RectangleLayerDelegate; + +namespace test { +class SquareInkDropAnimationTestApi; +} // namespace test + +// An ink drop animation that smoothly animates between a circle and a rounded +// rectangle of different sizes for each of the different InkDropStates. The +// final frame for each InkDropState will be bounded by either a |large_size_| +// rectangle or a |small_size_| rectangle. +// +// The valid InkDropState transitions are defined below: +// +// {All InkDropStates} => HIDDEN +// HIDDEN => ACTION_PENDING +// HIDDEN, ACTION_PENDING => QUICK_ACTION +// ACTION_PENDING => SLOW_ACTION_PENDING +// SLOW_ACTION_PENDING => SLOW_ACTION +// {All InkDropStates} => ACTIVATED +// {All InkDropStates} => DEACTIVATED +// +class VIEWS_EXPORT SquareInkDropAnimation : public InkDropAnimation { + public: + SquareInkDropAnimation(const gfx::Size& large_size, + int large_corner_radius, + const gfx::Size& small_size, + int small_corner_radius); + ~SquareInkDropAnimation() override; + + // InkDropAnimation: + ui::Layer* GetRootLayer() override; + InkDropState GetTargetInkDropState() const override; + bool IsVisible() const override; + void AnimateToState(InkDropState ink_drop_state) override; + void SetCenterPoint(const gfx::Point& center_point) override; + void HideImmediately() override; + + private: + friend class test::SquareInkDropAnimationTestApi; + + // Enumeration of the different shapes that compose the ink drop. + enum PaintedShape { + TOP_LEFT_CIRCLE = 0, + TOP_RIGHT_CIRCLE, + BOTTOM_RIGHT_CIRCLE, + BOTTOM_LEFT_CIRCLE, + HORIZONTAL_RECT, + VERTICAL_RECT, + // The total number of shapes, not an actual shape. + PAINTED_SHAPE_COUNT + }; + + // Returns a human readable string for the |painted_shape| value. + static std::string ToLayerName(PaintedShape painted_shape); + + // Type that contains a gfx::Tansform for each of the layers required by the + // ink drop. + typedef gfx::Transform InkDropTransforms[PAINTED_SHAPE_COUNT]; + + float GetCurrentOpacity() const; + + // Animates the ripple from the |old_ink_drop_state| to the + // |new_ink_drop_state|. |observer| is added to all LayerAnimationSequence's + // used if not null. + void AnimateStateChange(InkDropState old_ink_drop_state, + InkDropState new_ink_drop_state, + ui::LayerAnimationObserver* observer); + + // Animates all of the painted shape layers to the specified |transforms|. The + // animation will be configured with the given |duration|, |tween|, and + // |preemption_strategy| values. The |observer| will be added to all + // LayerAnimationSequences if not null. + void AnimateToTransforms( + const InkDropTransforms transforms, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* observer); + + // Updates the transforms, opacity, and visibility to a HIDDEN state. + void SetStateToHidden(); + + // Sets the |transforms| on all of the shape layers. Note that this does not + // perform any animation. + void SetTransforms(const InkDropTransforms transforms); + + // Sets the opacity of the ink drop. Note that this does not perform any + // animation. + void SetOpacity(float opacity); + + // Animates all of the painted shape layers to the specified |opacity|. The + // animation will be configured with the given |duration|, |tween|, and + // |preemption_strategy| values. The |observer| will be added to all + // LayerAnimationSequences if not null. + void AnimateToOpacity( + float opacity, + base::TimeDelta duration, + ui::LayerAnimator::PreemptionStrategy preemption_strategy, + gfx::Tween::Type tween, + ui::LayerAnimationObserver* observer); + + // Updates all of the Transforms in |transforms_out| for a circle of the given + // |size|. + void CalculateCircleTransforms(const gfx::Size& size, + InkDropTransforms* transforms_out) const; + + // Updates all of the Transforms in |transforms_out| for a rounded rectangle + // of the given |size| and |corner_radius|. + void CalculateRectTransforms(const gfx::Size& size, + float corner_radius, + InkDropTransforms* transforms_out) const; + + // Updates all of the Transforms in |transforms_out| to the current Transforms + // of the painted shape Layers. + void GetCurrentTransforms(InkDropTransforms* transforms_out) const; + + // Adds and configures a new |painted_shape| layer to |painted_layers_|. + void AddPaintLayer(PaintedShape painted_shape); + + void AbortAllAnimations(); + + // The Callback invoked when all of the animation sequences for the specific + // |ink_drop_state| animation have started. |observer| is the + // ui::CallbackLayerAnimationObserver which is notifying the callback. + void AnimationStartedCallback( + InkDropState ink_drop_state, + const ui::CallbackLayerAnimationObserver& observer); + + // The Callback invoked when all of the animation sequences for the specific + // |ink_drop_state| animation have finished. |observer| is the + // ui::CallbackLayerAnimationObserver which is notifying the callback. + bool AnimationEndedCallback( + InkDropState ink_drop_state, + const ui::CallbackLayerAnimationObserver& observer); + + // Maximum size that an ink drop will be drawn to for any InkDropState whose + // final frame should be large. + gfx::Size large_size_; + + // Corner radius used to draw the rounded rectangles corner for any + // InkDropState whose final frame should be large. + int large_corner_radius_; + + // Maximum size that an ink drop will be drawn to for any InkDropState whose + // final frame should be small. + gfx::Size small_size_; + + // Corner radius used to draw the rounded rectangles corner for any + // InkDropState whose final frame should be small. + int small_corner_radius_; + + // ui::LayerDelegate to paint circles for all the circle layers. + scoped_ptr<CircleLayerDelegate> circle_layer_delegate_; + + // ui::LayerDelegate to paint rectangles for all the rectangle layers. + scoped_ptr<RectangleLayerDelegate> rect_layer_delegate_; + + // The root layer that parents the animating layers. The root layer is used to + // manipulate opacity and location, and its children are used to manipulate + // the different painted shapes that compose the ink drop. + ui::Layer root_layer_; + + // ui::Layers for all of the painted shape layers that compose the ink drop. + scoped_ptr<ui::Layer> painted_layers_[PAINTED_SHAPE_COUNT]; + + // The current ink drop state. + InkDropState ink_drop_state_; + + DISALLOW_COPY_AND_ASSIGN(SquareInkDropAnimation); +}; + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_SQUARE_INK_DROP_ANIMATION_H_
diff --git a/ui/views/animation/square_ink_drop_animation_unittest.cc b/ui/views/animation/square_ink_drop_animation_unittest.cc new file mode 100644 index 0000000..21d33c2 --- /dev/null +++ b/ui/views/animation/square_ink_drop_animation_unittest.cc
@@ -0,0 +1,245 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_UNITTEST_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_UNITTEST_H_ + +#include "ui/views/animation/square_ink_drop_animation.h" + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_f.h" +#include "ui/views/animation/ink_drop_animation_observer.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/animation/test/square_ink_drop_animation_test_api.h" +#include "ui/views/animation/test/test_ink_drop_animation_observer.h" + +namespace views { +namespace test { + +namespace { + +using PaintedShape = views::test::SquareInkDropAnimationTestApi::PaintedShape; + +// Transforms a copy of |point| with |transform| and returns it. +gfx::Point TransformPoint(const gfx::Transform& transform, + const gfx::Point& point) { + gfx::Point transformed_point = point; + transform.TransformPoint(&transformed_point); + return transformed_point; +} + +class SquareInkDropAnimationCalculateTransformsTest : public testing::Test { + public: + SquareInkDropAnimationCalculateTransformsTest(); + ~SquareInkDropAnimationCalculateTransformsTest() override; + + protected: + // Half the width/height of the drawn ink drop. + static const int kHalfDrawnSize; + + // The full width/height of the drawn ink drop. + static const int kDrawnSize; + + // The radius of the rounded rectangle corners. + static const int kTransformedRadius; + + // Half the width/height of the transformed ink drop. + static const int kHalfTransformedSize; + + // The full width/height of the transformed ink drop. + static const int kTransformedSize; + + // Constant points in the drawn space that will be transformed. + static const gfx::Point kDrawnCenterPoint; + static const gfx::Point kDrawnMidLeftPoint; + static const gfx::Point kDrawnMidRightPoint; + static const gfx::Point kDrawnTopMidPoint; + static const gfx::Point kDrawnBottomMidPoint; + + // The test target. + SquareInkDropAnimation ink_drop_animation_; + + // Provides internal access to the test target. + SquareInkDropAnimationTestApi test_api_; + + // The gfx::Transforms collection that is populated via the + // Calculate*Transforms() calls. + SquareInkDropAnimationTestApi::InkDropTransforms transforms_; + + private: + DISALLOW_COPY_AND_ASSIGN(SquareInkDropAnimationCalculateTransformsTest); +}; + +const int SquareInkDropAnimationCalculateTransformsTest::kHalfDrawnSize = 5; +const int SquareInkDropAnimationCalculateTransformsTest::kDrawnSize = + 2 * kHalfDrawnSize; + +const int SquareInkDropAnimationCalculateTransformsTest::kTransformedRadius = + 10; +const int SquareInkDropAnimationCalculateTransformsTest::kHalfTransformedSize = + 100; +const int SquareInkDropAnimationCalculateTransformsTest::kTransformedSize = + 2 * kHalfTransformedSize; + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnCenterPoint = + gfx::Point(kHalfDrawnSize, kHalfDrawnSize); + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnMidLeftPoint = + gfx::Point(0, kHalfDrawnSize); + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnMidRightPoint = + gfx::Point(kDrawnSize, kHalfDrawnSize); + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnTopMidPoint = + gfx::Point(kHalfDrawnSize, 0); + +const gfx::Point + SquareInkDropAnimationCalculateTransformsTest::kDrawnBottomMidPoint = + gfx::Point(kHalfDrawnSize, kDrawnSize); + +SquareInkDropAnimationCalculateTransformsTest:: + SquareInkDropAnimationCalculateTransformsTest() + : ink_drop_animation_(gfx::Size(kDrawnSize, kDrawnSize), + 2, + gfx::Size(kHalfDrawnSize, kHalfDrawnSize), + 1), + test_api_(&ink_drop_animation_) {} + +SquareInkDropAnimationCalculateTransformsTest:: + ~SquareInkDropAnimationCalculateTransformsTest() {} + +} // namespace + +TEST_F(SquareInkDropAnimationCalculateTransformsTest, + TransformedPointsUsingTransformsFromCalculateCircleTransforms) { + test_api_.CalculateCircleTransforms( + gfx::Size(kTransformedSize, kTransformedSize), &transforms_); + + struct { + PaintedShape shape; + gfx::Point center_point; + gfx::Point mid_left_point; + gfx::Point mid_right_point; + gfx::Point top_mid_point; + gfx::Point bottom_mid_point; + } test_cases[] = { + {PaintedShape::TOP_LEFT_CIRCLE, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), + gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}, + {PaintedShape::TOP_RIGHT_CIRCLE, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), + gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}, + {PaintedShape::BOTTOM_RIGHT_CIRCLE, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), + gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}, + {PaintedShape::BOTTOM_LEFT_CIRCLE, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), + gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}, + {PaintedShape::HORIZONTAL_RECT, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), gfx::Point(0, 0), gfx::Point(0, 0)}, + {PaintedShape::VERTICAL_RECT, gfx::Point(0, 0), gfx::Point(0, 0), + gfx::Point(0, 0), gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}}; + + for (size_t i = 0; i < arraysize(test_cases); ++i) { + PaintedShape shape = test_cases[i].shape; + SCOPED_TRACE(testing::Message() << "i=" << i << " shape=" << shape); + gfx::Transform transform = transforms_[shape]; + + EXPECT_EQ(test_cases[i].center_point, + TransformPoint(transform, kDrawnCenterPoint)); + EXPECT_EQ(test_cases[i].mid_left_point, + TransformPoint(transform, kDrawnMidLeftPoint)); + EXPECT_EQ(test_cases[i].mid_right_point, + TransformPoint(transform, kDrawnMidRightPoint)); + EXPECT_EQ(test_cases[i].top_mid_point, + TransformPoint(transform, kDrawnTopMidPoint)); + EXPECT_EQ(test_cases[i].bottom_mid_point, + TransformPoint(transform, kDrawnBottomMidPoint)); + } +} + +TEST_F(SquareInkDropAnimationCalculateTransformsTest, + TransformedPointsUsingTransformsFromCalculateRectTransforms) { + test_api_.CalculateRectTransforms( + gfx::Size(kTransformedSize, kTransformedSize), kTransformedRadius, + &transforms_); + + const int x_offset = kHalfTransformedSize - kTransformedRadius; + const int y_offset = kHalfTransformedSize - kTransformedRadius; + + struct { + PaintedShape shape; + gfx::Point center_point; + gfx::Point mid_left_point; + gfx::Point mid_right_point; + gfx::Point top_mid_point; + gfx::Point bottom_mid_point; + } test_cases[] = { + {PaintedShape::TOP_LEFT_CIRCLE, gfx::Point(-x_offset, -y_offset), + gfx::Point(-kHalfTransformedSize, -y_offset), + gfx::Point(-x_offset + kTransformedRadius, -y_offset), + gfx::Point(-x_offset, -kHalfTransformedSize), + gfx::Point(-x_offset, -y_offset + kTransformedRadius)}, + {PaintedShape::TOP_RIGHT_CIRCLE, gfx::Point(x_offset, -y_offset), + gfx::Point(x_offset - kTransformedRadius, -y_offset), + gfx::Point(kHalfTransformedSize, -y_offset), + gfx::Point(x_offset, -kHalfTransformedSize), + gfx::Point(x_offset, -y_offset + kTransformedRadius)}, + {PaintedShape::BOTTOM_RIGHT_CIRCLE, gfx::Point(x_offset, y_offset), + gfx::Point(x_offset - kTransformedRadius, y_offset), + gfx::Point(kHalfTransformedSize, y_offset), + gfx::Point(x_offset, y_offset - kTransformedRadius), + gfx::Point(x_offset, kHalfTransformedSize)}, + {PaintedShape::BOTTOM_LEFT_CIRCLE, gfx::Point(-x_offset, y_offset), + gfx::Point(-kHalfTransformedSize, y_offset), + gfx::Point(-x_offset + kTransformedRadius, y_offset), + gfx::Point(-x_offset, y_offset - kTransformedRadius), + gfx::Point(-x_offset, kHalfTransformedSize)}, + {PaintedShape::HORIZONTAL_RECT, gfx::Point(0, 0), + gfx::Point(-kHalfTransformedSize, 0), + gfx::Point(kHalfTransformedSize, 0), gfx::Point(0, -y_offset), + gfx::Point(0, y_offset)}, + {PaintedShape::VERTICAL_RECT, gfx::Point(0, 0), gfx::Point(-x_offset, 0), + gfx::Point(x_offset, 0), gfx::Point(0, -kHalfTransformedSize), + gfx::Point(0, kHalfTransformedSize)}}; + + for (size_t i = 0; i < arraysize(test_cases); ++i) { + PaintedShape shape = test_cases[i].shape; + SCOPED_TRACE(testing::Message() << "i=" << i << " shape=" << shape); + gfx::Transform transform = transforms_[shape]; + + EXPECT_EQ(test_cases[i].center_point, + TransformPoint(transform, kDrawnCenterPoint)); + EXPECT_EQ(test_cases[i].mid_left_point, + TransformPoint(transform, kDrawnMidLeftPoint)); + EXPECT_EQ(test_cases[i].mid_right_point, + TransformPoint(transform, kDrawnMidRightPoint)); + EXPECT_EQ(test_cases[i].top_mid_point, + TransformPoint(transform, kDrawnTopMidPoint)); + EXPECT_EQ(test_cases[i].bottom_mid_point, + TransformPoint(transform, kDrawnBottomMidPoint)); + } +} + +} // namespace test +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_UNITTEST_H_
diff --git a/ui/views/animation/test/ink_drop_animation_test_api.cc b/ui/views/animation/test/ink_drop_animation_test_api.cc index ffc04d0..a5714ca 100644 --- a/ui/views/animation/test/ink_drop_animation_test_api.cc +++ b/ui/views/animation/test/ink_drop_animation_test_api.cc
@@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/views/animation/test/ink_drop_animation_test_api.h" + #include "base/time/time.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animator.h" #include "ui/compositor/test/layer_animator_test_controller.h" #include "ui/views/animation/ink_drop_animation.h" -#include "ui/views/animation/test/ink_drop_animation_test_api.h" namespace views { namespace test { @@ -40,21 +41,13 @@ } } -float InkDropAnimationTestApi::GetCurrentOpacity() const { - return ink_drop_animation_->GetCurrentOpacity(); +std::vector<ui::LayerAnimator*> InkDropAnimationTestApi::GetLayerAnimators() { + return static_cast<const InkDropAnimationTestApi*>(this)->GetLayerAnimators(); } -void InkDropAnimationTestApi::CalculateCircleTransforms( - const gfx::Size& size, - InkDropTransforms* transforms_out) const { - ink_drop_animation_->CalculateCircleTransforms(size, transforms_out); -} -void InkDropAnimationTestApi::CalculateRectTransforms( - const gfx::Size& size, - float corner_radius, - InkDropTransforms* transforms_out) const { - ink_drop_animation_->CalculateRectTransforms(size, corner_radius, - transforms_out); +std::vector<ui::LayerAnimator*> InkDropAnimationTestApi::GetLayerAnimators() + const { + return std::vector<ui::LayerAnimator*>(); } void InkDropAnimationTestApi::StepAnimations(const base::TimeDelta& duration) { @@ -65,18 +58,5 @@ } } -std::vector<ui::LayerAnimator*> InkDropAnimationTestApi::GetLayerAnimators() { - return static_cast<const InkDropAnimationTestApi*>(this)->GetLayerAnimators(); -} - -std::vector<ui::LayerAnimator*> InkDropAnimationTestApi::GetLayerAnimators() - const { - std::vector<ui::LayerAnimator*> animators; - animators.push_back(ink_drop_animation_->root_layer_->GetAnimator()); - for (int i = 0; i < InkDropAnimation::PAINTED_SHAPE_COUNT; ++i) - animators.push_back(ink_drop_animation_->painted_layers_[i]->GetAnimator()); - return animators; -} - } // namespace test } // namespace views
diff --git a/ui/views/animation/test/ink_drop_animation_test_api.h b/ui/views/animation/test/ink_drop_animation_test_api.h index 5c71e9f..1b64f6e9 100644 --- a/ui/views/animation/test/ink_drop_animation_test_api.h +++ b/ui/views/animation/test/ink_drop_animation_test_api.h
@@ -8,7 +8,7 @@ #include <vector> #include "base/macros.h" -#include "ui/gfx/geometry/size.h" +#include "base/time/time.h" namespace ui { class LayerAnimator; @@ -19,16 +19,14 @@ namespace test { -// Test API to provide internal access to an InkDropAnimation. +// Base Test API used by test fixtures to validate all concrete implementations +// of the InkDropAnimation class. class InkDropAnimationTestApi { public: - typedef InkDropAnimation::InkDropTransforms InkDropTransforms; - typedef InkDropAnimation::PaintedShape PaintedShape; - explicit InkDropAnimationTestApi(InkDropAnimation* ink_drop_animation); - ~InkDropAnimationTestApi(); + virtual ~InkDropAnimationTestApi(); - // Disables the animation timers when |disable_timers| is true. This + // Disables the animation timers when |disable_timers| is true. void SetDisableAnimationTimers(bool disable_timers); // Returns true if any animations are active. @@ -37,26 +35,30 @@ // Completes all animations for all the Layer's owned by the InkDropAnimation. void CompleteAnimations(); - // Wrapper functions the wrapped InkDropedAnimation: - float GetCurrentOpacity() const; - void CalculateCircleTransforms(const gfx::Size& size, - InkDropTransforms* transforms_out) const; - void CalculateRectTransforms(const gfx::Size& size, - float corner_radius, - InkDropTransforms* transforms_out) const; + // Gets the opacity of the ink drop. + virtual float GetCurrentOpacity() const = 0; - private: - // Progresses all running LayerAnimationSequences by the given |duration|. - // NOTE: This function will NOT progress LayerAnimationSequences that are - // queued, only the running ones will be progressed. - void StepAnimations(const base::TimeDelta& duration); + protected: + InkDropAnimation* ink_drop_animation() { + return static_cast<const InkDropAnimationTestApi*>(this) + ->ink_drop_animation(); + } + + InkDropAnimation* ink_drop_animation() const { return ink_drop_animation_; } // Get a list of all the LayerAnimator's used internally by the // InkDropAnimation. std::vector<ui::LayerAnimator*> GetLayerAnimators(); - std::vector<ui::LayerAnimator*> GetLayerAnimators() const; + virtual std::vector<ui::LayerAnimator*> GetLayerAnimators() const; - // The InkDropAnimation to provide internal access to. + private: + // Progresses all running LayerAnimationSequences by the given |duration|. + // + // NOTE: This function will NOT progress LayerAnimationSequences that are + // queued, only the running ones will be progressed. + void StepAnimations(const base::TimeDelta& duration); + + // The InkDropedAnimation to provide internal access to. InkDropAnimation* ink_drop_animation_; DISALLOW_COPY_AND_ASSIGN(InkDropAnimationTestApi);
diff --git a/ui/views/animation/test/square_ink_drop_animation_test_api.cc b/ui/views/animation/test/square_ink_drop_animation_test_api.cc new file mode 100644 index 0000000..2e47eb28 --- /dev/null +++ b/ui/views/animation/test/square_ink_drop_animation_test_api.cc
@@ -0,0 +1,53 @@ +// 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. + +#include "ui/views/animation/test/square_ink_drop_animation_test_api.h" + +#include <vector> + +#include "base/time/time.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animator.h" +#include "ui/compositor/test/layer_animator_test_controller.h" +#include "ui/views/animation/ink_drop_animation.h" + +namespace views { +namespace test { + +SquareInkDropAnimationTestApi::SquareInkDropAnimationTestApi( + SquareInkDropAnimation* ink_drop_animation) + : InkDropAnimationTestApi(ink_drop_animation) {} + +SquareInkDropAnimationTestApi::~SquareInkDropAnimationTestApi() {} + +void SquareInkDropAnimationTestApi::CalculateCircleTransforms( + const gfx::Size& size, + InkDropTransforms* transforms_out) const { + ink_drop_animation()->CalculateCircleTransforms(size, transforms_out); +} +void SquareInkDropAnimationTestApi::CalculateRectTransforms( + const gfx::Size& size, + float corner_radius, + InkDropTransforms* transforms_out) const { + ink_drop_animation()->CalculateRectTransforms(size, corner_radius, + transforms_out); +} + +float SquareInkDropAnimationTestApi::GetCurrentOpacity() const { + return ink_drop_animation()->GetCurrentOpacity(); +} + +std::vector<ui::LayerAnimator*> +SquareInkDropAnimationTestApi::GetLayerAnimators() const { + std::vector<ui::LayerAnimator*> animators = + InkDropAnimationTestApi::GetLayerAnimators(); + animators.push_back(ink_drop_animation()->GetRootLayer()->GetAnimator()); + for (int i = 0; i < SquareInkDropAnimation::PAINTED_SHAPE_COUNT; ++i) + animators.push_back( + ink_drop_animation()->painted_layers_[i]->GetAnimator()); + return animators; +} + +} // namespace test +} // namespace views
diff --git a/ui/views/animation/test/square_ink_drop_animation_test_api.h b/ui/views/animation/test/square_ink_drop_animation_test_api.h new file mode 100644 index 0000000..a23c4ce4 --- /dev/null +++ b/ui/views/animation/test/square_ink_drop_animation_test_api.h
@@ -0,0 +1,64 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_VIEWS_ANIMATION_TEST_SQUARE_INK_DROP_ANIMATION_TEST_API_H_ +#define UI_VIEWS_ANIMATION_TEST_SQUARE_INK_DROP_ANIMATION_TEST_API_H_ + +#include <vector> + +#include "base/macros.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/animation/square_ink_drop_animation.h" +#include "ui/views/animation/test/ink_drop_animation_test_api.h" + +namespace ui { +class LayerAnimator; +} // namespace ui + +namespace views { +namespace test { + +// Test API to provide internal access to a SquareInkDropAnimation. +class SquareInkDropAnimationTestApi : public InkDropAnimationTestApi { + public: + // Make the private typedefs accessible. + using InkDropTransforms = SquareInkDropAnimation::InkDropTransforms; + using PaintedShape = SquareInkDropAnimation::PaintedShape; + + explicit SquareInkDropAnimationTestApi( + SquareInkDropAnimation* ink_drop_animation); + ~SquareInkDropAnimationTestApi() override; + + // Wrapper functions to the wrapped InkDropAnimation: + void CalculateCircleTransforms(const gfx::Size& size, + InkDropTransforms* transforms_out) const; + void CalculateRectTransforms(const gfx::Size& size, + float corner_radius, + InkDropTransforms* transforms_out) const; + + // InkDropAnimationTestApi: + float GetCurrentOpacity() const override; + + protected: + // InkDropAnimationTestApi: + std::vector<ui::LayerAnimator*> GetLayerAnimators() const override; + + private: + SquareInkDropAnimation* ink_drop_animation() { + return static_cast<const SquareInkDropAnimationTestApi*>(this) + ->ink_drop_animation(); + } + + SquareInkDropAnimation* ink_drop_animation() const { + return static_cast<SquareInkDropAnimation*>( + InkDropAnimationTestApi::ink_drop_animation()); + } + + DISALLOW_COPY_AND_ASSIGN(SquareInkDropAnimationTestApi); +}; + +} // namespace test +} // namespace views + +#endif // UI_VIEWS_ANIMATION_TEST_SQUARE_INK_DROP_ANIMATION_TEST_API_H_
diff --git a/ui/views/animation/test/test_ink_drop_animation_observer.cc b/ui/views/animation/test/test_ink_drop_animation_observer.cc new file mode 100644 index 0000000..fdf4e3a9 --- /dev/null +++ b/ui/views/animation/test/test_ink_drop_animation_observer.cc
@@ -0,0 +1,84 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/animation/test/test_ink_drop_animation_observer.h" + +#include "ui/views/animation/ink_drop_animation.h" + +namespace views { +namespace test { + +TestInkDropAnimationObserver::TestInkDropAnimationObserver() + : last_animation_started_ordinal_(-1), + last_animation_ended_ordinal_(-1), + last_animation_state_started_(InkDropState::HIDDEN), + last_animation_state_ended_(InkDropState::HIDDEN), + last_animation_ended_reason_(InkDropAnimationEndedReason::SUCCESS), + target_state_at_last_animation_started_(InkDropState::HIDDEN), + target_state_at_last_animation_ended_(InkDropState::HIDDEN) {} + +TestInkDropAnimationObserver::~TestInkDropAnimationObserver() {} + +testing::AssertionResult TestInkDropAnimationObserver::AnimationHasStarted() { + if (last_animation_started_ordinal() > 0) { + return testing::AssertionSuccess() << "Animations were started at ordinal=" + << last_animation_started_ordinal() + << "."; + } + return testing::AssertionFailure() << "Animations have not started."; +} + +testing::AssertionResult +TestInkDropAnimationObserver::AnimationHasNotStarted() { + if (last_animation_started_ordinal() < 0) + return testing::AssertionSuccess(); + return testing::AssertionFailure() << "Animations were started at ordinal=" + << last_animation_started_ordinal() << "."; +} + +testing::AssertionResult TestInkDropAnimationObserver::AnimationHasEnded() { + if (last_animation_ended_ordinal() > 0) { + return testing::AssertionSuccess() << "Animations were ended at ordinal=" + << last_animation_ended_ordinal() << "."; + } + return testing::AssertionFailure() << "Animations have not ended."; +} + +testing::AssertionResult TestInkDropAnimationObserver::AnimationHasNotEnded() { + if (last_animation_ended_ordinal() < 0) + return testing::AssertionSuccess(); + return testing::AssertionFailure() << "Animations were ended at ordinal=" + << last_animation_ended_ordinal() << "."; +} + +void TestInkDropAnimationObserver::InkDropAnimationStarted( + InkDropState ink_drop_state) { + last_animation_started_ordinal_ = GetNextOrdinal(); + last_animation_state_started_ = ink_drop_state; + if (ink_drop_animation_) { + target_state_at_last_animation_started_ = + ink_drop_animation_->GetTargetInkDropState(); + } +} + +void TestInkDropAnimationObserver::InkDropAnimationEnded( + InkDropState ink_drop_state, + InkDropAnimationEndedReason reason) { + last_animation_ended_ordinal_ = GetNextOrdinal(); + last_animation_state_ended_ = ink_drop_state; + last_animation_ended_reason_ = reason; + if (ink_drop_animation_) { + target_state_at_last_animation_ended_ = + ink_drop_animation_->GetTargetInkDropState(); + } +} + +int TestInkDropAnimationObserver::GetNextOrdinal() const { + return std::max(1, std::max(last_animation_started_ordinal_, + last_animation_ended_ordinal_) + + 1); +} + +} // namespace test +} // namespace views
diff --git a/ui/views/animation/test/test_ink_drop_animation_observer.h b/ui/views/animation/test/test_ink_drop_animation_observer.h new file mode 100644 index 0000000..7ce6dc69 --- /dev/null +++ b/ui/views/animation/test/test_ink_drop_animation_observer.h
@@ -0,0 +1,130 @@ +// 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 UI_VIEWS_ANIMATION_TEST_TEST_INK_DROP_ANIMATION_OBSERVER_H_ +#define UI_VIEWS_ANIMATION_TEST_TEST_INK_DROP_ANIMATION_OBSERVER_H_ + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/animation/ink_drop_animation_observer.h" +#include "ui/views/animation/ink_drop_state.h" + +namespace views { +class InkDropAnimation; + +namespace test { + +// Simple InkDropAnimationObserver test double that tracks if +// InkDropAnimationObserver methods are invoked and the parameters used for the +// last invocation. +class TestInkDropAnimationObserver : public InkDropAnimationObserver { + public: + TestInkDropAnimationObserver(); + ~TestInkDropAnimationObserver() override; + + void set_ink_drop_animation(InkDropAnimation* ink_drop_animation) { + ink_drop_animation_ = ink_drop_animation; + } + + int last_animation_started_ordinal() const { + return last_animation_started_ordinal_; + } + + int last_animation_ended_ordinal() const { + return last_animation_ended_ordinal_; + } + + InkDropState last_animation_state_started() const { + return last_animation_state_started_; + } + + InkDropState last_animation_state_ended() const { + return last_animation_state_ended_; + } + + InkDropAnimationEndedReason last_animation_ended_reason() const { + return last_animation_ended_reason_; + } + + InkDropState target_state_at_last_animation_started() const { + return target_state_at_last_animation_started_; + } + + InkDropState target_state_at_last_animation_ended() const { + return target_state_at_last_animation_ended_; + } + + // + // Collection of assertion predicates to be used with GTest test assertions. + // i.e. EXPECT_TRUE/EXPECT_FALSE and the ASSERT_ counterparts. + // + // Example: + // + // TestInkDropAnimationObserver observer; + // observer.set_ink_drop_animation(ink_drop_animation); + // EXPECT_TRUE(observer.AnimationHasNotStarted()); + // + + // Passes *_TRUE assertions when an InkDropAnimationStarted() event has been + // observed. + testing::AssertionResult AnimationHasStarted(); + + // Passes *_TRUE assertions when an InkDropAnimationStarted() event has NOT + // been observed. + testing::AssertionResult AnimationHasNotStarted(); + + // Passes *_TRUE assertions when an InkDropAnimationEnded() event has been + // observed. + testing::AssertionResult AnimationHasEnded(); + + // Passes *_TRUE assertions when an InkDropAnimationEnded() event has NOT been + // observed. + testing::AssertionResult AnimationHasNotEnded(); + + // InkDropAnimation: + void InkDropAnimationStarted(InkDropState ink_drop_state) override; + void InkDropAnimationEnded(InkDropState ink_drop_state, + InkDropAnimationEndedReason reason) override; + + private: + // Returns the next event ordinal. The first returned ordinal will be 1. + int GetNextOrdinal() const; + + // The ordinal time of the last InkDropAnimationStarted() event. + int last_animation_started_ordinal_; + + // The ordinal time of the last InkDropAnimationended() event. + int last_animation_ended_ordinal_; + + // The |ink_drop_state| parameter used for the last invocation of + // InkDropAnimationStarted(). Only valid if |animation_started_| is true. + InkDropState last_animation_state_started_; + + // The |ink_drop_state| parameter used for the last invocation of + // InkDropAnimationEnded(). Only valid if |animation_ended_| is true. + InkDropState last_animation_state_ended_; + + // The |reason| parameter used for the last invocation of + // InkDropAnimationEnded(). Only valid if |animation_ended_| is true. + InkDropAnimationEndedReason last_animation_ended_reason_; + + // The value of InkDropAnimation::GetTargetInkDropState() the last time an + // InkDropAnimationStarted() event was handled. This is only valid if + // |ink_drop_animation_| is not null. + InkDropState target_state_at_last_animation_started_; + + // The value of InkDropAnimation::GetTargetInkDropState() the last time an + // InkDropAnimationEnded() event was handled. This is only valid if + // |ink_drop_animation_| is not null. + InkDropState target_state_at_last_animation_ended_; + + // An InkDropAnimation to spy info from when notifications are handled. + InkDropAnimation* ink_drop_animation_; + + DISALLOW_COPY_AND_ASSIGN(TestInkDropAnimationObserver); +}; + +} // namespace test +} // namespace views + +#endif // UI_VIEWS_ANIMATION_TEST_TEST_INK_DROP_ANIMATION_OBSERVER_H_
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc index 1cd057e..0a5dca62 100644 --- a/ui/views/controls/menu/menu_controller.cc +++ b/ui/views/controls/menu/menu_controller.cc
@@ -162,6 +162,101 @@ return NULL; } +#if defined(OS_WIN) || defined(OS_CHROMEOS) +// Determines the correct cooridinates and window to repost |event| to, if it is +// a mouse or touch event. +static void RepostEventImpl(const ui::LocatedEvent* event, + const gfx::Point& screen_loc, + gfx::NativeView native_view, + gfx::NativeWindow window) { + if (!event->IsMouseEvent() && !event->IsTouchEvent()) { + // TODO(rbyers): Gesture event repost is tricky to get right + // crbug.com/170987. + DCHECK(event->IsGestureEvent()); + return; + } + + if (!native_view) + return; + +#if defined(OS_WIN) + gfx::Point screen_loc_pixels = gfx::win::DIPToScreenPoint(screen_loc); + HWND target_window = ::WindowFromPoint(screen_loc_pixels.ToPOINT()); + // If we don't find a native window for the HWND at the current location, + // then attempt to find a native window from its parent if one exists. + // There are HWNDs created outside views, which don't have associated + // native windows. + if (!window) { + HWND parent = ::GetParent(target_window); + if (parent) { + aura::WindowTreeHost* host = + aura::WindowTreeHost::GetForAcceleratedWidget(parent); + if (host) { + target_window = parent; + window = host->window(); + } + } + } + // Convert screen_loc to pixels for the Win32 API's like WindowFromPoint, + // PostMessage/SendMessage to work correctly. These API's expect the + // coordinates to be in pixels. + if (event->IsMouseEvent()) { + HWND source_window = HWNDForNativeView(native_view); + if (!target_window || !source_window || + GetWindowThreadProcessId(source_window, NULL) != + GetWindowThreadProcessId(target_window, NULL)) { + // Even though we have mouse capture, windows generates a mouse event if + // the other window is in a separate thread. Only repost an event if + // |target_window| and |source_window| were created on the same thread, + // else double events can occur and lead to bad behavior. + return; + } + + // Determine whether the click was in the client area or not. + // NOTE: WM_NCHITTEST coordinates are relative to the screen. + LPARAM coords = MAKELPARAM(screen_loc_pixels.x(), screen_loc_pixels.y()); + LRESULT nc_hit_result = SendMessage(target_window, WM_NCHITTEST, 0, coords); + const bool client_area = nc_hit_result == HTCLIENT; + + // TODO(sky): this isn't right. The event to generate should correspond with + // the event we just got. MouseEvent only tells us what is down, which may + // differ. Need to add ability to get changed button from MouseEvent. + int event_type; + int flags = event->flags(); + if (flags & ui::EF_LEFT_MOUSE_BUTTON) { + event_type = client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN; + } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) { + event_type = client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN; + } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) { + event_type = client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN; + } else { + NOTREACHED(); + return; + } + + int window_x = screen_loc_pixels.x(); + int window_y = screen_loc_pixels.y(); + if (client_area) { + POINT pt = {window_x, window_y}; + ScreenToClient(target_window, &pt); + window_x = pt.x; + window_y = pt.y; + } + + WPARAM target = client_area ? event->native_event().wParam : nc_hit_result; + LPARAM window_coords = MAKELPARAM(window_x, window_y); + PostMessage(target_window, event_type, target, window_coords); + return; + } +#endif + // Non Aura window. + if (!window) + return; + + MenuMessageLoop::RepostEventToWindow(event, window, screen_loc); +} +#endif // defined(OS_WIN) || defined(OS_CHROMEOS) + } // namespace // MenuScrollTask -------------------------------------------------------------- @@ -2181,110 +2276,6 @@ } } -void MenuController::RepostEvent(SubmenuView* source, - const ui::LocatedEvent* event, - const gfx::Point& screen_loc, - gfx::NativeView native_view, - gfx::NativeWindow window) { - if (!event->IsMouseEvent() && !event->IsTouchEvent()) { - // TODO(rbyers): Gesture event repost is tricky to get right - // crbug.com/170987. - DCHECK(event->IsGestureEvent()); - return; - } - -#if defined(OS_WIN) - if (!state_.item) { - // We some times get an event after closing all the menus. Ignore it. Make - // sure the menu is in fact not visible. If the menu is visible, then - // we're in a bad state where we think the menu isn't visibile but it is. - DCHECK(!source->GetWidget()->IsVisible()); - return; - } - - state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture(); -#endif - - if (!native_view) - return; - -#if defined(OS_WIN) - gfx::Point screen_loc_pixels = gfx::win::DIPToScreenPoint(screen_loc); - HWND target_window = ::WindowFromPoint(screen_loc_pixels.ToPOINT()); - // If we don't find a native window for the HWND at the current location, - // then attempt to find a native window from its parent if one exists. - // There are HWNDs created outside views, which don't have associated - // native windows. - if (!window) { - HWND parent = ::GetParent(target_window); - if (parent) { - aura::WindowTreeHost* host = - aura::WindowTreeHost::GetForAcceleratedWidget(parent); - if (host) { - target_window = parent; - window = host->window(); - } - } - } - // Convert screen_loc to pixels for the Win32 API's like WindowFromPoint, - // PostMessage/SendMessage to work correctly. These API's expect the - // coordinates to be in pixels. - if (event->IsMouseEvent()) { - HWND source_window = HWNDForNativeView(native_view); - if (!target_window || !source_window || - GetWindowThreadProcessId(source_window, NULL) != - GetWindowThreadProcessId(target_window, NULL)) { - // Even though we have mouse capture, windows generates a mouse event if - // the other window is in a separate thread. Only repost an event if - // |target_window| and |source_window| were created on the same thread, - // else double events can occur and lead to bad behavior. - return; - } - - // Determine whether the click was in the client area or not. - // NOTE: WM_NCHITTEST coordinates are relative to the screen. - LPARAM coords = MAKELPARAM(screen_loc_pixels.x(), screen_loc_pixels.y()); - LRESULT nc_hit_result = SendMessage(target_window, WM_NCHITTEST, 0, coords); - const bool client_area = nc_hit_result == HTCLIENT; - - // TODO(sky): this isn't right. The event to generate should correspond with - // the event we just got. MouseEvent only tells us what is down, which may - // differ. Need to add ability to get changed button from MouseEvent. - int event_type; - int flags = event->flags(); - if (flags & ui::EF_LEFT_MOUSE_BUTTON) { - event_type = client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN; - } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) { - event_type = client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN; - } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) { - event_type = client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN; - } else { - NOTREACHED(); - return; - } - - int window_x = screen_loc_pixels.x(); - int window_y = screen_loc_pixels.y(); - if (client_area) { - POINT pt = { window_x, window_y }; - ScreenToClient(target_window, &pt); - window_x = pt.x; - window_y = pt.y; - } - - WPARAM target = client_area ? event->native_event().wParam : nc_hit_result; - LPARAM window_coords = MAKELPARAM(window_x, window_y); - PostMessage(target_window, event_type, target, window_coords); - return; - } -#endif - // Non Aura window. - if (!window) - return; - - MenuMessageLoop::RepostEventToWindow(event, window, screen_loc); -} - void MenuController::RepostEventAndCancel(SubmenuView* source, const ui::LocatedEvent* event) { // Cancel can lead to the deletion |source| so we save the view and window to @@ -2302,9 +2293,29 @@ #endif #if defined(OS_WIN) - // We're going to close and we own the event capture. We need to repost the - // event, otherwise the window the user clicked on won't get the event. - RepostEvent(source, event, screen_loc, native_view, window); + if (event->IsMouseEvent() || event->IsTouchEvent()) { + bool async_run = async_run_; + if (state_.item) { + state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture(); + RepostEventImpl(event, screen_loc, native_view, window); + } else { + // We some times get an event after closing all the menus. Ignore it. Make + // sure the menu is in fact not visible. If the menu is visible, then + // we're in a bad state where we think the menu isn't visibile but it is. + DCHECK(!source->GetWidget()->IsVisible()); + } + + // We're going to close and we own the event capture. We need to repost the + // event, otherwise the window the user clicked on won't get the event. + // RepostEvent(source, event, screen_loc, native_view, window); + // MenuController may have been deleted if |async_run_| so check for an + // active + // instance before accessing member variables. + if (!GetActiveInstance()) { + DCHECK(async_run); + return; + } + } #endif // Determine target to see if a complete or partial close of the menu should @@ -2325,7 +2336,7 @@ // is handled normally after the context menu has exited. We call // RepostEvent after Cancel so that event capture has been released so // that finding the event target is unaffected by the current capture. - RepostEvent(source, event, screen_loc, native_view, window); + RepostEventImpl(event, screen_loc, native_view, window); #endif } @@ -2478,7 +2489,8 @@ MenuItemView* result = ExitMenuRun(); delegate->OnMenuClosed(internal::MenuControllerDelegate::NOTIFY_DELEGATE, result, accept_event_flags_); - if (nested && exit_type_ == EXIT_ALL) + // MenuController may have been deleted by |delegate|. + if (GetActiveInstance() && nested && exit_type_ == EXIT_ALL) ExitAsyncRun(); }
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h index 43c3c0ee..88b00cc8 100644 --- a/ui/views/controls/menu/menu_controller.h +++ b/ui/views/controls/menu/menu_controller.h
@@ -497,20 +497,12 @@ // the title. void SelectByChar(base::char16 key); - // For Windows and Aura we repost an event for some events that dismiss - // the context menu. The event is then reprocessed to cause its result - // if the context menu had not been present. + // For Windows and Aura we repost an event which dismisses the |source| menu. + // The menu is also canceled dependent on the target of the event. The event + // is then reprocessed to cause its result if the menu had not been present. // On non-aura Windows, a new mouse event is generated and posted to // the window (if there is one) at the location of the event. On // aura, the event is reposted on the RootWindow. - void RepostEvent(SubmenuView* source, - const ui::LocatedEvent* event, - const gfx::Point& screen_loc, - gfx::NativeView native_view, - gfx::NativeWindow window); - - // For Windows and Aura we repost an event which dismisses the |source| menu. - // The menu is also canceled dependent on the target of the event. void RepostEventAndCancel(SubmenuView* source, const ui::LocatedEvent* event); // Sets the drop target to new_item.
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc index 1e2f28d..a25610c 100644 --- a/ui/views/controls/menu/menu_controller_unittest.cc +++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -4,6 +4,7 @@ #include "ui/views/controls/menu/menu_controller.h" +#include "base/callback.h" #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -89,6 +90,11 @@ return on_menu_closed_mouse_event_flags_; } + // On a subsequent call to OnMenuClosed |controller| will be deleted. + void set_on_menu_closed_callback(const base::Closure& callback) { + on_menu_closed_callback_ = callback; + } + // internal::MenuControllerDelegate: void OnMenuClosed(NotifyType type, MenuItemView* menu, @@ -104,6 +110,9 @@ MenuItemView* on_menu_closed_menu_; int on_menu_closed_mouse_event_flags_; + // Optional callback triggered during OnMenuClosed + base::Closure on_menu_closed_callback_; + DISALLOW_COPY_AND_ASSIGN(TestMenuControllerDelegate); }; @@ -111,7 +120,8 @@ : on_menu_closed_called_(0), on_menu_closed_notify_type_(NOTIFY_DELEGATE), on_menu_closed_menu_(nullptr), - on_menu_closed_mouse_event_flags_(0) {} + on_menu_closed_mouse_event_flags_(0), + on_menu_closed_callback_() {} void TestMenuControllerDelegate::OnMenuClosed(NotifyType type, MenuItemView* menu, @@ -120,6 +130,8 @@ on_menu_closed_notify_type_ = type; on_menu_closed_menu_ = menu; on_menu_closed_mouse_event_flags_ = mouse_event_flags; + if (!on_menu_closed_callback_.is_null()) + on_menu_closed_callback_.Run(); } void TestMenuControllerDelegate::SiblingMenuCreated(MenuItemView* menu) {} @@ -237,12 +249,7 @@ void TearDown() override { owner_->CloseNow(); - - menu_controller_->showing_ = false; - menu_controller_->owner_ = nullptr; - delete menu_controller_; - menu_controller_ = nullptr; - + DestroyMenuController(); ViewsTestBase::TearDown(); } @@ -344,6 +351,13 @@ MenuController::INCREMENT_SELECTION_UP); } + void DestroyMenuControllerOnMenuClosed(TestMenuControllerDelegate* delegate) { + // Unretained() is safe here as the test should outlive the delegate. If not + // we want to know. + delegate->set_on_menu_closed_callback(base::Bind( + &MenuControllerTest::DestroyMenuController, base::Unretained(this))); + } + MenuItemView* FindInitialSelectableMenuItemDown(MenuItemView* parent) { return menu_controller_->FindInitialSelectableMenuItem( parent, MenuController::INCREMENT_SELECTION_DOWN); @@ -428,6 +442,19 @@ } private: + void DestroyMenuController() { + if (!menu_controller_) + return; + + if (!owner_->IsClosed()) + owner_->RemoveObserver(menu_controller_); + + menu_controller_->showing_ = false; + menu_controller_->owner_ = nullptr; + delete menu_controller_; + menu_controller_ = nullptr; + } + void Init() { owner_.reset(new Widget); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); @@ -926,5 +953,48 @@ RunMenu(); } +// Tests that having the MenuController deleted during RepostEvent does not +// cause a crash. ASAN bots should not detect use-after-free in MenuController. +TEST_F(MenuControllerTest, AsynchronousRepostEventDeletesController) { + MenuController* controller = menu_controller(); + scoped_ptr<TestMenuControllerDelegate> nested_delegate( + new TestMenuControllerDelegate()); + + ASSERT_FALSE(IsAsyncRun()); + + controller->AddNestedDelegate(nested_delegate.get()); + controller->SetAsyncRun(true); + + EXPECT_TRUE(IsAsyncRun()); + EXPECT_EQ(nested_delegate.get(), GetCurrentDelegate()); + + MenuItemView* item = menu_item(); + int mouse_event_flags = 0; + MenuItemView* run_result = + controller->Run(owner(), nullptr, item, gfx::Rect(), MENU_ANCHOR_TOPLEFT, + false, false, &mouse_event_flags); + EXPECT_EQ(run_result, nullptr); + + // Show a sub menu to targert with a pointer selection. However have the event + // occur outside of the bounds of the entire menu. + SubmenuView* sub_menu = item->GetSubmenu(); + sub_menu->ShowAt(owner(), item->bounds(), true); + gfx::Point location(sub_menu->bounds().bottom_right()); + location.Offset(1, 1); + ui::MouseEvent event(ui::ET_MOUSE_PRESSED, location, location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0); + + // This will lead to MenuController being deleted during the event repost. + // The remainder of this test, and TearDown should not crash. + DestroyMenuControllerOnMenuClosed(nested_delegate.get()); + // When attempting to select outside of all menus this should lead to a + // shutdown. This should not crash while attempting to repost the event. + SetSelectionOnPointerDown(sub_menu, &event); + + // Close to remove observers before test TearDown + sub_menu->Close(); + EXPECT_EQ(1, nested_delegate->on_menu_closed_called()); +} + } // namespace test } // namespace views
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc index 05c8cae..fa71a83 100644 --- a/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -263,9 +263,6 @@ // Now change the role. state->role = ui::AX_ROLE_MENU_BAR; - // Some AT (like NVDA) will not process focus events on menu item children - // unless a parent claims to be focused. - state->AddStateFlag(ui::AX_STATE_FOCUSED); } void MenuScrollViewContainer::OnBoundsChanged(
diff --git a/ui/views/controls/scrollbar/base_scroll_bar.cc b/ui/views/controls/scrollbar/base_scroll_bar.cc index 117fe34..be0740f6 100644 --- a/ui/views/controls/scrollbar/base_scroll_bar.cc +++ b/ui/views/controls/scrollbar/base_scroll_bar.cc
@@ -495,18 +495,20 @@ // In some combination of viewport_size and contents_size_, the result of // simple division can be rounded and there could be 1 pixel gap even when the // contents scroll down to the bottom. See crbug.com/244671 - if (contents_scroll_offset + viewport_size_ == contents_size_) { - int track_size = GetTrackSize(); - return track_size - (viewport_size_ * GetTrackSize() / contents_size_); - } - return (contents_scroll_offset * GetTrackSize()) / contents_size_; + int thumb_max = GetTrackSize() - thumb_->GetSize(); + if (contents_scroll_offset + viewport_size_ == contents_size_) + return thumb_max; + return (contents_scroll_offset * thumb_max) / + (contents_size_ - viewport_size_); } int BaseScrollBar::CalculateContentsOffset(int thumb_position, bool scroll_to_middle) const { + int thumb_size = thumb_->GetSize(); if (scroll_to_middle) - thumb_position = thumb_position - (thumb_->GetSize() / 2); - return (thumb_position * contents_size_) / GetTrackSize(); + thumb_position = thumb_position - (thumb_size / 2); + return (thumb_position * (contents_size_ - viewport_size_)) / + (GetTrackSize() - thumb_size); } void BaseScrollBar::SetThumbTrackState(CustomButton::ButtonState state) {
diff --git a/ui/views/controls/scrollbar/scrollbar_unittest.cc b/ui/views/controls/scrollbar/scrollbar_unittest.cc index 68a6687..4bfab1074 100644 --- a/ui/views/controls/scrollbar/scrollbar_unittest.cc +++ b/ui/views/controls/scrollbar/scrollbar_unittest.cc
@@ -34,7 +34,7 @@ return 10; } - // We save the last values in order to assert the corectness of the scroll + // We save the last values in order to assert the correctness of the scroll // operation. views::ScrollBar* last_source; bool last_is_positive; @@ -70,7 +70,7 @@ scrollbar_ = static_cast<NativeScrollBarViews*>(native_scrollbar_->native_wrapper_); scrollbar_->SetBounds(0, 0, 100, 100); - scrollbar_->Update(100, 200, 0); + scrollbar_->Update(100, 1000, 0); track_size_ = scrollbar_->GetTrackBounds().width(); } @@ -107,50 +107,58 @@ #endif TEST_F(NativeScrollBarTest, MAYBE_Scrolling) { - EXPECT_EQ(scrollbar_->GetPosition(), 0); - EXPECT_EQ(scrollbar_->GetMaxPosition(), 100); - EXPECT_EQ(scrollbar_->GetMinPosition(), 0); + EXPECT_EQ(0, scrollbar_->GetPosition()); + EXPECT_EQ(900, scrollbar_->GetMaxPosition()); + EXPECT_EQ(0, scrollbar_->GetMinPosition()); // Scroll to middle. - scrollbar_->ScrollToThumbPosition(track_size_ / 4, false); - EXPECT_EQ(controller_->last_position, 50); - EXPECT_EQ(controller_->last_source, native_scrollbar_); + scrollbar_->ScrollToThumbPosition(track_size_ / 2, true); + EXPECT_EQ(450, controller_->last_position); + EXPECT_EQ(native_scrollbar_, controller_->last_source); // Scroll to the end. - scrollbar_->ScrollToThumbPosition(track_size_ / 2, false); - EXPECT_EQ(controller_->last_position, 100); + scrollbar_->ScrollToThumbPosition(track_size_, true); + EXPECT_EQ(900, controller_->last_position); // Overscroll. Last position should be the maximum position. - scrollbar_->ScrollToThumbPosition(track_size_, false); - EXPECT_EQ(controller_->last_position, 100); + scrollbar_->ScrollToThumbPosition(track_size_ + 100, true); + EXPECT_EQ(900, controller_->last_position); // Underscroll. Last position should be the minimum position. scrollbar_->ScrollToThumbPosition(-10, false); - EXPECT_EQ(controller_->last_position, 0); + EXPECT_EQ(0, controller_->last_position); // Test the different fixed scrolling amounts. Generally used by buttons, // or click on track. scrollbar_->ScrollToThumbPosition(0, false); scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_NEXT_LINE); - EXPECT_EQ(controller_->last_position, 10); + EXPECT_EQ(10, controller_->last_position); scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_PREV_LINE); - EXPECT_EQ(controller_->last_position, 0); + EXPECT_EQ(0, controller_->last_position); scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_NEXT_PAGE); - EXPECT_EQ(controller_->last_position, 20); + EXPECT_EQ(20, controller_->last_position); scrollbar_->ScrollByAmount(BaseScrollBar::SCROLL_PREV_PAGE); - EXPECT_EQ(controller_->last_position, 0); + EXPECT_EQ(0, controller_->last_position); } TEST_F(NativeScrollBarTest, MAYBE_ScrollBarFitsToBottom) { - scrollbar_->Update(100, 199, 0); + scrollbar_->Update(100, 1999, 0); EXPECT_EQ(0, scrollbar_->GetPosition()); - EXPECT_EQ(99, scrollbar_->GetMaxPosition()); + EXPECT_EQ(1899, scrollbar_->GetMaxPosition()); EXPECT_EQ(0, scrollbar_->GetMinPosition()); - scrollbar_->Update(100, 199, 99); + // Scroll to the midpoint of the document. + scrollbar_->Update(100, 1999, 950); + EXPECT_EQ((scrollbar_->GetTrackBounds().width() - + scrollbar_->GetThumbSizeForTest()) / + 2, + scrollbar_->GetPosition()); + + // Scroll to the end of the document. + scrollbar_->Update(100, 1999, 1899); EXPECT_EQ( scrollbar_->GetTrackBounds().width() - scrollbar_->GetThumbSizeForTest(), scrollbar_->GetPosition()); @@ -158,11 +166,11 @@ TEST_F(NativeScrollBarTest, ScrollToEndAfterShrinkAndExpand) { // Scroll to the end of the content. - scrollbar_->Update(100, 101, 0); + scrollbar_->Update(100, 1001, 0); EXPECT_TRUE(scrollbar_->ScrollByContentsOffset(-1)); - // Shrink and then re-exapnd the content. - scrollbar_->Update(100, 100, 0); - scrollbar_->Update(100, 101, 0); + // Shrink and then re-expand the content. + scrollbar_->Update(100, 1000, 0); + scrollbar_->Update(100, 1001, 0); // Ensure the scrollbar allows scrolling to the end. EXPECT_TRUE(scrollbar_->ScrollByContentsOffset(-1)); }
diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn index 1ea33d6..71f251aa 100644 --- a/ui/views/mus/BUILD.gn +++ b/ui/views/mus/BUILD.gn
@@ -161,6 +161,7 @@ "//mojo/shell/background:lib", "//mojo/shell/background:main", "//mojo/shell/public/cpp:sources", + "//mojo/shell/runner/host:lib", "//skia", "//testing/gtest", "//third_party/icu",
diff --git a/ui/views/mus/DEPS b/ui/views/mus/DEPS index 2c575ed..f51da41a 100644 --- a/ui/views/mus/DEPS +++ b/ui/views/mus/DEPS
@@ -25,6 +25,7 @@ specific_include_rules = { "platform_test_helper_mus.cc": [ - "+mojo/shell/background" + "+mojo/shell/background", + "+mojo/shell/runner/host/command_line_switch.h", ], }
diff --git a/ui/views/mus/platform_test_helper_mus.cc b/ui/views/mus/platform_test_helper_mus.cc index 68e2f28b..9f1d481 100644 --- a/ui/views/mus/platform_test_helper_mus.cc +++ b/ui/views/mus/platform_test_helper_mus.cc
@@ -8,6 +8,7 @@ #include "mojo/shell/background/background_shell.h" #include "mojo/shell/public/cpp/shell_client.h" #include "mojo/shell/public/cpp/shell_connection.h" +#include "mojo/shell/runner/host/command_line_switch.h" #include "ui/views/mus/window_manager_connection.h" #include "url/gurl.h" @@ -30,7 +31,7 @@ base::CommandLine::ForCurrentProcess()->AppendSwitch("use-new-edk"); background_shell_.reset(new mojo::shell::BackgroundShell); - background_shell_->Init(); + background_shell_->Init(std::vector<mojo::shell::CommandLineSwitch>()); shell_client_.reset(new DefaultShellClient); shell_connection_.reset(new mojo::ShellConnection( shell_client_.get(),
diff --git a/ui/views/mus/platform_window_mus.cc b/ui/views/mus/platform_window_mus.cc index 7233c4c..07e3c6e 100644 --- a/ui/views/mus/platform_window_mus.cc +++ b/ui/views/mus/platform_window_mus.cc
@@ -28,7 +28,6 @@ mus_window_(mus_window), show_state_(mus::mojom::ShowState::RESTORED), last_cursor_(mus::mojom::Cursor::CURSOR_NULL), - has_capture_(false), mus_window_destroyed_(false) { DCHECK(delegate_); DCHECK(mus_window_); @@ -104,15 +103,11 @@ } void PlatformWindowMus::SetCapture() { - // TODO(sky): this is wrong, need real capture api. - has_capture_ = true; - NOTIMPLEMENTED(); + mus_window_->SetCapture(); } void PlatformWindowMus::ReleaseCapture() { - // TODO(sky): this is wrong, need real capture api. - has_capture_ = false; - NOTIMPLEMENTED(); + mus_window_->ReleaseCapture(); } void PlatformWindowMus::ToggleFullscreen() {
diff --git a/ui/views/mus/platform_window_mus.h b/ui/views/mus/platform_window_mus.h index ee124f5..5cc440b 100644 --- a/ui/views/mus/platform_window_mus.h +++ b/ui/views/mus/platform_window_mus.h
@@ -90,7 +90,6 @@ mus::Window* mus_window_; mus::mojom::ShowState show_state_; mus::mojom::Cursor last_cursor_; - bool has_capture_; // True if OnWindowDestroyed() has been received. bool mus_window_destroyed_;
diff --git a/ui/views/views.gyp b/ui/views/views.gyp index f96ecb3..f067521 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp
@@ -35,6 +35,8 @@ 'animation/ink_drop_state.h', 'animation/scroll_animator.cc', 'animation/scroll_animator.h', + 'animation/square_ink_drop_animation.cc', + 'animation/square_ink_drop_animation.h', 'background.cc', 'background.h', 'border.cc', @@ -474,6 +476,10 @@ 'views_test_support_sources': [ 'animation/test/ink_drop_animation_test_api.cc', 'animation/test/ink_drop_animation_test_api.h', + 'animation/test/square_ink_drop_animation_test_api.cc', + 'animation/test/square_ink_drop_animation_test_api.h', + 'animation/test/test_ink_drop_animation_observer.cc', + 'animation/test/test_ink_drop_animation_observer.h', 'animation/test/test_ink_drop_host.cc', 'animation/test/test_ink_drop_host.h', 'controls/textfield/textfield_test_api.cc', @@ -536,6 +542,7 @@ 'animation/ink_drop_animation_controller_impl_unittest.cc', 'animation/ink_drop_animation_unittest.cc', 'animation/ink_drop_hover_unittest.cc', + 'animation/square_ink_drop_animation_unittest.cc', 'bubble/bubble_border_unittest.cc', 'bubble/bubble_delegate_unittest.cc', 'bubble/bubble_frame_view_unittest.cc',
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm index 4be92c4..f307abd 100644 --- a/ui/views/widget/native_widget_mac.mm +++ b/ui/views/widget/native_widget_mac.mm
@@ -410,6 +410,10 @@ bridge_->SetVisibilityState(state == ui::SHOW_STATE_INACTIVE ? BridgedNativeWidget::SHOW_INACTIVE : BridgedNativeWidget::SHOW_AND_ACTIVATE_WINDOW); + + // Ignore the SetInitialFocus() result. BridgedContentView should get + // firstResponder status regardless. + delegate_->SetInitialFocus(state); } bool NativeWidgetMac::IsVisible() const {
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc index 5d368bd1..32866e1b 100644 --- a/ui/views/widget/widget_interactive_uitest.cc +++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -52,7 +52,7 @@ ~ExitLoopOnRelease() override {} private: - // Overridden from View: + // View: void OnMouseReleased(const ui::MouseEvent& event) override { GetWidget()->Close(); base::MessageLoop::current()->QuitNow(); @@ -68,7 +68,7 @@ ~GestureCaptureView() override {} private: - // Overridden from View: + // View: void OnGestureEvent(ui::GestureEvent* event) override { if (event->type() == ui::ET_GESTURE_TAP_DOWN) { GetWidget()->SetCapture(this); @@ -132,7 +132,7 @@ ~NestedLoopCaptureView() override {} private: - // Overridden from View: + // View: bool OnMousePressed(const ui::MouseEvent& event) override { // Start a nested loop. widget_->Show(); @@ -1107,9 +1107,60 @@ RunPendingMessages(); } +// Testing widget delegate that creates a widget with a single view, which +// should be initially focused. +class TestInitialFocusWidgetDelegate : public TestDesktopWidgetDelegate { + public: + explicit TestInitialFocusWidgetDelegate(gfx::NativeWindow context) + : view_(new View) { + view_->SetFocusable(true); + + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.context = context; + InitWidget(params); + GetWidget()->GetContentsView()->AddChildView(view_); + } + + View* view() { return view_; } + + // DialogDelegateView: + View* GetInitiallyFocusedView() override { return view_; } + + private: + View* view_; + + DISALLOW_COPY_AND_ASSIGN(TestInitialFocusWidgetDelegate); +}; + +// Testing initial focus is assigned properly for normal top-level widgets, +// and subclasses that specify a initially focused child view. +TEST_F(WidgetTestInteractive, InitialFocus) { + // By default, there is no initially focused view (even if there is a + // focusable subview). + Widget* toplevel(CreateTopLevelPlatformWidget()); + View* view = new View; + view->SetFocusable(true); + toplevel->GetContentsView()->AddChildView(view); + + ShowSync(toplevel); + toplevel->Show(); + EXPECT_FALSE(view->HasFocus()); + EXPECT_FALSE(toplevel->GetFocusManager()->GetStoredFocusView()); + toplevel->CloseNow(); + + // Testing a widget which specifies a initially focused view. + TestInitialFocusWidgetDelegate delegate(GetContext()); + + Widget* widget = delegate.GetWidget(); + ShowSync(widget); + widget->Show(); + EXPECT_TRUE(delegate.view()->HasFocus()); + EXPECT_EQ(delegate.view(), widget->GetFocusManager()->GetStoredFocusView()); +} + namespace { -// Used to veirfy OnMouseCaptureLost() has been invoked. +// Used to verify OnMouseCaptureLost() has been invoked. class CaptureLostTrackingWidget : public Widget { public: CaptureLostTrackingWidget() : got_capture_lost_(false) {}
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc index c41d58b..124e0e4 100644 --- a/ui/views/window/dialog_delegate_unittest.cc +++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -241,4 +241,10 @@ dialog2->TearDown(); } +// Tests default focus is assigned correctly when showing a new dialog. +TEST_F(DialogTest, InitialFocus) { + EXPECT_TRUE(dialog()->input()->HasFocus()); + EXPECT_EQ(dialog()->input(), dialog()->GetFocusManager()->GetFocusedView()); +} + } // namespace views
diff --git a/ui/webui/resources/js/compiled_resources2.gyp b/ui/webui/resources/js/compiled_resources2.gyp index 2d0ce66..4f35014 100644 --- a/ui/webui/resources/js/compiled_resources2.gyp +++ b/ui/webui/resources/js/compiled_resources2.gyp
@@ -58,5 +58,12 @@ ], 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], }, + { + 'target_name': 'i18n_behavior', + 'dependencies': [ + 'load_time_data', + ], + 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], + }, ], }
diff --git a/ui/webui/resources/js/cr/ui/splitter.js b/ui/webui/resources/js/cr/ui/splitter.js index 2c1799e..aacfacee 100644 --- a/ui/webui/resources/js/cr/ui/splitter.js +++ b/ui/webui/resources/js/cr/ui/splitter.js
@@ -135,6 +135,8 @@ */ handleMouseDown_: function(e) { e = /** @type {!MouseEvent} */(e); + if (e.button) + return; this.startDrag(e.clientX, false); // Default action is to start selection and to move focus. e.preventDefault();
diff --git a/url/gurl.cc b/url/gurl.cc index db8b277d..2b67bee 100644 --- a/url/gurl.cc +++ b/url/gurl.cc
@@ -332,7 +332,7 @@ } GURL GURL::GetAsReferrer() const { - if (!is_valid_ || !SchemeIsHTTPOrHTTPS()) + if (!SchemeIsValidForReferrer()) return GURL(); if (!has_ref() && !has_username() && !has_password()) @@ -386,6 +386,10 @@ return SchemeIs(url::kHttpScheme) || SchemeIs(url::kHttpsScheme); } +bool GURL::SchemeIsValidForReferrer() const { + return is_valid_ && IsReferrerScheme(spec_.data(), parsed_.scheme); +} + bool GURL::SchemeIsWSOrWSS() const { return SchemeIs(url::kWsScheme) || SchemeIs(url::kWssScheme); }
diff --git a/url/gurl.h b/url/gurl.h index 6af2bac..c55959b 100644 --- a/url/gurl.h +++ b/url/gurl.h
@@ -215,6 +215,9 @@ // Returns true if the scheme is "http" or "https". bool SchemeIsHTTPOrHTTPS() const; + // Returns true if the scheme is valid for use as a referrer. + bool SchemeIsValidForReferrer() const; + // Returns true is the scheme is "ws" or "wss". bool SchemeIsWSOrWSS() const;
diff --git a/url/url_util.cc b/url/url_util.cc index cbb252f..c1e9979 100644 --- a/url/url_util.cc +++ b/url/url_util.cc
@@ -34,13 +34,21 @@ {kFileSystemScheme, SCHEME_WITHOUT_AUTHORITY}, }; -// List of the currently installed standard schemes. This list is lazily -// initialized by InitStandardSchemes and is leaked on shutdown to prevent -// any destructors from being called that will slow us down or cause problems. -std::vector<SchemeWithType>* standard_schemes = NULL; +const int kNumReferrerURLSchemes = 2; +const SchemeWithType kReferrerURLSchemes[kNumReferrerURLSchemes] = { + {kHttpScheme, SCHEME_WITH_PORT}, + {kHttpsScheme, SCHEME_WITH_PORT}, +}; -// See the LockStandardSchemes declaration in the header. -bool standard_schemes_locked = false; +// Lists of the currently installed standard and referrer schemes. These lists +// are lazily initialized by InitStandardSchemes and InitReferrerSchemes and are +// leaked on shutdown to prevent any destructors from being called that will +// slow us down or cause problems. +std::vector<SchemeWithType>* standard_schemes = nullptr; +std::vector<SchemeWithType>* referrer_schemes = nullptr; + +// See the LockSchemeRegistries declaration in the header. +bool scheme_registries_locked = false; // This template converts a given character type to the corresponding // StringPiece type. @@ -53,14 +61,27 @@ typedef base::StringPiece16 Piece; }; -// Ensures that the standard_schemes list is initialized, does nothing if it -// already has values. -void InitStandardSchemes() { - if (standard_schemes) +void InitSchemes(std::vector<SchemeWithType>** schemes, + const SchemeWithType* initial_schemes, + size_t size) { + if (*schemes) return; - standard_schemes = new std::vector<SchemeWithType>; - for (int i = 0; i < kNumStandardURLSchemes; i++) - standard_schemes->push_back(kStandardURLSchemes[i]); + *schemes = new std::vector<SchemeWithType>(size); + for (size_t i = 0; i < size; i++) { + (*schemes)->push_back(initial_schemes[i]); + } +} + +// Ensures that the standard_schemes list is initialized, does nothing if +// it already has values. +void InitStandardSchemes() { + InitSchemes(&standard_schemes, kStandardURLSchemes, kNumStandardURLSchemes); +} + +// Ensures that the referrer_schemes list is initialized, does nothing if +// it already has values. +void InitReferrerSchemes() { + InitSchemes(&referrer_schemes, kReferrerURLSchemes, kNumReferrerURLSchemes); } // Given a string and a range inside the string, compares it to the given @@ -78,22 +99,20 @@ } // Returns true and sets |type| to the SchemeType of the given scheme -// identified by |scheme| within |spec| if the scheme is one of the registered -// "standard" schemes. +// identified by |scheme| within |spec| if in |schemes|. template<typename CHAR> -bool DoIsStandard(const CHAR* spec, - const Component& scheme, - SchemeType* type) { +bool DoIsInSchemes(const CHAR* spec, + const Component& scheme, + SchemeType* type, + const std::vector<SchemeWithType>& schemes) { if (!scheme.is_nonempty()) return false; // Empty or invalid schemes are non-standard. - InitStandardSchemes(); - for (size_t i = 0; i < standard_schemes->size(); i++) { - if (base::LowerCaseEqualsASCII( - typename CharToStringPiece<CHAR>::Piece( - &spec[scheme.begin], scheme.len), - standard_schemes->at(i).scheme)) { - *type = standard_schemes->at(i).type; + for (const SchemeWithType& scheme_with_type : schemes) { + if (base::LowerCaseEqualsASCII(typename CharToStringPiece<CHAR>::Piece( + &spec[scheme.begin], scheme.len), + scheme_with_type.scheme)) { + *type = scheme_with_type.type; return true; } } @@ -101,6 +120,13 @@ } template<typename CHAR> +bool DoIsStandard(const CHAR* spec, const Component& scheme, SchemeType* type) { + InitStandardSchemes(); + return DoIsInSchemes(spec, scheme, type, *standard_schemes); +} + + +template<typename CHAR> bool DoFindAndCompareScheme(const CHAR* str, int str_len, const char* compare, @@ -364,31 +390,19 @@ return ReplacePathURL(spec, parsed, replacements, output, out_parsed); } -} // namespace - -void Initialize() { - InitStandardSchemes(); -} - -void Shutdown() { - if (standard_schemes) { - delete standard_schemes; - standard_schemes = NULL; - } -} - -void AddStandardScheme(const char* new_scheme, - SchemeType type) { - // If this assert triggers, it means you've called AddStandardScheme after - // LockStandardSchemes have been called (see the header file for - // LockStandardSchemes for more). +void DoAddScheme(const char* new_scheme, + SchemeType type, + std::vector<SchemeWithType>* schemes) { + DCHECK(schemes); + // If this assert triggers, it means you've called Add*Scheme after + // LockSchemeRegistries has been called (see the header file for + // LockSchemeRegistries for more). // - // This normally means you're trying to set up a new standard scheme too late - // in your application's init process. Locate where your app does this - // initialization and calls LockStandardSchemes, and add your new standard - // scheme there. - DCHECK(!standard_schemes_locked) << - "Trying to add a standard scheme after the list has been locked."; + // This normally means you're trying to set up a new scheme too late in your + // application's init process. Locate where your app does this initialization + // and calls LockSchemeRegistries, and add your new scheme there. + DCHECK(!scheme_registries_locked) + << "Trying to add a scheme after the lists have been locked."; size_t scheme_len = strlen(new_scheme); if (scheme_len == 0) @@ -400,15 +414,42 @@ ANNOTATE_LEAKING_OBJECT_PTR(dup_scheme); memcpy(dup_scheme, new_scheme, scheme_len + 1); - InitStandardSchemes(); SchemeWithType scheme_with_type; scheme_with_type.scheme = dup_scheme; scheme_with_type.type = type; - standard_schemes->push_back(scheme_with_type); + schemes->push_back(scheme_with_type); } -void LockStandardSchemes() { - standard_schemes_locked = true; +} // namespace + +void Initialize() { + InitStandardSchemes(); + InitReferrerSchemes(); +} + +void Shutdown() { + if (standard_schemes) { + delete standard_schemes; + standard_schemes = NULL; + } + if (referrer_schemes) { + delete referrer_schemes; + referrer_schemes = NULL; + } +} + +void AddStandardScheme(const char* new_scheme, SchemeType type) { + InitStandardSchemes(); + DoAddScheme(new_scheme, type, standard_schemes); +} + +void AddReferrerScheme(const char* new_scheme, SchemeType type) { + InitReferrerSchemes(); + DoAddScheme(new_scheme, type, referrer_schemes); +} + +void LockSchemeRegistries() { + scheme_registries_locked = true; } bool IsStandard(const char* spec, const Component& scheme) { @@ -427,6 +468,12 @@ return DoIsStandard(spec, scheme, &unused_scheme_type); } +bool IsReferrerScheme(const char* spec, const Component& scheme) { + InitReferrerSchemes(); + SchemeType unused_scheme_type; + return DoIsInSchemes(spec, scheme, &unused_scheme_type, *referrer_schemes); +} + bool FindAndCompareScheme(const char* str, int str_len, const char* compare,
diff --git a/url/url_util.h b/url/url_util.h index 04a6ef4..a209a61c 100644 --- a/url/url_util.h +++ b/url/url_util.h
@@ -20,7 +20,7 @@ // Initialization is NOT required, it will be implicitly initialized when first // used. However, this implicit initialization is NOT threadsafe. If you are // using this library in a threaded environment and don't have a consistent -// "first call" (an example might be calling AddStandardScheme with your special +// "first call" (an example might be calling Add*Scheme with your special // application-specific schemes) then you will want to call initialize before // spawning any threads. // @@ -61,24 +61,33 @@ // URI syntax" (https://tools.ietf.org/html/rfc3986#section-3). // // This function is not threadsafe and can not be called concurrently with any -// other url_util function. It will assert if the list of standard schemes has -// been locked (see LockStandardSchemes). +// other url_util function. It will assert if the lists of schemes have +// been locked (see LockSchemeRegistries). URL_EXPORT void AddStandardScheme(const char* new_scheme, SchemeType scheme_type); -// Sets a flag to prevent future calls to AddStandardScheme from succeeding. +// Adds an application-defined scheme to the internal list of schemes allowed +// for referrers. +// +// This function is not threadsafe and can not be called concurrently with any +// other url_util function. It will assert if the lists of schemes have +// been locked (see LockSchemeRegistries). +URL_EXPORT void AddReferrerScheme(const char* new_scheme, + SchemeType scheme_type); + +// Sets a flag to prevent future calls to Add*Scheme from succeeding. // // This is designed to help prevent errors for multithreaded applications. -// Normal usage would be to call AddStandardScheme for your custom schemes at -// the beginning of program initialization, and then LockStandardSchemes. This -// prevents future callers from mistakenly calling AddStandardScheme when the +// Normal usage would be to call Add*Scheme for your custom schemes at +// the beginning of program initialization, and then LockSchemeRegistries. This +// prevents future callers from mistakenly calling Add*Scheme when the // program is running with multiple threads, where such usage would be // dangerous. // -// We could have had AddStandardScheme use a lock instead, but that would add +// We could have had Add*Scheme use a lock instead, but that would add // some platform-specific dependencies we don't otherwise have now, and is // overkill considering the normal usage is so simple. -URL_EXPORT void LockStandardSchemes(); +URL_EXPORT void LockSchemeRegistries(); // Locates the scheme in the given string and places it into |found_scheme|, // which may be NULL to indicate the caller does not care about the range. @@ -112,6 +121,10 @@ URL_EXPORT bool IsStandard(const char* spec, const Component& scheme); URL_EXPORT bool IsStandard(const base::char16* spec, const Component& scheme); +// Returns true if the given scheme identified by |scheme| within |spec| is in +// the list of allowed schemes for referrers (see AddReferrerScheme). +URL_EXPORT bool IsReferrerScheme(const char* spec, const Component& scheme); + // Returns true and sets |type| to the SchemeType of the given scheme // identified by |scheme| within |spec| if the scheme is in the list of known // standard-format schemes (see AddStandardScheme).
diff --git a/url/url_util_unittest.cc b/url/url_util_unittest.cc index 41430621..f0032c2 100644 --- a/url/url_util_unittest.cc +++ b/url/url_util_unittest.cc
@@ -71,6 +71,21 @@ EXPECT_FALSE(IsStandard(kFooScheme, Component(0, strlen(kFooScheme)))); } +TEST(URLUtilTest, IsReferrerScheme) { + const char kHTTPScheme[] = "http"; + EXPECT_TRUE(IsReferrerScheme(kHTTPScheme, Component(0, strlen(kHTTPScheme)))); + + const char kFooScheme[] = "foo"; + EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme)))); +} + +TEST(URLUtilTest, AddReferrerScheme) { + const char kFooScheme[] = "foo"; + EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme)))); + AddReferrerScheme(kFooScheme, url::SCHEME_WITHOUT_PORT); + EXPECT_TRUE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme)))); +} + TEST(URLUtilTest, GetStandardSchemeType) { url::SchemeType scheme_type;